diff --git a/tauri/src/tests.rs b/tauri/src/tests.rs index 82495c4e6..720c442a7 100644 --- a/tauri/src/tests.rs +++ b/tauri/src/tests.rs @@ -531,3 +531,22 @@ fn the_parts_of_a_split_are_numbered_from_one() { assert_eq!(names, ["track (1).gpx", "track (2).gpx", "track.gpx"]); std::fs::remove_dir_all(folder).unwrap(); } + +#[test] +fn files_can_be_reordered() { + let app = App::new(); + let mut ids = vec![]; + for name in ["a", "b", "c"] { + let outcome = app.call("new_file", json!({ "name": name })); + ids.push(outcome["added"][0].as_str().unwrap().to_string()); + } + assert_eq!(app.call("file_order", json!({})), json!(ids)); + + let outcome = app.call("reorder", json!({ "fileIds": [ids[2]], "index": 0 })); + assert_eq!(outcome["changed"], true); + assert_eq!(outcome["orderChanged"], true); + assert_eq!( + app.call("file_order", json!({})), + json!([ids[2], ids[0], ids[1]]) + ); +} diff --git a/tauri/tauri.conf.json b/tauri/tauri.conf.json index 42dd411cc..d509348a1 100644 --- a/tauri/tauri.conf.json +++ b/tauri/tauri.conf.json @@ -18,7 +18,8 @@ "width": 1400, "height": 900, "minWidth": 800, - "minHeight": 600 + "minHeight": 600, + "dragDropEnabled": false } ], "security": { "csp": null } diff --git a/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte b/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte index ad9269fb9..6d478ec89 100644 --- a/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte +++ b/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte @@ -9,6 +9,7 @@ nohover = false, slotInsideTrigger = true, sticky = false, + treeState, children, }: { defaultState?: 'open' | 'closed'; @@ -21,10 +22,12 @@ * like its node. */ sticky?: boolean; + /** Which nodes are open, to keep it or to open nodes from the outside. */ + treeState?: CollapsibleTreeState; children: Snippet; } = $props(); - let open = $state(new CollapsibleTreeState(defaultState)); + let open = $state(treeState ?? new CollapsibleTreeState(defaultState)); setContext('collapsible-tree-state', open); setContext('collapsible-tree-side', side); diff --git a/website/src/lib/components/explorer/Explorer.svelte b/website/src/lib/components/explorer/Explorer.svelte index 8ba0322e9..0cf0dae67 100644 --- a/website/src/lib/components/explorer/Explorer.svelte +++ b/website/src/lib/components/explorer/Explorer.svelte @@ -1,29 +1,93 @@ -{#if folder} +{#if folder && trees}
{i18n._('menu.explorer')}
-
+
{#key folder} - - + + {#snippet trigger()} @@ -34,8 +98,7 @@ {#if opened}
- - +
diff --git a/website/src/lib/components/explorer/ExplorerTree.svelte b/website/src/lib/components/explorer/ExplorerTree.svelte index 4f1576ff3..c1b371c94 100644 --- a/website/src/lib/components/explorer/ExplorerTree.svelte +++ b/website/src/lib/components/explorer/ExplorerTree.svelte @@ -2,6 +2,9 @@ import { CollapsibleTreeNode } from '$lib/components/collapsible-tree/index'; import { Button } from '$lib/components/ui/button'; import { FileText } from '@lucide/svelte'; + import { getContext } from 'svelte'; + import { clickedInExplorer } from './state.svelte'; + import { stickyKey, type StickyLevel } from '$lib/components/collapsible-tree/sticky'; import ExplorerTree from './ExplorerTree.svelte'; import { openPath } from '$lib/actions/file-actions'; import { engine, type FolderEntry } from '$lib/engine'; @@ -16,6 +19,8 @@ } = $props(); const { workspace, selection, folderVersion } = engine; + // the rows of the folders that stick above the files of this one + const level = getContext(stickyKey); let entries: FolderEntry[] = $state([]); @@ -35,6 +40,9 @@ /** Opens the file, or selects it if it is open already. */ function open(entry: FolderEntry, id: string | undefined) { + // it is in view, the explorer does not have to scroll to it (which would move the rows + // under the pointer, and leave the one it was on highlighted) + clickedInExplorer.path = entry.path; if (id !== undefined) { engine.select([id]); } else { @@ -67,6 +75,8 @@ ? 'bg-accent' : ''} {id === undefined ? 'text-muted-foreground' : ''}" title={entry.path} + data-path={entry.path} + style="scroll-margin-top: {level.top}px" onclick={() => open(entry, id)} > diff --git a/website/src/lib/components/explorer/state.svelte.ts b/website/src/lib/components/explorer/state.svelte.ts new file mode 100644 index 000000000..05976e966 --- /dev/null +++ b/website/src/lib/components/explorer/state.svelte.ts @@ -0,0 +1,2 @@ +/** The file that the user just selected by clicking it in the explorer, until the selection changes. */ +export const clickedInExplorer: { path: string | undefined } = $state({ path: undefined });