UNPKG

@dvcol/neo-svelte

Version:

Neomorphic ui library for svelte 5

127 lines (126 loc) 3.73 kB
import { getContext, setContext } from 'svelte'; import { SvelteMap } from 'svelte/reactivity'; import { NeoErrorMissingTabId } from '../utils/error.utils.js'; import { Logger } from '../utils/logger.utils.js'; export class NeoTabContext { #tabs = new SvelteMap(); #panes = new SvelteMap(); #onChange; #onClose; #active = $state(); #previous = $state(); #position = $state({}); #options = $state({}); get active() { return this.#active; } get value() { return this.getValue(this.active); } get previous() { return this.getValue(this.#previous); } get position() { return this.#position; } get state() { return { ...this.#options, active: this.active, value: this.value, }; } constructor({ onChange, onClose } = {}) { this.#onChange = onChange; this.#onClose = onClose; } getValue(tabId) { if (!tabId) return; return this.#tabs.get(tabId); } getPane(tabId) { if (!tabId) return; return this.#panes.get(tabId); } onOption(options) { Object.assign(this.#options, options); if (!options.slide) delete this.#position.oldTab; } #getPosition(tabId) { if (!tabId) return; const _ref = this.getValue(tabId)?.ref; const parent = _ref?.parentElement?.getBoundingClientRect(); const rect = _ref?.getBoundingClientRect(); if (!parent || !rect) return; return { id: tabId, top: rect.top - parent.top, left: rect.left - parent.left, width: rect.width, height: rect.height, }; } onPosition(_ref = this.value?.ref) { const _new = { oldTab: this.#getPosition(this.position?.newTab?.id), }; if (this.active) { _new.newTab = this.#getPosition(this.active); } this.#position = _new; return this.position; } onChange(tabId, emit = true) { if (tabId === this.#active) { if (this.#active && this.state?.toggle) this.onChange(); return; } const current = this.value; this.#previous = this.active; this.#active = tabId; this.onPosition(); if (emit) this.#onChange?.(this.active, this.value, current); } onClose(tabId) { this.#onClose?.(tabId, this.value); } register(tabId, value) { if (!tabId) throw new NeoErrorMissingTabId(); if (this.#tabs.has(tabId)) { return Logger.warn(`Tab ID '${String(tabId)}' already exists. Tab registration ignored.`, { existing: this.getValue(tabId), ignored: value }); } this.#tabs.set(tabId, { ...value, index: this.#tabs.size }); } remove(tabId) { this.#tabs.delete(tabId); if (this.#active === tabId) this.onChange(); } registerPane(tabId, panelId) { if (this.#panes.has(tabId)) { return Logger.warn(`Tab ID '${String(tabId)}' already exists. Pane registration ignored.`, { existing: this.#panes.get(tabId), ignored: panelId, }); } this.#panes.set(tabId, panelId); } removePane(tabId) { this.#panes.delete(tabId); } } const NeoTabsContextSymbol = Symbol('NeoTabsContext'); export function getTabContext() { return getContext(NeoTabsContextSymbol); } export function setTabContext(callback) { return setContext(NeoTabsContextSymbol, new NeoTabContext(callback)); }