@sqlrooms/deck 0.29.0-rc.12 → 0.29.0-rc.13

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.
Files changed (55) hide show
  1. package/README.md +144 -22
  2. package/dist/DeckJsonMap.d.ts.map +1 -1
  3. package/dist/DeckJsonMap.js +58 -12
  4. package/dist/DeckJsonMap.js.map +1 -1
  5. package/dist/DeckMapBasemapSelect.d.ts +9 -0
  6. package/dist/DeckMapBasemapSelect.d.ts.map +1 -0
  7. package/dist/DeckMapBasemapSelect.js +20 -0
  8. package/dist/DeckMapBasemapSelect.js.map +1 -0
  9. package/dist/DeckMapDefaultStylesProvider.d.ts +13 -19
  10. package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -1
  11. package/dist/DeckMapDefaultStylesProvider.js +11 -18
  12. package/dist/DeckMapDefaultStylesProvider.js.map +1 -1
  13. package/dist/DeckMapsSlice.d.ts +5 -0
  14. package/dist/DeckMapsSlice.d.ts.map +1 -1
  15. package/dist/DeckMapsSlice.js +6 -3
  16. package/dist/DeckMapsSlice.js.map +1 -1
  17. package/dist/MapSettings.d.ts.map +1 -1
  18. package/dist/MapSettings.js +3 -2
  19. package/dist/MapSettings.js.map +1 -1
  20. package/dist/ai.d.ts.map +1 -1
  21. package/dist/ai.js +15 -10
  22. package/dist/ai.js.map +1 -1
  23. package/dist/basemap.d.ts +23 -0
  24. package/dist/basemap.d.ts.map +1 -0
  25. package/dist/basemap.js +23 -0
  26. package/dist/basemap.js.map +1 -0
  27. package/dist/block.d.ts.map +1 -1
  28. package/dist/block.js +12 -8
  29. package/dist/block.js.map +1 -1
  30. package/dist/index.d.ts +4 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/mapAiSharedInstructions.d.ts.map +1 -1
  35. package/dist/mapAiSharedInstructions.js +1 -0
  36. package/dist/mapAiSharedInstructions.js.map +1 -1
  37. package/dist/mapConfig.d.ts +5 -0
  38. package/dist/mapConfig.d.ts.map +1 -1
  39. package/dist/mapConfig.js +28 -3
  40. package/dist/mapConfig.js.map +1 -1
  41. package/dist/mapConfigUtils.d.ts.map +1 -1
  42. package/dist/mapConfigUtils.js +7 -2
  43. package/dist/mapConfigUtils.js.map +1 -1
  44. package/dist/mapStyles.d.ts +16 -0
  45. package/dist/mapStyles.d.ts.map +1 -0
  46. package/dist/mapStyles.js +32 -0
  47. package/dist/mapStyles.js.map +1 -0
  48. package/dist/protomapsStyles.d.ts +18 -0
  49. package/dist/protomapsStyles.d.ts.map +1 -0
  50. package/dist/protomapsStyles.js +38 -0
  51. package/dist/protomapsStyles.js.map +1 -0
  52. package/dist/types.d.ts +13 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js.map +1 -1
  55. package/package.json +11 -9
package/README.md CHANGED
@@ -1,7 +1,21 @@
1
+ # @sqlrooms/deck
2
+
1
3
  Deck.gl integration for SQLRooms with JSON-driven map specs, dataset registry
2
4
  binding, DuckDB-backed or in-memory Arrow datasets, and GeoArrow-first geometry
3
5
  preparation.
4
6
 
7
+ ## Package entry points
8
+
9
+ | Import | Use |
10
+ | ----------------------- | ----------------------------------------------------------------------------------------- |
11
+ | `@sqlrooms/deck` | Host-neutral maps, durable map resources, block-document integration, and authoring tools |
12
+ | `@sqlrooms/deck/mosaic` | Opt-in Mosaic dashboard renderers, configuration helpers, and AI tools |
13
+
14
+ The [Deck.gl example](https://github.com/sqlrooms/examples/tree/main/deckgl)
15
+ shows direct DuckDB-backed maps. The
16
+ [Deck.gl + Mosaic example](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
17
+ shows custom cross-filter integration.
18
+
5
19
  ## Map resources and dashboard adapters
6
20
 
7
21
  Document maps are first-class `deckMaps` resources. The root package export
@@ -39,9 +53,13 @@ resource ownership.
39
53
  ## Installation
40
54
 
41
55
  ```bash
42
- npm install @sqlrooms/deck @sqlrooms/duckdb @sqlrooms/ui
56
+ npm install @sqlrooms/deck @sqlrooms/room-shell apache-arrow
43
57
  ```
44
58
 
59
+ For Mosaic dashboard integration, also install `@sqlrooms/mosaic`,
60
+ `@uwdata/mosaic-core`, and `@uwdata/mosaic-sql`, then import the adapter from
61
+ `@sqlrooms/deck/mosaic`.
62
+
45
63
  ## What This Package Does
46
64
 
47
65
  `@sqlrooms/deck` is the JSON-spec bridge between SQLRooms data and deck.gl:
@@ -60,6 +78,10 @@ instead of hand-constructing deck layer instances in React code.
60
78
 
61
79
  ## Quick Start
62
80
 
81
+ `DeckJsonMap` resolves SQL and table datasets through the current SQLRooms
82
+ store. Render it inside `RoomShell` or `RoomStateProvider` with a store that
83
+ includes DuckDB state. The example below assumes that host is already mounted.
84
+
63
85
  ```tsx
64
86
  import {DeckJsonMap} from '@sqlrooms/deck';
65
87
 
@@ -87,7 +109,8 @@ const spec = {
87
109
  scheme: 'YlOrRd',
88
110
  domain: 'auto',
89
111
  },
90
- getRadius: '@@=6',
112
+ getRadius: 6,
113
+ radiusUnits: 'pixels',
91
114
  radiusMinPixels: 2,
92
115
  },
93
116
  ],
@@ -105,12 +128,78 @@ export function AirportsMap() {
105
128
  geometryEncodingHint: 'wkb',
106
129
  },
107
130
  }}
108
- mapStyle="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
109
131
  />
110
132
  );
111
133
  }
112
134
  ```
113
135
 
136
+ ## Basemaps
137
+
138
+ Deck maps use [OpenFreeMap](https://openfreemap.org/) vector tiles by default:
139
+ **Positron** for light mode and **Dark** for dark mode. No API key or registration
140
+ is required. The hosted styles include attribution, which MapLibre displays.
141
+ Maps work immediately with `createDeckMapsSlice()` or `DeckJsonMap`, without
142
+ additional configuration.
143
+
144
+ New map resources and dashboard panels save `mapStyle: 'light'` or
145
+ `'dark'` using the app theme at creation. Changing the app theme later
146
+ does not change existing maps. The **Basemap** dropdown in map settings selects
147
+ Light or Dark, including for maps with custom layer configurations. The selection
148
+ survives dataset changes, config updates, and saved-workspace reloads; keys and
149
+ generated style objects are not stored in map resources. Applications creating
150
+ configs outside the browser can use `getDefaultDeckMapStyle(theme)` explicitly.
151
+
152
+ Bare `DeckJsonMap` instances and older saved maps without a style continue to
153
+ follow the app theme. Choose a basemap in settings to persist it in an older map.
154
+ Explicit custom `mapStyle` URLs and `mapProps.mapStyle` objects remain supported;
155
+ the selector displays **Custom** until a built-in style is selected.
156
+
157
+ For custom basemaps, supply a `DeckMapBasemapProvider` callback:
158
+
159
+ ```tsx
160
+ createDeckMapsSlice({basemapProvider: (theme) => customStyles[theme]});
161
+ ```
162
+
163
+ Return stable MapLibre style objects or URLs. Direct `DeckJsonMap` callers
164
+ can pass `basemapProvider` as a prop, overriding the room's provider without
165
+ requiring the Deck maps slice. The existing room store is still required for
166
+ dataset preparation. Explicit custom map styles take precedence over
167
+ the provider.
168
+
169
+ `DeckMapDefaultStylesProvider` and `useDeckMapDefaultStyles` are deprecated.
170
+ To migrate an existing `styles={{light, dark}}` wrapper, keep the style objects
171
+ and pass `basemapProvider: (theme) => styles[theme]` to `createDeckMapsSlice`,
172
+ then remove the wrapper. For individual map overrides, pass the same callback
173
+ to `DeckJsonMap`. Code that reads `useDeckMapDefaultStyles` should use the room's
174
+ `deckMaps.basemapProvider` or the supplied callback instead.
175
+ The context API remains functional for backward compatibility, as a fallback
176
+ when no callback style is available. Removal is reserved for a future breaking
177
+ release.
178
+
179
+ Protomaps remains an optional provider for applications supplying their own key:
180
+
181
+ ```tsx
182
+ import {
183
+ createDeckMapsSlice,
184
+ createProtomapsBasemapProvider,
185
+ } from '@sqlrooms/deck';
186
+
187
+ createDeckMapsSlice({
188
+ basemapProvider: createProtomapsBasemapProvider(protomapsApiKey),
189
+ });
190
+ ```
191
+
192
+ The helper creates and reuses the light/dark style objects. Its callback lives
193
+ outside `deckMaps.config`, so its key is not persisted with maps. Obtain a
194
+ browser-visible key from [Protomaps](https://protomaps.com/api) and configure
195
+ allowed origins. A missing or blank key falls back to OpenFreeMap.
196
+ Document maps, dashboard maps, and `DeckJsonMap` inherit the room's provider.
197
+
198
+ `createProtomapsStyle(flavor, apiKey)` creates a MapLibre style object, and
199
+ `createProtomapsDefaultStyles(apiKey)` creates the Protomaps light/dark pair.
200
+ `DECK_MAP_BASEMAP_STYLES` lists the provider-neutral IDs and labels. Existing
201
+ `protomaps-light`/`protomaps-dark` selections are recognized as light/dark aliases.
202
+
114
203
  ## Auto Spec Generation
115
204
 
116
205
  If you want a starter JSON spec instead of writing every layer manually, use
@@ -164,10 +253,11 @@ const spec = createDeckJsonSpecFromDatasets({
164
253
 
165
254
  ## Mosaic Dashboard Renderer
166
255
 
167
- `@sqlrooms/deck` can contribute a `deck-json-map` panel renderer to
256
+ `@sqlrooms/deck/mosaic` contributes a `deck-json-map` panel renderer to
168
257
  `@sqlrooms/mosaic` dashboards without making the Mosaic package depend on
169
- deck.gl or MapLibre. Pass the renderer when creating the Mosaic dashboard
170
- slice.
258
+ deck.gl or MapLibre. `createDeckMapDashboardSliceOptions()` installs the map
259
+ renderer and add-panel action alongside the default Mosaic renderers, actions,
260
+ and chart types.
171
261
 
172
262
  The dashboard renderer exposes `DeckMapDashboardSettings` through its renderer
173
263
  definition. `DeckMapBlockSettings` is also exported for block-document hosts
@@ -176,20 +266,13 @@ that embed maps as stateful blocks.
176
266
  ```tsx
177
267
  import {
178
268
  createDeckMapDashboardPanelConfig,
179
- DECK_MAP_DASHBOARD_PANEL_TYPE,
180
- deckMapDashboardPanelRenderer,
181
- } from '@sqlrooms/deck';
182
- import {
183
- createDefaultMosaicDashboardPanelRenderers,
184
- createMosaicDashboardSlice,
185
- MosaicDashboard,
186
- } from '@sqlrooms/mosaic';
187
-
188
- const dashboardSlice = createMosaicDashboardSlice({
189
- panelRenderers: createDefaultMosaicDashboardPanelRenderers({
190
- [DECK_MAP_DASHBOARD_PANEL_TYPE]: deckMapDashboardPanelRenderer,
191
- }),
192
- });
269
+ createDeckMapDashboardSliceOptions,
270
+ } from '@sqlrooms/deck/mosaic';
271
+ import {createMosaicDashboardSlice, MosaicDashboard} from '@sqlrooms/mosaic';
272
+
273
+ const dashboardSlice = createMosaicDashboardSlice(
274
+ createDeckMapDashboardSliceOptions(),
275
+ );
193
276
 
194
277
  function Dashboard() {
195
278
  return <MosaicDashboard dashboardId="geo" />;
@@ -264,6 +347,10 @@ as durable resources without creating a dashboard. Compose
264
347
  `ensureDeckMapResourceState(...)` for a durable map id, and render it with
265
348
  `DeckMapBlockRenderer`.
266
349
 
350
+ See [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
351
+ for the surrounding artifact, renderer-provider, persistence, and ownership
352
+ setup.
353
+
267
354
  Runtime issue recovery can call `deckMaps.clearMapIssue(mapId, kind)` to clear
268
355
  only a matching issue kind; omit `kind` when the map state should clear any
269
356
  stale issue. Replacing a map config clears its prior render issue, while data
@@ -409,6 +496,24 @@ layers (e.g. render points under map labels) and reduces WebGL context usage.
409
496
  Set `interleaved` to `false` to render deck layers in a separate overlay canvas
410
497
  on top of all basemap layers (uses an additional WebGL context per map).
411
498
 
499
+ MapLibre's drawing buffer is preserved by default so DOM image capture can
500
+ include the basemap and interleaved deck layers after a frame finishes.
501
+ In separate-overlay mode, deck.gl/luma.gl also preserves its drawing buffer by
502
+ default. Both map canvases are identified for capture validation, including
503
+ overlays with a custom deck ID. A lost context or disabled preservation on
504
+ either canvas causes the CLI rendering tools to return an actionable error.
505
+ Capture readiness also tracks dataset preparation, MapLibre tile rendering,
506
+ and deck layers' asynchronous resources. While any map in the requested surface
507
+ is still loading, the CLI rendering tools return an error asking the caller to
508
+ wait and retry, instead of returning an incomplete image.
509
+ Preserving the buffer can increase GPU memory use and reduce rendering
510
+ performance. Hosts that do not need image capture can opt out by setting
511
+ `mapProps.canvasContextAttributes.preserveDrawingBuffer` to `false`.
512
+ Changing this context option requires remounting the map.
513
+ To opt out for a separate deck overlay, set
514
+ `deckProps.deviceProps.webgl.preserveDrawingBuffer` to `false` and remount
515
+ the map; DOM image capture will then be unavailable.
516
+
412
517
  ```tsx
413
518
  {
414
519
  /* Default (interleaved): */
@@ -653,7 +758,7 @@ Deck caches only the expensive geometry preparation layer on top.
653
758
 
654
759
  ## Supported Layers
655
760
 
656
- The current curated layer set is:
761
+ The map-resource validator and settings UI support this curated layer set:
657
762
 
658
763
  - `GeoArrowScatterplotLayer`
659
764
  - `GeoArrowHeatmapLayer`
@@ -665,6 +770,10 @@ The current curated layer set is:
665
770
  - `GeoArrowH3HexagonLayer`
666
771
  - `GeoJsonLayer`
667
772
 
773
+ The lower-level `DeckJsonMap` JSON converter also registers
774
+ `GeoArrowSolidPolygonLayer`. Durable resource and AI authoring intentionally do
775
+ not accept it; use `GeoArrowPolygonLayer` for authored map resources.
776
+
668
777
  GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls
669
778
  back to decoding and GeoJSON-binary preparation, with promotion available
670
779
  for point-focused GeoArrow layers such as `GeoArrowScatterplotLayer`,
@@ -684,7 +793,7 @@ not rewritten — the wrap is an outer pipeline query. Prefer writing
684
793
  `ST_AsWKB(...)` in transforms when you control the SQL; the pipeline covers
685
794
  bare `ST_Point(...)` / table `GEOMETRY` columns for every authoring surface.
686
795
 
687
- ## AI map config prepare
796
+ ## Prepare AI-authored map configs
688
797
 
689
798
  AI and host tooling should prepare authored Deck map configs through the shared
690
799
  helpers exported from `@sqlrooms/deck`:
@@ -708,6 +817,11 @@ helpers exported from `@sqlrooms/deck`:
708
817
  Skips unknown-field rejection when `transformSql` / `sqlQuery` is present
709
818
  (aliases may be transform-only).
710
819
 
820
+ Mosaic-specific AI helpers such as `createDeckMapDashboardTool()`,
821
+ `createDashboardWithDeckMapAiTools()`, and
822
+ `createDashboardAgentToolWithDeckMaps()` are exported from
823
+ `@sqlrooms/deck/mosaic`.
824
+
711
825
  Durable resource writes also run `getDeckMapResourceConfigIssues` /
712
826
  `assertDeckMapResourceConfig` for syntax, supported layer types, and type/scheme
713
827
  compatibility (e.g. `quantile` + `Viridis` is rejected; layer `@@type` must be
@@ -724,3 +838,11 @@ Keep the spec serializable, then pass runtime behavior separately:
724
838
 
725
839
  This lets the spec stay stable for storage, validation, and future AI-assisted
726
840
  generation while still supporting interactive React behavior at runtime.
841
+
842
+ ## More resources
843
+
844
+ - [`@sqlrooms/deck` API reference](https://sqlrooms.org/api/deck/)
845
+ - [Deck.gl example source](https://github.com/sqlrooms/examples/tree/main/deckgl)
846
+ - [Deck.gl + Mosaic example source](https://github.com/sqlrooms/examples/tree/main/deckgl-mosaic)
847
+ - [Blocks and Block Documents](https://sqlrooms.org/blocks-and-documents.html)
848
+ - [Commands](https://sqlrooms.org/commands.html)
@@ -1 +1 @@
1
- {"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAYA,OAAO,kCAAkC,CAAC;AAyB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AA+QjB,eAAO,MAAM,WAAW,gHAuVvB,CAAC"}
1
+ {"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAaA,OAAO,kCAAkC,CAAC;AAyB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AA8SjB,eAAO,MAAM,WAAW,gHAsXvB,CAAC"}
@@ -2,14 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { JSONConverter } from '@deck.gl/json';
3
3
  import { MapboxOverlay } from '@deck.gl/mapbox';
4
4
  import { ColorScaleLegend } from '@sqlrooms/color-scales';
5
+ import { useBaseRoomStore } from '@sqlrooms/room-store';
5
6
  import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger, useTheme, } from '@sqlrooms/ui';
6
7
  import { ChevronRightIcon } from 'lucide-react';
7
8
  import 'maplibre-gl/dist/maplibre-gl.css';
8
- import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
9
+ import { useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, } from 'react';
9
10
  import { forwardRef } from 'react';
10
11
  import Map, { useControl } from 'react-map-gl/maplibre';
11
12
  import { DeckJsonMapSpec } from './DeckJsonMapSpec';
12
- import { resolveDeckMapStyle, useDeckMapDefaultStyles, } from './DeckMapDefaultStylesProvider';
13
+ import { resolveDeckMapStyle } from './basemap';
14
+ import { useDeckMapDefaultStyles } from './DeckMapDefaultStylesProvider';
13
15
  import { normalizeDatasets } from './datasets/normalizeDatasets';
14
16
  import { usePreparedDatasetStates } from './datasets/usePreparedDatasetStates';
15
17
  import { createDeckJsonConfiguration } from './json/createDeckJsonConfiguration';
@@ -18,8 +20,8 @@ import { getLayerCompatibility } from './json/layerCompatibility';
18
20
  import { resolveDatasetId } from './json/layerConfig';
19
21
  import { buildDeckMapJumpToOptions } from './mapFit';
20
22
  const DEFAULT_MAP_STYLES = {
21
- light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
22
- dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
23
+ light: 'https://tiles.openfreemap.org/styles/positron',
24
+ dark: 'https://tiles.openfreemap.org/styles/dark',
23
25
  };
24
26
  function parseSpec(spec) {
25
27
  try {
@@ -121,7 +123,13 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
121
123
  const overlay = useControl(() => new MapboxOverlay({ interleaved }));
122
124
  const prevLayerKeyRef = useRef('');
123
125
  const [clearing, setClearing] = useState(false);
126
+ const captureStatusRef = useRef(null);
124
127
  const layers = deckProps.layers;
128
+ const captureLayers = useMemo(() => (layers?.flat(Infinity).filter(Boolean) ?? []), [layers]);
129
+ useLayoutEffect(() => {
130
+ // A previous frame no longer represents the current layers.
131
+ captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', 'true');
132
+ }, [layers, clearing]);
125
133
  const layerKey = Array.isArray(layers)
126
134
  ? layers.map(getDeckLayerTypeName).join(',')
127
135
  : '';
@@ -135,23 +143,44 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
135
143
  }, [layerKey, clearing]);
136
144
  useEffect(() => {
137
145
  if (clearing && overlay) {
138
- overlay.setProps({ layers: [] });
146
+ // The temporary empty frame must not advertise capture readiness.
147
+ overlay.setProps({ layers: [], onAfterRender: () => { } });
139
148
  requestAnimationFrame(() => setClearing(false));
140
149
  }
141
150
  }, [clearing, overlay]);
142
- if (!clearing && overlay) {
143
- overlay.setProps(deckProps);
144
- }
145
- return null;
151
+ useLayoutEffect(() => {
152
+ if (clearing || !overlay)
153
+ return;
154
+ overlay.setProps({
155
+ ...deckProps,
156
+ onLoad: () => {
157
+ // The separate canvas is created asynchronously. Mark it once deck
158
+ // initializes, including when the host supplies a custom canvas ID.
159
+ overlay.getCanvas()?.setAttribute('data-sqlrooms-deck-canvas', '');
160
+ if (typeof deckProps.onLoad === 'function')
161
+ deckProps.onLoad();
162
+ },
163
+ onAfterRender: (context) => {
164
+ const loaded = captureLayers.every((layer) => layer.isLoaded);
165
+ captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', String(!loaded));
166
+ if (typeof deckProps.onAfterRender === 'function') {
167
+ deckProps.onAfterRender(context);
168
+ }
169
+ },
170
+ });
171
+ }, [clearing, overlay, deckProps, captureLayers]);
172
+ return (_jsx("span", { hidden: true, ref: captureStatusRef, "data-sqlrooms-map-loading": "true" }));
146
173
  }
147
174
  function DeckMapRenderingErrorOverlay({ error }) {
148
175
  return (_jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center p-4", children: _jsx("div", { className: "max-w-sm rounded-md border border-red-200 bg-red-50/95 p-4 text-sm text-red-700 shadow-sm", children: _jsxs(Collapsible, { children: [_jsxs("div", { className: "flex items-start gap-1", children: [_jsx("span", { children: "Map couldn't be rendered" }), _jsx(CollapsibleTrigger, { className: "group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100", "aria-label": "Show error details", children: _jsx(ChevronRightIcon, { className: "h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90" }) })] }), _jsx(CollapsibleContent, { children: _jsx("pre", { className: "mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap", children: error.message }) })] }) }) }));
149
176
  }
150
- export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, mapStyle, interleaved = true, deckProps, mapProps, showLegends = true, className, children, onDatasetStatesChange, onRenderingError, }, ref) {
177
+ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, mapStyle, basemapProvider, interleaved = true, deckProps, mapProps, showLegends = true, className, children, onDatasetStatesChange, onRenderingError, }, ref) {
151
178
  const normalizedDatasets = useMemo(() => normalizeDatasets(datasets), [datasets]);
152
179
  const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());
153
180
  const datasetIds = useMemo(() => JSON.parse(datasetIdKey), [datasetIdKey]);
154
181
  const datasetStates = usePreparedDatasetStates(normalizedDatasets);
182
+ const [mapReady, setMapReady] = useState(false);
183
+ const datasetsLoading = datasetIds.some((id) => !datasetStates[id] || datasetStates[id]?.status === 'loading');
155
184
  const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);
156
185
  const { spec: parsedSpec, error: specError } = useMemo(() => parseSpec(spec), [spec]);
157
186
  const availableSpec = useMemo(() => parsedSpec
@@ -348,11 +377,19 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
348
377
  }, [extraMapProps]);
349
378
  const { resolvedTheme } = useTheme();
350
379
  const hostDefaultMapStyles = useDeckMapDefaultStyles();
380
+ const roomBasemapProvider = useBaseRoomStore((state) => state.deckMaps?.basemapProvider);
351
381
  const mergedMapProps = {
352
382
  ...extraMapProps,
383
+ // DOM image capture reads the canvas after the render frame has ended.
384
+ // Interleaved deck layers share this buffer with the basemap.
385
+ canvasContextAttributes: {
386
+ preserveDrawingBuffer: true,
387
+ ...mapProps?.canvasContextAttributes,
388
+ },
353
389
  mapStyle: resolveDeckMapStyle({
354
390
  mapStyle,
355
391
  mapPropsMapStyle: mapProps?.mapStyle,
392
+ basemapProvider: basemapProvider ?? roomBasemapProvider,
356
393
  hostDefaultStyles: hostDefaultMapStyles,
357
394
  resolvedTheme,
358
395
  fallbackStyles: DEFAULT_MAP_STYLES,
@@ -365,8 +402,17 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
365
402
  datasetStates,
366
403
  })
367
404
  : [], [availableSpec, datasetIds, showLegends, datasetStates]);
368
- return (_jsxs("div", { className: cn('relative h-full w-full', className), children: [finalDeckPropsResult.error ? (_jsx(DeckMapRenderingErrorOverlay, { error: finalDeckPropsResult.error })) : null, _jsxs(Map, { ref: mapRef, ...mergedMapProps, ...(initialViewState
405
+ return (_jsxs("div", { className: cn('relative h-full w-full', className), "data-sqlrooms-map-loading": !mapReady || datasetsLoading, children: [finalDeckPropsResult.error ? (_jsx(DeckMapRenderingErrorOverlay, { error: finalDeckPropsResult.error })) : null, _jsxs(Map, { ref: mapRef, ...mergedMapProps, ...(initialViewState
369
406
  ? { initialViewState: initialViewState }
370
- : {}), onLoad: handleMapLoad, style: { width: '100%', height: '100%', ...mapProps?.style }, children: [_jsx(DeckOverlayControl, { interleaved: interleaved, ...overlayDeckProps }), children] }), renderDatasetErrorOverlay(datasetStates), !hasRenderingError && showLegends ? (_jsxs("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10 max-w-56", children: [hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "mb-1", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null, _jsx(ColorScaleLegend, { legends: legends })] })) : hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null] }));
407
+ : {}), onLoad: handleMapLoad, onData: (event) => {
408
+ setMapReady(false);
409
+ mapProps?.onData?.(event);
410
+ }, onMoveStart: (event) => {
411
+ setMapReady(false);
412
+ mapProps?.onMoveStart?.(event);
413
+ }, onRender: (event) => {
414
+ setMapReady(event.target.loaded());
415
+ mapProps?.onRender?.(event);
416
+ }, style: { width: '100%', height: '100%', ...mapProps?.style }, children: [_jsx(DeckOverlayControl, { interleaved: interleaved, ...overlayDeckProps }), children] }), renderDatasetErrorOverlay(datasetStates), !hasRenderingError && showLegends ? (_jsxs("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10 max-w-56", children: [hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "mb-1", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null, _jsx(ColorScaleLegend, { legends: legends })] })) : hasTripsLayer && tripsMaxTimeValue > 0 ? (_jsx("div", { className: "pointer-events-none absolute bottom-2 left-2 z-10", children: _jsx(TripsTimeControl, { currentTime: tripsCurrentTime, maxTime: tripsMaxTimeValue, playing: tripsPlaying, onPlayPause: handleTripsPlayPause, onSeek: handleTripsSeek }) })) : null] }));
371
417
  });
372
418
  //# sourceMappingURL=DeckJsonMap.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeckJsonMap.js","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"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","sourcesContent":["import {JSONConverter} from '@deck.gl/json';\nimport {MapboxOverlay} from '@deck.gl/mapbox';\nimport {ColorScaleLegend} from '@sqlrooms/color-scales';\nimport {\n cn,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n ResolvedTheme,\n useTheme,\n} from '@sqlrooms/ui';\nimport {ChevronRightIcon} from 'lucide-react';\nimport 'maplibre-gl/dist/maplibre-gl.css';\nimport {\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type FC,\n} from 'react';\nimport {forwardRef} from 'react';\nimport Map, {useControl} from 'react-map-gl/maplibre';\nimport {ZodError} from 'zod';\nimport {DeckJsonMapSpec} from './DeckJsonMapSpec';\nimport {\n resolveDeckMapStyle,\n useDeckMapDefaultStyles,\n} from './DeckMapDefaultStylesProvider';\nimport {normalizeDatasets} from './datasets/normalizeDatasets';\nimport {usePreparedDatasetStates} from './datasets/usePreparedDatasetStates';\nimport {createDeckJsonConfiguration} from './json/createDeckJsonConfiguration';\nimport {extractColorScaleLegends} from './json/extractColorScaleLegends';\nimport {getLayerCompatibility} from './json/layerCompatibility';\nimport {resolveDatasetId} from './json/layerConfig';\nimport {buildDeckMapJumpToOptions} from './mapFit';\nimport type {\n DeckJsonMapHandle,\n DeckJsonMapProps,\n PreparedDeckDatasetState,\n} from './types';\n\nconst DEFAULT_MAP_STYLES: Record<ResolvedTheme, string> = {\n light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',\n dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',\n};\n\nfunction parseSpec(spec: DeckJsonMapProps['spec']) {\n try {\n const parsedValue = typeof spec === 'string' ? JSON.parse(spec) : spec;\n const validatedSpec = DeckJsonMapSpec.safeParse(parsedValue);\n if (!validatedSpec.success) {\n return {\n spec: null,\n error: new Error(formatSpecValidationError(validatedSpec.error)),\n };\n }\n\n return {\n spec: validatedSpec.data,\n error: null,\n };\n } catch (error) {\n return {\n spec: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction formatSpecValidationError(error: ZodError) {\n const issues = error.issues.map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : 'spec';\n return `${path}: ${issue.message}`;\n });\n\n return `Invalid DeckJsonMap spec. ${issues.join('; ')}`;\n}\n\nfunction extractFallbackDeckProps(spec: Record<string, unknown> | null) {\n if (!spec) {\n return {};\n }\n\n const fallbackProps: Record<string, unknown> = {};\n for (const key of ['initialViewState', 'viewState', 'controller']) {\n if (key in spec) {\n fallbackProps[key] = spec[key];\n }\n }\n\n return fallbackProps;\n}\n\nfunction filterUnavailableLayers(\n spec: Record<string, unknown>,\n datasetIds: string[],\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const layers = Array.isArray(spec.layers) ? spec.layers : [];\n const filteredLayers = layers.filter((layer) => {\n if (!layer || typeof layer !== 'object') {\n return true;\n }\n\n const layerProps = layer as Record<string, unknown>;\n const layerName = String(layerProps['@@type'] ?? '');\n const compatibility = getLayerCompatibility(layerName);\n if (!compatibility) {\n return true;\n }\n\n const datasetId = resolveDatasetId(layerProps, datasetIds);\n if (!datasetId) {\n return true;\n }\n\n const datasetState = datasetStates[datasetId];\n if (!datasetState) {\n return false;\n }\n\n return datasetState.status !== 'error';\n });\n\n return {\n ...spec,\n layers: filteredLayers,\n };\n}\n\nfunction renderDatasetErrorOverlay(\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const failedDatasets = Object.entries(datasetStates).filter(\n (\n entry,\n ): entry is [\n string,\n Extract<PreparedDeckDatasetState, {status: 'error'}>,\n ] => entry[1].status === 'error',\n );\n\n if (!failedDatasets.length) {\n return null;\n }\n\n return (\n <div className=\"pointer-events-none absolute inset-x-4 top-4 z-10 space-y-2\">\n {failedDatasets.map(([datasetId, state]) => (\n <div\n key={datasetId}\n className=\"rounded-md border border-red-200 bg-red-50/95 px-3 py-2 text-sm text-red-700 shadow-sm\"\n >\n {`Dataset \"${datasetId}\" failed: ${state.error.message}`}\n </div>\n ))}\n </div>\n );\n}\n\nconst TripsTimeControl: FC<{\n currentTime: number;\n maxTime: number;\n playing: boolean;\n onPlayPause: () => void;\n onSeek: (time: number) => void;\n}> = ({currentTime, maxTime, playing, onPlayPause, onSeek}) => {\n const [collapsed, setCollapsed] = useState(false);\n const progress = maxTime > 0 ? currentTime / maxTime : 0;\n\n if (collapsed) {\n return (\n <button\n type=\"button\"\n onClick={() => setCollapsed(false)}\n className=\"border-border/70 bg-background/90 text-muted-foreground hover:text-foreground pointer-events-auto flex w-fit items-center gap-1 rounded-md border px-2 py-1 text-[10px] leading-none font-medium shadow-sm backdrop-blur-sm transition-colors\"\n >\n Timeline\n </button>\n );\n }\n\n return (\n <div className=\"bg-background/90 border-border/70 pointer-events-auto relative flex items-center gap-1.5 rounded-md border px-2 py-1.5 pr-7 shadow-sm backdrop-blur-sm\">\n <button\n type=\"button\"\n onClick={onPlayPause}\n className=\"text-foreground hover:bg-accent flex h-5 w-5 shrink-0 items-center justify-center rounded\"\n title={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <rect x=\"1\" y=\"1\" width=\"3\" height=\"8\" />\n <rect x=\"6\" y=\"1\" width=\"3\" height=\"8\" />\n </svg>\n ) : (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <polygon points=\"2,1 9,5 2,9\" />\n </svg>\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1000}\n value={Math.round(progress * 1000)}\n onChange={(e) => onSeek((Number(e.target.value) / 1000) * maxTime)}\n className=\"h-1 w-24 flex-1 cursor-pointer\"\n />\n <button\n type=\"button\"\n onClick={() => setCollapsed(true)}\n className=\"text-muted-foreground hover:text-foreground absolute top-1.5 right-1.5 flex h-4 w-4 items-center justify-center rounded-sm transition-colors\"\n title=\"Collapse\"\n >\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n <path\n d=\"M1 1L7 7M7 1L1 7\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n );\n};\n\nfunction getDeckLayerTypeName(layer: unknown): string {\n if (!layer || typeof layer !== 'object') return '?';\n const ctor = (\n layer as {\n constructor?: {layerName?: string; name?: string};\n }\n ).constructor;\n // Prefer static layerName: table-to-RecordBatch adapters all share\n // constructor.name \"Adapter\", which would miss heatmap ↔ scatterplot swaps.\n return ctor?.layerName ?? ctor?.name ?? '?';\n}\n\n// Workaround for deck.gl HeatmapLayer not releasing its WebGL framebuffer\n// when replaced by another layer type (e.g. switching heatmap → scatterplot).\n// We detect layer class changes and clear layers for one animation frame,\n// giving deck.gl a chance to finalize the old layer's resources before\n// initializing the new one.\nfunction DeckOverlayControl({\n interleaved,\n ...deckProps\n}: {interleaved: boolean} & Record<string, unknown>) {\n const overlay = useControl<MapboxOverlay>(\n () => new MapboxOverlay({interleaved}),\n );\n const prevLayerKeyRef = useRef<string>('');\n const [clearing, setClearing] = useState(false);\n\n const layers = deckProps.layers as unknown[] | undefined;\n const layerKey = Array.isArray(layers)\n ? layers.map(getDeckLayerTypeName).join(',')\n : '';\n\n useEffect(() => {\n if (\n prevLayerKeyRef.current &&\n prevLayerKeyRef.current !== layerKey &&\n !clearing\n ) {\n setClearing(true);\n }\n prevLayerKeyRef.current = layerKey;\n }, [layerKey, clearing]);\n\n useEffect(() => {\n if (clearing && overlay) {\n overlay.setProps({layers: []});\n requestAnimationFrame(() => setClearing(false));\n }\n }, [clearing, overlay]);\n\n if (!clearing && overlay) {\n overlay.setProps(deckProps);\n }\n\n return null;\n}\n\nfunction DeckMapRenderingErrorOverlay({error}: {error: Error}) {\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center p-4\">\n <div className=\"max-w-sm rounded-md border border-red-200 bg-red-50/95 p-4 text-sm text-red-700 shadow-sm\">\n <Collapsible>\n <div className=\"flex items-start gap-1\">\n <span>{\"Map couldn't be rendered\"}</span>\n <CollapsibleTrigger\n className=\"group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100\"\n aria-label=\"Show error details\"\n >\n <ChevronRightIcon className=\"h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90\" />\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <pre className=\"mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap\">\n {error.message}\n </pre>\n </CollapsibleContent>\n </Collapsible>\n </div>\n </div>\n );\n}\n\nexport const DeckJsonMap = forwardRef<DeckJsonMapHandle, DeckJsonMapProps>(\n function DeckJsonMap(\n {\n spec,\n datasets,\n mapStyle,\n interleaved = true,\n deckProps,\n mapProps,\n showLegends = true,\n className,\n children,\n onDatasetStatesChange,\n onRenderingError,\n },\n ref,\n ) {\n const normalizedDatasets = useMemo(\n () => normalizeDatasets(datasets),\n [datasets],\n );\n const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());\n const datasetIds = useMemo(\n () => JSON.parse(datasetIdKey) as string[],\n [datasetIdKey],\n );\n const datasetStates = usePreparedDatasetStates(normalizedDatasets);\n const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);\n\n const {spec: parsedSpec, error: specError} = useMemo(\n () => parseSpec(spec),\n [spec],\n );\n\n const availableSpec = useMemo(\n () =>\n parsedSpec\n ? filterUnavailableLayers(parsedSpec, datasetIds, datasetStates)\n : null,\n [parsedSpec, datasetIds, datasetStates],\n );\n\n const converter = useMemo(\n () =>\n new JSONConverter({\n configuration: createDeckJsonConfiguration({\n datasetStates,\n datasetIds,\n }),\n onJSONChange: () => {},\n }),\n [datasetIds, datasetStates],\n );\n\n const convertedDeckPropsResult = useMemo(() => {\n if (!availableSpec) {\n return {props: null, error: specError};\n }\n\n try {\n return {\n props: converter.convert(availableSpec) as Record<string, unknown>,\n error: null,\n };\n } catch (error) {\n return {\n props: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }, [availableSpec, converter, specError]);\n\n // Animation for TripsLayer — update currentTime on each frame.\n // Only activate when the trips dataset is ready to avoid spinning the RAF\n // loop while data is still loading.\n const hasTripsLayer = useMemo(() => {\n if (!availableSpec || !Array.isArray(availableSpec.layers)) return false;\n return availableSpec.layers.some((l: unknown) => {\n if (\n !l ||\n typeof l !== 'object' ||\n (l as {'@@type'?: string})['@@type'] !== 'GeoArrowTripsLayer'\n )\n return false;\n const dsId = resolveDatasetId(l as Record<string, unknown>, datasetIds);\n if (!dsId) return true;\n const state = datasetStates[dsId];\n return state?.status === 'ready';\n });\n }, [availableSpec, datasetIds, datasetStates]);\n\n const [tripsTime, setTripsTime] = useState(0);\n const [tripsPlaying, setTripsPlaying] = useState(true);\n const tripsTimeRef = useRef(0);\n\n useEffect(() => {\n if (!hasTripsLayer || !tripsPlaying) return;\n let raf: number;\n let lastFrame = Date.now();\n const tick = () => {\n const now = Date.now();\n tripsTimeRef.current += now - lastFrame;\n lastFrame = now;\n setTripsTime(tripsTimeRef.current);\n raf = requestAnimationFrame(tick);\n };\n lastFrame = Date.now();\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [hasTripsLayer, tripsPlaying]);\n\n const handleTripsPlayPause = useCallback(() => {\n setTripsPlaying((p) => !p);\n }, []);\n\n const handleTripsSeek = useCallback((time: number) => {\n const rawTime = time * 30;\n tripsTimeRef.current = rawTime;\n setTripsTime(rawTime);\n }, []);\n\n const finalDeckPropsResult = convertedDeckPropsResult;\n\n const onRenderingErrorRef = useRef(onRenderingError);\n useEffect(() => {\n onRenderingErrorRef.current = onRenderingError;\n }, [onRenderingError]);\n\n useEffect(() => {\n if (finalDeckPropsResult.error) {\n console.error(finalDeckPropsResult.error);\n onRenderingErrorRef.current?.(finalDeckPropsResult.error);\n }\n }, [finalDeckPropsResult.error]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current = onDatasetStatesChange;\n }, [onDatasetStatesChange]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current?.(datasetStates);\n }, [datasetStates]);\n\n const fallbackDeckProps = useMemo(\n () => extractFallbackDeckProps(availableSpec),\n [availableSpec],\n );\n const convertedDeckProps = (finalDeckPropsResult.props ??\n fallbackDeckProps ??\n {}) as Record<string, unknown>;\n const extraDeckProps = (deckProps ?? {}) as Record<string, unknown>;\n const extraMapProps = (mapProps ?? {}) as Record<string, unknown>;\n const hasRenderingError = Boolean(finalDeckPropsResult.error);\n\n // Separate stable layer analysis from per-frame animation to avoid\n // re-running .map() over all layers 60 times per second.\n const baseLayers = useMemo(() => {\n const mergedLayers = hasRenderingError\n ? []\n : (deckProps?.layers ??\n (convertedDeckProps.layers as unknown[] | undefined) ??\n []);\n if (!hasTripsLayer || !Array.isArray(mergedLayers))\n return {layers: mergedLayers, maxTs: 0, tripsIndices: [] as number[]};\n let computedMaxTs = 0;\n const tripsIndices: number[] = [];\n for (let i = 0; i < mergedLayers.length; i++) {\n const layer = mergedLayers[i];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {props?: Record<string, unknown>};\n const maxTs = (layerObj.props as Record<string, unknown> | undefined)\n ?._tripsMaxTimestamp as number | undefined;\n if (maxTs && maxTs > 0) {\n computedMaxTs = Math.max(computedMaxTs, maxTs);\n tripsIndices.push(i);\n }\n }\n return {layers: mergedLayers, maxTs: computedMaxTs, tripsIndices};\n }, [\n hasTripsLayer,\n hasRenderingError,\n deckProps?.layers,\n convertedDeckProps.layers,\n ]);\n\n // Per-frame: only clone trips layers with updated currentTime\n const animatedLayers = useMemo(() => {\n const {layers, tripsIndices} = baseLayers;\n if (!tripsIndices.length) return layers;\n if (!Array.isArray(layers)) return layers;\n const result = layers.slice();\n for (const idx of tripsIndices) {\n const layer = result[idx];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {\n props?: Record<string, unknown>;\n clone?: (props: Record<string, unknown>) => unknown;\n };\n if (!layerObj.clone) continue;\n const props = layerObj.props as Record<string, unknown> | undefined;\n const maxTs = (props?._tripsMaxTimestamp as number | undefined) ?? 0;\n const trailFraction =\n (props?._trailLengthFactor as number | undefined) ?? 0.4;\n const widthPx = (props?.widthMinPixels as number | undefined) ?? 3;\n const animationSpeed = 30;\n const currentTime = (tripsTime / animationSpeed) % maxTs;\n result[idx] = layerObj.clone({\n currentTime,\n trailLength: maxTs * trailFraction,\n widthMinPixels: widthPx,\n });\n }\n return result;\n }, [baseLayers, tripsTime]);\n const tripsMaxTimeValue = baseLayers.maxTs;\n\n const tripsCurrentTime = useMemo(() => {\n if (!tripsMaxTimeValue || tripsMaxTimeValue <= 0) return 0;\n return (tripsTime / 30) % tripsMaxTimeValue;\n }, [tripsTime, tripsMaxTimeValue]);\n\n const mergedDeckProps = {\n ...convertedDeckProps,\n ...extraDeckProps,\n layers: animatedLayers,\n };\n\n // overlayDeckProps should not contain viewState/initialViewState\n const {\n initialViewState,\n viewState: _vs,\n onViewStateChange: _onViewStateChange,\n ...overlayDeckProps\n } = mergedDeckProps as Record<string, unknown>;\n void _vs;\n void _onViewStateChange;\n\n const mapRef = useRef<{jumpTo: (opts: object) => void} | null>(null);\n const pendingJumpRef = useRef<object | null>(null);\n\n useImperativeHandle(\n ref,\n () => ({\n jumpTo(opts) {\n const jumpOpts = buildDeckMapJumpToOptions(opts);\n if (mapRef.current) {\n mapRef.current.jumpTo(jumpOpts);\n } else {\n pendingJumpRef.current = jumpOpts;\n }\n },\n }),\n [],\n );\n\n const handleMapLoad = useCallback(\n (event?: unknown) => {\n if (pendingJumpRef.current && mapRef.current) {\n mapRef.current.jumpTo(pendingJumpRef.current);\n pendingJumpRef.current = null;\n }\n const externalOnLoad = (\n extraMapProps as {onLoad?: (e?: unknown) => void}\n ).onLoad;\n externalOnLoad?.(event);\n },\n [extraMapProps],\n );\n\n const {resolvedTheme} = useTheme();\n const hostDefaultMapStyles = useDeckMapDefaultStyles();\n\n const mergedMapProps = {\n ...extraMapProps,\n mapStyle: resolveDeckMapStyle({\n mapStyle,\n mapPropsMapStyle: mapProps?.mapStyle,\n hostDefaultStyles: hostDefaultMapStyles,\n resolvedTheme,\n fallbackStyles: DEFAULT_MAP_STYLES,\n }),\n };\n const legends = useMemo(\n () =>\n showLegends\n ? extractColorScaleLegends({\n spec: availableSpec,\n datasetIds,\n datasetStates,\n })\n : [],\n [availableSpec, datasetIds, showLegends, datasetStates],\n );\n\n return (\n <div className={cn('relative h-full w-full', className)}>\n {finalDeckPropsResult.error ? (\n <DeckMapRenderingErrorOverlay error={finalDeckPropsResult.error} />\n ) : null}\n\n <Map\n ref={mapRef as any}\n {...(mergedMapProps as object)}\n {...(initialViewState\n ? {initialViewState: initialViewState as object}\n : {})}\n onLoad={handleMapLoad}\n style={{width: '100%', height: '100%', ...mapProps?.style}}\n >\n <DeckOverlayControl interleaved={interleaved} {...overlayDeckProps} />\n {children}\n </Map>\n\n {renderDatasetErrorOverlay(datasetStates)}\n {!hasRenderingError && showLegends ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10 max-w-56\">\n {hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"mb-1\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n <ColorScaleLegend legends={legends} />\n </div>\n ) : hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n </div>\n );\n },\n);\n"]}
1
+ {"version":3,"file":"DeckJsonMap.js","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"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","sourcesContent":["import type {Layer} from '@deck.gl/core';\nimport {JSONConverter} from '@deck.gl/json';\nimport {MapboxOverlay} from '@deck.gl/mapbox';\nimport {ColorScaleLegend} from '@sqlrooms/color-scales';\nimport {useBaseRoomStore} from '@sqlrooms/room-store';\nimport {\n cn,\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n useTheme,\n} from '@sqlrooms/ui';\nimport {ChevronRightIcon} from 'lucide-react';\nimport 'maplibre-gl/dist/maplibre-gl.css';\nimport {\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type FC,\n} from 'react';\nimport {forwardRef} from 'react';\nimport Map, {useControl} from 'react-map-gl/maplibre';\nimport {ZodError} from 'zod';\nimport {DeckJsonMapSpec} from './DeckJsonMapSpec';\nimport {resolveDeckMapStyle, type DeckMapBasemapProvider} from './basemap';\nimport {useDeckMapDefaultStyles} from './DeckMapDefaultStylesProvider';\nimport type {DeckMapsSliceState} from './DeckMapsSlice';\nimport {normalizeDatasets} from './datasets/normalizeDatasets';\nimport {usePreparedDatasetStates} from './datasets/usePreparedDatasetStates';\nimport {createDeckJsonConfiguration} from './json/createDeckJsonConfiguration';\nimport {extractColorScaleLegends} from './json/extractColorScaleLegends';\nimport {getLayerCompatibility} from './json/layerCompatibility';\nimport {resolveDatasetId} from './json/layerConfig';\nimport {buildDeckMapJumpToOptions} from './mapFit';\nimport type {\n DeckJsonMapHandle,\n DeckJsonMapProps,\n PreparedDeckDatasetState,\n} from './types';\n\nconst DEFAULT_MAP_STYLES = {\n light: 'https://tiles.openfreemap.org/styles/positron',\n dark: 'https://tiles.openfreemap.org/styles/dark',\n} as const;\n\nfunction parseSpec(spec: DeckJsonMapProps['spec']) {\n try {\n const parsedValue = typeof spec === 'string' ? JSON.parse(spec) : spec;\n const validatedSpec = DeckJsonMapSpec.safeParse(parsedValue);\n if (!validatedSpec.success) {\n return {\n spec: null,\n error: new Error(formatSpecValidationError(validatedSpec.error)),\n };\n }\n\n return {\n spec: validatedSpec.data,\n error: null,\n };\n } catch (error) {\n return {\n spec: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n}\n\nfunction formatSpecValidationError(error: ZodError) {\n const issues = error.issues.map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join('.') : 'spec';\n return `${path}: ${issue.message}`;\n });\n\n return `Invalid DeckJsonMap spec. ${issues.join('; ')}`;\n}\n\nfunction extractFallbackDeckProps(spec: Record<string, unknown> | null) {\n if (!spec) {\n return {};\n }\n\n const fallbackProps: Record<string, unknown> = {};\n for (const key of ['initialViewState', 'viewState', 'controller']) {\n if (key in spec) {\n fallbackProps[key] = spec[key];\n }\n }\n\n return fallbackProps;\n}\n\nfunction filterUnavailableLayers(\n spec: Record<string, unknown>,\n datasetIds: string[],\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const layers = Array.isArray(spec.layers) ? spec.layers : [];\n const filteredLayers = layers.filter((layer) => {\n if (!layer || typeof layer !== 'object') {\n return true;\n }\n\n const layerProps = layer as Record<string, unknown>;\n const layerName = String(layerProps['@@type'] ?? '');\n const compatibility = getLayerCompatibility(layerName);\n if (!compatibility) {\n return true;\n }\n\n const datasetId = resolveDatasetId(layerProps, datasetIds);\n if (!datasetId) {\n return true;\n }\n\n const datasetState = datasetStates[datasetId];\n if (!datasetState) {\n return false;\n }\n\n return datasetState.status !== 'error';\n });\n\n return {\n ...spec,\n layers: filteredLayers,\n };\n}\n\nfunction renderDatasetErrorOverlay(\n datasetStates: Record<string, PreparedDeckDatasetState>,\n) {\n const failedDatasets = Object.entries(datasetStates).filter(\n (\n entry,\n ): entry is [\n string,\n Extract<PreparedDeckDatasetState, {status: 'error'}>,\n ] => entry[1].status === 'error',\n );\n\n if (!failedDatasets.length) {\n return null;\n }\n\n return (\n <div className=\"pointer-events-none absolute inset-x-4 top-4 z-10 space-y-2\">\n {failedDatasets.map(([datasetId, state]) => (\n <div\n key={datasetId}\n className=\"rounded-md border border-red-200 bg-red-50/95 px-3 py-2 text-sm text-red-700 shadow-sm\"\n >\n {`Dataset \"${datasetId}\" failed: ${state.error.message}`}\n </div>\n ))}\n </div>\n );\n}\n\nconst TripsTimeControl: FC<{\n currentTime: number;\n maxTime: number;\n playing: boolean;\n onPlayPause: () => void;\n onSeek: (time: number) => void;\n}> = ({currentTime, maxTime, playing, onPlayPause, onSeek}) => {\n const [collapsed, setCollapsed] = useState(false);\n const progress = maxTime > 0 ? currentTime / maxTime : 0;\n\n if (collapsed) {\n return (\n <button\n type=\"button\"\n onClick={() => setCollapsed(false)}\n className=\"border-border/70 bg-background/90 text-muted-foreground hover:text-foreground pointer-events-auto flex w-fit items-center gap-1 rounded-md border px-2 py-1 text-[10px] leading-none font-medium shadow-sm backdrop-blur-sm transition-colors\"\n >\n Timeline\n </button>\n );\n }\n\n return (\n <div className=\"bg-background/90 border-border/70 pointer-events-auto relative flex items-center gap-1.5 rounded-md border px-2 py-1.5 pr-7 shadow-sm backdrop-blur-sm\">\n <button\n type=\"button\"\n onClick={onPlayPause}\n className=\"text-foreground hover:bg-accent flex h-5 w-5 shrink-0 items-center justify-center rounded\"\n title={playing ? 'Pause' : 'Play'}\n >\n {playing ? (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <rect x=\"1\" y=\"1\" width=\"3\" height=\"8\" />\n <rect x=\"6\" y=\"1\" width=\"3\" height=\"8\" />\n </svg>\n ) : (\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"currentColor\">\n <polygon points=\"2,1 9,5 2,9\" />\n </svg>\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1000}\n value={Math.round(progress * 1000)}\n onChange={(e) => onSeek((Number(e.target.value) / 1000) * maxTime)}\n className=\"h-1 w-24 flex-1 cursor-pointer\"\n />\n <button\n type=\"button\"\n onClick={() => setCollapsed(true)}\n className=\"text-muted-foreground hover:text-foreground absolute top-1.5 right-1.5 flex h-4 w-4 items-center justify-center rounded-sm transition-colors\"\n title=\"Collapse\"\n >\n <svg width=\"8\" height=\"8\" viewBox=\"0 0 8 8\">\n <path\n d=\"M1 1L7 7M7 1L1 7\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n );\n};\n\nfunction getDeckLayerTypeName(layer: unknown): string {\n if (!layer || typeof layer !== 'object') return '?';\n const ctor = (\n layer as {\n constructor?: {layerName?: string; name?: string};\n }\n ).constructor;\n // Prefer static layerName: table-to-RecordBatch adapters all share\n // constructor.name \"Adapter\", which would miss heatmap ↔ scatterplot swaps.\n return ctor?.layerName ?? ctor?.name ?? '?';\n}\n\n// Workaround for deck.gl HeatmapLayer not releasing its WebGL framebuffer\n// when replaced by another layer type (e.g. switching heatmap → scatterplot).\n// We detect layer class changes and clear layers for one animation frame,\n// giving deck.gl a chance to finalize the old layer's resources before\n// initializing the new one.\nfunction DeckOverlayControl({\n interleaved,\n ...deckProps\n}: {interleaved: boolean} & Record<string, unknown>) {\n const overlay = useControl<MapboxOverlay>(\n () => new MapboxOverlay({interleaved}),\n );\n const prevLayerKeyRef = useRef<string>('');\n const [clearing, setClearing] = useState(false);\n const captureStatusRef = useRef<HTMLSpanElement>(null);\n\n const layers = deckProps.layers as unknown[] | undefined;\n const captureLayers = useMemo(\n () => (layers?.flat(Infinity).filter(Boolean) ?? []) as Layer[],\n [layers],\n );\n useLayoutEffect(() => {\n // A previous frame no longer represents the current layers.\n captureStatusRef.current?.setAttribute('data-sqlrooms-map-loading', 'true');\n }, [layers, clearing]);\n const layerKey = Array.isArray(layers)\n ? layers.map(getDeckLayerTypeName).join(',')\n : '';\n\n useEffect(() => {\n if (\n prevLayerKeyRef.current &&\n prevLayerKeyRef.current !== layerKey &&\n !clearing\n ) {\n setClearing(true);\n }\n prevLayerKeyRef.current = layerKey;\n }, [layerKey, clearing]);\n\n useEffect(() => {\n if (clearing && overlay) {\n // The temporary empty frame must not advertise capture readiness.\n overlay.setProps({layers: [], onAfterRender: () => {}});\n requestAnimationFrame(() => setClearing(false));\n }\n }, [clearing, overlay]);\n\n useLayoutEffect(() => {\n if (clearing || !overlay) return;\n overlay.setProps({\n ...deckProps,\n onLoad: () => {\n // The separate canvas is created asynchronously. Mark it once deck\n // initializes, including when the host supplies a custom canvas ID.\n overlay.getCanvas()?.setAttribute('data-sqlrooms-deck-canvas', '');\n if (typeof deckProps.onLoad === 'function') deckProps.onLoad();\n },\n onAfterRender: (context) => {\n const loaded = captureLayers.every((layer) => layer.isLoaded);\n captureStatusRef.current?.setAttribute(\n 'data-sqlrooms-map-loading',\n String(!loaded),\n );\n if (typeof deckProps.onAfterRender === 'function') {\n deckProps.onAfterRender(context);\n }\n },\n });\n }, [clearing, overlay, deckProps, captureLayers]);\n\n return (\n <span hidden ref={captureStatusRef} data-sqlrooms-map-loading=\"true\" />\n );\n}\n\nfunction DeckMapRenderingErrorOverlay({error}: {error: Error}) {\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center p-4\">\n <div className=\"max-w-sm rounded-md border border-red-200 bg-red-50/95 p-4 text-sm text-red-700 shadow-sm\">\n <Collapsible>\n <div className=\"flex items-start gap-1\">\n <span>{\"Map couldn't be rendered\"}</span>\n <CollapsibleTrigger\n className=\"group mt-0.5 shrink-0 rounded p-0.5 hover:bg-red-100\"\n aria-label=\"Show error details\"\n >\n <ChevronRightIcon className=\"h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90\" />\n </CollapsibleTrigger>\n </div>\n <CollapsibleContent>\n <pre className=\"mt-2 max-h-40 overflow-auto font-mono text-xs whitespace-pre-wrap\">\n {error.message}\n </pre>\n </CollapsibleContent>\n </Collapsible>\n </div>\n </div>\n );\n}\n\nexport const DeckJsonMap = forwardRef<DeckJsonMapHandle, DeckJsonMapProps>(\n function DeckJsonMap(\n {\n spec,\n datasets,\n mapStyle,\n basemapProvider,\n interleaved = true,\n deckProps,\n mapProps,\n showLegends = true,\n className,\n children,\n onDatasetStatesChange,\n onRenderingError,\n },\n ref,\n ) {\n const normalizedDatasets = useMemo(\n () => normalizeDatasets(datasets),\n [datasets],\n );\n const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());\n const datasetIds = useMemo(\n () => JSON.parse(datasetIdKey) as string[],\n [datasetIdKey],\n );\n const datasetStates = usePreparedDatasetStates(normalizedDatasets);\n const [mapReady, setMapReady] = useState(false);\n const datasetsLoading = datasetIds.some(\n (id) => !datasetStates[id] || datasetStates[id]?.status === 'loading',\n );\n const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);\n\n const {spec: parsedSpec, error: specError} = useMemo(\n () => parseSpec(spec),\n [spec],\n );\n\n const availableSpec = useMemo(\n () =>\n parsedSpec\n ? filterUnavailableLayers(parsedSpec, datasetIds, datasetStates)\n : null,\n [parsedSpec, datasetIds, datasetStates],\n );\n\n const converter = useMemo(\n () =>\n new JSONConverter({\n configuration: createDeckJsonConfiguration({\n datasetStates,\n datasetIds,\n }),\n onJSONChange: () => {},\n }),\n [datasetIds, datasetStates],\n );\n\n const convertedDeckPropsResult = useMemo(() => {\n if (!availableSpec) {\n return {props: null, error: specError};\n }\n\n try {\n return {\n props: converter.convert(availableSpec) as Record<string, unknown>,\n error: null,\n };\n } catch (error) {\n return {\n props: null,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }, [availableSpec, converter, specError]);\n\n // Animation for TripsLayer — update currentTime on each frame.\n // Only activate when the trips dataset is ready to avoid spinning the RAF\n // loop while data is still loading.\n const hasTripsLayer = useMemo(() => {\n if (!availableSpec || !Array.isArray(availableSpec.layers)) return false;\n return availableSpec.layers.some((l: unknown) => {\n if (\n !l ||\n typeof l !== 'object' ||\n (l as {'@@type'?: string})['@@type'] !== 'GeoArrowTripsLayer'\n )\n return false;\n const dsId = resolveDatasetId(l as Record<string, unknown>, datasetIds);\n if (!dsId) return true;\n const state = datasetStates[dsId];\n return state?.status === 'ready';\n });\n }, [availableSpec, datasetIds, datasetStates]);\n\n const [tripsTime, setTripsTime] = useState(0);\n const [tripsPlaying, setTripsPlaying] = useState(true);\n const tripsTimeRef = useRef(0);\n\n useEffect(() => {\n if (!hasTripsLayer || !tripsPlaying) return;\n let raf: number;\n let lastFrame = Date.now();\n const tick = () => {\n const now = Date.now();\n tripsTimeRef.current += now - lastFrame;\n lastFrame = now;\n setTripsTime(tripsTimeRef.current);\n raf = requestAnimationFrame(tick);\n };\n lastFrame = Date.now();\n raf = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(raf);\n }, [hasTripsLayer, tripsPlaying]);\n\n const handleTripsPlayPause = useCallback(() => {\n setTripsPlaying((p) => !p);\n }, []);\n\n const handleTripsSeek = useCallback((time: number) => {\n const rawTime = time * 30;\n tripsTimeRef.current = rawTime;\n setTripsTime(rawTime);\n }, []);\n\n const finalDeckPropsResult = convertedDeckPropsResult;\n\n const onRenderingErrorRef = useRef(onRenderingError);\n useEffect(() => {\n onRenderingErrorRef.current = onRenderingError;\n }, [onRenderingError]);\n\n useEffect(() => {\n if (finalDeckPropsResult.error) {\n console.error(finalDeckPropsResult.error);\n onRenderingErrorRef.current?.(finalDeckPropsResult.error);\n }\n }, [finalDeckPropsResult.error]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current = onDatasetStatesChange;\n }, [onDatasetStatesChange]);\n\n useEffect(() => {\n onDatasetStatesChangeRef.current?.(datasetStates);\n }, [datasetStates]);\n\n const fallbackDeckProps = useMemo(\n () => extractFallbackDeckProps(availableSpec),\n [availableSpec],\n );\n const convertedDeckProps = (finalDeckPropsResult.props ??\n fallbackDeckProps ??\n {}) as Record<string, unknown>;\n const extraDeckProps = (deckProps ?? {}) as Record<string, unknown>;\n const extraMapProps = (mapProps ?? {}) as Record<string, unknown>;\n const hasRenderingError = Boolean(finalDeckPropsResult.error);\n\n // Separate stable layer analysis from per-frame animation to avoid\n // re-running .map() over all layers 60 times per second.\n const baseLayers = useMemo(() => {\n const mergedLayers = hasRenderingError\n ? []\n : (deckProps?.layers ??\n (convertedDeckProps.layers as unknown[] | undefined) ??\n []);\n if (!hasTripsLayer || !Array.isArray(mergedLayers))\n return {layers: mergedLayers, maxTs: 0, tripsIndices: [] as number[]};\n let computedMaxTs = 0;\n const tripsIndices: number[] = [];\n for (let i = 0; i < mergedLayers.length; i++) {\n const layer = mergedLayers[i];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {props?: Record<string, unknown>};\n const maxTs = (layerObj.props as Record<string, unknown> | undefined)\n ?._tripsMaxTimestamp as number | undefined;\n if (maxTs && maxTs > 0) {\n computedMaxTs = Math.max(computedMaxTs, maxTs);\n tripsIndices.push(i);\n }\n }\n return {layers: mergedLayers, maxTs: computedMaxTs, tripsIndices};\n }, [\n hasTripsLayer,\n hasRenderingError,\n deckProps?.layers,\n convertedDeckProps.layers,\n ]);\n\n // Per-frame: only clone trips layers with updated currentTime\n const animatedLayers = useMemo(() => {\n const {layers, tripsIndices} = baseLayers;\n if (!tripsIndices.length) return layers;\n if (!Array.isArray(layers)) return layers;\n const result = layers.slice();\n for (const idx of tripsIndices) {\n const layer = result[idx];\n if (!layer || typeof layer !== 'object') continue;\n const layerObj = layer as {\n props?: Record<string, unknown>;\n clone?: (props: Record<string, unknown>) => unknown;\n };\n if (!layerObj.clone) continue;\n const props = layerObj.props as Record<string, unknown> | undefined;\n const maxTs = (props?._tripsMaxTimestamp as number | undefined) ?? 0;\n const trailFraction =\n (props?._trailLengthFactor as number | undefined) ?? 0.4;\n const widthPx = (props?.widthMinPixels as number | undefined) ?? 3;\n const animationSpeed = 30;\n const currentTime = (tripsTime / animationSpeed) % maxTs;\n result[idx] = layerObj.clone({\n currentTime,\n trailLength: maxTs * trailFraction,\n widthMinPixels: widthPx,\n });\n }\n return result;\n }, [baseLayers, tripsTime]);\n const tripsMaxTimeValue = baseLayers.maxTs;\n\n const tripsCurrentTime = useMemo(() => {\n if (!tripsMaxTimeValue || tripsMaxTimeValue <= 0) return 0;\n return (tripsTime / 30) % tripsMaxTimeValue;\n }, [tripsTime, tripsMaxTimeValue]);\n\n const mergedDeckProps = {\n ...convertedDeckProps,\n ...extraDeckProps,\n layers: animatedLayers,\n };\n\n // overlayDeckProps should not contain viewState/initialViewState\n const {\n initialViewState,\n viewState: _vs,\n onViewStateChange: _onViewStateChange,\n ...overlayDeckProps\n } = mergedDeckProps as Record<string, unknown>;\n void _vs;\n void _onViewStateChange;\n\n const mapRef = useRef<{jumpTo: (opts: object) => void} | null>(null);\n const pendingJumpRef = useRef<object | null>(null);\n\n useImperativeHandle(\n ref,\n () => ({\n jumpTo(opts) {\n const jumpOpts = buildDeckMapJumpToOptions(opts);\n if (mapRef.current) {\n mapRef.current.jumpTo(jumpOpts);\n } else {\n pendingJumpRef.current = jumpOpts;\n }\n },\n }),\n [],\n );\n\n const handleMapLoad = useCallback(\n (event?: unknown) => {\n if (pendingJumpRef.current && mapRef.current) {\n mapRef.current.jumpTo(pendingJumpRef.current);\n pendingJumpRef.current = null;\n }\n const externalOnLoad = (\n extraMapProps as {onLoad?: (e?: unknown) => void}\n ).onLoad;\n externalOnLoad?.(event);\n },\n [extraMapProps],\n );\n\n const {resolvedTheme} = useTheme();\n const hostDefaultMapStyles = useDeckMapDefaultStyles();\n const roomBasemapProvider = useBaseRoomStore<\n Partial<DeckMapsSliceState>,\n DeckMapBasemapProvider | undefined\n >((state) => state.deckMaps?.basemapProvider);\n\n const mergedMapProps = {\n ...extraMapProps,\n // DOM image capture reads the canvas after the render frame has ended.\n // Interleaved deck layers share this buffer with the basemap.\n canvasContextAttributes: {\n preserveDrawingBuffer: true,\n ...mapProps?.canvasContextAttributes,\n },\n mapStyle: resolveDeckMapStyle({\n mapStyle,\n mapPropsMapStyle: mapProps?.mapStyle,\n basemapProvider: basemapProvider ?? roomBasemapProvider,\n hostDefaultStyles: hostDefaultMapStyles,\n resolvedTheme,\n fallbackStyles: DEFAULT_MAP_STYLES,\n }),\n };\n const legends = useMemo(\n () =>\n showLegends\n ? extractColorScaleLegends({\n spec: availableSpec,\n datasetIds,\n datasetStates,\n })\n : [],\n [availableSpec, datasetIds, showLegends, datasetStates],\n );\n\n return (\n <div\n className={cn('relative h-full w-full', className)}\n data-sqlrooms-map-loading={!mapReady || datasetsLoading}\n >\n {finalDeckPropsResult.error ? (\n <DeckMapRenderingErrorOverlay error={finalDeckPropsResult.error} />\n ) : null}\n\n <Map\n ref={mapRef as any}\n {...(mergedMapProps as object)}\n {...(initialViewState\n ? {initialViewState: initialViewState as object}\n : {})}\n onLoad={handleMapLoad}\n onData={(event) => {\n setMapReady(false);\n mapProps?.onData?.(event);\n }}\n onMoveStart={(event) => {\n setMapReady(false);\n mapProps?.onMoveStart?.(event);\n }}\n onRender={(event) => {\n setMapReady(event.target.loaded());\n mapProps?.onRender?.(event);\n }}\n style={{width: '100%', height: '100%', ...mapProps?.style}}\n >\n <DeckOverlayControl interleaved={interleaved} {...overlayDeckProps} />\n {children}\n </Map>\n\n {renderDatasetErrorOverlay(datasetStates)}\n {!hasRenderingError && showLegends ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10 max-w-56\">\n {hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"mb-1\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n <ColorScaleLegend legends={legends} />\n </div>\n ) : hasTripsLayer && tripsMaxTimeValue > 0 ? (\n <div className=\"pointer-events-none absolute bottom-2 left-2 z-10\">\n <TripsTimeControl\n currentTime={tripsCurrentTime}\n maxTime={tripsMaxTimeValue}\n playing={tripsPlaying}\n onPlayPause={handleTripsPlayPause}\n onSeek={handleTripsSeek}\n />\n </div>\n ) : null}\n </div>\n );\n },\n);\n"]}
@@ -0,0 +1,9 @@
1
+ import type { FC } from 'react';
2
+ import type { DeckMapConfig } from './mapConfig';
3
+ /** Selects a persistent basemap independently of the app's current theme. */
4
+ export declare const DeckMapBasemapSelect: FC<{
5
+ config: DeckMapConfig;
6
+ onConfigChange: (config: DeckMapConfig) => void;
7
+ readOnly?: boolean;
8
+ }>;
9
+ //# sourceMappingURL=DeckMapBasemapSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeckMapBasemapSelect.d.ts","sourceRoot":"","sources":["../src/DeckMapBasemapSelect.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,OAAO,CAAC;AAC9B,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAQ/C,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,EAAE,CAAC;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,cAAc,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAqCA,CAAC"}