@sqlrooms/kepler 0.29.0-rc.7 → 0.29.0-rc.8

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 (46) hide show
  1. package/LICENSE.md +2 -1
  2. package/README.md +121 -4
  3. package/dist/KeplerSlice.d.ts +55 -2
  4. package/dist/KeplerSlice.d.ts.map +1 -1
  5. package/dist/KeplerSlice.js +92 -15
  6. package/dist/KeplerSlice.js.map +1 -1
  7. package/dist/components/CustomFilterManager.d.ts.map +1 -1
  8. package/dist/components/CustomFilterManager.js +16 -4
  9. package/dist/components/CustomFilterManager.js.map +1 -1
  10. package/dist/components/CustomLayerManager.d.ts.map +1 -1
  11. package/dist/components/CustomLayerManager.js +23 -110
  12. package/dist/components/CustomLayerManager.js.map +1 -1
  13. package/dist/components/CustomMapLegendPanel.d.ts +1 -1
  14. package/dist/components/CustomMapLegendPanel.d.ts.map +1 -1
  15. package/dist/components/CustomMapLegendPanel.js +14 -6
  16. package/dist/components/CustomMapLegendPanel.js.map +1 -1
  17. package/dist/components/CustomSourceDataSelector.d.ts +18 -0
  18. package/dist/components/CustomSourceDataSelector.d.ts.map +1 -0
  19. package/dist/components/CustomSourceDataSelector.js +130 -0
  20. package/dist/components/CustomSourceDataSelector.js.map +1 -0
  21. package/dist/components/KeplerInjector.d.ts.map +1 -1
  22. package/dist/components/KeplerInjector.js +3 -1
  23. package/dist/components/KeplerInjector.js.map +1 -1
  24. package/dist/components/KeplerMapContainer.d.ts.map +1 -1
  25. package/dist/components/KeplerMapContainer.js +9 -2
  26. package/dist/components/KeplerMapContainer.js.map +1 -1
  27. package/dist/components/KeplerProvider.d.ts.map +1 -1
  28. package/dist/components/KeplerProvider.js +3 -1
  29. package/dist/components/KeplerProvider.js.map +1 -1
  30. package/dist/components/KeplerTableSourceSelector.d.ts +20 -0
  31. package/dist/components/KeplerTableSourceSelector.d.ts.map +1 -0
  32. package/dist/components/KeplerTableSourceSelector.js +21 -0
  33. package/dist/components/KeplerTableSourceSelector.js.map +1 -0
  34. package/dist/index.d.ts +6 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/keplerTableSelection.d.ts +142 -0
  39. package/dist/keplerTableSelection.d.ts.map +1 -0
  40. package/dist/keplerTableSelection.js +231 -0
  41. package/dist/keplerTableSelection.js.map +1 -0
  42. package/dist/styles/theme.d.ts +12 -0
  43. package/dist/styles/theme.d.ts.map +1 -1
  44. package/dist/styles/theme.js +12 -0
  45. package/dist/styles/theme.js.map +1 -1
  46. package/package.json +13 -7
@@ -1,13 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useCallback, useMemo, useRef, useState } from 'react';
2
+ import { useCallback, useMemo, useState } from 'react';
3
3
  import { useIntl } from 'react-intl';
4
4
  import styled from 'styled-components';
5
- import { Accessor, Button, Icons, LayerListFactory, DatasetLayerGroupFactory, PanelTitleFactory, SidePanelSection, Typeahead, } from '@kepler.gl/components';
5
+ import { LayerListFactory, DatasetLayerGroupFactory, PanelTitleFactory, SidePanelSection, } from '@kepler.gl/components';
6
6
  import { PANEL_VIEW_TOGGLES, SIDEBAR_PANELS } from '@kepler.gl/constants';
7
7
  import { getApplicationConfig } from '@kepler.gl/utils';
8
+ import { Plus } from 'lucide-react';
9
+ import { Button } from '@sqlrooms/ui';
8
10
  import { getKeplerFactory } from './KeplerInjector';
9
11
  import { useKeplerStateActions, } from '../hooks/useKeplerStateActions';
12
+ import { buildKeplerTableSourceOptions, } from '../keplerTableSelection';
10
13
  import { useStoreWithKepler } from '../KeplerSlice';
14
+ import { KeplerTableSourceSelector } from './KeplerTableSourceSelector';
11
15
  const LayerList = getKeplerFactory(LayerListFactory);
12
16
  const DatasetLayerGroup = getKeplerFactory(DatasetLayerGroupFactory);
13
17
  const PanelTitle = getKeplerFactory(PanelTitleFactory);
@@ -51,124 +55,30 @@ const CustomLayerManagerContainer = styled.div `
51
55
  }
52
56
  }
53
57
  `;
54
- const DropdownWrapper = styled.div `
55
- position: relative;
56
- display: inline-block;
57
- overflow: visible;
58
- `;
59
- const DropdownMenu = styled.div `
60
- display: flex;
61
- flex-direction: column;
62
- min-width: 240px;
63
- max-width: 240px;
64
- position: fixed;
65
- z-index: 100;
66
-
67
- .list-selector {
68
- border-top: 1px solid ${(props) => props.theme.secondaryInputBorderColor};
69
- width: 100%;
70
- max-height: unset;
71
- }
72
- .list__item > div {
73
- display: flex;
74
- flex-direction: row;
75
- justify-content: flex-start;
76
- line-height: 18px;
77
- padding: 0;
78
- }
79
- `;
80
- const ListItemWrapper = styled.div `
81
- display: flex;
82
- color: ${(props) => props.theme.textColor};
83
- font-size: 11px;
84
- letter-spacing: 0.2px;
85
- overflow: auto;
86
- .table-name {
87
- white-space: nowrap;
88
- overflow: hidden;
89
- text-overflow: ellipsis;
90
- }
91
- `;
92
- const TYPEAHEAD_CLASS = 'typeahead';
93
- const TYPEAHEAD_INPUT_CLASS = 'typeahead__input';
94
- const TableListItem = ({ value }) => (_jsx(ListItemWrapper, { children: _jsx("div", { className: "table-name", title: value.label, children: value.label }) }));
95
58
  /**
96
59
  * Dropdown button that lists available datasets and DuckDB tables.
97
60
  * When a table is selected, it is synced to Kepler on demand and a new
98
61
  * layer is created for it.
99
62
  */
100
- const AddTableLayerButton = ({ onAdd, keplerDatasetIds, disabled }) => {
101
- const [isOpen, setIsOpen] = useState(false);
63
+ const AddTableLayerButton = ({ onAdd, datasets, disabled }) => {
102
64
  const [isAdding, setIsAdding] = useState(false);
103
- const dropdownRef = useRef(null);
104
- const buttonRef = useRef(null);
105
- const [menuPos, setMenuPos] = useState({
106
- top: 0,
107
- right: 0,
108
- });
109
65
  const intl = useIntl();
110
66
  const dbTables = useStoreWithKepler((state) => state.db.tables);
67
+ const tableSelection = useStoreWithKepler((state) => state.kepler.tableSelection);
111
68
  const options = useMemo(() => {
112
- const tableNames = new Set(dbTables
113
- .filter((t) => t.table.schema === 'main')
114
- .map((t) => t.table.table));
115
- for (const id of keplerDatasetIds) {
116
- tableNames.add(id);
117
- }
118
- return Array.from(tableNames)
119
- .sort()
120
- .map((name) => ({ label: name, value: name }));
121
- }, [dbTables, keplerDatasetIds]);
122
- const handleClick = useCallback(async () => {
123
- if (options.length === 1 && options[0]) {
124
- setIsAdding(true);
125
- try {
126
- await onAdd(options[0].value);
127
- }
128
- catch (e) {
129
- console.error('Failed to add layer:', e);
130
- }
131
- finally {
132
- setIsAdding(false);
133
- }
134
- return;
135
- }
136
- if (buttonRef.current) {
137
- const rect = buttonRef.current.getBoundingClientRect();
138
- setMenuPos({
139
- top: rect.bottom,
140
- right: window.innerWidth - rect.right,
141
- });
142
- }
143
- setIsOpen((prev) => !prev);
144
- }, [options, onAdd]);
69
+ return buildKeplerTableSourceOptions({
70
+ dbTables,
71
+ datasets,
72
+ tableSelection,
73
+ });
74
+ }, [datasets, dbTables, tableSelection]);
145
75
  const handleSelect = useCallback((option) => {
146
- if (!option)
147
- return;
148
76
  setIsAdding(true);
149
77
  onAdd(option.value)
150
- .then(() => setIsOpen(false))
151
78
  .catch((e) => console.error('Failed to add layer:', e))
152
79
  .finally(() => setIsAdding(false));
153
80
  }, [onAdd]);
154
- // Close dropdown on outside click
155
- React.useEffect(() => {
156
- if (!isOpen)
157
- return;
158
- const handleClickOutside = (e) => {
159
- if (dropdownRef.current &&
160
- !dropdownRef.current.contains(e.target)) {
161
- setIsOpen(false);
162
- }
163
- };
164
- document.addEventListener('mousedown', handleClickOutside);
165
- return () => document.removeEventListener('mousedown', handleClickOutside);
166
- }, [isOpen]);
167
- return (_jsxs(DropdownWrapper, { ref: dropdownRef, children: [_jsxs(Button, { ref: buttonRef, className: "add-layer-button", onClick: handleClick, disabled: !options.length || disabled || isAdding, children: [_jsx(Icons.Add, { height: "12px" }), intl.formatMessage({ id: 'layerManager.addLayer' })] }), isOpen && options.length > 1 && (_jsx(DropdownMenu, { style: { top: menuPos.top, right: menuPos.right }, children: _jsx(Typeahead, { className: TYPEAHEAD_CLASS, customClasses: {
168
- results: 'list-selector',
169
- input: TYPEAHEAD_INPUT_CLASS,
170
- listItem: 'list__item',
171
- }, placeholder: intl ? intl.formatMessage({ id: 'placeholder.search' }) : 'Search', selectedItems: null, options: options, displayOption: Accessor.generateOptionToStringFor('label'), filterOption: 'label', searchable: true, onOptionSelected: handleSelect, customListItemComponent: TableListItem }) }))] }));
81
+ return (_jsx(KeplerTableSourceSelector, { disabled: !options.length || disabled || isAdding, options: options, popoverAlign: "end", searchPlaceholder: intl ? intl.formatMessage({ id: 'placeholder.search' }) : 'Search', onSelect: handleSelect, renderTrigger: ({ disabled: isDisabled }) => (_jsxs(Button, { type: "button", variant: "ghost", size: "xs", className: "add-layer-button", disabled: isDisabled, children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), intl.formatMessage({ id: 'layerManager.addLayer' })] })) }));
172
82
  };
173
83
  function useCustomSidePanelActions(keplerActions) {
174
84
  const { openDeleteModal, toggleModal } = keplerActions.uiStateActions;
@@ -189,13 +99,16 @@ export const CustomLayerManager = ({ mapId, showDeleteDataset = true, }) => {
189
99
  const addTableToMap = useStoreWithKepler((state) => state.kepler.addTableToMap);
190
100
  const { onRemoveDataset, onUpdateTableColor } = useCustomSidePanelActions(keplerActions);
191
101
  const { addLayer } = keplerActions.visStateActions;
192
- const keplerDatasetIds = useMemo(() => Object.keys(keplerState?.visState.datasets ?? {}), [keplerState?.visState.datasets]);
193
102
  const onAddTableLayer = useCallback(async (tableName) => {
194
103
  const keplerDatasets = keplerState?.visState.datasets;
195
104
  if (!keplerDatasets?.[tableName]) {
196
- await addTableToMap(mapId, tableName, {
197
- autoCreateLayers: true,
198
- centerMap: true,
105
+ await addTableToMap({
106
+ mapId,
107
+ tableName,
108
+ options: {
109
+ autoCreateLayers: true,
110
+ centerMap: true,
111
+ },
199
112
  });
200
113
  }
201
114
  else {
@@ -226,6 +139,6 @@ export const CustomLayerManager = ({ mapId, showDeleteDataset = true, }) => {
226
139
  }
227
140
  return (_jsx(CustomLayerManagerContainer, { children: _jsxs("div", { className: "layer-manager", children: [_jsx(SidePanelSection, { children: _jsx(PanelTitle, { className: "layer-manager-title", title: intl.formatMessage({
228
141
  id: layerPanelMetadata?.label || 'Layers',
229
- }), children: _jsx(AddTableLayerButton, { onAdd: onAddTableLayer, keplerDatasetIds: keplerDatasetIds }) }) }), _jsx(SidePanelSection, { children: isSortByDatasetMode ? (_jsx(DatasetLayerGroup, { datasets: keplerState.visState.datasets, showDatasetTable: keplerActions.visStateActions.showDatasetTable, layers: keplerState.visState.layers, updateTableColor: onUpdateTableColor, removeDataset: onRemoveDataset, layerOrder: keplerState.visState.layerOrder, layerClasses: filteredLayerClasses, uiStateActions: keplerActions.uiStateActions, visStateActions: keplerActions.visStateActions, mapStateActions: keplerActions.mapStateActions, showDeleteDataset: showDeleteDataset })) : (_jsx(LayerList, { layers: keplerState.visState.layers, datasets: keplerState.visState.datasets, layerOrder: keplerState.visState.layerOrder, uiStateActions: keplerActions.uiStateActions, visStateActions: keplerActions.visStateActions, mapStateActions: keplerActions.mapStateActions, layerClasses: filteredLayerClasses })) })] }) }));
142
+ }), children: _jsx(AddTableLayerButton, { onAdd: onAddTableLayer, datasets: keplerState.visState.datasets }) }) }), _jsx(SidePanelSection, { children: isSortByDatasetMode ? (_jsx(DatasetLayerGroup, { datasets: keplerState.visState.datasets, showDatasetTable: keplerActions.visStateActions.showDatasetTable, layers: keplerState.visState.layers, updateTableColor: onUpdateTableColor, removeDataset: onRemoveDataset, layerOrder: keplerState.visState.layerOrder, layerClasses: filteredLayerClasses, uiStateActions: keplerActions.uiStateActions, visStateActions: keplerActions.visStateActions, mapStateActions: keplerActions.mapStateActions, showDeleteDataset: showDeleteDataset })) : (_jsx(LayerList, { layers: keplerState.visState.layers, datasets: keplerState.visState.datasets, layerOrder: keplerState.visState.layerOrder, uiStateActions: keplerActions.uiStateActions, visStateActions: keplerActions.visStateActions, mapStateActions: keplerActions.mapStateActions, layerClasses: filteredLayerClasses })) })] }) }));
230
143
  };
231
144
  //# sourceMappingURL=CustomLayerManager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CustomLayerManager.js","sourceRoot":"","sources":["../../src/components/CustomLayerManager.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useMemo, useRef, useState} from 'react';\nimport {useIntl} from 'react-intl';\nimport styled from 'styled-components';\n\nimport {\n Accessor,\n Button,\n Icons,\n LayerListFactory,\n DatasetLayerGroupFactory,\n PanelTitleFactory,\n SidePanelSection,\n Typeahead,\n} from '@kepler.gl/components';\nimport {PANEL_VIEW_TOGGLES, SIDEBAR_PANELS} from '@kepler.gl/constants';\nimport {LayerClassesType} from '@kepler.gl/layers';\nimport {getApplicationConfig} from '@kepler.gl/utils';\n\nimport {getKeplerFactory} from './KeplerInjector';\nimport {\n KeplerActions,\n useKeplerStateActions,\n} from '../hooks/useKeplerStateActions';\nimport {useStoreWithKepler} from '../KeplerSlice';\nimport {RGBColor} from '@kepler.gl/types';\n\nconst LayerList = getKeplerFactory(LayerListFactory);\nconst DatasetLayerGroup = getKeplerFactory(DatasetLayerGroupFactory);\nconst PanelTitle = getKeplerFactory(PanelTitleFactory);\n\nconst layerPanelMetadata = SIDEBAR_PANELS.find((p) => p.id === 'layer');\n\nconst CustomLayerManagerContainer = styled.div`\n .layer-manager-title {\n }\n\n .add-layer-button {\n background-color: ${(props) => props.theme.sidePanelBg || props.theme.panelBackground};\n color: #2563EB;\n border: 0px;\n height: 28px;\n font-weight: 500;\n font-size: 14px;\n }\n\n .layer__title__type {\n display: none !important;\n }\n\n .layer-panel__header {\n height: 36px;\n }\n\n .layer__title {\n min-width: 0;\n }\n\n .layer__title > div {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n }\n\n .layer__title__editor {\n width: 100%;\n min-width: 0;\n flex: 1;\n }\n}\n`;\n\nconst DropdownWrapper = styled.div`\n position: relative;\n display: inline-block;\n overflow: visible;\n`;\n\nconst DropdownMenu = styled.div`\n display: flex;\n flex-direction: column;\n min-width: 240px;\n max-width: 240px;\n position: fixed;\n z-index: 100;\n\n .list-selector {\n border-top: 1px solid ${(props) => props.theme.secondaryInputBorderColor};\n width: 100%;\n max-height: unset;\n }\n .list__item > div {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n line-height: 18px;\n padding: 0;\n }\n`;\n\nconst ListItemWrapper = styled.div`\n display: flex;\n color: ${(props) => props.theme.textColor};\n font-size: 11px;\n letter-spacing: 0.2px;\n overflow: auto;\n .table-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n`;\n\nconst TYPEAHEAD_CLASS = 'typeahead';\nconst TYPEAHEAD_INPUT_CLASS = 'typeahead__input';\n\ntype TableOption = {label: string; value: string};\ntype TypeaheadListItemComponent =\n (typeof Typeahead.defaultProps)['customListItemComponent'];\n\nconst TableListItem: React.FC<{value: TableOption}> = ({value}) => (\n <ListItemWrapper>\n <div className=\"table-name\" title={value.label}>\n {value.label}\n </div>\n </ListItemWrapper>\n);\n\n/**\n * Dropdown button that lists available datasets and DuckDB tables.\n * When a table is selected, it is synced to Kepler on demand and a new\n * layer is created for it.\n */\nconst AddTableLayerButton: React.FC<{\n onAdd: (tableName: string) => Promise<void>;\n keplerDatasetIds: string[];\n disabled?: boolean;\n}> = ({onAdd, keplerDatasetIds, disabled}) => {\n const [isOpen, setIsOpen] = useState(false);\n const [isAdding, setIsAdding] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [menuPos, setMenuPos] = useState<{top: number; right: number}>({\n top: 0,\n right: 0,\n });\n const intl = useIntl();\n\n const dbTables = useStoreWithKepler((state) => state.db.tables);\n\n const options: TableOption[] = useMemo(() => {\n const tableNames = new Set(\n dbTables\n .filter((t) => t.table.schema === 'main')\n .map((t) => t.table.table),\n );\n for (const id of keplerDatasetIds) {\n tableNames.add(id);\n }\n return Array.from(tableNames)\n .sort()\n .map((name) => ({label: name, value: name}));\n }, [dbTables, keplerDatasetIds]);\n\n const handleClick = useCallback(async () => {\n if (options.length === 1 && options[0]) {\n setIsAdding(true);\n try {\n await onAdd(options[0].value);\n } catch (e) {\n console.error('Failed to add layer:', e);\n } finally {\n setIsAdding(false);\n }\n return;\n }\n if (buttonRef.current) {\n const rect = buttonRef.current.getBoundingClientRect();\n setMenuPos({\n top: rect.bottom,\n right: window.innerWidth - rect.right,\n });\n }\n setIsOpen((prev) => !prev);\n }, [options, onAdd]);\n\n const handleSelect = useCallback(\n (option: TableOption | null) => {\n if (!option) return;\n setIsAdding(true);\n onAdd(option.value)\n .then(() => setIsOpen(false))\n .catch((e) => console.error('Failed to add layer:', e))\n .finally(() => setIsAdding(false));\n },\n [onAdd],\n );\n\n // Close dropdown on outside click\n React.useEffect(() => {\n if (!isOpen) return;\n const handleClickOutside = (e: MouseEvent) => {\n if (\n dropdownRef.current &&\n !dropdownRef.current.contains(e.target as Node)\n ) {\n setIsOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [isOpen]);\n\n return (\n <DropdownWrapper ref={dropdownRef}>\n <Button\n ref={buttonRef}\n className=\"add-layer-button\"\n onClick={handleClick}\n disabled={!options.length || disabled || isAdding}\n >\n <Icons.Add height=\"12px\" />\n {intl.formatMessage({id: 'layerManager.addLayer'})}\n </Button>\n {isOpen && options.length > 1 && (\n <DropdownMenu style={{top: menuPos.top, right: menuPos.right}}>\n <Typeahead\n className={TYPEAHEAD_CLASS}\n customClasses={{\n results: 'list-selector',\n input: TYPEAHEAD_INPUT_CLASS,\n listItem: 'list__item',\n }}\n placeholder={\n intl ? intl.formatMessage({id: 'placeholder.search'}) : 'Search'\n }\n selectedItems={null}\n options={options}\n displayOption={Accessor.generateOptionToStringFor('label')}\n filterOption={'label'}\n searchable\n onOptionSelected={handleSelect}\n customListItemComponent={\n TableListItem as unknown as TypeaheadListItemComponent\n }\n />\n </DropdownMenu>\n )}\n </DropdownWrapper>\n );\n};\n\ntype CustomLayerManagerProps = {\n mapId: string;\n showDeleteDataset?: boolean;\n};\n\nfunction useCustomSidePanelActions(keplerActions: KeplerActions) {\n const {openDeleteModal, toggleModal} = keplerActions.uiStateActions;\n const {updateTableColor} = keplerActions.visStateActions;\n\n const onRemoveDataset = useCallback(\n (dataId: string) => openDeleteModal(dataId),\n [openDeleteModal],\n );\n\n const onShowAddDataModal = useCallback(\n () => toggleModal('addData'),\n [toggleModal],\n );\n\n const onUpdateTableColor = useCallback(\n (dataId: string, newColor: RGBColor) => updateTableColor(dataId, newColor),\n [updateTableColor],\n );\n\n return {\n onRemoveDataset,\n onShowAddDataModal,\n onUpdateTableColor,\n };\n}\n\n// Main Custom Layer Manager Component\nexport const CustomLayerManager: React.FC<CustomLayerManagerProps> = ({\n mapId,\n showDeleteDataset = true,\n}) => {\n const {keplerActions, keplerState} = useKeplerStateActions({mapId});\n const intl = useIntl();\n const addTableToMap = useStoreWithKepler(\n (state) => state.kepler.addTableToMap,\n );\n\n const {onRemoveDataset, onUpdateTableColor} =\n useCustomSidePanelActions(keplerActions);\n\n const {addLayer} = keplerActions.visStateActions;\n\n const keplerDatasetIds = useMemo(\n () => Object.keys(keplerState?.visState.datasets ?? {}),\n [keplerState?.visState.datasets],\n );\n\n const onAddTableLayer = useCallback(\n async (tableName: string) => {\n const keplerDatasets = keplerState?.visState.datasets;\n if (!keplerDatasets?.[tableName]) {\n await addTableToMap(mapId, tableName, {\n autoCreateLayers: true,\n centerMap: true,\n });\n } else {\n addLayer(undefined, tableName);\n }\n },\n [addLayer, addTableToMap, keplerState?.visState.datasets, mapId],\n );\n\n const isSortByDatasetMode =\n keplerState?.uiState.layerPanelListView === PANEL_VIEW_TOGGLES.byDataset;\n\n const enableRasterTileLayer = getApplicationConfig().enableRasterTileLayer;\n const enableWMSLayer = getApplicationConfig().enableWMSLayer;\n\n const filteredLayerClasses = useMemo(() => {\n let filteredClasses = keplerState?.visState.layerClasses || {};\n if (!enableRasterTileLayer && 'rasterTile' in filteredClasses) {\n const {rasterTile: _rasterTile, ...rest} = filteredClasses;\n filteredClasses = rest as LayerClassesType;\n }\n if (!enableWMSLayer && 'wms' in filteredClasses) {\n const {wms: _wms, ...rest} = filteredClasses;\n filteredClasses = rest as LayerClassesType;\n }\n return filteredClasses as LayerClassesType;\n }, [\n enableRasterTileLayer,\n enableWMSLayer,\n keplerState?.visState.layerClasses,\n ]);\n\n if (!keplerState || !keplerActions) {\n return null;\n }\n\n return (\n <CustomLayerManagerContainer>\n <div className=\"layer-manager\">\n <SidePanelSection>\n <PanelTitle\n className=\"layer-manager-title\"\n title={intl.formatMessage({\n id: layerPanelMetadata?.label || 'Layers',\n })}\n >\n <AddTableLayerButton\n onAdd={onAddTableLayer}\n keplerDatasetIds={keplerDatasetIds}\n />\n </PanelTitle>\n </SidePanelSection>\n\n <SidePanelSection>\n {isSortByDatasetMode ? (\n <DatasetLayerGroup\n datasets={keplerState.visState.datasets}\n showDatasetTable={keplerActions.visStateActions.showDatasetTable}\n layers={keplerState.visState.layers}\n updateTableColor={onUpdateTableColor}\n removeDataset={onRemoveDataset}\n layerOrder={keplerState.visState.layerOrder}\n layerClasses={filteredLayerClasses}\n uiStateActions={keplerActions.uiStateActions}\n visStateActions={keplerActions.visStateActions}\n mapStateActions={keplerActions.mapStateActions}\n showDeleteDataset={showDeleteDataset}\n />\n ) : (\n <LayerList\n layers={keplerState.visState.layers}\n datasets={keplerState.visState.datasets}\n layerOrder={keplerState.visState.layerOrder}\n uiStateActions={keplerActions.uiStateActions}\n visStateActions={keplerActions.visStateActions}\n mapStateActions={keplerActions.mapStateActions}\n layerClasses={filteredLayerClasses}\n />\n )}\n </SidePanelSection>\n </div>\n </CustomLayerManagerContainer>\n );\n};\n"]}
1
+ {"version":3,"file":"CustomLayerManager.js","sourceRoot":"","sources":["../../src/components/CustomLayerManager.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useCallback, useMemo, useState} from 'react';\nimport {useIntl} from 'react-intl';\nimport styled from 'styled-components';\n\nimport {\n LayerListFactory,\n DatasetLayerGroupFactory,\n PanelTitleFactory,\n SidePanelSection,\n} from '@kepler.gl/components';\nimport {PANEL_VIEW_TOGGLES, SIDEBAR_PANELS} from '@kepler.gl/constants';\nimport {LayerClassesType} from '@kepler.gl/layers';\nimport {getApplicationConfig} from '@kepler.gl/utils';\nimport {Plus} from 'lucide-react';\nimport {Button} from '@sqlrooms/ui';\n\nimport {getKeplerFactory} from './KeplerInjector';\nimport {\n KeplerActions,\n useKeplerStateActions,\n} from '../hooks/useKeplerStateActions';\nimport {\n buildKeplerTableSourceOptions,\n type KeplerSourceDataset,\n type KeplerTableSourceOption,\n} from '../keplerTableSelection';\nimport {useStoreWithKepler} from '../KeplerSlice';\nimport type {RGBColor} from '@kepler.gl/types';\nimport {KeplerTableSourceSelector} from './KeplerTableSourceSelector';\n\nconst LayerList = getKeplerFactory(LayerListFactory);\nconst DatasetLayerGroup = getKeplerFactory(DatasetLayerGroupFactory);\nconst PanelTitle = getKeplerFactory(PanelTitleFactory);\n\nconst layerPanelMetadata = SIDEBAR_PANELS.find((p) => p.id === 'layer');\n\nconst CustomLayerManagerContainer = styled.div`\n .layer-manager-title {\n }\n\n .add-layer-button {\n background-color: ${(props) => props.theme.sidePanelBg || props.theme.panelBackground};\n color: #2563EB;\n border: 0px;\n height: 28px;\n font-weight: 500;\n font-size: 14px;\n }\n\n .layer__title__type {\n display: none !important;\n }\n\n .layer-panel__header {\n height: 36px;\n }\n\n .layer__title {\n min-width: 0;\n }\n\n .layer__title > div {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n }\n\n .layer__title__editor {\n width: 100%;\n min-width: 0;\n flex: 1;\n }\n}\n`;\n\n/**\n * Dropdown button that lists available datasets and DuckDB tables.\n * When a table is selected, it is synced to Kepler on demand and a new\n * layer is created for it.\n */\nconst AddTableLayerButton: React.FC<{\n onAdd: (tableName: string) => Promise<void>;\n datasets: Record<string, KeplerSourceDataset>;\n disabled?: boolean;\n}> = ({onAdd, datasets, disabled}) => {\n const [isAdding, setIsAdding] = useState(false);\n const intl = useIntl();\n\n const dbTables = useStoreWithKepler((state) => state.db.tables);\n const tableSelection = useStoreWithKepler(\n (state) => state.kepler.tableSelection,\n );\n\n const options = useMemo(() => {\n return buildKeplerTableSourceOptions({\n dbTables,\n datasets,\n tableSelection,\n });\n }, [datasets, dbTables, tableSelection]);\n\n const handleSelect = useCallback(\n (option: KeplerTableSourceOption) => {\n setIsAdding(true);\n onAdd(option.value)\n .catch((e) => console.error('Failed to add layer:', e))\n .finally(() => setIsAdding(false));\n },\n [onAdd],\n );\n\n return (\n <KeplerTableSourceSelector\n disabled={!options.length || disabled || isAdding}\n options={options}\n popoverAlign=\"end\"\n searchPlaceholder={\n intl ? intl.formatMessage({id: 'placeholder.search'}) : 'Search'\n }\n onSelect={handleSelect}\n renderTrigger={({disabled: isDisabled}) => (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"xs\"\n className=\"add-layer-button\"\n disabled={isDisabled}\n >\n <Plus className=\"h-3.5 w-3.5\" />\n {intl.formatMessage({id: 'layerManager.addLayer'})}\n </Button>\n )}\n />\n );\n};\n\ntype CustomLayerManagerProps = {\n mapId: string;\n showDeleteDataset?: boolean;\n};\n\nfunction useCustomSidePanelActions(keplerActions: KeplerActions) {\n const {openDeleteModal, toggleModal} = keplerActions.uiStateActions;\n const {updateTableColor} = keplerActions.visStateActions;\n\n const onRemoveDataset = useCallback(\n (dataId: string) => openDeleteModal(dataId),\n [openDeleteModal],\n );\n\n const onShowAddDataModal = useCallback(\n () => toggleModal('addData'),\n [toggleModal],\n );\n\n const onUpdateTableColor = useCallback(\n (dataId: string, newColor: RGBColor) => updateTableColor(dataId, newColor),\n [updateTableColor],\n );\n\n return {\n onRemoveDataset,\n onShowAddDataModal,\n onUpdateTableColor,\n };\n}\n\n// Main Custom Layer Manager Component\nexport const CustomLayerManager: React.FC<CustomLayerManagerProps> = ({\n mapId,\n showDeleteDataset = true,\n}) => {\n const {keplerActions, keplerState} = useKeplerStateActions({mapId});\n const intl = useIntl();\n const addTableToMap = useStoreWithKepler(\n (state) => state.kepler.addTableToMap,\n );\n\n const {onRemoveDataset, onUpdateTableColor} =\n useCustomSidePanelActions(keplerActions);\n\n const {addLayer} = keplerActions.visStateActions;\n\n const onAddTableLayer = useCallback(\n async (tableName: string) => {\n const keplerDatasets = keplerState?.visState.datasets;\n if (!keplerDatasets?.[tableName]) {\n await addTableToMap({\n mapId,\n tableName,\n options: {\n autoCreateLayers: true,\n centerMap: true,\n },\n });\n } else {\n addLayer(undefined, tableName);\n }\n },\n [addLayer, addTableToMap, keplerState?.visState.datasets, mapId],\n );\n\n const isSortByDatasetMode =\n keplerState?.uiState.layerPanelListView === PANEL_VIEW_TOGGLES.byDataset;\n\n const enableRasterTileLayer = getApplicationConfig().enableRasterTileLayer;\n const enableWMSLayer = getApplicationConfig().enableWMSLayer;\n\n const filteredLayerClasses = useMemo(() => {\n let filteredClasses = keplerState?.visState.layerClasses || {};\n if (!enableRasterTileLayer && 'rasterTile' in filteredClasses) {\n const {rasterTile: _rasterTile, ...rest} = filteredClasses;\n filteredClasses = rest as LayerClassesType;\n }\n if (!enableWMSLayer && 'wms' in filteredClasses) {\n const {wms: _wms, ...rest} = filteredClasses;\n filteredClasses = rest as LayerClassesType;\n }\n return filteredClasses as LayerClassesType;\n }, [\n enableRasterTileLayer,\n enableWMSLayer,\n keplerState?.visState.layerClasses,\n ]);\n\n if (!keplerState || !keplerActions) {\n return null;\n }\n\n return (\n <CustomLayerManagerContainer>\n <div className=\"layer-manager\">\n <SidePanelSection>\n <PanelTitle\n className=\"layer-manager-title\"\n title={intl.formatMessage({\n id: layerPanelMetadata?.label || 'Layers',\n })}\n >\n <AddTableLayerButton\n onAdd={onAddTableLayer}\n datasets={keplerState.visState.datasets}\n />\n </PanelTitle>\n </SidePanelSection>\n\n <SidePanelSection>\n {isSortByDatasetMode ? (\n <DatasetLayerGroup\n datasets={keplerState.visState.datasets}\n showDatasetTable={keplerActions.visStateActions.showDatasetTable}\n layers={keplerState.visState.layers}\n updateTableColor={onUpdateTableColor}\n removeDataset={onRemoveDataset}\n layerOrder={keplerState.visState.layerOrder}\n layerClasses={filteredLayerClasses}\n uiStateActions={keplerActions.uiStateActions}\n visStateActions={keplerActions.visStateActions}\n mapStateActions={keplerActions.mapStateActions}\n showDeleteDataset={showDeleteDataset}\n />\n ) : (\n <LayerList\n layers={keplerState.visState.layers}\n datasets={keplerState.visState.datasets}\n layerOrder={keplerState.visState.layerOrder}\n uiStateActions={keplerActions.uiStateActions}\n visStateActions={keplerActions.visStateActions}\n mapStateActions={keplerActions.mapStateActions}\n layerClasses={filteredLayerClasses}\n />\n )}\n </SidePanelSection>\n </div>\n </CustomLayerManagerContainer>\n );\n};\n"]}
@@ -1,4 +1,4 @@
1
- import { MapControlPanelFactory, MapControlTooltipFactory, MapLegendFactory, MapLegendPanelFactory } from '@kepler.gl/components';
1
+ import { MapControlTooltipFactory, MapControlPanelFactory, MapLegendFactory, MapLegendPanelFactory } from '@kepler.gl/components';
2
2
  export declare function CustomMapLegendPanelFactory(MapControlTooltip: ReturnType<typeof MapControlTooltipFactory>, MapControlPanel: ReturnType<typeof MapControlPanelFactory>, MapLegend: ReturnType<typeof MapLegendFactory>): ReturnType<typeof MapLegendPanelFactory>;
3
3
  export declare namespace CustomMapLegendPanelFactory {
4
4
  var deps: (typeof MapControlTooltipFactory | typeof MapControlPanelFactory | typeof MapLegendFactory)[];
@@ -1 +1 @@
1
- {"version":3,"file":"CustomMapLegendPanel.d.ts","sourceRoot":"","sources":["../../src/components/CustomMapLegendPanel.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,qBAAqB,EAEtB,MAAM,uBAAuB,CAAC;AAI/B,wBAAgB,2BAA2B,CACzC,iBAAiB,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,EAC9D,eAAe,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,EAC1D,SAAS,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAC7C,UAAU,CAAC,OAAO,qBAAqB,CAAC,CA0E1C;yBA9Ee,2BAA2B"}
1
+ {"version":3,"file":"CustomMapLegendPanel.d.ts","sourceRoot":"","sources":["../../src/components/CustomMapLegendPanel.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EAEtB,MAAM,uBAAuB,CAAC;AAI/B,wBAAgB,2BAA2B,CACzC,iBAAiB,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,EAC9D,eAAe,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,EAC1D,SAAS,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,GAC7C,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAsE1C;yBA1Ee,2BAA2B"}
@@ -1,25 +1,33 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useState } from 'react';
2
+ import { useCallback } from 'react';
3
3
  import { Icons, MapControlButton, MapLegendPanelFactory, } from '@kepler.gl/components';
4
4
  CustomMapLegendPanelFactory.deps = MapLegendPanelFactory.deps;
5
5
  export function CustomMapLegendPanelFactory(MapControlTooltip, MapControlPanel, MapLegend) {
6
6
  const OriginalMapLegendPanel = MapLegendPanelFactory(MapControlTooltip, MapControlPanel, MapLegend);
7
7
  const SplitPaneLegend = (props) => {
8
- const [isActive, setIsActive] = useState(false);
9
8
  const mapLegend = props.mapControls?.mapLegend;
9
+ const isActive = mapLegend?.active;
10
+ const { onToggleMapControl } = props;
10
11
  const toggleLegend = useCallback((e) => {
11
12
  e?.preventDefault();
12
- setIsActive((prev) => !prev);
13
- }, []);
13
+ onToggleMapControl('mapLegend');
14
+ }, [onToggleMapControl]);
14
15
  if (!mapLegend?.show)
15
16
  return null;
16
- return isActive ? (_jsxs(_Fragment, { children: [_jsx("style", { children: `.split-legend .map-control__panel-header { display: none !important; }` }), _jsx("div", { className: "split-legend", children: _jsx(MapControlPanel, { scale: props.scale, header: "header.layerLegend", onClick: toggleLegend, pinnable: false, disableClose: false, isExport: props.isExport, logoComponent: props.logoComponent, children: _jsx(MapLegend, { layers: props.layers, mapState: props.mapState, disableEdit: mapLegend.disableEdit, isExport: props.isExport, onLayerVisConfigChange: props.onLayerVisConfigChange, onToggleLayerVisibility: props.onToggleLayerVisibility, ...{ onClose: toggleLegend } }) }) })] })) : (_jsx(MapControlTooltip, { id: "show-legend", message: "tooltip.showLegend", children: _jsx(MapControlButton, { className: "map-control-button show-legend", onClick: toggleLegend, children: _jsx(Icons.Legend, { height: "22px" }) }) }));
17
+ // The secondary (right) pane only shows the toggle button
18
+ if (props.mapIndex !== 0) {
19
+ return (_jsx(MapControlTooltip, { id: "show-legend", message: isActive ? 'tooltip.hideLegend' : 'tooltip.showLegend', children: _jsx(MapControlButton, { className: "map-control-button show-legend", active: isActive, onClick: toggleLegend, children: _jsx(Icons.Legend, { height: "22px" }) }) }));
20
+ }
21
+ // Primary (left) pane: render the full draggable legend panel
22
+ // (OriginalMapLegendPanel handles DraggableLegend + createPortal,
23
+ // and MapControlPanel renders "Unlock Viewport" / "Sync Zoom" in its header)
24
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: `.draggable-legend .map-control__panel-header:not(.map-control__panel-split-viewport-tools) { display: none !important; }` }), _jsx(OriginalMapLegendPanel, { ...props })] }));
17
25
  };
18
26
  const CustomMapLegendPanel = (props) => {
19
27
  if (props.isSplit) {
20
28
  return _jsx(SplitPaneLegend, { ...props });
21
29
  }
22
- return (_jsxs(_Fragment, { children: [_jsx("style", { children: `.draggable-legend .map-control__panel-header { display: none !important; }` }), _jsx(OriginalMapLegendPanel, { ...props })] }));
30
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: `.draggable-legend .map-control__panel-header:not(.map-control__panel-split-viewport-tools) { display: none !important; }` }), _jsx(OriginalMapLegendPanel, { ...props })] }));
23
31
  };
24
32
  CustomMapLegendPanel.displayName = 'CustomMapLegendPanel';
25
33
  return CustomMapLegendPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"CustomMapLegendPanel.js","sourceRoot":"","sources":["../../src/components/CustomMapLegendPanel.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,WAAW,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AAC5C,OAAO,EACL,KAAK,EACL,gBAAgB,EAIhB,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC;AAE/B,2BAA2B,CAAC,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC;AAE9D,MAAM,UAAU,2BAA2B,CACzC,iBAA8D,EAC9D,eAA0D,EAC1D,SAA8C;IAE9C,MAAM,sBAAsB,GAAG,qBAAqB,CAClD,iBAAiB,EACjB,eAAe,EACf,SAAS,CACV,CAAC;IAEF,MAAM,eAAe,GAAG,CACtB,KAAgD,EAChD,EAAE;QACF,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC;QAE/C,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAoB,EAAE,EAAE;YACxD,CAAC,EAAE,cAAc,EAAE,CAAC;YACpB,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,EAAE,EAAE,CAAC,CAAC;QAEP,IAAI,CAAC,SAAS,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC;QAElC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAChB,8BACE,0BAAQ,wEAAwE,GAAS,EACzF,cAAK,SAAS,EAAC,cAAc,YAC3B,KAAC,eAAe,IACd,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,MAAM,EAAC,oBAAoB,EAC3B,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,KAAK,EACf,YAAY,EAAE,KAAK,EACnB,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,aAAa,EAAE,KAAK,CAAC,aAAa,YAElC,KAAC,SAAS,IACR,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,WAAW,EAAE,SAAS,CAAC,WAAW,EAClC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EACxB,sBAAsB,EAAE,KAAK,CAAC,sBAAsB,EACpD,uBAAuB,EAAE,KAAK,CAAC,uBAAuB,KACjD,EAAC,OAAO,EAAE,YAAY,EAAS,GACpC,GACc,GACd,IACL,CACJ,CAAC,CAAC,CAAC,CACF,KAAC,iBAAiB,IAAC,EAAE,EAAC,aAAa,EAAC,OAAO,EAAC,oBAAoB,YAC9D,KAAC,gBAAgB,IACf,SAAS,EAAC,gCAAgC,EAC1C,OAAO,EAAE,YAAY,YAErB,KAAC,KAAK,CAAC,MAAM,IAAC,MAAM,EAAC,MAAM,GAAG,GACb,GACD,CACrB,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,oBAAoB,GAAG,CAC3B,KAAmE,EACnE,EAAE;QACF,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,KAAC,eAAe,OAAK,KAAK,GAAI,CAAC;QACxC,CAAC;QAED,OAAO,CACL,8BACE,0BAAQ,4EAA4E,GAAS,EAC7F,KAAC,sBAAsB,OAAK,KAAK,GAAI,IACpC,CACJ,CAAC;IACJ,CAAC,CAAC;IAEF,oBAAoB,CAAC,WAAW,GAAG,sBAAsB,CAAC;IAC1D,OAAO,oBAAoB,CAAC;AAC9B,CAAC","sourcesContent":["import {useCallback, useState} from 'react';\nimport {\n Icons,\n MapControlButton,\n MapControlPanelFactory,\n MapControlTooltipFactory,\n MapLegendFactory,\n MapLegendPanelFactory,\n MapLegendPanelProps,\n} from '@kepler.gl/components';\n\nCustomMapLegendPanelFactory.deps = MapLegendPanelFactory.deps;\n\nexport function CustomMapLegendPanelFactory(\n MapControlTooltip: ReturnType<typeof MapControlTooltipFactory>,\n MapControlPanel: ReturnType<typeof MapControlPanelFactory>,\n MapLegend: ReturnType<typeof MapLegendFactory>,\n): ReturnType<typeof MapLegendPanelFactory> {\n const OriginalMapLegendPanel = MapLegendPanelFactory(\n MapControlTooltip,\n MapControlPanel,\n MapLegend,\n );\n\n const SplitPaneLegend = (\n props: MapLegendPanelProps & {mapIndex?: number},\n ) => {\n const [isActive, setIsActive] = useState(false);\n const mapLegend = props.mapControls?.mapLegend;\n\n const toggleLegend = useCallback((e?: React.MouseEvent) => {\n e?.preventDefault();\n setIsActive((prev) => !prev);\n }, []);\n\n if (!mapLegend?.show) return null;\n\n return isActive ? (\n <>\n <style>{`.split-legend .map-control__panel-header { display: none !important; }`}</style>\n <div className=\"split-legend\">\n <MapControlPanel\n scale={props.scale}\n header=\"header.layerLegend\"\n onClick={toggleLegend}\n pinnable={false}\n disableClose={false}\n isExport={props.isExport}\n logoComponent={props.logoComponent}\n >\n <MapLegend\n layers={props.layers}\n mapState={props.mapState}\n disableEdit={mapLegend.disableEdit}\n isExport={props.isExport}\n onLayerVisConfigChange={props.onLayerVisConfigChange}\n onToggleLayerVisibility={props.onToggleLayerVisibility}\n {...({onClose: toggleLegend} as any)}\n />\n </MapControlPanel>\n </div>\n </>\n ) : (\n <MapControlTooltip id=\"show-legend\" message=\"tooltip.showLegend\">\n <MapControlButton\n className=\"map-control-button show-legend\"\n onClick={toggleLegend}\n >\n <Icons.Legend height=\"22px\" />\n </MapControlButton>\n </MapControlTooltip>\n );\n };\n\n const CustomMapLegendPanel = (\n props: MapLegendPanelProps & {mapIndex?: number; isSplit?: boolean},\n ) => {\n if (props.isSplit) {\n return <SplitPaneLegend {...props} />;\n }\n\n return (\n <>\n <style>{`.draggable-legend .map-control__panel-header { display: none !important; }`}</style>\n <OriginalMapLegendPanel {...props} />\n </>\n );\n };\n\n CustomMapLegendPanel.displayName = 'CustomMapLegendPanel';\n return CustomMapLegendPanel;\n}\n"]}
1
+ {"version":3,"file":"CustomMapLegendPanel.js","sourceRoot":"","sources":["../../src/components/CustomMapLegendPanel.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAC,WAAW,EAAC,MAAM,OAAO,CAAC;AACzC,OAAO,EACL,KAAK,EACL,gBAAgB,EAIhB,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC;AAE/B,2BAA2B,CAAC,IAAI,GAAG,qBAAqB,CAAC,IAAI,CAAC;AAE9D,MAAM,UAAU,2BAA2B,CACzC,iBAA8D,EAC9D,eAA0D,EAC1D,SAA8C;IAE9C,MAAM,sBAAsB,GAAG,qBAAqB,CAClD,iBAAiB,EACjB,eAAe,EACf,SAAS,CACV,CAAC;IAEF,MAAM,eAAe,GAAG,CACtB,KAAmE,EACnE,EAAE;QACF,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC;QAC/C,MAAM,QAAQ,GAAG,SAAS,EAAE,MAAM,CAAC;QACnC,MAAM,EAAC,kBAAkB,EAAC,GAAG,KAAK,CAAC;QAEnC,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,CAAoB,EAAE,EAAE;YACvB,CAAC,EAAE,cAAc,EAAE,CAAC;YACpB,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAClC,CAAC,EACD,CAAC,kBAAkB,CAAC,CACrB,CAAC;QAEF,IAAI,CAAC,SAAS,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC;QAElC,0DAA0D;QAC1D,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CACL,KAAC,iBAAiB,IAChB,EAAE,EAAC,aAAa,EAChB,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oBAAoB,YAE/D,KAAC,gBAAgB,IACf,SAAS,EAAC,gCAAgC,EAC1C,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAE,YAAY,YAErB,KAAC,KAAK,CAAC,MAAM,IAAC,MAAM,EAAC,MAAM,GAAG,GACb,GACD,CACrB,CAAC;QACJ,CAAC;QAED,8DAA8D;QAC9D,kEAAkE;QAClE,6EAA6E;QAC7E,OAAO,CACL,8BACE,0BAAQ,0HAA0H,GAAS,EAC3I,KAAC,sBAAsB,OAAK,KAAK,GAAI,IACpC,CACJ,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,oBAAoB,GAAG,CAC3B,KAAmE,EACnE,EAAE;QACF,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,KAAC,eAAe,OAAK,KAAK,GAAI,CAAC;QACxC,CAAC;QAED,OAAO,CACL,8BACE,0BAAQ,0HAA0H,GAAS,EAC3I,KAAC,sBAAsB,OAAK,KAAK,GAAI,IACpC,CACJ,CAAC;IACJ,CAAC,CAAC;IAEF,oBAAoB,CAAC,WAAW,GAAG,sBAAsB,CAAC;IAC1D,OAAO,oBAAoB,CAAC;AAC9B,CAAC","sourcesContent":["import React, {useCallback} from 'react';\nimport {\n Icons,\n MapControlButton,\n MapControlTooltipFactory,\n MapControlPanelFactory,\n MapLegendFactory,\n MapLegendPanelFactory,\n MapLegendPanelProps,\n} from '@kepler.gl/components';\n\nCustomMapLegendPanelFactory.deps = MapLegendPanelFactory.deps;\n\nexport function CustomMapLegendPanelFactory(\n MapControlTooltip: ReturnType<typeof MapControlTooltipFactory>,\n MapControlPanel: ReturnType<typeof MapControlPanelFactory>,\n MapLegend: ReturnType<typeof MapLegendFactory>,\n): ReturnType<typeof MapLegendPanelFactory> {\n const OriginalMapLegendPanel = MapLegendPanelFactory(\n MapControlTooltip,\n MapControlPanel,\n MapLegend,\n );\n\n const SplitPaneLegend = (\n props: MapLegendPanelProps & {mapIndex?: number; isSplit?: boolean},\n ) => {\n const mapLegend = props.mapControls?.mapLegend;\n const isActive = mapLegend?.active;\n const {onToggleMapControl} = props;\n\n const toggleLegend = useCallback(\n (e?: React.MouseEvent) => {\n e?.preventDefault();\n onToggleMapControl('mapLegend');\n },\n [onToggleMapControl],\n );\n\n if (!mapLegend?.show) return null;\n\n // The secondary (right) pane only shows the toggle button\n if (props.mapIndex !== 0) {\n return (\n <MapControlTooltip\n id=\"show-legend\"\n message={isActive ? 'tooltip.hideLegend' : 'tooltip.showLegend'}\n >\n <MapControlButton\n className=\"map-control-button show-legend\"\n active={isActive}\n onClick={toggleLegend}\n >\n <Icons.Legend height=\"22px\" />\n </MapControlButton>\n </MapControlTooltip>\n );\n }\n\n // Primary (left) pane: render the full draggable legend panel\n // (OriginalMapLegendPanel handles DraggableLegend + createPortal,\n // and MapControlPanel renders \"Unlock Viewport\" / \"Sync Zoom\" in its header)\n return (\n <>\n <style>{`.draggable-legend .map-control__panel-header:not(.map-control__panel-split-viewport-tools) { display: none !important; }`}</style>\n <OriginalMapLegendPanel {...props} />\n </>\n );\n };\n\n const CustomMapLegendPanel = (\n props: MapLegendPanelProps & {mapIndex?: number; isSplit?: boolean},\n ) => {\n if (props.isSplit) {\n return <SplitPaneLegend {...props} />;\n }\n\n return (\n <>\n <style>{`.draggable-legend .map-control__panel-header:not(.map-control__panel-split-viewport-tools) { display: none !important; }`}</style>\n <OriginalMapLegendPanel {...props} />\n </>\n );\n };\n\n CustomMapLegendPanel.displayName = 'CustomMapLegendPanel';\n return CustomMapLegendPanel;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { SourceDataSelectorProps } from '@kepler.gl/components';
2
+ import React from 'react';
3
+ type SourceDataSelectorContent = React.ComponentType<SourceDataSelectorProps>;
4
+ type CustomSourceDataSelectorProps = SourceDataSelectorProps & {
5
+ id?: string;
6
+ };
7
+ /**
8
+ * Kepler uses the same SourceDataSelector in layer config and filter config.
9
+ * Layer config passes a layer id, so only that version includes unloaded
10
+ * SQLRooms table options. Filter selectors use the same UI with loaded Kepler
11
+ * datasets only because a filter needs a loaded dataset to expose fields.
12
+ */
13
+ export declare function CustomSourceDataSelectorFactory(_DataSourceSelectorContent: SourceDataSelectorContent): React.FC<CustomSourceDataSelectorProps>;
14
+ export declare namespace CustomSourceDataSelectorFactory {
15
+ var deps: typeof import("@kepler.gl/components/dist/side-panel/common/source-data-selector-content").default[];
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=CustomSourceDataSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomSourceDataSelector.d.ts","sourceRoot":"","sources":["../../src/components/CustomSourceDataSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,KAAuC,MAAM,OAAO,CAAC;AAW5D,KAAK,yBAAyB,GAAG,KAAK,CAAC,aAAa,CAAC,uBAAuB,CAAC,CAAC;AAC9E,KAAK,6BAA6B,GAAG,uBAAuB,GAAG;IAC7D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAyCF;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,0BAA0B,EAAE,yBAAyB,GACpD,KAAK,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAoJzC;yBAtJe,+BAA+B"}
@@ -0,0 +1,130 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PanelLabel, SidePanelSection, SourceDataSelectorFactory, } from '@kepler.gl/components';
3
+ import { FormattedMessage } from '@kepler.gl/localization';
4
+ import { Button, cn } from '@sqlrooms/ui';
5
+ import { useCallback, useMemo, useState } from 'react';
6
+ import { useStoreWithKepler } from '../KeplerSlice';
7
+ import { buildKeplerTableSourceOptions, } from '../keplerTableSelection';
8
+ import { KeplerTableSourceSelector } from './KeplerTableSourceSelector';
9
+ function getSelectedDatasetId(value) {
10
+ if (value === null) {
11
+ return undefined;
12
+ }
13
+ if (Array.isArray(value)) {
14
+ if (value.length === 0) {
15
+ return undefined;
16
+ }
17
+ return getSelectedDatasetId(value[0]);
18
+ }
19
+ if (typeof value === 'string') {
20
+ return value;
21
+ }
22
+ if (typeof value === 'number') {
23
+ return String(value);
24
+ }
25
+ if (typeof value === 'boolean') {
26
+ return String(value);
27
+ }
28
+ if (typeof value === 'object') {
29
+ const option = value;
30
+ if (typeof option.value === 'string') {
31
+ return option.value;
32
+ }
33
+ if (typeof option.id === 'string') {
34
+ return option.id;
35
+ }
36
+ }
37
+ return undefined;
38
+ }
39
+ /**
40
+ * Kepler uses the same SourceDataSelector in layer config and filter config.
41
+ * Layer config passes a layer id, so only that version includes unloaded
42
+ * SQLRooms table options. Filter selectors use the same UI with loaded Kepler
43
+ * datasets only because a filter needs a loaded dataset to expose fields.
44
+ */
45
+ export function CustomSourceDataSelectorFactory(_DataSourceSelectorContent) {
46
+ const SourceDataSelector = ({ dataId, datasets, disabled, onSelect, defaultValue = 'Select A Dataset', className, id: layerId, }) => {
47
+ const [isLoadingTable, setIsLoadingTable] = useState(false);
48
+ const dbTables = useStoreWithKepler((state) => state.db.tables);
49
+ const tableSelection = useStoreWithKepler((state) => state.kepler.tableSelection);
50
+ const addTableToMap = useStoreWithKepler((state) => state.kepler.addTableToMap);
51
+ const keplerMap = useStoreWithKepler((state) => state.kepler.map);
52
+ const mapId = useMemo(() => {
53
+ if (!layerId) {
54
+ return undefined;
55
+ }
56
+ for (const [candidateMapId, mapState] of Object.entries(keplerMap)) {
57
+ const hasLayer = mapState?.visState?.layers?.some((layer) => layer.id === layerId);
58
+ if (hasLayer) {
59
+ return candidateMapId;
60
+ }
61
+ }
62
+ return undefined;
63
+ }, [keplerMap, layerId]);
64
+ const mapDatasets = useMemo(() => {
65
+ if (!mapId) {
66
+ return undefined;
67
+ }
68
+ return keplerMap[mapId]?.visState.datasets;
69
+ }, [keplerMap, mapId]);
70
+ const loadedDatasetIds = useMemo(() => Object.keys(mapDatasets ?? datasets), [datasets, mapDatasets]);
71
+ const options = useMemo(() => {
72
+ return buildKeplerTableSourceOptions({
73
+ dbTables,
74
+ datasets,
75
+ includeUnloadedTables: Boolean(layerId && mapId),
76
+ loadedDatasetIds,
77
+ tableSelection,
78
+ });
79
+ }, [datasets, dbTables, layerId, loadedDatasetIds, mapId, tableSelection]);
80
+ const handleSelect = useCallback((option) => {
81
+ if (!layerId || !mapId) {
82
+ onSelect(option.value);
83
+ return;
84
+ }
85
+ if (option.isLoaded) {
86
+ onSelect(option.value);
87
+ return;
88
+ }
89
+ setIsLoadingTable(true);
90
+ void addTableToMap({
91
+ mapId,
92
+ tableName: option.value,
93
+ options: {
94
+ autoCreateLayers: false,
95
+ centerMap: false,
96
+ },
97
+ })
98
+ .then(() => {
99
+ onSelect(option.value);
100
+ })
101
+ .catch((e) => {
102
+ console.error('Failed to load layer data source:', e);
103
+ })
104
+ .finally(() => {
105
+ setIsLoadingTable(false);
106
+ });
107
+ }, [addTableToMap, layerId, mapId, onSelect]);
108
+ const selectedValue = getSelectedDatasetId(dataId);
109
+ const selectedDataset = selectedValue ? datasets[selectedValue] : undefined;
110
+ const selectedOption = options.find((option) => option.value === selectedValue);
111
+ const selectedLabel = selectedOption?.label ??
112
+ selectedDataset?.label ??
113
+ selectedValue ??
114
+ defaultValue;
115
+ const selectedColor = selectedOption?.color ??
116
+ selectedDataset?.color ?? [143, 149, 161];
117
+ const isDisabled = Boolean(disabled || isLoadingTable || !options.length);
118
+ return (_jsxs(SidePanelSection, { className: "data-source-selector", children: [_jsx(PanelLabel, { children: _jsx(FormattedMessage, { id: "misc.dataSource" }) }), _jsx(KeplerTableSourceSelector, { disabled: isDisabled, options: options, popoverClassName: "w-[360px]", searchPlaceholder: "Search", selectedValue: selectedValue, onSelect: handleSelect, renderTrigger: ({ disabled: triggerDisabled, selectedOption }) => {
119
+ const triggerColor = selectedOption?.color ?? selectedColor;
120
+ const triggerLabel = selectedOption?.label ?? selectedLabel;
121
+ return (_jsxs(Button, { type: "button", variant: "secondary", className: cn(className, 'h-9 w-full justify-start gap-2.5 rounded-sm px-3 text-left text-xs font-semibold'), disabled: triggerDisabled, children: [_jsx("span", { className: "h-2.5 w-2.5 shrink-0", style: {
122
+ backgroundColor: `rgb(${triggerColor.join(',')})`,
123
+ } }), _jsx("span", { className: "min-w-0 truncate", children: triggerLabel })] }));
124
+ } })] }));
125
+ };
126
+ SourceDataSelector.displayName = 'CustomSourceDataSelector';
127
+ return SourceDataSelector;
128
+ }
129
+ CustomSourceDataSelectorFactory.deps = SourceDataSelectorFactory.deps;
130
+ //# sourceMappingURL=CustomSourceDataSelector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomSourceDataSelector.js","sourceRoot":"","sources":["../../src/components/CustomSourceDataSelector.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n PanelLabel,\n SidePanelSection,\n SourceDataSelectorFactory,\n SourceDataSelectorProps,\n} from '@kepler.gl/components';\nimport {FormattedMessage} from '@kepler.gl/localization';\nimport {Button, cn} from '@sqlrooms/ui';\nimport React, {useCallback, useMemo, useState} from 'react';\nimport {useStoreWithKepler} from '../KeplerSlice';\nimport {\n buildKeplerTableSourceOptions,\n type KeplerTableSourceOption,\n} from '../keplerTableSelection';\nimport {KeplerTableSourceSelector} from './KeplerTableSourceSelector';\n\ntype SourceDataSelectorValue = Parameters<\n SourceDataSelectorProps['onSelect']\n>[0];\ntype SourceDataSelectorContent = React.ComponentType<SourceDataSelectorProps>;\ntype CustomSourceDataSelectorProps = SourceDataSelectorProps & {\n id?: string;\n};\n\nfunction getSelectedDatasetId(\n value: SourceDataSelectorValue,\n): string | undefined {\n if (value === null) {\n return undefined;\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return undefined;\n }\n return getSelectedDatasetId(value[0] as SourceDataSelectorValue);\n }\n\n if (typeof value === 'string') {\n return value;\n }\n\n if (typeof value === 'number') {\n return String(value);\n }\n\n if (typeof value === 'boolean') {\n return String(value);\n }\n\n if (typeof value === 'object') {\n const option = value as {id?: unknown; value?: unknown};\n if (typeof option.value === 'string') {\n return option.value;\n }\n if (typeof option.id === 'string') {\n return option.id;\n }\n }\n\n return undefined;\n}\n\n/**\n * Kepler uses the same SourceDataSelector in layer config and filter config.\n * Layer config passes a layer id, so only that version includes unloaded\n * SQLRooms table options. Filter selectors use the same UI with loaded Kepler\n * datasets only because a filter needs a loaded dataset to expose fields.\n */\nexport function CustomSourceDataSelectorFactory(\n _DataSourceSelectorContent: SourceDataSelectorContent,\n): React.FC<CustomSourceDataSelectorProps> {\n const SourceDataSelector: React.FC<CustomSourceDataSelectorProps> = ({\n dataId,\n datasets,\n disabled,\n onSelect,\n defaultValue = 'Select A Dataset',\n className,\n id: layerId,\n }) => {\n const [isLoadingTable, setIsLoadingTable] = useState(false);\n const dbTables = useStoreWithKepler((state) => state.db.tables);\n const tableSelection = useStoreWithKepler(\n (state) => state.kepler.tableSelection,\n );\n const addTableToMap = useStoreWithKepler(\n (state) => state.kepler.addTableToMap,\n );\n const keplerMap = useStoreWithKepler((state) => state.kepler.map);\n const mapId = useMemo(() => {\n if (!layerId) {\n return undefined;\n }\n\n for (const [candidateMapId, mapState] of Object.entries(keplerMap)) {\n const hasLayer = mapState?.visState?.layers?.some(\n (layer: {id?: string}) => layer.id === layerId,\n );\n if (hasLayer) {\n return candidateMapId;\n }\n }\n\n return undefined;\n }, [keplerMap, layerId]);\n const mapDatasets = useMemo(() => {\n if (!mapId) {\n return undefined;\n }\n\n return keplerMap[mapId]?.visState.datasets;\n }, [keplerMap, mapId]);\n const loadedDatasetIds = useMemo(\n () => Object.keys(mapDatasets ?? datasets),\n [datasets, mapDatasets],\n );\n\n const options = useMemo(() => {\n return buildKeplerTableSourceOptions({\n dbTables,\n datasets,\n includeUnloadedTables: Boolean(layerId && mapId),\n loadedDatasetIds,\n tableSelection,\n });\n }, [datasets, dbTables, layerId, loadedDatasetIds, mapId, tableSelection]);\n\n const handleSelect = useCallback(\n (option: KeplerTableSourceOption) => {\n if (!layerId || !mapId) {\n onSelect(option.value);\n return;\n }\n\n if (option.isLoaded) {\n onSelect(option.value);\n return;\n }\n\n setIsLoadingTable(true);\n void addTableToMap({\n mapId,\n tableName: option.value,\n options: {\n autoCreateLayers: false,\n centerMap: false,\n },\n })\n .then(() => {\n onSelect(option.value);\n })\n .catch((e) => {\n console.error('Failed to load layer data source:', e);\n })\n .finally(() => {\n setIsLoadingTable(false);\n });\n },\n [addTableToMap, layerId, mapId, onSelect],\n );\n\n const selectedValue = getSelectedDatasetId(dataId);\n const selectedDataset = selectedValue ? datasets[selectedValue] : undefined;\n const selectedOption = options.find(\n (option) => option.value === selectedValue,\n );\n const selectedLabel =\n selectedOption?.label ??\n selectedDataset?.label ??\n selectedValue ??\n defaultValue;\n const selectedColor = selectedOption?.color ??\n selectedDataset?.color ?? [143, 149, 161];\n const isDisabled = Boolean(disabled || isLoadingTable || !options.length);\n\n return (\n <SidePanelSection className=\"data-source-selector\">\n <PanelLabel>\n <FormattedMessage id=\"misc.dataSource\" />\n </PanelLabel>\n <KeplerTableSourceSelector\n disabled={isDisabled}\n options={options}\n popoverClassName=\"w-[360px]\"\n searchPlaceholder=\"Search\"\n selectedValue={selectedValue}\n onSelect={handleSelect}\n renderTrigger={({disabled: triggerDisabled, selectedOption}) => {\n const triggerColor = selectedOption?.color ?? selectedColor;\n const triggerLabel = selectedOption?.label ?? selectedLabel;\n\n return (\n <Button\n type=\"button\"\n variant=\"secondary\"\n className={cn(\n className,\n 'h-9 w-full justify-start gap-2.5 rounded-sm px-3 text-left text-xs font-semibold',\n )}\n disabled={triggerDisabled}\n >\n <span\n className=\"h-2.5 w-2.5 shrink-0\"\n style={{\n backgroundColor: `rgb(${triggerColor.join(',')})`,\n }}\n />\n <span className=\"min-w-0 truncate\">{triggerLabel}</span>\n </Button>\n );\n }}\n />\n </SidePanelSection>\n );\n };\n\n SourceDataSelector.displayName = 'CustomSourceDataSelector';\n return SourceDataSelector;\n}\n\nCustomSourceDataSelectorFactory.deps = SourceDataSelectorFactory.deps;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"KeplerInjector.d.ts","sourceRoot":"","sources":["../../src/components/KeplerInjector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAcL,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAA0B,MAAM,OAAO,CAAC;AAQ/C,eAAO,MAAM,0BAA0B,kBAEtC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;EAMnC,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,OAAO,GAAG,OAAO,IAAI,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AACjE,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAwG3D,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,mBAAmB,EAAE,EAC9B,OAAO,GAAE;IAAC,IAAI,CAAC,EAAE,uBAAuB,CAAA;CAAM,QAM/C;AAED,wBAAgB,0BAA0B,SAGzC;AAED,wBAAgB,iBAAiB,iBAEhC;AAeD,wBAAgB,gBAAgB,CAAC,QAAQ,SAAS,aAAa,EAC7D,OAAO,EAAE,QAAQ,GAChB,UAAU,CAAC,QAAQ,CAAC,CA+BtB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc;QACrB,QAAQ,SAAS,aAAa,WAAW,QAAQ;CAGtD,CAAC"}
1
+ {"version":3,"file":"KeplerInjector.d.ts","sourceRoot":"","sources":["../../src/components/KeplerInjector.tsx"],"names":[],"mappings":"AAAA,OAAO,EAeL,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAA0B,MAAM,OAAO,CAAC;AAS/C,eAAO,MAAM,0BAA0B,kBAEtC,CAAC;AAEF,eAAO,MAAM,uBAAuB;;EAMnC,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,OAAO,GAAG,OAAO,IAAI,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AACjE,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAyG3D,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,mBAAmB,EAAE,EAC9B,OAAO,GAAE;IAAC,IAAI,CAAC,EAAE,uBAAuB,CAAA;CAAM,QAM/C;AAED,wBAAgB,0BAA0B,SAGzC;AAED,wBAAgB,iBAAiB,iBAEhC;AAeD,wBAAgB,gBAAgB,CAAC,QAAQ,SAAS,aAAa,EAC7D,OAAO,EAAE,QAAQ,GAChB,UAAU,CAAC,QAAQ,CAAC,CA+BtB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc;QACrB,QAAQ,SAAS,aAAa,WAAW,QAAQ;CAGtD,CAAC"}
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { AddDataButtonFactory, ContainerFactory, injector as createInjector, DndContextFactory, FilterPanelHeaderFactory, flattenDeps, MapControlTooltipFactory, MapDrawPanelFactory, MapLegendFactory, MapLegendPanelFactory, PanelTitleFactory, typeCheckRecipe, } from '@kepler.gl/components';
2
+ import { AddDataButtonFactory, ContainerFactory, injector as createInjector, DndContextFactory, FilterPanelHeaderFactory, flattenDeps, MapControlTooltipFactory, MapDrawPanelFactory, MapLegendFactory, MapLegendPanelFactory, PanelTitleFactory, SourceDataSelectorFactory, typeCheckRecipe, } from '@kepler.gl/components';
3
3
  import { CustomDndContextFactory } from './CustomDndContext';
4
4
  import { CustomFilterPanelHeaderFactory } from './CustomFilterPanelHeader';
5
5
  import { CustomMapControlTooltipFactory } from './CustomMapControlTooltipFactory';
6
6
  import { CustomMapDrawPanelFactory } from './CustomMapDrawPanel';
7
7
  import { CustomMapLegendFactory } from './CustomMapLegend';
8
8
  import { CustomMapLegendPanelFactory } from './CustomMapLegendPanel';
9
+ import { CustomSourceDataSelectorFactory } from './CustomSourceDataSelector';
9
10
  export const CustomAddDataButtonFactory = () => {
10
11
  return () => null;
11
12
  };
@@ -22,6 +23,7 @@ const defaultRecipes = [
22
23
  [MapLegendFactory, CustomMapLegendFactory],
23
24
  [MapControlTooltipFactory, CustomMapControlTooltipFactory],
24
25
  [MapDrawPanelFactory, CustomMapDrawPanelFactory],
26
+ [SourceDataSelectorFactory, CustomSourceDataSelectorFactory],
25
27
  ];
26
28
  let customRecipes = [];
27
29
  let injector = createKeplerInjector();