175 lines
4.0 KiB
TypeScript
175 lines
4.0 KiB
TypeScript
import { createAttachmentKey, type Attachment } from 'svelte/attachments';
|
|
import { get, writable, type Writable } from 'svelte/store';
|
|
|
|
export type ToolbarToggleState = 'on' | 'off';
|
|
|
|
/**
|
|
* Defines options that may be passed when creating a toggleable toolbar item.
|
|
*/
|
|
export type ToolbarToggleOptions = {
|
|
state?: Writable<ToolbarToggleState>;
|
|
};
|
|
|
|
/** toggleValue swaps a toolbar toggle state to on or off, based on current state. */
|
|
const toggleValue = (current: ToolbarToggleState): ToolbarToggleState =>
|
|
current === 'on' ? 'off' : 'on';
|
|
|
|
/**
|
|
* A single toggleable toolbar item. Can coordinate with other toggles in a group.
|
|
*/
|
|
export class ToolbarToggle {
|
|
private _state: Writable<ToolbarToggleState>;
|
|
private _parent: ToolbarGroup | undefined;
|
|
|
|
constructor(opts: ToolbarToggleOptions = {}) {
|
|
this._state = opts.state ?? writable('off');
|
|
}
|
|
|
|
/**
|
|
* Creates a new toggleable toolbar item with a parent group.
|
|
*
|
|
* @param parent The parent group to which this toggle belongs.
|
|
* @param opts Options for the toggle.
|
|
* @returns A new ToolbarToggle instance.
|
|
*/
|
|
static withParent(parent: ToolbarGroup, opts: ToolbarToggleOptions = {}) {
|
|
const toggle = new ToolbarToggle(opts);
|
|
toggle._parent = parent;
|
|
return toggle;
|
|
}
|
|
|
|
/**
|
|
* Returns the store that manages the state of the toggle.
|
|
*/
|
|
get store() {
|
|
return this._state;
|
|
}
|
|
|
|
/**
|
|
* Returns the current state of the toggle.
|
|
*/
|
|
get state() {
|
|
return get(this._state);
|
|
}
|
|
|
|
/**
|
|
* Sets the state of the toggle.
|
|
*/
|
|
set state(value: ToolbarToggleState) {
|
|
this._state.set(value);
|
|
}
|
|
|
|
/**
|
|
* Returns a Svelte 5 Attachment for the toggle.
|
|
*/
|
|
get attachment(): Attachment {
|
|
return (node) => {
|
|
const el = node as HTMLElement;
|
|
// Subscribe to store
|
|
const unsubscribe = this._state.subscribe((state) => {
|
|
el.dataset.state = state;
|
|
});
|
|
|
|
// Add a listener for the click event
|
|
el.addEventListener('click', (e: MouseEvent) => {
|
|
const target = e.currentTarget as HTMLElement;
|
|
if (!target) return;
|
|
|
|
this._state.update(toggleValue);
|
|
|
|
// If this is a group, turn off all other toggles
|
|
if (this._parent) {
|
|
this._parent.disableAll(this);
|
|
}
|
|
});
|
|
|
|
return unsubscribe;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns props including the attachment for the toggle, allowing easy spreading
|
|
* to an HTML element. Should NOT be used in addition to manually attaching the toggle.
|
|
*
|
|
* Can be advantageous as spreading seems to allow CSS tree-shaking to work properly.
|
|
*/
|
|
get props() {
|
|
return {
|
|
[createAttachmentKey()]: this.attachment
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A group of toggles in a toolbar. Makes each toggle exclusive of the others, disabling all other
|
|
* toggles when any given toggle is enabled.
|
|
*/
|
|
export class ToolbarGroup {
|
|
private _toggles: ToolbarToggle[] = [];
|
|
|
|
/**
|
|
* Creates a new toolbar toogle item in the group.
|
|
* @param opts
|
|
* @returns ToolbarToggle instance
|
|
*/
|
|
toggle(opts: ToolbarToggleOptions = {}) {
|
|
const toggle = ToolbarToggle.withParent(this, opts);
|
|
this._toggles.push(toggle);
|
|
return toggle;
|
|
}
|
|
|
|
/**
|
|
* Disables all toggles in the group except the one specified.
|
|
* @param except - The toggle to exclude from disabling.
|
|
*/
|
|
disableAll(except?: ToolbarToggle) {
|
|
this._toggles.forEach((t) => {
|
|
if (t !== except) {
|
|
t.state = 'off';
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enables the specified toggle and disables all other toggles in the group.
|
|
* @param toggle - The toggle to enable.
|
|
*/
|
|
enable(toggle: ToolbarToggle) {
|
|
this._toggles.forEach((t) => {
|
|
if (t == toggle) {
|
|
t.state = 'on';
|
|
} else {
|
|
t.state = 'off';
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manages the top-level toolbar groups.
|
|
*/
|
|
export class Toolbar {
|
|
private _groups: ToolbarGroup[] = [];
|
|
|
|
constructor() {}
|
|
|
|
/**
|
|
* Creates a new toolbar group.
|
|
* @returns The newly created toolbar group.
|
|
*/
|
|
group() {
|
|
const g = new ToolbarGroup();
|
|
this._groups.push(g);
|
|
return g;
|
|
}
|
|
|
|
/**
|
|
* Creates a new toolbar toggle item.
|
|
* @param opts
|
|
* @returns ToolbarToggle instance
|
|
*/
|
|
toggle(opts: ToolbarToggleOptions = {}) {
|
|
return new ToolbarToggle(opts);
|
|
}
|
|
}
|