@trackunit/react-map 0.2.217 → 0.2.219
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +113 -0
- package/index.esm.js +111 -3
- package/package.json +11 -11
- package/src/core/types.d.ts +10 -1
- package/src/draw/useDraw.d.ts +62 -0
- package/src/index.d.ts +3 -0
package/index.cjs.js
CHANGED
|
@@ -32,6 +32,10 @@ var defaultTranslations = {
|
|
|
32
32
|
"controls.stepper.decrease": "Decrease",
|
|
33
33
|
"controls.stepper.increase": "Increase",
|
|
34
34
|
"controls.zoom": "Zoom",
|
|
35
|
+
"draw.clear": "Clear drawing",
|
|
36
|
+
"draw.polygon": "Draw polygon",
|
|
37
|
+
"draw.select": "Edit polygon",
|
|
38
|
+
"draw.selectDisabled": "Draw a polygon first",
|
|
35
39
|
"map.loading": "Loading map",
|
|
36
40
|
"map.loadingData": "Loading map data",
|
|
37
41
|
"map.loadingErrorMessage": "Unable to load the map. Please try again later.",
|
|
@@ -1375,6 +1379,8 @@ const useMap = (adapterConfig) => {
|
|
|
1375
1379
|
setShowRoads: adapter.setShowRoads.bind(adapter),
|
|
1376
1380
|
// Optional capability: stays undefined for adapters without traffic support
|
|
1377
1381
|
setShowTraffic: adapter.setShowTraffic?.bind(adapter),
|
|
1382
|
+
// Optional capability: stays undefined for adapters without drawing support (ADR 0030)
|
|
1383
|
+
draw: adapter.draw ?? undefined,
|
|
1378
1384
|
}), [adapter]);
|
|
1379
1385
|
// Stable event subscription callback
|
|
1380
1386
|
const on = react.useMemo(() => adapter.on.bind(adapter), [adapter]);
|
|
@@ -3383,6 +3389,108 @@ const useControls = (base, areas) => {
|
|
|
3383
3389
|
}, [base, areas]);
|
|
3384
3390
|
};
|
|
3385
3391
|
|
|
3392
|
+
const noopUnsubscribe$1 = () => undefined;
|
|
3393
|
+
const subscribeNoop = () => noopUnsubscribe$1;
|
|
3394
|
+
const getIdle = () => "idle";
|
|
3395
|
+
const getEmpty = () => reactMapAdapterShared.EMPTY_DRAW_VALUE;
|
|
3396
|
+
/**
|
|
3397
|
+
* Feature-facing hook for the optional drawing capability (ADR 0030).
|
|
3398
|
+
*
|
|
3399
|
+
* Calling this hook is the consumer's opt-in: a map only offers drawing where a
|
|
3400
|
+
* component renders `controls` (typically appended to the `tools` area through
|
|
3401
|
+
* `useControls`). Feature code sees GeoJSON in and GeoJSON out; the engine behind
|
|
3402
|
+
* the adapter is never exposed.
|
|
3403
|
+
*
|
|
3404
|
+
* @example
|
|
3405
|
+
* ```tsx
|
|
3406
|
+
* const [Map, api] = useMap(adapter);
|
|
3407
|
+
* const draw = useDraw(api);
|
|
3408
|
+
* const controls = useControls(api, { tools: { append: draw.controls } });
|
|
3409
|
+
*
|
|
3410
|
+
* return (
|
|
3411
|
+
* <Map>
|
|
3412
|
+
* <Controls api={api} controls={controls} />
|
|
3413
|
+
* </Map>
|
|
3414
|
+
* );
|
|
3415
|
+
* ```
|
|
3416
|
+
*/
|
|
3417
|
+
const useDraw = (api, options) => {
|
|
3418
|
+
const [t] = useTranslation();
|
|
3419
|
+
const port = api.actions.draw;
|
|
3420
|
+
const enabled = options?.enabled ?? true;
|
|
3421
|
+
const isSupported = port !== undefined;
|
|
3422
|
+
// Call through the port object (never pass its methods around unbound): adapters
|
|
3423
|
+
// implement DrawPort as a class whose methods read instance state.
|
|
3424
|
+
const subscribe = react.useMemo(() => (port === undefined ? subscribeNoop : (listener) => port.subscribe(listener)), [port]);
|
|
3425
|
+
const getMode = react.useMemo(() => (port === undefined ? getIdle : () => port.getMode()), [port]);
|
|
3426
|
+
const getValue = react.useMemo(() => (port === undefined ? getEmpty : () => port.getValue()), [port]);
|
|
3427
|
+
const mode = react.useSyncExternalStore(subscribe, getMode, getIdle);
|
|
3428
|
+
const value = react.useSyncExternalStore(subscribe, getValue, getEmpty);
|
|
3429
|
+
// A disabled hook must not leave the map in a drawing mode it can no longer exit.
|
|
3430
|
+
react.useEffect(() => {
|
|
3431
|
+
if (!enabled)
|
|
3432
|
+
port?.setMode("idle");
|
|
3433
|
+
}, [enabled, port]);
|
|
3434
|
+
const setMode = react.useCallback((nextMode) => {
|
|
3435
|
+
port?.setMode(nextMode);
|
|
3436
|
+
}, [port]);
|
|
3437
|
+
const setValue = react.useCallback((nextValue) => {
|
|
3438
|
+
port?.setValue(nextValue);
|
|
3439
|
+
}, [port]);
|
|
3440
|
+
const clear = react.useCallback(() => {
|
|
3441
|
+
port?.setValue(reactMapAdapterShared.EMPTY_DRAW_VALUE);
|
|
3442
|
+
port?.setMode("idle");
|
|
3443
|
+
}, [port]);
|
|
3444
|
+
const hasPolygons = value.features.length > 0;
|
|
3445
|
+
const controls = react.useMemo(() => {
|
|
3446
|
+
if (!isSupported || !enabled)
|
|
3447
|
+
return [];
|
|
3448
|
+
const polygonToggle = {
|
|
3449
|
+
type: "toggle",
|
|
3450
|
+
id: "draw-polygon",
|
|
3451
|
+
icon: { on: "Pencil", off: "Pencil" },
|
|
3452
|
+
label: t("draw.polygon"),
|
|
3453
|
+
section: t("controls.category.tools"),
|
|
3454
|
+
value: mode === "polygon",
|
|
3455
|
+
onChange: on => setMode(on ? "polygon" : "idle"),
|
|
3456
|
+
"data-testid": "map-draw-polygon-toggle",
|
|
3457
|
+
};
|
|
3458
|
+
const selectToggle = {
|
|
3459
|
+
type: "toggle",
|
|
3460
|
+
id: "draw-select",
|
|
3461
|
+
icon: { on: "CursorArrowRays", off: "CursorArrowRays" },
|
|
3462
|
+
label: t("draw.select"),
|
|
3463
|
+
section: t("controls.category.tools"),
|
|
3464
|
+
value: mode === "select",
|
|
3465
|
+
onChange: on => setMode(on ? "select" : "idle"),
|
|
3466
|
+
// Never disable the active toggle: the user must always be able to switch it off.
|
|
3467
|
+
disabled: !hasPolygons && mode !== "select",
|
|
3468
|
+
disabledReason: hasPolygons || mode === "select" ? undefined : t("draw.selectDisabled"),
|
|
3469
|
+
"data-testid": "map-draw-select-toggle",
|
|
3470
|
+
};
|
|
3471
|
+
const clearButton = {
|
|
3472
|
+
type: "button",
|
|
3473
|
+
id: "draw-clear",
|
|
3474
|
+
icon: "Trash",
|
|
3475
|
+
label: t("draw.clear"),
|
|
3476
|
+
section: t("controls.category.tools"),
|
|
3477
|
+
onClick: clear,
|
|
3478
|
+
"data-testid": "map-draw-clear-button",
|
|
3479
|
+
};
|
|
3480
|
+
return [polygonToggle, selectToggle, clearButton];
|
|
3481
|
+
}, [clear, enabled, hasPolygons, isSupported, mode, setMode, t]);
|
|
3482
|
+
return react.useMemo(() => ({
|
|
3483
|
+
isSupported,
|
|
3484
|
+
isDrawing: mode !== "idle",
|
|
3485
|
+
mode,
|
|
3486
|
+
value,
|
|
3487
|
+
setMode,
|
|
3488
|
+
setValue,
|
|
3489
|
+
clear,
|
|
3490
|
+
controls,
|
|
3491
|
+
}), [clear, controls, isSupported, mode, setMode, setValue, value]);
|
|
3492
|
+
};
|
|
3493
|
+
|
|
3386
3494
|
/**
|
|
3387
3495
|
* Reads the persisted map appearance once (on mount) so it can seed the adapter
|
|
3388
3496
|
* factory, letting the FIRST map be constructed with the user's stored theme,
|
|
@@ -13355,6 +13463,10 @@ Object.defineProperty(exports, "DEFAULT_MAP_APPEARANCE", {
|
|
|
13355
13463
|
enumerable: true,
|
|
13356
13464
|
get: function () { return reactMapAdapterShared.DEFAULT_MAP_APPEARANCE; }
|
|
13357
13465
|
});
|
|
13466
|
+
Object.defineProperty(exports, "EMPTY_DRAW_VALUE", {
|
|
13467
|
+
enumerable: true,
|
|
13468
|
+
get: function () { return reactMapAdapterShared.EMPTY_DRAW_VALUE; }
|
|
13469
|
+
});
|
|
13358
13470
|
Object.defineProperty(exports, "HIT_SURFACE_SELECTOR", {
|
|
13359
13471
|
enumerable: true,
|
|
13360
13472
|
get: function () { return reactMapAdapterShared.HIT_SURFACE_SELECTOR; }
|
|
@@ -13430,6 +13542,7 @@ exports.useControlStack = useControlStack;
|
|
|
13430
13542
|
exports.useControls = useControls;
|
|
13431
13543
|
exports.useDefaultControls = useDefaultControls;
|
|
13432
13544
|
exports.useDirectionIndicator = useDirectionIndicator;
|
|
13545
|
+
exports.useDraw = useDraw;
|
|
13433
13546
|
exports.useEntitiesNearCursor = useEntitiesNearCursor;
|
|
13434
13547
|
exports.useExpandedIds = useExpandedIds;
|
|
13435
13548
|
exports.useFitFeatureBounds = useFitFeatureBounds;
|
package/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { useNamespaceTranslation, registerTranslations } from '@trackunit/i18n-library-translation';
|
|
3
|
-
import { INITIAL_CAMERA_STATE, INITIAL_MAP_STATUS, KEYBOARD_PAN_AMOUNT, KEYBOARD_ZOOM_AMOUNT, mapAppearanceSchema, DEFAULT_MAP_APPEARANCE, WORLD_BBOX, INITIAL_INTERACTION_STATE, mapThemeSchema, mergeAntimeridianFeatures, extractSourceData, computeMarkerDomPortalZIndex, densifyGeodesicFeatures, angularDistance, intermediatePoint, GEODESIC_MAX_SEGMENT_KM, geometryTypeToShapeType, resolveStrokeColors } from '@trackunit/react-map-adapter-shared';
|
|
4
|
-
export { ANCHOR_SELECTOR, DEFAULT_MAP_APPEARANCE, HIT_SURFACE_SELECTOR, INITIAL_CAMERA_STATE, INITIAL_INTERACTION_STATE, INITIAL_MAP_STATE, INITIAL_MAP_STATUS, computeMarkerDomPortalZIndex, defineAdapter, geometryTypeToShapeType, mapAppearanceSchema, mapThemeSchema, mapTypeSchema } from '@trackunit/react-map-adapter-shared';
|
|
3
|
+
import { INITIAL_CAMERA_STATE, INITIAL_MAP_STATUS, KEYBOARD_PAN_AMOUNT, KEYBOARD_ZOOM_AMOUNT, mapAppearanceSchema, DEFAULT_MAP_APPEARANCE, EMPTY_DRAW_VALUE, WORLD_BBOX, INITIAL_INTERACTION_STATE, mapThemeSchema, mergeAntimeridianFeatures, extractSourceData, computeMarkerDomPortalZIndex, densifyGeodesicFeatures, angularDistance, intermediatePoint, GEODESIC_MAX_SEGMENT_KM, geometryTypeToShapeType, resolveStrokeColors } from '@trackunit/react-map-adapter-shared';
|
|
4
|
+
export { ANCHOR_SELECTOR, DEFAULT_MAP_APPEARANCE, EMPTY_DRAW_VALUE, HIT_SURFACE_SELECTOR, INITIAL_CAMERA_STATE, INITIAL_INTERACTION_STATE, INITIAL_MAP_STATE, INITIAL_MAP_STATUS, computeMarkerDomPortalZIndex, defineAdapter, geometryTypeToShapeType, mapAppearanceSchema, mapThemeSchema, mapTypeSchema } from '@trackunit/react-map-adapter-shared';
|
|
5
5
|
import { useSyncExternalStore, useMemo, useState, useRef, useCallback, useLayoutEffect, useEffect, useReducer, createContext, useContext, memo, Suspense, useId, Fragment as Fragment$1, forwardRef } from 'react';
|
|
6
6
|
import { cva } from '@trackunit/css-class-variance-utilities';
|
|
7
7
|
import { Tooltip, useMergeRefs, Icon, Spinner, Text, useMeasure, useContainerBreakpoints, useDebounce, useWatch, useKeyboardShortcut, IconButton, ToggleGroup as ToggleGroup$1, useIsFullscreen, Popover, PopoverTrigger, PopoverContent, MoreMenu, useHover as useHover$1, cvaInteractableItem, useLocalStorageReducer } from '@trackunit/react-components';
|
|
@@ -31,6 +31,10 @@ var defaultTranslations = {
|
|
|
31
31
|
"controls.stepper.decrease": "Decrease",
|
|
32
32
|
"controls.stepper.increase": "Increase",
|
|
33
33
|
"controls.zoom": "Zoom",
|
|
34
|
+
"draw.clear": "Clear drawing",
|
|
35
|
+
"draw.polygon": "Draw polygon",
|
|
36
|
+
"draw.select": "Edit polygon",
|
|
37
|
+
"draw.selectDisabled": "Draw a polygon first",
|
|
34
38
|
"map.loading": "Loading map",
|
|
35
39
|
"map.loadingData": "Loading map data",
|
|
36
40
|
"map.loadingErrorMessage": "Unable to load the map. Please try again later.",
|
|
@@ -1374,6 +1378,8 @@ const useMap = (adapterConfig) => {
|
|
|
1374
1378
|
setShowRoads: adapter.setShowRoads.bind(adapter),
|
|
1375
1379
|
// Optional capability: stays undefined for adapters without traffic support
|
|
1376
1380
|
setShowTraffic: adapter.setShowTraffic?.bind(adapter),
|
|
1381
|
+
// Optional capability: stays undefined for adapters without drawing support (ADR 0030)
|
|
1382
|
+
draw: adapter.draw ?? undefined,
|
|
1377
1383
|
}), [adapter]);
|
|
1378
1384
|
// Stable event subscription callback
|
|
1379
1385
|
const on = useMemo(() => adapter.on.bind(adapter), [adapter]);
|
|
@@ -3382,6 +3388,108 @@ const useControls = (base, areas) => {
|
|
|
3382
3388
|
}, [base, areas]);
|
|
3383
3389
|
};
|
|
3384
3390
|
|
|
3391
|
+
const noopUnsubscribe$1 = () => undefined;
|
|
3392
|
+
const subscribeNoop = () => noopUnsubscribe$1;
|
|
3393
|
+
const getIdle = () => "idle";
|
|
3394
|
+
const getEmpty = () => EMPTY_DRAW_VALUE;
|
|
3395
|
+
/**
|
|
3396
|
+
* Feature-facing hook for the optional drawing capability (ADR 0030).
|
|
3397
|
+
*
|
|
3398
|
+
* Calling this hook is the consumer's opt-in: a map only offers drawing where a
|
|
3399
|
+
* component renders `controls` (typically appended to the `tools` area through
|
|
3400
|
+
* `useControls`). Feature code sees GeoJSON in and GeoJSON out; the engine behind
|
|
3401
|
+
* the adapter is never exposed.
|
|
3402
|
+
*
|
|
3403
|
+
* @example
|
|
3404
|
+
* ```tsx
|
|
3405
|
+
* const [Map, api] = useMap(adapter);
|
|
3406
|
+
* const draw = useDraw(api);
|
|
3407
|
+
* const controls = useControls(api, { tools: { append: draw.controls } });
|
|
3408
|
+
*
|
|
3409
|
+
* return (
|
|
3410
|
+
* <Map>
|
|
3411
|
+
* <Controls api={api} controls={controls} />
|
|
3412
|
+
* </Map>
|
|
3413
|
+
* );
|
|
3414
|
+
* ```
|
|
3415
|
+
*/
|
|
3416
|
+
const useDraw = (api, options) => {
|
|
3417
|
+
const [t] = useTranslation();
|
|
3418
|
+
const port = api.actions.draw;
|
|
3419
|
+
const enabled = options?.enabled ?? true;
|
|
3420
|
+
const isSupported = port !== undefined;
|
|
3421
|
+
// Call through the port object (never pass its methods around unbound): adapters
|
|
3422
|
+
// implement DrawPort as a class whose methods read instance state.
|
|
3423
|
+
const subscribe = useMemo(() => (port === undefined ? subscribeNoop : (listener) => port.subscribe(listener)), [port]);
|
|
3424
|
+
const getMode = useMemo(() => (port === undefined ? getIdle : () => port.getMode()), [port]);
|
|
3425
|
+
const getValue = useMemo(() => (port === undefined ? getEmpty : () => port.getValue()), [port]);
|
|
3426
|
+
const mode = useSyncExternalStore(subscribe, getMode, getIdle);
|
|
3427
|
+
const value = useSyncExternalStore(subscribe, getValue, getEmpty);
|
|
3428
|
+
// A disabled hook must not leave the map in a drawing mode it can no longer exit.
|
|
3429
|
+
useEffect(() => {
|
|
3430
|
+
if (!enabled)
|
|
3431
|
+
port?.setMode("idle");
|
|
3432
|
+
}, [enabled, port]);
|
|
3433
|
+
const setMode = useCallback((nextMode) => {
|
|
3434
|
+
port?.setMode(nextMode);
|
|
3435
|
+
}, [port]);
|
|
3436
|
+
const setValue = useCallback((nextValue) => {
|
|
3437
|
+
port?.setValue(nextValue);
|
|
3438
|
+
}, [port]);
|
|
3439
|
+
const clear = useCallback(() => {
|
|
3440
|
+
port?.setValue(EMPTY_DRAW_VALUE);
|
|
3441
|
+
port?.setMode("idle");
|
|
3442
|
+
}, [port]);
|
|
3443
|
+
const hasPolygons = value.features.length > 0;
|
|
3444
|
+
const controls = useMemo(() => {
|
|
3445
|
+
if (!isSupported || !enabled)
|
|
3446
|
+
return [];
|
|
3447
|
+
const polygonToggle = {
|
|
3448
|
+
type: "toggle",
|
|
3449
|
+
id: "draw-polygon",
|
|
3450
|
+
icon: { on: "Pencil", off: "Pencil" },
|
|
3451
|
+
label: t("draw.polygon"),
|
|
3452
|
+
section: t("controls.category.tools"),
|
|
3453
|
+
value: mode === "polygon",
|
|
3454
|
+
onChange: on => setMode(on ? "polygon" : "idle"),
|
|
3455
|
+
"data-testid": "map-draw-polygon-toggle",
|
|
3456
|
+
};
|
|
3457
|
+
const selectToggle = {
|
|
3458
|
+
type: "toggle",
|
|
3459
|
+
id: "draw-select",
|
|
3460
|
+
icon: { on: "CursorArrowRays", off: "CursorArrowRays" },
|
|
3461
|
+
label: t("draw.select"),
|
|
3462
|
+
section: t("controls.category.tools"),
|
|
3463
|
+
value: mode === "select",
|
|
3464
|
+
onChange: on => setMode(on ? "select" : "idle"),
|
|
3465
|
+
// Never disable the active toggle: the user must always be able to switch it off.
|
|
3466
|
+
disabled: !hasPolygons && mode !== "select",
|
|
3467
|
+
disabledReason: hasPolygons || mode === "select" ? undefined : t("draw.selectDisabled"),
|
|
3468
|
+
"data-testid": "map-draw-select-toggle",
|
|
3469
|
+
};
|
|
3470
|
+
const clearButton = {
|
|
3471
|
+
type: "button",
|
|
3472
|
+
id: "draw-clear",
|
|
3473
|
+
icon: "Trash",
|
|
3474
|
+
label: t("draw.clear"),
|
|
3475
|
+
section: t("controls.category.tools"),
|
|
3476
|
+
onClick: clear,
|
|
3477
|
+
"data-testid": "map-draw-clear-button",
|
|
3478
|
+
};
|
|
3479
|
+
return [polygonToggle, selectToggle, clearButton];
|
|
3480
|
+
}, [clear, enabled, hasPolygons, isSupported, mode, setMode, t]);
|
|
3481
|
+
return useMemo(() => ({
|
|
3482
|
+
isSupported,
|
|
3483
|
+
isDrawing: mode !== "idle",
|
|
3484
|
+
mode,
|
|
3485
|
+
value,
|
|
3486
|
+
setMode,
|
|
3487
|
+
setValue,
|
|
3488
|
+
clear,
|
|
3489
|
+
controls,
|
|
3490
|
+
}), [clear, controls, isSupported, mode, setMode, setValue, value]);
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3385
3493
|
/**
|
|
3386
3494
|
* Reads the persisted map appearance once (on mount) so it can seed the adapter
|
|
3387
3495
|
* factory, letting the FIRST map be constructed with the user's stored theme,
|
|
@@ -13346,4 +13454,4 @@ const mockMapApi = (overrides) => {
|
|
|
13346
13454
|
*/
|
|
13347
13455
|
setupLibraryTranslations();
|
|
13348
13456
|
|
|
13349
|
-
export { ClusterMarker, ClusterStick, Controls, DEFAULT_APPEARANCE_PERSISTENCE_KEY, DEFAULT_MARKER_SIZE_BREAKPOINTS, DefaultControls, Layers, MARKER_DARK_PILL, MARKER_DISC_BORDER_WIDTH_PX, MARKER_LIGHT_PILL, MARKER_PILL_CONTENT_LAYOUT_SIZE, MARKER_SIZE_MAP, MARKER_TUNING, MapLoadingState, MapMarker, MapMarkerIcon, PanelIconButton, ShapeAnnotationLabel, buildExpandedIds, cvaMapMarker, cvaMarkerIndicator, mockMapApi, useAdaptiveMarkerHelpers, useAutoPanResolver, useCameraIdle, useCameraState, useClusterCountFormat, useControlStack, useControls, useDefaultControls, useDirectionIndicator, useEntitiesNearCursor, useExpandedIds, useFitFeatureBounds, useFitToContent, useHeatmap, useImageOverlay, useLabelPlacement, useLayers, useMap, useMapAnnotation, useMapAnnotations, useMapAppearanceControls, useMapKeyboardNavigation, useMarkerColors, useMarkerStateResolvers, useMarkers, usePanel, usePanelPreload, usePersistedAppearanceSeed, usePreviewMap, useRoute, useShapeLabelHelpers, useShapes, useViewportContext };
|
|
13457
|
+
export { ClusterMarker, ClusterStick, Controls, DEFAULT_APPEARANCE_PERSISTENCE_KEY, DEFAULT_MARKER_SIZE_BREAKPOINTS, DefaultControls, Layers, MARKER_DARK_PILL, MARKER_DISC_BORDER_WIDTH_PX, MARKER_LIGHT_PILL, MARKER_PILL_CONTENT_LAYOUT_SIZE, MARKER_SIZE_MAP, MARKER_TUNING, MapLoadingState, MapMarker, MapMarkerIcon, PanelIconButton, ShapeAnnotationLabel, buildExpandedIds, cvaMapMarker, cvaMarkerIndicator, mockMapApi, useAdaptiveMarkerHelpers, useAutoPanResolver, useCameraIdle, useCameraState, useClusterCountFormat, useControlStack, useControls, useDefaultControls, useDirectionIndicator, useDraw, useEntitiesNearCursor, useExpandedIds, useFitFeatureBounds, useFitToContent, useHeatmap, useImageOverlay, useLabelPlacement, useLayers, useMap, useMapAnnotation, useMapAnnotations, useMapAppearanceControls, useMapKeyboardNavigation, useMarkerColors, useMarkerStateResolvers, useMarkers, usePanel, usePanelPreload, usePersistedAppearanceSeed, usePreviewMap, useRoute, useShapeLabelHelpers, useShapes, useViewportContext };
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-map",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.219",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/react-components": "3.
|
|
11
|
-
"@trackunit/css-class-variance-utilities": "2.
|
|
12
|
-
"@trackunit/react-form-components": "2.
|
|
13
|
-
"@trackunit/react-core-hooks": "1.
|
|
14
|
-
"@trackunit/geo-json-utils": "1.
|
|
15
|
-
"@trackunit/i18n-library-translation": "2.
|
|
10
|
+
"@trackunit/react-components": "3.9.0",
|
|
11
|
+
"@trackunit/css-class-variance-utilities": "2.4.0",
|
|
12
|
+
"@trackunit/react-form-components": "2.11.0",
|
|
13
|
+
"@trackunit/react-core-hooks": "1.26.0",
|
|
14
|
+
"@trackunit/geo-json-utils": "1.19.0",
|
|
15
|
+
"@trackunit/i18n-library-translation": "2.9.0",
|
|
16
16
|
"react-minimal-pie-chart": "^8.4.0",
|
|
17
|
-
"@trackunit/react-map-adapter-shared": "0.0.
|
|
18
|
-
"@trackunit/react-map-color-utils": "0.0.
|
|
19
|
-
"@trackunit/shared-utils": "1.
|
|
20
|
-
"@trackunit/ui-design-tokens": "1.
|
|
17
|
+
"@trackunit/react-map-adapter-shared": "0.0.164",
|
|
18
|
+
"@trackunit/react-map-color-utils": "0.0.138",
|
|
19
|
+
"@trackunit/shared-utils": "1.20.0",
|
|
20
|
+
"@trackunit/ui-design-tokens": "1.19.0",
|
|
21
21
|
"@floating-ui/react": "^0.26.25",
|
|
22
22
|
"es-toolkit": "^1.52.0",
|
|
23
23
|
"tailwind-merge": "^2.0.0",
|
package/src/core/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DEFAULT_MAP_APPEARANCE, INITIAL_CAMERA_STATE, INITIAL_MAP_STATE, INITIAL_MAP_STATUS, mapAppearanceSchema, mapThemeSchema, mapTypeSchema, } from "@trackunit/react-map-adapter-shared";
|
|
2
2
|
export type { CameraState, ContainerRefHolder, FitBoundsOptions, GeoJsonBbox, GeoJsonPosition, InitialViewport, MapAppearance, MapComponentProps, MapEvent, MapEventHandler, MapEventOfType, MapState, MapStatus, MapTheme, MapType, StoredEventHandler, } from "@trackunit/react-map-adapter-shared";
|
|
3
|
-
import type { AdapterConfig, CameraState, ContainerRefHolder, FitBoundsOptions, GeoJsonBbox, GeoJsonPosition, MapComponentProps, MapEvent, MapEventHandler, MapStatus, MapTheme, MapType } from "@trackunit/react-map-adapter-shared";
|
|
3
|
+
import type { AdapterConfig, CameraState, ContainerRefHolder, DrawPort, FitBoundsOptions, GeoJsonBbox, GeoJsonPosition, MapComponentProps, MapEvent, MapEventHandler, MapStatus, MapTheme, MapType } from "@trackunit/react-map-adapter-shared";
|
|
4
4
|
import type { ComponentType } from "react";
|
|
5
5
|
import type { MapAnnotationStore } from "../annotations/mapAnnotations";
|
|
6
6
|
import type { PanelStore } from "../panel/store/panels";
|
|
@@ -92,6 +92,15 @@ export type MapActions = Readonly<{
|
|
|
92
92
|
* @returns Promise that resolves when the change is applied
|
|
93
93
|
*/
|
|
94
94
|
setShowTraffic?: (showTraffic: boolean) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Interactive polygon drawing (ADR 0030).
|
|
97
|
+
*
|
|
98
|
+
* Undefined when the adapter cannot draw — `useDraw` reports `isSupported: false`
|
|
99
|
+
* and renders no controls in that case. The port is a controlled input: set
|
|
100
|
+
* `mode` and `value`, subscribe for user edits. Prefer `useDraw(api)` from React
|
|
101
|
+
* components; this is the lower-level handle.
|
|
102
|
+
*/
|
|
103
|
+
draw?: DrawPort;
|
|
95
104
|
}>;
|
|
96
105
|
/**
|
|
97
106
|
* The API object returned as the second element of the useMap tuple.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type DrawMode, type DrawValue } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import type { ControlConfig } from "../controls/types";
|
|
3
|
+
import type { MapApi } from "../core/types";
|
|
4
|
+
/** Minimal slice of `MapApi` consumed by `useDraw`. */
|
|
5
|
+
type DrawApi = Readonly<{
|
|
6
|
+
actions: Pick<MapApi["actions"], "draw">;
|
|
7
|
+
}>;
|
|
8
|
+
export type UseDrawOptions = Readonly<{
|
|
9
|
+
/**
|
|
10
|
+
* Hide the draw controls and force the map back to `"idle"` without unmounting
|
|
11
|
+
* the hook, for example while a feature flag is off. Default `true`.
|
|
12
|
+
*/
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
export type UseDrawReturn = Readonly<{
|
|
16
|
+
/** False when the adapter has no drawing capability; `controls` is then empty. */
|
|
17
|
+
isSupported: boolean;
|
|
18
|
+
/** True while `mode` is not `"idle"`. Same signal as `api.state.drawing`. */
|
|
19
|
+
isDrawing: boolean;
|
|
20
|
+
/** Current drawing mode. */
|
|
21
|
+
mode: DrawMode;
|
|
22
|
+
/**
|
|
23
|
+
* Committed polygons as GeoJSON. Updates when the user finishes, edits or deletes a
|
|
24
|
+
* polygon, and when `setValue` is called.
|
|
25
|
+
*/
|
|
26
|
+
value: DrawValue;
|
|
27
|
+
/** Enter a drawing mode; `"idle"` ends drawing. */
|
|
28
|
+
setMode: (mode: DrawMode) => void;
|
|
29
|
+
/** Replace the committed polygons (controlled-input style). */
|
|
30
|
+
setValue: (value: DrawValue) => void;
|
|
31
|
+
/** Remove every polygon and return to `"idle"`. */
|
|
32
|
+
clear: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* Ready-made controls for the `tools` area: a "Draw polygon" toggle, an "Edit
|
|
35
|
+
* polygon" toggle and a "Clear drawing" button. Empty when drawing is unsupported
|
|
36
|
+
* or disabled.
|
|
37
|
+
*/
|
|
38
|
+
controls: ReadonlyArray<ControlConfig>;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Feature-facing hook for the optional drawing capability (ADR 0030).
|
|
42
|
+
*
|
|
43
|
+
* Calling this hook is the consumer's opt-in: a map only offers drawing where a
|
|
44
|
+
* component renders `controls` (typically appended to the `tools` area through
|
|
45
|
+
* `useControls`). Feature code sees GeoJSON in and GeoJSON out; the engine behind
|
|
46
|
+
* the adapter is never exposed.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* const [Map, api] = useMap(adapter);
|
|
51
|
+
* const draw = useDraw(api);
|
|
52
|
+
* const controls = useControls(api, { tools: { append: draw.controls } });
|
|
53
|
+
*
|
|
54
|
+
* return (
|
|
55
|
+
* <Map>
|
|
56
|
+
* <Controls api={api} controls={controls} />
|
|
57
|
+
* </Map>
|
|
58
|
+
* );
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare const useDraw: (api: DrawApi, options?: UseDrawOptions) => UseDrawReturn;
|
|
62
|
+
export {};
|
package/src/index.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ export type { ControlStackItem } from "./controls/defineControlStack";
|
|
|
16
16
|
export type { UseControlsAreas } from "./controls/types";
|
|
17
17
|
export { useControls } from "./controls/useControls";
|
|
18
18
|
export { useDefaultControls, type UseDefaultControlsOptions } from "./controls/useDefaultControls";
|
|
19
|
+
export { EMPTY_DRAW_VALUE } from "@trackunit/react-map-adapter-shared";
|
|
20
|
+
export type { DrawMode, DrawPort, DrawValue } from "@trackunit/react-map-adapter-shared";
|
|
21
|
+
export { useDraw, type UseDrawOptions, type UseDrawReturn } from "./draw/useDraw";
|
|
19
22
|
export { DEFAULT_APPEARANCE_PERSISTENCE_KEY } from "./appearance/appearanceTypes";
|
|
20
23
|
export type { AppearanceAction, AppearanceOption, AppearanceOptions, MapAppearanceApi, PersistedAppearance, UseMapAppearanceControlsParams, } from "./appearance/appearanceTypes";
|
|
21
24
|
export { useMapAppearanceControls } from "./appearance/useMapAppearanceControls";
|