mirror of
https://github.com/maputnik/editor.git
synced 2026-02-12 07:30:00 +00:00
Replace react-sortable-hoc with dnd-kit
This commit is contained in:
82
package-lock.json
generated
82
package-lock.json
generated
@@ -9,6 +9,8 @@
|
||||
"version": "2.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
||||
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
||||
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
||||
@@ -54,7 +56,6 @@
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-icon-base": "^2.1.2",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"slugify": "^1.6.6",
|
||||
"string-hash": "^1.1.3",
|
||||
@@ -680,6 +681,59 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
|
||||
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
|
||||
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.1.1",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/sortable": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
|
||||
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dnd-kit/core": "^6.3.0",
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
|
||||
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dual-bundle/import-meta-resolve": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
|
||||
@@ -7551,14 +7605,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -10967,21 +11013,6 @@
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-sortable-hoc": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz",
|
||||
"integrity": "sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"invariant": "^2.2.4",
|
||||
"prop-types": "^15.5.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.5.7",
|
||||
"react": "^16.3.0 || ^17.0.0",
|
||||
"react-dom": "^16.3.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reactcss": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
|
||||
@@ -12662,8 +12693,7 @@
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
|
||||
@@ -68,7 +68,8 @@
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-icon-base": "^2.1.2",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"slugify": "^1.6.6",
|
||||
"string-hash": "^1.1.3",
|
||||
|
||||
@@ -36,7 +36,6 @@ import LayerWatcher from '../libs/layerwatcher'
|
||||
import tokens from '../config/tokens.json'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import Debug from '../libs/debug'
|
||||
import { SortEnd } from 'react-sortable-hoc';
|
||||
import { MapOptions } from 'maplibre-gl';
|
||||
|
||||
// Buffer must be defined globally for @maplibre/maplibre-gl-style-spec validate() function to succeed.
|
||||
@@ -534,7 +533,7 @@ export default class App extends React.Component<any, AppState> {
|
||||
})
|
||||
}
|
||||
|
||||
onMoveLayer = (move: SortEnd) => {
|
||||
onMoveLayer = (move: {oldIndex: number; newIndex: number}) => {
|
||||
let { oldIndex, newIndex } = move;
|
||||
let layers = this.state.mapStyle.layers;
|
||||
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
||||
|
||||
@@ -6,7 +6,18 @@ import LayerListGroup from './LayerListGroup'
|
||||
import LayerListItem from './LayerListItem'
|
||||
import ModalAdd from './ModalAdd'
|
||||
|
||||
import {SortEndHandler, SortableContainer} from 'react-sortable-hoc';
|
||||
import {
|
||||
DndContext,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
closestCenter,
|
||||
DragEndEvent,
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
SortableContext,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import type {LayerSpecification} from 'maplibre-gl';
|
||||
import generateUniqueId from '../libs/document-uid';
|
||||
import { findClosestCommonPrefix, layerPrefix } from '../libs/layer';
|
||||
@@ -242,7 +253,6 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
||||
'maputnik-layer-list-item-group-last': idxInGroup == layers.length - 1 && layers.length > 1,
|
||||
'maputnik-layer-list-item--error': !!layerError
|
||||
})}
|
||||
index={idx}
|
||||
key={layer.key}
|
||||
id={layer.key}
|
||||
layerId={layer.id}
|
||||
@@ -319,20 +329,43 @@ class LayerListContainerInternal extends React.Component<LayerListContainerInter
|
||||
}
|
||||
|
||||
const LayerListContainer = withTranslation()(LayerListContainerInternal);
|
||||
const LayerListContainerSortable = SortableContainer((props: LayerListContainerProps) => <LayerListContainer {...props} />)
|
||||
|
||||
type LayerListProps = LayerListContainerProps & {
|
||||
onMoveLayer: SortEndHandler
|
||||
onMoveLayer: (move: {oldIndex: number; newIndex: number}) => void
|
||||
};
|
||||
|
||||
export default class LayerList extends React.Component<LayerListProps> {
|
||||
render() {
|
||||
return <LayerListContainerSortable
|
||||
{...this.props}
|
||||
helperClass='sortableHelper'
|
||||
onSortEnd={this.props.onMoveLayer.bind(this)}
|
||||
useDragHandle={true}
|
||||
shouldCancelStart={() => false}
|
||||
/>
|
||||
}
|
||||
export default function LayerList(props: LayerListProps) {
|
||||
const sensors = useSensors(useSensor(PointerSensor));
|
||||
|
||||
const buildItemIds = () => {
|
||||
const ids: string[] = [];
|
||||
const layerIdCount = new Map<string, number>();
|
||||
for (const layer of props.layers) {
|
||||
const count = layerIdCount.get(layer.id) ?? 0;
|
||||
ids.push(`layers-list-${layer.id}-${count}`);
|
||||
layerIdCount.set(layer.id, count + 1);
|
||||
}
|
||||
return ids;
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const {active, over} = event;
|
||||
if (!over) return;
|
||||
const ids = buildItemIds();
|
||||
const oldIndex = ids.indexOf(String(active.id));
|
||||
const newIndex = ids.indexOf(String(over.id));
|
||||
if (oldIndex !== newIndex) {
|
||||
props.onMoveLayer({oldIndex, newIndex});
|
||||
}
|
||||
};
|
||||
|
||||
const itemIds = buildItemIds();
|
||||
|
||||
return (
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
|
||||
<LayerListContainer {...props} />
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,16 +5,20 @@ import classnames from 'classnames'
|
||||
import {MdContentCopy, MdVisibility, MdVisibilityOff, MdDelete} from 'react-icons/md'
|
||||
|
||||
import IconLayer from './IconLayer'
|
||||
import {SortableElement, SortableHandle} from 'react-sortable-hoc'
|
||||
import {useSortable} from '@dnd-kit/sortable'
|
||||
import {CSS} from '@dnd-kit/utilities'
|
||||
|
||||
|
||||
type DraggableLabelProps = {
|
||||
layerId: string
|
||||
layerType: string
|
||||
dragAttributes?: React.HTMLAttributes<HTMLElement>
|
||||
dragListeners?: React.HTMLAttributes<HTMLElement>
|
||||
};
|
||||
|
||||
const DraggableLabel = SortableHandle((props: DraggableLabelProps) => {
|
||||
return <div className="maputnik-layer-list-item-handle">
|
||||
const DraggableLabel = (props: DraggableLabelProps) => {
|
||||
const {dragAttributes, dragListeners} = props;
|
||||
return <div className="maputnik-layer-list-item-handle" {...dragAttributes} {...dragListeners}>
|
||||
<IconLayer
|
||||
className="layer-handle__icon"
|
||||
type={props.layerType}
|
||||
@@ -23,7 +27,7 @@ const DraggableLabel = SortableHandle((props: DraggableLabelProps) => {
|
||||
{props.layerId}
|
||||
</button>
|
||||
</div>
|
||||
});
|
||||
};
|
||||
|
||||
type IconActionProps = {
|
||||
action: string
|
||||
@@ -80,6 +84,10 @@ type LayerListItemProps = {
|
||||
onLayerCopy?(...args: unknown[]): unknown
|
||||
onLayerDestroy?(...args: unknown[]): unknown
|
||||
onLayerVisibilityToggle?(...args: unknown[]): unknown
|
||||
dragAttributes?: React.HTMLAttributes<HTMLElement>
|
||||
dragListeners?: React.HTMLAttributes<HTMLElement>
|
||||
dragRef?: (node: HTMLElement | null) => void
|
||||
dragStyle?: React.CSSProperties
|
||||
};
|
||||
|
||||
class LayerListItem extends React.Component<LayerListItemProps> {
|
||||
@@ -105,8 +113,9 @@ class LayerListItem extends React.Component<LayerListItemProps> {
|
||||
const visibilityAction = this.props.visibility === 'visible' ? 'show' : 'hide';
|
||||
|
||||
return <li
|
||||
ref={this.props.dragRef}
|
||||
style={this.props.dragStyle}
|
||||
id={this.props.id}
|
||||
key={this.props.layerId}
|
||||
onClick={_e => this.props.onLayerSelect(this.props.layerIndex)}
|
||||
data-wd-key={"layer-list-item:"+this.props.layerId}
|
||||
className={classnames({
|
||||
@@ -114,7 +123,12 @@ class LayerListItem extends React.Component<LayerListItemProps> {
|
||||
"maputnik-layer-list-item-selected": this.props.isSelected,
|
||||
[this.props.className!]: true,
|
||||
})}>
|
||||
<DraggableLabel {...this.props} />
|
||||
<DraggableLabel
|
||||
layerId={this.props.layerId}
|
||||
layerType={this.props.layerType}
|
||||
dragAttributes={this.props.dragAttributes}
|
||||
dragListeners={this.props.dragListeners}
|
||||
/>
|
||||
<span style={{flexGrow: 1}} />
|
||||
<IconAction
|
||||
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
||||
@@ -139,6 +153,25 @@ class LayerListItem extends React.Component<LayerListItemProps> {
|
||||
}
|
||||
}
|
||||
|
||||
const LayerListItemSortable = SortableElement<LayerListItemProps>((props: LayerListItemProps) => <LayerListItem {...props} />);
|
||||
export default function LayerListItemSortable(props: LayerListItemProps) {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
} = useSortable({id: props.id!});
|
||||
|
||||
export default LayerListItemSortable;
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
} as React.CSSProperties;
|
||||
|
||||
return <LayerListItem
|
||||
{...props}
|
||||
dragAttributes={attributes}
|
||||
dragListeners={listeners}
|
||||
dragRef={setNodeRef}
|
||||
dragStyle={style}
|
||||
/>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user