@payglocal_ui/flux-ui 0.2.3 → 0.2.4
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.cjs +23 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/data-table.tsx +73 -1
package/package.json
CHANGED
package/src/data-table.tsx
CHANGED
|
@@ -72,6 +72,22 @@ interface DataTableProps<T> {
|
|
|
72
72
|
* flush with nothing trailing it. Takes precedence over `rowCta`.
|
|
73
73
|
*/
|
|
74
74
|
rowAction?: ReactNode | ((row: T, index: number) => ReactNode);
|
|
75
|
+
/**
|
|
76
|
+
* Makes the whole row a click target — the row itself opens a drawer, a
|
|
77
|
+
* detail page, whatever the table drills into — instead of that living in a
|
|
78
|
+
* per-cell wrapper or a hover-revealed button.
|
|
79
|
+
*
|
|
80
|
+
* The handler sits on the `<tr>`, so the entire row including cell padding
|
|
81
|
+
* and the empty space between columns is clickable, and the row gets
|
|
82
|
+
* `cursor-pointer` plus keyboard access (focusable, Enter / Space).
|
|
83
|
+
*
|
|
84
|
+
* Clicks that originate inside something interactive — a `<button>`, `<a>`,
|
|
85
|
+
* a form control, a Radix trigger, or anything marked
|
|
86
|
+
* `data-row-click-ignore` — do NOT fire this. Copy buttons, per-row menus
|
|
87
|
+
* and the `rowAction` overlay therefore keep doing only their own job
|
|
88
|
+
* without each having to stop propagation.
|
|
89
|
+
*/
|
|
90
|
+
onRowClick?: (row: T, index: number) => void;
|
|
75
91
|
/** Row / cell vertical rhythm and horizontal gutters */
|
|
76
92
|
density?: DataTableDensity;
|
|
77
93
|
/**
|
|
@@ -110,6 +126,7 @@ export function DataTable<T>({
|
|
|
110
126
|
rowKey,
|
|
111
127
|
rowCta,
|
|
112
128
|
rowAction,
|
|
129
|
+
onRowClick,
|
|
113
130
|
density = "default",
|
|
114
131
|
tableLayout = "fixed",
|
|
115
132
|
theadClassName,
|
|
@@ -176,6 +193,30 @@ export function DataTable<T>({
|
|
|
176
193
|
// edge of the viewport (it has no reserved column — it overlays the row).
|
|
177
194
|
const actionGutter = comfortable ? 20 : compact ? 12 : 16;
|
|
178
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Selector for everything a row-level click must keep its hands off. A click
|
|
198
|
+
* landing inside one of these belongs to that control alone — a copy button,
|
|
199
|
+
* a per-row menu, a link, a checkbox, the `rowAction` overlay — so the row
|
|
200
|
+
* handler ignores it rather than firing as well.
|
|
201
|
+
*
|
|
202
|
+
* `data-row-click-ignore` is the escape hatch for anything not covered here
|
|
203
|
+
* (a custom widget in a cell, a drag handle) without it needing to stop
|
|
204
|
+
* propagation itself.
|
|
205
|
+
*/
|
|
206
|
+
const ROW_CLICK_IGNORE =
|
|
207
|
+
'button, a, input, select, textarea, label, [role="button"], [role="link"], ' +
|
|
208
|
+
'[role="checkbox"], [role="menuitem"], [role="menu"], [role="dialog"], ' +
|
|
209
|
+
"[data-row-click-ignore]";
|
|
210
|
+
|
|
211
|
+
/** Whether a click/keypress inside a row should reach `onRowClick`. */
|
|
212
|
+
const isRowClickTarget = (target: EventTarget | null, rowEl: HTMLElement) => {
|
|
213
|
+
if (!(target instanceof Element)) return false;
|
|
214
|
+
const interactive = target.closest(ROW_CLICK_IGNORE);
|
|
215
|
+
// `closest` can walk out of the row entirely (a portalled menu, say); only
|
|
216
|
+
// a match inside THIS row means the click belonged to that control.
|
|
217
|
+
return !(interactive && rowEl.contains(interactive));
|
|
218
|
+
};
|
|
219
|
+
|
|
179
220
|
return (
|
|
180
221
|
<div
|
|
181
222
|
className={cn(
|
|
@@ -285,8 +326,39 @@ export function DataTable<T>({
|
|
|
285
326
|
compact && "min-h-[44px]",
|
|
286
327
|
"hover:bg-muted/40 dark:hover:bg-muted/25",
|
|
287
328
|
hasAction &&
|
|
288
|
-
"hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none"
|
|
329
|
+
"hover:shadow-[0_1px_0_rgba(0,0,0,0.04)] dark:hover:shadow-none",
|
|
330
|
+
// Clickable rows read as clickable, and show a focus ring
|
|
331
|
+
// when reached by keyboard. `focus-visible` only, so a
|
|
332
|
+
// mouse click does not leave a ring behind on the row.
|
|
333
|
+
onRowClick &&
|
|
334
|
+
"cursor-pointer focus-visible:outline-none focus-visible:bg-muted/40 dark:focus-visible:bg-muted/25"
|
|
289
335
|
)}
|
|
336
|
+
// Keyboard parity with the mouse: the row is reachable by
|
|
337
|
+
// Tab and activated by Enter / Space, which a bare <tr> with
|
|
338
|
+
// an onClick would not be. No role override — the row stays a
|
|
339
|
+
// row for assistive tech rather than claiming to be a button.
|
|
340
|
+
tabIndex={onRowClick ? 0 : undefined}
|
|
341
|
+
onClick={
|
|
342
|
+
onRowClick
|
|
343
|
+
? (e) => {
|
|
344
|
+
if (!isRowClickTarget(e.target, e.currentTarget)) return;
|
|
345
|
+
onRowClick(row, i);
|
|
346
|
+
}
|
|
347
|
+
: undefined
|
|
348
|
+
}
|
|
349
|
+
onKeyDown={
|
|
350
|
+
onRowClick
|
|
351
|
+
? (e) => {
|
|
352
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
353
|
+
// Only the row's own focus activates it; a keypress
|
|
354
|
+
// inside a control in the row belongs to that control.
|
|
355
|
+
if (e.target !== e.currentTarget) return;
|
|
356
|
+
// Space scrolls the page by default.
|
|
357
|
+
e.preventDefault();
|
|
358
|
+
onRowClick(row, i);
|
|
359
|
+
}
|
|
360
|
+
: undefined
|
|
361
|
+
}
|
|
290
362
|
>
|
|
291
363
|
{columns.map((col) => (
|
|
292
364
|
<td
|