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

299 lines (298 loc) 8.86 kB
import { NBSP } from './constants.js'; import { TableComponent } from './tableComponent.js'; import { derived } from 'svelte/store'; /** * Abstract base class representing a cell in the table header. * Extended by FlatHeaderCell, DataHeaderCell, GroupHeaderCell, etc. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class HeaderCell extends TableComponent { /** Label content or render function for the header. */ label; /** Number of columns this cell spans. */ colspan; /** Starting column index. */ colstart; /** * Creates a new HeaderCell. * * @param init - Initialization options. */ constructor({ id, label, colspan, colstart }) { super({ id }); this.label = label; this.colspan = colspan; this.colstart = colstart; } /** * Renders the header cell content. * * @returns The render configuration for displaying this cell. * @throws Error if state reference is missing when using a function label. */ render() { if (this.label instanceof Function) { if (this.state === undefined) { throw new Error('Missing `state` reference'); } return this.label(this, this.state); } return this.label; } /** * Gets the HTML attributes for this header cell. * * @returns A readable store of cell attributes. */ attrs() { return derived(super.attrs(), ($baseAttrs) => { return { ...$baseAttrs, role: 'columnheader', colspan: this.colspan }; }); } /** * Type guard to check if this is a flat header cell. * * @returns True if this is a FlatHeaderCell. */ // TODO Workaround for https://github.com/vitejs/vite/issues/9528 isFlat() { return '__flat' in this; } /** * Type guard to check if this is a data header cell. * * @returns True if this is a DataHeaderCell. */ // TODO Workaround for https://github.com/vitejs/vite/issues/9528 isData() { return '__data' in this; } /** * Type guard to check if this is a flat display header cell. * * @returns True if this is a FlatDisplayHeaderCell. */ // TODO Workaround for https://github.com/vitejs/vite/issues/9528 isFlatDisplay() { return '__flat' in this && '__display' in this; } /** * Type guard to check if this is a group header cell. * * @returns True if this is a GroupHeaderCell. */ // TODO Workaround for https://github.com/vitejs/vite/issues/9528 isGroup() { return '__group' in this; } /** * Type guard to check if this is a group display header cell. * * @returns True if this is a GroupDisplayHeaderCell. */ // TODO Workaround for https://github.com/vitejs/vite/issues/9528 isGroupDisplay() { return '__group' in this && '__display' in this; } } /** * A flat (non-grouped) header cell that spans a single column. * Base class for DataHeaderCell and FlatDisplayHeaderCell. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class FlatHeaderCell extends HeaderCell { // TODO Workaround for https://github.com/vitejs/vite/issues/9528 __flat = true; /** * Creates a new FlatHeaderCell. * * @param init - Initialization options. */ constructor({ id, label, colstart }) { super({ id, label, colspan: 1, colstart }); } /** * Creates a copy of this header cell. * * @returns A cloned FlatHeaderCell. */ clone() { return new FlatHeaderCell({ id: this.id, label: this.label, colstart: this.colstart }); } } /** * A header cell for a data column that displays values from the data source. * Contains accessor information for retrieving cell values. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class DataHeaderCell extends FlatHeaderCell { // TODO Workaround for https://github.com/vitejs/vite/issues/9528 __data = true; /** The key used to access data from the item. */ accessorKey; /** Function to extract data from the item. */ /* trunk-ignore(eslint/no-unused-vars) */ accessorFn; /** * Creates a new DataHeaderCell. * * @param init - Initialization options. */ constructor({ id, label, accessorKey, accessorFn, colstart }) { super({ id, label, colstart }); this.accessorKey = accessorKey; this.accessorFn = accessorFn; } /** * Creates a copy of this header cell. * * @returns A cloned DataHeaderCell. */ clone() { return new DataHeaderCell({ id: this.id, label: this.label, accessorFn: this.accessorFn, accessorKey: this.accessorKey, colstart: this.colstart }); } } /** * A flat header cell for display-only columns (e.g., action columns). * Does not contain data accessor information. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class FlatDisplayHeaderCell extends FlatHeaderCell { // TODO Workaround for https://github.com/vitejs/vite/issues/9528 __display = true; /** * Creates a new FlatDisplayHeaderCell. * * @param init - Initialization options. */ constructor({ id, label = NBSP, colstart }) { super({ id, label, colstart }); } /** * Creates a copy of this header cell. * * @returns A cloned FlatDisplayHeaderCell. */ clone() { return new FlatDisplayHeaderCell({ id: this.id, label: this.label, colstart: this.colstart }); } } /** * A header cell that spans multiple columns, used for column grouping. * Contains information about which columns are part of the group. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class GroupHeaderCell extends HeaderCell { // TODO Workaround for https://github.com/vitejs/vite/issues/9528 __group = true; /** Current column IDs covered by this group. */ ids; /** Combined ID string for all columns in the original group. */ allId; /** All column IDs in the original group definition. */ allIds; /** * Creates a new GroupHeaderCell. * * @param init - Initialization options. */ constructor({ label, ids, allIds, colspan, colstart }) { super({ id: `[${ids.join(',')}]`, label, colspan, colstart }); this.ids = ids; this.allId = `[${allIds.join(',')}]`; this.allIds = allIds; } /** * Sets the column IDs covered by this group and updates the cell ID. * * @param ids - The new array of column IDs. */ setIds(ids) { this.ids = ids; this.id = `[${this.ids.join(',')}]`; } /** * Adds a column ID to this group and updates the cell ID. * * @param id - The column ID to add. */ pushId(id) { this.ids = [...this.ids, id]; this.id = `[${this.ids.join(',')}]`; } /** * Creates a copy of this header cell. * * @returns A cloned GroupHeaderCell. */ clone() { return new GroupHeaderCell({ label: this.label, ids: this.ids, allIds: this.allIds, colspan: this.colspan, colstart: this.colstart }); } } /** * A group header cell for display purposes (e.g., empty group headers). * Used to fill gaps in the header row matrix. * * @template Item - The type of data items in the table. * @template Plugins - The plugins used by the table. */ export class GroupDisplayHeaderCell extends GroupHeaderCell { // TODO Workaround for https://github.com/vitejs/vite/issues/9528 __display = true; /** * Creates a new GroupDisplayHeaderCell. * * @param init - Initialization options. */ constructor({ label = NBSP, ids, allIds, colspan = 1, colstart }) { super({ label, ids, allIds, colspan, colstart }); } /** * Creates a copy of this header cell. * * @returns A cloned GroupDisplayHeaderCell. */ clone() { return new GroupDisplayHeaderCell({ label: this.label, ids: this.ids, allIds: this.allIds, colspan: this.colspan, colstart: this.colstart }); } }