Skip to content

Commit

Permalink
[feat] add LayerToggleVisibility for single splitMap
Browse files Browse the repository at this point in the history
Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
  • Loading branch information
ilyabo authored and igorDykhta committed Dec 25, 2024
1 parent 12b3231 commit 9a9c4fe
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 22 deletions.
1 change: 1 addition & 0 deletions src/actions/src/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const ActionTypes = {
LAYER_VISUAL_CHANNEL_CHANGE: `${ACTION_PREFIX}LAYER_VISUAL_CHANNEL_CHANGE`,
LAYER_TYPE_CHANGE: `${ACTION_PREFIX}LAYER_TYPE_CHANGE`,
LAYER_VIS_CONFIG_CHANGE: `${ACTION_PREFIX}LAYER_VIS_CONFIG_CHANGE`,
LAYER_TOGGLE_VISIBILITY: `${ACTION_PREFIX}LAYER_TOGGLE_VISIBILITY`,
LAYER_TEXT_LABEL_CHANGE: `${ACTION_PREFIX}LAYER_TEXT_LABEL_CHANGE`,
LAYER_HOVER: `${ACTION_PREFIX}LAYER_HOVER`,
LAYER_CLICK: `${ACTION_PREFIX}LAYER_CLICK`,
Expand Down
28 changes: 28 additions & 0 deletions src/actions/src/vis-state-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ export function layerConfigChange(
newConfig
};
}

export type LayerToggleVisibilityUpdaterAction = {
layerId: string;
isVisible: boolean;
splitMapId?: string;
};

/**
* Update layer visibility depends on splitMap single or dual
* @param layerId - layerId to be updated
* @param isVisible - whether this layer is visible globally
* @param splitMapId - id for this splitMap
* @returns action
* @public
*/
export function layerToggleVisibility(
layerId: string,
isVisible: boolean,
splitMapId?: string
): Merge<LayerToggleVisibilityUpdaterAction, {type: typeof ActionTypes.LAYER_TOGGLE_VISIBILITY}> {
return {
type: ActionTypes.LAYER_TOGGLE_VISIBILITY,
layerId,
isVisible,
splitMapId
};
}

export type LayerTextLabelChangeUpdaterAction = {
oldLayer: Layer;
idx: number | 'all';
Expand Down
4 changes: 2 additions & 2 deletions src/components/src/side-panel/filter-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import AddFilterButtonFactory from './filter-panel/add-filter-button';
import DatasetSectionFactory from './layer-panel/dataset-section';
import {PanelMeta} from './common/types';

type VisStateActionHandlers = ActionHandlers<typeof VisStateActions>;
type UiStateActionHandlers = ActionHandlers<typeof UIStateActions>;
export type VisStateActionHandlers = ActionHandlers<typeof VisStateActions>;
export type UiStateActionHandlers = ActionHandlers<typeof UIStateActions>;

export type FilterManagerProps = {
filters: Filter[];
Expand Down
11 changes: 8 additions & 3 deletions src/components/src/side-panel/layer-panel/layer-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import {CSS} from '@dnd-kit/utilities';
import LayerPanelFactory from './layer-panel';
import {findById} from '@kepler.gl/utils';
import {dataTestIds, SORTABLE_LAYER_TYPE, SORTABLE_SIDE_PANEL_TYPE} from '@kepler.gl/constants';
import {SplitMap} from '@kepler.gl/types';

export type LayerListProps = {
datasets: Datasets;
layers: Layer[];
layerOrder: string[];
layerClasses: LayerClassesType;
isSortable?: boolean;
splitMap?: SplitMap;
uiStateActions: typeof UIStateActions;
visStateActions: typeof VisStateActions;
mapStateActions: typeof MapStateActions;
Expand Down Expand Up @@ -120,7 +122,8 @@ function LayerListFactory(LayerPanel: ReturnType<typeof LayerPanelFactory>) {
visStateActions,
mapStateActions,
layerClasses,
isSortable = true
isSortable = true,
splitMap
} = props;
const {toggleModal: openModal} = uiStateActions;

Expand Down Expand Up @@ -157,6 +160,7 @@ function LayerListFactory(LayerPanel: ReturnType<typeof LayerPanelFactory>) {
layerColorUIChange: visStateActions.layerColorUIChange,
layerConfigChange: visStateActions.layerConfigChange,
layerVisualChannelConfigChange: visStateActions.layerVisualChannelConfigChange,
layerToggleVisibility: visStateActions.layerToggleVisibility,
layerTypeChange: visStateActions.layerTypeChange,
layerVisConfigChange: visStateActions.layerVisConfigChange,
layerTextLabelChange: visStateActions.layerTextLabelChange,
Expand All @@ -172,9 +176,10 @@ function LayerListFactory(LayerPanel: ReturnType<typeof LayerPanelFactory>) {
() => ({
datasets,
openModal,
layerTypeOptions
layerTypeOptions,
splitMap
}),
[datasets, openModal, layerTypeOptions]
[datasets, openModal, layerTypeOptions, splitMap]
);

return (
Expand Down
14 changes: 9 additions & 5 deletions src/components/src/side-panel/layer-panel/layer-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {ActionHandler, MapStateActions, VisStateActions, toggleModal} from '@kep
import {dataTestIds} from '@kepler.gl/constants';
import {Layer, LayerBaseConfig} from '@kepler.gl/layers';
import {Datasets} from '@kepler.gl/table';
import {ColorUI, LayerVisConfig, NestedPartial} from '@kepler.gl/types';
import {ColorUI, LayerVisConfig, NestedPartial, SplitMap} from '@kepler.gl/types';
import LayerConfiguratorFactory from './layer-configurator';
import LayerPanelHeaderFactory from './layer-panel-header';

Expand Down Expand Up @@ -47,6 +47,8 @@ type LayerPanelProps = {
zoomToLayer: ActionHandler<typeof MapStateActions.fitBounds>;
duplicateLayer: ActionHandler<typeof VisStateActions.duplicateLayer>;
listeners?: React.ElementType;
layerToggleVisibility: ActionHandler<typeof VisStateActions.layerToggleVisibility>;
splitMap?: SplitMap;
};

const PanelWrapper = styled.div<{active: boolean}>`
Expand Down Expand Up @@ -100,7 +102,7 @@ function LayerPanelFactory(
_toggleVisibility: MouseEventHandler = e => {
e.stopPropagation();
const isVisible = !this.props.layer.config.isVisible;
this.updateLayerConfig({isVisible});
this.props.layerToggleVisibility(this.props.layer.id, isVisible);
};

_resetIsValid: MouseEventHandler = e => {
Expand Down Expand Up @@ -136,10 +138,12 @@ function LayerPanelFactory(
};

render() {
const {layer, datasets, isDraggable, layerTypeOptions, listeners} = this.props;
const {layer, datasets, isDraggable, layerTypeOptions, listeners, splitMap} = this.props;
const {config, isValid} = layer;
const {isConfigActive} = config;
const allowDuplicate = typeof layer.isValidToSave === 'function' && layer.isValidToSave();
const allowDuplicate =
typeof layer.isValidToSave === 'function' && layer.isValidToSave() && isValid;
const layerVisInSplitMap = splitMap?.layers?.[layer.id];

return (
<PanelWrapper
Expand All @@ -153,7 +157,7 @@ function LayerPanelFactory(
<LayerPanelHeader
isConfigActive={isConfigActive}
layerId={layer.id}
isVisible={config.isVisible}
isVisible={layerVisInSplitMap ?? config.isVisible}
isValid={isValid}
label={config.label}
labelRCGColorValues={config.dataId ? datasets[config.dataId].color : null}
Expand Down
95 changes: 83 additions & 12 deletions src/reducers/src/vis-state-updaters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ import {
loadNextFile,
nextFileBatch,
processFileContent,
fitBounds as fitMapBounds
fitBounds as fitMapBounds,
toggleLayerForMap
} from '@kepler.gl/actions';

// Utils
Expand Down Expand Up @@ -484,6 +485,19 @@ export function applyLayerConfigUpdater(
return nextState;
}

function updatelayerVisibilty(state: VisState, newLayer: Layer, isVisible?: boolean): VisState {
let newState = updateStateOnLayerVisibilityChange(state, newLayer);
const filterIndex = filterSyncedWithTimeline(state);
if (isLayerAnimatable(newLayer) && filterIndex !== -1) {
// if layer is going to be visible we sync with filter otherwise we need to check whether other animatable layers exists and are visible
newState = syncTimeFilterWithLayerTimelineUpdater(newState, {
idx: filterIndex,
enable: isVisible ? isVisible : getAnimatableVisibleLayers(state.layers).length > 0
});
}
return newState;
}

/**
* Update layer base config: dataId, label, column, isVisible
* @memberof visStateUpdaters
Expand Down Expand Up @@ -526,17 +540,7 @@ export function layerConfigChangeUpdater(

let newState = state;
if ('isVisible' in action.newConfig) {
newState = updateStateOnLayerVisibilityChange(state, newLayer);
const filterIndex = filterSyncedWithTimeline(state);
if (isLayerAnimatable(newLayer) && filterIndex !== -1) {
// if layer is going to be visible we sync with filter otherwise we need to check whether other animatable layers exists and are visible
newState = syncTimeFilterWithLayerTimelineUpdater(newState, {
idx: filterIndex,
enable: action.newConfig.isVisible
? action.newConfig.isVisible
: getAnimatableVisibleLayers(state.layers).length > 0
});
}
newState = updatelayerVisibilty(newState, newLayer, action.newConfig.isVisible);
}

if ('columns' in action.newConfig && newLayer.config.animation.enabled) {
Expand Down Expand Up @@ -567,6 +571,73 @@ export function layerAnimationChangeUpdater<S extends VisState>(state: S, action
return updateStateWithLayerAndData(state, {layerData, layer, idx});
}

/**
* Update layerId, isVisible, splitMapId
* handles two cases:
* 1) toggle the visibility of local SplitMap layer (visState.splitMap.layers)
* 2) toggle the visibility of global layer (visState.layers)
* @memberof visStateUpdaters
* @returns nextState
*/
export function layerToggleVisibilityUpdater(
state: VisState,
action: VisStateActions.LayerToggleVisibilityUpdaterAction
): VisState {
const {layerId, isVisible, splitMapId} = action;
const layer = state.layers.find(d => d.id === layerId);

if (!layer) {
return state;
}

let newState = state;

if (splitMapId) {
// [case 1]: toggle local layer visibility for each SplitMap
const mapIndex = newState.splitMaps.findIndex(sm => sm.id === splitMapId);
if (isVisible) {
// 1) if the layer is invisible globally
// -> set global visibility to true
newState = layerConfigChangeUpdater(newState, layerConfigChange(layer, {isVisible: true}));

// -> set local visibility to true and the local visibilities of all other SplitMaps to false
return {
...newState,
splitMaps: newState.splitMaps.map(sm =>
sm.id !== splitMapId
? {
...sm,
layers: {
...sm.layers,
[layerId]: false
}
}
: {
...sm,
layers: {
...sm.layers,
[layerId]: true
}
}
)
};
}
// 2) else when the layer is visible globally
return toggleLayerForMapUpdater(newState, toggleLayerForMap(mapIndex, layerId));
} else {
// [case 2]: toggle global layer visibility
let newLayer = layer.updateLayerConfig({isVisible});
const idx = newState.layers.findIndex(l => l.id === layerId);

newState = updatelayerVisibilty(newState, newLayer, isVisible);
return updateStateWithLayerAndData(newState, {
layer: newLayer,
idx
});
}
}

/**
*
* @param state
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/src/vis-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const actionHandler = {

[ActionTypes.LAYER_VIS_CONFIG_CHANGE]: visStateUpdaters.layerVisConfigChangeUpdater,

[ActionTypes.LAYER_TOGGLE_VISIBILITY]: visStateUpdaters.layerToggleVisibilityUpdater,

[ActionTypes.LAYER_TEXT_LABEL_CHANGE]: visStateUpdaters.layerTextLabelChangeUpdater,

[ActionTypes.LAYER_VISUAL_CHANNEL_CHANGE]: visStateUpdaters.layerVisualChannelChangeUpdater,
Expand Down

0 comments on commit 9a9c4fe

Please sign in to comment.