cbm-calculator-lib
v0.0.3
Published
A modular Angular library for calculating **Cubic Meters (CBM)**, managing freight containers, and visualizing 3D cargo loading. Built with Angular Signals and fully tree-shakeable — **import only the components you need**.
Readme
cbm-calculator-lib
A modular Angular library for calculating Cubic Meters (CBM), managing freight containers, and visualizing 3D cargo loading. Built with Angular Signals and fully tree-shakeable — import only the components you need.
Installation
npm install cbm-calculator-libOptional: Only install
threeif you use theContainerVisualizercomponent:npm install three @types/three
Architecture Overview
All components share a single CalculatorState service (providedIn: 'root'). This means:
- Components automatically stay in sync — changing the container type in
ContainerUnitinstantly updatesSummary,VolumeWeightStatus, etc. - You can use any combination of components. No need to import all of them.
- Each component is a separate secondary entry point — only the code you import gets bundled.
cbm-calculator-lib
├── /calculator-state ← Shared reactive state service (core)
├── /container-unit ← Container type, unit system, transport mode selector
├── /item-details ← Cargo items table (add/edit/remove rows)
├── /summary ← Totals: volume, weight, chargeable weight, pricing basis
├── /volume-weight-status ← Visual progress bars for container utilization
├── /transport-comparison ← Side-by-side Sea / Air / Road freight comparison
└── /container-visualizer ← Interactive 3D container loading viewer (Three.js)Components
1. CalculatorState — Core Service
Import path: cbm-calculator-lib/calculator-state
The reactive state engine. All UI components inject this service. You can also inject it directly to read or mutate state in your own components.
import { CalculatorState } from 'cbm-calculator-lib/calculator-state';
@Component({ ... })
export class MyComponent {
state = inject(CalculatorState);
// Read computed values
totalVolume = this.state.totalItemsVolumeCbm; // signal: number (m³)
totalWeight = this.state.totalItemsWeightKg; // signal: number (kg)
utilization = this.state.volumeUtilization; // signal: number (%)
// Mutate state
addItem() { this.state.addItem(); }
removeItem(id) { this.state.removeItem(id); }
updateItem(id, patch) { this.state.updateItem(id, patch); }
}Exported types:
| Type / Constant | Description |
|---|---|
| CalculatorState | Injectable service class |
| CargoItem | { id, width, length, height, quantity, weightPerUnit } |
| UnitSystem | 'Metric' \| 'Imperial' |
| ContainerType | '20DC' \| '40DC' \| '40HC' |
| TransportMode | 'Sea' \| 'Air' \| 'Road' |
| CONTAINERS | Array of container definitions with inner dimensions |
| TRANSPORT_MODES | Array of transport mode definitions with volumetric factors |
Key computed signals on CalculatorState:
| Signal | Type | Description |
|---|---|---|
| unitSystem | Signal<UnitSystem> | Current unit system |
| containerType | Signal<ContainerType> | Selected container |
| transportMode | Signal<TransportMode> | Selected transport |
| items | Signal<CargoItem[]> | List of cargo items |
| activeContainer | Signal<ContainerDef> | Full spec of selected container |
| containerVolumeM3 | Signal<number> | Container inner volume in m³ |
| totalItemsVolumeCbm | Signal<number> | Sum of all items' volume in m³ |
| totalItemsWeightKg | Signal<number> | Sum of all items' weight in kg |
| volumetricWeightKg | Signal<number> | CBM × transport factor |
| chargeableWeightKg | Signal<number> | max(actual, volumetric) |
| pricingBasis | Signal<string> | 'Volume-Based' \| 'Weight-Based' \| 'Equal' |
| volumeUtilization | Signal<number> | Volume used / container capacity × 100 |
| weightUtilization | Signal<number> | Weight used / max payload × 100 |
2. ContainerUnit — Settings Panel
Import path: cbm-calculator-lib/container-unit
Selector: <app-container-unit>
Renders dropdowns to select container type, unit system (Metric/Imperial), and transport mode. Also shows transport-specific pricing inputs and the selected container's spec summary.
import { ContainerUnit } from 'cbm-calculator-lib/container-unit';
@Component({
imports: [ContainerUnit],
template: `<app-container-unit />`
})
export class MyPage {}Peer dependency required: @angular/common
3. ItemDetails — Cargo Items Table
Import path: cbm-calculator-lib/item-details
Selector: <app-item-details>
An editable table where users enter cargo dimensions (W × L × H), quantity, and weight per unit. Each row shows the calculated CBM. Rows can be added or removed.
import { ItemDetails } from 'cbm-calculator-lib/item-details';
@Component({
imports: [ItemDetails],
template: `<app-item-details />`
})
export class MyPage {}Peer dependency required: @angular/common, @angular/forms
4. Summary — Totals Card
Import path: cbm-calculator-lib/summary
Selector: <app-summary>
Displays total volume, total weight, converted values, volumetric weight, chargeable weight, containers needed, and pricing basis.
import { Summary } from 'cbm-calculator-lib/summary';
@Component({
imports: [Summary],
template: `<app-summary />`
})
export class MyPage {}Peer dependency required: @angular/common
5. VolumeWeightStatus — Utilization Bars
Import path: cbm-calculator-lib/volume-weight-status
Selector: <app-volume-weight-status>
Shows animated progress bars for volume utilization and weight utilization of the selected container. The weight bar turns red when utilization exceeds 90%.
import { VolumeWeightStatus } from 'cbm-calculator-lib/volume-weight-status';
@Component({
imports: [VolumeWeightStatus],
template: `<app-volume-weight-status />`
})
export class MyPage {}Peer dependency required: @angular/common
6. TransportComparison — Freight Mode Comparison
Import path: cbm-calculator-lib/transport-comparison
Selector: <app-transport-comparison>
Side-by-side cards comparing Sea Freight, Air Freight, and Road Freight. Shows the volumetric weight, chargeable weight, and smart insights for each mode based on the current cargo.
| Mode | Volumetric factor | |---|---| | Sea Freight | 1 m³ = 1,000 kg | | Air Freight (IATA) | 1 m³ = 167 kg | | Road Freight | 1 m³ = 333 kg |
import { TransportComparison } from 'cbm-calculator-lib/transport-comparison';
@Component({
imports: [TransportComparison],
template: `<app-transport-comparison />`
})
export class MyPage {}Peer dependency required: @angular/common
7. ContainerVisualizer — 3D Viewer
Import path: cbm-calculator-lib/container-visualizer
Selector: <app-container-visualizer>
An interactive 3D visualization of cargo items packed inside the selected container. Uses Three.js with OrbitControls — users can rotate, zoom, and pan the scene. The 3D scene is loaded on demand (click "Load 3D Container") to avoid unnecessary GPU usage.
import { ContainerVisualizer } from 'cbm-calculator-lib/container-visualizer';
@Component({
imports: [ContainerVisualizer],
template: `<app-container-visualizer />`
})
export class MyPage {}Peer dependency required: @angular/common, three (must be installed separately)
npm install three @types/threeUsage Examples
Minimal setup — Summary + Item entry only
No visualizer, no transport comparison. Only the components you need:
import { Component } from '@angular/core';
import { ItemDetails } from 'cbm-calculator-lib/item-details';
import { Summary } from 'cbm-calculator-lib/summary';
@Component({
selector: 'app-root',
imports: [ItemDetails, Summary],
template: `
<app-item-details />
<app-summary />
`
})
export class AppComponent {}three is not needed here. The ContainerVisualizer is never imported, so it is never bundled.
Full setup — all components
import { Component } from '@angular/core';
import { ContainerUnit } from 'cbm-calculator-lib/container-unit';
import { ItemDetails } from 'cbm-calculator-lib/item-details';
import { Summary } from 'cbm-calculator-lib/summary';
import { VolumeWeightStatus } from 'cbm-calculator-lib/volume-weight-status';
import { TransportComparison } from 'cbm-calculator-lib/transport-comparison';
import { ContainerVisualizer } from 'cbm-calculator-lib/container-visualizer';
@Component({
selector: 'app-root',
imports: [
ContainerUnit,
ItemDetails,
Summary,
VolumeWeightStatus,
TransportComparison,
ContainerVisualizer,
],
template: `
<div class="layout">
<app-container-unit />
<app-summary />
</div>
<app-item-details />
<app-volume-weight-status />
<app-transport-comparison />
<app-container-visualizer />
`
})
export class AppComponent {}Reading state in your own component
Inject CalculatorState directly to access computed values or trigger actions:
import { Component, inject } from '@angular/core';
import { CalculatorState } from 'cbm-calculator-lib/calculator-state';
@Component({
selector: 'app-my-widget',
template: `
<p>Total CBM: {{ state.totalItemsVolumeCbm() | number:'1.3-3' }}</p>
<p>Chargeable weight: {{ state.chargeableWeightKg() | number:'1.1-1' }} kg</p>
<p>Pricing basis: {{ state.pricingBasis() }}</p>
`
})
export class MyWidget {
state = inject(CalculatorState);
}Peer Dependencies Summary
| Package | Required by | Optional? |
|---|---|---|
| @angular/core | All components | No |
| @angular/common | All UI components | No |
| @angular/forms | ItemDetails only | Yes |
| three | ContainerVisualizer only | Yes |
Container Specifications
| Container | Inner L × W × H (mm) | Volume | Max Payload | |---|---|---|---| | 20' Dry Container (20DC) | 5898 × 2352 × 2393 | ~33.2 m³ | 28,000 kg | | 40' Dry Container (40DC) | 12025 × 2352 × 2393 | ~67.7 m³ | 28,800 kg | | 40' High Cube (40HC) | 12025 × 2352 × 2698 | ~76.3 m³ | 28,600 kg |
Development
# Clone and install
git clone <repo-url>
cd "cbm calculator"
npm install
# Run the demo app
npm start
# Build the library only
npm run build:lib
# Pack for local testing
npm run package:lib
# Publish to npm
npm run publish:npm