95 lines
2.2 KiB
Svelte
95 lines
2.2 KiB
Svelte
<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}
|