dexie progress

This commit is contained in:
vcoppe
2024-05-03 15:59:34 +02:00
parent cd919258ad
commit 6c9faf54b1
10 changed files with 334 additions and 175 deletions

View File

@@ -123,14 +123,14 @@
});
</script>
<div class="h-10 -translate-y-10 w-full pointer-events-none">
<div class="h-10 -translate-y-10 w-full pointer-events-none absolute z-30">
<ScrollArea orientation="horizontal" class="w-full h-full" scrollbarXClasses="h-2">
<div bind:this={container} class="flex flex-row gap-1">
{#if $fileObservers}
{#each $fileObservers.values() as file}
{#each $fileObservers.entries() as [fileId, file]}
<div
bind:this={buttons[get(file)._data.id]}
data-id={get(file)._data.id}
bind:this={buttons[fileId]}
data-id={fileId}
class="pointer-events-auto first:ml-1 last:mr-1 mb-1 bg-transparent"
>
<FileListItem {file} />

View File

@@ -4,43 +4,47 @@
import Shortcut from './Shortcut.svelte';
import { Copy, Trash2 } from 'lucide-svelte';
import { get, type Readable, type Writable } from 'svelte/store';
import { get, type Readable } from 'svelte/store';
import { selectedFiles, selectFiles } from '$lib/stores';
import { dbUtils } from '$lib/db';
import { _ } from 'svelte-i18n';
import type { GPXFile } from 'gpx';
import type { FreezedObject } from 'structurajs';
import { dbUtils } from '$lib/db';
export let file: Readable<FreezedObject<GPXFile>> | undefined;
export let file: Readable<GPXFile | undefined>;
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
on:contextmenu={() => {
if (!get(selectedFiles).has($file?._data.id)) {
get(selectFiles).select($file?._data.id);
}
}}
>
<ContextMenu.Root>
<ContextMenu.Trigger>
<Button variant="outline" class="h-9 px-1.5 py-1 border-none shadow-md">
{$file?.metadata.name}
</Button>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item on:click={dbUtils.duplicateSelectedFiles}>
<Copy size="16" class="mr-1" />
{$_('menu.duplicate')}
<Shortcut key="D" ctrl={true} /></ContextMenu.Item
>
<ContextMenu.Separator />
<ContextMenu.Item on:click={dbUtils.deleteSelectedFiles}
><Trash2 size="16" class="mr-1" />
{$_('menu.delete')}
<Shortcut key="⌫" ctrl={true} /></ContextMenu.Item
>
</ContextMenu.Content>
</ContextMenu.Root>
</div>
{#if $file}
<div
on:contextmenu={() => {
if (!get(selectedFiles).has($file._data.id)) {
get(selectFiles).select($file._data.id);
}
}}
>
<ContextMenu.Root>
<ContextMenu.Trigger>
<Button
variant="outline"
class="h-9 px-1.5 py-1 border-none shadow-md focus-visible:ring-0 focus-visible:ring-offset-0"
>
{$file.metadata.name}
</Button>
</ContextMenu.Trigger>
<ContextMenu.Content>
<ContextMenu.Item on:click={dbUtils.duplicateSelectedFiles}>
<Copy size="16" class="mr-1" />
{$_('menu.duplicate')}
<Shortcut key="D" ctrl={true} /></ContextMenu.Item
>
<ContextMenu.Separator />
<ContextMenu.Item on:click={dbUtils.deleteSelectedFiles}
><Trash2 size="16" class="mr-1" />
{$_('menu.delete')}
<Shortcut key="⌫" ctrl={true} /></ContextMenu.Item
>
</ContextMenu.Content>
</ContextMenu.Root>
</div>
{/if}

View File

@@ -19,7 +19,7 @@
import { _ } from 'svelte-i18n';
import { derived, get } from 'svelte/store';
import { canUndo, dbUtils, fileObservers, redo, undo } from '$lib/db';
import { canUndo, canRedo, dbUtils, fileObservers, redo, undo } from '$lib/db';
let showDistanceMarkers = false;
let showDirectionMarkers = false;
@@ -41,7 +41,7 @@
}
let undoDisabled = derived(canUndo, ($canUndo) => !$canUndo);
let redoDisabled = derived(canUndo, ($canUndo) => !$canUndo);
let redoDisabled = derived(canRedo, ($canRedo) => !$canRedo);
</script>
<div class="absolute top-2 left-0 right-0 z-20 flex flex-row justify-center pointer-events-none">

View File

@@ -39,31 +39,36 @@ function decrementColor(color: string) {
export class GPXLayer {
map: mapboxgl.Map;
file: Readable<FreezedObject<GPXFile>>;
fileId: string;
file: Readable<FreezedObject<GPXFile> | undefined>;
layerColor: string;
popup: mapboxgl.Popup;
popupElement: HTMLElement;
markers: mapboxgl.Marker[] = [];
unsubscribe: () => void;
addBinded: () => void = this.add.bind(this);
updateBinded: () => void = this.update.bind(this);
selectOnClickBinded: (e: any) => void = this.selectOnClick.bind(this);
constructor(map: mapboxgl.Map, file: Readable<FreezedObject<GPXFile>>, popup: mapboxgl.Popup, popupElement: HTMLElement) {
constructor(map: mapboxgl.Map, fileId: string, file: Readable<FreezedObject<GPXFile> | undefined>, popup: mapboxgl.Popup, popupElement: HTMLElement) {
this.map = map;
this.file = file;
this.fileId = get(file)._data.id;
this.fileId = fileId;
this.file = file
this.layerColor = getColor();
this.popup = popup;
this.popupElement = popupElement;
this.unsubscribe = file.subscribe(this.updateData.bind(this));
this.unsubscribe = file.subscribe(this.update.bind(this));
this.add();
this.map.on('style.load', this.addBinded);
this.map.on('style.load', this.updateBinded);
}
add() {
update() {
let file = get(this.file);
if (!file) {
return;
}
let addedSource = false;
if (!this.map.getSource(this.fileId)) {
let data = this.getGeoJSON();
@@ -71,6 +76,7 @@ export class GPXLayer {
type: 'geojson',
data
});
addedSource = true;
}
if (!this.map.getLayer(this.fileId)) {
@@ -93,16 +99,16 @@ export class GPXLayer {
this.map.on('mouseenter', this.fileId, toPointerCursor);
this.map.on('mouseleave', this.fileId, toDefaultCursor);
}
}
updateData() {
let source = this.map.getSource(this.fileId);
if (source) {
source.setData(this.getGeoJSON());
if (!addedSource) {
let source = this.map.getSource(this.fileId);
if (source) {
source.setData(this.getGeoJSON());
}
}
let markerIndex = 0;
get(this.file).wpt.forEach((waypoint) => { // Update markers
file.wpt.forEach((waypoint) => { // Update markers
if (markerIndex < this.markers.length) {
this.markers[markerIndex].setLngLat(waypoint.getCoordinates());
} else {
@@ -131,7 +137,7 @@ export class GPXLayer {
this.map.off('click', this.fileId, this.selectOnClickBinded);
this.map.off('mouseenter', this.fileId, toPointerCursor);
this.map.off('mouseleave', this.fileId, toDefaultCursor);
this.map.off('style.load', this.addBinded);
this.map.off('style.load', this.updateBinded);
this.map.removeLayer(this.fileId);
this.map.removeSource(this.fileId);
@@ -146,7 +152,9 @@ export class GPXLayer {
}
moveToFront() {
this.map.moveLayer(this.fileId);
if (this.map.getLayer(this.fileId)) {
this.map.moveLayer(this.fileId);
}
}
selectOnClick(e: any) {
@@ -161,7 +169,15 @@ export class GPXLayer {
}
getGeoJSON(): GeoJSON.FeatureCollection {
let data = get(this.file).toGeoJSON();
let file = get(this.file);
if (!file) {
return {
type: 'FeatureCollection',
features: []
};
}
let data = file.toGeoJSON();
for (let feature of data.features) {
if (!feature.properties) {
feature.properties = {};

View File

@@ -22,7 +22,7 @@
// add layers for new files
$fileObservers.forEach((file, fileId) => {
if (!$layers.has(fileId)) {
$layers.set(fileId, new GPXLayer(get(map), file, popup, popupElement));
$layers.set(fileId, new GPXLayer(get(map), fileId, file, popup, popupElement));
}
});
return $layers;

View File

@@ -61,7 +61,7 @@
if (selectedFileObserver) {
routingControls.set(
selectedId,
new RoutingControls(get(map), selectedFileObserver, popup, popupElement)
new RoutingControls(get(map), selectedId, selectedFileObserver, popup, popupElement)
);
}
} else {

View File

@@ -1,5 +1,5 @@
import { distance, type Coordinates, type GPXFile, TrackPoint, TrackSegment } from "gpx";
import { get, type Writable } from "svelte/store";
import { get, type Readable, type Writable } from "svelte/store";
import { computeAnchorPoints } from "./Simplify";
import mapboxgl from "mapbox-gl";
import { route } from "./Routing";
@@ -11,7 +11,8 @@ import { dbUtils } from "$lib/db";
export class RoutingControls {
map: mapboxgl.Map;
file: Writable<GPXFile>;
fileId: string = '';
file: Readable<GPXFile | undefined>;
anchors: AnchorWithMarker[] = [];
shownAnchors: AnchorWithMarker[] = [];
popup: mapboxgl.Popup;
@@ -24,8 +25,9 @@ export class RoutingControls {
updateTemporaryAnchorBinded: (e: any) => void = this.updateTemporaryAnchor.bind(this);
appendAnchorBinded: (e: mapboxgl.MapMouseEvent) => void = this.appendAnchor.bind(this);
constructor(map: mapboxgl.Map, file: Writable<GPXFile>, popup: mapboxgl.Popup, popupElement: HTMLElement) {
constructor(map: mapboxgl.Map, fileId: string, file: Writable<GPXFile>, popup: mapboxgl.Popup, popupElement: HTMLElement) {
this.map = map;
this.fileId = fileId;
this.file = file;
this.popup = popup;
this.popupElement = popupElement;
@@ -46,13 +48,18 @@ export class RoutingControls {
this.map.on('zoom', this.toggleAnchorsForZoomLevelAndBoundsBinded);
this.map.on('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
this.map.on('click', this.appendAnchorBinded);
this.map.on('mousemove', get(this.file)._data.id, this.showTemporaryAnchorBinded);
this.map.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
this.unsubscribe = this.file.subscribe(this.updateControls.bind(this));
}
updateControls() { // Update the markers when the file changes
let segments = get(this.file).getSegments();
let file = get(this.file);
if (!file) {
return;
}
let segments = file.getSegments();
let anchorIndex = 0;
for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex++) {
@@ -104,7 +111,7 @@ export class RoutingControls {
this.map.off('zoom', this.toggleAnchorsForZoomLevelAndBoundsBinded);
this.map.off('move', this.toggleAnchorsForZoomLevelAndBoundsBinded);
this.map.off('click', this.appendAnchorBinded);
this.map.off('mousemove', get(this.file)._data.id, this.showTemporaryAnchorBinded);
this.map.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
this.map.off('mousemove', this.updateTemporaryAnchorBinded);
this.unsubscribe();
@@ -290,17 +297,17 @@ export class RoutingControls {
let [previousAnchor, nextAnchor] = this.getNeighbouringAnchors(anchor);
if (previousAnchor === null && nextAnchor === null) { // Only one point, remove it
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[anchor.segmentIndex];
segment.replace(0, 0, []);
});
} else if (previousAnchor === null) { // First point, remove trackpoints until nextAnchor
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[anchor.segmentIndex];
segment.replace(0, nextAnchor.point._data.index - 1, []);
});
} else if (nextAnchor === null) { // Last point, remove trackpoints from previousAnchor
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[anchor.segmentIndex];
segment.replace(previousAnchor.point._data.index + 1, segment.trkpt.length - 1, []);
});
@@ -323,7 +330,7 @@ export class RoutingControls {
if (!lastAnchor) {
// TODO, create segment if it does not exist
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[0];
segment.replace(0, 0, [newPoint]);
});
@@ -365,7 +372,7 @@ export class RoutingControls {
let segment = anchors[0].segment;
if (anchors.length === 1) { // Only one anchor, update the point in the segment
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[anchors[0].segmentIndex];
segment.replace(0, 0, [new TrackPoint({
attributes: targetCoordinates[0],
@@ -425,7 +432,7 @@ export class RoutingControls {
anchor.point._data.zoom = 0; // Make these anchors permanent
});
dbUtils.applyToFile(get(this.file)._data.id, (file) => {
dbUtils.applyToFile(this.fileId, (file) => {
let segment = file.getSegments()[anchors[0].segmentIndex];
segment.replace(start, end, response);
});