UNPKG

@humanspeak/svelte-headless-table

Version:

A powerful, headless table library for Svelte that provides complete control over table UI while handling complex data operations like sorting, filtering, pagination, grouping, and row expansion. Build custom, accessible data tables with zero styling opin

187 lines (186 loc) 5.71 kB
import { readable, writable } from 'svelte/store'; /** * Type guard that checks if a value is a Svelte Readable store. * * @template T - The type of the store's value. * @param value - The value to check. * @returns True if the value has a subscribe method. * @example * ```typescript * if (isReadable(maybeStore)) { * maybeStore.subscribe(value => console.log(value)) * } * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isReadable = (value) => { return value?.subscribe instanceof Function; }; /** * Type guard that checks if a value is a Svelte Writable store. * * @template T - The type of the store's value. * @param store - The value to check. * @returns True if the value has both update and set methods. * @example * ```typescript * if (isWritable(maybeStore)) { * maybeStore.set(newValue) * } * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export const isWritable = (store) => { return store?.update instanceof Function && store.set instanceof Function; }; /** A readable store that always contains undefined. */ export const Undefined = readable(undefined); /** * Returns the Undefined store typed as a Readable of type T. * Useful for providing a default store value. * * @template T - The type to cast the undefined store to. * @returns A Readable store typed as Readable<T>. */ export const UndefinedAs = () => Undefined; /** * Creates a Svelte store that maintains a unique set of items as an array. * Supports toggle, add, remove, and clear operations. * * @template T - The type of elements in the set. * @param initial - The initial array of items. * @param options - Configuration options including custom equality function. * @returns An ArraySetStore with set-like operations. * @example * ```typescript * const selectedIds = arraySetStore<string>(['id1']) * selectedIds.toggle('id2') // Adds 'id2' * selectedIds.toggle('id1') // Removes 'id1' * ``` */ export const arraySetStore = (initial = [], { isEqual = (a, b) => a === b } = {}) => { const { subscribe, update, set } = writable(initial); const toggle = (item, { clearOthers = false } = {}) => { update(($arraySet) => { const index = $arraySet.findIndex(($item) => isEqual($item, item)); if (index === -1) { if (clearOthers) { return [item]; } return [...$arraySet, item]; } if (clearOthers) { return []; } return [...$arraySet.slice(0, index), ...$arraySet.slice(index + 1)]; }); }; const add = (item) => { update(($arraySet) => { const index = $arraySet.findIndex(($item) => isEqual($item, item)); if (index === -1) { return [...$arraySet, item]; } return $arraySet; }); }; const remove = (item) => { update(($arraySet) => { const index = $arraySet.findIndex(($item) => isEqual($item, item)); if (index === -1) { return $arraySet; } return [...$arraySet.slice(0, index), ...$arraySet.slice(index + 1)]; }); }; const clear = () => { set([]); }; return { subscribe, update, set, toggle, add, remove, clear }; }; /** * Creates a Svelte store that maintains a set using a Record with boolean values. * False values are automatically removed to keep the record clean. * * @template T - The type of keys (must be string or number). * @param initial - The initial record of key-boolean pairs. * @returns A RecordSetStore with set-like operations. * @example * ```typescript * const expandedIds = recordSetStore<string>({ row1: true }) * expandedIds.toggle('row2') // Adds 'row2' * expandedIds.toggle('row1') // Removes 'row1' * ``` */ export const recordSetStore = (initial = {}) => { const withFalseRemoved = (record) => { return Object.fromEntries(Object.entries(record).filter(([, v]) => v)); }; const { subscribe, update, set } = writable(withFalseRemoved(initial)); const updateAndRemoveFalse = (fn) => { update(($recordSet) => { const newRecordSet = fn($recordSet); return withFalseRemoved(newRecordSet); }); }; const toggle = (item) => { update(($recordSet) => { if ($recordSet[item] === true) { delete $recordSet[item]; return $recordSet; } return { ...$recordSet, [item]: true }; }); }; const add = (item) => { update(($recordSet) => ({ ...$recordSet, [item]: true })); }; const addAll = (items) => { update(($recordSet) => ({ ...$recordSet, ...Object.fromEntries(items.map((item) => [item, true])) })); }; const remove = (item) => { update(($recordSet) => { delete $recordSet[item]; return $recordSet; }); }; const removeAll = (items) => { update(($recordSet) => { for (const item of items) { delete $recordSet[item]; } return $recordSet; }); }; const clear = () => { set({}); }; return { subscribe, update: updateAndRemoveFalse, set: (newValue) => updateAndRemoveFalse(() => newValue), toggle, add, addAll, remove, removeAll, clear }; };