diff --git a/src/lib/Toolbar.svelte b/src/lib/Toolbar.svelte deleted file mode 100644 index a00f12e..0000000 --- a/src/lib/Toolbar.svelte +++ /dev/null @@ -1,94 +0,0 @@ - - - - -
- {#each items as toolbarItem} - {@render item(toolbarItem)} - {/each} -
- -{#snippet item(i: ToolbarItem)} - {#if i.type === 'button' || i.type === 'toggle'} - - {:else if i.type === 'divider'} -
- {:else if i.type === 'group'} -
- {#each i.items as groupItem} - {@render item(groupItem)} - {/each} -
- {/if} -{/snippet} diff --git a/src/lib/Toolbar.svelte.ts b/src/lib/Toolbar.svelte.ts new file mode 100644 index 0000000..6c1a695 --- /dev/null +++ b/src/lib/Toolbar.svelte.ts @@ -0,0 +1,78 @@ +import { writable, type Writable } from 'svelte/store'; + +export type ToolbarToggleState = 'on' | 'off'; + +export type ToolbarToggleOptions = { + state?: Writable; + group?: string; +}; + +export class Toolbar { + private _groups: string[] = []; + + constructor() {} + + group(name: string) { + if (this._groups.includes(name)) { + throw new Error(`Toolbar group "${name}" already exists.`); + } + this._groups.push(name); + + return { + ['data-sui-toolbar-group']: name + }; + } + + toggle(opts: ToolbarToggleOptions = {}) { + if (opts.group && !this._groups.includes(opts.group)) { + throw new Error(`Toolbar group "${opts.group}" does not exist.`); + } + + const toggleState = opts.state ?? writable('off'); + + const bindDataState = (node: HTMLElement) => { + const unsubscribe = toggleState.subscribe((value) => { + console.log('state changed', value); + node.dataset.state = value; + }); + return { destroy: unsubscribe }; + }; + + return { + ['data-sui-toolbar-toggle']: opts?.group || '', + onclick: (event: MouseEvent) => { + const target = event.currentTarget as HTMLElement; + if (!target) return; + + // Toggle data-state + // const currentState = target.dataset.state || 'off'; + // if (opts?.state) { + // opts.state = currentState === 'on' ? 'off' : 'on'; + // } else { + // target.dataset.state = currentState === 'on' ? 'off' : 'on'; + // } + + toggleState.update((current) => (current === 'on' ? 'off' : 'on')); + + // opts.state = opts.state === 'on' ? 'off' : 'on'; + + // If this is a member of a group, find the adjacent toggles and turn them off + if (opts?.group) { + const groupToggles = document.querySelectorAll( + `[data-sui-toolbar-toggle="${opts.group}"]` + ) as NodeListOf; + + groupToggles.forEach((toggle) => { + if (toggle !== target) { + toggle.dataset.state = 'off'; + } + }); + } + + console.log('onlick', opts.group, toggleState); + }, + bindDataState, // expose data-state attribute binder + state: toggleState // expose state to consumer + }; + } +} diff --git a/src/lib/index.ts b/src/lib/index.ts index 3d4e38c..33498f3 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -23,4 +23,4 @@ export { default as TimeInput } from './TimeInput.svelte'; export { default as TimezoneInput } from './TimezoneInput.svelte'; export { default as ToggleGroup } from './ToggleGroup.svelte'; export { default as ToggleSelect } from './ToggleSelect.svelte'; -export { type Option, getLabel, getValue } from './util'; +export { type Option, type MaybeGetter, getLabel, getValue } from './util'; diff --git a/src/lib/util.ts b/src/lib/util.ts index 3ea0455..6a0f124 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -1,3 +1,10 @@ +/** + * MaybeGetter is a type that can either be a value of type T or a function that returns a value of type T. + * This is useful for cases where you might want to pass a value directly or a function that computes the + * value later, potentially taking advantage of reactivity. + */ +export type MaybeGetter = T | (() => T); + /** * Generates a unique identifier string unless an identifier is provided. * If a prefix is provided, it will be prepended to the identifier. diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 1642e7d..6948e32 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -17,13 +17,23 @@ 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'; + import { Toolbar, type ToolbarToggleState } from '$lib/Toolbar.svelte'; + import { + ArrowUUpLeft, + ArrowUUpRight, + TextB, + TextItalic, + TextStrikethrough, + TextUnderline + } from 'phosphor-svelte'; let dateInputValue = $state(undefined); let checkboxValue = $state('indeterminate'); let dialogOpen = $state(false); + + const toolbar = new Toolbar(); + const fontGroup = toolbar.group('font'); + const { state: boldState, ...boldToggle } = toolbar.toggle({ group: 'font' }); sui @@ -208,21 +218,59 @@

Toolbar

- +
+

Bold is enabled: {$boldState}

+
+ +
+
+ + +
+
+
+ + + + +
+