mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-10-19 09:22:12 +00:00
sticky folders
This commit is contained in:
1 parent
8fa88680b3
commit
593b43e433
5 files changed
+48
-4
No files matched your search
@@ -10,6 +10,9 @@
|
||||
id: string | number;
|
||||
class?: ClassValue;
|
||||
trigger: Snippet;
|
||||
/** Classes and style of the trigger row (when the trigger holds the content), to make it stick. */
|
||||
triggerClass?: ClassValue;
|
||||
triggerStyle?: string;
|
||||
/** Gets whether the node is open, for contents that are only worth building then. */
|
||||
content: Snippet<[boolean]>;
|
||||
} = $props();
|
||||
@@ -32,7 +35,7 @@
|
||||
|
||||
<Collapsible.Root bind:open={open.current} class={props.class}>
|
||||
{#if slotInsideTrigger}
|
||||
<Collapsible.Trigger class="w-full">
|
||||
<Collapsible.Trigger class={['w-full', props.triggerClass]} style={props.triggerStyle}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { CollapsibleTree, CollapsibleTreeNode } from '$lib/components/collapsible-tree/index';
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area/index';
|
||||
import ExplorerTree from './ExplorerTree.svelte';
|
||||
import { stickyRowStyle } from './sticky';
|
||||
import { i18n } from '$lib/i18n/i18n.svelte';
|
||||
import { engine } from '$lib/engine';
|
||||
import { folderName } from '$lib/engine/workspace';
|
||||
@@ -24,7 +25,11 @@
|
||||
{#key folder}
|
||||
<!-- the folder itself, which is open at first -->
|
||||
<CollapsibleTree side="left" defaultState="open">
|
||||
<CollapsibleTreeNode id="folder">
|
||||
<CollapsibleTreeNode
|
||||
id="folder"
|
||||
triggerClass="sticky flex items-center bg-background"
|
||||
triggerStyle={stickyRowStyle(0)}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<span class="grow truncate text-left" title={folder}>
|
||||
{folderName(folder)}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { FileText } from '@lucide/svelte';
|
||||
import ExplorerTree from './ExplorerTree.svelte';
|
||||
import { stickyRowStyle } from './sticky';
|
||||
import { openPath } from '$lib/actions/file-actions';
|
||||
import { engine, type FolderEntry } from '$lib/engine';
|
||||
import { selectedFileIds } from '$lib/engine/selection-helpers';
|
||||
@@ -10,9 +11,12 @@
|
||||
|
||||
let {
|
||||
path,
|
||||
depth = 1,
|
||||
}: {
|
||||
/** The folder whose content is listed, the open folder if it is not given. */
|
||||
path?: string;
|
||||
/** How many folders deep the folders listed are (the open folder is 0). */
|
||||
depth?: number;
|
||||
} = $props();
|
||||
|
||||
const { workspace, selection, folderVersion } = engine;
|
||||
@@ -45,7 +49,11 @@
|
||||
|
||||
{#each entries as entry (entry.path)}
|
||||
{#if entry.isDir}
|
||||
<CollapsibleTreeNode id={entry.name}>
|
||||
<CollapsibleTreeNode
|
||||
id={entry.name}
|
||||
triggerClass="sticky flex items-center bg-background"
|
||||
triggerStyle={stickyRowStyle(depth)}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<span class="grow truncate text-left">{entry.name}</span>
|
||||
{/snippet}
|
||||
@@ -53,7 +61,7 @@
|
||||
<!-- the content of a folder is only read when it is opened -->
|
||||
{#if opened}
|
||||
<div class="ml-4">
|
||||
<ExplorerTree path={entry.path} />
|
||||
<ExplorerTree path={entry.path} depth={depth + 1} />
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { describe, it } from 'node:test';
|
||||
import { rowHeight, stickyRowStyle } from './sticky';
|
||||
|
||||
describe('stickyRowStyle', () => {
|
||||
it('puts each row under the ones of the folders it is in', () => {
|
||||
assert.match(stickyRowStyle(0), /top: 0px/);
|
||||
assert.match(stickyRowStyle(2), new RegExp(`top: ${2 * rowHeight}px`));
|
||||
});
|
||||
|
||||
it('puts the rows of the folders above over the rows below', () => {
|
||||
const zIndex = (depth: number) => Number(/z-index: (\d+)/.exec(stickyRowStyle(depth))![1]);
|
||||
assert.ok(zIndex(0) > zIndex(1));
|
||||
assert.ok(zIndex(1) > zIndex(5));
|
||||
assert.ok(zIndex(500) >= 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
/** The height of a row of the explorer. */
|
||||
export const rowHeight = 28;
|
||||
|
||||
/**
|
||||
* The style of the row of a folder that is `depth` folders deep (0 for the open folder), to stick
|
||||
* under the rows of the folders it is in while any of its content can be seen: the rows of the
|
||||
* folders above are above it too.
|
||||
*/
|
||||
export function stickyRowStyle(depth: number): string {
|
||||
return `top: ${depth * rowHeight}px; height: ${rowHeight}px; z-index: ${Math.max(100 - depth, 1)};`;
|
||||
}
|
||||
Reference in new issue
Block a user