@rozie-ui/data-table-solid 0.1.0 → 0.2.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 +2 -2
- package/dist/index.cjs +68 -287
- package/dist/index.d.cts +5 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +43 -285
- package/package.json +5 -7
- package/src/Column.tsx +1 -1
- package/src/DataTable.tsx +68 -15
- package/src/EditorSelect.tsx +1 -1
- package/src/EditorText.tsx +21 -4
- package/src/FilterSelect.tsx +1 -1
- package/src/GroupBar.tsx +1 -1
- package/src/Popover.tsx +0 -416
- package/src/internal/middleware.ts +0 -54
package/src/DataTable.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import type { JSX } from 'solid-js';
|
|
|
2
2
|
import { Show, createEffect, createSignal, mergeProps, on, onCleanup, onMount, splitProps, untrack } from 'solid-js';
|
|
3
3
|
import { Key } from '@solid-primitives/keyed';
|
|
4
4
|
import { __rozieInjectStyle, createControllableSignal, parseInlineStyle, rozieAttr, rozieClass, rozieContext, rozieDisplay } from '@rozie/runtime-solid';
|
|
5
|
-
import Popover from '
|
|
5
|
+
import Popover from '@rozie-ui/popover-solid';
|
|
6
6
|
import { createTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, getExpandedRowModel, getGroupedRowModel,
|
|
7
7
|
// Faceted filtering (phase 50 reqs 8-9, D-03). All three are supplied UNCONDITIONALLY
|
|
8
8
|
// (mirrors the expand/group models) — inert until a consumer READS a column facet via the
|
|
@@ -355,7 +355,7 @@ interface SelectCellSlotCtx { row: any; checked: any; toggle: any; }
|
|
|
355
355
|
|
|
356
356
|
interface CellSlotCtx { columnId: any; column: any; row: any; value: any; }
|
|
357
357
|
|
|
358
|
-
interface EditorSlotCtx { columnId: any; column: any; row: any; value: any; commit: any; cancel: any; }
|
|
358
|
+
interface EditorSlotCtx { columnId: any; column: any; row: any; value: any; commit: any; cancel: any; autofocus: any; }
|
|
359
359
|
|
|
360
360
|
interface DetailSlotCtx { row: any; }
|
|
361
361
|
|
|
@@ -565,7 +565,7 @@ export interface DataTableHandle {
|
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
568
|
-
const _merged = mergeProps({ columns: (() => [])(), selectionMode: 'none', manual: false, rowCount: null, pageCount: null, expandable: false, getSubRows: null, groupable: false, stickyHeader: false, interactionMode: 'table', singleClickEdit: false, undoable: false, undoLimit: 100, virtual: false, estimateRowHeight: 40, maxHeight: '' }, _props);
|
|
568
|
+
const _merged = mergeProps({ columns: (() => [])() as any[], selectionMode: 'none', manual: false, rowCount: null, pageCount: null, expandable: false, getSubRows: null, groupable: false, stickyHeader: false, interactionMode: 'table', singleClickEdit: false, undoable: false, undoLimit: 100, virtual: false, estimateRowHeight: 40, maxHeight: '' }, _props);
|
|
569
569
|
const [local, attrs] = splitProps(_merged, ['data', 'columns', 'selectionMode', 'sorting', 'globalFilter', 'columnFilters', 'pagination', 'manual', 'rowCount', 'pageCount', 'expandable', 'expanded', 'getSubRows', 'groupable', 'grouping', 'rowSelection', 'columnVisibility', 'columnSizing', 'columnOrder', 'columnPinning', 'stickyHeader', 'interactionMode', 'singleClickEdit', 'undoable', 'undoLimit', 'virtual', 'estimateRowHeight', 'maxHeight', 'children', 'ref']);
|
|
570
570
|
const resolved = () => local.children;
|
|
571
571
|
onMount(() => { local.ref?.({ sortColumn, clearSorting, toggleRowExpanded, expandAll, collapseAll, getExpandedRows, applyGrouping, clearGrouping, getFacetedUniqueValues, getFacetedMinMaxValues, getColumnDefs, toggleAllRows, clearSelection, getSelectedRows, setPage, setRowsPerPage, toggleColumnVisibility, applyColumnOrder, resetColumnSizing, pinColumn, focusCell, getActiveCell, clearActiveCell, getRowIndexRelativeToPage, editCell, commitEditing, editRow, getSelectedRange, cut, undo, redo, canUndo, canRedo, clearHistory }); });
|
|
@@ -579,8 +579,8 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
579
579
|
pageIndex: 0,
|
|
580
580
|
pageSize: 10
|
|
581
581
|
}))());
|
|
582
|
-
const [expanded, setExpanded] = createControllableSignal<Record<string, any> | boolean>(_props as unknown as Record<string, unknown>, 'expanded', null);
|
|
583
|
-
const [grouping, setGrouping] = createControllableSignal<any[]>(_props as unknown as Record<string, unknown>, 'grouping', null);
|
|
582
|
+
const [expanded, setExpanded] = createControllableSignal<Record<string, any> | boolean | null>(_props as unknown as Record<string, unknown>, 'expanded', null);
|
|
583
|
+
const [grouping, setGrouping] = createControllableSignal<any[] | null>(_props as unknown as Record<string, unknown>, 'grouping', null);
|
|
584
584
|
const [rowSelection, setRowSelection] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'rowSelection', (() => ({}))());
|
|
585
585
|
const [columnVisibility, setColumnVisibility] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'columnVisibility', (() => ({}))());
|
|
586
586
|
const [columnSizing, setColumnSizing] = createControllableSignal<Record<string, any>>(_props as unknown as Record<string, unknown>, 'columnSizing', (() => ({}))());
|
|
@@ -630,6 +630,7 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
630
630
|
const [draftValue, setDraftValue] = createSignal<any>(null);
|
|
631
631
|
const [invalidMsg, setInvalidMsg] = createSignal('');
|
|
632
632
|
const [editVer, setEditVer] = createSignal(0);
|
|
633
|
+
const [editFocusColId, setEditFocusColId] = createSignal<any>(null);
|
|
633
634
|
const [editingRowIndex, setEditingRowIndex] = createSignal<any>(null);
|
|
634
635
|
const [rowDraft, setRowDraft] = createSignal<Record<string, any>>({});
|
|
635
636
|
const [rangeAnchor, setRangeAnchor] = createSignal<any>(null);
|
|
@@ -2605,15 +2606,9 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
2605
2606
|
function bodyCellStyle(row: any, colId: any) {
|
|
2606
2607
|
const base = pinStyle(colId);
|
|
2607
2608
|
if (isExpanderColumn(colId) && row && row.depth) {
|
|
2609
|
+
// Only the expander column indents (the tree affordance lives here).
|
|
2608
2610
|
const pad = 'padding-left:' + (0.5 + row.depth * 1.25) + 'rem';
|
|
2609
|
-
|
|
2610
|
-
// second ';' produces a `;;` empty declaration. Browsers tolerate that in a
|
|
2611
|
-
// raw style attribute (React/Vue/Svelte/Lit) and Solid's cssText path does
|
|
2612
|
-
// too, but Angular's [style] binding parses the string via ɵɵstyleMap and
|
|
2613
|
-
// DROPS the declaration following the empty one — so the depth padding-left
|
|
2614
|
-
// silently vanished on Angular only. Emit a single, well-formed separator.
|
|
2615
|
-
const sep = base.endsWith(';') ? '' : ';';
|
|
2616
|
-
return base ? base + sep + pad : pad;
|
|
2611
|
+
return base ? base + pad : pad;
|
|
2617
2612
|
}
|
|
2618
2613
|
return base;
|
|
2619
2614
|
}
|
|
@@ -5169,8 +5164,18 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5169
5164
|
// draft (selecting the seeded char makes the next keystroke replace it: Zeta → eta).
|
|
5170
5165
|
// beginEdit threads `seed == null` so a seeded entry skips the select and the caret sits
|
|
5171
5166
|
// AFTER the seeded char; every other caller keeps the default select-all.
|
|
5167
|
+
// Editor-owns-focus contract (quick 260711-i5m): REVERTS the g52 shadow-piercing helper
|
|
5168
|
+
// (commit 5fa30045) that recursed into descendant shadow roots. Built-in editors are
|
|
5169
|
+
// host-DOM — the plain direct query resolves them on all 6 targets (no shadow to cross). A
|
|
5170
|
+
// #editor DROP-IN now owns its OWN focus via the reactive `autofocus` prop (EditorText's
|
|
5171
|
+
// $onMount + lazy $watch), so the host never needs to reach across a Lit drop-in's nested
|
|
5172
|
+
// shadow root at all — see the !hasEditorSlot gate below, which skips the host focus call
|
|
5173
|
+
// entirely for a drop-in target.
|
|
5172
5174
|
function focusEditorWhenReady(selectAll = true) {
|
|
5173
5175
|
if (!gridRoot) return;
|
|
5176
|
+
// Editor-owns-focus contract: when the CURRENT focus target is a #editor drop-in, the host
|
|
5177
|
+
// does NOT reach into its DOM — the drop-in self-focuses via its own autofocus prop.
|
|
5178
|
+
if (editFocusColId() != null && hasEditorSlot(editFocusColId())) return;
|
|
5174
5179
|
let attempts = 0;
|
|
5175
5180
|
const tryFocus = () => {
|
|
5176
5181
|
const el = gridRoot ? gridRoot.querySelector('[data-editing-cell]') : null;
|
|
@@ -5246,6 +5251,10 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5246
5251
|
setDraftValue(seed != null ? seed : cellValueAt(rowIndex, colIndex));
|
|
5247
5252
|
setActiveInControl(true);
|
|
5248
5253
|
setEditVer(editVer() + 1);
|
|
5254
|
+
// Editor-owns-focus contract (quick 260711-i5m): THIS cell's column is the current
|
|
5255
|
+
// focus target — editorAutofocusFor derives the reactive `autofocus` #editor scope prop
|
|
5256
|
+
// from it. Cleared on endEdit.
|
|
5257
|
+
setEditFocusColId(colId);
|
|
5249
5258
|
// B2: a seeded (type-to-edit) entry must NOT select-all — keep the caret after the
|
|
5250
5259
|
// seeded char so subsequent typing appends instead of replacing it.
|
|
5251
5260
|
focusEditorWhenReady(seed == null);
|
|
@@ -5300,6 +5309,7 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5300
5309
|
setInvalidMsg('');
|
|
5301
5310
|
setActiveInControl(false);
|
|
5302
5311
|
setEditVer(editVer() + 1);
|
|
5312
|
+
setEditFocusColId(null);
|
|
5303
5313
|
}
|
|
5304
5314
|
|
|
5305
5315
|
// endRowEdit: tear down full-row edit (shared by commitRow/cancelRow). Clears the row
|
|
@@ -5311,6 +5321,23 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5311
5321
|
setInvalidMsg('');
|
|
5312
5322
|
setActiveInControl(false);
|
|
5313
5323
|
setEditVer(editVer() + 1);
|
|
5324
|
+
setEditFocusColId(null);
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
// editorAutofocusFor (quick 260711-i5m, editor-owns-focus contract): the reactive `autofocus`
|
|
5328
|
+
// #editor scope prop for a given (colId, rowIndex) — true for EXACTLY the current focus-
|
|
5329
|
+
// target cell, re-deriving on every editVer bump (mirrors isEditing's reactive gate so
|
|
5330
|
+
// Svelte/Solid re-run this per-cell on a foreign-slot-callback state mutation). Works for
|
|
5331
|
+
// BOTH single-cell ($data.editingRow) and row mode ($data.editingRowIndex) since
|
|
5332
|
+
// $data.editFocusColId is set by both beginEdit and beginRowEdit/commitRow/rowEditTab.
|
|
5333
|
+
function editorAutofocusFor(colId: any, rowIndex: any) {
|
|
5334
|
+
if (editVer() < 0) return false;
|
|
5335
|
+
if (editingRowIndex() != null) {
|
|
5336
|
+
if (editingRowIndex() !== rowIndex) return false;
|
|
5337
|
+
} else {
|
|
5338
|
+
if (editingRow() !== rowIndex) return false;
|
|
5339
|
+
}
|
|
5340
|
+
return editFocusColId() != null && editFocusColId() === colId;
|
|
5314
5341
|
}
|
|
5315
5342
|
|
|
5316
5343
|
// B3: coerce the committed value by the column's built-in editor type at the single
|
|
@@ -5528,8 +5555,15 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5528
5555
|
// focuses its [data-editing-cell] control. Bounded rAF-poll (mirrors focusEditorWhenReady)
|
|
5529
5556
|
// so a React re-render that recreates the input across the focus call still lands it. select-
|
|
5530
5557
|
// all on text/number editors (a no-op try/catch on select/checkbox).
|
|
5558
|
+
// Editor-owns-focus contract (quick 260711-i5m): when the TARGET column is a #editor
|
|
5559
|
+
// drop-in, the host does NOT reach into its DOM (early return, before starting the rAF poll
|
|
5560
|
+
// at all) — the drop-in self-focuses via its own reactive `autofocus` prop, which the caller
|
|
5561
|
+
// (commitRow's B22 reject path / rowEditTab) already flips via $data.editFocusColId. Built-in
|
|
5562
|
+
// columns are unaffected (hasEditorSlot is false for them) — unchanged host direct-focus.
|
|
5531
5563
|
function focusRowEditorAt(rowIndex: any, colIndex: any) {
|
|
5532
5564
|
if (!gridRoot) return;
|
|
5565
|
+
const colId = columnIdAt(rowIndex, colIndex);
|
|
5566
|
+
if (colId != null && hasEditorSlot(colId)) return;
|
|
5533
5567
|
let attempts = 0;
|
|
5534
5568
|
const tryFocus = () => {
|
|
5535
5569
|
const cellEl = resolveCellEl(String(rowIndex), colIndex);
|
|
@@ -5581,6 +5615,11 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5581
5615
|
setEditingRowIndex(rowIndex);
|
|
5582
5616
|
setActiveInControl(true);
|
|
5583
5617
|
setEditVer(editVer() + 1);
|
|
5618
|
+
// Editor-owns-focus contract (quick 260711-i5m): the row's FIRST editable column is the
|
|
5619
|
+
// initial focus target — editorAutofocusFor derives the reactive `autofocus` #editor scope
|
|
5620
|
+
// prop from it (a built-in column is also host-focused below via focusEditorWhenReady; a
|
|
5621
|
+
// drop-in column self-focuses via its own $onMount, gated off the host reach-in in Task 3).
|
|
5622
|
+
setEditFocusColId(editable[0].colId);
|
|
5584
5623
|
focusEditorWhenReady();
|
|
5585
5624
|
}
|
|
5586
5625
|
|
|
@@ -5613,9 +5652,18 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5613
5652
|
const err = runValidator(ec.colId, coerceCellValue(ec.colId, draft[ec.colId]), rowOriginal);
|
|
5614
5653
|
if (err !== true) {
|
|
5615
5654
|
setInvalid(err);
|
|
5655
|
+
// Editor-owns-focus contract (quick 260711-i5m): the OFFENDING column becomes the new
|
|
5656
|
+
// reactive focus target BEFORE the host-focus call below — a #editor drop-in already
|
|
5657
|
+
// mounted (full-row edit opens every editable cell at once) picks this up via its own
|
|
5658
|
+
// lazy $watch on the `autofocus` scope prop flipping false→true. Bump editVer so the
|
|
5659
|
+
// coarse-render targets (React/Vue/Angular/Svelte) re-derive the slot binding (Solid's
|
|
5660
|
+
// fine-grained accessor re-runs without the bump, but the bump keeps all 6 in lockstep).
|
|
5661
|
+
setEditFocusColId(ec.colId);
|
|
5662
|
+
setEditVer(editVer() + 1);
|
|
5616
5663
|
// B22: focus the OFFENDING column's editor (the one whose validator rejected), NOT
|
|
5617
5664
|
// unconditionally the first editor (focusEditorWhenReady resolves the first
|
|
5618
5665
|
// [data-editing-cell] in DOM order). ec.colIndex is the offending cell's visible col.
|
|
5666
|
+
// Gated (Task 3) so a #editor drop-in self-focuses instead of a host DOM reach-in.
|
|
5619
5667
|
focusRowEditorAt(rowIndex, ec.colIndex);
|
|
5620
5668
|
return false;
|
|
5621
5669
|
}
|
|
@@ -5894,6 +5942,11 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
5894
5942
|
if (pos < 0) pos = 0;
|
|
5895
5943
|
const len = cols.length;
|
|
5896
5944
|
const nextPos = backward ? (pos - 1 + len) % len : (pos + 1) % len;
|
|
5945
|
+
// Editor-owns-focus contract (quick 260711-i5m): the Tab target becomes the new reactive
|
|
5946
|
+
// focus target BEFORE the host-focus call below, so Tab onto an already-mounted #editor
|
|
5947
|
+
// drop-in (row mode) also refocuses it via its own lazy $watch.
|
|
5948
|
+
setEditFocusColId(editable[nextPos].colId);
|
|
5949
|
+
setEditVer(editVer() + 1);
|
|
5897
5950
|
focusRowEditorAt(rowIndex, cols[nextPos]);
|
|
5898
5951
|
}
|
|
5899
5952
|
|
|
@@ -6426,7 +6479,7 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
6426
6479
|
{<Show when={hasEditorSlot(cell().column.id)} fallback={<Show when={editorTypeOf(cell().column.id) === 'number'} fallback={<Show when={editorTypeOf(cell().column.id) === 'select'} fallback={<Show when={editorTypeOf(cell().column.id) === 'checkbox'} fallback={<input type="text" data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" />}><input type="checkbox" data-editing-cell="" class={"rdt-cell-editor"} checked={editorCheckedFor(cell().column.id)} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorCheckbox(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" /></Show>}><select data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onChange={($event: Event & { currentTarget: HTMLSelectElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="">
|
|
6427
6480
|
<Key each={editorOptionsOf(cell().column.id) as readonly any[]} by={(opt) => opt.value}>{(opt) => <option value={rozieAttr(opt().value)} data-rozie-s-d5dcab4c="">{rozieDisplay(opt().label)}</option>}</Key>
|
|
6428
6481
|
</select></Show>}><input type="number" data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" /></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
|
|
6429
|
-
{(_props.editorSlot ?? _props.slots?.['editor'])?.({ columnId: cell().column.id, column: cell().column, row: row().original, value: editorValueFor(cell().column.id), commit: editorCommitFor(cell().column.id), cancel: editorCancelFor() })}
|
|
6482
|
+
{(_props.editorSlot ?? _props.slots?.['editor'])?.({ columnId: cell().column.id, column: cell().column, row: row().original, value: editorValueFor(cell().column.id), commit: editorCommitFor(cell().column.id), cancel: editorCancelFor(), autofocus: editorAutofocusFor(cell().column.id, rowIndexOf(row())) })}
|
|
6430
6483
|
</span></Show>}</span></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
|
|
6431
6484
|
<button type="button" data-expander="" aria-expanded={!!rowIsExpanded(row())} aria-label={rozieAttr(rowIsExpanded(row()) ? 'Collapse group' : 'Expand group')} class={"rdt-expander rdt-group-toggle"} onClick={($event: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }) => { onToggleExpand(row(), $event); }} data-rozie-s-d5dcab4c="">{rozieDisplay(rowIsExpanded(row()) ? '▾' : '▸')}</button>
|
|
6432
6485
|
<span class={"rdt-group-value"} data-rozie-s-d5dcab4c="">
|
|
@@ -6499,7 +6552,7 @@ export default function DataTable(_props: DataTableProps): JSX.Element {
|
|
|
6499
6552
|
{<Show when={hasEditorSlot(cell().column.id)} fallback={<Show when={editorTypeOf(cell().column.id) === 'number'} fallback={<Show when={editorTypeOf(cell().column.id) === 'select'} fallback={<Show when={editorTypeOf(cell().column.id) === 'checkbox'} fallback={<input type="text" data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" />}><input type="checkbox" data-editing-cell="" class={"rdt-cell-editor"} checked={editorCheckedFor(cell().column.id)} onChange={($event: Event & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorCheckbox(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" /></Show>}><select data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onChange={($event: Event & { currentTarget: HTMLSelectElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLSelectElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="">
|
|
6500
6553
|
<Key each={editorOptionsOf(cell().column.id) as readonly any[]} by={(opt) => opt.value}>{(opt) => <option value={rozieAttr(opt().value)} data-rozie-s-d5dcab4c="">{rozieDisplay(opt().label)}</option>}</Key>
|
|
6501
6554
|
</select></Show>}><input type="number" data-editing-cell="" class={"rdt-cell-editor"} value={editorValueFor(cell().column.id)} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onCellEditorInput(cell().column.id, $event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorKeyDown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onEditorBlur($event); }} data-rozie-s-d5dcab4c="" /></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
|
|
6502
|
-
{(_props.editorSlot ?? _props.slots?.['editor'])?.({ columnId: cell().column.id, column: cell().column, row: wr().row.original, value: editorValueFor(cell().column.id), commit: editorCommitFor(cell().column.id), cancel: editorCancelFor() })}
|
|
6555
|
+
{(_props.editorSlot ?? _props.slots?.['editor'])?.({ columnId: cell().column.id, column: cell().column, row: wr().row.original, value: editorValueFor(cell().column.id), commit: editorCommitFor(cell().column.id), cancel: editorCancelFor(), autofocus: editorAutofocusFor(cell().column.id, wr().vi.index) })}
|
|
6503
6556
|
</span></Show>}</span></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
|
|
6504
6557
|
<button type="button" data-expander="" aria-expanded={!!rowIsExpanded(wr().row)} aria-label={rozieAttr(rowIsExpanded(wr().row) ? 'Collapse group' : 'Expand group')} class={"rdt-expander rdt-group-toggle"} onClick={($event: MouseEvent & { currentTarget: HTMLButtonElement; target: Element }) => { onToggleExpand(wr().row, $event); }} data-rozie-s-d5dcab4c="">{rozieDisplay(rowIsExpanded(wr().row) ? '▾' : '▸')}</button>
|
|
6505
6558
|
<span class={"rdt-group-value"} data-rozie-s-d5dcab4c="">
|
package/src/EditorSelect.tsx
CHANGED
|
@@ -35,7 +35,7 @@ interface EditorSelectProps {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export default function EditorSelect(_props: EditorSelectProps): JSX.Element {
|
|
38
|
-
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, options: (() => [])() }, _props);
|
|
38
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, options: (() => [])() as any[] }, _props);
|
|
39
39
|
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel', 'options']);
|
|
40
40
|
|
|
41
41
|
const [draft, setDraft] = createSignal('');
|
package/src/EditorText.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { JSX } from 'solid-js';
|
|
2
|
-
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
|
2
|
+
import { createEffect, createSignal, mergeProps, on, onMount, splitProps, untrack } from 'solid-js';
|
|
3
3
|
|
|
4
4
|
interface EditorTextProps {
|
|
5
5
|
/**
|
|
@@ -26,13 +26,24 @@ interface EditorTextProps {
|
|
|
26
26
|
* `() => void` — revert the edit and close the editor (from the `#editor` slot scope). Null-guarded at call sites.
|
|
27
27
|
*/
|
|
28
28
|
cancel?: ((...args: unknown[]) => unknown) | null;
|
|
29
|
+
/**
|
|
30
|
+
* Focus this editor's primary input when true — the host sets it for the one editor that should hold focus; reactive.
|
|
31
|
+
*/
|
|
32
|
+
autofocus?: boolean;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
export default function EditorText(_props: EditorTextProps): JSX.Element {
|
|
32
|
-
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null }, _props);
|
|
33
|
-
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel']);
|
|
36
|
+
const _merged = mergeProps({ columnId: '', column: null, row: null, value: null, commit: null, cancel: null, autofocus: false }, _props);
|
|
37
|
+
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'row', 'value', 'commit', 'cancel', 'autofocus']);
|
|
34
38
|
|
|
35
39
|
const [draft, setDraft] = createSignal('');
|
|
40
|
+
onMount(() => {
|
|
41
|
+
if (local.autofocus) inputElRef?.focus();
|
|
42
|
+
});
|
|
43
|
+
createEffect(on(() => (() => local.autofocus)(), (v) => untrack(() => ((v: any) => {
|
|
44
|
+
if (v) inputElRef?.focus();
|
|
45
|
+
})(v)), { defer: true }));
|
|
46
|
+
let inputElRef: HTMLElement | null = null;
|
|
36
47
|
|
|
37
48
|
// Seed the draft once at setup from the incoming value (setup-once, NOT in the
|
|
38
49
|
// template). Normalize null/undefined to '' so the input value binds to a string.
|
|
@@ -64,9 +75,15 @@ export default function EditorText(_props: EditorTextProps): JSX.Element {
|
|
|
64
75
|
doCommit();
|
|
65
76
|
}
|
|
66
77
|
|
|
78
|
+
// Editor-owns-focus contract: focus OUR OWN input when the host says we should hold focus.
|
|
79
|
+
// $onMount covers the initial open (autofocus already true on first render); the LAZY $watch
|
|
80
|
+
// (NOT { immediate: true } — an immediate watch fires PRE-mount, null ref on Lit/Solid) covers
|
|
81
|
+
// a REACTIVE refocus while already mounted (e.g. a row-mode validation failure that flips
|
|
82
|
+
// autofocus back onto this already-open drop-in).
|
|
83
|
+
|
|
67
84
|
return (
|
|
68
85
|
<>
|
|
69
|
-
<input type="text" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-0d17f43a="" />
|
|
86
|
+
<input type="text" data-editing-cell="" aria-label={local.columnId} ref={(el) => { inputElRef = el as HTMLElement; }} class={"rdt-cell-editor"} value={draft()} onInput={($event: InputEvent & { currentTarget: HTMLInputElement; target: Element }) => { onInput($event); }} onKeyDown={($event: KeyboardEvent & { currentTarget: HTMLInputElement; target: Element }) => { onKeydown($event); }} onBlur={($event: FocusEvent & { currentTarget: HTMLInputElement; target: Element }) => { onBlur(); }} data-rozie-s-0d17f43a="" />
|
|
70
87
|
</>
|
|
71
88
|
);
|
|
72
89
|
}
|
package/src/FilterSelect.tsx
CHANGED
|
@@ -27,7 +27,7 @@ interface FilterSelectProps {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export default function FilterSelect(_props: FilterSelectProps): JSX.Element {
|
|
30
|
-
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, uniqueValues: (() => [])() }, _props);
|
|
30
|
+
const _merged = mergeProps({ columnId: '', column: null, value: null, setFilter: null, uniqueValues: (() => [])() as any[] }, _props);
|
|
31
31
|
const [local, attrs] = splitProps(_merged, ['columnId', 'column', 'value', 'setFilter', 'uniqueValues']);
|
|
32
32
|
|
|
33
33
|
// The <select> value binding coerced to a string. $props.value is typed `unknown`
|
package/src/GroupBar.tsx
CHANGED
|
@@ -81,7 +81,7 @@ interface GroupBarProps {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
export default function GroupBar(_props: GroupBarProps): JSX.Element {
|
|
84
|
-
const _merged = mergeProps({ grouping: (() => [])(), groupableColumns: (() => [])(), applyGrouping: null, clearGrouping: null }, _props);
|
|
84
|
+
const _merged = mergeProps({ grouping: (() => [])() as any[], groupableColumns: (() => [])() as any[], applyGrouping: null, clearGrouping: null }, _props);
|
|
85
85
|
const [local, attrs] = splitProps(_merged, ['grouping', 'groupableColumns', 'applyGrouping', 'clearGrouping']);
|
|
86
86
|
|
|
87
87
|
const [draggingId, setDraggingId] = createSignal('');
|