@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
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { ArrowDownWideNarrow, ArrowUpDown, ArrowUpNarrowWide, Check, ChevronDown, ChevronLeft, ChevronRight, CircleHelp, Download, GripVertical, ListFilter, MoreVertical, Plus, SlidersHorizontal, Trash2, Upload, X, } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { isInsideLaterFloatingLayer, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
|
|
5
|
+
import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
|
|
6
6
|
import { DatePicker, } from "../datepicker/index.js";
|
|
7
7
|
import { MenuCheckboxItem, useMenuKeyboard } from "../menu/index.js";
|
|
8
8
|
import { Select } from "../select/index.js";
|
|
@@ -412,6 +412,12 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
412
412
|
React.useEffect(() => {
|
|
413
413
|
setImportExportTarget(importExportRef?.current ?? null);
|
|
414
414
|
}, [importExportRef]);
|
|
415
|
+
const rootRef = React.useRef(null);
|
|
416
|
+
const viewSettingsTriggerRef = React.useRef(null);
|
|
417
|
+
const viewSettingsContentRef = React.useRef(null);
|
|
418
|
+
const [viewSettingsPos, setViewSettingsPos] = React.useState({});
|
|
419
|
+
const importExportTriggerRef = React.useRef(null);
|
|
420
|
+
const [importExportPos, setImportExportPos] = React.useState({});
|
|
415
421
|
const [openMenu, setOpenMenu] = React.useState(null);
|
|
416
422
|
const [dragSort, setDragSort] = React.useState(null);
|
|
417
423
|
const [filterQuery, setFilterQuery] = React.useState("");
|
|
@@ -439,6 +445,65 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
439
445
|
// built here -- hiding an attribute is a trailing control instead, see
|
|
440
446
|
// the hide button below).
|
|
441
447
|
const [addingAttribute, setAddingAttribute] = React.useState(false);
|
|
448
|
+
// crisp/#807: `viewSettingsRef`/`importExportRef` (#766) relocate the
|
|
449
|
+
// TRIGGER out of this toolbar's own row, but their POPOVERS stayed
|
|
450
|
+
// `position: absolute` inside the (now relocated) trigger's own
|
|
451
|
+
// `.crisp-tabletoolbar-toolgroup` -- correct chrome-wise (#772 already
|
|
452
|
+
// fixed that), but its stacking is trapped inside whatever DOM branch the
|
|
453
|
+
// consumer's own `viewSettingsRef` slot lives in. A consumer header
|
|
454
|
+
// sitting in an EARLIER sibling branch than the toolbar/grid below it
|
|
455
|
+
// (GRC's own layout) has that later branch simply paint over the popover
|
|
456
|
+
// regardless of its own z-index: 30, because z-index only orders siblings
|
|
457
|
+
// WITHIN the same stacking context, and the two branches aren't one.
|
|
458
|
+
// Fixed the same way `Menu`/`Select`/`Popover` already solve this: the
|
|
459
|
+
// popover content itself portals to `document.body` (`FloatingPortal`),
|
|
460
|
+
// positioned with `placeFloating` against the trigger's own rect (a real
|
|
461
|
+
// `position: fixed` box, anchored by viewport coordinates, escapes ANY
|
|
462
|
+
// ancestor's stacking context) -- not just relocated alongside the
|
|
463
|
+
// trigger. Wrapped in its own `tabletoolbarRecipe()` span (the same
|
|
464
|
+
// reason `ViewBarSlot` wraps the trigger) so every nested
|
|
465
|
+
// `.crisp-tabletoolbar-*` class inside the popover still resolves once
|
|
466
|
+
// portaled outside the toolbar's own root. Sort/Filter's own popovers
|
|
467
|
+
// are untouched -- they never relocate, so they were never trapped.
|
|
468
|
+
const placeViewSettingsMenu = () => {
|
|
469
|
+
const rect = viewSettingsTriggerRef.current?.getBoundingClientRect();
|
|
470
|
+
if (!rect)
|
|
471
|
+
return;
|
|
472
|
+
const { style } = placeFloating({
|
|
473
|
+
rect,
|
|
474
|
+
gap: 4,
|
|
475
|
+
flipBelow: 280,
|
|
476
|
+
anchor: viewSettingsTriggerRef.current,
|
|
477
|
+
});
|
|
478
|
+
setViewSettingsPos(style);
|
|
479
|
+
};
|
|
480
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: reposition each time it opens (and when its own screen changes, since the two screens differ in height)
|
|
481
|
+
React.useLayoutEffect(() => {
|
|
482
|
+
if (openMenu === "viewsettings")
|
|
483
|
+
placeViewSettingsMenu();
|
|
484
|
+
}, [openMenu, addingAttribute]);
|
|
485
|
+
React.useEffect(() => {
|
|
486
|
+
if (openMenu !== "viewsettings" || !viewSettingsContentRef.current)
|
|
487
|
+
return;
|
|
488
|
+
return registerFloatingLayer(viewSettingsContentRef.current);
|
|
489
|
+
}, [openMenu]);
|
|
490
|
+
const placeImportExportMenu = () => {
|
|
491
|
+
const rect = importExportTriggerRef.current?.getBoundingClientRect();
|
|
492
|
+
if (!rect)
|
|
493
|
+
return;
|
|
494
|
+
const { style } = placeFloating({
|
|
495
|
+
rect,
|
|
496
|
+
gap: 4,
|
|
497
|
+
flipBelow: 280,
|
|
498
|
+
anchor: importExportTriggerRef.current,
|
|
499
|
+
});
|
|
500
|
+
setImportExportPos(style);
|
|
501
|
+
};
|
|
502
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: reposition each time it opens
|
|
503
|
+
React.useLayoutEffect(() => {
|
|
504
|
+
if (openMenu === "importexport")
|
|
505
|
+
placeImportExportMenu();
|
|
506
|
+
}, [openMenu]);
|
|
442
507
|
// close any open menu on outside click (anything not inside a menu or its
|
|
443
508
|
// trigger) or Escape
|
|
444
509
|
React.useEffect(() => {
|
|
@@ -589,6 +654,13 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
589
654
|
close: () => setOpenMenu(null),
|
|
590
655
|
contentRef: importExportMenuRef,
|
|
591
656
|
});
|
|
657
|
+
// crisp/#807: registers the (now-portaled) Import/Export menu on the
|
|
658
|
+
// shared floating-layer stack, the same reason `Menu` itself does.
|
|
659
|
+
React.useEffect(() => {
|
|
660
|
+
if (openMenu !== "importexport" || !importExportMenuRef.current)
|
|
661
|
+
return;
|
|
662
|
+
return registerFloatingLayer(importExportMenuRef.current);
|
|
663
|
+
}, [openMenu]);
|
|
592
664
|
// #754: generalized from the Filter-only `filterFieldPicker` (#592) to
|
|
593
665
|
// also drive Sort's own field picker and View settings' add-attribute
|
|
594
666
|
// screen — all three offer the identical nested-drill-down construct on
|
|
@@ -661,7 +733,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
661
733
|
: sorts.length
|
|
662
734
|
? `${crispMessages["Sorted by"] ?? "Sorted by"} ${sortField}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
|
|
663
735
|
: sortLabel;
|
|
664
|
-
return (_jsx("div", { className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-tabletoolbar-toolbar", "data-inline": inline ? "" : undefined, children: [showSort && (_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : " crisp-tabletoolbar-tool--dashed"}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortTriggerLabel || !sorts.length ? (sortedLabel) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "crisp-tabletoolbar-sorted-prefix", children: crispMessages["Sorted by"] ?? "Sorted by" }), " ", _jsx("span", { className: "crisp-tabletoolbar-sorted-field", children: sortField }), sorts.length > 1 && (_jsxs("span", { className: "crisp-tabletoolbar-sorted-count", children: [" ", "+", sorts.length - 1] }))] }))] }), openMenu === "sort" && (_jsxs("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
|
|
736
|
+
return (_jsx("div", { ref: rootRef, className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-tabletoolbar-toolbar", "data-inline": inline ? "" : undefined, children: [showSort && (_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `crisp-tabletoolbar-tool${sorts.length ? " crisp-tabletoolbar-tool--active" : " crisp-tabletoolbar-tool--dashed"}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortTriggerLabel || !sorts.length ? (sortedLabel) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "crisp-tabletoolbar-sorted-prefix", children: crispMessages["Sorted by"] ?? "Sorted by" }), " ", _jsx("span", { className: "crisp-tabletoolbar-sorted-field", children: sortField }), sorts.length > 1 && (_jsxs("span", { className: "crisp-tabletoolbar-sorted-count", children: [" ", "+", sorts.length - 1] }))] }))] }), openMenu === "sort" && (_jsxs("div", { ref: sortListRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "crisp-tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
|
|
665
737
|
if (dragSort !== null)
|
|
666
738
|
e.preventDefault();
|
|
667
739
|
}, onDrop: () => {
|
|
@@ -797,7 +869,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
797
869
|
}, children: label }, label))) })] })) : (_jsx("input", { className: "crisp-tabletoolbar-menu-input", type: col?.type === "number" ? "number" : "text", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter value`,
|
|
798
870
|
// biome-ignore lint/a11y/noAutofocus: focus the value field
|
|
799
871
|
autoFocus: true, value: cond.value, onChange: (e) => patchFilter(cond.id, { value: e.target.value }) })) }))] }, cond.id));
|
|
800
|
-
}), showViewSettings && !viewSettingsTarget && (_jsx("span", { className: "crisp-tabletoolbar-divider", "aria-hidden": "true" })), showViewSettings && (_jsx(ViewBarSlot, { target: viewSettingsTarget, children: _jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: "crisp-tabletoolbar-tool crisp-tabletoolbar-tool--raised", onClick: () => {
|
|
872
|
+
}), showViewSettings && !viewSettingsTarget && (_jsx("span", { className: "crisp-tabletoolbar-divider", "aria-hidden": "true" })), showViewSettings && (_jsx(ViewBarSlot, { target: viewSettingsTarget, children: _jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { ref: viewSettingsTriggerRef, type: "button", className: "crisp-tabletoolbar-tool crisp-tabletoolbar-tool--raised", onClick: () => {
|
|
801
873
|
if (openMenu === "viewsettings") {
|
|
802
874
|
setOpenMenu(null);
|
|
803
875
|
}
|
|
@@ -807,33 +879,44 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
807
879
|
setFilterQuery("");
|
|
808
880
|
setOpenMenu("viewsettings");
|
|
809
881
|
}
|
|
810
|
-
}, "aria-haspopup": "dialog", "aria-expanded": openMenu === "viewsettings", "data-menu-trigger": true, children: [_jsx(SlidersHorizontal, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), viewSettingsLabel, _jsx(ChevronDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" })] }), openMenu === "viewsettings" &&
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
882
|
+
}, "aria-haspopup": "dialog", "aria-expanded": openMenu === "viewsettings", "data-menu-trigger": true, children: [_jsx(SlidersHorizontal, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), viewSettingsLabel, _jsx(ChevronDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" })] }), openMenu === "viewsettings" && (_jsx(FloatingPortal, { sourceRef: rootRef, children: _jsx("span", { className: tabletoolbarRecipe(), children: addingAttribute ? (_jsx("div", { ref: viewSettingsContentRef, className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": addAttributeToViewLabel, style: {
|
|
883
|
+
position: "fixed",
|
|
884
|
+
zIndex: 70,
|
|
885
|
+
...viewSettingsPos,
|
|
886
|
+
}, children: nestedFieldPicker(hiddenAttrs, showAttr, searchAttributesPlaceholder, searchAttributesPlaceholder) })) : (_jsxs("div", { ref: viewSettingsContentRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": viewSettingsLabel, style: {
|
|
887
|
+
position: "fixed",
|
|
888
|
+
zIndex: 70,
|
|
889
|
+
...viewSettingsPos,
|
|
890
|
+
}, children: [shownAttrs.map((col, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
|
|
891
|
+
if (dragAttr !== null)
|
|
892
|
+
e.preventDefault();
|
|
893
|
+
}, onDrop: () => {
|
|
894
|
+
if (dragAttr !== null)
|
|
895
|
+
moveAttr(dragAttr, i);
|
|
896
|
+
setDragAttr(null);
|
|
897
|
+
}, children: [_jsx("button", { type: "button", className: "crisp-tabletoolbar-sort-grip", "aria-label": reorderAttributeLabel, draggable: true, onDragStart: () => setDragAttr(i), onDragEnd: () => setDragAttr(null), children: _jsx(GripVertical, { size: 15, strokeWidth: 1.75 }) }), _jsxs("span", { className: "crisp-tabletoolbar-sort-optlabel crisp-tabletoolbar-sort-field", children: [col.icon ? (_jsx(col.icon, { size: 15, strokeWidth: 1.75 })) : null, col.label] }), _jsx("button", { type: "button", className: "crisp-tabletoolbar-sort-remove", onClick: () => hideAttr(col.key), "aria-label": hideAttributeLabel?.(col.label) ??
|
|
898
|
+
`${crispMessages.Hide ?? "Hide"} ${col.label} ${crispMessages["from view"] ?? "from view"}`, children: _jsx(X, { size: 15, strokeWidth: 1.75 }) })] }, col.key))), _jsx("div", { className: "crisp-tabletoolbar-sort-addrow", children: _jsxs("button", { type: "button", className: "crisp-tabletoolbar-sort-add", onClick: () => {
|
|
899
|
+
setAddingAttribute(true);
|
|
900
|
+
setFieldPickerPath([]);
|
|
901
|
+
setFilterQuery("");
|
|
902
|
+
}, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), addAttributeToViewLabel] }) })] })) }) }))] }) })), (onImportCsv ||
|
|
824
903
|
onExportCsv ||
|
|
825
904
|
onExportExcel ||
|
|
826
|
-
importExportHelpHref) && (_jsx(ViewBarSlot, { target: importExportTarget, children: _jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: "crisp-tabletoolbar-tool crisp-tabletoolbar-tool--raised", onClick: () => setOpenMenu(openMenu === "importexport" ? null : "importexport"), "aria-haspopup": "menu", "aria-expanded": openMenu === "importexport", "data-menu-trigger": true, children: [_jsx(Upload, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), importExportLabel, _jsx(ChevronDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" })] }), openMenu === "importexport" && (_jsxs("div", { ref: importExportMenuRef, className: "crisp-tabletoolbar-menu", role: "menu", "aria-label": importExportLabel, onKeyDown: onImportExportKeyDown,
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
905
|
+
importExportHelpHref) && (_jsx(ViewBarSlot, { target: importExportTarget, children: _jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("button", { ref: importExportTriggerRef, type: "button", className: "crisp-tabletoolbar-tool crisp-tabletoolbar-tool--raised", onClick: () => setOpenMenu(openMenu === "importexport" ? null : "importexport"), "aria-haspopup": "menu", "aria-expanded": openMenu === "importexport", "data-menu-trigger": true, children: [_jsx(Upload, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), importExportLabel, _jsx(ChevronDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" })] }), openMenu === "importexport" && (_jsx(FloatingPortal, { sourceRef: rootRef, children: _jsx("span", { className: tabletoolbarRecipe(), children: _jsxs("div", { ref: importExportMenuRef, className: "crisp-tabletoolbar-menu", role: "menu", "aria-label": importExportLabel, onKeyDown: onImportExportKeyDown, style: {
|
|
906
|
+
position: "fixed",
|
|
907
|
+
zIndex: 70,
|
|
908
|
+
...importExportPos,
|
|
909
|
+
}, children: [onImportCsv && (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", onClick: () => {
|
|
910
|
+
onImportCsv();
|
|
911
|
+
setOpenMenu(null);
|
|
912
|
+
}, children: [_jsx(Upload, { size: 15, strokeWidth: 1.75 }), importCsvLabel] })), onImportCsv && (onExportCsv || onExportExcel) && (_jsx("hr", { className: "crisp-tabletoolbar-menu-sep" })), onExportCsv && (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", onClick: () => {
|
|
913
|
+
onExportCsv();
|
|
914
|
+
setOpenMenu(null);
|
|
915
|
+
}, children: [_jsx(Download, { size: 15, strokeWidth: 1.75 }), exportCsvLabel] })), onExportExcel && (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", onClick: () => {
|
|
916
|
+
onExportExcel();
|
|
917
|
+
setOpenMenu(null);
|
|
918
|
+
}, children: [_jsx(Download, { size: 15, strokeWidth: 1.75 }), exportExcelLabel] })), importExportHelpHref &&
|
|
919
|
+
(onImportCsv || onExportCsv || onExportExcel) && (_jsx("hr", { className: "crisp-tabletoolbar-menu-sep" })), importExportHelpHref && (_jsxs("a", { className: "crisp-tabletoolbar-menu-item", role: "menuitem", href: importExportHelpHref, target: "_blank", rel: "noreferrer", children: [_jsx(CircleHelp, { size: 15, strokeWidth: 1.75 }), learnImportExportLabel] }))] }) }) }))] }) })), _jsx("span", { className: "crisp-tabletoolbar-spacer" }), count != null && (_jsx("span", { className: "crisp-tabletoolbar-count", children: count })), children] }) }));
|
|
837
920
|
}
|
|
838
921
|
/**
|
|
839
922
|
* The Sort/Filter chip's own chrome (`.crisp-tabletoolbar-tool`), standalone
|
package/dist/esm/index.js
CHANGED
|
@@ -86,7 +86,6 @@ export * from "./product/objectsettings/index.js";
|
|
|
86
86
|
export * from "./product/onboarding/index.js";
|
|
87
87
|
export * from "./product/recordpage/index.js";
|
|
88
88
|
export * from "./product/recordpanel/index.js";
|
|
89
|
-
export * from "./product/recordstable/index.js";
|
|
90
89
|
export * from "./product/richtext/index.js";
|
|
91
90
|
export * from "./product/savedviews/index.js";
|
|
92
91
|
export * from "./product/settingssection/index.js";
|
|
@@ -94,7 +93,6 @@ export * from "./product/settingsshell/index.js";
|
|
|
94
93
|
export * from "./product/sidebar/index.js";
|
|
95
94
|
export * from "./product/statlist/index.js";
|
|
96
95
|
export * from "./product/tasklist/index.js";
|
|
97
|
-
export * from "./product/taskstable/index.js";
|
|
98
96
|
export * from "./product/themepicker/index.js";
|
|
99
97
|
export * from "./product/trialbanner/index.js";
|
|
100
98
|
export * from "./product/viewtoggle/index.js";
|
|
@@ -12,6 +12,20 @@ import { cx, isRtlElement, useCrispComponentDefaults } from "../../core/index.js
|
|
|
12
12
|
import { tagTone } from "./column.js";
|
|
13
13
|
import { editableInputType, useEditableField } from "./editable-field.js";
|
|
14
14
|
import { recordpageRecipe } from "./recordpage.recipe.js";
|
|
15
|
+
// crisp/#798: the details column's own drag clamp -- re-measured live on
|
|
16
|
+
// Attio's own record page (a real drag test, not a source read): min 619px,
|
|
17
|
+
// max 1170px, default (also the double-click-reset target) 734px. These
|
|
18
|
+
// replace an earlier, much narrower 300/560/320 that was never actually
|
|
19
|
+
// checked against Attio and turned out roughly half the real max. Confirmed
|
|
20
|
+
// live these are literal px, not a viewport ratio: at a narrower emulated
|
|
21
|
+
// width the already-dragged-to-max column shrank below 1170px, but its OWN
|
|
22
|
+
// `getComputedStyle(...).width` still read a plain px number the whole
|
|
23
|
+
// time (not a `%`) — that shrink is ordinary flex-shrink (both panels
|
|
24
|
+
// share `flex: 1 1 auto` and can't both keep their preferred width once the
|
|
25
|
+
// window is too narrow), not the clamp itself changing with viewport width.
|
|
26
|
+
const DETAILS_MIN_W = 619;
|
|
27
|
+
const DETAILS_MAX_W = 1170;
|
|
28
|
+
const DETAILS_DEFAULT_W = 734;
|
|
15
29
|
function valueEmpty(v) {
|
|
16
30
|
return v == null || v === "" || (Array.isArray(v) && v.length === 0);
|
|
17
31
|
}
|
|
@@ -419,7 +433,7 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
419
433
|
const tasks = tasksProp ?? internalTasks;
|
|
420
434
|
const [showAllActivity, setShowAllActivity] = React.useState(false);
|
|
421
435
|
// Drag-resizable left details column.
|
|
422
|
-
const [detailsW, setDetailsW] = React.useState(
|
|
436
|
+
const [detailsW, setDetailsW] = React.useState(DETAILS_DEFAULT_W);
|
|
423
437
|
const [resizing, setResizing] = React.useState(false);
|
|
424
438
|
const dragRef = React.useRef(null);
|
|
425
439
|
const onResizeDown = (e) => {
|
|
@@ -437,7 +451,7 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
437
451
|
if (!dragRef.current)
|
|
438
452
|
return;
|
|
439
453
|
const next = dragRef.current.w + dragRef.current.dir * (e.clientX - dragRef.current.x);
|
|
440
|
-
setDetailsW(Math.max(
|
|
454
|
+
setDetailsW(Math.max(DETAILS_MIN_W, Math.min(DETAILS_MAX_W, next)));
|
|
441
455
|
};
|
|
442
456
|
const onResizeUp = (e) => {
|
|
443
457
|
dragRef.current = null;
|
|
@@ -622,7 +636,7 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
622
636
|
const effectivePanel = tabsProp
|
|
623
637
|
? (active) => tabsProp.find((tb) => tb.id === active)?.render()
|
|
624
638
|
: builtinPanel;
|
|
625
|
-
return (_jsxs("div", { className: recordpageRecipe({ className }), ...rest, children: [_jsxs("div", { className: "crisp-recordpage-topbar", children: [_jsxs("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [_jsx("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), _jsx("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), _jsx("span", { className: "crisp-recordpage-crumb-name", children: name })] }), _jsxs("div", { className: "crisp-recordpage-actions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [_jsx(Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && (_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? (_jsx(Minimize2, { size: 15, strokeWidth: 1.75 })) : (_jsx(Maximize2, { size: 15, strokeWidth: 1.75 })) })), _jsxs(Menu, { align: "end", "aria-label": t.recordActions, trigger: _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [_jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), shortcut: "\u2318C", children: t.copyRecordLink }), _jsx(MenuItem, { icon: _jsx(Plus, { size: 15, strokeWidth: 1.75 }), children: t.addToList }), _jsx(MenuItem, { icon: _jsx(Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, shortcut: "\u232B", children: t.deleteRecord })] })] })] }), _jsxs("div", { className: "crisp-recordpage-body", children: [_jsxs("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [_jsxs("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? (_jsx("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : (_jsx(Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), _jsx("span", { className: "crisp-recordpage-entity-name", children: name }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: _jsx(Pencil, { size: 15, strokeWidth: 1.75 }) }), _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: _jsx(Star, { size: 15, strokeWidth: 1.75 }) })] }), _jsxs("div", { className: "crisp-recordpage-quickactions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.copyRecordLink, children: _jsx(Copy, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.logANote, children: _jsx(FileText, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.linkRecords, children: _jsx(Share2, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.createATask, children: _jsx(SquareCheck, { size: 15, strokeWidth: 1.75 }) })] }), _jsx(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), _jsxs("div", { className: "crisp-recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "crisp-recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "crisp-recordpage-emptystate", children: t.noLists })] })] }), _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":
|
|
639
|
+
return (_jsxs("div", { className: recordpageRecipe({ className }), ...rest, children: [_jsxs("div", { className: "crisp-recordpage-topbar", children: [_jsxs("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [_jsx("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), _jsx("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), _jsx("span", { className: "crisp-recordpage-crumb-name", children: name })] }), _jsxs("div", { className: "crisp-recordpage-actions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [_jsx(Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && (_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? (_jsx(Minimize2, { size: 15, strokeWidth: 1.75 })) : (_jsx(Maximize2, { size: 15, strokeWidth: 1.75 })) })), _jsxs(Menu, { align: "end", "aria-label": t.recordActions, trigger: _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [_jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), shortcut: "\u2318C", children: t.copyRecordLink }), _jsx(MenuItem, { icon: _jsx(Plus, { size: 15, strokeWidth: 1.75 }), children: t.addToList }), _jsx(MenuItem, { icon: _jsx(Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, shortcut: "\u232B", children: t.deleteRecord })] })] })] }), _jsxs("div", { className: "crisp-recordpage-body", children: [_jsxs("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [_jsxs("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? (_jsx("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : (_jsx(Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), _jsx("span", { className: "crisp-recordpage-entity-name", children: name }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: _jsx(Pencil, { size: 15, strokeWidth: 1.75 }) }), _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: _jsx(Star, { size: 15, strokeWidth: 1.75 }) })] }), _jsxs("div", { className: "crisp-recordpage-quickactions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.copyRecordLink, children: _jsx(Copy, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.logANote, children: _jsx(FileText, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.linkRecords, children: _jsx(Share2, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": t.createATask, children: _jsx(SquareCheck, { size: 15, strokeWidth: 1.75 }) })] }), _jsx(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), _jsxs("div", { className: "crisp-recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "crisp-recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "crisp-recordpage-emptystate", children: t.noLists })] })] }), _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) => {
|
|
626
640
|
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
627
641
|
return;
|
|
628
642
|
// The pointer drag learned its direction in #212; this did not.
|
|
@@ -631,7 +645,9 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
631
645
|
// half-conversion the drag had, one handler away (#255).
|
|
632
646
|
const rtl = isRtlElement(e.currentTarget);
|
|
633
647
|
const grow = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
634
|
-
setDetailsW((w) => grow
|
|
648
|
+
setDetailsW((w) => grow
|
|
649
|
+
? Math.min(DETAILS_MAX_W, w + 16)
|
|
650
|
+
: Math.max(DETAILS_MIN_W, w - 16));
|
|
635
651
|
} }), _jsx("main", { className: "crisp-recordpage-main", children: _jsx(Tabs, { tabs: effectiveTabs, defaultValue: tabsProp ? defaultTab : "overview", "aria-label": `${name} record`, children: effectivePanel }) })] })] }));
|
|
636
652
|
}
|
|
637
653
|
RecordPage.displayName = "RecordPage";
|