UNPKG

@tanstack/lit-table

Version:

Headless UI for building powerful tables & datagrids for Lit.

130 lines (128 loc) 3.75 kB
import { FlexRender } from "./flexRender.js"; import { litReactivity } from "./reactivity.js"; import { subscribe } from "./subscribe-directive.js"; import { constructTable } from "@tanstack/table-core"; import { createRenderPhaseSource } from "@tanstack/table-core/reactivity"; import { table_publishExternalState, table_setOptions } from "@tanstack/table-core/static-functions"; import { shallow } from "@tanstack/lit-store"; //#region src/TableController.ts /** * A Lit ReactiveController for TanStack Table integration. * * Uses `constructReactivityFeature` from table-core to properly integrate * with the TanStack Store reactivity system, matching the pattern used by * all other framework adapters (React, Vue, Solid, Svelte, Angular). * * @example * ```ts * @customElement('my-table') * class MyTable extends LitElement { * private tableController = new TableController<typeof features, Person>(this) * * protected render() { * const table = this.tableController.table( * { * features, * columns, * data, * }, * (state) => ({ sorting: state.sorting }), * ) * // use table in your template... * } * } * ``` */ var TableController = class { host; _table = null; _rootSource; _storeSubscription; _capturedState; _capturedSnapshot; _hasSelector = false; _latestSelector; _lastSelected; constructor(host) { (this.host = host).addController(this); } /** * Returns the Lit-backed table instance for the current render pass. * * The first call constructs the table with Lit reactivity bindings and * subscribes the host to table state/options changes. Later calls merge new * options into the same table instance and expose selected state through * `table.state`. * * @example * ```ts * const table = this.tableController.table( * { features, columns, data }, * (state) => ({ sorting: state.sorting }), * ) * ``` */ table(tableOptions, selector) { if (!this._table) { const mergedOptions = { ...tableOptions, features: { coreReactivityFeature: litReactivity(), ...tableOptions.features }, mergeOptions: (defaultOptions, newOptions) => { return { ...defaultOptions, ...newOptions }; } }; this._table = constructTable(mergedOptions); this._rootSource = createRenderPhaseSource(this._table.store, shallow); this._setupSubscriptions(); } table_setOptions(this._table, (prev) => ({ ...prev, ...tableOptions }), { syncExternalState: false }); this._capturedState = this._table.options.state; const renderSnapshot = this._rootSource.get(); this._capturedSnapshot = renderSnapshot; this._hasSelector = selector !== void 0; this._latestSelector = selector; this._lastSelected = selector ? selector(renderSnapshot) : renderSnapshot; return { ...this._table, subscribe, FlexRender, get state() { return selector?.(renderSnapshot) ?? renderSnapshot; } }; } _setupSubscriptions() { if (this._table && !this._storeSubscription) this._storeSubscription = this._rootSource.subscribe((state) => { if (this._hasSelector) { const nextSelected = this._latestSelector(state); if (shallow(this._lastSelected, nextSelected)) return; this._lastSelected = nextSelected; } this.host.requestUpdate(); }); } hostConnected() { this._setupSubscriptions(); if (this._table) this.host.requestUpdate(); } hostUpdated() { if (!this._table) return; this._rootSource.markCommitted(this._capturedSnapshot); table_publishExternalState(this._table, this._capturedState ?? null, shallow); } hostDisconnected() { this._storeSubscription?.unsubscribe(); this._storeSubscription = void 0; } }; //#endregion export { TableController };