Skip to content

Commit

Permalink
[chore] Export effects types/utils and incapsulate dnd logic into new…
Browse files Browse the repository at this point in the history
… hooks (#2384)

* [chore] Export effects types/utils and incapsulate dnd logic into new hooks

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>

* try to fix tests

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>

* adjust mocks

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>

---------

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
Co-authored-by: Giuseppe Macrì <macri.giuseppe@gmail.com>
  • Loading branch information
igorDykhta and macrigiuseppe committed Oct 20, 2023
1 parent 2500a27 commit 08492a8
Show file tree
Hide file tree
Showing 11 changed files with 648 additions and 160 deletions.
21 changes: 21 additions & 0 deletions effects.js
@@ -0,0 +1,21 @@
// Copyright (c) 2023 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

module.exports = require('./dist/effects');
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -150,7 +150,6 @@
"@testing-library/dom": "^9.0.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@testing-library/react-hooks": "^3.4.2",
"@testing-library/user-event": "^14.4.3",
"@types/d3-array": "^2.0.0",
"@types/d3-scale": "^3.2.2",
Expand Down
154 changes: 23 additions & 131 deletions src/components/src/dnd-context.tsx
@@ -1,32 +1,21 @@
import React, {useCallback, useMemo, useState, PropsWithChildren} from 'react';
import React, {useCallback, useMemo, PropsWithChildren} from 'react';
import styled from 'styled-components';
import {DndContext as DndKitContext, DragOverlay} from '@dnd-kit/core';
import Console from 'global/console';
import {useDispatch} from 'react-redux';

import {
DND_EMPTY_MODIFIERS,
DND_MODIFIERS,
DROPPABLE_MAP_CONTAINER_TYPE,
SORTABLE_LAYER_TYPE,
SORTABLE_SIDE_PANEL_TYPE,
SORTABLE_EFFECT_TYPE,
SORTABLE_EFFECT_PANEL_TYPE
SORTABLE_EFFECT_TYPE
} from '@kepler.gl/constants';
import {
layerConfigChange,
reorderLayer,
toggleLayerForMap,
updateEffect,
reorderEffect
} from '@kepler.gl/actions';
import LayerPanelHeaderFactory from './side-panel/layer-panel/layer-panel-header';
import {withState} from './injector';
import {visStateLens} from '@kepler.gl/reducers';
import {reorderLayerOrder} from '@kepler.gl/reducers';
import {VisState} from '@kepler.gl/schemas';
import {Layer} from '@kepler.gl/layers';
import {Effect} from '@kepler.gl/types';
import {reorderEffectOrder} from '@kepler.gl/utils';

import LayerPanelHeaderFactory from './side-panel/layer-panel/layer-panel-header';
import {withState} from './injector';
import useDndLayers from './hooks/use-dnd-layers';
import useDndEffects from './hooks/use-dnd-effects';

export type DndContextProps = PropsWithChildren<{
visState: VisState;
Expand Down Expand Up @@ -77,48 +66,18 @@ function DndContextFactory(
const DndContext = ({children, visState}: DndContextProps) => {
const {datasets, layerOrder, layers, effects, effectOrder, splitMaps} = visState;

const [activeLayer, setActiveLayer]: [
Layer | undefined,
(l: Layer | undefined) => void
] = useState();

const [activeEffect, setActiveEffect]: [
Effect | undefined,
(l: Effect | undefined) => void
] = useState();
const {activeLayer, onDragStart: onLayerDragStart, onDragEnd: onLayerDragEnd} = useDndLayers(
layers,
layerOrder
);
const {onDragStart: onEffectDragStart, onDragEnd: onEffectDragEnd} = useDndEffects(
effects,
effectOrder
);

const dispatch = useDispatch();
const isSplit = useMemo(() => splitMaps?.length > 1, [splitMaps]);
const dndModifiers = useMemo(() => (isSplit ? DND_EMPTY_MODIFIERS : DND_MODIFIERS), [isSplit]);

const onLayerDragStart = useCallback(
event => {
const {active} = event;
const newActiveLayer = layers.find(layer => layer.id === active.id);
if (newActiveLayer) {
setActiveLayer(newActiveLayer);
if (newActiveLayer?.config.isConfigActive) {
dispatch(layerConfigChange(newActiveLayer, {isConfigActive: false}));
}
}
},
[dispatch, layers]
);

const onEffectDragStart = useCallback(
event => {
const {active} = event;
const newActiveEffect = effects.find(effect => effect.id === active.id);
if (newActiveEffect) {
setActiveEffect(newActiveEffect);
if (newActiveEffect.config.isConfigActive) {
dispatch(updateEffect(newActiveEffect.id, {isConfigActive: false}));
}
}
},
[dispatch, effects]
);

const onDragStart = useCallback(
event => {
const activeType = event.active.data?.current?.type;
Expand All @@ -136,88 +95,21 @@ function DndContextFactory(
[onLayerDragStart, onEffectDragStart]
);

const onLayerDragEnd = useCallback(
const onDragEnd = useCallback(
event => {
const {active, over} = event;

const {id: activeLayerId} = active;
const overType = over?.data.current?.type;

if (!overType) {
setActiveLayer(undefined);
return;
}

switch (overType) {
// moving layers into maps
case DROPPABLE_MAP_CONTAINER_TYPE:
const mapIndex = over.data.current?.index ?? 0;
dispatch(toggleLayerForMap(mapIndex, activeLayerId));
break;
// swaping layers
const activeType = event.active.data?.current?.type;
switch (activeType) {
case SORTABLE_LAYER_TYPE:
const newLayerOrder = reorderLayerOrder(layerOrder, activeLayerId, over.id);
dispatch(reorderLayer(newLayerOrder));
break;
// moving layers within side panel
case SORTABLE_SIDE_PANEL_TYPE:
// move layer to the end of the list
dispatch(
reorderLayer(
reorderLayerOrder(layerOrder, activeLayerId, layerOrder[layerOrder.length - 1])
)
);
onLayerDragEnd(event);
break;
default:
break;
}

setActiveLayer(undefined);
},
[dispatch, layerOrder]
);

const onEffectDragEnd = useCallback(
event => {
const {active, over} = event;

const {id: activeEffectId} = active;
const overType = over?.data.current?.type;

if (!overType) {
setActiveEffect(undefined);
return;
}

switch (overType) {
// swaping effects
case SORTABLE_EFFECT_TYPE:
dispatch(reorderEffect(reorderEffectOrder(effectOrder, activeEffectId, over.id)));
break;
// moving effects within side panel
case SORTABLE_EFFECT_PANEL_TYPE:
// move effect to the end of the list
dispatch(
reorderEffect(
reorderEffectOrder(effectOrder, activeEffectId, effectOrder[effectOrder.length - 1])
)
);
onEffectDragEnd(event);
break;
default:
break;
Console.log(`activeType ${activeType} unknown`);
}

setActiveEffect(undefined);
},
[dispatch, effectOrder]
);

const onDragEnd = useCallback(
event => {
if (activeLayer) onLayerDragEnd(event);
if (activeEffect) onEffectDragEnd(event);
},
[activeLayer, activeEffect, onLayerDragEnd, onEffectDragEnd]
[onLayerDragEnd, onEffectDragEnd]
);

return (
Expand Down
74 changes: 74 additions & 0 deletions src/components/src/hooks/use-dnd-effects.tsx
@@ -0,0 +1,74 @@
import {useCallback, useState} from 'react';
import {useDispatch} from 'react-redux';
import {DragEndEvent, DragStartEvent} from '@dnd-kit/core';

import {reorderEffect, updateEffect} from '@kepler.gl/actions';
import {SORTABLE_EFFECT_PANEL_TYPE, SORTABLE_EFFECT_TYPE} from '@kepler.gl/constants';
import {reorderEffectOrder} from '@kepler.gl/utils';
import {Effect} from '@kepler.gl/types';

type DndEffectsHook = {
activeEffect: Effect | undefined;
onDragStart: (event: DragStartEvent) => void;
onDragEnd: (event: DragEndEvent) => void;
};

const useDndEffects: (effects: Effect[], effectOrder: string[]) => DndEffectsHook = (
effects,
effectOrder
) => {
const dispatch = useDispatch();
const [activeEffect, setActiveEffect]: [activeEffect: Effect | undefined, setActiveEffect: (effect: Effect | undefined) => void] = useState();
const onEffectDragStart = useCallback(
event => {
const {active} = event;
const newActiveEffect = effects.find(effect => effect.id === active.id);
if (newActiveEffect) {
setActiveEffect(newActiveEffect);
if (newActiveEffect.config.isConfigActive) {
dispatch(updateEffect(newActiveEffect.id, {isConfigActive: false}));
}
}
},
[dispatch, effects]
);

const onEffectDragEnd = useCallback(
event => {
const {active, over} = event;

const {id: activeEffectId} = active;
const overType = over?.data?.current?.type;

if (!overType) {
setActiveEffect(undefined);
return;
}

switch (overType) {
// swaping effects
case SORTABLE_EFFECT_TYPE:
dispatch(reorderEffect(reorderEffectOrder(effectOrder, activeEffectId, over.id)));
break;
// moving effects within side panel
case SORTABLE_EFFECT_PANEL_TYPE:
// move effect to the end of the list
dispatch(
reorderEffect(
reorderEffectOrder(effectOrder, activeEffectId, effectOrder[effectOrder.length - 1])
)
);
break;
default:
break;
}

setActiveEffect(undefined);
},
[dispatch, effectOrder]
);

return {activeEffect, onDragStart: onEffectDragStart, onDragEnd: onEffectDragEnd};
};

export default useDndEffects;
82 changes: 82 additions & 0 deletions src/components/src/hooks/use-dnd-layers.tsx
@@ -0,0 +1,82 @@
import {useCallback, useState} from 'react';
import {useDispatch} from 'react-redux';
import {DragEndEvent, DragStartEvent} from '@dnd-kit/core';
import {layerConfigChange, reorderLayer, toggleLayerForMap} from '@kepler.gl/actions';
import {
DROPPABLE_MAP_CONTAINER_TYPE,
SORTABLE_LAYER_TYPE,
SORTABLE_SIDE_PANEL_TYPE
} from '@kepler.gl/constants';
import {reorderLayerOrder} from '@kepler.gl/reducers';
import {Layer} from '@kepler.gl/layers';

type DndEffectsHook = {
activeLayer: Layer | undefined;
onDragStart: (event: DragStartEvent) => void;
onDragEnd: (event: DragEndEvent) => void;
};

const useDndLayers: (layers: Layer[], layerOrder: string[]) => DndEffectsHook = (layers, layerOrder) => {
const dispatch = useDispatch();

const [activeLayer, setActiveLayer]: [activeEffect: Layer | undefined, setActiveEffect: (effect: Layer | undefined) => void] = useState();

const onDragStart = useCallback(
event => {
const {active} = event;
const newActiveLayer = layers.find(layer => layer.id === active.id);
if (newActiveLayer) {
setActiveLayer(newActiveLayer);
if (newActiveLayer?.config.isConfigActive) {
dispatch(layerConfigChange(newActiveLayer, {isConfigActive: false}));
}
}
},
[dispatch, layers]
);

const onDragEnd = useCallback(
event => {
const {active, over} = event;

const {id: activeLayerId} = active;
const overType = over?.data?.current?.type;

if (!overType) {
setActiveLayer(undefined);
return;
}

switch (overType) {
// moving layers into maps
case DROPPABLE_MAP_CONTAINER_TYPE:
const mapIndex = over.data.current?.index ?? 0;
dispatch(toggleLayerForMap(mapIndex, activeLayerId));
break;
// swaping layers
case SORTABLE_LAYER_TYPE:
const newLayerOrder = reorderLayerOrder(layerOrder, activeLayerId, over.id);
dispatch(reorderLayer(newLayerOrder));
break;
// moving layers within side panel
case SORTABLE_SIDE_PANEL_TYPE:
// move layer to the end of the list
dispatch(
reorderLayer(
reorderLayerOrder(layerOrder, activeLayerId, layerOrder[layerOrder.length - 1])
)
);
break;
default:
break;
}

setActiveLayer(undefined);
},
[dispatch, layerOrder]
);

return {activeLayer, onDragStart, onDragEnd};
};

export default useDndLayers;
12 changes: 12 additions & 0 deletions src/components/src/hooks/use-feature-flags.tsx
@@ -0,0 +1,12 @@
import {useContext} from 'react';
import {FeatureFlagsContext} from '../context';

type FeatureFlags = {
// Define your feature flags here
[flagName: string]: boolean;
};

// @ts-expect-error FeatureFlagsContext is typed as object
const useFeatureFlags = () => useContext<FeatureFlags>(FeatureFlagsContext);

export default useFeatureFlags;

0 comments on commit 08492a8

Please sign in to comment.