Files
gpx.studio/website/src/lib/components/file-list/FileList.ts

373 lines
11 KiB
TypeScript
Raw Normal View History

2024-06-11 19:08:46 +02:00
import { dbUtils } from "$lib/db";
2024-06-05 21:08:01 +02:00
import { castDraft, freeze } from "immer";
2024-06-04 16:11:47 +02:00
import { Track, TrackSegment, Waypoint } from "gpx";
import { selection } from "./Selection";
2024-06-05 21:08:01 +02:00
import { newGPXFile } from "$lib/stores";
2024-05-22 16:05:31 +02:00
2024-05-24 13:16:41 +02:00
export enum ListLevel {
ROOT,
FILE,
TRACK,
SEGMENT,
WAYPOINTS,
WAYPOINT
}
2024-05-22 16:05:31 +02:00
export abstract class ListItem {
level: ListLevel;
constructor(level: ListLevel) {
this.level = level;
}
abstract getId(): string | number;
2024-06-11 19:08:46 +02:00
abstract getFullId(): string;
2024-05-22 16:05:31 +02:00
abstract getIdAtLevel(level: ListLevel): string | number | undefined;
abstract getFileId(): string;
abstract extend(id: string | number): ListItem;
}
export class ListRootItem extends ListItem {
constructor() {
2024-05-24 13:16:41 +02:00
super(ListLevel.ROOT);
2024-05-22 16:05:31 +02:00
}
getId(): string {
return 'root';
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return 'root';
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
return undefined;
}
getFileId(): string {
return '';
}
extend(id: string): ListFileItem {
return new ListFileItem(id);
}
}
export class ListFileItem extends ListItem {
fileId: string;
constructor(fileId: string) {
2024-05-24 13:16:41 +02:00
super(ListLevel.FILE);
2024-05-22 16:05:31 +02:00
this.fileId = fileId;
}
getId(): string {
return this.fileId;
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return this.fileId;
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
switch (level) {
2024-05-24 13:16:41 +02:00
case ListLevel.ROOT:
2024-05-22 16:05:31 +02:00
return this.fileId;
default:
return undefined;
}
}
getFileId(): string {
return this.fileId;
}
extend(id: number | 'waypoints'): ListTrackItem | ListWaypointsItem {
if (id === 'waypoints') {
return new ListWaypointsItem(this.fileId);
} else {
return new ListTrackItem(this.fileId, id);
}
}
}
export class ListTrackItem extends ListItem {
fileId: string;
trackIndex: number;
constructor(fileId: string, trackIndex: number) {
2024-05-24 13:16:41 +02:00
super(ListLevel.TRACK);
2024-05-22 16:05:31 +02:00
this.fileId = fileId;
this.trackIndex = trackIndex;
}
getId(): number {
return this.trackIndex;
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return `${this.fileId}-track-${this.trackIndex}`;
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
switch (level) {
2024-05-24 13:16:41 +02:00
case ListLevel.ROOT:
2024-05-22 16:05:31 +02:00
return this.fileId;
2024-05-24 13:16:41 +02:00
case ListLevel.FILE:
2024-05-22 16:05:31 +02:00
return this.trackIndex;
default:
return undefined;
}
}
getFileId(): string {
return this.fileId;
}
2024-05-23 14:44:07 +02:00
getTrackIndex(): number {
return this.trackIndex;
}
extend(id: number): ListTrackSegmentItem {
return new ListTrackSegmentItem(this.fileId, this.trackIndex, id);
2024-05-22 16:05:31 +02:00
}
}
2024-05-23 14:44:07 +02:00
export class ListTrackSegmentItem extends ListItem {
2024-05-22 16:05:31 +02:00
fileId: string;
trackIndex: number;
segmentIndex: number;
constructor(fileId: string, trackIndex: number, segmentIndex: number) {
2024-05-24 13:16:41 +02:00
super(ListLevel.SEGMENT);
2024-05-22 16:05:31 +02:00
this.fileId = fileId;
this.trackIndex = trackIndex;
this.segmentIndex = segmentIndex;
}
getId(): number {
return this.segmentIndex;
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return `${this.fileId}-track-${this.trackIndex}--${this.segmentIndex}`;
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
switch (level) {
2024-05-24 13:16:41 +02:00
case ListLevel.ROOT:
2024-05-22 16:05:31 +02:00
return this.fileId;
2024-05-24 13:16:41 +02:00
case ListLevel.FILE:
2024-05-22 16:05:31 +02:00
return this.trackIndex;
2024-05-24 13:16:41 +02:00
case ListLevel.TRACK:
2024-05-22 16:05:31 +02:00
return this.segmentIndex;
default:
return undefined;
}
}
getFileId(): string {
return this.fileId;
}
2024-05-23 14:44:07 +02:00
getTrackIndex(): number {
return this.trackIndex;
}
getSegmentIndex(): number {
return this.segmentIndex;
}
extend(): ListTrackSegmentItem {
2024-05-22 16:05:31 +02:00
return this;
}
}
export class ListWaypointsItem extends ListItem {
fileId: string;
constructor(fileId: string) {
2024-05-24 13:16:41 +02:00
super(ListLevel.WAYPOINTS);
2024-05-22 16:05:31 +02:00
this.fileId = fileId;
}
getId(): string {
return 'waypoints';
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return `${this.fileId}-waypoints`;
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
switch (level) {
2024-05-24 13:16:41 +02:00
case ListLevel.ROOT:
2024-05-22 16:05:31 +02:00
return this.fileId;
2024-05-24 13:16:41 +02:00
case ListLevel.FILE:
2024-05-22 16:05:31 +02:00
return 'waypoints';
default:
return undefined;
}
}
getFileId(): string {
return this.fileId;
}
extend(id: number): ListWaypointItem {
return new ListWaypointItem(this.fileId, id);
}
}
export class ListWaypointItem extends ListItem {
fileId: string;
waypointIndex: number;
constructor(fileId: string, waypointIndex: number) {
2024-05-24 13:16:41 +02:00
super(ListLevel.WAYPOINT);
2024-05-22 16:05:31 +02:00
this.fileId = fileId;
this.waypointIndex = waypointIndex;
}
getId(): number {
return this.waypointIndex;
}
2024-06-11 19:08:46 +02:00
getFullId(): string {
return `${this.fileId}-waypoint-${this.waypointIndex}`;
}
2024-05-22 16:05:31 +02:00
getIdAtLevel(level: ListLevel): string | number | undefined {
switch (level) {
2024-05-24 13:16:41 +02:00
case ListLevel.ROOT:
2024-05-22 16:05:31 +02:00
return this.fileId;
2024-05-24 13:16:41 +02:00
case ListLevel.FILE:
2024-05-22 16:05:31 +02:00
return 'waypoints';
2024-05-24 13:16:41 +02:00
case ListLevel.WAYPOINTS:
2024-05-22 16:05:31 +02:00
return this.waypointIndex;
default:
return undefined;
}
}
getFileId(): string {
return this.fileId;
}
2024-05-23 14:44:07 +02:00
getWaypointIndex(): number {
return this.waypointIndex;
}
2024-05-22 16:05:31 +02:00
extend(): ListWaypointItem {
return this;
}
}
2024-06-04 16:11:47 +02:00
export function sortItems(items: ListItem[], reverse: boolean = false) {
items.sort((a, b) => {
if (a instanceof ListTrackItem && b instanceof ListTrackItem) {
return a.getTrackIndex() - b.getTrackIndex();
} else if (a instanceof ListTrackSegmentItem && b instanceof ListTrackSegmentItem) {
return a.getSegmentIndex() - b.getSegmentIndex();
} else if (a instanceof ListWaypointItem && b instanceof ListWaypointItem) {
return a.getWaypointIndex() - b.getWaypointIndex();
}
return a.level - b.level;
});
if (reverse) {
items.reverse();
}
}
export function moveItems(fromParent: ListItem, toParent: ListItem, fromItems: ListItem[], toItems: ListItem[]) {
sortItems(fromItems, true);
sortItems(toItems, false);
2024-06-05 21:08:01 +02:00
dbUtils.applyEachToFilesAndGlobal([fromParent.getFileId(), toParent.getFileId()], [
2024-06-04 16:11:47 +02:00
(file, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
let newFile = file;
fromItems.forEach((item) => {
if (item instanceof ListTrackItem) {
let [result, removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex(), []);
newFile = castDraft(result);
context.push(...removed);
} else if (item instanceof ListTrackSegmentItem) {
let [result, removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex(), []);
newFile = castDraft(result);
context.push(...removed);
} else if (item instanceof ListWaypointsItem) {
let [result, removed] = newFile.replaceWaypoints(0, newFile.wpt.length - 1, []);
newFile = castDraft(result);
context.push(removed);
} else if (item instanceof ListWaypointItem) {
let [result, removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex(), []);
newFile = castDraft(result);
context.push(...removed);
}
});
context.reverse();
return newFile;
},
(file, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
let newFile = file;
toItems.forEach((item, i) => {
2024-06-05 21:08:01 +02:00
if (item instanceof ListTrackItem) {
if (context[i] instanceof Track) {
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [context[i]]);
newFile = castDraft(result);
} else if (context[i] instanceof TrackSegment) {
let [result, _removed] = newFile.replaceTracks(item.getTrackIndex(), item.getTrackIndex() - 1, [new Track({
trkseg: [context[i]]
})]);
newFile = castDraft(result);
}
2024-06-04 16:11:47 +02:00
} else if (item instanceof ListTrackSegmentItem && context[i] instanceof TrackSegment) {
let [result, _removed] = newFile.replaceTrackSegments(item.getTrackIndex(), item.getSegmentIndex(), item.getSegmentIndex() - 1, [context[i]]);
newFile = castDraft(result);
2024-06-05 21:08:01 +02:00
} else if (item instanceof ListWaypointsItem) {
if (Array.isArray(context[i]) && context[i].length > 0 && context[i][0] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, context[i]);
newFile = castDraft(result);
} else if (context[i] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(newFile.wpt.length, newFile.wpt.length - 1, [context[i]]);
newFile = castDraft(result);
}
2024-06-04 16:11:47 +02:00
} else if (item instanceof ListWaypointItem && context[i] instanceof Waypoint) {
let [result, _removed] = newFile.replaceWaypoints(item.getWaypointIndex(), item.getWaypointIndex() - 1, [context[i]]);
newFile = castDraft(result);
}
});
return newFile;
}
2024-06-05 21:08:01 +02:00
], (files, context: (Track | TrackSegment | Waypoint[] | Waypoint)[]) => {
toItems.forEach((item, i) => {
if (item instanceof ListFileItem) {
if (context[i] instanceof Track) {
let newFile = newGPXFile();
newFile._data.id = item.getFileId();
if (context[i].name) {
newFile.metadata.name = context[i].name;
}
newFile = newFile.replaceTracks(0, 0, [context[i]])[0];
files.set(item.getFileId(), freeze(newFile));
} else if (context[i] instanceof TrackSegment) {
let newFile = newGPXFile();
newFile._data.id = item.getFileId();
newFile = newFile.replaceTracks(0, 0, [new Track({
trkseg: [context[i]]
})])[0];
files.set(item.getFileId(), freeze(newFile));
}
}
2024-06-05 17:19:03 +02:00
});
2024-06-05 21:08:01 +02:00
}, []);
2024-06-05 21:36:24 +02:00
selection.update(($selection) => {
$selection.clear();
toItems.forEach((item) => {
$selection.set(item, true);
});
return $selection;
});
2024-06-04 16:11:47 +02:00
}