From 970cf7d1e16b24b29dc6c9f21464f6862f6d74fc Mon Sep 17 00:00:00 2001 From: vcoppe Date: Sat, 17 Oct 2026 16:39:41 +0200 Subject: [PATCH] improved sticky folders --- .../collapsible-tree/CollapsibleTree.svelte | 21 ++++- .../CollapsibleTreeNode.svelte | 64 +++++++++++++-- .../lib/components/collapsible-tree/sticky.ts | 79 +++++++++++++++++++ .../collapsible-tree/utils.svelte.ts | 21 +++++ .../lib/components/explorer/Explorer.svelte | 9 +-- .../components/explorer/ExplorerTree.svelte | 12 +-- .../lib/components/explorer/sticky.test.ts | 17 ---- website/src/lib/components/explorer/sticky.ts | 11 --- 8 files changed, 180 insertions(+), 54 deletions(-) create mode 100644 website/src/lib/components/collapsible-tree/sticky.ts delete mode 100644 website/src/lib/components/explorer/sticky.test.ts delete mode 100644 website/src/lib/components/explorer/sticky.ts diff --git a/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte b/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte index 4c55f0a65..ad9269fb9 100644 --- a/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte +++ b/website/src/lib/components/collapsible-tree/CollapsibleTree.svelte @@ -1,18 +1,26 @@ {@render children()} diff --git a/website/src/lib/components/collapsible-tree/CollapsibleTreeNode.svelte b/website/src/lib/components/collapsible-tree/CollapsibleTreeNode.svelte index ddd11dac6..1352fabd6 100644 --- a/website/src/lib/components/collapsible-tree/CollapsibleTreeNode.svelte +++ b/website/src/lib/components/collapsible-tree/CollapsibleTreeNode.svelte @@ -5,37 +5,58 @@ import { getContext, setContext, type Snippet } from 'svelte'; import type { ClassValue } from 'svelte/elements'; import type { CollapsibleTreeState } from './utils.svelte'; + import { stickyKey, stickyZIndex, trackStuck, type StickyLevel } from './sticky'; const props: { 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(); - let state = getContext('collapsible-tree-state'); + let treeState = getContext('collapsible-tree-state'); let side = getContext<'left' | 'right'>('collapsible-tree-side'); let nohover = getContext('collapsible-tree-nohover'); let slotInsideTrigger = getContext('collapsible-tree-slot-inside-trigger'); let parentId = getContext('collapsible-tree-parent-id'); + // where the row sticks, under the rows of the nodes this one is in + let level = getContext(stickyKey); let fullId = `${parentId}.${props.id}`; setContext('collapsible-tree-parent-id', fullId); - let open = state.get(fullId); + let open = treeState.get(fullId); + + // the rows of the nodes in this one stick under this row, whatever its height + let rowHeight = $state(0); + setContext(stickyKey, { + get enabled() { + return level.enabled; + }, + get top() { + return level.top + rowHeight; + }, + get depth() { + return level.depth + 1; + }, + get stuckRows() { + return level.stuckRows; + }, + }); + // the row is on top of the content that goes under it + let stuck = $state(false); + // only the deepest row that is stuck has a border: the rows above it run into it + let bordered = $derived(stuck && level.stuckRows.deepest === level.depth); export function openNode() { open.current = true; } - +{#snippet row()} {#if slotInsideTrigger} - +