@sqlrooms/kepler 0.29.0-rc.2 → 0.29.0-rc.4

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 (45) hide show
  1. package/README.md +12 -2
  2. package/dist/KeplerSlice.d.ts +7 -5
  3. package/dist/KeplerSlice.d.ts.map +1 -1
  4. package/dist/KeplerSlice.js +48 -141
  5. package/dist/KeplerSlice.js.map +1 -1
  6. package/dist/components/CustomDndContext.d.ts.map +1 -1
  7. package/dist/components/CustomDndContext.js +5 -2
  8. package/dist/components/CustomDndContext.js.map +1 -1
  9. package/dist/components/CustomFilterPanelHeader.d.ts.map +1 -1
  10. package/dist/components/CustomFilterPanelHeader.js +5 -5
  11. package/dist/components/CustomFilterPanelHeader.js.map +1 -1
  12. package/dist/components/CustomLayerManager.d.ts.map +1 -1
  13. package/dist/components/CustomLayerManager.js.map +1 -1
  14. package/dist/components/CustomMapLegend.d.ts +4 -1
  15. package/dist/components/CustomMapLegend.d.ts.map +1 -1
  16. package/dist/components/CustomMapLegend.js +19 -8
  17. package/dist/components/CustomMapLegend.js.map +1 -1
  18. package/dist/components/CustomMapLegendPanel.d.ts +3 -3
  19. package/dist/components/CustomMapLegendPanel.d.ts.map +1 -1
  20. package/dist/components/CustomMapLegendPanel.js +19 -4
  21. package/dist/components/CustomMapLegendPanel.js.map +1 -1
  22. package/dist/components/KeplerMapContainer.d.ts.map +1 -1
  23. package/dist/components/KeplerMapContainer.js +36 -4
  24. package/dist/components/KeplerMapContainer.js.map +1 -1
  25. package/dist/components/KeplerPlotContainer.d.ts +1 -1
  26. package/dist/components/KeplerPlotContainer.d.ts.map +1 -1
  27. package/dist/components/KeplerPlotContainer.js +3 -1
  28. package/dist/components/KeplerPlotContainer.js.map +1 -1
  29. package/dist/components/SplitMapIndexContext.d.ts +2 -0
  30. package/dist/components/SplitMapIndexContext.d.ts.map +1 -0
  31. package/dist/components/SplitMapIndexContext.js +3 -0
  32. package/dist/components/SplitMapIndexContext.js.map +1 -0
  33. package/dist/hooks/useDndEffects.d.ts +1 -1
  34. package/dist/hooks/useDndEffects.d.ts.map +1 -1
  35. package/dist/hooks/useDndEffects.js +4 -0
  36. package/dist/hooks/useDndEffects.js.map +1 -1
  37. package/dist/hooks/useDndLayers.d.ts +1 -1
  38. package/dist/hooks/useDndLayers.d.ts.map +1 -1
  39. package/dist/hooks/useDndLayers.js +4 -0
  40. package/dist/hooks/useDndLayers.js.map +1 -1
  41. package/dist/index.d.ts +3 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +2 -1
  44. package/dist/index.js.map +1 -1
  45. package/package.json +23 -23
package/README.md CHANGED
@@ -7,7 +7,16 @@ Use this package when you want a **map-first analytics experience** in a SQLRoom
7
7
  - `createKeplerSlice()` to add Kepler state/actions to your Room store
8
8
  - `KeplerMapContainer` and `KeplerPlotContainer` for rendering maps/overlays
9
9
  - `KeplerSidePanels` for layer/filter/interaction UI
10
- - utilities for map config persistence and dataset synchronization
10
+ - utilities for map config persistence, dataset synchronization, and migration
11
+ from legacy Kepler-owned tabs to artifact-backed tabs
12
+
13
+ ## Selection model
14
+
15
+ - `createKeplerSlice()` manages Kepler map documents and runtime state keyed by
16
+ map id, but it does not own host-level map selection.
17
+ - Render maps with explicit ids, for example `<KeplerMapContainer mapId={id} />`.
18
+ - Use `@sqlrooms/artifacts` when an app needs multiple user-managed map tabs;
19
+ artifact state should own the selected map artifact.
11
20
 
12
21
  ## Installation
13
22
 
@@ -51,7 +60,7 @@ export const {roomStore, useRoomStore} = createRoomStore<RoomState>(
51
60
  );
52
61
 
53
62
  function MapPanel() {
54
- const mapId = useRoomStore((state) => state.kepler.config.currentMapId);
63
+ const mapId = useRoomStore((state) => state.kepler.config.maps[0]?.id);
55
64
  const addTableToMap = useRoomStore((state) => state.kepler.addTableToMap);
56
65
  const isTableReady = useRoomStore((state) =>
57
66
  Boolean(state.db.findTableByName('earthquakes')),
@@ -94,6 +103,7 @@ createKeplerSlice({
94
103
 
95
104
  ## Related packages
96
105
 
106
+ - `@sqlrooms/artifacts` for artifact-backed map tabs
97
107
  - `@sqlrooms/kepler-config` for Zod schemas used by persisted Kepler config
98
108
  - `@sqlrooms/room-shell` for Room store composition and UI shell
99
109
  - `@sqlrooms/duckdb` for DuckDB-backed table loading/querying
@@ -66,7 +66,10 @@ export type KeplerSliceState = {
66
66
  setFilter: (mapId: string, filterIdx: number, prop: string | string[], value: any, valueIndex?: number) => void;
67
67
  setFilterAnimationTime: (mapId: string, filterIdx: number, prop: string, value: any) => void;
68
68
  setFilterAnimationWindow: (mapId: string, filterId: string, animationWindow: string) => void;
69
+ setFilterView: (mapId: string, filterIdx: number, view: 'side' | 'enlarged' | 'minified') => void;
69
70
  updateTooltipFields: (mapId: string, datasetId: string, fieldNames: string[]) => void;
71
+ toggleSplitMap: (mapId: string, index?: number) => void;
72
+ toggleLayerForMap: (mapId: string, mapIndex: number, layerId: string) => void;
70
73
  addTableToMap: (mapId: string, tableName: string, options?: AddDataToMapPayload['options'], config?: AddDataToMapPayload['config']) => Promise<void>;
71
74
  addTileSetToMap: (mapId: string, tableName: string, tileset: {
72
75
  name: string;
@@ -76,13 +79,15 @@ export type KeplerSliceState = {
76
79
  addConfigToMap: (mapId: string, config: KeplerMapSchema) => void;
77
80
  removeDatasetFromMaps: (datasetId: string) => void;
78
81
  dispatchAction: (mapId: string, action: KeplerAction) => void;
79
- setCurrentMapId: (mapId: string) => void;
82
+ ensureMap: (mapId: string, name?: string) => void;
80
83
  /**
81
84
  * Create a new map and return the map id
82
85
  * @param name - The name of the map
83
86
  * @returns The map id
84
87
  */
85
- createMap: (name?: string) => string;
88
+ createMap: (name?: string, options?: {
89
+ id?: string;
90
+ }) => string;
86
91
  deleteMap: (mapId: string) => void;
87
92
  duplicateMap: (mapId: string) => Promise<{
88
93
  success: boolean;
@@ -90,9 +95,6 @@ export type KeplerSliceState = {
90
95
  code?: string;
91
96
  }>;
92
97
  renameMap: (mapId: string, name: string) => void;
93
- closeMap: (mapId: string) => void;
94
- setOpenTabs: (tabIds: string[]) => void;
95
- getCurrentMap: () => KeplerMapSchema | undefined;
96
98
  registerKeplerMapIfNotExists: (mapId: string) => void;
97
99
  __reduxProviderStore: ReduxStore<KeplerGlReduxState, AnyAction> | undefined;
98
100
  };
@@ -1 +1 @@
1
- {"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"KeplerSlice.d.ts","sourceRoot":"","sources":["../src/KeplerSlice.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
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';
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, setFilterView as setFilterViewAction, toggleLayerForMap as toggleLayerForMapAction, toggleSplitMap as toggleSplitMapAction, 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,7 +7,8 @@ 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, registerCommandsForOwner, unregisterCommandsForOwner, useBaseRoomShellStore, } from '@sqlrooms/room-shell';
10
+ import { KeplerSliceConfig } from '@sqlrooms/kepler-config';
11
+ import { createSlice, useBaseRoomShellStore, } from '@sqlrooms/room-shell';
11
12
  import { getTheme } from '@sqlrooms/ui';
12
13
  import { produce, setAutoFreeze } from 'immer';
13
14
  import { taskMiddleware } from 'react-palm/tasks';
@@ -15,7 +16,6 @@ import { compose } from 'redux';
15
16
  import { createLogger } from 'redux-logger';
16
17
  setAutoFreeze(false); // Kepler attempts to mutate redux state, so we need to disable immer's auto freeze to avoid errors
17
18
  const KeplerGLSchemaManager = new KeplerGLSchemaClass();
18
- const KEPLER_COMMAND_OWNER = '@sqlrooms/kepler';
19
19
  class DesktopKeplerTable extends KeplerTable {
20
20
  static getInputDataValidator = function () {
21
21
  // Default validator accepts only string timestamps
@@ -32,6 +32,10 @@ function createDefaultMapKeplerState(resolvedTheme) {
32
32
  currentModal: null,
33
33
  mapControls: {
34
34
  visibleLayers: INITIAL_UI_STATE.mapControls.visibleLayers,
35
+ splitMap: {
36
+ show: true,
37
+ active: false,
38
+ },
35
39
  mapLegend: {
36
40
  show: true,
37
41
  active: false,
@@ -50,7 +54,7 @@ function createDefaultMapKeplerState(resolvedTheme) {
50
54
  }
51
55
  export function createDefaultKeplerConfig(props) {
52
56
  const mapId = createId();
53
- return {
57
+ const config = {
54
58
  maps: [
55
59
  {
56
60
  id: mapId,
@@ -59,70 +63,9 @@ export function createDefaultKeplerConfig(props) {
59
63
  lastOpenedAt: Date.now(),
60
64
  },
61
65
  ],
62
- currentMapId: mapId,
63
- openTabs: [mapId],
64
66
  ...props,
65
67
  };
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
- ];
68
+ return KeplerSliceConfig.parse(config);
126
69
  }
127
70
  export function hasMapId(action) {
128
71
  return (typeof action.payload === 'object' &&
@@ -158,7 +101,7 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
158
101
  map.lastOpenedAt = now;
159
102
  }
160
103
  };
161
- return createSlice((set, get, store) => {
104
+ return createSlice((set, get, _store) => {
162
105
  function resolveInitialMapKeplerState(context) {
163
106
  const resolvedTheme = getTheme();
164
107
  const defaultInitialMapKeplerState = createDefaultMapKeplerState(resolvedTheme);
@@ -205,8 +148,9 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
205
148
  __reduxProviderStore: undefined,
206
149
  forwardDispatch: {},
207
150
  setConfig: (config) => {
151
+ const nextConfig = KeplerSliceConfig.parse(config);
208
152
  set((state) => produce(state, (draft) => {
209
- draft.kepler.config = config;
153
+ draft.kepler.config = nextConfig;
210
154
  }));
211
155
  updateMapReduxStates();
212
156
  updateForwardDispatch();
@@ -215,11 +159,11 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
215
159
  },
216
160
  async initialize() {
217
161
  const config = get().kepler.config;
218
- const currentMapId = config.currentMapId;
219
- const keplerInitialState = createKeplerReducer({
162
+ const keplerInitialState = config.maps.reduce((mapState, map) => createKeplerReducer({
220
163
  reason: 'initialize',
221
- mapId: currentMapId,
222
- })(undefined, registerEntry({ id: currentMapId }));
164
+ mapId: map.id,
165
+ name: map.name,
166
+ })(mapState, registerEntry({ id: map.id })), {});
223
167
  set({
224
168
  kepler: {
225
169
  ...get().kepler,
@@ -247,13 +191,12 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
247
191
  updateForwardDispatch();
248
192
  await get().kepler.syncKeplerDatasets();
249
193
  updateMapConfigs();
250
- requestMapStyle(config.currentMapId);
251
- // Register Kepler commands
252
- const keplerCommands = createKeplerCommands();
253
- registerCommandsForOwner(store, KEPLER_COMMAND_OWNER, keplerCommands);
194
+ for (const mapId of Object.keys(get().kepler.map)) {
195
+ requestMapStyle(mapId);
196
+ }
254
197
  },
255
198
  async destroy() {
256
- unregisterCommandsForOwner(store, KEPLER_COMMAND_OWNER);
199
+ // no-op
257
200
  },
258
201
  addLayer: (mapId, layer, datasetId) => {
259
202
  get().kepler.registerKeplerMapIfNotExists(mapId);
@@ -275,6 +218,10 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
275
218
  get().kepler.registerKeplerMapIfNotExists(mapId);
276
219
  get().kepler.dispatchAction(mapId, setFilterAnimationWindowAction({ id: filterId, animationWindow }));
277
220
  },
221
+ setFilterView: (mapId, filterIdx, view) => {
222
+ get().kepler.registerKeplerMapIfNotExists(mapId);
223
+ get().kepler.dispatchAction(mapId, setFilterViewAction(filterIdx, view));
224
+ },
278
225
  updateTooltipFields: (mapId, datasetId, fieldNames) => {
279
226
  get().kepler.registerKeplerMapIfNotExists(mapId);
280
227
  const mapState = get().kepler.map[mapId];
@@ -304,6 +251,14 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
304
251
  },
305
252
  }));
306
253
  },
254
+ toggleSplitMap: (mapId, index) => {
255
+ get().kepler.registerKeplerMapIfNotExists(mapId);
256
+ get().kepler.dispatchAction(mapId, toggleSplitMapAction(index));
257
+ },
258
+ toggleLayerForMap: (mapId, mapIndex, layerId) => {
259
+ get().kepler.registerKeplerMapIfNotExists(mapId);
260
+ get().kepler.dispatchAction(mapId, toggleLayerForMapAction(mapIndex, layerId));
261
+ },
307
262
  addTableToMap: async (mapId, tableName, options = {}, config = {}) => {
308
263
  const connector = await get().db.getConnector();
309
264
  const duckDbColumns = await getDuckDBColumnTypes({
@@ -326,26 +281,21 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
326
281
  get().kepler.dispatchAction(mapId, addDataToMap({ datasets, options, config }));
327
282
  }
328
283
  },
329
- getCurrentMap: () => {
330
- return get().kepler.config.maps.find((map) => map.id === get().kepler.config.currentMapId);
331
- },
332
- setCurrentMapId: (mapId) => {
333
- return set((state) => produce(state, (draft) => {
334
- const now = Date.now();
335
- draft.kepler.config.currentMapId = mapId;
336
- updateMapLastOpenedAt(draft.kepler.config.maps, mapId, now);
337
- }));
338
- },
339
- createMap: (name) => {
340
- const mapId = createId();
284
+ ensureMap: (mapId, name) => {
341
285
  const now = Date.now();
342
286
  set((state) => produce(state, (draft) => {
287
+ const existing = draft.kepler.config.maps.find((map) => map.id === mapId);
288
+ if (existing) {
289
+ if (name && existing.name !== name) {
290
+ existing.name = name;
291
+ }
292
+ return;
293
+ }
343
294
  draft.kepler.config.maps.push({
344
295
  id: mapId,
345
296
  name: name ?? 'Untitled Map',
346
297
  lastOpenedAt: now,
347
298
  });
348
- draft.kepler.config.openTabs.push(mapId);
349
299
  draft.kepler.map = createKeplerReducer({
350
300
  reason: 'create-map',
351
301
  mapId,
@@ -353,8 +303,15 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
353
303
  })(draft.kepler.map, registerEntry({ id: mapId }));
354
304
  draft.kepler.forwardDispatch[mapId] = getForwardDispatch(mapId);
355
305
  }));
306
+ if (!get().kepler.map[mapId]) {
307
+ get().kepler.registerKeplerMapIfNotExists(mapId);
308
+ }
356
309
  requestMapStyle(mapId);
357
310
  get().kepler.syncKeplerDatasets();
311
+ },
312
+ createMap: (name, options) => {
313
+ const mapId = options?.id ?? createId();
314
+ get().kepler.ensureMap(mapId, name);
358
315
  return mapId;
359
316
  },
360
317
  async syncKeplerDatasets() {
@@ -409,38 +366,8 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
409
366
  },
410
367
  deleteMap: (mapId) => {
411
368
  set((state) => produce(state, (draft) => {
412
- const openTabs = draft.kepler.config.openTabs;
413
369
  const maps = draft.kepler.config.maps;
414
- const wasCurrentMap = draft.kepler.config.currentMapId === mapId;
415
- const deletingIndex = openTabs.indexOf(mapId);
416
- // Remove from maps and openTabs
417
370
  draft.kepler.config.maps = maps.filter((map) => map.id !== mapId);
418
- draft.kepler.config.openTabs = openTabs.filter((id) => id !== mapId);
419
- // If we deleted the current map, select another one
420
- if (wasCurrentMap) {
421
- const newOpenTabs = draft.kepler.config.openTabs;
422
- const remainingMaps = draft.kepler.config.maps;
423
- if (newOpenTabs.length > 0) {
424
- // Select from remaining open tabs
425
- const newIndex = deletingIndex === 0
426
- ? 0
427
- : Math.min(deletingIndex - 1, newOpenTabs.length - 1);
428
- const newSelectedId = newOpenTabs[newIndex];
429
- if (newSelectedId) {
430
- draft.kepler.config.currentMapId = newSelectedId;
431
- updateMapLastOpenedAt(draft.kepler.config.maps, newSelectedId, Date.now());
432
- }
433
- }
434
- else if (remainingMaps.length > 0) {
435
- // No open tabs left, open a closed map
436
- const mapToOpen = remainingMaps[0];
437
- if (mapToOpen) {
438
- draft.kepler.config.openTabs.push(mapToOpen.id);
439
- draft.kepler.config.currentMapId = mapToOpen.id;
440
- updateMapLastOpenedAt(draft.kepler.config.maps, mapToOpen.id, Date.now());
441
- }
442
- }
443
- }
444
371
  draft.kepler.map = keplerReducer(draft.kepler.map, deleteEntry(mapId));
445
372
  delete draft.kepler.forwardDispatch[mapId];
446
373
  }));
@@ -468,8 +395,6 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
468
395
  config: savedConfig,
469
396
  lastOpenedAt: now,
470
397
  });
471
- draft.kepler.config.openTabs.push(newMapId);
472
- draft.kepler.config.currentMapId = newMapId;
473
398
  // Register the new map with empty state, then load the config
474
399
  draft.kepler.map = createKeplerReducer({
475
400
  reason: 'duplicate-map',
@@ -496,21 +421,6 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
496
421
  }
497
422
  }));
498
423
  },
499
- closeMap: (mapId) => {
500
- set((state) => produce(state, (draft) => {
501
- const openTabs = draft.kepler.config.openTabs;
502
- // Don't close if it's the last open tab (defensive check, TabStrip also prevents this)
503
- if (openTabs.length <= 1)
504
- return;
505
- // Just remove from openTabs - TabStrip handles selection via onSelect before calling onClose
506
- draft.kepler.config.openTabs = openTabs.filter((id) => id !== mapId);
507
- }));
508
- },
509
- setOpenTabs: (tabIds) => {
510
- set((state) => produce(state, (draft) => {
511
- draft.kepler.config.openTabs = tabIds;
512
- }));
513
- },
514
424
  addDataToMap: (mapId, data) => {
515
425
  get().kepler.registerKeplerMapIfNotExists(mapId);
516
426
  get().kepler.dispatchAction(mapId, addDataToMap(data));
@@ -646,10 +556,7 @@ export function createKeplerSlice({ basicKeplerProps = {}, config: initialConfig
646
556
  }
647
557
  function updateForwardDispatch() {
648
558
  const config = get().kepler.config;
649
- const currentMapId = config.currentMapId;
650
- const forwardDispatch = {
651
- [currentMapId]: getForwardDispatch(currentMapId),
652
- };
559
+ const forwardDispatch = {};
653
560
  if (config) {
654
561
  for (const { id } of config.maps) {
655
562
  forwardDispatch[id] = getForwardDispatch(id);