@accelint/map-toolkit 0.5.0 → 1.0.0
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/CHANGELOG.md +82 -0
- package/catalog-info.yaml +6 -3
- package/dist/camera/index.d.ts +2 -2
- package/dist/camera/index.js +2 -2
- package/dist/camera/store.d.ts +120 -0
- package/dist/camera/store.js +279 -0
- package/dist/camera/store.js.map +1 -0
- package/dist/deckgl/base-map/constants.d.ts +9 -7
- package/dist/deckgl/base-map/constants.js +9 -7
- package/dist/deckgl/base-map/constants.js.map +1 -1
- package/dist/deckgl/base-map/controls.js +2 -0
- package/dist/deckgl/base-map/controls.js.map +1 -1
- package/dist/deckgl/base-map/index.d.ts +2 -0
- package/dist/deckgl/base-map/index.js +14 -16
- package/dist/deckgl/base-map/index.js.map +1 -1
- package/dist/deckgl/base-map/provider.js +1 -1
- package/dist/deckgl/base-map/types.d.ts +6 -1
- package/dist/deckgl/index.d.ts +8 -2
- package/dist/deckgl/index.js +6 -2
- package/dist/deckgl/saved-viewports/index.d.ts +1 -1
- package/dist/deckgl/saved-viewports/index.js +1 -2
- package/dist/deckgl/saved-viewports/index.js.map +1 -1
- package/dist/deckgl/saved-viewports/storage.js +10 -2
- package/dist/deckgl/saved-viewports/storage.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +58 -0
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -0
- package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js → deckgl/shapes/display-shape-layer/fiber.d.ts} +11 -7
- package/dist/{packages/hotkey-manager/dist/lib/is-client/index.js → deckgl/shapes/display-shape-layer/fiber.js} +6 -7
- package/dist/deckgl/shapes/display-shape-layer/fiber.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/index.d.ts +210 -0
- package/dist/deckgl/shapes/display-shape-layer/index.js +449 -0
- package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js +102 -0
- package/dist/deckgl/shapes/display-shape-layer/shape-label-layer.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/store.js +102 -0
- package/dist/deckgl/shapes/display-shape-layer/store.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/types.d.ts +126 -0
- package/dist/{node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/layer.js → deckgl/shapes/display-shape-layer/types.js} +0 -3
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.d.ts +89 -0
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js +88 -0
- package/dist/deckgl/shapes/display-shape-layer/use-select-shape.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js +50 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/display-style.js.map +1 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.d.ts +133 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js +250 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/labels.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/constants.js +46 -0
- package/dist/deckgl/shapes/draw-shape-layer/constants.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/events.d.ts +92 -0
- package/dist/deckgl/shapes/draw-shape-layer/events.js +56 -0
- package/dist/deckgl/shapes/draw-shape-layer/events.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/fiber.d.ts +11 -0
- package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js → deckgl/shapes/draw-shape-layer/fiber.js} +5 -4
- package/dist/deckgl/shapes/draw-shape-layer/fiber.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +53 -0
- package/dist/deckgl/shapes/draw-shape-layer/index.js +95 -0
- package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +51 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +73 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +87 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +88 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +77 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js +64 -0
- package/dist/deckgl/shapes/draw-shape-layer/modes/index.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/store.js +175 -0
- package/dist/deckgl/shapes/draw-shape-layer/store.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +86 -0
- package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fly-to-viewport.js → deckgl/shapes/draw-shape-layer/types.js} +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.d.ts +82 -0
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js +112 -0
- package/dist/deckgl/shapes/draw-shape-layer/use-draw-shape.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js +147 -0
- package/dist/deckgl/shapes/draw-shape-layer/utils/feature-conversion.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/constants.js +41 -0
- package/dist/deckgl/shapes/edit-shape-layer/constants.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/events.d.ts +92 -0
- package/dist/deckgl/shapes/edit-shape-layer/events.js +56 -0
- package/dist/deckgl/shapes/edit-shape-layer/events.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/fiber.d.ts +13 -0
- package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-viewport.js → deckgl/shapes/edit-shape-layer/fiber.js} +1 -3
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +63 -0
- package/dist/deckgl/shapes/edit-shape-layer/index.js +162 -0
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js +154 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/base-transform-mode.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +147 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +87 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js +61 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/index.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js +109 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/rotate-mode-with-snap.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js +289 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/scale-mode-with-free-transform.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js +121 -0
- package/dist/deckgl/shapes/edit-shape-layer/modes/vertex-transform-mode.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/store.js +194 -0
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -0
- package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +93 -0
- package/dist/{node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/normalize-viewport-props.js → deckgl/shapes/edit-shape-layer/types.js} +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +82 -0
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +114 -0
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js.map +1 -0
- package/dist/deckgl/shapes/index.d.ts +29 -0
- package/dist/deckgl/shapes/index.js +27 -0
- package/dist/deckgl/shapes/shared/constants.d.ts +73 -0
- package/dist/deckgl/shapes/shared/constants.js +273 -0
- package/dist/deckgl/shapes/shared/constants.js.map +1 -0
- package/dist/deckgl/shapes/shared/events.d.ts +54 -0
- package/dist/{packages/hotkey-manager/dist/lib/is-mac/index.js → deckgl/shapes/shared/events.js} +10 -6
- package/dist/deckgl/shapes/shared/events.js.map +1 -0
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js +84 -0
- package/dist/deckgl/shapes/shared/hooks/use-shift-zoom-disable.js.map +1 -0
- package/dist/deckgl/shapes/shared/types.d.ts +317 -0
- package/dist/deckgl/shapes/shared/types.js +83 -0
- package/dist/deckgl/shapes/shared/types.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js +128 -0
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/layer-config.js +50 -0
- package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/mode-utils.js +113 -0
- package/dist/deckgl/shapes/shared/utils/mode-utils.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js +57 -0
- package/dist/deckgl/shapes/shared/utils/pick-filtering.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/style-utils.d.ts +64 -0
- package/dist/deckgl/shapes/shared/utils/style-utils.js +101 -0
- package/dist/deckgl/shapes/shared/utils/style-utils.js.map +1 -0
- package/dist/deckgl/symbol-layer/index.d.ts +1 -1
- package/dist/deckgl/text-layer/default-settings.js +4 -24
- package/dist/deckgl/text-layer/default-settings.js.map +1 -1
- package/dist/deckgl/text-settings.d.ts +77 -0
- package/dist/deckgl/text-settings.js +83 -0
- package/dist/deckgl/text-settings.js.map +1 -0
- package/dist/map-cursor/index.d.ts +2 -2
- package/dist/map-cursor/index.js +2 -2
- package/dist/map-cursor/store.d.ts +32 -61
- package/dist/map-cursor/store.js +165 -294
- package/dist/map-cursor/store.js.map +1 -1
- package/dist/map-cursor/use-map-cursor.d.ts +5 -2
- package/dist/map-cursor/use-map-cursor.js +33 -15
- package/dist/map-cursor/use-map-cursor.js.map +1 -1
- package/dist/map-mode/index.d.ts +2 -2
- package/dist/map-mode/index.js +2 -2
- package/dist/map-mode/store.d.ts +36 -37
- package/dist/map-mode/store.js +131 -237
- package/dist/map-mode/store.js.map +1 -1
- package/dist/map-mode/use-map-mode.js +6 -5
- package/dist/map-mode/use-map-mode.js.map +1 -1
- package/dist/maplibre/index.d.ts +2 -2
- package/dist/maplibre/index.js +2 -2
- package/dist/shared/constants.d.ts +19 -0
- package/dist/{maplibre → shared}/constants.js +9 -3
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/create-map-store.d.ts +202 -0
- package/dist/shared/create-map-store.js +223 -0
- package/dist/shared/create-map-store.js.map +1 -0
- package/dist/shared/units.d.ts +39 -0
- package/dist/shared/units.js +49 -0
- package/dist/shared/units.js.map +1 -0
- package/dist/viewport/index.d.ts +3 -3
- package/dist/viewport/index.js +3 -3
- package/dist/viewport/store.d.ts +69 -0
- package/dist/viewport/store.js +125 -0
- package/dist/viewport/store.js.map +1 -0
- package/dist/viewport/types.d.ts +2 -2
- package/dist/viewport/utils.js +2 -2
- package/dist/viewport/utils.js.map +1 -1
- package/dist/viewport/viewport-size.js +2 -2
- package/dist/viewport/viewport-size.js.map +1 -1
- package/package.json +60 -26
- package/dist/_virtual/rolldown_runtime.js +0 -22
- package/dist/camera/use-camera-state.d.ts +0 -153
- package/dist/camera/use-camera-state.js +0 -419
- package/dist/camera/use-camera-state.js.map +0 -1
- package/dist/decorators/deckgl.d.ts +0 -19
- package/dist/decorators/deckgl.js +0 -32
- package/dist/decorators/deckgl.js.map +0 -1
- package/dist/maplibre/constants.d.ts +0 -13
- package/dist/maplibre/constants.js.map +0 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/assert.js.map +0 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js +0 -63
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/fit-bounds.js.map +0 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/get-bounds.js.map +0 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/index.js +0 -19
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js +0 -25
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/math-utils.js.map +0 -1
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js +0 -59
- package/dist/node_modules/.pnpm/@math.gl_web-mercator@4.1.0/node_modules/@math.gl/web-mercator/dist/web-mercator-utils.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js +0 -29
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/attribution-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js +0 -29
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/fullscreen-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js +0 -54
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/geolocate-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js +0 -29
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/logo-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js +0 -91
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/map.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js +0 -88
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/marker.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js +0 -29
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/navigation-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js +0 -69
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/popup.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js +0 -35
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/scale-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/source.js +0 -15
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js +0 -29
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/terrain-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js +0 -40
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-control.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js +0 -23
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/components/use-map.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/index.js +0 -27
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js +0 -57
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/create-ref.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js +0 -343
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/maplibre/maplibre.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js +0 -28
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/apply-react-style.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js +0 -31
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/compare-class-names.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js +0 -57
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/deep-equal.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js +0 -30
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/set-globals.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js +0 -53
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/style-utils.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js +0 -52
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/transform.js.map +0 -1
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js +0 -22
- package/dist/node_modules/.pnpm/@vis.gl_react-maplibre@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@vis.gl/react-maplibre/dist/utils/use-isomorphic-layout-effect.js.map +0 -1
- package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js +0 -812
- package/dist/node_modules/.pnpm/immer@10.2.0/node_modules/immer/dist/immer.js.map +0 -1
- package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js +0 -35
- package/dist/node_modules/.pnpm/radashi@12.7.1/node_modules/radashi/dist/radashi.js.map +0 -1
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js +0 -195
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.development.js.map +0 -1
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js +0 -76
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/cjs/react-dom.production.js.map +0 -1
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js +0 -39
- package/dist/node_modules/.pnpm/react-dom@19.2.3_react@19.2.3/node_modules/react-dom/index.js.map +0 -1
- package/dist/node_modules/.pnpm/react-map-gl@8.1.0_maplibre-gl@5.15.0_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/react-map-gl/dist/maplibre.js +0 -16
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js +0 -27
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/middleware/immer.js.map +0 -1
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js +0 -45
- package/dist/node_modules/.pnpm/zustand@5.0.9_@types_react@19.2.7_immer@10.2.0_react@19.2.3_use-sync-external-store@1.6.0_react@19.2.3_/node_modules/zustand/esm/vanilla.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js +0 -78
- package/dist/packages/hotkey-manager/dist/actions/register-hotkey/index.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/constants.js +0 -47
- package/dist/packages/hotkey-manager/dist/constants.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/enums/keycode.js +0 -130
- package/dist/packages/hotkey-manager/dist/enums/keycode.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/lib/is-client/index.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/lib/is-mac/index.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js +0 -39
- package/dist/packages/hotkey-manager/dist/lib/key-to-id/index.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js +0 -27
- package/dist/packages/hotkey-manager/dist/lib/key-to-string/index.js.map +0 -1
- package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js +0 -95
- package/dist/packages/hotkey-manager/dist/stores/hotkey-store/index.js.map +0 -1
- package/dist/viewport/constants.d.ts +0 -11
- package/dist/viewport/constants.js +0 -25
- package/dist/viewport/constants.js.map +0 -1
- package/dist/viewport/use-viewport-state.d.ts +0 -100
- package/dist/viewport/use-viewport-state.js +0 -222
- package/dist/viewport/use-viewport-state.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["features: Shape['feature'][]"],"sources":["../../../../src/deckgl/shapes/display-shape-layer/index.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { Broadcast } from '@accelint/bus';\nimport { getLogger } from '@accelint/logger';\nimport { CompositeLayer } from '@deck.gl/core';\nimport { PathStyleExtension } from '@deck.gl/extensions';\nimport { GeoJsonLayer, IconLayer } from '@deck.gl/layers';\nimport { DASH_ARRAYS, SHAPE_LAYER_IDS } from '../shared/constants';\nimport { type ShapeEvent, ShapeEvents } from '../shared/events';\nimport {\n getDashArray,\n getFillColor,\n getLineColor,\n} from '../shared/utils/style-utils';\nimport {\n COFFIN_CORNERS,\n DEFAULT_DISPLAY_PROPS,\n MAP_INTERACTION,\n} from './constants';\nimport { createShapeLabelLayer } from './shape-label-layer';\nimport {\n getHighlightColor,\n getHighlightLineWidth,\n getHoverLineWidth,\n} from './utils/display-style';\nimport type { Layer, PickingInfo } from '@deck.gl/core';\nimport type { Shape, ShapeId } from '../shared/types';\nimport type { DisplayShapeLayerProps } from './types';\n\nconst logger = getLogger({\n enabled: process.env.NODE_ENV !== 'production',\n level: 'warn',\n prefix: '[DisplayShapeLayer]',\n pretty: true,\n});\n\n/**\n * Typed event bus instance for shape events.\n * Provides type-safe event emission for shape interactions.\n */\nconst shapeBus = Broadcast.getInstance<ShapeEvent>();\n\n/**\n * State type for DisplayShapeLayer\n */\ntype DisplayShapeLayerState = {\n /** Index of currently hovered shape, undefined when not hovering */\n hoverIndex?: number;\n /** ID of the last hovered shape for event deduplication */\n lastHoveredId?: ShapeId | null;\n /** Allow additional properties from base layer state */\n [key: string]: unknown;\n};\n\n/**\n * Cache for transformed features to avoid recreating objects on every render.\n */\ntype FeaturesCache = {\n /** Reference to the original data array for identity comparison */\n data: Shape[];\n /** Transformed features with shapeId added to properties */\n features: Shape['feature'][];\n /** Map of shapeId to feature index for O(1) lookup */\n shapeIdToIndex: Map<ShapeId, number>;\n};\n\n/**\n * DisplayShapeLayer - Read-only shapes visualization layer\n *\n * A composite deck.gl layer for displaying geographic shapes with interactive features.\n * Ideal for rendering shapes from external APIs or displaying read-only geographic data.\n *\n * ## Features\n * - **Multiple geometry types**: Point, LineString, Polygon, and Circle\n * - **Icon support**: Custom icons for Point geometries via icon atlases\n * - **Interactive selection**: Click handling with dotted border and optional highlight\n * - **Hover effects**: Border/outline width increases on hover for better UX\n * - **Customizable labels**: Flexible label positioning with per-shape or global options\n * - **Style properties**: Full control over colors, border/outline patterns, and opacity\n * - **Event bus integration**: Automatically emits shape events via @accelint/bus\n * - **Multi-map support**: Events include map instance ID for isolation\n *\n * ## Selection Visual Feedback\n * When a shape is selected via `selectedShapeId`:\n * - The shape's border/outline pattern changes to dotted\n * - An optional highlight renders underneath (controlled by `showHighlight` prop)\n *\n * ## Layer Structure\n * Renders up to four sublayers (in order, bottom to top):\n * 1. **Highlight layer**: Selection highlight effect for non-icon-Point shapes (if showHighlight=true)\n * 2. **Coffin corners layer**: Selection/hover feedback for Point shapes with icons\n * 3. **Main GeoJsonLayer**: Shape geometries with styling and interaction\n * 4. **Label layer**: Text labels (if showLabels enabled)\n *\n * ## Icon Atlas Constraint\n * When using icons for Point geometries, all shapes in a single layer must share the\n * same icon atlas. The layer uses the first atlas found across all features. If you\n * need icons from different atlases, use separate DisplayShapeLayer instances.\n *\n * ## Event Bus Integration\n * Automatically emits shape events that can be consumed anywhere in your app:\n * - `shapes:selected` - Emitted when a shape is clicked (includes mapId)\n * - `shapes:hovered` - Emitted when the hovered shape changes (deduplicated, includes mapId)\n *\n * For selection with auto-deselection, use the companion `useSelectShape` hook which handles\n * all the event wiring automatically. See the example below.\n *\n * @example Basic usage with useSelectShape hook (recommended)\n * ```tsx\n * import '@accelint/map-toolkit/deckgl/shapes/display-shape-layer/fiber';\n * import { useSelectShape } from '@accelint/map-toolkit/deckgl/shapes';\n * import { uuid } from '@accelint/core';\n *\n * const MAP_ID = uuid();\n *\n * function MapWithShapes() {\n * const { selectedId } = useSelectShape(MAP_ID);\n *\n * return (\n * <BaseMap id={MAP_ID}>\n * <displayShapeLayer\n * id=\"my-shapes\"\n * mapId={MAP_ID}\n * data={shapes}\n * selectedShapeId={selectedId}\n * showLabels=\"always\"\n * pickable={true}\n * />\n * </BaseMap>\n * );\n * }\n * ```\n *\n * @example With custom label positioning\n * ```tsx\n * <displayShapeLayer\n * id=\"my-shapes\"\n * data={shapes}\n * showLabels=\"always\"\n * labelOptions={{\n * // Position circle labels at the top\n * circleLabelCoordinateAnchor: 'top',\n * circleLabelVerticalAnchor: 'bottom',\n * circleLabelOffset: [0, -10],\n * // Position line labels at the middle\n * lineStringLabelCoordinateAnchor: 'middle',\n * }}\n * />\n * ```\n */\nexport class DisplayShapeLayer extends CompositeLayer<DisplayShapeLayerProps> {\n // State is typed via DisplayShapeLayerState but deck.gl doesn't support generic state\n declare state: DisplayShapeLayerState;\n\n /** Cache for transformed features to avoid recreating objects on every render */\n private featuresCache: FeaturesCache | null = null;\n\n static override layerName = 'DisplayShapeLayer';\n\n static override defaultProps = {\n ...DEFAULT_DISPLAY_PROPS,\n };\n\n /**\n * Clean up state and caches when layer is destroyed\n */\n override finalizeState(): void {\n // Clear hover state to prevent stale references\n if (this.state?.hoverIndex !== undefined) {\n this.setState({ hoverIndex: undefined, lastHoveredId: undefined });\n }\n // Clear features cache\n this.featuresCache = null;\n }\n\n /**\n * Override getPickingInfo to handle events from sublayers\n * This is the correct pattern for CompositeLayer event handling\n */\n override getPickingInfo({\n info,\n mode,\n sourceLayer,\n }: {\n info: PickingInfo;\n mode?: string;\n // biome-ignore lint/suspicious/noExplicitAny: sourceLayer type from deck.gl is not well-typed\n sourceLayer?: any;\n }) {\n // Check if this picking event came from our main shapes layer\n if (sourceLayer?.id === `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`) {\n // Handle click events (deck.gl uses 'query' mode for clicks)\n if (mode === 'query') {\n this.handleShapeClick(info);\n }\n\n // Handle hover events (including when mode is undefined, which is hover)\n if (mode === 'hover' || !mode) {\n // Update hover state\n if (info.index !== undefined && info.index !== this.state?.hoverIndex) {\n this.setState({ hoverIndex: info.index });\n } else if (\n info.index === undefined &&\n this.state?.hoverIndex !== undefined\n ) {\n this.setState({ hoverIndex: undefined });\n }\n\n // Call hover callback\n this.handleShapeHover(info);\n }\n }\n\n return info;\n }\n\n /**\n * Convert shapes to GeoJSON features with shapeId in properties.\n * Uses caching to avoid recreating objects on every render cycle.\n */\n private getFeaturesWithId(): Shape['feature'][] {\n const { data } = this.props;\n\n // Return cached features if data hasn't changed (identity check)\n if (this.featuresCache?.data === data) {\n return this.featuresCache.features;\n }\n\n // Transform features and build shapeId->index map in a single pass\n const features: Shape['feature'][] = [];\n const shapeIdToIndex = new Map<ShapeId, number>();\n\n for (const [i, shape] of data.entries()) {\n features.push({\n ...shape.feature,\n properties: {\n ...shape.feature.properties,\n shapeId: shape.id,\n },\n });\n shapeIdToIndex.set(shape.id, i);\n }\n\n this.featuresCache = { data, features, shapeIdToIndex };\n return features;\n }\n\n /**\n * Look up a shape by ID from the data prop.\n * Used by event handlers to get full shape without storing in feature properties.\n */\n private getShapeById(shapeId: ShapeId): Shape | undefined {\n return this.props.data.find((shape) => shape.id === shapeId);\n }\n\n /**\n * Handle shape click\n */\n private handleShapeClick = (info: PickingInfo): void => {\n const { onShapeClick, mapId } = this.props;\n\n if (!info.object) {\n return;\n }\n\n // Look up shape from data prop using shapeId stored in feature properties\n const shapeId = info.object.properties?.shapeId as ShapeId | undefined;\n if (!shapeId) {\n return;\n }\n\n const shape = this.getShapeById(shapeId);\n if (!shape) {\n return;\n }\n\n // Emit shape selected event via bus (include mapId for multi-map isolation)\n shapeBus.emit(ShapeEvents.selected, { shapeId: shape.id, mapId });\n\n // Call callback if provided\n if (onShapeClick) {\n onShapeClick(shape);\n }\n };\n\n /**\n * Handle shape hover\n */\n private handleShapeHover = (info: PickingInfo): void => {\n const { onShapeHover, mapId } = this.props;\n\n // Look up shape from data prop using shapeId stored in feature properties\n const shapeId =\n (info.object?.properties?.shapeId as ShapeId | undefined) ?? null;\n const shape = shapeId ? (this.getShapeById(shapeId) ?? null) : null;\n\n // Dedupe hover events - only emit if hovered shape changed\n if (shapeId !== this.state?.lastHoveredId) {\n this.setState({ lastHoveredId: shapeId });\n\n // Emit shape hovered event via bus (include mapId for multi-map isolation)\n shapeBus.emit(ShapeEvents.hovered, {\n shapeId,\n mapId,\n });\n }\n\n // Always call callback if provided (for local state updates)\n if (onShapeHover) {\n onShapeHover(shape);\n }\n };\n\n /**\n * Render highlight sublayer (underneath main layer)\n * Note: Points with icons use coffin corners instead of highlight layer\n */\n private renderHighlightLayer(\n features: Shape['feature'][],\n ): GeoJsonLayer | null {\n const { selectedShapeId, showHighlight, highlightColor } = this.props;\n\n if (!selectedShapeId || showHighlight === false) {\n return null;\n }\n\n const selectedFeature = features.find(\n (f) => f.properties?.shapeId === selectedShapeId,\n );\n\n if (!selectedFeature) {\n return null;\n }\n\n // Skip highlight layer for Point geometries with icons - they use coffin corners instead\n // Points without icons should still show the highlight layer\n if (selectedFeature.geometry.type === 'Point') {\n const hasIcon = !!selectedFeature.properties?.styleProperties?.icon;\n if (hasIcon) {\n return null;\n }\n }\n\n return new GeoJsonLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_HIGHLIGHT}`,\n // biome-ignore lint/suspicious/noExplicitAny: GeoJsonLayer accepts various feature formats\n data: [selectedFeature] as any,\n\n // Styling\n filled: true,\n stroked: true,\n lineWidthUnits: 'pixels',\n lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,\n getFillColor: () => [0, 0, 0, 0], // Transparent fill\n getLineColor: () => highlightColor || getHighlightColor(),\n getLineWidth: getHighlightLineWidth,\n\n // Behavior\n pickable: false,\n updateTriggers: {\n getLineColor: [highlightColor],\n getLineWidth: [selectedShapeId, features],\n },\n });\n }\n\n /**\n * Render coffin corners layer for Point geometries that have icons on hover/select\n * Coffin corners provide visual feedback for points instead of highlight layer\n */\n private renderCoffinCornersLayer(\n features: Shape['feature'][],\n ): IconLayer | null {\n const { selectedShapeId } = this.props;\n const hoverIndex = this.state?.hoverIndex;\n\n // Use cached shapeId->index map for O(1) lookup\n const shapeIdToIndex = this.featuresCache?.shapeIdToIndex;\n if (!shapeIdToIndex) {\n return null;\n }\n\n // Find point features that need coffin corners (hovered or selected)\n const pointFeatures = features.filter((f) => {\n if (f.geometry.type !== 'Point') {\n return false;\n }\n const hasIcon = !!f.properties?.styleProperties?.icon;\n if (!hasIcon) {\n return false;\n }\n\n const shapeId = f.properties?.shapeId;\n const isSelected = shapeId === selectedShapeId;\n const featureIndex = shapeId ? shapeIdToIndex.get(shapeId) : undefined;\n const isHovered = hoverIndex !== undefined && featureIndex === hoverIndex;\n\n return isSelected || isHovered;\n });\n\n if (pointFeatures.length === 0) {\n return null;\n }\n\n // Get icon atlas from first point feature (all should share the same atlas)\n const firstPointIcon = pointFeatures[0]?.properties?.styleProperties?.icon;\n const iconAtlas = firstPointIcon?.atlas;\n const iconMapping = firstPointIcon?.mapping;\n\n if (!(iconAtlas && iconMapping)) {\n logger.warn(\n 'Point shape has icon style but missing iconAtlas or iconMapping - coffin corners will not render',\n );\n return null;\n }\n\n // Add coffin corners icons to the mapping\n const extendedMapping = {\n ...iconMapping,\n [COFFIN_CORNERS.HOVER_ICON]: {\n x: 0,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n [COFFIN_CORNERS.SELECTED_ICON]: {\n x: 76,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n [COFFIN_CORNERS.SELECTED_HOVER_ICON]: {\n x: 152,\n y: 0,\n width: 76,\n height: 76,\n mask: false,\n },\n };\n\n return new IconLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}-coffin-corners`,\n data: pointFeatures,\n iconAtlas,\n iconMapping: extendedMapping,\n getIcon: (d: Shape['feature']) => {\n const shapeId = d.properties?.shapeId;\n const isSelected = shapeId === selectedShapeId;\n const featureIndex = shapeId ? shapeIdToIndex.get(shapeId) : undefined;\n const isHovered =\n hoverIndex !== undefined && featureIndex === hoverIndex;\n\n if (isSelected && isHovered) {\n return COFFIN_CORNERS.SELECTED_HOVER_ICON;\n }\n if (isSelected) {\n return COFFIN_CORNERS.SELECTED_ICON;\n }\n return COFFIN_CORNERS.HOVER_ICON;\n },\n getSize: COFFIN_CORNERS.SIZE,\n getPosition: (d: Shape['feature']) => {\n const coords =\n d.geometry.type === 'Point' ? d.geometry.coordinates : [0, 0];\n return coords as [number, number];\n },\n getPixelOffset: (d: Shape['feature']) => {\n const iconSize =\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE;\n // Center the coffin corners on the point icon\n return [-1, -iconSize / 2];\n },\n billboard: false,\n pickable: false,\n updateTriggers: {\n getIcon: [selectedShapeId, this.state?.hoverIndex],\n data: [features, selectedShapeId, this.state?.hoverIndex],\n },\n });\n }\n\n /**\n * Extract icon configuration from features in a single pass.\n * Returns the first icon's atlas and mapping (all shapes share the same atlas).\n * Uses early return for O(1) best case when first feature has icons.\n */\n private getIconConfig(features: Shape['feature'][]): {\n hasIcons: boolean;\n atlas?: string;\n mapping?: Record<\n string,\n { x: number; y: number; width: number; height: number; mask?: boolean }\n >;\n } {\n for (const f of features) {\n const icon = f.properties?.styleProperties?.icon;\n if (icon) {\n return {\n hasIcons: true,\n atlas: icon.atlas,\n mapping: icon.mapping,\n };\n }\n }\n return { hasIcons: false };\n }\n\n /**\n * Render main shapes layer\n */\n private renderMainLayer(features: Shape['feature'][]): GeoJsonLayer {\n const { pickable, applyBaseOpacity, selectedShapeId } = this.props;\n\n // Single-pass icon config extraction (O(1) best case with early return)\n const {\n hasIcons,\n atlas: iconAtlas,\n mapping: iconMapping,\n } = this.getIconConfig(features);\n\n return new GeoJsonLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY}`,\n // biome-ignore lint/suspicious/noExplicitAny: GeoJsonLayer accepts various feature formats\n data: features as any,\n\n // Styling\n filled: true,\n stroked: true,\n getFillColor: (d: Shape['feature']) => getFillColor(d, applyBaseOpacity),\n getLineColor,\n getLineWidth: (d, info) => {\n const isHovered = info?.index === this.state?.hoverIndex;\n return getHoverLineWidth(d, isHovered);\n },\n lineWidthUnits: 'pixels',\n lineWidthMinPixels: MAP_INTERACTION.LINE_WIDTH_MIN_PIXELS,\n lineWidthMaxPixels: 20,\n\n // Points - use icons if any feature has icon config, otherwise circles\n pointType: hasIcons ? 'icon' : 'circle',\n getPointRadius: (d) => {\n const iconSize = d.properties?.styleProperties?.icon?.size;\n return iconSize ?? 2;\n },\n pointRadiusUnits: 'pixels',\n\n // Icon configuration (only used if pointType includes 'icon')\n ...(hasIcons && iconAtlas ? { iconAtlas } : {}),\n ...(hasIcons && iconMapping ? { iconMapping } : {}),\n ...(hasIcons\n ? {\n getIcon: (d: Shape['feature']) =>\n d.properties?.styleProperties?.icon?.name ?? 'marker',\n getIconSize: (d: Shape['feature']) => {\n return (\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE\n );\n },\n getIconColor: getLineColor,\n getIconPixelOffset: (d: Shape['feature']) => {\n const iconSize =\n d.properties?.styleProperties?.icon?.size ??\n MAP_INTERACTION.ICON_SIZE;\n return [-1, -iconSize / 2];\n },\n iconBillboard: false,\n }\n : {}),\n\n // Dash pattern support - selected shapes get dotted border\n extensions: [new PathStyleExtension({ dash: true })],\n getDashArray: (d: Shape['feature']) => {\n const isSelected = d.properties?.shapeId === selectedShapeId;\n if (isSelected) {\n return DASH_ARRAYS.dotted;\n }\n return getDashArray(d);\n },\n\n // Behavior\n pickable,\n autoHighlight: false, // We handle highlighting manually\n // Note: onClick and onHover are handled via getPickingInfo() override\n\n // Update triggers\n updateTriggers: {\n getFillColor: [features, applyBaseOpacity],\n getLineColor: [features],\n getLineWidth: [features, this.state?.hoverIndex],\n getDashArray: [features, selectedShapeId],\n getPointRadius: [features],\n ...(hasIcons\n ? {\n getIcon: [features],\n getIconSize: [features],\n getIconColor: [features],\n getIconPixelOffset: [features],\n }\n : {}),\n },\n });\n }\n\n /**\n * Render labels layer\n * Supports three modes:\n * - 'always': Show labels for all shapes\n * - 'hover': Show label only for the currently hovered shape\n * - 'never': No labels\n */\n private renderLabelsLayer(): ReturnType<typeof createShapeLabelLayer> | null {\n const { showLabels, data, labelOptions } = this.props;\n\n // No labels if disabled\n if (showLabels === 'never') {\n return null;\n }\n\n // Determine which shapes to show labels for\n let labelData = data;\n if (showLabels === 'hover') {\n const hoverIndex = this.state?.hoverIndex;\n if (hoverIndex === undefined) {\n return null; // No shape hovered, no label to show\n }\n const hoveredShape = data[hoverIndex];\n labelData = hoveredShape ? [hoveredShape] : [];\n }\n\n if (labelData.length === 0) {\n return null;\n }\n\n return createShapeLabelLayer({\n id: `${this.props.id}-${SHAPE_LAYER_IDS.DISPLAY_LABELS}`,\n data: labelData,\n labelOptions,\n });\n }\n\n /**\n * Render all sublayers\n */\n renderLayers(): Layer[] {\n // Compute features once per render cycle for performance\n const features = this.getFeaturesWithId();\n\n return [\n this.renderHighlightLayer(features),\n this.renderCoffinCornersLayer(features),\n this.renderMainLayer(features),\n this.renderLabelsLayer(),\n ].filter(Boolean) as Layer[];\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
'use client';
|
|
15
|
+
|
|
16
|
+
import { DEFAULT_TEXT_STYLE } from "../../text-settings.js";
|
|
17
|
+
import { SHAPE_LAYER_IDS } from "../shared/constants.js";
|
|
18
|
+
import { getLabelPosition2d, getLabelText } from "./utils/labels.js";
|
|
19
|
+
import { TextLayer } from "@deck.gl/layers";
|
|
20
|
+
|
|
21
|
+
//#region src/deckgl/shapes/display-shape-layer/shape-label-layer.ts
|
|
22
|
+
/**
|
|
23
|
+
* Creates a cached label position getter to avoid computing position multiple times per shape.
|
|
24
|
+
* Uses WeakMap so positions are garbage collected when shapes are removed.
|
|
25
|
+
*/
|
|
26
|
+
function createCachedPositionGetter(labelOptions) {
|
|
27
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
28
|
+
const getNullable = (shape) => {
|
|
29
|
+
if (cache.has(shape)) return cache.get(shape) ?? null;
|
|
30
|
+
const position = getLabelPosition2d(shape, labelOptions);
|
|
31
|
+
cache.set(shape, position);
|
|
32
|
+
return position;
|
|
33
|
+
};
|
|
34
|
+
const getRequired = (shape) => {
|
|
35
|
+
const position = getNullable(shape);
|
|
36
|
+
if (!position) throw new Error("Shape has no valid position - should have been filtered");
|
|
37
|
+
return position;
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
getRequired,
|
|
41
|
+
getNullable
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates a TextLayer for rendering shape labels with intelligent positioning
|
|
46
|
+
*
|
|
47
|
+
* ## Features
|
|
48
|
+
* - **Geometry-aware positioning**: Different defaults for Point, LineString, Polygon, and Circle
|
|
49
|
+
* - **Three-tier priority system**: Per-shape properties > labelOptions > defaults
|
|
50
|
+
* - **Coordinate anchoring**: Position labels at start/middle/end (or edge positions for circles)
|
|
51
|
+
* - **Pixel-based offsets**: Consistent label placement at all zoom levels
|
|
52
|
+
* - **Text-only styling**: White uppercase text with black outline for legibility
|
|
53
|
+
* - **Position caching**: Label positions are computed once per shape using a WeakMap cache
|
|
54
|
+
*
|
|
55
|
+
* ## Label Positioning Priority
|
|
56
|
+
* 1. Per-shape `styleProperties` (highest priority)
|
|
57
|
+
* 2. Global `labelOptions` parameter
|
|
58
|
+
* 3. Geometry-specific defaults (fallback)
|
|
59
|
+
*
|
|
60
|
+
* @param props - Shape label layer configuration
|
|
61
|
+
* @returns Configured TextLayer instance
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* const labelLayer = createShapeLabelLayer({
|
|
66
|
+
* id: 'my-labels',
|
|
67
|
+
* data: shapes,
|
|
68
|
+
* labelOptions: {
|
|
69
|
+
* circleLabelCoordinateAnchor: 'top',
|
|
70
|
+
* pointLabelOffset: [0, -20],
|
|
71
|
+
* },
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
function createShapeLabelLayer(props) {
|
|
76
|
+
const { id = SHAPE_LAYER_IDS.DISPLAY_LABELS, data, labelOptions } = props;
|
|
77
|
+
const { getRequired, getNullable } = createCachedPositionGetter(labelOptions);
|
|
78
|
+
return new TextLayer({
|
|
79
|
+
id,
|
|
80
|
+
data: data.filter((shape) => getNullable(shape) !== null),
|
|
81
|
+
getText: getLabelText,
|
|
82
|
+
getPosition: (d) => getRequired(d).coordinates,
|
|
83
|
+
getPixelOffset: (d) => getRequired(d).pixelOffset,
|
|
84
|
+
getTextAnchor: (d) => getRequired(d).textAnchor,
|
|
85
|
+
getAlignmentBaseline: (d) => getRequired(d).alignmentBaseline,
|
|
86
|
+
...DEFAULT_TEXT_STYLE,
|
|
87
|
+
getAngle: 0,
|
|
88
|
+
background: false,
|
|
89
|
+
fontFamily: "Roboto MonoVariable, monospace",
|
|
90
|
+
updateTriggers: {
|
|
91
|
+
getPosition: [labelOptions],
|
|
92
|
+
getPixelOffset: [labelOptions],
|
|
93
|
+
getTextAnchor: [labelOptions],
|
|
94
|
+
getAlignmentBaseline: [labelOptions]
|
|
95
|
+
},
|
|
96
|
+
pickable: false
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
export { createShapeLabelLayer };
|
|
102
|
+
//# sourceMappingURL=shape-label-layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape-label-layer.js","names":[],"sources":["../../../../src/deckgl/shapes/display-shape-layer/shape-label-layer.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { TextLayer } from '@deck.gl/layers';\nimport { DEFAULT_TEXT_STYLE } from '../../text-settings';\nimport { SHAPE_LAYER_IDS } from '../shared/constants';\nimport {\n getLabelPosition2d,\n getLabelText,\n type LabelPosition2d,\n type LabelPositionOptions,\n} from './utils/labels';\nimport type { Shape } from '../shared/types';\n\n/**\n * Creates a cached label position getter to avoid computing position multiple times per shape.\n * Uses WeakMap so positions are garbage collected when shapes are removed.\n */\nfunction createCachedPositionGetter(\n labelOptions: LabelPositionOptions | undefined,\n) {\n const cache = new WeakMap<Shape, LabelPosition2d | null>();\n\n // Returns nullable position for filtering\n const getNullable = (shape: Shape): LabelPosition2d | null => {\n if (cache.has(shape)) {\n return cache.get(shape) ?? null;\n }\n const position = getLabelPosition2d(shape, labelOptions);\n cache.set(shape, position);\n return position;\n };\n\n // Returns position, throwing if null (use only after filtering)\n const getRequired = (shape: Shape): LabelPosition2d => {\n const position = getNullable(shape);\n if (!position) {\n throw new Error(\n 'Shape has no valid position - should have been filtered',\n );\n }\n return position;\n };\n\n return { getRequired, getNullable };\n}\n\n/**\n * Props for creating a shape label layer\n */\nexport interface ShapeLabelLayerProps {\n /** Layer ID (defaults to DISPLAY_LABELS constant) */\n id?: string;\n /** Array of shapes to label */\n data: Shape[];\n /**\n * Global label positioning options\n * Per-shape properties in styleProperties take precedence\n */\n labelOptions?: LabelPositionOptions;\n}\n\n/**\n * Creates a TextLayer for rendering shape labels with intelligent positioning\n *\n * ## Features\n * - **Geometry-aware positioning**: Different defaults for Point, LineString, Polygon, and Circle\n * - **Three-tier priority system**: Per-shape properties > labelOptions > defaults\n * - **Coordinate anchoring**: Position labels at start/middle/end (or edge positions for circles)\n * - **Pixel-based offsets**: Consistent label placement at all zoom levels\n * - **Text-only styling**: White uppercase text with black outline for legibility\n * - **Position caching**: Label positions are computed once per shape using a WeakMap cache\n *\n * ## Label Positioning Priority\n * 1. Per-shape `styleProperties` (highest priority)\n * 2. Global `labelOptions` parameter\n * 3. Geometry-specific defaults (fallback)\n *\n * @param props - Shape label layer configuration\n * @returns Configured TextLayer instance\n *\n * @example\n * ```tsx\n * const labelLayer = createShapeLabelLayer({\n * id: 'my-labels',\n * data: shapes,\n * labelOptions: {\n * circleLabelCoordinateAnchor: 'top',\n * pointLabelOffset: [0, -20],\n * },\n * });\n * ```\n */\nexport function createShapeLabelLayer(\n props: ShapeLabelLayerProps,\n): TextLayer<Shape> {\n const { id = SHAPE_LAYER_IDS.DISPLAY_LABELS, data, labelOptions } = props;\n\n // Create cached position getter to avoid computing position 4x per shape\n const { getRequired, getNullable } = createCachedPositionGetter(labelOptions);\n\n // Filter out shapes with invalid positions (null coordinates)\n const validData = data.filter((shape) => getNullable(shape) !== null);\n\n return new TextLayer<Shape>({\n id,\n data: validData,\n\n // Text content - uppercase\n getText: getLabelText,\n\n // Position - use cached getter for all position-related properties\n // getRequired is safe because we filtered out null positions above\n getPosition: (d: Shape) => getRequired(d).coordinates,\n getPixelOffset: (d: Shape) => getRequired(d).pixelOffset,\n getTextAnchor: (d: Shape) => getRequired(d).textAnchor,\n getAlignmentBaseline: (d: Shape) => getRequired(d).alignmentBaseline,\n\n // Cross-platform text styling (SDF font settings for legibility on Windows)\n ...DEFAULT_TEXT_STYLE,\n getAngle: 0,\n\n // No background or border\n background: false,\n\n // Font overrides\n fontFamily: 'Roboto MonoVariable, monospace',\n\n // Update triggers - tell deck.gl to recalculate when labelOptions change\n updateTriggers: {\n getPosition: [labelOptions],\n getPixelOffset: [labelOptions],\n getTextAnchor: [labelOptions],\n getAlignmentBaseline: [labelOptions],\n },\n\n // Behavior\n pickable: false,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAS,2BACP,cACA;CACA,MAAM,wBAAQ,IAAI,SAAwC;CAG1D,MAAM,eAAe,UAAyC;AAC5D,MAAI,MAAM,IAAI,MAAM,CAClB,QAAO,MAAM,IAAI,MAAM,IAAI;EAE7B,MAAM,WAAW,mBAAmB,OAAO,aAAa;AACxD,QAAM,IAAI,OAAO,SAAS;AAC1B,SAAO;;CAIT,MAAM,eAAe,UAAkC;EACrD,MAAM,WAAW,YAAY,MAAM;AACnC,MAAI,CAAC,SACH,OAAM,IAAI,MACR,0DACD;AAEH,SAAO;;AAGT,QAAO;EAAE;EAAa;EAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiDrC,SAAgB,sBACd,OACkB;CAClB,MAAM,EAAE,KAAK,gBAAgB,gBAAgB,MAAM,iBAAiB;CAGpE,MAAM,EAAE,aAAa,gBAAgB,2BAA2B,aAAa;AAK7E,QAAO,IAAI,UAAiB;EAC1B;EACA,MAJgB,KAAK,QAAQ,UAAU,YAAY,MAAM,KAAK,KAAK;EAOnE,SAAS;EAIT,cAAc,MAAa,YAAY,EAAE,CAAC;EAC1C,iBAAiB,MAAa,YAAY,EAAE,CAAC;EAC7C,gBAAgB,MAAa,YAAY,EAAE,CAAC;EAC5C,uBAAuB,MAAa,YAAY,EAAE,CAAC;EAGnD,GAAG;EACH,UAAU;EAGV,YAAY;EAGZ,YAAY;EAGZ,gBAAgB;GACd,aAAa,CAAC,aAAa;GAC3B,gBAAgB,CAAC,aAAa;GAC9B,eAAe,CAAC,aAAa;GAC7B,sBAAsB,CAAC,aAAa;GACrC;EAGD,UAAU;EACX,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import { createMapStore } from "../../../shared/create-map-store.js";
|
|
15
|
+
import { MapEvents } from "../../base-map/events.js";
|
|
16
|
+
import { ShapeEvents } from "../shared/events.js";
|
|
17
|
+
import { Broadcast } from "@accelint/bus";
|
|
18
|
+
|
|
19
|
+
//#region src/deckgl/shapes/display-shape-layer/store.ts
|
|
20
|
+
/**
|
|
21
|
+
* Shape Selection Store
|
|
22
|
+
*
|
|
23
|
+
* Manages shape selection state per map instance.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* import { shapeSelectionStore } from '@accelint/map-toolkit/deckgl/shapes';
|
|
28
|
+
*
|
|
29
|
+
* function ShapePanel({ mapId }) {
|
|
30
|
+
* const { state, setSelectedId, clearSelection } = shapeSelectionStore.use(mapId);
|
|
31
|
+
*
|
|
32
|
+
* return (
|
|
33
|
+
* <div>
|
|
34
|
+
* <p>Selected: {state.selectedId ?? 'none'}</p>
|
|
35
|
+
* <button onClick={() => setSelectedId('shape-1')}>Select Shape 1</button>
|
|
36
|
+
* <button onClick={clearSelection}>Clear</button>
|
|
37
|
+
* </div>
|
|
38
|
+
* );
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* // Or with selector for specific values:
|
|
42
|
+
* function SelectedIndicator({ mapId }) {
|
|
43
|
+
* const selectedId = shapeSelectionStore.useSelector(
|
|
44
|
+
* mapId,
|
|
45
|
+
* (s) => s.selectedId
|
|
46
|
+
* );
|
|
47
|
+
* return selectedId ? <Badge>Selected</Badge> : null;
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
const shapeBus = Broadcast.getInstance();
|
|
52
|
+
const mapBus = Broadcast.getInstance();
|
|
53
|
+
/**
|
|
54
|
+
* Shape selection store
|
|
55
|
+
*/
|
|
56
|
+
const shapeSelectionStore = createMapStore({
|
|
57
|
+
defaultState: { selectedId: void 0 },
|
|
58
|
+
actions: (mapId, { get }) => ({
|
|
59
|
+
setSelectedId: (id) => {
|
|
60
|
+
const currentId = get().selectedId;
|
|
61
|
+
if (id === void 0 && currentId !== void 0) shapeBus.emit(ShapeEvents.deselected, { mapId });
|
|
62
|
+
else if (id !== void 0 && currentId !== id) shapeBus.emit(ShapeEvents.selected, {
|
|
63
|
+
shapeId: id,
|
|
64
|
+
mapId
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
clearSelection: () => {
|
|
68
|
+
shapeBus.emit(ShapeEvents.deselected, { mapId });
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
bus: (mapId, { get, set }) => {
|
|
72
|
+
const unsubSelected = shapeBus.on(ShapeEvents.selected, (event) => {
|
|
73
|
+
if (event.payload.mapId !== mapId) return;
|
|
74
|
+
if (get().selectedId !== event.payload.shapeId) set({ selectedId: event.payload.shapeId });
|
|
75
|
+
});
|
|
76
|
+
const unsubDeselected = shapeBus.on(ShapeEvents.deselected, (event) => {
|
|
77
|
+
if (event.payload.mapId !== mapId) return;
|
|
78
|
+
if (get().selectedId !== void 0) set({ selectedId: void 0 });
|
|
79
|
+
});
|
|
80
|
+
const unsubClick = mapBus.on(MapEvents.click, (event) => {
|
|
81
|
+
if (get().selectedId !== void 0 && event.payload.id === mapId && event.payload.info.index === -1) shapeBus.emit(ShapeEvents.deselected, { mapId });
|
|
82
|
+
});
|
|
83
|
+
return () => {
|
|
84
|
+
unsubSelected();
|
|
85
|
+
unsubDeselected();
|
|
86
|
+
unsubClick();
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Hook for shape selection - primary API
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* const { state, setSelectedId, clearSelection } = useSelectShape(mapId);
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
const useSelectShape = shapeSelectionStore.use;
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
export { shapeSelectionStore };
|
|
102
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","names":[],"sources":["../../../../src/deckgl/shapes/display-shape-layer/store.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n/**\n * Shape Selection Store\n *\n * Manages shape selection state per map instance.\n *\n * @example\n * ```tsx\n * import { shapeSelectionStore } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function ShapePanel({ mapId }) {\n * const { state, setSelectedId, clearSelection } = shapeSelectionStore.use(mapId);\n *\n * return (\n * <div>\n * <p>Selected: {state.selectedId ?? 'none'}</p>\n * <button onClick={() => setSelectedId('shape-1')}>Select Shape 1</button>\n * <button onClick={clearSelection}>Clear</button>\n * </div>\n * );\n * }\n *\n * // Or with selector for specific values:\n * function SelectedIndicator({ mapId }) {\n * const selectedId = shapeSelectionStore.useSelector(\n * mapId,\n * (s) => s.selectedId\n * );\n * return selectedId ? <Badge>Selected</Badge> : null;\n * }\n * ```\n */\n\nimport { Broadcast } from '@accelint/bus';\nimport { createMapStore } from '@/shared/create-map-store';\nimport { MapEvents } from '../../base-map/events';\nimport { ShapeEvents } from '../shared/events';\nimport type { UniqueId } from '@accelint/core';\nimport type { MapClickEvent, MapEventType } from '../../base-map/types';\nimport type { ShapeEvent } from '../shared/events';\nimport type { ShapeId } from '../shared/types';\n\n/**\n * State shape for shape selection\n */\ntype ShapeSelectionState = {\n selectedId: ShapeId | undefined;\n};\n\n/**\n * Actions for shape selection\n */\ntype ShapeSelectionActions = {\n /** Set the selected shape ID (emits appropriate events) */\n setSelectedId: (id: ShapeId | undefined) => void;\n /** Clear the current selection */\n clearSelection: () => void;\n};\n\nconst shapeBus = Broadcast.getInstance<ShapeEvent>();\nconst mapBus = Broadcast.getInstance<MapEventType>();\n\n/**\n * Shape selection store\n */\nexport const shapeSelectionStore = createMapStore<\n ShapeSelectionState,\n ShapeSelectionActions\n>({\n defaultState: { selectedId: undefined },\n\n actions: (mapId, { get }) => ({\n setSelectedId: (id: ShapeId | undefined) => {\n const currentId = get().selectedId;\n\n if (id === undefined && currentId !== undefined) {\n // Emit deselection event\n shapeBus.emit(ShapeEvents.deselected, { mapId });\n } else if (id !== undefined && currentId !== id) {\n // Emit selection event\n shapeBus.emit(ShapeEvents.selected, { shapeId: id, mapId });\n }\n },\n\n clearSelection: () => {\n shapeBus.emit(ShapeEvents.deselected, { mapId });\n },\n }),\n\n bus: (mapId, { get, set }) => {\n // Listen for shape selection events\n const unsubSelected = shapeBus.on(ShapeEvents.selected, (event) => {\n if (event.payload.mapId !== mapId) {\n return;\n }\n if (get().selectedId !== event.payload.shapeId) {\n set({ selectedId: event.payload.shapeId });\n }\n });\n\n // Listen for shape deselection events\n const unsubDeselected = shapeBus.on(ShapeEvents.deselected, (event) => {\n if (event.payload.mapId !== mapId) {\n return;\n }\n if (get().selectedId !== undefined) {\n set({ selectedId: undefined });\n }\n });\n\n // Listen for map clicks to detect clicks on empty space\n const unsubClick = mapBus.on(MapEvents.click, (event: MapClickEvent) => {\n // Deselect if clicked on empty space (index === -1)\n if (\n get().selectedId !== undefined &&\n event.payload.id === mapId &&\n event.payload.info.index === -1\n ) {\n shapeBus.emit(ShapeEvents.deselected, { mapId });\n }\n });\n\n return () => {\n unsubSelected();\n unsubDeselected();\n unsubClick();\n };\n },\n});\n\n// =============================================================================\n// Convenience exports for common patterns\n// =============================================================================\n\n/**\n * Hook for shape selection - primary API\n *\n * @example\n * ```tsx\n * const { state, setSelectedId, clearSelection } = useSelectShape(mapId);\n * ```\n */\nexport const useSelectShape = shapeSelectionStore.use;\n\n/**\n * Hook to get just the selected ID\n *\n * @example\n * ```tsx\n * const selectedId = useSelectedShapeId(mapId);\n * ```\n */\nexport function useSelectedShapeId(mapId: UniqueId): ShapeId | undefined {\n return shapeSelectionStore.useSelector(mapId, (s) => s.selectedId);\n}\n\n/**\n * Get selected shape ID imperatively (non-reactive)\n */\nexport function getSelectedShapeId(mapId: UniqueId): ShapeId | undefined {\n return shapeSelectionStore.get(mapId).selectedId;\n}\n\n/**\n * Clear selection state (for tests/cleanup)\n */\nexport function clearSelectionState(mapId: UniqueId): void {\n shapeSelectionStore.clear(mapId);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsEA,MAAM,WAAW,UAAU,aAAyB;AACpD,MAAM,SAAS,UAAU,aAA2B;;;;AAKpD,MAAa,sBAAsB,eAGjC;CACA,cAAc,EAAE,YAAY,QAAW;CAEvC,UAAU,OAAO,EAAE,WAAW;EAC5B,gBAAgB,OAA4B;GAC1C,MAAM,YAAY,KAAK,CAAC;AAExB,OAAI,OAAO,UAAa,cAAc,OAEpC,UAAS,KAAK,YAAY,YAAY,EAAE,OAAO,CAAC;YACvC,OAAO,UAAa,cAAc,GAE3C,UAAS,KAAK,YAAY,UAAU;IAAE,SAAS;IAAI;IAAO,CAAC;;EAI/D,sBAAsB;AACpB,YAAS,KAAK,YAAY,YAAY,EAAE,OAAO,CAAC;;EAEnD;CAED,MAAM,OAAO,EAAE,KAAK,UAAU;EAE5B,MAAM,gBAAgB,SAAS,GAAG,YAAY,WAAW,UAAU;AACjE,OAAI,MAAM,QAAQ,UAAU,MAC1B;AAEF,OAAI,KAAK,CAAC,eAAe,MAAM,QAAQ,QACrC,KAAI,EAAE,YAAY,MAAM,QAAQ,SAAS,CAAC;IAE5C;EAGF,MAAM,kBAAkB,SAAS,GAAG,YAAY,aAAa,UAAU;AACrE,OAAI,MAAM,QAAQ,UAAU,MAC1B;AAEF,OAAI,KAAK,CAAC,eAAe,OACvB,KAAI,EAAE,YAAY,QAAW,CAAC;IAEhC;EAGF,MAAM,aAAa,OAAO,GAAG,UAAU,QAAQ,UAAyB;AAEtE,OACE,KAAK,CAAC,eAAe,UACrB,MAAM,QAAQ,OAAO,SACrB,MAAM,QAAQ,KAAK,UAAU,GAE7B,UAAS,KAAK,YAAY,YAAY,EAAE,OAAO,CAAC;IAElD;AAEF,eAAa;AACX,kBAAe;AACf,oBAAiB;AACjB,eAAY;;;CAGjB,CAAC;;;;;;;;;AAcF,MAAa,iBAAiB,oBAAoB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Shape, ShapeId, StyledFeature as StyledFeature$1 } from "../shared/types.js";
|
|
14
|
+
import { LabelPositionOptions } from "./utils/labels.js";
|
|
15
|
+
import { UniqueId } from "@accelint/core";
|
|
16
|
+
import { CompositeLayerProps } from "@deck.gl/core";
|
|
17
|
+
|
|
18
|
+
//#region src/deckgl/shapes/display-shape-layer/types.d.ts
|
|
19
|
+
/**
|
|
20
|
+
* Label display mode for shapes
|
|
21
|
+
* - `'always'`: Show labels for all shapes
|
|
22
|
+
* - `'hover'`: Show label only for the currently hovered shape
|
|
23
|
+
* - `'never'`: Never show labels
|
|
24
|
+
*/
|
|
25
|
+
type ShowLabelsMode = 'always' | 'hover' | 'never';
|
|
26
|
+
/**
|
|
27
|
+
* Re-export StyledFeature from shared types
|
|
28
|
+
*/
|
|
29
|
+
type StyledFeature = StyledFeature$1;
|
|
30
|
+
/**
|
|
31
|
+
* Re-export StyledFeatureProperties from shared types
|
|
32
|
+
*/
|
|
33
|
+
type StyledFeatureProperties = StyledFeature$1['properties'];
|
|
34
|
+
/**
|
|
35
|
+
* Props for DisplayShapeLayer
|
|
36
|
+
*
|
|
37
|
+
* @example Basic usage
|
|
38
|
+
* ```tsx
|
|
39
|
+
* const props: DisplayShapeLayerProps = {
|
|
40
|
+
* id: 'my-shapes',
|
|
41
|
+
* data: myShapes,
|
|
42
|
+
* pickable: true,
|
|
43
|
+
* showLabels: true,
|
|
44
|
+
* };
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
interface DisplayShapeLayerProps extends CompositeLayerProps {
|
|
48
|
+
/** Unique layer ID - required for deck.gl layer management */
|
|
49
|
+
id: string;
|
|
50
|
+
/**
|
|
51
|
+
* Map instance ID for event bus isolation in multi-map scenarios.
|
|
52
|
+
* Shape events will include this ID in their payload,
|
|
53
|
+
* allowing listeners to filter events by map instance.
|
|
54
|
+
*/
|
|
55
|
+
mapId: UniqueId;
|
|
56
|
+
/**
|
|
57
|
+
* Array of shapes to display
|
|
58
|
+
* Each shape must have a GeoJSON feature with styleProperties
|
|
59
|
+
*/
|
|
60
|
+
data: Shape[];
|
|
61
|
+
/**
|
|
62
|
+
* Currently selected shape ID (for highlighting)
|
|
63
|
+
* When set, renders a highlight layer around the selected shape
|
|
64
|
+
*/
|
|
65
|
+
selectedShapeId?: ShapeId;
|
|
66
|
+
/**
|
|
67
|
+
* Callback when a shape is clicked
|
|
68
|
+
* Also triggers a shapes:selected event on the event bus
|
|
69
|
+
* @param shape - The clicked shape with full properties
|
|
70
|
+
*/
|
|
71
|
+
onShapeClick?: (shape: Shape) => void;
|
|
72
|
+
/**
|
|
73
|
+
* Callback when a shape is hovered
|
|
74
|
+
* Called with null when hover ends
|
|
75
|
+
* @param shape - The hovered shape, or null when hover ends
|
|
76
|
+
*/
|
|
77
|
+
onShapeHover?: (shape: Shape | null) => void;
|
|
78
|
+
/**
|
|
79
|
+
* Label display mode for shapes
|
|
80
|
+
* - `'always'`: Show labels for all shapes
|
|
81
|
+
* - `'hover'`: Show label only for the currently hovered shape (requires `pickable={true}`, the default)
|
|
82
|
+
* - `'never'`: Never show labels
|
|
83
|
+
*
|
|
84
|
+
* Labels use the shape's `label` property, or `name` if label is not set
|
|
85
|
+
* @default 'always'
|
|
86
|
+
*/
|
|
87
|
+
showLabels?: ShowLabelsMode;
|
|
88
|
+
/**
|
|
89
|
+
* Global label positioning options
|
|
90
|
+
* Can be overridden per-shape via styleProperties
|
|
91
|
+
* Priority: per-shape properties > labelOptions > defaults
|
|
92
|
+
* @see LabelPositionOptions for available options
|
|
93
|
+
*/
|
|
94
|
+
labelOptions?: LabelPositionOptions;
|
|
95
|
+
/**
|
|
96
|
+
* Whether to show the highlight layer around selected shapes
|
|
97
|
+
* When false, no highlight effect is rendered even when a shape is selected
|
|
98
|
+
* @default false
|
|
99
|
+
*/
|
|
100
|
+
showHighlight?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Custom highlight color for selected shapes [r, g, b, a]
|
|
103
|
+
* Each channel is 0-255
|
|
104
|
+
* @default [40, 245, 190, 100] - Turquoise at ~39% opacity
|
|
105
|
+
* @example Custom red highlight
|
|
106
|
+
* ```tsx
|
|
107
|
+
* highlightColor={[255, 0, 0, 128]} // Red at 50% opacity
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
highlightColor?: [number, number, number, number];
|
|
111
|
+
/**
|
|
112
|
+
* When true (default), multiplies fill color alpha by 0.2 (reducing to 20% of original opacity)
|
|
113
|
+
* for a standard semi-transparent look.
|
|
114
|
+
* When false, colors are rendered exactly as specified in styleProperties.
|
|
115
|
+
* @default true
|
|
116
|
+
* @example Standard semi-transparent fills
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <DisplayShapeLayer data={shapes} applyBaseOpacity />
|
|
119
|
+
* // Shape with fillColor [98, 166, 255, 255] renders at alpha 51 (255 × 0.2)
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
applyBaseOpacity?: boolean;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { DisplayShapeLayerProps, ShowLabelsMode, StyledFeature, StyledFeatureProperties };
|
|
126
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { ShapeId } from "../shared/types.js";
|
|
14
|
+
import { UniqueId } from "@accelint/core";
|
|
15
|
+
|
|
16
|
+
//#region src/deckgl/shapes/display-shape-layer/use-select-shape.d.ts
|
|
17
|
+
/**
|
|
18
|
+
* Return type for useSelectShape hook
|
|
19
|
+
*/
|
|
20
|
+
interface UseSelectShapeReturn {
|
|
21
|
+
/** Currently selected shape ID, or undefined if nothing selected */
|
|
22
|
+
selectedId: ShapeId | undefined;
|
|
23
|
+
/** Manually set the selected shape ID (useful for programmatic selection) */
|
|
24
|
+
setSelectedId: (id: ShapeId | undefined) => void;
|
|
25
|
+
/** Manually clear the selection */
|
|
26
|
+
clearSelection: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Hook to manage shape selection state with automatic deselection
|
|
30
|
+
*
|
|
31
|
+
* This hook encapsulates the common pattern of:
|
|
32
|
+
* 1. Listening to `shapes:selected` events and updating state
|
|
33
|
+
* 2. Listening to `shapes:deselected` events and clearing state
|
|
34
|
+
* 3. Listening to map clicks on empty space and emitting `shapes:deselected`
|
|
35
|
+
*
|
|
36
|
+
* Uses a store pattern with `useSyncExternalStore` for proper listener cleanup
|
|
37
|
+
* during HMR (Hot Module Replacement) in development. The store ensures only
|
|
38
|
+
* one bus listener exists per map instance, regardless of how many React
|
|
39
|
+
* components subscribe.
|
|
40
|
+
*
|
|
41
|
+
* @param mapId - The map instance ID for event filtering
|
|
42
|
+
* @returns Selection state and control functions
|
|
43
|
+
*
|
|
44
|
+
* @example Basic usage
|
|
45
|
+
* ```tsx
|
|
46
|
+
* import { useSelectShape } from '@accelint/map-toolkit/deckgl/shapes';
|
|
47
|
+
*
|
|
48
|
+
* function MapWithShapes() {
|
|
49
|
+
* const { selectedId } = useSelectShape(MAP_ID);
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <BaseMap id={MAP_ID}>
|
|
53
|
+
* <displayShapeLayer
|
|
54
|
+
* id="shapes"
|
|
55
|
+
* mapId={MAP_ID}
|
|
56
|
+
* data={shapes}
|
|
57
|
+
* selectedShapeId={selectedId}
|
|
58
|
+
* />
|
|
59
|
+
* </BaseMap>
|
|
60
|
+
* );
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example With programmatic selection control
|
|
65
|
+
* ```tsx
|
|
66
|
+
* function MapWithShapes() {
|
|
67
|
+
* const { selectedId, setSelectedId, clearSelection } = useSelectShape(MAP_ID);
|
|
68
|
+
*
|
|
69
|
+
* return (
|
|
70
|
+
* <>
|
|
71
|
+
* <button onClick={() => setSelectedId(shapes[0].id)}>Select First Shape</button>
|
|
72
|
+
* <button onClick={clearSelection}>Clear Selection</button>
|
|
73
|
+
* <BaseMap id={MAP_ID}>
|
|
74
|
+
* <displayShapeLayer
|
|
75
|
+
* id="shapes"
|
|
76
|
+
* mapId={MAP_ID}
|
|
77
|
+
* data={shapes}
|
|
78
|
+
* selectedShapeId={selectedId}
|
|
79
|
+
* />
|
|
80
|
+
* </BaseMap>
|
|
81
|
+
* </>
|
|
82
|
+
* );
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
declare function useSelectShape(mapId: UniqueId): UseSelectShapeReturn;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { UseSelectShapeReturn, useSelectShape };
|
|
89
|
+
//# sourceMappingURL=use-select-shape.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
'use client';
|
|
15
|
+
|
|
16
|
+
import { shapeSelectionStore } from "./store.js";
|
|
17
|
+
|
|
18
|
+
//#region src/deckgl/shapes/display-shape-layer/use-select-shape.ts
|
|
19
|
+
/**
|
|
20
|
+
* Hook to manage shape selection state with automatic deselection
|
|
21
|
+
*
|
|
22
|
+
* This hook encapsulates the common pattern of:
|
|
23
|
+
* 1. Listening to `shapes:selected` events and updating state
|
|
24
|
+
* 2. Listening to `shapes:deselected` events and clearing state
|
|
25
|
+
* 3. Listening to map clicks on empty space and emitting `shapes:deselected`
|
|
26
|
+
*
|
|
27
|
+
* Uses a store pattern with `useSyncExternalStore` for proper listener cleanup
|
|
28
|
+
* during HMR (Hot Module Replacement) in development. The store ensures only
|
|
29
|
+
* one bus listener exists per map instance, regardless of how many React
|
|
30
|
+
* components subscribe.
|
|
31
|
+
*
|
|
32
|
+
* @param mapId - The map instance ID for event filtering
|
|
33
|
+
* @returns Selection state and control functions
|
|
34
|
+
*
|
|
35
|
+
* @example Basic usage
|
|
36
|
+
* ```tsx
|
|
37
|
+
* import { useSelectShape } from '@accelint/map-toolkit/deckgl/shapes';
|
|
38
|
+
*
|
|
39
|
+
* function MapWithShapes() {
|
|
40
|
+
* const { selectedId } = useSelectShape(MAP_ID);
|
|
41
|
+
*
|
|
42
|
+
* return (
|
|
43
|
+
* <BaseMap id={MAP_ID}>
|
|
44
|
+
* <displayShapeLayer
|
|
45
|
+
* id="shapes"
|
|
46
|
+
* mapId={MAP_ID}
|
|
47
|
+
* data={shapes}
|
|
48
|
+
* selectedShapeId={selectedId}
|
|
49
|
+
* />
|
|
50
|
+
* </BaseMap>
|
|
51
|
+
* );
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example With programmatic selection control
|
|
56
|
+
* ```tsx
|
|
57
|
+
* function MapWithShapes() {
|
|
58
|
+
* const { selectedId, setSelectedId, clearSelection } = useSelectShape(MAP_ID);
|
|
59
|
+
*
|
|
60
|
+
* return (
|
|
61
|
+
* <>
|
|
62
|
+
* <button onClick={() => setSelectedId(shapes[0].id)}>Select First Shape</button>
|
|
63
|
+
* <button onClick={clearSelection}>Clear Selection</button>
|
|
64
|
+
* <BaseMap id={MAP_ID}>
|
|
65
|
+
* <displayShapeLayer
|
|
66
|
+
* id="shapes"
|
|
67
|
+
* mapId={MAP_ID}
|
|
68
|
+
* data={shapes}
|
|
69
|
+
* selectedShapeId={selectedId}
|
|
70
|
+
* />
|
|
71
|
+
* </BaseMap>
|
|
72
|
+
* </>
|
|
73
|
+
* );
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
function useSelectShape(mapId) {
|
|
78
|
+
const { state, setSelectedId, clearSelection } = shapeSelectionStore.use(mapId);
|
|
79
|
+
return {
|
|
80
|
+
selectedId: state.selectedId,
|
|
81
|
+
setSelectedId,
|
|
82
|
+
clearSelection
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
export { useSelectShape };
|
|
88
|
+
//# sourceMappingURL=use-select-shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-select-shape.js","names":[],"sources":["../../../../src/deckgl/shapes/display-shape-layer/use-select-shape.ts"],"sourcesContent":["/*\n * Copyright 2025 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\n'use client';\n\nimport { shapeSelectionStore } from './store';\nimport type { UniqueId } from '@accelint/core';\nimport type { ShapeId } from '../shared/types';\n\n/**\n * Return type for useSelectShape hook\n */\nexport interface UseSelectShapeReturn {\n /** Currently selected shape ID, or undefined if nothing selected */\n selectedId: ShapeId | undefined;\n /** Manually set the selected shape ID (useful for programmatic selection) */\n setSelectedId: (id: ShapeId | undefined) => void;\n /** Manually clear the selection */\n clearSelection: () => void;\n}\n\n/**\n * Hook to manage shape selection state with automatic deselection\n *\n * This hook encapsulates the common pattern of:\n * 1. Listening to `shapes:selected` events and updating state\n * 2. Listening to `shapes:deselected` events and clearing state\n * 3. Listening to map clicks on empty space and emitting `shapes:deselected`\n *\n * Uses a store pattern with `useSyncExternalStore` for proper listener cleanup\n * during HMR (Hot Module Replacement) in development. The store ensures only\n * one bus listener exists per map instance, regardless of how many React\n * components subscribe.\n *\n * @param mapId - The map instance ID for event filtering\n * @returns Selection state and control functions\n *\n * @example Basic usage\n * ```tsx\n * import { useSelectShape } from '@accelint/map-toolkit/deckgl/shapes';\n *\n * function MapWithShapes() {\n * const { selectedId } = useSelectShape(MAP_ID);\n *\n * return (\n * <BaseMap id={MAP_ID}>\n * <displayShapeLayer\n * id=\"shapes\"\n * mapId={MAP_ID}\n * data={shapes}\n * selectedShapeId={selectedId}\n * />\n * </BaseMap>\n * );\n * }\n * ```\n *\n * @example With programmatic selection control\n * ```tsx\n * function MapWithShapes() {\n * const { selectedId, setSelectedId, clearSelection } = useSelectShape(MAP_ID);\n *\n * return (\n * <>\n * <button onClick={() => setSelectedId(shapes[0].id)}>Select First Shape</button>\n * <button onClick={clearSelection}>Clear Selection</button>\n * <BaseMap id={MAP_ID}>\n * <displayShapeLayer\n * id=\"shapes\"\n * mapId={MAP_ID}\n * data={shapes}\n * selectedShapeId={selectedId}\n * />\n * </BaseMap>\n * </>\n * );\n * }\n * ```\n */\nexport function useSelectShape(mapId: UniqueId): UseSelectShapeReturn {\n const { state, setSelectedId, clearSelection } =\n shapeSelectionStore.use(mapId);\n\n return {\n selectedId: state.selectedId,\n setSelectedId,\n clearSelection,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwFA,SAAgB,eAAe,OAAuC;CACpE,MAAM,EAAE,OAAO,eAAe,mBAC5B,oBAAoB,IAAI,MAAM;AAEhC,QAAO;EACL,YAAY,MAAM;EAClB;EACA;EACD"}
|