This commit is contained in:
vcoppe
2025-06-21 21:07:36 +02:00
parent f0230d4634
commit 1cc07901f6
803 changed files with 7937 additions and 6329 deletions

View File

@@ -1,15 +1,23 @@
<script lang="ts">
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
import { setContext, type Snippet } from 'svelte';
import { CollapsibleTreeState } from './utils.svelte';
export let defaultState: 'open' | 'closed' = 'open';
export let side: 'left' | 'right' = 'right';
export let nohover: boolean = false;
export let slotInsideTrigger: boolean = true;
const {
defaultState = 'open',
side = 'right',
nohover = false,
slotInsideTrigger = true,
children,
}: {
defaultState?: 'open' | 'closed';
side?: 'left' | 'right';
nohover?: boolean;
slotInsideTrigger?: boolean;
children: Snippet;
} = $props();
let open = writable<Record<string, boolean>>({});
let open = $state(new CollapsibleTreeState(defaultState));
setContext('collapsible-tree-default-state', defaultState);
setContext('collapsible-tree-state', open);
setContext('collapsible-tree-side', side);
setContext('collapsible-tree-nohover', nohover);
@@ -17,4 +25,4 @@
setContext('collapsible-tree-slot-inside-trigger', slotInsideTrigger);
</script>
<slot />
{@render children()}

View File

@@ -1,40 +1,35 @@
<script lang="ts">
import * as Collapsible from '$lib/components/ui/collapsible';
import { Button } from '$lib/components/ui/button';
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-svelte';
import { getContext, onMount, setContext } from 'svelte';
import { get, type Writable } from 'svelte/store';
import { ChevronDown, ChevronLeft, ChevronRight } from '@lucide/svelte';
import { getContext, setContext, type Snippet } from 'svelte';
import type { ClassValue } from 'svelte/elements';
import type { CollapsibleTreeState } from './utils.svelte';
export let id: string | number;
const props: {
id: string | number;
class?: ClassValue;
trigger: Snippet;
content: Snippet;
} = $props();
let defaultState = getContext<'open' | 'closed'>('collapsible-tree-default-state');
let open = getContext<Writable<Record<string, boolean>>>('collapsible-tree-state');
let state = getContext<CollapsibleTreeState>('collapsible-tree-state');
let side = getContext<'left' | 'right'>('collapsible-tree-side');
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 fullId = `${parentId}.${id}`;
let fullId = `${parentId}.${props.id}`;
setContext('collapsible-tree-parent-id', fullId);
onMount(() => {
if (!get(open).hasOwnProperty(fullId)) {
open.update((value) => {
value[fullId] = defaultState === 'open';
return value;
});
}
});
let open = state.get(fullId);
export function openNode() {
open.update((value) => {
value[fullId] = true;
return value;
});
open.current = true;
}
</script>
<Collapsible.Root bind:open={$open[fullId]} class={$$props.class ?? ''}>
<Collapsible.Root bind:open={open.current} class={props.class}>
{#if slotInsideTrigger}
<Collapsible.Trigger class="w-full">
<Button
@@ -46,15 +41,15 @@
: ''} pointer-events-none"
>
{#if side === 'left'}
{#if $open[fullId]}
{#if open.current}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronRight size="16" class="shrink-0" />
{/if}
{/if}
<slot name="trigger" />
{@render props.trigger()}
{#if side === 'right'}
{#if $open[fullId]}
{#if open.current}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronLeft size="16" class="shrink-0" />
@@ -71,17 +66,17 @@
>
{#if side === 'left'}
<Collapsible.Trigger>
{#if $open[fullId]}
{#if open.current}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronRight size="16" class="shrink-0" />
{/if}
</Collapsible.Trigger>
{/if}
<slot name="trigger" />
{@render props.trigger()}
{#if side === 'right'}
<Collapsible.Trigger>
{#if $open[fullId]}
{#if open.current}
<ChevronDown size="16" class="shrink-0" />
{:else}
<ChevronLeft size="16" class="shrink-0" />
@@ -92,6 +87,6 @@
{/if}
<Collapsible.Content class="ml-2">
<slot name="content" />
{@render props.content()}
</Collapsible.Content>
</Collapsible.Root>

View File

@@ -0,0 +1,31 @@
export class CollapsibleNodeState {
private _open: boolean;
constructor(defaultState: 'open' | 'closed') {
this._open = $state(defaultState === 'open');
}
get current(): boolean {
return this._open;
}
set current(value: boolean) {
this._open = value;
}
}
export class CollapsibleTreeState {
private _open: Record<string, CollapsibleNodeState> = {};
private _defaultState: 'open' | 'closed';
constructor(defaultState: 'open' | 'closed') {
this._defaultState = defaultState;
}
get(id: string): CollapsibleNodeState {
if (this._open[id] === undefined) {
this._open[id] = new CollapsibleNodeState(this._defaultState);
}
return this._open[id];
}
}