diff --git a/bindings/kepler.gl-jupyter/js/package.json b/bindings/kepler.gl-jupyter/js/package.json
index d0add4497a..12362ed265 100644
--- a/bindings/kepler.gl-jupyter/js/package.json
+++ b/bindings/kepler.gl-jupyter/js/package.json
@@ -50,6 +50,7 @@
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "~7.12.4",
+ "@jupyterlab/builder": "^4.0.0",
"html-webpack-plugin": "^4.3.0",
"react-dev-utils": "^10.2.1",
"rimraf": "^2.6.1",
diff --git a/src/actions/src/actions.ts b/src/actions/src/actions.ts
index b520a31a0a..8d7159d2ba 100644
--- a/src/actions/src/actions.ts
+++ b/src/actions/src/actions.ts
@@ -30,7 +30,13 @@ import {
ProtoDataset
} from '@kepler.gl/types';
-export type ActionHandler any> = (...args: Parameters) => void;
+type Handler = (...args: any) => any;
+
+export type ActionHandler = (...args: Parameters) => void;
+
+export type ActionHandlers = {
+ [K in keyof T]: ActionHandler;
+};
/**
* Add data to kepler.gl reducer, prepare map with preset configuration if config is passed.
diff --git a/src/actions/src/ui-state-actions.ts b/src/actions/src/ui-state-actions.ts
index 2fcba9193a..c8e4a2c345 100644
--- a/src/actions/src/ui-state-actions.ts
+++ b/src/actions/src/ui-state-actions.ts
@@ -25,7 +25,7 @@ import {ExportImage} from '@kepler.gl/constants';
/** TOGGLE_SIDE_PANEL */
export type ToggleSidePanelUpdaterAction = {
- payload: string;
+ payload: string | null;
};
/**
* Toggle active side panel
@@ -34,11 +34,11 @@ export type ToggleSidePanelUpdaterAction = {
* @public
*/
export const toggleSidePanel: (
- id: string
+ id: string | null
) => Merge<
ToggleSidePanelUpdaterAction,
{type: typeof ActionTypes.TOGGLE_SIDE_PANEL}
-> = createAction(ActionTypes.TOGGLE_SIDE_PANEL, (id: string) => ({payload: id}));
+> = createAction(ActionTypes.TOGGLE_SIDE_PANEL, (id: string | null) => ({payload: id}));
/** TOGGLE_MODAL */
export type ToggleModalUpdaterAction = {
diff --git a/src/components/src/editor/editor.tsx b/src/components/src/editor/editor.tsx
index 987afe9eff..ea4a1362f0 100644
--- a/src/components/src/editor/editor.tsx
+++ b/src/components/src/editor/editor.tsx
@@ -33,8 +33,8 @@ import {
KeyEvent
} from '@kepler.gl/constants';
import {Layer, EditorLayerUtils} from '@kepler.gl/layers';
-import {Filter, FeatureSelectionContext} from '@kepler.gl/types';
-import {Feature} from '@nebula.gl/edit-modes';
+import {Filter, FeatureSelectionContext, Feature} from '@kepler.gl/types';
+import {FeatureOf, Polygon} from '@nebula.gl/edit-modes';
import {Datasets} from '@kepler.gl/table';
const DECKGL_RENDER_LAYER = 'default-deckgl-overlay-wrapper';
@@ -53,12 +53,12 @@ interface EditorProps {
datasets: Datasets;
editor: {selectedFeature: Feature; mode: string; selectionContext?: FeatureSelectionContext};
index: number;
- className: string;
+ className?: string;
style: CSSProperties;
- onSelect: (f: Feature | null) => void;
+ onSelect: (f: Feature | null) => any;
onSetEditorMode: (m: any) => void;
- onDeleteFeature: (f: Feature) => void;
- onTogglePolygonFilter: (l: Layer, f: Feature) => void;
+ onDeleteFeature: (f: Feature) => any;
+ onTogglePolygonFilter: (l: Layer, f: Feature) => any;
}
export default function EditorFactory(
@@ -163,7 +163,7 @@ export default function EditorFactory(
{Boolean(rightClick) && selectedFeature && index === mapIndex ? (
}
datasets={datasets}
layers={availableLayers}
currentFilter={currentFilter}
diff --git a/src/components/src/index.ts b/src/components/src/index.ts
index 8b430b67b3..26c8d005ee 100644
--- a/src/components/src/index.ts
+++ b/src/components/src/index.ts
@@ -42,7 +42,7 @@ export {default as KeplerGl, default, injectComponents, ContainerFactory, ERROR_
export {default as KeplerGlFactory, DEFAULT_KEPLER_GL_PROPS, getVisibleDatasets, mapFieldsSelector, plotContainerSelector} from './kepler-gl';
export {default as SidePanelFactory} from './side-panel';
export {default as PanelTitleFactory} from './side-panel/panel-title';
-export {default as MapContainerFactory} from './map-container';
+export {default as MapContainerFactory, Attribution} from './map-container';
export {default as MapsLayoutFactory} from './maps-layout';
export {default as BottomWidgetFactory} from './bottom-widget';
export {default as LayerAnimationControllerFactory} from './layer-animation-controller';
@@ -268,6 +268,10 @@ export type {LayerGroupColorPickerProps} from './side-panel/map-style-panel/map-
export type {MapLegendPanelProps, MapLegendPanelFactoryDeps} from './map/map-legend-panel';
export type {FormatterDropdownProps} from './common/data-table/option-dropdown';
export type {LayerListProps, LayerListFactoryDeps} from './side-panel/layer-panel/layer-list';
+export type {MapContainerProps} from './map-container';
+export type {MapControlProps} from './map/map-control';
+export type {MapDrawPanelProps} from './map/map-draw-panel';
+export type {PanelHeaderProps} from './side-panel/panel-header';
export {
Icons,
diff --git a/src/components/src/map-container.tsx b/src/components/src/map-container.tsx
index 20b2d4f177..a4d477a68b 100644
--- a/src/components/src/map-container.tsx
+++ b/src/components/src/map-container.tsx
@@ -202,11 +202,11 @@ const DatasetAttributions = ({
>
);
-export const Attribution = ({
- showMapboxLogo = true,
- showOsmBasemapAttribution = false,
- datasetAttributions
-}) => {
+export const Attribution: React.FC<{
+ showMapboxLogo: boolean;
+ showOsmBasemapAttribution: boolean;
+ datasetAttributions: DatasetAttribution[];
+}> = ({showMapboxLogo = true, showOsmBasemapAttribution = false, datasetAttributions}) => {
const isPalm = hasMobileWidth(breakPointValues);
const memoizedComponents = useMemo(() => {
@@ -268,7 +268,7 @@ MapContainerFactory.deps = [MapPopoverFactory, MapControlFactory, EditorFactory]
type MapboxStyle = string | object | undefined;
type PropSelector = Selector;
-interface MapContainerProps {
+export interface MapContainerProps {
visState: VisState;
mapState: MapState;
mapControls: MapControls;
@@ -310,9 +310,9 @@ interface MapContainerProps {
}
export default function MapContainerFactory(
- MapPopover,
- MapControl,
- Editor
+ MapPopover: ReturnType,
+ MapControl: ReturnType,
+ Editor: ReturnType
): React.ComponentType {
class MapContainer extends Component {
displayName = 'MapContainer';
@@ -592,7 +592,7 @@ export default function MapContainerFactory(
? interactionConfig.tooltip.config.compareMode
: false;
- let pinnedPosition = {};
+ let pinnedPosition = {x: 0, y: 0};
let layerPinnedProp: LayerHoverProp | null = null;
if (pinned || clicked) {
// project lnglat to screen so that tooltip follows the object on zoom
@@ -903,11 +903,11 @@ export default function MapContainerFactory(
availableLocales={LOCALE_CODES_ARRAY}
dragRotate={mapState.dragRotate}
isSplit={isSplit}
- primary={primary}
+ primary={Boolean(primary)}
isExport={isExport}
layers={layers}
layersToRender={layersToRender}
- mapIndex={index}
+ mapIndex={index || 0}
mapControls={mapControls}
readOnly={this.props.readOnly}
scale={mapState.scale || 1}
@@ -941,7 +941,7 @@ export default function MapContainerFactory(
{this._renderDeckOverlay(layersForDeck, {primaryMap: true})}
{this._renderMapboxOverlays()}
void;
- onToggleSplitMap: () => void;
+ onToggleSplitMap: typeof MapStateActions.toggleSplitMap;
onToggleSplitMapViewport: ({
isViewportSynced,
isZoomLocked
@@ -74,13 +75,15 @@ export type MapControlProps = {
isViewportSynced: boolean;
isZoomLocked: boolean;
}) => void;
+ onMapToggleLayer: (layerId: string) => void;
onToggleMapControl: (control: string) => void;
onSetEditorMode: (mode: string) => void;
onToggleEditorVisibility: () => void;
top: number;
- onSetLocale: () => void;
+ onSetLocale: typeof UIStateActions.setLocale;
locale: string;
- logoComponent: React.FC | React.ReactNode;
+ logoComponent?: React.FC | React.ReactNode;
+ isExport?: boolean;
// optional
mapState?: MapState;
@@ -88,25 +91,25 @@ export type MapControlProps = {
scale?: number;
mapLayers?: {[key: string]: boolean};
editor: Editor;
- actionComponents: React.FC[] | React.Component[];
+ actionComponents?: React.ComponentType[];
mapHeight?: number;
};
MapControlFactory.deps = [
- MapDrawPanelFactory,
- Toggle3dButtonFactory,
SplitMapButtonFactory,
- MapLegendPanelFactory,
+ Toggle3dButtonFactory,
LayerSelectorPanelFactory,
+ MapLegendPanelFactory,
+ MapDrawPanelFactory,
LocalePanelFactory
];
function MapControlFactory(
- MapDrawPanel: ReturnType,
- Toggle3dButton: ReturnType,
SplitMapButton: ReturnType,
- MapLegendPanel: ReturnType,
+ Toggle3dButton: ReturnType,
LayerSelectorPanel: ReturnType,
+ MapLegendPanel: ReturnType,
+ MapDrawPanel: ReturnType,
LocalePanel: ReturnType
) {
const DEFAULT_ACTIONS = [
@@ -118,23 +121,23 @@ function MapControlFactory(
MapLegendPanel
];
- /** @type {import('./map-control').MapControl} */
- const MapControl: React.FC = React.memo(({actionComponents, ...props}) => {
- return (
-
- {actionComponents.map((ActionComponent, index) => (
-
- ))}
-
- );
- });
+ const MapControl: React.FC = React.memo(
+ ({actionComponents = DEFAULT_ACTIONS, ...props}) => {
+ return (
+
+ {actionComponents.map((ActionComponent, index) => (
+
+ ))}
+
+ );
+ }
+ );
MapControl.defaultProps = {
isSplit: false,
top: 0,
mapIndex: 0,
logoComponent: LegendLogo,
- // @ts-expect-error
actionComponents: DEFAULT_ACTIONS
};
diff --git a/src/components/src/map/map-draw-panel.tsx b/src/components/src/map/map-draw-panel.tsx
index a8b92e0098..551c0b4a1f 100644
--- a/src/components/src/map/map-draw-panel.tsx
+++ b/src/components/src/map/map-draw-panel.tsx
@@ -41,7 +41,10 @@ export type MapDrawPanelProps = {
actionIcons: {[id: string]: React.ComponentType>};
};
-function MapDrawPanelFactory(MapControlTooltip, MapControlToolbar) {
+function MapDrawPanelFactory(
+ MapControlTooltip: ReturnType,
+ MapControlToolbar: ReturnType
+) {
const defaultActionIcons = {
visible: EyeSeen,
hidden: EyeUnseen,
@@ -50,7 +53,7 @@ function MapDrawPanelFactory(MapControlTooltip, MapControlToolbar) {
innerPolygon: Polygon,
rectangle: Rectangle
};
- /** @type {import('./map-draw-panel').MapDrawPanelComponent} */
+
const MapDrawPanel: React.FC = React.memo(
({
editor,
diff --git a/src/components/src/map/map-popover.tsx b/src/components/src/map/map-popover.tsx
index 918115d6c3..c65dc453e0 100644
--- a/src/components/src/map/map-popover.tsx
+++ b/src/components/src/map/map-popover.tsx
@@ -30,7 +30,7 @@ import {parseGeoJsonRawFeature} from '@kepler.gl/layers';
import {idToPolygonGeo, generateHashId} from '@kepler.gl/utils';
import {LAYER_TYPES} from '@kepler.gl/constants';
import {LayerHoverProp} from '@kepler.gl/reducers';
-import {Feature} from '@kepler.gl/types';
+import {Feature, FeatureSelectionContext} from '@kepler.gl/types';
const SELECTABLE_LAYERS: string[] = [LAYER_TYPES.hexagonId, LAYER_TYPES.geojson];
const MAX_WIDTH = 500;
@@ -228,7 +228,7 @@ export type MapPopoverProps = {
container?: HTMLElement | null;
onClose: () => void;
onSetFeatures: (features: Feature[]) => any;
- setSelectedFeature: (feature: Feature, clickContext: object) => any;
+ setSelectedFeature: (feature: Feature | null, clickContext?: FeatureSelectionContext) => any;
featureCollection?: {
type: string;
features: Feature[];
diff --git a/src/components/src/side-panel.tsx b/src/components/src/side-panel.tsx
index 311bb26c87..4cb0d60580 100644
--- a/src/components/src/side-panel.tsx
+++ b/src/components/src/side-panel.tsx
@@ -45,7 +45,7 @@ import CustomPanelsFactory from './side-panel/custom-panel';
import styled from 'styled-components';
import get from 'lodash.get';
-import {SidePanelProps} from './types';
+import {SidePanelProps, SidePanelItem} from './types';
export const StyledSidePanelContent = styled.div`
${props => props.theme.sidePanelScrollBar};
@@ -101,7 +101,7 @@ export default function SidePanelFactory(
};
// We should defined sidebar panels here but keeping them for backward compatible
- const fullPanels = SIDEBAR_PANELS.map(component => ({
+ const fullPanels: SidePanelItem[] = SIDEBAR_PANELS.map(component => ({
...component,
component: SIDEBAR_COMPONENTS[component.id],
iconComponent: SIDEBAR_ICONS[component.id]
@@ -110,7 +110,7 @@ export default function SidePanelFactory(
const getCustomPanelProps = get(CustomPanels, ['defaultProps', 'getProps']) || (() => ({}));
// eslint-disable-next-line max-statements
- const SidePanel = (props: SidePanelProps) => {
+ const SidePanel: React.FC = (props: SidePanelProps) => {
const {
appName,
appWebsite,
@@ -123,7 +123,7 @@ export default function SidePanelFactory(
layerClasses,
layerOrder,
interactionConfig,
- panels,
+ panels = fullPanels,
mapInfo,
mapSaved,
mapStateActions,
@@ -181,7 +181,7 @@ export default function SidePanelFactory(
() => (hasStorage && mapSaved ? onClickSaveAsToStorage : null),
[hasStorage, mapSaved, onClickSaveAsToStorage]
);
- const currentPanel = useMemo(() => panels.find(({id}) => id === activeSidePanel), [
+ const currentPanel = useMemo(() => panels.find(({id}) => id === activeSidePanel) || null, [
activeSidePanel,
panels
]);
@@ -190,7 +190,6 @@ export default function SidePanelFactory(
onClickShareMap
]);
const customPanelProps = useMemo(() => getCustomPanelProps(props), [props]);
-
const PanelComponent = currentPanel?.component;
return (
@@ -268,11 +267,6 @@ export default function SidePanelFactory(
SidePanel.defaultProps = {
panels: fullPanels,
- sidebarComponents: SIDEBAR_COMPONENTS,
- uiState: {},
- visStateActions: {},
- mapStyleActions: {},
- uiStateActions: {},
availableProviders: {},
mapInfo: {}
};
diff --git a/src/components/src/side-panel/filter-manager.tsx b/src/components/src/side-panel/filter-manager.tsx
index d59eabb964..b311caab39 100644
--- a/src/components/src/side-panel/filter-manager.tsx
+++ b/src/components/src/side-panel/filter-manager.tsx
@@ -27,7 +27,7 @@ import {FILTER_VIEW_TYPES, PANEL_VIEW_TOGGLES} from '@kepler.gl/constants';
import {Filter} from '@kepler.gl/types';
import {Layer} from '@kepler.gl/layers';
import {isSideFilter} from '@kepler.gl/utils';
-import {VisStateActions, ActionHandler, UIStateActions} from '@kepler.gl/actions';
+import {VisStateActions, ActionHandler, UIStateActions, ActionHandlers} from '@kepler.gl/actions';
import {Datasets} from '@kepler.gl/table';
import PanelViewListToggleFactory from './panel-view-list-toggle';
@@ -36,6 +36,9 @@ import AddFilterButtonFactory from './filter-panel/add-filter-button';
import DatasetSectionFactory from './layer-panel/dataset-section';
import {PanelMeta} from './common/types';
+type VisStateActionHandlers = ActionHandlers;
+type UiStateActionHandlers = ActionHandlers;
+
type FilterManagerProps = {
filters: Filter[];
datasets: Datasets;
@@ -47,8 +50,8 @@ type FilterManagerProps = {
panelMetadata: PanelMeta;
panelListView: string;
- visStateActions: typeof VisStateActions;
- uiStateActions: typeof UIStateActions;
+ visStateActions: VisStateActionHandlers;
+ uiStateActions: UiStateActionHandlers;
};
type FilterListProps = {
@@ -60,7 +63,7 @@ type FilterListProps = {
idx: number;
}[];
isAnyFilterAnimating: boolean;
- visStateActions: typeof VisStateActions;
+ visStateActions: VisStateActionHandlers;
};
FilterManagerFactory.deps = [
diff --git a/src/components/src/side-panel/panel-header.tsx b/src/components/src/side-panel/panel-header.tsx
index c02709ef9d..775daf4504 100644
--- a/src/components/src/side-panel/panel-header.tsx
+++ b/src/components/src/side-panel/panel-header.tsx
@@ -54,18 +54,20 @@ type PanelActionProps = {
};
type PanelHeaderDropdownProps = {
- id: string;
items: ToolbarItemProps[];
show?: boolean;
onClose: () => void;
+ id: string;
+};
+
+type LogoComponentProps = {
+ appName: string;
+ appWebsite: string;
+ version: string;
};
type DropdownCallbacks = {
- logoComponent?: React.ComponentType<{
- appName: string;
- appWebsite: string;
- version: string;
- }>;
+ logoComponent?: React.FC | React.ComponentType;
onExportImage: () => void;
onExportData: () => void;
onExportConfig?: () => void;
@@ -89,7 +91,7 @@ type DropdownComponentProps = {
items?: Item[];
} & DropdownCallbacks;
-type PanelHeaderProps = {
+export type PanelHeaderProps = {
appName: string;
appWebsite: string;
version: string;
@@ -225,7 +227,9 @@ export const SaveExportDropdownFactory = (
) => {
const dropdownItemsSelector = getDropdownItemsSelector();
- const SaveExportDropdown: React.FC = props => (
+ const SaveExportDropdown: React.FC & {
+ defaultProps: {items: ToolbarItemProps[]};
+ } = props => (
;
};
diff --git a/src/components/src/types.ts b/src/components/src/types.ts
index c2a8f07c0e..bad0deba72 100644
--- a/src/components/src/types.ts
+++ b/src/components/src/types.ts
@@ -15,7 +15,7 @@ export type SidePanelItem = {
id: string;
label: string;
iconComponent: ComponentType;
- component?: ComponentType;
+ component: ComponentType;
};
export type SidePanelProps = {
@@ -29,18 +29,18 @@ export type SidePanelProps = {
layerClasses: LayerClassesType;
layerOrder: string[];
mapStyle: MapStyle;
- onSaveMap?: () => void;
+ mapInfo: {title?: string; description?: string};
width: number;
- mapInfo: {title: string; description: string};
datasets: Datasets;
uiStateActions: typeof UIStateActions;
visStateActions: typeof VisStateActions;
mapStateActions: typeof MapStateActions;
mapStyleActions: typeof MapStyleActions;
uiState: UiState;
- availableProviders: {hasShare: boolean; hasStorage: boolean};
+ availableProviders: {[k: string]: {hasShare?: boolean; hasStorage?: boolean}};
mapSaved?: string | null;
- panels: SidePanelItem[];
+ panels?: SidePanelItem[];
+ onSaveMap?: () => void;
version: string;
};
diff --git a/src/types/reducers.d.ts b/src/types/reducers.d.ts
index 371bf5ced7..3936e646bd 100644
--- a/src/types/reducers.d.ts
+++ b/src/types/reducers.d.ts
@@ -168,6 +168,7 @@ export type Feature = {
type: string;
coordinates: any;
};
+ type?: string;
};
export type FeatureSelectionContext = {
rightClick: boolean;
@@ -375,7 +376,7 @@ export type PanelListView = string;
export type UiState = {
readOnly: boolean;
- activeSidePanel: string;
+ activeSidePanel: string | null;
currentModal: string | null;
datasetKeyToRemove: string | null;
visibleDropdown: string | null;
diff --git a/src/utils/src/index.ts b/src/utils/src/index.ts
index e3fa519843..753f989699 100644
--- a/src/utils/src/index.ts
+++ b/src/utils/src/index.ts
@@ -68,7 +68,7 @@ export {
formatNumber,
getColumnFormatter
} from './data-utils';
-export {getTimelineFromAnimationConfig, getTimelineFromFilter} from './time';
+export {getTimelineFromAnimationConfig, getTimelineFromFilter, SAMPLE_TIMELINE, TIMELINE_MODES} from './time';
export {
datasetColorMaker,