mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-04 17:32:56 +00:00
hide any file item and undo-redo it
This commit is contained in:
@@ -48,11 +48,8 @@
|
||||
triggerFileInput,
|
||||
createFile,
|
||||
loadFiles,
|
||||
toggleSelectionVisibility,
|
||||
updateSelectionFromKey,
|
||||
showSelection,
|
||||
hideSelection,
|
||||
anyHidden,
|
||||
allHidden,
|
||||
editMetadata,
|
||||
editStyle,
|
||||
exportState,
|
||||
@@ -231,15 +228,15 @@
|
||||
</Menubar.Item>
|
||||
<Menubar.Item
|
||||
on:click={() => {
|
||||
if ($anyHidden) {
|
||||
showSelection();
|
||||
if ($allHidden) {
|
||||
dbUtils.setHiddenToSelection(false);
|
||||
} else {
|
||||
hideSelection();
|
||||
dbUtils.setHiddenToSelection(true);
|
||||
}
|
||||
}}
|
||||
disabled={$selection.size == 0}
|
||||
>
|
||||
{#if $anyHidden}
|
||||
{#if $allHidden}
|
||||
<Eye size="16" class="mr-1" />
|
||||
{$_('menu.unhide')}
|
||||
{:else}
|
||||
@@ -249,7 +246,7 @@
|
||||
<Shortcut key="H" ctrl={true} />
|
||||
</Menubar.Item>
|
||||
<Menubar.Separator />
|
||||
<Menubar.Item on:click={selectAll}>
|
||||
<Menubar.Item on:click={selectAll} disabled={$fileObservers.size == 0}>
|
||||
<FileStack size="16" class="mr-1" />
|
||||
{$_('menu.select_all')}
|
||||
<Shortcut key="A" ctrl={true} />
|
||||
@@ -543,7 +540,11 @@
|
||||
$verticalFileView = !$verticalFileView;
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'h' && (e.metaKey || e.ctrlKey)) {
|
||||
toggleSelectionVisibility();
|
||||
if ($allHidden) {
|
||||
dbUtils.setHiddenToSelection(false);
|
||||
} else {
|
||||
dbUtils.setHiddenToSelection(true);
|
||||
}
|
||||
e.preventDefault();
|
||||
} else if (e.key === 'F1') {
|
||||
switchBasemaps();
|
||||
|
@@ -5,8 +5,8 @@
|
||||
import { fileObservers, settings } from '$lib/db';
|
||||
import { setContext } from 'svelte';
|
||||
import { ListFileItem, ListLevel, ListRootItem, allowedPastes } from './FileList';
|
||||
import { copied, pasteSelection, selection } from './Selection';
|
||||
import { ClipboardPaste, Plus } from 'lucide-svelte';
|
||||
import { copied, pasteSelection, selectAll, selection } from './Selection';
|
||||
import { ClipboardPaste, FileStack, Plus } from 'lucide-svelte';
|
||||
import Shortcut from '$lib/components/Shortcut.svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { createFile } from '$lib/stores';
|
||||
@@ -66,6 +66,12 @@
|
||||
<Shortcut key="+" ctrl={true} />
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator />
|
||||
<ContextMenu.Item on:click={selectAll} disabled={$fileObservers.size === 0}>
|
||||
<FileStack size="16" class="mr-1" />
|
||||
{$_('menu.select_all')}
|
||||
<Shortcut key="A" ctrl={true} />
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator />
|
||||
<ContextMenu.Item
|
||||
disabled={$copied === undefined ||
|
||||
$copied.length === 0 ||
|
||||
|
@@ -14,8 +14,10 @@
|
||||
import FileListNodeLabel from './FileListNodeLabel.svelte';
|
||||
import { afterUpdate, getContext } from 'svelte';
|
||||
import {
|
||||
ListFileItem,
|
||||
ListTrackSegmentItem,
|
||||
ListWaypointItem,
|
||||
ListWaypointsItem,
|
||||
type ListItem,
|
||||
type ListTrackItem
|
||||
} from './FileList';
|
||||
@@ -34,7 +36,7 @@
|
||||
let collapsible: CollapsibleTreeNode;
|
||||
|
||||
$: label =
|
||||
node instanceof GPXFile
|
||||
node instanceof GPXFile && item instanceof ListFileItem
|
||||
? node.metadata.name
|
||||
: node instanceof Track
|
||||
? node.name ?? `${$_('gpx.track')} ${(item as ListTrackItem).trackIndex + 1}`
|
||||
@@ -42,7 +44,7 @@
|
||||
? `${$_('gpx.segment')} ${(item as ListTrackSegmentItem).segmentIndex + 1}`
|
||||
: node instanceof Waypoint
|
||||
? node.name ?? `${$_('gpx.waypoint')} ${(item as ListWaypointItem).waypointIndex + 1}`
|
||||
: Array.isArray(node) && node.length > 0 && node[0] instanceof Waypoint
|
||||
: node instanceof GPXFile && item instanceof ListWaypointsItem
|
||||
? $_('gpx.waypoints')
|
||||
: '';
|
||||
|
||||
|
@@ -12,14 +12,21 @@
|
||||
import { get, writable, type Readable, type Writable } from 'svelte/store';
|
||||
import FileListNodeStore from './FileListNodeStore.svelte';
|
||||
import FileListNode from './FileListNode.svelte';
|
||||
import { ListLevel, ListRootItem, allowedMoves, moveItems, type ListItem } from './FileList';
|
||||
import {
|
||||
ListFileItem,
|
||||
ListLevel,
|
||||
ListRootItem,
|
||||
ListWaypointsItem,
|
||||
allowedMoves,
|
||||
moveItems,
|
||||
type ListItem
|
||||
} from './FileList';
|
||||
import { selection } from './Selection';
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
export let node:
|
||||
| Map<string, Readable<GPXFileWithStatistics | undefined>>
|
||||
| GPXTreeElement<AnyGPXTreeElement>
|
||||
| ReadonlyArray<Readonly<Waypoint>>
|
||||
| Readonly<Waypoint>;
|
||||
export let item: ListItem;
|
||||
export let waypointRoot: boolean = false;
|
||||
@@ -32,7 +39,9 @@
|
||||
: node instanceof GPXFile
|
||||
? waypointRoot
|
||||
? ListLevel.WAYPOINTS
|
||||
: ListLevel.TRACK
|
||||
: item instanceof ListWaypointsItem
|
||||
? ListLevel.WAYPOINT
|
||||
: ListLevel.TRACK
|
||||
: node instanceof Track
|
||||
? ListLevel.SEGMENT
|
||||
: ListLevel.WAYPOINT;
|
||||
@@ -268,10 +277,16 @@
|
||||
</div>
|
||||
{/each}
|
||||
{:else if node instanceof GPXFile}
|
||||
{#if waypointRoot}
|
||||
{#if item instanceof ListWaypointsItem}
|
||||
{#each node.wpt as wpt, i (wpt)}
|
||||
<div data-id={i} class="ml-1">
|
||||
<FileListNode node={wpt} item={item.extend(i)} />
|
||||
</div>
|
||||
{/each}
|
||||
{:else if waypointRoot}
|
||||
{#if node.wpt.length > 0}
|
||||
<div data-id="waypoints">
|
||||
<FileListNode node={node.wpt} item={item.extend('waypoints')} />
|
||||
<FileListNode {node} item={item.extend('waypoints')} />
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
@@ -287,16 +302,10 @@
|
||||
<FileListNode node={child} item={item.extend(i)} />
|
||||
</div>
|
||||
{/each}
|
||||
{:else if Array.isArray(node) && node.length > 0 && node[0] instanceof Waypoint}
|
||||
{#each node as wpt, i (wpt)}
|
||||
<div data-id={i} class="ml-1">
|
||||
<FileListNode node={wpt} item={item.extend(i)} />
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if node instanceof GPXFile}
|
||||
{#if node instanceof GPXFile && item instanceof ListFileItem}
|
||||
{#if !waypointRoot}
|
||||
<svelte:self {node} {item} waypointRoot={true} />
|
||||
{/if}
|
||||
|
@@ -38,15 +38,7 @@
|
||||
} from './Selection';
|
||||
import { getContext } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
anyHidden,
|
||||
editMetadata,
|
||||
editStyle,
|
||||
gpxLayers,
|
||||
hideSelection,
|
||||
map,
|
||||
showSelection
|
||||
} from '$lib/stores';
|
||||
import { allHidden, editMetadata, editStyle, gpxLayers, map } from '$lib/stores';
|
||||
import {
|
||||
GPXTreeElement,
|
||||
Track,
|
||||
@@ -189,9 +181,18 @@
|
||||
{:else if item.level === ListLevel.WAYPOINT}
|
||||
<MapPin size="16" class="mr-1 shrink-0" />
|
||||
{/if}
|
||||
<span class="grow select-none truncate {$verticalFileView ? 'mr-2' : ''}">
|
||||
<span class="grow select-none truncate {$verticalFileView ? 'last:mr-2' : ''}">
|
||||
{label}
|
||||
</span>
|
||||
{#if (item.level !== ListLevel.WAYPOINTS && node._data.hidden) || (item.level === ListLevel.WAYPOINTS && node._data.hiddenWpt)}
|
||||
<EyeOff
|
||||
size="12"
|
||||
class="shrink-0 mt-1 ml-1 {$verticalFileView ? 'mr-2' : ''} {item.level ===
|
||||
ListLevel.SEGMENT || item.level === ListLevel.WAYPOINT
|
||||
? 'mr-3'
|
||||
: ''}"
|
||||
/>
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
</ContextMenu.Trigger>
|
||||
@@ -206,28 +207,26 @@
|
||||
<PaintBucket size="16" class="mr-1" />
|
||||
{$_('menu.style.button')}
|
||||
</ContextMenu.Item>
|
||||
{#if item instanceof ListFileItem}
|
||||
<ContextMenu.Item
|
||||
on:click={() => {
|
||||
if ($anyHidden) {
|
||||
showSelection();
|
||||
} else {
|
||||
hideSelection();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $anyHidden}
|
||||
<Eye size="16" class="mr-1" />
|
||||
{$_('menu.unhide')}
|
||||
{:else}
|
||||
<EyeOff size="16" class="mr-1" />
|
||||
{$_('menu.hide')}
|
||||
{/if}
|
||||
<Shortcut key="H" ctrl={true} />
|
||||
</ContextMenu.Item>
|
||||
{/if}
|
||||
<ContextMenu.Separator />
|
||||
{/if}
|
||||
<ContextMenu.Item
|
||||
on:click={() => {
|
||||
if ($allHidden) {
|
||||
dbUtils.setHiddenToSelection(false);
|
||||
} else {
|
||||
dbUtils.setHiddenToSelection(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $allHidden}
|
||||
<Eye size="16" class="mr-1" />
|
||||
{$_('menu.unhide')}
|
||||
{:else}
|
||||
<EyeOff size="16" class="mr-1" />
|
||||
{$_('menu.hide')}
|
||||
{/if}
|
||||
<Shortcut key="H" ctrl={true} />
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator />
|
||||
{#if $verticalFileView}
|
||||
{#if item instanceof ListFileItem}
|
||||
<ContextMenu.Item
|
||||
|
@@ -50,7 +50,6 @@ export class GPXLayer {
|
||||
fileId: string;
|
||||
file: Readable<GPXFileWithStatistics | undefined>;
|
||||
layerColor: string;
|
||||
hidden: boolean = false;
|
||||
markers: mapboxgl.Marker[] = [];
|
||||
selected: boolean = false;
|
||||
draggable: boolean;
|
||||
@@ -165,6 +164,15 @@ export class GPXLayer {
|
||||
this.map.removeLayer(this.fileId + '-direction');
|
||||
}
|
||||
}
|
||||
|
||||
let visibleItems: [number, number][] = [];
|
||||
file.forEachSegment((segment, trackIndex, segmentIndex) => {
|
||||
if (!segment._data.hidden) {
|
||||
visibleItems.push([trackIndex, segmentIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
this.map.setFilter(this.fileId, ['any', ...visibleItems.map(([trackIndex, segmentIndex]) => ['all', ['==', 'trackIndex', trackIndex], ['==', 'segmentIndex', segmentIndex]])], { validate: false });
|
||||
} catch (e) { // No reliable way to check if the map is ready to add sources and layers
|
||||
return;
|
||||
}
|
||||
@@ -244,7 +252,11 @@ export class GPXLayer {
|
||||
}
|
||||
|
||||
this.markers.forEach((marker) => {
|
||||
marker.addTo(this.map);
|
||||
if (!marker._waypoint._data.hidden) {
|
||||
marker.addTo(this.map);
|
||||
} else {
|
||||
marker.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -366,17 +378,6 @@ export class GPXLayer {
|
||||
}
|
||||
}
|
||||
|
||||
toggleVisibility() {
|
||||
this.hidden = !this.hidden;
|
||||
if (this.hidden) {
|
||||
this.map.setLayoutProperty(this.fileId, 'visibility', 'none');
|
||||
this.markers.forEach(marker => marker.remove());
|
||||
} else {
|
||||
this.map.setLayoutProperty(this.fileId, 'visibility', 'visible');
|
||||
this.markers.forEach(marker => marker.addTo(this.map));
|
||||
}
|
||||
}
|
||||
|
||||
getGeoJSON(): GeoJSON.FeatureCollection {
|
||||
let file = get(this.file)?.file;
|
||||
if (!file) {
|
||||
|
Reference in New Issue
Block a user