@crisp-ui-kit/crisp 0.54.11 → 0.55.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/cjs/components/datatable/datatable.js +130 -38
- package/dist/cjs/components/resizablepanel/resizablepanel.js +10 -1
- package/dist/cjs/components/table/table.js +8 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +110 -27
- package/dist/cjs/index.js +0 -2
- package/dist/cjs/product/recordpage/index.js +6 -1
- package/dist/cjs/product/recordpage/recordpage.js +20 -4
- package/dist/esm/components/datatable/datatable.js +131 -39
- package/dist/esm/components/resizablepanel/resizablepanel.js +10 -1
- package/dist/esm/components/table/table.js +8 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +111 -28
- package/dist/esm/index.js +0 -2
- package/dist/esm/product/recordpage/index.js +2 -0
- package/dist/esm/product/recordpage/recordpage.js +20 -4
- package/dist/styles.css +0 -272
- package/dist/styles.layered.css +0 -272
- package/dist/types/components/table/table.d.ts +14 -0
- package/dist/types/index.d.ts +0 -2
- package/dist/types/product/recordpage/index.d.ts +2 -0
- package/package.json +2 -12
- package/dist/cjs/product/recordstable/company-logos.js +0 -29
- package/dist/cjs/product/recordstable/index.js +0 -13
- package/dist/cjs/product/recordstable/recordstable.js +0 -1362
- package/dist/cjs/product/recordstable/recordstable.recipe.js +0 -175
- package/dist/cjs/product/recordstable/recordstable.tag.js +0 -26
- package/dist/cjs/product/taskstable/index.js +0 -7
- package/dist/cjs/product/taskstable/taskstable.js +0 -32
- package/dist/cjs/product/taskstable/taskstable.recipe.js +0 -56
- package/dist/esm/product/recordstable/company-logos.js +0 -26
- package/dist/esm/product/recordstable/index.js +0 -3
- package/dist/esm/product/recordstable/recordstable.js +0 -1322
- package/dist/esm/product/recordstable/recordstable.recipe.js +0 -172
- package/dist/esm/product/recordstable/recordstable.tag.js +0 -23
- package/dist/esm/product/taskstable/index.js +0 -2
- package/dist/esm/product/taskstable/taskstable.js +0 -29
- package/dist/esm/product/taskstable/taskstable.recipe.js +0 -53
- package/dist/recordstable.css +0 -240
- package/dist/recordstable.d.ts +0 -7
- package/dist/recordstable.layered.css +0 -242
- package/dist/taskstable.css +0 -30
- package/dist/taskstable.d.ts +0 -7
- package/dist/taskstable.layered.css +0 -32
- package/dist/types/product/recordstable/company-logos.d.ts +0 -1
- package/dist/types/product/recordstable/index.d.ts +0 -3
- package/dist/types/product/recordstable/recordstable.d.ts +0 -294
- package/dist/types/product/recordstable/recordstable.recipe.d.ts +0 -5
- package/dist/types/product/recordstable/recordstable.tag.d.ts +0 -15
- package/dist/types/product/taskstable/index.d.ts +0 -2
- package/dist/types/product/taskstable/taskstable.d.ts +0 -59
- package/dist/types/product/taskstable/taskstable.recipe.d.ts +0 -3
|
@@ -50,6 +50,20 @@ const core_1 = require("../../core");
|
|
|
50
50
|
const column_1 = require("./column");
|
|
51
51
|
const editable_field_1 = require("./editable-field");
|
|
52
52
|
const recordpage_recipe_1 = require("./recordpage.recipe");
|
|
53
|
+
// crisp/#798: the details column's own drag clamp -- re-measured live on
|
|
54
|
+
// Attio's own record page (a real drag test, not a source read): min 619px,
|
|
55
|
+
// max 1170px, default (also the double-click-reset target) 734px. These
|
|
56
|
+
// replace an earlier, much narrower 300/560/320 that was never actually
|
|
57
|
+
// checked against Attio and turned out roughly half the real max. Confirmed
|
|
58
|
+
// live these are literal px, not a viewport ratio: at a narrower emulated
|
|
59
|
+
// width the already-dragged-to-max column shrank below 1170px, but its OWN
|
|
60
|
+
// `getComputedStyle(...).width` still read a plain px number the whole
|
|
61
|
+
// time (not a `%`) — that shrink is ordinary flex-shrink (both panels
|
|
62
|
+
// share `flex: 1 1 auto` and can't both keep their preferred width once the
|
|
63
|
+
// window is too narrow), not the clamp itself changing with viewport width.
|
|
64
|
+
const DETAILS_MIN_W = 619;
|
|
65
|
+
const DETAILS_MAX_W = 1170;
|
|
66
|
+
const DETAILS_DEFAULT_W = 734;
|
|
53
67
|
function valueEmpty(v) {
|
|
54
68
|
return v == null || v === "" || (Array.isArray(v) && v.length === 0);
|
|
55
69
|
}
|
|
@@ -457,7 +471,7 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
457
471
|
const tasks = tasksProp ?? internalTasks;
|
|
458
472
|
const [showAllActivity, setShowAllActivity] = React.useState(false);
|
|
459
473
|
// Drag-resizable left details column.
|
|
460
|
-
const [detailsW, setDetailsW] = React.useState(
|
|
474
|
+
const [detailsW, setDetailsW] = React.useState(DETAILS_DEFAULT_W);
|
|
461
475
|
const [resizing, setResizing] = React.useState(false);
|
|
462
476
|
const dragRef = React.useRef(null);
|
|
463
477
|
const onResizeDown = (e) => {
|
|
@@ -475,7 +489,7 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
475
489
|
if (!dragRef.current)
|
|
476
490
|
return;
|
|
477
491
|
const next = dragRef.current.w + dragRef.current.dir * (e.clientX - dragRef.current.x);
|
|
478
|
-
setDetailsW(Math.max(
|
|
492
|
+
setDetailsW(Math.max(DETAILS_MIN_W, Math.min(DETAILS_MAX_W, next)));
|
|
479
493
|
};
|
|
480
494
|
const onResizeUp = (e) => {
|
|
481
495
|
dragRef.current = null;
|
|
@@ -660,7 +674,7 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
660
674
|
const effectivePanel = tabsProp
|
|
661
675
|
? (active) => tabsProp.find((tb) => tb.id === active)?.render()
|
|
662
676
|
: builtinPanel;
|
|
663
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordpage_recipe_1.recordpageRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-topbar", children: [(0, jsx_runtime_1.jsxs)("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-name", children: name })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-actions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Minimize2, { size: 15, strokeWidth: 1.75 })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Maximize2, { size: 15, strokeWidth: 1.75 })) })), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.recordActions, trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), shortcut: "\u2318C", children: t.copyRecordLink }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), children: t.addToList }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, shortcut: "\u232B", children: t.deleteRecord })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-body", children: [(0, jsx_runtime_1.jsxs)("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? ((0, jsx_runtime_1.jsx)("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-entity-name", children: name }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "data-active": favorite ? "" : undefined, "aria-pressed": favorite, onClick: () => setFavorite((f) => !f), "aria-label": favorite ? t.removeFromFavourites : t.addToFavourites, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-quickactions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-compose", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.copyRecordLink, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.logANote, children: (0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.linkRecords, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Share2, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.createATask, children: (0, jsx_runtime_1.jsx)(lucide_react_1.SquareCheck, { size: 15, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsx)(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-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: "crisp-recordpage-emptystate", children: t.noLists })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin":
|
|
677
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordpage_recipe_1.recordpageRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-topbar", children: [(0, jsx_runtime_1.jsxs)("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-name", children: name })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-actions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Minimize2, { size: 15, strokeWidth: 1.75 })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Maximize2, { size: 15, strokeWidth: 1.75 })) })), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.recordActions, trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), shortcut: "\u2318C", children: t.copyRecordLink }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), children: t.addToList }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, shortcut: "\u232B", children: t.deleteRecord })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-body", children: [(0, jsx_runtime_1.jsxs)("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? ((0, jsx_runtime_1.jsx)("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-entity-name", children: name }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "data-active": favorite ? "" : undefined, "aria-pressed": favorite, onClick: () => setFavorite((f) => !f), "aria-label": favorite ? t.removeFromFavourites : t.addToFavourites, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-quickactions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-compose", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.copyRecordLink, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.logANote, children: (0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.linkRecords, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Share2, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.createATask, children: (0, jsx_runtime_1.jsx)(lucide_react_1.SquareCheck, { size: 15, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsx)(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-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: "crisp-recordpage-emptystate", children: t.noLists })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": DETAILS_MIN_W, "aria-valuemax": DETAILS_MAX_W, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
664
678
|
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
665
679
|
return;
|
|
666
680
|
// The pointer drag learned its direction in #212; this did not.
|
|
@@ -669,7 +683,9 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
669
683
|
// half-conversion the drag had, one handler away (#255).
|
|
670
684
|
const rtl = (0, core_1.isRtlElement)(e.currentTarget);
|
|
671
685
|
const grow = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
672
|
-
setDetailsW((w) => grow
|
|
686
|
+
setDetailsW((w) => grow
|
|
687
|
+
? Math.min(DETAILS_MAX_W, w + 16)
|
|
688
|
+
: Math.max(DETAILS_MIN_W, w - 16));
|
|
673
689
|
} }), (0, jsx_runtime_1.jsx)("main", { className: "crisp-recordpage-main", children: (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { tabs: effectiveTabs, defaultValue: tabsProp ? defaultTab : "overview", "aria-label": `${name} record`, children: effectivePanel }) })] })] }));
|
|
674
690
|
}
|
|
675
691
|
RecordPage.displayName = "RecordPage";
|
|
@@ -1,7 +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, CircleUserRound, ClipboardPaste, Copy, GripVertical, History, Lock, PanelRight, Plus, Search, SlidersHorizontal, X, } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { cellDatePrecision, formatCellDate, isRtlElement, parseCellDate, useCrispMessage, useCrispMessages, } from "../../core/index.js";
|
|
4
|
+
import { cellDatePrecision, formatCellDate, isRtlElement, parseCellDate, useCrispMessage, useCrispMessages, usePrefersReducedMotion, } from "../../core/index.js";
|
|
5
5
|
// crisp/#601 supersession: a deliberate exception to "DS atoms stay
|
|
6
6
|
// generic; product components compose them" (AUTHORING.md) -- the row's
|
|
7
7
|
// own comment affordance is `CommentCountBadge` (product/comments, #625),
|
|
@@ -1033,14 +1033,48 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1033
1033
|
const colAriaIndex = (i) => ariaColIndexBase + i + 1;
|
|
1034
1034
|
const allSelected = rows.length > 0 && rows.every((r) => selected.has(r.id));
|
|
1035
1035
|
const someSelected = selected.size > 0 && !allSelected;
|
|
1036
|
+
// crisp/#782, ported from RecordsTable's own already-shipped logic: the
|
|
1037
|
+
// header checkbox speaks for the rows you can SEE. Rebuilding the whole
|
|
1038
|
+
// set from `rows` (the filtered/visible set) would silently drop any
|
|
1039
|
+
// selection an active filter is hiding -- add/remove only the visible
|
|
1040
|
+
// rows and leave the rest of `selected` alone.
|
|
1036
1041
|
function toggleAll() {
|
|
1037
|
-
|
|
1042
|
+
const next = new Set(selected);
|
|
1043
|
+
for (const r of rows) {
|
|
1044
|
+
if (allSelected)
|
|
1045
|
+
next.delete(r.id);
|
|
1046
|
+
else
|
|
1047
|
+
next.add(r.id);
|
|
1048
|
+
}
|
|
1049
|
+
setSelected(next);
|
|
1038
1050
|
}
|
|
1039
1051
|
function toggleRow(id) {
|
|
1040
1052
|
const next = new Set(selected);
|
|
1041
1053
|
next.has(id) ? next.delete(id) : next.add(id);
|
|
1042
1054
|
setSelected(next);
|
|
1043
1055
|
}
|
|
1056
|
+
// crisp/#781: Shift+Click on a row checkbox selects the whole contiguous
|
|
1057
|
+
// range from the last plain click (the anchor) to this row -- confirmed
|
|
1058
|
+
// live, the mouse counterpart to #749's Shift+Arrow keyboard gesture,
|
|
1059
|
+
// sharing the same `rowSelectAnchor` state and the same recompute-the-
|
|
1060
|
+
// whole-span shape (not accumulate) so shift-clicking back toward the
|
|
1061
|
+
// anchor shrinks the same way clicking away from it grows. A plain click
|
|
1062
|
+
// both toggles normally AND re-anchors, so the NEXT Shift+Click extends
|
|
1063
|
+
// from wherever the user last plain-clicked, matching Attio.
|
|
1064
|
+
function toggleRowOrExtend(id, shiftKey) {
|
|
1065
|
+
if (shiftKey && rowSelectAnchor != null) {
|
|
1066
|
+
const anchorIdx = rows.findIndex((r) => r.id === rowSelectAnchor);
|
|
1067
|
+
const idx = rows.findIndex((r) => r.id === id);
|
|
1068
|
+
if (anchorIdx >= 0 && idx >= 0) {
|
|
1069
|
+
const lo = Math.min(anchorIdx, idx);
|
|
1070
|
+
const hi = Math.max(anchorIdx, idx);
|
|
1071
|
+
setSelected(new Set(rows.slice(lo, hi + 1).map((r) => r.id)));
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
setRowSelectAnchor(id);
|
|
1076
|
+
toggleRow(id);
|
|
1077
|
+
}
|
|
1044
1078
|
function cycleSort(key) {
|
|
1045
1079
|
// Clicking a header drives a single sort, as before. Multi-sort is supplied
|
|
1046
1080
|
// by the caller through the `sort` prop (built in a sort popover).
|
|
@@ -1247,6 +1281,28 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1247
1281
|
setSelected(new Set(rows.map((rw) => rw.id)));
|
|
1248
1282
|
return;
|
|
1249
1283
|
}
|
|
1284
|
+
// crisp/#772: Cmd/Ctrl+ArrowUp jumps the selected cell to the first row,
|
|
1285
|
+
// Cmd/Ctrl+ArrowDown to the last -- confirmed live (Attio's own help
|
|
1286
|
+
// drawer documents "Jump to top"/"Jump to bottom" on these exact
|
|
1287
|
+
// bindings). `!e.shiftKey` guards this from stealing Cmd+Shift+Arrow,
|
|
1288
|
+
// a separate gesture (#773, extend-selection-to-first/last) that falls
|
|
1289
|
+
// through to the Shift+Arrow branch below untouched. Placed before that
|
|
1290
|
+
// branch for the same reason: without the guard, Cmd+Shift+Arrow would
|
|
1291
|
+
// still reach it (that branch doesn't check metaKey/ctrlKey either),
|
|
1292
|
+
// just not do the "jump" part -- the guard here is what keeps the two
|
|
1293
|
+
// gestures from fighting over the same keys.
|
|
1294
|
+
if ((e.metaKey || e.ctrlKey) &&
|
|
1295
|
+
!e.shiftKey &&
|
|
1296
|
+
(e.key === "ArrowUp" || e.key === "ArrowDown")) {
|
|
1297
|
+
const r0 = rows.findIndex((row) => row.id === selCell.id);
|
|
1298
|
+
if (r0 < 0)
|
|
1299
|
+
return;
|
|
1300
|
+
e.preventDefault();
|
|
1301
|
+
const nr0 = e.key === "ArrowUp" ? 0 : rows.length - 1;
|
|
1302
|
+
setSelCell({ id: rows[nr0].id, key: selCell.key });
|
|
1303
|
+
clipboardProxyRef.current?.select();
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1250
1306
|
// crisp/#749: Shift+ArrowUp/Down extends the row-checkbox selection --
|
|
1251
1307
|
// confirmed live, distinct from Shift+Click's cell-range gesture above
|
|
1252
1308
|
// (that one is `selAnchor`; this is `rowSelectAnchor`, and the two
|
|
@@ -1398,15 +1454,21 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1398
1454
|
}
|
|
1399
1455
|
setEditing(null);
|
|
1400
1456
|
}
|
|
1457
|
+
const reduceMotion = usePrefersReducedMotion();
|
|
1401
1458
|
// crisp/#577 -- keyboard nav to an off-screen (virtualized-out) cell
|
|
1402
|
-
// scrolls it into the rendered window, on both axes.
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1405
|
-
//
|
|
1406
|
-
//
|
|
1407
|
-
//
|
|
1408
|
-
//
|
|
1409
|
-
//
|
|
1459
|
+
// scrolls it into the rendered window, on both axes. `renderItems`/
|
|
1460
|
+
// `rowOffsets` are computed unconditionally (`height == null` just makes
|
|
1461
|
+
// them a no-op elsewhere, see the comment above `renderItems`), so this
|
|
1462
|
+
// runs on both axes regardless of `height` too (crisp/#780 -- it used to
|
|
1463
|
+
// gate the vertical half on `height != null`, on the reasoning that only
|
|
1464
|
+
// `height` gives the grid an internal scroll position to move; that's
|
|
1465
|
+
// wrong when the grid's own container still scrolls without an explicit
|
|
1466
|
+
// height, e.g. inside a shorter ancestor, matching `RecordsTable`'s own
|
|
1467
|
+
// already-shipped #257 fix, which had no such gate). `widths` is read only
|
|
1468
|
+
// indirectly, through `colWidth`'s closure over it -- biome can't see
|
|
1469
|
+
// that, but a column resize while a cell is selected genuinely needs to
|
|
1470
|
+
// re-run this to keep it in view, so the dependency is real even though
|
|
1471
|
+
// the lint rule can't prove it.
|
|
1410
1472
|
// biome-ignore lint/correctness/useExhaustiveDependencies: widths is read via colWidth's closure, see comment above
|
|
1411
1473
|
React.useEffect(() => {
|
|
1412
1474
|
if (!selCell)
|
|
@@ -1414,28 +1476,37 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1414
1476
|
const el = scrollRef.current;
|
|
1415
1477
|
if (!el)
|
|
1416
1478
|
return;
|
|
1417
|
-
//
|
|
1418
|
-
//
|
|
1419
|
-
//
|
|
1420
|
-
//
|
|
1421
|
-
//
|
|
1479
|
+
// `el.scrollTo` rather than a direct `el.scrollTop =`/`el.scrollLeft =`
|
|
1480
|
+
// assignment (crisp/#780, matching RecordsTable's own #208/#257 rule):
|
|
1481
|
+
// property assignment is ALWAYS instant, so it can never honour a
|
|
1482
|
+
// consumer's own `scroll-behavior: smooth` on this container; `scrollTo`
|
|
1483
|
+
// defers to that CSS when no `behavior` is given, and only forces
|
|
1484
|
+
// `"auto"` under `prefers-reduced-motion`, which is the one case that
|
|
1485
|
+
// must win even over a host page's own smooth-scroll opt-in.
|
|
1486
|
+
const scrollTo = (opts) => el.scrollTo({
|
|
1487
|
+
...opts,
|
|
1488
|
+
...(reduceMotion ? { behavior: "auto" } : null),
|
|
1489
|
+
});
|
|
1490
|
+
// Sets `viewport` directly from the SAME value just passed to
|
|
1491
|
+
// `scrollTo`, rather than waiting for a follow-up `scroll` event to
|
|
1492
|
+
// round-trip through the `onScroll` handler above -- a real browser
|
|
1493
|
+
// does fire one when the scroll position changes programmatically, but
|
|
1494
|
+
// relying on that would make this effect's own visible result (the
|
|
1422
1495
|
// newly-selected cell actually rendering) depend on a second render
|
|
1423
1496
|
// triggered from outside React's own update, which is both slower and
|
|
1424
1497
|
// one more thing that has to be true for this to work.
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
setViewport((v) => ({ ...v, top: newTop }));
|
|
1438
|
-
}
|
|
1498
|
+
const itemIndex = rowIdToItemIndex.get(selCell.id);
|
|
1499
|
+
if (itemIndex != null) {
|
|
1500
|
+
const itemTop = rowOffsets[itemIndex];
|
|
1501
|
+
const itemBottom = itemTop + renderItems[itemIndex].height;
|
|
1502
|
+
if (itemTop < el.scrollTop) {
|
|
1503
|
+
scrollTo({ top: itemTop });
|
|
1504
|
+
setViewport((v) => ({ ...v, top: itemTop }));
|
|
1505
|
+
}
|
|
1506
|
+
else if (itemBottom > el.scrollTop + el.clientHeight) {
|
|
1507
|
+
const newTop = itemBottom - el.clientHeight;
|
|
1508
|
+
scrollTo({ top: newTop });
|
|
1509
|
+
setViewport((v) => ({ ...v, top: newTop }));
|
|
1439
1510
|
}
|
|
1440
1511
|
}
|
|
1441
1512
|
const ci = columns.findIndex((c) => c.key === selCell.key);
|
|
@@ -1445,24 +1516,24 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1445
1516
|
left += colWidth(columns[i]);
|
|
1446
1517
|
const right = left + colWidth(columns[ci]);
|
|
1447
1518
|
if (left < el.scrollLeft) {
|
|
1448
|
-
|
|
1519
|
+
scrollTo({ left });
|
|
1449
1520
|
setViewport((v) => ({ ...v, left }));
|
|
1450
1521
|
}
|
|
1451
1522
|
else if (right > el.scrollLeft + el.clientWidth) {
|
|
1452
1523
|
const newLeft = right - el.clientWidth;
|
|
1453
|
-
|
|
1524
|
+
scrollTo({ left: newLeft });
|
|
1454
1525
|
setViewport((v) => ({ ...v, left: newLeft }));
|
|
1455
1526
|
}
|
|
1456
1527
|
}
|
|
1457
1528
|
}, [
|
|
1458
1529
|
selCell,
|
|
1459
|
-
height,
|
|
1460
1530
|
columns,
|
|
1461
1531
|
widths,
|
|
1462
1532
|
selectable,
|
|
1463
1533
|
rowIdToItemIndex,
|
|
1464
1534
|
rowOffsets,
|
|
1465
1535
|
renderItems,
|
|
1536
|
+
reduceMotion,
|
|
1466
1537
|
]);
|
|
1467
1538
|
const totalCols = columns.length + (selectable ? 1 : 0) + (showGrip ? 1 : 0);
|
|
1468
1539
|
const hasSummary = columns.some((c) => c.summary || c.calc);
|
|
@@ -1836,12 +1907,33 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1836
1907
|
}, onDragEnd: () => {
|
|
1837
1908
|
setDragRowId(null);
|
|
1838
1909
|
setDropRowId(null);
|
|
1839
|
-
}, children: _jsx(GripVertical, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }) })), selectable && (_jsx(TableCell, { className: "crisp-datatable-col-check", "aria-colindex": showGrip ? 2 : 1, ...stickyCol(-1), onClick: (e) => e.stopPropagation(), children: _jsx("span", { className: "crisp-datatable-check", children: _jsx(Checkbox, { "aria-label": selectRowLabel(row), checked: selected.has(row.id),
|
|
1910
|
+
}, children: _jsx(GripVertical, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }) })), selectable && (_jsx(TableCell, { className: "crisp-datatable-col-check", "aria-colindex": showGrip ? 2 : 1, ...stickyCol(-1), onClick: (e) => e.stopPropagation(), children: _jsx("span", { className: "crisp-datatable-check", children: _jsx(Checkbox, { "aria-label": selectRowLabel(row), checked: selected.has(row.id),
|
|
1911
|
+
// preventDefault stops the native check/change
|
|
1912
|
+
// this click would otherwise also fire -- this
|
|
1913
|
+
// handler is the single source of truth so a
|
|
1914
|
+
// Shift+Click's range-select and a plain
|
|
1915
|
+
// click's toggle-and-re-anchor never race each
|
|
1916
|
+
// other (crisp/#781).
|
|
1917
|
+
onClick: (e) => {
|
|
1918
|
+
e.preventDefault();
|
|
1919
|
+
toggleRowOrExtend(row.id, e.shiftKey);
|
|
1920
|
+
}, onChange: () => { } }) }) })), colSlots.map((slot) => {
|
|
1840
1921
|
if (slot.type === "spacer") {
|
|
1841
1922
|
return (_jsx("td", { role: "presentation", "aria-hidden": "true", style: { padding: 0, border: "none" } }, `__spacer_${slot.side}`));
|
|
1842
1923
|
}
|
|
1843
1924
|
const col = columns[slot.i];
|
|
1844
1925
|
const i = slot.i;
|
|
1926
|
+
// crisp/#779, matching RecordsTable's own #231 fix:
|
|
1927
|
+
// column 0 is the row's own identity (its name), not
|
|
1928
|
+
// a value it holds -- a screen reader crossing the
|
|
1929
|
+
// row should read "Google, Employees, 182,000", not a
|
|
1930
|
+
// bare "182,000". Spread onto every TableCell this
|
|
1931
|
+
// column can render as (every editor branch, not just
|
|
1932
|
+
// the default one), since the row-identity semantics
|
|
1933
|
+
// don't depend on which editor is open.
|
|
1934
|
+
const rowHeaderProps = i === 0
|
|
1935
|
+
? { as: "th", scope: "row" }
|
|
1936
|
+
: undefined;
|
|
1845
1937
|
const isEditing = editing?.id === row.id && editing?.key === col.key;
|
|
1846
1938
|
const value = cellValue(col, row);
|
|
1847
1939
|
const sticky = dataStickyCol(i);
|
|
@@ -1864,14 +1956,14 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1864
1956
|
// right). See the `isEditing` block below for their
|
|
1865
1957
|
// actual editor.
|
|
1866
1958
|
if (col.editable && col.editor === "switch") {
|
|
1867
|
-
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(Switch, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commitEdit(row.id, col.key, String(checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1959
|
+
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(Switch, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onCheckedChange: (checked) => commitEdit(row.id, col.key, String(checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1868
1960
|
}
|
|
1869
1961
|
// crisp/#760: `editor: "rating"` is the same always-
|
|
1870
1962
|
// live shape as `switch` above -- clicking a star IS
|
|
1871
1963
|
// the commit gesture, confirmed live.
|
|
1872
1964
|
if (col.editable && col.editor === "rating") {
|
|
1873
1965
|
const n = typeof value === "number" ? value : Number(value);
|
|
1874
|
-
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(RatingStars, { "aria-label": `Edit ${col.key}`, value: Number.isNaN(n) ? 0 : n, max: col.maxRating ?? 5, onChange: (next) => commitEdit(row.id, col.key, String(next)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1966
|
+
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(RatingStars, { "aria-label": `Edit ${col.key}`, value: Number.isNaN(n) ? 0 : n, max: col.maxRating ?? 5, onChange: (next) => commitEdit(row.id, col.key, String(next)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1875
1967
|
}
|
|
1876
1968
|
// crisp/#761: `editor: "checkbox"` is a THIRD always-
|
|
1877
1969
|
// live boolean shape, distinct from `switch` --
|
|
@@ -1880,13 +1972,13 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1880
1972
|
// toggle. Reuses crisp's own `Checkbox` (already the
|
|
1881
1973
|
// measured 16px/r6/brand-fill grid checkbox).
|
|
1882
1974
|
if (col.editable && col.editor === "checkbox") {
|
|
1883
|
-
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(Checkbox, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onChange: (e) => commitEdit(row.id, col.key, String(e.currentTarget.checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1975
|
+
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined, children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [_jsx(Checkbox, { "aria-label": `Edit ${col.key}`, checked: value === true || value === "true", disabled: isPending, onChange: (e) => commitEdit(row.id, col.key, String(e.currentTarget.checked)) }), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1884
1976
|
}
|
|
1885
1977
|
if (isEditing &&
|
|
1886
1978
|
(col.editor === "select" ||
|
|
1887
1979
|
col.editor === "reference" ||
|
|
1888
1980
|
col.editor === "actor")) {
|
|
1889
|
-
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined,
|
|
1981
|
+
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, className: "crisp-datatable-td--picker", "data-pending": isPending ? "" : undefined,
|
|
1890
1982
|
// Same as the plain text editor's own cell:
|
|
1891
1983
|
// opening/using the picker must not also fire
|
|
1892
1984
|
// the row's onRowClick. Escape is handled by
|
|
@@ -1900,7 +1992,7 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1900
1992
|
onClick: (e) => e.stopPropagation(), children: [_jsxs("span", { className: "crisp-datatable-edit-picker", children: [col.editor === "select" ? (_jsx(DataSelectEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })) : (_jsx(DataReferenceEditor, { col: col, value: value, isPending: isPending, onCommit: (v) => commitEdit(row.id, col.key, v), onCancel: () => setEditing(null) })), isPending && (_jsx(Spinner, { size: 14, className: "crisp-datatable-edit-pending" }))] }), errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere }))] }, col.key));
|
|
1901
1993
|
}
|
|
1902
1994
|
if (isEditing) {
|
|
1903
|
-
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, onClick: (e) => e.stopPropagation(), children: [_jsx("input", { className: "crisp-datatable-edit",
|
|
1995
|
+
return (_jsxs(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, onClick: (e) => e.stopPropagation(), children: [_jsx("input", { className: "crisp-datatable-edit",
|
|
1904
1996
|
// biome-ignore lint/a11y/noAutofocus: focus the field the moment it opens
|
|
1905
1997
|
autoFocus: true, ...editorFor(col), "data-editor": editorFor(col).type, disabled: isPending, defaultValue: col.type === "date"
|
|
1906
1998
|
? toDateInputValue(value)
|
|
@@ -1959,7 +2051,7 @@ export function DataTable({ columns: columnsProp, data, selectable = false, sele
|
|
|
1959
2051
|
const isExpanded = expandedCell?.id === row.id &&
|
|
1960
2052
|
expandedCell?.key === col.key;
|
|
1961
2053
|
const cellContent = (_jsxs(_Fragment, { children: [!isExpanded && rendered, errorHere && (_jsx("span", { className: "crisp-datatable-edit-error", children: errorHere })), isExpanded && (_jsx("div", { className: "crisp-datatable-cell-overlay", onClick: (e) => e.stopPropagation(), children: rendered }))] }));
|
|
1962
|
-
return (_jsx(ContextMenu, { asChild: true, variant: "dark", menuLabel: cellMenuLabel, items: cellMenuItems(row, col), children: _jsx(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, className: col.editable
|
|
2054
|
+
return (_jsx(ContextMenu, { asChild: true, variant: "dark", menuLabel: cellMenuLabel, items: cellMenuItems(row, col), children: _jsx(TableCell, { align: alignFor(col), "aria-colindex": colAriaIndex(i), ...sticky, ...rowHeaderProps, className: col.editable
|
|
1963
2055
|
? "crisp-datatable-editable"
|
|
1964
2056
|
: undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
|
|
1965
2057
|
? ""
|
|
@@ -122,6 +122,15 @@ export function ResizablePanel({ side = "left", size, defaultSize = 280, onSizeC
|
|
|
122
122
|
const finalWidth = rect ? Math.round(rect.width) : width;
|
|
123
123
|
commitSize(finalWidth);
|
|
124
124
|
}
|
|
125
|
+
// crisp/#795: double-click resets to `defaultSize`, confirmed live on
|
|
126
|
+
// Attio's own record-page divider (dragged 722px -> 924px, double-click
|
|
127
|
+
// snapped it back to exactly 722px -- its own default/initial width, not
|
|
128
|
+
// some other fixed value). Reuses `commitSize`, the same commit path a
|
|
129
|
+
// drag already uses, so persistence (`storageKey`) picks up the reset the
|
|
130
|
+
// same way it picks up a drag -- no separate reset-specific write.
|
|
131
|
+
function onHandleDoubleClick() {
|
|
132
|
+
commitSize(defaultSize);
|
|
133
|
+
}
|
|
125
134
|
const maxStyle = max == null ? undefined : typeof max === "number" ? `${max}px` : max;
|
|
126
135
|
const panelSizeStyle = {
|
|
127
136
|
width,
|
|
@@ -130,6 +139,6 @@ export function ResizablePanel({ side = "left", size, defaultSize = 280, onSizeC
|
|
|
130
139
|
};
|
|
131
140
|
return (_jsxs("div", { ref: containerRef, className: resizablepanelRecipe({ className }), ...rest, children: [_jsx("div", { className: "crisp-resizablepanel-spacer", style: floating ? { width: 0 } : { width, minWidth: min, maxWidth: maxStyle } }), _jsxs("div", { ref: panelRef, className: "crisp-resizablepanel-panel", "data-side": side, "data-floating": floating ? "true" : "false", style: panelSizeStyle, children: [_jsx("div", { className: "crisp-resizablepanel-inner", children: children }), _jsx("div", { className: handleVariant === "column"
|
|
132
141
|
? "crisp-resizablepanel-handle crisp-resizablepanel-handle--column"
|
|
133
|
-
: "crisp-resizablepanel-handle", "data-side": side, "data-dragging": dragging ? "true" : undefined, "data-handle": "", onPointerDown: onHandlePointerDown, onPointerMove: onHandlePointerMove, onPointerUp: onHandlePointerUp, children: handleVariant === "column" && (_jsx("span", { className: "crisp-resizablepanel-handle-inner", "aria-hidden": "true" })) })] })] }));
|
|
142
|
+
: "crisp-resizablepanel-handle", "data-side": side, "data-dragging": dragging ? "true" : undefined, "data-handle": "", onPointerDown: onHandlePointerDown, onPointerMove: onHandlePointerMove, onPointerUp: onHandlePointerUp, onDoubleClick: onHandleDoubleClick, children: handleVariant === "column" && (_jsx("span", { className: "crisp-resizablepanel-handle-inner", "aria-hidden": "true" })) })] })] }));
|
|
134
143
|
}
|
|
135
144
|
ResizablePanel.displayName = "ResizablePanel";
|
|
@@ -28,7 +28,14 @@ export const TableRow = React.forwardRef((props, ref) => _jsx("tr", { ref: ref,
|
|
|
28
28
|
TableRow.displayName = "TableRow";
|
|
29
29
|
export const TableHead = React.forwardRef(({ align, ...props }, ref) => _jsx("th", { ref: ref, "data-align": align, ...props }));
|
|
30
30
|
TableHead.displayName = "TableHead";
|
|
31
|
-
export const TableCell = React.forwardRef(({ align, ...props }, ref) =>
|
|
31
|
+
export const TableCell = React.forwardRef(({ align, as, ...props }, ref) => {
|
|
32
|
+
// Cast to "td", same as RecordsTable's own dynamic-tag precedent --
|
|
33
|
+
// `props` (which may carry `scope`) would otherwise need to satisfy
|
|
34
|
+
// both <td> and <th>'s JSX attribute types at once for a bare union
|
|
35
|
+
// element name.
|
|
36
|
+
const Cell = (as ?? "td");
|
|
37
|
+
return _jsx(Cell, { ref: ref, "data-align": align, ...props });
|
|
38
|
+
});
|
|
32
39
|
TableCell.displayName = "TableCell";
|
|
33
40
|
export const TableCaption = React.forwardRef((props, ref) => _jsx("caption", { ref: ref, ...props }));
|
|
34
41
|
TableCaption.displayName = "TableCaption";
|