partial toolbar implementation
This commit is contained in:
94
src/lib/Toolbar.svelte
Normal file
94
src/lib/Toolbar.svelte
Normal file
@@ -0,0 +1,94 @@
|
||||
<script lang="ts" module>
|
||||
type ToolbarRenderable<P extends unknown[] = any[]> =
|
||||
| { type: 'component'; component: Component; props?: P }
|
||||
| { type: 'snippet'; snippet: Snippet<P>; props?: P }
|
||||
| { type: 'string'; value: string };
|
||||
|
||||
interface ToolbarButton<P extends unknown[] = any[]> {
|
||||
type: 'toggle' | 'button';
|
||||
title: string;
|
||||
render: ToolbarRenderable<P>;
|
||||
}
|
||||
|
||||
interface ToolbarDivider {
|
||||
type: 'divider';
|
||||
}
|
||||
|
||||
interface ToolbarGroup {
|
||||
type: 'group';
|
||||
items: ToolbarItem[];
|
||||
}
|
||||
|
||||
export type ToolbarItem = ToolbarButton<any> | ToolbarDivider | ToolbarGroup;
|
||||
|
||||
type SnippetArgs<T> = T extends Snippet<infer Args extends unknown[]> ? Args : never;
|
||||
|
||||
// For snippet-based buttons
|
||||
export function toolbarItem<S extends Snippet<any>>(item: {
|
||||
type: 'button' | 'toggle';
|
||||
title: string;
|
||||
render: {
|
||||
type: 'snippet';
|
||||
snippet: S;
|
||||
props: SnippetArgs<S>;
|
||||
};
|
||||
}): typeof item;
|
||||
|
||||
// For component-based buttons (optional, for completeness)
|
||||
export function toolbarItem<C extends Component>(item: {
|
||||
type: 'button' | 'toggle';
|
||||
title: string;
|
||||
render: {
|
||||
type: 'component';
|
||||
component: C;
|
||||
props: ComponentProps<C>;
|
||||
};
|
||||
}): typeof item;
|
||||
|
||||
// For other items (divider, group, etc)
|
||||
export function toolbarItem<T extends ToolbarDivider | ToolbarGroup>(item: T): T;
|
||||
|
||||
// Implementation
|
||||
export function toolbarItem(item: any): any {
|
||||
return item;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { type Component, type Snippet, type ComponentProps } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
items: ToolbarItem[];
|
||||
}
|
||||
|
||||
let { items }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#each items as toolbarItem}
|
||||
{@render item(toolbarItem)}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#snippet item(i: ToolbarItem)}
|
||||
{#if i.type === 'button' || i.type === 'toggle'}
|
||||
<button class="toolbar-button">
|
||||
{#if i.render.type === 'component'}
|
||||
<item.render.component {...i.render.props} />
|
||||
{:else if i.render.type === 'snippet'}
|
||||
{@render i.render.snippet(i.render.props)}
|
||||
{:else if i.render.type === 'string'}
|
||||
<span>{i.render.value}</span>
|
||||
{/if}
|
||||
<span>{i.title}</span>
|
||||
</button>
|
||||
{:else if i.type === 'divider'}
|
||||
<hr class="toolbar-divider" />
|
||||
{:else if i.type === 'group'}
|
||||
<div class="toolbar-group">
|
||||
{#each i.items as groupItem}
|
||||
{@render item(groupItem)}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
@@ -17,6 +17,9 @@
|
||||
import TimezoneInput from '$lib/TimezoneInput.svelte';
|
||||
import ToggleGroup from '$lib/ToggleGroup.svelte';
|
||||
import ToggleSelect from '$lib/ToggleSelect.svelte';
|
||||
import Toolbar, { toolbarItem } from '$lib/Toolbar.svelte';
|
||||
|
||||
import { BowlFood } from 'phosphor-svelte';
|
||||
|
||||
let dateInputValue = $state<Date | undefined>(undefined);
|
||||
let checkboxValue = $state<CheckboxState>('indeterminate');
|
||||
@@ -202,6 +205,26 @@
|
||||
<ToggleSelect name="example-toggle-select">Toggle Me!</ToggleSelect>
|
||||
</div>
|
||||
|
||||
<div class="component">
|
||||
<p class="title">Toolbar</p>
|
||||
|
||||
<Toolbar
|
||||
items={[
|
||||
toolbarItem({
|
||||
type: 'button',
|
||||
title: 'Button Action',
|
||||
render: {
|
||||
type: 'component',
|
||||
component: BowlFood,
|
||||
props: {
|
||||
size: '1.5em'
|
||||
}
|
||||
}
|
||||
})
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Dialog
|
||||
bind:open={dialogOpen}
|
||||
title="Dialog Title"
|
||||
|
||||
Reference in New Issue
Block a user