);
}
// Usage
// Renders: "Location not available"
```
### Example: Resetting coordinates [#example-resetting-coordinates]
```typescript
import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates';
class LocationTracker {
private location: [number, number] = DEFAULT_COORDINATE;
setLocation(lat: number, lon: number): void {
this.location = [lat, lon];
}
clearLocation(): void {
this.location = DEFAULT_COORDINATE;
}
hasLocation(): boolean {
return !Number.isNaN(this.location[0]) && !Number.isNaN(this.location[1]);
}
}
```
### Example: Type-safe coordinate handling [#example-type-safe-coordinate-handling]
```typescript
import { DEFAULT_COORDINATE } from '@accelint/constants/coordinates';
type Coordinate = [number, number];
type ValidCoordinate = [lat: number, lon: number];
function normalizeCoordinate(coord: Coordinate): ValidCoordinate | null {
if (Number.isNaN(coord[0]) || Number.isNaN(coord[1])) {
return null;
}
// Clamp latitude to [-90, 90]
const lat = Math.max(-90, Math.min(90, coord[0]));
// Normalize longitude to [-180, 180]
const lon = ((coord[1] + 180) % 360) - 180;
return [lat, lon];
}
normalizeCoordinate(DEFAULT_COORDINATE); // null
normalizeCoordinate([40.7128, -74.0060]); // [40.7128, -74.0060]
```
## Related [#related]
* [Null Island](https://en.wikipedia.org/wiki/Null_Island) - Wikipedia article on the \[0,0] coordinate
* [Geolocation API](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API) - MDN documentation
# Overview (/docs/packages/constants)
## Installation [#installation]
```bash
pnpm add @accelint/constants
```
## Usage [#usage]
```typescript
// Import from root (all exports)
import {
CSS_RGBA_LEGACY_REGEX,
DEFAULT_COORDINATE,
DISTANCE_UNIT_SYMBOLS
} from '@accelint/constants';
// Import from sub-modules
import { HEX_REGEX } from '@accelint/constants/color';
import { DISTANCE_UNIT_BY_SYMBOL } from '@accelint/constants/units';
```
## Available Exports [#available-exports]
| Export Path | Description |
| ---------------------------- | ------------------------------------------------------------------------- |
| [color](./color) | Regular expressions for validating CSS rgba() and hex color formats |
| [coordinates](./coordinates) | Default empty coordinate value using NaN to avoid "Null Island" at \[0,0] |
| [units](./units) | SI-compliant display symbols for distance units and bidirectional mapping |
## Related [#related]
* [@accelint/converters](../converters/index.md) - Color format converters
* [@accelint/predicates](../predicates/index.md) - Validation predicates
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# Distances (/docs/packages/constants/units)
## Usage [#usage]
```typescript
import {
DISTANCE_UNIT_SYMBOLS,
DISTANCE_UNIT_BY_SYMBOL,
METERS_PER_UNIT,
type DistanceUnit,
type DistanceUnitSymbol,
} from '@accelint/constants/units';
// Get display symbol for a unit
const symbol = DISTANCE_UNIT_SYMBOLS.kilometers; // 'km'
// Get unit name from display symbol
const unit = DISTANCE_UNIT_BY_SYMBOL['km']; // 'kilometers'
// Convert meters to a unit
const nauticalMiles = 3704 / METERS_PER_UNIT.nauticalmiles; // 2
// Format distance with correct symbol
const distance = 42.195;
const label = `${distance} ${DISTANCE_UNIT_SYMBOLS.kilometers}`; // '42.195 km'
```
## Reference [#reference]
### DISTANCE\_UNIT\_SYMBOLS [#distance_unit_symbols]
```typescript
const DISTANCE_UNIT_SYMBOLS: {
readonly kilometers: 'km';
readonly meters: 'm';
readonly nauticalmiles: 'NM';
readonly miles: 'mi';
readonly feet: 'ft';
};
```
Maps Turf.js-compatible unit names to their correct display symbols per the International System of Units (SI) and international standards.
**Key distinctions from informal abbreviations:**
* `km` not `KM` - SI uses lowercase k for kilo prefix, lowercase m for meters
* `m` not `M` - SI uppercase M is the mega prefix, not meters
* `NM` for nautical miles - ICAO/IMO aviation/maritime convention
* `mi` and `ft` - Not SI units but follow conventional lowercase
**Available units:**
* `kilometers` → `'km'` (SI unit)
* `meters` → `'m'` (SI unit)
* `nauticalmiles` → `'NM'` (ICAO/IMO standard)
* `miles` → `'mi'` (Imperial unit)
* `feet` → `'ft'` (Imperial unit)
### DISTANCE\_UNIT\_BY\_SYMBOL [#distance_unit_by_symbol]
```typescript
const DISTANCE_UNIT_BY_SYMBOL: Record;
```
Reverse lookup map from display symbol to Turf.js unit name.
```typescript
DISTANCE_UNIT_BY_SYMBOL['km'] // 'kilometers'
DISTANCE_UNIT_BY_SYMBOL['m'] // 'meters'
DISTANCE_UNIT_BY_SYMBOL['NM'] // 'nauticalmiles'
DISTANCE_UNIT_BY_SYMBOL['mi'] // 'miles'
DISTANCE_UNIT_BY_SYMBOL['ft'] // 'feet'
```
### METERS\_PER\_UNIT [#meters_per_unit]
```typescript
const METERS_PER_UNIT: {
readonly kilometers: 1000;
readonly meters: 1;
readonly nauticalmiles: 1852;
readonly miles: 1609.344;
readonly feet: 0.3048;
};
```
Meters in one of each supported distance unit. This is the single conversion table for distance formatting across packages. Divide a distance in meters by the factor to convert to the unit, or multiply to convert back to meters.
```typescript
METERS_PER_UNIT.kilometers // 1000
METERS_PER_UNIT.meters // 1
METERS_PER_UNIT.nauticalmiles // 1852
METERS_PER_UNIT.miles // 1609.344
METERS_PER_UNIT.feet // 0.3048
```
The object is declared `as const satisfies Record`, so it has exactly one entry per `DistanceUnit` and each value is a literal type.
### DistanceUnit [#distanceunit]
```typescript
type DistanceUnit = 'kilometers' | 'meters' | 'nauticalmiles' | 'miles' | 'feet';
```
Turf.js-compatible unit names for distance calculations.
### DistanceUnitSymbol [#distanceunitsymbol]
```typescript
type DistanceUnitSymbol = 'km' | 'm' | 'NM' | 'mi' | 'ft';
```
Display symbol for a distance unit.
> **Good to know:** These symbols follow SI conventions where applicable. Using the correct symbol (lowercase `km` not uppercase `KM`) matters for scientific accuracy and internationalization.
## Examples [#examples]
### Example: Formatting distance with correct symbols [#example-formatting-distance-with-correct-symbols]
```typescript
import { DISTANCE_UNIT_SYMBOLS } from '@accelint/constants/units';
function formatDistance(value: number, unit: keyof typeof DISTANCE_UNIT_SYMBOLS): string {
const symbol = DISTANCE_UNIT_SYMBOLS[unit];
return `${value.toFixed(2)} ${symbol}`;
}
formatDistance(42.195, 'kilometers'); // '42.19 km'
formatDistance(26.219, 'miles'); // '26.22 mi'
formatDistance(1852, 'meters'); // '1852.00 m'
formatDistance(22.24, 'nauticalmiles'); // '22.24 NM'
```
### Example: Converting between meters and a unit [#example-converting-between-meters-and-a-unit]
```typescript
import { METERS_PER_UNIT, type DistanceUnit } from '@accelint/constants/units';
function metersToUnit(meters: number, unit: DistanceUnit): number {
return meters / METERS_PER_UNIT[unit];
}
function unitToMeters(value: number, unit: DistanceUnit): number {
return value * METERS_PER_UNIT[unit];
}
metersToUnit(3704, 'nauticalmiles'); // 2
metersToUnit(42300, 'kilometers'); // 42.3
unitToMeters(2, 'nauticalmiles'); // 3704
unitToMeters(1, 'miles'); // 1609.344
```
### Example: Parsing user input with symbol lookup [#example-parsing-user-input-with-symbol-lookup]
```typescript
import { DISTANCE_UNIT_BY_SYMBOL, type DistanceUnitSymbol } from '@accelint/constants/units';
function parseDistance(input: string): { value: number; unit: string } | null {
const match = input.match(/^([\d.]+)\s*([a-zA-Z]+)$/);
if (!match) return null;
const [, valueStr, symbolStr] = match;
const value = parseFloat(valueStr);
const symbol = symbolStr as DistanceUnitSymbol;
const unit = DISTANCE_UNIT_BY_SYMBOL[symbol];
return unit ? { value, unit } : null;
}
parseDistance('100 km'); // { value: 100, unit: 'kilometers' }
parseDistance('5.5 mi'); // { value: 5.5, unit: 'miles' }
parseDistance('1852 m'); // { value: 1852, unit: 'meters' }
parseDistance('10 NM'); // { value: 10, unit: 'nauticalmiles' }
parseDistance('100 KM'); // null (incorrect case)
```
### Example: Unit converter with Turf.js [#example-unit-converter-with-turfjs]
```typescript
import { DISTANCE_UNIT_SYMBOLS, type DistanceUnit } from '@accelint/constants/units';
import { convertLength } from '@turf/helpers';
function convertDistance(
value: number,
from: DistanceUnit,
to: DistanceUnit,
): string {
const converted = convertLength(value, from, to);
const symbol = DISTANCE_UNIT_SYMBOLS[to];
return `${converted.toFixed(2)} ${symbol}`;
}
convertDistance(1, 'kilometers', 'meters'); // '1000.00 m'
convertDistance(1, 'miles', 'kilometers'); // '1.61 km'
convertDistance(1, 'nauticalmiles', 'kilometers'); // '1.85 km'
```
### Example: Distance unit selector UI [#example-distance-unit-selector-ui]
```typescript
import { DISTANCE_UNIT_SYMBOLS, type DistanceUnit } from '@accelint/constants/units';
function DistanceUnitSelect({ value, onChange }: {
value: DistanceUnit;
onChange: (unit: DistanceUnit) => void;
}) {
return (
);
}
```
### Example: Aviation distance calculator [#example-aviation-distance-calculator]
```typescript
import {
DISTANCE_UNIT_SYMBOLS,
DISTANCE_UNIT_BY_SYMBOL
} from '@accelint/constants/units';
import { distance } from '@turf/distance';
function calculateFlightDistance(
from: [number, number],
to: [number, number],
): string {
// Aviation uses nautical miles
const nm = distance(from, to, { units: 'nauticalmiles' });
const symbol = DISTANCE_UNIT_SYMBOLS.nauticalmiles;
return `${nm.toFixed(1)} ${symbol}`;
}
calculateFlightDistance([40.7128, -74.0060], [51.5074, -0.1278]);
// "3000.5 NM" (New York to London)
```
### Example: Type-safe unit validation [#example-type-safe-unit-validation]
```typescript
import {
DISTANCE_UNIT_SYMBOLS,
type DistanceUnit,
type DistanceUnitSymbol,
} from '@accelint/constants/units';
function isValidUnit(unit: string): unit is DistanceUnit {
return unit in DISTANCE_UNIT_SYMBOLS;
}
function isValidSymbol(symbol: string): symbol is DistanceUnitSymbol {
return Object.values(DISTANCE_UNIT_SYMBOLS).includes(symbol as DistanceUnitSymbol);
}
isValidUnit('kilometers'); // true
isValidUnit('invalid'); // false
isValidSymbol('km'); // true
isValidSymbol('KM'); // false
```
### Example: Map viewport labeling [#example-map-viewport-labeling]
```typescript
import { DISTANCE_UNIT_SYMBOLS } from '@accelint/constants/units';
interface ViewportScale {
width: number;
height: number;
unit: keyof typeof DISTANCE_UNIT_SYMBOLS;
}
function formatViewport(scale: ViewportScale): string {
const symbol = DISTANCE_UNIT_SYMBOLS[scale.unit];
return `Viewport: ${scale.width} × ${scale.height} ${symbol}`;
}
formatViewport({ width: 600, height: 400, unit: 'nauticalmiles' });
// "Viewport: 600 × 400 NM"
```
## Related [#related]
* [formatBearing & formatDistance](../formatters/bearing.md) - Formatters that consume `METERS_PER_UNIT` and `DISTANCE_UNIT_SYMBOLS`
* [International System of Units (SI)](https://en.wikipedia.org/wiki/International_System_of_Units) - SI unit standards
* [@turf/helpers](https://turfjs.org/docs/api/helpers) - Turf.js unit conversion utilities
* [ICAO standards](https://www.icao.int/) - Aviation unit conventions
# booleanToNumber (/docs/packages/converters/boolean-to-number)
Convert a boolean to an integer using bitwise operations.
## Usage [#usage]
```typescript
import { booleanToNumber } from '@accelint/converters/boolean-to-number';
const one = booleanToNumber(true);
// 1
const zero = booleanToNumber(false);
// 0
```
## Reference [#reference]
```typescript
function booleanToNumber(val: boolean): number
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | --------- | ----------------------------------------- |
| `val` | `boolean` | The boolean value to convert to a number. |
### Returns [#returns]
Returns `1` if `true`, `0` if `false`.
## Examples [#examples]
### Example: Basic conversion [#example-basic-conversion]
```typescript
import { booleanToNumber } from '@accelint/converters/boolean-to-number';
console.log(booleanToNumber(true));
// 1
console.log(booleanToNumber(false));
// 0
```
### Example: Array transformation [#example-array-transformation]
```typescript
import { booleanToNumber } from '@accelint/converters/boolean-to-number';
const flags = [true, false, true, true, false];
const numbers = flags.map(booleanToNumber);
console.log(numbers);
// [1, 0, 1, 1, 0]
```
### Example: Conditional arithmetic [#example-conditional-arithmetic]
```typescript
import { booleanToNumber } from '@accelint/converters/boolean-to-number';
function countActive(items: { active: boolean }[]): number {
return items.reduce((sum, item) => sum + booleanToNumber(item.active), 0);
}
const items = [
{ active: true },
{ active: false },
{ active: true }
];
console.log(countActive(items));
// 2
```
### Example: Bitwise operations [#example-bitwise-operations]
```typescript
import { booleanToNumber } from '@accelint/converters/boolean-to-number';
// Create bit flags
const hasRead = true;
const hasWrite = false;
const hasExecute = true;
const permissions =
(booleanToNumber(hasRead) << 2) |
(booleanToNumber(hasWrite) << 1) |
booleanToNumber(hasExecute);
console.log(permissions.toString(2).padStart(3, '0'));
// "101"
```
> **Good to know:** This function uses bitwise OR with 0 (`| 0`) for efficient conversion. It's faster than using the ternary operator (`val ? 1 : 0`) or unary plus (`+val`).
## Related [#related]
* [toBoolean](../to-boolean/index.md) - Convert various values to boolean
# CSS RGBA Converters (/docs/packages/converters/css-rgba)
Convert between CSS rgba formats (strings, tuples, objects) and RGBA 0-255 tuples. Supports CSS Color Module Level 4 spec including legacy and modern syntax.
## cssRgbaStringToRgba255Tuple [#cssrgbastringtorgba255tuple]
Parse a CSS rgba/rgb string to a Rgba255Tuple.
### Usage [#usage]
```typescript
import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba';
// Legacy comma-separated syntax
const color = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 0.5)');
// [255, 128, 64, 128]
// Modern space-separated syntax
const modernColor = cssRgbaStringToRgba255Tuple('rgb(255 128 64 / 50%)');
// [255, 128, 64, 128]
```
### Reference [#reference]
```typescript
function cssRgbaStringToRgba255Tuple(css: string): Rgba255Tuple
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------------------------------------------------- |
| `css` | `string` | The CSS string to parse. Supports legacy comma-separated and modern space-separated syntax. |
#### Returns [#returns]
Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255.
#### Throws [#throws]
Throws `Error` if:
* The CSS string is invalid
* Values are out of range
* RGB values mix integers and percentages
### Examples [#examples]
#### Example: Legacy comma-separated syntax [#example-legacy-comma-separated-syntax]
```typescript
import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba';
const color = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 0.5)');
console.log(color);
// [255, 128, 64, 128]
const rgb = cssRgbaStringToRgba255Tuple('rgb(255, 128, 64)');
console.log(rgb);
// [255, 128, 64, 255]
```
#### Example: Modern space-separated syntax [#example-modern-space-separated-syntax]
```typescript
import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba';
const color = cssRgbaStringToRgba255Tuple('rgb(255 128 64)');
console.log(color);
// [255, 128, 64, 255]
const colorWithAlpha = cssRgbaStringToRgba255Tuple('rgb(255 128 64 / 0.5)');
console.log(colorWithAlpha);
// [255, 128, 64, 128]
```
#### Example: Percentage values [#example-percentage-values]
```typescript
import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba';
// RGB percentages
const color = cssRgbaStringToRgba255Tuple('rgb(100% 50% 25%)');
console.log(color);
// [255, 128, 64, 255]
// Alpha percentage
const colorWithAlpha = cssRgbaStringToRgba255Tuple('rgba(255, 128, 64, 50%)');
console.log(colorWithAlpha);
// [255, 128, 64, 128]
```
> **Good to know:** RGB values must all be integers or all be percentages—no mixing. Alpha can be decimal (0-1) or percentage (0-100%) regardless of RGB format. If alpha is omitted, it defaults to 255 (fully opaque).
## rgba255TupleToCssRgbaString [#rgba255tupletocssrgbastring]
Convert a Rgba255Tuple to a CSS rgba string.
### Usage [#usage-1]
```typescript
import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba';
const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 128]);
// "rgba(255, 128, 64, 0.5)"
```
### Reference [#reference-1]
```typescript
function rgba255TupleToCssRgbaString(color: Rgba255Tuple): string
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------- |
| `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. |
#### Returns [#returns-1]
Returns a CSS rgba string using legacy comma-separated syntax (e.g., "rgba(255, 128, 64, 0.5)").
### Examples [#examples-1]
#### Example: Basic conversion [#example-basic-conversion]
```typescript
import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba';
const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 255]);
console.log(cssString);
// "rgba(255, 128, 64, 1)"
```
#### Example: With alpha transparency [#example-with-alpha-transparency]
```typescript
import { rgba255TupleToCssRgbaString } from '@accelint/converters/css-rgba';
const cssString = rgba255TupleToCssRgbaString([255, 128, 64, 128]);
console.log(cssString);
// "rgba(255, 128, 64, 0.5)"
```
## cssRgbaTupleToRgba255Tuple [#cssrgbatupletorgba255tuple]
Parse a CSS rgba tuple to a Rgba255Tuple.
### Usage [#usage-2]
```typescript
import { cssRgbaTupleToRgba255Tuple } from '@accelint/converters/css-rgba';
// CSS rgba tuple has alpha in 0-1 range
const rgba255 = cssRgbaTupleToRgba255Tuple([255, 128, 64, 0.5]);
// [255, 128, 64, 128]
```
### Reference [#reference-2]
```typescript
function cssRgbaTupleToRgba255Tuple(tuple: CssRgbaTuple): Rgba255Tuple
type CssRgbaTuple = readonly [
r: number, // 0-255
g: number, // 0-255
b: number, // 0-255
a: number, // 0-1
]
```
#### Parameters [#parameters-2]
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------------------- |
| `tuple` | `CssRgbaTuple` | The CSS rgba tuple \[r, g, b, a] where RGB are 0-255 and alpha is 0-1. |
#### Returns [#returns-2]
Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255.
#### Throws [#throws-1]
Throws `Error` if the tuple is invalid or values are out of range.
### Examples [#examples-2]
#### Example: Converting CSS tuple format [#example-converting-css-tuple-format]
```typescript
import { cssRgbaTupleToRgba255Tuple } from '@accelint/converters/css-rgba';
// CSS format: RGB 0-255, alpha 0-1
const cssFormat = [255, 128, 64, 0.5] as const;
const rgba255 = cssRgbaTupleToRgba255Tuple(cssFormat);
console.log(rgba255);
// [255, 128, 64, 128]
```
> **Good to know:** CSS rgba tuples use 0-1 for alpha (matching CSS standard) while Rgba255Tuple uses 0-255 for all channels. This function converts alpha from 0-1 to 0-255.
## rgba255TupleToCssRgbaTuple [#rgba255tupletocssrgbatuple]
Convert a Rgba255Tuple to a CSS rgba tuple.
### Usage [#usage-3]
```typescript
import { rgba255TupleToCssRgbaTuple } from '@accelint/converters/css-rgba';
const cssTuple = rgba255TupleToCssRgbaTuple([255, 128, 64, 128]);
// [255, 128, 64, 0.5019607843137255]
```
### Reference [#reference-3]
```typescript
function rgba255TupleToCssRgbaTuple(color: Rgba255Tuple): CssRgbaTuple
type CssRgbaTuple = readonly [
r: number, // 0-255
g: number, // 0-255
b: number, // 0-255
a: number, // 0-1
]
```
#### Parameters [#parameters-3]
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------- |
| `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. |
#### Returns [#returns-3]
Returns a CSS rgba tuple `[r, g, b, a]` where RGB are 0-255 and alpha is 0-1.
### Examples [#examples-3]
#### Example: Converting to CSS tuple format [#example-converting-to-css-tuple-format]
```typescript
import { rgba255TupleToCssRgbaTuple } from '@accelint/converters/css-rgba';
const rgba255 = [255, 128, 64, 128] as const;
const cssTuple = rgba255TupleToCssRgbaTuple(rgba255);
console.log(cssTuple);
// [255, 128, 64, 0.5019607843137255]
```
## cssRgbaObjectToRgba255Tuple [#cssrgbaobjecttorgba255tuple]
Convert a CssRgbaObject to a Rgba255Tuple.
### Usage [#usage-4]
```typescript
import { cssRgbaObjectToRgba255Tuple } from '@accelint/converters/css-rgba';
const rgba255 = cssRgbaObjectToRgba255Tuple({
r: 255,
g: 128,
b: 64,
a: 0.5
});
// [255, 128, 64, 128]
```
### Reference [#reference-4]
```typescript
function cssRgbaObjectToRgba255Tuple(obj: CssRgbaObject): Rgba255Tuple
```
#### Parameters [#parameters-4]
| Parameter | Type | Description |
| --------- | --------------- | ------------------------------------------------------- |
| `obj` | `CssRgbaObject` | The CssRgbaObject where RGB are 0-255 and alpha is 0-1. |
#### Returns [#returns-4]
Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255.
#### Throws [#throws-2]
Throws `Error` if any values are out of range.
### Examples [#examples-4]
#### Example: Converting object format [#example-converting-object-format]
```typescript
import { cssRgbaObjectToRgba255Tuple } from '@accelint/converters/css-rgba';
const cssObject = { r: 255, g: 128, b: 64, a: 1 };
const rgba255 = cssRgbaObjectToRgba255Tuple(cssObject);
console.log(rgba255);
// [255, 128, 64, 255]
```
## rgba255TupleToCssRgbaObject [#rgba255tupletocssrgbaobject]
Convert a Rgba255Tuple to a CssRgbaObject.
### Usage [#usage-5]
```typescript
import { rgba255TupleToCssRgbaObject } from '@accelint/converters/css-rgba';
const cssObject = rgba255TupleToCssRgbaObject([255, 128, 64, 128]);
// { r: 255, g: 128, b: 64, a: 0.5019607843137255 }
```
### Reference [#reference-5]
```typescript
function rgba255TupleToCssRgbaObject(color: Rgba255Tuple): CssRgbaObject
```
#### Parameters [#parameters-5]
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------- |
| `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. |
#### Returns [#returns-5]
Returns a CssRgbaObject `{r, g, b, a}` where RGB are 0-255 and alpha is 0-1.
### Examples [#examples-5]
#### Example: Converting to object format [#example-converting-to-object-format]
```typescript
import { rgba255TupleToCssRgbaObject } from '@accelint/converters/css-rgba';
const rgba255 = [255, 128, 64, 255] as const;
const cssObject = rgba255TupleToCssRgbaObject(rgba255);
console.log(cssObject);
// { r: 255, g: 128, b: 64, a: 1 }
```
#### Example: Round-trip conversion [#example-round-trip-conversion]
```typescript
import {
cssRgbaObjectToRgba255Tuple,
rgba255TupleToCssRgbaObject
} from '@accelint/converters/css-rgba';
const original = { r: 255, g: 128, b: 64, a: 0.5 };
const tuple = cssRgbaObjectToRgba255Tuple(original);
const converted = rgba255TupleToCssRgbaObject(tuple);
console.log(converted.r === original.r); // true
console.log(converted.g === original.g); // true
console.log(converted.b === original.b); // true
console.log(Math.abs(converted.a - original.a) < 0.01); // true (floating point precision)
```
## Related [#related]
* [hexToRgba255Tuple](../hex/index.md) - Parse hex color strings
* [rgba255TupleToHex](../hex/index.md) - Convert to hex strings
* [glslToRgba255Tuple](../glsl/index.md) - Convert from GLSL normalized colors
# GLSL Converters (/docs/packages/converters/glsl)
## rgba255TupleToGlsl [#rgba255tupletoglsl]
Convert a Rgba255Tuple from deck.gl format (0-255) to GLSL format (0-1).
### Usage [#usage]
```typescript
import { rgba255TupleToGlsl } from '@accelint/converters/glsl';
const glslColor = rgba255TupleToGlsl([255, 128, 64, 255]);
// [1, 0.5019607843137255, 0.25098039215686274, 1]
```
### Reference [#reference]
```typescript
function rgba255TupleToGlsl(color: Rgba255Tuple): GlslRgbaTuple
type GlslRgbaTuple = readonly [
r: number, // 0-1
g: number, // 0-1
b: number, // 0-1
a: number, // 0-1
]
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------------- | ---------------------------------------------------------- |
| `color` | `Rgba255Tuple` | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. |
#### Returns [#returns]
Returns a GlslRgbaTuple `[r, g, b, a]` where all values are normalized to 0-1.
### Examples [#examples]
#### Example: Basic conversion [#example-basic-conversion]
```typescript
import { rgba255TupleToGlsl } from '@accelint/converters/glsl';
const rgba255 = [255, 128, 64, 255] as const;
const glsl = rgba255TupleToGlsl(rgba255);
console.log(glsl);
// [1, 0.5019607843137255, 0.25098039215686274, 1]
```
#### Example: Using in WebGL shader [#example-using-in-webgl-shader]
```typescript
import { rgba255TupleToGlsl } from '@accelint/converters/glsl';
const color = [255, 128, 64, 255] as const;
const [r, g, b, a] = rgba255TupleToGlsl(color);
// Pass to shader uniform
gl.uniform4f(colorUniformLocation, r, g, b, a);
```
#### Example: With deck.gl layers [#example-with-deckgl-layers]
```typescript
import { rgba255TupleToGlsl } from '@accelint/converters/glsl';
import { ScatterplotLayer } from '@deck.gl/layers';
const deckColor = [255, 128, 64, 255] as const;
const glslColor = rgba255TupleToGlsl(deckColor);
// Use in custom shader
const layer = new ScatterplotLayer({
data: points,
getFillColor: d => deckColor, // deck.gl uses 0-255
getLineColor: glslColor, // Custom shader uses 0-1
updateTriggers: {
getLineColor: glslColor
}
});
```
> **Good to know:** GLSL shaders expect colors in 0-1 range. This function normalizes by dividing each channel by 255. Precision is preserved with full floating point values.
## glslToRgba255Tuple [#glsltorgba255tuple]
Convert a GlslRgbaTuple from GLSL format (0-1) to deck.gl format (0-255).
### Usage [#usage-1]
```typescript
import { glslToRgba255Tuple } from '@accelint/converters/glsl';
const rgba255 = glslToRgba255Tuple([1, 0.5, 0.25, 1]);
// [255, 128, 64, 255]
```
### Reference [#reference-1]
```typescript
function glslToRgba255Tuple(color: GlslRgbaTuple): Rgba255Tuple
type GlslRgbaTuple = readonly [
r: number, // 0-1
g: number, // 0-1
b: number, // 0-1
a: number, // 0-1
]
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | --------------- | --------------------------------------------------------- |
| `color` | `GlslRgbaTuple` | The GlslRgbaTuple \[r, g, b, a] where all values are 0-1. |
#### Returns [#returns-1]
Returns a Rgba255Tuple `[r, g, b, a]` where all values are clamped to 0-255 and rounded.
### Examples [#examples-1]
#### Example: Basic conversion [#example-basic-conversion-1]
```typescript
import { glslToRgba255Tuple } from '@accelint/converters/glsl';
const glsl = [1, 0.5, 0.25, 1] as const;
const rgba255 = glslToRgba255Tuple(glsl);
console.log(rgba255);
// [255, 128, 64, 255]
```
#### Example: Handling out-of-range values [#example-handling-out-of-range-values]
```typescript
import { glslToRgba255Tuple } from '@accelint/converters/glsl';
// Values are clamped to 0-1 range before conversion
const glsl = [1.5, -0.2, 0.5, 0.8] as const;
const rgba255 = glslToRgba255Tuple(glsl);
console.log(rgba255);
// [255, 0, 128, 204]
```
#### Example: Round-trip conversion [#example-round-trip-conversion]
```typescript
import {
rgba255TupleToGlsl,
glslToRgba255Tuple
} from '@accelint/converters/glsl';
const original = [255, 128, 64, 255] as const;
const glsl = rgba255TupleToGlsl(original);
const converted = glslToRgba255Tuple(glsl);
console.log(converted);
// [255, 128, 64, 255] (matches original)
```
> **Good to know:** Values are clamped to 0-1 range before conversion and rounded to nearest integer. This prevents invalid RGB values from out-of-range GLSL colors.
## Related [#related]
* [hexToRgba255Tuple](../hex/index.md) - Parse hex color strings
* [cssRgbaStringToRgba255Tuple](../css-rgba/index.md) - Parse CSS rgba strings
* [rgba255TupleToHex](../hex/index.md) - Convert to hex strings
# Hex Converters (/docs/packages/converters/hex)
## hexToRgba255Tuple [#hextorgba255tuple]
Parse a hex color string to a Rgba255Tuple.
### Usage [#usage]
```typescript
import { hexToRgba255Tuple } from '@accelint/converters/hex';
// 6-character hex
const color = hexToRgba255Tuple('#FF8040');
// [255, 128, 64, 255]
// 3-character shorthand
const shortColor = hexToRgba255Tuple('#F84');
// [255, 136, 68, 255]
```
### Reference [#reference]
```typescript
function hexToRgba255Tuple(hex: string): Rgba255Tuple
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | -------------------------------------------------------------------------------------------- |
| `hex` | `string` | The hex string to parse. Supports "#RGB", "#RGBA", "#RRGGBB", "#RRGGBBAA". Hash is optional. |
#### Returns [#returns]
Returns a Rgba255Tuple `[r, g, b, a]` where all values are 0-255.
#### Throws [#throws]
Throws `Error` if the hex string is invalid.
### Examples [#examples]
#### Example: Standard 6-character hex [#example-standard-6-character-hex]
```typescript
import { hexToRgba255Tuple } from '@accelint/converters/hex';
const color = hexToRgba255Tuple('#FF8040');
console.log(color);
// [255, 128, 64, 255]
```
#### Example: 3-character shorthand [#example-3-character-shorthand]
```typescript
import { hexToRgba255Tuple } from '@accelint/converters/hex';
// Each digit doubles: #F84 → #FF8844
const color = hexToRgba255Tuple('#F84');
console.log(color);
// [255, 136, 68, 255]
```
#### Example: With alpha channel [#example-with-alpha-channel]
```typescript
import { hexToRgba255Tuple } from '@accelint/converters/hex';
// 8-character hex includes alpha
const color = hexToRgba255Tuple('#FF804080');
console.log(color);
// [255, 128, 64, 128]
// 4-character shorthand with alpha
const shortColor = hexToRgba255Tuple('#F840');
console.log(shortColor);
// [255, 136, 68, 0]
```
#### Example: Without hash prefix [#example-without-hash-prefix]
```typescript
import { hexToRgba255Tuple } from '@accelint/converters/hex';
// Hash is optional
const color = hexToRgba255Tuple('FF8040');
console.log(color);
// [255, 128, 64, 255]
```
> **Good to know:** 3-character hex values are expanded by doubling each digit (#RGB → #RRGGBB). 4-character hex values include alpha (#RGBA → #RRGGBBAA). If no alpha is specified, it defaults to 255 (fully opaque).
## rgba255TupleToHex [#rgba255tupletohex]
Convert a Rgba255Tuple to a hex string.
### Usage [#usage-1]
```typescript
import { rgba255TupleToHex } from '@accelint/converters/hex';
// Convert to hex without alpha
const hex = rgba255TupleToHex([255, 128, 64, 255]);
// "#FF8040"
// Include alpha channel
const hexWithAlpha = rgba255TupleToHex([255, 128, 64, 128], true);
// "#FF804080"
```
### Reference [#reference-1]
```typescript
function rgba255TupleToHex(
color: Rgba255Tuple,
includeAlpha?: boolean
): string
```
#### Parameters [#parameters-1]
| Parameter | Type | Default | Description |
| -------------- | -------------- | ------- | ---------------------------------------------------------- |
| `color` | `Rgba255Tuple` | - | The Rgba255Tuple \[r, g, b, a] where all values are 0-255. |
| `includeAlpha` | `boolean` | `false` | Whether to include alpha channel in output. |
#### Returns [#returns-1]
Returns a hex string (e.g., "#FF8040" or "#FF804080").
### Examples [#examples-1]
#### Example: Basic conversion [#example-basic-conversion]
```typescript
import { rgba255TupleToHex } from '@accelint/converters/hex';
const hex = rgba255TupleToHex([255, 128, 64, 255]);
console.log(hex);
// "#FF8040"
```
#### Example: Including alpha channel [#example-including-alpha-channel]
```typescript
import { rgba255TupleToHex } from '@accelint/converters/hex';
const hexWithAlpha = rgba255TupleToHex([255, 128, 64, 128], true);
console.log(hexWithAlpha);
// "#FF804080"
```
#### Example: Round-trip conversion [#example-round-trip-conversion]
```typescript
import { hexToRgba255Tuple, rgba255TupleToHex } from '@accelint/converters/hex';
const original = '#FF8040';
const tuple = hexToRgba255Tuple(original);
const converted = rgba255TupleToHex(tuple);
console.log(converted === original);
// true
```
> **Good to know:** By default, the alpha channel is omitted from the output. Set `includeAlpha: true` to include it. All hex values are uppercase.
## Related [#related]
* [cssRgbaStringToRgba255Tuple](../css-rgba/index.md) - Parse CSS rgba strings
* [rgba255TupleToCssRgbaString](../css-rgba/index.md) - Convert to CSS rgba strings
* [glslToRgba255Tuple](../glsl/index.md) - Convert from GLSL normalized colors
# Overview (/docs/packages/converters)
## Installation [#installation]
```bash
pnpm add @accelint/converters
```
## Usage [#usage]
```typescript
// Import from root (all exports)
import {
hexToRgba255Tuple,
rgba255TupleToHex,
booleanToNumber,
toBoolean
} from '@accelint/converters';
// Import from sub-modules
import { hexToRgba255Tuple } from '@accelint/converters/hex';
import { cssRgbaStringToRgba255Tuple } from '@accelint/converters/css-rgba';
import { glslToRgba255Tuple } from '@accelint/converters/glsl';
```
## Available Exports [#available-exports]
| Export Path | Description |
| ---------------------------------------- | ------------------------------------------------------------------ |
| [boolean-to-number](./boolean-to-number) | Convert boolean values to numeric representations (0/1) |
| [css-rgba](./css-rgba) | Convert between CSS rgba strings/objects and RGBA 0-255 tuples |
| [glsl](./glsl) | Convert between GLSL normalized colors (0-1) and RGBA 0-255 tuples |
| [hex](./hex) | Convert between hex color strings and RGBA 0-255 tuples |
| [to-boolean](./to-boolean) | Convert various value types to boolean |
| [zxy-to-bbox](./zxy-to-bbox) | Convert map tile coordinates (z/x/y) to bounding box coordinates |
## Related [#related]
* [@accelint/constants](../constants) - Color and coordinate constants
* [@accelint/predicates](../predicates) - Value validation predicates
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# toBoolean (/docs/packages/converters/to-boolean)
Returns true for any value not found to be a "false" value. Handles numeric zeros, string literals, and all JavaScript falsy values.
## Usage [#usage]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
console.log(toBoolean(1)); // true
console.log(toBoolean(' FaLsE ')); // false
console.log(toBoolean(' true')); // true
console.log(toBoolean('000.000')); // false
```
## Reference [#reference]
```typescript
function toBoolean(val: unknown): boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | --------- | ---------------------------------- |
| `val` | `unknown` | The value to convert to a boolean. |
### Returns [#returns]
Returns `true` if the value is truthy, `false` otherwise.
### False Values [#false-values]
The following are considered "false" values:
* **Inherently false**: `''` (empty string), `0`, `false`, `undefined`, `null`, `NaN`
* **Numeric zero**: `'0.000'` - any number of leading or trailing zeros
* **String literal**: `'false'` - any capitalizations or space-padding
## Examples [#examples]
### Example: Basic truthy/falsy [#example-basic-truthyfalsy]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
// Inherently falsy values
console.log(toBoolean(false)); // false
console.log(toBoolean(0)); // false
console.log(toBoolean('')); // false
console.log(toBoolean(null)); // false
console.log(toBoolean(undefined)); // false
console.log(toBoolean(NaN)); // false
// Truthy values
console.log(toBoolean(true)); // true
console.log(toBoolean(1)); // true
console.log(toBoolean('hello')); // true
console.log(toBoolean([])); // true
console.log(toBoolean({})); // true
```
### Example: Numeric zero detection [#example-numeric-zero-detection]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
// Various representations of zero
console.log(toBoolean('0')); // false
console.log(toBoolean('0.0')); // false
console.log(toBoolean('0.000')); // false
console.log(toBoolean('000')); // false
console.log(toBoolean('00.00')); // false
// Non-zero numbers
console.log(toBoolean('1')); // true
console.log(toBoolean('0.1')); // true
console.log(toBoolean('-1')); // true
```
### Example: String literal 'false' [#example-string-literal-false]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
// Various forms of the string 'false'
console.log(toBoolean('false')); // false
console.log(toBoolean('False')); // false
console.log(toBoolean('FALSE')); // false
console.log(toBoolean(' false ')); // false
console.log(toBoolean(' FaLsE ')); // false
// Other strings are truthy
console.log(toBoolean('true')); // true
console.log(toBoolean('True')); // true
console.log(toBoolean('yes')); // true
console.log(toBoolean('no')); // true (not special-cased)
```
### Example: Environment variables [#example-environment-variables]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
// Parse environment variable flags
const enableFeature = toBoolean(process.env.ENABLE_FEATURE);
const debugMode = toBoolean(process.env.DEBUG);
// Works with various formats:
// ENABLE_FEATURE=true
// ENABLE_FEATURE=1
// ENABLE_FEATURE=yes
// DEBUG=false
// DEBUG=0
```
### Example: Form input validation [#example-form-input-validation]
```typescript
import { toBoolean } from '@accelint/converters/to-boolean';
function validateCheckbox(input: string | undefined): boolean {
// Handles: "true", "1", "on", or any truthy value
// Rejects: "false", "0", "", undefined, null
return toBoolean(input);
}
console.log(validateCheckbox('on')); // true
console.log(validateCheckbox('true')); // true
console.log(validateCheckbox('1')); // true
console.log(validateCheckbox('false')); // false
console.log(validateCheckbox('0')); // false
console.log(validateCheckbox(undefined)); // false
```
> **Good to know:** This function is lenient by design. For more restrictive boolean parsing (e.g., only accepting true/false/on/off/yes/no), see the predicates package (`@accelint/predicates`).
## Related [#related]
* [booleanToNumber](../boolean-to-number/index.md) - Convert boolean to 1 or 0
# zxyToBbox (/docs/packages/converters/zxy-to-bbox)
Converts a slippy map tile (z, x, y) to a geographic bounding box (west, south, east, north).
## Usage [#usage]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
const bbox = zxyToBbox([8, 71, 96]);
// [west, south, east, north]
```
## Reference [#reference]
```typescript
function zxyToBbox(tile: ZxyTuple): BoundingBoxTuple
type ZxyTuple = [z: number, x: number, y: number]
type BoundingBoxTuple = [
west: number, // left longitude
south: number, // bottom latitude
east: number, // right longitude
north: number // top latitude
]
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | ---------- | --------------------------------------------------------------------------------------------- |
| `tile` | `ZxyTuple` | A tuple containing \[z, x, y] tile coordinates following the slippy map tilenames convention. |
### Returns [#returns]
Returns a BoundingBoxTuple `[west, south, east, north]` representing the geographic bounds of the tile in decimal degrees.
## Examples [#examples]
### Example: Basic tile conversion [#example-basic-tile-conversion]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
// Zoom level 8, tile (71, 96)
const bbox = zxyToBbox([8, 71, 96]);
console.log(bbox);
// [west, south, east, north] in decimal degrees
```
### Example: Web map tile integration [#example-web-map-tile-integration]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
// Get bounds for a tile at zoom 12
const zoom = 12;
const tileX = 1234;
const tileY = 5678;
const [west, south, east, north] = zxyToBbox([zoom, tileX, tileY]);
console.log(`Tile bounds:`);
console.log(` West: ${west}°`);
console.log(` South: ${south}°`);
console.log(` East: ${east}°`);
console.log(` North: ${north}°`);
```
### Example: Tile viewport calculation [#example-tile-viewport-calculation]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
interface TileViewport {
zoom: number;
tileX: number;
tileY: number;
bounds: {
west: number;
south: number;
east: number;
north: number;
};
}
function getTileViewport(
zoom: number,
tileX: number,
tileY: number
): TileViewport {
const [west, south, east, north] = zxyToBbox([zoom, tileX, tileY]);
return {
zoom,
tileX,
tileY,
bounds: { west, south, east, north }
};
}
const viewport = getTileViewport(10, 512, 768);
console.log(viewport);
```
### Example: Batch tile processing [#example-batch-tile-processing]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
// Get bounds for multiple tiles
const tiles = [
[8, 71, 96],
[8, 72, 96],
[8, 71, 97],
[8, 72, 97]
] as const;
const bounds = tiles.map(tile => ({
tile,
bbox: zxyToBbox(tile)
}));
console.log(bounds);
```
### Example: Checking if point is in tile [#example-checking-if-point-is-in-tile]
```typescript
import { zxyToBbox } from '@accelint/converters/zxy-to-bbox';
function isPointInTile(
lon: number,
lat: number,
tile: readonly [number, number, number]
): boolean {
const [west, south, east, north] = zxyToBbox(tile);
return lon >= west && lon <= east && lat >= south && lat <= north;
}
const tile = [10, 512, 768] as const;
console.log(isPointInTile(-122.4194, 37.7749, tile));
// true if San Francisco is in this tile
```
> **Good to know:** This function follows the [OpenStreetMap slippy map tilenames convention](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames). Tile coordinates use a top-left origin, while geographic coordinates use bottom-left (south-west). The function handles the coordinate system conversion automatically.
## Related [#related]
* [Slippy map tilenames (OSM Wiki)](https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames)
# Overview (/docs/packages/core)
## Installation [#installation]
```bash
pnpm add @accelint/core
```
## Main Export [#main-export]
```typescript
import { map, filter, pipe } from '@accelint/core';
// Or import from subpaths for better tree-shaking
import { map } from '@accelint/core/array/map';
import { pipe } from '@accelint/core/composition/pipe';
```
## Categories [#categories]
### Array Functions [#array-functions]
Curried array manipulation functions that never mutate the original array:
* **[map](./array/map/index.md)** - Transform array elements
* **[filter](./array/filter/index.md)** - Filter elements by predicate
* **[reduce](./array/reduce/index.md)** - Reduce array to single value
* **[find](./array/find/index.md)** - Find first matching element
* **[every](./array/every/index.md)** - Test if all elements pass
* **[some](./array/some/index.md)** - Test if any element passes
* **[concat](./array/concat/index.md)** - Concatenate arrays
* `findIndex`, `findLast`, `findLastIndex`, `includes`, `indexOf`, `push`, `reduceRight`, `reverse`, `shift`, `slice`, `unshift`
### Composition Functions [#composition-functions]
Function composition and currying utilities:
* **[pipe](./composition/pipe/index.md)** - Compose functions left-to-right
* **[compose](./composition/compose/index.md)** - Compose functions right-to-left
* **[autoCurry](./composition/curry/index.md)** - Transform function to accept partial application
### Combinators [#combinators]
Fundamental functional programming primitives:
* `identity` - Returns input unchanged
* `constant` - Returns constant value ignoring input
* `apply` - Apply function to arguments
* `applyTo` - Apply value to function
* `fork` - Apply multiple functions to same input
* `composition` - Compose two functions
* `inverseConstant` - Returns input ignoring constant
### Logical Operators [#logical-operators]
Functional alternatives to logical operators:
* `and`, `or`, `not`, `xor` - Boolean logic operations
* `nand`, `nor` - Negated logic operations
* `equality` - Curried equality comparison
* `nullishOr` - Nullish coalescing operator
### SafeEnum [#safeenum]
Type-safe enum pattern avoiding TypeScript enum pitfalls:
* **[SafeEnum](./safe-enum/safe-enum/index.md)** - Type for creating safe enums
* **[getSafeEnumValues](./safe-enum/get-safe-enum-values/index.md)** - Extract enum values as array
* `IsLiteralEnum` - Type guard for literal enums
* `ValidEnumStructures` - Base constraint for enums
### Object Manipulation [#object-manipulation]
Immutable object transformation utilities:
* `lens` - Functional lens for deep property access
* `get`, `set` - Get/set values using lenses
* `prop`, `property` - Property accessor functions
* `assoc`, `associate` - Associate values with keys
* `composeLens`, `lensProp`, `lensOptionalProp` - Lens composition
### Iterables [#iterables]
Functional approaches to iteration:
* `createIterable` - Create custom iterables
* `range` - Generate number ranges
### Utility Functions [#utility-functions]
General-purpose utilities:
* `lookup` - Safe property lookup
* `noop` - No-operation function
* `once` - Execute function only once
* `tap` - Tap into pipeline for side effects
* `uuid` - Generate UUIDs
* `isUUID` - Validate UUID format
## Design Principles [#design-principles]
### Currying [#currying]
All functions are curried for easy partial application and composition:
```typescript
import { map, filter, pipe } from '@accelint/core';
// Partial application
const double = map((x: number) => x * 2);
double([1, 2, 3]); // [2, 4, 6]
// Composition
const processNumbers = pipe(
filter((x: number) => x > 0),
map((x: number) => x * 2)
);
processNumbers([-1, 0, 1, 2]); // [2, 4]
```
### Purity [#purity]
All functions are pure with no side effects:
```typescript
import { map } from '@accelint/core';
const original = [1, 2, 3];
const doubled = map((x: number) => x * 2)(original);
console.log(original); // [1, 2, 3] - unchanged
console.log(doubled); // [2, 4, 6] - new array
```
### Type Safety [#type-safety]
Full TypeScript support with precise type inference:
```typescript
import { pipe } from '@accelint/core/composition/pipe';
// Types flow through the pipeline
const transform = pipe(
(x: number) => x * 2, // number => number
(x: number) => x.toString(), // number => string
(x: string) => x.length // string => number
);
const result: number = transform(5); // Type: number
```
## Examples [#examples]
### Example: Data transformation pipeline [#example-data-transformation-pipeline]
```typescript
import { pipe } from '@accelint/core/composition/pipe';
import { filter } from '@accelint/core/array/filter';
import { map } from '@accelint/core/array/map';
import { reduce } from '@accelint/core/array/reduce';
interface Product {
name: string;
price: number;
inStock: boolean;
}
const products: Product[] = [
{ name: 'Widget', price: 10, inStock: true },
{ name: 'Gadget', price: 20, inStock: false },
{ name: 'Doohickey', price: 15, inStock: true }
];
const totalInStockValue = pipe(
filter((p: Product) => p.inStock),
map((p: Product) => p.price),
reduce((sum: number, price: number) => sum + price)(0)
);
totalInStockValue(products); // 25
```
### Example: Safe enum usage [#example-safe-enum-usage]
```typescript
import type { SafeEnum } from '@accelint/core/safe-enum/safe-enum';
import { getSafeEnumValues } from '@accelint/core/safe-enum/get-safe-enum-values';
export const UserRole = Object.freeze({
Admin: 'admin',
Editor: 'editor',
Viewer: 'viewer',
} as const);
export type UserRole = SafeEnum;
function hasPermission(role: UserRole, action: string): boolean {
if (role === UserRole.Admin) return true;
if (role === UserRole.Editor && action === 'edit') return true;
return false;
}
// Validate user input
const validRoles = getSafeEnumValues(UserRole);
function isValidRole(input: string): input is UserRole {
return validRoles.includes(input as UserRole);
}
```
### Example: Function composition [#example-function-composition]
```typescript
import { pipe } from '@accelint/core/composition/pipe';
import { autoCurry } from '@accelint/core/composition/curry';
// Build a flexible greeting function
const greet = autoCurry(
(greeting: string, name: string, punctuation: string) =>
`${greeting}, ${name}${punctuation}`
);
const hello = greet('Hello');
const helloWorld = hello('World');
helloWorld('!'); // "Hello, World!"
helloWorld('?'); // "Hello, World?"
```
## Related Packages [#related-packages]
* `@accelint/converters` - Type conversion utilities
* `@accelint/math` - Mathematical operations
* `@accelint/temporal` - Time and date utilities
* `@accelint/bus` - Event bus system
## License [#license]
Apache-2.0
# Overview (/docs/packages/dataset)
## Installation [#installation]
```bash
pnpm add @accelint/dataset
```
## Usage [#usage]
```typescript
import {
// Type definitions
type AnyDataset,
type GeoJsonWFSDataset,
type LayerDatasetMetadata,
// Lens accessors
datasetId,
datasetName,
datasetTable,
datasetServiceUrls,
datasetGeometryProperty,
// Validation
validateDatasetConfig,
anyDatasetSchema,
} from '@accelint/dataset';
// Define a dataset with type safety
const dataset: GeoJsonWFSDataset = {
id: 'weather-stations',
level: 'generated',
visible: true,
mutatable: false,
name: 'Weather Stations',
description: 'Real-time weather monitoring stations',
serviceType: 'WFS',
dataType: 'GEOJSON',
presentationTypes: { temperature: ['icon', 'point'] },
fields: [
{
id: 'temp',
visible: true,
nullable: false,
type: 'f32',
label: 'Temperature (°C)',
},
],
metadata: {
table: 'weather_stations',
serviceUrls: ['https://api.weather.gov/geoserver/wfs'],
geometryProperty: 'location',
defaultFields: ['station_id', 'temp', 'location'],
},
};
// Extract properties using lenses
const id = datasetId(dataset);
const name = datasetName(dataset);
const tableName = datasetTable(dataset);
// Validate dataset configuration
const validatedDataset = validateDatasetConfig(rawConfig);
```
## Features [#features]
### Type Safety [#type-safety]
Comprehensive TypeScript types covering all dataset configurations:
* **Service Types**: WFS, WMS, VTS, REST, FS, Unknown
* **Data Formats**: GeoJSON, Apache Arrow, Unknown
* **Field Types**: Primitives, temporal types, geometry types
* **Discriminated Unions**: Type-safe handling of different dataset combinations
### Lens-Based Accessors [#lens-based-accessors]
Functional lens utilities for extracting dataset properties:
* **Root Properties**: id, name, description, visibility, mutability
* **Service Configuration**: serviceType, dataType, serviceUrls, serviceVersion
* **Metadata Properties**: table, geometryProperty, zoom levels, batch size
* **Composed Lenses**: Navigate dataset → metadata → property in one call
* **Type-Safe Generics**: Compile-time property key validation
### Validation [#validation]
Zod-based schema validation with formatted error messages:
* **Schema Validation**: Comprehensive anyDatasetSchema
* **Field Validation**: All field types, availableValues, nullability
* **Metadata Validation**: Required/optional properties, format constraints
* **Error Formatting**: Full property paths for debugging
* **Curried Validators**: Reusable validation functions
## Package Structure [#package-structure]
### [Types](./types/datasets.mdx) [#types]
Complete TypeScript type definitions for datasets, fields, and metadata. Includes:
* `LayerDataset` - Generic dataset type
* `AnyDataset`, `AnyWFSDataset`, `AnyGeoJsonDataset` - Union types
* `LayerDatasetField`, `LayerDatasetMetadata` - Configuration types
* Concrete types for all service/data combinations
### [Lenses](./lenses.mdx) [#lenses]
Lens-based accessors for type-safe property extraction. Includes:
* **Root Property Lenses**: `datasetId`, `datasetName`, `datasetServiceType`
* **Metadata Lenses**: `metaDataTable`, `metaDataServiceUrls`, `metaDataBackend`
* **Composed Lenses**: `datasetTable`, `datasetServiceUrls`, `datasetGeometryProperty`
* **Generic Lenses**: `datasetIdProperty`, `datasetGeometryProperty`
### [Validation](./validation.mdx) [#validation-1]
Zod schema validation with error formatting. Includes:
* `anyDatasetSchema` - Complete dataset validation schema
* `validateDatasetConfig` - Pre-configured dataset validator
* `validateSchema` - Curried validation function for any schema
## Core Concepts [#core-concepts]
### Dataset Configuration [#dataset-configuration]
A dataset combines service integration with data specification:
```typescript
type LayerDataset = {
// Identification
id: string;
name: string;
description: string;
// Service integration
readonly serviceType: ServiceType;
readonly dataType: DataType;
// Visualization
presentationTypes: Record;
// Schema
fields: LayerDatasetField[];
// Service metadata
metadata: LayerDatasetMetadata;
}
```
### Service Types [#service-types]
Datasets support multiple service protocols:
* **VTS** - Vector Tile Service (pre-processed tiles)
* **WFS** - Web Feature Service (query-capable vector data)
* **WMS** - Web Map Service (raster tiles)
* **REST** - RESTful API endpoints
* **FS** - File System (local/CDN files)
* **Unknown** - Fallback for unrecognized sources
### Data Formats [#data-formats]
Datasets support multiple data formats:
* **GEOJSON** - RFC 7946 JSON format (universal, human-readable)
* **ARROW** - Apache Arrow columnar format (high performance)
* **Unknown** - Format detection pending or unsupported
### Metadata Configuration [#metadata-configuration]
Metadata contains service-specific configuration:
```typescript
type LayerDatasetMetadata = {
// Required
table: string;
geometryProperty: string;
defaultFields: string[];
// Service integration
serviceUrls?: string[];
serviceVersion?: SemVerVersion;
backend?: string;
vendorParams?: Record;
// Rendering
minZoom?: number;
maxZoom?: number;
positionFormat?: 'XY' | 'XYZ';
// Performance
maxRequests?: number;
refetchInterval?: number;
batchSize?: number;
// Querying
filterDialect?: 'cql' | 'gml';
}
```
## Examples [#examples]
### Example: Complete dataset configuration [#example-complete-dataset-configuration]
```typescript
import type { GeoJsonWFSDataset } from '@accelint/dataset';
const dataset: GeoJsonWFSDataset = {
id: 'earthquake-data-2024',
level: 'generated',
visible: true,
mutatable: false,
name: 'Recent Earthquakes',
description: 'Earthquake events from the last 30 days',
serviceType: 'WFS',
dataType: 'GEOJSON',
presentationTypes: {
magnitude: ['point', 'icon'],
depth: ['point'],
},
fields: [
{
id: 'magnitude',
visible: true,
nullable: false,
type: 'f32',
label: 'Magnitude',
},
{
id: 'depth',
visible: true,
nullable: false,
type: 'f32',
label: 'Depth (km)',
},
{
id: 'region',
visible: true,
nullable: true,
type: 'str',
label: 'Region',
},
],
metadata: {
table: 'earthquakes',
serviceUrls: ['https://earthquake.usgs.gov/geoserver/wfs'],
serviceVersion: '2.0.0',
backend: 'geoserver',
vendorParams: {
viewparams: 'minMagnitude:3.0',
},
geometryProperty: 'location',
idProperty: 'event_id',
minZoom: 2,
maxZoom: 18,
positionFormat: 'XYZ',
defaultFields: ['event_id', 'magnitude', 'depth', 'location'],
filterDialect: 'cql',
},
};
```
### Example: Using lenses for property extraction [#example-using-lenses-for-property-extraction]
```typescript
import {
datasetName,
datasetServiceUrls,
datasetTable,
datasetDefaultFields,
datasetGeometryProperty,
} from '@accelint/dataset';
function buildQueryUrl(dataset: AnyDataset): string {
const urls = datasetServiceUrls(dataset);
const table = datasetTable(dataset);
const fields = datasetDefaultFields(dataset);
const baseUrl = urls?.[0] || '';
const params = new URLSearchParams({
typeName: table,
propertyName: fields.join(','),
});
return `${baseUrl}?${params}`;
}
type Feature = { id: number; geom: Point };
const geomKey = datasetGeometryProperty(dataset);
const coordinates = feature[geomKey];
```
### Example: Validating dataset configurations [#example-validating-dataset-configurations]
```typescript
import { validateDatasetConfig } from '@accelint/dataset';
async function loadDataset(id: string) {
const response = await fetch(`/api/datasets/${id}`);
const rawData = await response.json();
try {
const dataset = validateDatasetConfig(rawData);
return { success: true, dataset };
} catch (error) {
console.error('Validation failed:', error.message);
return { success: false, error: error.message };
}
}
```
### Example: Type-safe dataset handling [#example-type-safe-dataset-handling]
```typescript
import type { AnyDataset, AnyArrowDataset } from '@accelint/dataset';
function isArrowDataset(dataset: AnyDataset): dataset is AnyArrowDataset {
return dataset.dataType === 'ARROW';
}
function selectParser(dataset: AnyDataset) {
if (isArrowDataset(dataset)) {
// TypeScript knows this is AnyArrowDataset
return new ArrowParser({ batchSize: dataset.metadata.batchSize });
}
return new GeoJsonParser();
}
```
> **Good to know:** The package uses functional lenses from `@accelint/core` for immutable property access. All lens functions are pure and return new values without modifying inputs. The type system enforces valid service/data type combinations at compile time through generic constraints.
## Related [#related]
* [@accelint/core](../core/index.mdx) - Lens utilities (lensProp, composeLens, get)
* [Zod Documentation](https://zod.dev/) - Schema validation library
* [Deck.gl Documentation](https://deck.gl/) - Visualization layer reference
# Lenses (/docs/packages/dataset/lenses)
## Usage [#usage]
```typescript
import {
datasetId,
datasetName,
datasetTable,
datasetServiceUrls,
datasetGeometryProperty,
} from '@accelint/dataset';
// Extract root properties
const id = datasetId(dataset);
const name = datasetName(dataset);
// Extract metadata properties (composed lenses)
const tableName = datasetTable(dataset);
const urls = datasetServiceUrls(dataset);
// Type-safe property extraction with generics
type Feature = { id: number; location: Point };
const geomKey = datasetGeometryProperty(dataset); // 'location'
const coords = feature[geomKey];
```
## Reference [#reference]
The package exports two categories of lens functions: **Root Property Lenses** for direct dataset properties and **Composed Lenses** for nested metadata properties.
### Root Property Lenses [#root-property-lenses]
Extract top-level dataset properties directly.
#### datasetId [#datasetid]
```typescript
function datasetId(dataset: AnyDataset): string
```
Extracts the unique identifier from a dataset.
**Parameters:**
* `dataset` - The dataset to extract id from
**Returns:** The unique dataset identifier string
#### datasetLevel [#datasetlevel]
```typescript
function datasetLevel(dataset: AnyDataset): 'generated' | 'user'
```
Extracts the provenance level from a dataset.
**Parameters:**
* `dataset` - The dataset to extract level from
**Returns:** `'generated'` for system-created datasets or `'user'` for user-uploaded datasets
#### datasetVisible [#datasetvisible]
```typescript
function datasetVisible(dataset: AnyDataset): boolean
```
Extracts the visibility state from a dataset.
**Parameters:**
* `dataset` - The dataset to extract visibility from
**Returns:** Boolean indicating initial visibility
#### datasetMutatable [#datasetmutatable]
```typescript
function datasetMutatable(dataset: AnyDataset): boolean
```
Extracts the mutability flag from a dataset.
**Parameters:**
* `dataset` - The dataset to extract mutability from
**Returns:** Boolean indicating if dataset can be modified
#### datasetName [#datasetname]
```typescript
function datasetName(dataset: AnyDataset): string
```
Extracts the display name from a dataset.
**Parameters:**
* `dataset` - The dataset to extract name from
**Returns:** Human-readable dataset name
#### datasetDescription [#datasetdescription]
```typescript
function datasetDescription(dataset: AnyDataset): string
```
Extracts the description from a dataset.
**Parameters:**
* `dataset` - The dataset to extract description from
**Returns:** Detailed dataset description string
#### datasetServiceType [#datasetservicetype]
```typescript
function datasetServiceType(dataset: AnyDataset): LayerServiceType
```
Extracts the service type from a dataset.
**Parameters:**
* `dataset` - The dataset to extract service type from
**Returns:** The service protocol type: `'VTS'` | `'WMS'` | `'WFS'` | `'REST'` | `'FS'` | `'Unknown'`
#### datasetDataType [#datasetdatatype]
```typescript
function datasetDataType(dataset: AnyDataset): LayerDataType
```
Extracts the data format type from a dataset.
**Parameters:**
* `dataset` - The dataset to extract data type from
**Returns:** The data format type: `'GEOJSON'` | `'ARROW'` | `'Unknown'`
#### datasetPresentationTypes [#datasetpresentationtypes]
```typescript
function datasetPresentationTypes(
dataset: AnyDataset
): Record
```
Extracts presentation types from a dataset.
**Parameters:**
* `dataset` - The dataset to extract presentation types from
**Returns:** Record mapping field names to available visualization types
#### datasetFields [#datasetfields]
```typescript
function datasetFields(dataset: AnyDataset): LayerDatasetField[]
```
Extracts field definitions from a dataset.
**Parameters:**
* `dataset` - The dataset to extract fields from
**Returns:** Array of `LayerDatasetField` objects
#### datasetMetaData [#datasetmetadata]
```typescript
function datasetMetaData(dataset: AnyDataset): LayerDatasetMetadata
```
Extracts metadata from a dataset.
**Parameters:**
* `dataset` - The dataset to extract metadata from
**Returns:** The `LayerDatasetMetadata` object
### Metadata Property Lenses (Direct Access) [#metadata-property-lenses-direct-access]
Extract properties from `LayerDatasetMetadata` objects directly.
#### metaDataTable [#metadatatable]
```typescript
function metaDataTable(metadata: LayerDatasetMetadata): string
```
Extracts the table name from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** The database table name
#### metaDataServiceUrls [#metadataserviceurls]
```typescript
function metaDataServiceUrls(metadata: LayerDatasetMetadata): string[] | undefined
```
Extracts service URLs from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** Array of service endpoint URLs or undefined
#### metaDataServiceVersion [#metadataserviceversion]
```typescript
function metaDataServiceVersion(metadata: LayerDatasetMetadata): SemVerVersion | undefined
```
Extracts the service version from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** The service version string or undefined
#### metaDataServiceLayer [#metadataservicelayer]
```typescript
function metaDataServiceLayer(metadata: LayerDatasetMetadata): string | undefined
```
Extracts the service layer identifier from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** The service layer identifier or undefined
#### metaDataBackend [#metadatabackend]
```typescript
function metaDataBackend(metadata: LayerDatasetMetadata): string | undefined
```
Extracts the backend implementation identifier from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** Backend identifier string or undefined
#### metaDataVendorParams [#metadatavendorparams]
```typescript
function metaDataVendorParams(metadata: LayerDatasetMetadata): Record | undefined
```
Extracts backend-specific vendor parameters from metadata.
**Parameters:**
* `metadata` - The metadata object to extract from
**Returns:** Record of vendor-specific parameters or undefined
### Composed Dataset Lenses [#composed-dataset-lenses]
Extract metadata properties directly from dataset objects using composed lenses (dataset → metadata → property).
#### datasetTable [#datasettable]
```typescript
function datasetTable(dataset: AnyDataset): string
```
Extracts the table name directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract table name from
**Returns:** The database table name
#### datasetServiceUrls [#datasetserviceurls]
```typescript
function datasetServiceUrls(dataset: AnyDataset): string[] | undefined
```
Extracts service URLs directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract service URLs from
**Returns:** Array of service endpoint URLs or undefined
#### datasetServiceVersion [#datasetserviceversion]
```typescript
function datasetServiceVersion(dataset: AnyDataset): SemVerVersion | undefined
```
Extracts the service version directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract service version from
**Returns:** The service version string or undefined
#### datasetServiceLayer [#datasetservicelayer]
```typescript
function datasetServiceLayer(dataset: AnyDataset): string | undefined
```
Extracts the service layer identifier directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract service layer from
**Returns:** The service layer identifier or undefined
#### datasetBackend [#datasetbackend]
```typescript
function datasetBackend(dataset: AnyDataset): string | undefined
```
Extracts the backend implementation identifier directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract backend from
**Returns:** Backend identifier string or undefined
#### datasetVendorParams [#datasetvendorparams]
```typescript
function datasetVendorParams(dataset: AnyDataset): Record | undefined
```
Extracts backend-specific vendor parameters directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract vendor params from
**Returns:** Record of vendor-specific parameters or undefined
#### datasetIdProperty [#datasetidproperty]
```typescript
function datasetIdProperty(dataset: AnyDataset): keyof T
```
Extracts the feature identifier property name directly from a dataset.
**Type Parameters:**
* `T` - The expected feature type with typed property keys
**Parameters:**
* `dataset` - The dataset to extract id property from
**Returns:** The id property name as a key of T
#### datasetGeometryProperty [#datasetgeometryproperty]
```typescript
function datasetGeometryProperty(dataset: AnyDataset): keyof T
```
Extracts the geometry property name directly from a dataset.
**Type Parameters:**
* `T` - The expected feature type with typed property keys
**Parameters:**
* `dataset` - The dataset to extract geometry property from
**Returns:** The geometry property name as a key of T
#### datasetMinZoom [#datasetminzoom]
```typescript
function datasetMinZoom(dataset: AnyDataset): number | undefined
```
Extracts the minimum zoom level directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract min zoom from
**Returns:** The minimum zoom level or undefined
#### datasetMaxZoom [#datasetmaxzoom]
```typescript
function datasetMaxZoom(dataset: AnyDataset): number | undefined
```
Extracts the maximum zoom level directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract max zoom from
**Returns:** The maximum zoom level or undefined
#### datasetPositionFormat [#datasetpositionformat]
```typescript
function datasetPositionFormat(dataset: AnyDataset): 'XY' | 'XYZ' | undefined
```
Extracts the position format directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract position format from
**Returns:** `'XY'` | `'XYZ'` | undefined
#### datasetMaxRequests [#datasetmaxrequests]
```typescript
function datasetMaxRequests(dataset: AnyDataset): number | undefined
```
Extracts the maximum concurrent requests directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract max requests from
**Returns:** The max requests limit or undefined
#### datasetRefetchInterval [#datasetrefetchinterval]
```typescript
function datasetRefetchInterval(dataset: AnyDataset): number | undefined
```
Extracts the refetch interval directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract refetch interval from
**Returns:** The refetch interval in milliseconds or undefined
#### datasetDefaultFields [#datasetdefaultfields]
```typescript
function datasetDefaultFields(dataset: AnyDataset): string[]
```
Extracts the default fields array directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract default fields from
**Returns:** Array of field names
#### datasetBatchSize [#datasetbatchsize]
```typescript
function datasetBatchSize(dataset: AnyDataset): number | undefined
```
Extracts the batch size directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract batch size from
**Returns:** The batch size or undefined
#### datasetFilterDialect [#datasetfilterdialect]
```typescript
function datasetFilterDialect(dataset: AnyDataset): FilterDialect | undefined
```
Extracts the filter dialect directly from a dataset.
**Parameters:**
* `dataset` - The dataset to extract filter dialect from
**Returns:** `'cql'` | `'gml'` | undefined
## Examples [#examples]
### Example: Building a dataset display component [#example-building-a-dataset-display-component]
```typescript
import {
datasetId,
datasetName,
datasetDescription,
datasetServiceType,
datasetVisible,
} from '@accelint/dataset';
function DatasetCard({ dataset }: { dataset: AnyDataset }) {
const id = datasetId(dataset);
const name = datasetName(dataset);
const description = datasetDescription(dataset);
const serviceType = datasetServiceType(dataset);
const isVisible = datasetVisible(dataset);
return (
{name}
{description}
Service: {serviceType}
{isVisible && Currently visible}
);
}
```
### Example: Building service endpoint URLs [#example-building-service-endpoint-urls]
```typescript
import {
datasetServiceUrls,
datasetServiceVersion,
datasetServiceLayer,
datasetTable,
} from '@accelint/dataset';
function buildWFSUrl(dataset: AnyDataset): string {
const urls = datasetServiceUrls(dataset);
const version = datasetServiceVersion(dataset);
const layer = datasetServiceLayer(dataset);
const table = datasetTable(dataset);
const baseUrl = urls?.[0] || '';
const queryParams = new URLSearchParams({
service: 'WFS',
version: version || '2.0.0',
request: 'GetFeature',
typeName: layer || table,
outputFormat: 'application/json',
});
return `${baseUrl}?${queryParams}`;
}
```
### Example: Type-safe feature property access [#example-type-safe-feature-property-access]
```typescript
import { datasetIdProperty, datasetGeometryProperty } from '@accelint/dataset';
type WeatherFeature = {
// biome-ignore lint/style/useNamingConvention: API response format
station_id: string;
temperature: number;
humidity: number;
location: [number, number];
};
function extractFeatureData(
dataset: AnyDataset,
feature: WeatherFeature
) {
// Type-safe property extraction
const idKey = datasetIdProperty(dataset); // 'station_id'
const geomKey = datasetGeometryProperty(dataset); // 'location'
return {
id: feature[idKey],
coordinates: feature[geomKey],
temperature: feature.temperature,
};
}
```
### Example: Configuring data fetching [#example-configuring-data-fetching]
```typescript
import {
datasetMaxRequests,
datasetRefetchInterval,
datasetBatchSize,
datasetDefaultFields,
} from '@accelint/dataset';
function createFetchConfig(dataset: AnyDataset) {
const maxRequests = datasetMaxRequests(dataset) || 6;
const refetchInterval = datasetRefetchInterval(dataset);
const batchSize = datasetBatchSize(dataset) || 10000;
const fields = datasetDefaultFields(dataset);
return {
concurrency: maxRequests,
polling: refetchInterval ? { interval: refetchInterval } : null,
pagination: { pageSize: batchSize },
fields: fields.join(','),
};
}
```
### Example: Handling backend-specific configuration [#example-handling-backend-specific-configuration]
```typescript
import {
datasetBackend,
datasetVendorParams,
datasetFilterDialect,
} from '@accelint/dataset';
function buildBackendConfig(dataset: AnyDataset) {
const backend = datasetBackend(dataset);
const vendorParams = datasetVendorParams(dataset);
const filterDialect = datasetFilterDialect(dataset);
if (backend === 'geoserver' && vendorParams) {
return {
backend,
formatOptions: vendorParams.formatOptions,
viewparams: vendorParams.viewparams,
filterFormat: filterDialect || 'cql',
};
}
return { backend, filterFormat: filterDialect || 'cql' };
}
```
### Example: Rendering field-based visualizations [#example-rendering-field-based-visualizations]
```typescript
import {
datasetFields,
datasetPresentationTypes,
datasetDataType,
} from '@accelint/dataset';
function selectVisualization(dataset: AnyDataset, fieldName: string) {
const fields = datasetFields(dataset);
const presentationTypes = datasetPresentationTypes(dataset);
const dataType = datasetDataType(dataset);
const field = fields.find(f => f.id === fieldName);
const availableTypes = presentationTypes[fieldName];
if (!field || !availableTypes) {
return null;
}
// Choose appropriate visualization based on field type and data format
if (dataType === 'ARROW' && availableTypes.includes('point')) {
return { type: 'point', field: field.id, label: field.label };
}
return { type: availableTypes[0], field: field.id, label: field.label };
}
```
> **Good to know:** All lens functions are pure and do not modify the input dataset. Optional properties return `undefined` when not present. The generic type parameters on `datasetIdProperty` and `datasetGeometryProperty` provide compile-time type safety for feature property access.
## Related [#related]
* [types/datasets](./types/datasets.mdx) - Dataset type definitions and unions
* [validation](./validation.mdx) - Dataset configuration validation
* [@accelint/core](../../core/index.mdx) - Lens utilities (lensProp, composeLens, get)
# Types (/docs/packages/dataset/types)
## Usage [#usage]
```typescript
import type {
AnyDataset,
LayerDataset,
GeoJsonWFSDataset,
ArrowRESTDataset,
LayerDatasetMetadata,
LayerDatasetField,
} from '@accelint/dataset';
// Use specific dataset types for type safety
const wfsDataset: GeoJsonWFSDataset = {
id: 'weather-stations',
level: 'generated',
visible: true,
mutatable: false,
name: 'Weather Stations',
description: 'Real-time weather monitoring stations',
serviceType: 'WFS',
dataType: 'GEOJSON',
presentationTypes: { temperature: ['icon', 'point'] },
fields: [],
metadata: {
table: 'weather_stations',
geometryProperty: 'location',
defaultFields: ['id', 'temp', 'location'],
},
};
// Use AnyDataset for generic handling
function processDataset(dataset: AnyDataset) {
// Works with any dataset type
}
```
## Reference [#reference]
### Core Types [#core-types]
#### LayerDataset [#layerdataset]
```typescript
type LayerDataset<
DataType extends LayerDataType,
ServiceType extends LayerServiceType,
ExtensionType = ''
>
```
Core dataset configuration combining service integration and data specification. Generic type with compile-time validation of service/data type combinations.
**Type Parameters:**
* `DataType` - Must extend `LayerDataType` (`'GEOJSON'` | `'ARROW'` | `'Unknown'`)
* `ServiceType` - Must extend `LayerServiceType` (`'VTS'` | `'WMS'` | `'WFS'` | `'REST'` | `'FS'` | `'Unknown'`)
* `ExtensionType` - String union of available visual extension types (default: `''`)
**Properties:**
* `id: string` - Unique dataset identifier
* `level: 'generated' | 'user'` - Dataset provenance indicator
* `visible: boolean` - Initial visibility state
* `mutatable: boolean` - Mutability flag for dataset modifications
* `name: string` - Human-readable dataset name
* `description: string` - Detailed dataset description
* `readonly serviceType: ServiceType` - Service integration type
* `readonly dataType: DataType` - Expected response data format
* `presentationTypes: Record` - Available presentation modes per field
* `fields: LayerDatasetField[]` - Complete field schema
* `metadata: LayerDatasetMetadata` - Service integration and rendering metadata
#### LayerServiceType [#layerservicetype]
```typescript
type LayerServiceType = 'VTS' | 'WMS' | 'WFS' | 'REST' | 'FS' | 'Unknown'
```
Service protocols supported for dataset integration.
* **`'VTS'`** - Vector Tile Service (high performance, pre-processed tiles)
* **`'WMS'`** - Web Map Service (raster tiles with feature information)
* **`'WFS'`** - Web Feature Service (vector feature data with query capabilities)
* **`'REST'`** - RESTful Service (generic resource-oriented endpoints)
* **`'FS'`** - File System (static files, local uploads, CDN-hosted data)
* **`'Unknown'`** - Fallback for unrecognized or pending service detection
#### LayerDataType [#layerdatatype]
```typescript
type LayerDataType = 'GEOJSON' | 'ARROW' | 'Unknown'
```
Supported data formats for dataset responses and processing.
* **`'GEOJSON'`** - RFC 7946 compliant JSON format, universally supported, human-readable
* **`'ARROW'`** - Apache Arrow columnar format, high performance, efficient memory usage
* **`'Unknown'`** - Format detection pending, error state, or unsupported formats
#### LayerConfigType [#layerconfigtype]
```typescript
type LayerConfigType = 'icon' | 'point' | 'path' | 'polygon' | 'raster'
```
Built-in layer types for Deck.gl visualization layers.
* **`'icon'`** - MaskedIconLayer for point data with custom symbols
* **`'point'`** - ScatterplotLayer for simple circular point visualization
* **`'path'`** - PathLayer for line/polyline data
* **`'polygon'`** - PolygonLayer for filled area shapes
* **`'raster'`** - Raster layer for image-based rendering
Custom layer types can be added via extension without modifying this core type.
#### SemVerVersion [#semverversion]
```typescript
type SemVerVersion = `${string}.${string}.${string}`
```
Supported semver style protocol versions for WMS/WFS service endpoints.
Version selection affects available request parameters, response formats, spatial reference system support, query capabilities, and performance characteristics.
#### FilterDialect [#filterdialect]
```typescript
type FilterDialect = 'cql' | 'gml'
```
Query language dialect for server-side spatial filtering operations.
* **`'cql'`** - Common Query Language (ECQL), text-based syntax (e.g., `temperature > 20 AND status = 'active'`)
* **`'gml'`** - OGC Filter Encoding, XML-based GML format
### Field Configuration [#field-configuration]
#### LayerDatasetField [#layerdatasetfield]
```typescript
type LayerDatasetField = {
id: string;
visible: boolean;
nullable: boolean;
type: LayerDatasetFieldTypes;
label: string;
availableValues?: { name: string; label: string }[];
}
```
Configuration for a single field within a dataset. Defines field metadata, visibility, and data constraints.
**Properties:**
* `id` - Unique identifier for this field
* `visible` - Controls field visibility in filter UI components
* `nullable` - Whether this field accepts null/undefined values
* `type` - Data type specification (see `LayerDatasetFieldTypes`)
* `label` - Human-readable display name
* `availableValues` - Predefined options for select/dropdown components (optional)
#### LayerDatasetFieldTypes [#layerdatasetfieldtypes]
```typescript
type LayerDatasetFieldTypes =
| 'bool' | 'date' | 'datetime' | 'f32' | 'f64'
| 'i32' | 'i64' | 'linestring' | 'multilinestring'
| 'multipoint' | 'multipolygon' | 'point'
| 'polygon' | 'str' | 'time'
```
Supported data types for layer dataset fields.
**Primitive types:** `bool`, `i32`, `i64`, `str`, `f32`, `f64`
**Temporal types:** `date`, `datetime`, `time`
**Geometry types:** `point`, `multipoint`, `linestring`, `multilinestring`, `polygon`, `multipolygon`
### Metadata Configuration [#metadata-configuration]
#### LayerDatasetMetadata [#layerdatasetmetadata]
```typescript
type LayerDatasetMetadata = {
table: string;
serviceUrls?: string[];
serviceVersion?: SemVerVersion;
backend?: string;
vendorParams?: Record;
serviceLayer?: string;
idProperty?: string;
geometryProperty: string;
minZoom?: number;
maxZoom?: number;
positionFormat?: 'XYZ' | 'XY';
maxRequests?: number;
refetchInterval?: number;
defaultFields: string[];
batchSize?: number;
filterDialect?: FilterDialect;
}
```
Metadata configuration for dataset service integration and rendering. Contains service endpoint information, zoom constraints, and field specifications.
**Required Properties:**
* `table` - Original database table name
* `geometryProperty` - Primary geometry field name
* `defaultFields` - Essential fields for client-side operations
**Optional Properties:**
* `serviceUrls` - List of service endpoint URL templates
* `serviceVersion` - Geoserver service version override
* `backend` - Backend implementation identifier (e.g., `'geoserver'`, `'mapserver'`)
* `vendorParams` - Backend-specific vendor parameters
* `serviceLayer` - Layer identifier for service requests
* `idProperty` - Unique feature identifier property name
* `minZoom` / `maxZoom` - Zoom level constraints
* `positionFormat` - Coordinate system format (`'XY'` or `'XYZ'`)
* `maxRequests` - Concurrent request limit (default: 6)
* `refetchInterval` - Data refresh interval in milliseconds
* `batchSize` - Batch size for Arrow data requests (default: 10000)
* `filterDialect` - Filter expression dialect (default: `'cql'`)
### Concrete Dataset Types [#concrete-dataset-types]
#### GeoJSON Dataset Variants [#geojson-dataset-variants]
```typescript
type GeoJsonFSDataset = LayerDataset<'GEOJSON', 'FS', ExtensionType>
type GeoJsonUnknownDataset = LayerDataset<'GEOJSON', 'Unknown', ExtensionType>
type GeoJsonVTSDataset = LayerDataset<'GEOJSON', 'VTS', ExtensionType>
type GeoJsonWFSDataset = LayerDataset<'GEOJSON', 'WFS', ExtensionType>
type GeoJsonWMSDataset = LayerDataset<'GEOJSON', 'WMS', ExtensionType>
type GeoJsonRESTDataset = LayerDataset<'GEOJSON', 'REST', ExtensionType>
```
Specific GeoJSON dataset types for each service protocol.
#### Arrow Dataset Variants [#arrow-dataset-variants]
```typescript
type ArrowFSDataset = LayerDataset<'ARROW', 'FS', ExtensionType>
type ArrowUnknownDataset = LayerDataset<'ARROW', 'Unknown', ExtensionType>
type ArrowVTSDataset = LayerDataset<'ARROW', 'VTS', ExtensionType>
type ArrowWFSDataset = LayerDataset<'ARROW', 'WFS', ExtensionType>
type ArrowWMSDataset = LayerDataset<'ARROW', 'WMS', ExtensionType>
type ArrowRESTDataset = LayerDataset<'ARROW', 'REST', ExtensionType>
```
Specific Arrow dataset types for each service protocol.
#### Unknown Data Type Variants [#unknown-data-type-variants]
```typescript
type UnknownFSDataset = LayerDataset<'Unknown', 'FS', ExtensionType>
type UnknownUnknownDataset = LayerDataset<'Unknown', 'Unknown', ExtensionType>
type UnknownVTSDataset = LayerDataset<'Unknown', 'VTS', ExtensionType>
type UnknownWFSDataset = LayerDataset<'Unknown', 'WFS', ExtensionType>
type UnknownWMSDataset = LayerDataset<'Unknown', 'WMS', ExtensionType>
type UnknownRESTDataset = LayerDataset<'Unknown', 'REST', ExtensionType>
```
Dataset types with unknown data format (format detection pending, error states, unsupported formats).
### Union Types [#union-types]
#### Service Type Unions [#service-type-unions]
```typescript
type AnyVTSDataset =
| GeoJsonVTSDataset
| ArrowVTSDataset
| UnknownVTSDataset
type AnyWFSDataset =
| GeoJsonWFSDataset
| ArrowWFSDataset
| UnknownWFSDataset
type AnyWMSDataset =
| GeoJsonWMSDataset
| ArrowWMSDataset
| UnknownWMSDataset
type AnyRESTDataset =
| GeoJsonRESTDataset
| ArrowRESTDataset
| UnknownRESTDataset
type AnyFSDataset =
| GeoJsonFSDataset
| ArrowFSDataset
| UnknownFSDataset
type AnyUnknownDataset =
| GeoJsonUnknownDataset
| ArrowUnknownDataset
| UnknownUnknownDataset
```
Unions grouping datasets by service type, accepting any data format.
#### Data Type Unions [#data-type-unions]
```typescript
type AnyGeoJsonDataset =
| GeoJsonFSDataset
| GeoJsonUnknownDataset
| GeoJsonVTSDataset
| GeoJsonWFSDataset
| GeoJsonWMSDataset
| GeoJsonRESTDataset
type AnyArrowDataset =
| ArrowFSDataset
| ArrowUnknownDataset
| ArrowVTSDataset
| ArrowWFSDataset
| ArrowWMSDataset
| ArrowRESTDataset
type AnyUnknownDataDataset =
| UnknownFSDataset
| UnknownUnknownDataset
| UnknownVTSDataset
| UnknownWFSDataset
| UnknownWMSDataset
| UnknownRESTDataset
```
Unions grouping datasets by data format, accepting any service type.
#### AnyDataset [#anydataset]
```typescript
type AnyDataset =
| AnyArrowDataset
| AnyGeoJsonDataset
| AnyUnknownDataDataset
```
Complete union of all possible dataset type combinations. Use for generic dataset handling where specific type constraints are not required.
## Examples [#examples]
### Example: Defining a WFS dataset [#example-defining-a-wfs-dataset]
```typescript
import type { GeoJsonWFSDataset } from '@accelint/dataset';
const weatherDataset: GeoJsonWFSDataset = {
id: 'weather-stations-2024',
level: 'generated',
visible: true,
mutatable: false,
name: 'Weather Stations',
description: 'Real-time weather monitoring stations across the region',
serviceType: 'WFS',
dataType: 'GEOJSON',
presentationTypes: {
temperature: ['icon', 'point'],
humidity: ['point'],
},
fields: [
{
id: 'temp',
visible: true,
nullable: false,
type: 'f32',
label: 'Temperature (°C)',
},
{
id: 'humidity',
visible: true,
nullable: false,
type: 'f32',
label: 'Humidity (%)',
},
{
id: 'status',
visible: true,
nullable: true,
type: 'str',
label: 'Status',
availableValues: [
{ name: 'active', label: 'Active' },
{ name: 'inactive', label: 'Inactive' },
],
},
],
metadata: {
table: 'weather_stations',
serviceUrls: ['https://api.weather.gov/geoserver/wfs'],
serviceVersion: '2.0.0',
backend: 'geoserver',
vendorParams: {
formatOptions: 'includeFids:false;batchSize:10000',
},
geometryProperty: 'location',
idProperty: 'station_id',
minZoom: 5,
maxZoom: 18,
defaultFields: ['station_id', 'temp', 'humidity', 'location'],
filterDialect: 'cql',
},
};
```
### Example: Type-safe dataset handling with discriminated unions [#example-type-safe-dataset-handling-with-discriminated-unions]
```typescript
import type { AnyDataset } from '@accelint/dataset';
function getDataSourceLabel(dataset: AnyDataset): string {
switch (dataset.serviceType) {
case 'WFS':
return 'Web Feature Service';
case 'WMS':
return 'Web Map Service';
case 'VTS':
return 'Vector Tile Service';
case 'REST':
return 'REST API';
case 'FS':
return 'File System';
case 'Unknown':
return 'Unknown Source';
}
}
function getFormatLabel(dataset: AnyDataset): string {
switch (dataset.dataType) {
case 'GEOJSON':
return 'GeoJSON';
case 'ARROW':
return 'Apache Arrow';
case 'Unknown':
return 'Unknown Format';
}
}
```
### Example: Generic dataset processing [#example-generic-dataset-processing]
```typescript
import type { AnyDataset, AnyWFSDataset } from '@accelint/dataset';
function isWFSDataset(dataset: AnyDataset): dataset is AnyWFSDataset {
return dataset.serviceType === 'WFS';
}
function processDataset(dataset: AnyDataset) {
if (isWFSDataset(dataset)) {
// TypeScript knows this is AnyWFSDataset
const version = dataset.metadata.serviceVersion || '2.0.0';
console.log(`Processing WFS ${version} dataset`);
}
}
```
### Example: Building metadata with optional properties [#example-building-metadata-with-optional-properties]
```typescript
import type { LayerDatasetMetadata } from '@accelint/dataset';
function createMetadata(
table: string,
geometryProperty: string,
defaultFields: string[],
options?: {
serviceUrls?: string[];
backend?: string;
minZoom?: number;
maxZoom?: number;
}
): LayerDatasetMetadata {
return {
table,
geometryProperty,
defaultFields,
...options,
};
}
const metadata = createMetadata(
'cities',
'geom',
['id', 'name', 'geom'],
{
serviceUrls: ['https://geoserver.example.com/wfs'],
backend: 'geoserver',
minZoom: 3,
maxZoom: 15,
}
);
```
### Example: Field configuration with available values [#example-field-configuration-with-available-values]
```typescript
import type { LayerDatasetField } from '@accelint/dataset';
const statusField: LayerDatasetField = {
id: 'status',
visible: true,
nullable: true,
type: 'str',
label: 'Status',
availableValues: [
{ name: 'active', label: 'Active' },
{ name: 'inactive', label: 'Inactive' },
{ name: 'pending', label: 'Pending Review' },
{ name: 'archived', label: 'Archived' },
],
};
const temperatureField: LayerDatasetField = {
id: 'temp_celsius',
visible: true,
nullable: false,
type: 'f32',
label: 'Temperature (°C)',
};
```
### Example: Extension types for custom layers [#example-extension-types-for-custom-layers]
```typescript
import type { LayerDataset } from '@accelint/dataset';
// Define custom extension types
type CustomLayerTypes = 'heatmap' | 'contour' | '3d-surface';
// Use with dataset type
type CustomGeoJsonWFS = LayerDataset<'GEOJSON', 'WFS', CustomLayerTypes>;
const customDataset: CustomGeoJsonWFS = {
id: 'elevation-data',
level: 'generated',
visible: true,
mutatable: false,
name: 'Elevation Data',
description: 'Terrain elevation with custom visualizations',
serviceType: 'WFS',
dataType: 'GEOJSON',
presentationTypes: {
elevation: ['heatmap', 'contour', '3d-surface', 'polygon'],
},
fields: [],
metadata: {
table: 'elevation',
geometryProperty: 'geom',
defaultFields: ['elevation', 'geom'],
},
};
```
> **Good to know:** The type system uses generics to enforce valid service/data type combinations at compile time. The `readonly` modifiers on `serviceType` and `dataType` prevent accidental mutation after creation. Union types like `AnyDataset` provide flexibility for generic handling while specific types like `GeoJsonWFSDataset` enable precise type checking.
## Related [#related]
* [lenses](../lenses.mdx) - Lens-based property accessors
* [validation](../validation.mdx) - Zod schema validation for datasets
* [Deck.gl Layer Documentation](https://deck.gl/docs/api-reference/layers) - Visualization layer reference
# Validation (/docs/packages/dataset/validation)
## Usage [#usage]
```typescript
import { validateDatasetConfig, validateSchema, anyDatasetSchema } from '@accelint/dataset';
// Validate a dataset configuration
const dataset = validateDatasetConfig(rawConfig);
// Create reusable validators for any schema
const validateCustom = validateSchema(customSchema);
const result = validateCustom(data);
```
## Reference [#reference]
### anyDatasetSchema [#anydatasetschema]
```typescript
const anyDatasetSchema: z.ZodObject<{
id: z.ZodString;
level: z.ZodEnum<['generated', 'user']>;
visible: z.ZodBoolean;
mutatable: z.ZodBoolean;
name: z.ZodString;
description: z.ZodString;
serviceType: z.ZodEnum<['VTS', 'WMS', 'WFS', 'FS', 'Unknown']>;
dataType: z.ZodEnum<['GEOJSON', 'ARROW', 'Unknown']>;
presentationTypes: z.ZodRecord>>;
fields: z.ZodArray>;
metadata: z.ZodObject<...>;
}>
```
Complete Zod schema for validating dataset configurations. Validates structure, enums, types, and nested metadata.
**Supported service types:** `'VTS'` | `'WMS'` | `'WFS'` | `'FS'` | `'REST'` | `'Unknown'`
**Supported data types:** `'GEOJSON'` | `'ARROW'` | `'Unknown'`
**Field types:** `'bool'`, `'date'`, `'datetime'`, `'f32'`, `'f64'`, `'i32'`, `'i64'`, `'linestring'`, `'multilinestring'`, `'multipoint'`, `'multipolygon'`, `'point'`, `'polygon'`, `'str'`, `'time'`
**Metadata validation includes:**
* Required: `table`, `geometryProperty`, `defaultFields`
* Optional: `serviceUrls`, `serviceVersion`, `serviceLayer`, `backend`, `vendorParams`, `idProperty`, `minZoom`, `maxZoom`, `positionFormat`, `maxRequests`, `refetchInterval`, `batchSize`, `filterDialect`
* Version format: Must be `x.y.z` semver format
* Position format: `'XY'` | `'XYZ'`
* Filter dialect: `'cql'` | `'gml'`
### validateSchema [#validateschema]
```typescript
function validateSchema(schema: z.ZodType): (data: unknown) => T
```
Curried validation function for any Zod schema with consistent error handling.
**Type Parameters:**
* `T` - The expected output type after successful validation
**Parameters:**
* `schema` - Zod schema to validate against
**Returns:** Function that accepts data and returns validated result
**Throws:** Error with formatted path and message details
Provides consistent error formatting across all validation operations. Error messages include full property paths for easy debugging.
### validateDatasetConfig [#validatedatasetconfig]
```typescript
function validateDatasetConfig(data: unknown): AnyDataset
```
Pre-configured validator for dataset configurations. Convenience export that pre-applies `anyDatasetSchema` to the `validateSchema` function.
**Parameters:**
* `data` - Raw dataset configuration to validate
**Returns:** Validated `AnyDataset` object
**Throws:** Error with formatted validation errors
## Examples [#examples]
### Example: Validating dataset from API response [#example-validating-dataset-from-api-response]
```typescript
import { validateDatasetConfig } from '@accelint/dataset';
async function fetchDataset(id: string) {
const response = await fetch(`/api/datasets/${id}`);
const rawData = await response.json();
try {
const dataset = validateDatasetConfig(rawData);
return { success: true, dataset };
} catch (error) {
console.error('Invalid dataset:', error.message);
return { success: false, error: error.message };
}
}
```
### Example: Validating user-uploaded configuration [#example-validating-user-uploaded-configuration]
```typescript
import { validateDatasetConfig } from '@accelint/dataset';
function handleFileUpload(file: File) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const rawConfig = JSON.parse(e.target?.result as string);
const validDataset = validateDatasetConfig(rawConfig);
console.log('Valid dataset:', validDataset.name);
saveDataset(validDataset);
} catch (error) {
showError(`Invalid configuration: ${error.message}`);
}
};
reader.readAsText(file);
}
```
### Example: Creating reusable validators [#example-creating-reusable-validators]
```typescript
import { validateSchema } from '@accelint/dataset';
import { z } from 'zod';
// Define custom schemas
const userSchema = z.object({
name: z.string(),
age: z.number(),
email: z.string().email(),
});
const configSchema = z.object({
apiKey: z.string(),
timeout: z.number().min(1000),
retries: z.number().max(5),
});
// Create validators
const validateUser = validateSchema(userSchema);
const validateConfig = validateSchema(configSchema);
// Use validators multiple times
const user1 = validateUser(rawUserData1);
const user2 = validateUser(rawUserData2);
const config = validateConfig(rawConfigData);
```
### Example: Handling validation errors gracefully [#example-handling-validation-errors-gracefully]
```typescript
import { validateDatasetConfig } from '@accelint/dataset';
function processDatasets(rawDatasets: unknown[]) {
const results = rawDatasets.map((raw, index) => {
try {
const dataset = validateDatasetConfig(raw);
return { index, status: 'valid', dataset };
} catch (error) {
return {
index,
status: 'invalid',
errors: error.message.split('\n').filter(Boolean),
};
}
});
const valid = results.filter(r => r.status === 'valid');
const invalid = results.filter(r => r.status === 'invalid');
console.log(`Validated ${valid.length} datasets`);
console.log(`Rejected ${invalid.length} datasets`);
return { valid, invalid };
}
```
### Example: Validating partial updates [#example-validating-partial-updates]
```typescript
import { validateSchema } from '@accelint/dataset';
import { z } from 'zod';
import { anyDatasetSchema } from '@accelint/dataset';
// Create schema for partial updates
const datasetUpdateSchema = anyDatasetSchema.partial();
const validateUpdate = validateSchema(datasetUpdateSchema);
function updateDataset(existingDataset: AnyDataset, updates: unknown) {
// Validate the updates
const validUpdates = validateUpdate(updates);
// Merge with existing dataset
const updated = { ...existingDataset, ...validUpdates };
// Validate complete dataset
return validateDatasetConfig(updated);
}
```
### Example: Custom validation with refinements [#example-custom-validation-with-refinements]
```typescript
import { validateSchema } from '@accelint/dataset';
import { z } from 'zod';
const datasetWithBusinessRules = z.object({
id: z.string(),
name: z.string().min(3, 'Name must be at least 3 characters'),
visible: z.boolean(),
metadata: z.object({
table: z.string(),
geometryProperty: z.string(),
defaultFields: z.array(z.string()).min(1, 'At least one field required'),
}),
}).refine(
(data) => {
// Business rule: geometry property must be in default fields
return data.metadata.defaultFields.includes(data.metadata.geometryProperty);
},
{
message: 'geometryProperty must be included in defaultFields',
path: ['metadata', 'geometryProperty'],
}
);
const validateWithRules = validateSchema(datasetWithBusinessRules);
```
### Example: Batch validation with error collection [#example-batch-validation-with-error-collection]
```typescript
import { validateDatasetConfig } from '@accelint/dataset';
function validateBatch(rawDatasets: unknown[]) {
const errors: Array<{ index: number; id: string; errors: string[] }> = [];
const validated: AnyDataset[] = [];
for (const [index, raw] of rawDatasets.entries()) {
try {
const dataset = validateDatasetConfig(raw);
validated.push(dataset);
} catch (error) {
errors.push({
index,
id: (raw as any)?.id || 'unknown',
errors: error.message
.split('\n')
.filter((line: string) => line.trim()),
});
}
}
return {
validated,
errors,
summary: {
total: rawDatasets.length,
valid: validated.length,
invalid: errors.length,
},
};
}
```
> **Good to know:** The validation functions throw errors on invalid data. Always wrap validation calls in try-catch blocks when handling user input or API responses. Error messages include full property paths (e.g., `metadata.serviceVersion: Invalid version: must be x.y.z`) for precise debugging.
## Related [#related]
* [types/datasets](./types/datasets.mdx) - Dataset type definitions
* [lenses](./lenses.mdx) - Lens-based property accessors
* [Zod Documentation](https://zod.dev/) - Schema validation library
# Overview (/docs/packages/design-foundation)
## Installation [#installation]
```bash
pnpm add @accelint/design-foundation
```
## Usage [#usage]
```typescript
// Import design tokens
import { tokens } from '@accelint/design-foundation/tokens';
// Import CSS utilities
import { css } from '@accelint/design-foundation/lib/css';
import { utils } from '@accelint/design-foundation/lib/utils';
```
```css
/* Import styles */
@import '@accelint/design-foundation/styles';
```
## Available Exports [#available-exports]
| Export Path | Description |
| --------------------------- | ------------------------------------------------------------------------------------------- |
| [tokens](./tokens/index.md) | Complete design token system including colors, typography, spacing, shadows, and animations |
| [lib/css](./lib/css.md) | CSS utilities and helpers |
| [lib/utils](./lib/utils.md) | JavaScript utility functions for design tokens |
| styles | Core CSS styles and variables |
## Related [#related]
* [@accelint/design-toolkit](../../toolkits/design-toolkit/index.md) - React component library
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# designTokens (/docs/packages/design-foundation/tokens)
Complete design token system providing colors, typography, spacing, radius, shadows, and animations as deck.gl-compatible RGBA tuples and numeric values.
## Usage [#usage]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
import { ScatterplotLayer } from '@deck.gl/layers';
// Use semantic tokens for themed colors
const layer = new ScatterplotLayer({
id: 'points',
data,
// Colors in deck.gl RGBA format: [R, G, B, A] all 0-255
getFillColor: designTokens.dark.bg.accent.primary.bold,
// [0, 79, 126, 255]
});
// Access primitive colors
const brandColor = designTokens.primitive.accent.primary['700'];
// [0, 79, 126, 255]
// Use spacing and typography tokens
const padding = designTokens.spacing.m; // 12
const fontSize = designTokens.typography.size.body.m; // 14
```
## Reference [#reference]
```typescript
const designTokens: ThemeTokens
```
Frozen object containing the complete design token system. All color values use deck.gl RGBA format with alpha channel 0-255.
### Token Structure [#token-structure]
**Theme Tokens** (`dark`, `light`):
* `bg` - Background colors (surface, interactive, accent, utility states)
* `fg` - Foreground/text colors (primary, inverse, accent, utility states)
* `outline` - Border and outline colors (static, interactive, accent, utility states)
* `shadow` - Box shadow definitions (elevation.raised, elevation.overlay)
**Primitive Tokens** (`primitive`):
* `neutral` - Grayscale palette (`'50'` through `'950'`)
* `alpha` - Transparent black/white overlays (`'100'` through `'500'`)
* `accent` - Brand colors (primary, secondary, tertiary)
* `utility` - Semantic colors (info, advisory, normal, serious, critical)
* `on` - Colors for text on colored backgrounds
**Domain Tokens** (`domain`):
* `classification` - Security classification colors
* `roe` - Rules of engagement status colors
**Typography Tokens** (`typography`):
* `height` - Line heights for header/body/button (xxl, xl, l, m, s, xs, xxs)
* `size` - Font sizes for header/body/button
* `spacing` - Letter spacing for header/body/button
* `weight` - Font weights for header/body/button
**Spacing Tokens** (`spacing`):
* Standard spacing scale: `'0'`, `base`, `none`, `xxs`, `xs`, `s`, `m`, `l`, `xl`, `xxl`, `oversized`
**Radius Tokens** (`radius`):
* Border radius values: `none`, `small`, `medium`, `large`, `round`
**Shadow Tokens** (`shadow`):
* Elevation shadows: `elevation.default`, `elevation.overlay.bold/muted`, `elevation.raised.bold/muted`
**Animation Tokens** (`animation`):
* `duration` - Timing values: `instant`, `fast`, `normal`, `slow`
* `easing` - Cubic bezier curves: `standard`, `decelerate`, `accelerate`
### Color Format [#color-format]
All colors use **deck.gl RGBA format**: `[R, G, B, A]` where all channels are 0-255.
This differs from CSS rgba where alpha is 0-1. For CSS usage, import the generated CSS variables instead (see Related section).
> **Good to know:** The design token system ensures consistency between CSS and JavaScript. All tokens are generated from a single source of truth in `primitive.json` and `semantic.json`. When importing `designTokens` directly, you must specify the theme (`dark` or `light`) explicitly. For React components that need theme-aware tokens, use the `useTheme()` hook from `@accelint/design-foundation` instead.
## Examples [#examples]
### Example: Using semantic colors in deck.gl [#example-using-semantic-colors-in-deckgl]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
import { ScatterplotLayer } from '@deck.gl/layers';
const layer = new ScatterplotLayer({
id: 'status-points',
data: statusData,
getFillColor: (d) => {
switch (d.status) {
case 'normal':
return designTokens.dark.bg.normal.bold;
case 'warning':
return designTokens.dark.bg.serious.bold;
case 'critical':
return designTokens.dark.bg.critical.bold;
default:
return designTokens.dark.bg.info.bold;
}
},
getLineColor: designTokens.dark.outline.static,
radiusScale: designTokens.spacing.s,
});
```
### Example: Themed color palette selection [#example-themed-color-palette-selection]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
function getThemeColors(theme: 'dark' | 'light') {
const tokens = designTokens[theme];
return {
background: tokens.bg.surface.default,
foreground: tokens.fg.primary.bold,
accent: tokens.bg.accent.primary.bold,
border: tokens.outline.static,
};
}
// Dark theme
const darkColors = getThemeColors('dark');
// {
// background: [21, 21, 23, 255],
// foreground: [255, 255, 255, 255],
// accent: [0, 79, 126, 255],
// border: [65, 66, 69, 255],
// }
```
### Example: Using primitive colors for data visualization [#example-using-primitive-colors-for-data-visualization]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
// Create a categorical color scale
const categoryColors = [
designTokens.primitive.accent.primary['500'],
designTokens.primitive.accent.secondary['500'],
designTokens.primitive.accent.tertiary['500'],
designTokens.primitive.utility.advisory['500'],
designTokens.primitive.utility.normal['500'],
];
// Use in chart
const chartData = categories.map((category, index) => ({
category,
color: categoryColors[index % categoryColors.length],
}));
```
### Example: Domain-specific colors [#example-domain-specific-colors]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
function getClassificationColor(level: string) {
const colors = designTokens.domain.classification;
switch (level.toUpperCase()) {
case 'UNCLASS':
return colors.unclass;
case 'CUI':
return colors.cui;
case 'CONFIDENTIAL':
return colors.confidential;
case 'SECRET':
return colors.secret;
case 'TOP SECRET':
return colors['top-secret'];
case 'TS/SCI':
return colors['ts-sci'];
default:
return colors.missing;
}
}
// ROE status colors
function getRoeColor(status: string) {
const colors = designTokens.domain.roe;
const statusMap = {
pending: colors.pending,
friend: colors.friend,
'assumed-friend': colors['assumed-friend'],
neutral: colors.neutral,
unknown: colors.unknown,
suspect: colors.suspect,
hostile: colors.hostile,
};
return statusMap[status] || colors['no-statement'];
}
```
### Example: Typography scale [#example-typography-scale]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
const typography = designTokens.typography;
// Generate text style objects
const headerStyles = {
xxl: {
fontSize: typography.size.header.xxl, // 32
lineHeight: typography.height.header.xxl, // 40
fontWeight: typography.weight.header.xxl, // 500
letterSpacing: typography.spacing.header.xxl, // 0
},
xl: {
fontSize: typography.size.header.xl,
lineHeight: typography.height.header.xl,
fontWeight: typography.weight.header.xl,
letterSpacing: typography.spacing.header.xl,
},
// ... more sizes
};
// Use in canvas rendering
function drawText(ctx: CanvasRenderingContext2D, text: string, size: 'xxl' | 'xl' | 'l') {
const style = headerStyles[size];
ctx.font = `${style.fontWeight} ${style.fontSize}px Roboto Flex`;
ctx.letterSpacing = `${style.letterSpacing}px`;
ctx.fillText(text, 0, 0);
}
```
### Example: Spacing and layout [#example-spacing-and-layout]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
const spacing = designTokens.spacing;
// Create a layout grid
const layoutConfig = {
padding: spacing.l, // 16
gap: spacing.m, // 12
margin: spacing.xl, // 24
buttonSpacing: spacing.s, // 8
};
// Use in WebGL/Canvas positioning
function positionElements(elements: Element[]) {
let y = layoutConfig.margin;
for (const element of elements) {
element.y = y;
y += element.height + layoutConfig.gap;
}
}
```
### Example: Border radius for shapes [#example-border-radius-for-shapes]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
const radius = designTokens.radius;
// Use in canvas drawing
function drawRoundedRect(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
width: number,
height: number,
cornerRadius: 'small' | 'medium' | 'large' | 'round'
) {
const r = radius[cornerRadius];
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + width - r, y);
ctx.arcTo(x + width, y, x + width, y + r, r);
ctx.lineTo(x + width, y + height - r);
ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
ctx.lineTo(x + r, y + height);
ctx.arcTo(x, y + height, x, y + height - r, r);
ctx.lineTo(x, y + r);
ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
}
```
### Example: Animation timing [#example-animation-timing]
```typescript
import { designTokens } from '@accelint/design-foundation/tokens';
const animation = designTokens.animation;
// Use in GSAP or custom animation
function animateElement(element: HTMLElement) {
element.style.transition = `all ${animation.duration.normal} ${animation.easing.standard}`;
}
// Keyframe timing
const keyframes = {
fast: parseInt(animation.duration.fast), // 80ms
normal: parseInt(animation.duration.normal), // 160ms
slow: parseInt(animation.duration.slow), // 320ms
};
// Easing curves for canvas animations
const easingFunctions = {
standard: parseEasing(animation.easing.standard),
decelerate: parseEasing(animation.easing.decelerate),
accelerate: parseEasing(animation.easing.accelerate),
};
```
## Related [#related]
* [ThemeTokens](../types/index.md) - TypeScript type definitions for design tokens
* [Design Tokens README](https://github.com/gohypergiant/standard-toolkit/blob/main/packages/design-foundation/src/tokens/README.md) - Token generation system documentation
* [useTheme hook](../../design-toolkit/hooks/use-theme/index.md) - React hook for theme-aware tokens
* [tokens.css](https://github.com/gohypergiant/standard-toolkit/blob/main/packages/design-foundation/src/tokens/tokens.css) - Generated CSS variables for CSS-based styling
* [deck.gl Color Format](https://deck.gl/docs/developer-guide/using-layers#colors) - deck.gl RGBA format documentation
# clsx (/docs/packages/design-foundation/utils)
## Usage [#usage]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
// Basic usage
const className = clsx('btn', 'btn-primary');
// 'btn btn-primary'
// Conditional classes
const className = clsx('btn', isActive && 'btn-active');
// 'btn btn-active' (if isActive is true)
// 'btn' (if isActive is false)
// Object syntax
const className = clsx({
'btn': true,
'btn-active': isActive,
'btn-disabled': isDisabled,
});
```
## Reference [#reference]
```typescript
const clsx: (...inputs: ClassValue[]) => string
```
Re-export of the `clsx` utility from the `clsx` package. Provides a standard interface for conditionally joining class names.
### Parameters [#parameters]
| Parameter | Type | Description |
| ----------- | -------------- | ------------------------------------------------------------------------------ |
| `...inputs` | `ClassValue[]` | Any number of class values: strings, numbers, objects, arrays, or falsy values |
### Returns [#returns]
Returns a single string with all valid class names joined by spaces. Falsy values are filtered out.
### Supported Input Types [#supported-input-types]
* **String**: `'btn btn-primary'`
* **Number**: `1` (converted to `'1'`)
* **Object**: `{ 'btn-active': true, 'btn-disabled': false }` (only keys with truthy values included)
* **Array**: `['btn', { 'btn-active': isActive }]` (recursively flattened)
* **Falsy values**: `null`, `undefined`, `false`, `''`, `0` (filtered out)
> **Good to know:** This is a re-export from the `clsx` package maintained as a stable entrypoint. If internal behavior needs customization in the future, it can be modified here without changing the import path.
## Examples [#examples]
### Example: Conditional styling with boolean flags [#example-conditional-styling-with-boolean-flags]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
function Button({ primary, disabled, children }) {
return (
);
}
```
### Example: Object syntax for state classes [#example-object-syntax-for-state-classes]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
function Card({ isActive, isHovered, isDisabled }) {
return (
);
}
```
### Example: Combining with Tailwind utility classes [#example-combining-with-tailwind-utility-classes]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
function Alert({ variant, fullWidth, className }) {
return (
);
}
```
### Example: Nested arrays and complex conditions [#example-nested-arrays-and-complex-conditions]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
function Menu({ items, activeId }) {
return (
{items.map(item => (
{item.label}
))}
);
}
```
### Example: Dynamic component variants [#example-dynamic-component-variants]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
type ButtonSize = 'sm' | 'md' | 'lg';
type ButtonVariant = 'primary' | 'secondary' | 'outline';
function Button({
size = 'md',
variant = 'primary',
disabled = false,
children
}: {
size?: ButtonSize;
variant?: ButtonVariant;
disabled?: boolean;
children: React.ReactNode;
}) {
return (
);
}
```
### Example: Filtering out falsy values [#example-filtering-out-falsy-values]
```typescript
import { clsx } from '@accelint/design-foundation/lib/utils';
const className = clsx(
'btn',
null, // filtered out
undefined, // filtered out
false, // filtered out
'', // filtered out
0, // filtered out
'btn-primary', // kept
);
// Result: 'btn btn-primary'
```
## Related [#related]
* [clsx package](https://github.com/lukeed/clsx) - Original package documentation
* [classnames](https://github.com/JedWatson/classnames) - Alternative library with similar API
* [Tailwind CSS](https://tailwindcss.com/) - CSS framework commonly used with clsx
# formatBearing & formatDistance (/docs/packages/formatters/bearing)
## Usage [#usage]
```typescript
import { formatBearing, formatDistance } from '@accelint/formatters/bearing';
// Bearing: wrapped into 0–360, rounded to a whole degree, zero-padded
formatBearing(45); // "045°"
formatBearing(-10); // "350°"
formatBearing(359.6); // "000°"
// Distance: meters in, one decimal place and a unit symbol out
formatDistance(42300, 'kilometers'); // "42.3 km"
formatDistance(42300, ['kilometers', 'nauticalmiles']); // "42.3 km / 22.8 NM"
```
Both functions are pure. Conversion factors come from `METERS_PER_UNIT` and symbols from `DISTANCE_UNIT_SYMBOLS`, both in `@accelint/constants/units`.
## formatBearing [#formatbearing]
Format a bearing in degrees as a zero-padded 3-digit string with a trailing degree symbol. Uses true north reference per NTDS/C2 tactical display conventions.
### Reference [#reference]
```typescript
function formatBearing(degrees: number): string
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------------------------ |
| `degrees` | `number` | The bearing in degrees. May be negative or greater than 360. |
#### Returns [#returns]
A zero-padded 3-digit string with a trailing degree symbol, e.g. `"045°"`. The value is rounded to the nearest whole degree and wrapped into the `0–360` range, so the output is always `"000°"` through `"359°"`.
#### Throws [#throws]
* `RangeError` - `degrees must be a finite number.` when `degrees` is `NaN`, `Infinity`, or `-Infinity`.
### Examples [#examples]
#### Example: Formatting whole-degree bearings [#example-formatting-whole-degree-bearings]
```typescript
import { formatBearing } from '@accelint/formatters/bearing';
formatBearing(45); // "045°"
formatBearing(0); // "000°"
formatBearing(180); // "180°"
```
#### Example: Wrapping out-of-range bearings [#example-wrapping-out-of-range-bearings]
```typescript
import { formatBearing } from '@accelint/formatters/bearing';
formatBearing(-10); // "350°"
formatBearing(370); // "010°"
formatBearing(360); // "000°"
formatBearing(-360); // "000°"
```
#### Example: Rounding fractional bearings [#example-rounding-fractional-bearings]
```typescript
import { formatBearing } from '@accelint/formatters/bearing';
formatBearing(44.5); // "045°"
formatBearing(0.4); // "000°"
formatBearing(-0.4); // "000°"
formatBearing(359.6); // "000°" (rounds to 360, then wraps)
```
#### Example: Rejecting non-finite input [#example-rejecting-non-finite-input]
```typescript
import { formatBearing } from '@accelint/formatters/bearing';
formatBearing(Number.NaN); // RangeError: degrees must be a finite number.
formatBearing(Number.POSITIVE_INFINITY); // RangeError: degrees must be a finite number.
```
> **Good to know:** Rounding happens before wrapping, so a bearing that rounds up to exactly 360 renders as `"000°"`. Bearings 360 degrees apart always produce the same string.
## formatDistance [#formatdistance]
Format a distance in meters as a string with one decimal place and a unit symbol. Pass a single `DistanceUnit` or an array of one or two units; two units are joined with `" / "`.
Supported units: `kilometers`, `meters`, `nauticalmiles`, `miles`, `feet`.
### Reference [#reference-1]
```typescript
function formatDistance(
meters: number,
units: DistanceUnit | DistanceUnit[],
): string
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | -------------------------------- | ------------------------------------------------------------------------- |
| `meters` | `number` | The distance in meters. |
| `units` | `DistanceUnit \| DistanceUnit[]` | A single `DistanceUnit`, or an array of one or two `DistanceUnit` values. |
#### Returns [#returns-1]
A formatted distance string with unit symbol(s). Each value is `meters / METERS_PER_UNIT[unit]`, rounded to one decimal place and followed by `DISTANCE_UNIT_SYMBOLS[unit]`. A single-element array formats the same as a bare unit.
#### Throws [#throws-1]
* `RangeError` - `meters must be a finite number.` when `meters` is `NaN`, `Infinity`, or `-Infinity`.
* `Error` - `formatDistance accepts 1 or 2 units.` when `units` is an empty array or has more than two entries.
* `Error` - `Unsupported distance unit: ` when a unit is not a key of `METERS_PER_UNIT`.
### Examples [#examples-1]
#### Example: Formatting a single unit [#example-formatting-a-single-unit]
```typescript
import { formatDistance } from '@accelint/formatters/bearing';
formatDistance(42300, 'kilometers'); // "42.3 km"
formatDistance(42336, 'nauticalmiles'); // "22.9 NM"
formatDistance(500, 'meters'); // "500.0 m"
formatDistance(1609.344, 'miles'); // "1.0 mi"
formatDistance(30.48, 'feet'); // "100.0 ft"
formatDistance(0, 'kilometers'); // "0.0 km"
```
#### Example: Formatting dual units for a range readout [#example-formatting-dual-units-for-a-range-readout]
```typescript
import { formatDistance } from '@accelint/formatters/bearing';
formatDistance(42300, ['kilometers', 'nauticalmiles']); // "42.3 km / 22.8 NM"
formatDistance(1609.344, ['miles', 'feet']); // "1.0 mi / 5280.0 ft"
formatDistance(0, ['kilometers', 'nauticalmiles']); // "0.0 km / 0.0 NM"
```
#### Example: Passing a single-element array [#example-passing-a-single-element-array]
```typescript
import { formatDistance } from '@accelint/formatters/bearing';
formatDistance(42300, ['kilometers']); // "42.3 km" (same as the bare unit)
```
#### Example: Combining with formatBearing for a bearing/range label [#example-combining-with-formatbearing-for-a-bearingrange-label]
```typescript
import { formatBearing, formatDistance } from '@accelint/formatters/bearing';
function formatBearingRange(degrees: number, meters: number): string {
return `${formatBearing(degrees)} ${formatDistance(meters, ['kilometers', 'nauticalmiles'])}`;
}
formatBearingRange(44.5, 42300); // "045° 42.3 km / 22.8 NM"
```
#### Example: Rejecting invalid input [#example-rejecting-invalid-input]
```typescript
import { formatDistance } from '@accelint/formatters/bearing';
formatDistance(42300, []); // Error: formatDistance accepts 1 or 2 units.
formatDistance(42300, ['kilometers', 'miles', 'feet']); // Error: formatDistance accepts 1 or 2 units.
formatDistance(Number.NaN, 'kilometers'); // RangeError: meters must be a finite number.
```
> **Good to know:** Output always has exactly one decimal place, even for whole numbers (`"1.0 mi"`, not `"1 mi"`). Symbols follow SI and ICAO/IMO conventions (`km`, `m`, `NM`, `mi`, `ft`).
## Related [#related]
* [IFF Formatters](./iff.md) - Format IFF transponder mode codes
* [Distances](../constants/units.md) - `DISTANCE_UNIT_SYMBOLS`, `METERS_PER_UNIT`, and `DistanceUnit` from `@accelint/constants/units`
* [round](../math/round.md) - Round to a fixed number of decimal places
# IFF Formatters (/docs/packages/formatters/iff)
## Usage [#usage]
```typescript
import { formatM1, formatM2, formatM3A, formatM4, formatM5 } from '@accelint/formatters/iff';
// Mode 1: 2-digit mission/type code
const m1 = formatM1(5); // '05'
const m1 = formatM1(); // '--'
// Mode 2: 4-digit unit code
const m2 = formatM2(123); // '0123'
const m2 = formatM2(); // '----'
// Mode 3A: 4-digit squawk code
const m3a = formatM3A(1200); // '1200'
const m3a = formatM3A(7700); // '7700' (emergency)
const m3a = formatM3A(); // '----'
// Mode 4: 4-digit encrypted challenge
const m4 = formatM4(123); // '0123'
// Mode 5: 4-digit cryptographic GPS position
const m5 = formatM5(7654); // '7654'
```
## formatM1 [#formatm1]
Format a 2-digit octal IFF Mode 1 code. Mode 1 identifies the aircraft's mission or type and is only used by military aircraft.
### Reference [#reference]
```typescript
function formatM1(value?: string | number): string
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | ------------------------------- | ---------------------------------------------------------- |
| `value` | `string \| number \| undefined` | The IFF Mode 1 code to format. Accepts numbers or strings. |
#### Returns [#returns]
A 2-digit zero-padded string, or `'--'` if value is null/undefined/empty string.
### Examples [#examples]
#### Example: Formatting single-digit codes [#example-formatting-single-digit-codes]
```typescript
import { formatM1 } from '@accelint/formatters/iff';
const code = formatM1(5); // '05'
const code = formatM1('5'); // '05'
const code = formatM1(9); // '09'
```
#### Example: Formatting two-digit codes [#example-formatting-two-digit-codes]
```typescript
import { formatM1 } from '@accelint/formatters/iff';
const code = formatM1(42); // '42'
const code = formatM1(10); // '10'
```
#### Example: Handling missing values [#example-handling-missing-values]
```typescript
import { formatM1 } from '@accelint/formatters/iff';
const code = formatM1(); // '--'
const code = formatM1(undefined); // '--'
const code = formatM1(''); // '--'
```
> **Good to know:** Mode 1 is a 2-digit octal code (0-77). Values greater than 2 digits are not truncated—they will be returned as-is without validation. Zero values are treated as falsy and return the default value `'--'`.
## formatM2 [#formatm2]
Format a 4-digit octal IFF Mode 2 code. Mode 2 identifies the aircraft's unit code or tail number and is only used by military aircraft.
### Reference [#reference-1]
```typescript
function formatM2(value?: string | number): string
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | ------------------------------- | ---------------------------------------------------------- |
| `value` | `string \| number \| undefined` | The IFF Mode 2 code to format. Accepts numbers or strings. |
#### Returns [#returns-1]
A 4-digit zero-padded string, or `'----'` if value is null/undefined/empty string.
### Examples [#examples-1]
#### Example: Formatting unit codes [#example-formatting-unit-codes]
```typescript
import { formatM2 } from '@accelint/formatters/iff';
const code = formatM2(123); // '0123'
const code = formatM2('123'); // '0123'
const code = formatM2(7654); // '7654'
const code = formatM2(1); // '0001'
```
#### Example: Handling missing values [#example-handling-missing-values-1]
```typescript
import { formatM2 } from '@accelint/formatters/iff';
const code = formatM2(); // '----'
const code = formatM2(undefined); // '----'
const code = formatM2(''); // '----'
```
> **Good to know:** Mode 2 is a 4-digit octal code (0000-7777). Values greater than 4 digits are not truncated. Zero values are treated as falsy and return the default value `'----'`.
## formatM3A [#formatm3a]
Format a 4-digit octal IFF Mode 3A code (squawk code). Mode 3A identifies the aircraft and is used by both military and civilian aircraft. Air traffic controllers assign these codes.
### Reference [#reference-2]
```typescript
function formatM3A(value?: string | number): string
```
#### Parameters [#parameters-2]
| Parameter | Type | Description |
| --------- | ------------------------------- | ------------------------------------------------------------------ |
| `value` | `string \| number \| undefined` | The IFF Mode 3A squawk code to format. Accepts numbers or strings. |
#### Returns [#returns-2]
A 4-digit zero-padded string, or `'----'` if value is null/undefined/empty string.
### Examples [#examples-2]
#### Example: Standard squawk codes [#example-standard-squawk-codes]
```typescript
import { formatM3A } from '@accelint/formatters/iff';
const code = formatM3A(1200); // '1200' (VFR)
const code = formatM3A(1201); // '1201'
const code = formatM3A('1200'); // '1200'
```
#### Example: Emergency squawk codes [#example-emergency-squawk-codes]
```typescript
import { formatM3A } from '@accelint/formatters/iff';
const hijack = formatM3A(7500); // '7500' (hijack/unlawful interference)
const radio = formatM3A(7600); // '7600' (radio failure)
const emergency = formatM3A(7700); // '7700' (general emergency)
```
#### Example: Handling missing values [#example-handling-missing-values-2]
```typescript
import { formatM3A } from '@accelint/formatters/iff';
const code = formatM3A(); // '----'
const code = formatM3A(undefined); // '----'
const code = formatM3A(''); // '----'
```
> **Good to know:** Mode 3A is a 4-digit octal code (0000-7777). Common codes include 1200 (VFR in the US), 7500 (hijack), 7600 (radio failure), and 7700 (emergency). Zero values are treated as falsy and return the default value `'----'`.
## formatM4 [#formatm4]
Format a 4-digit IFF Mode 4 code. Mode 4 uses an encrypted challenge-response system to determine the delay. This mode is only used by military aircraft.
### Reference [#reference-3]
```typescript
function formatM4(value: string | number): string
```
#### Parameters [#parameters-3]
| Parameter | Type | Description |
| --------- | ------------------ | ---------------------------------------------------------- |
| `value` | `string \| number` | The IFF Mode 4 code to format. Accepts numbers or strings. |
#### Returns [#returns-3]
A 4-digit zero-padded string, or `'----'` if value is an empty string.
### Examples [#examples-3]
#### Example: Formatting Mode 4 codes [#example-formatting-mode-4-codes]
```typescript
import { formatM4 } from '@accelint/formatters/iff';
const code = formatM4(123); // '0123'
const code = formatM4('7654'); // '7654'
const code = formatM4(1); // '0001'
```
#### Example: Handling empty strings [#example-handling-empty-strings]
```typescript
import { formatM4 } from '@accelint/formatters/iff';
const code = formatM4(''); // '----'
```
> **Good to know:** Mode 4 format verification is pending (see source TODO). Unlike other modes, formatM4 requires a value parameter. Zero values are treated as falsy and return the default value `'----'`.
## formatM5 [#formatm5]
Format a 4-digit IFF Mode 5 code. Mode 5 is a cryptographically secured version of Mode S and ADS-B GPS position. This mode is only used by military aircraft.
### Reference [#reference-4]
```typescript
function formatM5(value: string | number): string
```
#### Parameters [#parameters-4]
| Parameter | Type | Description |
| --------- | ------------------ | ---------------------------------------------------------- |
| `value` | `string \| number` | The IFF Mode 5 code to format. Accepts numbers or strings. |
#### Returns [#returns-4]
A 4-digit zero-padded string, or `'----'` if value is an empty string.
### Examples [#examples-4]
#### Example: Formatting Mode 5 codes [#example-formatting-mode-5-codes]
```typescript
import { formatM5 } from '@accelint/formatters/iff';
const code = formatM5(123); // '0123'
const code = formatM5('7654'); // '7654'
const code = formatM5(1); // '0001'
```
#### Example: Handling empty strings [#example-handling-empty-strings-1]
```typescript
import { formatM5 } from '@accelint/formatters/iff';
const code = formatM5(''); // '----'
```
> **Good to know:** Mode 5 format verification is pending (see source TODO). Unlike other modes, formatM5 requires a value parameter. Zero values are treated as falsy and return the default value `'----'`.
## Related [#related]
* [formatM1](#formatm1) - Format 2-digit Mode 1 mission/type codes
* [formatM2](#formatm2) - Format 4-digit Mode 2 unit codes
* [formatM3A](#formatm3a) - Format 4-digit Mode 3A squawk codes
* [formatM4](#formatm4) - Format 4-digit Mode 4 encrypted codes
* [formatM5](#formatm5) - Format 4-digit Mode 5 cryptographic codes
# Overview (/docs/packages/formatters)
## Installation [#installation]
```bash
pnpm add @accelint/formatters
```
## Usage [#usage]
```typescript
// Import from root (all exports)
import { formatBearing, formatDistance, formatM3A } from '@accelint/formatters';
// Import from sub-modules
import { formatBearing, formatDistance } from '@accelint/formatters/bearing';
import { formatM1 } from '@accelint/formatters/iff';
```
## Available Exports [#available-exports]
| Export Path | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------------- |
| [bearing](./bearing.md) | Format tactical bearing readouts as zero-padded degrees and distances in meters with SI unit symbols |
| [iff](./iff/index.md) | Format IFF (Identification Friend or Foe) transponder mode codes with proper zero-padding |
## Related [#related]
* [@accelint/converters](../converters/index.md) - Format conversion utilities
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# Overview (/docs/packages/geo)
## Installation [#installation]
```bash
pnpm add @accelint/geo
```
## Usage [#usage]
```typescript
// Import coordinate utilities
import { createCoordinate, coordinateSystems } from '@accelint/geo';
// Import formatters and parsers
import { formatDecimalDegrees, parseDecimalDegrees } from '@accelint/geo';
import { parseMGRS, parseUTM } from '@accelint/geo';
// Import geodesy functions
import { bearing, distance, greatCirclePoints, midpoint } from '@accelint/geo/geodesy';
```
## Available Exports [#available-exports]
| Export Path | Description |
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| [coordinates/coordinate](./coordinates/coordinate.md) | Parse, validate, convert, and format geographic coordinates across multiple notation systems |
| [coordinates/latlon/decimal-degrees](./coordinates/latlon/decimal-degrees/) | Format and parse latitude/longitude in decimal degrees notation |
| [coordinates/latlon/degrees-decimal-minutes](./coordinates/latlon/degrees-decimal-minutes/) | Format and parse coordinates in degrees decimal minutes notation |
| [coordinates/latlon/degrees-minutes-seconds](./coordinates/latlon/degrees-minutes-seconds/) | Format and parse coordinates in degrees minutes seconds notation |
| [coordinates/mgrs/parser](./coordinates/mgrs/parser.md) | Parse Military Grid Reference System (MGRS) coordinate strings |
| [coordinates/utm/parser](./coordinates/utm/parser.md) | Parse Universal Transverse Mercator (UTM) coordinate strings |
| [geodesy](./geodesy/) | Great-circle bearing, distance, midpoint, and path sampling between two `[longitude, latitude]` points |
## Geodesy [#geodesy]
Compute great-circle relationships between two `[longitude, latitude]` points on a spherical Earth. All geodesy functions take `LonLatTuple` values (GeoJSON order), which is the reverse of the `[latitude, longitude]` order used by `createCoordinate`.
* [bearing](./geodesy/bearing.md) - Initial bearing in degrees clockwise from true north, `[0, 360)`
* [distance](./geodesy/distance.md) - Distance in meters
* [midpoint](./geodesy/midpoint.md) - Midpoint along the shortest great-circle path
* [greatCirclePoints](./geodesy/great-circle-points.md) - Evenly spaced vertices along the path, with unwrapped longitudes for drawing
* [toSphericalPoints](./geodesy/to-spherical-points.md) - Shared validation step that returns `LatLonSpherical` points
## Related [#related]
* [@accelint/constants](../constants/index.md) - Coordinate constants and defaults
* [@accelint/predicates](../predicates/index.md) - Coordinate validation predicates
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# Overview (/docs/packages/hotkey-manager)
## Usage [#usage]
```typescript
import { registerHotkey, globalBind, Keycode } from '@accelint/hotkey-manager';
// Bind global listeners once
globalBind();
// Register a save hotkey (Cmd+S on Mac, Ctrl+S elsewhere)
const saveHotkey = registerHotkey({
key: {
code: Keycode.KeyS,
ctrl: true,
autoMacStyle: true,
},
onKeyUp: () => console.log('Save triggered'),
});
// Activate the hotkey
const unbind = saveHotkey.bind();
// Later, clean up
unbind();
```
## globalBind [#globalbind]
Binds keyboard event listeners to the global window object. Safe to call on the server (no-op if not in browser). Only binds once—subsequent calls are ignored until `globalUnbind` is called.
### Usage [#usage-1]
```typescript
import { globalBind } from '@accelint/hotkey-manager';
// Call once at application startup
globalBind();
```
### Reference [#reference]
```typescript
function globalBind(): void
```
Attaches `keydown` and `keyup` listeners to `window`. Tracks binding state internally to prevent duplicate listeners.
### Examples [#examples]
#### Example: Initialize in app entry point [#example-initialize-in-app-entry-point]
```typescript
import { globalBind } from '@accelint/hotkey-manager';
// In your main.ts or App.tsx
globalBind();
```
#### Example: Conditional binding in framework apps [#example-conditional-binding-in-framework-apps]
```typescript
import { globalBind } from '@accelint/hotkey-manager';
import { useEffect } from 'react';
function App() {
useEffect(() => {
globalBind();
}, []);
return
My App
;
}
```
> **Good to know:** `globalBind` is idempotent—calling it multiple times has no effect until `globalUnbind` is called. It's safe to call on the server; it will silently return.
## globalUnbind [#globalunbind]
Unbinds keyboard event listeners from the global window object. Safe to call on the server (no-op if not in browser).
### Usage [#usage-2]
```typescript
import { globalUnbind } from '@accelint/hotkey-manager';
// Remove global listeners
globalUnbind();
```
### Reference [#reference-1]
```typescript
function globalUnbind(): void
```
Removes `keydown` and `keyup` listeners from `window`. Only removes listeners if they were previously bound via `globalBind`.
### Examples [#examples-1]
#### Example: Cleanup on app unmount [#example-cleanup-on-app-unmount]
```typescript
import { globalBind, globalUnbind } from '@accelint/hotkey-manager';
import { useEffect } from 'react';
function App() {
useEffect(() => {
globalBind();
return () => globalUnbind();
}, []);
return
My App
;
}
```
#### Example: Toggle hotkeys on/off [#example-toggle-hotkeys-onoff]
```typescript
import { globalBind, globalUnbind } from '@accelint/hotkey-manager';
function enableHotkeys() {
globalBind();
}
function disableHotkeys() {
globalUnbind();
}
```
> **Good to know:** `globalUnbind` only removes listeners if `globalBind` was called first. It's safe to call multiple times.
## registerHotkey [#registerhotkey]
Registers a keyboard shortcut and returns a manager object to control its activation. Supports single keys, modifier combinations, and chord sequences.
### Usage [#usage-3]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
const saveHotkey = registerHotkey({
key: {
code: Keycode.KeyS,
meta: true,
},
onKeyUp: () => console.log('Saved'),
});
// Activate the hotkey
const unbind = saveHotkey.bind();
```
### Reference [#reference-2]
```typescript
function registerHotkey(options: HotkeyOptions): HotkeyManager
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | --------------- | --------------------------------------------------------------------- |
| `options` | `HotkeyOptions` | Hotkey configuration including key combination(s) and event handlers. |
#### `HotkeyOptions` [#hotkeyoptions]
```typescript
type HotkeyOptions = {
key: KeyOption | KeyOption[];
id?: string;
triggerOnInput?: boolean;
} & RequireAtLeastOne<{
onKeyDown?: HotkeyAction;
onKeyUp?: HotkeyAction;
}>;
```
| Property | Type | Default | Description |
| ---------------- | -------------------------- | -------------- | ---------------------------------------------------- |
| `key` | `KeyOption \| KeyOption[]` | - | Single key combination or array for chord sequences. |
| `id` | `string` | auto-generated | Optional unique identifier for the hotkey. |
| `triggerOnInput` | `boolean` | `false` | Whether to trigger when focused on input elements. |
| `onKeyDown` | `HotkeyAction` | - | Handler called on keydown. |
| `onKeyUp` | `HotkeyAction` | - | Handler called on keyup. |
At least one of `onKeyDown` or `onKeyUp` must be provided.
#### `KeyOption` [#keyoption]
```typescript
type KeyOption = {
code: Keycode;
ctrl?: boolean;
alt?: boolean;
shift?: boolean;
meta?: boolean;
autoMacStyle?: boolean;
};
```
| Property | Type | Default | Description |
| -------------- | --------- | ------- | --------------------------------------------- |
| `code` | `Keycode` | - | The primary key code (use `Keycode` enum). |
| `ctrl` | `boolean` | `false` | Require Control key. |
| `alt` | `boolean` | `false` | Require Alt/Option key. |
| `shift` | `boolean` | `false` | Require Shift key. |
| `meta` | `boolean` | `false` | Require Command (Mac) or Windows key. |
| `autoMacStyle` | `boolean` | `false` | On Mac, swap `ctrl` to `meta` and vice versa. |
### Returns [#returns]
Returns a `HotkeyManager` object:
```typescript
type HotkeyManager = {
id: HotkeyId;
config: HotkeyConfig;
bind(): CleanupFunction;
forceBind(): CleanupFunction;
forceUnbind(): void;
readonly isBound: boolean;
};
```
| Property | Description |
| --------------- | ------------------------------------------------------------------ |
| `id` | Unique identifier for the hotkey. |
| `config` | Full normalized configuration. |
| `bind()` | Activate with ref-counting. Returns cleanup function. |
| `forceBind()` | Force activation, ignoring ref-counting. Returns cleanup function. |
| `forceUnbind()` | Force deactivation, clearing all activations. |
| `isBound` | Whether the hotkey is currently active. |
### Examples [#examples-2]
#### Example: Basic keyboard shortcut [#example-basic-keyboard-shortcut]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
const escapeHotkey = registerHotkey({
key: { code: Keycode.Escape },
onKeyUp: () => closeModal(),
});
const unbind = escapeHotkey.bind();
```
#### Example: Modifier key combinations [#example-modifier-key-combinations]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
// Ctrl+Shift+P for command palette
const paletteHotkey = registerHotkey({
key: {
code: Keycode.KeyP,
ctrl: true,
shift: true,
},
onKeyUp: () => openCommandPalette(),
});
```
#### Example: Platform-aware shortcuts with autoMacStyle [#example-platform-aware-shortcuts-with-automacstyle]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
// Ctrl+S on Windows/Linux, Cmd+S on Mac
const saveHotkey = registerHotkey({
key: {
code: Keycode.KeyS,
ctrl: true,
autoMacStyle: true, // Swaps to Cmd on Mac
},
onKeyUp: () => save(),
});
```
#### Example: Chord sequences (multi-key combinations) [#example-chord-sequences-multi-key-combinations]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
// Vim-style "g d" (go to definition)
const gotoDefHotkey = registerHotkey({
key: [
{ code: Keycode.KeyG },
{ code: Keycode.KeyD },
],
onKeyUp: () => goToDefinition(),
});
```
#### Example: Ref-counted binding [#example-ref-counted-binding]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: { code: Keycode.KeyA },
onKeyUp: () => console.log('A pressed'),
});
// Multiple components can bind the same hotkey
const unbind1 = hotkey.bind();
const unbind2 = hotkey.bind();
// Hotkey remains active until all bindings are cleaned up
unbind1();
console.log(hotkey.isBound); // true
unbind2();
console.log(hotkey.isBound); // false
```
#### Example: Force binding and unbinding [#example-force-binding-and-unbinding]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: { code: Keycode.KeyB },
onKeyUp: () => console.log('B pressed'),
});
hotkey.bind();
hotkey.bind();
console.log(hotkey.isBound); // true
// Force unbind clears all activations immediately
hotkey.forceUnbind();
console.log(hotkey.isBound); // false
```
#### Example: Trigger on input fields [#example-trigger-on-input-fields]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
// Escape works even when focused on input
const cancelHotkey = registerHotkey({
key: { code: Keycode.Escape },
triggerOnInput: true,
onKeyUp: () => cancelForm(),
});
```
#### Example: Both keydown and keyup handlers [#example-both-keydown-and-keyup-handlers]
```typescript
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
const spaceHotkey = registerHotkey({
key: { code: Keycode.Space },
onKeyDown: () => console.log('Space pressed'),
onKeyUp: () => console.log('Space released'),
});
```
> **Good to know:** Hotkeys are registered globally but only active when bound. Use `bind()` for ref-counted activation suitable for component lifecycles, or `forceBind()` for imperative control.
## unregisterHotkey [#unregisterhotkey]
Unregisters a hotkey, removing it from the internal registry. Accepts a hotkey ID, config, or manager object.
### Usage [#usage-4]
```typescript
import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: { code: Keycode.KeyA },
onKeyUp: () => console.log('A pressed'),
});
// Unregister by manager
unregisterHotkey(hotkey);
```
### Reference [#reference-3]
```typescript
function unregisterHotkey(id: HotkeyId): void
function unregisterHotkey(config: HotkeyConfig): void
function unregisterHotkey(manager: HotkeyManager): void
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | --------------- | ------------------------------------------------ |
| `id` | `HotkeyId` | The hotkey identifier string. |
| `config` | `HotkeyConfig` | The full hotkey configuration object. |
| `manager` | `HotkeyManager` | The hotkey manager returned by `registerHotkey`. |
### Examples [#examples-3]
#### Example: Unregister by manager [#example-unregister-by-manager]
```typescript
import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: { code: Keycode.KeyA },
onKeyUp: () => console.log('A pressed'),
});
unregisterHotkey(hotkey);
```
#### Example: Unregister by ID [#example-unregister-by-id]
```typescript
import { registerHotkey, unregisterHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
id: 'my-custom-hotkey',
key: { code: Keycode.KeyA },
onKeyUp: () => console.log('A pressed'),
});
unregisterHotkey('my-custom-hotkey');
```
> **Good to know:** Unregistering a hotkey automatically unbinds it. Any existing cleanup functions will still work but will be no-ops.
## Keycode [#keycode]
Frozen object containing standard KeyboardEvent code values. Provides type-safe access to all keyboard codes.
### Usage [#usage-5]
```typescript
import { Keycode, registerHotkey } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: {
code: Keycode.KeyS,
meta: true,
},
onKeyUp: () => console.log('Save triggered'),
});
```
### Reference [#reference-4]
```typescript
const Keycode: {
readonly KeyA: 'KeyA';
readonly KeyB: 'KeyB';
// ... all keys
}
type Keycode = SafeEnum;
```
### Available Values [#available-values]
**Letter keys:** `KeyA` through `KeyZ`
**Number keys:** `Digit0` through `Digit9`
**Function keys:** `F1` through `F12`
**Arrow keys:** `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown`
**Modifier keys:**
* `ShiftLeft`, `ShiftRight`
* `ControlLeft`, `ControlRight`
* `AltLeft`, `AltRight`
* `MetaLeft`, `MetaRight`
**Navigation keys:** `Home`, `End`, `PageUp`, `PageDown`, `Insert`, `Delete`
**Special keys:** `Enter`, `Escape`, `Tab`, `Space`, `Backspace`, `Backquote`, `CapsLock`
**Punctuation:** `Slash`, `IntlBackslash`, `Equal`, `Minus`, `BracketLeft`, `BracketRight`, `Quote`, `Semicolon`
**Numpad keys:**
* `Numpad0` through `Numpad9`
* `NumpadAdd`, `NumpadSubtract`, `NumpadMultiply`, `NumpadDivide`
* `NumpadDecimal`, `NumpadEnter`, `NumpadEqual`, `NumpadComma`
* `NumLock`
**Media keys:** `AudioVolumeUp`, `AudioVolumeDown`, `AudioVolumeMute`
**International:** `IntlYen`, `IntlRo`
### Examples [#examples-4]
#### Example: Arrow key navigation [#example-arrow-key-navigation]
```typescript
import { Keycode, registerHotkey } from '@accelint/hotkey-manager';
const navigation = [
registerHotkey({
key: { code: Keycode.ArrowUp },
onKeyUp: () => moveFocusUp(),
}),
registerHotkey({
key: { code: Keycode.ArrowDown },
onKeyUp: () => moveFocusDown(),
}),
];
```
#### Example: Function keys [#example-function-keys]
```typescript
import { Keycode, registerHotkey } from '@accelint/hotkey-manager';
const f2Hotkey = registerHotkey({
key: { code: Keycode.F2 },
onKeyUp: () => startRename(),
});
```
> **Good to know:** These values match the standard [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values) specification. Use `code` rather than `key` for reliable cross-keyboard layout detection.
## isMac [#ismac]
Boolean constant that is `true` when running client-side on macOS, `false` otherwise.
### Usage [#usage-6]
```typescript
import { isMac } from '@accelint/hotkey-manager';
if (isMac) {
console.log('Show ⌘ symbol');
} else {
console.log('Show Ctrl key');
}
```
### Reference [#reference-5]
```typescript
const isMac: boolean
```
Detects macOS by checking `window.navigator.userAgent` for "Mac". Returns `false` on the server.
### Examples [#examples-5]
#### Example: Platform-specific UI hints [#example-platform-specific-ui-hints]
```typescript
import { isMac } from '@accelint/hotkey-manager';
function ShortcutHint() {
return (
{isMac ? '⌘' : 'Ctrl'} + S to save
);
}
```
#### Example: Conditional hotkey registration [#example-conditional-hotkey-registration]
```typescript
import { isMac, registerHotkey, Keycode } from '@accelint/hotkey-manager';
const hotkey = registerHotkey({
key: {
code: Keycode.KeyS,
...(isMac ? { meta: true } : { ctrl: true }),
},
onKeyUp: () => save(),
});
```
> **Good to know:** Use `autoMacStyle: true` in your hotkey configuration for automatic platform handling instead of manually checking `isMac`.
## keyToId [#keytoid]
Converts a key combination to a standard internal identifier string. Handles `autoMacStyle` transformation on macOS.
### Usage [#usage-7]
```typescript
import { keyToId } from '@accelint/hotkey-manager';
const id = keyToId({
code: 'KeyS',
ctrl: true,
alt: false,
shift: false,
meta: false,
autoMacStyle: false,
id: '',
});
// "KeyS|no-alt|ctrl|no-meta|no-shift"
```
### Reference [#reference-6]
```typescript
function keyToId(key: KeyCombination): KeyCombinationId
```
#### Parameters [#parameters-2]
| Parameter | Type | Description |
| --------- | ---------------- | ---------------------------------------------------- |
| `key` | `KeyCombination` | Key combination object with code and modifier flags. |
### Returns [#returns-1]
Returns a pipe-delimited string identifier in the format:
`{code}|{alt}|{ctrl}|{meta}|{shift}`
### Examples [#examples-6]
#### Example: Generate key identifier [#example-generate-key-identifier]
```typescript
import { keyToId, Keycode } from '@accelint/hotkey-manager';
const id = keyToId({
code: Keycode.KeyA,
ctrl: true,
alt: false,
shift: true,
meta: false,
autoMacStyle: false,
id: '',
});
console.log(id); // "KeyA|no-alt|ctrl|no-meta|shift"
```
#### Example: Platform-aware ID with autoMacStyle [#example-platform-aware-id-with-automacstyle]
```typescript
import { keyToId, Keycode, isMac } from '@accelint/hotkey-manager';
const id = keyToId({
code: Keycode.KeyS,
ctrl: true,
alt: false,
shift: false,
meta: false,
autoMacStyle: true,
id: '',
});
// On Mac: "KeyS|no-alt|no-ctrl|meta|no-shift"
// On Windows/Linux: "KeyS|no-alt|ctrl|no-meta|no-shift"
```
> **Good to know:** This function is used internally by `registerHotkey`. You typically don't need to call it directly unless building custom hotkey abstractions.
## keyToString [#keytostring]
Converts a key combination to a human-readable string representation with modifier symbols.
### Usage [#usage-8]
```typescript
import { keyToString } from '@accelint/hotkey-manager';
const str = keyToString({
code: 'KeyS',
ctrl: false,
alt: false,
shift: false,
meta: true,
autoMacStyle: false,
id: '',
});
// "[Win/⌘] + [KeyS]"
```
### Reference [#reference-7]
```typescript
function keyToString(key: KeyCombination): string
```
#### Parameters [#parameters-3]
| Parameter | Type | Description |
| --------- | ---------------- | ---------------------------------------------------- |
| `key` | `KeyCombination` | Key combination object with code and modifier flags. |
### Returns [#returns-2]
Returns a human-readable string like `"[CTRL] + [SHIFT] + [KeyA]"`.
### Examples [#examples-7]
#### Example: Display hotkey hint [#example-display-hotkey-hint]
```typescript
import { keyToString, Keycode } from '@accelint/hotkey-manager';
const hotkey = {
code: Keycode.KeyS,
ctrl: true,
alt: false,
shift: true,
meta: false,
autoMacStyle: false,
id: '',
};
console.log(keyToString(hotkey));
// "[CTRL] + [SHIFT] + [KeyS]"
```
#### Example: Generate tooltip text [#example-generate-tooltip-text]
```typescript
import { keyToString, registerHotkey, Keycode } from '@accelint/hotkey-manager';
const saveHotkey = registerHotkey({
key: {
code: Keycode.KeyS,
ctrl: true,
},
onKeyUp: () => save(),
});
const tooltipText = `Save file: ${keyToString(saveHotkey.config.key[0])}`;
```
> **Good to know:** This function is primarily for debugging and user-facing displays. The output format is not customizable—it always uses the pattern `[MODIFIER] + [KEY]`.
## Related [#related]
* [useHotkey](./react/use-hotkey.md) - React hook for binding hotkeys in components
* [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) - MDN documentation for keyboard codes
# React Hooks (/docs/packages/hotkey-manager/react)
## Usage [#usage]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { useHotkey } from '@accelint/hotkey-manager/react';
const saveHotkey = registerHotkey({
key: { code: Keycode.KeyS, meta: true },
onKeyUp: () => console.log('Save triggered'),
});
function Editor() {
useHotkey(saveHotkey);
return ;
}
```
## useHotkey [#usehotkey]
Binds a hotkey manager when the component mounts and automatically unbinds when it unmounts.
### Reference [#reference]
```typescript
function useHotkey(manager: HotkeyManager): void
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | --------------- | ------------------------------------------------ |
| `manager` | `HotkeyManager` | The hotkey manager returned by `registerHotkey`. |
### Examples [#examples]
#### Example: Component-scoped hotkey [#example-component-scoped-hotkey]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { useHotkey } from '@accelint/hotkey-manager/react';
const escapeHotkey = registerHotkey({
key: { code: Keycode.Escape },
onKeyUp: () => console.log('Escape pressed'),
});
function Modal() {
useHotkey(escapeHotkey);
return
Press Escape to close
;
}
```
#### Example: Multiple hotkeys in one component [#example-multiple-hotkeys-in-one-component]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { useHotkey } from '@accelint/hotkey-manager/react';
const saveHotkey = registerHotkey({
key: { code: Keycode.KeyS, ctrl: true, autoMacStyle: true },
onKeyUp: () => save(),
});
const undoHotkey = registerHotkey({
key: { code: Keycode.KeyZ, ctrl: true, autoMacStyle: true },
onKeyUp: () => undo(),
});
function Editor() {
useHotkey(saveHotkey);
useHotkey(undoHotkey);
return ;
}
```
#### Example: Conditional hotkey binding [#example-conditional-hotkey-binding]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { useHotkey } from '@accelint/hotkey-manager/react';
import { useState } from 'react';
const submitHotkey = registerHotkey({
key: { code: Keycode.Enter, ctrl: true },
onKeyUp: () => submit(),
});
function Form() {
const [isEnabled, setIsEnabled] = useState(true);
// Only bind when enabled
if (isEnabled) {
useHotkey(submitHotkey);
}
return ;
}
```
> **Good to know:** The hotkey manager is automatically bound on mount and unbound on unmount. If the same manager is used in multiple components, ref-counting ensures it remains active until all components unmount.
#### Example: Dynamic hotkey creation [#example-dynamic-hotkey-creation]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { useHotkey } from '@accelint/hotkey-manager/react';
import { useMemo } from 'react';
function SearchModal({ onClose }: { onClose: () => void }) {
const closeHotkey = useMemo(
() =>
registerHotkey({
key: { code: Keycode.Escape },
onKeyUp: onClose,
}),
[onClose],
);
useHotkey(closeHotkey);
return
Search...
;
}
```
## createUseHotkey [#createusehotkey]
Creates a custom React hook from a HotkeyManager for module-level usage. Useful when you want to define the hotkey at module scope and use it as a reusable hook.
### Reference [#reference-1]
```typescript
function createUseHotkey(manager: HotkeyManager): () => void
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | --------------- | --------------------------- |
| `manager` | `HotkeyManager` | The hotkey manager to wrap. |
### Returns [#returns]
Returns a React hook function that binds the hotkey on mount.
### Examples [#examples-1]
#### Example: Module-level hotkey definition [#example-module-level-hotkey-definition]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { createUseHotkey } from '@accelint/hotkey-manager/react';
// Define at module scope
const helpHotkey = registerHotkey({
key: { code: Keycode.Slash, shift: true },
onKeyUp: () => openHelp(),
});
// Create a custom hook
const useHelpHotkey = createUseHotkey(helpHotkey);
// Use in any component
function App() {
useHelpHotkey();
return
Press ? for help
;
}
```
#### Example: Shared hotkey across components [#example-shared-hotkey-across-components]
```tsx
import { registerHotkey, Keycode } from '@accelint/hotkey-manager';
import { createUseHotkey } from '@accelint/hotkey-manager/react';
const paletteHotkey = registerHotkey({
key: { code: Keycode.KeyP, ctrl: true, shift: true },
onKeyUp: () => openCommandPalette(),
});
export const useCommandPalette = createUseHotkey(paletteHotkey);
// Use in multiple components
function Layout() {
useCommandPalette();
return
Layout
;
}
function Editor() {
useCommandPalette();
return ;
}
```
> **Good to know:** `createUseHotkey` is ideal for library authors distributing hotkey hooks. The manager is captured in a stable closure, eliminating the need to pass it through dependencies.
## Related [#related]
* [registerHotkey](../index.md#registerhotkey) - Register a keyboard shortcut
* [globalBind](../index.md#globalbind) - Bind global event listeners
* [HotkeyManager](../index.md#registerhotkey) - Type definition and API reference
# Overview (/docs/packages/icons)
## Usage [#usage]
```tsx
import { Add, Bell, Check } from '@accelint/icons';
export function MyComponent() {
return (
);
}
```
## Reference [#reference]
All icon components share the same API:
```typescript
interface SVGRProps {
title?: string;
titleId?: string;
}
type IconComponent = (
props: SVGProps & SVGRProps
) => JSX.Element;
```
### Props [#props]
| Prop | Type | Default | Required |
| ----------- | ------------------ | ------- | -------- |
| `width` | `number \| string` | - | No |
| `height` | `number \| string` | - | No |
| `className` | `string` | - | No |
| `style` | `CSSProperties` | - | No |
| `fill` | `string` | - | No |
| `title` | `string` | - | No |
| `titleId` | `string` | - | No |
All standard SVG element props are supported via `SVGProps`.
#### `title` [#title]
Provides accessible text description for screen readers. When provided, renders a `` element inside the SVG.
#### `titleId` [#titleid]
Custom ID for the title element. Used with `aria-labelledby` for accessibility. Auto-generated if `title` is provided without `titleId`.
### Icon Sizing [#icon-sizing]
Icons use `currentColor` for fill, inheriting text color from parent elements. Size should be controlled via `width` and `height` props or CSS.
## Available Icons [#available-icons]
### Navigation & Actions [#navigation--actions]
`Add`, `AddFill`, `AddToList`, `Remove`, `RemoveFill`, `RemoveFromList`, `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Arrow`, `ArrowNortheast`, `ArrowNorthwest`, `ArrowSoutheast`, `ArrowSouthwest`, `ChevronUp`, `ChevronDown`, `ChevronLeft`, `ChevronRight`, `ChevronFillUp`, `ChevronFillDown`, `ChevronFillLeft`, `ChevronFillRight`, `Cancel`, `CancelFill`, `Check`, `Edit`, `Delete`, `Duplicate`, `Undo`, `Move`, `Expand`, `ExpandAll`, `ExpandLeft`, `ExpandRight`, `ExpandWindow`, `ExpandLeftPanel`, `Collapse`, `CollapseAll`, `CollapseWindow`, `Condense`
### Media Controls [#media-controls]
`Play`, `Pause`, `Stop`, `Record`, `FastForward`, `FastRewind`, `JumpTo`, `JumpToStart`, `JumpToEnd`, `Loop`, `Reverse`, `Playback`
### UI Elements [#ui-elements]
`Bell`, `Dashboard`, `Filter`, `Search`, `Settings`, `Help`, `Information`, `Hamburger`, `Kebab`, `Waffle`, `Grid`, `Calendar`, `Time`, `Timer`, `Plus`, `Minus`, `CopyToClipboard`, `PasteFromClipboard`, `Screenshot`, `AttachFile`, `Link`, `Share`, `GlobalShare`, `Flag`, `Pin`, `Note`, `Message`, `Phone`, `Label`, `Tag`
### Tools & Drawing [#tools--drawing]
`CircleTool`, `EllipseTool`, `LineTool`, `PolygonTool`, `RectangleTool`, `RectangleSelect`, `Lasso`, `PolygonLasso`, `ColorPicker`, `CursorDefault`, `CursorCrosshair`, `CursorDrag`, `DragHorz`, `DragVert`
### Status & Indicators [#status--indicators]
`Alert`, `AlertBase`, `Warning`, `Success`, `Problem`, `Hide`, `Show`, `Lock`, `LockFill`, `LockOutline`, `Live`, `Connection`, `ConnectionLost`, `BadData`, `NonEditable`, `None`, `Placeholder`
### Data & Visualization [#data--visualization]
`Data`, `ExploreData`, `InsertChart`, `ChartStackedBar`, `Gantt`, `Plots`, `Feed`, `Layers`, `LayerGroup`, `Overlays`, `Attributes`, `ArrangeTable`, `SelectionList`, `StackCards`, `Stack`, `Newspaper`, `TrackChanges`
### Geography & Mapping [#geography--mapping]
`Airport`, `MilitaryAirfield`, `Airspaces`, `Terrain`, `Weather`, `Wind`, `Radar`, `RadarSite`, `RadarAnomaly`, `Region`, `Sector`, `MapPin`, `Marker`, `LocationFill`, `LocationStroke`, `MercatorProjection`, `Coverage`, `CenterOn`, `ZoomIn`, `ZoomOut`, `ZoomTo`, `Focus`, `Spotlight`
### Military & Defense [#military--defense]
`Target`, `PresumedTarget`, `DefendedPointCaalDal`, `Missile`, `Missiles`, `ThreatAssess`, `BattleStation`, `WatchTower`, `CommonTrack`, `CommonAirTrack`, `CommonOperatingPic`, `AirTrackHistory`, `TrackHistory`, `TrackEta`, `TrackEtaEndpoint`, `FlightPlan`, `Mission`, `Assignment`, `Handover`, `Handshake`, `CreateCoa`, `CopSettings`, `InterceptPoint`, `Hold`, `Tether`
### Vehicles & Assets [#vehicles--assets]
`Car`, `Helicopter`, `Uas`, `UasHomePoint`, `LargeJet`, `SmallJet`, `Prop`, `CommercialShip`, `MilitaryVessel`, `PersonalBoat`, `Birds`, `Landmine`, `Skull`
### Infrastructure [#infrastructure]
`Building`, `Stadium`, `Artcc`, `ConnectingAirports`, `SensorHealth`, `UnscheduledMaintenance`, `ElectromagneticSpectrum`, `Radio`, `Remote`
### Interface Elements [#interface-elements]
`PanelOpen`, `PanelClosed`, `OpenContainer`, `FullScreen`, `PopOut`, `PopBackIn`, `Appearance`, `Brightness`, `Contrast`, `Mode`, `Keyboard`, `ArrowKeys`, `MouseLeftClick`, `MouseMiddleClick`, `MouseRightClick`, `MouseScroll`, `ScrollClick`
### User & Access [#user--access]
`Person`, `Login`, `Logout`, `Ticket`, `UserManual`, `WhatsNew`, `BugReport`
### Shapes & Geometry [#shapes--geometry]
`PointFill`, `PointOutline`, `SquareFill`, `SquareOutline`, `TriangleFill`, `TriangleOutline`, `CircleTool`, `Slash`, `Star`, `Wheel`
### Media & Audio [#media--audio]
`SoundAudible`, `SoundMute`, `Mute`, `Bolt`, `BoltNotVisible`
### Organization [#organization]
`Group`, `Land`, `Ascending`, `Descending`
### Other [#other]
`Import`, `Duplicate`, `Slash`, `None`
## Examples [#examples]
### Example: Basic icon usage [#example-basic-icon-usage]
```tsx
import { Add } from '@accelint/icons';
```
### Example: Icon with color [#example-icon-with-color]
Icons inherit `currentColor` by default:
```tsx
import { Bell } from '@accelint/icons';
```
Or override with className:
```tsx
import { Check } from '@accelint/icons';
```
### Example: Icon with accessibility [#example-icon-with-accessibility]
```tsx
import { Settings } from '@accelint/icons';
```
### Example: Icon in button [#example-icon-in-button]
```tsx
import { Add } from '@accelint/icons';
import { Button } from '@accelint/design-toolkit';
```
### Example: Icon with custom styling [#example-icon-with-custom-styling]
```tsx
import { Warning } from '@accelint/icons';
```
### Example: Dynamic icon selection [#example-dynamic-icon-selection]
```tsx
import * as Icons from '@accelint/icons';
interface IconDisplayProps {
name: keyof typeof Icons;
size?: number;
}
export function IconDisplay({ name, size = 24 }: IconDisplayProps) {
const IconComponent = Icons[name];
return ;
}
// Usage
```
### Example: Icon with responsive sizing [#example-icon-with-responsive-sizing]
```tsx
import { Dashboard } from '@accelint/icons';
```
### Example: Icon in design system [#example-icon-in-design-system]
```tsx
import { Icon } from '@accelint/design-toolkit';
import { Settings } from '@accelint/icons';
// Icon component automatically sizes based on context
```
> **Good to know:** All icons are 24×24 viewBox by default. When using with the design system's `Icon` component, size is controlled by context and you don't need to specify width/height.
## Related [#related]
* [Icon](../../toolkits/design-toolkit/components/icon/index.mdx) - Design system Icon component with automatic sizing
* [Button](../../toolkits/design-toolkit/components/button/index.mdx) - Button component that works with icons
# Log Level Constants (/docs/packages/logger/definitions)
## Usage [#usage]
```typescript
import { getLogger, DEBUG, INFO, WARN, ERROR } from '@accelint/logger';
// Use constants for type safety
const devLogger = getLogger({ enabled: true, level: DEBUG });
const prodLogger = getLogger({ enabled: true, level: WARN });
```
## Reference [#reference]
```typescript
const TRACE: LogLevelType = 'trace';
const DEBUG: LogLevelType = 'debug';
const INFO: LogLevelType = 'info';
const WARN: LogLevelType = 'warn';
const ERROR: LogLevelType = 'error';
const FATAL: LogLevelType = 'fatal';
type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
```
### Constants [#constants]
## TRACE [#trace]
Use for fine-grained debugging information, more verbose than debug.
```typescript
import { getLogger, TRACE } from '@accelint/logger';
const logger = getLogger({ level: TRACE, enabled: true });
logger.trace('Entering function', { args: [1, 2, 3] });
```
## DEBUG [#debug]
Use for detailed diagnostic information useful during development.
```typescript
import { getLogger, DEBUG } from '@accelint/logger';
const logger = getLogger({ level: DEBUG, enabled: true });
logger.debug('Cache miss', { key: 'user:123' });
```
## INFO [#info]
Use for general informational messages about application progress.
```typescript
import { getLogger, INFO } from '@accelint/logger';
const logger = getLogger({ level: INFO, enabled: true });
logger.info('Server started', { port: 3000 });
```
## WARN [#warn]
Use for potentially harmful situations that don't prevent execution.
```typescript
import { getLogger, WARN } from '@accelint/logger';
const logger = getLogger({ level: WARN, enabled: true });
logger.warn('API rate limit approaching', { remaining: 10 });
```
## ERROR [#error]
Use for critical errors that require immediate attention.
```typescript
import { getLogger, ERROR } from '@accelint/logger';
const logger = getLogger({ level: ERROR, enabled: true });
logger.error('Payment processing failed', { orderId: 'ord_123' });
```
## FATAL [#fatal]
Use for severe errors that may cause the application to terminate.
```typescript
import { getLogger, FATAL } from '@accelint/logger';
const logger = getLogger({ level: FATAL, enabled: true });
logger.fatal('Database connection lost', { error });
```
## LogLevel Type [#loglevel-type]
The `LogLevel` type represents all valid log level strings. Use this for type annotations when accepting log levels as parameters.
```typescript
import type { LogLevel } from '@accelint/logger';
function createLogger(level: LogLevel) {
return getLogger({ enabled: true, level });
}
createLogger('info'); // ✓ Valid
createLogger('verbose'); // ✗ Type error
```
## LoggerOptions Type [#loggeroptions-type]
Configuration options for the logger.
```typescript
type LoggerOptions = {
enabled: boolean;
level?: LogLevel;
env?: 'production' | 'development';
pretty?: boolean;
prefix?: string;
plugins?: LogLayerPlugin[];
transports?: LogLayerTransport[];
groups?: LogGroupsConfig;
}
```
### Properties [#properties]
| Property | Type | Default | Description |
| ------------ | ------------------------------- | --------------- | ------------------------------------------------------------ |
| `enabled` | `boolean` | Required | Whether logging is enabled; `false` makes all calls no-ops |
| `level` | `LogLevel` | `'debug'` | Minimum log level to output |
| `env` | `'production' \| 'development'` | `'development'` | Runtime environment; controls server detection |
| `pretty` | `boolean` | `true` | Use pretty console output; `false` emits structured JSON |
| `prefix` | `string` | `''` | String prepended to all log messages |
| `plugins` | `LogLayerPlugin[]` | `[]` | Additional plugins applied after the built-in ones |
| `transports` | `LogLayerTransport[]` | `[]` | Custom transports that replace the default console transport |
| `groups` | `LogGroupsConfig` | `{}` | Named group configuration for conditional group logging |
## Examples [#examples]
### Example: Log level hierarchy [#example-log-level-hierarchy]
```typescript
import { getLogger, WARN } from '@accelint/logger';
// Logger set to WARN level
const logger = getLogger({ enabled: true, level: WARN });
logger.trace('Not logged'); // Below threshold
logger.debug('Not logged'); // Below threshold
logger.info('Not logged'); // Below threshold
logger.warn('Logged'); // At threshold
logger.error('Logged'); // Above threshold
logger.fatal('Logged'); // Above threshold
```
### Example: Dynamic log level [#example-dynamic-log-level]
```typescript
import { getLogger, DEBUG, INFO, WARN, type LogLevel } from '@accelint/logger';
function getLogLevel(): LogLevel {
if (process.env.DEBUG === 'true') return DEBUG;
if (process.env.NODE_ENV === 'production') return WARN;
return INFO;
}
const logger = getLogger({
enabled: true,
level: getLogLevel(),
});
```
### Example: Type-safe logger configuration [#example-type-safe-logger-configuration]
```typescript
import type { LoggerOptions } from '@accelint/logger';
import { getLogger, WARN } from '@accelint/logger';
const config: LoggerOptions = {
enabled: process.env.LOGGING_ENABLED === 'true',
level: WARN,
env: process.env.NODE_ENV as 'production' | 'development',
prefix: '[API]',
};
const logger = getLogger(config);
```
> **Good to know:** Log levels are hierarchical. Setting the logger to a specific level enables that level and all higher (more severe) levels. For example, setting `level: WARN` will output warn, error, and fatal logs, but suppress trace, debug, and info logs.
## Related [#related]
* [getLogger](./default/index.md) - Singleton logger factory
* [bootstrap](./default/bootstrap.md) - Direct logger initialization
* [LogLayer Log Levels](https://loglayer.dev/logging-api/adjusting-log-levels.html) - Upstream documentation
# Overview (/docs/packages/logger)
## Usage [#usage]
```typescript
import { getLogger } from '@accelint/logger';
// Create a singleton logger instance
const logger = getLogger({
enabled: true,
env: process.env.NODE_ENV as 'production' | 'development',
level: 'info',
prefix: '[MyApp]',
});
logger.info('Application started');
logger.withMetadata({ userId: 123 }).warn('Token expiring soon');
```
## Exports [#exports]
### Functions [#functions]
* [getLogger](./default/index.md) - Returns a singleton LogLayer logger instance
* [bootstrap](./default/bootstrap.md) - Initializes a new LogLayer instance with default plugins
### Constants [#constants]
* [DEBUG](./definitions.md#debug) - Log level constant for debug messages
* [ERROR](./definitions.md#error) - Log level constant for error messages
* [FATAL](./definitions.md#fatal) - Log level constant for fatal error messages
* [INFO](./definitions.md#info) - Log level constant for informational messages
* [TRACE](./definitions.md#trace) - Log level constant for trace messages
* [WARN](./definitions.md#warn) - Log level constant for warning messages
### Plugins [#plugins]
* [callsitePlugin](./plugins/callsite.md) - Tracks and injects source code location into log data
* [environmentPlugin](./plugins/environment.md) - Injects environment context (server/browser) into log data
### Transports [#transports]
* [prettyTransport](./transports/pretty.md) - Pretty-printed console transport
* [structuredTransport](./transports/structured.md) - Structured JSON log transport
### Types [#types]
* `LogLevel` - The log level type: `'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'`
* `LoggerOptions` - Configuration options for the logger
* `CallsitePluginOptions` - Options for callsite tracking plugin
* `EnvironmentPluginOptions` - Options for environment detection plugin
## Examples [#examples]
### Example: Basic logger setup [#example-basic-logger-setup]
```typescript
import { getLogger, INFO } from '@accelint/logger';
const logger = getLogger({
enabled: true,
level: INFO,
env: 'production',
});
logger.info('Server started', { port: 3000 });
```
### Example: Development logger with pretty output [#example-development-logger-with-pretty-output]
```typescript
import { getLogger, DEBUG } from '@accelint/logger';
const logger = getLogger({
enabled: true,
level: DEBUG,
env: 'development',
pretty: true,
prefix: '[Dev]',
});
logger.debug('Cache miss', { key: 'user:123' });
```
### Example: Production logger with structured JSON [#example-production-logger-with-structured-json]
```typescript
import { getLogger, WARN } from '@accelint/logger';
const logger = getLogger({
enabled: true,
level: WARN,
env: 'production',
pretty: false, // Outputs JSON
});
logger.error('Payment failed', { orderId: 'ord_123', reason: 'insufficient_funds' });
```
### Example: Custom plugins [#example-custom-plugins]
```typescript
import { getLogger } from '@accelint/logger';
import type { LogLayerPlugin } from '@loglayer/shared';
const timingPlugin: LogLayerPlugin = {
id: 'timing',
onBeforeDataOut({ data }) {
return {
...data,
timestamp: Date.now(),
};
},
};
const logger = getLogger({
enabled: true,
plugins: [timingPlugin],
});
```
### Example: Custom transports [#example-custom-transports]
```typescript
import { getLogger, prettyTransport, structuredTransport } from '@accelint/logger';
// Multiple transports: pretty console + structured JSON
const logger = getLogger({
enabled: true,
transports: [
prettyTransport({ level: 'debug' }),
structuredTransport({ level: 'info' }),
],
});
logger.info('Logged to both transports');
```
### Example: Log groups [#example-log-groups]
```typescript
import { getLogger } from '@accelint/logger';
const logger = getLogger({
enabled: true,
groups: {
database: { enabled: true },
network: { enabled: false }, // Network logs disabled
},
});
logger.withGroup('database').info('Query executed');
logger.withGroup('network').info('This will not log');
```
> **Good to know:** `getLogger` returns a singleton. The first call initializes the logger with the provided options; subsequent calls return the same instance and ignore new options. Use `bootstrap` directly if you need multiple independent logger instances.
## Related [#related]
* [LogLayer Documentation](https://loglayer.dev/) - Upstream library documentation
* [getLogger](./default/index.md) - Singleton logger factory
* [bootstrap](./default/bootstrap.md) - Direct logger initialization
# clamp (/docs/packages/math/clamp)
## Usage [#usage]
```typescript
import { clamp } from '@accelint/math';
const result = clamp(5, 15, 10);
// 10
const belowMin = clamp(5, 15, 2);
// 5
const aboveMax = clamp(5, 15, 20);
// 15
```
## Reference [#reference]
```typescript
function clamp(min: number, max: number, value: number): number
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | --------------------------------------------- |
| `min` | `number` | The lower bound to clamp to. |
| `max` | `number` | The upper bound to clamp to. |
| `value` | `number` | The number value to clamp to the given range. |
### Returns [#returns]
Returns the clamped value. If `value` is below `min`, returns `min`. If `value` is above `max`, returns `max`. Otherwise returns `value`.
### Throws [#throws]
* `RangeError` - Throws if `min > max`.
## Examples [#examples]
### Example: Clamping below minimum [#example-clamping-below-minimum]
```typescript
import { clamp } from '@accelint/math';
const result = clamp(1, 2, 0);
// 1
```
### Example: Clamping above maximum [#example-clamping-above-maximum]
```typescript
import { clamp } from '@accelint/math';
const result = clamp(1, 2, 3);
// 2
```
### Example: Value within range [#example-value-within-range]
```typescript
import { clamp } from '@accelint/math';
const result = clamp(1, 3, 2);
// 2
```
> **Good to know:** The function validates that `min` does not exceed `max` and throws a `RangeError` if this constraint is violated.
## Related [#related]
* [round](../round) - Rounds a number to specified precision
# Overview (/docs/packages/math)
## Installation [#installation]
```bash
pnpm add @accelint/math
```
## Usage [#usage]
```typescript
// Import from root (all exports)
import { clamp, random, randomInt, round } from '@accelint/math';
// Import from sub-modules
import { clamp } from '@accelint/math/clamp';
import { random, randomInt } from '@accelint/math/random';
import { round } from '@accelint/math/round';
```
## Available Exports [#available-exports]
| Export Path | Description |
| ------------------ | ------------------------------------------------------------ |
| [clamp](./clamp) | Clamps a number within the specified bounds |
| [random](./random) | Generate random numbers and integers within specified bounds |
| [round](./round) | Rounds a number to a specified precision |
## Related [#related]
* [@accelint/core](../core) - Core functional utilities
* [GitHub Repository](https://github.com/gohypergiant/standard-toolkit) - Source code
# random (/docs/packages/math/random)
## random [#random]
Generate a random number within the given bounds.
### Usage [#usage]
```typescript
import { random } from '@accelint/math';
const value = random(0, 10);
// value >= 0 && value <= 10
```
### Reference [#reference]
```typescript
function random(min: number, max: number): number
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------- |
| `min` | `number` | The minimum value in the range (inclusive). |
| `max` | `number` | The maximum value in the range (inclusive). |
#### Returns [#returns]
Returns a random number between `min` and `max` (inclusive).
#### Throws [#throws]
* `RangeError` - Throws if `min > max`.
### Examples [#examples]
#### Example: Generating random decimal [#example-generating-random-decimal]
```typescript
import { random } from '@accelint/math';
const value = random(0, 1);
// A decimal between 0 and 1 (inclusive)
```
#### Example: Negative range [#example-negative-range]
```typescript
import { random } from '@accelint/math';
const value = random(-10, -5);
// A decimal between -10 and -5 (inclusive)
```
#### Example: Same min and max [#example-same-min-and-max]
```typescript
import { random } from '@accelint/math';
const value = random(5, 5);
// Approximately 5
```
> **Good to know:** Both bounds are inclusive. The function adds `Number.EPSILON` to ensure the maximum value can be generated.
***
## randomInt [#randomint]
Generate a random integer within the given bounds.
### Usage [#usage-1]
```typescript
import { randomInt } from '@accelint/math';
const value = randomInt(0, 10);
// An integer: 0, 1, 2, ..., 9, or 10
```
### Reference [#reference-1]
```typescript
function randomInt(min: number, max: number): number
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | -------- | ------------------------------------------- |
| `min` | `number` | The minimum value in the range (inclusive). |
| `max` | `number` | The maximum value in the range (inclusive). |
#### Returns [#returns-1]
Returns a random integer between `min` and `max` (inclusive).
#### Throws [#throws-1]
* `RangeError` - Throws if `min > max`.
### Examples [#examples-1]
#### Example: Generating dice roll [#example-generating-dice-roll]
```typescript
import { randomInt } from '@accelint/math';
const roll = randomInt(1, 6);
// A whole number: 1, 2, 3, 4, 5, or 6
```
#### Example: Floating point boundaries [#example-floating-point-boundaries]
```typescript
import { randomInt } from '@accelint/math';
const value = randomInt(0.5, 10.5);
// Integer between Math.ceil(0.5) and Math.floor(10.5)
// Result: 1, 2, 3, ..., 9, or 10
```
#### Example: Same min and max [#example-same-min-and-max-1]
```typescript
import { randomInt } from '@accelint/math';
const value = randomInt(5, 5);
// Always returns 5
```
> **Good to know:** When given floating point boundaries, the function uses `Math.ceil` on `min` and `Math.floor` on `max` to ensure integer results.
## Related [#related]
* [clamp](../clamp) - Clamps a number within specified bounds
# round (/docs/packages/math/round)
## Usage [#usage]
```typescript
import { round } from '@accelint/math';
const result = round(1, 1.2345);
// 1.2
const result = round(2, 1.2345);
// 1.23
const result = round(3, 1.2345);
// 1.235
```
## Reference [#reference]
```typescript
function round(precision: number, value: number): number
```
### Parameters [#parameters]
| Parameter | Type | Description |
| ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `precision` | `number` | The precision of the rounded output. Must be an integer. Positive values round to decimal places; negative values round to positions left of the decimal point. |
| `value` | `number` | The value to round. |
### Returns [#returns]
Returns the rounded value.
### Throws [#throws]
* `Error` - Throws if `precision` is not an integer.
## Examples [#examples]
### Example: Positive precision (decimal places) [#example-positive-precision-decimal-places]
```typescript
import { round } from '@accelint/math';
const value = 1.2345;
round(1, value); // 1.2
round(2, value); // 1.23
round(3, value); // 1.235
round(4, value); // 1.2345
```
### Example: Zero precision (round to integer) [#example-zero-precision-round-to-integer]
```typescript
import { round } from '@accelint/math';
const result = round(0, 1.2345);
// 1
```
### Example: Negative precision (round left of decimal) [#example-negative-precision-round-left-of-decimal]
```typescript
import { round } from '@accelint/math';
const value = 12345.6789;
round(-1, value); // 12350
round(-2, value); // 12300
round(-3, value); // 12000
round(-4, value); // 10000
```
> **Good to know:** Precision must be an integer. Positive values round to decimal places (e.g., `1` = tenths, `2` = hundredths). Negative values round to positions left of the decimal point (e.g., `-1` = tens, `-2` = hundreds).
## Related [#related]
* [clamp](../clamp) - Clamps a number within specified bounds
# Color Constants (/docs/packages/ntds/constants)
Standard NTDS (Naval Tactical Data System) color values used for classifying and displaying entity affiliations in tactical displays.
## Usage [#usage]
```typescript
import {
NTDS_COLOR_FRIEND,
NTDS_COLOR_HOSTILE,
NTDS_COLOR_UNKNOWN,
} from '@accelint/ntds/constants';
function EntityIcon({ affiliation }) {
const color = affiliation === 'friend'
? NTDS_COLOR_FRIEND
: NTDS_COLOR_HOSTILE;
return ;
}
```
## Reference [#reference]
### NTDS\_COLOR\_ASSUMED\_FRIEND [#ntds_color_assumed_friend]
```typescript
const NTDS_COLOR_ASSUMED_FRIEND: '#0E8F37';
```
Green color indicating units that are likely friendly based on available intelligence but not definitively confirmed.
**Value:** `#0E8F37`
### NTDS\_COLOR\_FRIEND [#ntds_color_friend]
```typescript
const NTDS_COLOR_FRIEND: '#1484F4';
```
Blue color indicating units that are confirmed as friendly forces.
**Value:** `#1484F4`
### NTDS\_COLOR\_HOSTILE [#ntds_color_hostile]
```typescript
const NTDS_COLOR_HOSTILE: '#FF0033';
```
Red color indicating units that are confirmed as hostile or enemy forces.
**Value:** `#FF0033`
### NTDS\_COLOR\_NEUTRAL [#ntds_color_neutral]
```typescript
const NTDS_COLOR_NEUTRAL: '#5B137A';
```
Purple color indicating units that are confirmed as neutral, neither friendly nor hostile.
**Value:** `#5B137A`
### NTDS\_COLOR\_PENDING [#ntds_color_pending]
```typescript
const NTDS_COLOR_PENDING: '#404040';
```
Dark gray color indicating units whose status is pending classification or awaiting further information.
**Value:** `#404040`
### NTDS\_COLOR\_SUSPECT [#ntds_color_suspect]
```typescript
const NTDS_COLOR_SUSPECT: '#FFAD38';
```
Orange color indicating units that are suspected to be hostile but not yet confirmed.
**Value:** `#FFAD38`
### NTDS\_COLOR\_UNKNOWN [#ntds_color_unknown]
```typescript
const NTDS_COLOR_UNKNOWN: '#EDDA0A';
```
Yellow color indicating units whose affiliation or status is completely unknown.
**Value:** `#EDDA0A`
## Examples [#examples]
### Example: Dynamic color selection [#example-dynamic-color-selection]
```typescript
import {
NTDS_COLOR_FRIEND,
NTDS_COLOR_HOSTILE,
NTDS_COLOR_NEUTRAL,
NTDS_COLOR_UNKNOWN,
} from '@accelint/ntds/constants';
type Affiliation = 'friend' | 'hostile' | 'neutral' | 'unknown';
function getAffiliationColor(affiliation: Affiliation): string {
switch (affiliation) {
case 'friend':
return NTDS_COLOR_FRIEND;
case 'hostile':
return NTDS_COLOR_HOSTILE;
case 'neutral':
return NTDS_COLOR_NEUTRAL;
case 'unknown':
return NTDS_COLOR_UNKNOWN;
}
}
```
### Example: Color mapping for all classifications [#example-color-mapping-for-all-classifications]
```typescript
import {
NTDS_COLOR_ASSUMED_FRIEND,
NTDS_COLOR_FRIEND,
NTDS_COLOR_HOSTILE,
NTDS_COLOR_NEUTRAL,
NTDS_COLOR_PENDING,
NTDS_COLOR_SUSPECT,
NTDS_COLOR_UNKNOWN,
} from '@accelint/ntds/constants';
const AFFILIATION_COLORS = {
assumedFriend: NTDS_COLOR_ASSUMED_FRIEND,
friend: NTDS_COLOR_FRIEND,
hostile: NTDS_COLOR_HOSTILE,
neutral: NTDS_COLOR_NEUTRAL,
pending: NTDS_COLOR_PENDING,
suspect: NTDS_COLOR_SUSPECT,
unknown: NTDS_COLOR_UNKNOWN,
} as const;
```
### Example: Using with NTDS icons [#example-using-with-ntds-icons]
```typescript
import { NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
import { SvgAirHostile } from '@accelint/ntds/core/air-hostile';
function HostileAircraft() {
return (
);
}
```
### Example: CSS custom properties [#example-css-custom-properties]
```typescript
import { NTDS_COLOR_FRIEND, NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
const styles = {
'--color-friend': NTDS_COLOR_FRIEND,
'--color-hostile': NTDS_COLOR_HOSTILE,
} as React.CSSProperties;
function TacticalDisplay() {
return (
{/* Elements can reference CSS vars */}
);
}
```
## Related [#related]
* [SvgAirFriend](./core/air-friend.mdx) - Friendly air entity icon component
* [SvgAirHostile](./core/air-hostile.mdx) - Hostile air entity icon component
* [Spritesheet Constants](./spritesheets/masked.mdx) - Spritesheet ID constants for COP usage
# Icon Components (/docs/packages/ntds/core)
React SVG icon components representing NTDS (Naval Tactical Data System) tactical symbols for military entities. Each component renders a 24×24 SVG with customizable fill color and accessibility support.
## Usage [#usage]
```tsx
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function TacticalDisplay() {
return (
);
}
```
## Reference [#reference]
All NTDS SVG components share a common interface:
```typescript
interface Props {
title?: string;
titleId?: string;
}
type SvgComponent = (props: SVGProps & Props) => JSX.Element;
```
### Props [#props]
| Prop | Type | Default | Required |
| ----------- | ------------------ | --------- | -------- |
| `fill` | `string` | `#FF69B4` | No |
| `width` | `number \| string` | `24` | No |
| `height` | `number \| string` | `24` | No |
| `title` | `string` | - | No |
| `titleId` | `string` | - | No |
| `className` | `string` | - | No |
| `style` | `CSSProperties` | - | No |
#### `fill` [#fill]
Color value for the SVG path fills. Defaults to hot pink (`#FF69B4`) as a visual indicator that fill should be explicitly set.
Use with NTDS color constants for proper affiliation colors:
* `NTDS_COLOR_FRIEND` - Blue for friendly entities
* `NTDS_COLOR_HOSTILE` - Red for hostile entities
* `NTDS_COLOR_UNKNOWN` - Yellow for unknown entities
* And other NTDS standard colors
#### `title` [#title]
Accessible title for screen readers. When provided, the SVG element includes an `aria-labelledby` attribute referencing the title.
> **Good to know:** Always provide a `title` prop when the icon conveys important information, especially in interactive contexts.
#### `titleId` [#titleid]
Custom ID for the `` element. Defaults to automatic ID generation if not provided. Useful for ensuring unique IDs when multiple instances exist on the page.
### Inherited Props [#inherited-props]
All components accept standard SVGSVGElement props including:
* `className` - CSS class name
* `style` - Inline styles
* `onClick` - Click handler
* `onMouseEnter` / `onMouseLeave` - Mouse events
* `aria-*` - Additional ARIA attributes
## Available Components [#available-components]
### Air Entities [#air-entities]
| Component | Import Path | Description |
| ---------------------- | ---------------------------------------- | ------------------- |
| `SvgAirFriend` | `@accelint/ntds/core/air-friend` | Friendly aircraft |
| `SvgAirHostile` | `@accelint/ntds/core/air-hostile` | Hostile aircraft |
| `SvgAirUnknown` | `@accelint/ntds/core/air-unknown` | Unknown aircraft |
| `SvgAirBaseFriend` | `@accelint/ntds/core/air-base-friend` | Friendly air base |
| `SvgAirBaseHostile` | `@accelint/ntds/core/air-base-hostile` | Hostile air base |
| `SvgAirBaseUnknown` | `@accelint/ntds/core/air-base-unknown` | Unknown air base |
| `SvgAirGroupFriend` | `@accelint/ntds/core/air-group-friend` | Friendly air group |
| `SvgAirGroupHostile` | `@accelint/ntds/core/air-group-hostile` | Hostile air group |
| `SvgAirGroupUnknown` | `@accelint/ntds/core/air-group-unknown` | Unknown air group |
| `SvgHelicopterFriend` | `@accelint/ntds/core/helicopter-friend` | Friendly helicopter |
| `SvgHelicopterHostile` | `@accelint/ntds/core/helicopter-hostile` | Hostile helicopter |
| `SvgHelicopterUnknown` | `@accelint/ntds/core/helicopter-unknown` | Unknown helicopter |
### Surface Entities [#surface-entities]
| Component | Import Path | Description |
| ------------------------ | ------------------------------------------- | ---------------------- |
| `SvgSurfaceFriend` | `@accelint/ntds/core/surface-friend` | Friendly surface unit |
| `SvgSurfaceHostile` | `@accelint/ntds/core/surface-hostile` | Hostile surface unit |
| `SvgSurfaceUnknown` | `@accelint/ntds/core/surface-unknown` | Unknown surface unit |
| `SvgSurfaceGroupFriend` | `@accelint/ntds/core/surface-group-friend` | Friendly surface group |
| `SvgSurfaceGroupHostile` | `@accelint/ntds/core/surface-group-hostile` | Hostile surface group |
| `SvgSurfaceGroupUnknown` | `@accelint/ntds/core/surface-group-unknown` | Unknown surface group |
| `SvgCarrierFriend` | `@accelint/ntds/core/carrier-friend` | Friendly carrier |
| `SvgCarrierHostile` | `@accelint/ntds/core/carrier-hostile` | Hostile carrier |
| `SvgCarrierUnknown` | `@accelint/ntds/core/carrier-unknown` | Unknown carrier |
### Subsurface Entities [#subsurface-entities]
| Component | Import Path | Description |
| ---------------------- | ----------------------------------------- | ------------------------ |
| `SvgSubSurfaceFriend` | `@accelint/ntds/core/sub-surface-friend` | Friendly submarine |
| `SvgSubSurfaceHostile` | `@accelint/ntds/core/sub-surface-hostile` | Hostile submarine |
| `SvgSubSurfaceUnknown` | `@accelint/ntds/core/sub-surface-unknown` | Unknown submarine |
| `SvgSubGroupFriend` | `@accelint/ntds/core/sub-group-friend` | Friendly submarine group |
| `SvgSubGroupHostile` | `@accelint/ntds/core/sub-group-hostile` | Hostile submarine group |
| `SvgSubGroupUnknown` | `@accelint/ntds/core/sub-group-unknown` | Unknown submarine group |
### Land Entities [#land-entities]
| Component | Import Path | Description |
| ---------------- | ---------------------------------- | ------------------ |
| `SvgLandFriend` | `@accelint/ntds/core/land-friend` | Friendly land unit |
| `SvgLandHostile` | `@accelint/ntds/core/land-hostile` | Hostile land unit |
| `SvgLandUnknown` | `@accelint/ntds/core/land-unknown` | Unknown land unit |
| `SvgBaseFriend` | `@accelint/ntds/core/base-friend` | Friendly base |
| `SvgBaseHostile` | `@accelint/ntds/core/base-hostile` | Hostile base |
| `SvgBaseUnknown` | `@accelint/ntds/core/base-unknown` | Unknown base |
### Weapons & Ordnance [#weapons--ordnance]
| Component | Import Path | Description |
| ------------------- | ------------------------------------- | --------------------------- |
| `SvgMissileFriend` | `@accelint/ntds/core/missile-friend` | Friendly missile |
| `SvgMissileHostile` | `@accelint/ntds/core/missile-hostile` | Hostile missile |
| `SvgMissileUnknown` | `@accelint/ntds/core/missile-unknown` | Unknown missile |
| `SvgTorpedoFriend` | `@accelint/ntds/core/torpedo-friend` | Friendly torpedo |
| `SvgTorpedoHostile` | `@accelint/ntds/core/torpedo-hostile` | Hostile torpedo |
| `SvgTorpedoUnknown` | `@accelint/ntds/core/torpedo-unknown` | Unknown torpedo |
| `SvgTarget` | `@accelint/ntds/core/target` | Target designator |
| `SvgSam` | `@accelint/ntds/core/sam` | Surface-to-air missile site |
| `SvgMineField` | `@accelint/ntds/core/mine-field` | Mine field |
### Infrastructure & Facilities [#infrastructure--facilities]
| Component | Import Path | Description |
| ------------------- | ------------------------------------ | ---------------------------------------- |
| `SvgPort` | `@accelint/ntds/core/port` | Port facility |
| `SvgRunway` | `@accelint/ntds/core/runway` | Runway |
| `SvgStructure` | `@accelint/ntds/core/structure` | Generic structure |
| `SvgPol` | `@accelint/ntds/core/pol` | Petroleum, oils, and lubricants facility |
| `SvgCommunications` | `@accelint/ntds/core/communications` | Communications facility |
## Examples [#examples]
### Example: Entity with standard color [#example-entity-with-standard-color]
```tsx
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function FriendlyAircraft() {
return (
);
}
```
### Example: Dynamic affiliation rendering [#example-dynamic-affiliation-rendering]
```tsx
import { NTDS_COLOR_FRIEND, NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { SvgAirHostile } from '@accelint/ntds/core/air-hostile';
type Entity = {
id: string;
affiliation: 'friend' | 'hostile';
};
function EntityIcon({ entity }: { entity: Entity }) {
if (entity.affiliation === 'hostile') {
return (
);
}
return (
);
}
```
### Example: Custom sizing [#example-custom-sizing]
```tsx
import { SvgSurfaceFriend } from '@accelint/ntds/core/surface-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function LargeShipIcon() {
return (
);
}
```
### Example: Interactive icon with events [#example-interactive-icon-with-events]
```tsx
import { SvgHelicopterFriend } from '@accelint/ntds/core/helicopter-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function InteractiveHelicopter({ onSelect }) {
const [isHovered, setIsHovered] = useState(false);
return (
setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{ cursor: 'pointer', transition: 'fill 0.2s' }}
/>
);
}
```
### Example: Multiple entities on tactical map [#example-multiple-entities-on-tactical-map]
```tsx
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { SvgSurfaceHostile } from '@accelint/ntds/core/surface-hostile';
import { SvgSubSurfaceUnknown } from '@accelint/ntds/core/sub-surface-unknown';
import {
NTDS_COLOR_FRIEND,
NTDS_COLOR_HOSTILE,
NTDS_COLOR_UNKNOWN,
} from '@accelint/ntds/constants';
function TacticalMap({ entities }) {
return (
);
}
```
### Example: All affiliation states [#example-all-affiliation-states]
```tsx
import { SvgMissileFriend } from '@accelint/ntds/core/missile-friend';
import { SvgMissileHostile } from '@accelint/ntds/core/missile-hostile';
import { SvgMissileUnknown } from '@accelint/ntds/core/missile-unknown';
import {
NTDS_COLOR_FRIEND,
NTDS_COLOR_HOSTILE,
NTDS_COLOR_UNKNOWN,
} from '@accelint/ntds/constants';
function MissileLegend() {
return (
Friendly
Hostile
Unknown
);
}
```
## Related [#related]
* [NTDS Color Constants](../constants.mdx) - Standard NTDS affiliation colors
* [Spritesheet Constants](../spritesheets/masked.mdx) - Spritesheet IDs for COP rendering
# Overview (/docs/packages/ntds)
## Overview [#overview]
The `@accelint/ntds` package provides two rendering approaches for NTDS tactical symbols:
1. **React SVG Components** - Individual components for flexible UI integration
2. **Spritesheet Constants** - Numeric IDs for efficient canvas/WebGL rendering
All symbols follow NTDS (Naval Tactical Data System) standards for military tactical displays.
## Installation [#installation]
```bash
pnpm add @accelint/ntds react react-dom
```
## Quick Start [#quick-start]
### Using SVG Components [#using-svg-components]
```tsx
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function TacticalIcon() {
return (
);
}
```
### Using Spritesheet Constants [#using-spritesheet-constants]
```typescript
import { AIR_FRIEND, SURFACE_HOSTILE } from '@accelint/ntds/spritesheets/masked';
// Use sprite IDs with your rendering engine
const friendlyAircraftSprite = spritesheet.getFrame(AIR_FRIEND);
const hostileShipSprite = spritesheet.getFrame(SURFACE_HOSTILE);
```
## Package Exports [#package-exports]
### Color Constants [#color-constants]
Standard NTDS affiliation colors:
```typescript
import {
NTDS_COLOR_FRIEND, // Blue (#1484F4)
NTDS_COLOR_HOSTILE, // Red (#FF0033)
NTDS_COLOR_UNKNOWN, // Yellow (#EDDA0A)
NTDS_COLOR_NEUTRAL, // Purple (#5B137A)
NTDS_COLOR_ASSUMED_FRIEND, // Green (#0E8F37)
NTDS_COLOR_SUSPECT, // Orange (#FFAD38)
NTDS_COLOR_PENDING, // Dark gray (#404040)
} from '@accelint/ntds/constants';
```
[View full documentation →](./constants.mdx)
### SVG Components [#svg-components]
47 React components for NTDS tactical symbols:
**Air Domain:**
* `SvgAirFriend`, `SvgAirHostile`, `SvgAirUnknown`
* `SvgAirBaseFriend`, `SvgAirBaseHostile`, `SvgAirBaseUnknown`
* `SvgAirGroupFriend`, `SvgAirGroupHostile`, `SvgAirGroupUnknown`
* `SvgHelicopterFriend`, `SvgHelicopterHostile`, `SvgHelicopterUnknown`
**Surface Domain:**
* `SvgSurfaceFriend`, `SvgSurfaceHostile`, `SvgSurfaceUnknown`
* `SvgSurfaceGroupFriend`, `SvgSurfaceGroupHostile`, `SvgSurfaceGroupUnknown`
* `SvgCarrierFriend`, `SvgCarrierHostile`, `SvgCarrierUnknown`
**Subsurface Domain:**
* `SvgSubSurfaceFriend`, `SvgSubSurfaceHostile`, `SvgSubSurfaceUnknown`
* `SvgSubGroupFriend`, `SvgSubGroupHostile`, `SvgSubGroupUnknown`
**Land Domain:**
* `SvgLandFriend`, `SvgLandHostile`, `SvgLandUnknown`
* `SvgBaseFriend`, `SvgBaseHostile`, `SvgBaseUnknown`
**Weapons & Infrastructure:**
* `SvgMissileFriend`, `SvgMissileHostile`, `SvgMissileUnknown`
* `SvgTorpedoFriend`, `SvgTorpedoHostile`, `SvgTorpedoUnknown`
* `SvgTarget`, `SvgSam`, `SvgMineField`
* `SvgPort`, `SvgRunway`, `SvgStructure`, `SvgPol`, `SvgCommunications`
[View full documentation →](./core.mdx)
### Spritesheet Constants [#spritesheet-constants]
64 numeric string identifiers for sprite-based rendering:
```typescript
import {
AIR_FRIEND,
AIR_HOSTILE,
SURFACE_FRIEND,
SURFACE_HOSTILE,
// ... and 60 more constants
} from '@accelint/ntds/spritesheets/masked';
```
[View full documentation →](./spritesheets/masked.mdx)
## Usage Patterns [#usage-patterns]
### Pattern: Dynamic Entity Rendering [#pattern-dynamic-entity-rendering]
```tsx
import { NTDS_COLOR_FRIEND, NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
import { SvgAirFriend } from '@accelint/ntds/core/air-friend';
import { SvgAirHostile } from '@accelint/ntds/core/air-hostile';
type Entity = {
id: string;
type: 'air' | 'surface' | 'subsurface';
affiliation: 'friend' | 'hostile';
position: { x: number; y: number };
};
function EntityIcon({ entity }: { entity: Entity }) {
const Component = entity.affiliation === 'friend'
? SvgAirFriend
: SvgAirHostile;
const color = entity.affiliation === 'friend'
? NTDS_COLOR_FRIEND
: NTDS_COLOR_HOSTILE;
return ;
}
```
### Pattern: Tactical Map with Multiple Entities [#pattern-tactical-map-with-multiple-entities]
```tsx
import { SvgAirFriend, SvgSurfaceHostile } from '@accelint/ntds';
import { NTDS_COLOR_FRIEND, NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
function TacticalMap({ entities }) {
return (
);
}
```
### Pattern: Spritesheet Rendering for Performance [#pattern-spritesheet-rendering-for-performance]
```typescript
import { AIR_FRIEND, SURFACE_HOSTILE } from '@accelint/ntds/spritesheets/masked';
class HighPerformanceCop {
private spritesheet: HTMLImageElement;
private spriteData: SpritesheetData;
renderEntities(entities: Entity[], ctx: CanvasRenderingContext2D): void {
for (const entity of entities) {
const spriteId = this.getSpriteId(entity);
const sprite = this.spriteData.frames[spriteId];
ctx.drawImage(
this.spritesheet,
sprite.frame.x,
sprite.frame.y,
sprite.frame.w,
sprite.frame.h,
entity.x,
entity.y,
24,
24
);
}
}
}
```
## Type Safety [#type-safety]
All exports are fully typed:
```typescript
import type { SVGProps } from 'react';
// Component props type
type IconProps = SVGProps & {
title?: string;
titleId?: string;
};
// Sprite ID type (from constants)
type SpriteId = string;
// Color type (from constants)
type NtdsColor = string; // hex color strings
```
## Browser Support [#browser-support]
* Modern browsers with SVG support
* React 19+
* Canvas/WebGL for spritesheet rendering
## Performance Considerations [#performance-considerations]
**Use SVG Components when:**
* Building React UIs with moderate entity counts (\< 100 entities)
* Need interactive icons with hover states, clicks
* Styling flexibility is important
* Accessibility features (titles, ARIA labels) are required
**Use Spritesheet Constants when:**
* Rendering hundreds or thousands of entities
* Building canvas/WebGL-based tactical displays
* Performance is critical
* COP or real-time tracking applications
## Examples [#examples]
### Example: Legend Component [#example-legend-component]
```tsx
import { SvgAirFriend, SvgSurfaceHostile } from '@accelint/ntds';
import { NTDS_COLOR_FRIEND, NTDS_COLOR_HOSTILE } from '@accelint/ntds/constants';
function TacticalLegend() {
return (
Entity Types
Friendly Aircraft
Hostile Surface Vessel
);
}
```
### Example: Interactive Selectable Entity [#example-interactive-selectable-entity]
```tsx
import { useState } from 'react';
import { SvgHelicopterFriend } from '@accelint/ntds/core/helicopter-friend';
import { NTDS_COLOR_FRIEND } from '@accelint/ntds/constants';
function SelectableEntity({ entity, onSelect }) {
const [isHovered, setIsHovered] = useState(false);
return (
onSelect(entity)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{
cursor: 'pointer',
transition: 'fill 0.2s',
}}
/>
);
}
```
## Contributing [#contributing]
This package is generated from NTDS-compliant SVG source files using the `smeegl` tool. To add new symbols:
1. Add SVG files to `packages/ntds/icons/core/` or `packages/ntds/icons/masked/`
2. Run `pnpm build` to regenerate components and spritesheet
3. Run `pnpm index` to update package exports
## License [#license]
Apache-2.0
## Related Packages [#related-packages]
* `@accelint/design-toolkit` - UI component library
* `@accelint/icons` - General purpose icon library
# Icon Constants (/docs/packages/ntds/masked)
Numeric string identifiers for NTDS tactical symbols in spritesheet format. These constants are used for efficient rendering in Common Operating Picture (COP) applications where sprite-based rendering is preferred over individual SVG components.
## Usage [#usage]
```typescript
import { AIR_FRIEND, SURFACE_HOSTILE } from '@accelint/ntds/spritesheets/masked';
// Reference sprites by ID in COP rendering engine
const friendlyAircraftSprite = spritesheet.getSprite(AIR_FRIEND);
const hostileShipSprite = spritesheet.getSprite(SURFACE_HOSTILE);
```
## Reference [#reference]
All constants are numeric string identifiers generated using CRC-32 hashing:
```typescript
const CONSTANT_NAME: string; // e.g., '978699787'
```
Each constant represents a unique sprite frame in the NTDS spritesheet. The numeric IDs are deterministic and stable across builds.
> **Good to know:** These constants are generated by the `smeegl` tool with CRC-32 decimal encoding. The IDs remain consistent as long as the source SVG files don't change.
## Available Constants [#available-constants]
### Air Domain [#air-domain]
**Standard Air Entities:**
* `AIR_FRIEND` - Friendly aircraft
* `AIR_HOSTILE` - Hostile aircraft
* `AIR_UNKNOWN` - Unknown aircraft
* `AIR_ASSUMED_FRIEND` - Assumed friendly aircraft
* `AIR_NEUTRAL` - Neutral aircraft
* `AIR_PENDING` - Pending classification aircraft
* `AIR_SUSPECT` - Suspect aircraft
**Air Bases:**
* `AIR_BASE_FRIEND` - Friendly air base
* `AIR_BASE_HOSTILE` - Hostile air base
* `AIR_BASE_UNKNOWN` - Unknown air base
**Air Groups:**
* `AIR_GROUP_FRIEND` - Friendly air group
* `AIR_GROUP_HOSTILE` - Hostile air group
* `AIR_GROUP_UNKNOWN` - Unknown air group
**Helicopters:**
* `HELICOPTER_FRIEND` - Friendly helicopter
* `HELICOPTER_HOSTILE` - Hostile helicopter
* `HELICOPTER_UNKNOWN` - Unknown helicopter
### Surface Domain [#surface-domain]
**Standard Surface Units:**
* `SURFACE_FRIEND` - Friendly surface unit
* `SURFACE_HOSTILE` - Hostile surface unit
* `SURFACE_UNKNOWN` - Unknown surface unit
* `SURFACE_ASSUMED_FRIEND` - Assumed friendly surface unit
* `SURFACE_NEUTRAL` - Neutral surface unit
* `SURFACE_PENDING` - Pending classification surface unit
* `SURFACE_SUSPECT` - Suspect surface unit
**Surface Groups:**
* `SURFACE_GROUP_FRIEND` - Friendly surface group
* `SURFACE_GROUP_HOSTILE` - Hostile surface group
* `SURFACE_GROUP_UNKNOWN` - Unknown surface group
**Carriers:**
* `CARRIER_FRIEND` - Friendly carrier
* `CARRIER_HOSTILE` - Hostile carrier
* `CARRIER_UNKNOWN` - Unknown carrier
### Subsurface Domain [#subsurface-domain]
**Standard Subsurface Units:**
* `SUB_SURFACE_FRIEND` - Friendly submarine
* `SUB_SURFACE_HOSTILE` - Hostile submarine
* `SUB_SURFACE_UNKNOWN` - Unknown submarine
* `SUB_SURFACE_ASSUMED_FRIEND` - Assumed friendly submarine
* `SUB_SURFACE_NEUTRAL` - Neutral submarine
* `SUB_SURFACE_PENDING` - Pending classification submarine
* `SUB_SURFACE_SUSPECT` - Suspect submarine
**Submarine Groups:**
* `SUB_GROUP_FRIEND` - Friendly submarine group
* `SUB_GROUP_HOSTILE` - Hostile submarine group
* `SUB_GROUP_UNKNOWN` - Unknown submarine group
### Land Domain [#land-domain]
**Land Units:**
* `LAND_FRIEND` - Friendly land unit
* `LAND_HOSTILE` - Hostile land unit
* `LAND_UNKNOWN` - Unknown land unit
* `LAND_ASSUMED_FRIEND` - Assumed friendly land unit
* `LAND_NEUTRAL` - Neutral land unit
* `LAND_PENDING` - Pending classification land unit
* `LAND_SUSPECT` - Suspect land unit
**Bases:**
* `BASE_FRIEND` - Friendly base
* `BASE_HOSTILE` - Hostile base
* `BASE_UNKNOWN` - Unknown base
### Weapons & Ordnance [#weapons--ordnance]
**Missiles:**
* `MISSILE_FRIEND` - Friendly missile
* `MISSILE_HOSTILE` - Hostile missile
* `MISSILE_UNKNOWN` - Unknown missile
**Torpedoes:**
* `TORPEDO_FRIEND` - Friendly torpedo
* `TORPEDO_HOSTILE` - Hostile torpedo
* `TORPEDO_UNKNOWN` - Unknown torpedo
**Other Weapons:**
* `SAM` - Surface-to-air missile site
* `MINE_FIELD` - Mine field
* `TARGET` - Target designator
### Infrastructure & Facilities [#infrastructure--facilities]
* `PORT` - Port facility
* `RUNWAY` - Runway
* `STRUCTURE` - Generic structure
* `POL` - Petroleum, oils, and lubricants facility
* `COMMUNICATIONS` - Communications facility
## Examples [#examples]
### Example: Sprite lookup in rendering engine [#example-sprite-lookup-in-rendering-engine]
```typescript
import { AIR_FRIEND, SURFACE_HOSTILE } from '@accelint/ntds/spritesheets/masked';
class CopRenderer {
private spritesheet: Spritesheet;
renderEntity(entity: Entity): void {
const spriteId = this.getSpriteId(entity);
const sprite = this.spritesheet.getSprite(spriteId);
this.drawSprite(sprite, entity.x, entity.y);
}
private getSpriteId(entity: Entity): string {
if (entity.domain === 'air' && entity.affiliation === 'friend') {
return AIR_FRIEND;
}
if (entity.domain === 'surface' && entity.affiliation === 'hostile') {
return SURFACE_HOSTILE;
}
// ... more mappings
}
}
```
### Example: Sprite ID mapping table [#example-sprite-id-mapping-table]
```typescript
import {
AIR_FRIEND,
AIR_HOSTILE,
AIR_UNKNOWN,
SURFACE_FRIEND,
SURFACE_HOSTILE,
SURFACE_UNKNOWN,
} from '@accelint/ntds/spritesheets/masked';
type Domain = 'air' | 'surface';
type Affiliation = 'friend' | 'hostile' | 'unknown';
const SPRITE_MAP: Record> = {
air: {
friend: AIR_FRIEND,
hostile: AIR_HOSTILE,
unknown: AIR_UNKNOWN,
},
surface: {
friend: SURFACE_FRIEND,
hostile: SURFACE_HOSTILE,
unknown: SURFACE_UNKNOWN,
},
};
function getSpriteId(domain: Domain, affiliation: Affiliation): string {
return SPRITE_MAP[domain][affiliation];
}
```
### Example: Canvas rendering with sprites [#example-canvas-rendering-with-sprites]
```typescript
import { AIR_FRIEND } from '@accelint/ntds/spritesheets/masked';
function renderTacticalIcon(
ctx: CanvasRenderingContext2D,
spritesheet: HTMLImageElement,
spriteData: SpritesheetData,
x: number,
y: number
): void {
const sprite = spriteData.frames[AIR_FRIEND];
ctx.drawImage(
spritesheet,
sprite.frame.x,
sprite.frame.y,
sprite.frame.w,
sprite.frame.h,
x,
y,
sprite.frame.w,
sprite.frame.h
);
}
```
### Example: WebGL sprite rendering [#example-webgl-sprite-rendering]
```typescript
import { SURFACE_GROUP_HOSTILE } from '@accelint/ntds/spritesheets/masked';
interface SpriteInstance {
spriteId: string;
x: number;
y: number;
scale: number;
}
class WebGLCopRenderer {
private spritesheet: WebGLTexture;
private spriteData: SpritesheetData;
renderBatch(instances: SpriteInstance[]): void {
const vertices = instances.flatMap((instance) => {
const sprite = this.spriteData.frames[instance.spriteId];
return this.createQuad(sprite, instance.x, instance.y, instance.scale);
});
this.drawVertices(vertices);
}
}
// Usage
const hostileFleet: SpriteInstance[] = [
{ spriteId: SURFACE_GROUP_HOSTILE, x: 100, y: 200, scale: 1.0 },
{ spriteId: SURFACE_GROUP_HOSTILE, x: 150, y: 220, scale: 1.0 },
];
```
### Example: Dynamic affiliation state changes [#example-dynamic-affiliation-state-changes]
```typescript
import {
SURFACE_UNKNOWN,
SURFACE_SUSPECT,
SURFACE_HOSTILE,
} from '@accelint/ntds/spritesheets/masked';
class TrackedEntity {
private currentSpriteId: string;
constructor() {
this.currentSpriteId = SURFACE_UNKNOWN;
}
updateAffiliation(confidence: number): void {
if (confidence > 0.8) {
this.currentSpriteId = SURFACE_HOSTILE;
} else if (confidence > 0.5) {
this.currentSpriteId = SURFACE_SUSPECT;
} else {
this.currentSpriteId = SURFACE_UNKNOWN;
}
}
getSpriteId(): string {
return this.currentSpriteId;
}
}
```
### Example: Sprite preloading [#example-sprite-preloading]
```typescript
import * as SPRITES from '@accelint/ntds/spritesheets/masked';
class SpritesheetManager {
private loadedSprites = new Set();
async preloadCommonSprites(): Promise {
const commonSprites = [
SPRITES.AIR_FRIEND,
SPRITES.AIR_HOSTILE,
SPRITES.SURFACE_FRIEND,
SPRITES.SURFACE_HOSTILE,
SPRITES.SUB_SURFACE_UNKNOWN,
];
await Promise.all(
commonSprites.map((spriteId) => this.loadSprite(spriteId))
);
}
private async loadSprite(spriteId: string): Promise {
// Load sprite data
this.loadedSprites.add(spriteId);
}
}
```
### Example: Type-safe sprite selection [#example-type-safe-sprite-selection]
```typescript
import type * as SPRITES from '@accelint/ntds/spritesheets/masked';
type SpriteId = typeof SPRITES[keyof typeof SPRITES];
function renderSprite(spriteId: SpriteId, x: number, y: number): void {
// Type-safe sprite ID
console.log(`Rendering sprite ${spriteId} at (${x}, ${y})`);
}
// Valid
renderSprite(SPRITES.AIR_FRIEND, 10, 20);
// Type error - invalid sprite ID
// renderSprite('invalid-id', 10, 20);
```
## Related [#related]
* [NTDS Color Constants](../constants.mdx) - Standard NTDS affiliation colors
* [SVG Icon Components](../core.mdx) - Individual React SVG components for general usage
# Overview (/docs/packages/postcss-tailwind-css-modules)
PostCSS plugin that wraps Tailwind CSS `group/` and `peer/` classes in `:global()` pseudo-class to fix compatibility issues with CSS Modules.
## Usage [#usage]
```typescript
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};
```
## Reference [#reference]
```typescript
function tailwindCssModulesPlugin(): Plugin
```
### Returns [#returns]
Returns a PostCSS plugin instance configured to transform `group/` and `peer/` classes.
The plugin:
* Only processes files ending in `.module.css`
* Wraps Tailwind `group/` and `peer/` classes in `:global()` pseudo-class
* Prevents duplicate transformations using symbol-based tracking
### Plugin Behavior [#plugin-behavior]
The plugin automatically identifies and transforms selectors containing:
* Classes starting with `group/` (e.g., `group/sidebar`, `group/header`)
* Classes starting with `peer/` (e.g., `peer/checked`, `peer/focus`)
## Examples [#examples]
### Example: Basic configuration with Turbo and Webpack [#example-basic-configuration-with-turbo-and-webpack]
```javascript
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
'@accelint/postcss-tailwind-css-modules': {},
},
};
```
### Example: Configuration with Vite [#example-configuration-with-vite]
```javascript
// postcss.config.js
export default {
plugins: {
'@accelint/postcss-tailwind-css-modules': {},
},
};
```
> **Good to know:** When using Vite, the Tailwind Vite plugin handles Tailwind's PostCSS processing, so you only need this plugin in your PostCSS config. Do not use array syntax (`plugins: ['...']`) as it will cause errors with Vite—use the object syntax shown above.
### Example: CSS transformation [#example-css-transformation]
**Input:**
```css
/* styles.module.css */
.group/sidebar {
color: red;
}
.peer/checked {
color: blue;
}
```
**Output:**
```css
:global(.group/sidebar) {
color: red;
}
:global(.peer/checked) {
color: blue;
}
```
### Example: Complex selectors [#example-complex-selectors]
The plugin handles complex selectors including pseudo-classes, pseudo-elements, and descendant selectors:
```css
/* Input */
.container .group/sidebar:hover > .item { }
.peer/checked:focus::after { }
/* Output */
.container :global(.group/sidebar):hover > .item { }
:global(.peer/checked):focus::after { }
```
### Example: Multiple classes in one selector [#example-multiple-classes-in-one-selector]
```css
/* Input */
.group/header.group/nav { }
/* Output */
:global(.group/header):global(.group/nav) { }
```
> **Good to know:** The plugin only transforms classes that start with `group/` or `peer/` followed by a forward slash. Classes like `.group` or `.peer` (without the slash) are not transformed. Regular CSS Modules classes in the same selector remain unaffected.
## Related [#related]
* [PostCSS Documentation](https://postcss.org/)
* [postcss-selector-parser](https://www.npmjs.com/package/postcss-selector-parser) - Used internally for selector transformation
* [Tailwind CSS - Parent State](https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state)
* [Tailwind CSS - Sibling State](https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-sibling-state)
# doesEndWith (/docs/packages/predicates/does-end-with)
Creates a predicate function that determines if a string ends with a specific suffix.
## Usage [#usage]
```typescript
import { doesEndWith } from '@accelint/predicates';
const isJsFile = doesEndWith('.js');
isJsFile('app.js'); // true
isJsFile('style.css'); // false
```
## Reference [#reference]
```typescript
function doesEndWith(suffix: string): (input: string) => boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------- |
| `suffix` | `string` | The substring to use as the suffix |
### Returns [#returns]
Curried function that accepts a string and returns `true` if it ends with the suffix.
## Examples [#examples]
### Example: File extension filtering [#example-file-extension-filtering]
```typescript
import { doesEndWith } from '@accelint/predicates';
const isImageFile = doesEndWith('.jpg');
const isConfigFile = doesEndWith('.json');
const files = ['avatar.jpg', 'config.json', 'script.js', 'photo.jpg'];
const images = files.filter(doesEndWith('.jpg'));
// ['avatar.jpg', 'photo.jpg']
```
### Example: Email domain validation [#example-email-domain-validation]
```typescript
import { doesEndWith } from '@accelint/predicates';
const isCompanyEmail = doesEndWith('@company.com');
function validateEmail(email: string): boolean {
return isCompanyEmail(email);
}
validateEmail('user@company.com'); // true
validateEmail('user@external.com'); // false
```
> **Good to know:** Comparisons are case-sensitive. Use native String methods like `.toLowerCase()` before applying this predicate if case-insensitive matching is needed.
## Related [#related]
* [doesStartWith](./does-start-with) - Check if string starts with prefix
* [doesNotStartWith](./does-not-start-with) - Check if string does not start with prefix
* [doesNotEndWith](./does-not-end-with) - Negated version of this function
* [isLike](../is-like/index.md) - Pattern matching with RegExp
* [isNotLike](../is-not-like/index.md) - Negated pattern matching
* [isString](../is-string/index.md) - Type guard for strings
# doesNotEndWith (/docs/packages/predicates/does-not-end-with)
Creates a predicate function that determines if a string does **not** end with a specific suffix.
## Usage [#usage]
```typescript
import { doesNotEndWith } from '@accelint/predicates';
const isNotBackup = doesNotEndWith('.bak');
const files = ['data.json', 'data.json.bak', 'config.yaml', 'config.yaml.bak'];
const active = files.filter(isNotBackup);
// ['data.json', 'config.yaml']
```
## Reference [#reference]
```typescript
function doesNotEndWith(suffix: string): (input: string) => boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------- |
| `suffix` | `string` | The substring to use as the suffix |
### Returns [#returns]
Curried function that accepts a string and returns `true` if it does **not** end with the suffix.
## Examples [#examples]
### Example: Excluding backup files [#example-excluding-backup-files]
```typescript
import { doesNotEndWith } from '@accelint/predicates';
const isNotBackup = doesNotEndWith('.bak');
const files = ['data.json', 'data.json.bak', 'config.yaml', 'config.yaml.bak'];
const active = files.filter(isNotBackup);
// ['data.json', 'config.yaml']
```
> **Good to know:** Comparisons are case-sensitive. Use native String methods like `.toLowerCase()` before applying this predicate if case-insensitive matching is needed.
## Related [#related]
* [doesEndWith](./does-end-with) - Positive version of this function
* [doesStartWith](./does-start-with) - Check if string starts with prefix
* [doesNotStartWith](./does-not-start-with) - Check if string does not start with prefix
* [isLike](../is-like/index.md) - Pattern matching with RegExp
* [isNotLike](../is-not-like/index.md) - Negated pattern matching
* [isString](../is-string/index.md) - Type guard for strings
# doesNotStartWith (/docs/packages/predicates/does-not-start-with)
Creates a predicate function that determines if a string does **not** start with a specific prefix.
## Usage [#usage]
```typescript
import { doesNotStartWith } from '@accelint/predicates';
const isNotHidden = doesNotStartWith('.');
const files = ['.git', '.env', 'README.md', '.gitignore', 'src'];
const visible = files.filter(isNotHidden);
// ['README.md', 'src']
```
## Reference [#reference]
```typescript
function doesNotStartWith(prefix: string): (input: string) => boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------- |
| `prefix` | `string` | The substring to use as the prefix |
### Returns [#returns]
Curried function that accepts a string and returns `true` if it does **not** start with the prefix.
## Examples [#examples]
### Example: Excluding system files [#example-excluding-system-files]
```typescript
import { doesNotStartWith } from '@accelint/predicates';
const isNotHidden = doesNotStartWith('.');
const files = ['.git', '.env', 'README.md', '.gitignore', 'src'];
const visible = files.filter(isNotHidden);
// ['README.md', 'src']
```
> **Good to know:** Comparisons are case-sensitive. Use native String methods like `.toLowerCase()` before applying this predicate if case-insensitive matching is needed.
## Related [#related]
* [doesStartWith](./does-start-with) - Positive version of this function
* [doesEndWith](./does-end-with) - Check if string ends with suffix
* [doesNotEndWith](./does-not-end-with) - Check if string does not end with suffix
* [isLike](../is-like/index.md) - Pattern matching with RegExp
* [isNotLike](../is-not-like/index.md) - Negated pattern matching
* [isString](../is-string/index.md) - Type guard for strings
# doesStartWith (/docs/packages/predicates/does-start-with)
Creates a predicate function that determines if a string starts with a specific prefix.
## Usage [#usage]
```typescript
import { doesStartWith } from '@accelint/predicates';
const isHttpUrl = doesStartWith('http');
isHttpUrl('https://example.com'); // true
isHttpUrl('ftp://example.com'); // false
```
## Reference [#reference]
```typescript
function doesStartWith(prefix: string): (input: string) => boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------- |
| `prefix` | `string` | The substring to use as the prefix |
### Returns [#returns]
Curried function that accepts a string and returns `true` if it starts with the prefix.
## Examples [#examples]
### Example: URL validation [#example-url-validation]
```typescript
import { doesStartWith } from '@accelint/predicates';
const isHttps = doesStartWith('https://');
const isHttp = doesStartWith('http://');
function isSecureUrl(url: string): boolean {
return isHttps(url);
}
isSecureUrl('https://example.com'); // true
isSecureUrl('http://example.com'); // false
```
### Example: Filtering file paths [#example-filtering-file-paths]
```typescript
import { doesStartWith } from '@accelint/predicates';
const paths = [
'/home/user/documents',
'/var/log/system',
'/home/user/downloads',
'/etc/config'
];
const userPaths = paths.filter(doesStartWith('/home/user'));
// ['/home/user/documents', '/home/user/downloads']
```
> **Good to know:** Comparisons are case-sensitive. Use native String methods like `.toLowerCase()` before applying this predicate if case-insensitive matching is needed.
## Related [#related]
* [doesEndWith](./does-end-with) - Check if string ends with suffix
* [doesNotStartWith](./does-not-start-with) - Negated version of this function
* [doesNotEndWith](./does-not-end-with) - Check if string does not end with suffix
* [isLike](../is-like/index.md) - Pattern matching with RegExp
* [isNotLike](../is-not-like/index.md) - Negated pattern matching
* [isString](../is-string/index.md) - Type guard for strings
# Overview (/docs/packages/predicates)
## Installation [#installation]
```bash
pnpm add @accelint/predicates
```
## Categories [#categories]
### Type Guards [#type-guards]
Core type checking predicates:
* [isString](./is-string/index.md) - Type guard for string values
* [isNumber](./is-number/index.md) - Number type guards including finite checks
* [isNothing](./is-nothing/index.md) - Check for null or undefined
* [isSomething](./is-something/index.md) - Check for defined values
### Boolean-like Values [#boolean-like-values]
Parse and validate truthy/falsy string representations:
* [is-noyes](./is-noyes/index.md) - Yes/no, on/off, true/false predicates
* `isTrue`, `isFalse`, `isYes`, `isNo`, `isOn`, `isOff`
* `isAnyTruthy`, `isAnyFalsy`
### String Patterns [#string-patterns]
Test strings for prefixes, suffixes, and patterns:
* [does-start-with](./does-start-with/index.md) - String prefix/suffix predicates
* `doesStartWith`, `doesEndWith`
* `doesNotStartWith`, `doesNotEndWith`
* [is-like](./is-like/index.md) - Pattern matching with RegExp
* `isLike`, `isNotLike`
### Comparison [#comparison]
Numeric and lexicographic comparisons:
* [is-equal](./is-equal/index.md) - Comparison predicates
* `isEqual`, `isNotEqual`
* `isGreater`, `isGreaterEqual`
* `isLesser`, `isLesserEqual`
* [is-between](./is-between/index.md) - Range validation
* `isBetween`, `isNotBetween`
### Array Membership [#array-membership]
Test if values exist in arrays:
* [is-in](./is-in/index.md) - Array membership predicates
* `isIn`, `isNotIn`
### Colors [#colors]
Validate color format representations:
* [is-hex-color](./is-hex-color/index.md) - Color format predicates
* `isHexColor` - Hex color strings
* `isCssRgbaString` - CSS rgb/rgba strings
* `isCssRgbaObject` - React Aria RGBA objects
* `isRgba255Tuple` - deck.gl RGBA tuples
* `isValid255Channel` - Channel validation
### Geographic Coordinates [#geographic-coordinates]
Validate latitude, longitude, and bounding boxes:
* [is-latitude](./is-latitude/index.md) - Geographic predicates
* `isLatitude` - Latitude validation (-90 to 90)
* `isLongitude` - Longitude validation (-180 to 180)
* `isBbox` - Bounding box validation
### Web APIs [#web-apis]
Browser API instance checks:
* [is-worker](./is-worker/index.md) - Web Worker predicates
* `isWorker` - Worker instance check
* `isSharedWorker` - SharedWorker instance check
## Usage Patterns [#usage-patterns]
### Basic Usage [#basic-usage]
```typescript
import { isString, isNumber } from '@accelint/predicates';
isString('hello'); // true
isNumber(123); // true
```
### Array Filtering [#array-filtering]
```typescript
import { isString, isNothing } from '@accelint/predicates';
const mixed = ['hello', null, 42, 'world', undefined];
const strings = mixed.filter(isString);
// ['hello', 'world']
const defined = mixed.filter(v => !isNothing(v));
// ['hello', 42, 'world']
```
### Curried Predicates [#curried-predicates]
```typescript
import { isGreater, isIn } from '@accelint/predicates';
const isAdult = isGreater Equal(18);
isAdult(21); // true
isAdult(16); // false
const isValidStatus = isIn(['pending', 'approved', 'rejected']);
isValidStatus('pending'); // true
isValidStatus('unknown'); // false
```
### Type Narrowing [#type-narrowing]
```typescript
import { isNumber } from '@accelint/predicates';
function processValue(val: unknown) {
if (isNumber(val)) {
// TypeScript knows val is number here
return val.toFixed(2);
}
throw new Error('Expected number');
}
```
### Functional Composition [#functional-composition]
```typescript
import { isGreaterEqual, isLesserEqual } from '@accelint/predicates';
import { andFn } from '@accelint/core';
const isValidScore = andFn(
isGreaterEqual(0),
isLesserEqual(100)
);
isValidScore(50); // true
isValidScore(150); // false
```
## Design Principles [#design-principles]
### Pure Functions [#pure-functions]
All predicates are pure functions with no side effects:
```typescript
const isEven = (n: number) => n % 2 === 0;
isEven(4); // true (always returns same result for same input)
```
### Curried by Default [#curried-by-default]
Most predicates are curried for partial application:
```typescript
import { isGreater } from '@accelint/predicates';
// Create specialized predicates
const isAdult = isGreaterEqual(18);
const isRetired = isGreaterEqual(65);
ages.filter(isAdult);
ages.filter(isRetired);
```
### Type Safety [#type-safety]
Predicates provide runtime validation and TypeScript type guards:
```typescript
import { isNumber } from '@accelint/predicates';
function double(val: unknown): number {
if (!isNumber(val)) {
throw new Error('Expected number');
}
return val * 2; // TypeScript knows val is number
}
```
## Related Packages [#related-packages]
* [@accelint/core](../core/index.md) - Functional utilities for composition
* [@accelint/converters](../converters/index.md) - Type conversion utilities
* [@accelint/constants](../constants/index.md) - Shared constants and regex patterns
# isBbox (/docs/packages/predicates/is-bbox)
Validates bounding box as array of 4 finite numbers: `[minLon, minLat, maxLon, maxLat]`.
## Usage [#usage]
```typescript
import { isBbox } from '@accelint/predicates';
isBbox([-122.5, 37.7, -122.4, 37.8]); // true (San Francisco area)
isBbox([-180, -90, 180, 90]); // true (entire world)
isBbox([1, 2, 3]); // false (wrong length)
```
## Reference [#reference]
```typescript
function isBbox(bbox: unknown): boolean
```
Format: `[minLongitude, minLatitude, maxLongitude, maxLatitude]`
## Examples [#examples]
```typescript
import { isBbox } from '@accelint/predicates/is-bbox';
// Valid bounding boxes
isBbox([-180, -90, 180, 90]); // true (entire world)
isBbox([-122.5, 37.7, -122.4, 37.8]); // true (San Francisco area)
// Invalid bounding boxes
isBbox([1, 2, 3]); // false (wrong length)
isBbox([1, 2, 3, 'four']); // false (non-number)
isBbox([1, 2, NaN, 4]); // false (NaN not allowed)
isBbox(null); // false
```
### Example: Map bounds validation [#example-map-bounds-validation]
```typescript
import { isBbox } from '@accelint/predicates/is-bbox';
function setMapBounds(bounds: unknown) {
if (!isBbox(bounds)) {
throw new Error('Invalid bounding box format');
}
const [minLon, minLat, maxLon, maxLat] = bounds;
// Set map bounds...
}
setMapBounds([-122.5, 37.7, -122.4, 37.8]); // OK
setMapBounds([1, 2, 3]); // Error: Invalid bounding box format
```
## Real-World Example: Complete bounding box validation [#real-world-example-complete-bounding-box-validation]
```typescript
import { isLatitude } from '@accelint/predicates/is-latitude';
import { isLongitude } from '@accelint/predicates/is-longitude';
import { isBbox } from '@accelint/predicates/is-bbox';
function isValidBounds(bounds: unknown): bounds is [number, number, number, number] {
if (!isBbox(bounds)) {
return false;
}
const [minLon, minLat, maxLon, maxLat] = bounds;
return (
isLongitude(minLon) &&
isLatitude(minLat) &&
isLongitude(maxLon) &&
isLatitude(maxLat) &&
minLon < maxLon &&
minLat < maxLat
);
}
const sfBounds = [-122.5, 37.7, -122.4, 37.8];
isValidBounds(sfBounds); // true
```
> **Good to know:** `isBbox` currently validates array length and numeric types but does not validate coordinate ranges. Use `isLatitude` and `isLongitude` for range validation as shown in the example above.
## Related [#related]
* [isLatitude](./is-latitude) - Validates latitude values
* [isLongitude](./is-longitude) - Validates longitude values
* [isBetween](../is-between/index.md) - Range validation for numbers
* [isNumber](../is-number/index.md) - Type guard for numbers
* \[isFiniteNumber]\(../is-number/index.md#isfinite number) - Finite number validation
# isBetween (/docs/packages/predicates/is-between)
Creates a curried predicate function that determines if a number is between two bounds (inclusive).
## Usage [#usage]
```typescript
import { isBetween } from '@accelint/predicates';
const isValidScore = isBetween([0, 100]);
isValidScore(89); // true
isValidScore(150); // false
isValidScore(0); // true (inclusive)
isValidScore(100); // true (inclusive)
// Order doesn't matter
const isInRange = isBetween([100, 0]);
isInRange(50); // true
```
## Reference [#reference]
```typescript
function isBetween(bounds: [number, number]): (value: number) => boolean
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | ------------------ | -------------------------------------------------------------- |
| `bounds` | `[number, number]` | Tuple containing lower and upper bounds (order doesn't matter) |
### Returns [#returns]
Curried function that accepts a number and returns `true` if the number is between the bounds (inclusive), `false` otherwise.
## Examples [#examples]
### Example: Validating user input [#example-validating-user-input]
```typescript
import { isBetween } from '@accelint/predicates';
const isValidAge = isBetween([0, 120]);
const isValidPercentage = isBetween([0, 100]);
function validateInput(age: number, score: number): boolean {
return isValidAge(age) && isValidPercentage(score);
}
validateInput(25, 85); // true
validateInput(150, 85); // false
validateInput(25, 150); // false
```
### Example: Filtering arrays [#example-filtering-arrays]
```typescript
import { isBetween } from '@accelint/predicates';
const temperatures = [-5, 15, 25, 35, 45];
const comfortable = temperatures.filter(isBetween([18, 28]));
// [25]
```
### Example: Validating geographic coordinates (with other predicates) [#example-validating-geographic-coordinates-with-other-predicates]
```typescript
import { isBetween } from '@accelint/predicates';
const isValidLatitude = isBetween([-90, 90]);
const isValidLongitude = isBetween([-180, 180]);
function isValidCoordinate(lat: number, lon: number): boolean {
return isValidLatitude(lat) && isValidLongitude(lon);
}
isValidCoordinate(37.7749, -122.4194); // true (San Francisco)
isValidCoordinate(100, 200); // false
```
### Example: Range validation for sliders [#example-range-validation-for-sliders]
```typescript
import { isBetween } from '@accelint/predicates';
const isValidVolume = isBetween([0, 100]);
const isValidBrightness = isBetween([0, 100]);
function updateSettings(volume: number, brightness: number) {
if (!isValidVolume(volume)) {
throw new Error(`Volume must be between 0 and 100, got ${volume}`);
}
if (!isValidBrightness(brightness)) {
throw new Error(`Brightness must be between 0 and 100, got ${brightness}`);
}
// Update settings...
}
```
> **Good to know:** The bounds are automatically sorted, so `isBetween([100, 0])` and `isBetween([0, 100])` are equivalent. The check is inclusive on both ends (`>=` lower and `<=` upper).
## Related [#related]
* [isNotBetween](../is-not-between/index.md) - Opposite check: value is outside the range
* [isGreater](../is-greater/index.md) - Check if value is greater than threshold
* [isLesser](../is-lesser/index.md) - Check if value is less than threshold
* [isGreaterEqual](../is-greater-equal/index.md) - Check if value is greater than or equal to threshold
* [isLesserEqual](../is-lesser-equal/index.md) - Check if value is less than or equal to threshold
# isCssRgbaObject (/docs/packages/predicates/is-css-rgba-object)
Validates CSS-style RGBA color object (React Aria Components format).
## Usage [#usage]
```typescript
import { isCssRgbaObject } from '@accelint/predicates/is-css-rgba-object';
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 1 }); // true
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 0.5 }); // true
isCssRgbaObject({ r: 255, g: 128, b: 64 }); // false (missing alpha)
isCssRgbaObject([255, 128, 64, 255]); // false (array, not object)
isCssRgbaObject({ r: 256, g: 128, b: 64, a: 1 }); // false (r out of range)
```
## Reference [#reference]
```typescript
type CssRgbaObject = {
readonly r: number; // 0-255
readonly g: number; // 0-255
readonly b: number; // 0-255
readonly a: number; // 0-1
};
function isCssRgbaObject(value: unknown): value is CssRgbaObject
```
RGB channels: 0-255, Alpha channel: 0-1
## Examples [#examples]
```typescript
import { isCssRgbaObject } from '@accelint/predicates/is-css-rgba-object';
// Valid RGBA objects
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 1 }); // true
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 0.5 }); // true
isCssRgbaObject({ r: 0, g: 0, b: 0, a: 0 }); // true
// Missing properties
isCssRgbaObject({ r: 255, g: 128, b: 64 }); // false (missing alpha)
isCssRgbaObject({ r: 255, g: 128, a: 1 }); // false (missing blue)
// Wrong types
isCssRgbaObject([255, 128, 64, 1]); // false (array, not object)
isCssRgbaObject('rgba(255, 128, 64, 1)'); // false (string, not object)
// Out of range values
isCssRgbaObject({ r: 256, g: 128, b: 64, a: 1 }); // false (r > 255)
isCssRgbaObject({ r: -1, g: 128, b: 64, a: 1 }); // false (r < 0)
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 2 }); // false (a > 1)
isCssRgbaObject({ r: 255, g: 128, b: 64, a: -0.1 }); // false (a < 0)
// Extra properties are allowed
isCssRgbaObject({ r: 255, g: 128, b: 64, a: 1, format: 'css' }); // true
```
## Real-World Example: React Aria ColorPicker integration [#real-world-example-react-aria-colorpicker-integration]
```typescript
import { isCssRgbaObject } from '@accelint/predicates/is-css-rgba-object';
import type { Color } from 'react-aria-components';
function normalizeColorValue(color: unknown): CssRgbaObject {
if (isCssRgbaObject(color)) {
return color;
}
throw new Error('Invalid color: expected CssRgbaObject format');
}
function ColorDisplay({ value }: { value: unknown }) {
const color = normalizeColorValue(value);
return (
);
}
```
> **Good to know:** Initializing camera state before subscribing prevents MapLibre from rendering at default (0,0,0) and firing onMove events that would overwrite the initialized state.
## CameraEventTypes [#cameraeventtypes]
Event type constants for camera operations via the broadcast bus.
### Usage [#usage-2]
```typescript
import { Broadcast } from '@accelint/bus';
import { CameraEventTypes } from '@accelint/map-toolkit/camera';
import type { CameraEvent } from '@accelint/map-toolkit/camera';
const bus = Broadcast.getInstance();
// Set camera center
bus.emit(CameraEventTypes.setCenter, {
id: 'map-1',
latitude: 37.7749,
longitude: -122.4194,
zoom: 10,
});
```
### Reference [#reference-2]
```typescript
const CameraEventTypes = {
setView: 'camera:setView',
setProjection: 'camera:setProjection',
setZoom: 'camera:setZoom',
setRotation: 'camera:setRotation',
setPitch: 'camera:setPitch',
setCenter: 'camera:setCenter',
fitBounds: 'camera:fitBounds',
reset: 'camera:reset',
} as const;
```
### Examples [#examples-2]
#### Example: Setting camera center [#example-setting-camera-center]
```typescript
import { Broadcast } from '@accelint/bus';
import { CameraEventTypes } from '@accelint/map-toolkit/camera';
import type { CameraEvent } from '@accelint/map-toolkit/camera';
const bus = Broadcast.getInstance();
bus.emit(CameraEventTypes.setCenter, {
id: 'map-1',
latitude: 37.7749,
longitude: -122.4194,
zoom: 10,
heading: 45,
});
```
#### Example: Fitting to bounds [#example-fitting-to-bounds]
```typescript
import { Broadcast } from '@accelint/bus';
import { CameraEventTypes } from '@accelint/map-toolkit/camera';
import type { CameraEvent } from '@accelint/map-toolkit/camera';
const bus = Broadcast.getInstance();
// Fit camera to show San Francisco Bay Area
bus.emit(CameraEventTypes.fitBounds, {
id: 'map-1',
bounds: [-122.5, 37.7, -122.3, 37.9],
width: 800,
height: 600,
padding: 20,
});
```
#### Example: Changing view mode [#example-changing-view-mode]
```typescript
import { Broadcast } from '@accelint/bus';
import { CameraEventTypes } from '@accelint/map-toolkit/camera';
import type { CameraEvent } from '@accelint/map-toolkit/camera';
const bus = Broadcast.getInstance();
// Switch to 2.5D tilted view
bus.emit(CameraEventTypes.setView, {
id: 'map-1',
view: '2.5D',
});
```
#### Example: Resetting camera [#example-resetting-camera]
```typescript
import { Broadcast } from '@accelint/bus';
import { CameraEventTypes } from '@accelint/map-toolkit/camera';
import type { CameraEvent } from '@accelint/map-toolkit/camera';
const bus = Broadcast.getInstance();
// Full reset to initial state
bus.emit(CameraEventTypes.reset, {
id: 'map-1',
});
// Reset but preserve current zoom
bus.emit(CameraEventTypes.reset, {
id: 'map-1',
zoom: false,
});
```
> **Good to know:** Camera events are processed asynchronously through the broadcast bus, enabling decoupled control of camera state from anywhere in your application.
## clearCameraState [#clearcamerastate]
Manually clears camera state for a specific map instance, removing all cached state and subscription tracking.
### Usage [#usage-3]
```typescript
import { clearCameraState } from '@accelint/map-toolkit/camera';
// Clean up when removing a map
clearCameraState('map-1');
```
### Reference [#reference-3]
```typescript
function clearCameraState(mapId: UniqueId): void
```
#### Parameters [#parameters-1]
| Parameter | Type | Description |
| --------- | ---------- | --------------------------------------------------- |
| `mapId` | `UniqueId` | The unique identifier for the map instance to clear |
### Examples [#examples-3]
#### Example: Cleanup in tests [#example-cleanup-in-tests]
```typescript
import { clearCameraState } from '@accelint/map-toolkit/camera';
import { afterEach } from 'vitest';
afterEach(() => {
clearCameraState('test-map-id');
});
```
#### Example: Dynamic map removal [#example-dynamic-map-removal]
```typescript
import { clearCameraState } from '@accelint/map-toolkit/camera';
function removeMap(mapId: UniqueId) {
clearCameraState(mapId);
// ... remove map component
}
```
> **Good to know:** In most cases, cleanup happens automatically when all subscribers unmount. Use this function only for manual cleanup when dynamically destroying map instances or in test environments.
## ViewType [#viewtype]
Camera view modes defining perspective and interaction capabilities.
### Reference [#reference-4]
```typescript
type ViewType = '2D' | '2.5D' | '3D';
```
* `'2D'` - Traditional flat map view with rotation (mercator projection)
* `'2.5D'` - Tilted perspective view with pitch and rotation (mercator projection)
* `'3D'` - Globe view with fixed orientation (globe projection)
## ProjectionType [#projectiontype]
Map projection types supported by the camera system.
### Reference [#reference-5]
```typescript
type ProjectionType = 'mercator' | 'globe';
```
* `'mercator'` - Web Mercator projection for 2D and 2.5D views
* `'globe'` - Spherical globe projection for 3D views
## Related [#related]
* [Viewport](../viewport/index.md) - Viewport state management (bounds, dimensions)
* [MapEvents](../deckgl/base-map/events.md) - Event types for map interactions
* [createMapStore](../shared/create-map-store.md) - Underlying store pattern
# toLonLat (/docs/toolkits/map-toolkit/coordinates)
## Usage [#usage]
```typescript
import { toLonLat } from '@accelint/map-toolkit/shared/coordinates';
import type { PickingInfo } from '@deck.gl/core';
// `info.coordinate` is `number[] | undefined`; normalize before storing it.
function onHover(info: PickingInfo) {
const coordinate = toLonLat(info.coordinate);
if (coordinate) {
setCoordinate(coordinate);
}
}
```
## Reference [#reference]
```typescript
function toLonLat(value?: number[] | null): [number, number] | null
```
### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | ------------------------------- | ------------------------------------------------------------------- |
| `value` | `number[] \| null \| undefined` | Picked coordinate to normalize, typically `PickingInfo.coordinate`. |
### Returns [#returns]
Returns a new `[longitude, latitude]` tuple when the first two entries are finite numbers, otherwise `null`. A trailing `z` entry is dropped.
deck.gl's `PickingInfo.coordinate` is typed `number[]` and can be empty, non-finite, or three-element (`[longitude, latitude, z]` when any layer is `pickable: '3d'`), so pass it through `toLonLat` before treating it as a coordinate.
> **Good to know:** `toLonLat` is a pure function and always returns a fresh tuple; it never returns the input array. It runs on every hover and drag inside `BaseMap`, so it performs two direct checks with no callback allocation.
## Examples [#examples]
### Example: Guarding a bus payload [#example-guarding-a-bus-payload]
```typescript
import { toLonLat } from '@accelint/map-toolkit/shared/coordinates';
const coordinate = toLonLat(info.coordinate);
// Skip the emit when the pointer is off the globe.
if (coordinate) {
emitDragStart({ id, coordinate, shiftKey, ctrlKey, altKey });
}
```
### Example: Dropping a z component [#example-dropping-a-z-component]
```typescript
import { toLonLat } from '@accelint/map-toolkit/shared/coordinates';
toLonLat([-97.74, 30.27, 120]);
// [-97.74, 30.27]
```
### Example: Rejecting invalid input [#example-rejecting-invalid-input]
```typescript
import { toLonLat } from '@accelint/map-toolkit/shared/coordinates';
toLonLat(undefined); // null
toLonLat(null); // null
toLonLat([]); // null
toLonLat([Number.NaN, 30.27]); // null
toLonLat([-97.74]); // null
```
## Related [#related]
* [BaseMap](./deckgl/base-map.mdx) - Normalizes drag coordinates with `toLonLat` before emitting `map:dragStart` / `map:drag` / `map:dragEnd`
* [Measurement Layer](./deckgl/measurement-layer.mdx) - Applies `toLonLat` to `pointA` / `pointB` so non-finite points render nothing
* [cursor-coordinates](./cursor-coordinates.mdx) - Track and format cursor coordinates in multiple systems
# Cursor Coordinates (/docs/toolkits/map-toolkit/cursor-coordinates)
Hook for tracking and displaying formatted cursor coordinates on map components with support for multiple coordinate systems including Decimal Degrees, DMS, MGRS, and UTM.
## useCursorCoordinates [#usecursorcoordinates]
Tracks and formats cursor hover position coordinates on a map in real-time.
### Usage [#usage]
```tsx
import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';
function CoordinateDisplay({ mapId }: { mapId: string }) {
const { formattedCoord, setFormat } = useCursorCoordinates(mapId);
return (
{formattedCoord}
);
}
```
### Reference [#reference]
```typescript
function useCursorCoordinates(
id?: UniqueId,
options?: UseCursorCoordinatesOptions
): UseCursorCoordinatesReturn
interface UseCursorCoordinatesOptions {
formatter?: CoordinateFormatter;
}
interface UseCursorCoordinatesReturn {
formattedCoord: string;
setFormat: (format: CoordinateFormatTypes) => void;
rawCoord: RawCoordinate;
currentFormat: CoordinateFormatTypes;
}
type CoordinateFormatTypes = 'dd' | 'ddm' | 'dms' | 'mgrs' | 'utm';
type RawCoordinate = {
longitude: number;
latitude: number;
} | null;
```
#### Parameters [#parameters]
| Parameter | Type | Description |
| --------- | ----------------------------- | --------------------------------------------------------------------------- |
| `id` | `UniqueId` | Optional map instance ID. If not provided, uses ID from MapProvider context |
| `options` | `UseCursorCoordinatesOptions` | Optional configuration |
#### Options [#options]
| Option | Type | Description |
| ----------- | --------------------- | -------------------------------------------------------- |
| `formatter` | `CoordinateFormatter` | Custom formatter function that overrides built-in format |
#### Returns [#returns]
| Property | Type | Description |
| ---------------- | ----------------------------------------- | -------------------------------------------------------------------- |
| `formattedCoord` | `string` | Formatted coordinate string using current format or custom formatter |
| `setFormat` | `(format: CoordinateFormatTypes) => void` | Function to change coordinate format system |
| `rawCoord` | `RawCoordinate` | Raw coordinate data (longitude/latitude) or null |
| `currentFormat` | `CoordinateFormatTypes` | Current active format type |
### Examples [#examples]
#### Example: Basic coordinate display [#example-basic-coordinate-display]
```tsx
import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';
function CoordinateDisplay({ mapId }: { mapId: string }) {
const { formattedCoord } = useCursorCoordinates(mapId);
return
{formattedCoord}
;
}
// Displays: "45.500000° N / 30.250000° E" (default DD format)
```
#### Example: Format selector [#example-format-selector]
```tsx
import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';
import type { CoordinateFormatTypes } from '@accelint/map-toolkit/cursor-coordinates';
function CoordinateSelector({ mapId }: { mapId: string }) {
const { formattedCoord, setFormat, currentFormat } = useCursorCoordinates(mapId);
return (
);
}
```
#### Example: Within MapProvider context [#example-within-mapprovider-context]
```tsx
import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';
import { MapProvider } from '@accelint/map-toolkit/deckgl/base-map';
function CoordinateWidget() {
// No need to pass mapId when inside MapProvider
const { formattedCoord } = useCursorCoordinates();
return
{formattedCoord}
;
}
function App() {
return (
);
}
```
> **Good to know:** UTM and MGRS coordinate systems are only valid between 80°S and 84°N. Coordinates outside this range (e.g., polar regions) will display the default placeholder `-- / --`. Other formats (DD, DDM, DMS) work correctly at all latitudes.
#### Example: Error handling for custom formatter [#example-error-handling-for-custom-formatter]
```tsx
import { useCursorCoordinates } from '@accelint/map-toolkit/cursor-coordinates';
function SafeCoordinateDisplay({ mapId }: { mapId: string }) {
const { formattedCoord } = useCursorCoordinates(mapId, {
formatter: (coord) => {
// Custom formatter errors are caught and logged
// Falls back to default placeholder
if (coord.latitude > 90) {
throw new Error('Invalid latitude');
}
return `${coord.latitude}, ${coord.longitude}`;
},
});
return
{formattedCoord}
;
}
```
## CoordinateFormatTypes [#coordinateformattypes]
Supported coordinate format types for displaying map coordinates.
### Reference [#reference-1]
```typescript
type CoordinateFormatTypes = 'dd' | 'ddm' | 'dms' | 'mgrs' | 'utm';
```
* `'dd'` - Decimal Degrees (e.g., "45.500000° N / 30.250000° E")
* `'ddm'` - Degrees Decimal Minutes (e.g., "45° 30.0000' N / 30° 15.0000' E")
* `'dms'` - Degrees Minutes Seconds (e.g., "45° 30' 0.00" N / 30° 15' 0.00" E")
* `'mgrs'` - Military Grid Reference System (e.g., "31U DQ 48251 11932")
* `'utm'` - Universal Transverse Mercator (e.g., "31N 448251 5411932")
### Precision [#precision]
Each format uses precision matching the CoordinateField component:
* DD: 6 decimal places
* DDM: 4 decimal places for minutes
* DMS: 2 decimal places for seconds
## cursorCoordinateStore [#cursorcoordinatestore]
Global store managing cursor coordinate state for all map instances.
### Usage [#usage-1]
```tsx
import { cursorCoordinateStore } from '@accelint/map-toolkit/cursor-coordinates';
function CoordinateInfo({ mapId }: { mapId: string }) {
const { state, setFormat } = cursorCoordinateStore.use(mapId);
return (
);
}
```
### Reference [#reference-2]
```typescript
const cursorCoordinateStore: MapStore;
interface CursorCoordinateState {
coordinate: [number, number] | null;
format: CoordinateFormatTypes;
}
```
State is updated automatically from map hover events via the broadcast bus.
## clearCursorCoordinateState [#clearcursorcoordinatestate]
Manually clears cursor coordinate state for a specific map instance.
### Usage [#usage-2]
```typescript
import { clearCursorCoordinateState } from '@accelint/map-toolkit/cursor-coordinates';
clearCursorCoordinateState('map-1');
```
### Reference [#reference-3]
```typescript
function clearCursorCoordinateState(mapId: UniqueId): void
```
> **Good to know:** Cleanup happens automatically when all subscribers unmount. Use this only for manual cleanup in tests or when dynamically destroying map instances.
## Related [#related]
* [Viewport](../viewport/index.md) - Viewport state management
* [MapEvents](../deckgl/base-map/events.md) - Event types including hover events
* [createMapStore](../shared/create-map-store.md) - Underlying store pattern
# Overview (/docs/toolkits/map-toolkit)
## Installation [#installation]
```bash
pnpm add @accelint/map-toolkit
```
## Overview [#overview]
The Map Toolkit provides React components and utilities for building interactive geospatial applications. Built on top of Deck.gl and MapLibre GL, it offers a comprehensive set of tools for:
* **Base Maps** - Deck.gl-powered maps with MapLibre GL integration
* **State Management** - Coordinated camera, cursor, and mode management
* **Visualization Layers** - Symbol layers, grid layers (GARS, MGRS), and shape drawing
* **Interactive Tools** - Drawing, editing, and selecting shapes
* **Widgets** - HTML overlay system for custom UI elements
## Main Entry Points [#main-entry-points]
| Export Path | Description |
| ---------------------------------------------- | --------------------------------------------------------- |
| [camera](./camera.mdx) | Camera state management and coordinate system operations |
| [cursor-coordinates](./cursor-coordinates.mdx) | Track and format cursor coordinates in multiple systems |
| [deckgl](./deckgl/index.mdx) | Deck.gl base map, layers, extensions, and widgets |
| [map-cursor](./map-cursor.mdx) | Cursor state management and custom cursor icons |
| [map-mode](./map-mode.mdx) | Map interaction mode management (pan, draw, select, etc.) |
| [maplibre](./maplibre.mdx) | MapLibre GL integration and custom handlers |
| [viewport](./viewport.mdx) | Viewport state management and responsive sizing |
## Quick Start [#quick-start]
```tsx
import { BaseMap } from '@accelint/map-toolkit/deckgl';
import { useCamera } from '@accelint/map-toolkit/camera';
import { uuid } from '@accelint/core';
const MAP_ID = uuid();
export function MapView() {
// Access camera state
const camera = useCamera(MAP_ID);
return (
);
}
```
## Architecture [#architecture]
The toolkit uses a **centralized event bus pattern** for coordinating state across components:
1. **Event Bus** - All map interactions flow through `@accelint/bus`
2. **Stores** - Each subsystem (camera, cursor, mode) maintains its own store
3. **Hooks** - React hooks subscribe to store updates and emit events
4. **Components** - Map components emit and respond to events
This architecture enables:
* Multiple map instances with isolated state
* Coordinated interactions across components
* Custom UI that responds to map state changes
* Easy integration with existing event-driven systems
## Related [#related]
* [@accelint/bus](../packages/bus/index.mdx) - Event bus for component coordination
* [@accelint/hotkey-manager](../packages/hotkey-manager/index.mdx) - Keyboard shortcut management
* [@accelint/geo](../packages/geo/index.mdx) - Geographic coordinate utilities
* [Deck.gl Documentation](https://deck.gl) - Official Deck.gl docs
* [MapLibre GL Documentation](https://maplibre.org) - Official MapLibre GL docs
# Map Cursor (/docs/toolkits/map-toolkit/map-cursor)
## Usage [#usage]
```tsx
import { useMapCursor, useMapCursorEffect } from '@accelint/map-toolkit/map-cursor';
import type { UniqueId } from '@accelint/core';
import type { PickingInfo } from '@deck.gl/core';
// Inside MapProvider - read and update cursor
function InteractiveLayer() {
const { cursor, requestCursorChange, clearCursor } = useMapCursor();
const handleHover = (info: PickingInfo) => {
if (info.object) {
requestCursorChange('pointer', 'layer-id');
} else {
clearCursor('layer-id');
}
};
return ;
}
// Lifecycle-based cursor (auto-cleanup on unmount)
function DrawingMode() {
useMapCursorEffect('crosshair', 'drawing-mode');
return
Drawing active
;
}
```
## Reference [#reference]
### `useMapCursor(id?: UniqueId): UseMapCursorReturn` [#usemapcursorid-uniqueid-usemapcursorreturn]
Hook to access cursor state and actions. Uses `useSyncExternalStore` for concurrent-safe updates with a fan-out pattern.
**Owner-based Priority System:**
* Mode owners (from MapModeStore) have highest priority
* Non-owners can set cursors only in default/ownerless modes
* Rejected requests emit events with rejection reasons
#### Parameters [#parameters]
* `id` (optional) - Map instance ID. If omitted, uses `MapContext` (requires being within `MapProvider`).
#### Returns [#returns]
```typescript
type UseMapCursorReturn = {
cursor: CSSCursorType;
requestCursorChange: (cursor: CSSCursorType, requestOwner: string) => void;
clearCursor: (owner: string) => void;
};
```
* `cursor` - Current active cursor (CSS cursor string)
* `requestCursorChange` - Request cursor change with ownership
* `clearCursor` - Clear cursor for specific owner
#### Throws [#throws]
Error if no `id` is provided and hook is used outside `MapProvider`.
### `useMapCursorEffect(cursorType: CSSCursorType, owner: string, id?: UniqueId): void` [#usemapcursoreffectcursortype-csscursortype-owner-string-id-uniqueid-void]
Lifecycle hook that automatically manages cursor for a component.
Requests cursor on mount, maintains it while mounted, and clears on unmount. Does NOT re-render on cursor state changes—use `useMapCursor` if you need to read current cursor value.
#### Parameters [#parameters-1]
* `cursorType` - The CSS cursor string to request
* `owner` - Owner identifier for priority management
* `id` (optional) - Map instance ID
#### Throws [#throws-1]
Error if no `id` is provided and hook is used outside `MapProvider`.
### Events [#events]
Cursor management uses event-driven coordination:
#### `MapCursorEvents.changeRequest` [#mapcursoreventschangerequest]
Emitted when cursor change is requested.
```typescript
type CursorChangeRequestPayload = {
cursor: CSSCursorType;
owner: string;
id: UniqueId;
};
```
#### `MapCursorEvents.changed` [#mapcursoreventschanged]
Emitted when cursor successfully changes.
```typescript
type CursorChangedPayload = {
previousCursor: CSSCursorType;
currentCursor: CSSCursorType;
owner: string;
id: UniqueId;
};
```
#### `MapCursorEvents.rejected` [#mapcursoreventsrejected]
Emitted when cursor request is rejected due to priority.
```typescript
type CursorRejectionPayload = {
rejectedCursor: CSSCursorType;
rejectedOwner: string;
currentOwner: string;
reason: string;
id: UniqueId;
};
```
### Store Functions [#store-functions]
```typescript
// Get current cursor
getCursor(id: UniqueId): CSSCursorType;
// Clear state for cleanup
clearCursorState(id: UniqueId): void;
// Low-level store access
cursorStore.useSelector(id: UniqueId, selector: (state) => T): T;
cursorStore.actions(id: UniqueId): { requestCursorChange, clearCursor };
```
### Types [#types]
```typescript
// CSS cursor string from React CSSProperties
type CSSCursorType = NonNullable;
```
Includes all standard CSS cursor keywords (`pointer`, `crosshair`, `move`, etc.) and custom `url()` syntax.
## Examples [#examples]
### Example: Hover-based cursor change [#example-hover-based-cursor-change]
```tsx
import { useMapCursor } from '@accelint/map-toolkit/map-cursor';
import { ScatterplotLayer } from '@deck.gl/layers';
import type { PickingInfo } from '@deck.gl/core';
function PointLayer() {
const { requestCursorChange, clearCursor } = useMapCursor();
const handleHover = (info: PickingInfo) => {
if (info.object) {
// Show pointer when hovering over a point
requestCursorChange('pointer', 'point-layer');
} else {
// Clear cursor when not hovering
clearCursor('point-layer');
}
};
return (
d.position}
getRadius={10}
/>
);
}
```
### Example: Mode-coordinated cursor [#example-mode-coordinated-cursor]
```tsx
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import { useMapCursorEffect } from '@accelint/map-toolkit/map-cursor';
function DrawingMode() {
const { mode } = useMapMode();
// Automatically set crosshair cursor when in drawing mode
useMapCursorEffect(
mode === 'drawing' ? 'crosshair' : 'default',
'drawing-mode'
);
return mode === 'drawing' ? : null;
}
```
### Example: Custom cursor with icon [#example-custom-cursor-with-icon]
```tsx
import { useMapCursor } from '@accelint/map-toolkit/map-cursor';
function CustomCursorControl() {
const { requestCursorChange } = useMapCursor();
const setCustomCursor = () => {
// Use custom cursor image
requestCursorChange(
'url(/cursors/crosshair.svg) 16 16, crosshair',
'custom-control'
);
};
return ;
}
```
### Example: Priority-aware cursor management [#example-priority-aware-cursor-management]
```tsx
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import { useMapCursor } from '@accelint/map-toolkit/map-cursor';
import { useEffect } from 'react';
function ModeAwareCursor() {
const { mode } = useMapMode();
const { requestCursorChange, clearCursor } = useMapCursor();
useEffect(() => {
// Mode owners have priority
if (mode === 'editing') {
requestCursorChange('move', 'editing-mode');
} else {
clearCursor('editing-mode');
}
}, [mode, requestCursorChange, clearCursor]);
return null;
}
```
### Example: Listening to cursor rejections [#example-listening-to-cursor-rejections]
```tsx
import { useOn } from '@accelint/bus/react';
import { MapCursorEvents } from '@accelint/map-toolkit/map-cursor';
import type { CursorRejectedEvent } from '@accelint/map-toolkit/map-cursor';
function CursorRejectionLogger({ mapId }: { mapId: UniqueId }) {
useOn(MapCursorEvents.rejected, (event) => {
if (event.payload.id === mapId) {
console.warn(
`Cursor request rejected: ${event.payload.reason}`,
`Owner: ${event.payload.rejectedOwner}`
);
}
});
return null;
}
```
### Example: Conditional cursor in layer [#example-conditional-cursor-in-layer]
```tsx
import { useMapCursor } from '@accelint/map-toolkit/map-cursor';
import { IconLayer } from '@deck.gl/layers';
import type { PickingInfo } from '@deck.gl/core';
function IconMarkerLayer({ editable }: { editable: boolean }) {
const { requestCursorChange, clearCursor } = useMapCursor();
const handleHover = (info: PickingInfo) => {
if (!editable) return;
if (info.object) {
requestCursorChange('grab', 'icon-layer');
} else {
clearCursor('icon-layer');
}
};
return (
d.position}
getIcon={() => 'marker'}
/>
);
}
```
> **Good to know:** Cursor changes respect map mode ownership. If a mode owner sets a cursor, non-owners' requests will be rejected until the mode changes.
## Related [#related]
* [BaseMap](../deckgl/base-map/index.mdx) - Automatically creates MapProvider for cursor management
* [useMapMode](../map-mode/index.mdx) - Coordinate map modes with cursor ownership
* [@accelint/bus](../../../packages/bus/index.mdx) - Event bus used for cursor coordination
# Map Mode (/docs/toolkits/map-toolkit/map-mode)
## Usage [#usage]
```tsx
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import type { UniqueId } from '@accelint/core';
// Inside MapProvider (Deck.gl layer component)
function CustomLayer() {
const { mode, requestModeChange } = useMapMode();
return mode === 'editing' ? : ;
}
// Outside MapProvider (UI control)
function ModeControl({ mapId }: { mapId: UniqueId }) {
const { mode, requestModeChange } = useMapMode(mapId);
return (
);
}
```
## Reference [#reference]
### `useMapMode(id?: UniqueId): UseMapModeReturn` [#usemapmodeid-uniqueid-usemapmodereturn]
Hook to access the map mode state and actions. Uses `useSyncExternalStore` for concurrent-safe updates with a fan-out pattern where one bus listener per map notifies N React subscribers.
#### Parameters [#parameters]
* `id` (optional) - Map instance ID. If omitted, uses `MapContext` (requires being within `MapProvider`).
#### Returns [#returns]
```typescript
type UseMapModeReturn = {
mode: string;
requestModeChange: (desiredMode: string, requestOwner: string) => void;
};
```
* `mode` - Current active map mode string
* `requestModeChange` - Function to request mode change with ownership
#### Throws [#throws]
Error if no `id` is provided and hook is used outside `MapProvider`.
### Events [#events]
Map mode uses an event-driven approval flow for coordinating mode changes:
#### `MapModeEvents.changeRequest` [#mapmodeeventschangerequest]
Emitted when a component requests a mode change.
```typescript
type ModeChangeRequestPayload = {
desiredMode: string;
owner: string;
id: UniqueId;
};
```
#### `MapModeEvents.changeAuthorization` [#mapmodeeventschangeauthorization]
Sent to current mode owner to approve/reject the request.
```typescript
type ModeChangeAuthorizationPayload = {
authId: string;
desiredMode: string;
currentMode: string;
id: UniqueId;
};
```
#### `MapModeEvents.changeDecision` [#mapmodeeventschangedecision]
Response from current owner approving or rejecting the change.
```typescript
type ModeChangeDecisionPayload = {
authId: string;
approved: boolean;
owner: string;
reason?: string;
id: UniqueId;
};
```
#### `MapModeEvents.changed` [#mapmodeeventschanged]
Emitted when mode successfully changes.
```typescript
type ModeChangedPayload = {
previousMode: string;
currentMode: string;
id: UniqueId;
};
```
### Store Functions [#store-functions]
```typescript
// Get current mode and owner
getMode(id: UniqueId): string;
getCurrentModeOwner(id: UniqueId): string;
// Clear state for cleanup
clearMapModeState(id: UniqueId): void;
// Low-level store access
modeStore.useSelector(id: UniqueId, selector: (state) => T): T;
modeStore.actions(id: UniqueId): { requestModeChange };
```
## Examples [#examples]
### Example: Toolbar with multiple mode buttons [#example-toolbar-with-multiple-mode-buttons]
```tsx
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import type { UniqueId } from '@accelint/core';
function MapToolbar({ mapId }: { mapId: UniqueId }) {
const { mode, requestModeChange } = useMapMode(mapId);
const modes = [
{ id: 'default', label: 'Navigate' },
{ id: 'drawing', label: 'Draw' },
{ id: 'editing', label: 'Edit' },
{ id: 'measuring', label: 'Measure' },
];
return (
{modes.map((m) => (
))}
);
}
```
### Example: Conditionally rendering layer based on mode [#example-conditionally-rendering-layer-based-on-mode]
```tsx
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import { ScatterplotLayer } from '@deck.gl/layers';
function ConditionalLayer() {
const { mode } = useMapMode();
// Only render when in specific mode
if (mode !== 'analysis') {
return null;
}
return (
d.position}
getRadius={5}
/>
);
}
```
### Example: Listening to mode changes with event bus [#example-listening-to-mode-changes-with-event-bus]
```tsx
import { useOn } from '@accelint/bus/react';
import { MapModeEvents } from '@accelint/map-toolkit/map-mode';
import type { ModeChangedEvent } from '@accelint/map-toolkit/map-mode';
function ModeLogger({ mapId }: { mapId: UniqueId }) {
useOn(MapModeEvents.changed, (event) => {
if (event.payload.id === mapId) {
console.log(
`Mode changed: ${event.payload.previousMode} → ${event.payload.currentMode}`
);
}
});
return null;
}
```
### Example: Implementing approval logic [#example-implementing-approval-logic]
```tsx
import { useEffect } from 'react';
import { useOn, useEmit } from '@accelint/bus/react';
import { MapModeEvents } from '@accelint/map-toolkit/map-mode';
import type {
ModeChangeAuthorizationEvent,
ModeChangeDecisionPayload,
} from '@accelint/map-toolkit/map-mode';
function DrawingModeOwner({ mapId, ownerId }: Props) {
const emitDecision = useEmit(
MapModeEvents.changeDecision
);
useOn(
MapModeEvents.changeAuthorization,
(event) => {
if (event.payload.id !== mapId) return;
// Only respond if we own the current mode
if (event.payload.currentMode !== 'drawing') return;
// Check if we have unsaved changes
const hasUnsavedChanges = checkForUnsavedChanges();
if (hasUnsavedChanges) {
// Reject the mode change
emitDecision({
authId: event.payload.authId,
approved: false,
owner: ownerId,
reason: 'Unsaved changes in drawing mode',
id: mapId,
});
} else {
// Approve the mode change
emitDecision({
authId: event.payload.authId,
approved: true,
owner: ownerId,
id: mapId,
});
}
}
);
return null;
}
```
### Example: Multiple map instances [#example-multiple-map-instances]
```tsx
import { BaseMap } from '@accelint/map-toolkit/deckgl';
import { useMapMode } from '@accelint/map-toolkit/map-mode';
import { uuid } from '@accelint/core';
const MAP_1_ID = uuid();
const MAP_2_ID = uuid();
function SharedToolbar() {
const map1 = useMapMode(MAP_1_ID);
const map2 = useMapMode(MAP_2_ID);
return (
);
}
export function DualMapView() {
return (
<>
>
);
}
```
> **Good to know:** Each map instance maintains independent mode state. Use shared IDs at module level to coordinate between UI controls and map instances.
## Related [#related]
* [BaseMap](../deckgl/base-map/index.mdx) - Automatically creates MapProvider for mode management
* [useMapCursor](../map-cursor/index.mdx) - Manage cursor styles coordinated with map modes
* [@accelint/bus](../../../packages/bus/index.mdx) - Event bus used for mode coordination
# MapLibre (/docs/toolkits/map-toolkit/maplibre)
## Installation [#installation]
```bash
pnpm add @accelint/map-toolkit maplibre-gl
```
## Usage [#usage]
```typescript
// Import hook
import { useMapLibre } from '@accelint/map-toolkit/maplibre';
// Import RBZ handler
import { RbzHandler, DEFAULT_RBZ_STYLE } from '@accelint/map-toolkit/maplibre';
// Import constants
import { DEFAULT_VIEW_STATE } from '@accelint/map-toolkit/maplibre';
```
## Available Exports [#available-exports]
### Hooks [#hooks]
| Export | Description |
| ------------- | ----------------------------------------------------------- |
| `useMapLibre` | React hook for creating and managing a MapLibre GL instance |
### Handlers [#handlers]
| Export | Description |
| ------------ | -------------------------------------------------------------------------- |
| `RbzHandler` | Custom MapLibre handler for rubber band zoom (click-drag rectangular zoom) |
### Constants [#constants]
| Export | Description |
| -------------------- | ----------------------------------------------------- |
| `DEFAULT_VIEW_STATE` | Default map view state (center, zoom, pitch, bearing) |
| `DEFAULT_RBZ_STYLE` | Default styling for rubber band zoom rectangle |
### Types [#types]
| Export | Description |
| ----------------- | ---------------------------------------------- |
| `RbzOptions` | Configuration options for RbzHandler |
| `RbzStyleOptions` | Styling options for rubber band zoom rectangle |
| `RbzBuffer` | Buffer dimensions for zoom rectangle |
| `RbzOrigin` | Origin point for rubber band zoom |
## Quick Start [#quick-start]
### Basic MapLibre Integration [#basic-maplibre-integration]
```tsx
import { useMapLibre, DEFAULT_VIEW_STATE } from '@accelint/map-toolkit/maplibre';
import { useEffect, useRef } from 'react';
import 'maplibre-gl/dist/maplibre-gl.css';
export function SimpleMap() {
const containerRef = useRef(null);
const map = useMapLibre({
container: containerRef.current,
style: 'https://demotiles.maplibre.org/style.json',
...DEFAULT_VIEW_STATE,
});
useEffect(() => {
if (!map) return;
// Add custom layer or markers
map.on('load', () => {
map.addSource('my-data', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [],
},
});
});
}, [map]);
return ;
}
```
### Rubber Band Zoom [#rubber-band-zoom]
```tsx
import { useMapLibre, RbzHandler, DEFAULT_RBZ_STYLE } from '@accelint/map-toolkit/maplibre';
import { useEffect, useRef } from 'react';
import 'maplibre-gl/dist/maplibre-gl.css';
export function MapWithRbz() {
const containerRef = useRef(null);
const map = useMapLibre({
container: containerRef.current,
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
});
useEffect(() => {
if (!map) return;
// Add rubber band zoom handler
const rbzHandler = new RbzHandler({
map,
style: {
...DEFAULT_RBZ_STYLE,
fillColor: 'rgba(0, 123, 255, 0.1)',
strokeColor: 'rgba(0, 123, 255, 0.8)',
},
});
map.addHandler('rbz', rbzHandler);
return () => {
map.removeHandler('rbz');
};
}, [map]);
return (
Hold Shift + Click and drag to zoom to a rectangle
);
}
```
### With BaseMap Integration [#with-basemap-integration]
While BaseMap uses Deck.gl, MapLibre GL provides the base map tiles and 2D vector features:
```tsx
import { BaseMap } from '@accelint/map-toolkit/deckgl';
import { LIGHT_BASE_MAP_STYLE } from '@accelint/map-toolkit/deckgl';
import { uuid } from '@accelint/core';
const MAP_ID = uuid();
export function HybridMap() {
return (
);
}
```
## Architecture Notes [#architecture-notes]
* **BaseMap** internally uses MapLibre GL for base map tiles
* Deck.gl renders 3D visualizations on top of the MapLibre layer
* `useMapLibre` hook is for standalone MapLibre usage without Deck.gl
* `RbzHandler` can be added to either standalone MapLibre or BaseMap instances
## Related [#related]
* [deckgl](./deckgl/index.mdx) - Deck.gl integration with MapLibre
* [deckgl/base-map](./deckgl/base-map.mdx) - Combined Deck.gl + MapLibre component
* [MapLibre GL JS Documentation](https://maplibre.org/maplibre-gl-js-docs/api/) - Official MapLibre docs
# Viewport (/docs/toolkits/map-toolkit/viewport)
## viewportStore [#viewportstore]
Global store managing viewport state for all map instances. Automatically subscribes to `MapEvents.viewport` bus events.
### Usage [#usage]
```tsx
import { viewportStore } from '@accelint/map-toolkit/viewport';
function MapInfo({ mapId }: { mapId: string }) {
const { state } = viewportStore.use(mapId);
return (