{"version":3,"file":"table.mjs","names":[],"sources":["../src/table/util.ts","../src/table/column.model.ts","../src/table/components/cell-slot.tsx","../src/table/components/cell-style.ts","../src/table/components/checkbox.tsx","../src/table/components/column-resizer.tsx","../src/table/table.context.ts","../src/table/components/table-body.tsx","../src/table/components/table-header.tsx","../src/table/components/selection.tsx","../src/table/components/table-overlay.tsx","../src/table/components/table-empty.tsx","../src/table/components/table-error.tsx","../src/table/components/table-gutter.tsx","../src/table/components/table-loading.tsx","../src/table/components/table-expansion.tsx","../src/table/components/table-root.tsx","../src/table/use-scroll.ts","../src/table/components/table-scroll.tsx","../src/table/components/table-status-bar.tsx","../src/table/components/namespace.tsx","../src/table/search-filter.model.ts","../src/table/is-lazy.ts","../src/table/table.model.ts","../src/table/use-table.ts"],"sourcesContent":["import type { RowData } from \"./table.types\";\n\nexport const titleCase = (str: string): string => {\n  return str\n    .trim()\n    .replace(/([a-z])([A-Z]+)/g, \"$1 $2\")\n    .split(/[-_\\s]+/)\n    .filter((s) => s.trim())\n    .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())\n    .join(\" \");\n};\n\n/**\n * Resolve a column key against a row: a direct property hit wins (so a literal \"a.b\" property\n * still works), otherwise the key is walked as a dot-path (\"owner.name\").\n */\nexport const getPath = (obj: unknown, path: string): unknown => {\n  if (obj == null) return undefined;\n  const direct = (obj as RowData)[path];\n  if (direct !== undefined || !path.includes(\".\")) return direct;\n  let current: unknown = obj;\n  for (const segment of path.split(\".\")) {\n    if (current == null) return undefined;\n    current = (current as RowData)[segment];\n  }\n  return current;\n};\n\n// Values reaching the string fallback below are primitives in practice — numbers and Dates are\n// already handled, and a column whose values are plain objects has no meaningful order anyway. The\n// cast is what lets the type-aware linter accept stringifying an `unknown`.\nconst asString = (value: unknown): string => String(value as string);\n\n/**\n * Default sort comparator over extracted cell values — nullish first, numbers numerically, Dates\n * chronologically, everything else by locale string.\n */\nexport const compareValues = (a: unknown, b: unknown): number => {\n  if (a == null && b == null) return 0;\n  if (a == null) return -1;\n  if (b == null) return 1;\n  if (typeof a === \"number\" && typeof b === \"number\") return a - b;\n  if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n  return asString(a).localeCompare(asString(b));\n};\n","import { action, computed, makeObservable, observable } from \"mobx\";\nimport type { Facet, FilterCondition, SetFilterValue } from \"../filter/filter.types\";\n// Two pure functions, imported from the module rather than the `../filter` barrel so no filter class\n// is ever reachable from `src/table/index.ts`. `facetValues` in particular *must* be the same\n// function `SetFilter.matches` uses, or the facet list would offer a value that selects no rows.\nimport { BLANK, facetValues } from \"../filter/util\";\nimport type { TableModel } from \"./table.model\";\nimport type {\n  BaseColumnDef,\n  ColumnConfig,\n  ColumnMeta,\n  ColumnPin,\n  ColumnConfigPatch,\n  ColumnDef,\n  ColumnFilter,\n  FilterMode,\n  RowData,\n  SortDirection,\n} from \"./table.types\";\nimport { compareValues, getPath, titleCase } from \"./util\";\n\nconst DEFAULT_MIN_WIDTH = 120;\n\n/** Key assigned to a selection column when its def doesn't supply one. */\nexport const SELECTION_COLUMN_KEY = \"__selection__\";\n\nexport class ColumnModel {\n  readonly table: TableModel;\n  /**\n   * The resolved column configuration. An `observable.ref` — replaced wholesale by\n   * {@link setConfig}, never mutated in place — so every getter over it is genuinely reactive.\n   */\n  config: ColumnConfig;\n\n  /** Which edge this column is pinned to. Never `undefined` — an unpinned column is `false`. */\n  pinned: ColumnPin = false;\n\n  // Hidden columns are excluded from layout/rendering (see TableModel.orderedColumns).\n  hidden = false;\n\n  // Manual override (e.g. drag-to-resize). When set the column is treated as fixed at this width\n  // in the distribution; normally unset.\n  manualWidth: number | undefined = undefined;\n\n  /** Resolved pixel width — distributed across the viewport by the table (see `columnWidths`). */\n  get width(): number {\n    return this.table.columnWidths.get(this) ?? 0;\n  }\n\n  /** Fixed pixel width when the column isn't flexible (manual override or an explicit number). */\n  get fixedWidth(): number | undefined {\n    if (this.manualWidth !== undefined) return this.manualWidth;\n    return typeof this.config.width === \"number\" ? this.config.width : undefined;\n  }\n\n  /** Flex weight (the `N` in `\"Nfr\"`); `0` when fixed. An unspecified width means `1fr`. */\n  get grow(): number {\n    if (this.fixedWidth !== undefined) return 0;\n    if (typeof this.config.width === \"string\") {\n      const n = Number.parseFloat(this.config.width);\n      return Number.isFinite(n) && n > 0 ? n : 1;\n    }\n    return 1;\n  }\n\n  get minWidth(): number {\n    return this.config.minWidth ?? DEFAULT_MIN_WIDTH;\n  }\n\n  get maxWidth(): number {\n    return this.config.maxWidth ?? Number.POSITIVE_INFINITY;\n  }\n\n  get resizable(): boolean {\n    return this.config.resizable !== false;\n  }\n\n  /**\n   * Whether a column picker should offer to change this column's visibility. See\n   * {@link BaseColumnDef.hideable} — it locks `hidden` at its initial value rather than forbidding\n   * hiding, and `setHidden` is never gated by it.\n   */\n  get hideable(): boolean {\n    return this.config.hideable !== false;\n  }\n\n  /** Whether a header UI should offer to pin this column. See {@link BaseColumnDef.pinnable}. */\n  get pinnable(): boolean {\n    return this.config.pinnable !== false;\n  }\n\n  /** Whether header UIs should offer sorting on this column (selection columns never do). */\n  get sortable(): boolean {\n    return this.config.sortable !== false && !this.selection;\n  }\n\n  /** Whether this is the built-in row-selection column. */\n  get selection(): boolean {\n    return this.config.selection === true;\n  }\n\n  /**\n   * True for the innermost pinned column on its side (the one bordering the scrollable area).\n   * Consumers hang the pinned boundary shadow off `[data-pinned-edge]` so a group of pinned\n   * columns shows a single shadow at the seam.\n   */\n  get isPinnedEdge(): boolean {\n    const siblings = this.pinnedSiblings;\n    return siblings ? siblings[siblings.length - 1] === this : false;\n  }\n\n  /**\n   * True for the outermost pinned column on its side (the one at the viewport edge). Used by the\n   * header to round its outer corners so a pinned column doesn't paint a square over the rounded\n   * header background. Both rendered pinned arrays are ordered outer-edge-first.\n   */\n  get isPinnedOuterEdge(): boolean {\n    const siblings = this.pinnedSiblings;\n    return siblings ? siblings[0] === this : false;\n  }\n\n  get offset(): number {\n    const colsMap = {\n      left: this.table.leftPinnedRenderedColumns,\n      right: this.table.rightPinnedRenderedColumns,\n      unpinned: this.table.unpinnedColumns,\n    };\n\n    const cols = colsMap[this.pinned || \"unpinned\"];\n    return cols.slice(0, cols.indexOf(this)).reduce((sum, c) => sum + c.width, 0);\n  }\n\n  get title(): string {\n    return this.config.title ?? titleCase(this.config.key);\n  }\n\n  /** 1-based visual column position (pinned blocks at the edges) — the aria-colindex value. */\n  get ariaColIndex(): number {\n    return this.table.visualColumns.indexOf(this) + 1;\n  }\n\n  get key(): string {\n    return this.config.key;\n  }\n\n  /** Active sort direction for this column, or undefined when it doesn't participate in the sort. */\n  get sortDirection(): SortDirection | undefined {\n    return this.table.sorts.find((s) => s.key === this.key)?.direction;\n  }\n\n  /** 1-based position in the sort priority (the \"1\"/\"2\" badge in multi-sort UIs); undefined when unsorted. */\n  get sortIndex(): number | undefined {\n    const index = this.table.sorts.findIndex((s) => s.key === this.key);\n    return index >= 0 ? index + 1 : undefined;\n  }\n\n  /**\n   * The filter attached to this column's def, if any — already resolved, so a factory def has been\n   * called exactly once, when this column was built. See {@link BaseColumnDef.filter}.\n   */\n  get filter(): ColumnFilter | undefined {\n    return this.config.filter;\n  }\n\n  /**\n   * Whatever the def said this column represents — see {@link ColumnMeta}.\n   *\n   * A plain getter over `config`, which is an `observable.ref` replaced wholesale, so this is\n   * reactive with no state of its own. Every render-prop the table has already receives the\n   * `ColumnModel`: header cells through `<Table.Header>`, body cells through `<Table.Row>`.\n   */\n  get meta(): ColumnMeta | undefined {\n    return this.config.meta;\n  }\n\n  /**\n   * Whether header UIs should offer a filter control. Advisory exactly like `sortable`: the model is\n   * never gated, so a `filterable: false` column with an active filter still narrows rows.\n   */\n  get filterable(): boolean {\n    return this.config.filterable !== false && this.filter !== undefined && !this.selection;\n  }\n\n  /**\n   * Who applies this column's filter. See {@link BaseColumnDef.filterMode}.\n   *\n   * Falls back to the table's resolved mode rather than to a literal, and reads *through* the table\n   * rather than capturing it: columns are built before the data exists and `setData` can point an\n   * existing table at a paged source, so a default baked in at construction would leave those\n   * columns filtering client-side over one page of a server-driven dataset.\n   */\n  get filterMode(): FilterMode {\n    return this.config.filterMode ?? this.table.filterMode;\n  }\n\n  /** The name this column's data goes by on the server. Defaults to `key`. */\n  get field(): string {\n    return this.config.field ?? this.key;\n  }\n\n  /**\n   * This column's contribution to {@link TableModel.filterQuery} — its filter's condition tagged\n   * with `field`. `undefined` unless the column is server-mode with an active filter.\n   */\n  get filterCondition(): FilterCondition | undefined {\n    if (this.filterMode !== \"server\") return undefined;\n    const condition = this.filter?.condition;\n    return condition ? { field: this.field, ...condition } : undefined;\n  }\n\n  /** Whether the built-in search reads this column. See {@link BaseColumnDef.searchable}. */\n  get searchable(): boolean {\n    return this.config.searchable !== false && !this.selection;\n  }\n\n  /**\n   * The value this column's filter compares against — its own `facets` domain, in other words.\n   *\n   * `[]` when no filter is attached: a distinct-values API for every column would be a different\n   * (and much more expensive) feature, and nothing here should be mistaken for one.\n   *\n   * A filter with a `project` (a `BucketFilter`, say) lists its *projected* domain — grades rather\n   * than scores — while the column goes on showing and sorting the raw value.\n   *\n   * Three cost tiers, chosen by the filter rather than configured here:\n   *\n   * | tier | when | walk |\n   * | --- | --- | --- |\n   * | static | `options` declared, `counts` falsy | none |\n   * | values | the default | `rows`, invalidated by `rows` alone |\n   * | counted | `counts: true` | `rows` narrowed by every *other* active filter |\n   *\n   * The walk itself is not what costs — running every other filter per row is, plus the\n   * invalidation storm where one toggle dirties every other column's facets. That is what `counts`\n   * gates, and why the default tier still populates a checkbox list.\n   *\n   * Ordering is declared `options` first in declaration order, then discovered values sorted by\n   * value, blank last. Insertion order alone would be first-appearance-in-rows, which reshuffles\n   * the list every time the table is sorted.\n   *\n   * A count means \"how many rows carry this value\", among rows passing every *other* filter — so it\n   * previews what picking it gives you. Under a set filter's `\"all\"` mode, where each pick narrows\n   * instead of widening, it is the size of the intersection with the current selection instead.\n   *\n   * Zero-count entries are kept: a popover is exactly where you go to undo an over-narrowed filter.\n   * A standing facet rail drops them at the call site —\n   * `facets.filter((f) => f.count > 0 || filter.has(f.value))`.\n   */\n  get facets(): Facet[] {\n    const filter = this.filter;\n    if (!filter) return [];\n\n    // server mode never counts: `rows` are already narrowed by this very filter, so any tally\n    // would describe the current selection rather than what selecting something else would give\n    const counts = filter.counts === true && this.filterMode !== \"server\";\n    const tally = this.facetScan;\n    const declared = filter.options;\n\n    // static tier: the domain was declared and no counts were asked for, so the rows are never read\n    if (!tally) return (declared ?? []).map((value) => ({ value }));\n\n    const facets: Facet[] = [];\n    const seen = new Set<SetFilterValue>();\n\n    for (const value of declared ?? []) {\n      seen.add(value);\n      facets.push(counts ? { value, count: tally.get(value) ?? 0 } : { value });\n    }\n\n    const discovered = [...tally.keys()]\n      .filter((value) => value !== BLANK && !seen.has(value))\n      .sort(compareValues);\n    for (const value of discovered) {\n      facets.push(counts ? { value, count: tally.get(value) ?? 0 } : { value });\n    }\n\n    // blank is offered only where the walk actually found one, so the static tier never shows it\n    const blanks = tally.get(BLANK);\n    if (blanks !== undefined) {\n      facets.push(\n        counts ? { value: BLANK, blank: true, count: blanks } : { value: BLANK, blank: true },\n      );\n    }\n\n    return facets;\n  }\n\n  // The one pass over the rows behind `facets`. `undefined` marks the static tier — a declared\n  // domain with no counts asked for, where there is nothing to discover.\n  private get facetScan(): Map<SetFilterValue, number> | undefined {\n    const filter = this.filter;\n    if (!filter) return undefined;\n    // Server mode is always the static tier. Walking would discover only the values that survived\n    // the current selection, so the list would collapse to what is already chosen and could never\n    // be widened again. A server-mode filter without `options` therefore has an empty facet list.\n    if (this.filterMode === \"server\") return undefined;\n    if (filter.options && !filter.counts) return undefined;\n\n    // The cross-filter deliberately keeps the *other* filters and the search: a row those already\n    // exclude must not be counted, or the tally would promise rows that selecting the value could\n    // never surface.\n    const cross =\n      filter.counts === true ? this.table.filterPredicateExcluding(this.key) : undefined;\n\n    // When picks intersect (a set filter in \"all\" mode) each extra one *narrows*, so a count of\n    // \"rows carrying this value\" answers a question the filter is no longer asking — it would read\n    // higher than the row count you actually get. Fold this column's own filter back in, and the\n    // count becomes the size of the intersection with what is already picked.\n    //\n    // Gated on `counts`, not on `cross`: `filterPredicateExcluding` is undefined whenever no *other*\n    // filter is active, which is exactly the lone-filter case this still has to cover.\n    const intersecting = filter.counts === true && filter.intersecting === true;\n\n    const tally = new Map<SetFilterValue, number>();\n    for (const row of this.table.rows) {\n      // Every row contributes its *keys* — only the count is gated by the cross-filter. Skipping\n      // excluded rows outright would build the domain out of the surviving rows, so a value found\n      // only in excluded ones would vanish from the list instead of sitting there at zero. If it\n      // were currently selected there would then be no way to untick it short of clearing\n      // everything: the funnel still narrows, but the checkbox is gone.\n      const raw = this.config.value(row);\n      const counted = (!cross || cross(row)) && (!intersecting || filter.matches(raw));\n      // `matches` projects internally, so it gets the raw value — but the walk has to project for\n      // itself, or the list would offer raw values that select nothing (see ValueFilter.project).\n      for (const value of facetValues(filter.project ? filter.project(raw) : raw)) {\n        tally.set(value, (tally.get(value) ?? 0) + (counted ? 1 : 0));\n      }\n    }\n    return tally;\n  }\n\n  // The rendered pinned block this column belongs to (outer-edge-first), or undefined when unpinned.\n  private get pinnedSiblings(): ColumnModel[] | undefined {\n    if (this.pinned === \"left\") return this.table.leftPinnedRenderedColumns;\n    if (this.pinned === \"right\") return this.table.rightPinnedRenderedColumns;\n    return undefined;\n  }\n\n  constructor(table: TableModel, config: ColumnConfig) {\n    this.table = table;\n    this.config = config;\n\n    makeObservable<this, \"facetScan\">(this, {\n      config: observable.ref,\n      pinned: observable,\n      hidden: observable,\n      manualWidth: observable,\n\n      width: computed,\n      fixedWidth: computed,\n      grow: computed,\n      isPinnedEdge: computed,\n      isPinnedOuterEdge: computed,\n      offset: computed,\n      title: computed,\n      ariaColIndex: computed,\n      sortDirection: computed,\n      sortIndex: computed,\n      facets: computed,\n      facetScan: computed,\n      filterCondition: computed,\n\n      setPinned: action,\n      setManualWidth: action,\n      setHidden: action,\n      setConfig: action,\n    });\n\n    // `config.pinned` is optional; the model's is not, so the default lands here rather than\n    // leaving every reader to treat `undefined` and `false` as the same thing.\n    this.setPinned(config.pinned ?? false);\n    if (config.hidden === true) this.setHidden(true);\n  }\n\n  /** Pin to an edge, or `false` to unpin. */\n  setPinned(pinned: ColumnPin): void {\n    this.pinned = pinned;\n  }\n\n  setManualWidth(width: number | undefined): void {\n    this.manualWidth = width;\n  }\n\n  setHidden(hidden: boolean): void {\n    this.hidden = hidden;\n  }\n\n  /**\n   * Patch this column's configuration — the way to drive a column option from something the table\n   * was not constructed with, such as React state or a prop:\n   *\n   * ```tsx\n   * useEffect(() => table.column(\"amount\")?.setConfig({ title: label }), [label]);\n   * ```\n   *\n   * `setColumns` deliberately cannot do this: it preserves the `ColumnModel` behind a key it already\n   * has, so a new def for an existing key is ignored wholesale. That is what keeps a column's\n   * position, width, pinning and filter through a def change — and it is why patching is a separate\n   * operation rather than a side effect of redeclaring.\n   *\n   * Everything the user has done to the column survives: `hidden`, `pinned` and `manualWidth` are\n   * their own state, not configuration. `key`, `filter` and `selection` cannot be patched — see\n   * {@link ColumnConfigPatch}.\n   *\n   * One coupling worth knowing: a def with no `render` had it defaulted to `value` when the column\n   * was built, so patching `value` alone changes what is sorted and filtered but not what is\n   * displayed. Patch both to change both.\n   */\n  setConfig(patch: ColumnConfigPatch): void {\n    this.config = { ...this.config, ...patch };\n  }\n\n  /** Sort by this column — replaces the sort list unless `preserve: true` (see TableModel.setSort). */\n  sortBy(direction: SortDirection, opts?: { preserve?: boolean }): void {\n    this.table.setSort(this.key, direction, opts);\n  }\n\n  /** Remove this column from the sort; other columns' sorts are untouched. */\n  clearSort(): void {\n    this.table.clearSort(this.key);\n  }\n\n  /** Raw cell value for a row — what sorting compares and the default render displays. */\n  getValue(row: RowData): unknown {\n    return this.config.value(row);\n  }\n\n  /**\n   * What the built-in search matches this row against: the `searchable` projection when one is\n   * given, otherwise the raw cell value.\n   */\n  searchValue(row: RowData): unknown {\n    const searchable = this.config.searchable;\n    return typeof searchable === \"function\" ? searchable(row) : this.config.value(row);\n  }\n\n  /** Reset this column's filter, if it has one. A no-op otherwise. */\n  clearFilter(): void {\n    this.filter?.clear();\n  }\n\n  /** Ascending comparison of two rows by this column's extracted values (`compare` def or the default). */\n  compareRows(a: RowData, b: RowData): number {\n    return (this.config.compare ?? compareValues)(this.getValue(a), this.getValue(b));\n  }\n\n  /**\n   * The key a def will produce, without building the column — what `TableModel` matches defs by\n   * (`removeColumn`) and checks for collisions with. Mirrors `fromDef`: a string def is its own\n   * key, and a selection def may omit one.\n   */\n  /**\n   * The `meta` a def carries, or `undefined`. Narrowing lives here rather than at the call site\n   * because a string def and a selection def have no place for one — a selection column is a\n   * checkbox, not a column *about* something.\n   */\n  static metaOf(def: ColumnDef<any>): ColumnMeta | undefined {\n    if (typeof def === \"string\") return undefined;\n    return (def as BaseColumnDef<any>).meta;\n  }\n\n  static keyOf(def: ColumnDef<any>): string {\n    if (typeof def === \"string\") return def;\n    const { key, selection } = def as { key?: string; selection?: boolean };\n    return key ?? (selection ? SELECTION_COLUMN_KEY : \"\");\n  }\n\n  static fromDef(table: TableModel, def: ColumnDef<any>): ColumnModel {\n    const normalizedDef = typeof def === \"string\" ? { key: def } : def;\n    const { render, filter, ...config } = normalizedDef as BaseColumnDef<any> & {\n      key?: string;\n      value?: (row: RowData) => unknown;\n      selection?: boolean;\n    };\n    const key = ColumnModel.keyOf(def);\n    // the raw accessor: computed columns bring their own `value`; field columns resolve the key\n    // as a (dot-)path. Selection columns have no value (rendered via <Table.SelectionCell>).\n    const value =\n      config.value ?? (config.selection ? (): null => null : (row: RowData) => getPath(row, key));\n\n    return new ColumnModel(table, {\n      ...config,\n      key,\n      value,\n      // a custom render wins for display; sorting always goes through `value`\n      render: render ?? value,\n      // A factory def is called here and only here. `syncColumns` builds a column only for a key it\n      // does not already have, so this runs once per column: each table gets its own filter and a\n      // remount starts clean, while `setData`/`setColumns` — which preserve the `ColumnModel` —\n      // leave the user's selection alone.\n      filter: typeof filter === \"function\" ? filter() : filter,\n    });\n  }\n}\n","import { observer } from \"mobx-react-lite\";\nimport type { ReactNode } from \"react\";\nimport type { ColumnModel } from \"../column.model\";\n\nexport type RenderColumn = (column: ColumnModel) => ReactNode;\n\n/**\n * Per-cell reactive boundary. `Table.Header`/`Table.Row` iterate the rendered columns and hand each\n * off to a `CellSlot` rather than calling the consumer's render function inline — so the render runs\n * inside *this* component's MobX reaction. The upshot: a cell re-renders only when the observables\n * *it* reads change (e.g. one field of one row), never because a sibling cell or the row did.\n *\n * It renders a transparent fragment, so whatever the consumer returns (a `<Table.Cell>`) lands\n * directly in the parent grid with no wrapper element.\n */\nexport const CellSlot = observer<{ column: ColumnModel; render: RenderColumn }>(\n  ({ column, render }) => {\n    return <>{render(column)}</>;\n  },\n);\n","import type { CSSProperties } from \"react\";\nimport type { ColumnModel } from \"../column.model\";\n\n/**\n * Structural style shared by header and body cells: pinned cells stick to their edge at the\n * column's offset and must be opaque (they overlap scrolling cells). The opaque fill is a CSS var\n * so the consumer owns the color — set `--table-pinned-bg` once (and override it inside the header\n * to match a header background). `Canvas` is a theme-aware system default for zero-config use.\n *\n * These are the *only* styles the library forces on a cell; everything cosmetic (padding, font,\n * borders, hover) is left to the consumer's `className`/`style`.\n */\nexport const pinnedCellStyle = (column: ColumnModel): CSSProperties => {\n  if (!column.pinned) return { position: \"relative\" };\n  return {\n    position: \"sticky\",\n    [column.pinned]: column.offset,\n    background: \"var(--table-pinned-bg, Canvas)\",\n    // every cell is positioned, so paint order is DOM order — without this lift, the unpinned\n    // cells that follow a left-pinned cell would paint over it while scrolling underneath\n    zIndex: 1,\n  };\n};\n","import { type FC, useEffect, useRef } from \"react\";\n\n/**\n * Props a selection-control component receives. Kept intentionally minimal so any checkbox — the\n * native input below, a Chakra `Checkbox`, a Tailwind one — can satisfy it.\n */\nexport interface TableCheckboxProps {\n  checked: boolean;\n  indeterminate?: boolean;\n  onChange: () => void;\n  \"aria-label\"?: string;\n}\n\n/**\n * Zero-config fallback used by `<Table.SelectionCell>` / `<Table.SelectAll>` when the consumer\n * neither registers a `checkbox` on `<Table.Root>` nor passes a render-prop. `indeterminate` is a\n * DOM-only property, so it's applied via ref rather than an attribute.\n */\nexport const NativeCheckbox: FC<TableCheckboxProps> = ({\n  checked,\n  indeterminate = false,\n  onChange,\n  ...rest\n}) => {\n  const ref = useRef<HTMLInputElement>(null);\n  useEffect(() => {\n    if (ref.current) ref.current.indeterminate = indeterminate;\n  }, [indeterminate]);\n  return <input ref={ref} type=\"checkbox\" checked={checked} onChange={onChange} {...rest} />;\n};\n","import { observer } from \"mobx-react-lite\";\nimport { type CSSProperties, type FC, useEffect, useRef, useState } from \"react\";\nimport type { ColumnModel } from \"../column.model\";\n\nexport interface TableResizerProps {\n  column: ColumnModel;\n}\n\n/**\n * Drag handle on a header cell's edge that resizes its column. Dragging sets the column's\n * `manualWidth` (treated as a fixed width in the distribution, so the remaining flex columns reflow\n * to fill); double-click resets it to auto. Right-pinned columns are anchored to the right, so their\n * handle sits on the left edge and the drag delta is inverted.\n *\n * Move/up listeners live on the window for the duration of the drag, so the resize tracks and ends\n * wherever the pointer is released — not only over the handle.\n */\nexport const TableResizer: FC<TableResizerProps> = observer(({ column }) => {\n  const [resizing, setResizing] = useState(false);\n  const drag = useRef<{ startX: number; startWidth: number } | null>(null);\n  const raf = useRef<number | undefined>(undefined);\n  const teardown = useRef<(() => void) | undefined>(undefined);\n\n  const onLeftEdge = column.pinned === \"right\";\n\n  // if we unmount mid-drag, drop the window listeners (no state update here — the component is gone)\n  useEffect(() => () => teardown.current?.(), []);\n\n  const beginResize = (e: React.PointerEvent): void => {\n    e.preventDefault();\n    e.stopPropagation(); // don't open the header menu\n    drag.current = { startX: e.clientX, startWidth: column.width };\n    setResizing(true);\n\n    let latestX = e.clientX;\n    const applyFrame = (): void => {\n      raf.current = undefined;\n      if (!drag.current) return;\n      const delta = latestX - drag.current.startX;\n      const next = drag.current.startWidth + (onLeftEdge ? -delta : delta);\n      column.setManualWidth(Math.max(column.minWidth, next));\n    };\n    const onMove = (ev: PointerEvent): void => {\n      latestX = ev.clientX;\n      if (raf.current === undefined) raf.current = requestAnimationFrame(applyFrame);\n    };\n    const stop = (): void => {\n      drag.current = null;\n      setResizing(false);\n      teardown.current?.();\n    };\n\n    teardown.current = (): void => {\n      window.removeEventListener(\"pointermove\", onMove);\n      window.removeEventListener(\"pointerup\", stop);\n      window.removeEventListener(\"pointercancel\", stop);\n      if (raf.current !== undefined) {\n        cancelAnimationFrame(raf.current);\n        raf.current = undefined;\n      }\n      document.body.style.userSelect = \"\";\n      document.body.style.cursor = \"\";\n      teardown.current = undefined;\n    };\n\n    window.addEventListener(\"pointermove\", onMove);\n    window.addEventListener(\"pointerup\", stop);\n    window.addEventListener(\"pointercancel\", stop);\n    // keep the resize cursor and suppress text selection for the whole drag, not just over the handle\n    document.body.style.userSelect = \"none\";\n    document.body.style.cursor = \"col-resize\";\n  };\n\n  const resetWidth = (e: React.MouseEvent): void => {\n    e.stopPropagation();\n    column.setManualWidth(undefined);\n  };\n\n  const style: CSSProperties = {\n    position: \"absolute\",\n    top: 0,\n    ...(onLeftEdge ? { left: 0 } : { right: 0 }),\n    width: \"9px\",\n    height: \"100%\",\n    cursor: \"col-resize\",\n    touchAction: \"none\",\n    userSelect: \"none\",\n    zIndex: 1,\n  };\n\n  return (\n    <div\n      role=\"separator\"\n      aria-orientation=\"vertical\"\n      className=\"column-resizer\"\n      data-resizing={resizing || undefined}\n      onPointerDown={beginResize}\n      onDoubleClick={resetWidth}\n      style={style}\n    />\n  );\n});\n","import { createContext, type FC, useContext } from \"react\";\nimport type { TableCheckboxProps } from \"./components/checkbox\";\nimport { NativeCheckbox } from \"./components/checkbox\";\nimport type { TableModel } from \"./table.model\";\n\nexport const tableContext = createContext<TableModel | undefined>(undefined);\nexport const useTableContext = () => {\n  const context = useContext(tableContext);\n  if (!context) {\n    throw new Error(\"Table context not available. Are you within the <Table.Root /> component?\");\n  }\n  return context;\n};\n\nexport const TableProvider = tableContext.Provider;\n\n/**\n * Slots let a consumer register defaults once on `<Table.Root>` (currently just the selection\n * `checkbox`) that the built-in parts fall back to. Defaults to a native checkbox so selection\n * works with zero wiring.\n */\nexport interface TableSlots {\n  checkbox: FC<TableCheckboxProps>;\n}\n\nconst defaultSlots: TableSlots = { checkbox: NativeCheckbox };\n\nexport const slotsContext = createContext<TableSlots>(defaultSlots);\nexport const TableSlotsProvider = slotsContext.Provider;\nexport const useTableSlots = (): TableSlots => useContext(slotsContext);\n\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Marks the scrollport. `<Table.Scroll>` provides it; the parts that only work inside a scrolling\n * box check for it.\n */\nexport const scrollportContext = createContext(false);\nexport const TableScrollportProvider = scrollportContext.Provider;\n\n/**\n * Throws when a part that belongs inside the scrollport is mounted outside one.\n *\n * Worth a hard error rather than a degraded render: without `<Table.Scroll>` no width is ever\n * measured, so the render gate never opens and the table is simply blank — no message, nothing in\n * the DOM to inspect, and every symptom pointing at the data rather than the markup.\n *\n * **Development only**, behind the same `process.env.NODE_ENV` guard mobx uses (see\n * `makeRoutes`), so a consumer's bundler strips it from production builds. The check is purely\n * structural, so production has nothing left to learn from it.\n */\nexport const useOutsideScrollportGuard = (component: string): void => {\n  const inScrollport = useContext(scrollportContext);\n  if (process.env.NODE_ENV !== \"production\" && inScrollport) {\n    throw new Error(\n      `<Table.${component}> must be rendered outside <Table.Scroll>, as a direct child of ` +\n        `<Table.Root>. Inside the scrolling box it sits within the box the scrollbars measure, so ` +\n        `the vertical scrollbar runs past it and it stops short of the gutter.`,\n    );\n  }\n};\n\nexport const useScrollportGuard = (component: string): void => {\n  const inScrollport = useContext(scrollportContext);\n  if (process.env.NODE_ENV !== \"production\" && !inScrollport) {\n    throw new Error(\n      `<Table.${component}> must be rendered inside <Table.Scroll>. Chrome that belongs outside the ` +\n        `scrolling box — <Table.StatusBar>, a toolbar, pagination — goes directly in <Table.Root>.`,\n    );\n  }\n};\n","import { observer } from \"mobx-react-lite\";\nimport {\n  type CSSProperties,\n  type FC,\n  Fragment,\n  type HTMLAttributes,\n  memo,\n  type ReactNode,\n} from \"react\";\nimport type { ColumnModel } from \"../column.model\";\nimport { useScrollportGuard, useTableContext } from \"../table.context\";\nimport type { RowData } from \"../table.types\";\nimport { CellSlot, type RenderColumn } from \"./cell-slot\";\nimport { pinnedCellStyle } from \"./cell-style\";\n\nexport interface TableBodyProps {\n  className?: string;\n  style?: CSSProperties;\n  /** Renders one row. Called once per *rendered* row; return a `<Table.Row>`. */\n  children: (row: RowData) => ReactNode;\n}\n\n/**\n * The virtualized body. Owns the scroll-sized spacer and the `translate3d` window offset, then maps\n * the rendered slice of rows through the `children` render-prop. Rows are keyed by their row id\n * (see `rowIds`): by default the row's own object identity — stable under sort, filter, scroll and\n * `appendRows` — or the consumer's `getRowId`, which stays stable even when a refetch replaces the\n * row objects with fresh ones.\n */\nexport const TableBody: FC<TableBodyProps> = observer(({ className, style, children }) => {\n  const table = useTableContext();\n  useScrollportGuard(\"Body\");\n\n  return (\n    <div style={{ width: `${table.virtualWidth}px`, height: `${table.virtualHeight}px` }}>\n      <div\n        style={{\n          position: \"absolute\",\n          transform: `translate3d(0px, ${table.virtualOffsetY}px, 0px)`,\n        }}\n      >\n        <div\n          role=\"rowgroup\"\n          className={className}\n          style={{\n            display: \"grid\",\n            gridTemplateColumns: table.gridTemplateColumns,\n            ...style,\n          }}\n        >\n          {table.renderedRows.map((row) => (\n            <Fragment key={table.rowIds.get(row)}>{children(row)}</Fragment>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n});\n\nexport interface TableRowProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\n  row: RowData;\n  /** Renders one body cell. Called once per rendered column; return a `<Table.Cell>` / `<Table.SelectionCell>`. */\n  children: RenderColumn;\n}\n\n/**\n * A single body row. Mirrors the header's layout ownership (grid subgrid, pinned/spacer ordering)\n * and defers each cell to `children` via a per-cell `CellSlot`. Exposes `data-selected` so the\n * consumer can highlight selected rows in CSS. Reading selection here (not row field data) keeps\n * single-cell content updates from re-rendering the whole row — only a selection toggle does.\n */\nconst TableRowInner: FC<TableRowProps> = observer(\n  ({ row, className, style, children, ...rest }) => {\n    const table = useTableContext();\n    const displayIndex = table.displayRowIndexMap.get(row);\n\n    return (\n      <div\n        {...rest}\n        role=\"row\"\n        // 1-based, offset past the header row (aria-rowindex 1)\n        aria-rowindex={displayIndex !== undefined ? displayIndex + 2 : undefined}\n        aria-selected={table.selectable ? table.isRowSelected(row) : undefined}\n        data-selected={table.isRowSelected(row) || undefined}\n        data-expanded={table.isRowExpanded(row) || undefined}\n        className={className}\n        style={{\n          height: `${table.rowHeight}px`,\n          display: \"grid\",\n          gridColumn: \"1 / -1\",\n          gridTemplateColumns: \"subgrid\",\n          alignItems: \"stretch\",\n          textAlign: \"left\",\n          ...style,\n        }}\n      >\n        {table.leftPinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n        <div role=\"presentation\" />\n        {table.unpinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n        {table.rightPinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n      </div>\n    );\n  },\n);\n\n/**\n * Memoized on identity so scrolling (and filtering) only renders rows that actually entered/changed.\n * `children` (the per-column render-prop) gets a fresh closure on every `TableBody` render, so it is\n * *deliberately excluded* from the comparison — for a row still in the window that closure is\n * equivalent (it closes over the same `row` and reads row/column state live). Every other prop —\n * including pass-through DOM props like `onClick` — is compared shallowly. Layout changes still\n * flow through because the inner `observer` re-renders on the column/width observables it reads, and\n * per-cell data changes flow through each `CellSlot`'s own observer — neither is gated by this memo.\n * (Pass stable `className`/`style`/handlers, not fresh inline values, or the row re-renders every frame.)\n */\nexport const TableRow = memo(TableRowInner, (prev, next) => {\n  const keys = new Set([...Object.keys(prev), ...Object.keys(next)]);\n  for (const key of keys) {\n    if (key === \"children\") continue;\n    if (prev[key as keyof TableRowProps] !== next[key as keyof TableRowProps]) return false;\n  }\n  return true;\n});\n\nexport interface TableCellProps extends HTMLAttributes<HTMLDivElement> {\n  column: ColumnModel;\n}\n\n/**\n * A single body cell. Owns pinning/offset/`data-pinned*`; cosmetics are the consumer's via\n * `className`/`style`, other DOM props pass through.\n */\nexport const TableCell: FC<TableCellProps> = observer(\n  ({ column, children, className, style, ...rest }) => {\n    return (\n      <div\n        {...rest}\n        role=\"cell\"\n        aria-colindex={column.ariaColIndex}\n        data-pinned={column.pinned || undefined}\n        data-pinned-corner={\n          (column.pinned && column.isPinnedOuterEdge && column.pinned) || undefined\n        }\n        data-pinned-edge={column.isPinnedEdge || undefined}\n        className={className}\n        style={{ ...pinnedCellStyle(column), ...style }}\n      >\n        {children}\n      </div>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { CSSProperties, FC, HTMLAttributes } from \"react\";\nimport type { ColumnModel } from \"../column.model\";\nimport { useScrollportGuard, useTableContext } from \"../table.context\";\nimport { CellSlot, type RenderColumn } from \"./cell-slot\";\nimport { pinnedCellStyle } from \"./cell-style\";\n\nexport interface TableHeaderProps {\n  className?: string;\n  style?: CSSProperties;\n  /**\n   * Renders one header cell. Called once per *rendered* column (the library owns which columns are\n   * live, their order, and the virtualization spacer); return a `<Table.ColumnHeader>` /\n   * `<Table.SelectionHeaderCell>`.\n   */\n  children: RenderColumn;\n}\n\n/**\n * The sticky header row group. Owns layout — the grid track template, the left-pinned / spacer /\n * unpinned / right-pinned ordering — and defers each cell's content to the `children` render-prop\n * via a per-cell `CellSlot`.\n *\n * Sized by `table.headerHeight` — the configured `headerHeight`, or `rowHeight` when the config\n * says nothing — applied as a **border-box** height so that number is the space the header\n * occupies rather than a claim about it. `<Table.Overlay>` starts below it and `<Table.Root>`\n * publishes it as `--table-header-height`, and nothing has to be kept in agreement because there\n * is one number.\n *\n * Express the gap between the header and the rows as padding here: border-box means padding comes\n * out of that height rather than adding to it, so the published number stays true. Content needing\n * more room than the height overflows onto the first row instead of growing the header — raise\n * `headerHeight` for a taller one.\n *\n * The inner `role=\"row\"` stretches to fill whatever is left, so it carries no height of its own —\n * header cells are free to be styled without fighting an inline number.\n *\n * Nothing here reports back to the model: `table.headerHeight` is what the config says, so it is\n * already right when the first frame paints. A table composed without this component still\n * reserves that height for its overlays — `headerHeight: 0` is how you say there is no header.\n */\nexport const TableHeader: FC<TableHeaderProps> = observer(({ className, style, children }) => {\n  const table = useTableContext();\n  useScrollportGuard(\"Header\");\n\n  return (\n    <div\n      role=\"rowgroup\"\n      className={[\"table-header\", className].filter(Boolean).join(\" \")}\n      style={{\n        position: \"sticky\",\n        top: 0,\n        zIndex: 20,\n        width: `${table.virtualWidth}px`,\n        height: `${table.headerHeight}px`,\n        // the height *is* the space taken, so a consumer's padding comes out of it rather than\n        // extending it past the number `--table-header-height` and `<Table.Overlay>` are using\n        boxSizing: \"border-box\",\n        display: \"grid\",\n        gridTemplateColumns: table.gridTemplateColumns,\n        ...style,\n      }}\n    >\n      {/*\n       * The rounded muted header background is a `.table-header::before` layer (consumer CSS). The\n       * rowgroup is `virtualWidth` wide, so a background on it would put its corners at the ends of\n       * the scrollable content — never both on screen. That layer is instead `position: sticky;\n       * left: 0` with an explicit viewport width so both rounded corners stay visible at any scrollX.\n       */}\n      <div\n        role=\"row\"\n        aria-rowindex={1}\n        style={{\n          gridColumn: \"1 / -1\",\n          gridRow: \"1\",\n          display: \"grid\",\n          gridTemplateColumns: \"subgrid\",\n          alignItems: \"stretch\",\n          textAlign: \"left\",\n        }}\n      >\n        {table.leftPinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n        <div role=\"presentation\" />\n        {table.unpinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n        {table.rightPinnedRenderedColumns.map((col) => (\n          <CellSlot key={col.key} column={col} render={children} />\n        ))}\n      </div>\n    </div>\n  );\n});\n\nexport interface TableColumnHeaderProps extends HTMLAttributes<HTMLDivElement> {\n  column: ColumnModel;\n}\n\n/**\n * A single header cell. Owns the structural bits (sticky pinning, offset, `data-pinned*`) and stays\n * cosmetically open — the consumer's `className`/`style` add padding, font, borders, etc.; other\n * DOM props pass through.\n */\nexport const TableColumnHeader: FC<TableColumnHeaderProps> = observer(\n  ({ column, children, className, style, ...rest }) => {\n    return (\n      <div\n        {...rest}\n        role=\"columnheader\"\n        aria-colindex={column.ariaColIndex}\n        aria-sort={\n          column.sortDirection\n            ? column.sortDirection === \"asc\"\n              ? \"ascending\"\n              : \"descending\"\n            : undefined\n        }\n        data-pinned={column.pinned || undefined}\n        data-pinned-edge={column.isPinnedEdge || undefined}\n        data-pinned-corner={\n          (column.pinned && column.isPinnedOuterEdge && column.pinned) || undefined\n        }\n        className={className}\n        style={{\n          ...pinnedCellStyle(column),\n          scrollSnapAlign: column.pinned ? undefined : \"start\",\n          ...style,\n        }}\n      >\n        {children}\n      </div>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC, ReactNode } from \"react\";\nimport type { ColumnModel } from \"../column.model\";\nimport { useTableContext, useTableSlots } from \"../table.context\";\nimport type { RowData } from \"../table.types\";\nimport type { TableCheckboxProps } from \"./checkbox\";\nimport { TableCell } from \"./table-body\";\nimport { TableColumnHeader } from \"./table-header\";\n\n// The selection state a render-prop receives — a subset of TableCheckboxProps.\ntype RowSelectState = Pick<TableCheckboxProps, \"checked\" | \"onChange\">;\ntype SelectAllState = Pick<TableCheckboxProps, \"checked\" | \"indeterminate\" | \"onChange\">;\n\n// centers the control both axes regardless of the consumer's cell CSS\nconst centerStyle = { display: \"flex\", alignItems: \"center\", justifyContent: \"center\" } as const;\n\nexport interface SelectionCellProps {\n  column: ColumnModel;\n  row: RowData;\n  /** Custom control. Omit to use the checkbox registered on `<Table.Root>` (native by default). */\n  children?: (state: RowSelectState) => ReactNode;\n}\n\n/** A body cell wired to per-row selection. Renders the registered checkbox unless given a render-prop. */\nexport const SelectionCell: FC<SelectionCellProps> = observer(({ column, row, children }) => {\n  const table = useTableContext();\n  const { checkbox: Checkbox } = useTableSlots();\n  const state: RowSelectState = {\n    checked: table.isRowSelected(row),\n    onChange: () => table.toggleRow(row),\n  };\n  return (\n    <TableCell column={column} style={centerStyle}>\n      {children ? children(state) : <Checkbox {...state} aria-label=\"Select row\" />}\n    </TableCell>\n  );\n});\n\nexport interface SelectAllProps {\n  /** Custom control. Omit to use the checkbox registered on `<Table.Root>` (native by default). */\n  children?: (state: SelectAllState) => ReactNode;\n}\n\n/** The select-all control (checked / indeterminate / none). Place inside a header cell, or use `<Table.SelectionHeaderCell>`. */\nexport const SelectAll: FC<SelectAllProps> = observer(({ children }) => {\n  const table = useTableContext();\n  const { checkbox: Checkbox } = useTableSlots();\n  const state: SelectAllState = {\n    checked: table.allRowsSelected,\n    indeterminate: table.someRowsSelected,\n    onChange: () => table.toggleAllRows(),\n  };\n  return children ? <>{children(state)}</> : <Checkbox {...state} aria-label=\"Select all rows\" />;\n});\n\nexport interface SelectionHeaderCellProps {\n  column: ColumnModel;\n  children?: (state: SelectAllState) => ReactNode;\n}\n\n/** A header cell holding the centered select-all control — the header twin of `<Table.SelectionCell>`. */\nexport const SelectionHeaderCell: FC<SelectionHeaderCellProps> = observer(\n  ({ column, children }) => {\n    return (\n      <TableColumnHeader column={column} style={centerStyle}>\n        <SelectAll>{children}</SelectAll>\n      </TableColumnHeader>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC, HTMLAttributes, ReactNode } from \"react\";\nimport { useScrollportGuard, useTableContext } from \"../table.context\";\n\nexport type TableOverlayProps = HTMLAttributes<HTMLDivElement>;\n\n/**\n * The placement primitive every table-wide message is built from, and the one the gated slots —\n * `<Table.Empty>`, `<Table.Loading>`, `<Table.Error>` — each wrap. Render it anywhere inside\n * `<Table.Scroll>` and show it yourself.\n *\n * It exists as public API because the placement is the hard part and the gate isn't. Filling the\n * viewport below a sticky header, staying centred in the visible area at any scroll offset, and\n * sizing off the table's own height takes a measured header and an anchor the consumer can't move;\n * deciding whether to mention a failed save takes an `if`. The gated slots cover the states the\n * table can evaluate for itself — everything else is yours:\n *\n * ```tsx\n * {saveError && <Table.Overlay>Couldn't save changes</Table.Overlay>}\n * ```\n *\n * Carries no data attribute of its own: `data-empty` and friends mean \"the table decided this\",\n * and a hand-shown overlay hasn't earned that claim. Pass your own if you want a styling hook.\n *\n * Structurally it is an out-of-flow anchor with a sticky child. Both halves are load-bearing:\n *\n * - The anchor is **absolutely positioned at the top of the scrollport**, so where it lands does\n *   not depend on where in `<Table.Scroll>` this was written. A sticky element would inherit its\n *   flow position instead, putting the message below the rows rather than over them — and there is\n *   no ordering rule that could fix that, since it would have to be `virtualHeight` above wherever\n *   the consumer put it.\n * - Being out of flow is also what keeps the overlay from contributing to the scrollport's content\n *   height, which would otherwise make a root that hugs its content circular — the box sized from\n *   the overlay, the overlay sized from the box.\n * - The anchor spans the **scrollable extent** rather than the viewport, because that is the room\n *   the sticky child needs to travel: a shorter anchor clamps it and the message drifts up as you\n *   reach the bottom. `table.height` is the floor for the empty case, where there is no extent to\n *   speak of, and it is the measured client box — so the anchor adds no scrollable overflow of its\n *   own and an empty table gets no scrollbar out of it.\n *\n *   The extent is the model's (`virtualHeight`), not the scrollport's measured `scrollHeight`,\n *   which is what keeps this free of a second measurement. The one thing that gets past it is\n *   `<Table.Gutter>`: it adds a row's worth of flow content the model doesn't count here, so an\n *   overlay shown *over rows* with a gutter present drifts up by the gutter's height at the very\n *   bottom of the scroll. Nothing in the library can reach that today — all three gated slots\n *   render only when there are no rows, and so nothing to scroll — and the alternative is the\n *   gutter reporting its height to the model for one bounded edge case.\n * - The sticky child is what keeps the message in the visible area at any scroll offset, on the\n *   compositor rather than through a re-render per scroll event.\n *\n * `z-index` sits between the rows (`auto`) and the header (`20`), so what the overlay covers is\n * decided here rather than by the order the consumer happened to write things in. The anchor is\n * `pointer-events: none` so only the sized box intercepts, exactly as when the box was the whole\n * of it.\n */\nexport const TableOverlay: FC<TableOverlayProps & { children?: ReactNode }> = observer(\n  ({ children, className, style, ...rest }) => {\n    const table = useTableContext();\n    useScrollportGuard(\"Overlay\");\n    return (\n      <div\n        role=\"presentation\"\n        style={{\n          position: \"absolute\",\n          top: 0,\n          left: 0,\n          width: `${table.virtualWidth}px`,\n          height: `${Math.max(table.virtualHeight + table.headerHeight, table.height)}px`,\n          zIndex: 10,\n          pointerEvents: \"none\",\n        }}\n      >\n        <div\n          {...rest}\n          className={className}\n          style={{\n            position: \"sticky\",\n            // `table.headerHeight` is the border-box height `<Table.Header>` renders at, so any\n            // header padding is already inside it and this starts exactly where the rows do. It is\n            // the configured number rather than a measured one, so a table composed without a\n            // header reserves it anyway — `headerHeight: 0` for one of those.\n            top: `${table.headerHeight}px`,\n            left: 0,\n            width: \"var(--table-scroll-width)\",\n            height: `${Math.max(0, table.height - table.headerHeight)}px`,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            pointerEvents: \"auto\",\n            ...style,\n          }}\n        >\n          {children}\n        </div>\n      </div>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC } from \"react\";\nimport { useTableContext } from \"../table.context\";\nimport { TableOverlay, type TableOverlayProps } from \"./table-overlay\";\n\n/**\n * The empty-state surface. Render it anywhere inside `<Table.Scroll>`; it shows itself only when\n * the table is genuinely empty — settled, with no rows — and renders nothing while a first load is\n * still running.\n *\n * That gating used to be the consumer's, which meant every table author wrote\n * `list.loading ? undefined : <Empty/>` once they noticed their table claiming \"no results\" during\n * the first fetch. The table can tell the difference now, so it does. The library decides *when*;\n * what to say is still entirely yours, including the distinction the gate can't make for you.\n *\n * To tell \"no data\" from \"a filter hid it all\", read `rows` — the dataset *before* filtering.\n * Inside this slot there is nothing on screen by definition, so any rows at all mean the filter\n * is what emptied it:\n *\n * ```tsx\n * <Table.Empty>{table.rows.length ? \"No matches\" : \"No studies yet\"}</Table.Empty>\n * ```\n *\n * Owns placement only — cosmetics are the consumer's, and `data-empty` is the styling hook.\n */\nexport const TableEmpty: FC<TableOverlayProps> = observer(({ children, ...rest }) => {\n  const table = useTableContext();\n  if (!table.isEmpty) return null;\n  return (\n    <TableOverlay data-empty=\"\" {...rest}>\n      {children}\n    </TableOverlay>\n  );\n});\n","import { observer } from \"mobx-react-lite\";\nimport type { FC, ReactNode } from \"react\";\nimport { useTableContext } from \"../table.context\";\nimport { TableOverlay, type TableOverlayProps } from \"./table-overlay\";\n\n// `children` is widened to a render prop, which HTMLAttributes' own ReactNode-only\n// declaration would otherwise forbid.\nexport interface TableErrorProps extends Omit<TableOverlayProps, \"children\"> {\n  /**\n   * What to say about the failure. A function is called with whatever the source failed with, so a\n   * message can be derived from it without reaching back into the model:\n   *\n   * ```tsx\n   * <Table.Error>{(error) => (error instanceof HttpError ? error.status : \"Something went wrong\")}</Table.Error>\n   * ```\n   */\n  children?: ReactNode | ((error: unknown) => ReactNode);\n}\n\n/**\n * The failure surface. Render it inside `<Table.Scroll>` alongside `<Table.Empty>` and\n * `<Table.Loading>`; it shows itself only when the request failed and left nothing to show for it.\n *\n * **A failed *refresh* does not render this**, and that is the whole point of the gate. Rows\n * already on screen are still perfectly good rows, and blanking a working table because a\n * background request came back 500 destroys scroll position, column arrangement and selection over\n * something the user never asked for. The table says nothing at all about that case; the error is\n * still on your lazy, or still in the prop you passed, and belongs on a refresh control or in a\n * toast — somewhere that isn't the rows.\n *\n * The three slots are mutually exclusive by construction, so ordering them is not your problem:\n * `loading` excludes a failure, `isEmpty` excludes both, and this renders only for the failure\n * with nothing behind it.\n *\n * Needs the table to have been told about the failure — a `data` that is a lazy, which carries its\n * own `error`, or an `error` prop passed to `useTable` alongside an array or getter. If the error\n * isn't about the dataset at all, `<Table.Overlay>` gives you the same surface with no gate.\n *\n * Owns placement only — cosmetics are the consumer's, and `data-error` is the styling hook.\n */\nexport const TableError: FC<TableErrorProps> = observer(({ children, ...rest }) => {\n  const table = useTableContext();\n  const error = table.error;\n  if (error === undefined) return null;\n  return (\n    <TableOverlay data-error=\"\" {...rest}>\n      {typeof children === \"function\" ? children(error) : children}\n    </TableOverlay>\n  );\n});\n","import { observer } from \"mobx-react-lite\";\nimport type { FC, HTMLAttributes, ReactNode } from \"react\";\nimport { useScrollportGuard, useTableContext } from \"../table.context\";\n\nexport interface TableGutterProps extends HTMLAttributes<HTMLDivElement> {\n  children?: ReactNode;\n  /**\n   * Height in pixels. Defaults to the table's `rowHeight`, so the strip reads as one more row and\n   * needs no measuring — the same fixed-height contract the rows themselves are under.\n   */\n  height?: number;\n}\n\n/**\n * One more row's worth of space at the **end of the rows**, inside the scroll flow.\n *\n * You only see it by scrolling to the bottom of the list, which is exactly what it is for: the\n * indicator that shows up when you outrun the fetch. With a paged source the table is already\n * loading the next page as the window nears the end, so the only thing left to say down there is\n * either \"still coming\" or \"that was all\":\n *\n * ```tsx\n * <Table.Gutter>\n *   {feed.loadingMore ? <Spinner /> : !feed.hasMore && <EndOfResults total={feed.total} />}\n * </Table.Gutter>\n * ```\n *\n * Render it after `<Table.Body>`. It is part of the scroll content rather than floating over it,\n * which is the whole point and the reason it can't be built from `<Table.Overlay>`: an overlay\n * fills the viewport and stays centred in it, so a message built on one would cover the rows\n * instead of following them. `<Table.Body>` already reserves the virtualized height in normal flow,\n * so this lands after it with no geometry of its own.\n *\n * **It is not a bar across the bottom of the table.** A persistent \"Showing 1,000 of 2,000\" belongs\n * on screen whether or not you have scrolled anywhere, which is a different component in a\n * different place — and the two are wanted together, a spinner at the tail *and* a count that is\n * always visible. Nor is it a `<tfoot>`: that is a row, aligned to the columns and scrolling\n * horizontally with them, where this is a strip that knows nothing about columns.\n *\n * **Ungated, unlike `<Table.Empty>` / `<Table.Loading>` / `<Table.Error>`** — what goes here is\n * what the *source* knows, so the condition is yours. Nothing needs guarding for the empty and\n * error states in practice, since both render an overlay across the viewport and there are no rows\n * to scroll past to reach this.\n *\n * **Entirely ungated** — unlike `<Table.Empty>` / `<Table.Loading>` / `<Table.Error>`, which render\n * the states the table can evaluate for itself. What goes here is what the *source* knows, so the\n * condition is yours:\n *\n * ```tsx\n * <Table.Footer>\n *   {feed.loadingMore ? <Spinner /> : feed.hasMore ? null : <EndOfResults total={feed.total} />}\n * </Table.Footer>\n * ```\n *\n * Reach the source through `table.pages` when all you have is the model:\n *\n * ```tsx\n * const FooterStatus = observer(() => {\n *   const { pages } = useTableContext();\n *   if (!pages) return null;\n *   return pages.loadingMore ? <Spinner /> : pages.hasMore ? null : <EndOfResults />;\n * });\n * ```\n *\n * Rendering `null` children still occupies the strip. Skip the element entirely for no footer at\n * all — a table with nothing to say below its rows shouldn't reserve a row's worth of space.\n *\n * Sticky-left at the visible width, like every other table-wide surface, so it stays put under\n * horizontal scrolling rather than sliding out of view with the columns. Vertically it scrolls with\n * the rows — it is part of the list, not part of the frame.\n */\nexport const TableGutter: FC<TableGutterProps> = observer(\n  ({ children, className, style, height, ...rest }) => {\n    const table = useTableContext();\n    useScrollportGuard(\"Gutter\");\n    return (\n      <div\n        {...rest}\n        role=\"presentation\"\n        data-table-gutter=\"\"\n        className={className}\n        style={{\n          position: \"sticky\",\n          left: 0,\n          width: \"var(--table-scroll-width)\",\n          height: `${height ?? table.rowHeight}px`,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          ...style,\n        }}\n      >\n        {children}\n      </div>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC } from \"react\";\nimport { useTableContext } from \"../table.context\";\nimport { useSlowLoading, type SlowLoadingOptions } from \"../../util/use-slow-loading\";\nimport { TableOverlay, type TableOverlayProps } from \"./table-overlay\";\n\nexport interface TableLoadingProps extends TableOverlayProps {\n  /**\n   * Timing for the indicator, passed to `useSlowLoading`. Defaults to 300 ms before it appears and\n   * 300 ms minimum on screen, so a fast first load renders nothing at all rather than flashing.\n   *\n   * Pass `false` to show it the moment loading starts.\n   */\n  sustain?: boolean | SlowLoadingOptions;\n}\n\nconst NEVER: SlowLoadingOptions = { after: 0, minDuration: 0 };\n\n/**\n * The first-load surface. Render it inside `<Table.Scroll>` alongside `<Table.Empty>`; it shows\n * itself only while the table has nothing yet and a request is in flight, and only once that wait\n * has gone on long enough to be worth mentioning.\n *\n * It has nothing to say about a *refresh* — rows already on screen stay put and stay interactive,\n * because replacing them to fetch mostly-identical rows would throw away scroll position, column\n * arrangement and selection. Nor does the table: a request running behind rows it already has is\n * not its business, and whoever owns the fetching knows about it anyway (`refreshing` on a lazy,\n * `isFetching` on a query). Put a quiet indication somewhere that isn't the rows themselves.\n *\n * Needs the table to have been told about loading — a `data` that is a lazy, which knows on its\n * own, or a `loading` prop passed to `useTable` alongside an array or getter.\n */\nexport const TableLoading: FC<TableLoadingProps> = observer(({ children, sustain, ...rest }) => {\n  const table = useTableContext();\n  const show = useSlowLoading(\n    table.loading,\n    sustain === false ? NEVER : sustain === true || sustain === undefined ? undefined : sustain,\n  );\n  if (!show) return null;\n  return (\n    <TableOverlay data-loading=\"\" {...rest}>\n      {children}\n    </TableOverlay>\n  );\n});\n","import { observer } from \"mobx-react-lite\";\nimport { type CSSProperties, type FC, memo, type ReactNode } from \"react\";\nimport { useScrollportGuard, useTableContext } from \"../table.context\";\nimport type { RowData } from \"../table.types\";\n\nexport interface TableExpansionProps {\n  row: RowData;\n  className?: string;\n  style?: CSSProperties;\n  children?: ReactNode;\n}\n\n/**\n * The detail panel below an expanded row. Render it as a sibling immediately after the row's\n * `<Table.Row>` inside `<Table.Body>`'s render-prop, gated on `table.isRowExpanded(row)`.\n *\n * Owns the geometry contract: the block is exactly `expansionHeight` tall (taller content scrolls\n * internally), and the cell pins to the viewport (`sticky` + explicit width — the same trick as\n * the header background) so horizontal scrolling moves the columns underneath the panel, not the\n * panel itself. Cosmetics are the consumer's via `className`/`style`.\n */\nconst TableExpansionInner: FC<TableExpansionProps> = observer(({ className, style, children }) => {\n  const table = useTableContext();\n  useScrollportGuard(\"Expansion\");\n  return (\n    <div\n      role=\"row\"\n      data-expansion=\"\"\n      style={{ gridColumn: \"1 / -1\", height: `${table.expansionHeight}px`, minWidth: 0 }}\n    >\n      <div\n        role=\"cell\"\n        data-expansion=\"\"\n        className={className}\n        style={{\n          position: \"sticky\",\n          left: 0,\n          width: \"var(--table-scroll-width)\",\n          height: \"100%\",\n          overflowY: \"auto\",\n          ...style,\n        }}\n      >\n        {children}\n      </div>\n    </div>\n  );\n});\n\n/**\n * Memoized on row identity like `TableRow`, with `children` deliberately excluded: the panel's\n * element tree is rebuilt by the body render-prop every window shift, but for the same row it is\n * equivalent. Panel content must derive from `row` (or be an observer reading live state) — not\n * from other values captured in the render-prop closure.\n */\nexport const TableExpansion = memo(\n  TableExpansionInner,\n  (prev, next) =>\n    prev.row === next.row && prev.className === next.className && prev.style === next.style,\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC } from \"react\";\nimport { TableProvider, TableSlotsProvider } from \"../table.context\";\nimport type { TableModel } from \"../table.model\";\nimport { NativeCheckbox, type TableCheckboxProps } from \"./checkbox\";\n\nexport interface TableRootProps {\n  table: TableModel;\n  children?: React.ReactNode;\n  style?: React.CSSProperties;\n  className?: string;\n  /**\n   * Selection control used by `<Table.SelectionCell>` / `<Table.SelectAll>` when no render-prop is\n   * given. Register it once here to capture your app's checkbox everywhere. Defaults to a native\n   * `<input type=\"checkbox\">`.\n   */\n  checkbox?: FC<TableCheckboxProps>;\n}\n\n/**\n * The table's outer box: a flex column holding `<Table.Scroll>` and whatever chrome sits outside\n * the scrollbars — `<Table.StatusBar>`, a toolbar, pagination. Owns the shared CSS variables and\n * the model context; measures nothing itself.\n *\n * Chrome is an ordinary flex child, so the space it takes is subtracted from the scrolling box by\n * the browser rather than reserved by arithmetic here. Nothing has to be told how tall a status bar\n * is, and the vertical scrollbar terminates at it instead of running past.\n *\n * `className`/`style` land here, on the box a border and a `border-radius` belong on;\n * `<Table.Scroll>` takes its own for styling the scrolling area.\n *\n * **The table's shape is three CSS values on this element**, which is why there is no prop for any\n * of them — `style` reaches the right box, and the browser does the rest:\n *\n * | | |\n * | --- | --- |\n * | *(default)* | `height: 100%` — fills a sized parent; a short list leaves dead space below the rows, which is the classic table shape and what a bordered design wants |\n * | `maxHeight: 480` | caps the box, so whatever follows in flow sits directly beneath it |\n * | `height: \"auto\"` | hugs the rows — no dead space, and `<Table.StatusBar>` follows the last row. Pair it with a `minHeight` if the table can be empty, since an empty box hugs to just its header and `<Table.Overlay>` has nowhere to put the message |\n */\nexport const TableRoot: FC<TableRootProps> = observer(\n  ({ table, children, style, className, checkbox }) => (\n    <TableProvider value={table}>\n      <TableSlotsProvider value={{ checkbox: checkbox ?? NativeCheckbox }}>\n        <div\n          className={[\"table-viewport\", className].filter(Boolean).join(\" \")}\n          style={\n            {\n              display: \"flex\",\n              flexDirection: \"column\",\n              width: \"100%\",\n              height: \"100%\",\n              position: \"relative\",\n              \"--table-row-height\": `${table.rowHeight}px`,\n              // Published, not consumed, exactly like the row height above: `<Table.Header>`\n              // applies this same number as its border-box height, so consumer CSS that has to\n              // line up with where the rows start — insetting a custom scrollbar below the header,\n              // say — can read it instead of restating it. What the config says, header rendered\n              // or not; `headerHeight: 0` is how a table without one says so.\n              \"--table-header-height\": `${table.headerHeight}px`,\n              ...style,\n            } as React.CSSProperties\n          }\n        >\n          {children}\n        </div>\n      </TableSlotsProvider>\n    </TableProvider>\n  ),\n);\n","import { useEffect, useRef } from \"react\";\n\n/**\n * Reports a scroll container's offsets on every scroll event.\n *\n * `onScroll` is read through a ref so an inline arrow doesn't re-subscribe on every render — the\n * table's root re-renders as the window shifts, and re-attaching the listener each time would be\n * pure waste.\n */\nexport const useScroll = (\n  ref: React.RefObject<HTMLElement | null>,\n  onScroll: (x: number, y: number) => void,\n): void => {\n  const onScrollRef = useRef(onScroll);\n  onScrollRef.current = onScroll;\n\n  useEffect(() => {\n    const scrollContainer = ref.current;\n    if (!scrollContainer) {\n      return;\n    }\n\n    // No rAF throttle: scroll events already fire once per frame in the rendering step (the same\n    // tick as rAF), so coalescing them is redundant. Reading scrollLeft/Top is cheap and doesn't\n    // force layout, and setScroll does near-zero work within a row (integer-bound windowing), so we\n    // update synchronously — the change lands before paint with no extra scheduling hop or lag.\n    const handleScroll = () =>\n      onScrollRef.current(scrollContainer.scrollLeft, scrollContainer.scrollTop);\n\n    scrollContainer.addEventListener(\"scroll\", handleScroll, { passive: true });\n    return () => scrollContainer.removeEventListener(\"scroll\", handleScroll);\n  }, [ref]);\n};\n","import { reaction } from \"mobx\";\nimport { observer } from \"mobx-react-lite\";\nimport { type ComponentPropsWithRef, type FC, useEffect, useRef } from \"react\";\nimport { useMergedRef } from \"../../react-util/useMergedRef\";\nimport { useResize } from \"../../react-util/useResize\";\nimport { TableScrollportProvider, useTableContext } from \"../table.context\";\nimport { useScroll } from \"../use-scroll\";\n\nexport type TableScrollProps = ComponentPropsWithRef<\"div\">;\n\n/**\n * The scrolling box: the element that actually overflows, and the one whose size the whole\n * virtualization budget is measured from. Header, body and the overlay surfaces go inside it;\n * anything that belongs *outside* the scrollbars — `<Table.StatusBar>`, a toolbar, pagination —\n * goes directly in `<Table.Root>` alongside this.\n *\n * That separation is the point. A bar rendered inside here is inside the box the vertical scrollbar\n * measures, so the scrollbar always runs past it and no amount of consumer styling reaches the\n * overlap. As a sibling, the bar spans the full width — scrollbar gutter included — and the\n * scrollbar terminates at its top edge.\n *\n * **Sizing lives in flex, not in JS.** This is `flex: 1 1 auto; min-height: 0` inside `<Table.Root>`'s\n * column, so the browser resolves all four cases — filling a sized parent, shrinking when the\n * content overflows, hugging the rows when the root is `height: auto`, and absorbing the difference\n * under a `maxHeight` — and `table.height` is then measured off the result rather than computed\n * ahead of it. `min-height: 0` is load-bearing: flex items default to `min-height: auto` and refuse\n * to shrink below their content, which would leave the box uncapped.\n *\n * Takes every div prop, and merges an incoming `ref` with its own, so a scroll-area primitive that\n * needs the scrolling element (Ark UI's `<ScrollArea.Viewport asChild>`, for instance) can compose\n * onto it. Nested that way it is no longer a direct child of the root's flex column, so move the\n * sizing to the wrapper and pass `style={{ flex: \"initial\", height: \"100%\" }}` here.\n */\nexport const TableScroll: FC<TableScrollProps> = observer(\n  ({ children, className, style, ref, ...rest }) => {\n    const table = useTableContext();\n    const scrollRef = useRef<HTMLDivElement>(null);\n    const mergedRef = useMergedRef(scrollRef, ref);\n\n    useScroll(scrollRef, (x, y) => table.setScroll(x, y));\n\n    // Both dimensions come from this element's content box, which excludes the scrollbars: width\n    // excludes the vertical one (or a reserved `scrollbar-gutter` strip) so column widths fill the\n    // visible area with no phantom horizontal scroll, and height excludes the horizontal one so\n    // `table.height` is the row area rather than the row area plus a scrollbar strip.\n    //\n    // Measuring the box that scrolls used to be circular, because JS also set its `max-height`.\n    // Nothing does now — flex resolves the height — so this is a pure read.\n    useResize(scrollRef, (width, height) => {\n      table.setWidth(width);\n      table.setHeight(height);\n    });\n\n    // Execute programmatic scroll intents (scrollToRow/scrollToEnd). The sticky header's flow\n    // height exactly offsets the content's start, so blockOffset values map 1:1 onto scrollTop.\n    useEffect(\n      () =>\n        reaction(\n          () => table.scrollRequest,\n          (request) => {\n            const container = scrollRef.current;\n            if (!request || !container) return;\n            container.scrollTo({\n              top: request.y === \"end\" ? container.scrollHeight : request.y,\n            });\n            table.clearScrollRequest();\n          },\n        ),\n      [table],\n    );\n\n    return (\n      <TableScrollportProvider value={true}>\n        <div\n          {...rest}\n          ref={mergedRef}\n          role=\"table\"\n          // only a window of rows/columns is in the DOM, so assistive tech needs the true\n          // extent (+1 row for the header) and each row/cell carries its absolute index.\n          // For a paged dataset that extent is the server's, not the count fetched so far —\n          // see `ariaRowCount`, which reports -1 when it is genuinely unknown.\n          aria-rowcount={table.ariaRowCount}\n          aria-colcount={table.orderedColumns.length}\n          aria-multiselectable={table.selectable || undefined}\n          className={[\"table-scroll\", className].filter(Boolean).join(\" \")}\n          style={\n            {\n              position: \"relative\",\n              overflow: \"auto\",\n              flex: \"1 1 auto\",\n              minHeight: 0,\n              // scroll-state query container: the documented pattern for pinned-edge\n              // shadows (`@container scroll-state(scrollable: …)`) needs the container\n              // declared here or those consumer rules silently never match\n              containerType: \"scroll-state\",\n              scrollSnapType: \"x proximity\",\n              scrollPaddingLeft: `${table.leftPinnedRenderedColumns.reduce((sum, c) => sum + c.width, 0)}px`,\n              width: \"100%\",\n              // this box's visible content width, vertical scrollbar already excluded — what\n              // everything pinned horizontally is sized against, including the rounded header\n              // background layer (`.table-header::before`)\n              \"--table-scroll-width\": `${table.width}px`,\n              ...style,\n            } as React.CSSProperties\n          }\n        >\n          {/*\n           * Gated on width alone. Height deliberately isn't a precondition: when the root hugs its\n           * content, this box's height *comes from* what renders here, so requiring it first\n           * deadlocks — nothing renders, nothing measures, forever. Width is safe because it comes\n           * from the parent either way, and height bootstraps on its own since `<Table.Body>`'s\n           * spacer is `virtualHeight`, which depends on the row count rather than on `table.height`.\n           */}\n          {table.width > 0 && children}\n        </div>\n      </TableScrollportProvider>\n    );\n  },\n);\n","import { observer } from \"mobx-react-lite\";\nimport type { FC, HTMLAttributes, ReactNode } from \"react\";\nimport { useOutsideScrollportGuard, useTableContext } from \"../table.context\";\n\nexport interface TableStatusBarProps extends HTMLAttributes<HTMLDivElement> {\n  children?: ReactNode;\n  /**\n   * Height in pixels. Defaults to the table's `rowHeight` — the same fixed-height contract the\n   * rows are under, so nothing has to be measured.\n   */\n  height?: number;\n}\n\n/**\n * A bar across the bottom of the table — \"Showing 1,000 of 2,000\", a Load all button, page\n * controls. Render it as a direct child of `<Table.Root>`, **after** `<Table.Scroll>`:\n *\n * ```tsx\n * <Table.Root table={table}>\n *   <Table.Scroll>…</Table.Scroll>\n *   <Table.StatusBar>\n *     Showing {table.rows.length} of {table.pages?.total ?? table.rows.length}\n *     {table.pages?.hasMore && (\n *       <button onClick={() => void table.pages?.loadAll()}>Load all</button>\n *     )}\n *   </Table.StatusBar>\n * </Table.Root>\n * ```\n *\n * **Outside the scrolling box**, which is the whole design. Inside it the bar sits within the box\n * the scrollbars measure: the vertical scrollbar spans past it no matter what, and the bar stops\n * short of the scrollbar gutter — a divider along its top edge visibly runs out into the middle of\n * a scrollbar. Out here it spans the full width, gutter included, and the scrollbar terminates at\n * its top edge.\n *\n * Being an ordinary flex child of the root is also what makes it free of arithmetic. The space it\n * takes is subtracted from `<Table.Scroll>` by the browser, so `table.height` — and with it the\n * render window, the auto-fetch threshold and `<Table.Overlay>` — is already correct. Nothing has\n * to be told how tall the bar is.\n *\n * It follows the rows on a short list only if the table hugs its content; by default the table\n * fills its parent and the bar sits at the bottom with the dead space above it. Hug with\n * `style={{ height: \"auto\" }}` on `<Table.Root>`.\n *\n * A plain block: no `position`, no `z-index`, no background, because nothing scrolls behind it.\n * Style it through your own `className`, or `[data-table-status-bar]`.\n *\n * **Not a `<tfoot>`.** That is a row: aligned to the columns, scrolling horizontally with them, one\n * cell per column — and therefore something that has to live *inside* the scrollport. This spans\n * the table and knows nothing about columns. The name `Table.Footer` is left free for the row.\n *\n * **Ungated**, like `<Table.Gutter>` and unlike `<Table.Empty>` / `<Table.Loading>` /\n * `<Table.Error>`. Those three describe states the table can evaluate; a row count is the source's\n * business. Note that out here it no longer overlaps the overlay surfaces, so a bar reading\n * \"Showing 0 of 0\" now sits *below* \"Couldn't load\" rather than painting over it. Gate it yourself\n * if showing both at once reads badly:\n *\n * ```tsx\n * {!table.error && !table.loading && <Table.StatusBar>…</Table.StatusBar>}\n * ```\n */\nexport const TableStatusBar: FC<TableStatusBarProps> = observer(\n  ({ children, className, style, height, ...rest }) => {\n    const table = useTableContext();\n    useOutsideScrollportGuard(\"StatusBar\");\n    return (\n      <div\n        {...rest}\n        role=\"presentation\"\n        data-table-status-bar=\"\"\n        className={className}\n        style={{\n          flex: \"0 0 auto\",\n          width: \"100%\",\n          height: `${height ?? table.rowHeight}px`,\n          display: \"flex\",\n          alignItems: \"center\",\n          ...style,\n        }}\n      >\n        {children}\n      </div>\n    );\n  },\n);\n","import { TableResizer } from \"./column-resizer\";\nimport { SelectAll, SelectionCell, SelectionHeaderCell } from \"./selection\";\nimport { TableBody, TableCell, TableRow } from \"./table-body\";\nimport { TableEmpty } from \"./table-empty\";\nimport { TableError } from \"./table-error\";\nimport { TableGutter } from \"./table-gutter\";\nimport { TableColumnHeader, TableHeader } from \"./table-header\";\nimport { TableLoading } from \"./table-loading\";\nimport { TableOverlay } from \"./table-overlay\";\nimport { TableExpansion } from \"./table-expansion\";\nimport { TableRoot } from \"./table-root\";\nimport { TableScroll } from \"./table-scroll\";\nimport { TableStatusBar } from \"./table-status-bar\";\n\n/**\n * Compound namespace for the table skeleton. Consumers compose these into their own closed\n * component (styles + defaults captured once), e.g.\n * `<Table.Root><Table.Scroll><Table.Header>…`.\n *\n * Two boxes, and which one a part goes in is the whole structure: `Root` is the outer frame,\n * `Scroll` is the box that overflows. Header, body, rows and the overlay surfaces go inside\n * `Scroll`; chrome that must sit outside the scrollbars — `StatusBar`, a toolbar, pagination —\n * goes directly in `Root`.\n */\nexport const Table = {\n  Root: TableRoot,\n  Scroll: TableScroll,\n  Header: TableHeader,\n  ColumnHeader: TableColumnHeader,\n  Body: TableBody,\n  Row: TableRow,\n  Cell: TableCell,\n  Empty: TableEmpty,\n  Loading: TableLoading,\n  Error: TableError,\n  Gutter: TableGutter,\n  StatusBar: TableStatusBar,\n  Overlay: TableOverlay,\n  Expansion: TableExpansion,\n  Resizer: TableResizer,\n  SelectionCell: SelectionCell,\n  SelectionHeaderCell: SelectionHeaderCell,\n  SelectAll: SelectAll,\n};\n","import { action, computed, makeObservable, observable } from \"mobx\";\nimport { textMatches } from \"../filter/util\";\nimport type { TableModel } from \"./table.model\";\nimport type { FilterCondition, FilterMode, RowData } from \"./table.types\";\n\n/**\n * The table's built-in search filter: one query matched across many columns.\n *\n * The second of the table's two kinds of filter, and the reason there are two: matching one query\n * against *many* columns needs every column's accessor at once, which is the one thing a\n * `matches(value)` predicate structurally cannot do. So it holds a row `predicate` where a\n * `ColumnFilter` holds `matches`, and having no column of its own is what the `column` qualifier\n * excludes it from — `activeColumnFilters`, `clearColumnFilters`, `TableState.columnFilters`.\n *\n * It joins `filterPredicate` and `filterQuery` like any column filter, and gets its own\n * `TableState.search` key.\n *\n\n *\n * Which columns it reads is per-column config (`searchable`), including hidden ones — see\n * {@link BaseColumnDef.searchable}. Comparison goes through the same `textMatches` a `TextFilter`\n * uses, so a per-column \"contains\" and the search box agree.\n *\n * Debouncing is deliberately not here. Like `onStateChange`, the cadence belongs to whoever owns\n * the input: a client-side search over rows already in memory usually wants none at all.\n */\nexport class TableSearchFilter {\n  readonly table: TableModel;\n\n  /** The query. Not trimmed — a trailing space is a legitimate part of a \"contains\" query. */\n  text = \"\";\n\n  get active(): boolean {\n    return this.text !== \"\";\n  }\n\n  /** Who does the searching. See {@link TableConfig.search}. */\n  get mode(): FilterMode {\n    // Same fallback as a column's, and read through the table for the same reason: a server-driven\n    // dataset must not be searched over the one page of it that happens to be here.\n    return this.table.config?.search?.mode ?? this.table.filterMode;\n  }\n\n  /**\n   * Row predicate, or `undefined` when nothing is typed (the pass-through convention every filter\n   * source here follows). A row passes when *any* searchable column matches — OR across columns,\n   * unlike the AND across filters.\n   */\n  get predicate(): ((row: RowData) => boolean) | undefined {\n    // server mode: the rows already arrived searched, so matching again here would be applying the\n    // same query twice — and per-column `searchable` says nothing about what the server looked at\n    if (this.text === \"\" || this.mode === \"server\") return undefined;\n    const query = this.text;\n    const columns = this.table.searchableColumns;\n    if (columns.length === 0) return undefined;\n    return (row) => columns.some((column) => textMatches(query, column.searchValue(row)));\n  }\n\n  /**\n   * The query as a `{ op: \"search\" }` condition for {@link TableModel.filterQuery}, or `undefined`\n   * unless the search is server-mode and non-empty. No `field`: it is not tied to one.\n   */\n  get condition(): FilterCondition | undefined {\n    if (this.text === \"\" || this.mode !== \"server\") return undefined;\n    return { op: \"search\", value: this.text };\n  }\n\n  constructor(table: TableModel) {\n    this.table = table;\n\n    makeObservable(this, {\n      text: observable,\n\n      active: computed,\n      mode: computed,\n      predicate: computed,\n      condition: computed,\n\n      setText: action.bound,\n      clear: action.bound,\n    });\n  }\n\n  setText(text: string): void {\n    this.text = text;\n  }\n\n  /**\n   * Clear the query. Note `TableModel.clearColumnFilters()` does *not* call this — wiping text the user\n   * typed as a side effect of \"clear filters\" is more surprising than leaving it.\n   */\n  clear(): void {\n    this.text = \"\";\n  }\n}\n","import type { LazyArray, LazyPages } from \"../lazy/lazy\";\nimport type { RowData, TableQuery } from \"./table.types\";\n\n/**\n * Whether `data` was given as a lazy rather than an array or a getter.\n *\n * Structural rather than an `instanceof`, because `lazyArray` is a factory over a closure\n * and there is no class to test against. Checking two members no plain dataset has is enough, and\n * it keeps this a *type-only* dependency on `lazy` — nothing from that module is\n * imported at runtime, so a consumer who only ever hands the table arrays never bundles it.\n *\n * Not exported from the package: the shapes `data` accepts are documented, so a caller always knows\n * which one they passed, and `table.lazy` answers the question for anyone holding only a model.\n */\nexport const isLazy = (data: unknown): data is LazyArray<RowData> =>\n  typeof data === \"object\" &&\n  data !== null &&\n  !Array.isArray(data) &&\n  \"loaded\" in data &&\n  \"getOrLoad\" in data;\n\n/**\n * Whether `data` is a *paged* lazy — one that grows by appending rather than replacing.\n *\n * Structural for the same reason {@link isLazy} is, and narrower on purpose: `loadMore` is what\n * distinguishes an accumulating source from any other lazy, and it is the member the table actually\n * needs. A discrete-page source (one that replaces its rows per page) has none, so it is correctly\n * *not* one of these and never gets auto-fetched.\n *\n * Not exported from the package; `table.pages` answers the question for a caller holding a model.\n */\nexport const isPaged = (data: unknown): data is LazyPages<RowData, TableQuery> =>\n  isLazy(data) && \"loadMore\" in data && \"setQuery\" in data;\n","import {\n  action,\n  comparer,\n  computed,\n  type IReactionDisposer,\n  makeObservable,\n  observable,\n  reaction,\n} from \"mobx\";\nimport { ColumnModel } from \"./column.model\";\nimport { TableSearchFilter } from \"./search-filter.model\";\nimport type { LazyArray, LazyPages } from \"../lazy/lazy\";\nimport type {\n  ColumnDef,\n  ColumnSort,\n  ColumnsDef,\n  ColumnState,\n  FilterCondition,\n  FilterMode,\n  RowData,\n  RowId,\n  SortDirection,\n  TableConfig,\n  TableQuery,\n  TableState,\n} from \"./table.types\";\nimport { isLazy, isPaged } from \"./is-lazy\";\n\nexport class TableModel {\n  readonly config?: TableConfig<any>;\n\n  rows: RowData[] = [];\n\n  columns = new Map<string, ColumnModel>();\n  // column keys in display order; maintained by syncColumns and rearranged by moveColumn\n  columnOrder: string[] = [];\n\n  // The def list the columns are built from. Seeded from config.columns and replaced by the\n  // What the consumer curated: `config.columns`, replaced by `setColumns`. `undefined` means none\n  // were configured, which is what makes `autoColumns` default on. See effectiveDefs.\n  private configuredDefs: ColumnsDef<any> | undefined;\n\n  // Columns added at runtime by `addColumn`. Kept apart from `configuredDefs` so adding one never\n  // switches auto-generation off.\n  private runtimeDefs: ColumnDef<any>[] = [];\n\n  // Keys `removeColumn` took out. A suppression rather than a def-list edit, so a removal survives\n  // the next re-derivation instead of being undone by it.\n  private suppressedKeys = new Set<string>();\n\n  /**\n   * The built-in cross-column text search. Always present and inert until something is typed, so\n   * there is no config to switch it on. See {@link TableSearchFilter}.\n   */\n  readonly searchFilter: TableSearchFilter = new TableSearchFilter(this);\n\n  scrollX = 0;\n  scrollY = 0;\n\n  height = 0;\n  width = 0;\n\n  // active column sorts in priority order — earlier entries win, later ones break ties\n  // (empty = original row order)\n  sorts: ColumnSort[] = [];\n\n  // Rows selected via the checkbox column, tracked by row id (see rowIds). Row-scoped state is\n  // always stored as ids, never references — `selectedRows` derives the objects back.\n  selectedIds = new Set<RowId>();\n\n  // Rows expanded to show a detail panel, tracked by row id like selection. Ephemeral: reset by\n  // setData, preserved by appendRows, excluded from persisted TableState.\n  expandedIds = new Set<RowId>();\n\n  // A pending programmatic scroll. The model owns the intent; <Table.Root> executes it against\n  // the scroll container and clears it. \"end\" resolves to the bottom of the content at\n  // execution time (the live-tail follow position).\n  scrollRequest: { y: number | \"end\" } | undefined = undefined;\n\n  // The last snapshot given to applyState. Consulted whenever columns (re)sync, so state applied\n  // before the columns exist (applyState before the first setData, factory defs materializing on\n  // first data) still lands. Never re-applied to columns that already exist — later user changes win.\n  private appliedState: Partial<TableState> | undefined;\n\n  private stateReactionDisposer: IReactionDisposer | undefined;\n\n  // Only set for the getter form of `config.rows`; see activate().\n  private rowsReactionDisposer: IReactionDisposer | undefined;\n\n  /**\n   * The live `data` binding: the lazy or getter currently driving the dataset, if it is one of\n   * those. An array is applied outright and leaves nothing to hold.\n   *\n   * Held here rather than read off `config` because it can be replaced — a keyed collection hands\n   * out a *different* lazy per key, so `store.byOrg({ orgId })` is a new lazy whenever `orgId`\n   * changes. See {@link setData}.\n   */\n  private binding:\n    | LazyArray<RowData>\n    | LazyPages<RowData, TableQuery>\n    | (() => RowData[])\n    | undefined;\n\n  /**\n   * The status a caller supplies for a dataset that cannot describe its own — see\n   * {@link UseTableConfig.loading}. Meaningless, and ignored, when {@link TableModel.lazy} is set.\n   */\n  private givenLoading = false;\n  private givenError: unknown = undefined;\n\n  // Re-derives factory columns once data exists; see activate().\n  private columnsReactionDisposer: IReactionDisposer | undefined;\n\n  // Only doing anything while `data` is a paged lazy; see activate().\n  private queryReactionDisposer: IReactionDisposer | undefined;\n  private loadMoreReactionDisposer: IReactionDisposer | undefined;\n  private restartReactionDisposer: IReactionDisposer | undefined;\n\n  get rowHeight(): number {\n    return this.config?.rowHeight ?? 40;\n  }\n\n  /**\n   * The space the header occupies: the configured {@link TableConfig.headerHeight}, or `rowHeight`\n   * when it says nothing.\n   *\n   * `<Table.Header>` applies this as its border-box height rather than reading it back, so this is\n   * the number *and* the rendered geometry. `<Table.Overlay>` starts below it, and `<Table.Root>`\n   * publishes it as `--table-header-height` for consumer CSS that has to line up with where the\n   * rows start.\n   *\n   * Declared rather than derived from what is rendered, so it is right on the first paint and there\n   * is no frame where an overlay is placed against a header height of zero. The consequence is that\n   * a table composed **without** a `<Table.Header>` still reserves this much: set\n   * `headerHeight: 0` for one. Nothing but the three overlay slots and the CSS variable read this,\n   * so getting it wrong misplaces an empty state — it cannot drift a row.\n   */\n  get headerHeight(): number {\n    return this.config?.headerHeight ?? this.rowHeight;\n  }\n\n  get rowOverscan(): number {\n    return this.config?.rowOverscan ?? 3;\n  }\n\n  get expansionHeight(): number {\n    return this.config?.expansionHeight ?? 320;\n  }\n\n  get columnOverscan(): number {\n    return this.config?.columnOverscan ?? 1;\n  }\n\n  /**\n   * Stable ids for rows when no `getRowId` is configured, keyed by the row object itself.\n   *\n   * Weak, so it never holds a row alive, and it needs no knowledge of what a row *is* — a dataset\n   * that hands back the same objects keeps its row-keyed state, and one that rebuilds them drops\n   * it. That covers identity-mapped records without the table knowing anything about models.\n   */\n  private readonly identityIds = new WeakMap<RowData, RowId>();\n  private nextIdentityId = 0;\n\n  private identityId(row: RowData): RowId {\n    let id = this.identityIds.get(row);\n    if (id === undefined) {\n      id = this.nextIdentityId++;\n      this.identityIds.set(row, id);\n    }\n    return id;\n  }\n\n  /**\n   * row → id, from `config.getRowId` when given and from the row's own object identity otherwise.\n   *\n   * The default used to be the row's *index*, which is only safe while the dataset is re-applied\n   * wholesale: a source that replaces its contents in place — which is what a `LazyArray`\n   * does — would leave a selected index pointing at whatever row later occupied that slot.\n   */\n  get rowIds(): Map<RowData, RowId> {\n    const getRowId = this.config?.getRowId;\n    return new Map(\n      this.rows.map((row, i) => [row, getRowId ? getRowId(row, i) : this.identityId(row)]),\n    );\n  }\n\n  get allColumns(): ColumnModel[] {\n    return this.columnOrder.flatMap((key) => {\n      const col = this.columns.get(key);\n      return col ? [col] : [];\n    });\n  }\n\n  // hidden columns are excluded from layout and rendering\n  get orderedColumns(): ColumnModel[] {\n    return this.allColumns.filter((c) => !c.hidden);\n  }\n\n  /**\n   * Resolved pixel width for every column, distributed across the viewport (`width`).\n   * Fixed columns (explicit px or a manual override) claim their width; the rest are flex\n   * (`\"Nfr\"`, default `1fr`) and share the remaining space by weight, clamped to\n   * [minWidth, maxWidth] via a freeze-redistribute pass (a column that hits a clamp is frozen\n   * and its share is re-split among the others). Any leftover slack — every flex column capped\n   * at its max — is absorbed by the last column so the columns always fill the viewport (this\n   * also soaks up sub-pixel rounding). When the minimums don't fit, the total exceeds the\n   * viewport and the table scrolls horizontally.\n   */\n  get columnWidths(): Map<ColumnModel, number> {\n    const cols = this.orderedColumns;\n    const result = new Map<ColumnModel, number>();\n    if (cols.length === 0) return result;\n\n    const flex: ColumnModel[] = [];\n    let fixedTotal = 0;\n    for (const col of cols) {\n      if (col.fixedWidth !== undefined) {\n        result.set(col, col.fixedWidth);\n        fixedTotal += col.fixedWidth;\n      } else {\n        flex.push(col);\n      }\n    }\n\n    const free = this.width - fixedTotal;\n    const frozen = new Set<ColumnModel>();\n\n    while (frozen.size < flex.length) {\n      const active = flex.filter((c) => !frozen.has(c));\n      const frozenTotal = flex.reduce(\n        (sum, c) => sum + (frozen.has(c) ? (result.get(c) ?? 0) : 0),\n        0,\n      );\n      const remaining = free - frozenTotal;\n      const totalGrow = active.reduce((sum, c) => sum + c.grow, 0);\n\n      if (totalGrow <= 0) {\n        for (const c of active) result.set(c, c.minWidth);\n        break;\n      }\n\n      let clamped = false;\n      for (const c of active) {\n        const share = (remaining * c.grow) / totalGrow;\n        if (share < c.minWidth) {\n          result.set(c, c.minWidth);\n          frozen.add(c);\n          clamped = true;\n        } else if (share > c.maxWidth) {\n          result.set(c, c.maxWidth);\n          frozen.add(c);\n          clamped = true;\n        }\n      }\n\n      if (!clamped) {\n        for (const c of active) result.set(c, (remaining * c.grow) / totalGrow);\n        break;\n      }\n    }\n\n    // no-gap: absorb any leftover (underfill) into the last column, even past its max\n    const used = cols.reduce((sum, c) => sum + (result.get(c) ?? 0), 0);\n    const slack = this.width - used;\n    if (slack > 0) {\n      const sink = cols[cols.length - 1]!;\n      result.set(sink, (result.get(sink) ?? 0) + slack);\n    }\n\n    return result;\n  }\n\n  get virtualWidth(): number {\n    return this.orderedColumns.reduce((sum, col) => sum + col.width, 0);\n  }\n\n  get virtualHeight(): number {\n    return (\n      this.clientFilteredRows.length * this.rowHeight +\n      this.expandedDisplayIndices.length * this.expansionHeight\n    );\n  }\n\n  // Display indices of expanded rows, ascending. The expansion geometry below keys off this tiny\n  // array (0–few entries), which is what keeps the block math effectively closed-form.\n  get expandedDisplayIndices(): number[] {\n    if (!this.expandedIds.size) return [];\n    const indices: number[] = [];\n    this.displayRows.forEach((row, i) => {\n      const id = this.rowIds.get(row);\n      if (id !== undefined && this.expandedIds.has(id)) indices.push(i);\n    });\n    return indices;\n  }\n\n  get unpinnedColumns(): ColumnModel[] {\n    return this.orderedColumns.filter((c) => !c.pinned);\n  }\n\n  get firstUnpinnedRenderedIndex(): number {\n    const firstVisibleIndex = this.unpinnedColumns.findIndex((col) => col.offset >= this.scrollX);\n    return Math.max(0, firstVisibleIndex - this.columnOverscan);\n  }\n\n  get lastUnpinnedRenderedIndex(): number {\n    const first = this.firstUnpinnedRenderedIndex;\n    const maxOffset = this.scrollX + this.width;\n    let lastVisibleIndex = this.unpinnedColumns.length - 1;\n    for (let i = first; i < this.unpinnedColumns.length; i++) {\n      if ((this.unpinnedColumns[i]?.offset ?? 0) >= maxOffset) {\n        lastVisibleIndex = i;\n        break;\n      }\n    }\n    return Math.min(this.unpinnedColumns.length - 1, lastVisibleIndex + this.columnOverscan);\n  }\n\n  // Integer-bound like renderedRows, so horizontal scrolling only re-renders on column boundaries.\n  get unpinnedRenderedColumns(): ColumnModel[] {\n    return this.unpinnedColumns.slice(\n      this.firstUnpinnedRenderedIndex,\n      this.lastUnpinnedRenderedIndex + 1,\n    );\n  }\n\n  get leftPinnedRenderedColumns(): ColumnModel[] {\n    return this.orderedColumns.filter((c) => c.pinned === \"left\");\n  }\n\n  get rightPinnedRenderedColumns(): ColumnModel[] {\n    return this.orderedColumns.filter((c) => c.pinned === \"right\").reverse();\n  }\n\n  /**\n   * Everything narrowing the rows client-side, AND-composed into one predicate: every active\n   * client-mode column filter, and the search. `undefined` when nothing is active.\n   *\n   * One predicate rather than several is the point: \"what is hiding my rows\" has a single answer.\n   */\n  get filterPredicate(): ((row: RowData) => boolean) | undefined {\n    return this.composePredicate();\n  }\n\n  /**\n   * The rows this table narrowed itself: `rows` with {@link predicate} applied.\n   *\n   * \"Client\" because that is the only half it applies — a server-mode filter was already applied to\n   * `rows` before they arrived, so running it again here would filter twice. The pipeline reads\n   * `rows` -> `clientFilteredRows` -> `displayRows`, each name saying what that step added.\n   *\n   * Narrowing happens *over* `rows` rather than replacing them, which is what lets selection survive\n   * a filter change and what makes `rows.length` vs this length answer \"no data\" vs \"filtered to\n   * nothing\".\n   */\n  get clientFilteredRows(): RowData[] {\n    const predicate = this.filterPredicate;\n    return predicate ? this.rows.filter(predicate) : this.rows;\n  }\n\n  /** Columns the built-in search reads — hidden ones included, since `searchable` describes data. */\n  get searchableColumns(): ColumnModel[] {\n    return this.allColumns.filter((c) => c.searchable);\n  }\n\n  /**\n   * The columns whose filter is currently narrowing rows — `.length` is the count a filter chip\n   * shows, and the models themselves are what a rail renders removable chips from.\n   *\n   * **Search is not in here.** It holds a row `predicate` rather than a `ColumnFilter`, belongs to\n   * no column, and `clearColumnFilters` does not reset it. The `column` in the name is doing real\n   * work — say what you mean at the call site instead:\n   *\n   * ```ts\n   * table.activeColumnFilters.length + (table.searchFilter.active ? 1 : 0); // everything narrowing\n   * table.activeColumnFilters.some((c) => c.filterMode === \"client\"); // what Clear would reset\n   * ```\n   *\n   * Includes hidden and `filterable: false` columns — a filter with no visible control is exactly\n   * the one a chip needs to disclose.\n   */\n  get activeColumnFilters(): ColumnModel[] {\n    return this.activeColumnFiltersIn();\n  }\n\n  /**\n   * The active column filters this table applies itself — what `clearColumnFilters({ mode:\n   * \"client\" })` would reset, and so what a facet rail's Clear should gate on.\n   */\n  get activeClientColumnFilters(): ColumnModel[] {\n    return this.activeColumnFiltersIn(\"client\");\n  }\n\n  /** The active column filters the server applied — the ones behind `filterQuery`. */\n  get activeServerColumnFilters(): ColumnModel[] {\n    return this.activeColumnFiltersIn(\"server\");\n  }\n\n  // Each side is its own computed rather than a `.filter()` over the combined list, so reading one\n  // never touches the other side's `active` flags — a server toggle can't invalidate a client-count\n  // chip. The `&&` short-circuit is what does it.\n  private activeColumnFiltersIn(mode?: FilterMode): ColumnModel[] {\n    return this.allColumns.filter(\n      (column) => (!mode || column.filterMode === mode) && column.filter?.active === true,\n    );\n  }\n\n  /**\n   * The conditions of every active **server-mode** filter, plus the search when it is server-mode\n   * too. `undefined` when there are none.\n   *\n   * Disjoint from `predicate` by construction — a filter is either evaluated here or serialized\n   * here, never both — so nothing is double-applied and there is nothing to reconcile.\n   *\n   * Plain JSON, so it compares with `comparer.structural`: react to it, map the conditions onto\n   * your endpoint's shape, refetch, and `setData`. Debouncing and cursor invalidation are yours —\n   * the table has no idea what a request costs you.\n   *\n   * ```ts\n   * reaction(\n   *   () => table.filterQuery,\n   *   (query) => void refetch({ where: query?.map(toClause) }),\n   *   { equals: comparer.structural },\n   * );\n   * ```\n   */\n  get filterQuery(): FilterCondition[] | undefined {\n    const conditions: FilterCondition[] = [];\n    for (const column of this.allColumns) {\n      const condition = column.filterCondition;\n      if (condition) conditions.push(condition);\n    }\n    const search = this.searchFilter.condition;\n    if (search) conditions.push(search);\n    return conditions.length > 0 ? conditions : undefined;\n  }\n\n  // Rows in display order (filtered, then sorted by the active columns — first non-zero\n  // comparison in priority order wins). Comparison goes through each column's value accessor\n  // (dot-paths, computed `value` fns) and optional `compare` def — never a raw `row[key]` lookup.\n  // Sort keys with no matching column are skipped.\n  /**\n   * The lazy currently driving the table, or `undefined` if `data` was an array or a getter.\n   *\n   * Exposed for the one case that cannot be served any other way: a component handed a `TableModel`\n   * and nothing else — a generic table wrapper, a toolbar rendered from context — that wants to\n   * know whether this dataset can be refreshed and offer a control for it. `table.lazy?.reload()`\n   * triggers one, `refreshing` says whether a request is running behind rows already on screen, and\n   * `error` alongside `loaded` says how the last one ended.\n   *\n   * `fetching` covers requests the lazy started by *itself* — revalidating on reobservation, a\n   * `reloadEvery` tick — so an indicator reading it is honest about background work. (The warning\n   * on the lazy's own `fetching` is narrower than it looks: reading it doesn't mark the lazy\n   * *observed*, so it can't keep one alive or trigger a load. A mounted table is already observing\n   * its lazy, so reading through here is safe.)\n   */\n  get lazy(): LazyArray<RowData> | undefined {\n    return isLazy(this.binding) ? this.binding : undefined;\n  }\n\n  /**\n   * The paged lazy driving the table, or `undefined` — the narrower counterpart of {@link lazy}.\n   *\n   * Exposed for the same reason `lazy` is: a component handed only a `TableModel` — a generic\n   * wrapper, a footer rendered from context — can ask whether this dataset has more to fetch and\n   * get the real source if it does. `loadingMore`, `hasMore` and `total` are all on it.\n   *\n   * A paged source is also what {@link mode} infers from, and the only shape the table drives by\n   * itself: it pushes {@link query} into it and asks for the next page as the window nears the end.\n   */\n  get pages(): LazyPages<RowData, TableQuery> | undefined {\n    return isPaged(this.binding) ? this.binding : undefined;\n  }\n\n  /**\n   * Who narrows and orders the rows — see {@link TableConfig.mode}. Explicit config wins;\n   * otherwise a paged source means `\"server\"` and anything else means `\"client\"`.\n   *\n   * A getter rather than a constructor-time decision, so `setData` pointing an existing table at a\n   * paged source flips its sorting and its columns' filter modes with it.\n   */\n  get mode(): \"client\" | \"server\" {\n    return this.config?.mode ?? (this.pages ? \"server\" : \"client\");\n  }\n\n  /** Resolved {@link TableConfig.sortMode}: `\"manual\"` under `mode: \"server\"` unless overridden. */\n  get sortMode(): \"auto\" | \"manual\" {\n    return this.config?.sortMode ?? (this.mode === \"server\" ? \"manual\" : \"auto\");\n  }\n\n  /**\n   * What a column's `filterMode` falls back to, and what the built-in search's does. Read through\n   * by `ColumnModel.filterMode` rather than copied into each column, so it tracks `mode`.\n   */\n  get filterMode(): FilterMode {\n    return this.mode === \"server\" ? \"server\" : \"client\";\n  }\n\n  /**\n   * Everything a server needs in order to answer for this table — see {@link TableQuery}.\n   *\n   * **The identity is stable while the contents are**, which is load-bearing rather than an\n   * optimization: it is what lets this be a `useEffect` dependency or a query key without a column\n   * resize, a scroll, or a selection change issuing a request. That takes structural equality\n   * *and* `keepAlive` — mobx applies `equals` only on its cached path, so an unobserved computed\n   * would hand back a new object on every read and defeat the whole point.\n   *\n   * ```tsx\n   * const query = table.query;\n   * useEffect(() => void refetch(query), [query]);\n   * ```\n   */\n  get query(): TableQuery {\n    return {\n      filters: this.filterQuery,\n      // Only when the table isn't sorting for itself. Under `\"auto\"` the rows are already in this\n      // order, so sending them would ask for work that's done — and would churn this object on\n      // every header click for a request that changes nothing.\n      sorts: this.sortMode === \"manual\" ? this.sorts.slice() : [],\n    };\n  }\n\n  /**\n   * What `aria-rowcount` should report: the extent of the dataset, not of what happens to be\n   * loaded, plus one for the header row.\n   *\n   * It matters most for exactly the tables this is about. A virtualized table already tells\n   * assistive tech the true extent because only a window is in the DOM — but a paged one had been\n   * reporting the rows *fetched so far*, so a screen reader announced \"row 30 of 30\" about a\n   * dataset of four thousand, and the number grew under the user as they scrolled.\n   *\n   * `-1` is ARIA's own answer for an unknown total, and a cursor-paginated list genuinely has one:\n   * there is more, and nothing has said how much. A client-side filter puts us in the same\n   * position from the other direction — the server's `total` counts rows this table is hiding — so\n   * it falls back rather than reporting a number it knows is wrong.\n   */\n  get ariaRowCount(): number {\n    const source = this.pages;\n    if (!source) return this.displayRows.length + 1;\n    if (this.filterPredicate === undefined && source.total !== undefined) return source.total + 1;\n    return source.hasMore ? -1 : this.displayRows.length + 1;\n  }\n\n  /** How many rows one viewport holds, at the fixed row height. */\n  get visibleRowCount(): number {\n    return Math.max(1, Math.ceil(this.height / this.rowHeight));\n  }\n\n  /**\n   * How many rows lie below the render window — the distance to the end of the content, in rows.\n   *\n   * This is the load-more trigger, and it is a **magnitude rather than a threshold** on purpose. A\n   * boolean (`nearEnd`) only changes on its edges, so the case that matters most silently stalls:\n   * a page lands, a client-side filter rejects most of it, the window is still near the end, the\n   * boolean never changed, and nothing asks for the next page. A number moves every time rows\n   * arrive, so the same `if` fires again and the list keeps filling until it can't:\n   *\n   * ```tsx\n   * useEffect(() => {\n   *   if (table.rowsToEnd < PAGE_SIZE) void loadMore();\n   * }, [table.rowsToEnd, table.rows.length]);\n   * ```\n   *\n   * The second dependency covers the one gap a display-row count can't: a page whose rows are\n   * *entirely* filtered out doesn't move this at all, and `rows` is the dataset before filtering.\n   * Bind `data` to a paged lazy and none of this is yours — the table drives `loadMore()` itself.\n   *\n   * Measured from the end of the **rendered** window, overscan included, so it is the distance to\n   * the end of what has been committed to the DOM. `0` on an empty table, which reads correctly as\n   * \"nothing below here\".\n   *\n   * Changes at row granularity rather than per scroll frame (the window bounds are integers), so\n   * reading it in a render subscribes that component to roughly one update per row scrolled — the\n   * cadence `<Table.Body>` already re-renders at.\n   */\n  get rowsToEnd(): number {\n    return this.displayRows.length - 1 - this.lastRenderedIndex;\n  }\n\n  /**\n   * Nothing has arrived yet and nothing has gone wrong — the state a first-load treatment belongs\n   * to, and the one where the empty slot would be a lie.\n   *\n   * The `error` term is load-bearing. Without it a failed first load reads as loading forever:\n   * nothing ever arrives to end it, and `isEmpty` stays `false` too, so the table shows a permanent\n   * spinner with no way out. `lazy` removed a property with exactly this bug (its own\n   * `loading`, which mishandled a failed first load), and this is the same fix one module over.\n   *\n   * Deliberately not gated on `fetching`. A source typically defers its first request past the\n   * render that observes it, so there is a beat where nothing has arrived and nothing is in flight\n   * either; gating on `fetching` would call that beat \"not loading\" and flash the empty slot before\n   * the spinner. Absence of a value with no error to explain it is the honest reading.\n   *\n   * Only ever a *first* load. A request running behind rows already on screen is not this and has\n   * no state here at all: the rows stay rendered and fully interactive, because replacing them to\n   * fetch mostly-identical ones would throw away scroll position, column arrangement and selection.\n   * Whoever owns the fetching knows a refresh is running — `refreshing` on a lazy, `isFetching` on\n   * a query — and can say so somewhere that isn't the rows.\n   *\n   * Reported for either form of dataset: a lazy works it out itself, and an array or getter is\n   * described by the `loading` prop passed to `useTable`. A model given a bare array and never told\n   * otherwise has no loading story, and does not invent one.\n   */\n  get loading(): boolean {\n    const lazy = this.lazy;\n    if (lazy) return lazy.value === undefined && lazy.error === undefined;\n    // No lazy: the caller's own account of it, and only meaningful with nothing to show. Rows on\n    // screen mean any request behind them is a refresh, which is not this and has no state here.\n    return this.givenLoading && this.rows.length === 0;\n  }\n\n  /**\n   * The request failed and there is nothing to show for it — the fatal state, and the only one\n   * `<Table.Error>` renders for.\n   *\n   * A failure behind rows that are still on screen is deliberately *not* this. Blanking a working\n   * table over a background request would destroy scroll position, column arrangement and selection\n   * for something the user never asked for, so a failed refresh leaves this `undefined` and the\n   * table carries on showing what it has. Whoever owns the fetching still has that error — on the\n   * lazy as `error`, or in hand as the prop they passed — and can surface it somewhere that isn't\n   * the rows.\n   *\n   * Raw passthrough — whatever the lazy was rejected with, or whatever was handed to `useTable` as\n   * the `error` prop. Unwrapped and uninterpreted either way.\n   */\n  get error(): unknown {\n    const lazy = this.lazy;\n    if (lazy) return lazy.value === undefined ? lazy.error : undefined;\n    return this.rows.length === 0 ? this.givenError : undefined;\n  }\n\n  /**\n   * Supply the status for a dataset that cannot describe its own. Called by `useTable` on every\n   * render whose `loading` or `error` prop changed; pointless for a lazy, which is asked directly.\n   */\n  setStatus(loading: boolean, error: unknown): void {\n    this.givenLoading = loading;\n    this.givenError = error;\n  }\n\n  /**\n   * There is genuinely nothing to show — as opposed to nothing *yet*, or nothing *because the\n   * request failed*. This is the gate the empty slot uses, and the reason a table over a loading\n   * source never claims \"no results\".\n   *\n   * A failed first load is excluded for the same reason a running one is: \"No results\" is a lie\n   * about a request that never came back with any. Fixing `loading` without fixing this would only\n   * trade a permanent spinner for a permanent — and wrong — empty state.\n   */\n  get isEmpty(): boolean {\n    return !this.loading && this.error === undefined && this.displayRows.length === 0;\n  }\n\n  get displayRows(): RowData[] {\n    const rows = this.clientFilteredRows;\n    // manual mode: sorts is reactive state for the consumer to serialize; rows arrive pre-sorted\n    if (this.sortMode === \"manual\") return rows;\n    const active = this.sorts.flatMap(({ key, direction }) => {\n      const col = this.columns.get(key);\n      return col ? [{ col, dir: direction === \"desc\" ? -1 : 1 }] : [];\n    });\n    if (!active.length) return rows;\n    return [...rows].sort((a, b) => {\n      for (const { col, dir } of active) {\n        const result = col.compareRows(a, b) * dir;\n        if (result !== 0) return result;\n      }\n      return 0;\n    });\n  }\n\n  get firstRenderedIndex(): number {\n    return Math.max(0, this.indexAtOffset(this.scrollY) - this.rowOverscan);\n  }\n\n  get lastRenderedIndex(): number {\n    const lastVisibleIndex = this.indexAtOffset(this.scrollY + this.height);\n    return Math.min(this.displayRows.length - 1, lastVisibleIndex + this.rowOverscan);\n  }\n\n  // Windowed rows. Depends only on the integer slice bounds (which change once per crossed row\n  // boundary), never on raw scrollY — so scrolling within a row does not invalidate this computed,\n  // and the body re-renders per row boundary instead of on every scroll frame.\n  get renderedRows(): RowData[] {\n    return this.displayRows.slice(this.firstRenderedIndex, this.lastRenderedIndex + 1);\n  }\n\n  get virtualOffsetX(): number {\n    return this.unpinnedRenderedColumns.at(0)?.offset ?? 0;\n  }\n\n  get virtualOffsetY(): number {\n    return this.blockOffset(this.firstRenderedIndex);\n  }\n\n  get renderedColumns(): ColumnModel[] {\n    return [\n      ...this.leftPinnedRenderedColumns,\n      ...this.unpinnedRenderedColumns,\n      ...this.rightPinnedRenderedColumns,\n    ];\n  }\n\n  // Visible columns in visual (left-to-right) order — pinned blocks at the edges regardless of\n  // their position in columnOrder. Backs each column's ariaColIndex.\n  get visualColumns(): ColumnModel[] {\n    return [\n      ...this.leftPinnedRenderedColumns,\n      ...this.unpinnedColumns,\n      ...this.rightPinnedRenderedColumns.slice().reverse(),\n    ];\n  }\n\n  // row → display index (post filter/sort); backs each row's aria-rowindex\n  get displayRowIndexMap(): Map<RowData, number> {\n    return new Map(this.displayRows.map((row, i) => [row, i]));\n  }\n\n  /** Whether the table has a selection column (drives aria-multiselectable / aria-selected). */\n  get selectable(): boolean {\n    return this.allColumns.some((c) => c.selection);\n  }\n\n  get gridTemplateColumns(): string {\n    const cols: string[] = [];\n    cols.push(...this.leftPinnedRenderedColumns.map((c) => `${c.width}px`));\n    cols.push(`${this.virtualOffsetX}px`);\n    cols.push(...this.unpinnedRenderedColumns.map((c) => `${c.width}px`));\n    cols.push(...this.rightPinnedRenderedColumns.map((c) => `${c.width}px`));\n    return cols.join(\" \");\n  }\n\n  /** Whether the viewport is scrolled to (within one row of) the end of the content. */\n  get atEnd(): boolean {\n    return this.scrollY + this.height >= this.virtualHeight - this.rowHeight;\n  }\n\n  /** The selected row objects, in source order. Derived from `selectedIds`, so ids without a\n   * matching row (possible only if a consumer mutates `selectedIds` directly) drop out. */\n  get selectedRows(): RowData[] {\n    const selected: RowData[] = [];\n    for (const [row, id] of this.rowIds) {\n      if (this.selectedIds.has(id)) selected.push(row);\n    }\n    return selected;\n  }\n\n  /**\n   * The selected rows the user can currently see — selection intersected with the filter. The\n   * counterpart to `selectedRows`, which spans the whole dataset: selection is keyed to a row\n   * *existing*, not to it being visible, so filtering something out does not deselect it.\n   *\n   * Use this for a bulk action that should only touch what is on screen, and `selectedRows` for one\n   * that should touch everything the user has picked.\n   */\n  get visibleSelectedRows(): RowData[] {\n    const ids = this.rowIds;\n    return this.clientFilteredRows.filter((row) => {\n      const id = ids.get(row);\n      return id !== undefined && this.selectedIds.has(id);\n    });\n  }\n\n  /**\n   * Whether every *visible* row is selected — the header checkbox's state. Derived from\n   * `visibleSelectedRows` rather than `selectedRows`, so a selection hidden by the filter can't\n   * report the header as fully checked when nothing on screen is selected.\n   */\n  get allRowsSelected(): boolean {\n    return (\n      this.clientFilteredRows.length > 0 &&\n      this.visibleSelectedRows.length >= this.clientFilteredRows.length\n    );\n  }\n\n  get someRowsSelected(): boolean {\n    return this.visibleSelectedRows.length > 0 && !this.allRowsSelected;\n  }\n\n  constructor(config?: TableConfig<any>) {\n    this.config = config;\n    this.configuredDefs = config?.columns;\n    // Set before `makeObservable`, alongside the other plain-field seeding: afterwards `binding` is\n    // an observable ref, and a bare write to one from the constructor trips `enforceActions: 'always'`\n    // on every table built against a getter or lazy. The array form is applied further down instead,\n    // through `applyRows`, which is an action and so needs no such care.\n    if (config?.data && !Array.isArray(config.data)) {\n      this.binding = config.data;\n    }\n\n    makeObservable<\n      this,\n      | \"syncColumns\"\n      | \"applyRows\"\n      | \"configuredDefs\"\n      | \"runtimeDefs\"\n      | \"suppressedKeys\"\n      | \"binding\"\n      | \"givenLoading\"\n      | \"givenError\"\n      | \"identityIds\"\n      | \"nextIdentityId\"\n      | \"identityId\"\n      | \"applyFilterState\"\n      | \"activeColumnFiltersIn\"\n    >(this, {\n      rows: observable.ref,\n      columns: observable,\n      columnOrder: observable.ref,\n      configuredDefs: observable.ref,\n      runtimeDefs: observable.ref,\n      suppressedKeys: observable.ref,\n      // Its own observable object, held by reference — mobx must not convert it.\n      searchFilter: false,\n      scrollX: observable,\n      scrollY: observable,\n      height: observable,\n      width: observable,\n      sorts: observable.ref,\n      selectedIds: observable.shallow,\n      expandedIds: observable.shallow,\n      scrollRequest: observable.ref,\n\n      headerHeight: computed,\n      rowIds: computed,\n      visibleSelectedRows: computed,\n      allColumns: computed,\n      orderedColumns: computed,\n      columnWidths: computed,\n      virtualWidth: computed,\n      virtualHeight: computed,\n      expandedDisplayIndices: computed,\n      unpinnedColumns: computed,\n      firstUnpinnedRenderedIndex: computed,\n      lastUnpinnedRenderedIndex: computed,\n      unpinnedRenderedColumns: computed,\n      leftPinnedRenderedColumns: computed,\n      rightPinnedRenderedColumns: computed,\n      // Someone else's object, so `ref` rather than `observable` — mobx must not convert what it\n      // holds, only track which one is held. Tracking that much matters: `lazy`, and `loading` and\n      // `error` through it, all read whichever binding is current, and a keyed collection replaces\n      // it (`store.byOrg({ orgId })` is a different lazy per key). Left untracked, swapping to a\n      // lazy that has not loaded yet left every one of those computeds holding the previous one's\n      // answer until something else happened to invalidate them.\n      binding: observable.ref,\n      givenLoading: observable,\n      givenError: observable.ref,\n      setStatus: action.bound,\n\n      // Memoization behind `rowIds`, not state: a WeakMap keyed by row and a counter. Neither is\n      // observable, and `identityId` only ever fills a gap in the map.\n      identityIds: false,\n      nextIdentityId: false,\n      identityId: false,\n\n      filterPredicate: computed,\n      clientFilteredRows: computed,\n      searchableColumns: computed,\n      activeColumnFilters: computed,\n      activeClientColumnFilters: computed,\n      activeServerColumnFilters: computed,\n      activeColumnFiltersIn: false,\n      filterQuery: computed,\n      lazy: computed,\n      pages: computed,\n      mode: computed,\n      sortMode: computed,\n      filterMode: computed,\n      // Structural *and* kept alive, which together are what make the identity stable — see the\n      // getter. `computed.struct` alone is not enough: mobx only applies `equals` on the cached\n      // path, and an unobserved computed recomputes on every read and assigns the result\n      // unconditionally. So a consumer reading `table.query` outside a reactive context — in an\n      // effect, in a query key, in a plain callback — would get a fresh object every time and\n      // refetch on every render, which is precisely the case this exists to serve.\n      query: computed({ equals: comparer.structural, keepAlive: true }),\n      ariaRowCount: computed,\n      visibleRowCount: computed,\n      rowsToEnd: computed,\n      loading: computed,\n      error: computed,\n      isEmpty: computed,\n      displayRows: computed,\n      firstRenderedIndex: computed,\n      lastRenderedIndex: computed,\n      renderedRows: computed,\n      virtualOffsetX: computed,\n      virtualOffsetY: computed,\n      renderedColumns: computed,\n      visualColumns: computed,\n      displayRowIndexMap: computed,\n      selectable: computed,\n      gridTemplateColumns: computed,\n      atEnd: computed,\n      selectedRows: computed,\n      allRowsSelected: computed,\n      someRowsSelected: computed,\n\n      applyState: action.bound,\n      applyFilterState: action,\n      clearColumnFilters: action.bound,\n      syncColumns: action,\n      setColumns: action.bound,\n      addColumn: action.bound,\n      removeColumn: action.bound,\n      moveColumn: action.bound,\n      applyRows: action,\n      setData: action.bound,\n      appendRows: action.bound,\n      setScroll: action.bound,\n      scrollToRow: action.bound,\n      scrollToTop: action.bound,\n      scrollToEnd: action.bound,\n      clearScrollRequest: action.bound,\n      setWidth: action.bound,\n      setHeight: action.bound,\n      setSort: action.bound,\n      setSorts: action.bound,\n      clearSort: action.bound,\n      toggleRow: action.bound,\n      selectAllRows: action.bound,\n      clearSelection: action.bound,\n      toggleRowExpanded: action.bound,\n      collapseAllRows: action.bound,\n      toggleAllRows: action.bound,\n    });\n\n    // Configured columns don't depend on data, so build them now rather than waiting for rows.\n    //\n    // Without this there is a window where `columns` is empty: the rows reaction fires immediately\n    // but its handler skips an `undefined` value (nothing has arrived yet is not an empty dataset),\n    // and the columns reaction isn't immediate — so a lazy that starts empty leaves\n    // `column()`, `activeColumnFilters` and `filterQuery` blank until the first response lands.\n    // That inverts the dependency for a page that *fetches from* `filterQuery`: its first request,\n    // the one the user actually waits on, would go out with no conditions at all.\n    //\n    // Data-dependent columns are unaffected — `syncColumns` reads `rows.at(0)` only to resolve\n    // factory defs, and `autoColumns` goes on waiting for a row exactly as before.\n    if (this.configuredDefs) {\n      this.syncColumns();\n    }\n    // the getter and lazy forms were bound above, and are applied by their reaction in activate(), below\n    if (Array.isArray(config?.data)) {\n      this.applyRows(config.data);\n    }\n    // registered after initial config so construction itself never fires; structural equality\n    // suppresses echoes from unrelated observable churn\n    this.activate();\n  }\n\n  /**\n   * (Re)start the model's reactions. Pairs with `dispose` — `useTable` calls both across\n   * effect cycles, so a StrictMode dev remount (mount → cleanup → mount against the same model)\n   * re-arms them instead of leaving the surviving model deaf. No-op for a reaction already\n   * running, or one the config gives nothing to do.\n   */\n  activate(): void {\n    // A getter `rows` is tracked here rather than read once in the constructor: the model follows\n    // whatever observables the getter touches. Ordered before the state reaction so the columns\n    // this first materializes are part of that reaction's baseline rather than a change to report.\n    // One reaction for the model's lifetime, reading *through* the binding rather than being built\n    // against a fixed one. `binding` is an observable ref, so replacing it re-runs this, which\n    // re-tracks against the new binding and drops the old dependency — no disarm/re-arm dance, and\n    // no window where a replaced lazy can still write its rows back over the new dataset.\n    //\n    // A lazy is tracked by the *identity* of its `value`, not a copy of its contents. That is what\n    // lets a `LazyArray` — which keeps one array for its lifetime and replaces the\n    // contents on each load — be applied exactly once: later loads reach the table's computeds\n    // through MobX directly, with no re-application and no copy of every row.\n    //\n    // A lazy whose `value` is a fresh array each load still works: its identity changes, so the\n    // reaction fires and the dataset is re-applied, which is the correct behaviour there.\n    if (!this.rowsReactionDisposer) {\n      this.rowsReactionDisposer = reaction(\n        () => {\n          const binding = this.binding;\n          if (typeof binding === \"function\") return binding();\n          return isLazy(binding) ? binding.value : undefined;\n        },\n        // `undefined` means nothing has arrived — or that the dataset is a plain array, which is\n        // applied outright and has no binding to read. Either way it is not an empty dataset, so\n        // leave the rows alone and let `loading` describe the state.\n        (next) => {\n          if (next) this.applyRows(next);\n        },\n        { fireImmediately: true },\n      );\n    }\n\n    // Factory column defs read the first row, which may not exist at construction — rows arriving\n    // from a lazy load, or a live observable array whose contents fill in later. Re-syncing when the\n    // first row changes materializes those columns as soon as there is data to derive them from.\n    // `syncColumns` keeps columns that already exist, so this never disturbs user changes.\n    if (!this.columnsReactionDisposer) {\n      this.columnsReactionDisposer = reaction(\n        () => this.rows.at(0),\n        () => this.syncColumns(),\n      );\n    }\n\n    // The three reactions below are what \"the table drives a paged source itself\" is. Each reads\n    // *through* `this.pages` rather than closing over a source, exactly as the rows reaction reads\n    // through `binding` — so `setData` pointing at a different paged lazy re-targets them, and\n    // pointing at an array leaves them inert rather than needing to be torn down.\n\n    // The table owns the query — filters live on columns, sorts on the model, search on the search\n    // filter — so the source is downstream of it and this is a push, not a subscription. Immediate,\n    // because the first page must go out with the filters a restored snapshot already applied\n    // rather than fetching twice.\n    if (!this.queryReactionDisposer) {\n      this.queryReactionDisposer = reaction(\n        () => this.query,\n        (query) => {\n          // `setQuery` ignores a structurally equal query, so re-targeting at a source that happens\n          // to carry the same one costs nothing, and a table with no paged source does nothing at\n          // all here.\n          this.pages?.setQuery(query);\n        },\n        { fireImmediately: true },\n      );\n    }\n\n    // Fetch the next page while fewer than a viewport's worth of rows remain below the window.\n    //\n    // `pages` is in the tracked expression alongside the distance, and it is the whole reason this\n    // keeps working in the case a boolean gets wrong: a page whose rows are all rejected by a\n    // client-side filter doesn't move `rowsToEnd` at all, so without a second term the chain would\n    // stall one page in. With it, every landed page re-asks the question and the list fills until\n    // it reaches the window or runs out.\n    //\n    // No guard against re-entry is needed: `loadMore()` resolves immediately when there is nothing\n    // more and joins a request already in flight rather than starting a second.\n    if (!this.loadMoreReactionDisposer) {\n      this.loadMoreReactionDisposer = reaction(\n        () => {\n          const source = this.pages;\n          if (!source?.hasMore) return undefined;\n          // Not while the last request failed. Retrying on the table's own initiative would mean a\n          // request per row scrolled against an endpoint that is already answering with errors —\n          // and the user gets no say, because nothing they can see is asking. Recovery is an\n          // explicit `loadMore()` from a footer retry, which clears the error and lets this resume.\n          if (source.error !== undefined) return undefined;\n          // Not until the viewport has been measured. With `height` still 0 there is no answer to\n          // \"how many rows fit\", so every threshold is a guess — and the guess over-fetches,\n          // because an unmeasured window looks like it reaches the end of the content. Mirrors\n          // `<Table.Root>`, which renders nothing until it has a non-zero size.\n          if (!this.height) return undefined;\n          return { distance: this.rowsToEnd, pages: source.pages };\n        },\n        (probe) => {\n          if (!probe || probe.distance >= this.visibleRowCount) return;\n          // Caught, not `void`ed. The table asked for this page on its own initiative, so there is\n          // no caller to reject at — the same reason a lazy's observation-triggered load reports\n          // through `error` rather than throwing. The source records the failure on itself\n          // (`pages.error`), which is where a footer reads it; leaving the promise unhandled would\n          // turn a failed page into an unhandled rejection and, under a strict test runner or a\n          // window-level handler, into a crash.\n          this.pages?.loadMore().catch(() => {});\n        },\n        { equals: comparer.structural, fireImmediately: true },\n      );\n    }\n\n    // A restart is not a growth, and the scroll position belongs to the run that produced the rows\n    // it was measured against: a filter change that drops fifty pages to one leaves the user\n    // parked past the end, which reads as an empty table and — worse — as \"near the end\", so the\n    // fetch-ahead above would immediately refill everything they just filtered away.\n    //\n    // `pages` returning to 0 is the source's own restart signal; the array identity is stable by\n    // design and so cannot say.\n    if (!this.restartReactionDisposer) {\n      this.restartReactionDisposer = reaction(\n        () => this.pages?.pages === 0,\n        (restarted) => {\n          if (restarted && this.scrollY > 0) this.scrollToTop();\n        },\n      );\n    }\n\n    const onStateChange = this.config?.onStateChange;\n    if (onStateChange && !this.stateReactionDisposer) {\n      this.stateReactionDisposer = reaction(() => this.getState(), onStateChange, {\n        equals: comparer.structural,\n      });\n    }\n  }\n\n  /**\n   * Point the table at a different dataset — a new array, getter or lazy.\n   *\n   * The one setter, because the three shapes differ only in who decides the rows changed. An array\n   * is applied outright; a getter or a lazy becomes the binding a reaction reads through, which is\n   * what makes a keyed collection work: `store.byOrg({ orgId })` hands back a different lazy per\n   * key, and the table has to follow it rather than keep reading the one it was built with.\n   *\n   * Row-keyed state is not cleared: rows are intersected, so with `getRowId` configured a row\n   * present in both datasets keeps its selection and expansion.\n   */\n  setData(\n    data: RowData[] | (() => RowData[]) | LazyArray<RowData> | LazyPages<RowData, TableQuery>,\n  ): void {\n    // Nothing to arm or disarm: the rows reaction reads through `binding`, so assigning it is the\n    // whole operation. An array clears the binding — there is nothing to read through — and is\n    // applied outright.\n    if (Array.isArray(data)) {\n      this.binding = undefined;\n      this.applyRows(data);\n      return;\n    }\n    if (data === this.binding) return;\n    this.binding = data;\n  }\n\n  /** Drop the model's reactions. Pairs with `activate`. */\n  dispose(): void {\n    this.queryReactionDisposer?.();\n    this.queryReactionDisposer = undefined;\n    this.loadMoreReactionDisposer?.();\n    this.loadMoreReactionDisposer = undefined;\n    this.restartReactionDisposer?.();\n    this.restartReactionDisposer = undefined;\n    this.stateReactionDisposer?.();\n    this.stateReactionDisposer = undefined;\n    this.rowsReactionDisposer?.();\n    this.rowsReactionDisposer = undefined;\n    this.columnsReactionDisposer?.();\n    this.columnsReactionDisposer = undefined;\n  }\n\n  rowId(row: RowData): RowId | undefined {\n    return this.rowIds.get(row);\n  }\n\n  /** Snapshot of the user-curated arrangement (see `TableState`). JSON-serializable. */\n  getState(): TableState {\n    const columns: Record<string, ColumnState> = {};\n    for (const col of this.allColumns) {\n      const entry: ColumnState = { hidden: col.hidden, pinned: col.pinned };\n      if (col.manualWidth !== undefined) entry.width = col.manualWidth;\n      columns[col.key] = entry;\n    }\n    const state: TableState = {\n      columnOrder: this.columnOrder.slice(),\n      columns,\n      sorts: this.sorts.map((s) => ({ ...s })),\n    };\n\n    // Only *active* filters get an entry, so the map stays small — but it is always present, even\n    // empty, exactly as `columns` and `sorts` are. That is what lets restoring a view saved with\n    // nothing filtered actually clear filters applied since; omit it and a snapshot could only ever\n    // add them.\n    const columnFilters: Record<string, unknown> = {};\n    for (const col of this.allColumns) {\n      const filter = col.filter;\n      if (filter?.active && filter.value !== undefined && filter.setValue) {\n        columnFilters[col.key] = filter.value;\n      }\n    }\n    state.columnFilters = columnFilters;\n    state.search = this.searchFilter.text;\n\n    return state;\n  }\n\n  /**\n   * Restore a (possibly partial) snapshot. Keys with no matching column are kept aside and land\n   * when a matching column appears (see `appliedState`); columns the snapshot doesn't mention are\n   * left as they are, ordered after the snapshot's columns.\n   */\n  applyState(state: Partial<TableState>): void {\n    this.appliedState = { ...this.appliedState, ...state };\n    if (state.columns) {\n      for (const col of this.columns.values()) this.applyColumnState(col);\n    }\n    if (state.columnOrder) {\n      this.columnOrder = this.mergedOrder(state.columnOrder);\n    }\n    // stale sort keys are harmless — displayRows skips sorts with no matching column\n    if (state.sorts) {\n      this.sorts = state.sorts.map((s) => ({ ...s }));\n    }\n    // Present means complete: a filter the map doesn't mention is cleared, which is what makes\n    // getState -> applyState exact. Keys with no column yet land when one appears, via\n    // applyColumnState — same as column state applied before the first setData.\n    if (state.columnFilters) {\n      for (const col of this.columns.values()) this.applyFilterState(col);\n    }\n    if (state.search !== undefined) {\n      this.searchFilter.setText(state.search);\n    }\n  }\n\n  // Restore (or clear) one column's filter from the last applied snapshot. Split out from\n  // applyColumnState because it also runs for columns that already exist, where column state\n  // deliberately does not — a later user change to hidden/pinned/width outranks the snapshot,\n  // whereas re-applying a filter snapshot is the whole point of applying one.\n  private applyFilterState(col: ColumnModel): void {\n    const columnFilters = this.appliedState?.columnFilters;\n    if (!columnFilters) return;\n    const filter = col.filter;\n    if (!filter?.setValue) return;\n    const value = columnFilters[col.key];\n    if (value === undefined) filter.clear();\n    else filter.setValue(value);\n  }\n\n  /** The column under this key, if it exists. */\n  column(key: string): ColumnModel | undefined {\n    return this.columns.get(key);\n  }\n\n  /**\n   * `predicate` without the named column's own filter — what that column's facet counts are tallied\n   * over, so each option answers \"how many rows would this add\".\n   *\n   * Everything else stays in, including the search and page-level sources: a row those already\n   * exclude must not be counted, or the tally promises rows the selection could never surface.\n   */\n  filterPredicateExcluding(key: string): ((row: RowData) => boolean) | undefined {\n    return this.composePredicate(key);\n  }\n\n  /**\n   * Reset every column filter, or only those on one side of the client/server split\n   * (`clearColumnFilters({ mode: \"client\" })`).\n   *\n   * Leaves the search filter alone — it is the other kind of filter, and the `column` in this name\n   * says so. Wiping text the user typed as a side effect would be surprising anyway; clear it\n   * explicitly with `searchFilter.clear()`.\n   */\n  clearColumnFilters(opts?: { mode?: FilterMode }): void {\n    const mode = opts?.mode;\n    for (const column of this.allColumns) {\n      if (mode && column.filterMode !== mode) continue;\n      column.filter?.clear();\n    }\n  }\n\n  // Reading each filter's `active` here tracks it; the returned closure reads the filter's own\n  // state when it is invoked inside `clientFilteredRows`, which is tracked there. So a change that leaves\n  // `active` alone — swapping one selected value for another — still invalidates the rows.\n  private composePredicate(excludeKey?: string): ((row: RowData) => boolean) | undefined {\n    const parts: ((row: RowData) => boolean)[] = [];\n\n    for (const column of this.allColumns) {\n      if (column.key === excludeKey) continue;\n      // a server-mode filter is already applied to `rows`; running it again would filter twice, and\n      // for facets it means the cross-filter predicate excludes them for free\n      if (column.filterMode === \"server\") continue;\n      const filter = column.filter;\n      if (filter?.active) parts.push((row) => filter.matches(column.getValue(row)));\n    }\n\n    const search = this.searchFilter.predicate;\n    if (search) parts.push(search);\n\n    if (parts.length === 0) return undefined;\n    return (row) => parts.every((part) => part(row));\n  }\n\n  /**\n   * Replace the column definitions. Takes over from `config.columns` — and from the\n   * derive-from-the-first-row default, so a table that was deriving its columns stops doing so.\n   *\n   * Columns that survive the change keep everything the user did to them: display position,\n   * visibility, pinning and manual width. Columns no longer defined are dropped; their entries in\n   * the sort list are left in place but inert (as for any column that disappears), so restoring\n   * the column restores its sort.\n   */\n  setColumns(defs: ColumnsDef<any>): void {\n    this.assertUniqueKeys(defs);\n    // a full reset of what the user curated: runtime additions and removals go with it\n    this.configuredDefs = defs.slice();\n    this.runtimeDefs = [];\n    this.suppressedKeys = new Set();\n    this.syncColumns();\n  }\n\n  /**\n   * Add one column definition — the runtime counterpart to a `config.columns` entry, for\n   * user-curated columns (a picker adding a metric that isn't on the row objects; see\n   * `ComputedColumnDef.value`, which can read any observable, not just the row).\n   *\n   * `index` is the position in the display order; omitted, the column lands last. The column is\n   * always shown, even if a persisted snapshot had it hidden — adding a column means showing it.\n   *\n   * Throws when the key is already taken. Column pickers should offer only what isn't added yet\n   * (`table.columns.has(key)`), so a collision here is a bug rather than a user action.\n   */\n  addColumn(def: ColumnDef<any>, index?: number): void {\n    const key = ColumnModel.keyOf(def);\n\n    if (this.suppressedKeys.has(key)) {\n      // adding back something `removeColumn` took out: lift the suppression, and only carry the def\n      // if nothing already provides one — a configured or auto column comes back on its own\n      const next = new Set(this.suppressedKeys);\n      next.delete(key);\n      this.suppressedKeys = next;\n      if (!this.configuredKeys().has(key)) this.runtimeDefs = [...this.runtimeDefs, def];\n    } else {\n      // a column already under that key — configured, added, or auto — would silently replace it\n      if (this.columns.has(key)) {\n        throw new Error(`Duplicate table column key \"${key}\" — column keys must be unique.`);\n      }\n      this.runtimeDefs = [...this.runtimeDefs, def];\n    }\n\n    this.syncColumns();\n\n    // adding a column means showing it — unless its def says otherwise, which is how a data-only\n    // column survives being added at runtime\n    const added = this.columns.get(key);\n    if (added && added.config.hidden !== true) added.setHidden(false);\n    if (index !== undefined) this.moveColumn(key, index);\n  }\n\n  /**\n   * Remove the column with this key. A no-op when no def matches — including for columns produced\n   * by a factory def, whose keys aren't known until they're built: hide those\n   * (`ColumnModel.setHidden`) or replace the list with `setColumns`.\n   *\n   * Removal drops the column's live state but not any *persisted* state for it, so a later\n   * `addColumn` with the same key restores the pinning and width the user had given it.\n   */\n  removeColumn(key: string): void {\n    if (!this.columns.has(key)) return;\n    this.suppressedKeys = new Set(this.suppressedKeys).add(key);\n    this.runtimeDefs = this.runtimeDefs.filter((def) => ColumnModel.keyOf(def) !== key);\n    this.syncColumns();\n  }\n\n  /** Move a column to a new index in the display order. */\n  moveColumn(key: string, toIndex: number): void {\n    const from = this.columnOrder.indexOf(key);\n    if (from < 0) return;\n    const order = this.columnOrder.slice();\n    order.splice(from, 1);\n    order.splice(Math.max(0, Math.min(order.length, toIndex)), 0, key);\n    this.columnOrder = order;\n  }\n\n  /**\n   * Replace the dataset. Row-keyed state (selection, expansion) is **intersected** against the\n   * incoming rows: an id that still resolves to a row survives, and one that does not is dropped.\n   * A refresh — a refetch, a poll, an invalidation — therefore arrives without clearing the user's\n   * selection, while genuinely switching datasets drops it naturally.\n   *\n   * What \"still resolves\" means depends on where the ids come from:\n   *\n   * - **With `getRowId`** they are derived from the data, so the same record survives even when it\n   *   arrives as a different object. That is what a plain-JSON refetch needs.\n   * - **Without it** they follow the row's object identity, so state survives for a dataset that\n   *   hands back the same objects — anything identity-mapped — and is dropped for one that rebuilds\n   *   them, which is the honest answer there.\n   *\n   * Use `appendRows` to add without resetting. Re-passing the array already in place is a no-op:\n   * same array, same dataset. (`rows` is an `observable.ref`, so mutating one in place is invisible\n   * either way — hand over a new array to change the data.)\n   */\n  private applyRows(rows: RowData[]): void {\n    if (rows === this.rows) return;\n    this.rows = rows;\n    this.syncColumns();\n\n    const live = new Set(this.rowIds.values());\n    for (const id of this.selectedIds) if (!live.has(id)) this.selectedIds.delete(id);\n    for (const id of this.expandedIds) if (!live.has(id)) this.expandedIds.delete(id);\n  }\n\n  /** Append rows without resetting row-keyed state — the \"load more\" path. Existing rows keep\n   * their ids either way, so selection survives. */\n  appendRows(rows: RowData[]): void {\n    this.rows = [...this.rows, ...rows];\n    this.syncColumns();\n  }\n\n  setScroll(x: number, y: number): void {\n    this.scrollX = x;\n    this.scrollY = y;\n  }\n\n  /** Content offset of a display index's block top (row plus any expansion panels above it). */\n  blockOffset(index: number): number {\n    return index * this.rowHeight + this.expandedAbove(index) * this.expansionHeight;\n  }\n\n  /** Scroll so the row's block top lands at the viewport top, or its block end at the bottom. */\n  scrollToRow(row: RowData, align: \"top\" | \"bottom\" = \"top\"): void {\n    const index = this.displayRowIndexMap.get(row);\n    if (index === undefined) return;\n    if (align === \"top\") {\n      this.scrollRequest = { y: this.blockOffset(index) };\n      return;\n    }\n    const id = this.rowIds.get(row);\n    const expanded = id !== undefined && this.expandedIds.has(id);\n    const blockEnd =\n      this.blockOffset(index) + this.rowHeight + (expanded ? this.expansionHeight : 0);\n    this.scrollRequest = { y: Math.max(0, blockEnd - this.height) };\n  }\n\n  /**\n   * Scroll back to the first row.\n   *\n   * Called by the model itself when a paged source restarts — a query change, a reload — because\n   * a scroll offset measured against fifty pages is meaningless against one, and leaves the user\n   * parked past the end of the new results.\n   */\n  scrollToTop(): void {\n    this.scrollRequest = { y: 0 };\n  }\n\n  /** Scroll to the very end of the content. */\n  scrollToEnd(): void {\n    this.scrollRequest = { y: \"end\" };\n  }\n\n  clearScrollRequest(): void {\n    this.scrollRequest = undefined;\n  }\n\n  setWidth(width: number): void {\n    this.width = width;\n  }\n\n  setHeight(height: number): void {\n    this.height = height;\n  }\n\n  /**\n   * Set a column's sort. By default the whole sort list is replaced (single-sort behavior).\n   * With `preserve: true` existing sorts are kept: a column already in the list changes\n   * direction in place (keeping its priority), a new column is appended at the lowest priority.\n   */\n  setSort(key: string, direction: SortDirection, opts?: { preserve?: boolean }): void {\n    if (!opts?.preserve) {\n      this.sorts = [{ key, direction }];\n      return;\n    }\n    const sorts = this.sorts.slice();\n    const existing = sorts.findIndex((s) => s.key === key);\n    if (existing >= 0) sorts[existing] = { key, direction };\n    else sorts.push({ key, direction });\n    this.sorts = sorts;\n  }\n\n  /** Replace the whole sort list at once (restoring a saved view); `setSort` covers per-column interactions. */\n  setSorts(sorts: ColumnSort[]): void {\n    this.sorts = sorts.map((s) => ({ ...s }));\n  }\n\n  /** Remove one column from the sort (later entries move up in priority), or all sorts when no key is given. */\n  clearSort(key?: string): void {\n    this.sorts = key === undefined ? [] : this.sorts.filter((s) => s.key !== key);\n  }\n\n  isRowSelected(row: RowData): boolean {\n    const id = this.rowIds.get(row);\n    return id !== undefined && this.selectedIds.has(id);\n  }\n\n  toggleRow(row: RowData): void {\n    const id = this.rowIds.get(row);\n    if (id === undefined) return;\n    if (this.selectedIds.has(id)) this.selectedIds.delete(id);\n    else this.selectedIds.add(id);\n  }\n\n  selectAllRows(): void {\n    this.selectedIds.clear();\n    for (const row of this.clientFilteredRows) {\n      const id = this.rowIds.get(row);\n      if (id !== undefined) this.selectedIds.add(id);\n    }\n  }\n\n  clearSelection(): void {\n    this.selectedIds.clear();\n  }\n\n  toggleAllRows(): void {\n    if (this.allRowsSelected) this.clearSelection();\n    else this.selectAllRows();\n  }\n\n  isRowExpanded(row: RowData): boolean {\n    const id = this.rowIds.get(row);\n    return id !== undefined && this.expandedIds.has(id);\n  }\n\n  toggleRowExpanded(row: RowData): void {\n    const id = this.rowIds.get(row);\n    if (id === undefined) return;\n    if (this.expandedIds.has(id)) {\n      this.expandedIds.delete(id);\n    } else {\n      if (this.config?.expandMode === \"single\") this.expandedIds.clear();\n      this.expandedIds.add(id);\n    }\n  }\n\n  collapseAllRows(): void {\n    this.expandedIds.clear();\n  }\n\n  /**\n   * The def list to build columns from: the explicit list when there is one, otherwise the first\n   * row's keys. Materializing the fallback here is what lets `addColumn`/`removeColumn` build on\n   * a derived column set instead of replacing it.\n   */\n  /**\n   * The defs a sync builds from: what the consumer curated, then what was added at runtime, then\n   * whatever `autoColumns` makes of the first row's remaining keys — minus anything `removeColumn`\n   * suppressed.\n   */\n  private effectiveDefs(): ColumnsDef<any> {\n    const curated = [...(this.configuredDefs ?? []), ...this.runtimeDefs];\n    const defs = [...curated, ...this.autoDefs(curated)];\n    if (!this.suppressedKeys.size) return defs;\n    return defs.filter(\n      (def) => typeof def === \"function\" || !this.suppressedKeys.has(ColumnModel.keyOf(def)),\n    );\n  }\n\n  /** Defs for first-row keys no curated column covers. Empty unless `autoColumns` is in play. */\n  private autoDefs(curated: ColumnsDef<any>): ColumnDef<any>[] {\n    // on by default only when nothing was configured — which keys off `configuredDefs`, not the\n    // curated list, so `addColumn` cannot turn it off\n    const auto = this.config?.autoColumns ?? this.configuredDefs === undefined;\n    if (!auto) return [];\n\n    const firstRow = this.rows?.at(0);\n    if (!firstRow) return [];\n\n    const covered = new Set(\n      curated.filter((def) => typeof def !== \"function\").map((def) => ColumnModel.keyOf(def)),\n    );\n    const decide = typeof auto === \"function\" ? auto : undefined;\n\n    return Object.keys(firstRow).flatMap((key) => {\n      if (covered.has(key) || this.suppressedKeys.has(key)) return [];\n      if (!decide) return [key];\n      const def = decide(key, (firstRow as Record<string, unknown>)[key], firstRow);\n      if (def === true) return [key];\n      if (!def) return [];\n      return [def];\n    });\n  }\n\n  // Two columns cannot share a key: `columns` is keyed by it, so the second would silently vanish\n  // and take its header and cells with it. Loud at the call site that introduced the collision.\n  /** Keys the consumer configured, ignoring factory defs, which resolve only at sync time. */\n  private configuredKeys(): Set<string> {\n    return new Set(\n      (this.configuredDefs ?? [])\n        .filter((def) => typeof def !== \"function\")\n        .map((def) => ColumnModel.keyOf(def)),\n    );\n  }\n\n  private assertUniqueKeys(defs: ColumnsDef<any>): void {\n    const seen = new Set<string>();\n    for (const def of defs) {\n      // factory defs resolve at sync time, against data this may not have yet\n      if (typeof def === \"function\") continue;\n      const key = ColumnModel.keyOf(def);\n      if (seen.has(key)) {\n        throw new Error(`Duplicate table column key \"${key}\" — column keys must be unique.`);\n      }\n      seen.add(key);\n    }\n  }\n\n  private syncColumns(): void {\n    const firstRow = this.rows?.at(0);\n\n    const columnsDef = this.effectiveDefs();\n\n    // the factory form allows for dynamic columns, which use the first\n    // row of data to construct the column definition(s)\n    const syncedDefs = columnsDef.flatMap((defOrFactory) => {\n      if (typeof defOrFactory === \"function\") {\n        if (!firstRow) return [];\n        return [defOrFactory(firstRow)].flat();\n      }\n      return defOrFactory;\n    });\n\n    // Keys are derived from the defs rather than from built columns: this runs on every setData and\n    // appendRows, and all but the first sync needs a `ColumnModel` only for keys it doesn't already\n    // have. `keyOf` is what `fromDef` itself uses to assign the key, so the two cannot disagree.\n    // Deduped because a repeated key collapses in `columns` below — leaving it twice in the display\n    // order would render that one column twice, under one React key.\n    const keys = [...new Set(syncedDefs.map((def) => ColumnModel.keyOf(def)))];\n    const syncedKeys = new Set(keys);\n\n    // remove any stale\n    for (const key of this.columns.keys()) {\n      if (!syncedKeys.has(key)) {\n        this.columns.delete(key);\n      }\n    }\n\n    // add any new columns; freshly created ones pick up persisted state (applyState may have\n    // run before they existed — e.g. before the first setData)\n    const firstSync = this.columns.size === 0;\n    for (const def of syncedDefs) {\n      const key = ColumnModel.keyOf(def);\n      const existing = this.columns.get(key);\n      if (!existing) {\n        const column = ColumnModel.fromDef(this, def);\n        this.columns.set(key, column);\n        this.applyColumnState(column);\n        continue;\n      }\n\n      // `meta` is the one thing a new def for an existing key is allowed to change, and it is the\n      // *opposite* of how `filter` behaves on purpose. A filter holds the user's live selection, so\n      // re-reading it would throw that away. `meta` is structure the def supplies, and what a\n      // column represents can legitimately change while its key stays the same — a republished\n      // survey rewording a question whose id, and therefore whose column key, is unchanged.\n      //\n      // Shallow rather than by identity: this runs on every `setData` and `appendRows` too, and a\n      // factory def rebuilds its `meta` object each time it is called. Comparing identity would\n      // replace `config` on every appended page and re-render every header cell for no change,\n      // while a shallow compare over a handful of keys costs nothing and is right for the shape\n      // `meta` actually has — a wrapper around values the app already holds.\n      const meta = ColumnModel.metaOf(def);\n      if (!comparer.shallow(existing.config.meta, meta)) existing.setConfig({ meta });\n    }\n\n    const orderOf = (key: string) => this.columns.get(key)?.config.order ?? 0;\n\n    if (firstSync) {\n      // nothing has been rearranged yet, so `order` decides outright. The sort is stable, so columns\n      // sharing an `order` keep the def order — configured columns before auto ones.\n      this.columnOrder = [...keys].sort((a, b) => orderOf(a) - orderOf(b));\n    } else {\n      // Surviving columns stay where they are, including wherever the user dragged them. A column\n      // appearing now is placed by its `order` rather than appended: immediately before the first\n      // column that should come after it.\n      const next = this.columnOrder.filter((k) => keys.includes(k));\n      for (const key of keys) {\n        if (next.includes(key)) continue;\n        const at = next.findIndex((k) => orderOf(k) > orderOf(key));\n        if (at === -1) next.push(key);\n        else next.splice(at, 0, key);\n      }\n      this.columnOrder = next;\n    }\n\n    // A persisted arrangement outranks `order` — it is what the user last did. Applied only on the\n    // sync where the columns first materialize; afterwards later rearrangement beats the snapshot.\n    if (firstSync && this.appliedState?.columnOrder) {\n      this.columnOrder = this.mergedOrder(this.appliedState.columnOrder);\n    }\n  }\n\n  private applyColumnState(col: ColumnModel): void {\n    // A snapshot may have been applied before this column existed — before the first setData, or\n    // before a factory def had a row to build from — so both halves are re-consulted here.\n    this.applyFilterState(col);\n    const state = this.appliedState?.columns?.[col.key];\n    if (!state) return;\n    // Structure outranks a saved view: a column that declares its visibility, pin or width locked\n    // is not moved by a snapshot written before that was true. `setHidden`/`setPinned`/\n    // `setManualWidth` stay ungated, so a page's own layout logic is never denied — only a stale\n    // snapshot is.\n    if (col.hideable) col.setHidden(state.hidden);\n    if (col.pinnable) col.setPinned(state.pinned);\n    if (col.resizable) col.setManualWidth(state.width);\n  }\n\n  // snapshot order first (unknown keys dropped), then current columns the snapshot doesn't know\n  private mergedOrder(order: string[]): string[] {\n    const known = order.filter((k) => this.columns.has(k));\n    const rest = this.columnOrder.filter((k) => !known.includes(k));\n    return [...known, ...rest];\n  }\n\n  // number of expansion panels fully above the given display index\n  private expandedAbove(index: number): number {\n    let count = 0;\n    for (const i of this.expandedDisplayIndices) {\n      if (i < index) count++;\n      else break;\n    }\n    return count;\n  }\n\n  /**\n   * The display index of the row whose block (row + its expansion panel, if any) contains the\n   * vertical content offset `y`. Walks the expanded indices accumulating their extra height —\n   * a row scrolled past its own top stays \"at\" `y` while its panel is in view, so expanded rows\n   * render as long as any part of their block does.\n   */\n  private indexAtOffset(y: number): number {\n    const { rowHeight, expansionHeight } = this;\n    let extra = 0;\n    for (const i of this.expandedDisplayIndices) {\n      const panelTop = (i + 1) * rowHeight + extra;\n      if (panelTop > y) break;\n      if (panelTop + expansionHeight > y) return i;\n      extra += expansionHeight;\n    }\n    return Math.floor((y - extra) / rowHeight);\n  }\n}\n","import { useEffect, useRef } from \"react\";\nimport { TableModel } from \"./table.model\";\nimport type { RowData, UseTableConfig } from \"./table.types\";\n\n/**\n * Creates a `TableModel` that lives as long as the component.\n *\n * The config is read once, at construction — with three exceptions: `data`, `loading` and `error`\n * are kept in sync, because a route's params can change without remounting the page (same component\n * type at the same tree position), and a table that ignored the new data would keep rendering the\n * previous org's rows.\n *\n * How \"changed\" is decided depends on which shape of `config.data` you pass, and the difference\n * matters — see {@link TableConfig.data}. An **array** is re-applied when its identity changes, so\n * it must be referentially stable. A **getter** is tracked by MobX instead, and must read\n * observables. A **lazy** is re-pointed when you hand over a different one, which is what makes a\n * keyed collection work: `data={store.byOrg({ orgId })}` is a new lazy each time `orgId` changes.\n *\n * A lazy also knows whether a request is running and how the last one ended, so it needs no help\n * describing itself and `loading` / `error` are ignored. The other two shapes carry no such story,\n * which is what those props are for — pass what your fetching already knows and the table derives\n * `loading`, `error` and `isEmpty` from it just the same.\n *\n * Everything else (`columns`, `getRowId`, `onStateChange`) is captured at construction; change them\n * through the model (`setColumns`/`addColumn`/`removeColumn`, `applyState`) rather than by\n * re-rendering. Per-column filters need none of that — they are instances the caller holds and\n * mutates directly, and the model reads through to them.\n */\nexport const useTable = <T>(config?: UseTableConfig<T>): TableModel => {\n  const tableRef = useRef<TableModel | undefined>(undefined);\n  if (!tableRef.current) {\n    tableRef.current = new TableModel(config);\n  }\n  const table = tableRef.current;\n\n  // What the model has actually been given, so the first render doesn't re-apply what the\n  // constructor already applied — and a StrictMode remount doesn't either.\n  const data = config?.data;\n  const appliedData = useRef(data);\n\n  useEffect(() => {\n    if (data === appliedData.current) return;\n    appliedData.current = data;\n    table.setData((data ?? []) as RowData[]);\n  }, [table, data]);\n\n  // Mirrored rather than read once: these are ordinary React values that change between renders,\n  // and the model is where every state derived from them is computed. Written in an effect so the\n  // render itself stays side-effect free; the model lands them one commit later, which no indicator\n  // is fast enough to show. A lazy answers for itself, so this is skipped entirely for one.\n  const loading = config?.loading ?? false;\n  const error = config?.error;\n  useEffect(() => {\n    if (table.lazy) return;\n    table.setStatus(loading, error);\n  }, [table, loading, error]);\n\n  // The model's reactions must die with the component or they leak past unmount.\n  // activate/dispose as an effect pair (not dispose alone) because StrictMode dev remounts run\n  // cleanup against a model the surviving ref will hand out again.\n  useEffect(() => {\n    table.activate();\n    return () => table.dispose();\n  }, [table]);\n\n  return table;\n};\n"],"mappings":"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"}