mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 08:42:31 +00:00
progress
This commit is contained in:
@@ -2,15 +2,41 @@
|
|||||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index';
|
import { ScrollArea } from '$lib/components/ui/scroll-area/index';
|
||||||
import FileListNode from './FileListNode.svelte';
|
import FileListNode from './FileListNode.svelte';
|
||||||
|
|
||||||
import { fileObservers } from '$lib/db';
|
import { fileObservers, settings } from '$lib/db';
|
||||||
import { setContext } from 'svelte';
|
import { setContext } from 'svelte';
|
||||||
import { ListRootItem } from './FileList';
|
import { ListFileItem, ListRootItem } from './FileList';
|
||||||
|
import { selection } from './Selection';
|
||||||
|
|
||||||
export let orientation: 'vertical' | 'horizontal';
|
export let orientation: 'vertical' | 'horizontal';
|
||||||
export let recursive = false;
|
export let recursive = false;
|
||||||
|
|
||||||
setContext('orientation', orientation);
|
setContext('orientation', orientation);
|
||||||
setContext('recursive', recursive);
|
setContext('recursive', recursive);
|
||||||
|
|
||||||
|
const { verticalFileView } = settings;
|
||||||
|
|
||||||
|
verticalFileView.subscribe(($vertical) => {
|
||||||
|
if ($vertical) {
|
||||||
|
selection.update(($selection) => {
|
||||||
|
$selection.forEach((item) => {
|
||||||
|
if ($selection.hasAnyChildren(item, false)) {
|
||||||
|
$selection.toggle(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $selection;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
selection.update(($selection) => {
|
||||||
|
$selection.forEach((item) => {
|
||||||
|
if (!(item instanceof ListFileItem)) {
|
||||||
|
$selection.toggle(item);
|
||||||
|
$selection.set(new ListFileItem(item.getFileId()), true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $selection;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
|
@@ -49,7 +49,7 @@
|
|||||||
}}
|
}}
|
||||||
on:mouseenter={() => {
|
on:mouseenter={() => {
|
||||||
if (item instanceof ListWaypointItem) {
|
if (item instanceof ListWaypointItem) {
|
||||||
let layer = get(gpxLayers).get(item.getFileId());
|
let layer = gpxLayers.get(item.getFileId());
|
||||||
let fileStore = get(fileObservers).get(item.getFileId());
|
let fileStore = get(fileObservers).get(item.getFileId());
|
||||||
if (layer && fileStore) {
|
if (layer && fileStore) {
|
||||||
let waypoint = get(fileStore)?.file.wpt[item.getWaypointIndex()];
|
let waypoint = get(fileStore)?.file.wpt[item.getWaypointIndex()];
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
}}
|
}}
|
||||||
on:mouseleave={() => {
|
on:mouseleave={() => {
|
||||||
if (item instanceof ListWaypointItem) {
|
if (item instanceof ListWaypointItem) {
|
||||||
let layer = get(gpxLayers).get(item.getFileId());
|
let layer = gpxLayers.get(item.getFileId());
|
||||||
if (layer) {
|
if (layer) {
|
||||||
layer.hideWaypointPopup();
|
layer.hideWaypointPopup();
|
||||||
}
|
}
|
||||||
|
@@ -87,7 +87,7 @@ export function applyToOrderedSelectedItemsFromFile(callback: (fileId: string, l
|
|||||||
} else if (a instanceof ListWaypointItem && b instanceof ListWaypointItem) {
|
} else if (a instanceof ListWaypointItem && b instanceof ListWaypointItem) {
|
||||||
return b.getWaypointIndex() - a.getWaypointIndex();
|
return b.getWaypointIndex() - a.getWaypointIndex();
|
||||||
}
|
}
|
||||||
return 0;
|
return b.level - a.level;
|
||||||
});
|
});
|
||||||
|
|
||||||
callback(fileId, level, items);
|
callback(fileId, level, items);
|
||||||
|
@@ -14,11 +14,7 @@ export class DistanceMarkers {
|
|||||||
|
|
||||||
gpxStatistics.subscribe(this.updateBinded);
|
gpxStatistics.subscribe(this.updateBinded);
|
||||||
distanceMarkers.subscribe(this.updateBinded);
|
distanceMarkers.subscribe(this.updateBinded);
|
||||||
distanceUnits.subscribe(() => {
|
distanceUnits.subscribe(this.updateBinded);
|
||||||
if (get(distanceMarkers)) {
|
|
||||||
this.update();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
@@ -1,14 +1,15 @@
|
|||||||
import { map, currentTool, Tool } from "$lib/stores";
|
import { map, currentTool, Tool } from "$lib/stores";
|
||||||
import { settings, type GPXFileWithStatistics } from "$lib/db";
|
import { settings, type GPXFileWithStatistics, dbUtils } 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 { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
|
import { addSelectItem, selectItem, selection } from "$lib/components/file-list/Selection";
|
||||||
import { ListTrackSegmentItem, type ListItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
import { ListTrackSegmentItem, type ListItem, ListWaypointItem, ListWaypointsItem, ListTrackItem, ListFileItem, ListRootItem } from "$lib/components/file-list/FileList";
|
||||||
import type { Waypoint } from "gpx";
|
import type { Waypoint } from "gpx";
|
||||||
|
import { produce } from "immer";
|
||||||
|
|
||||||
let defaultWeight = 5;
|
let defaultWeight = 5;
|
||||||
let defaultOpacity = 0.7;
|
let defaultOpacity = 0.6;
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
'#ff0000',
|
'#ff0000',
|
||||||
@@ -48,7 +49,8 @@ export class GPXLayer {
|
|||||||
file: Readable<GPXFileWithStatistics | undefined>;
|
file: Readable<GPXFileWithStatistics | undefined>;
|
||||||
layerColor: string;
|
layerColor: string;
|
||||||
markers: mapboxgl.Marker[] = [];
|
markers: mapboxgl.Marker[] = [];
|
||||||
selected: ListItem[] = [];
|
selected: boolean = false;
|
||||||
|
draggable: boolean;
|
||||||
unsubscribe: Function[] = [];
|
unsubscribe: Function[] = [];
|
||||||
|
|
||||||
updateBinded: () => void = this.update.bind(this);
|
updateBinded: () => void = this.update.bind(this);
|
||||||
@@ -61,16 +63,26 @@ export class GPXLayer {
|
|||||||
this.layerColor = getColor();
|
this.layerColor = getColor();
|
||||||
this.unsubscribe.push(file.subscribe(this.updateBinded));
|
this.unsubscribe.push(file.subscribe(this.updateBinded));
|
||||||
this.unsubscribe.push(selection.subscribe($selection => {
|
this.unsubscribe.push(selection.subscribe($selection => {
|
||||||
let selected = $selection.getChild(fileId)?.getSelected() || [];
|
let newSelected = $selection.hasAnyChildren(new ListFileItem(this.fileId));
|
||||||
if (selected.length !== this.selected.length || selected.some((item, index) => item !== this.selected[index])) {
|
if (this.selected || newSelected) {
|
||||||
this.selected = selected;
|
this.selected = newSelected;
|
||||||
this.update();
|
this.update();
|
||||||
if (this.selected.length > 0) {
|
}
|
||||||
this.moveToFront();
|
if (newSelected) {
|
||||||
}
|
this.moveToFront();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this.unsubscribe.push(directionMarkers.subscribe(this.updateBinded));
|
this.unsubscribe.push(directionMarkers.subscribe(this.updateBinded));
|
||||||
|
this.unsubscribe.push(currentTool.subscribe(tool => {
|
||||||
|
if (tool === Tool.WAYPOINT && !this.draggable) {
|
||||||
|
this.draggable = true;
|
||||||
|
this.markers.forEach(marker => marker.setDraggable(true));
|
||||||
|
} else if (tool !== Tool.WAYPOINT && this.draggable) {
|
||||||
|
this.draggable = false;
|
||||||
|
this.markers.forEach(marker => marker.setDraggable(false));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.draggable = get(currentTool) === Tool.WAYPOINT;
|
||||||
|
|
||||||
this.map.on('style.load', this.updateBinded);
|
this.map.on('style.load', this.updateBinded);
|
||||||
}
|
}
|
||||||
@@ -144,30 +156,67 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let markerIndex = 0;
|
let markerIndex = 0;
|
||||||
file.wpt.forEach((waypoint) => { // Update markers
|
|
||||||
if (markerIndex < this.markers.length) {
|
if (get(selection).hasAnyChildren(new ListFileItem(this.fileId))) {
|
||||||
this.markers[markerIndex].setLngLat(waypoint.getCoordinates());
|
file.wpt.forEach((waypoint) => { // Update markers
|
||||||
Object.defineProperty(this.markers[markerIndex], '_waypoint', { value: waypoint, writable: true });
|
if (markerIndex < this.markers.length) {
|
||||||
} else {
|
this.markers[markerIndex].setLngLat(waypoint.getCoordinates());
|
||||||
let marker = new mapboxgl.Marker().setLngLat(waypoint.getCoordinates());
|
Object.defineProperty(this.markers[markerIndex], '_waypoint', { value: waypoint, writable: true });
|
||||||
Object.defineProperty(marker, '_waypoint', { value: waypoint, writable: true });
|
} else {
|
||||||
marker.getElement().addEventListener('mouseover', (e) => {
|
let marker = new mapboxgl.Marker({
|
||||||
this.showWaypointPopup(marker._waypoint);
|
draggable: this.draggable
|
||||||
e.stopPropagation();
|
}).setLngLat(waypoint.getCoordinates());
|
||||||
});
|
Object.defineProperty(marker, '_waypoint', { value: waypoint, writable: true });
|
||||||
marker.getElement().addEventListener('mouseout', () => {
|
let dragEndTimestamp = 0;
|
||||||
this.hideWaypointPopup();
|
marker.getElement().addEventListener('mouseover', (e) => {
|
||||||
});
|
if (marker._isDragging) {
|
||||||
marker.getElement().addEventListener('click', (e) => {
|
return;
|
||||||
if (get(verticalFileView)) {
|
}
|
||||||
selectItem(new ListWaypointItem(this.fileId, marker._waypoint._data.index));
|
this.showWaypointPopup(marker._waypoint);
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}
|
});
|
||||||
});
|
marker.getElement().addEventListener('mouseout', () => {
|
||||||
this.markers.push(marker);
|
this.hideWaypointPopup();
|
||||||
}
|
});
|
||||||
markerIndex++;
|
marker.getElement().addEventListener('click', (e) => {
|
||||||
});
|
if (dragEndTimestamp && Date.now() - dragEndTimestamp < 1000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((e.shiftKey || e.ctrlKey || e.metaKey) && get(selection).hasAnyChildren(new ListWaypointsItem(this.fileId), false)) {
|
||||||
|
addSelectItem(new ListWaypointItem(this.fileId, marker._waypoint._data.index));
|
||||||
|
} else {
|
||||||
|
selectItem(new ListWaypointItem(this.fileId, marker._waypoint._data.index));
|
||||||
|
}
|
||||||
|
if (!get(verticalFileView) && !get(selection).has(new ListFileItem(this.fileId))) {
|
||||||
|
addSelectItem(new ListFileItem(this.fileId));
|
||||||
|
}
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
marker.on('dragstart', () => {
|
||||||
|
this.map.getCanvas().style.cursor = 'grabbing';
|
||||||
|
marker.getElement().style.cursor = 'grabbing';
|
||||||
|
this.hideWaypointPopup();
|
||||||
|
});
|
||||||
|
marker.on('dragend', (e) => {
|
||||||
|
this.map.getCanvas().style.cursor = '';
|
||||||
|
marker.getElement().style.cursor = '';
|
||||||
|
dbUtils.applyToFile(this.fileId, (file) => {
|
||||||
|
return produce(file, (draft) => {
|
||||||
|
let latLng = marker.getLngLat();
|
||||||
|
draft.wpt[marker._waypoint._data.index].setCoordinates({
|
||||||
|
lat: latLng.lat,
|
||||||
|
lon: latLng.lng
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dragEndTimestamp = Date.now()
|
||||||
|
});
|
||||||
|
this.markers.push(marker);
|
||||||
|
}
|
||||||
|
markerIndex++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
while (markerIndex < this.markers.length) { // Remove extra markers
|
while (markerIndex < this.markers.length) { // Remove extra markers
|
||||||
this.markers.pop()?.remove();
|
this.markers.pop()?.remove();
|
||||||
@@ -249,7 +298,7 @@ export class GPXLayer {
|
|||||||
let waypoint = get(currentWaypoint);
|
let waypoint = get(currentWaypoint);
|
||||||
if (waypoint) {
|
if (waypoint) {
|
||||||
let marker = this.markers[waypoint._data.index];
|
let marker = this.markers[waypoint._data.index];
|
||||||
marker.togglePopup();
|
marker.getPopup()?.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +330,7 @@ export class GPXLayer {
|
|||||||
}
|
}
|
||||||
if (get(selection).hasAnyParent(new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)) || get(selection).hasAnyChildren(new ListWaypointsItem(this.fileId), true)) {
|
if (get(selection).hasAnyParent(new ListTrackSegmentItem(this.fileId, trackIndex, segmentIndex)) || get(selection).hasAnyChildren(new ListWaypointsItem(this.fileId), true)) {
|
||||||
feature.properties.weight = feature.properties.weight + 2;
|
feature.properties.weight = feature.properties.weight + 2;
|
||||||
feature.properties.opacity = (feature.properties.opacity + 1) / 2;
|
feature.properties.opacity = (feature.properties.opacity + 2) / 3;
|
||||||
}
|
}
|
||||||
feature.properties.trackIndex = trackIndex;
|
feature.properties.trackIndex = trackIndex;
|
||||||
feature.properties.segmentIndex = segmentIndex;
|
feature.properties.segmentIndex = segmentIndex;
|
||||||
|
@@ -9,21 +9,18 @@
|
|||||||
let distanceMarkers: DistanceMarkers;
|
let distanceMarkers: DistanceMarkers;
|
||||||
|
|
||||||
$: if ($map && $fileObservers) {
|
$: if ($map && $fileObservers) {
|
||||||
gpxLayers.update(($layers) => {
|
// remove layers for deleted files
|
||||||
// remove layers for deleted files
|
gpxLayers.forEach((layer, fileId) => {
|
||||||
$layers.forEach((layer, fileId) => {
|
if (!$fileObservers.has(fileId)) {
|
||||||
if (!$fileObservers.has(fileId)) {
|
layer.remove();
|
||||||
layer.remove();
|
gpxLayers.delete(fileId);
|
||||||
$layers.delete(fileId);
|
}
|
||||||
}
|
});
|
||||||
});
|
// add layers for new files
|
||||||
// add layers for new files
|
$fileObservers.forEach((file, fileId) => {
|
||||||
$fileObservers.forEach((file, fileId) => {
|
if (!gpxLayers.has(fileId)) {
|
||||||
if (!$layers.has(fileId)) {
|
gpxLayers.set(fileId, new GPXLayer(get(map), fileId, file));
|
||||||
$layers.set(fileId, new GPXLayer(get(map), fileId, file));
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
return $layers;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Tool } from '$lib/stores';
|
import { Tool } from '$lib/stores';
|
||||||
import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte';
|
|
||||||
import Waypoint from '$lib/components/toolbar/tools/waypoint/Waypoint.svelte';
|
|
||||||
import ToolbarItem from './ToolbarItem.svelte';
|
import ToolbarItem from './ToolbarItem.svelte';
|
||||||
import {
|
import {
|
||||||
Group,
|
Group,
|
||||||
@@ -11,11 +9,11 @@
|
|||||||
Ungroup,
|
Ungroup,
|
||||||
MapPin,
|
MapPin,
|
||||||
Palette,
|
Palette,
|
||||||
FolderTree,
|
|
||||||
Filter
|
Filter
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
|
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
|
import ToolbarItemMenu from './ToolbarItemMenu.svelte';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="absolute top-0 bottom-0 left-0 z-20 flex flex-col justify-center pointer-events-none">
|
<div class="absolute top-0 bottom-0 left-0 z-20 flex flex-col justify-center pointer-events-none">
|
||||||
@@ -27,6 +25,10 @@
|
|||||||
<Pencil slot="icon" size="18" />
|
<Pencil slot="icon" size="18" />
|
||||||
<span slot="tooltip">{$_('toolbar.routing.tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.routing.tooltip')}</span>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
|
<ToolbarItem tool={Tool.WAYPOINT}>
|
||||||
|
<MapPin slot="icon" size="18" />
|
||||||
|
<span slot="tooltip">{$_('toolbar.waypoint_tooltip')}</span>
|
||||||
|
</ToolbarItem>
|
||||||
<ToolbarItem tool={Tool.TIME}>
|
<ToolbarItem tool={Tool.TIME}>
|
||||||
<CalendarClock slot="icon" size="18" />
|
<CalendarClock slot="icon" size="18" />
|
||||||
<span slot="tooltip">{$_('toolbar.time_tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.time_tooltip')}</span>
|
||||||
@@ -39,10 +41,6 @@
|
|||||||
<Ungroup slot="icon" size="18" />
|
<Ungroup slot="icon" size="18" />
|
||||||
<span slot="tooltip">{$_('toolbar.extract_tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.extract_tooltip')}</span>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
<ToolbarItem tool={Tool.WAYPOINT}>
|
|
||||||
<MapPin slot="icon" size="18" />
|
|
||||||
<span slot="tooltip">{$_('toolbar.waypoint_tooltip')}</span>
|
|
||||||
</ToolbarItem>
|
|
||||||
<ToolbarItem tool={Tool.REDUCE}>
|
<ToolbarItem tool={Tool.REDUCE}>
|
||||||
<Filter slot="icon" size="18" />
|
<Filter slot="icon" size="18" />
|
||||||
<span slot="tooltip">{$_('toolbar.reduce_tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.reduce_tooltip')}</span>
|
||||||
@@ -56,7 +54,6 @@
|
|||||||
<span slot="tooltip">{$_('toolbar.style_tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.style_tooltip')}</span>
|
||||||
</ToolbarItem>
|
</ToolbarItem>
|
||||||
</div>
|
</div>
|
||||||
<Routing />
|
<ToolbarItemMenu />
|
||||||
<Waypoint />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,20 +1,39 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type Tool, currentTool } from '$lib/stores';
|
import { Tool, currentTool } from '$lib/stores';
|
||||||
import { flyAndScale } from '$lib/utils';
|
import { flyAndScale } from '$lib/utils';
|
||||||
import * as Card from '$lib/components/ui/card';
|
import * as Card from '$lib/components/ui/card';
|
||||||
|
import Routing from '$lib/components/toolbar/tools/routing/Routing.svelte';
|
||||||
|
import Waypoint from '$lib/components/toolbar/tools/waypoint/Waypoint.svelte';
|
||||||
|
import RoutingControlPopup from '$lib/components/toolbar/tools/routing/RoutingControlPopup.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import mapboxgl from 'mapbox-gl';
|
||||||
|
|
||||||
export let tool: Tool;
|
let popupElement: HTMLElement;
|
||||||
export let active = false;
|
let popup: mapboxgl.Popup;
|
||||||
|
|
||||||
$: active = $currentTool === tool;
|
onMount(() => {
|
||||||
|
popup = new mapboxgl.Popup({
|
||||||
|
closeButton: false,
|
||||||
|
maxWidth: undefined
|
||||||
|
});
|
||||||
|
popup.setDOMContent(popupElement);
|
||||||
|
popupElement.classList.remove('hidden');
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if active}
|
{#if $currentTool !== null}
|
||||||
<div in:flyAndScale={{ x: -2, y: 0, duration: 100 }} class="translate-x-1 h-full">
|
<div
|
||||||
|
in:flyAndScale={{ x: -2, y: 0, duration: 100 }}
|
||||||
|
class="translate-x-1 h-full {$$props.class ?? ''}"
|
||||||
|
>
|
||||||
<div class="rounded-md shadow-md pointer-events-auto">
|
<div class="rounded-md shadow-md pointer-events-auto">
|
||||||
<Card.Root class="border-none">
|
<Card.Root class="border-none">
|
||||||
<Card.Content class="p-3 flex flex-col gap-3">
|
<Card.Content class="p-3">
|
||||||
<slot />
|
{#if $currentTool === Tool.ROUTING}
|
||||||
|
<Routing {popup} {popupElement} />
|
||||||
|
{:else if $currentTool === Tool.WAYPOINT}
|
||||||
|
<Waypoint />
|
||||||
|
{/if}
|
||||||
</Card.Content>
|
</Card.Content>
|
||||||
</Card.Root>
|
</Card.Root>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,8 +42,10 @@
|
|||||||
|
|
||||||
<svelte:window
|
<svelte:window
|
||||||
on:keydown={(e) => {
|
on:keydown={(e) => {
|
||||||
if (active && e.key === 'Escape') {
|
if ($currentTool && e.key === 'Escape') {
|
||||||
currentTool.set(null);
|
currentTool.set(null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<RoutingControlPopup bind:element={popupElement} />
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ToolbarItemMenu from '$lib/components/toolbar/ToolbarItemMenu.svelte';
|
|
||||||
import * as Select from '$lib/components/ui/select';
|
import * as Select from '$lib/components/ui/select';
|
||||||
import { Switch } from '$lib/components/ui/switch';
|
import { Switch } from '$lib/components/ui/switch';
|
||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
@@ -18,26 +17,22 @@
|
|||||||
RouteOff
|
RouteOff
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
|
|
||||||
import { map, Tool } from '$lib/stores';
|
import { map, routingControls } from '$lib/stores';
|
||||||
import { dbUtils, settings } from '$lib/db';
|
import { dbUtils, settings } from '$lib/db';
|
||||||
import { brouterProfiles, routingProfileSelectItem } from './Routing';
|
import { brouterProfiles, routingProfileSelectItem } from './Routing';
|
||||||
|
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import { RoutingControls } from './RoutingControls';
|
import { RoutingControls } from './RoutingControls';
|
||||||
import RoutingControlPopup from './RoutingControlPopup.svelte';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import mapboxgl from 'mapbox-gl';
|
import mapboxgl from 'mapbox-gl';
|
||||||
import { fileObservers } from '$lib/db';
|
import { fileObservers } from '$lib/db';
|
||||||
import { slide } from 'svelte/transition';
|
import { slide } from 'svelte/transition';
|
||||||
import { selection } from '$lib/components/file-list/Selection';
|
import { selection } from '$lib/components/file-list/Selection';
|
||||||
import { ListRootItem, type ListItem } from '$lib/components/file-list/FileList';
|
import { ListRootItem, type ListItem } from '$lib/components/file-list/FileList';
|
||||||
|
|
||||||
let routingControls: Map<string, RoutingControls> = new Map();
|
export let popup: mapboxgl.Popup;
|
||||||
let popupElement: HTMLElement;
|
export let popupElement: HTMLElement;
|
||||||
let popup: mapboxgl.Popup | null = null;
|
|
||||||
let selectedItem: ListItem | null = null;
|
let selectedItem: ListItem | null = null;
|
||||||
let active = false;
|
|
||||||
|
|
||||||
const { privateRoads, routing } = settings;
|
const { privateRoads, routing } = settings;
|
||||||
|
|
||||||
@@ -65,18 +60,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: validSelection = $selection.hasAnyChildren(new ListRootItem(), true, ['waypoints']);
|
$: validSelection = $selection.hasAnyChildren(new ListRootItem(), true, ['waypoints']);
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
popup = new mapboxgl.Popup({
|
|
||||||
closeButton: false,
|
|
||||||
maxWidth: undefined
|
|
||||||
});
|
|
||||||
popup.setDOMContent(popupElement);
|
|
||||||
popupElement.classList.remove('hidden');
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ToolbarItemMenu tool={Tool.ROUTING} bind:active>
|
<div class=" flex flex-col gap-3">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<div slot="data" class="w-full flex flex-row justify-between items-center gap-2">
|
<div slot="data" class="w-full flex flex-row justify-between items-center gap-2">
|
||||||
<Label for="routing" class="flex flex-row gap-1">
|
<Label for="routing" class="flex flex-row gap-1">
|
||||||
@@ -91,6 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<span slot="tooltip">{$_('toolbar.routing.use_routing_tooltip')}</span>
|
<span slot="tooltip">{$_('toolbar.routing.use_routing_tooltip')}</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{#if $routing}
|
{#if $routing}
|
||||||
<div class="flex flex-col gap-3" in:slide>
|
<div class="flex flex-col gap-3" in:slide>
|
||||||
<div class="w-full flex flex-row justify-between items-center gap-2">
|
<div class="w-full flex flex-row justify-between items-center gap-2">
|
||||||
@@ -177,6 +164,4 @@
|
|||||||
<div>{$_('toolbar.routing.help')}</div>
|
<div>{$_('toolbar.routing.help')}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</Help>
|
</Help>
|
||||||
</ToolbarItemMenu>
|
</div>
|
||||||
|
|
||||||
<RoutingControlPopup bind:element={popupElement} />
|
|
||||||
|
@@ -165,6 +165,9 @@ export class RoutingControls {
|
|||||||
});
|
});
|
||||||
marker.getElement().addEventListener('click', (e) => {
|
marker.getElement().addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
if (marker === this.temporaryAnchor.marker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Date.now() - lastDragEvent < 100) { // Prevent click event during drag
|
if (Date.now() - lastDragEvent < 100) { // Prevent click event during drag
|
||||||
return;
|
return;
|
||||||
|
@@ -1,19 +1,35 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ToolbarItemMenu from '$lib/components/toolbar/ToolbarItemMenu.svelte';
|
|
||||||
import * as Alert from '$lib/components/ui/alert';
|
import * as Alert from '$lib/components/ui/alert';
|
||||||
import { CircleHelp } from 'lucide-svelte';
|
import { CircleHelp } from 'lucide-svelte';
|
||||||
import { selection } from '$lib/components/file-list/Selection';
|
import { selection } from '$lib/components/file-list/Selection';
|
||||||
import { Tool } from '$lib/stores';
|
import type { Waypoint } from 'gpx';
|
||||||
|
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { ListWaypointItem } from '$lib/components/file-list/FileList';
|
||||||
|
import { fileObservers } from '$lib/db';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
|
||||||
$: $selection.forEach((item) => {
|
let waypoint: Waypoint | undefined = undefined;
|
||||||
// todo
|
|
||||||
});
|
$: if ($selection) {
|
||||||
|
waypoint = undefined;
|
||||||
|
$selection.forEach((item) => {
|
||||||
|
if (item instanceof ListWaypointItem) {
|
||||||
|
if (waypoint) return;
|
||||||
|
let fileStore = get(fileObservers).get(item.getFileId());
|
||||||
|
if (fileStore) {
|
||||||
|
waypoint = get(fileStore)?.file.wpt[item.getWaypointIndex()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ToolbarItemMenu tool={Tool.WAYPOINT}>
|
<div class="flex flex-col gap-3 max-w-96">
|
||||||
<div class="w-full flex flex-row justify-between items-center gap-2">todo</div>
|
{#if waypoint}
|
||||||
|
<span>{waypoint.name}</span>
|
||||||
|
<span>{waypoint.desc ?? ''}</span>
|
||||||
|
<span>{waypoint.cmt ?? ''}</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<Alert.Root class="max-w-64">
|
<Alert.Root class="max-w-64">
|
||||||
<CircleHelp size="16" />
|
<CircleHelp size="16" />
|
||||||
@@ -21,4 +37,4 @@
|
|||||||
<div>{$_('toolbar.waypoint.help')}</div>
|
<div>{$_('toolbar.waypoint.help')}</div>
|
||||||
</Alert.Description>
|
</Alert.Description>
|
||||||
</Alert.Root>
|
</Alert.Root>
|
||||||
</ToolbarItemMenu>
|
</div>
|
||||||
|
@@ -488,10 +488,6 @@ export const dbUtils = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
selection.update(($selection) => {
|
|
||||||
$selection.clear();
|
|
||||||
return $selection;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
deleteAllFiles: () => {
|
deleteAllFiles: () => {
|
||||||
applyGlobal((draft) => {
|
applyGlobal((draft) => {
|
||||||
|
@@ -6,8 +6,9 @@ import { tick } from 'svelte';
|
|||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
import type { GPXLayer } from '$lib/components/gpx-layer/GPXLayer';
|
||||||
import { settings, dbUtils, fileObservers } from './db';
|
import { settings, dbUtils, fileObservers } from './db';
|
||||||
import { selection } from '$lib/components/file-list/Selection';
|
import { selectFile, selection } from '$lib/components/file-list/Selection';
|
||||||
import { ListFileItem, ListWaypointItem } from '$lib/components/file-list/FileList';
|
import { ListFileItem, ListWaypointItem } from '$lib/components/file-list/FileList';
|
||||||
|
import type { RoutingControls } from '$lib/components/toolbar/tools/routing/RoutingControls';
|
||||||
|
|
||||||
export const map = writable<mapboxgl.Map | null>(null);
|
export const map = writable<mapboxgl.Map | null>(null);
|
||||||
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
export const selectFiles = writable<{ [key: string]: (fileId?: string) => void }>({});
|
||||||
@@ -113,15 +114,15 @@ export function updateTargetMapBounds(bounds: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const gpxLayers: Writable<Map<string, GPXLayer>> = writable(new Map());
|
export const gpxLayers: Map<string, GPXLayer> = new Map();
|
||||||
|
export const routingControls: Map<string, RoutingControls> = new Map();
|
||||||
|
|
||||||
export enum Tool {
|
export enum Tool {
|
||||||
ROUTING,
|
ROUTING,
|
||||||
|
WAYPOINT,
|
||||||
TIME,
|
TIME,
|
||||||
REVERSE,
|
|
||||||
MERGE,
|
MERGE,
|
||||||
EXTRACT,
|
EXTRACT,
|
||||||
WAYPOINT,
|
|
||||||
REDUCE,
|
REDUCE,
|
||||||
CLEAN,
|
CLEAN,
|
||||||
STYLE
|
STYLE
|
||||||
@@ -192,11 +193,7 @@ function selectFileWhenLoaded(fileId: string) {
|
|||||||
const unsubscribe = fileObservers.subscribe((files) => {
|
const unsubscribe = fileObservers.subscribe((files) => {
|
||||||
if (files.has(fileId)) {
|
if (files.has(fileId)) {
|
||||||
tick().then(() => {
|
tick().then(() => {
|
||||||
selection.update(($selection) => {
|
selectFile(fileId);
|
||||||
$selection.clear();
|
|
||||||
$selection.toggle(new ListFileItem(fileId));
|
|
||||||
return $selection;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user