Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] add LayerToggleVisibility for single splitMap #2863

Merged
merged 1 commit into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading