use ids in each blocks for sortable

This commit is contained in:
vcoppe
2024-06-05 14:51:32 +02:00
parent 256996379a
commit 5f7155090b
6 changed files with 78 additions and 32 deletions

View File

@@ -6,6 +6,7 @@
export let side: 'left' | 'right' = 'right'; export let side: 'left' | 'right' = 'right';
export let margin: number = 1; export let margin: number = 1;
export let nohover: boolean = false; export let nohover: boolean = false;
export let slotInsideTrigger: boolean = true;
let open = writable<Record<string, boolean>>({}); let open = writable<Record<string, boolean>>({});
@@ -15,6 +16,7 @@
setContext('collapsible-tree-margin', margin); setContext('collapsible-tree-margin', margin);
setContext('collapsible-tree-nohover', nohover); setContext('collapsible-tree-nohover', nohover);
setContext('collapsible-tree-parent-id', 'root'); setContext('collapsible-tree-parent-id', 'root');
setContext('collapsible-tree-slot-inside-trigger', slotInsideTrigger);
</script> </script>
<slot /> <slot />

View File

@@ -12,6 +12,7 @@
let side = getContext<'left' | 'right'>('collapsible-tree-side'); let side = getContext<'left' | 'right'>('collapsible-tree-side');
let margin = getContext<number>('collapsible-tree-margin'); let margin = getContext<number>('collapsible-tree-margin');
let nohover = getContext<boolean>('collapsible-tree-nohover'); let nohover = getContext<boolean>('collapsible-tree-nohover');
let slotInsideTrigger = getContext<boolean>('collapsible-tree-slot-inside-trigger');
let parentId = getContext<string>('collapsible-tree-parent-id'); let parentId = getContext<string>('collapsible-tree-parent-id');
let fullId = `${parentId}.${id}`; let fullId = `${parentId}.${id}`;
@@ -34,7 +35,32 @@
</script> </script>
<Collapsible.Root bind:open={$open[fullId]} class={$$props.class ?? ''}> <Collapsible.Root bind:open={$open[fullId]} class={$$props.class ?? ''}>
<Collapsible.Trigger class="w-full"> {#if slotInsideTrigger}
<Collapsible.Trigger class="w-full">
<Button
variant="ghost"
class="w-full flex flex-row {side === 'right'
? 'justify-between'
: 'justify-start'} py-0 px-1 h-fit {nohover ? 'hover:bg-background' : ''}"
>
{#if side === 'left'}
{#if $open[fullId]}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronRight size="16" class="shrink-0" />
{/if}
{/if}
<slot name="trigger" />
{#if side === 'right'}
{#if $open[fullId]}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronLeft size="16" class="shrink-0" />
{/if}
{/if}
</Button>
</Collapsible.Trigger>
{:else}
<Button <Button
variant="ghost" variant="ghost"
class="w-full flex flex-row {side === 'right' class="w-full flex flex-row {side === 'right'
@@ -42,23 +68,28 @@
: 'justify-start'} py-0 px-1 h-fit {nohover ? 'hover:bg-background' : ''}" : 'justify-start'} py-0 px-1 h-fit {nohover ? 'hover:bg-background' : ''}"
> >
{#if side === 'left'} {#if side === 'left'}
{#if $open[fullId]} <Collapsible.Trigger>
<ChevronDown size="16" class="shrink-0" /> {#if $open[fullId]}
{:else} <ChevronDown size="16" class="shrink-0" />
<ChevronRight size="16" class="shrink-0" /> {:else}
{/if} <ChevronRight size="16" class="shrink-0" />
{/if}
</Collapsible.Trigger>
{/if} {/if}
<slot name="trigger" /> <slot name="trigger" />
{#if side === 'right'} {#if side === 'right'}
{#if $open[fullId]} <Collapsible.Trigger>
<ChevronDown size="16" class="shrink-0" /> {#if $open[fullId]}
{:else} <ChevronDown size="16" class="shrink-0" />
<ChevronLeft size="16" class="shrink-0" /> {:else}
{/if} <ChevronLeft size="16" class="shrink-0" />
{/if}
</Collapsible.Trigger>
{/if} {/if}
</Button> </Button>
</Collapsible.Trigger> {/if}
<Collapsible.Content class="ml-{margin}">
<Collapsible.Content class="ml-{margin} pl-{margin}">
<slot name="content" /> <slot name="content" />
</Collapsible.Content> </Collapsible.Content>
</Collapsible.Root> </Collapsible.Root>

View File

@@ -1,12 +1,24 @@
<script lang="ts"> <script lang="ts">
import { GPXFile, Track, Waypoint, type AnyGPXTreeElement, type GPXTreeElement } from 'gpx'; import {
GPXFile,
Track,
TrackSegment,
Waypoint,
type AnyGPXTreeElement,
type GPXTreeElement
} from 'gpx';
import { CollapsibleTreeNode } from '$lib/components/collapsible-tree/index'; import { CollapsibleTreeNode } from '$lib/components/collapsible-tree/index';
import { settings, type GPXFileWithStatistics } from '$lib/db'; import { settings, type GPXFileWithStatistics } from '$lib/db';
import { get, type Readable } from 'svelte/store'; import { get, type Readable } from 'svelte/store';
import FileListNodeContent from './FileListNodeContent.svelte'; import FileListNodeContent from './FileListNodeContent.svelte';
import FileListNodeLabel from './FileListNodeLabel.svelte'; import FileListNodeLabel from './FileListNodeLabel.svelte';
import { getContext, onDestroy } from 'svelte'; import { getContext, onDestroy } from 'svelte';
import { type ListItem, type ListTrackItem } from './FileList'; import {
ListTrackSegmentItem,
ListWaypointItem,
type ListItem,
type ListTrackItem
} from './FileList';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
import { selection } from './Selection'; import { selection } from './Selection';
@@ -25,9 +37,13 @@
? node.metadata.name ? node.metadata.name
: node instanceof Track : node instanceof Track
? node.name ?? `${$_('gpx.track')} ${(item as ListTrackItem).trackIndex + 1}` ? node.name ?? `${$_('gpx.track')} ${(item as ListTrackItem).trackIndex + 1}`
: Array.isArray(node) && node.length > 0 && node[0] instanceof Waypoint : node instanceof TrackSegment
? $_('gpx.waypoints') ? `${$_('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
? $_('gpx.waypoints')
: '';
const { verticalFileView } = settings; const { verticalFileView } = settings;
const unsubscribe = selection.subscribe(($selection) => { const unsubscribe = selection.subscribe(($selection) => {
@@ -43,6 +59,10 @@
{#if node instanceof Map} {#if node instanceof Map}
<FileListNodeContent {node} {item} /> <FileListNodeContent {node} {item} />
{:else if node instanceof TrackSegment}
<FileListNodeLabel {item} {label} />
{:else if node instanceof Waypoint}
<FileListNodeLabel {item} {label} />
{:else if recursive} {:else if recursive}
<CollapsibleTreeNode id={item.getId()} bind:this={collapsible}> <CollapsibleTreeNode id={item.getId()} bind:this={collapsible}>
<FileListNodeLabel {item} {label} slot="trigger" /> <FileListNodeLabel {item} {label} slot="trigger" />

View File

@@ -6,7 +6,6 @@
import { get, type Readable } from 'svelte/store'; import { get, type Readable } from 'svelte/store';
import FileListNodeStore from './FileListNodeStore.svelte'; import FileListNodeStore from './FileListNodeStore.svelte';
import FileListNode from './FileListNode.svelte'; import FileListNode from './FileListNode.svelte';
import FileListNodeLabel from './FileListNodeLabel.svelte';
import { ListLevel, moveItems, type ListItem } from './FileList'; import { ListLevel, moveItems, type ListItem } from './FileList';
import { selection } from './Selection'; import { selection } from './Selection';
import { _ } from 'svelte-i18n'; import { _ } from 'svelte-i18n';
@@ -237,7 +236,7 @@
class="sortable {orientation} flex {orientation === 'vertical' ? 'flex-col' : 'flex-row gap-1'}" class="sortable {orientation} flex {orientation === 'vertical' ? 'flex-col' : 'flex-row gap-1'}"
> >
{#if node instanceof Map} {#if node instanceof Map}
{#each node as [fileId, file]} {#each node as [fileId, file] (fileId)}
<div bind:this={elements[fileId]} data-id={fileId}> <div bind:this={elements[fileId]} data-id={fileId}>
<FileListNodeStore {file} /> <FileListNodeStore {file} />
</div> </div>
@@ -250,25 +249,22 @@
</div> </div>
{/if} {/if}
{:else} {:else}
{#each node.children as child, i} {#each node.children as child, i (child)}
<div bind:this={elements[i]} data-id={i}> <div bind:this={elements[i]} data-id={i}>
<FileListNode node={child} item={item.extend(i)} /> <FileListNode node={child} item={item.extend(i)} />
</div> </div>
{/each} {/each}
{/if} {/if}
{:else if node instanceof Track} {:else if node instanceof Track}
{#each node.children as child, i} {#each node.children as child, i (child)}
<div bind:this={elements[i]} data-id={i} class="ml-1"> <div bind:this={elements[i]} data-id={i} class="ml-1">
<FileListNodeLabel item={item.extend(i)} label={`${$_('gpx.segment')} ${i + 1}`} /> <FileListNode node={child} item={item.extend(i)} />
</div> </div>
{/each} {/each}
{:else if Array.isArray(node) && node.length > 0 && node[0] instanceof Waypoint} {:else if Array.isArray(node) && node.length > 0 && node[0] instanceof Waypoint}
{#each node as wpt, i} {#each node as wpt, i (wpt)}
<div bind:this={elements[i]} data-id={i} class="ml-1"> <div bind:this={elements[i]} data-id={i} class="ml-1">
<FileListNodeLabel <FileListNode node={wpt} item={item.extend(i)} />
item={item.extend(i)}
label={wpt.name ?? `${$_('gpx.waypoint')} ${i + 1}`}
/>
</div> </div>
{/each} {/each}
{/if} {/if}

View File

@@ -36,9 +36,6 @@
> >
<span <span
class="w-full text-left truncate py-1 flex flex-row items-center" class="w-full text-left truncate py-1 flex flex-row items-center"
on:click={(e) => {
e.stopPropagation(); // Avoid toggling the collapsible element
}}
on:contextmenu={(e) => { on:contextmenu={(e) => {
if (e.ctrlKey) { if (e.ctrlKey) {
// Add to selection instead of opening context menu // Add to selection instead of opening context menu

View File

@@ -14,7 +14,7 @@
{#if $file} {#if $file}
{#if recursive} {#if recursive}
<CollapsibleTree side="left" margin={4} defaultState="closed"> <CollapsibleTree side="left" margin={4} defaultState="closed" slotInsideTrigger={false}>
<FileListNode node={$file.file} item={new ListFileItem($file.file._data.id)} /> <FileListNode node={$file.file} item={new ListFileItem($file.file._data.id)} />
</CollapsibleTree> </CollapsibleTree>
{:else} {:else}