@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.
Files changed (50) hide show
  1. package/dist/cjs/components/datatable/datatable.js +130 -38
  2. package/dist/cjs/components/resizablepanel/resizablepanel.js +10 -1
  3. package/dist/cjs/components/table/table.js +8 -1
  4. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +110 -27
  5. package/dist/cjs/index.js +0 -2
  6. package/dist/cjs/product/recordpage/index.js +6 -1
  7. package/dist/cjs/product/recordpage/recordpage.js +20 -4
  8. package/dist/esm/components/datatable/datatable.js +131 -39
  9. package/dist/esm/components/resizablepanel/resizablepanel.js +10 -1
  10. package/dist/esm/components/table/table.js +8 -1
  11. package/dist/esm/components/tabletoolbar/tabletoolbar.js +111 -28
  12. package/dist/esm/index.js +0 -2
  13. package/dist/esm/product/recordpage/index.js +2 -0
  14. package/dist/esm/product/recordpage/recordpage.js +20 -4
  15. package/dist/styles.css +0 -272
  16. package/dist/styles.layered.css +0 -272
  17. package/dist/types/components/table/table.d.ts +14 -0
  18. package/dist/types/index.d.ts +0 -2
  19. package/dist/types/product/recordpage/index.d.ts +2 -0
  20. package/package.json +2 -12
  21. package/dist/cjs/product/recordstable/company-logos.js +0 -29
  22. package/dist/cjs/product/recordstable/index.js +0 -13
  23. package/dist/cjs/product/recordstable/recordstable.js +0 -1362
  24. package/dist/cjs/product/recordstable/recordstable.recipe.js +0 -175
  25. package/dist/cjs/product/recordstable/recordstable.tag.js +0 -26
  26. package/dist/cjs/product/taskstable/index.js +0 -7
  27. package/dist/cjs/product/taskstable/taskstable.js +0 -32
  28. package/dist/cjs/product/taskstable/taskstable.recipe.js +0 -56
  29. package/dist/esm/product/recordstable/company-logos.js +0 -26
  30. package/dist/esm/product/recordstable/index.js +0 -3
  31. package/dist/esm/product/recordstable/recordstable.js +0 -1322
  32. package/dist/esm/product/recordstable/recordstable.recipe.js +0 -172
  33. package/dist/esm/product/recordstable/recordstable.tag.js +0 -23
  34. package/dist/esm/product/taskstable/index.js +0 -2
  35. package/dist/esm/product/taskstable/taskstable.js +0 -29
  36. package/dist/esm/product/taskstable/taskstable.recipe.js +0 -53
  37. package/dist/recordstable.css +0 -240
  38. package/dist/recordstable.d.ts +0 -7
  39. package/dist/recordstable.layered.css +0 -242
  40. package/dist/taskstable.css +0 -30
  41. package/dist/taskstable.d.ts +0 -7
  42. package/dist/taskstable.layered.css +0 -32
  43. package/dist/types/product/recordstable/company-logos.d.ts +0 -1
  44. package/dist/types/product/recordstable/index.d.ts +0 -3
  45. package/dist/types/product/recordstable/recordstable.d.ts +0 -294
  46. package/dist/types/product/recordstable/recordstable.recipe.d.ts +0 -5
  47. package/dist/types/product/recordstable/recordstable.tag.d.ts +0 -15
  48. package/dist/types/product/taskstable/index.d.ts +0 -2
  49. package/dist/types/product/taskstable/taskstable.d.ts +0 -59
  50. 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
- (addingAttribute ? (_jsx("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": addAttributeToViewLabel, children: nestedFieldPicker(hiddenAttrs, showAttr, searchAttributesPlaceholder, searchAttributesPlaceholder) })) : (_jsxs("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-sortpop", role: "dialog", "aria-label": viewSettingsLabel, children: [shownAttrs.map((col, i) => (_jsxs("div", { className: "crisp-tabletoolbar-sort-row", onDragOver: (e) => {
812
- if (dragAttr !== null)
813
- e.preventDefault();
814
- }, onDrop: () => {
815
- if (dragAttr !== null)
816
- moveAttr(dragAttr, i);
817
- setDragAttr(null);
818
- }, 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) ??
819
- `${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: () => {
820
- setAddingAttribute(true);
821
- setFieldPickerPath([]);
822
- setFilterQuery("");
823
- }, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), addAttributeToViewLabel] }) })] })))] }) })), (onImportCsv ||
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, children: [onImportCsv && (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", onClick: () => {
827
- onImportCsv();
828
- setOpenMenu(null);
829
- }, 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: () => {
830
- onExportCsv();
831
- setOpenMenu(null);
832
- }, children: [_jsx(Download, { size: 15, strokeWidth: 1.75 }), exportCsvLabel] })), onExportExcel && (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", onClick: () => {
833
- onExportExcel();
834
- setOpenMenu(null);
835
- }, children: [_jsx(Download, { size: 15, strokeWidth: 1.75 }), exportExcelLabel] })), importExportHelpHref &&
836
- (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] }) }));
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";
@@ -1,2 +1,4 @@
1
+ export { tagTone, } from "./column.js";
2
+ export { editableInputType, useEditableField, } from "./editable-field.js";
1
3
  export { RecordDetails, RecordPage, recordPageLabels, } from "./recordpage.js";
2
4
  export { recordpageRecipe } from "./recordpage.recipe.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(320);
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(300, Math.min(560, next)));
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": 300, "aria-valuemax": 560, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
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 ? Math.min(560, w + 16) : Math.max(300, w - 16));
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";