2024-05-17 15:02:45 +02:00
|
|
|
<script lang="ts">
|
2025-10-17 23:54:45 +02:00
|
|
|
import { getContext, onDestroy, onMount } from 'svelte';
|
2024-09-12 11:13:55 +02:00
|
|
|
import FileListNodeStore from './FileListNodeStore.svelte';
|
|
|
|
|
import FileListNode from './FileListNode.svelte';
|
2025-10-26 12:12:23 +01:00
|
|
|
import FileListNodeContent from './FileListNodeContent.svelte';
|
2026-10-08 18:34:19 +02:00
|
|
|
import { allowedMoves, dragging, SortableFileList, type ListLevel } from './sortable-file-list';
|
2026-10-04 11:00:46 +02:00
|
|
|
import { engine, type FileState } from '$lib/engine';
|
2026-10-11 11:18:36 +02:00
|
|
|
import type { FileTreeNode } from '$lib/engine/selection-helpers';
|
2024-05-17 15:02:45 +02:00
|
|
|
|
2025-10-17 23:54:45 +02:00
|
|
|
let {
|
2026-10-04 11:00:46 +02:00
|
|
|
fileState,
|
2025-10-17 23:54:45 +02:00
|
|
|
node,
|
|
|
|
|
waypointRoot = false,
|
|
|
|
|
}: {
|
2026-10-04 11:00:46 +02:00
|
|
|
/** State of the file of the node (not needed for the root). */
|
|
|
|
|
fileState?: FileState;
|
|
|
|
|
/** The node whose children are listed, `null` for the list of the files. */
|
|
|
|
|
node: FileTreeNode | null;
|
|
|
|
|
/** List the node standing for the waypoints of the file, instead of its tracks. */
|
2025-10-17 23:54:45 +02:00
|
|
|
waypointRoot?: boolean;
|
|
|
|
|
} = $props();
|
2024-05-17 15:02:45 +02:00
|
|
|
|
2026-10-04 11:00:46 +02:00
|
|
|
const { files, order } = engine;
|
|
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
let container: HTMLElement;
|
|
|
|
|
let sortableLevel: ListLevel =
|
2026-10-04 11:00:46 +02:00
|
|
|
node === null
|
2026-10-08 18:34:19 +02:00
|
|
|
? 'file'
|
2026-10-04 11:00:46 +02:00
|
|
|
: node.type === 'file'
|
2024-09-12 11:13:55 +02:00
|
|
|
? waypointRoot
|
2026-10-08 18:34:19 +02:00
|
|
|
? 'waypoints'
|
|
|
|
|
: 'track'
|
2026-10-04 11:00:46 +02:00
|
|
|
: node.type === 'waypoints'
|
2026-10-08 18:34:19 +02:00
|
|
|
? 'waypoint'
|
|
|
|
|
: 'segment';
|
2024-09-12 11:13:55 +02:00
|
|
|
let orientation = getContext<'vertical' | 'horizontal'>('orientation');
|
2024-05-21 13:22:14 +02:00
|
|
|
|
2025-10-26 12:12:23 +01:00
|
|
|
let canDrop = $derived($dragging !== null && allowedMoves[$dragging].includes(sortableLevel));
|
2024-07-03 22:42:08 +02:00
|
|
|
|
2025-10-26 12:12:23 +01:00
|
|
|
let sortable: SortableFileList;
|
2024-05-21 22:37:52 +02:00
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
onMount(() => {
|
2026-10-04 11:00:46 +02:00
|
|
|
sortable = new SortableFileList(container, node, waypointRoot, sortableLevel, orientation);
|
2024-09-12 11:13:55 +02:00
|
|
|
});
|
2024-05-24 13:16:41 +02:00
|
|
|
|
2025-11-02 16:01:17 +01:00
|
|
|
$effect(() => {
|
2026-10-04 11:00:46 +02:00
|
|
|
// the elements of the list changed
|
|
|
|
|
$order;
|
|
|
|
|
fileState;
|
|
|
|
|
if (sortable) {
|
2025-11-02 16:01:17 +01:00
|
|
|
sortable.updateElements();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
onDestroy(() => {
|
2025-10-26 12:12:23 +01:00
|
|
|
sortable.destroy();
|
2024-09-12 11:13:55 +02:00
|
|
|
});
|
2024-05-17 15:02:45 +02:00
|
|
|
</script>
|
|
|
|
|
|
2024-05-21 17:47:08 +02:00
|
|
|
<div
|
2024-09-12 11:13:55 +02:00
|
|
|
bind:this={container}
|
|
|
|
|
class="sortable {orientation} flex {orientation === 'vertical'
|
|
|
|
|
? 'flex-col'
|
|
|
|
|
: 'flex-row gap-1'} {canDrop ? 'min-h-5' : ''}"
|
2024-05-21 17:47:08 +02:00
|
|
|
>
|
2026-10-04 11:00:46 +02:00
|
|
|
{#if node === null}
|
|
|
|
|
{#each $order as fileId (fileId)}
|
|
|
|
|
{@const file = $files.get(fileId)}
|
|
|
|
|
{#if file}
|
|
|
|
|
<div data-id={fileId}>
|
|
|
|
|
<FileListNodeStore {file} />
|
2024-09-12 11:13:55 +02:00
|
|
|
</div>
|
|
|
|
|
{/if}
|
2026-10-04 11:00:46 +02:00
|
|
|
{/each}
|
|
|
|
|
{:else if fileState}
|
|
|
|
|
{#if node.type === 'file'}
|
|
|
|
|
{#if waypointRoot}
|
|
|
|
|
{#if fileState.structure.waypoints.length > 0}
|
|
|
|
|
<div data-id="waypoints">
|
|
|
|
|
<FileListNode
|
|
|
|
|
{fileState}
|
|
|
|
|
node={{ type: 'waypoints', fileId: node.fileId }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
{:else}
|
|
|
|
|
{#each fileState.structure.tracks as track (track.id)}
|
|
|
|
|
<div data-id={track.id}>
|
|
|
|
|
<FileListNode
|
|
|
|
|
{fileState}
|
|
|
|
|
node={{ type: 'track', fileId: node.fileId, trackId: track.id }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
{/if}
|
|
|
|
|
{:else if node.type === 'waypoints'}
|
|
|
|
|
{#each fileState.structure.waypoints as waypoint (waypoint.id)}
|
|
|
|
|
<div data-id={waypoint.id} class="ml-1">
|
|
|
|
|
<FileListNode
|
|
|
|
|
{fileState}
|
|
|
|
|
node={{ type: 'waypoint', fileId: node.fileId, waypointId: waypoint.id }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
{:else if node.type === 'track'}
|
|
|
|
|
{#each fileState.structure.tracks.find((t) => t.id === node.trackId)?.segments ?? [] as segment (segment.id)}
|
|
|
|
|
<div data-id={segment.id} class="ml-1">
|
|
|
|
|
<FileListNode
|
|
|
|
|
{fileState}
|
|
|
|
|
node={{
|
|
|
|
|
type: 'segment',
|
|
|
|
|
fileId: node.fileId,
|
|
|
|
|
trackId: node.trackId,
|
|
|
|
|
segmentId: segment.id,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2024-09-12 11:13:55 +02:00
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
{/if}
|
|
|
|
|
{/if}
|
2024-05-17 15:02:45 +02:00
|
|
|
</div>
|
|
|
|
|
|
2026-10-04 11:00:46 +02:00
|
|
|
{#if fileState && node?.type === 'file' && !waypointRoot}
|
|
|
|
|
<FileListNodeContent {fileState} {node} waypointRoot={true} />
|
2024-05-21 13:22:14 +02:00
|
|
|
{/if}
|
|
|
|
|
|
2024-05-17 15:02:45 +02:00
|
|
|
<style lang="postcss">
|
2025-06-21 21:07:36 +02:00
|
|
|
@reference "../../../app.css";
|
|
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
.sortable > div {
|
|
|
|
|
@apply rounded-md;
|
|
|
|
|
@apply h-fit;
|
|
|
|
|
@apply leading-none;
|
|
|
|
|
}
|
2024-05-21 13:22:14 +02:00
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
.vertical :global(button) {
|
2025-11-14 18:45:49 +01:00
|
|
|
@apply hover:bg-[var(--selection)];
|
2024-09-12 11:13:55 +02:00
|
|
|
}
|
2024-07-19 10:09:49 +02:00
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
.vertical :global(.sortable-selected) {
|
2025-11-14 18:45:49 +01:00
|
|
|
@apply bg-[var(--selection)];
|
2024-09-12 11:13:55 +02:00
|
|
|
}
|
2024-05-21 17:47:08 +02:00
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
.horizontal :global(button) {
|
2025-11-14 18:45:49 +01:00
|
|
|
@apply bg-[var(--selection)];
|
|
|
|
|
@apply hover:bg-background;
|
2024-09-12 11:13:55 +02:00
|
|
|
}
|
2024-05-21 17:47:08 +02:00
|
|
|
|
2024-09-12 11:13:55 +02:00
|
|
|
.horizontal :global(.sortable-selected button) {
|
|
|
|
|
@apply bg-background;
|
|
|
|
|
}
|
2024-05-17 15:02:45 +02:00
|
|
|
</style>
|