integrate pane stack into sidebar store
sidebar now uses PaneStackStore internally - openWithComponent creates root pane, and child components can push/pop via context. simplified Sidebar.svelte to render PaneStack when stack has items.
This commit is contained in:
parent
096214bc52
commit
d907e32d12
5 changed files with 205 additions and 176 deletions
|
|
@ -1,30 +1,30 @@
|
||||||
<svelte:options runes={true} />
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PaneConfig } from '$lib/stores/paneStack.svelte'
|
import {
|
||||||
|
type PaneConfig,
|
||||||
|
type PaneStackStore,
|
||||||
|
setPaneStackContext
|
||||||
|
} from '$lib/stores/paneStack.svelte'
|
||||||
import SidebarHeader from './SidebarHeader.svelte'
|
import SidebarHeader from './SidebarHeader.svelte'
|
||||||
import Button from './Button.svelte'
|
import Button from './Button.svelte'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** Array of panes to render */
|
/** The pane stack store to use */
|
||||||
panes: PaneConfig[]
|
stack: PaneStackStore
|
||||||
/** Whether an animation is in progress */
|
|
||||||
isAnimating?: boolean
|
|
||||||
/** Direction of animation */
|
|
||||||
animationDirection?: 'push' | 'pop' | null
|
|
||||||
/** Callback to pop the current pane */
|
|
||||||
onPop?: () => void
|
|
||||||
/** Callback to close the entire sidebar (for root pane close button) */
|
/** Callback to close the entire sidebar (for root pane close button) */
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { stack, onClose }: Props = $props()
|
||||||
panes,
|
|
||||||
isAnimating = false,
|
// Set context so child components can access the pane stack
|
||||||
animationDirection = null,
|
setPaneStackContext(stack)
|
||||||
onPop,
|
|
||||||
onClose
|
// Derive values from the stack
|
||||||
}: Props = $props()
|
const panes = $derived(stack.panes)
|
||||||
|
const isAnimating = $derived(stack.isAnimating)
|
||||||
|
const animationDirection = $derived(stack.animationDirection)
|
||||||
|
|
||||||
function handleBack(pane: PaneConfig, index: number) {
|
function handleBack(pane: PaneConfig, index: number) {
|
||||||
if (index === 0 && pane.onback) {
|
if (index === 0 && pane.onback) {
|
||||||
|
|
@ -33,9 +33,9 @@
|
||||||
} else if (index === 0 && onClose) {
|
} else if (index === 0 && onClose) {
|
||||||
// Root pane, close sidebar
|
// Root pane, close sidebar
|
||||||
onClose()
|
onClose()
|
||||||
} else if (onPop) {
|
} else {
|
||||||
// Non-root pane, pop from stack
|
// Non-root pane, pop from stack
|
||||||
onPop()
|
stack.pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +93,8 @@
|
||||||
</SidebarHeader>
|
</SidebarHeader>
|
||||||
|
|
||||||
<div class="pane-content">
|
<div class="pane-content">
|
||||||
<svelte:component this={pane.component} {...pane.props ?? {}} />
|
{@const PaneComponent = pane.component}
|
||||||
|
<PaneComponent {...pane.props ?? {}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,33 @@
|
||||||
<svelte:options runes={true} />
|
<svelte:options runes={true} />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SidebarHeader from './SidebarHeader.svelte'
|
|
||||||
import Button from './Button.svelte'
|
|
||||||
import { SIDEBAR_WIDTH } from '$lib/stores/sidebar.svelte'
|
import { SIDEBAR_WIDTH } from '$lib/stores/sidebar.svelte'
|
||||||
|
import PaneStack from './PaneStack.svelte'
|
||||||
|
import type { PaneStackStore } from '$lib/stores/paneStack.svelte'
|
||||||
import type { Snippet } from 'svelte'
|
import type { Snippet } from 'svelte'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** Whether the sidebar is open */
|
/** Whether the sidebar is open */
|
||||||
open?: boolean
|
open?: boolean
|
||||||
/** Title for the sidebar header */
|
/** The pane stack to render */
|
||||||
title?: string
|
stack?: PaneStackStore
|
||||||
/** Callback when close is requested (camelCase preferred) */
|
/** Callback when close is requested */
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
/** Callback when close is requested (lowercase, deprecated - use onClose) */
|
/** Legacy: Content to render in the sidebar (when not using pane stack) */
|
||||||
onclose?: () => void
|
|
||||||
/** Callback when back is requested (shows arrow instead of X) */
|
|
||||||
onback?: () => void
|
|
||||||
/** Callback when save/done is requested */
|
|
||||||
onsave?: () => void
|
|
||||||
/** Label for the save button */
|
|
||||||
saveLabel?: string
|
|
||||||
/** Element for styling the save button */
|
|
||||||
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
|
||||||
/** Content to render in the sidebar */
|
|
||||||
children?: Snippet
|
children?: Snippet
|
||||||
/** Whether the sidebar content should scroll. Default true. */
|
|
||||||
scrollable?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { open = false, title, onClose, onclose, onback, onsave, saveLabel = 'Done', element, children, scrollable = true }: Props = $props()
|
const { open = false, stack, onClose, children }: Props = $props()
|
||||||
|
|
||||||
// Support both onClose (camelCase) and onclose (lowercase) for backward compatibility
|
|
||||||
const handleClose = $derived(onClose ?? onclose)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet leftAccessory()}
|
|
||||||
{#if onback}
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="small"
|
|
||||||
iconOnly
|
|
||||||
icon="arrow-left"
|
|
||||||
onclick={onback}
|
|
||||||
aria-label="Go back"
|
|
||||||
/>
|
|
||||||
{:else if handleClose}
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="small"
|
|
||||||
iconOnly
|
|
||||||
icon="close"
|
|
||||||
onclick={handleClose}
|
|
||||||
aria-label="Close sidebar"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
{#snippet rightAccessory()}
|
|
||||||
{#if onsave}
|
|
||||||
<Button variant="ghost" size="small" {element} elementStyle={!!element} onclick={onsave}>
|
|
||||||
{saveLabel}
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
<aside class="sidebar" class:open style:--sidebar-width={SIDEBAR_WIDTH}>
|
<aside class="sidebar" class:open style:--sidebar-width={SIDEBAR_WIDTH}>
|
||||||
{#if title}
|
{#if stack && !stack.isEmpty}
|
||||||
<SidebarHeader {title} {leftAccessory} {rightAccessory} />
|
<PaneStack {stack} {onClose} />
|
||||||
{/if}
|
{:else if children}
|
||||||
|
<div class="sidebar-content scrollable">
|
||||||
<div class="sidebar-content" class:scrollable>
|
|
||||||
{#if children}
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
{/if}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Component } from 'svelte'
|
import type { Component } from 'svelte'
|
||||||
|
import { getContext, setContext } from 'svelte'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pane Stack Store
|
* Pane Stack Store
|
||||||
|
|
@ -9,6 +10,9 @@ import type { Component } from 'svelte'
|
||||||
|
|
||||||
export type ElementType = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
export type ElementType = 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
||||||
|
|
||||||
|
/** Context key for pane stack */
|
||||||
|
const PANE_STACK_CONTEXT_KEY = Symbol('pane-stack')
|
||||||
|
|
||||||
export interface PaneConfig {
|
export interface PaneConfig {
|
||||||
/** Unique identifier for this pane */
|
/** Unique identifier for this pane */
|
||||||
id: string
|
id: string
|
||||||
|
|
@ -39,7 +43,7 @@ interface PaneStackState {
|
||||||
animationDirection: 'push' | 'pop' | null
|
animationDirection: 'push' | 'pop' | null
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaneStackStore {
|
export class PaneStackStore {
|
||||||
state = $state<PaneStackState>({
|
state = $state<PaneStackState>({
|
||||||
panes: [],
|
panes: [],
|
||||||
isAnimating: false,
|
isAnimating: false,
|
||||||
|
|
@ -195,3 +199,36 @@ export function createPaneStack() {
|
||||||
|
|
||||||
// Default global pane stack for sidebar
|
// Default global pane stack for sidebar
|
||||||
export const paneStack = new PaneStackStore()
|
export const paneStack = new PaneStackStore()
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Context API for child components
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the pane stack in context for child components
|
||||||
|
* Call this in the parent component that owns the pane stack
|
||||||
|
*/
|
||||||
|
export function setPaneStackContext(stack: PaneStackStore) {
|
||||||
|
setContext(PANE_STACK_CONTEXT_KEY, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the pane stack from context
|
||||||
|
* Call this in child components that need to push/pop panes
|
||||||
|
* Returns undefined if no pane stack is in context
|
||||||
|
*/
|
||||||
|
export function getPaneStackContext(): PaneStackStore | undefined {
|
||||||
|
return getContext<PaneStackStore | undefined>(PANE_STACK_CONTEXT_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the pane stack from context, throwing if not found
|
||||||
|
* Use this when you know the pane stack should be available
|
||||||
|
*/
|
||||||
|
export function usePaneStack(): PaneStackStore {
|
||||||
|
const stack = getPaneStackContext()
|
||||||
|
if (!stack) {
|
||||||
|
throw new Error('usePaneStack must be used within a PaneStack context')
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,56 @@
|
||||||
import type { Snippet, Component } from 'svelte'
|
import type { Snippet, Component } from 'svelte'
|
||||||
|
import {
|
||||||
|
PaneStackStore,
|
||||||
|
type PaneConfig,
|
||||||
|
type ElementType
|
||||||
|
} from '$lib/stores/paneStack.svelte'
|
||||||
|
|
||||||
// Standard sidebar width
|
// Standard sidebar width
|
||||||
export const SIDEBAR_WIDTH = '420px'
|
export const SIDEBAR_WIDTH = '420px'
|
||||||
|
|
||||||
interface SidebarState {
|
|
||||||
open: boolean
|
|
||||||
title: string | undefined
|
|
||||||
content: Snippet | undefined
|
|
||||||
component: Component<any, any, any> | undefined
|
|
||||||
componentProps: Record<string, any> | undefined
|
|
||||||
scrollable: boolean
|
|
||||||
activeItemId: string | undefined
|
|
||||||
onsave: (() => void) | undefined
|
|
||||||
saveLabel: string | undefined
|
|
||||||
element: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light' | undefined
|
|
||||||
onback: (() => void) | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OpenWithComponentOptions {
|
interface OpenWithComponentOptions {
|
||||||
scrollable?: boolean
|
scrollable?: boolean
|
||||||
onsave?: () => void
|
onsave?: () => void
|
||||||
saveLabel?: string
|
saveLabel?: string
|
||||||
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
element?: ElementType
|
||||||
onback?: () => void
|
onback?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SidebarState {
|
||||||
|
open: boolean
|
||||||
|
activeItemId: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SidebarStore
|
||||||
|
*
|
||||||
|
* Manages the sidebar open/close state and its pane stack.
|
||||||
|
* The sidebar always uses a pane stack internally - even a "single pane"
|
||||||
|
* is just a stack with one item.
|
||||||
|
*/
|
||||||
class SidebarStore {
|
class SidebarStore {
|
||||||
state = $state<SidebarState>({
|
state = $state<SidebarState>({
|
||||||
open: false,
|
open: false,
|
||||||
title: undefined,
|
activeItemId: undefined
|
||||||
content: undefined,
|
|
||||||
component: undefined,
|
|
||||||
componentProps: undefined,
|
|
||||||
scrollable: true,
|
|
||||||
activeItemId: undefined,
|
|
||||||
onsave: undefined,
|
|
||||||
saveLabel: undefined,
|
|
||||||
element: undefined,
|
|
||||||
onback: undefined
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/** The pane stack for sidebar navigation */
|
||||||
|
paneStack = new PaneStackStore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the sidebar with a snippet content (legacy API)
|
||||||
|
*/
|
||||||
open(title?: string, content?: Snippet, scrollable = true) {
|
open(title?: string, content?: Snippet, scrollable = true) {
|
||||||
|
// For snippet content, we don't use the pane stack
|
||||||
|
// This is for backwards compatibility
|
||||||
this.state.open = true
|
this.state.open = true
|
||||||
this.state.title = title
|
// Clear any existing panes
|
||||||
this.state.content = content
|
this.paneStack.clear()
|
||||||
this.state.component = undefined
|
|
||||||
this.state.componentProps = undefined
|
|
||||||
this.state.scrollable = scrollable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the sidebar with a component as the root pane
|
||||||
|
*/
|
||||||
openWithComponent(
|
openWithComponent(
|
||||||
title: string,
|
title: string,
|
||||||
component: Component<any, any, any>,
|
component: Component<any, any, any>,
|
||||||
|
|
@ -59,106 +61,153 @@ class SidebarStore {
|
||||||
const opts: OpenWithComponentOptions =
|
const opts: OpenWithComponentOptions =
|
||||||
typeof options === 'boolean' ? { scrollable: options } : options ?? {}
|
typeof options === 'boolean' ? { scrollable: options } : options ?? {}
|
||||||
|
|
||||||
|
// Build the pane config
|
||||||
|
const paneConfig: PaneConfig = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
title,
|
||||||
|
component,
|
||||||
|
props,
|
||||||
|
onback: opts.onback,
|
||||||
|
scrollable: opts.scrollable ?? true,
|
||||||
|
action:
|
||||||
|
opts.onsave ?
|
||||||
|
{
|
||||||
|
label: opts.saveLabel ?? 'Done',
|
||||||
|
handler: opts.onsave,
|
||||||
|
element: opts.element
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the pane stack with this as the root pane
|
||||||
|
this.paneStack.reset(paneConfig)
|
||||||
this.state.open = true
|
this.state.open = true
|
||||||
this.state.title = title
|
|
||||||
this.state.component = component
|
|
||||||
this.state.componentProps = props
|
|
||||||
this.state.content = undefined
|
|
||||||
this.state.scrollable = opts.scrollable ?? true
|
|
||||||
this.state.onsave = opts.onsave
|
|
||||||
this.state.saveLabel = opts.saveLabel
|
|
||||||
this.state.element = opts.element
|
|
||||||
this.state.onback = opts.onback
|
|
||||||
// Extract and store the item ID if it's a details sidebar
|
// Extract and store the item ID if it's a details sidebar
|
||||||
if (props?.item?.id) {
|
if (props?.item?.id) {
|
||||||
this.state.activeItemId = String(props.item.id)
|
this.state.activeItemId = String(props.item.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a new pane onto the sidebar's pane stack
|
||||||
|
*/
|
||||||
|
push(config: PaneConfig) {
|
||||||
|
this.paneStack.push(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pop the current pane from the sidebar's pane stack
|
||||||
|
*/
|
||||||
|
pop(): boolean {
|
||||||
|
return this.paneStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the sidebar
|
||||||
|
*/
|
||||||
close() {
|
close() {
|
||||||
this.state.open = false
|
this.state.open = false
|
||||||
this.state.activeItemId = undefined
|
this.state.activeItemId = undefined
|
||||||
// Clear content after animation
|
// Clear pane stack after animation
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.state.title = undefined
|
this.paneStack.clear()
|
||||||
this.state.content = undefined
|
|
||||||
this.state.component = undefined
|
|
||||||
this.state.componentProps = undefined
|
|
||||||
this.state.onsave = undefined
|
|
||||||
this.state.saveLabel = undefined
|
|
||||||
this.state.element = undefined
|
|
||||||
this.state.onback = undefined
|
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the sidebar open/close state
|
||||||
|
*/
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.state.open) {
|
if (this.state.open) {
|
||||||
this.close()
|
this.close()
|
||||||
} else {
|
} else {
|
||||||
this.open()
|
this.state.open = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update the right accessory action button dynamically */
|
/**
|
||||||
|
* Update the action button for the current pane
|
||||||
|
*/
|
||||||
setAction(
|
setAction(
|
||||||
onsave: (() => void) | undefined,
|
onsave: (() => void) | undefined,
|
||||||
saveLabel?: string,
|
saveLabel?: string,
|
||||||
element?: 'wind' | 'fire' | 'water' | 'earth' | 'dark' | 'light'
|
element?: ElementType
|
||||||
) {
|
) {
|
||||||
this.state.onsave = onsave
|
const currentPane = this.paneStack.currentPane
|
||||||
this.state.saveLabel = saveLabel
|
if (currentPane) {
|
||||||
this.state.element = element
|
this.paneStack.updateCurrentProps({})
|
||||||
|
// Update action on current pane
|
||||||
|
const panes = this.paneStack.panes
|
||||||
|
const currentIndex = panes.length - 1
|
||||||
|
if (currentIndex >= 0 && panes[currentIndex]) {
|
||||||
|
panes[currentIndex] = {
|
||||||
|
...panes[currentIndex],
|
||||||
|
action:
|
||||||
|
onsave ?
|
||||||
|
{
|
||||||
|
label: saveLabel ?? 'Done',
|
||||||
|
handler: onsave,
|
||||||
|
element
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clear the right accessory action button */
|
/**
|
||||||
|
* Clear the action button for the current pane
|
||||||
|
*/
|
||||||
clearAction() {
|
clearAction() {
|
||||||
this.state.onsave = undefined
|
this.setAction(undefined)
|
||||||
this.state.saveLabel = undefined
|
|
||||||
this.state.element = undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getters for reactive access
|
||||||
get isOpen() {
|
get isOpen() {
|
||||||
return this.state.open
|
return this.state.open
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
|
||||||
return this.state.title
|
|
||||||
}
|
|
||||||
|
|
||||||
get content() {
|
|
||||||
return this.state.content
|
|
||||||
}
|
|
||||||
|
|
||||||
get component() {
|
|
||||||
return this.state.component
|
|
||||||
}
|
|
||||||
|
|
||||||
get componentProps() {
|
|
||||||
return this.state.componentProps
|
|
||||||
}
|
|
||||||
|
|
||||||
get scrollable() {
|
|
||||||
return this.state.scrollable ?? true
|
|
||||||
}
|
|
||||||
|
|
||||||
get activeItemId() {
|
get activeItemId() {
|
||||||
return this.state.activeItemId
|
return this.state.activeItemId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backwards compatibility getters (delegate to pane stack)
|
||||||
|
get title() {
|
||||||
|
return this.paneStack.currentPane?.title
|
||||||
|
}
|
||||||
|
|
||||||
|
get component() {
|
||||||
|
return this.paneStack.currentPane?.component
|
||||||
|
}
|
||||||
|
|
||||||
|
get componentProps() {
|
||||||
|
return this.paneStack.currentPane?.props
|
||||||
|
}
|
||||||
|
|
||||||
|
get scrollable() {
|
||||||
|
return this.paneStack.currentPane?.scrollable ?? true
|
||||||
|
}
|
||||||
|
|
||||||
get onsave() {
|
get onsave() {
|
||||||
return this.state.onsave
|
return this.paneStack.currentPane?.action?.handler
|
||||||
}
|
}
|
||||||
|
|
||||||
get saveLabel() {
|
get saveLabel() {
|
||||||
return this.state.saveLabel
|
return this.paneStack.currentPane?.action?.label
|
||||||
}
|
}
|
||||||
|
|
||||||
get element() {
|
get element() {
|
||||||
return this.state.element
|
return this.paneStack.currentPane?.action?.element
|
||||||
}
|
}
|
||||||
|
|
||||||
get onback() {
|
get onback() {
|
||||||
return this.state.onback
|
return this.paneStack.currentPane?.onback
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy getter for content (not used with pane stack)
|
||||||
|
get content(): Snippet | undefined {
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,20 +112,9 @@
|
||||||
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
open={sidebar.isOpen}
|
open={sidebar.isOpen}
|
||||||
title={sidebar.title}
|
stack={sidebar.paneStack}
|
||||||
onclose={() => sidebar.close()}
|
onClose={() => sidebar.close()}
|
||||||
scrollable={sidebar.scrollable}
|
/>
|
||||||
onsave={sidebar.onsave}
|
|
||||||
saveLabel={sidebar.saveLabel}
|
|
||||||
element={sidebar.element}
|
|
||||||
onback={sidebar.onback}
|
|
||||||
>
|
|
||||||
{#if sidebar.component}
|
|
||||||
<svelte:component this={sidebar.component} {...sidebar.componentProps} />
|
|
||||||
{:else if sidebar.content}
|
|
||||||
{@render sidebar.content()}
|
|
||||||
{/if}
|
|
||||||
</Sidebar>
|
|
||||||
</div>
|
</div>
|
||||||
</Tooltip.Provider>
|
</Tooltip.Provider>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue