@crisp-ui-kit/crisp 0.31.0 → 0.31.2
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/cjs/components/radiogroup/radiogroup-keyboard.js +6 -1
- package/dist/cjs/components/tabs/tabs-keyboard.js +9 -2
- package/dist/cjs/product/board/board.js +7 -5
- package/dist/cjs/product/recordpage/recordpage.js +9 -4
- package/dist/cjs/product/recordstable/recordstable.js +40 -5
- package/dist/esm/components/radiogroup/radiogroup-keyboard.js +6 -1
- package/dist/esm/components/tabs/tabs-keyboard.js +9 -2
- package/dist/esm/product/board/board.js +7 -5
- package/dist/esm/product/recordpage/recordpage.js +9 -4
- package/dist/esm/product/recordstable/recordstable.js +41 -6
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.useRadioGroupKeyboard = useRadioGroupKeyboard;
|
|
37
37
|
const React = __importStar(require("react"));
|
|
38
|
+
const core_1 = require("../../core");
|
|
38
39
|
/**
|
|
39
40
|
* The WAI-ARIA radio group keyboard model: ←/→/↑/↓ move focus to the next or
|
|
40
41
|
* previous enabled radio, wrapping, and check it on arrival. Home/End jump to
|
|
@@ -113,7 +114,11 @@ function useRadioGroupKeyboard({ select, groupRef: externalRef, }) {
|
|
|
113
114
|
else if (e.key === "End")
|
|
114
115
|
next = radios[radios.length - 1];
|
|
115
116
|
else {
|
|
116
|
-
|
|
117
|
+
// Down always means forward; the horizontal pair follows the direction
|
|
118
|
+
// the radios are drawn in, which for Segmented's pill row is the whole
|
|
119
|
+
// point (#255).
|
|
120
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
121
|
+
const fwd = e.key === "ArrowDown" || e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
117
122
|
// focus outside the group: enter at the near end rather than stepping
|
|
118
123
|
// off an index of -1, which would land two from the back going backwards
|
|
119
124
|
if (idx < 0)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useTablistKeyboard = useTablistKeyboard;
|
|
4
|
+
const core_1 = require("../../core");
|
|
4
5
|
/**
|
|
5
6
|
* The WAI-ARIA tablist keyboard model: ←/→ to move (wrapping), Home/End to
|
|
6
7
|
* jump, disabled tabs skipped, focus following selection.
|
|
@@ -20,9 +21,15 @@ function useTablistKeyboard({ tabs, active, select, listRef, }) {
|
|
|
20
21
|
return;
|
|
21
22
|
const idx = enabled.findIndex((t) => t.value === active);
|
|
22
23
|
let next = -1;
|
|
23
|
-
|
|
24
|
+
// The arrows follow the strip as it is drawn: under RTL the next tab is to
|
|
25
|
+
// the LEFT, so the keys swap. Everything else about the model is unchanged
|
|
26
|
+
// (#255).
|
|
27
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
28
|
+
const forwardKey = rtl ? "ArrowLeft" : "ArrowRight";
|
|
29
|
+
const backKey = rtl ? "ArrowRight" : "ArrowLeft";
|
|
30
|
+
if (e.key === forwardKey)
|
|
24
31
|
next = (idx + 1) % enabled.length;
|
|
25
|
-
else if (e.key ===
|
|
32
|
+
else if (e.key === backKey)
|
|
26
33
|
next = (idx - 1 + enabled.length) % enabled.length;
|
|
27
34
|
else if (e.key === "Home")
|
|
28
35
|
next = 0;
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.Board = Board;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const React = __importStar(require("react"));
|
|
39
|
+
const core_1 = require("../../core");
|
|
39
40
|
const board_recipe_1 = require("./board.recipe");
|
|
40
41
|
/**
|
|
41
42
|
* A kanban board with native drag-and-drop between columns. Cards are
|
|
@@ -108,11 +109,12 @@ function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLa
|
|
|
108
109
|
}, onKeyDown: (e) => {
|
|
109
110
|
if (!(e.ctrlKey || e.metaKey))
|
|
110
111
|
return;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
// the columns are a flex row, so under RTL the next one
|
|
113
|
+
// is to the LEFT and the keys swap (#255)
|
|
114
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
115
|
+
const forwardKey = rtl ? "ArrowLeft" : "ArrowRight";
|
|
116
|
+
const backKey = rtl ? "ArrowRight" : "ArrowLeft";
|
|
117
|
+
const delta = e.key === backKey ? -1 : e.key === forwardKey ? 1 : 0;
|
|
116
118
|
// only swallow the key when the move actually happens, so
|
|
117
119
|
// Ctrl+Right at the last column still reaches the browser
|
|
118
120
|
if (delta && moveByKey(card, delta))
|
|
@@ -414,10 +414,15 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
414
414
|
if (!next)
|
|
415
415
|
setFieldQuery("");
|
|
416
416
|
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), (0, jsx_runtime_1.jsxs)("div", { className: "recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-emptystate", children: t.noLists })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": 300, "aria-valuemax": 560, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
417
|
-
if (e.key
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
417
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
418
|
+
return;
|
|
419
|
+
// The pointer drag learned its direction in #212; this did not.
|
|
420
|
+
// The details column is the flex row's first child, so it sits on
|
|
421
|
+
// the RIGHT under RTL and ArrowLeft has to grow it — the same
|
|
422
|
+
// half-conversion the drag had, one handler away (#255).
|
|
423
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
424
|
+
const grow = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
425
|
+
setDetailsW((w) => grow ? Math.min(560, w + 16) : Math.max(300, w - 16));
|
|
421
426
|
} }), (0, jsx_runtime_1.jsx)("main", { className: "recordpage-main", children: (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { tabs: tabs, defaultValue: "overview", "aria-label": `${name} record`, children: panel }) })] })] }));
|
|
422
427
|
}
|
|
423
428
|
RecordPage.displayName = "RecordPage";
|
|
@@ -640,10 +640,15 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
640
640
|
nr = Math.max(0, r - 1);
|
|
641
641
|
else if (e.key === "ArrowDown")
|
|
642
642
|
nr = Math.min(visibleRows.length - 1, r + 1);
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
643
|
+
// The arrows follow what the user SEES. Under RTL the first column renders
|
|
644
|
+
// rightmost, so ArrowLeft moves to the next column and ArrowRight to the
|
|
645
|
+
// previous — the grid's own half-conversion, of the same kind the resize
|
|
646
|
+
// grips had (#255).
|
|
647
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
648
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
649
|
+
const forward = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
650
|
+
nc = forward ? Math.min(cols.length - 1, c + 1) : Math.max(0, c - 1);
|
|
651
|
+
}
|
|
647
652
|
else if (e.key === "Escape") {
|
|
648
653
|
setSelCell(null);
|
|
649
654
|
return;
|
|
@@ -716,6 +721,36 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
716
721
|
const setSortField = (i, key) => setSorts(sorts.map((s, j) => (j === i ? { ...s, key } : s)));
|
|
717
722
|
const setSortDir = (i, dir) => setSorts(sorts.map((s, j) => (j === i ? { ...s, dir } : s)));
|
|
718
723
|
const removeSort = (i) => setSorts(sorts.filter((_, j) => j !== i));
|
|
724
|
+
/**
|
|
725
|
+
* Keep the selected cell on screen.
|
|
726
|
+
*
|
|
727
|
+
* Focus stays on the scroll region, never on a cell, so the browser has
|
|
728
|
+
* nothing to scroll to: the arrow keys moved the outline and the viewport
|
|
729
|
+
* stayed put. Measured in Chrome on a 384px-tall region over 40 rows —
|
|
730
|
+
* twenty ArrowDowns left `scrollTop` at 0 with the selected cell 761px below
|
|
731
|
+
* the top edge, i.e. the user was driving a selection they could not see
|
|
732
|
+
* (#257).
|
|
733
|
+
*
|
|
734
|
+
* `nearest` on both axes: a cell already in view is left exactly where it
|
|
735
|
+
* is, which is what makes this safe to run after a click too.
|
|
736
|
+
*
|
|
737
|
+
* No `behavior` of our own — the page's `scroll-behavior` decides, and Attio
|
|
738
|
+
* jumps rather than glides — except under reduced motion, where an explicit
|
|
739
|
+
* "auto" is the only thing that beats a host page that set
|
|
740
|
+
* `scroll-behavior: smooth` on the scroller (#208's rule: JS motion reads
|
|
741
|
+
* the preference itself, because CSS cannot reach an explicit argument).
|
|
742
|
+
*/
|
|
743
|
+
const reduceMotion = (0, core_1.usePrefersReducedMotion)();
|
|
744
|
+
React.useEffect(() => {
|
|
745
|
+
if (!selCell)
|
|
746
|
+
return;
|
|
747
|
+
const cell = scrollRef.current?.querySelector(`[data-cell="${CSS.escape(`${selCell.id}:${selCell.key}`)}"]`);
|
|
748
|
+
cell?.scrollIntoView({
|
|
749
|
+
block: "nearest",
|
|
750
|
+
inline: "nearest",
|
|
751
|
+
...(reduceMotion ? { behavior: "auto" } : null),
|
|
752
|
+
});
|
|
753
|
+
}, [selCell, reduceMotion]);
|
|
719
754
|
const commitEdit = (id, key, raw) => {
|
|
720
755
|
const col = cols.find((c) => c.key === key);
|
|
721
756
|
const numeric = col?.type === "number" || col?.type === "currency";
|
|
@@ -1064,7 +1099,7 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
1064
1099
|
// row instead of a bare "182,000", and the same header is
|
|
1065
1100
|
// repeated in every cell's context (#231).
|
|
1066
1101
|
const Cell = (i === 0 ? "th" : "td");
|
|
1067
|
-
return ((0, jsx_runtime_1.jsxs)(Cell, { scope: i === 0 ? "row" : undefined, className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-enriched": col.enriched ? "" : undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1102
|
+
return ((0, jsx_runtime_1.jsxs)(Cell, { scope: i === 0 ? "row" : undefined, "data-cell": `${row.id}:${col.key}`, className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-enriched": col.enriched ? "" : undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1068
1103
|
? ""
|
|
1069
1104
|
: undefined, onClick: () => {
|
|
1070
1105
|
if (isPicker)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { isRtlElement } from "../../core/index.js";
|
|
2
3
|
/**
|
|
3
4
|
* The WAI-ARIA radio group keyboard model: ←/→/↑/↓ move focus to the next or
|
|
4
5
|
* previous enabled radio, wrapping, and check it on arrival. Home/End jump to
|
|
@@ -77,7 +78,11 @@ export function useRadioGroupKeyboard({ select, groupRef: externalRef, }) {
|
|
|
77
78
|
else if (e.key === "End")
|
|
78
79
|
next = radios[radios.length - 1];
|
|
79
80
|
else {
|
|
80
|
-
|
|
81
|
+
// Down always means forward; the horizontal pair follows the direction
|
|
82
|
+
// the radios are drawn in, which for Segmented's pill row is the whole
|
|
83
|
+
// point (#255).
|
|
84
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
85
|
+
const fwd = e.key === "ArrowDown" || e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
81
86
|
// focus outside the group: enter at the near end rather than stepping
|
|
82
87
|
// off an index of -1, which would land two from the back going backwards
|
|
83
88
|
if (idx < 0)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRtlElement } from "../../core/index.js";
|
|
1
2
|
/**
|
|
2
3
|
* The WAI-ARIA tablist keyboard model: ←/→ to move (wrapping), Home/End to
|
|
3
4
|
* jump, disabled tabs skipped, focus following selection.
|
|
@@ -17,9 +18,15 @@ export function useTablistKeyboard({ tabs, active, select, listRef, }) {
|
|
|
17
18
|
return;
|
|
18
19
|
const idx = enabled.findIndex((t) => t.value === active);
|
|
19
20
|
let next = -1;
|
|
20
|
-
|
|
21
|
+
// The arrows follow the strip as it is drawn: under RTL the next tab is to
|
|
22
|
+
// the LEFT, so the keys swap. Everything else about the model is unchanged
|
|
23
|
+
// (#255).
|
|
24
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
25
|
+
const forwardKey = rtl ? "ArrowLeft" : "ArrowRight";
|
|
26
|
+
const backKey = rtl ? "ArrowRight" : "ArrowLeft";
|
|
27
|
+
if (e.key === forwardKey)
|
|
21
28
|
next = (idx + 1) % enabled.length;
|
|
22
|
-
else if (e.key ===
|
|
29
|
+
else if (e.key === backKey)
|
|
23
30
|
next = (idx - 1 + enabled.length) % enabled.length;
|
|
24
31
|
else if (e.key === "Home")
|
|
25
32
|
next = 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { isRtlElement } from "../../core/index.js";
|
|
3
4
|
import { boardRecipe } from "./board.recipe.js";
|
|
4
5
|
/**
|
|
5
6
|
* A kanban board with native drag-and-drop between columns. Cards are
|
|
@@ -72,11 +73,12 @@ export function Board({ columns, cards, onCardsChange, renderCard, onNewCard, ne
|
|
|
72
73
|
}, onKeyDown: (e) => {
|
|
73
74
|
if (!(e.ctrlKey || e.metaKey))
|
|
74
75
|
return;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
// the columns are a flex row, so under RTL the next one
|
|
77
|
+
// is to the LEFT and the keys swap (#255)
|
|
78
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
79
|
+
const forwardKey = rtl ? "ArrowLeft" : "ArrowRight";
|
|
80
|
+
const backKey = rtl ? "ArrowRight" : "ArrowLeft";
|
|
81
|
+
const delta = e.key === backKey ? -1 : e.key === forwardKey ? 1 : 0;
|
|
80
82
|
// only swallow the key when the move actually happens, so
|
|
81
83
|
// Ctrl+Right at the last column still reaches the browser
|
|
82
84
|
if (delta && moveByKey(card, delta))
|
|
@@ -377,10 +377,15 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
377
377
|
if (!next)
|
|
378
378
|
setFieldQuery("");
|
|
379
379
|
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : (_jsxs("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), _jsxs("div", { className: "recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "recordpage-emptystate", children: t.noLists })] })] }), _jsx("div", { className: "recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": 300, "aria-valuemax": 560, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
380
|
-
if (e.key
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
380
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
381
|
+
return;
|
|
382
|
+
// The pointer drag learned its direction in #212; this did not.
|
|
383
|
+
// The details column is the flex row's first child, so it sits on
|
|
384
|
+
// the RIGHT under RTL and ArrowLeft has to grow it — the same
|
|
385
|
+
// half-conversion the drag had, one handler away (#255).
|
|
386
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
387
|
+
const grow = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
388
|
+
setDetailsW((w) => grow ? Math.min(560, w + 16) : Math.max(300, w - 16));
|
|
384
389
|
} }), _jsx("main", { className: "recordpage-main", children: _jsx(Tabs, { tabs: tabs, defaultValue: "overview", "aria-label": `${name} record`, children: panel }) })] })] }));
|
|
385
390
|
}
|
|
386
391
|
RecordPage.displayName = "RecordPage";
|
|
@@ -14,7 +14,7 @@ import { Menu, MenuItem, useMenuKeyboard } from "../../components/menu/index.js"
|
|
|
14
14
|
import { MultiSelect } from "../../components/multiselect/index.js";
|
|
15
15
|
import { Select } from "../../components/select/index.js";
|
|
16
16
|
import { Switch } from "../../components/switch/index.js";
|
|
17
|
-
import { isRtlElement } from "../../core/index.js";
|
|
17
|
+
import { isRtlElement, usePrefersReducedMotion } from "../../core/index.js";
|
|
18
18
|
import { companyLogos } from "./company-logos.js";
|
|
19
19
|
import { recordstableRecipe } from "./recordstable.recipe.js";
|
|
20
20
|
const EDITABLE = new Set(["text", "number", "url", "currency"]);
|
|
@@ -600,10 +600,15 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
600
600
|
nr = Math.max(0, r - 1);
|
|
601
601
|
else if (e.key === "ArrowDown")
|
|
602
602
|
nr = Math.min(visibleRows.length - 1, r + 1);
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
603
|
+
// The arrows follow what the user SEES. Under RTL the first column renders
|
|
604
|
+
// rightmost, so ArrowLeft moves to the next column and ArrowRight to the
|
|
605
|
+
// previous — the grid's own half-conversion, of the same kind the resize
|
|
606
|
+
// grips had (#255).
|
|
607
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
608
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
609
|
+
const forward = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
610
|
+
nc = forward ? Math.min(cols.length - 1, c + 1) : Math.max(0, c - 1);
|
|
611
|
+
}
|
|
607
612
|
else if (e.key === "Escape") {
|
|
608
613
|
setSelCell(null);
|
|
609
614
|
return;
|
|
@@ -676,6 +681,36 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
676
681
|
const setSortField = (i, key) => setSorts(sorts.map((s, j) => (j === i ? { ...s, key } : s)));
|
|
677
682
|
const setSortDir = (i, dir) => setSorts(sorts.map((s, j) => (j === i ? { ...s, dir } : s)));
|
|
678
683
|
const removeSort = (i) => setSorts(sorts.filter((_, j) => j !== i));
|
|
684
|
+
/**
|
|
685
|
+
* Keep the selected cell on screen.
|
|
686
|
+
*
|
|
687
|
+
* Focus stays on the scroll region, never on a cell, so the browser has
|
|
688
|
+
* nothing to scroll to: the arrow keys moved the outline and the viewport
|
|
689
|
+
* stayed put. Measured in Chrome on a 384px-tall region over 40 rows —
|
|
690
|
+
* twenty ArrowDowns left `scrollTop` at 0 with the selected cell 761px below
|
|
691
|
+
* the top edge, i.e. the user was driving a selection they could not see
|
|
692
|
+
* (#257).
|
|
693
|
+
*
|
|
694
|
+
* `nearest` on both axes: a cell already in view is left exactly where it
|
|
695
|
+
* is, which is what makes this safe to run after a click too.
|
|
696
|
+
*
|
|
697
|
+
* No `behavior` of our own — the page's `scroll-behavior` decides, and Attio
|
|
698
|
+
* jumps rather than glides — except under reduced motion, where an explicit
|
|
699
|
+
* "auto" is the only thing that beats a host page that set
|
|
700
|
+
* `scroll-behavior: smooth` on the scroller (#208's rule: JS motion reads
|
|
701
|
+
* the preference itself, because CSS cannot reach an explicit argument).
|
|
702
|
+
*/
|
|
703
|
+
const reduceMotion = usePrefersReducedMotion();
|
|
704
|
+
React.useEffect(() => {
|
|
705
|
+
if (!selCell)
|
|
706
|
+
return;
|
|
707
|
+
const cell = scrollRef.current?.querySelector(`[data-cell="${CSS.escape(`${selCell.id}:${selCell.key}`)}"]`);
|
|
708
|
+
cell?.scrollIntoView({
|
|
709
|
+
block: "nearest",
|
|
710
|
+
inline: "nearest",
|
|
711
|
+
...(reduceMotion ? { behavior: "auto" } : null),
|
|
712
|
+
});
|
|
713
|
+
}, [selCell, reduceMotion]);
|
|
679
714
|
const commitEdit = (id, key, raw) => {
|
|
680
715
|
const col = cols.find((c) => c.key === key);
|
|
681
716
|
const numeric = col?.type === "number" || col?.type === "currency";
|
|
@@ -1024,7 +1059,7 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
1024
1059
|
// row instead of a bare "182,000", and the same header is
|
|
1025
1060
|
// repeated in every cell's context (#231).
|
|
1026
1061
|
const Cell = (i === 0 ? "th" : "td");
|
|
1027
|
-
return (_jsxs(Cell, { scope: i === 0 ? "row" : undefined, className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-enriched": col.enriched ? "" : undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1062
|
+
return (_jsxs(Cell, { scope: i === 0 ? "row" : undefined, "data-cell": `${row.id}:${col.key}`, className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-enriched": col.enriched ? "" : undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1028
1063
|
? ""
|
|
1029
1064
|
: undefined, onClick: () => {
|
|
1030
1065
|
if (isPicker)
|