@sqlrooms/kepler 0.29.0-rc.6 → 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.
- package/LICENSE.md +2 -1
- package/README.md +121 -4
- package/dist/KeplerSlice.d.ts +58 -2
- package/dist/KeplerSlice.d.ts.map +1 -1
- package/dist/KeplerSlice.js +152 -24
- package/dist/KeplerSlice.js.map +1 -1
- package/dist/components/CustomFilterManager.d.ts.map +1 -1
- package/dist/components/CustomFilterManager.js +16 -4
- package/dist/components/CustomFilterManager.js.map +1 -1
- package/dist/components/CustomLayerManager.d.ts.map +1 -1
- package/dist/components/CustomLayerManager.js +23 -110
- package/dist/components/CustomLayerManager.js.map +1 -1
- package/dist/components/CustomMapLegendPanel.d.ts +1 -1
- package/dist/components/CustomMapLegendPanel.d.ts.map +1 -1
- package/dist/components/CustomMapLegendPanel.js +14 -6
- package/dist/components/CustomMapLegendPanel.js.map +1 -1
- package/dist/components/CustomSourceDataSelector.d.ts +18 -0
- package/dist/components/CustomSourceDataSelector.d.ts.map +1 -0
- package/dist/components/CustomSourceDataSelector.js +130 -0
- package/dist/components/CustomSourceDataSelector.js.map +1 -0
- package/dist/components/KeplerInjector.d.ts.map +1 -1
- package/dist/components/KeplerInjector.js +3 -1
- package/dist/components/KeplerInjector.js.map +1 -1
- package/dist/components/KeplerMapContainer.d.ts.map +1 -1
- package/dist/components/KeplerMapContainer.js +9 -2
- package/dist/components/KeplerMapContainer.js.map +1 -1
- package/dist/components/KeplerProvider.d.ts.map +1 -1
- package/dist/components/KeplerProvider.js +3 -1
- package/dist/components/KeplerProvider.js.map +1 -1
- package/dist/components/KeplerTableSourceSelector.d.ts +20 -0
- package/dist/components/KeplerTableSourceSelector.d.ts.map +1 -0
- package/dist/components/KeplerTableSourceSelector.js +21 -0
- package/dist/components/KeplerTableSourceSelector.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/keplerTableSelection.d.ts +142 -0
- package/dist/keplerTableSelection.d.ts.map +1 -0
- package/dist/keplerTableSelection.js +231 -0
- package/dist/keplerTableSelection.js.map +1 -0
- package/dist/styles/theme.d.ts +12 -0
- package/dist/styles/theme.d.ts.map +1 -1
- package/dist/styles/theme.js +12 -0
- package/dist/styles/theme.js.map +1 -1
- package/package.json +13 -7
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright
|
|
3
|
+
Copyright 2024-2026 SQLRooms Contributors
|
|
4
|
+
Copyright Vis.gl contributors
|
|
4
5
|
|
|
5
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
7
|
|
package/README.md
CHANGED
|
@@ -63,14 +63,18 @@ function MapPanel() {
|
|
|
63
63
|
const mapId = useRoomStore((state) => state.kepler.config.maps[0]?.id);
|
|
64
64
|
const addTableToMap = useRoomStore((state) => state.kepler.addTableToMap);
|
|
65
65
|
const isTableReady = useRoomStore((state) =>
|
|
66
|
-
Boolean(state.db.
|
|
66
|
+
Boolean(state.db.findTable('earthquakes')),
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
useEffect(() => {
|
|
70
70
|
if (!isTableReady || !mapId) return;
|
|
71
|
-
void addTableToMap(
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
void addTableToMap({
|
|
72
|
+
mapId,
|
|
73
|
+
tableName: 'earthquakes',
|
|
74
|
+
options: {
|
|
75
|
+
autoCreateLayers: true,
|
|
76
|
+
centerMap: true,
|
|
77
|
+
},
|
|
74
78
|
});
|
|
75
79
|
}, [isTableReady, mapId, addTableToMap]);
|
|
76
80
|
|
|
@@ -88,19 +92,132 @@ export function App() {
|
|
|
88
92
|
}
|
|
89
93
|
```
|
|
90
94
|
|
|
95
|
+
## Adding tables to maps
|
|
96
|
+
|
|
97
|
+
Use `state.kepler.addTableToMap()` to load a DuckDB table into a Kepler map. The
|
|
98
|
+
preferred API is an object parameter:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
await state.kepler.addTableToMap({
|
|
102
|
+
mapId,
|
|
103
|
+
tableName: 'earthquakes',
|
|
104
|
+
options: {
|
|
105
|
+
autoCreateLayers: true,
|
|
106
|
+
centerMap: true,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`tableName` is the SQL table reference to load. It can also be a saved Kepler
|
|
112
|
+
dataset id when the configured table-selection policy can resolve that id back
|
|
113
|
+
to a DuckDB table.
|
|
114
|
+
|
|
115
|
+
For normal add-table flows, omit `datasetId`. SQLRooms derives the persisted
|
|
116
|
+
Kepler dataset id from the table-selection policy:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
await state.kepler.addTableToMap({
|
|
120
|
+
mapId,
|
|
121
|
+
tableName: 'main.places',
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Only pass `datasetId` when you need to load a table under an existing Kepler
|
|
126
|
+
`dataId`, such as when restoring a saved map config:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
await state.kepler.addTableToMap({
|
|
130
|
+
mapId,
|
|
131
|
+
tableName: savedDataId,
|
|
132
|
+
options: {
|
|
133
|
+
autoCreateLayers: false,
|
|
134
|
+
centerMap: false,
|
|
135
|
+
},
|
|
136
|
+
datasetId: savedDataId,
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The older positional signature is still accepted for compatibility, but new code
|
|
141
|
+
should use the object form so the table reference, Kepler options, config, and
|
|
142
|
+
dataset-id override remain clear at the call site.
|
|
143
|
+
|
|
91
144
|
## Common customization
|
|
92
145
|
|
|
93
146
|
Pass options to `createKeplerSlice()`:
|
|
94
147
|
|
|
95
148
|
```ts
|
|
149
|
+
import {createKeplerTheme, type KeplerThemeOverrides} from '@sqlrooms/kepler';
|
|
150
|
+
|
|
96
151
|
createKeplerSlice({
|
|
97
152
|
basicKeplerProps: {
|
|
98
153
|
mapboxApiAccessToken: import.meta.env.VITE_MAPBOX_TOKEN,
|
|
99
154
|
},
|
|
155
|
+
keplerTheme: createKeplerTheme({
|
|
156
|
+
modalOverLayZ: 40,
|
|
157
|
+
} satisfies KeplerThemeOverrides),
|
|
158
|
+
modalPortalTarget: 'body',
|
|
100
159
|
actionLogging: false,
|
|
101
160
|
});
|
|
102
161
|
```
|
|
103
162
|
|
|
163
|
+
Notes:
|
|
164
|
+
|
|
165
|
+
- `basicKeplerProps` is for base Kepler registration/component props.
|
|
166
|
+
- `keplerTheme` (optional) sets the theme passed to Kepler `ThemeProvider`.
|
|
167
|
+
- `modalPortalTarget` controls modal portal placement: `'container'` (default)
|
|
168
|
+
or `'body'`.
|
|
169
|
+
|
|
170
|
+
### Table selection and dataset ids
|
|
171
|
+
|
|
172
|
+
`tableSelection` controls which DuckDB tables Kepler exposes and how those
|
|
173
|
+
tables are represented in persisted Kepler layer and filter config.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
createKeplerSlice({
|
|
177
|
+
tableSelection: {
|
|
178
|
+
defaultDbSchema: {
|
|
179
|
+
database: 'project',
|
|
180
|
+
schema: 'main',
|
|
181
|
+
},
|
|
182
|
+
includeTable: (table) => table.table.database === 'project',
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
By default, tables in `defaultDbSchema` use bare dataset ids such as `places`.
|
|
188
|
+
Tables outside that database/schema use qualified SQL table references. This
|
|
189
|
+
keeps common main-schema project tables readable while preserving enough
|
|
190
|
+
identity for tables from other schemas.
|
|
191
|
+
|
|
192
|
+
Use `includeTable` to hide tables from Kepler's Add Layer UI and to skip matching
|
|
193
|
+
saved dataset ids during dataset synchronization. Host apps commonly use this to
|
|
194
|
+
hide attached databases that will not be available when a project is reopened.
|
|
195
|
+
|
|
196
|
+
If the default dataset-id policy is not right for your app, provide both
|
|
197
|
+
`getDatasetIdForTable` and `findTableForDatasetId` so new layers and restored
|
|
198
|
+
layers agree on the same identity scheme:
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
createKeplerSlice({
|
|
202
|
+
tableSelection: {
|
|
203
|
+
getDatasetIdForTable: (table) =>
|
|
204
|
+
`${table.table.schema}:${table.table.table}`,
|
|
205
|
+
findTableForDatasetId: (tables, datasetId) => {
|
|
206
|
+
const [schema, tableName] = datasetId.split(':');
|
|
207
|
+
return tables.find(
|
|
208
|
+
(table) =>
|
|
209
|
+
table.table.schema === schema && table.table.table === tableName,
|
|
210
|
+
);
|
|
211
|
+
},
|
|
212
|
+
getTableLabel: (table) =>
|
|
213
|
+
[table.table.schema, table.table.table].filter(Boolean).join('.'),
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`getTableLabel` only affects display labels in Kepler table selectors. It does
|
|
219
|
+
not change persisted dataset ids.
|
|
220
|
+
|
|
104
221
|
## Related packages
|
|
105
222
|
|
|
106
223
|
- `@sqlrooms/artifacts` for artifact-backed map tabs
|
package/dist/KeplerSlice.d.ts
CHANGED
|
@@ -7,9 +7,12 @@ import { RoomShellSliceState, type StateCreator } from '@sqlrooms/room-shell';
|
|
|
7
7
|
import type { AnyAction, Store as ReduxStore } from 'redux';
|
|
8
8
|
import { Dispatch, Middleware } from 'redux';
|
|
9
9
|
import { ReduxLoggerOptions } from 'redux-logger';
|
|
10
|
+
import type { DefaultTheme } from 'styled-components';
|
|
11
|
+
import { type KeplerTableSelectionOptions } from './keplerTableSelection';
|
|
10
12
|
export type KeplerGLBasicProps = {
|
|
11
13
|
mapboxApiAccessToken?: string;
|
|
12
14
|
};
|
|
15
|
+
export type KeplerModalPortalTarget = 'body' | 'container';
|
|
13
16
|
export type CreateInitialMapKeplerStateContext = {
|
|
14
17
|
reason: 'initialize' | 'create-map' | 'duplicate-map' | 'register-map' | 'sync-config';
|
|
15
18
|
defaultInitialMapKeplerState: Partial<KeplerGlState>;
|
|
@@ -20,9 +23,26 @@ export type CreateKeplerSliceOptions = {
|
|
|
20
23
|
config?: Partial<KeplerSliceConfig>;
|
|
21
24
|
createInitialMapKeplerState?: (context: CreateInitialMapKeplerStateContext) => Partial<KeplerGlState>;
|
|
22
25
|
basicKeplerProps?: Partial<KeplerGLBasicProps>;
|
|
26
|
+
/**
|
|
27
|
+
* Theme passed to Kepler's ThemeProvider.
|
|
28
|
+
* Use createKeplerTheme() to merge app-level overrides into the default theme.
|
|
29
|
+
*/
|
|
30
|
+
keplerTheme?: DefaultTheme;
|
|
31
|
+
/**
|
|
32
|
+
* Where the Kepler modal (Add Map Style, Export, etc.) should be portaled.
|
|
33
|
+
* - 'body': portals to document.body (escapes stacking contexts)
|
|
34
|
+
* - 'container': portals inside the kepler map container element
|
|
35
|
+
* @default 'container'
|
|
36
|
+
*/
|
|
37
|
+
modalPortalTarget?: KeplerModalPortalTarget;
|
|
23
38
|
actionLogging?: boolean | ReduxLoggerOptions;
|
|
24
39
|
middlewares?: Middleware[];
|
|
25
40
|
applicationConfig?: KeplerApplicationConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Controls which DuckDB tables appear in Kepler's Add Layer menu and how
|
|
43
|
+
* those tables are represented in saved Kepler dataset ids.
|
|
44
|
+
*/
|
|
45
|
+
tableSelection?: KeplerTableSelectionOptions;
|
|
26
46
|
/**
|
|
27
47
|
* Called when a kepler action is dispatched
|
|
28
48
|
* @param mapId - The map id
|
|
@@ -45,11 +65,44 @@ export declare function hasMapId(action: KeplerAction): action is KeplerAction &
|
|
|
45
65
|
export type KeplerGlReduxState = {
|
|
46
66
|
[id: string]: KeplerGlState;
|
|
47
67
|
};
|
|
68
|
+
export type AddTableToMapLoadOptions = {
|
|
69
|
+
/**
|
|
70
|
+
* Load the table under an explicit Kepler dataset id.
|
|
71
|
+
*
|
|
72
|
+
* Normal callers should omit this so the table-selection policy decides the
|
|
73
|
+
* id. Restore uses it to satisfy already-saved layer/filter dataIds.
|
|
74
|
+
*/
|
|
75
|
+
datasetId?: string;
|
|
76
|
+
};
|
|
77
|
+
export type AddTableToMapParams = {
|
|
78
|
+
mapId: string;
|
|
79
|
+
/**
|
|
80
|
+
* Table reference to load. This can also be an existing/saved Kepler dataset
|
|
81
|
+
* id when the table-selection policy can resolve it back to a table.
|
|
82
|
+
*/
|
|
83
|
+
tableName: string;
|
|
84
|
+
options?: AddDataToMapPayload['options'];
|
|
85
|
+
config?: AddDataToMapPayload['config'];
|
|
86
|
+
/**
|
|
87
|
+
* Explicit Kepler dataset id to write into `datasets.info.id`.
|
|
88
|
+
*
|
|
89
|
+
* Most callers should omit this so `tableSelection.getDatasetIdForTable`
|
|
90
|
+
* controls new ids. Restore passes it to preserve saved layer/filter dataIds.
|
|
91
|
+
*/
|
|
92
|
+
datasetId?: string;
|
|
93
|
+
};
|
|
94
|
+
export type AddTableToMapFn = {
|
|
95
|
+
(params: AddTableToMapParams): Promise<void>;
|
|
96
|
+
(mapId: string, tableName: string, options?: AddDataToMapPayload['options'], config?: AddDataToMapPayload['config'], loadOptions?: AddTableToMapLoadOptions): Promise<void>;
|
|
97
|
+
};
|
|
48
98
|
export type KeplerSliceState = {
|
|
49
99
|
kepler: {
|
|
50
100
|
config: KeplerSliceConfig;
|
|
51
101
|
map: KeplerGlReduxState;
|
|
52
102
|
basicKeplerProps?: Partial<KeplerGLBasicProps>;
|
|
103
|
+
keplerTheme?: DefaultTheme;
|
|
104
|
+
modalPortalTarget: KeplerModalPortalTarget;
|
|
105
|
+
tableSelection: KeplerTableSelectionOptions;
|
|
53
106
|
forwardDispatch: {
|
|
54
107
|
[mapId: string]: Dispatch;
|
|
55
108
|
};
|
|
@@ -70,13 +123,16 @@ export type KeplerSliceState = {
|
|
|
70
123
|
updateTooltipFields: (mapId: string, datasetId: string, fieldNames: string[]) => void;
|
|
71
124
|
toggleSplitMap: (mapId: string, index?: number) => void;
|
|
72
125
|
toggleLayerForMap: (mapId: string, mapIndex: number, layerId: string) => void;
|
|
73
|
-
addTableToMap:
|
|
126
|
+
addTableToMap: AddTableToMapFn;
|
|
74
127
|
addTileSetToMap: (mapId: string, tableName: string, tileset: {
|
|
75
128
|
name: string;
|
|
76
129
|
type: string;
|
|
77
130
|
metadata: VectorTileDatasetMetadata;
|
|
78
131
|
}, tileMetadata: Record<string, any>, autoCreateLayers: boolean) => void;
|
|
79
132
|
addConfigToMap: (mapId: string, config: KeplerMapSchema) => void;
|
|
133
|
+
isRestoringConfig: boolean;
|
|
134
|
+
withConfigPersistencePaused: <TResult>(fn: () => TResult | Promise<TResult>) => Promise<TResult>;
|
|
135
|
+
waitForConfigRestore: () => Promise<void>;
|
|
80
136
|
removeDatasetFromMaps: (datasetId: string) => void;
|
|
81
137
|
dispatchAction: (mapId: string, action: KeplerAction) => void;
|
|
82
138
|
ensureMap: (mapId: string, name?: string) => void;
|
|
@@ -99,7 +155,7 @@ export type KeplerSliceState = {
|
|
|
99
155
|
__reduxProviderStore: ReduxStore<KeplerGlReduxState, AnyAction> | undefined;
|
|
100
156
|
};
|
|
101
157
|
};
|
|
102
|
-
export declare function createKeplerSlice({ basicKeplerProps, config: initialConfigProps, createInitialMapKeplerState, actionLogging, middlewares: additionalMiddlewares, applicationConfig, onAction, }?: CreateKeplerSliceOptions): StateCreator<KeplerSliceState>;
|
|
158
|
+
export declare function createKeplerSlice({ basicKeplerProps, keplerTheme, modalPortalTarget, config: initialConfigProps, createInitialMapKeplerState, actionLogging, middlewares: additionalMiddlewares, applicationConfig, tableSelection, onAction, }?: CreateKeplerSliceOptions): StateCreator<KeplerSliceState>;
|
|
103
159
|
type RoomStateWithDiscuss = RoomShellSliceState & KeplerSliceState;
|
|
104
160
|
export declare function useStoreWithKepler<T>(selector: (state: RoomStateWithDiscuss) => T): T;
|
|
105
161
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAkB,yBAAyB,EAAC,MAAM,sBAAsB,CAAC;AAShF,OAAO,EAGL,aAAa,EAEd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,uBAAuB,EACxB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAC,eAAe,EAAE,iBAAiB,EAAC,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAIL,mBAAmB,EAEnB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,KAAK,EAEV,SAAS,EAET,KAAK,IAAI,UAAU,EACpB,MAAM,OAAO,CAAC;AACf,OAAO,EAAU,QAAQ,EAAE,UAAU,EAAC,MAAM,OAAO,CAAC;AACpD,OAAO,EAAe,kBAAkB,EAAC,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"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"}
|
package/dist/KeplerSlice.js
CHANGED
|
@@ -14,6 +14,8 @@ import { produce, setAutoFreeze } from 'immer';
|
|
|
14
14
|
import { taskMiddleware } from 'react-palm/tasks';
|
|
15
15
|
import { compose } from 'redux';
|
|
16
16
|
import { createLogger } from 'redux-logger';
|
|
17
|
+
import { getUnqualifiedSqlIdentifier } from '@sqlrooms/duckdb-core';
|
|
18
|
+
import { findKeplerTableForDatasetId, getKeplerDatasetIdForTable, getKeplerTableLabel, } from './keplerTableSelection';
|
|
17
19
|
setAutoFreeze(false); // Kepler attempts to mutate redux state, so we need to disable immer's auto freeze to avoid errors
|
|
18
20
|
const KeplerGLSchemaManager = new KeplerGLSchemaClass();
|
|
19
21
|
class DesktopKeplerTable extends KeplerTable {
|
|
@@ -29,6 +31,7 @@ function createDefaultMapKeplerState(resolvedTheme) {
|
|
|
29
31
|
},
|
|
30
32
|
uiState: {
|
|
31
33
|
...INITIAL_UI_STATE,
|
|
34
|
+
activeSidePanel: null,
|
|
32
35
|
currentModal: null,
|
|
33
36
|
mapControls: {
|
|
34
37
|
visibleLayers: INITIAL_UI_STATE.mapControls.visibleLayers,
|
|
@@ -75,6 +78,25 @@ export function hasMapId(action) {
|
|
|
75
78
|
action.payload.meta !== null &&
|
|
76
79
|
'_id_' in action.payload.meta);
|
|
77
80
|
}
|
|
81
|
+
function normalizeAddTableToMapParams(paramsOrMapId, tableName, options = {}, config = {}, loadOptions = {}) {
|
|
82
|
+
if (typeof paramsOrMapId === 'object') {
|
|
83
|
+
return {
|
|
84
|
+
options: {},
|
|
85
|
+
config: {},
|
|
86
|
+
...paramsOrMapId,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!tableName) {
|
|
90
|
+
throw new Error('addTableToMap requires a tableName.');
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
mapId: paramsOrMapId,
|
|
94
|
+
tableName,
|
|
95
|
+
options,
|
|
96
|
+
config,
|
|
97
|
+
datasetId: loadOptions.datasetId,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
78
100
|
// Auto save will be triggered in middleware on every kepler action
|
|
79
101
|
// skip these actions to avoid unnecessary save
|
|
80
102
|
const SKIP_AUTO_SAVE_ACTIONS = [
|
|
@@ -82,13 +104,15 @@ const SKIP_AUTO_SAVE_ACTIONS = [
|
|
|
82
104
|
KeplerActionTypes.UPDATE_MAP,
|
|
83
105
|
KeplerActionTypes.MOUSE_MOVE,
|
|
84
106
|
];
|
|
85
|
-
export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfigProps, createInitialMapKeplerState, actionLogging = false, middlewares: additionalMiddlewares = [], applicationConfig, onAction, } = {}) {
|
|
107
|
+
export function createKeplerSlice({ basicKeplerProps = {}, keplerTheme, modalPortalTarget = 'container', config: initialConfigProps, createInitialMapKeplerState, actionLogging = false, middlewares: additionalMiddlewares = [], applicationConfig, tableSelection = {}, onAction, } = {}) {
|
|
86
108
|
const initialConfig = createDefaultKeplerConfig(initialConfigProps);
|
|
87
109
|
initApplicationConfig({
|
|
88
110
|
table: DesktopKeplerTable,
|
|
89
111
|
...applicationConfig,
|
|
90
112
|
});
|
|
91
113
|
let syncKeplerPromise = null;
|
|
114
|
+
let configRestoreDepth = 0;
|
|
115
|
+
const configRestorePromises = new Set();
|
|
92
116
|
// When a caller arrives while a sync is already in-flight, the in-flight run
|
|
93
117
|
// may have captured a stale snapshot of db.tables/kepler.map (missing maps or
|
|
94
118
|
// tables added by createMap, duplicateMap, etc.). Setting this flag tells the
|
|
@@ -102,6 +126,23 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
102
126
|
}
|
|
103
127
|
};
|
|
104
128
|
return createSlice((set, get, _store) => {
|
|
129
|
+
function getDefaultDbSchema() {
|
|
130
|
+
const currentDatabase = get().db.currentDatabase;
|
|
131
|
+
const currentSchema = get().db.currentSchema;
|
|
132
|
+
if (!currentDatabase || !currentSchema) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
database: currentDatabase,
|
|
137
|
+
schema: currentSchema,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const resolvedTableSelection = {
|
|
141
|
+
...tableSelection,
|
|
142
|
+
};
|
|
143
|
+
if (!resolvedTableSelection.defaultDbSchema) {
|
|
144
|
+
resolvedTableSelection.defaultDbSchema = getDefaultDbSchema;
|
|
145
|
+
}
|
|
105
146
|
function resolveInitialMapKeplerState(context) {
|
|
106
147
|
const resolvedTheme = getTheme();
|
|
107
148
|
const defaultInitialMapKeplerState = createDefaultMapKeplerState(resolvedTheme);
|
|
@@ -146,19 +187,29 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
146
187
|
kepler: {
|
|
147
188
|
config: initialConfig,
|
|
148
189
|
basicKeplerProps,
|
|
190
|
+
keplerTheme,
|
|
191
|
+
modalPortalTarget,
|
|
192
|
+
tableSelection: resolvedTableSelection,
|
|
149
193
|
map: {},
|
|
194
|
+
isRestoringConfig: false,
|
|
150
195
|
dispatchAction: () => { },
|
|
151
196
|
__reduxProviderStore: undefined,
|
|
152
197
|
forwardDispatch: {},
|
|
153
198
|
setConfig: (config) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
199
|
+
void get()
|
|
200
|
+
.kepler.withConfigPersistencePaused(async () => {
|
|
201
|
+
const nextConfig = KeplerSliceConfig.parse(config);
|
|
202
|
+
set((state) => produce(state, (draft) => {
|
|
203
|
+
draft.kepler.config = nextConfig;
|
|
204
|
+
}));
|
|
205
|
+
updateMapReduxStates();
|
|
206
|
+
updateForwardDispatch();
|
|
207
|
+
updateMapConfigs();
|
|
208
|
+
await get().kepler.syncKeplerDatasets();
|
|
209
|
+
})
|
|
210
|
+
.catch((error) => {
|
|
211
|
+
console.error('setConfig: failed to restore Kepler config', error);
|
|
212
|
+
});
|
|
162
213
|
},
|
|
163
214
|
async initialize() {
|
|
164
215
|
const config = get().kepler.config;
|
|
@@ -189,6 +240,7 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
189
240
|
// @ts-expect-error - Symbol.observable is not defined in the Redux type definitions
|
|
190
241
|
[Symbol.observable]: () => { },
|
|
191
242
|
},
|
|
243
|
+
isRestoringConfig: get().kepler.isRestoringConfig,
|
|
192
244
|
},
|
|
193
245
|
});
|
|
194
246
|
updateForwardDispatch();
|
|
@@ -262,13 +314,25 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
262
314
|
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
263
315
|
get().kepler.dispatchAction(mapId, toggleLayerForMapAction(mapIndex, layerId));
|
|
264
316
|
},
|
|
265
|
-
addTableToMap: async (
|
|
317
|
+
addTableToMap: (async (paramsOrMapId, legacyTableName, legacyOptions, legacyConfig, legacyLoadOptions) => {
|
|
318
|
+
const { mapId, tableName, options, config, datasetId: explicitDatasetId, } = normalizeAddTableToMapParams(paramsOrMapId, legacyTableName, legacyOptions, legacyConfig, legacyLoadOptions);
|
|
319
|
+
const tableSelection = get().kepler.tableSelection;
|
|
320
|
+
const table = findKeplerTableForDatasetId(get().db.tables, tableName, tableSelection);
|
|
321
|
+
const sqlTableName = table ? table.table.toString() : tableName;
|
|
322
|
+
let datasetId = explicitDatasetId;
|
|
323
|
+
if (!datasetId && table) {
|
|
324
|
+
datasetId = getKeplerDatasetIdForTable(table, tableSelection);
|
|
325
|
+
}
|
|
326
|
+
if (!datasetId) {
|
|
327
|
+
datasetId =
|
|
328
|
+
getUnqualifiedSqlIdentifier(String(sqlTableName)) ?? tableName;
|
|
329
|
+
}
|
|
266
330
|
const connector = await get().db.getConnector();
|
|
267
331
|
const duckDbColumns = await getDuckDBColumnTypes({
|
|
268
332
|
query: (query) => connector.query(query).result,
|
|
269
|
-
},
|
|
333
|
+
}, sqlTableName);
|
|
270
334
|
const tableDuckDBTypes = getDuckDBColumnTypesMap(duckDbColumns);
|
|
271
|
-
const adjustedQuery = castDuckDBTypesForKepler(
|
|
335
|
+
const adjustedQuery = castDuckDBTypesForKepler(sqlTableName, duckDbColumns);
|
|
272
336
|
const arrowResult = await connector.query(adjustedQuery).result;
|
|
273
337
|
setGeoArrowWKBExtension(arrowResult, duckDbColumns);
|
|
274
338
|
// TODO remove once DuckDB doesn't drop geoarrow metadata
|
|
@@ -276,14 +340,22 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
276
340
|
const fields = arrowSchemaToFields(arrowResult, tableDuckDBTypes);
|
|
277
341
|
const cols = Array.from({ length: arrowResult.numCols }, (_, i) => arrowResult.getChildAt(i)).filter((col) => col);
|
|
278
342
|
if (fields && cols) {
|
|
343
|
+
let label = tableName;
|
|
344
|
+
if (table) {
|
|
345
|
+
label = getKeplerTableLabel(table, tableSelection);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
label =
|
|
349
|
+
getUnqualifiedSqlIdentifier(String(sqlTableName)) ?? tableName;
|
|
350
|
+
}
|
|
279
351
|
const datasets = {
|
|
280
352
|
data: { fields, cols, rows: [], arrowTable: arrowResult },
|
|
281
|
-
info: { label
|
|
282
|
-
metadata: { tableName },
|
|
353
|
+
info: { label, id: datasetId },
|
|
354
|
+
metadata: { tableName: sqlTableName },
|
|
283
355
|
};
|
|
284
356
|
get().kepler.dispatchAction(mapId, addDataToMap({ datasets, options, config }));
|
|
285
357
|
}
|
|
286
|
-
},
|
|
358
|
+
}),
|
|
287
359
|
ensureMap: (mapId, name) => {
|
|
288
360
|
const now = Date.now();
|
|
289
361
|
set((state) => produce(state, (draft) => {
|
|
@@ -345,15 +417,30 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
345
417
|
referencedDataIds.add(dataId);
|
|
346
418
|
}
|
|
347
419
|
}
|
|
348
|
-
const availableTables =
|
|
349
|
-
.db.tables.filter((t) => t.table.schema === 'main')
|
|
350
|
-
.map((t) => t.table.table));
|
|
420
|
+
const availableTables = get().db.tables;
|
|
351
421
|
for (const dataId of referencedDataIds) {
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
422
|
+
if (keplerDatasets?.[dataId]) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const table = findKeplerTableForDatasetId(availableTables, dataId, get().kepler.tableSelection);
|
|
426
|
+
if (!table) {
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
await get().kepler.addTableToMap({
|
|
431
|
+
mapId,
|
|
432
|
+
tableName: dataId,
|
|
433
|
+
options: {
|
|
434
|
+
autoCreateLayers: false,
|
|
435
|
+
centerMap: false,
|
|
436
|
+
},
|
|
437
|
+
datasetId: dataId,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
console.error('syncKeplerDatasets: addTableToMap failed', {
|
|
442
|
+
dataId,
|
|
443
|
+
e,
|
|
357
444
|
});
|
|
358
445
|
}
|
|
359
446
|
}
|
|
@@ -472,6 +559,44 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
472
559
|
}
|
|
473
560
|
get().kepler.dispatchAction(mapId, addDataToMap({ config: parsedConfig, datasets: [] }));
|
|
474
561
|
},
|
|
562
|
+
withConfigPersistencePaused: async (fn) => {
|
|
563
|
+
configRestoreDepth += 1;
|
|
564
|
+
if (configRestoreDepth === 1) {
|
|
565
|
+
set((state) => produce(state, (draft) => {
|
|
566
|
+
draft.kepler.isRestoringConfig = true;
|
|
567
|
+
}));
|
|
568
|
+
}
|
|
569
|
+
const restorePromise = (async () => {
|
|
570
|
+
try {
|
|
571
|
+
return await fn();
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
// Kepler restore actions can enqueue follow-up work through
|
|
575
|
+
// react-palm; let those actions settle before persisted config
|
|
576
|
+
// writes are allowed again.
|
|
577
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
578
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
579
|
+
configRestoreDepth -= 1;
|
|
580
|
+
if (configRestoreDepth === 0) {
|
|
581
|
+
set((state) => produce(state, (draft) => {
|
|
582
|
+
draft.kepler.isRestoringConfig = false;
|
|
583
|
+
}));
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
})();
|
|
587
|
+
configRestorePromises.add(restorePromise);
|
|
588
|
+
try {
|
|
589
|
+
return await restorePromise;
|
|
590
|
+
}
|
|
591
|
+
finally {
|
|
592
|
+
configRestorePromises.delete(restorePromise);
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
waitForConfigRestore: async () => {
|
|
596
|
+
while (configRestorePromises.size > 0) {
|
|
597
|
+
await Promise.allSettled(Array.from(configRestorePromises));
|
|
598
|
+
}
|
|
599
|
+
},
|
|
475
600
|
registerKeplerMapIfNotExists(mapId) {
|
|
476
601
|
if (!get().kepler.map[mapId]) {
|
|
477
602
|
set({
|
|
@@ -514,7 +639,10 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
514
639
|
if (!mapId)
|
|
515
640
|
throw new Error('Map ID not found in action payload');
|
|
516
641
|
const result = next(action);
|
|
517
|
-
if (
|
|
642
|
+
if (configRestoreDepth === 0 &&
|
|
643
|
+
!get().kepler.isRestoringConfig &&
|
|
644
|
+
!SKIP_AUTO_SAVE_ACTIONS.includes(action.type) &&
|
|
645
|
+
mapId) {
|
|
518
646
|
// save kepler config to store
|
|
519
647
|
set((state) => produce(state, (draft) => {
|
|
520
648
|
const mapToSave = draft.kepler.config.maps.find((map) => map.id === mapId);
|