mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-11-05 05:51:08 +00:00
progress
This commit is contained in:
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
|
||||
31
website/src/lib/components/collapsible-tree/utils.svelte.ts
Normal file
31
website/src/lib/components/collapsible-tree/utils.svelte.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user