mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-01 08:12:32 +00:00
duplicate any selection
This commit is contained in:
@@ -23,7 +23,7 @@ export abstract class GPXTreeElement<T extends GPXTreeElement<any>> {
|
|||||||
abstract toGeoJSON(): GeoJSON.Feature | GeoJSON.Feature[] | GeoJSON.FeatureCollection | GeoJSON.FeatureCollection[];
|
abstract toGeoJSON(): GeoJSON.Feature | GeoJSON.Feature[] | GeoJSON.FeatureCollection | GeoJSON.FeatureCollection[];
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
abstract replace(segment: number, start: number, end: number, points: TrackPoint[]);
|
abstract replaceTrackPoints(segment: number, start: number, end: number, points: TrackPoint[]);
|
||||||
abstract reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date);
|
abstract reverse(originalNextTimestamp?: Date, newPreviousTimestamp?: Date);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,14 +56,14 @@ abstract class GPXTreeNode<T extends GPXTreeElement<any>> extends GPXTreeElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
replace(segment: number, start: number, end: number, points: TrackPoint[]) {
|
replaceTrackPoints(segment: number, start: number, end: number, points: TrackPoint[]) {
|
||||||
return produce(this, (draft: Draft<GPXTreeNode<T>>) => {
|
return produce(this, (draft: Draft<GPXTreeNode<T>>) => {
|
||||||
let og = getOriginal(draft);
|
let og = getOriginal(draft);
|
||||||
let cumul = 0;
|
let cumul = 0;
|
||||||
for (let i = 0; i < og.children.length; i++) {
|
for (let i = 0; i < og.children.length; i++) {
|
||||||
let childSegments = og.children[i].getSegments();
|
let childSegments = og.children[i].getSegments();
|
||||||
if (segment < cumul + childSegments.length) {
|
if (segment < cumul + childSegments.length) {
|
||||||
draft.children[i] = draft.children[i].replace(segment - cumul, start, end, points);
|
draft.children[i] = draft.children[i].replaceTrackPoints(segment - cumul, start, end, points);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cumul += childSegments.length;
|
cumul += childSegments.length;
|
||||||
@@ -178,6 +178,34 @@ export class GPXFile extends GPXTreeNode<Track>{
|
|||||||
trk: this.trk.map((track) => track.toTrackType())
|
trk: this.trk.map((track) => track.toTrackType())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Producers
|
||||||
|
replaceTracks(start: number, end: number, tracks: Track[]) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
||||||
|
let trk = og.trk.slice();
|
||||||
|
trk.splice(start, end - start + 1, ...tracks);
|
||||||
|
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceTrackSegments(trackIndex: number, start: number, end: number, segments: TrackSegment[]) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
||||||
|
let trk = og.trk.slice();
|
||||||
|
trk[trackIndex] = trk[trackIndex].replaceTrackSegments(start, end, segments);
|
||||||
|
draft.trk = freeze(trk); // Pre-freeze the array, faster as well
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceWaypoints(start: number, end: number, waypoints: Waypoint[]) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
||||||
|
let wpt = og.wpt.slice();
|
||||||
|
wpt.splice(start, end - start + 1, ...waypoints);
|
||||||
|
draft.wpt = freeze(wpt); // Pre-freeze the array, faster as well
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A class that represents a Track in a GPX file
|
// A class that represents a Track in a GPX file
|
||||||
@@ -260,6 +288,16 @@ export class Track extends GPXTreeNode<TrackSegment> {
|
|||||||
extensions: this.extensions,
|
extensions: this.extensions,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Producers
|
||||||
|
replaceTrackSegments(start: number, end: number, segments: TrackSegment[]) {
|
||||||
|
return produce(this, (draft) => {
|
||||||
|
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
||||||
|
let trkseg = og.trkseg.slice();
|
||||||
|
trkseg.splice(start, end - start + 1, ...segments);
|
||||||
|
draft.trkseg = freeze(trkseg); // Pre-freeze the array, faster as well
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A class that represents a TrackSegment in a GPX file
|
// A class that represents a TrackSegment in a GPX file
|
||||||
@@ -410,7 +448,7 @@ export class TrackSegment extends GPXTreeLeaf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Producers
|
// Producers
|
||||||
replace(segment: number, start: number, end: number, points: TrackPoint[]) {
|
replaceTrackPoints(segment: number, start: number, end: number, points: TrackPoint[]) {
|
||||||
return produce(this, (draft) => {
|
return produce(this, (draft) => {
|
||||||
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
let og = getOriginal(draft); // Read as much as possible from the original object because it is faster
|
||||||
let trkpt = og.trkpt.slice();
|
let trkpt = og.trkpt.slice();
|
||||||
|
@@ -189,12 +189,16 @@ export class ListTrackItem extends ListItem {
|
|||||||
return this.fileId;
|
return this.fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
extend(id: number): ListSegmentItem {
|
getTrackIndex(): number {
|
||||||
return new ListSegmentItem(this.fileId, this.trackIndex, id);
|
return this.trackIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(id: number): ListTrackSegmentItem {
|
||||||
|
return new ListTrackSegmentItem(this.fileId, this.trackIndex, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ListSegmentItem extends ListItem {
|
export class ListTrackSegmentItem extends ListItem {
|
||||||
fileId: string;
|
fileId: string;
|
||||||
trackIndex: number;
|
trackIndex: number;
|
||||||
segmentIndex: number;
|
segmentIndex: number;
|
||||||
@@ -227,7 +231,15 @@ export class ListSegmentItem extends ListItem {
|
|||||||
return this.fileId;
|
return this.fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
extend(): ListSegmentItem {
|
getTrackIndex(): number {
|
||||||
|
return this.trackIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSegmentIndex(): number {
|
||||||
|
return this.segmentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend(): ListTrackSegmentItem {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,6 +307,10 @@ export class ListWaypointItem extends ListItem {
|
|||||||
return this.fileId;
|
return this.fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWaypointIndex(): number {
|
||||||
|
return this.waypointIndex;
|
||||||
|
}
|
||||||
|
|
||||||
extend(): ListWaypointItem {
|
extend(): ListWaypointItem {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@@ -5,9 +5,10 @@
|
|||||||
import { dbUtils } from '$lib/db';
|
import { dbUtils } from '$lib/db';
|
||||||
import { Copy, Trash2 } from 'lucide-svelte';
|
import { Copy, Trash2 } from 'lucide-svelte';
|
||||||
import { type ListItem } from './FileList';
|
import { type ListItem } from './FileList';
|
||||||
import { selection } from './Selection';
|
import { selectItem, selection } from './Selection';
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { getContext } from 'svelte';
|
import { getContext } from 'svelte';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
export let item: ListItem;
|
export let item: ListItem;
|
||||||
export let label: string | undefined;
|
export let label: string | undefined;
|
||||||
@@ -17,7 +18,13 @@
|
|||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<ContextMenu.Root>
|
<ContextMenu.Root
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (open && !get(selection).has(item)) {
|
||||||
|
selectItem(item);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ContextMenu.Trigger class="grow truncate">
|
<ContextMenu.Trigger class="grow truncate">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -45,12 +52,14 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</ContextMenu.Trigger>
|
</ContextMenu.Trigger>
|
||||||
<ContextMenu.Content>
|
<ContextMenu.Content>
|
||||||
<ContextMenu.Item on:click={dbUtils.duplicateSelection}>
|
{#if item.level !== 'waypoints'}
|
||||||
<Copy size="16" class="mr-1" />
|
<ContextMenu.Item on:click={dbUtils.duplicateSelection}>
|
||||||
{$_('menu.duplicate')}
|
<Copy size="16" class="mr-1" />
|
||||||
<Shortcut key="D" ctrl={true} /></ContextMenu.Item
|
{$_('menu.duplicate')}
|
||||||
>
|
<Shortcut key="D" ctrl={true} /></ContextMenu.Item
|
||||||
<ContextMenu.Separator />
|
>
|
||||||
|
<ContextMenu.Separator />
|
||||||
|
{/if}
|
||||||
<ContextMenu.Item on:click={dbUtils.deleteSelection}
|
<ContextMenu.Item on:click={dbUtils.deleteSelection}
|
||||||
><Trash2 size="16" class="mr-1" />
|
><Trash2 size="16" class="mr-1" />
|
||||||
{$_('menu.delete')}
|
{$_('menu.delete')}
|
||||||
|
@@ -1,17 +1,21 @@
|
|||||||
import { get, writable } from "svelte/store";
|
import { get, writable } from "svelte/store";
|
||||||
import { ListFileItem, ListRootItem, SelectionTreeType } from "./FileList";
|
import { ListFileItem, ListItem, ListRootItem, SelectionTreeType } from "./FileList";
|
||||||
import { fileObservers } from "$lib/db";
|
import { fileObservers } from "$lib/db";
|
||||||
|
|
||||||
export const selection = writable<SelectionTreeType>(new SelectionTreeType(new ListRootItem()));
|
export const selection = writable<SelectionTreeType>(new SelectionTreeType(new ListRootItem()));
|
||||||
|
|
||||||
export function select(fileId: string) {
|
export function selectItem(item: ListItem) {
|
||||||
selection.update(($selection) => {
|
selection.update(($selection) => {
|
||||||
$selection.clear();
|
$selection.clear();
|
||||||
$selection.set(new ListFileItem(fileId), true);
|
$selection.set(item, true);
|
||||||
return $selection;
|
return $selection;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectFile(fileId: string) {
|
||||||
|
selectItem(new ListFileItem(fileId));
|
||||||
|
}
|
||||||
|
|
||||||
export function addSelect(fileId: string) {
|
export function addSelect(fileId: string) {
|
||||||
selection.update(($selection) => {
|
selection.update(($selection) => {
|
||||||
$selection.toggle(new ListFileItem(fileId));
|
$selection.toggle(new ListFileItem(fileId));
|
||||||
|
@@ -3,8 +3,8 @@ import { settings, type GPXFileWithStatistics } from "$lib/db";
|
|||||||
import { get, type Readable } from "svelte/store";
|
import { get, type Readable } from "svelte/store";
|
||||||
import mapboxgl from "mapbox-gl";
|
import mapboxgl from "mapbox-gl";
|
||||||
import { currentWaypoint, waypointPopup } from "./WaypointPopup";
|
import { currentWaypoint, waypointPopup } from "./WaypointPopup";
|
||||||
import { addSelect, select, selection } from "$lib/components/file-list/Selection";
|
import { addSelect, selectFile, selection } from "$lib/components/file-list/Selection";
|
||||||
import { ListSegmentItem, type ListItem, ListFileItem, ListTrackItem } from "$lib/components/file-list/FileList";
|
import { ListTrackSegmentItem, type ListItem, ListFileItem, ListTrackItem } from "$lib/components/file-list/FileList";
|
||||||
|
|
||||||
let defaultWeight = 5;
|
let defaultWeight = 5;
|
||||||
let defaultOpacity = 1;
|
let defaultOpacity = 1;
|
||||||
@@ -213,7 +213,7 @@ export class GPXLayer {
|
|||||||
if (e.originalEvent.shiftKey) {
|
if (e.originalEvent.shiftKey) {
|
||||||
addSelect(this.fileId);
|
addSelect(this.fileId);
|
||||||
} else {
|
} else {
|
||||||
select(this.fileId);
|
selectFile(this.fileId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ export class GPXLayer {
|
|||||||
if (!feature.properties.opacity) {
|
if (!feature.properties.opacity) {
|
||||||
feature.properties.opacity = defaultOpacity;
|
feature.properties.opacity = defaultOpacity;
|
||||||
}
|
}
|
||||||
if (get(selection).has(new ListFileItem(this.fileId)) || get(selection).has(new ListTrackItem(this.fileId, trackIndex)) || get(selection).has(new ListSegmentItem(this.fileId, trackIndex, segmentIndex))) {
|
if (get(selection).has(new ListFileItem(this.fileId)) || get(selection).has(new ListTrackItem(this.fileId, trackIndex)) || get(selection).has(new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex))) {
|
||||||
feature.properties.weight = feature.properties.weight + 2;
|
feature.properties.weight = feature.properties.weight + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -281,13 +281,13 @@ export class RoutingControls {
|
|||||||
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
|
||||||
|
|
||||||
if (previousAnchor === null && nextAnchor === null) { // Only one point, remove it
|
if (previousAnchor === null && nextAnchor === null) { // Only one point, remove it
|
||||||
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchor.segmentIndex, 0, 0, []));
|
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(anchor.segmentIndex, 0, 0, []));
|
||||||
} else if (previousAnchor === null) { // First point, remove trackpoints until nextAnchor
|
} else if (previousAnchor === null) { // First point, remove trackpoints until nextAnchor
|
||||||
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchor.segmentIndex, 0, nextAnchor.point._data.index - 1, []));
|
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(anchor.segmentIndex, 0, nextAnchor.point._data.index - 1, []));
|
||||||
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
|
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
|
||||||
dbUtils.applyToFile(this.fileId, (file) => {
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
let segment = file.getSegments()[anchor.segmentIndex];
|
let segment = file.getSegments()[anchor.segmentIndex];
|
||||||
return file.replace(anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
|
return file.replaceTrackPoints(anchor.segmentIndex, previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
|
||||||
});
|
});
|
||||||
} else { // Route between previousAnchor and nextAnchor
|
} else { // Route between previousAnchor and nextAnchor
|
||||||
this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]);
|
this.routeBetweenAnchors([previousAnchor, nextAnchor], [previousAnchor.point.getCoordinates(), nextAnchor.point.getCoordinates()]);
|
||||||
@@ -312,7 +312,7 @@ export class RoutingControls {
|
|||||||
|
|
||||||
if (!lastAnchor) {
|
if (!lastAnchor) {
|
||||||
// TODO, create segment if it does not exist
|
// TODO, create segment if it does not exist
|
||||||
dbUtils.applyToFile(this.fileId, (file) => file.replace(0, 0, 0, [newPoint]));
|
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(0, 0, 0, [newPoint]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +362,7 @@ export class RoutingControls {
|
|||||||
let segment = original(file).getSegments()[segments.length - 1];
|
let segment = original(file).getSegments()[segments.length - 1];
|
||||||
let newSegment = segment.clone();
|
let newSegment = segment.clone();
|
||||||
newSegment = newSegment.reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
|
newSegment = newSegment.reverse(segment.getEndTimestamp(), segment.getEndTimestamp());
|
||||||
return file.replace(segments.length - 1, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
|
return file.replaceTrackPoints(segments.length - 1, segment.trkpt.length, segment.trkpt.length, newSegment.trkpt.map((point) => point));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +391,7 @@ export class RoutingControls {
|
|||||||
let segment = anchors[0].segment;
|
let segment = anchors[0].segment;
|
||||||
|
|
||||||
if (anchors.length === 1) { // Only one anchor, update the point in the segment
|
if (anchors.length === 1) { // Only one anchor, update the point in the segment
|
||||||
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchors[0].segmentIndex, 0, 0, [new TrackPoint({
|
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(anchors[0].segmentIndex, 0, 0, [new TrackPoint({
|
||||||
attributes: targetCoordinates[0],
|
attributes: targetCoordinates[0],
|
||||||
})]));
|
})]));
|
||||||
return true;
|
return true;
|
||||||
@@ -416,7 +416,7 @@ export class RoutingControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (anchors[0].point._data.index === 0) { // First anchor is the first point of the segment
|
if (anchors[0].point._data.index === 0) { // First anchor is the first point of the segment
|
||||||
anchors[0].point = response[0]; // Replace the first anchor
|
anchors[0].point = response[0]; // replaceTrackPoints the first anchor
|
||||||
anchors[0].point._data.index = 0;
|
anchors[0].point._data.index = 0;
|
||||||
} else {
|
} else {
|
||||||
anchors[0].point = anchors[0].point.clone(); // Clone the anchor to assign new properties
|
anchors[0].point = anchors[0].point.clone(); // Clone the anchor to assign new properties
|
||||||
@@ -424,7 +424,7 @@ export class RoutingControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (anchors[anchors.length - 1].point._data.index === segment.trkpt.length - 1) { // Last anchor is the last point of the segment
|
if (anchors[anchors.length - 1].point._data.index === segment.trkpt.length - 1) { // Last anchor is the last point of the segment
|
||||||
anchors[anchors.length - 1].point = response[response.length - 1]; // Replace the last anchor
|
anchors[anchors.length - 1].point = response[response.length - 1]; // replaceTrackPoints the last anchor
|
||||||
anchors[anchors.length - 1].point._data.index = segment.trkpt.length - 1;
|
anchors[anchors.length - 1].point._data.index = segment.trkpt.length - 1;
|
||||||
} else {
|
} else {
|
||||||
anchors[anchors.length - 1].point = anchors[anchors.length - 1].point.clone(); // Clone the anchor to assign new properties
|
anchors[anchors.length - 1].point = anchors[anchors.length - 1].point.clone(); // Clone the anchor to assign new properties
|
||||||
@@ -451,7 +451,7 @@ export class RoutingControls {
|
|||||||
anchor.point._data.zoom = 0; // Make these anchors permanent
|
anchor.point._data.zoom = 0; // Make these anchors permanent
|
||||||
});
|
});
|
||||||
|
|
||||||
dbUtils.applyToFile(this.fileId, (file) => file.replace(anchors[0].segmentIndex, anchors[0].point._data.index, anchors[anchors.length - 1].point._data.index, response));
|
dbUtils.applyToFile(this.fileId, (file) => file.replaceTrackPoints(anchors[0].segmentIndex, anchors[0].point._data.index, anchors[anchors.length - 1].point._data.index, response));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ import { initTargetMapBounds, updateTargetMapBounds } from './stores';
|
|||||||
import { mode } from 'mode-watcher';
|
import { mode } from 'mode-watcher';
|
||||||
import { defaultBasemap, defaultBasemapTree, defaultOverlayTree, defaultOverlays } from './assets/layers';
|
import { defaultBasemap, defaultBasemapTree, defaultOverlayTree, defaultOverlays } from './assets/layers';
|
||||||
import { selection } from '$lib/components/file-list/Selection';
|
import { selection } from '$lib/components/file-list/Selection';
|
||||||
import { ListFileItem, ListItem, type ListLevel } from '$lib/components/file-list/FileList';
|
import { ListFileItem, ListItem, ListTrackItem, type ListLevel, ListTrackSegmentItem, ListWaypointItem } from '$lib/components/file-list/FileList';
|
||||||
import { updateAnchorPoints } from '$lib/components/toolbar/tools/routing/Simplify';
|
import { updateAnchorPoints } from '$lib/components/toolbar/tools/routing/Simplify';
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
@@ -358,16 +358,62 @@ export const dbUtils = {
|
|||||||
},
|
},
|
||||||
duplicateSelection: () => {
|
duplicateSelection: () => {
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
// TODO
|
|
||||||
let ids = getFileIds(get(settings.fileOrder).length);
|
let ids = getFileIds(get(settings.fileOrder).length);
|
||||||
get(settings.fileOrder).forEach((fileId, index) => {
|
get(settings.fileOrder).forEach((fileId, index) => {
|
||||||
if (get(selection).has(new ListFileItem(fileId))) {
|
let file = original(draft)?.get(fileId);
|
||||||
let file = original(draft)?.get(fileId);
|
if (file) {
|
||||||
if (file) {
|
let level: ListLevel | undefined = undefined;
|
||||||
let clone = file.clone();
|
let items: ListItem[] = [];
|
||||||
clone._data.id = ids[index];
|
get(selection).forEach((item) => {
|
||||||
draft.set(clone._data.id, freeze(clone));
|
if (item.getFileId() === fileId) {
|
||||||
|
if (item instanceof ListFileItem) {
|
||||||
|
level = 'file';
|
||||||
|
} else if (item instanceof ListTrackItem) {
|
||||||
|
level = 'track';
|
||||||
|
items.push(item);
|
||||||
|
} else if (item instanceof ListTrackSegmentItem) {
|
||||||
|
level = 'segment';
|
||||||
|
items.push(item);
|
||||||
|
} else if (item instanceof ListWaypointItem) {
|
||||||
|
level = 'waypoint';
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
items.sort((a, b) => { // Process the items in reverse order to avoid index conflicts
|
||||||
|
if (a instanceof ListTrackItem && b instanceof ListTrackItem) {
|
||||||
|
return b.getTrackIndex() - a.getTrackIndex();
|
||||||
|
} else if (a instanceof ListTrackSegmentItem && b instanceof ListTrackSegmentItem) {
|
||||||
|
return b.getSegmentIndex() - a.getSegmentIndex();
|
||||||
|
} else if (a instanceof ListWaypointItem && b instanceof ListWaypointItem) {
|
||||||
|
return b.getWaypointIndex() - a.getWaypointIndex();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
let newFile = file;
|
||||||
|
if (level === 'file') {
|
||||||
|
newFile = file.clone();
|
||||||
|
newFile._data.id = ids[index];
|
||||||
|
} else if (level === 'track') {
|
||||||
|
for (let item of items) {
|
||||||
|
let trackIndex = (item as ListTrackItem).getTrackIndex();
|
||||||
|
newFile = newFile.replaceTracks(trackIndex + 1, trackIndex, [file.trk[trackIndex].clone()]);
|
||||||
|
}
|
||||||
|
} else if (level === 'segment') {
|
||||||
|
for (let item of items) {
|
||||||
|
let trackIndex = (item as ListTrackSegmentItem).getTrackIndex();
|
||||||
|
let segmentIndex = (item as ListTrackSegmentItem).getSegmentIndex();
|
||||||
|
newFile = newFile.replaceTrackSegments(trackIndex, segmentIndex + 1, segmentIndex, [file.trk[trackIndex].trkseg[segmentIndex].clone()]);
|
||||||
|
}
|
||||||
|
} else if (level === 'waypoint') {
|
||||||
|
for (let item of items) {
|
||||||
|
let waypointIndex = (item as ListWaypointItem).getWaypointIndex();
|
||||||
|
newFile = newFile.replaceWaypoints(waypointIndex + 1, waypointIndex, [file.wpt[waypointIndex].clone()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
draft.set(fileId, freeze(newFile));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user