@crisp-ui-kit/crisp 0.31.1 → 0.31.3
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/datatable/datatable.js +14 -5
- package/dist/cjs/components/datatable/datatable.recipe.js +2 -2
- package/dist/cjs/product/recordstable/recordstable.js +31 -1
- package/dist/datatable.css +2 -2
- package/dist/esm/components/datatable/datatable.js +14 -5
- package/dist/esm/components/datatable/datatable.recipe.js +2 -2
- package/dist/esm/product/recordstable/recordstable.js +32 -2
- package/dist/styles.css +2 -2
- package/package.json +1 -1
|
@@ -37,6 +37,7 @@ exports.DataTable = DataTable;
|
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const lucide_react_1 = require("lucide-react");
|
|
39
39
|
const React = __importStar(require("react"));
|
|
40
|
+
const core_1 = require("../../core");
|
|
40
41
|
const checkbox_1 = require("../checkbox");
|
|
41
42
|
const menu_1 = require("../menu");
|
|
42
43
|
const table_1 = require("../table");
|
|
@@ -365,10 +366,13 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
365
366
|
nr = Math.max(0, r - 1);
|
|
366
367
|
else if (e.key === "ArrowDown")
|
|
367
368
|
nr = Math.min(rows.length - 1, r + 1);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
else if (e.key === "ArrowRight")
|
|
371
|
-
|
|
369
|
+
// The arrows follow what the user SEES: under RTL the first column renders
|
|
370
|
+
// rightmost, so ArrowLeft steps forward and ArrowRight steps back (#255).
|
|
371
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
372
|
+
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
373
|
+
const forward = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
374
|
+
nc = forward ? Math.min(columns.length - 1, c + 1) : Math.max(0, c - 1);
|
|
375
|
+
}
|
|
372
376
|
else if (e.key === "Escape") {
|
|
373
377
|
setSelCell(null);
|
|
374
378
|
return;
|
|
@@ -426,6 +430,11 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
426
430
|
key: col.key,
|
|
427
431
|
startX: e.clientX,
|
|
428
432
|
startW: colWidth(col),
|
|
433
|
+
// The columns reverse under RTL, so the grip — anchored at
|
|
434
|
+
// `inset-inline-end` — lands on the column's LEFT edge there, and
|
|
435
|
+
// pulling it further left must GROW the column. `clientX` stays
|
|
436
|
+
// physical, so the sign is the other half of that conversion (#214).
|
|
437
|
+
dir: (0, core_1.isRtlElement)(e.currentTarget) ? -1 : 1,
|
|
429
438
|
};
|
|
430
439
|
setResizingKey(col.key);
|
|
431
440
|
}
|
|
@@ -433,7 +442,7 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
433
442
|
const r = resizeRef.current;
|
|
434
443
|
if (!r)
|
|
435
444
|
return;
|
|
436
|
-
const w = Math.max(64, r.startW + (e.clientX - r.startX));
|
|
445
|
+
const w = Math.max(64, r.startW + r.dir * (e.clientX - r.startX));
|
|
437
446
|
setWidths((prev) => ({ ...prev, [r.key]: w }));
|
|
438
447
|
}
|
|
439
448
|
function endResize(e) {
|
|
@@ -73,8 +73,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
73
73
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
74
74
|
& table { table-layout: fixed; }
|
|
75
75
|
/* column resize grip */
|
|
76
|
-
& .datatable-resize { position: absolute; top: 0;
|
|
77
|
-
& .datatable-resize::after { content: ""; position: absolute; top: 0;
|
|
76
|
+
& .datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
77
|
+
& .datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: ${tokens_1.contract.bgBrandSolid}; opacity: 0; transition: opacity .12s; }
|
|
78
78
|
& .datatable-resize:hover::after, & .datatable-resize[data-resizing]::after { opacity: 1; }
|
|
79
79
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
80
80
|
& .datatable-entity { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; min-width: 0; color: ${tokens_1.contract.fgPrimary}; font-weight: ${tokens_1.contract.weight.medium}; }
|
|
@@ -721,6 +721,36 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
721
721
|
const setSortField = (i, key) => setSorts(sorts.map((s, j) => (j === i ? { ...s, key } : s)));
|
|
722
722
|
const setSortDir = (i, dir) => setSorts(sorts.map((s, j) => (j === i ? { ...s, dir } : s)));
|
|
723
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]);
|
|
724
754
|
const commitEdit = (id, key, raw) => {
|
|
725
755
|
const col = cols.find((c) => c.key === key);
|
|
726
756
|
const numeric = col?.type === "number" || col?.type === "currency";
|
|
@@ -1069,7 +1099,7 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
1069
1099
|
// row instead of a bare "182,000", and the same header is
|
|
1070
1100
|
// repeated in every cell's context (#231).
|
|
1071
1101
|
const Cell = (i === 0 ? "th" : "td");
|
|
1072
|
-
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
|
|
1073
1103
|
? ""
|
|
1074
1104
|
: undefined, onClick: () => {
|
|
1075
1105
|
if (isPicker)
|
package/dist/datatable.css
CHANGED
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
64
64
|
& table { table-layout: fixed; }
|
|
65
65
|
/* column resize grip */
|
|
66
|
-
& .datatable-resize { position: absolute; top: 0;
|
|
67
|
-
& .datatable-resize::after { content: ""; position: absolute; top: 0;
|
|
66
|
+
& .datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
67
|
+
& .datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--bg-brand-solid); opacity: 0; transition: opacity .12s; }
|
|
68
68
|
& .datatable-resize:hover::after, & .datatable-resize[data-resizing]::after { opacity: 1; }
|
|
69
69
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
70
70
|
& .datatable-entity { display: flex; align-items: center; gap: var(--space-1-5); min-width: 0; color: var(--fg-primary); font-weight: var(--weight-medium); }
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ArrowDown, ArrowUp, Check, ChevronsUpDown, GripVertical, Plus, Search, SlidersHorizontal, } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { isRtlElement } from "../../core/index.js";
|
|
4
5
|
import { Checkbox } from "../checkbox/index.js";
|
|
5
6
|
import { Menu, MenuItem } from "../menu/index.js";
|
|
6
7
|
import { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from "../table/index.js";
|
|
@@ -329,10 +330,13 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
329
330
|
nr = Math.max(0, r - 1);
|
|
330
331
|
else if (e.key === "ArrowDown")
|
|
331
332
|
nr = Math.min(rows.length - 1, r + 1);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
else if (e.key === "ArrowRight")
|
|
335
|
-
|
|
333
|
+
// The arrows follow what the user SEES: under RTL the first column renders
|
|
334
|
+
// rightmost, so ArrowLeft steps forward and ArrowRight steps back (#255).
|
|
335
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
|
336
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
337
|
+
const forward = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
338
|
+
nc = forward ? Math.min(columns.length - 1, c + 1) : Math.max(0, c - 1);
|
|
339
|
+
}
|
|
336
340
|
else if (e.key === "Escape") {
|
|
337
341
|
setSelCell(null);
|
|
338
342
|
return;
|
|
@@ -390,6 +394,11 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
390
394
|
key: col.key,
|
|
391
395
|
startX: e.clientX,
|
|
392
396
|
startW: colWidth(col),
|
|
397
|
+
// The columns reverse under RTL, so the grip — anchored at
|
|
398
|
+
// `inset-inline-end` — lands on the column's LEFT edge there, and
|
|
399
|
+
// pulling it further left must GROW the column. `clientX` stays
|
|
400
|
+
// physical, so the sign is the other half of that conversion (#214).
|
|
401
|
+
dir: isRtlElement(e.currentTarget) ? -1 : 1,
|
|
393
402
|
};
|
|
394
403
|
setResizingKey(col.key);
|
|
395
404
|
}
|
|
@@ -397,7 +406,7 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
397
406
|
const r = resizeRef.current;
|
|
398
407
|
if (!r)
|
|
399
408
|
return;
|
|
400
|
-
const w = Math.max(64, r.startW + (e.clientX - r.startX));
|
|
409
|
+
const w = Math.max(64, r.startW + r.dir * (e.clientX - r.startX));
|
|
401
410
|
setWidths((prev) => ({ ...prev, [r.key]: w }));
|
|
402
411
|
}
|
|
403
412
|
function endResize(e) {
|
|
@@ -70,8 +70,8 @@ const config = defineRecipe({
|
|
|
70
70
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
71
71
|
& table { table-layout: fixed; }
|
|
72
72
|
/* column resize grip */
|
|
73
|
-
& .datatable-resize { position: absolute; top: 0;
|
|
74
|
-
& .datatable-resize::after { content: ""; position: absolute; top: 0;
|
|
73
|
+
& .datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
74
|
+
& .datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: ${c.bgBrandSolid}; opacity: 0; transition: opacity .12s; }
|
|
75
75
|
& .datatable-resize:hover::after, & .datatable-resize[data-resizing]::after { opacity: 1; }
|
|
76
76
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
77
77
|
& .datatable-entity { display: flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; color: ${c.fgPrimary}; font-weight: ${c.weight.medium}; }
|
|
@@ -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"]);
|
|
@@ -681,6 +681,36 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
681
681
|
const setSortField = (i, key) => setSorts(sorts.map((s, j) => (j === i ? { ...s, key } : s)));
|
|
682
682
|
const setSortDir = (i, dir) => setSorts(sorts.map((s, j) => (j === i ? { ...s, dir } : s)));
|
|
683
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]);
|
|
684
714
|
const commitEdit = (id, key, raw) => {
|
|
685
715
|
const col = cols.find((c) => c.key === key);
|
|
686
716
|
const numeric = col?.type === "number" || col?.type === "currency";
|
|
@@ -1029,7 +1059,7 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
1029
1059
|
// row instead of a bare "182,000", and the same header is
|
|
1030
1060
|
// repeated in every cell's context (#231).
|
|
1031
1061
|
const Cell = (i === 0 ? "th" : "td");
|
|
1032
|
-
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
|
|
1033
1063
|
? ""
|
|
1034
1064
|
: undefined, onClick: () => {
|
|
1035
1065
|
if (isPicker)
|
package/dist/styles.css
CHANGED
|
@@ -522,8 +522,8 @@
|
|
|
522
522
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
523
523
|
& table { table-layout: fixed; }
|
|
524
524
|
/* column resize grip */
|
|
525
|
-
& .datatable-resize { position: absolute; top: 0;
|
|
526
|
-
& .datatable-resize::after { content: ""; position: absolute; top: 0;
|
|
525
|
+
& .datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
526
|
+
& .datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--bg-brand-solid); opacity: 0; transition: opacity .12s; }
|
|
527
527
|
& .datatable-resize:hover::after, & .datatable-resize[data-resizing]::after { opacity: 1; }
|
|
528
528
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
529
529
|
& .datatable-entity { display: flex; align-items: center; gap: var(--space-1-5); min-width: 0; color: var(--fg-primary); font-weight: var(--weight-medium); }
|