@sqlrooms/kepler 0.29.0-rc.1 → 0.29.0-rc.2
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/dist/KeplerSlice.d.ts +22 -6
- package/dist/KeplerSlice.d.ts.map +1 -1
- package/dist/KeplerSlice.js +273 -45
- package/dist/KeplerSlice.js.map +1 -1
- package/dist/components/CustomFilterManager.d.ts.map +1 -1
- package/dist/components/CustomFilterManager.js +4 -1
- package/dist/components/CustomFilterManager.js.map +1 -1
- package/dist/components/CustomInteractionManager.d.ts.map +1 -1
- package/dist/components/CustomInteractionManager.js +4 -1
- package/dist/components/CustomInteractionManager.js.map +1 -1
- package/dist/components/CustomLayerManager.d.ts.map +1 -1
- package/dist/components/CustomLayerManager.js +138 -17
- package/dist/components/CustomLayerManager.js.map +1 -1
- package/dist/components/CustomMapDrawPanel.d.ts +8 -0
- package/dist/components/CustomMapDrawPanel.d.ts.map +1 -0
- package/dist/components/CustomMapDrawPanel.js +28 -0
- package/dist/components/CustomMapDrawPanel.js.map +1 -0
- package/dist/components/KeplerAddDataDialog.d.ts +2 -3
- package/dist/components/KeplerAddDataDialog.d.ts.map +1 -1
- package/dist/components/KeplerAddDataDialog.js +18 -8
- package/dist/components/KeplerAddDataDialog.js.map +1 -1
- package/dist/components/KeplerImageExport.d.ts.map +1 -1
- package/dist/components/KeplerImageExport.js +26 -8
- package/dist/components/KeplerImageExport.js.map +1 -1
- 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/hooks/useKeplerStateActions.d.ts +7 -6
- package/dist/hooks/useKeplerStateActions.d.ts.map +1 -1
- package/dist/hooks/useKeplerStateActions.js +1 -1
- package/dist/hooks/useKeplerStateActions.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +20 -20
package/dist/KeplerSlice.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { VectorTileDatasetMetadata } from '@kepler.gl/constants';
|
|
2
|
-
import { Layer } from '@kepler.gl/layers';
|
|
3
2
|
import { KeplerGlState } from '@kepler.gl/reducers';
|
|
4
|
-
import { AddDataToMapPayload } from '@kepler.gl/types';
|
|
3
|
+
import { AddDataToMapPayload, MinSavedLayer, ParsedLayer } from '@kepler.gl/types';
|
|
5
4
|
import { KeplerApplicationConfig } from '@kepler.gl/utils';
|
|
6
5
|
import { KeplerMapSchema, KeplerSliceConfig } from '@sqlrooms/kepler-config';
|
|
7
6
|
import { RoomShellSliceState, type StateCreator } from '@sqlrooms/room-shell';
|
|
@@ -11,9 +10,15 @@ import { ReduxLoggerOptions } from 'redux-logger';
|
|
|
11
10
|
export type KeplerGLBasicProps = {
|
|
12
11
|
mapboxApiAccessToken?: string;
|
|
13
12
|
};
|
|
13
|
+
export type CreateInitialMapKeplerStateContext = {
|
|
14
|
+
reason: 'initialize' | 'create-map' | 'duplicate-map' | 'register-map' | 'sync-config';
|
|
15
|
+
defaultInitialMapKeplerState: Partial<KeplerGlState>;
|
|
16
|
+
mapId?: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
};
|
|
14
19
|
export type CreateKeplerSliceOptions = {
|
|
15
20
|
config?: Partial<KeplerSliceConfig>;
|
|
16
|
-
|
|
21
|
+
createInitialMapKeplerState?: (context: CreateInitialMapKeplerStateContext) => Partial<KeplerGlState>;
|
|
17
22
|
basicKeplerProps?: Partial<KeplerGLBasicProps>;
|
|
18
23
|
actionLogging?: boolean | ReduxLoggerOptions;
|
|
19
24
|
middlewares?: Middleware[];
|
|
@@ -49,14 +54,20 @@ export type KeplerSliceState = {
|
|
|
49
54
|
[mapId: string]: Dispatch;
|
|
50
55
|
};
|
|
51
56
|
initialize: () => Promise<void>;
|
|
57
|
+
destroy: () => Promise<void>;
|
|
52
58
|
setConfig: (config: KeplerSliceConfig) => void;
|
|
53
59
|
/**
|
|
54
60
|
* Update the datasets in all the kepler map so that they correspond to
|
|
55
61
|
* the latest table schemas in the database
|
|
56
62
|
*/
|
|
57
63
|
syncKeplerDatasets: () => Promise<void>;
|
|
58
|
-
addLayer: (mapId: string, layer:
|
|
59
|
-
|
|
64
|
+
addLayer: (mapId: string, layer: ParsedLayer | MinSavedLayer | undefined, datasetId: string) => void;
|
|
65
|
+
addFilter: (mapId: string, datasetId: string) => void;
|
|
66
|
+
setFilter: (mapId: string, filterIdx: number, prop: string | string[], value: any, valueIndex?: number) => void;
|
|
67
|
+
setFilterAnimationTime: (mapId: string, filterIdx: number, prop: string, value: any) => void;
|
|
68
|
+
setFilterAnimationWindow: (mapId: string, filterId: string, animationWindow: string) => void;
|
|
69
|
+
updateTooltipFields: (mapId: string, datasetId: string, fieldNames: string[]) => void;
|
|
70
|
+
addTableToMap: (mapId: string, tableName: string, options?: AddDataToMapPayload['options'], config?: AddDataToMapPayload['config']) => Promise<void>;
|
|
60
71
|
addTileSetToMap: (mapId: string, tableName: string, tileset: {
|
|
61
72
|
name: string;
|
|
62
73
|
type: string;
|
|
@@ -73,6 +84,11 @@ export type KeplerSliceState = {
|
|
|
73
84
|
*/
|
|
74
85
|
createMap: (name?: string) => string;
|
|
75
86
|
deleteMap: (mapId: string) => void;
|
|
87
|
+
duplicateMap: (mapId: string) => Promise<{
|
|
88
|
+
success: boolean;
|
|
89
|
+
message?: string;
|
|
90
|
+
code?: string;
|
|
91
|
+
}>;
|
|
76
92
|
renameMap: (mapId: string, name: string) => void;
|
|
77
93
|
closeMap: (mapId: string) => void;
|
|
78
94
|
setOpenTabs: (tabIds: string[]) => void;
|
|
@@ -81,7 +97,7 @@ export type KeplerSliceState = {
|
|
|
81
97
|
__reduxProviderStore: ReduxStore<KeplerGlReduxState, AnyAction> | undefined;
|
|
82
98
|
};
|
|
83
99
|
};
|
|
84
|
-
export declare function createKeplerSlice({ basicKeplerProps, config: initialConfigProps,
|
|
100
|
+
export declare function createKeplerSlice({ basicKeplerProps, config: initialConfigProps, createInitialMapKeplerState, actionLogging, middlewares: additionalMiddlewares, applicationConfig, onAction, }?: CreateKeplerSliceOptions): StateCreator<KeplerSliceState>;
|
|
85
101
|
type RoomStateWithDiscuss = RoomShellSliceState & KeplerSliceState;
|
|
86
102
|
export declare function useStoreWithKepler<T>(selector: (state: RoomStateWithDiscuss) => T): T;
|
|
87
103
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"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"}
|
package/dist/KeplerSlice.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { addDataToMap, addLayer as addLayerAction, deleteEntry, ActionTypes as KeplerActionTypes, registerEntry, removeDataset, requestMapStyles, wrapTo, } from '@kepler.gl/actions';
|
|
1
|
+
import { addDataToMap, addFilter as addFilterAction, addLayer as addLayerAction, deleteEntry, interactionConfigChange, ActionTypes as KeplerActionTypes, registerEntry, removeDataset, requestMapStyles, setFilter as setFilterAction, setFilterAnimationTime as setFilterAnimationTimeAction, setFilterAnimationWindow as setFilterAnimationWindowAction, wrapTo, } from '@kepler.gl/actions';
|
|
2
2
|
import { ALL_FIELD_TYPES } from '@kepler.gl/constants';
|
|
3
3
|
import { castDuckDBTypesForKepler, getDuckDBColumnTypes, getDuckDBColumnTypesMap, restoreGeoarrowMetadata, setGeoArrowWKBExtension, } from '@kepler.gl/duckdb';
|
|
4
4
|
import { arrowSchemaToFields } from '@kepler.gl/processors';
|
|
@@ -7,19 +7,47 @@ import { KeplerGLSchemaClass } from '@kepler.gl/schemas';
|
|
|
7
7
|
import { KeplerTable } from '@kepler.gl/table';
|
|
8
8
|
import { initApplicationConfig, } from '@kepler.gl/utils';
|
|
9
9
|
import { createId } from '@paralleldrive/cuid2';
|
|
10
|
-
import { createSlice, useBaseRoomShellStore, } from '@sqlrooms/room-shell';
|
|
10
|
+
import { createSlice, registerCommandsForOwner, unregisterCommandsForOwner, useBaseRoomShellStore, } from '@sqlrooms/room-shell';
|
|
11
|
+
import { getTheme } from '@sqlrooms/ui';
|
|
11
12
|
import { produce, setAutoFreeze } from 'immer';
|
|
12
13
|
import { taskMiddleware } from 'react-palm/tasks';
|
|
13
14
|
import { compose } from 'redux';
|
|
14
15
|
import { createLogger } from 'redux-logger';
|
|
15
16
|
setAutoFreeze(false); // Kepler attempts to mutate redux state, so we need to disable immer's auto freeze to avoid errors
|
|
16
17
|
const KeplerGLSchemaManager = new KeplerGLSchemaClass();
|
|
18
|
+
const KEPLER_COMMAND_OWNER = '@sqlrooms/kepler';
|
|
17
19
|
class DesktopKeplerTable extends KeplerTable {
|
|
18
20
|
static getInputDataValidator = function () {
|
|
19
21
|
// Default validator accepts only string timestamps
|
|
20
22
|
return (d) => d;
|
|
21
23
|
};
|
|
22
24
|
}
|
|
25
|
+
function createDefaultMapKeplerState(resolvedTheme) {
|
|
26
|
+
return {
|
|
27
|
+
mapStyle: {
|
|
28
|
+
styleType: resolvedTheme === 'dark' ? 'dark-matter' : 'positron',
|
|
29
|
+
},
|
|
30
|
+
uiState: {
|
|
31
|
+
...INITIAL_UI_STATE,
|
|
32
|
+
currentModal: null,
|
|
33
|
+
mapControls: {
|
|
34
|
+
visibleLayers: INITIAL_UI_STATE.mapControls.visibleLayers,
|
|
35
|
+
mapLegend: {
|
|
36
|
+
show: true,
|
|
37
|
+
active: false,
|
|
38
|
+
},
|
|
39
|
+
toggle3d: {
|
|
40
|
+
show: true,
|
|
41
|
+
active: false,
|
|
42
|
+
},
|
|
43
|
+
mapDraw: {
|
|
44
|
+
show: true,
|
|
45
|
+
active: false,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
23
51
|
export function createDefaultKeplerConfig(props) {
|
|
24
52
|
const mapId = createId();
|
|
25
53
|
return {
|
|
@@ -36,6 +64,66 @@ export function createDefaultKeplerConfig(props) {
|
|
|
36
64
|
...props,
|
|
37
65
|
};
|
|
38
66
|
}
|
|
67
|
+
function createKeplerCommands() {
|
|
68
|
+
const DUPLICATE_MAP_COMMAND_ID = 'kepler.duplicate-tab';
|
|
69
|
+
// Error codes for internal tracking/logging (kebab-case for consistency)
|
|
70
|
+
const ERROR_CODES = {
|
|
71
|
+
MAP_NOT_FOUND: 'map-not-found',
|
|
72
|
+
MAP_STATE_NOT_INITIALIZED: 'map-state-not-initialized',
|
|
73
|
+
};
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
id: DUPLICATE_MAP_COMMAND_ID,
|
|
77
|
+
name: 'Duplicate Tab',
|
|
78
|
+
description: 'Duplicate the current map tab',
|
|
79
|
+
group: 'Kepler',
|
|
80
|
+
keywords: ['kepler', 'map', 'duplicate', 'tab', 'copy'],
|
|
81
|
+
metadata: {
|
|
82
|
+
readOnly: false,
|
|
83
|
+
idempotent: false,
|
|
84
|
+
riskLevel: 'low',
|
|
85
|
+
},
|
|
86
|
+
execute: async ({ getState }) => {
|
|
87
|
+
const currentMapId = getState().kepler.config.currentMapId;
|
|
88
|
+
const sourceMap = getState().kepler.config.maps.find((m) => m.id === currentMapId);
|
|
89
|
+
if (!sourceMap) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
commandId: DUPLICATE_MAP_COMMAND_ID,
|
|
93
|
+
message: 'Unable to duplicate map: current map not found',
|
|
94
|
+
code: ERROR_CODES.MAP_NOT_FOUND,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Ensure the map's redux state is registered before attempting to duplicate
|
|
98
|
+
getState().kepler.registerKeplerMapIfNotExists(currentMapId);
|
|
99
|
+
// Re-read state after registration to avoid stale state
|
|
100
|
+
const sourceMapState = getState().kepler.map[currentMapId];
|
|
101
|
+
if (!sourceMapState) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
commandId: DUPLICATE_MAP_COMMAND_ID,
|
|
105
|
+
message: 'Unable to duplicate map: map state not initialized',
|
|
106
|
+
code: ERROR_CODES.MAP_STATE_NOT_INITIALIZED,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const duplicateResult = await getState().kepler.duplicateMap(currentMapId);
|
|
110
|
+
if (!duplicateResult.success) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
commandId: DUPLICATE_MAP_COMMAND_ID,
|
|
114
|
+
message: duplicateResult.message,
|
|
115
|
+
code: duplicateResult.code,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
commandId: DUPLICATE_MAP_COMMAND_ID,
|
|
121
|
+
message: 'Duplicated map tab',
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
39
127
|
export function hasMapId(action) {
|
|
40
128
|
return (typeof action.payload === 'object' &&
|
|
41
129
|
action.payload !== null &&
|
|
@@ -51,37 +139,38 @@ const SKIP_AUTO_SAVE_ACTIONS = [
|
|
|
51
139
|
KeplerActionTypes.UPDATE_MAP,
|
|
52
140
|
KeplerActionTypes.MOUSE_MOVE,
|
|
53
141
|
];
|
|
54
|
-
export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfigProps,
|
|
55
|
-
mapStyle: { styleType: 'positron' },
|
|
56
|
-
uiState: {
|
|
57
|
-
...INITIAL_UI_STATE,
|
|
58
|
-
currentModal: null,
|
|
59
|
-
mapControls: {
|
|
60
|
-
visibleLayers: INITIAL_UI_STATE.mapControls.visibleLayers,
|
|
61
|
-
mapLegend: {
|
|
62
|
-
show: true,
|
|
63
|
-
active: false,
|
|
64
|
-
},
|
|
65
|
-
toggle3d: {
|
|
66
|
-
show: true,
|
|
67
|
-
active: false,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
}, actionLogging = false, middlewares: additionalMiddlewares = [], applicationConfig, onAction, } = {}) {
|
|
142
|
+
export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfigProps, createInitialMapKeplerState, actionLogging = false, middlewares: additionalMiddlewares = [], applicationConfig, onAction, } = {}) {
|
|
72
143
|
const initialConfig = createDefaultKeplerConfig(initialConfigProps);
|
|
73
144
|
initApplicationConfig({
|
|
74
145
|
table: DesktopKeplerTable,
|
|
75
146
|
...applicationConfig,
|
|
76
147
|
});
|
|
148
|
+
let syncKeplerPromise = null;
|
|
149
|
+
// When a caller arrives while a sync is already in-flight, the in-flight run
|
|
150
|
+
// may have captured a stale snapshot of db.tables/kepler.map (missing maps or
|
|
151
|
+
// tables added by createMap, duplicateMap, etc.). Setting this flag tells the
|
|
152
|
+
// active run to loop once more with a fresh snapshot after it finishes, so
|
|
153
|
+
// late mutations are never silently dropped.
|
|
154
|
+
let pendingKeplerSync = false;
|
|
77
155
|
const updateMapLastOpenedAt = (maps, mapId, now = Date.now()) => {
|
|
78
156
|
const map = maps.find((item) => item.id === mapId);
|
|
79
157
|
if (map) {
|
|
80
158
|
map.lastOpenedAt = now;
|
|
81
159
|
}
|
|
82
160
|
};
|
|
83
|
-
return createSlice((set, get) => {
|
|
84
|
-
|
|
161
|
+
return createSlice((set, get, store) => {
|
|
162
|
+
function resolveInitialMapKeplerState(context) {
|
|
163
|
+
const resolvedTheme = getTheme();
|
|
164
|
+
const defaultInitialMapKeplerState = createDefaultMapKeplerState(resolvedTheme);
|
|
165
|
+
return (createInitialMapKeplerState?.({
|
|
166
|
+
...context,
|
|
167
|
+
defaultInitialMapKeplerState,
|
|
168
|
+
}) ?? defaultInitialMapKeplerState);
|
|
169
|
+
}
|
|
170
|
+
function createKeplerReducer(context) {
|
|
171
|
+
return keplerGlReducer.initialState(resolveInitialMapKeplerState(context));
|
|
172
|
+
}
|
|
173
|
+
const keplerReducer = createKeplerReducer({ reason: 'initialize' });
|
|
85
174
|
const middlewares = [
|
|
86
175
|
taskMiddleware,
|
|
87
176
|
saveKeplerConfigMiddleware,
|
|
@@ -121,13 +210,16 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
121
210
|
}));
|
|
122
211
|
updateMapReduxStates();
|
|
123
212
|
updateForwardDispatch();
|
|
124
|
-
updateMapConfigs();
|
|
125
213
|
get().kepler.syncKeplerDatasets();
|
|
214
|
+
updateMapConfigs();
|
|
126
215
|
},
|
|
127
216
|
async initialize() {
|
|
128
217
|
const config = get().kepler.config;
|
|
129
218
|
const currentMapId = config.currentMapId;
|
|
130
|
-
const keplerInitialState =
|
|
219
|
+
const keplerInitialState = createKeplerReducer({
|
|
220
|
+
reason: 'initialize',
|
|
221
|
+
mapId: currentMapId,
|
|
222
|
+
})(undefined, registerEntry({ id: currentMapId }));
|
|
131
223
|
set({
|
|
132
224
|
kepler: {
|
|
133
225
|
...get().kepler,
|
|
@@ -153,15 +245,66 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
153
245
|
},
|
|
154
246
|
});
|
|
155
247
|
updateForwardDispatch();
|
|
156
|
-
updateMapConfigs();
|
|
157
248
|
await get().kepler.syncKeplerDatasets();
|
|
249
|
+
updateMapConfigs();
|
|
158
250
|
requestMapStyle(config.currentMapId);
|
|
251
|
+
// Register Kepler commands
|
|
252
|
+
const keplerCommands = createKeplerCommands();
|
|
253
|
+
registerCommandsForOwner(store, KEPLER_COMMAND_OWNER, keplerCommands);
|
|
254
|
+
},
|
|
255
|
+
async destroy() {
|
|
256
|
+
unregisterCommandsForOwner(store, KEPLER_COMMAND_OWNER);
|
|
159
257
|
},
|
|
160
258
|
addLayer: (mapId, layer, datasetId) => {
|
|
161
259
|
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
162
260
|
get().kepler.dispatchAction(mapId, addLayerAction(layer, datasetId));
|
|
163
261
|
},
|
|
164
|
-
|
|
262
|
+
addFilter: (mapId, datasetId) => {
|
|
263
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
264
|
+
get().kepler.dispatchAction(mapId, addFilterAction(datasetId));
|
|
265
|
+
},
|
|
266
|
+
setFilter: (mapId, filterIdx, prop, value, valueIndex) => {
|
|
267
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
268
|
+
get().kepler.dispatchAction(mapId, setFilterAction(filterIdx, prop, value, valueIndex));
|
|
269
|
+
},
|
|
270
|
+
setFilterAnimationTime: (mapId, filterIdx, prop, value) => {
|
|
271
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
272
|
+
get().kepler.dispatchAction(mapId, setFilterAnimationTimeAction(filterIdx, prop, value));
|
|
273
|
+
},
|
|
274
|
+
setFilterAnimationWindow: (mapId, filterId, animationWindow) => {
|
|
275
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
276
|
+
get().kepler.dispatchAction(mapId, setFilterAnimationWindowAction({ id: filterId, animationWindow }));
|
|
277
|
+
},
|
|
278
|
+
updateTooltipFields: (mapId, datasetId, fieldNames) => {
|
|
279
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
280
|
+
const mapState = get().kepler.map[mapId];
|
|
281
|
+
const tooltipConfig = mapState?.visState?.interactionConfig?.tooltip;
|
|
282
|
+
if (!tooltipConfig)
|
|
283
|
+
return;
|
|
284
|
+
const currentFieldsToShow = tooltipConfig.config?.fieldsToShow || {};
|
|
285
|
+
const existingFields = currentFieldsToShow[datasetId] || [];
|
|
286
|
+
const existingByName = new Map(existingFields.map((field) => [
|
|
287
|
+
field.name,
|
|
288
|
+
field,
|
|
289
|
+
]));
|
|
290
|
+
const nextFields = Array.from(new Set(fieldNames)).map((name) => existingByName.get(name) ?? { name, format: null });
|
|
291
|
+
if (nextFields.length === existingFields.length &&
|
|
292
|
+
nextFields.every((field, index) => field.name === existingFields[index]?.name &&
|
|
293
|
+
field.format === existingFields[index]?.format)) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
get().kepler.dispatchAction(mapId, interactionConfigChange({
|
|
297
|
+
...tooltipConfig,
|
|
298
|
+
config: {
|
|
299
|
+
...tooltipConfig.config,
|
|
300
|
+
fieldsToShow: {
|
|
301
|
+
...currentFieldsToShow,
|
|
302
|
+
[datasetId]: nextFields,
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
}));
|
|
306
|
+
},
|
|
307
|
+
addTableToMap: async (mapId, tableName, options = {}, config = {}) => {
|
|
165
308
|
const connector = await get().db.getConnector();
|
|
166
309
|
const duckDbColumns = await getDuckDBColumnTypes({
|
|
167
310
|
query: (query) => connector.query(query).result,
|
|
@@ -180,7 +323,7 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
180
323
|
info: { label: tableName, id: tableName },
|
|
181
324
|
metadata: { tableName },
|
|
182
325
|
};
|
|
183
|
-
get().kepler.dispatchAction(mapId, addDataToMap({ datasets, options }));
|
|
326
|
+
get().kepler.dispatchAction(mapId, addDataToMap({ datasets, options, config }));
|
|
184
327
|
}
|
|
185
328
|
},
|
|
186
329
|
getCurrentMap: () => {
|
|
@@ -203,7 +346,11 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
203
346
|
lastOpenedAt: now,
|
|
204
347
|
});
|
|
205
348
|
draft.kepler.config.openTabs.push(mapId);
|
|
206
|
-
draft.kepler.map =
|
|
349
|
+
draft.kepler.map = createKeplerReducer({
|
|
350
|
+
reason: 'create-map',
|
|
351
|
+
mapId,
|
|
352
|
+
name,
|
|
353
|
+
})(draft.kepler.map, registerEntry({ id: mapId }));
|
|
207
354
|
draft.kepler.forwardDispatch[mapId] = getForwardDispatch(mapId);
|
|
208
355
|
}));
|
|
209
356
|
requestMapStyle(mapId);
|
|
@@ -211,22 +358,53 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
211
358
|
return mapId;
|
|
212
359
|
},
|
|
213
360
|
async syncKeplerDatasets() {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
361
|
+
if (syncKeplerPromise) {
|
|
362
|
+
pendingKeplerSync = true;
|
|
363
|
+
return syncKeplerPromise;
|
|
364
|
+
}
|
|
365
|
+
syncKeplerPromise = (async () => {
|
|
366
|
+
// Loop until no new sync was requested during the current pass.
|
|
367
|
+
// Each iteration reads a fresh snapshot of db.tables and kepler.map,
|
|
368
|
+
// so tables/maps added concurrently are picked up on the next pass.
|
|
369
|
+
do {
|
|
370
|
+
pendingKeplerSync = false;
|
|
371
|
+
for (const mapId of Object.keys(get().kepler.map)) {
|
|
372
|
+
const mapState = get().kepler.map[mapId];
|
|
373
|
+
if (!mapState)
|
|
374
|
+
continue;
|
|
375
|
+
const keplerDatasets = mapState.visState.datasets;
|
|
376
|
+
// Only sync tables that are referenced by existing layers or filters
|
|
377
|
+
const referencedDataIds = new Set();
|
|
378
|
+
for (const layer of mapState.visState.layers ?? []) {
|
|
379
|
+
if (layer.config.dataId) {
|
|
380
|
+
referencedDataIds.add(layer.config.dataId);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
for (const filter of mapState.visState.filters ?? []) {
|
|
384
|
+
for (const dataId of filter.dataId ?? []) {
|
|
385
|
+
referencedDataIds.add(dataId);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
const availableTables = new Set(get()
|
|
389
|
+
.db.tables.filter((t) => t.table.schema === 'main')
|
|
390
|
+
.map((t) => t.table.table));
|
|
391
|
+
for (const dataId of referencedDataIds) {
|
|
392
|
+
if (!keplerDatasets?.[dataId] &&
|
|
393
|
+
availableTables.has(dataId)) {
|
|
394
|
+
await get().kepler.addTableToMap(mapId, dataId, {
|
|
395
|
+
autoCreateLayers: false,
|
|
396
|
+
centerMap: false,
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
228
400
|
}
|
|
229
|
-
}
|
|
401
|
+
} while (pendingKeplerSync);
|
|
402
|
+
})();
|
|
403
|
+
try {
|
|
404
|
+
await syncKeplerPromise;
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
syncKeplerPromise = null;
|
|
230
408
|
}
|
|
231
409
|
},
|
|
232
410
|
deleteMap: (mapId) => {
|
|
@@ -267,6 +445,49 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
267
445
|
delete draft.kepler.forwardDispatch[mapId];
|
|
268
446
|
}));
|
|
269
447
|
},
|
|
448
|
+
duplicateMap: async (mapId) => {
|
|
449
|
+
// Ensure the map's redux state is registered, consistent with other kepler actions
|
|
450
|
+
get().kepler.registerKeplerMapIfNotExists(mapId);
|
|
451
|
+
const sourceMap = get().kepler.config.maps.find((m) => m.id === mapId);
|
|
452
|
+
const sourceMapState = get().kepler.map[mapId];
|
|
453
|
+
if (!sourceMap || !sourceMapState) {
|
|
454
|
+
return {
|
|
455
|
+
success: false,
|
|
456
|
+
message: 'Unable to duplicate map: source map or state not found',
|
|
457
|
+
code: 'source-map-not-found',
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
const newMapId = createId();
|
|
461
|
+
const now = Date.now();
|
|
462
|
+
// Save the source map state using Kepler's schema manager
|
|
463
|
+
const savedConfig = KeplerGLSchemaManager.getConfigToSave(sourceMapState);
|
|
464
|
+
set((state) => produce(state, (draft) => {
|
|
465
|
+
draft.kepler.config.maps.push({
|
|
466
|
+
id: newMapId,
|
|
467
|
+
name: `Copy of ${sourceMap.name}`,
|
|
468
|
+
config: savedConfig,
|
|
469
|
+
lastOpenedAt: now,
|
|
470
|
+
});
|
|
471
|
+
draft.kepler.config.openTabs.push(newMapId);
|
|
472
|
+
draft.kepler.config.currentMapId = newMapId;
|
|
473
|
+
// Register the new map with empty state, then load the config
|
|
474
|
+
draft.kepler.map = createKeplerReducer({
|
|
475
|
+
reason: 'duplicate-map',
|
|
476
|
+
mapId: newMapId,
|
|
477
|
+
name: `Copy of ${sourceMap.name}`,
|
|
478
|
+
})(draft.kepler.map, registerEntry({ id: newMapId }));
|
|
479
|
+
draft.kepler.forwardDispatch[newMapId] =
|
|
480
|
+
getForwardDispatch(newMapId);
|
|
481
|
+
}));
|
|
482
|
+
// Load the saved config into the new map
|
|
483
|
+
get().kepler.addConfigToMap(newMapId, savedConfig);
|
|
484
|
+
requestMapStyle(newMapId);
|
|
485
|
+
get().kepler.syncKeplerDatasets();
|
|
486
|
+
return {
|
|
487
|
+
success: true,
|
|
488
|
+
message: 'Map duplicated successfully',
|
|
489
|
+
};
|
|
490
|
+
},
|
|
270
491
|
renameMap: (mapId, name) => {
|
|
271
492
|
set((state) => produce(state, (draft) => {
|
|
272
493
|
const map = draft.kepler.config.maps.find((map) => map.id === mapId);
|
|
@@ -343,7 +564,10 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
343
564
|
set({
|
|
344
565
|
kepler: {
|
|
345
566
|
...get().kepler,
|
|
346
|
-
map:
|
|
567
|
+
map: createKeplerReducer({
|
|
568
|
+
reason: 'register-map',
|
|
569
|
+
mapId,
|
|
570
|
+
})(get().kepler.map, registerEntry({ id: mapId })),
|
|
347
571
|
forwardDispatch: {
|
|
348
572
|
...get().kepler.forwardDispatch,
|
|
349
573
|
[mapId]: getForwardDispatch(mapId),
|
|
@@ -410,7 +634,11 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
|
|
|
410
634
|
// Register redux state of maps that are not in the config
|
|
411
635
|
for (const map of draft.kepler.config.maps) {
|
|
412
636
|
if (!draft.kepler.map[map.id]) {
|
|
413
|
-
draft.kepler.map =
|
|
637
|
+
draft.kepler.map = createKeplerReducer({
|
|
638
|
+
reason: 'sync-config',
|
|
639
|
+
mapId: map.id,
|
|
640
|
+
name: map.name,
|
|
641
|
+
})(draft.kepler.map, registerEntry({ id: map.id }));
|
|
414
642
|
draft.kepler.forwardDispatch[map.id] = getForwardDispatch(map.id);
|
|
415
643
|
}
|
|
416
644
|
}
|