@rozie-ui/data-table-solid 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -95,9 +95,13 @@ export function Demo() {
95
95
  }
96
96
 
97
97
  // TOKEN form — the same height via the CSS custom property (the prop wins when
98
- // both are set; the token is the fallback). estimateRowHeight tunes the seed:
99
- // <DataTable data={rows} virtual estimateRowHeight={48}
100
- // style={{ '--rozie-data-table-max-height': '400px' }} />
98
+ // both are set; the token is the fallback). DataTable takes no style/class prop, so
99
+ // set the token in your stylesheet on the table's own wrap element, scoped by a
100
+ // parent class (this also outranks the base.css default). estimateRowHeight tunes the seed:
101
+ // .orders .rozie-data-table-wrap { --rozie-data-table-max-height: 400px; }
102
+ // <div class="orders">
103
+ // <DataTable data={rows} virtual estimateRowHeight={48} />
104
+ // </div>
101
105
  ```
102
106
 
103
107
  ### Editable cells (inline edit + validation)
@@ -252,7 +256,7 @@ export function Demo() {
252
256
  filterSlot={({ columnId, uniqueValues, minMax }) =>
253
257
  columnId === 'category' ? (
254
258
  <fieldset>
255
- {uniqueValues.map((v) => <label><input type="checkbox" /> {String(v)}</label>)}
259
+ {uniqueValues.map((v: unknown) => <label><input type="checkbox" /> {String(v)}</label>)}
256
260
  </fieldset>
257
261
  ) : (
258
262
  <input type="range" min={minMax?.[0]} max={minMax?.[1]} />
@@ -433,7 +437,7 @@ export function Demo() {
433
437
  'filter-category': ({ columnId, uniqueValues, setFilter }) => (
434
438
  <select onChange={(e) => setFilter(columnId, e.currentTarget.value || null)}>
435
439
  <option value="">All</option>
436
- {uniqueValues.map((v) => <option value={String(v)}>{String(v)}</option>)}
440
+ {uniqueValues.map((v: unknown) => <option value={String(v)}>{String(v)}</option>)}
437
441
  </select>
438
442
  ),
439
443
  }}
@@ -471,6 +475,7 @@ import '@rozie-ui/data-table-solid/themes/shadcn.css'; // or material.css, bo
471
475
  | `pageCount` | `Number` | `null` | | | Explicit total page count for `manual` pagination; overrides rowCount-derived count. |
472
476
  | `expandable` | `Boolean` | `false` | | | Opt-in **expandable rows**. When `true`, a leading chevron expander column auto-injects (after the select column) and `getExpandedRowModel` activates; default `false` is byte-identical-off. Every row can expand to reveal a `#detail` panel unless `getSubRows` is supplied (then only rows with children expand). Pass `expandable` as a real boolean — a valueless attribute only coerces to `true` on Vue and Lit. |
473
477
  | `expanded` | `any` | `null` | ✓ | | `ExpandedState` — `{ [rowId]: true }`, or the `true` literal after `expandAll` (declared `type: [Object, Boolean]`). Multi-expand (multiple rows open at once). Surfaces through `expand-change`; uncontrolled fallback (`$data.expandedDefault`) when unbound — the default is `null` so the uncontrolled fallback AND the grouping auto-expand default are reachable (a non-null default would short-circuit them). When grouping is active and `expanded` is untouched, group subtrees auto-expand. |
478
+ | `getRowId` | `Function` | `null` | | | Row identity `(originalRow, index, parentRow?) => string \| number` — the key `rowSelection`, `expanded` and the per-row caches use. Default null → table-core keys rows by position (`"0"`, `"1"`, … and `"0.1"` for sub-rows), so inserting or removing rows above a selected or expanded row moves that state onto whatever row now sits at its index. Supply it whenever rows can be added or removed while state is held (server push, live lists); the result is coerced to a string. |
474
479
  | `getSubRows` | `Function` | `null` | | | Table-level child-row accessor `(originalRow, index) => TData[] \| undefined` that drives nested sub-rows. When supplied (with `expandable`), table-core flattens the hierarchy and the expand seam reveals depth-indented child rows. Null → the `#detail` scoped slot is the expand mode. |
475
480
  | `groupable` | `Boolean` | `false` | | | Opt-in gate for the **headless `#groupBar`** host region. Default `false` is byte-identical-off. `getGroupedRowModel` is wired unconditionally (inert when `grouping` is empty), so grouping is driven by the `grouping` model; this flag only gates the consumer-facing group-bar surface (the component ships **no** built-in drag UI). |
476
481
  | `grouping` | `Array` | `null` | ✓ | | `GroupingState` — an ordered `string[]` of column ids (multi-column → nested groups, e.g. `['region','category']`). An empty/unbound list is ungrouped (byte-identical-off). Group-header rows are collapsible (they ride the expand model). Surfaces through `group-change`; uncontrolled fallback (`$data.groupingDefault`, default `[]`) when unbound — the default is `null` (mirroring `expanded`) so the uncontrolled fallback is reachable and the grouping auto-expand default can activate when a consumer applies grouping without binding the two-way `grouping` model (a non-null `[]` default would short-circuit it). All reads are null-guarded, so table-core still receives an array. |
@@ -504,7 +509,9 @@ import '@rozie-ui/data-table-solid/themes/shadcn.css'; // or material.css, bo
504
509
  | `reorder-change` | Fired when the column order changes. Column reorder is programmatic only — an `applyColumnOrder` call or a two-way `columnOrder` write; the component ships NO drag or menu affordance for it. Payload is the fresh `ColumnOrderState` `string[]`. |
505
510
  | `pin-change` | Fired when a column is pinned/unpinned (the per-header pin buttons or a `pinColumn` call). Payload is the fresh `ColumnPinningState` `{ left: string[], right: string[] }` object. |
506
511
  | `history-change` | Fired when undo/redo AVAILABILITY changes (one unified grid-wide undo/redo stack, not scoped per-cell or per-row) — EDGE-triggered (only when `canUndo`/`canRedo` actually flip, not on every recorded edit) so a toolbar can enable/disable its buttons without polling. Only meaningful when `undoable` is `true`. Payload is `{ canUndo, canRedo }` booleans. No `history` model prop exists, so there is no model-prop==emit-name duplicate-identifier collision. |
512
+ | `visible-range-change` | Fired when the RENDERED row window changes under row windowing (`virtual`) — the window the table actually renders, overscan included — and once after mount. Payload `{ start, end }` over the full row space (`end` exclusive; with lazy rows that space is `rowCount` long). Deduped: a scroll that does not move the window emits nothing. Pair with `manual` + `rowCount` and sparse `data` to fetch the rows the viewport needs. |
507
513
  | `activecell-change` | Fired (grid interaction mode only) whenever the active cell moves — by keyboard navigation or a `focusCell` call. One-way notification; payload is `{ rowIndex, colIndex }` integers over the visible model. Fires on every move including arrow keypresses. |
514
+ | `row-activate` | Fired when the user opens a row (list-style "open this item" — oinbox's email thread). A plain click on a body data row, outside any control in the clicked cell (the selection checkbox, the expander, links, inputs, drop-in widgets and an open editor do NOT activate), in both interaction modes; not a shift/ctrl/meta click (selection gestures), the end of a drag-select, or a `singleClickEdit` click on an editable grid cell (that opens the editor). In grid mode also Enter on a non-editable cell that has no controls of its own (Enter on an editable cell still begins the edit; Enter on a cell with controls still enters them). Group-header and detail rows never activate. Payload is `{ row, index, trigger }` — `row` the original data object, `index` its position in the rendered model (page-relative; the full model when virtual), `trigger` `'click'` or `'keyboard'`. |
508
515
  | `range-change` | Fired whenever the rectangular cell-range selection changes — extended by `Shift+Arrow` / `Shift+Click` from the single `extendRange`/`setRangeFocus` call sites (React multi-emit dedup). One-way notification (the range is NOT a `model:true` slice — the model:true count stays at 12, leaving the Angular multi-model-CVA condition untouched); it is a SEPARATE layer from the row-selection slice and the two never corrupt each other. Payload is `getSelectedRange()` → `{ anchor, focus }` where each corner is a `{ rowIndex, colIndex }` index pair over the visible model (integers only — no row data, no DOM node), or `{ anchor: null, focus: null }` when no range is set. |
509
516
  | `cell-edit-commit` | Fired when an editable cell commits a NEW, validated value — once per committed cell, from the single `commitEdit` call site (a rejected/invalid commit fires nothing). Pairs with the two-way `data` write (one fresh-array replace per commit). Payload is `{ rowId, columnId, oldValue, newValue }`. `cancel`/`Escape` and a validation failure emit nothing. |
510
517
  | `row-edit-commit` | Fired when a FULL-ROW edit commits — once per row save, from the single `commitRow` call site (React multi-emit dedup). A row enters full-row edit via `Shift+F2` or the `editRow` verb (every editable cell edits at once); one save validates every edited cell (a single failure blocks the whole row and emits nothing) then writes the two-way `data` model ONCE (a single fresh-array row-object replace with all changes applied) and fires this event ONCE. `Escape` reverts the whole row as a unit and emits nothing. Payload is `{ rowId, changes }` where `changes` is `[{ columnId, oldValue, newValue }]` for ONLY the columns whose value actually changed. |
@@ -568,7 +575,7 @@ const cats = handle?.getFacetedUniqueValues('category'); // getFacetedMinMaxValu
568
575
 
569
576
  All rendering slots live on the parent `<DataTable>` (a `<Column>` carries metadata only). The `cell` / `colHeader` slots are single renderers dispatched by `columnId` — switch on it to vary the render per column; a column the slot does not render shows the plain accessor value. (On React/Solid these are render-prop props — `renderCell` / `renderColHeader` / `cellSlot` / `colHeaderSlot`; on Lit they are the `.cell` / `.colHeader` properties — the documented cross-framework divergence.)
570
577
 
571
- The `detail` (expandable rows), `groupBar` (grouping) and `filter` (faceted filtering) scoped slots follow the SAME render-prop convention: on React they are `renderDetail` / `renderGroupBar` / `renderFilter`; on Solid they are `detailSlot` / `groupBarSlot` / `filterSlot`; on Lit they are the `.detail` / `.groupBar` / `.filter` properties — the documented React render-prop edge (per the cross-framework compatibility bar). On Vue / Svelte / Angular they are ordinary named scoped slots (`#detail` / `#groupBar` / `#filter`). The `groupBar` and `filter` slots are HEADLESS — the component ships NO built-in group-bar / facet control, so the consumer builds the UI purely from the exposed slot props.
578
+ The `detail` (expandable rows), `groupBar` (grouping), `filter` (faceted filtering) and `placeholder` (lazy-loading skeleton) scoped slots follow the SAME render-prop convention: on React they are `renderDetail` / `renderGroupBar` / `renderFilter` / `renderPlaceholder`; on Solid they are `detailSlot` / `groupBarSlot` / `filterSlot` / `placeholderSlot`; on Lit they are the `.detail` / `.groupBar` / `.filter` / `.placeholder` properties — the documented React render-prop edge (per the cross-framework compatibility bar). On Vue / Svelte / Angular they are ordinary named scoped slots (`#detail` / `#groupBar` / `#filter` / `#placeholder`). The `groupBar` and `filter` slots are HEADLESS — the component ships NO built-in group-bar / facet control, so the consumer builds the UI purely from the exposed slot props. `placeholder` DOES ship a default (a skeleton bar) — fill it only to replace that default with your own loading affordance; it renders for a `virtual` + `manual` + `rowCount` row not yet present in `data`.
572
579
 
573
580
  `cell`, `colHeader`, `filter` and `editor` each also have a **per-column slot family** — a statically-named fill per `columnId` (`cell-price`, `colHeader-price`, `filter-category`, `editor-score`, …) that takes precedence OVER the matching generic slot above it: `<seam>-<id>` → the generic `<seam>` slot → the built-in render. `editor-<id>` (and `editor`) only render on a column declared `editor="custom"`; `filter-<id>` (and `filter`) only render on a column declared `filterable`; `cell-<id>` / `colHeader-<id>` have no gate. The auto-injected row-select / row-expander chrome columns never reach any family — they are excluded structurally. On Solid the family is the additive **`slots`** record prop — `slots={{ 'cell-price': (ctx) => … }}` — keyed by the exact `<seam>-<id>` string.
574
581
 
@@ -580,12 +587,13 @@ A misspelled family key does not fail to compile — the emitted type carries a
580
587
  | groupBar | grouping, groupableColumns, applyGrouping, clearGrouping |
581
588
  | selectAll | checked, indeterminate, toggle |
582
589
  | colHeader-<id> | columnId, column, label |
583
- | filter-<id> | columnId, value, uniqueValues, minMax, setFilter |
590
+ | filter-<id> | columnId, value, uniqueValues, minMax, columnLabel, setFilter |
591
+ | placeholder | index, columnId |
584
592
  | selectCell | row, checked, toggle |
585
593
  | cell-<id> | columnId, column, row, value |
586
- | editor-<id> | columnId, column, row, value, commit, cancel, autofocus |
594
+ | editor-<id> | columnId, column, row, value, commit, cancel, columnLabel, autofocus |
587
595
  | detail | row |
588
596
  | colHeader | columnId, column, label |
589
- | filter | columnId, value, uniqueValues, minMax, setFilter |
597
+ | filter | columnId, value, uniqueValues, minMax, columnLabel, setFilter |
590
598
  | cell | columnId, column, row, value |
591
- | editor | columnId, column, row, value, commit, cancel, autofocus |
599
+ | editor | columnId, column, row, value, commit, cancel, columnLabel, autofocus |