diff --git a/package-lock.json b/package-lock.json index 4dc53c7..553c947 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,12 @@ "": { "dependencies": { "@dwayneparton/geojson-to-gpx": "^0.0.30", + "@types/sortablejs": "^1.15.5", "file-saver": "^2.0.5", "leaflet": "^1.9.4", "nanoid": "^5.0.3", - "preact": "^10.13.1" + "preact": "^10.13.1", + "sortablejs": "^1.15.0" }, "devDependencies": { "@babel/core": "^7.23.3", @@ -1185,6 +1187,11 @@ "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, + "node_modules/@types/sortablejs": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.5.tgz", + "integrity": "sha512-qqqbEFbB1EZt08I1Ok2BA3Sx0zlI8oizdIguMsajk4Yo/iHgXhCb3GM6N09JOJqT9xIMYM9LTFy8vit3RNY71Q==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", @@ -5236,6 +5243,11 @@ "node": ">=8" } }, + "node_modules/sortablejs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.0.tgz", + "integrity": "sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==" + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", diff --git a/package.json b/package.json index 4705063..e07a94b 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,12 @@ }, "dependencies": { "@dwayneparton/geojson-to-gpx": "^0.0.30", + "@types/sortablejs": "^1.15.5", "file-saver": "^2.0.5", "leaflet": "^1.9.4", "nanoid": "^5.0.3", - "preact": "^10.13.1" + "preact": "^10.13.1", + "sortablejs": "^1.15.0" }, "devDependencies": { "@babel/core": "^7.23.3", diff --git a/src/routePlanner/export.tsx b/src/routePlanner/export.tsx index ab0a86d..0e9de65 100644 --- a/src/routePlanner/export.tsx +++ b/src/routePlanner/export.tsx @@ -1,9 +1,9 @@ import { exportMarkersToGpx } from '../exporters/gpx'; -import { InternalProps } from './types'; +import { ExportProps } from './types'; import 'leaflet/dist/leaflet.css'; -export const ExportComponent = ({ markers }: InternalProps) => { +export const ExportComponent = ({ markers }: ExportProps) => { return ( <> + + + + + ); +}; diff --git a/src/routePlanner/markers.tsx b/src/routePlanner/markers.tsx index a087e05..31d64d1 100644 --- a/src/routePlanner/markers.tsx +++ b/src/routePlanner/markers.tsx @@ -1,17 +1,44 @@ -import { InternalProps } from './types'; +import { useEffect, useRef } from 'preact/hooks'; +import Sortable from 'sortablejs'; +import { MarkerComponent } from './marker'; +import { MarkersProps } from './types'; -import 'leaflet/dist/leaflet.css'; +export const MarkersComponent = ({ + markers, + onMarkersReorder, +}: MarkersProps) => { + const listContainerRef = useRef(null); + useEffect(() => { + if (!listContainerRef.current) { + return; + } + + const sortable = Sortable.create(listContainerRef.current, { + onEnd: (event) => { + if ( + event.oldIndex === undefined || + event.newIndex === undefined + ) { + return; + } + + onMarkersReorder({ + oldIndex: event.oldIndex, + newIndex: event.newIndex, + }); + }, + }); + return () => { + sortable.destroy(); + }; + }, [listContainerRef, markers, onMarkersReorder]); -export const MarkersComponent = ({ markers }: InternalProps) => { return ( <>

Markers

-
    +
      {markers.map((marker) => ( -
    1. - {`Waypoint ${marker.index}`} - -
    2. + ))}
    diff --git a/src/routePlanner/types.ts b/src/routePlanner/types.ts index 9fe80e4..883fe87 100644 --- a/src/routePlanner/types.ts +++ b/src/routePlanner/types.ts @@ -3,17 +3,30 @@ import { Coordinates, Waypoint } from '../shared/types'; export type Marker = Waypoint & { key: string; remove: () => void; - index: number; + moveUp?: () => void; + moveDown?: () => void; + label: string; }; -export type InternalProps = { +export type ReorderMarkersParams = { + oldIndex: number; + newIndex: number; +}; + +export type MarkersProps = { markers: Marker[]; + onMarkersReorder: (params: ReorderMarkersParams) => void; }; -export type InternalMapProps = InternalProps & { +export type MapProps = { + markers: Marker[]; onMapClick: (coordinates: Coordinates) => void; }; -export type MarkerListElementProps = { +export type ExportProps = { + markers: Marker[]; +}; + +export type MarkerProps = { marker: Marker; }; diff --git a/src/shared/collections.ts b/src/shared/collections.ts new file mode 100644 index 0000000..58388ea --- /dev/null +++ b/src/shared/collections.ts @@ -0,0 +1,31 @@ +export const reorderElements = ( + collection: T[], + oldIndex: number, + newIndex: number, +) => { + if (newIndex < 0) { + return [ + ...collection.slice(0, oldIndex), + ...collection.slice(oldIndex + 1, collection.length), + ]; + } + + if (oldIndex < newIndex) { + return [ + ...collection.slice(0, oldIndex), + ...collection.slice(oldIndex + 1, newIndex + 1), + ...collection.slice(oldIndex, oldIndex + 1), + ...collection.slice(newIndex + 1, collection.length), + ]; + } + if (oldIndex > newIndex) { + return [ + ...collection.slice(0, newIndex), + ...collection.slice(oldIndex, oldIndex + 1), + ...collection.slice(newIndex, oldIndex), + ...collection.slice(oldIndex + 1, collection.length), + ]; + } + + return collection; +};