@crispy-seed/data-table 0.10.5 → 0.11.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/index.cjs CHANGED
@@ -17,6 +17,9 @@ var reactCheckbox = require('@seed-design/react-checkbox');
17
17
  var reactScaleFeedback = require('@seed-design/react-scale-feedback');
18
18
  var checkboxGroup = require('@seed-design/css/recipes/checkbox-group');
19
19
  var reactDom = require('react-dom');
20
+ var reactMenu = require('@seed-design/react-menu');
21
+ var menu = require('@seed-design/css/recipes/menu');
22
+ var menuItem = require('@seed-design/css/recipes/menu-item');
20
23
  var reactImage = require('@seed-design/react-image');
21
24
  var avatar = require('@seed-design/css/recipes/avatar');
22
25
  require('@seed-design/css/recipes/avatar-stack');
@@ -31,9 +34,6 @@ var _switch = require('@seed-design/css/recipes/switch');
31
34
  var switchmark = require('@seed-design/css/recipes/switchmark');
32
35
  var reactTextField = require('@seed-design/react-text-field');
33
36
  var textInput = require('@seed-design/css/recipes/text-input');
34
- var reactMenu = require('@seed-design/react-menu');
35
- var menu = require('@seed-design/css/recipes/menu');
36
- var menuItem = require('@seed-design/css/recipes/menu-item');
37
37
  var tabs = require('@seed-design/css/recipes/tabs');
38
38
 
39
39
  function _interopNamespace(e) {
@@ -780,7 +780,10 @@ function isFunction$1(value) {
780
780
  const DATA_TABLE_UNIVERSAL_CALC_OPS = [
781
781
  "count",
782
782
  "empty",
783
- "notEmpty"
783
+ "notEmpty",
784
+ "percentEmpty",
785
+ "percentFilled",
786
+ "none"
784
787
  ];
785
788
  const DATA_TABLE_NUMERIC_CALC_OPS = [
786
789
  "sum",
@@ -795,8 +798,11 @@ const DATA_TABLE_NUMERIC_CALC_OPS = [
795
798
  ];
796
799
  const DATA_TABLE_CALC_LABEL = {
797
800
  count: "Count",
798
- empty: "Empty",
799
- notEmpty: "Not empty",
801
+ empty: "Count empty",
802
+ notEmpty: "Count filled",
803
+ percentEmpty: "Percent empty",
804
+ percentFilled: "Percent filled",
805
+ none: "None",
800
806
  sum: "Sum",
801
807
  average: "Average",
802
808
  median: "Median",
@@ -826,7 +832,12 @@ const DATA_TABLE_CALC_LABEL = {
826
832
  * (`JSON.stringify`-normalized, so two arrays/objects with the same shape count
827
833
  * once) -- empties are already their own op (`empty`), so folding them into "one
828
834
  * more unique value" here would double-count the same information two different
829
- * ways in the same menu.
835
+ * ways in the same menu. `percentEmpty`/`percentFilled` are `empty`/`notEmpty` as a
836
+ * share of the row count (0-100, unrounded -- formatting rounds it for display) --
837
+ * `null` on zero rows, since 0/0 has no meaningful percentage (not 0%, not 100%).
838
+ * `none` always renders nothing: it is Attio's own "no calculation" menu entry, not a
839
+ * separate always-appended "Clear" item, and picking it is a real, storable op so it
840
+ * overrides a `summaryDefaultOp` instead of falling back to it.
830
841
  */ function computeColumnCalc(values, op) {
831
842
  switch(op){
832
843
  case "count":
@@ -835,6 +846,14 @@ const DATA_TABLE_CALC_LABEL = {
835
846
  return values.filter(isEmptyValueForCalc).length;
836
847
  case "notEmpty":
837
848
  return values.filter((value)=>!isEmptyValueForCalc(value)).length;
849
+ case "percentEmpty":
850
+ if (values.length === 0) return null;
851
+ return values.filter(isEmptyValueForCalc).length / values.length * 100;
852
+ case "percentFilled":
853
+ if (values.length === 0) return null;
854
+ return values.filter((value)=>!isEmptyValueForCalc(value)).length / values.length * 100;
855
+ case "none":
856
+ return null;
838
857
  case "countUnique":
839
858
  {
840
859
  const seen = new Set(values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>JSON.stringify(value)));
@@ -1653,6 +1672,17 @@ function useDataTableSelection(props) {
1653
1672
  rowIds,
1654
1673
  setSelectedIds
1655
1674
  ]);
1675
+ // Bulk-action bar's own "×" (gap #4, DATATABLE-ATTIO-SPEC's Attio review) --
1676
+ // deliberately its own function rather than reusing `toggleAll`: `toggleAll` only
1677
+ // clears from an "all" selection, and otherwise SELECTS every row (`selectionState
1678
+ // === "some"` -> `rowIds`, above) -- exactly the wrong direction for a "clear
1679
+ // selection" affordance shown while `selectionState` is "some". This always empties
1680
+ // the set, regardless of `selectionState`.
1681
+ const clearSelection = React.useCallback(()=>{
1682
+ setSelectedIds([]);
1683
+ }, [
1684
+ setSelectedIds
1685
+ ]);
1656
1686
  const getRowProps = React.useCallback((rowId)=>{
1657
1687
  const isRowSelected = isSelected(rowId);
1658
1688
  return domUtils.elementProps({
@@ -1671,6 +1701,7 @@ function useDataTableSelection(props) {
1671
1701
  isSelected,
1672
1702
  toggleRow,
1673
1703
  toggleAll,
1704
+ clearSelection,
1674
1705
  getRowProps
1675
1706
  };
1676
1707
  }
@@ -2679,11 +2710,70 @@ CheckboxIndicator.displayName = "CheckboxIndicator";
2679
2710
  const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2680
2711
  const CheckboxHiddenInput = reactCheckbox.Checkbox.HiddenInput;
2681
2712
 
2682
- const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar.avatar);
2713
+ const { ClassNamesProvider: ClassNamesProvider$2, withContext: withContext$5, useClassNames: useClassNames$2 } = createSlotRecipeContext(menu.menu);
2714
+ const { PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, ClassNamesProvider: ItemClassNamesProvider$1 } = createSlotRecipeContext(menuItem.menuItem);
2715
+ const withStateProps$3 = createWithStateProps([
2716
+ reactMenu.useMenuContext
2717
+ ]);
2718
+ const withItemStateProps$1 = createWithStateProps([
2719
+ reactMenu.useMenuItemContext
2720
+ ]);
2721
+ const MenuRoot = (props)=>{
2722
+ const [variantProps, otherProps] = menu.menu.splitVariantProps(props);
2723
+ const classNames = menu.menu(variantProps);
2724
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$2, {
2725
+ value: classNames,
2726
+ children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider$1, {
2727
+ value: {
2728
+ size: variantProps.size
2729
+ },
2730
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Root, {
2731
+ ...otherProps
2732
+ })
2733
+ })
2734
+ });
2735
+ };
2736
+ const MenuAnchor = reactMenu.Menu.Anchor;
2737
+ const MenuTrigger = reactMenu.Menu.Trigger;
2738
+ const MenuPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
2739
+ const classNames = useClassNames$2();
2740
+ return /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Positioner, {
2741
+ ref: ref,
2742
+ className: clsx(classNames.positioner, className),
2743
+ ...props
2744
+ });
2745
+ });
2746
+ MenuPositioner.displayName = "MenuPositioner";
2747
+ const MenuContent = withContext$5(reactMenu.Menu.Content, "content");
2748
+ const MenuScrollArea = withContext$5(withStateProps$3(Primitive.div), "scrollArea");
2749
+ withContext$5(reactMenu.Menu.Group, "group");
2750
+ withContext$5(reactMenu.Menu.GroupLabel, "groupLabel");
2751
+ const MenuItem = /*#__PURE__*/ React__namespace.forwardRef(({ className: propClassName, ...props }, ref)=>{
2752
+ const [variantProps, otherProps] = menuItem.menuItem.splitVariantProps(props);
2753
+ const parentProps = useItemProps$1();
2754
+ const classNames = menuItem.menuItem({
2755
+ ...parentProps,
2756
+ ...variantProps
2757
+ });
2758
+ return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider$1, {
2759
+ value: classNames,
2760
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Item, {
2761
+ ref: ref,
2762
+ className: clsx(classNames.root, propClassName),
2763
+ ...otherProps
2764
+ })
2765
+ });
2766
+ });
2767
+ MenuItem.displayName = "MenuItem";
2768
+ withItemContext$1(withItemStateProps$1(Primitive.div), "body");
2769
+ const MenuItemLabel = withItemContext$1(withItemStateProps$1(Primitive.span), "label");
2770
+ withItemContext$1(withItemStateProps$1(Primitive.span), "description");
2771
+
2772
+ const { withProvider: withProvider$2, withContext: withContext$4 } = createSlotRecipeContext(avatar.avatar);
2683
2773
  const AvatarRoot = withProvider$2(reactImage.Image.Root, "root");
2684
- const AvatarImage = withContext$5(reactImage.Image.Content, "image");
2685
- const AvatarFallback = withContext$5(reactImage.Image.Fallback, "fallback");
2686
- withContext$5(Primitive.div, "badge");
2774
+ const AvatarImage = withContext$4(reactImage.Image.Content, "image");
2775
+ const AvatarFallback = withContext$4(reactImage.Image.Fallback, "fallback");
2776
+ withContext$4(Primitive.div, "badge");
2687
2777
 
2688
2778
  const Badge = /*#__PURE__*/ React.forwardRef(({ className, children, ...props }, ref)=>{
2689
2779
  const [variantProps, restProps] = badge.badge.splitVariantProps(props);
@@ -3227,6 +3317,141 @@ function RatingCell({ value, maxRating = 5 }) {
3227
3317
  });
3228
3318
  }
3229
3319
 
3320
+ /** Clear is withheld for `chip-list`: that column already owns its own per-tag remove
3321
+ * affordance (`ChipListEditor`) and commits a JSON-STRINGIFIED array through `onEdit`
3322
+ * (`DataTable.chips.test.tsx`'s own `TagsHarness` -- and this package's own Storybook
3323
+ * demo, `CellFeaturesDemo`, does exactly `JSON.parse(value)` on commit) -- committing
3324
+ * a bare `""` through that same path throws in the consumer's `onEdit`, it doesn't
3325
+ * clear anything. Every OTHER editable shape's commit value is a plain string
3326
+ * (`"true"`/`"false"`, a stringified rating, an option's own `value`, free text) that
3327
+ * a consumer's `onEdit` already has to accept a possibly-empty version of. */ function isClearableEditableCell(column) {
3328
+ return !!column.editable && column.type !== "chip-list";
3329
+ }
3330
+ /** Paste is narrower still -- restricted to a PLAIN `editable` column (no `editor`,
3331
+ * and not one of the always-live widget types) whose committed value IS the raw
3332
+ * text/number a user typed. `select`/`reference`/`actor` expect one of THEIR OWN
3333
+ * option values (not arbitrary clipboard text), `switch`/`rating`/`checkbox` are
3334
+ * always-live widgets with their own encoding, and `chip-list` is excluded for the
3335
+ * same JSON reason `isClearableEditableCell` documents above. This is the task's own
3336
+ * "only for simple editable text/number cells" guard -- committing pasted text
3337
+ * through any of the excluded shapes would corrupt the row rather than edit it
3338
+ * (crispy-seed's own #1094 lesson: never assume an editor's commit shape).
3339
+ */ function isSimpleEditableCell(column) {
3340
+ if (!column.editable) return false;
3341
+ if (column.editor) return false;
3342
+ if (column.type === "chip-list" || column.type === "checkbox" || column.type === "rating") {
3343
+ return false;
3344
+ }
3345
+ return true;
3346
+ }
3347
+ /** Copy's own text representation -- deliberately mirrors `DisplayCellContent`'s
3348
+ * (body-cells.tsx) switch closely enough to read the same as what's on screen, but
3349
+ * returns a plain string (`navigator.clipboard.writeText`) rather than JSX. Not
3350
+ * exported -- it exists only to feed Copy, not as a general-purpose formatter. */ function cellValueAsText(column, value, locale) {
3351
+ if (value == null) return "";
3352
+ switch(column.type){
3353
+ case "entity":
3354
+ if (typeof value === "object") return String(value.name ?? "");
3355
+ return String(value);
3356
+ case "tags":
3357
+ case "chip-list":
3358
+ return Array.isArray(value) ? value.join(", ") : String(value);
3359
+ case "number":
3360
+ return typeof value === "number" ? String(formatNumberCell(value, locale) ?? "") : String(value);
3361
+ case "currency":
3362
+ return typeof value === "number" ? String(formatCurrencyCell(value, column.symbol ?? "$", locale) ?? "") : String(value);
3363
+ case "date":
3364
+ return String(formatDateCell(value, locale) ?? "");
3365
+ case "datetime":
3366
+ return String(formatDateTimeCell(value, locale) ?? "");
3367
+ case "checkbox":
3368
+ return value === true || value === "true" ? "true" : "false";
3369
+ default:
3370
+ return String(value);
3371
+ }
3372
+ }
3373
+ /**
3374
+ * One cell's right-click menu -- mounted by `body-cells.tsx`'s `DataTableBodyCell`
3375
+ * only while actually open (the same "don't keep an unused floating-ui instance alive
3376
+ * per cell" reasoning as that file's own portaled chip-list popover), so this never
3377
+ * costs anything for the row×column cells a user never right-clicks.
3378
+ */ function CellContextMenu({ column, row, value, locale, point, open, onOpenChange, onCommit, onViewEditHistory, cellContextMenuItems }) {
3379
+ const clearable = isClearableEditableCell(column);
3380
+ const pasteable = isSimpleEditableCell(column);
3381
+ return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
3382
+ open: open,
3383
+ onOpenChange: onOpenChange,
3384
+ placement: "bottom-start",
3385
+ strategy: "fixed",
3386
+ gutter: 2,
3387
+ children: [
3388
+ /*#__PURE__*/ jsxRuntime.jsx(MenuAnchor, {
3389
+ "aria-hidden": true,
3390
+ style: {
3391
+ position: "fixed",
3392
+ top: point.y,
3393
+ left: point.x,
3394
+ width: 0,
3395
+ height: 0,
3396
+ pointerEvents: "none"
3397
+ }
3398
+ }),
3399
+ /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
3400
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
3401
+ style: {
3402
+ minWidth: 180
3403
+ },
3404
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
3405
+ children: [
3406
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3407
+ onClick: ()=>{
3408
+ const text = cellValueAsText(column, value, locale);
3409
+ // Best-effort: an insecure context or a denied permission rejects
3410
+ // this silently (no `MenuItem` state depends on the outcome) --
3411
+ // there's nothing else for a plain "Copy" item to do about a
3412
+ // browser that refused the write.
3413
+ void navigator.clipboard?.writeText?.(text);
3414
+ },
3415
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3416
+ children: "Copy"
3417
+ })
3418
+ }),
3419
+ pasteable && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3420
+ onClick: ()=>{
3421
+ void navigator.clipboard?.readText?.().then((text)=>onCommit(text)).catch(()=>{
3422
+ // Clipboard read denied/unavailable -- best-effort, same
3423
+ // reasoning as Copy above.
3424
+ });
3425
+ },
3426
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3427
+ children: "Paste"
3428
+ })
3429
+ }),
3430
+ clearable && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3431
+ onClick: ()=>onCommit(""),
3432
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3433
+ children: "Clear value"
3434
+ })
3435
+ }),
3436
+ onViewEditHistory && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
3437
+ onClick: ()=>onViewEditHistory(row, column.key),
3438
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
3439
+ children: "View edit history"
3440
+ })
3441
+ }),
3442
+ cellContextMenuItems?.({
3443
+ row,
3444
+ column,
3445
+ value
3446
+ })
3447
+ ]
3448
+ })
3449
+ })
3450
+ })
3451
+ ]
3452
+ });
3453
+ }
3454
+
3230
3455
  /** Fallback width (px) used to compute a pinned column's cumulative sticky offset when
3231
3456
  * it has no explicit `width` and hasn't been drag-resized -- pinning needs a STATIC
3232
3457
  * number to put in an inline `insetInlineStart`, and `table-layout: fixed` only
@@ -3385,11 +3610,11 @@ function RatingCell({ value, maxRating = 5 }) {
3385
3610
  // recipe, so a per-instance variant can override the root's default.
3386
3611
  const { ClassNamesProvider: TriggerClassNamesProvider, PropsProvider: TriggerPropsProvider, useProps: useTriggerProps, withContext: withTriggerContext, useClassNames: useTriggerClassNames } = createSlotRecipeContext(selectTrigger.selectTrigger);
3387
3612
  const { ClassNamesProvider: ContentClassNamesProvider, withContext: withContentContext, useClassNames: useContentClassNames } = createSlotRecipeContext(select.select);
3388
- const { ClassNamesProvider: ItemClassNamesProvider$1, PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem.selectItem);
3389
- const withStateProps$3 = createWithStateProps([
3613
+ const { ClassNamesProvider: ItemClassNamesProvider, PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem.selectItem);
3614
+ const withStateProps$2 = createWithStateProps([
3390
3615
  reactSelect.useSelectContext
3391
3616
  ]);
3392
- const withItemStateProps$1 = createWithStateProps([
3617
+ const withItemStateProps = createWithStateProps([
3393
3618
  reactSelect.useSelectItemContext
3394
3619
  ]);
3395
3620
  // NOTE: `gutter` and `overflowPadding` are specified in Rootage (`select.yaml`,
@@ -3410,7 +3635,7 @@ const SelectRoot = (props)=>{
3410
3635
  value: contentClassNames,
3411
3636
  children: /*#__PURE__*/ jsxRuntime.jsx(TriggerPropsProvider, {
3412
3637
  value: triggerVariantProps,
3413
- children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider$1, {
3638
+ children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider, {
3414
3639
  value: itemVariantProps,
3415
3640
  children: /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Root, {
3416
3641
  ...otherProps
@@ -3450,8 +3675,8 @@ const SelectTrigger = /*#__PURE__*/ React__namespace.forwardRef(({ className, ..
3450
3675
  });
3451
3676
  });
3452
3677
  SelectTrigger.displayName = "SelectTrigger";
3453
- const SelectValue = withTriggerContext(withStateProps$3(reactSelect.Select.Value), "value");
3454
- const SelectPlaceholder = withTriggerContext(withStateProps$3(reactSelect.Select.Placeholder), "placeholder");
3678
+ const SelectValue = withTriggerContext(withStateProps$2(reactSelect.Select.Value), "value");
3679
+ const SelectPlaceholder = withTriggerContext(withStateProps$2(reactSelect.Select.Placeholder), "placeholder");
3455
3680
  const SelectPrefixIcon = /*#__PURE__*/ React__namespace.forwardRef(({ fallback, className, ...otherProps }, ref)=>{
3456
3681
  const { selectedItem, stateProps } = reactSelect.useSelectContext();
3457
3682
  const classNames = useTriggerClassNames();
@@ -3466,7 +3691,7 @@ const SelectPrefixIcon = /*#__PURE__*/ React__namespace.forwardRef(({ fallback,
3466
3691
  });
3467
3692
  });
3468
3693
  SelectPrefixIcon.displayName = "SelectPrefixIcon";
3469
- const SelectSuffixIcon = withTriggerContext(withStateProps$3(InternalIcon), "suffixIcon");
3694
+ const SelectSuffixIcon = withTriggerContext(withStateProps$2(InternalIcon), "suffixIcon");
3470
3695
  const SelectPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
3471
3696
  const classNames = useContentClassNames();
3472
3697
  return /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Positioner, {
@@ -3497,12 +3722,12 @@ withContentContext(reactSelect.Select.Group, "group");
3497
3722
  withContentContext(reactSelect.Select.GroupLabel, "groupLabel");
3498
3723
  const SelectItem = /*#__PURE__*/ React__namespace.forwardRef(({ prefixIcon, className, ...props }, ref)=>{
3499
3724
  const [variantProps, otherProps] = selectItem.selectItem.splitVariantProps(props);
3500
- const rootProps = useItemProps$1();
3725
+ const rootProps = useItemProps();
3501
3726
  const classNames = selectItem.selectItem({
3502
3727
  ...rootProps,
3503
3728
  ...variantProps
3504
3729
  });
3505
- return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider$1, {
3730
+ return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider, {
3506
3731
  value: classNames,
3507
3732
  children: /*#__PURE__*/ jsxRuntime.jsx(reactSelect.Select.Item, {
3508
3733
  ref: ref,
@@ -3527,7 +3752,7 @@ const SelectItemPrefixIcon = /*#__PURE__*/ React__namespace.forwardRef(({ svg: s
3527
3752
  });
3528
3753
  });
3529
3754
  SelectItemPrefixIcon.displayName = "SelectItemPrefixIcon";
3530
- const SelectItemBody = withItemContext$1(withItemStateProps$1(Primitive.div), "body");
3755
+ const SelectItemBody = withItemContext(withItemStateProps(Primitive.div), "body");
3531
3756
  const SelectItemLabel = /*#__PURE__*/ React__namespace.forwardRef(({ children, className, ...otherProps }, ref)=>{
3532
3757
  const { label, stateProps } = reactSelect.useSelectItemContext();
3533
3758
  const classNames = useItemClassNames();
@@ -3542,7 +3767,7 @@ const SelectItemLabel = /*#__PURE__*/ React__namespace.forwardRef(({ children, c
3542
3767
  });
3543
3768
  });
3544
3769
  SelectItemLabel.displayName = "SelectItemLabel";
3545
- withItemContext$1(withItemStateProps$1(Primitive.span), "description");
3770
+ withItemContext(withItemStateProps(Primitive.span), "description");
3546
3771
  const SelectItemIndicator = /*#__PURE__*/ React__namespace.forwardRef(({ selected: selectedSvg, unselected: unselectedSvg, className, ...otherProps }, ref)=>{
3547
3772
  const { isSelected, stateProps } = reactSelect.useSelectItemContext();
3548
3773
  const classNames = useItemClassNames();
@@ -3574,9 +3799,9 @@ const SelectHiddenSelect = /*#__PURE__*/ React__namespace.forwardRef((props, ref
3574
3799
  });
3575
3800
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3576
3801
 
3577
- const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
3802
+ const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$1 } = createSlotRecipeContext(_switch.switchStyle);
3578
3803
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark.switchmark);
3579
- const withStateProps$2 = createWithStateProps([
3804
+ const withStateProps$1 = createWithStateProps([
3580
3805
  reactSwitch.useSwitchContext
3581
3806
  ]);
3582
3807
  const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ className, ...props }, ref)=>{
@@ -3587,7 +3812,7 @@ const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ classNam
3587
3812
  const classNames = _switch.switchStyle(switchVariantProps);
3588
3813
  return /*#__PURE__*/ jsxRuntime.jsx(ControlPropsProvider, {
3589
3814
  value: switchmarkVariantProps,
3590
- children: /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$2, {
3815
+ children: /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$1, {
3591
3816
  value: classNames,
3592
3817
  children: /*#__PURE__*/ jsxRuntime.jsx(reactSwitch.Switch.Root, {
3593
3818
  ref: ref,
@@ -3600,20 +3825,20 @@ const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ classNam
3600
3825
  SwitchRoot.displayName = "SwitchRoot";
3601
3826
  const SwitchControl = withScaleFeedback(withControlProvider(reactSwitch.Switch.Control, "root"));
3602
3827
  const SwitchThumb = withControlContext(reactSwitch.Switch.Thumb, "thumb");
3603
- withContext$4(withStateProps$2(Primitive.span), "label");
3828
+ withContext$3(withStateProps$1(Primitive.span), "label");
3604
3829
  const SwitchHiddenInput = reactSwitch.Switch.HiddenInput;
3605
3830
 
3606
3831
  // src/use-layout-effect.tsx
3607
3832
  var useLayoutEffect2 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
3608
3833
 
3609
- const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
3834
+ const { withProvider: withProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(textInput.textInput);
3610
3835
  const withFieldStateProps = createWithStateProps([
3611
3836
  {
3612
3837
  useContext: reactField.useFieldContext,
3613
3838
  strict: false
3614
3839
  }
3615
3840
  ]);
3616
- const withStateProps$1 = createWithStateProps([
3841
+ const withStateProps = createWithStateProps([
3617
3842
  reactTextField.useTextFieldContext,
3618
3843
  {
3619
3844
  useContext: reactField.useFieldContext,
@@ -3621,12 +3846,12 @@ const withStateProps$1 = createWithStateProps([
3621
3846
  }
3622
3847
  ]);
3623
3848
  const TextFieldRoot = withProvider$1(withFieldStateProps(reactTextField.TextField.Root), "root");
3624
- withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3625
- withContext$3(withStateProps$1(Primitive.span), "prefixText");
3626
- withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3627
- withContext$3(withStateProps$1(Primitive.span), "suffixText");
3849
+ withContext$2(withStateProps(InternalIcon), "prefixIcon");
3850
+ withContext$2(withStateProps(Primitive.span), "prefixText");
3851
+ withContext$2(withStateProps(InternalIcon), "suffixIcon");
3852
+ withContext$2(withStateProps(Primitive.span), "suffixText");
3628
3853
  const TextFieldInput = /*#__PURE__*/ React.forwardRef(({ className, ...otherProps }, ref)=>{
3629
- const classNames = useClassNames$2();
3854
+ const classNames = useClassNames$1();
3630
3855
  const textFieldContext = reactTextField.useTextFieldContext();
3631
3856
  const fieldContext = reactField.useFieldContext({
3632
3857
  strict: false
@@ -3644,7 +3869,7 @@ const TextFieldInput = /*#__PURE__*/ React.forwardRef(({ className, ...otherProp
3644
3869
  });
3645
3870
  TextFieldInput.displayName = "TextFieldInput";
3646
3871
  const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresize = true, ...otherProps }, ref)=>{
3647
- const classNames = useClassNames$2();
3872
+ const classNames = useClassNames$1();
3648
3873
  const textFieldContext = reactTextField.useTextFieldContext();
3649
3874
  const fieldContext = reactField.useFieldContext({
3650
3875
  strict: false
@@ -3701,7 +3926,7 @@ const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresiz
3701
3926
  });
3702
3927
  TextFieldTextarea.displayName = "TextFieldTextarea";
3703
3928
 
3704
- __insertCSS(".seed-data-table__root{box-sizing:border-box;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:not([data-editable]){user-select:none}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-overlay-open]{overflow:visible;z-index:20}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0;transition:opacity .15s}tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityActions:focus-within{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0;transition:background-color .2s,color .2s,box-shadow .2s}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__chip{box-sizing:border-box;display:inline-flex;align-items:center;flex-shrink:0;max-width:100%;height:22px;padding:0 4px;border-radius:7px;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{position:relative;display:flex;align-items:center;flex-wrap:nowrap;gap:3px;overflow:hidden;min-width:0;width:100%}.seed-data-table__chipOverflow{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;padding:0 4px;border-radius:6px;background-color:#f6f7f7;color:#000;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;white-space:nowrap}.seed-data-table__chipListEditor{display:flex;align-items:center;flex-wrap:wrap;gap:3px;min-width:0;width:100%}.seed-data-table__chipRemove{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:12px;height:12px;margin:0;margin-inline-start:2px;padding:0;border:none;border-radius:999px;background:0 0;color:inherit;opacity:.6;cursor:pointer;transition:opacity .12s,background-color .12s}.seed-data-table__chipRemove:hover{opacity:1;background-color:rgba(0,0,0,.08)}.seed-data-table__chipInput{flex:1 1 auto;min-width:40px;height:22px;margin:0;padding:0;border:none;outline:0;background:0 0;font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);letter-spacing:-.14px}.seed-data-table__expandOverlay{box-sizing:border-box;position:absolute;top:-1px;inset-inline-start:-1px;inset-inline-end:-1px;z-index:10;min-height:calc(100% + 2px);display:flex;align-items:flex-start;padding-inline:var(--seed-dimension-x3);padding-block:var(--seed-dimension-x3);background-color:var(--seed-color-bg-layer-floating);border-radius:4px;box-shadow:none;border:1px solid #266df0;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);letter-spacing:-.14px;cursor:default}.seed-data-table__expandOverlayText{width:100%;min-width:0;white-space:normal;word-break:break-word}.seed-data-table__cellSuffixRow{display:flex;align-items:center;gap:4px;min-width:0;width:100%}.seed-data-table__cellSuffixText{flex:1 1 0%;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cellSuffixIcon{flex-shrink:0;display:inline-flex;align-items:center;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent)}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
3929
+ __insertCSS(".seed-data-table__root{box-sizing:border-box;position:relative;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:not([data-editable]){user-select:none}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-overlay-open]{overflow:visible;z-index:20}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0;transition:opacity .15s}tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityActions:focus-within{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0;transition:background-color .2s,color .2s,box-shadow .2s}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__chip{box-sizing:border-box;display:inline-flex;align-items:center;flex-shrink:0;max-width:100%;height:22px;padding:0 4px;border-radius:7px;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{position:relative;display:flex;align-items:center;flex-wrap:nowrap;gap:3px;overflow:hidden;min-width:0;width:100%}.seed-data-table__chipOverflow{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;padding:0 4px;border-radius:6px;background-color:#f6f7f7;color:#000;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;white-space:nowrap}.seed-data-table__chipListEditor{display:flex;align-items:center;flex-wrap:wrap;gap:3px;min-width:0;width:100%}.seed-data-table__chipRemove{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:12px;height:12px;margin:0;margin-inline-start:2px;padding:0;border:none;border-radius:999px;background:0 0;color:inherit;opacity:.6;cursor:pointer;transition:opacity .12s,background-color .12s}.seed-data-table__chipRemove:hover{opacity:1;background-color:rgba(0,0,0,.08)}.seed-data-table__chipInput{flex:1 1 auto;min-width:40px;height:22px;margin:0;padding:0;border:none;outline:0;background:0 0;font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);letter-spacing:-.14px}.seed-data-table__expandOverlay{box-sizing:border-box;position:absolute;top:-1px;inset-inline-start:-1px;inset-inline-end:-1px;z-index:10;min-height:calc(100% + 2px);display:flex;align-items:flex-start;padding-inline:var(--seed-dimension-x3);padding-block:var(--seed-dimension-x3);background-color:var(--seed-color-bg-layer-floating);border-radius:4px;box-shadow:none;border:1px solid #266df0;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);letter-spacing:-.14px;cursor:default}.seed-data-table__expandOverlayText{width:100%;min-width:0;white-space:normal;word-break:break-word}.seed-data-table__cellSuffixRow{display:flex;align-items:center;gap:4px;min-width:0;width:100%}.seed-data-table__cellSuffixText{flex:1 1 0%;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cellSuffixIcon{flex-shrink:0;display:inline-flex;align-items:center;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent)}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__bulkBar{box-sizing:border-box;position:absolute;inset-inline-start:50%;transform:translateX(-50%);bottom:var(--seed-dimension-x4);z-index:30;display:flex;align-items:center;gap:var(--seed-dimension-x3);min-height:36px;padding-inline:var(--seed-dimension-x3_5);padding-block:var(--seed-dimension-x2);border-radius:var(--seed-radius-full);box-shadow:var(--seed-shadow-s3);background-color:var(--seed-color-bg-layer-floating);color:var(--seed-color-fg-neutral);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap}.seed-data-table__bulkBarCount{flex-shrink:0;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums}.seed-data-table__bulkBarClear{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;transition:background-color .2s,color .2s}.seed-data-table__bulkBarClear:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 8%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__bulkBarActions{display:flex;align-items:center;flex-shrink:0;gap:var(--seed-dimension-x3);padding-inline-start:var(--seed-dimension-x3);border-inline-start:1px solid color-mix(in srgb,var(--seed-color-fg-neutral) 16%,transparent)}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
3705
3930
 
3706
3931
  const createClassName = (className, variants, compoundVariants = [])=>{
3707
3932
  const variantKeys = Object.keys(variants);
@@ -3895,6 +4120,22 @@ const dataTableSlotNames = [
3895
4120
  [
3896
4121
  "selectionCell",
3897
4122
  "seed-data-table__selectionCell"
4123
+ ],
4124
+ [
4125
+ "bulkBar",
4126
+ "seed-data-table__bulkBar"
4127
+ ],
4128
+ [
4129
+ "bulkBarCount",
4130
+ "seed-data-table__bulkBarCount"
4131
+ ],
4132
+ [
4133
+ "bulkBarClear",
4134
+ "seed-data-table__bulkBarClear"
4135
+ ],
4136
+ [
4137
+ "bulkBarActions",
4138
+ "seed-data-table__bulkBarActions"
3898
4139
  ]
3899
4140
  ];
3900
4141
  const defaultVariant$6 = {
@@ -3932,7 +4173,7 @@ Object.assign(dataTable, {
3932
4173
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3933
4174
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3934
4175
  // than re-deriving its own.
3935
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
4176
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(dataTable);
3936
4177
 
3937
4178
  /**
3938
4179
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -4640,7 +4881,7 @@ function isCellTruncated(cell) {
4640
4881
  * actually reported against a pinned column. */ function isChipListEditable(column) {
4641
4882
  return !!column.editable && column.type === "chip-list";
4642
4883
  }
4643
- function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
4884
+ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
4644
4885
  const effective = useEffectiveColumns(columns, {
4645
4886
  selectable,
4646
4887
  stickyFirst
@@ -4655,7 +4896,10 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4655
4896
  row: row,
4656
4897
  classNames: classNames,
4657
4898
  locale: locale,
4658
- sticky: sticky
4899
+ sticky: sticky,
4900
+ cellContextMenu: cellContextMenu,
4901
+ onViewEditHistory: onViewEditHistory,
4902
+ cellContextMenuItems: cellContextMenuItems
4659
4903
  }, column.key);
4660
4904
  })
4661
4905
  });
@@ -4667,7 +4911,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4667
4911
  * on every row's render. React keeps this instance stable across re-renders because
4668
4912
  * `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
4669
4913
  * `<DataTableCell>` before this split.
4670
- */ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
4914
+ */ function DataTableBodyCell({ column, row, classNames, locale, sticky, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
4671
4915
  const { startEdit, cancelEdit, isEditing, isPending, commitEdit, isPeeking, openPeek, closePeek } = useDataTableContext();
4672
4916
  const cellRef = React__namespace.useRef(null);
4673
4917
  // Feature B rework (#8) -- the chip-list popover's own live position, in VIEWPORT
@@ -4683,6 +4927,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4683
4927
  // editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
4684
4928
  // rendering exactly as it always has, not suddenly gain overlay chrome.
4685
4929
  const [overlayOpen, setOverlayOpen] = React__namespace.useState(false);
4930
+ // Attio parity (attio-column-features-full.md gap #1) -- the right-click menu's own
4931
+ // point + open state. `contextMenuPoint` is frozen at the moment of the right-click
4932
+ // (never re-read live), same reasoning as `overlayOpen` above: the menu shouldn't
4933
+ // chase the cursor after it opens. `<CellContextMenu>` itself is only rendered below
4934
+ // while `contextMenuOpen` is true (not permanently mounted per cell) -- unlike
4935
+ // `ColumnMenu` (one per COLUMN, header row only), this component runs once per
4936
+ // row×column cell, and a floating-ui instance genuinely idle in every one of
4937
+ // thousands of cells would be real, avoidable overhead.
4938
+ const [contextMenuPoint, setContextMenuPoint] = React__namespace.useState(null);
4939
+ const [contextMenuOpen, setContextMenuOpen] = React__namespace.useState(false);
4686
4940
  const alwaysLive = isAlwaysLiveEditable(column);
4687
4941
  const chipListEditable = isChipListEditable(column);
4688
4942
  // A gated `editable` column (everything except the always-live widgets AND the
@@ -4734,6 +4988,19 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4734
4988
  setOverlayOpen(isCellTruncated(event.currentTarget));
4735
4989
  startEdit(row.id, column.key);
4736
4990
  }
4991
+ function handleContextMenu(event) {
4992
+ if (!cellContextMenu) return;
4993
+ // Replaces the browser's own native context menu (Attio's own gesture) -- and
4994
+ // must not also reach the row's own `onRowClick`, same reasoning as every other
4995
+ // cell-local click handler in this file.
4996
+ event.preventDefault();
4997
+ event.stopPropagation();
4998
+ setContextMenuPoint({
4999
+ x: event.clientX,
5000
+ y: event.clientY
5001
+ });
5002
+ setContextMenuOpen(true);
5003
+ }
4737
5004
  function handleClick(event) {
4738
5005
  if (chipListEditable) {
4739
5006
  // Attio's own chip-list gesture: click opens the editor regardless of
@@ -4837,7 +5104,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4837
5104
  const pending = isPending(row.id, column.key);
4838
5105
  return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4839
5106
  children: [
4840
- /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
5107
+ /*#__PURE__*/ jsxRuntime.jsxs(DataTableCell, {
4841
5108
  ref: cellRef,
4842
5109
  "data-numeric": domUtils.dataAttr(column.type === "number" || column.type === "currency"),
4843
5110
  "data-editable": domUtils.dataAttr(!!column.editable),
@@ -4848,14 +5115,34 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4848
5115
  style: sticky.style,
4849
5116
  onClick: handleClick,
4850
5117
  onDoubleClick: handleDoubleClick,
4851
- children: /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
4852
- column: column,
4853
- row: row,
4854
- rowId: row.id,
4855
- classNames: classNames,
4856
- locale: locale,
4857
- overlayActive: overlayActive
4858
- })
5118
+ onContextMenu: handleContextMenu,
5119
+ children: [
5120
+ /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
5121
+ column: column,
5122
+ row: row,
5123
+ rowId: row.id,
5124
+ classNames: classNames,
5125
+ locale: locale,
5126
+ overlayActive: overlayActive
5127
+ }),
5128
+ cellContextMenu && contextMenuOpen && contextMenuPoint && // A `<td>` (unlike its own parent `<tr>`) can validly hold any flow
5129
+ // content, so `CellContextMenu`'s own 0x0 `MenuAnchor` -- itself never
5130
+ // portaled, only `MenuPositioner`'s actual dropdown content is (via
5131
+ // floating-ui's own `FloatingPortal`, straight to `document.body`) --
5132
+ // renders here, INSIDE this cell, rather than as a sibling of it.
5133
+ /*#__PURE__*/ jsxRuntime.jsx(CellContextMenu, {
5134
+ column: column,
5135
+ row: row,
5136
+ value: rawValue,
5137
+ locale: locale,
5138
+ point: contextMenuPoint,
5139
+ open: contextMenuOpen,
5140
+ onOpenChange: setContextMenuOpen,
5141
+ onCommit: (value)=>commitEdit(row.id, column.key, value),
5142
+ onViewEditHistory: onViewEditHistory,
5143
+ cellContextMenuItems: cellContextMenuItems
5144
+ })
5145
+ ]
4859
5146
  }),
4860
5147
  chipListEditorOpen && popoverStyle && /*#__PURE__*/ reactDom.createPortal(// biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- same shape as `CellExpandOverlay` (editors.tsx), which this box is a portaled variant of: it never responds to interaction on ITSELF, only stops propagation of clicks that already occurred on a real interactive descendant (the editor's input/remove buttons). No role fits it because it isn't a control.
4861
5148
  /*#__PURE__*/ jsxRuntime.jsx("div", {
@@ -5085,64 +5372,52 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
5085
5372
  }
5086
5373
  }
5087
5374
 
5088
- const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu.menu);
5089
- const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem.menuItem);
5090
- const withStateProps = createWithStateProps([
5091
- reactMenu.useMenuContext
5092
- ]);
5093
- const withItemStateProps = createWithStateProps([
5094
- reactMenu.useMenuItemContext
5095
- ]);
5096
- const MenuRoot = (props)=>{
5097
- const [variantProps, otherProps] = menu.menu.splitVariantProps(props);
5098
- const classNames = menu.menu(variantProps);
5099
- return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider, {
5100
- value: classNames,
5101
- children: /*#__PURE__*/ jsxRuntime.jsx(ItemPropsProvider, {
5102
- value: {
5103
- size: variantProps.size
5104
- },
5105
- children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Root, {
5106
- ...otherProps
5375
+ const DataTableBulkBarRoot = withContext$1(Primitive.div, "bulkBar");
5376
+ const DataTableBulkBarCount = withContext$1(Primitive.span, "bulkBarCount");
5377
+ const DataTableBulkBarClear = withContext$1(Primitive.button, "bulkBarClear");
5378
+ /** Wraps whatever the CONSUMER passes as this bar's own action buttons -- the bar
5379
+ * itself only ever supplies chrome (count + clear); every actual action (Add to
5380
+ * list, Send email, …) is the consumer's own `<ActionButton>`/`<Menu>`, the same
5381
+ * "chrome vs. consumer content" split `toolbarActions`/`toolbarEnd` already draw for
5382
+ * the toolbar's own custom actions. */ const DataTableBulkBarActions = withContext$1(Primitive.div, "bulkBarActions");
5383
+ /**
5384
+ * The floating bulk-action pill (gap #4) -- renders nothing while no row is selected
5385
+ * (reads `selectedIds` off the table-wide context itself, so a consumer doesn't have
5386
+ * to gate this on `selectable`/`selectedIds` on their own). Composed as the LAST
5387
+ * child of the table's own root (`DataTable.tsx`), which is `position: relative` for
5388
+ * exactly this reason -- see `data-table.ts`'s own `bulkBar` slot comment for why its
5389
+ * geometry borrows `contextual-floating-button`'s already-measured vars rather than a
5390
+ * guessed literal.
5391
+ */ function DataTableBulkBar({ clearLabel = "Clear selection", children }) {
5392
+ const { selectedIds, clearSelection } = useDataTableContext();
5393
+ if (selectedIds.length === 0) return null;
5394
+ return /*#__PURE__*/ jsxRuntime.jsxs(DataTableBulkBarRoot, {
5395
+ role: "toolbar",
5396
+ "aria-label": "Bulk actions",
5397
+ "aria-orientation": "horizontal",
5398
+ children: [
5399
+ /*#__PURE__*/ jsxRuntime.jsxs(DataTableBulkBarCount, {
5400
+ "aria-live": "polite",
5401
+ children: [
5402
+ selectedIds.length,
5403
+ " selected"
5404
+ ]
5405
+ }),
5406
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableBulkBarClear, {
5407
+ type: "button",
5408
+ "aria-label": clearLabel,
5409
+ onClick: clearSelection,
5410
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconXmarkLine_default, {
5411
+ size: 12,
5412
+ "aria-hidden": "true"
5413
+ })
5414
+ }),
5415
+ children && /*#__PURE__*/ jsxRuntime.jsx(DataTableBulkBarActions, {
5416
+ children: children(selectedIds)
5107
5417
  })
5108
- })
5109
- });
5110
- };
5111
- reactMenu.Menu.Anchor;
5112
- const MenuTrigger = reactMenu.Menu.Trigger;
5113
- const MenuPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...props }, ref)=>{
5114
- const classNames = useClassNames();
5115
- return /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Positioner, {
5116
- ref: ref,
5117
- className: clsx(classNames.positioner, className),
5118
- ...props
5119
- });
5120
- });
5121
- MenuPositioner.displayName = "MenuPositioner";
5122
- const MenuContent = withContext$1(reactMenu.Menu.Content, "content");
5123
- const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
5124
- withContext$1(reactMenu.Menu.Group, "group");
5125
- withContext$1(reactMenu.Menu.GroupLabel, "groupLabel");
5126
- const MenuItem = /*#__PURE__*/ React__namespace.forwardRef(({ className: propClassName, ...props }, ref)=>{
5127
- const [variantProps, otherProps] = menuItem.menuItem.splitVariantProps(props);
5128
- const parentProps = useItemProps();
5129
- const classNames = menuItem.menuItem({
5130
- ...parentProps,
5131
- ...variantProps
5132
- });
5133
- return /*#__PURE__*/ jsxRuntime.jsx(ItemClassNamesProvider, {
5134
- value: classNames,
5135
- children: /*#__PURE__*/ jsxRuntime.jsx(reactMenu.Menu.Item, {
5136
- ref: ref,
5137
- className: clsx(classNames.root, propClassName),
5138
- ...otherProps
5139
- })
5418
+ ]
5140
5419
  });
5141
- });
5142
- MenuItem.displayName = "MenuItem";
5143
- withItemContext(withItemStateProps(Primitive.div), "body");
5144
- const MenuItemLabel = withItemContext(withItemStateProps(Primitive.span), "label");
5145
- withItemContext(withItemStateProps(Primitive.span), "description");
5420
+ }
5146
5421
 
5147
5422
  function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast }) {
5148
5423
  const effective = useEffectiveColumns(columns, {
@@ -5169,10 +5444,11 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
5169
5444
  })
5170
5445
  });
5171
5446
  }
5172
- function formatCalcValue(value) {
5173
- return value.toLocaleString(undefined, {
5447
+ function formatCalcValue(value, op) {
5448
+ const formatted = value.toLocaleString(undefined, {
5174
5449
  maximumFractionDigits: 2
5175
5450
  });
5451
+ return op === "percentEmpty" || op === "percentFilled" ? `${formatted}%` : formatted;
5176
5452
  }
5177
5453
  /**
5178
5454
  * One ColumnCalc footer cell -- a click-anywhere-in-the-cell trigger (crisp's own
@@ -5207,7 +5483,7 @@ function formatCalcValue(value) {
5207
5483
  children: [
5208
5484
  /*#__PURE__*/ jsxRuntime.jsx("span", {
5209
5485
  className: "seed-data-table-column-calc-value",
5210
- children: formatCalcValue(result)
5486
+ children: formatCalcValue(result, op)
5211
5487
  }),
5212
5488
  /*#__PURE__*/ jsxRuntime.jsx("span", {
5213
5489
  className: "seed-data-table-column-calc-op",
@@ -5228,21 +5504,13 @@ function formatCalcValue(value) {
5228
5504
  style: {
5229
5505
  minWidth: 160
5230
5506
  },
5231
- children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
5232
- children: [
5233
- ops.map((candidate)=>/*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
5234
- onClick: ()=>setColumnCalcOp(column.key, candidate),
5235
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
5236
- children: DATA_TABLE_CALC_LABEL[candidate]
5237
- })
5238
- }, candidate)),
5239
- op && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
5240
- onClick: ()=>setColumnCalcOp(column.key, undefined),
5507
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuScrollArea, {
5508
+ children: ops.map((candidate)=>/*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
5509
+ onClick: ()=>setColumnCalcOp(column.key, candidate),
5241
5510
  children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
5242
- children: "Clear"
5511
+ children: DATA_TABLE_CALC_LABEL[candidate]
5243
5512
  })
5244
- })
5245
- ]
5513
+ }, candidate))
5246
5514
  })
5247
5515
  })
5248
5516
  })
@@ -10507,6 +10775,18 @@ ContinuousDatePicker.displayName = "ContinuousDatePicker";
10507
10775
  // value list) down to `is`/`is not`/`empty`/`not empty`. `"isanyof"` is appended to any
10508
10776
  // of the four whenever the column supplies `filterValues` -- a faceted checkbox list,
10509
10777
  // independent of the column's own kind.
10778
+ //
10779
+ // Multi-select fix (DATATABLE-ATTIO-SPEC.md gap #12, live-measured 2026-09-30): a
10780
+ // `kind: "select"` column whose CELL is multi-valued (`type: "tags"`/`"chip-list"` --
10781
+ // a chip-list/tag column, not a single status pill) offers Attio's own
10782
+ // `contains`/`does not contain` instead of `is`/`is not` -- "is" reads as an exact-
10783
+ // set-equality check against an array value, which is not what Attio's own multi-
10784
+ // select filter means or what `applyToolbarFilters`' `is`/`isnot` branch (a plain
10785
+ // string-equality compare via `asText`) computes. `contains`/`doesnotcontain` are
10786
+ // already correct here unmodified: `applyToolbarFilters` already runs them against
10787
+ // `asText(raw)` (an array's own values joined), which is exactly "does any tag
10788
+ // match". Single-select (no `filterValues`-array cell type) keeps `is`/`is not` --
10789
+ // not re-measured against Attio, so left alone per the discovery doc's own hedge.
10510
10790
  ////////////////////////////////////////////////////////////////////////////////////
10511
10791
  const OP_ORDER = [
10512
10792
  "contains",
@@ -10540,9 +10820,19 @@ const SELECT_OP_ORDER = [
10540
10820
  "empty",
10541
10821
  "notempty"
10542
10822
  ];
10823
+ const MULTI_SELECT_OP_ORDER = [
10824
+ "contains",
10825
+ "doesnotcontain",
10826
+ "empty",
10827
+ "notempty"
10828
+ ];
10543
10829
  /** The operators a column of this `kind` offers, in display order -- `hasValues`
10544
- * (the column supplies `filterValues`) appends `"isanyof"` regardless of kind. */ function opsForKind(kind, hasValues) {
10545
- const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? SELECT_OP_ORDER : OP_ORDER;
10830
+ * (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
10831
+ * `multiValue` (the column's CELL type is a chip-list/tags column, not just its
10832
+ * filter `kind`) swaps `SELECT_OP_ORDER`'s `is`/`is not` for `contains`/`does not
10833
+ * contain`, matching Attio's own multi-select filter -- see the operator-sets
10834
+ * comment above. */ function opsForKind(kind, hasValues, multiValue = false) {
10835
+ const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? multiValue ? MULTI_SELECT_OP_ORDER : SELECT_OP_ORDER : OP_ORDER;
10546
10836
  return hasValues ? [
10547
10837
  ...base,
10548
10838
  "isanyof"
@@ -10910,66 +11200,58 @@ const formatFilterDate = (iso)=>{
10910
11200
  return `${OPERATOR_LABELS[filter.op]} ${value}`;
10911
11201
  }
10912
11202
  /**
10913
- * One filter condition, INSIDE the Filter popover -- a compact one-line summary row
10914
- * (field: operator value) with a trailing remove "x", expanding in place (accordion,
10915
- * not a second popover) to the field/operator/value editor when clicked. Presentation
10916
- * note (crisp/#593-style live re-check, 2026-09-28): Attio's own toolbar keeps active
10917
- * conditions as a compact "Filter · N" count on the trigger, not a row of always-on
10918
- * pill chips spread across the toolbar -- this replaces that first pass (which DID
10919
- * render one big chip per condition, directly in the toolbar row) with conditions
10920
- * living inside the ONE popover the trigger opens, the same list-of-rows shape
10921
- * `SortControl` above already uses for its own conditions.
10922
- */ function FilterConditionRow({ filter, columns, expanded, onToggle }) {
11203
+ * One filter condition, INSIDE the Filter popover -- Attio's own compact chip shape
11204
+ * (DATATABLE-ATTIO-SPEC.md gap #12, re-measured live 2026-09-30): `[attribute ▾]
11205
+ * [operator …] [value] [remove ×]`, ALL of it visible at once, never behind a click-
11206
+ * to-expand accordion. Supersedes this file's earlier 2026-09-28 note (which read
11207
+ * Attio as keeping conditions collapsed behind a bare "Filter · N" count and put them
11208
+ * in an accordion for that reason) -- the fresh read found the opposite: every active
11209
+ * condition shows attribute/operator/value simultaneously. This keeps the outer shell
11210
+ * (the "Filter"/"Filter · N" trigger opening ONE popover, `SortControl`'s own
11211
+ * list-of-rows shape) rather than moving chips into the toolbar row itself, so the
11212
+ * already-covered trigger/`Add filter` DOM (`DataTable.test.tsx`) stays put; only the
11213
+ * PER-CONDITION row tightens, from an accordion to an always-open card. The operator
11214
+ * set stays a row of small pill buttons (not a dropdown) so every existing test that
11215
+ * clicks an operator by its exact button name keeps working unchanged.
11216
+ */ function FilterConditionRow({ filter, columns }) {
10923
11217
  const { patchToolbarFilter, changeToolbarFilterField, removeToolbarFilter } = useDataTableContext();
10924
11218
  const column = columns.find((c)=>c.key === filter.key);
10925
11219
  const kind = column?.kind;
11220
+ const multiValue = column?.type === "tags" || column?.type === "chip-list";
10926
11221
  const values = column?.filterValues ?? [];
10927
- const ops = opsForKind(kind, values.length > 0);
10928
- return /*#__PURE__*/ jsxRuntime.jsxs("div", {
11222
+ const ops = opsForKind(kind, values.length > 0, multiValue);
11223
+ return /*#__PURE__*/ jsxRuntime.jsxs("fieldset", {
11224
+ "aria-label": `${column ? columnLabel(column) : filter.key} filter: ${conditionSummary(filter, column)}`,
10929
11225
  style: {
11226
+ margin: 0,
10930
11227
  display: "flex",
10931
11228
  flexDirection: "column",
10932
- gap: 6,
11229
+ gap: 8,
10933
11230
  borderRadius: 6,
10934
- background: expanded ? "var(--seed-color-bg-neutral-weak)" : "transparent",
10935
- padding: expanded ? 6 : 0
11231
+ border: "1px solid var(--seed-color-border-neutral)",
11232
+ background: "var(--seed-color-bg-neutral-weak)",
11233
+ padding: 6
10936
11234
  },
10937
11235
  children: [
10938
11236
  /*#__PURE__*/ jsxRuntime.jsxs("div", {
10939
11237
  style: {
10940
11238
  display: "flex",
10941
11239
  alignItems: "center",
10942
- gap: 2
11240
+ gap: 4
10943
11241
  },
10944
11242
  children: [
10945
- /*#__PURE__*/ jsxRuntime.jsxs("button", {
10946
- type: "button",
10947
- onClick: onToggle,
11243
+ /*#__PURE__*/ jsxRuntime.jsx(InlineSelect, {
11244
+ ariaLabel: "Filter by field",
11245
+ value: filter.key,
11246
+ onChange: (key)=>changeToolbarFilterField(filter.id, key),
11247
+ options: columns.map((c)=>({
11248
+ value: c.key,
11249
+ label: columnLabel(c)
11250
+ })),
10948
11251
  style: {
10949
11252
  flex: 1,
10950
- minWidth: 0,
10951
- textAlign: "left",
10952
- padding: "6px 8px",
10953
- border: "none",
10954
- borderRadius: 6,
10955
- background: "transparent",
10956
- cursor: "pointer",
10957
- fontSize: 13,
10958
- overflow: "hidden",
10959
- textOverflow: "ellipsis",
10960
- whiteSpace: "nowrap"
10961
- },
10962
- children: [
10963
- /*#__PURE__*/ jsxRuntime.jsx("strong", {
10964
- style: {
10965
- fontWeight: 500
10966
- },
10967
- children: column ? columnLabel(column) : filter.key
10968
- }),
10969
- ":",
10970
- " ",
10971
- conditionSummary(filter, column)
10972
- ]
11253
+ minWidth: 0
11254
+ }
10973
11255
  }),
10974
11256
  /*#__PURE__*/ jsxRuntime.jsx("button", {
10975
11257
  type: "button",
@@ -10983,136 +11265,103 @@ const formatFilterDate = (iso)=>{
10983
11265
  })
10984
11266
  ]
10985
11267
  }),
10986
- expanded && /*#__PURE__*/ jsxRuntime.jsxs("div", {
11268
+ /*#__PURE__*/ jsxRuntime.jsx("div", {
10987
11269
  style: {
10988
- padding: "0 8px 4px",
10989
11270
  display: "flex",
10990
- flexDirection: "column",
10991
- gap: 8
11271
+ flexWrap: "wrap",
11272
+ gap: 2
10992
11273
  },
10993
- children: [
10994
- /*#__PURE__*/ jsxRuntime.jsx("div", {
10995
- style: {
10996
- fontSize: 12,
10997
- color: "var(--seed-color-fg-neutral-subtle)"
10998
- },
10999
- children: "Field"
11000
- }),
11001
- /*#__PURE__*/ jsxRuntime.jsx(InlineSelect, {
11002
- ariaLabel: "Filter by field",
11003
- value: filter.key,
11004
- onChange: (key)=>changeToolbarFilterField(filter.id, key),
11005
- options: columns.map((c)=>({
11006
- value: c.key,
11007
- label: columnLabel(c)
11008
- }))
11009
- }),
11010
- /*#__PURE__*/ jsxRuntime.jsx("div", {
11011
- style: {
11012
- fontSize: 12,
11013
- color: "var(--seed-color-fg-neutral-subtle)"
11014
- },
11015
- children: "Condition"
11016
- }),
11017
- /*#__PURE__*/ jsxRuntime.jsx("div", {
11274
+ children: ops.map((op)=>/*#__PURE__*/ jsxRuntime.jsx("button", {
11275
+ type: "button",
11276
+ "data-active": filter.op === op ? "" : undefined,
11277
+ onClick: ()=>patchToolbarFilter(filter.id, {
11278
+ op
11279
+ }),
11018
11280
  style: {
11019
- display: "flex",
11020
- flexDirection: "column",
11021
- gap: 2
11281
+ textAlign: "left",
11282
+ padding: "4px 8px",
11283
+ borderRadius: 999,
11284
+ border: "none",
11285
+ cursor: "pointer",
11286
+ background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
11287
+ fontSize: 12
11022
11288
  },
11023
- children: ops.map((op)=>/*#__PURE__*/ jsxRuntime.jsx("button", {
11024
- type: "button",
11025
- "data-active": filter.op === op ? "" : undefined,
11026
- onClick: ()=>patchToolbarFilter(filter.id, {
11027
- op
11028
- }),
11029
- style: {
11030
- textAlign: "left",
11031
- padding: "6px 8px",
11032
- borderRadius: 6,
11033
- border: "none",
11034
- cursor: "pointer",
11035
- background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
11036
- fontSize: 13
11037
- },
11038
- children: OPERATOR_LABELS[op]
11039
- }, op))
11040
- }),
11041
- opNeedsValues(filter.op) && /*#__PURE__*/ jsxRuntime.jsx("div", {
11289
+ children: OPERATOR_LABELS[op]
11290
+ }, op))
11291
+ }),
11292
+ opNeedsValues(filter.op) && /*#__PURE__*/ jsxRuntime.jsx("div", {
11293
+ style: {
11294
+ display: "flex",
11295
+ flexDirection: "column",
11296
+ gap: 4,
11297
+ maxHeight: 160,
11298
+ overflow: "auto"
11299
+ },
11300
+ children: values.map((value)=>{
11301
+ const checked = (filter.values ?? []).includes(value);
11302
+ return /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
11303
+ checked: checked,
11304
+ onCheckedChange: ()=>patchToolbarFilter(filter.id, {
11305
+ values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
11306
+ ...filter.values ?? [],
11307
+ value
11308
+ ]
11309
+ }),
11042
11310
  style: {
11043
11311
  display: "flex",
11044
- flexDirection: "column",
11045
- gap: 4,
11046
- maxHeight: 160,
11047
- overflow: "auto"
11312
+ alignItems: "center",
11313
+ gap: 8,
11314
+ padding: "4px 0",
11315
+ cursor: "pointer"
11048
11316
  },
11049
- children: values.map((value)=>{
11050
- const checked = (filter.values ?? []).includes(value);
11051
- return /*#__PURE__*/ jsxRuntime.jsxs(CheckboxRoot, {
11052
- checked: checked,
11053
- onCheckedChange: ()=>patchToolbarFilter(filter.id, {
11054
- values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
11055
- ...filter.values ?? [],
11056
- value
11057
- ]
11058
- }),
11059
- style: {
11060
- display: "flex",
11061
- alignItems: "center",
11062
- gap: 8,
11063
- padding: "4px 0",
11064
- cursor: "pointer"
11065
- },
11066
- children: [
11067
- /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
11068
- children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
11069
- checked: /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkLine_default, {
11070
- size: 12
11071
- })
11072
- })
11073
- }),
11074
- /*#__PURE__*/ jsxRuntime.jsx(CheckboxLabel, {
11075
- children: value
11076
- }),
11077
- /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
11078
- "aria-label": value
11317
+ children: [
11318
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxControl, {
11319
+ children: /*#__PURE__*/ jsxRuntime.jsx(CheckboxIndicator, {
11320
+ checked: /*#__PURE__*/ jsxRuntime.jsx(IconCheckmarkLine_default, {
11321
+ size: 12
11079
11322
  })
11080
- ]
11081
- }, value);
11082
- })
11083
- }),
11084
- opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsxRuntime.jsx(DatePicker, {
11085
- value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
11086
- onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
11087
- value: dateToISO(fromDatePickerDate(d))
11088
- })
11089
- }),
11090
- opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsxRuntime.jsx(InlineSelect, {
11091
- ariaLabel: "Filter value",
11092
- placeholder: "Value…",
11093
- value: filter.value || undefined,
11094
- onChange: (value)=>patchToolbarFilter(filter.id, {
11095
- value
11323
+ })
11096
11324
  }),
11097
- options: values.map((value)=>({
11098
- value,
11099
- label: value
11100
- }))
11101
- }),
11102
- opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsxRuntime.jsx(TextFieldRoot, {
11103
- variant: "outline",
11104
- size: "medium",
11105
- children: /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
11106
- type: kind === "number" ? "number" : "text",
11107
- value: filter.value,
11108
- onChange: (e)=>patchToolbarFilter(filter.id, {
11109
- value: e.target.value
11110
- }),
11111
- placeholder: "Value…",
11112
- "aria-label": "Filter value"
11113
- })
11325
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxLabel, {
11326
+ children: value
11327
+ }),
11328
+ /*#__PURE__*/ jsxRuntime.jsx(CheckboxHiddenInput, {
11329
+ "aria-label": value
11330
+ })
11331
+ ]
11332
+ }, value);
11333
+ })
11334
+ }),
11335
+ opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsxRuntime.jsx(DatePicker, {
11336
+ value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
11337
+ onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
11338
+ value: dateToISO(fromDatePickerDate(d))
11114
11339
  })
11115
- ]
11340
+ }),
11341
+ opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsxRuntime.jsx(InlineSelect, {
11342
+ ariaLabel: "Filter value",
11343
+ placeholder: "Value…",
11344
+ value: filter.value || undefined,
11345
+ onChange: (value)=>patchToolbarFilter(filter.id, {
11346
+ value
11347
+ }),
11348
+ options: values.map((value)=>({
11349
+ value,
11350
+ label: value
11351
+ }))
11352
+ }),
11353
+ opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsxRuntime.jsx(TextFieldRoot, {
11354
+ variant: "outline",
11355
+ size: "medium",
11356
+ children: /*#__PURE__*/ jsxRuntime.jsx(TextFieldInput, {
11357
+ type: kind === "number" ? "number" : "text",
11358
+ value: filter.value,
11359
+ onChange: (e)=>patchToolbarFilter(filter.id, {
11360
+ value: e.target.value
11361
+ }),
11362
+ placeholder: "Value…",
11363
+ "aria-label": "Filter value"
11364
+ })
11116
11365
  })
11117
11366
  ]
11118
11367
  });
@@ -11120,15 +11369,14 @@ const formatFilterDate = (iso)=>{
11120
11369
  /**
11121
11370
  * The toolbar's Filter trigger + popover -- a compact `"Filter"` / `"Filter · N"`
11122
11371
  * button (matching `SortControl`'s own `"Sort"` / `"Sort · N"`), opening ONE popover
11123
- * that holds both the active conditions (compact rows, `FilterConditionRow` above,
11124
- * one expanded at a time) and, behind "+ Add filter", the field picker that appends a
11125
- * new one -- not a row of always-on pill chips spread across the toolbar (this
11126
- * replaces that first pass; see `FilterConditionRow`'s own doc comment).
11372
+ * that holds both the active conditions (compact chips, `FilterConditionRow` above,
11373
+ * ALL open at once -- no accordion) and, behind "+ Add filter", the field picker that
11374
+ * appends a new one. See `FilterConditionRow`'s own doc comment for why the chips
11375
+ * live inside this one popover rather than spread across the toolbar row itself.
11127
11376
  */ function FilterControl({ columns, label = "Filter" }) {
11128
11377
  const { toolbarFilters, addToolbarFilter } = useDataTableContext();
11129
11378
  const [open, setOpen] = React__namespace.useState(false);
11130
11379
  const [pickingField, setPickingField] = React__namespace.useState(false);
11131
- const [expandedId, setExpandedId] = React__namespace.useState(null);
11132
11380
  return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
11133
11381
  open: open,
11134
11382
  onOpenChange: (next)=>{
@@ -11190,8 +11438,7 @@ const formatFilterDate = (iso)=>{
11190
11438
  columns.map((c)=>/*#__PURE__*/ jsxRuntime.jsx("button", {
11191
11439
  type: "button",
11192
11440
  onClick: ()=>{
11193
- const id = addToolbarFilter(c.key);
11194
- setExpandedId(id);
11441
+ addToolbarFilter(c.key);
11195
11442
  setPickingField(false);
11196
11443
  },
11197
11444
  style: {
@@ -11224,9 +11471,7 @@ const formatFilterDate = (iso)=>{
11224
11471
  }),
11225
11472
  toolbarFilters.map((f)=>/*#__PURE__*/ jsxRuntime.jsx(FilterConditionRow, {
11226
11473
  filter: f,
11227
- columns: columns,
11228
- expanded: expandedId === f.id,
11229
- onToggle: ()=>setExpandedId((id)=>id === f.id ? null : f.id)
11474
+ columns: columns
11230
11475
  }, f.id)),
11231
11476
  /*#__PURE__*/ jsxRuntime.jsxs("button", {
11232
11477
  type: "button",
@@ -11255,8 +11500,8 @@ const formatFilterDate = (iso)=>{
11255
11500
  });
11256
11501
  }
11257
11502
 
11258
- const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
11259
- const DataTableCount = withContext$2(Primitive.span, "count");
11503
+ const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
11504
+ const DataTableCount = withContext$1(Primitive.span, "count");
11260
11505
  /**
11261
11506
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
11262
11507
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -11267,7 +11512,7 @@ const DataTableCount = withContext$2(Primitive.span, "count");
11267
11512
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
11268
11513
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
11269
11514
  * `toolbarActions` is passed.
11270
- */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
11515
+ */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
11271
11516
  /**
11272
11517
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
11273
11518
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -11317,7 +11562,7 @@ function RowCount({ shown, total, countLabel }) {
11317
11562
  }
11318
11563
 
11319
11564
  function DataTableInner(props, ref) {
11320
- const { columns, data, surface, className, locale = "en-US", sort, defaultSort, onSortChange, sortIcon, selectable, selectedIds, defaultSelectedIds, onSelectionChange, selectAllLabel = "Select all rows", getRowSelectionLabel = ()=>"Select row", selectionCheckedIcon, selectionIndeterminateIcon, query, defaultQuery, onQueryChange, searchPlaceholder = "Search…", searchLabel = "Search table", toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, showCount = true, countLabel, toolbarActions, columnMenu = false, reorderable = true, resizable = true, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, onColumnsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, stickyFirst = false, stickyLast = false, onEdit, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", loading = false, loadingLabel = "Loading...", error, empty = "No results.", onLoadMore, hasMore, loadingMore, scrollProps, ...restProps } = props;
11565
+ const { columns, data, surface, className, locale = "en-US", sort, defaultSort, onSortChange, sortIcon, selectable, selectedIds, defaultSelectedIds, onSelectionChange, selectAllLabel = "Select all rows", getRowSelectionLabel = ()=>"Select row", selectionCheckedIcon, selectionIndeterminateIcon, bulkActions, clearSelectionLabel, query, defaultQuery, onQueryChange, searchPlaceholder = "Search…", searchLabel = "Search table", toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, showCount = true, countLabel, toolbarActions, columnMenu = false, reorderable = true, resizable = true, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, onColumnsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, stickyFirst = false, stickyLast = false, onEdit, cellContextMenu = true, onViewEditHistory, cellContextMenuItems, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", loading = false, loadingLabel = "Loading...", error, empty = "No results.", onLoadMore, hasMore, loadingMore, scrollProps, ...restProps } = props;
11321
11566
  const classNames = dataTable({
11322
11567
  surface,
11323
11568
  interactive
@@ -11441,7 +11686,7 @@ function DataTableInner(props, ref) {
11441
11686
  // loading/error/empty body, infinite scroll's sentinel, the add-row footer) reads
11442
11687
  // this ONE value rather than each re-deriving it, so they can never drift apart.
11443
11688
  const totalCols = columns.length + (selectable ? 1 : 0);
11444
- return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider$1, {
11689
+ return /*#__PURE__*/ jsxRuntime.jsx(ClassNamesProvider, {
11445
11690
  value: classNames,
11446
11691
  children: /*#__PURE__*/ jsxRuntime.jsxs(DataTableRoot, {
11447
11692
  ref: ref,
@@ -11597,7 +11842,10 @@ function DataTableInner(props, ref) {
11597
11842
  locale: locale,
11598
11843
  selectable: !!selectable,
11599
11844
  stickyFirst: stickyFirst,
11600
- stickyLast: resolvedStickyLast
11845
+ stickyLast: resolvedStickyLast,
11846
+ cellContextMenu: cellContextMenu,
11847
+ onViewEditHistory: onViewEditHistory,
11848
+ cellContextMenuItems: cellContextMenuItems
11601
11849
  })
11602
11850
  ]
11603
11851
  }, row.id)),
@@ -11636,6 +11884,10 @@ function DataTableInner(props, ref) {
11636
11884
  })
11637
11885
  ]
11638
11886
  })
11887
+ }),
11888
+ selectable && /*#__PURE__*/ jsxRuntime.jsx(DataTableBulkBar, {
11889
+ clearLabel: clearSelectionLabel,
11890
+ children: bulkActions
11639
11891
  })
11640
11892
  ]
11641
11893
  })
@@ -11689,7 +11941,7 @@ DataTableInner.displayName = "DataTable";
11689
11941
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
11690
11942
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
11691
11943
  // child without the HTML parser foster-parenting it back out.
11692
- const DataTableTable = withContext$2(DataTableTable$1, "table");
11944
+ const DataTableTable = withContext$1(DataTableTable$1, "table");
11693
11945
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
11694
11946
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
11695
11947
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -11726,15 +11978,15 @@ const DataTableTable = withContext$2(DataTableTable$1, "table");
11726
11978
  ]
11727
11979
  });
11728
11980
  }
11729
- const DataTableHeader = withContext$2(DataTableHeader$1, "header");
11730
- const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
11981
+ const DataTableHeader = withContext$1(DataTableHeader$1, "header");
11982
+ const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
11731
11983
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
11732
11984
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
11733
11985
  // for why this lives one level below the cell itself rather than on it.
11734
- const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
11986
+ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
11735
11987
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
11736
11988
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
11737
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
11989
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
11738
11990
  /**
11739
11991
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
11740
11992
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -11745,7 +11997,7 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
11745
11997
  * a second recipe call.
11746
11998
  */ function withSelectionCell(Component, baseSlot) {
11747
11999
  const StyledComponent = /*#__PURE__*/ React.forwardRef((props, ref)=>{
11748
- const classNames = useClassNames$1();
12000
+ const classNames = useClassNames();
11749
12001
  return /*#__PURE__*/ jsxRuntime.jsx(Component, {
11750
12002
  ref: ref,
11751
12003
  ...props,
@@ -11756,12 +12008,12 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
11756
12008
  return StyledComponent;
11757
12009
  }
11758
12010
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
11759
- const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
12011
+ const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
11760
12012
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
11761
12013
  // `<tbody>`, so it is not wrapped in `withContext`.
11762
12014
  const DataTableBody = DataTableBody$1;
11763
- const DataTableRow = withContext$2(DataTableRow$1, "row");
11764
- const DataTableCell = withContext$2(DataTableCell$1, "cell");
12015
+ const DataTableRow = withContext$1(DataTableRow$1, "row");
12016
+ const DataTableCell = withContext$1(DataTableCell$1, "cell");
11765
12017
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
11766
12018
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
11767
12019
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -11771,7 +12023,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
11771
12023
  // stays out of this slot's own base css (nothing here has a bounded height of its own
11772
12024
  // to clip), so it never introduces a second vertical scrollbar next to whatever
11773
12025
  // ambient container the whole DataTable already sits in.
11774
- const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
12026
+ const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
11775
12027
  function DataTableScrollBody({ children, scrollProps }) {
11776
12028
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
11777
12029
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -11786,7 +12038,7 @@ function DataTableScrollBody({ children, scrollProps }) {
11786
12038
  children: children
11787
12039
  });
11788
12040
  }
11789
- const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
12041
+ const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
11790
12042
  function DataTableFooter({ children, ...props }) {
11791
12043
  return /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterBase, {
11792
12044
  ...props,
@@ -11795,7 +12047,7 @@ function DataTableFooter({ children, ...props }) {
11795
12047
  })
11796
12048
  });
11797
12049
  }
11798
- const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
12050
+ const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
11799
12051
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
11800
12052
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
11801
12053
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -15733,6 +15985,7 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
15733
15985
  RecordPreviewPanel.displayName = "RecordPreviewPanel";
15734
15986
 
15735
15987
  exports.DataTable = DataTable;
15988
+ exports.DataTableBulkBar = DataTableBulkBar;
15736
15989
  exports.DataTableViewsControl = DataTableViewsControl;
15737
15990
  exports.RecordPreview = RecordPreview;
15738
15991
  exports.ResizablePanel = ResizablePanel;