@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/dist/index.mjs CHANGED
@@ -1,283 +1,9 @@
1
- import { Show, children, createEffect, createSignal, mergeProps, on, onCleanup, onMount, splitProps, untrack, useContext } from "solid-js";
1
+ import { Show, createEffect, createSignal, mergeProps, on, onCleanup, onMount, splitProps, untrack, useContext } from "solid-js";
2
2
  import { Key } from "@solid-primitives/keyed";
3
- import { __rozieInjectStyle, createControllableSignal, createOutsideClick, parseInlineStyle, rozieAttr, rozieClass, rozieContext, rozieDisplay } from "@rozie/runtime-solid";
4
- import { arrow, autoUpdate, computePosition, flip, offset, shift } from "@floating-ui/dom";
3
+ import { __rozieInjectStyle, createControllableSignal, parseInlineStyle, rozieAttr, rozieClass, rozieContext, rozieDisplay } from "@rozie/runtime-solid";
4
+ import Popover from "@rozie-ui/popover-solid";
5
5
  import { createTable, getCoreRowModel, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getPaginationRowModel, getSortedRowModel } from "@tanstack/table-core";
6
6
  import { Virtualizer, elementScroll, measureElement, observeElementOffset, observeElementRect } from "@tanstack/virtual-core";
7
- //#region src/internal/middleware.ts
8
- /**
9
- * Build the ordered middleware array for `computePosition`. Pure — no engine
10
- * import, no DOM read beyond the passed-in arrow element.
11
- */
12
- function buildMiddleware(factories, config) {
13
- const mw = [factories.offset(config.offset)];
14
- if (!config.disableFlip) mw.push(factories.flip());
15
- if (!config.disableShift) mw.push(factories.shift());
16
- if (config.arrow && config.arrowEl) mw.push(factories.arrow({ element: config.arrowEl }));
17
- return mw;
18
- }
19
- //#endregion
20
- //#region src/Popover.tsx
21
- __rozieInjectStyle("Popover-c6cf02ea", `.rozie-popover[data-rozie-s-c6cf02ea] {
22
- display: contents;
23
- }
24
- .rozie-popover-anchor[data-rozie-s-c6cf02ea] {
25
- display: inline-block;
26
- }
27
- .rozie-popover-floating[data-rozie-s-c6cf02ea] {
28
- position: absolute;
29
- left: 0;
30
- top: 0;
31
- z-index: var(--rozie-popover-z, 1000);
32
- width: max-content;
33
- max-width: var(--rozie-popover-max-width, calc(100vw - 16px));
34
- background: var(--rozie-popover-bg, #fff);
35
- color: var(--rozie-popover-color, inherit);
36
- border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
37
- border-radius: var(--rozie-popover-radius, 8px);
38
- box-shadow: var(--rozie-popover-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
39
- padding: var(--rozie-popover-padding, 8px 12px);
40
- }
41
- .rozie-popover-arrow[data-rozie-s-c6cf02ea] {
42
- position: absolute;
43
- width: var(--rozie-popover-arrow-size, 8px);
44
- height: var(--rozie-popover-arrow-size, 8px);
45
- background: var(--rozie-popover-bg, #fff);
46
- border: var(--rozie-popover-border, 1px solid rgba(0, 0, 0, 0.12));
47
- transform: rotate(45deg);
48
- }`);
49
- function Popover(_props) {
50
- const [local, attrs] = splitProps(mergeProps({
51
- placement: "bottom",
52
- trigger: "click",
53
- offset: 8,
54
- disableFlip: false,
55
- disableShift: false,
56
- arrow: false,
57
- disabled: false,
58
- modal: false,
59
- strategy: "absolute"
60
- }, _props), [
61
- "open",
62
- "placement",
63
- "trigger",
64
- "offset",
65
- "disableFlip",
66
- "disableShift",
67
- "arrow",
68
- "disabled",
69
- "modal",
70
- "strategy",
71
- "children",
72
- "ref"
73
- ]);
74
- const resolved = children(() => local.children);
75
- onMount(() => {
76
- local.ref?.({
77
- show,
78
- hide,
79
- toggle,
80
- reposition
81
- });
82
- });
83
- const [open, setOpen] = createControllableSignal(_props, "open", false);
84
- onMount(() => {
85
- const _cleanup = (() => {
86
- anchorNode = anchorElRef;
87
- if (open() && !local.disabled) {
88
- floatingNode = floatingElRef;
89
- arrowNode = arrowElRef;
90
- startTracking();
91
- }
92
- })();
93
- if (_cleanup) onCleanup(_cleanup);
94
- onCleanup(() => {
95
- stopTracking();
96
- });
97
- });
98
- createEffect(on(() => open(), (v) => untrack(() => ((isOpen) => {
99
- if (isOpen && !local.disabled) queueMicrotask(() => {
100
- if (!open() || local.disabled) return;
101
- floatingNode = floatingElRef;
102
- arrowNode = arrowElRef;
103
- startTracking();
104
- });
105
- else stopTracking();
106
- })(v)), { defer: true }));
107
- createEffect(on(() => local.placement, (v) => untrack(() => (() => {
108
- if (open()) position();
109
- })()), { defer: true }));
110
- createEffect(on(() => local.offset, (v) => untrack(() => (() => {
111
- if (open()) position();
112
- })()), { defer: true }));
113
- createEffect(on(() => local.disableFlip, (v) => untrack(() => (() => {
114
- if (open()) position();
115
- })()), { defer: true }));
116
- createEffect(on(() => local.disableShift, (v) => untrack(() => (() => {
117
- if (open()) position();
118
- })()), { defer: true }));
119
- createEffect(on(() => local.strategy, (v) => untrack(() => (() => {
120
- if (open()) position();
121
- })()), { defer: true }));
122
- let anchorElRef = null;
123
- let floatingElRef = null;
124
- let arrowElRef = null;
125
- let anchorNode = null;
126
- let floatingNode = null;
127
- let arrowNode = null;
128
- let stopAutoUpdate = null;
129
- let lastFocusedEl = null;
130
- function deepActiveElement() {
131
- let el = document.activeElement;
132
- while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
133
- return el;
134
- }
135
- function requestOpen(next) {
136
- if (open() === next) return;
137
- if (next && local.trigger === "click") lastFocusedEl = deepActiveElement();
138
- setOpen(next);
139
- _props.onChange?.(next);
140
- if (!next && local.trigger === "click" && lastFocusedEl && lastFocusedEl.isConnected && typeof lastFocusedEl.focus === "function") lastFocusedEl.focus();
141
- if (!next) lastFocusedEl = null;
142
- }
143
- function applyPosition(x, y, middlewareData) {
144
- if (!floatingNode) return;
145
- floatingNode.style.left = x + "px";
146
- floatingNode.style.top = y + "px";
147
- if (arrowNode && middlewareData && middlewareData.arrow) {
148
- const ax = middlewareData.arrow.x;
149
- const ay = middlewareData.arrow.y;
150
- arrowNode.style.left = ax == null ? "" : ax + "px";
151
- arrowNode.style.top = ay == null ? "" : ay + "px";
152
- }
153
- }
154
- function position() {
155
- if (!anchorNode || !floatingNode) return;
156
- const middleware = buildMiddleware({
157
- offset,
158
- flip,
159
- shift,
160
- arrow
161
- }, {
162
- offset: local.offset,
163
- disableFlip: local.disableFlip,
164
- disableShift: local.disableShift,
165
- arrow: local.arrow,
166
- arrowEl: arrowNode
167
- });
168
- if (local.strategy === "fixed") floatingNode.style.position = "fixed";
169
- else floatingNode.style.position = "";
170
- let opts = null;
171
- opts = {
172
- placement: local.placement,
173
- strategy: local.strategy,
174
- middleware
175
- };
176
- computePosition(anchorNode, floatingNode, opts).then((result) => {
177
- applyPosition(result.x, result.y, result.middlewareData);
178
- });
179
- }
180
- function startTracking() {
181
- if (!anchorNode || !floatingNode) return;
182
- if (stopAutoUpdate) {
183
- stopAutoUpdate();
184
- stopAutoUpdate = null;
185
- }
186
- stopAutoUpdate = autoUpdate(anchorNode, floatingNode, position);
187
- }
188
- function stopTracking() {
189
- if (stopAutoUpdate) {
190
- stopAutoUpdate();
191
- stopAutoUpdate = null;
192
- }
193
- }
194
- function onAnchorClick() {
195
- if (local.disabled) return;
196
- requestOpen(!open());
197
- }
198
- function onAnchorPointerEnter() {
199
- if (local.disabled) return;
200
- requestOpen(true);
201
- }
202
- function onAnchorPointerLeave() {
203
- if (local.disabled) return;
204
- requestOpen(false);
205
- }
206
- function onAnchorFocus() {
207
- if (local.disabled) return;
208
- requestOpen(true);
209
- }
210
- function onAnchorBlur() {
211
- if (local.disabled) return;
212
- requestOpen(false);
213
- }
214
- function dismiss() {
215
- requestOpen(false);
216
- }
217
- function isTooltip() {
218
- return local.trigger === "hover" || local.trigger === "focus";
219
- }
220
- function floatingRole() {
221
- return isTooltip() ? "tooltip" : local.modal ? "dialog" : void 0;
222
- }
223
- function show() {
224
- if (!local.disabled) requestOpen(true);
225
- }
226
- function hide() {
227
- requestOpen(false);
228
- }
229
- function toggle() {
230
- if (!local.disabled) requestOpen(!open());
231
- }
232
- function reposition() {
233
- position();
234
- }
235
- createEffect(() => {
236
- if (!open()) return;
237
- const _rozieHandler = ($event) => {
238
- if ($event.key !== "Escape") return;
239
- dismiss();
240
- };
241
- document.addEventListener("keydown", _rozieHandler);
242
- onCleanup(() => document.removeEventListener("keydown", _rozieHandler));
243
- });
244
- createOutsideClick([() => anchorElRef, () => floatingElRef], dismiss, () => open());
245
- return <>
246
- <div {...attrs} class={"rozie-popover" + (attrs.class ? " " + attrs.class : "")} data-rozie-s-c6cf02ea="">
247
-
248
-
249
- <div aria-haspopup="dialog" aria-expanded={!!open()} aria-describedby={rozieAttr(isTooltip() && open() ? "rozie-popover-floating" : null)} class={"rozie-popover-anchor"} ref={(el) => {
250
- anchorElRef = el;
251
- }} onClick={($event) => {
252
- local.trigger === "click" && onAnchorClick();
253
- }} onPointerEnter={($event) => {
254
- local.trigger === "hover" && onAnchorPointerEnter();
255
- }} onPointerLeave={($event) => {
256
- local.trigger === "hover" && onAnchorPointerLeave();
257
- }} onFocusIn={($event) => {
258
- local.trigger === "focus" && onAnchorFocus();
259
- }} onFocusOut={($event) => {
260
- local.trigger === "focus" && onAnchorBlur();
261
- }} data-rozie-s-c6cf02ea="">
262
- {(_props.anchorSlot ?? _props.slots?.["anchor"])?.({
263
- open: open(),
264
- toggle,
265
- show,
266
- hide
267
- })}
268
- </div>
269
-
270
-
271
- {<Show when={open() && !local.disabled}><div class={"rozie-popover-floating"} ref={(el) => {
272
- floatingElRef = el;
273
- }} id="rozie-popover-floating" role={rozieAttr(floatingRole())} aria-modal={!!(floatingRole() === "dialog")} data-rozie-s-c6cf02ea="">
274
- {<Show when={local.arrow}><div class={"rozie-popover-arrow"} ref={(el) => {
275
- arrowElRef = el;
276
- }} data-rozie-s-c6cf02ea="" /></Show>}{resolved()}
277
- </div></Show>}</div>
278
- </>;
279
- }
280
- //#endregion
281
7
  //#region src/DataTable.tsx
282
8
  __rozieInjectStyle("DataTable-d5dcab4c", `.rozie-data-table[data-rozie-s-d5dcab4c] {
283
9
  border-collapse: collapse;
@@ -734,6 +460,7 @@ function DataTable(_props) {
734
460
  const [draftValue, setDraftValue] = createSignal(null);
735
461
  const [invalidMsg, setInvalidMsg] = createSignal("");
736
462
  const [editVer, setEditVer] = createSignal(0);
463
+ const [editFocusColId, setEditFocusColId] = createSignal(null);
737
464
  const [editingRowIndex, setEditingRowIndex] = createSignal(null);
738
465
  const [rowDraft, setRowDraft] = createSignal({});
739
466
  const [rangeAnchor, setRangeAnchor] = createSignal(null);
@@ -1718,8 +1445,7 @@ function DataTable(_props) {
1718
1445
  const base = pinStyle(colId);
1719
1446
  if (isExpanderColumn(colId) && row && row.depth) {
1720
1447
  const pad = "padding-left:" + (.5 + row.depth * 1.25) + "rem";
1721
- const sep = base.endsWith(";") ? "" : ";";
1722
- return base ? base + sep + pad : pad;
1448
+ return base ? base + pad : pad;
1723
1449
  }
1724
1450
  return base;
1725
1451
  }
@@ -3176,6 +2902,7 @@ function DataTable(_props) {
3176
2902
  }
3177
2903
  function focusEditorWhenReady(selectAll = true) {
3178
2904
  if (!gridRoot) return;
2905
+ if (editFocusColId() != null && hasEditorSlot(editFocusColId())) return;
3179
2906
  let attempts = 0;
3180
2907
  const tryFocus = () => {
3181
2908
  const el = gridRoot ? gridRoot.querySelector("[data-editing-cell]") : null;
@@ -3226,6 +2953,7 @@ function DataTable(_props) {
3226
2953
  setDraftValue(seed != null ? seed : cellValueAt(rowIndex, colIndex));
3227
2954
  setActiveInControl(true);
3228
2955
  setEditVer(editVer() + 1);
2956
+ setEditFocusColId(colId);
3229
2957
  focusEditorWhenReady(seed == null);
3230
2958
  }
3231
2959
  function focusCellWhenReady(row, col) {
@@ -3262,6 +2990,7 @@ function DataTable(_props) {
3262
2990
  setInvalidMsg("");
3263
2991
  setActiveInControl(false);
3264
2992
  setEditVer(editVer() + 1);
2993
+ setEditFocusColId(null);
3265
2994
  }
3266
2995
  function endRowEdit() {
3267
2996
  setEditingRowIndex(null);
@@ -3269,6 +2998,14 @@ function DataTable(_props) {
3269
2998
  setInvalidMsg("");
3270
2999
  setActiveInControl(false);
3271
3000
  setEditVer(editVer() + 1);
3001
+ setEditFocusColId(null);
3002
+ }
3003
+ function editorAutofocusFor(colId, rowIndex) {
3004
+ if (editVer() < 0) return false;
3005
+ if (editingRowIndex() != null) {
3006
+ if (editingRowIndex() !== rowIndex) return false;
3007
+ } else if (editingRow() !== rowIndex) return false;
3008
+ return editFocusColId() != null && editFocusColId() === colId;
3272
3009
  }
3273
3010
  function coerceCellValue(colId, raw) {
3274
3011
  if (editorTypeOf(colId) !== "number") return raw;
@@ -3387,6 +3124,8 @@ function DataTable(_props) {
3387
3124
  }
3388
3125
  function focusRowEditorAt(rowIndex, colIndex) {
3389
3126
  if (!gridRoot) return;
3127
+ const colId = columnIdAt(rowIndex, colIndex);
3128
+ if (colId != null && hasEditorSlot(colId)) return;
3390
3129
  let attempts = 0;
3391
3130
  const tryFocus = () => {
3392
3131
  const cellEl = resolveCellEl(String(rowIndex), colIndex);
@@ -3427,6 +3166,7 @@ function DataTable(_props) {
3427
3166
  setEditingRowIndex(rowIndex);
3428
3167
  setActiveInControl(true);
3429
3168
  setEditVer(editVer() + 1);
3169
+ setEditFocusColId(editable[0].colId);
3430
3170
  focusEditorWhenReady();
3431
3171
  }
3432
3172
  function commitRow() {
@@ -3446,6 +3186,8 @@ function DataTable(_props) {
3446
3186
  const err = runValidator(ec.colId, coerceCellValue(ec.colId, draft[ec.colId]), rowOriginal);
3447
3187
  if (err !== true) {
3448
3188
  setInvalid(err);
3189
+ setEditFocusColId(ec.colId);
3190
+ setEditVer(editVer() + 1);
3449
3191
  focusRowEditorAt(rowIndex, ec.colIndex);
3450
3192
  return false;
3451
3193
  }
@@ -3619,7 +3361,10 @@ function DataTable(_props) {
3619
3361
  let pos = cols.indexOf(cur);
3620
3362
  if (pos < 0) pos = 0;
3621
3363
  const len = cols.length;
3622
- focusRowEditorAt(rowIndex, cols[backward ? (pos - 1 + len) % len : (pos + 1) % len]);
3364
+ const nextPos = backward ? (pos - 1 + len) % len : (pos + 1) % len;
3365
+ setEditFocusColId(editable[nextPos].colId);
3366
+ setEditVer(editVer() + 1);
3367
+ focusRowEditorAt(rowIndex, cols[nextPos]);
3623
3368
  }
3624
3369
  function onEditorKeyDown(e) {
3625
3370
  if (!e) return;
@@ -4038,7 +3783,8 @@ function DataTable(_props) {
4038
3783
  row: row().original,
4039
3784
  value: editorValueFor(cell().column.id),
4040
3785
  commit: editorCommitFor(cell().column.id),
4041
- cancel: editorCancelFor()
3786
+ cancel: editorCancelFor(),
3787
+ autofocus: editorAutofocusFor(cell().column.id, rowIndexOf(row()))
4042
3788
  })}
4043
3789
  </span></Show>}</span></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
4044
3790
  <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) => {
@@ -4227,7 +3973,8 @@ function DataTable(_props) {
4227
3973
  row: wr().row.original,
4228
3974
  value: editorValueFor(cell().column.id),
4229
3975
  commit: editorCommitFor(cell().column.id),
4230
- cancel: editorCancelFor()
3976
+ cancel: editorCancelFor(),
3977
+ autofocus: editorAutofocusFor(cell().column.id, wr().vi.index)
4231
3978
  })}
4232
3979
  </span></Show>}</span></Show>}><span style={{ display: "contents" }} data-rozie-s-d5dcab4c="">
4233
3980
  <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) => {
@@ -4402,16 +4149,25 @@ function EditorText(_props) {
4402
4149
  row: null,
4403
4150
  value: null,
4404
4151
  commit: null,
4405
- cancel: null
4152
+ cancel: null,
4153
+ autofocus: false
4406
4154
  }, _props), [
4407
4155
  "columnId",
4408
4156
  "column",
4409
4157
  "row",
4410
4158
  "value",
4411
4159
  "commit",
4412
- "cancel"
4160
+ "cancel",
4161
+ "autofocus"
4413
4162
  ]);
4414
4163
  const [draft, setDraft] = createSignal("");
4164
+ onMount(() => {
4165
+ if (local.autofocus) inputElRef?.focus();
4166
+ });
4167
+ createEffect(on(() => local.autofocus, (v) => untrack(() => ((v) => {
4168
+ if (v) inputElRef?.focus();
4169
+ })(v)), { defer: true }));
4170
+ let inputElRef = null;
4415
4171
  setDraft(local.value != null ? String(local.value) : "");
4416
4172
  function onInput(e) {
4417
4173
  setDraft(e && e.target ? e.target.value : "");
@@ -4435,7 +4191,9 @@ function EditorText(_props) {
4435
4191
  doCommit();
4436
4192
  }
4437
4193
  return <>
4438
- <input type="text" data-editing-cell="" aria-label={local.columnId} class={"rdt-cell-editor"} value={draft()} onInput={($event) => {
4194
+ <input type="text" data-editing-cell="" aria-label={local.columnId} ref={(el) => {
4195
+ inputElRef = el;
4196
+ }} class={"rdt-cell-editor"} value={draft()} onInput={($event) => {
4439
4197
  onInput($event);
4440
4198
  }} onKeyDown={($event) => {
4441
4199
  onKeydown($event);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozie-ui/data-table-solid",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -46,13 +46,13 @@
46
46
  ],
47
47
  "dependencies": {
48
48
  "@solid-primitives/keyed": "^1.5.3",
49
- "@rozie/runtime-solid": "0.1.3"
49
+ "@rozie/runtime-solid": "0.1.4"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "solid-js": "^1.8",
53
53
  "@tanstack/table-core": "^8.21",
54
54
  "@tanstack/virtual-core": "^3",
55
- "@floating-ui/dom": "^1.7.2"
55
+ "@rozie-ui/popover-solid": "^0.1.0"
56
56
  },
57
57
  "peerDependenciesMeta": {
58
58
  "solid-js": {
@@ -64,13 +64,11 @@
64
64
  "@tanstack/virtual-core": {
65
65
  "optional": false
66
66
  },
67
- "@floating-ui/dom": {
67
+ "@rozie-ui/popover-solid": {
68
68
  "optional": false
69
69
  }
70
70
  },
71
- "devDependencies": {
72
- "@floating-ui/dom": "^1.7.2"
73
- },
71
+ "devDependencies": {},
74
72
  "publishConfig": {
75
73
  "access": "public"
76
74
  },
package/src/Column.tsx CHANGED
@@ -64,7 +64,7 @@ interface ColumnProps {
64
64
  }
65
65
 
66
66
  export default function Column(_props: ColumnProps): JSX.Element {
67
- const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, editable: false, editor: 'text', editorOptions: (() => [])(), validate: null }, _props);
67
+ const _merged = mergeProps({ id: '', field: '', header: '', sortable: false, filterable: false, pinned: '', width: '', expandable: false, groupable: true, aggregationFn: null, editable: false, editor: 'text', editorOptions: (() => [])() as any[], validate: null }, _props);
68
68
  const [local, attrs] = splitProps(_merged, ['id', 'field', 'header', 'sortable', 'filterable', 'pinned', 'width', 'expandable', 'groupable', 'aggregationFn', 'editable', 'editor', 'editorOptions', 'validate']);
69
69
 
70
70
  const registry = useContext(rozieContext("data-table:columns"));