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

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 (173) hide show
  1. package/README.md +88 -16
  2. package/dist/BlockMapSettings.d.ts.map +1 -1
  3. package/dist/BlockMapSettings.js +4 -16
  4. package/dist/BlockMapSettings.js.map +1 -1
  5. package/dist/DashboardMapSettings.d.ts.map +1 -1
  6. package/dist/DashboardMapSettings.js +7 -10
  7. package/dist/DashboardMapSettings.js.map +1 -1
  8. package/dist/DeckJsonMap.d.ts.map +1 -1
  9. package/dist/DeckJsonMap.js +20 -18
  10. package/dist/DeckJsonMap.js.map +1 -1
  11. package/dist/DeckJsonMapSpec.d.ts +916 -53
  12. package/dist/DeckJsonMapSpec.d.ts.map +1 -1
  13. package/dist/DeckJsonMapSpec.js +9 -0
  14. package/dist/DeckJsonMapSpec.js.map +1 -1
  15. package/dist/DeckMapDefaultStylesProvider.d.ts +3 -4
  16. package/dist/DeckMapDefaultStylesProvider.d.ts.map +1 -1
  17. package/dist/DeckMapDefaultStylesProvider.js +13 -7
  18. package/dist/DeckMapDefaultStylesProvider.js.map +1 -1
  19. package/dist/DeckMapSurface.d.ts +1 -1
  20. package/dist/DeckMapSurface.js +1 -1
  21. package/dist/DeckMapSurface.js.map +1 -1
  22. package/dist/MapSettings.d.ts +14 -0
  23. package/dist/MapSettings.d.ts.map +1 -1
  24. package/dist/MapSettings.js +555 -186
  25. package/dist/MapSettings.js.map +1 -1
  26. package/dist/MapSettingsControls.d.ts +8 -1
  27. package/dist/MapSettingsControls.d.ts.map +1 -1
  28. package/dist/MapSettingsControls.js +24 -2
  29. package/dist/MapSettingsControls.js.map +1 -1
  30. package/dist/ai.d.ts +19 -5
  31. package/dist/ai.d.ts.map +1 -1
  32. package/dist/ai.js +63 -112
  33. package/dist/ai.js.map +1 -1
  34. package/dist/aiNormalize.d.ts +31 -0
  35. package/dist/aiNormalize.d.ts.map +1 -0
  36. package/dist/aiNormalize.js +630 -0
  37. package/dist/aiNormalize.js.map +1 -0
  38. package/dist/block.d.ts +1 -1
  39. package/dist/block.d.ts.map +1 -1
  40. package/dist/block.js +1 -1
  41. package/dist/block.js.map +1 -1
  42. package/dist/createOrUpdateDeckMapResource.d.ts +6 -2
  43. package/dist/createOrUpdateDeckMapResource.d.ts.map +1 -1
  44. package/dist/createOrUpdateDeckMapResource.js +57 -4
  45. package/dist/createOrUpdateDeckMapResource.js.map +1 -1
  46. package/dist/dashboard.d.ts.map +1 -1
  47. package/dist/dashboard.js +72 -8
  48. package/dist/dashboard.js.map +1 -1
  49. package/dist/dashboardConfig.d.ts +10 -1
  50. package/dist/dashboardConfig.d.ts.map +1 -1
  51. package/dist/dashboardConfig.js +55 -22
  52. package/dist/dashboardConfig.js.map +1 -1
  53. package/dist/datasets/PreparedDatasetStore.d.ts +3 -4
  54. package/dist/datasets/PreparedDatasetStore.d.ts.map +1 -1
  55. package/dist/datasets/PreparedDatasetStore.js +35 -10
  56. package/dist/datasets/PreparedDatasetStore.js.map +1 -1
  57. package/dist/datasets/helpers.d.ts +39 -2
  58. package/dist/datasets/helpers.d.ts.map +1 -1
  59. package/dist/datasets/helpers.js +86 -3
  60. package/dist/datasets/helpers.js.map +1 -1
  61. package/dist/datasets/usePreparedDatasetStates.d.ts.map +1 -1
  62. package/dist/datasets/usePreparedDatasetStates.js +12 -8
  63. package/dist/datasets/usePreparedDatasetStates.js.map +1 -1
  64. package/dist/datasets/wrapGeometryAsWkb.d.ts +20 -0
  65. package/dist/datasets/wrapGeometryAsWkb.d.ts.map +1 -0
  66. package/dist/datasets/wrapGeometryAsWkb.js +65 -0
  67. package/dist/datasets/wrapGeometryAsWkb.js.map +1 -0
  68. package/dist/index.d.ts +3 -2
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +2 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/json/colorScaleFunction.d.ts +12 -4
  73. package/dist/json/colorScaleFunction.d.ts.map +1 -1
  74. package/dist/json/colorScaleFunction.js.map +1 -1
  75. package/dist/json/compileColorScale.d.ts +3 -2
  76. package/dist/json/compileColorScale.d.ts.map +1 -1
  77. package/dist/json/compileColorScale.js +16 -1
  78. package/dist/json/compileColorScale.js.map +1 -1
  79. package/dist/json/compileGeoArrowAccessor.d.ts +5 -0
  80. package/dist/json/compileGeoArrowAccessor.d.ts.map +1 -1
  81. package/dist/json/compileGeoArrowAccessor.js +17 -3
  82. package/dist/json/compileGeoArrowAccessor.js.map +1 -1
  83. package/dist/json/createDeckJsonConfiguration.d.ts.map +1 -1
  84. package/dist/json/createDeckJsonConfiguration.js +278 -151
  85. package/dist/json/createDeckJsonConfiguration.js.map +1 -1
  86. package/dist/json/defaultClasses.d.ts.map +1 -1
  87. package/dist/json/defaultClasses.js +3 -3
  88. package/dist/json/defaultClasses.js.map +1 -1
  89. package/dist/json/extractColorScaleLegends.d.ts.map +1 -1
  90. package/dist/json/extractColorScaleLegends.js +43 -14
  91. package/dist/json/extractColorScaleLegends.js.map +1 -1
  92. package/dist/json/heatmapDefaults.d.ts +17 -7
  93. package/dist/json/heatmapDefaults.d.ts.map +1 -1
  94. package/dist/json/heatmapDefaults.js +54 -12
  95. package/dist/json/heatmapDefaults.js.map +1 -1
  96. package/dist/json/layers/DeckH3HexagonLayer.d.ts.map +1 -1
  97. package/dist/json/layers/DeckH3HexagonLayer.js +9 -14
  98. package/dist/json/layers/DeckH3HexagonLayer.js.map +1 -1
  99. package/dist/json/layers/DeckHeatmapLayer.d.ts +32 -0
  100. package/dist/json/layers/DeckHeatmapLayer.d.ts.map +1 -0
  101. package/dist/json/layers/DeckHeatmapLayer.js +69 -0
  102. package/dist/json/layers/DeckHeatmapLayer.js.map +1 -0
  103. package/dist/json/layers/h3IndexFormat.d.ts +6 -0
  104. package/dist/json/layers/h3IndexFormat.d.ts.map +1 -0
  105. package/dist/json/layers/h3IndexFormat.js +17 -0
  106. package/dist/json/layers/h3IndexFormat.js.map +1 -0
  107. package/dist/json/layers/index.d.ts +2 -0
  108. package/dist/json/layers/index.d.ts.map +1 -1
  109. package/dist/json/layers/index.js +2 -0
  110. package/dist/json/layers/index.js.map +1 -1
  111. package/dist/json/rewriteGeoArrowAccessors.d.ts.map +1 -1
  112. package/dist/json/rewriteGeoArrowAccessors.js +22 -5
  113. package/dist/json/rewriteGeoArrowAccessors.js.map +1 -1
  114. package/dist/json/scaleFunction.d.ts +27 -0
  115. package/dist/json/scaleFunction.d.ts.map +1 -0
  116. package/dist/json/scaleFunction.js +151 -0
  117. package/dist/json/scaleFunction.js.map +1 -0
  118. package/dist/mapAiConfig.d.ts +8 -0
  119. package/dist/mapAiConfig.d.ts.map +1 -1
  120. package/dist/mapAiConfig.js +9 -1
  121. package/dist/mapAiConfig.js.map +1 -1
  122. package/dist/mapAiSharedInstructions.d.ts +3 -0
  123. package/dist/mapAiSharedInstructions.d.ts.map +1 -0
  124. package/dist/mapAiSharedInstructions.js +16 -0
  125. package/dist/mapAiSharedInstructions.js.map +1 -0
  126. package/dist/mapConfig.d.ts +7 -1
  127. package/dist/mapConfig.d.ts.map +1 -1
  128. package/dist/mapConfig.js.map +1 -1
  129. package/dist/mapConfigUtils.d.ts +22 -0
  130. package/dist/mapConfigUtils.d.ts.map +1 -1
  131. package/dist/mapConfigUtils.js +147 -3
  132. package/dist/mapConfigUtils.js.map +1 -1
  133. package/dist/mapFit.d.ts +16 -0
  134. package/dist/mapFit.d.ts.map +1 -1
  135. package/dist/mapFit.js +208 -30
  136. package/dist/mapFit.js.map +1 -1
  137. package/dist/mapLayerConfigUtils.d.ts +64 -1
  138. package/dist/mapLayerConfigUtils.d.ts.map +1 -1
  139. package/dist/mapLayerConfigUtils.js +277 -16
  140. package/dist/mapLayerConfigUtils.js.map +1 -1
  141. package/dist/mapResourceAuthoring.d.ts +3 -1
  142. package/dist/mapResourceAuthoring.d.ts.map +1 -1
  143. package/dist/mapResourceAuthoring.js +413 -20
  144. package/dist/mapResourceAuthoring.js.map +1 -1
  145. package/dist/mosaic.d.ts +1 -1
  146. package/dist/mosaic.d.ts.map +1 -1
  147. package/dist/mosaic.js.map +1 -1
  148. package/dist/prepare/geoarrow.js.map +1 -1
  149. package/dist/prepare/prepareDeckDataset.d.ts.map +1 -1
  150. package/dist/prepare/prepareDeckDataset.js +1 -0
  151. package/dist/prepare/prepareDeckDataset.js.map +1 -1
  152. package/dist/prepare/types.d.ts +3 -0
  153. package/dist/prepare/types.d.ts.map +1 -1
  154. package/dist/prepare/types.js.map +1 -1
  155. package/dist/prepare/wkbDecoder.d.ts +10 -0
  156. package/dist/prepare/wkbDecoder.d.ts.map +1 -1
  157. package/dist/prepare/wkbDecoder.js +141 -20
  158. package/dist/prepare/wkbDecoder.js.map +1 -1
  159. package/dist/prepare/wkbParser.d.ts +9 -0
  160. package/dist/prepare/wkbParser.d.ts.map +1 -1
  161. package/dist/prepare/wkbParser.js +22 -0
  162. package/dist/prepare/wkbParser.js.map +1 -1
  163. package/dist/selectStarAsWkbCollision.d.ts +3 -0
  164. package/dist/selectStarAsWkbCollision.d.ts.map +1 -0
  165. package/dist/selectStarAsWkbCollision.js +78 -0
  166. package/dist/selectStarAsWkbCollision.js.map +1 -0
  167. package/dist/types.d.ts +1 -1
  168. package/dist/types.d.ts.map +1 -1
  169. package/dist/types.js.map +1 -1
  170. package/dist/useDeckMapFitToBounds.d.ts.map +1 -1
  171. package/dist/useDeckMapFitToBounds.js +1 -1
  172. package/dist/useDeckMapFitToBounds.js.map +1 -1
  173. package/package.json +15 -15
package/README.md CHANGED
@@ -4,7 +4,7 @@ preparation.
4
4
 
5
5
  ## Map resources and dashboard adapters
6
6
 
7
- Worksheet maps are first-class `deckMaps` resources. The root package export
7
+ Document maps are first-class `deckMaps` resources. The root package export
8
8
  contains the resource slice, renderer, settings, direct DuckDB data adapter,
9
9
  and resource orchestration APIs. It does not require Mosaic.
10
10
 
@@ -14,14 +14,14 @@ selection, and issue translation inside that adapter boundary.
14
14
 
15
15
  `DeckMapSettingsPanel` is the shared host-neutral editor for both surfaces. It
16
16
  receives a map config, selected table, available tables, and edit callbacks;
17
- worksheet resources and Mosaic panels only adapt their respective stores to
17
+ document resources and Mosaic panels only adapt their respective stores to
18
18
  that contract. Layer, binding, style, extrusion, and code-view controls therefore
19
- stay consistent without putting Mosaic APIs in the worksheet settings path.
19
+ stay consistent without putting Mosaic APIs in the document settings path.
20
20
 
21
21
  Use `getDeckMapDataPolicy(...)` to resolve a map config into the exported
22
22
  `DeckMapDataPolicy` runtime row-limit policy.
23
23
 
24
- Worksheet map runtime issues distinguish dataset SQL failures (`sql-error`)
24
+ Document map runtime issues distinguish dataset SQL failures (`sql-error`)
25
25
  from fit-to-data bounds failures (`fit-error`), so each issue is cleared only
26
26
  after its corresponding operation recovers.
27
27
 
@@ -29,7 +29,7 @@ after its corresponding operation recovers.
29
29
  fit-to-data bounds queries. The direct adapter uses the authored source for
30
30
  bounds while applying the configured row-limit policy only to rendered rows.
31
31
 
32
- Worksheet maps deliberately use independent selection semantics. Their direct
32
+ Document maps deliberately use independent selection semantics. Their direct
33
33
  data adapter executes each configured SQL/table dataset through the room's
34
34
  DuckDB connector and neither reads nor publishes Mosaic selections. This drops
35
35
  the old incidental intra-map cross-filtering between datasets; a future
@@ -134,7 +134,8 @@ By default, the helper is conservative:
134
134
 
135
135
  - point / multipoint -> `GeoArrowScatterplotLayer`
136
136
  - linestring / multilinestring -> `GeoArrowPathLayer`
137
- - polygon / multipolygon -> `GeoArrowPolygonLayer`
137
+ - native GeoArrow polygon / multipolygon -> `GeoArrowPolygonLayer`
138
+ - WKB/WKT multipolygon -> `GeoJsonLayer`
138
139
  - mixed, unknown, or unsupported -> `GeoJsonLayer`
139
140
 
140
141
  You can provide semantic hints for special layers:
@@ -267,11 +268,11 @@ Runtime issue recovery can call `deckMaps.clearMapIssue(mapId, kind)` to clear
267
268
  only a matching issue kind; omit `kind` when the map state should clear any
268
269
  stale issue. Replacing a map config clears its prior render issue, while data
269
270
  issues remain until the corresponding dataset recovery is reported.
270
- Direct worksheet maps automatically fit the configured dataset when the map or
271
+ Direct document maps automatically fit the configured dataset when the map or
271
272
  its source first becomes ready; the header action remains available for manual
272
273
  refitting.
273
274
 
274
- Hosts that expose direct worksheet-map AI capability should include
275
+ Hosts that expose direct document-map AI capability should include
275
276
  `getDeckMapResourceAiInstructions()` in the responsible agent and tool
276
277
  instructions. `createOrUpdateDeckMapResource(...)` validates the fully merged
277
278
  resource before any durable block or map write: each dataset needs a
@@ -333,6 +334,11 @@ const result = await createOrUpdateDeckMapResource(
333
334
  blockDocumentId,
334
335
  mapId,
335
336
  config,
337
+ pointBinding: {
338
+ dataset: 'places',
339
+ longitudeColumn: 'longitude',
340
+ latitudeColumn: 'latitude',
341
+ },
336
342
  tableName,
337
343
  title,
338
344
  intent,
@@ -354,10 +360,22 @@ title and uses it as the default block caption. Block metadata is written only
354
360
  after the map write succeeds.
355
361
 
356
362
  Map authoring helpers such as `normalizeDeckMapPointConfig(...)`,
357
- `normalizeDeckMapFillColor(...)`, `regenerateMapConfigForTable(...)`, and
363
+ `applyDeckMapPointBinding(...)`, `normalizeDeckMapFillColor(...)`,
364
+ `regenerateMapConfigForTable(...)`, and
358
365
  dataset-source helpers such as `getFirstDatasetSourceTableName(...)` are
359
366
  exported so hosts can normalize AI-authored configs before calling
360
- `createOrUpdateDeckMapResource(...)`. `normalizeDeckMapPointConfig(...)` only adds
367
+ `createOrUpdateDeckMapResource(...)`. Passing structured `pointBinding` to the
368
+ resource helper applies `applyDeckMapPointBinding(...)`: it generates canonical
369
+ WKB point SQL through `createDeckMapPointTransformSql(...)` and aligns the target
370
+ dataset, point layers, brush interaction, and fit binding. The host table lookup
371
+ also supplies source columns so missing coordinate columns and a generated
372
+ geometry alias that would duplicate an existing column are rejected before
373
+ durable state is written. For a single table-backed dataset, its canonical table
374
+ identity must also match the selected table because that selection overrides the
375
+ authored dataset source at render time. This is the preferred path for standard
376
+ table-backed longitude/latitude maps; raw `transformSql` remains available for
377
+ custom spatial transforms.
378
+ `normalizeDeckMapPointConfig(...)` only adds
361
379
  the standard lon/lat point transform to table-backed datasets that do not
362
380
  already declare `geometryColumn`, `source.sqlQuery`, or `source.transformSql`
363
381
  and whose resolved table does not expose a native geometry column; native
@@ -539,6 +557,21 @@ getFillColor: {
539
557
  }
540
558
  ```
541
559
 
560
+ Optional `opacity` (0–1) is a SQLRooms `colorScale` extension. The compiler
561
+ multiplies it into the color's alpha so fill, stroke, and arc endpoints can be
562
+ dimmed independently of deck.gl `layer.opacity`:
563
+
564
+ ```tsx
565
+ getFillColor: {
566
+ '@@function': 'colorScale',
567
+ field: 'Magnitude',
568
+ type: 'sequential',
569
+ scheme: 'YlOrRd',
570
+ domain: 'auto',
571
+ opacity: 0.6,
572
+ }
573
+ ```
574
+
542
575
  Discrete numeric palettes are supported too:
543
576
 
544
577
  ```tsx
@@ -596,6 +629,10 @@ scenes. It accepts resolved Arrow tables with geometry stored as:
596
629
  - WKB / GeoArrow WKB
597
630
  - WKT / GeoArrow WKT
598
631
 
632
+ The returned `PreparedDeckDataset` records the dataset-level
633
+ `datasetGeometryColumn` and `datasetGeometryEncodingHint` used to prepare
634
+ and identify that payload.
635
+
599
636
  It then produces canonical deck-facing geometry outputs for:
600
637
 
601
638
  - GeoArrow-native layers such as `GeoArrowScatterplotLayer`
@@ -623,7 +660,6 @@ The current curated layer set is:
623
660
  - `GeoArrowColumnLayer`
624
661
  - `GeoArrowPathLayer`
625
662
  - `GeoArrowPolygonLayer`
626
- - `GeoArrowSolidPolygonLayer`
627
663
  - `GeoArrowArcLayer`
628
664
  - `GeoArrowTripsLayer`
629
665
  - `GeoArrowH3HexagonLayer`
@@ -632,15 +668,51 @@ The current curated layer set is:
632
668
  GeoArrow-native geometry columns are the efficient path. WKB/WKT geometry falls
633
669
  back to decoding and GeoJSON-binary preparation, with promotion available
634
670
  for point-focused GeoArrow layers such as `GeoArrowScatterplotLayer`,
635
- `GeoArrowHeatmapLayer`, and `GeoArrowColumnLayer`, plus polygon promotion for
636
- `GeoArrowPolygonLayer` and `GeoArrowSolidPolygonLayer`.
671
+ `GeoArrowHeatmapLayer`, and `GeoArrowColumnLayer`, plus Polygon promotion for
672
+ `GeoArrowPolygonLayer`. WKB/WKT MultiPolygon
673
+ uses the GeoJSON-binary path so separate polygon parts retain their nesting.
637
674
 
638
675
  The GeoArrow layer implementations themselves come from
639
676
  [`@geoarrow/deck.gl-geoarrow`](https://github.com/geoarrow/deck.gl-geoarrow).
640
677
 
641
- When querying DuckDB spatial `GEOMETRY` columns directly, convert them first
642
- with `ST_AsWKB(...)` or `ST_AsText(...)`. DuckDB's internal geometry payload is
643
- not the same as standard WKB.
678
+ When querying DuckDB spatial `GEOMETRY` columns, the dataset pipeline probes
679
+ output types with `DESCRIBE` and projects native `GEOMETRY` columns through
680
+ `SELECT * REPLACE (ST_AsWKB(col) AS col)` before prepare/decode. Matching is
681
+ exact for `GEOMETRY` and CRS-parameterized forms such as
682
+ `GEOMETRY('EPSG:4326')` — not containers like `GEOMETRY[]`. Authored SQL is
683
+ not rewritten — the wrap is an outer pipeline query. Prefer writing
684
+ `ST_AsWKB(...)` in transforms when you control the SQL; the pipeline covers
685
+ bare `ST_Point(...)` / table `GEOMETRY` columns for every authoring surface.
686
+
687
+ ## AI map config prepare
688
+
689
+ AI and host tooling should prepare authored Deck map configs through the shared
690
+ helpers exported from `@sqlrooms/deck`:
691
+
692
+ - `prepareAiDeckMapConfig(config, {resolveTable?, stripCatalogNames?})` — preferred entrypoint:
693
+ validates `colorScale.field` names against known tables when a resolver is
694
+ provided, then runs normalization. Hosts that attach a workspace DB under a
695
+ catalog that is **not** in scope for dataset SQL should pass that catalog via
696
+ `stripCatalogNames` (CLI passes `['sqlrooms-cli']`). Default is **no
697
+ stripping** so other apps keep their own catalogs / attached remotes intact.
698
+ Dashboard AI helpers (`createDashboardAgentToolWithDeckMaps`,
699
+ `createDashboardWithDeckMapAiTools`, `createDeckMapDashboardTool`) accept the
700
+ same option.
701
+ - `normalizeAiDeckMapConfig(config, {stripCatalogNames?})` — safe structural defaults only (scheme
702
+ casing, solo-dataset binding inject, size clamps, heatmap `colorRange` strip,
703
+ lon/lat → WKB transform inject). `SELECT *` / `ST_AsWKB` alias collisions are
704
+ rejected by validation for agent retry — SQL is not rewritten.
705
+ Does not invent schemes or silently mutate polygon geometry into centroids.
706
+ - `validateAndFixColorScaleFields(config, resolveTable)` — casing fix for
707
+ base-table columns; hard-rejects unknown fields on bare `{tableName}` sources.
708
+ Skips unknown-field rejection when `transformSql` / `sqlQuery` is present
709
+ (aliases may be transform-only).
710
+
711
+ Durable resource writes also run `getDeckMapResourceConfigIssues` /
712
+ `assertDeckMapResourceConfig` for syntax, supported layer types, and type/scheme
713
+ compatibility (e.g. `quantile` + `Viridis` is rejected; layer `@@type` must be
714
+ one of the settings picker classes). Native `GEOMETRY` columns are
715
+ normalized to WKB by the dataset pipeline (not by SQL-string validation).
644
716
 
645
717
  ## Runtime Props and Children
646
718
 
@@ -1 +1 @@
1
- {"version":3,"file":"BlockMapSettings.d.ts","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,qBAAqB,CAAC;AAS7B,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,WAAW,EACX,eAAe,EACf,OAAO,EACP,QAAQ,GACT,EAAE,2BAA2B,2CA0G7B"}
1
+ {"version":3,"file":"BlockMapSettings.d.ts","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,2BAA2B,EAEjC,MAAM,qBAAqB,CAAC;AAM7B,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,WAAW,EACX,eAAe,EACf,OAAO,EACP,QAAQ,GACT,EAAE,2BAA2B,2CA2E7B"}
@@ -1,13 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getTableIdentity } from '@sqlrooms/duckdb';
3
3
  import { blockDocumentContentToBlocks, useStoreWithBlockDocuments, } from '@sqlrooms/documents';
4
- import { Button } from '@sqlrooms/ui';
5
- import { XIcon } from 'lucide-react';
6
4
  import { useCallback, useMemo } from 'react';
7
5
  import { useStoreWithDeckMaps } from './DeckMapsSlice';
8
6
  import { DeckMapSettingsPanel } from './MapSettings';
9
- import { regenerateMapConfigForTable } from './mapConfigUtils';
10
- import { getDeckMapResourceConfigIssues } from './mapResourceAuthoring';
7
+ import { applyDeckMapTableSelection } from './mapConfigUtils';
11
8
  export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, onClose, readOnly, }) {
12
9
  const mapId = blockInstanceId ?? blockId;
13
10
  const map = useStoreWithDeckMaps((state) => state.deckMaps.config.mapsById[mapId]);
@@ -22,7 +19,6 @@ export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, on
22
19
  candidate.type === 'statefulBlock' &&
23
20
  candidate.blockType === 'map')
24
21
  : undefined, [artifact, blockId]);
25
- const configIssues = useMemo(() => map ? getDeckMapResourceConfigIssues(map.config, { allowEmpty: true }) : [], [map]);
26
22
  const handleTitleChange = useCallback((title) => {
27
23
  if (readOnly)
28
24
  return;
@@ -37,21 +33,13 @@ export function DeckMapBlockSettings({ blockId, dashboardId, blockInstanceId, on
37
33
  const handleTableChange = useCallback((table) => {
38
34
  if (readOnly || !map)
39
35
  return;
40
- const config = regenerateMapConfigForTable({ config: map.config }, table);
41
- if (config === map.config)
42
- return;
43
36
  updateMap(mapId, {
44
37
  selectedTable: getTableIdentity(table.table),
45
- config: config,
38
+ config: applyDeckMapTableSelection(map.config, table),
46
39
  });
47
40
  }, [map, mapId, readOnly, updateMap]);
48
41
  if (!map)
49
42
  return (_jsx("div", { className: "text-muted-foreground p-4 text-sm", children: "Map settings not available" }));
50
- if (configIssues.length > 0 || map.config.configMode === 'custom') {
51
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium", children: [_jsx("span", { children: "Map settings" }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", onClick: onClose, "aria-label": "Close", children: _jsx(XIcon, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("div", { className: "text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm", children: configIssues.length > 0
52
- ? `Invalid map configuration: ${configIssues[0].path}: ${configIssues[0].message}`
53
- : 'This custom map configuration cannot be safely edited with the basic settings controls.' })] }));
54
- }
55
- return (_jsx(DeckMapSettingsPanel, { title: block?.caption ?? map.title, selectedTable: map.selectedTable, config: map.config, tables: tables.filter((table) => table.columns.length > 0), onClose: onClose, onTitleChange: handleTitleChange, onTableChange: handleTableChange, onConfigChange: (config) => updateMap(mapId, { config }), readOnly: readOnly }));
43
+ return (_jsx(DeckMapSettingsPanel, { title: block?.caption ?? map.title, selectedTable: map.selectedTable, config: map.config, tables: tables.filter((table) => table.columns.length > 0), onClose: onClose, onTitleChange: handleTitleChange, onTableChange: handleTableChange, onConfigChange: (config) => updateMap(mapId, { config }), readOnly: readOnly, customConfig: map.config.configMode === 'custom', preferDatasetSource: true }));
56
44
  }
57
45
  //# sourceMappingURL=BlockMapSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlockMapSettings.js","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport {\n blockDocumentContentToBlocks,\n type BlockDocumentStatefulBlockBlock,\n type BlockSettingsComponentProps,\n useStoreWithBlockDocuments,\n} from '@sqlrooms/documents';\nimport {Button} from '@sqlrooms/ui';\nimport {XIcon} from 'lucide-react';\nimport {useCallback, useMemo} from 'react';\nimport {useStoreWithDeckMaps} from './DeckMapsSlice';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {regenerateMapConfigForTable} from './mapConfigUtils';\nimport {getDeckMapResourceConfigIssues} from './mapResourceAuthoring';\n\nexport function DeckMapBlockSettings({\n blockId,\n dashboardId,\n blockInstanceId,\n onClose,\n readOnly,\n}: BlockSettingsComponentProps) {\n const mapId = blockInstanceId ?? blockId;\n const map = useStoreWithDeckMaps(\n (state) => state.deckMaps.config.mapsById[mapId],\n );\n const tables = useStoreWithDeckMaps((state) => state.db.tables);\n const updateMap = useStoreWithDeckMaps((state) => state.deckMaps.updateMap);\n const artifact = useStoreWithBlockDocuments((state) =>\n dashboardId\n ? state.blockDocuments.config.artifacts[dashboardId]\n : undefined,\n );\n const updateBlock = useStoreWithBlockDocuments(\n (state) => state.blockDocuments.updateBlock,\n );\n const block = useMemo(\n () =>\n artifact\n ? blockDocumentContentToBlocks(artifact.content).find(\n (candidate): candidate is BlockDocumentStatefulBlockBlock =>\n candidate.id === blockId &&\n candidate.type === 'statefulBlock' &&\n candidate.blockType === 'map',\n )\n : undefined,\n [artifact, blockId],\n );\n const configIssues = useMemo(\n () =>\n map ? getDeckMapResourceConfigIssues(map.config, {allowEmpty: true}) : [],\n [map],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n updateMap(mapId, {title});\n if (dashboardId && block) {\n updateBlock(dashboardId, blockId, {\n ...block,\n caption: title || undefined,\n });\n }\n },\n [block, blockId, dashboardId, mapId, readOnly, updateBlock, updateMap],\n );\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly || !map) return;\n const config = regenerateMapConfigForTable({config: map.config}, table);\n if (config === map.config) return;\n updateMap(mapId, {\n selectedTable: getTableIdentity(table.table),\n config: config as typeof map.config,\n });\n },\n [map, mapId, readOnly, updateMap],\n );\n\n if (!map)\n return (\n <div className=\"text-muted-foreground p-4 text-sm\">\n Map settings not available\n </div>\n );\n\n if (configIssues.length > 0 || map.config.configMode === 'custom') {\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium\">\n <span>Map settings</span>\n {onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-5 w-5\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <XIcon className=\"h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n <div className=\"text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm\">\n {configIssues.length > 0\n ? `Invalid map configuration: ${configIssues[0]!.path}: ${configIssues[0]!.message}`\n : 'This custom map configuration cannot be safely edited with the basic settings controls.'}\n </div>\n </div>\n );\n }\n\n return (\n <DeckMapSettingsPanel\n title={block?.caption ?? map.title}\n selectedTable={map.selectedTable}\n config={map.config}\n tables={tables.filter((table) => table.columns.length > 0)}\n onClose={onClose}\n onTitleChange={handleTitleChange}\n onTableChange={handleTableChange}\n onConfigChange={(config) => updateMap(mapId, {config})}\n readOnly={readOnly}\n />\n );\n}\n"]}
1
+ {"version":3,"file":"BlockMapSettings.js","sourceRoot":"","sources":["../src/BlockMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport {\n blockDocumentContentToBlocks,\n type BlockDocumentStatefulBlockBlock,\n type BlockSettingsComponentProps,\n useStoreWithBlockDocuments,\n} from '@sqlrooms/documents';\nimport {useCallback, useMemo} from 'react';\nimport {useStoreWithDeckMaps} from './DeckMapsSlice';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {applyDeckMapTableSelection} from './mapConfigUtils';\n\nexport function DeckMapBlockSettings({\n blockId,\n dashboardId,\n blockInstanceId,\n onClose,\n readOnly,\n}: BlockSettingsComponentProps) {\n const mapId = blockInstanceId ?? blockId;\n const map = useStoreWithDeckMaps(\n (state) => state.deckMaps.config.mapsById[mapId],\n );\n const tables = useStoreWithDeckMaps((state) => state.db.tables);\n const updateMap = useStoreWithDeckMaps((state) => state.deckMaps.updateMap);\n const artifact = useStoreWithBlockDocuments((state) =>\n dashboardId\n ? state.blockDocuments.config.artifacts[dashboardId]\n : undefined,\n );\n const updateBlock = useStoreWithBlockDocuments(\n (state) => state.blockDocuments.updateBlock,\n );\n const block = useMemo(\n () =>\n artifact\n ? blockDocumentContentToBlocks(artifact.content).find(\n (candidate): candidate is BlockDocumentStatefulBlockBlock =>\n candidate.id === blockId &&\n candidate.type === 'statefulBlock' &&\n candidate.blockType === 'map',\n )\n : undefined,\n [artifact, blockId],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n updateMap(mapId, {title});\n if (dashboardId && block) {\n updateBlock(dashboardId, blockId, {\n ...block,\n caption: title || undefined,\n });\n }\n },\n [block, blockId, dashboardId, mapId, readOnly, updateBlock, updateMap],\n );\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly || !map) return;\n updateMap(mapId, {\n selectedTable: getTableIdentity(table.table),\n config: applyDeckMapTableSelection(map.config, table),\n });\n },\n [map, mapId, readOnly, updateMap],\n );\n\n if (!map)\n return (\n <div className=\"text-muted-foreground p-4 text-sm\">\n Map settings not available\n </div>\n );\n\n return (\n <DeckMapSettingsPanel\n title={block?.caption ?? map.title}\n selectedTable={map.selectedTable}\n config={map.config}\n tables={tables.filter((table) => table.columns.length > 0)}\n onClose={onClose}\n onTitleChange={handleTitleChange}\n onTableChange={handleTableChange}\n onConfigChange={(config) => updateMap(mapId, {config})}\n readOnly={readOnly}\n customConfig={map.config.configMode === 'custom'}\n preferDatasetSource\n />\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardMapSettings.d.ts","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,qBAAqB,CAAC;AASrE,OAAO,EAAC,KAAK,EAAE,EAAc,MAAM,OAAO,CAAC;AAQ3C;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,EAAE,CAAC,2BAA2B,CAoHpE,CAAC"}
1
+ {"version":3,"file":"DashboardMapSettings.d.ts","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,qBAAqB,CAAC;AAOrE,OAAO,EAAC,KAAK,EAAE,EAAc,MAAM,OAAO,CAAC;AAQ3C;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,EAAE,CAAC,2BAA2B,CA+FpE,CAAC"}
@@ -1,8 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getTableIdentity } from '@sqlrooms/duckdb';
3
3
  import { ConfirmDatasetChangeDialog, useConfirmDatasetChange, useStoreWithMosaicDashboard, useTablesWithColumns, } from '@sqlrooms/mosaic';
4
- import { Button } from '@sqlrooms/ui';
5
- import { XIcon } from 'lucide-react';
6
4
  import { useCallback } from 'react';
7
5
  import { DeckMapSettingsPanel } from './MapSettings';
8
6
  import { DECK_MAP_DASHBOARD_PANEL_TYPE, } from './dashboardConfig';
@@ -36,17 +34,16 @@ export const DeckMapDashboardSettings = ({ blockId, dashboardId, onClose, readOn
36
34
  });
37
35
  }, [blockId, dashboardId, readOnly, updatePanel]);
38
36
  const { handleChangeRequest, handleConfirm, handleCancel, isDialogOpen } = useConfirmDatasetChange(handleTableChange);
37
+ const panel = dashboard?.panels.find((candidate) => candidate.id === blockId);
38
+ const mapConfig = panel?.type === DECK_MAP_DASHBOARD_PANEL_TYPE
39
+ ? panel.config
40
+ : undefined;
39
41
  if (!dashboard) {
40
42
  return (_jsx("div", { className: "flex h-full items-center justify-center p-4", children: _jsx("p", { className: "text-muted-foreground text-sm", children: "Dashboard not found" }) }));
41
43
  }
42
- const panel = dashboard.panels.find((candidate) => candidate.id === blockId);
43
- if (!panel || panel.type !== DECK_MAP_DASHBOARD_PANEL_TYPE) {
44
+ if (!panel || !mapConfig) {
44
45
  return (_jsx("div", { className: "flex h-full items-center justify-center p-4", children: _jsx("p", { className: "text-muted-foreground text-sm", children: "Map panel not found" }) }));
45
46
  }
46
- const mapConfig = panel.config;
47
- if (mapConfig.configMode === 'custom') {
48
- return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium", children: [_jsx("span", { children: "Map settings" }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon", className: "h-5 w-5", onClick: onClose, "aria-label": "Close", children: _jsx(XIcon, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("div", { className: "text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm", children: "This custom map configuration cannot be safely edited with the basic settings controls." })] }));
49
- }
50
- return (_jsxs(_Fragment, { children: [_jsx(DeckMapSettingsPanel, { title: panel.title, selectedTable: dashboard.selectedTable, config: mapConfig, tables: tables, onClose: onClose, onTableChange: handleChangeRequest, onTitleChange: handleTitleChange, onConfigChange: handleConfigChange, readOnly: readOnly }), _jsx(ConfirmDatasetChangeDialog, { open: isDialogOpen, onConfirm: handleConfirm, onCancel: handleCancel })] }));
47
+ return (_jsxs(_Fragment, { children: [_jsx(DeckMapSettingsPanel, { title: panel.title, selectedTable: dashboard.selectedTable, config: mapConfig, tables: tables, onClose: onClose, onTableChange: handleChangeRequest, onTitleChange: handleTitleChange, onConfigChange: handleConfigChange, readOnly: readOnly, customConfig: mapConfig.configMode === 'custom' }), _jsx(ConfirmDatasetChangeDialog, { open: isDialogOpen, onConfirm: handleConfirm, onCancel: handleCancel })] }));
51
48
  };
52
49
  //# sourceMappingURL=DashboardMapSettings.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardMapSettings.js","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport type {BlockSettingsComponentProps} from '@sqlrooms/documents';\nimport {\n ConfirmDatasetChangeDialog,\n useConfirmDatasetChange,\n useStoreWithMosaicDashboard,\n useTablesWithColumns,\n} from '@sqlrooms/mosaic';\nimport {Button} from '@sqlrooms/ui';\nimport {XIcon} from 'lucide-react';\nimport {type FC, useCallback} from 'react';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n type DeckMapDashboardPanelConfig,\n} from './dashboardConfig';\nimport type {DeckMapConfig} from './mapConfig';\n\n/**\n * Settings adapter for a Deck map panel inside a Mosaic dashboard.\n */\nexport const DeckMapDashboardSettings: FC<BlockSettingsComponentProps> = ({\n blockId,\n dashboardId,\n onClose,\n readOnly,\n}) => {\n const dashboard = useStoreWithMosaicDashboard((state) =>\n dashboardId ? state.mosaicDashboard.getDashboard(dashboardId) : undefined,\n );\n const updatePanel = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.updatePanel,\n );\n const setSelectedTable = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.setSelectedTable,\n );\n const tables = useTablesWithColumns();\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly) return;\n\n if (dashboardId) {\n setSelectedTable(dashboardId, getTableIdentity(table.table));\n }\n },\n [dashboardId, readOnly, setSelectedTable],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n\n if (dashboardId) {\n updatePanel(dashboardId, blockId, {title: title || undefined});\n }\n },\n [dashboardId, blockId, readOnly, updatePanel],\n );\n\n const handleConfigChange = useCallback(\n (config: DeckMapConfig) => {\n if (readOnly || !dashboardId) return;\n updatePanel(dashboardId, blockId, {\n config: config as unknown as Record<string, unknown>,\n });\n },\n [blockId, dashboardId, readOnly, updatePanel],\n );\n\n const {handleChangeRequest, handleConfirm, handleCancel, isDialogOpen} =\n useConfirmDatasetChange(handleTableChange);\n\n if (!dashboard) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Dashboard not found</p>\n </div>\n );\n }\n\n const panel = dashboard.panels.find((candidate) => candidate.id === blockId);\n\n if (!panel || panel.type !== DECK_MAP_DASHBOARD_PANEL_TYPE) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Map panel not found</p>\n </div>\n );\n }\n\n const mapConfig = panel.config as DeckMapDashboardPanelConfig;\n if (mapConfig.configMode === 'custom') {\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between border-b px-3 py-1.5 text-xs font-medium\">\n <span>Map settings</span>\n {onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-5 w-5\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n <XIcon className=\"h-3.5 w-3.5\" />\n </Button>\n ) : null}\n </div>\n <div className=\"text-muted-foreground flex flex-1 items-center justify-center p-4 text-center text-sm\">\n This custom map configuration cannot be safely edited with the basic\n settings controls.\n </div>\n </div>\n );\n }\n\n return (\n <>\n <DeckMapSettingsPanel\n title={panel.title}\n selectedTable={dashboard.selectedTable}\n config={mapConfig}\n tables={tables}\n onClose={onClose}\n onTableChange={handleChangeRequest}\n onTitleChange={handleTitleChange}\n onConfigChange={handleConfigChange}\n readOnly={readOnly}\n />\n <ConfirmDatasetChangeDialog\n open={isDialogOpen}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n </>\n );\n};\n"]}
1
+ {"version":3,"file":"DashboardMapSettings.js","sourceRoot":"","sources":["../src/DashboardMapSettings.tsx"],"names":[],"mappings":"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","sourcesContent":["import {getTableIdentity, type DataTable} from '@sqlrooms/duckdb';\nimport type {BlockSettingsComponentProps} from '@sqlrooms/documents';\nimport {\n ConfirmDatasetChangeDialog,\n useConfirmDatasetChange,\n useStoreWithMosaicDashboard,\n useTablesWithColumns,\n} from '@sqlrooms/mosaic';\nimport {type FC, useCallback} from 'react';\nimport {DeckMapSettingsPanel} from './MapSettings';\nimport {\n DECK_MAP_DASHBOARD_PANEL_TYPE,\n type DeckMapDashboardPanelConfig,\n} from './dashboardConfig';\nimport type {DeckMapConfig} from './mapConfig';\n\n/**\n * Settings adapter for a Deck map panel inside a Mosaic dashboard.\n */\nexport const DeckMapDashboardSettings: FC<BlockSettingsComponentProps> = ({\n blockId,\n dashboardId,\n onClose,\n readOnly,\n}) => {\n const dashboard = useStoreWithMosaicDashboard((state) =>\n dashboardId ? state.mosaicDashboard.getDashboard(dashboardId) : undefined,\n );\n const updatePanel = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.updatePanel,\n );\n const setSelectedTable = useStoreWithMosaicDashboard(\n (state) => state.mosaicDashboard.setSelectedTable,\n );\n const tables = useTablesWithColumns();\n\n const handleTableChange = useCallback(\n (table: DataTable) => {\n if (readOnly) return;\n\n if (dashboardId) {\n setSelectedTable(dashboardId, getTableIdentity(table.table));\n }\n },\n [dashboardId, readOnly, setSelectedTable],\n );\n\n const handleTitleChange = useCallback(\n (title: string) => {\n if (readOnly) return;\n\n if (dashboardId) {\n updatePanel(dashboardId, blockId, {title: title || undefined});\n }\n },\n [dashboardId, blockId, readOnly, updatePanel],\n );\n\n const handleConfigChange = useCallback(\n (config: DeckMapConfig) => {\n if (readOnly || !dashboardId) return;\n updatePanel(dashboardId, blockId, {\n config: config as unknown as Record<string, unknown>,\n });\n },\n [blockId, dashboardId, readOnly, updatePanel],\n );\n\n const {handleChangeRequest, handleConfirm, handleCancel, isDialogOpen} =\n useConfirmDatasetChange(handleTableChange);\n\n const panel = dashboard?.panels.find((candidate) => candidate.id === blockId);\n const mapConfig =\n panel?.type === DECK_MAP_DASHBOARD_PANEL_TYPE\n ? (panel.config as DeckMapDashboardPanelConfig)\n : undefined;\n\n if (!dashboard) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Dashboard not found</p>\n </div>\n );\n }\n\n if (!panel || !mapConfig) {\n return (\n <div className=\"flex h-full items-center justify-center p-4\">\n <p className=\"text-muted-foreground text-sm\">Map panel not found</p>\n </div>\n );\n }\n\n return (\n <>\n <DeckMapSettingsPanel\n title={panel.title}\n selectedTable={dashboard.selectedTable}\n config={mapConfig}\n tables={tables}\n onClose={onClose}\n onTableChange={handleChangeRequest}\n onTitleChange={handleTitleChange}\n onConfigChange={handleConfigChange}\n readOnly={readOnly}\n customConfig={mapConfig.configMode === 'custom'}\n />\n <ConfirmDatasetChangeDialog\n open={isDialogOpen}\n onConfirm={handleConfirm}\n onCancel={handleCancel}\n />\n </>\n );\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DeckJsonMap.d.ts","sourceRoot":"","sources":["../src/DeckJsonMap.tsx"],"names":[],"mappings":"AAIA,OAAO,kCAAkC,CAAC;AAwB1C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAgPjB,eAAO,MAAM,WAAW,gHA+VvB,CAAC"}
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"}
@@ -2,7 +2,8 @@ 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 { cn, useTheme } from '@sqlrooms/ui';
5
+ import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger, useTheme, } from '@sqlrooms/ui';
6
+ import { ChevronRightIcon } from 'lucide-react';
6
7
  import 'maplibre-gl/dist/maplibre-gl.css';
7
8
  import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
8
9
  import { forwardRef } from 'react';
@@ -15,6 +16,7 @@ import { createDeckJsonConfiguration } from './json/createDeckJsonConfiguration'
15
16
  import { extractColorScaleLegends } from './json/extractColorScaleLegends';
16
17
  import { getLayerCompatibility } from './json/layerCompatibility';
17
18
  import { resolveDatasetId } from './json/layerConfig';
19
+ import { buildDeckMapJumpToOptions } from './mapFit';
18
20
  const DEFAULT_MAP_STYLES = {
19
21
  light: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
20
22
  dark: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
@@ -102,6 +104,14 @@ const TripsTimeControl = ({ currentTime, maxTime, playing, onPlayPause, onSeek }
102
104
  }
103
105
  return (_jsxs("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", children: [_jsx("button", { type: "button", onClick: onPlayPause, className: "text-foreground hover:bg-accent flex h-5 w-5 shrink-0 items-center justify-center rounded", title: playing ? 'Pause' : 'Play', children: playing ? (_jsxs("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "currentColor", children: [_jsx("rect", { x: "1", y: "1", width: "3", height: "8" }), _jsx("rect", { x: "6", y: "1", width: "3", height: "8" })] })) : (_jsx("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "currentColor", children: _jsx("polygon", { points: "2,1 9,5 2,9" }) })) }), _jsx("input", { type: "range", min: 0, max: 1000, value: Math.round(progress * 1000), onChange: (e) => onSeek((Number(e.target.value) / 1000) * maxTime), className: "h-1 w-24 flex-1 cursor-pointer" }), _jsx("button", { type: "button", onClick: () => setCollapsed(true), 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", title: "Collapse", children: _jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", children: _jsx("path", { d: "M1 1L7 7M7 1L1 7", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) })] }));
104
106
  };
107
+ function getDeckLayerTypeName(layer) {
108
+ if (!layer || typeof layer !== 'object')
109
+ return '?';
110
+ const ctor = layer.constructor;
111
+ // Prefer static layerName: table-to-RecordBatch adapters all share
112
+ // constructor.name "Adapter", which would miss heatmap ↔ scatterplot swaps.
113
+ return ctor?.layerName ?? ctor?.name ?? '?';
114
+ }
105
115
  // Workaround for deck.gl HeatmapLayer not releasing its WebGL framebuffer
106
116
  // when replaced by another layer type (e.g. switching heatmap → scatterplot).
107
117
  // We detect layer class changes and clear layers for one animation frame,
@@ -113,14 +123,7 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
113
123
  const [clearing, setClearing] = useState(false);
114
124
  const layers = deckProps.layers;
115
125
  const layerKey = Array.isArray(layers)
116
- ? layers
117
- .map((l) => {
118
- if (!l || typeof l !== 'object')
119
- return '?';
120
- const ctor = l.constructor;
121
- return ctor?.name ?? '?';
122
- })
123
- .join(',')
126
+ ? layers.map(getDeckLayerTypeName).join(',')
124
127
  : '';
125
128
  useEffect(() => {
126
129
  if (prevLayerKeyRef.current &&
@@ -141,9 +144,13 @@ function DeckOverlayControl({ interleaved, ...deckProps }) {
141
144
  }
142
145
  return null;
143
146
  }
147
+ function DeckMapRenderingErrorOverlay({ error }) {
148
+ 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
+ }
144
150
  export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, mapStyle, interleaved = true, deckProps, mapProps, showLegends = true, className, children, onDatasetStatesChange, onRenderingError, }, ref) {
145
151
  const normalizedDatasets = useMemo(() => normalizeDatasets(datasets), [datasets]);
146
- const datasetIds = useMemo(() => Object.keys(normalizedDatasets), [normalizedDatasets]);
152
+ const datasetIdKey = JSON.stringify(Object.keys(normalizedDatasets).sort());
153
+ const datasetIds = useMemo(() => JSON.parse(datasetIdKey), [datasetIdKey]);
147
154
  const datasetStates = usePreparedDatasetStates(normalizedDatasets);
148
155
  const onDatasetStatesChangeRef = useRef(onDatasetStatesChange);
149
156
  const { spec: parsedSpec, error: specError } = useMemo(() => parseSpec(spec), [spec]);
@@ -322,12 +329,7 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
322
329
  const pendingJumpRef = useRef(null);
323
330
  useImperativeHandle(ref, () => ({
324
331
  jumpTo(opts) {
325
- const jumpOpts = {
326
- center: [opts.longitude, opts.latitude],
327
- zoom: opts.zoom,
328
- bearing: opts.bearing ?? 0,
329
- pitch: opts.pitch ?? 0,
330
- };
332
+ const jumpOpts = buildDeckMapJumpToOptions(opts);
331
333
  if (mapRef.current) {
332
334
  mapRef.current.jumpTo(jumpOpts);
333
335
  }
@@ -362,8 +364,8 @@ export const DeckJsonMap = forwardRef(function DeckJsonMap({ spec, datasets, map
362
364
  datasetIds,
363
365
  datasetStates,
364
366
  })
365
- : [], [availableSpec, datasetIds, datasetStates, showLegends]);
366
- return (_jsxs("div", { className: cn('relative h-full w-full', className), children: [hasRenderingError ? (_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: `Map couldn't be rendered. Check the console for details.` }) })) : null, _jsxs(Map, { ref: mapRef, ...mergedMapProps, ...(initialViewState
367
+ : [], [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
367
369
  ? { initialViewState: initialViewState }
368
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] }));
369
371
  });