@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.js CHANGED
@@ -18,6 +18,9 @@ import { Checkbox, useCheckboxContext } from '@seed-design/react-checkbox';
18
18
  import { useScaleFeedback } from '@seed-design/react-scale-feedback';
19
19
  import { checkboxGroup } from '@seed-design/css/recipes/checkbox-group';
20
20
  import { createPortal } from 'react-dom';
21
+ import { Menu, useMenuContext, useMenuItemContext } from '@seed-design/react-menu';
22
+ import { menu } from '@seed-design/css/recipes/menu';
23
+ import { menuItem } from '@seed-design/css/recipes/menu-item';
21
24
  import { Image } from '@seed-design/react-image';
22
25
  import { avatar } from '@seed-design/css/recipes/avatar';
23
26
  import '@seed-design/css/recipes/avatar-stack';
@@ -32,9 +35,6 @@ import { switchStyle } from '@seed-design/css/recipes/switch';
32
35
  import { switchmark } from '@seed-design/css/recipes/switchmark';
33
36
  import { TextField, useTextFieldContext } from '@seed-design/react-text-field';
34
37
  import { textInput } from '@seed-design/css/recipes/text-input';
35
- import { Menu, useMenuContext, useMenuItemContext } from '@seed-design/react-menu';
36
- import { menu } from '@seed-design/css/recipes/menu';
37
- import { menuItem } from '@seed-design/css/recipes/menu-item';
38
38
  import { tabs } from '@seed-design/css/recipes/tabs';
39
39
 
40
40
  // src/IconArrowUpLine.tsx
@@ -761,7 +761,10 @@ function isFunction$1(value) {
761
761
  const DATA_TABLE_UNIVERSAL_CALC_OPS = [
762
762
  "count",
763
763
  "empty",
764
- "notEmpty"
764
+ "notEmpty",
765
+ "percentEmpty",
766
+ "percentFilled",
767
+ "none"
765
768
  ];
766
769
  const DATA_TABLE_NUMERIC_CALC_OPS = [
767
770
  "sum",
@@ -776,8 +779,11 @@ const DATA_TABLE_NUMERIC_CALC_OPS = [
776
779
  ];
777
780
  const DATA_TABLE_CALC_LABEL = {
778
781
  count: "Count",
779
- empty: "Empty",
780
- notEmpty: "Not empty",
782
+ empty: "Count empty",
783
+ notEmpty: "Count filled",
784
+ percentEmpty: "Percent empty",
785
+ percentFilled: "Percent filled",
786
+ none: "None",
781
787
  sum: "Sum",
782
788
  average: "Average",
783
789
  median: "Median",
@@ -807,7 +813,12 @@ const DATA_TABLE_CALC_LABEL = {
807
813
  * (`JSON.stringify`-normalized, so two arrays/objects with the same shape count
808
814
  * once) -- empties are already their own op (`empty`), so folding them into "one
809
815
  * more unique value" here would double-count the same information two different
810
- * ways in the same menu.
816
+ * ways in the same menu. `percentEmpty`/`percentFilled` are `empty`/`notEmpty` as a
817
+ * share of the row count (0-100, unrounded -- formatting rounds it for display) --
818
+ * `null` on zero rows, since 0/0 has no meaningful percentage (not 0%, not 100%).
819
+ * `none` always renders nothing: it is Attio's own "no calculation" menu entry, not a
820
+ * separate always-appended "Clear" item, and picking it is a real, storable op so it
821
+ * overrides a `summaryDefaultOp` instead of falling back to it.
811
822
  */ function computeColumnCalc(values, op) {
812
823
  switch(op){
813
824
  case "count":
@@ -816,6 +827,14 @@ const DATA_TABLE_CALC_LABEL = {
816
827
  return values.filter(isEmptyValueForCalc).length;
817
828
  case "notEmpty":
818
829
  return values.filter((value)=>!isEmptyValueForCalc(value)).length;
830
+ case "percentEmpty":
831
+ if (values.length === 0) return null;
832
+ return values.filter(isEmptyValueForCalc).length / values.length * 100;
833
+ case "percentFilled":
834
+ if (values.length === 0) return null;
835
+ return values.filter((value)=>!isEmptyValueForCalc(value)).length / values.length * 100;
836
+ case "none":
837
+ return null;
819
838
  case "countUnique":
820
839
  {
821
840
  const seen = new Set(values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>JSON.stringify(value)));
@@ -1634,6 +1653,17 @@ function useDataTableSelection(props) {
1634
1653
  rowIds,
1635
1654
  setSelectedIds
1636
1655
  ]);
1656
+ // Bulk-action bar's own "×" (gap #4, DATATABLE-ATTIO-SPEC's Attio review) --
1657
+ // deliberately its own function rather than reusing `toggleAll`: `toggleAll` only
1658
+ // clears from an "all" selection, and otherwise SELECTS every row (`selectionState
1659
+ // === "some"` -> `rowIds`, above) -- exactly the wrong direction for a "clear
1660
+ // selection" affordance shown while `selectionState` is "some". This always empties
1661
+ // the set, regardless of `selectionState`.
1662
+ const clearSelection = useCallback(()=>{
1663
+ setSelectedIds([]);
1664
+ }, [
1665
+ setSelectedIds
1666
+ ]);
1637
1667
  const getRowProps = useCallback((rowId)=>{
1638
1668
  const isRowSelected = isSelected(rowId);
1639
1669
  return elementProps({
@@ -1652,6 +1682,7 @@ function useDataTableSelection(props) {
1652
1682
  isSelected,
1653
1683
  toggleRow,
1654
1684
  toggleAll,
1685
+ clearSelection,
1655
1686
  getRowProps
1656
1687
  };
1657
1688
  }
@@ -2660,11 +2691,70 @@ CheckboxIndicator.displayName = "CheckboxIndicator";
2660
2691
  const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2661
2692
  const CheckboxHiddenInput = Checkbox.HiddenInput;
2662
2693
 
2663
- const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar);
2694
+ const { ClassNamesProvider: ClassNamesProvider$2, withContext: withContext$5, useClassNames: useClassNames$2 } = createSlotRecipeContext(menu);
2695
+ const { PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, ClassNamesProvider: ItemClassNamesProvider$1 } = createSlotRecipeContext(menuItem);
2696
+ const withStateProps$3 = createWithStateProps([
2697
+ useMenuContext
2698
+ ]);
2699
+ const withItemStateProps$1 = createWithStateProps([
2700
+ useMenuItemContext
2701
+ ]);
2702
+ const MenuRoot = (props)=>{
2703
+ const [variantProps, otherProps] = menu.splitVariantProps(props);
2704
+ const classNames = menu(variantProps);
2705
+ return /*#__PURE__*/ jsx(ClassNamesProvider$2, {
2706
+ value: classNames,
2707
+ children: /*#__PURE__*/ jsx(ItemPropsProvider$1, {
2708
+ value: {
2709
+ size: variantProps.size
2710
+ },
2711
+ children: /*#__PURE__*/ jsx(Menu.Root, {
2712
+ ...otherProps
2713
+ })
2714
+ })
2715
+ });
2716
+ };
2717
+ const MenuAnchor = Menu.Anchor;
2718
+ const MenuTrigger = Menu.Trigger;
2719
+ const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
2720
+ const classNames = useClassNames$2();
2721
+ return /*#__PURE__*/ jsx(Menu.Positioner, {
2722
+ ref: ref,
2723
+ className: clsx(classNames.positioner, className),
2724
+ ...props
2725
+ });
2726
+ });
2727
+ MenuPositioner.displayName = "MenuPositioner";
2728
+ const MenuContent = withContext$5(Menu.Content, "content");
2729
+ const MenuScrollArea = withContext$5(withStateProps$3(Primitive.div), "scrollArea");
2730
+ withContext$5(Menu.Group, "group");
2731
+ withContext$5(Menu.GroupLabel, "groupLabel");
2732
+ const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
2733
+ const [variantProps, otherProps] = menuItem.splitVariantProps(props);
2734
+ const parentProps = useItemProps$1();
2735
+ const classNames = menuItem({
2736
+ ...parentProps,
2737
+ ...variantProps
2738
+ });
2739
+ return /*#__PURE__*/ jsx(ItemClassNamesProvider$1, {
2740
+ value: classNames,
2741
+ children: /*#__PURE__*/ jsx(Menu.Item, {
2742
+ ref: ref,
2743
+ className: clsx(classNames.root, propClassName),
2744
+ ...otherProps
2745
+ })
2746
+ });
2747
+ });
2748
+ MenuItem.displayName = "MenuItem";
2749
+ withItemContext$1(withItemStateProps$1(Primitive.div), "body");
2750
+ const MenuItemLabel = withItemContext$1(withItemStateProps$1(Primitive.span), "label");
2751
+ withItemContext$1(withItemStateProps$1(Primitive.span), "description");
2752
+
2753
+ const { withProvider: withProvider$2, withContext: withContext$4 } = createSlotRecipeContext(avatar);
2664
2754
  const AvatarRoot = withProvider$2(Image.Root, "root");
2665
- const AvatarImage = withContext$5(Image.Content, "image");
2666
- const AvatarFallback = withContext$5(Image.Fallback, "fallback");
2667
- withContext$5(Primitive.div, "badge");
2755
+ const AvatarImage = withContext$4(Image.Content, "image");
2756
+ const AvatarFallback = withContext$4(Image.Fallback, "fallback");
2757
+ withContext$4(Primitive.div, "badge");
2668
2758
 
2669
2759
  const Badge = /*#__PURE__*/ forwardRef(({ className, children, ...props }, ref)=>{
2670
2760
  const [variantProps, restProps] = badge.splitVariantProps(props);
@@ -3208,6 +3298,141 @@ function RatingCell({ value, maxRating = 5 }) {
3208
3298
  });
3209
3299
  }
3210
3300
 
3301
+ /** Clear is withheld for `chip-list`: that column already owns its own per-tag remove
3302
+ * affordance (`ChipListEditor`) and commits a JSON-STRINGIFIED array through `onEdit`
3303
+ * (`DataTable.chips.test.tsx`'s own `TagsHarness` -- and this package's own Storybook
3304
+ * demo, `CellFeaturesDemo`, does exactly `JSON.parse(value)` on commit) -- committing
3305
+ * a bare `""` through that same path throws in the consumer's `onEdit`, it doesn't
3306
+ * clear anything. Every OTHER editable shape's commit value is a plain string
3307
+ * (`"true"`/`"false"`, a stringified rating, an option's own `value`, free text) that
3308
+ * a consumer's `onEdit` already has to accept a possibly-empty version of. */ function isClearableEditableCell(column) {
3309
+ return !!column.editable && column.type !== "chip-list";
3310
+ }
3311
+ /** Paste is narrower still -- restricted to a PLAIN `editable` column (no `editor`,
3312
+ * and not one of the always-live widget types) whose committed value IS the raw
3313
+ * text/number a user typed. `select`/`reference`/`actor` expect one of THEIR OWN
3314
+ * option values (not arbitrary clipboard text), `switch`/`rating`/`checkbox` are
3315
+ * always-live widgets with their own encoding, and `chip-list` is excluded for the
3316
+ * same JSON reason `isClearableEditableCell` documents above. This is the task's own
3317
+ * "only for simple editable text/number cells" guard -- committing pasted text
3318
+ * through any of the excluded shapes would corrupt the row rather than edit it
3319
+ * (crispy-seed's own #1094 lesson: never assume an editor's commit shape).
3320
+ */ function isSimpleEditableCell(column) {
3321
+ if (!column.editable) return false;
3322
+ if (column.editor) return false;
3323
+ if (column.type === "chip-list" || column.type === "checkbox" || column.type === "rating") {
3324
+ return false;
3325
+ }
3326
+ return true;
3327
+ }
3328
+ /** Copy's own text representation -- deliberately mirrors `DisplayCellContent`'s
3329
+ * (body-cells.tsx) switch closely enough to read the same as what's on screen, but
3330
+ * returns a plain string (`navigator.clipboard.writeText`) rather than JSX. Not
3331
+ * exported -- it exists only to feed Copy, not as a general-purpose formatter. */ function cellValueAsText(column, value, locale) {
3332
+ if (value == null) return "";
3333
+ switch(column.type){
3334
+ case "entity":
3335
+ if (typeof value === "object") return String(value.name ?? "");
3336
+ return String(value);
3337
+ case "tags":
3338
+ case "chip-list":
3339
+ return Array.isArray(value) ? value.join(", ") : String(value);
3340
+ case "number":
3341
+ return typeof value === "number" ? String(formatNumberCell(value, locale) ?? "") : String(value);
3342
+ case "currency":
3343
+ return typeof value === "number" ? String(formatCurrencyCell(value, column.symbol ?? "$", locale) ?? "") : String(value);
3344
+ case "date":
3345
+ return String(formatDateCell(value, locale) ?? "");
3346
+ case "datetime":
3347
+ return String(formatDateTimeCell(value, locale) ?? "");
3348
+ case "checkbox":
3349
+ return value === true || value === "true" ? "true" : "false";
3350
+ default:
3351
+ return String(value);
3352
+ }
3353
+ }
3354
+ /**
3355
+ * One cell's right-click menu -- mounted by `body-cells.tsx`'s `DataTableBodyCell`
3356
+ * only while actually open (the same "don't keep an unused floating-ui instance alive
3357
+ * per cell" reasoning as that file's own portaled chip-list popover), so this never
3358
+ * costs anything for the row×column cells a user never right-clicks.
3359
+ */ function CellContextMenu({ column, row, value, locale, point, open, onOpenChange, onCommit, onViewEditHistory, cellContextMenuItems }) {
3360
+ const clearable = isClearableEditableCell(column);
3361
+ const pasteable = isSimpleEditableCell(column);
3362
+ return /*#__PURE__*/ jsxs(MenuRoot, {
3363
+ open: open,
3364
+ onOpenChange: onOpenChange,
3365
+ placement: "bottom-start",
3366
+ strategy: "fixed",
3367
+ gutter: 2,
3368
+ children: [
3369
+ /*#__PURE__*/ jsx(MenuAnchor, {
3370
+ "aria-hidden": true,
3371
+ style: {
3372
+ position: "fixed",
3373
+ top: point.y,
3374
+ left: point.x,
3375
+ width: 0,
3376
+ height: 0,
3377
+ pointerEvents: "none"
3378
+ }
3379
+ }),
3380
+ /*#__PURE__*/ jsx(MenuPositioner, {
3381
+ children: /*#__PURE__*/ jsx(MenuContent, {
3382
+ style: {
3383
+ minWidth: 180
3384
+ },
3385
+ children: /*#__PURE__*/ jsxs(MenuScrollArea, {
3386
+ children: [
3387
+ /*#__PURE__*/ jsx(MenuItem, {
3388
+ onClick: ()=>{
3389
+ const text = cellValueAsText(column, value, locale);
3390
+ // Best-effort: an insecure context or a denied permission rejects
3391
+ // this silently (no `MenuItem` state depends on the outcome) --
3392
+ // there's nothing else for a plain "Copy" item to do about a
3393
+ // browser that refused the write.
3394
+ void navigator.clipboard?.writeText?.(text);
3395
+ },
3396
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
3397
+ children: "Copy"
3398
+ })
3399
+ }),
3400
+ pasteable && /*#__PURE__*/ jsx(MenuItem, {
3401
+ onClick: ()=>{
3402
+ void navigator.clipboard?.readText?.().then((text)=>onCommit(text)).catch(()=>{
3403
+ // Clipboard read denied/unavailable -- best-effort, same
3404
+ // reasoning as Copy above.
3405
+ });
3406
+ },
3407
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
3408
+ children: "Paste"
3409
+ })
3410
+ }),
3411
+ clearable && /*#__PURE__*/ jsx(MenuItem, {
3412
+ onClick: ()=>onCommit(""),
3413
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
3414
+ children: "Clear value"
3415
+ })
3416
+ }),
3417
+ onViewEditHistory && /*#__PURE__*/ jsx(MenuItem, {
3418
+ onClick: ()=>onViewEditHistory(row, column.key),
3419
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
3420
+ children: "View edit history"
3421
+ })
3422
+ }),
3423
+ cellContextMenuItems?.({
3424
+ row,
3425
+ column,
3426
+ value
3427
+ })
3428
+ ]
3429
+ })
3430
+ })
3431
+ })
3432
+ ]
3433
+ });
3434
+ }
3435
+
3211
3436
  /** Fallback width (px) used to compute a pinned column's cumulative sticky offset when
3212
3437
  * it has no explicit `width` and hasn't been drag-resized -- pinning needs a STATIC
3213
3438
  * number to put in an inline `insetInlineStart`, and `table-layout: fixed` only
@@ -3366,11 +3591,11 @@ function RatingCell({ value, maxRating = 5 }) {
3366
3591
  // recipe, so a per-instance variant can override the root's default.
3367
3592
  const { ClassNamesProvider: TriggerClassNamesProvider, PropsProvider: TriggerPropsProvider, useProps: useTriggerProps, withContext: withTriggerContext, useClassNames: useTriggerClassNames } = createSlotRecipeContext(selectTrigger);
3368
3593
  const { ClassNamesProvider: ContentClassNamesProvider, withContext: withContentContext, useClassNames: useContentClassNames } = createSlotRecipeContext(select);
3369
- const { ClassNamesProvider: ItemClassNamesProvider$1, PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem);
3370
- const withStateProps$3 = createWithStateProps([
3594
+ const { ClassNamesProvider: ItemClassNamesProvider, PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem);
3595
+ const withStateProps$2 = createWithStateProps([
3371
3596
  useSelectContext
3372
3597
  ]);
3373
- const withItemStateProps$1 = createWithStateProps([
3598
+ const withItemStateProps = createWithStateProps([
3374
3599
  useSelectItemContext
3375
3600
  ]);
3376
3601
  // NOTE: `gutter` and `overflowPadding` are specified in Rootage (`select.yaml`,
@@ -3391,7 +3616,7 @@ const SelectRoot = (props)=>{
3391
3616
  value: contentClassNames,
3392
3617
  children: /*#__PURE__*/ jsx(TriggerPropsProvider, {
3393
3618
  value: triggerVariantProps,
3394
- children: /*#__PURE__*/ jsx(ItemPropsProvider$1, {
3619
+ children: /*#__PURE__*/ jsx(ItemPropsProvider, {
3395
3620
  value: itemVariantProps,
3396
3621
  children: /*#__PURE__*/ jsx(Select.Root, {
3397
3622
  ...otherProps
@@ -3431,8 +3656,8 @@ const SelectTrigger = /*#__PURE__*/ React.forwardRef(({ className, ...props }, r
3431
3656
  });
3432
3657
  });
3433
3658
  SelectTrigger.displayName = "SelectTrigger";
3434
- const SelectValue = withTriggerContext(withStateProps$3(Select.Value), "value");
3435
- const SelectPlaceholder = withTriggerContext(withStateProps$3(Select.Placeholder), "placeholder");
3659
+ const SelectValue = withTriggerContext(withStateProps$2(Select.Value), "value");
3660
+ const SelectPlaceholder = withTriggerContext(withStateProps$2(Select.Placeholder), "placeholder");
3436
3661
  const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className, ...otherProps }, ref)=>{
3437
3662
  const { selectedItem, stateProps } = useSelectContext();
3438
3663
  const classNames = useTriggerClassNames();
@@ -3447,7 +3672,7 @@ const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className,
3447
3672
  });
3448
3673
  });
3449
3674
  SelectPrefixIcon.displayName = "SelectPrefixIcon";
3450
- const SelectSuffixIcon = withTriggerContext(withStateProps$3(InternalIcon), "suffixIcon");
3675
+ const SelectSuffixIcon = withTriggerContext(withStateProps$2(InternalIcon), "suffixIcon");
3451
3676
  const SelectPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
3452
3677
  const classNames = useContentClassNames();
3453
3678
  return /*#__PURE__*/ jsx(Select.Positioner, {
@@ -3478,12 +3703,12 @@ withContentContext(Select.Group, "group");
3478
3703
  withContentContext(Select.GroupLabel, "groupLabel");
3479
3704
  const SelectItem = /*#__PURE__*/ React.forwardRef(({ prefixIcon, className, ...props }, ref)=>{
3480
3705
  const [variantProps, otherProps] = selectItem.splitVariantProps(props);
3481
- const rootProps = useItemProps$1();
3706
+ const rootProps = useItemProps();
3482
3707
  const classNames = selectItem({
3483
3708
  ...rootProps,
3484
3709
  ...variantProps
3485
3710
  });
3486
- return /*#__PURE__*/ jsx(ItemClassNamesProvider$1, {
3711
+ return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
3487
3712
  value: classNames,
3488
3713
  children: /*#__PURE__*/ jsx(Select.Item, {
3489
3714
  ref: ref,
@@ -3508,7 +3733,7 @@ const SelectItemPrefixIcon = /*#__PURE__*/ React.forwardRef(({ svg: svgOverride,
3508
3733
  });
3509
3734
  });
3510
3735
  SelectItemPrefixIcon.displayName = "SelectItemPrefixIcon";
3511
- const SelectItemBody = withItemContext$1(withItemStateProps$1(Primitive.div), "body");
3736
+ const SelectItemBody = withItemContext(withItemStateProps(Primitive.div), "body");
3512
3737
  const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, ...otherProps }, ref)=>{
3513
3738
  const { label, stateProps } = useSelectItemContext();
3514
3739
  const classNames = useItemClassNames();
@@ -3523,7 +3748,7 @@ const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, .
3523
3748
  });
3524
3749
  });
3525
3750
  SelectItemLabel.displayName = "SelectItemLabel";
3526
- withItemContext$1(withItemStateProps$1(Primitive.span), "description");
3751
+ withItemContext(withItemStateProps(Primitive.span), "description");
3527
3752
  const SelectItemIndicator = /*#__PURE__*/ React.forwardRef(({ selected: selectedSvg, unselected: unselectedSvg, className, ...otherProps }, ref)=>{
3528
3753
  const { isSelected, stateProps } = useSelectItemContext();
3529
3754
  const classNames = useItemClassNames();
@@ -3555,9 +3780,9 @@ const SelectHiddenSelect = /*#__PURE__*/ React.forwardRef((props, ref)=>{
3555
3780
  });
3556
3781
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3557
3782
 
3558
- const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(switchStyle);
3783
+ const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$1 } = createSlotRecipeContext(switchStyle);
3559
3784
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark);
3560
- const withStateProps$2 = createWithStateProps([
3785
+ const withStateProps$1 = createWithStateProps([
3561
3786
  useSwitchContext
3562
3787
  ]);
3563
3788
  const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...props }, ref)=>{
@@ -3568,7 +3793,7 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
3568
3793
  const classNames = switchStyle(switchVariantProps);
3569
3794
  return /*#__PURE__*/ jsx(ControlPropsProvider, {
3570
3795
  value: switchmarkVariantProps,
3571
- children: /*#__PURE__*/ jsx(ClassNamesProvider$2, {
3796
+ children: /*#__PURE__*/ jsx(ClassNamesProvider$1, {
3572
3797
  value: classNames,
3573
3798
  children: /*#__PURE__*/ jsx(Switch.Root, {
3574
3799
  ref: ref,
@@ -3581,20 +3806,20 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
3581
3806
  SwitchRoot.displayName = "SwitchRoot";
3582
3807
  const SwitchControl = withScaleFeedback(withControlProvider(Switch.Control, "root"));
3583
3808
  const SwitchThumb = withControlContext(Switch.Thumb, "thumb");
3584
- withContext$4(withStateProps$2(Primitive.span), "label");
3809
+ withContext$3(withStateProps$1(Primitive.span), "label");
3585
3810
  const SwitchHiddenInput = Switch.HiddenInput;
3586
3811
 
3587
3812
  // src/use-layout-effect.tsx
3588
3813
  var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
3589
3814
 
3590
- const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput);
3815
+ const { withProvider: withProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(textInput);
3591
3816
  const withFieldStateProps = createWithStateProps([
3592
3817
  {
3593
3818
  useContext: useFieldContext,
3594
3819
  strict: false
3595
3820
  }
3596
3821
  ]);
3597
- const withStateProps$1 = createWithStateProps([
3822
+ const withStateProps = createWithStateProps([
3598
3823
  useTextFieldContext,
3599
3824
  {
3600
3825
  useContext: useFieldContext,
@@ -3602,12 +3827,12 @@ const withStateProps$1 = createWithStateProps([
3602
3827
  }
3603
3828
  ]);
3604
3829
  const TextFieldRoot = withProvider$1(withFieldStateProps(TextField.Root), "root");
3605
- withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3606
- withContext$3(withStateProps$1(Primitive.span), "prefixText");
3607
- withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3608
- withContext$3(withStateProps$1(Primitive.span), "suffixText");
3830
+ withContext$2(withStateProps(InternalIcon), "prefixIcon");
3831
+ withContext$2(withStateProps(Primitive.span), "prefixText");
3832
+ withContext$2(withStateProps(InternalIcon), "suffixIcon");
3833
+ withContext$2(withStateProps(Primitive.span), "suffixText");
3609
3834
  const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, ref)=>{
3610
- const classNames = useClassNames$2();
3835
+ const classNames = useClassNames$1();
3611
3836
  const textFieldContext = useTextFieldContext();
3612
3837
  const fieldContext = useFieldContext({
3613
3838
  strict: false
@@ -3625,7 +3850,7 @@ const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, r
3625
3850
  });
3626
3851
  TextFieldInput.displayName = "TextFieldInput";
3627
3852
  const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = true, ...otherProps }, ref)=>{
3628
- const classNames = useClassNames$2();
3853
+ const classNames = useClassNames$1();
3629
3854
  const textFieldContext = useTextFieldContext();
3630
3855
  const fieldContext = useFieldContext({
3631
3856
  strict: false
@@ -3682,7 +3907,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
3682
3907
  });
3683
3908
  TextFieldTextarea.displayName = "TextFieldTextarea";
3684
3909
 
3685
- __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))}");
3910
+ __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))}");
3686
3911
 
3687
3912
  const createClassName = (className, variants, compoundVariants = [])=>{
3688
3913
  const variantKeys = Object.keys(variants);
@@ -3876,6 +4101,22 @@ const dataTableSlotNames = [
3876
4101
  [
3877
4102
  "selectionCell",
3878
4103
  "seed-data-table__selectionCell"
4104
+ ],
4105
+ [
4106
+ "bulkBar",
4107
+ "seed-data-table__bulkBar"
4108
+ ],
4109
+ [
4110
+ "bulkBarCount",
4111
+ "seed-data-table__bulkBarCount"
4112
+ ],
4113
+ [
4114
+ "bulkBarClear",
4115
+ "seed-data-table__bulkBarClear"
4116
+ ],
4117
+ [
4118
+ "bulkBarActions",
4119
+ "seed-data-table__bulkBarActions"
3879
4120
  ]
3880
4121
  ];
3881
4122
  const defaultVariant$6 = {
@@ -3913,7 +4154,7 @@ Object.assign(dataTable, {
3913
4154
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3914
4155
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3915
4156
  // than re-deriving its own.
3916
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
4157
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(dataTable);
3917
4158
 
3918
4159
  /**
3919
4160
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -4621,7 +4862,7 @@ function isCellTruncated(cell) {
4621
4862
  * actually reported against a pinned column. */ function isChipListEditable(column) {
4622
4863
  return !!column.editable && column.type === "chip-list";
4623
4864
  }
4624
- function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
4865
+ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
4625
4866
  const effective = useEffectiveColumns(columns, {
4626
4867
  selectable,
4627
4868
  stickyFirst
@@ -4636,7 +4877,10 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4636
4877
  row: row,
4637
4878
  classNames: classNames,
4638
4879
  locale: locale,
4639
- sticky: sticky
4880
+ sticky: sticky,
4881
+ cellContextMenu: cellContextMenu,
4882
+ onViewEditHistory: onViewEditHistory,
4883
+ cellContextMenuItems: cellContextMenuItems
4640
4884
  }, column.key);
4641
4885
  })
4642
4886
  });
@@ -4648,7 +4892,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4648
4892
  * on every row's render. React keeps this instance stable across re-renders because
4649
4893
  * `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
4650
4894
  * `<DataTableCell>` before this split.
4651
- */ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
4895
+ */ function DataTableBodyCell({ column, row, classNames, locale, sticky, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
4652
4896
  const { startEdit, cancelEdit, isEditing, isPending, commitEdit, isPeeking, openPeek, closePeek } = useDataTableContext();
4653
4897
  const cellRef = React.useRef(null);
4654
4898
  // Feature B rework (#8) -- the chip-list popover's own live position, in VIEWPORT
@@ -4664,6 +4908,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4664
4908
  // editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
4665
4909
  // rendering exactly as it always has, not suddenly gain overlay chrome.
4666
4910
  const [overlayOpen, setOverlayOpen] = React.useState(false);
4911
+ // Attio parity (attio-column-features-full.md gap #1) -- the right-click menu's own
4912
+ // point + open state. `contextMenuPoint` is frozen at the moment of the right-click
4913
+ // (never re-read live), same reasoning as `overlayOpen` above: the menu shouldn't
4914
+ // chase the cursor after it opens. `<CellContextMenu>` itself is only rendered below
4915
+ // while `contextMenuOpen` is true (not permanently mounted per cell) -- unlike
4916
+ // `ColumnMenu` (one per COLUMN, header row only), this component runs once per
4917
+ // row×column cell, and a floating-ui instance genuinely idle in every one of
4918
+ // thousands of cells would be real, avoidable overhead.
4919
+ const [contextMenuPoint, setContextMenuPoint] = React.useState(null);
4920
+ const [contextMenuOpen, setContextMenuOpen] = React.useState(false);
4667
4921
  const alwaysLive = isAlwaysLiveEditable(column);
4668
4922
  const chipListEditable = isChipListEditable(column);
4669
4923
  // A gated `editable` column (everything except the always-live widgets AND the
@@ -4715,6 +4969,19 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4715
4969
  setOverlayOpen(isCellTruncated(event.currentTarget));
4716
4970
  startEdit(row.id, column.key);
4717
4971
  }
4972
+ function handleContextMenu(event) {
4973
+ if (!cellContextMenu) return;
4974
+ // Replaces the browser's own native context menu (Attio's own gesture) -- and
4975
+ // must not also reach the row's own `onRowClick`, same reasoning as every other
4976
+ // cell-local click handler in this file.
4977
+ event.preventDefault();
4978
+ event.stopPropagation();
4979
+ setContextMenuPoint({
4980
+ x: event.clientX,
4981
+ y: event.clientY
4982
+ });
4983
+ setContextMenuOpen(true);
4984
+ }
4718
4985
  function handleClick(event) {
4719
4986
  if (chipListEditable) {
4720
4987
  // Attio's own chip-list gesture: click opens the editor regardless of
@@ -4818,7 +5085,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4818
5085
  const pending = isPending(row.id, column.key);
4819
5086
  return /*#__PURE__*/ jsxs(Fragment, {
4820
5087
  children: [
4821
- /*#__PURE__*/ jsx(DataTableCell, {
5088
+ /*#__PURE__*/ jsxs(DataTableCell, {
4822
5089
  ref: cellRef,
4823
5090
  "data-numeric": dataAttr(column.type === "number" || column.type === "currency"),
4824
5091
  "data-editable": dataAttr(!!column.editable),
@@ -4829,14 +5096,34 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4829
5096
  style: sticky.style,
4830
5097
  onClick: handleClick,
4831
5098
  onDoubleClick: handleDoubleClick,
4832
- children: /*#__PURE__*/ jsx(CellContent, {
4833
- column: column,
4834
- row: row,
4835
- rowId: row.id,
4836
- classNames: classNames,
4837
- locale: locale,
4838
- overlayActive: overlayActive
4839
- })
5099
+ onContextMenu: handleContextMenu,
5100
+ children: [
5101
+ /*#__PURE__*/ jsx(CellContent, {
5102
+ column: column,
5103
+ row: row,
5104
+ rowId: row.id,
5105
+ classNames: classNames,
5106
+ locale: locale,
5107
+ overlayActive: overlayActive
5108
+ }),
5109
+ cellContextMenu && contextMenuOpen && contextMenuPoint && // A `<td>` (unlike its own parent `<tr>`) can validly hold any flow
5110
+ // content, so `CellContextMenu`'s own 0x0 `MenuAnchor` -- itself never
5111
+ // portaled, only `MenuPositioner`'s actual dropdown content is (via
5112
+ // floating-ui's own `FloatingPortal`, straight to `document.body`) --
5113
+ // renders here, INSIDE this cell, rather than as a sibling of it.
5114
+ /*#__PURE__*/ jsx(CellContextMenu, {
5115
+ column: column,
5116
+ row: row,
5117
+ value: rawValue,
5118
+ locale: locale,
5119
+ point: contextMenuPoint,
5120
+ open: contextMenuOpen,
5121
+ onOpenChange: setContextMenuOpen,
5122
+ onCommit: (value)=>commitEdit(row.id, column.key, value),
5123
+ onViewEditHistory: onViewEditHistory,
5124
+ cellContextMenuItems: cellContextMenuItems
5125
+ })
5126
+ ]
4840
5127
  }),
4841
5128
  chipListEditorOpen && popoverStyle && /*#__PURE__*/ 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.
4842
5129
  /*#__PURE__*/ jsx("div", {
@@ -5066,64 +5353,52 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
5066
5353
  }
5067
5354
  }
5068
5355
 
5069
- const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu);
5070
- const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem);
5071
- const withStateProps = createWithStateProps([
5072
- useMenuContext
5073
- ]);
5074
- const withItemStateProps = createWithStateProps([
5075
- useMenuItemContext
5076
- ]);
5077
- const MenuRoot = (props)=>{
5078
- const [variantProps, otherProps] = menu.splitVariantProps(props);
5079
- const classNames = menu(variantProps);
5080
- return /*#__PURE__*/ jsx(ClassNamesProvider, {
5081
- value: classNames,
5082
- children: /*#__PURE__*/ jsx(ItemPropsProvider, {
5083
- value: {
5084
- size: variantProps.size
5085
- },
5086
- children: /*#__PURE__*/ jsx(Menu.Root, {
5087
- ...otherProps
5356
+ const DataTableBulkBarRoot = withContext$1(Primitive.div, "bulkBar");
5357
+ const DataTableBulkBarCount = withContext$1(Primitive.span, "bulkBarCount");
5358
+ const DataTableBulkBarClear = withContext$1(Primitive.button, "bulkBarClear");
5359
+ /** Wraps whatever the CONSUMER passes as this bar's own action buttons -- the bar
5360
+ * itself only ever supplies chrome (count + clear); every actual action (Add to
5361
+ * list, Send email, …) is the consumer's own `<ActionButton>`/`<Menu>`, the same
5362
+ * "chrome vs. consumer content" split `toolbarActions`/`toolbarEnd` already draw for
5363
+ * the toolbar's own custom actions. */ const DataTableBulkBarActions = withContext$1(Primitive.div, "bulkBarActions");
5364
+ /**
5365
+ * The floating bulk-action pill (gap #4) -- renders nothing while no row is selected
5366
+ * (reads `selectedIds` off the table-wide context itself, so a consumer doesn't have
5367
+ * to gate this on `selectable`/`selectedIds` on their own). Composed as the LAST
5368
+ * child of the table's own root (`DataTable.tsx`), which is `position: relative` for
5369
+ * exactly this reason -- see `data-table.ts`'s own `bulkBar` slot comment for why its
5370
+ * geometry borrows `contextual-floating-button`'s already-measured vars rather than a
5371
+ * guessed literal.
5372
+ */ function DataTableBulkBar({ clearLabel = "Clear selection", children }) {
5373
+ const { selectedIds, clearSelection } = useDataTableContext();
5374
+ if (selectedIds.length === 0) return null;
5375
+ return /*#__PURE__*/ jsxs(DataTableBulkBarRoot, {
5376
+ role: "toolbar",
5377
+ "aria-label": "Bulk actions",
5378
+ "aria-orientation": "horizontal",
5379
+ children: [
5380
+ /*#__PURE__*/ jsxs(DataTableBulkBarCount, {
5381
+ "aria-live": "polite",
5382
+ children: [
5383
+ selectedIds.length,
5384
+ " selected"
5385
+ ]
5386
+ }),
5387
+ /*#__PURE__*/ jsx(DataTableBulkBarClear, {
5388
+ type: "button",
5389
+ "aria-label": clearLabel,
5390
+ onClick: clearSelection,
5391
+ children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
5392
+ size: 12,
5393
+ "aria-hidden": "true"
5394
+ })
5395
+ }),
5396
+ children && /*#__PURE__*/ jsx(DataTableBulkBarActions, {
5397
+ children: children(selectedIds)
5088
5398
  })
5089
- })
5090
- });
5091
- };
5092
- Menu.Anchor;
5093
- const MenuTrigger = Menu.Trigger;
5094
- const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
5095
- const classNames = useClassNames();
5096
- return /*#__PURE__*/ jsx(Menu.Positioner, {
5097
- ref: ref,
5098
- className: clsx(classNames.positioner, className),
5099
- ...props
5100
- });
5101
- });
5102
- MenuPositioner.displayName = "MenuPositioner";
5103
- const MenuContent = withContext$1(Menu.Content, "content");
5104
- const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
5105
- withContext$1(Menu.Group, "group");
5106
- withContext$1(Menu.GroupLabel, "groupLabel");
5107
- const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
5108
- const [variantProps, otherProps] = menuItem.splitVariantProps(props);
5109
- const parentProps = useItemProps();
5110
- const classNames = menuItem({
5111
- ...parentProps,
5112
- ...variantProps
5113
- });
5114
- return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
5115
- value: classNames,
5116
- children: /*#__PURE__*/ jsx(Menu.Item, {
5117
- ref: ref,
5118
- className: clsx(classNames.root, propClassName),
5119
- ...otherProps
5120
- })
5399
+ ]
5121
5400
  });
5122
- });
5123
- MenuItem.displayName = "MenuItem";
5124
- withItemContext(withItemStateProps(Primitive.div), "body");
5125
- const MenuItemLabel = withItemContext(withItemStateProps(Primitive.span), "label");
5126
- withItemContext(withItemStateProps(Primitive.span), "description");
5401
+ }
5127
5402
 
5128
5403
  function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast }) {
5129
5404
  const effective = useEffectiveColumns(columns, {
@@ -5150,10 +5425,11 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
5150
5425
  })
5151
5426
  });
5152
5427
  }
5153
- function formatCalcValue(value) {
5154
- return value.toLocaleString(undefined, {
5428
+ function formatCalcValue(value, op) {
5429
+ const formatted = value.toLocaleString(undefined, {
5155
5430
  maximumFractionDigits: 2
5156
5431
  });
5432
+ return op === "percentEmpty" || op === "percentFilled" ? `${formatted}%` : formatted;
5157
5433
  }
5158
5434
  /**
5159
5435
  * One ColumnCalc footer cell -- a click-anywhere-in-the-cell trigger (crisp's own
@@ -5188,7 +5464,7 @@ function formatCalcValue(value) {
5188
5464
  children: [
5189
5465
  /*#__PURE__*/ jsx("span", {
5190
5466
  className: "seed-data-table-column-calc-value",
5191
- children: formatCalcValue(result)
5467
+ children: formatCalcValue(result, op)
5192
5468
  }),
5193
5469
  /*#__PURE__*/ jsx("span", {
5194
5470
  className: "seed-data-table-column-calc-op",
@@ -5209,21 +5485,13 @@ function formatCalcValue(value) {
5209
5485
  style: {
5210
5486
  minWidth: 160
5211
5487
  },
5212
- children: /*#__PURE__*/ jsxs(MenuScrollArea, {
5213
- children: [
5214
- ops.map((candidate)=>/*#__PURE__*/ jsx(MenuItem, {
5215
- onClick: ()=>setColumnCalcOp(column.key, candidate),
5216
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
5217
- children: DATA_TABLE_CALC_LABEL[candidate]
5218
- })
5219
- }, candidate)),
5220
- op && /*#__PURE__*/ jsx(MenuItem, {
5221
- onClick: ()=>setColumnCalcOp(column.key, undefined),
5488
+ children: /*#__PURE__*/ jsx(MenuScrollArea, {
5489
+ children: ops.map((candidate)=>/*#__PURE__*/ jsx(MenuItem, {
5490
+ onClick: ()=>setColumnCalcOp(column.key, candidate),
5222
5491
  children: /*#__PURE__*/ jsx(MenuItemLabel, {
5223
- children: "Clear"
5492
+ children: DATA_TABLE_CALC_LABEL[candidate]
5224
5493
  })
5225
- })
5226
- ]
5494
+ }, candidate))
5227
5495
  })
5228
5496
  })
5229
5497
  })
@@ -10488,6 +10756,18 @@ ContinuousDatePicker.displayName = "ContinuousDatePicker";
10488
10756
  // value list) down to `is`/`is not`/`empty`/`not empty`. `"isanyof"` is appended to any
10489
10757
  // of the four whenever the column supplies `filterValues` -- a faceted checkbox list,
10490
10758
  // independent of the column's own kind.
10759
+ //
10760
+ // Multi-select fix (DATATABLE-ATTIO-SPEC.md gap #12, live-measured 2026-09-30): a
10761
+ // `kind: "select"` column whose CELL is multi-valued (`type: "tags"`/`"chip-list"` --
10762
+ // a chip-list/tag column, not a single status pill) offers Attio's own
10763
+ // `contains`/`does not contain` instead of `is`/`is not` -- "is" reads as an exact-
10764
+ // set-equality check against an array value, which is not what Attio's own multi-
10765
+ // select filter means or what `applyToolbarFilters`' `is`/`isnot` branch (a plain
10766
+ // string-equality compare via `asText`) computes. `contains`/`doesnotcontain` are
10767
+ // already correct here unmodified: `applyToolbarFilters` already runs them against
10768
+ // `asText(raw)` (an array's own values joined), which is exactly "does any tag
10769
+ // match". Single-select (no `filterValues`-array cell type) keeps `is`/`is not` --
10770
+ // not re-measured against Attio, so left alone per the discovery doc's own hedge.
10491
10771
  ////////////////////////////////////////////////////////////////////////////////////
10492
10772
  const OP_ORDER = [
10493
10773
  "contains",
@@ -10521,9 +10801,19 @@ const SELECT_OP_ORDER = [
10521
10801
  "empty",
10522
10802
  "notempty"
10523
10803
  ];
10804
+ const MULTI_SELECT_OP_ORDER = [
10805
+ "contains",
10806
+ "doesnotcontain",
10807
+ "empty",
10808
+ "notempty"
10809
+ ];
10524
10810
  /** The operators a column of this `kind` offers, in display order -- `hasValues`
10525
- * (the column supplies `filterValues`) appends `"isanyof"` regardless of kind. */ function opsForKind(kind, hasValues) {
10526
- const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? SELECT_OP_ORDER : OP_ORDER;
10811
+ * (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
10812
+ * `multiValue` (the column's CELL type is a chip-list/tags column, not just its
10813
+ * filter `kind`) swaps `SELECT_OP_ORDER`'s `is`/`is not` for `contains`/`does not
10814
+ * contain`, matching Attio's own multi-select filter -- see the operator-sets
10815
+ * comment above. */ function opsForKind(kind, hasValues, multiValue = false) {
10816
+ const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? multiValue ? MULTI_SELECT_OP_ORDER : SELECT_OP_ORDER : OP_ORDER;
10527
10817
  return hasValues ? [
10528
10818
  ...base,
10529
10819
  "isanyof"
@@ -10891,66 +11181,58 @@ const formatFilterDate = (iso)=>{
10891
11181
  return `${OPERATOR_LABELS[filter.op]} ${value}`;
10892
11182
  }
10893
11183
  /**
10894
- * One filter condition, INSIDE the Filter popover -- a compact one-line summary row
10895
- * (field: operator value) with a trailing remove "x", expanding in place (accordion,
10896
- * not a second popover) to the field/operator/value editor when clicked. Presentation
10897
- * note (crisp/#593-style live re-check, 2026-09-28): Attio's own toolbar keeps active
10898
- * conditions as a compact "Filter · N" count on the trigger, not a row of always-on
10899
- * pill chips spread across the toolbar -- this replaces that first pass (which DID
10900
- * render one big chip per condition, directly in the toolbar row) with conditions
10901
- * living inside the ONE popover the trigger opens, the same list-of-rows shape
10902
- * `SortControl` above already uses for its own conditions.
10903
- */ function FilterConditionRow({ filter, columns, expanded, onToggle }) {
11184
+ * One filter condition, INSIDE the Filter popover -- Attio's own compact chip shape
11185
+ * (DATATABLE-ATTIO-SPEC.md gap #12, re-measured live 2026-09-30): `[attribute ▾]
11186
+ * [operator …] [value] [remove ×]`, ALL of it visible at once, never behind a click-
11187
+ * to-expand accordion. Supersedes this file's earlier 2026-09-28 note (which read
11188
+ * Attio as keeping conditions collapsed behind a bare "Filter · N" count and put them
11189
+ * in an accordion for that reason) -- the fresh read found the opposite: every active
11190
+ * condition shows attribute/operator/value simultaneously. This keeps the outer shell
11191
+ * (the "Filter"/"Filter · N" trigger opening ONE popover, `SortControl`'s own
11192
+ * list-of-rows shape) rather than moving chips into the toolbar row itself, so the
11193
+ * already-covered trigger/`Add filter` DOM (`DataTable.test.tsx`) stays put; only the
11194
+ * PER-CONDITION row tightens, from an accordion to an always-open card. The operator
11195
+ * set stays a row of small pill buttons (not a dropdown) so every existing test that
11196
+ * clicks an operator by its exact button name keeps working unchanged.
11197
+ */ function FilterConditionRow({ filter, columns }) {
10904
11198
  const { patchToolbarFilter, changeToolbarFilterField, removeToolbarFilter } = useDataTableContext();
10905
11199
  const column = columns.find((c)=>c.key === filter.key);
10906
11200
  const kind = column?.kind;
11201
+ const multiValue = column?.type === "tags" || column?.type === "chip-list";
10907
11202
  const values = column?.filterValues ?? [];
10908
- const ops = opsForKind(kind, values.length > 0);
10909
- return /*#__PURE__*/ jsxs("div", {
11203
+ const ops = opsForKind(kind, values.length > 0, multiValue);
11204
+ return /*#__PURE__*/ jsxs("fieldset", {
11205
+ "aria-label": `${column ? columnLabel(column) : filter.key} filter: ${conditionSummary(filter, column)}`,
10910
11206
  style: {
11207
+ margin: 0,
10911
11208
  display: "flex",
10912
11209
  flexDirection: "column",
10913
- gap: 6,
11210
+ gap: 8,
10914
11211
  borderRadius: 6,
10915
- background: expanded ? "var(--seed-color-bg-neutral-weak)" : "transparent",
10916
- padding: expanded ? 6 : 0
11212
+ border: "1px solid var(--seed-color-border-neutral)",
11213
+ background: "var(--seed-color-bg-neutral-weak)",
11214
+ padding: 6
10917
11215
  },
10918
11216
  children: [
10919
11217
  /*#__PURE__*/ jsxs("div", {
10920
11218
  style: {
10921
11219
  display: "flex",
10922
11220
  alignItems: "center",
10923
- gap: 2
11221
+ gap: 4
10924
11222
  },
10925
11223
  children: [
10926
- /*#__PURE__*/ jsxs("button", {
10927
- type: "button",
10928
- onClick: onToggle,
11224
+ /*#__PURE__*/ jsx(InlineSelect, {
11225
+ ariaLabel: "Filter by field",
11226
+ value: filter.key,
11227
+ onChange: (key)=>changeToolbarFilterField(filter.id, key),
11228
+ options: columns.map((c)=>({
11229
+ value: c.key,
11230
+ label: columnLabel(c)
11231
+ })),
10929
11232
  style: {
10930
11233
  flex: 1,
10931
- minWidth: 0,
10932
- textAlign: "left",
10933
- padding: "6px 8px",
10934
- border: "none",
10935
- borderRadius: 6,
10936
- background: "transparent",
10937
- cursor: "pointer",
10938
- fontSize: 13,
10939
- overflow: "hidden",
10940
- textOverflow: "ellipsis",
10941
- whiteSpace: "nowrap"
10942
- },
10943
- children: [
10944
- /*#__PURE__*/ jsx("strong", {
10945
- style: {
10946
- fontWeight: 500
10947
- },
10948
- children: column ? columnLabel(column) : filter.key
10949
- }),
10950
- ":",
10951
- " ",
10952
- conditionSummary(filter, column)
10953
- ]
11234
+ minWidth: 0
11235
+ }
10954
11236
  }),
10955
11237
  /*#__PURE__*/ jsx("button", {
10956
11238
  type: "button",
@@ -10964,136 +11246,103 @@ const formatFilterDate = (iso)=>{
10964
11246
  })
10965
11247
  ]
10966
11248
  }),
10967
- expanded && /*#__PURE__*/ jsxs("div", {
11249
+ /*#__PURE__*/ jsx("div", {
10968
11250
  style: {
10969
- padding: "0 8px 4px",
10970
11251
  display: "flex",
10971
- flexDirection: "column",
10972
- gap: 8
11252
+ flexWrap: "wrap",
11253
+ gap: 2
10973
11254
  },
10974
- children: [
10975
- /*#__PURE__*/ jsx("div", {
10976
- style: {
10977
- fontSize: 12,
10978
- color: "var(--seed-color-fg-neutral-subtle)"
10979
- },
10980
- children: "Field"
10981
- }),
10982
- /*#__PURE__*/ jsx(InlineSelect, {
10983
- ariaLabel: "Filter by field",
10984
- value: filter.key,
10985
- onChange: (key)=>changeToolbarFilterField(filter.id, key),
10986
- options: columns.map((c)=>({
10987
- value: c.key,
10988
- label: columnLabel(c)
10989
- }))
10990
- }),
10991
- /*#__PURE__*/ jsx("div", {
10992
- style: {
10993
- fontSize: 12,
10994
- color: "var(--seed-color-fg-neutral-subtle)"
10995
- },
10996
- children: "Condition"
10997
- }),
10998
- /*#__PURE__*/ jsx("div", {
11255
+ children: ops.map((op)=>/*#__PURE__*/ jsx("button", {
11256
+ type: "button",
11257
+ "data-active": filter.op === op ? "" : undefined,
11258
+ onClick: ()=>patchToolbarFilter(filter.id, {
11259
+ op
11260
+ }),
10999
11261
  style: {
11000
- display: "flex",
11001
- flexDirection: "column",
11002
- gap: 2
11262
+ textAlign: "left",
11263
+ padding: "4px 8px",
11264
+ borderRadius: 999,
11265
+ border: "none",
11266
+ cursor: "pointer",
11267
+ background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
11268
+ fontSize: 12
11003
11269
  },
11004
- children: ops.map((op)=>/*#__PURE__*/ jsx("button", {
11005
- type: "button",
11006
- "data-active": filter.op === op ? "" : undefined,
11007
- onClick: ()=>patchToolbarFilter(filter.id, {
11008
- op
11009
- }),
11010
- style: {
11011
- textAlign: "left",
11012
- padding: "6px 8px",
11013
- borderRadius: 6,
11014
- border: "none",
11015
- cursor: "pointer",
11016
- background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
11017
- fontSize: 13
11018
- },
11019
- children: OPERATOR_LABELS[op]
11020
- }, op))
11021
- }),
11022
- opNeedsValues(filter.op) && /*#__PURE__*/ jsx("div", {
11270
+ children: OPERATOR_LABELS[op]
11271
+ }, op))
11272
+ }),
11273
+ opNeedsValues(filter.op) && /*#__PURE__*/ jsx("div", {
11274
+ style: {
11275
+ display: "flex",
11276
+ flexDirection: "column",
11277
+ gap: 4,
11278
+ maxHeight: 160,
11279
+ overflow: "auto"
11280
+ },
11281
+ children: values.map((value)=>{
11282
+ const checked = (filter.values ?? []).includes(value);
11283
+ return /*#__PURE__*/ jsxs(CheckboxRoot, {
11284
+ checked: checked,
11285
+ onCheckedChange: ()=>patchToolbarFilter(filter.id, {
11286
+ values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
11287
+ ...filter.values ?? [],
11288
+ value
11289
+ ]
11290
+ }),
11023
11291
  style: {
11024
11292
  display: "flex",
11025
- flexDirection: "column",
11026
- gap: 4,
11027
- maxHeight: 160,
11028
- overflow: "auto"
11293
+ alignItems: "center",
11294
+ gap: 8,
11295
+ padding: "4px 0",
11296
+ cursor: "pointer"
11029
11297
  },
11030
- children: values.map((value)=>{
11031
- const checked = (filter.values ?? []).includes(value);
11032
- return /*#__PURE__*/ jsxs(CheckboxRoot, {
11033
- checked: checked,
11034
- onCheckedChange: ()=>patchToolbarFilter(filter.id, {
11035
- values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
11036
- ...filter.values ?? [],
11037
- value
11038
- ]
11039
- }),
11040
- style: {
11041
- display: "flex",
11042
- alignItems: "center",
11043
- gap: 8,
11044
- padding: "4px 0",
11045
- cursor: "pointer"
11046
- },
11047
- children: [
11048
- /*#__PURE__*/ jsx(CheckboxControl, {
11049
- children: /*#__PURE__*/ jsx(CheckboxIndicator, {
11050
- checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
11051
- size: 12
11052
- })
11053
- })
11054
- }),
11055
- /*#__PURE__*/ jsx(CheckboxLabel, {
11056
- children: value
11057
- }),
11058
- /*#__PURE__*/ jsx(CheckboxHiddenInput, {
11059
- "aria-label": value
11298
+ children: [
11299
+ /*#__PURE__*/ jsx(CheckboxControl, {
11300
+ children: /*#__PURE__*/ jsx(CheckboxIndicator, {
11301
+ checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
11302
+ size: 12
11060
11303
  })
11061
- ]
11062
- }, value);
11063
- })
11064
- }),
11065
- opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
11066
- value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
11067
- onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
11068
- value: dateToISO(fromDatePickerDate(d))
11069
- })
11070
- }),
11071
- opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
11072
- ariaLabel: "Filter value",
11073
- placeholder: "Value…",
11074
- value: filter.value || undefined,
11075
- onChange: (value)=>patchToolbarFilter(filter.id, {
11076
- value
11304
+ })
11077
11305
  }),
11078
- options: values.map((value)=>({
11079
- value,
11080
- label: value
11081
- }))
11082
- }),
11083
- opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsx(TextFieldRoot, {
11084
- variant: "outline",
11085
- size: "medium",
11086
- children: /*#__PURE__*/ jsx(TextFieldInput, {
11087
- type: kind === "number" ? "number" : "text",
11088
- value: filter.value,
11089
- onChange: (e)=>patchToolbarFilter(filter.id, {
11090
- value: e.target.value
11091
- }),
11092
- placeholder: "Value…",
11093
- "aria-label": "Filter value"
11094
- })
11306
+ /*#__PURE__*/ jsx(CheckboxLabel, {
11307
+ children: value
11308
+ }),
11309
+ /*#__PURE__*/ jsx(CheckboxHiddenInput, {
11310
+ "aria-label": value
11311
+ })
11312
+ ]
11313
+ }, value);
11314
+ })
11315
+ }),
11316
+ opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
11317
+ value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
11318
+ onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
11319
+ value: dateToISO(fromDatePickerDate(d))
11095
11320
  })
11096
- ]
11321
+ }),
11322
+ opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
11323
+ ariaLabel: "Filter value",
11324
+ placeholder: "Value…",
11325
+ value: filter.value || undefined,
11326
+ onChange: (value)=>patchToolbarFilter(filter.id, {
11327
+ value
11328
+ }),
11329
+ options: values.map((value)=>({
11330
+ value,
11331
+ label: value
11332
+ }))
11333
+ }),
11334
+ opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsx(TextFieldRoot, {
11335
+ variant: "outline",
11336
+ size: "medium",
11337
+ children: /*#__PURE__*/ jsx(TextFieldInput, {
11338
+ type: kind === "number" ? "number" : "text",
11339
+ value: filter.value,
11340
+ onChange: (e)=>patchToolbarFilter(filter.id, {
11341
+ value: e.target.value
11342
+ }),
11343
+ placeholder: "Value…",
11344
+ "aria-label": "Filter value"
11345
+ })
11097
11346
  })
11098
11347
  ]
11099
11348
  });
@@ -11101,15 +11350,14 @@ const formatFilterDate = (iso)=>{
11101
11350
  /**
11102
11351
  * The toolbar's Filter trigger + popover -- a compact `"Filter"` / `"Filter · N"`
11103
11352
  * button (matching `SortControl`'s own `"Sort"` / `"Sort · N"`), opening ONE popover
11104
- * that holds both the active conditions (compact rows, `FilterConditionRow` above,
11105
- * one expanded at a time) and, behind "+ Add filter", the field picker that appends a
11106
- * new one -- not a row of always-on pill chips spread across the toolbar (this
11107
- * replaces that first pass; see `FilterConditionRow`'s own doc comment).
11353
+ * that holds both the active conditions (compact chips, `FilterConditionRow` above,
11354
+ * ALL open at once -- no accordion) and, behind "+ Add filter", the field picker that
11355
+ * appends a new one. See `FilterConditionRow`'s own doc comment for why the chips
11356
+ * live inside this one popover rather than spread across the toolbar row itself.
11108
11357
  */ function FilterControl({ columns, label = "Filter" }) {
11109
11358
  const { toolbarFilters, addToolbarFilter } = useDataTableContext();
11110
11359
  const [open, setOpen] = React.useState(false);
11111
11360
  const [pickingField, setPickingField] = React.useState(false);
11112
- const [expandedId, setExpandedId] = React.useState(null);
11113
11361
  return /*#__PURE__*/ jsxs(MenuRoot, {
11114
11362
  open: open,
11115
11363
  onOpenChange: (next)=>{
@@ -11171,8 +11419,7 @@ const formatFilterDate = (iso)=>{
11171
11419
  columns.map((c)=>/*#__PURE__*/ jsx("button", {
11172
11420
  type: "button",
11173
11421
  onClick: ()=>{
11174
- const id = addToolbarFilter(c.key);
11175
- setExpandedId(id);
11422
+ addToolbarFilter(c.key);
11176
11423
  setPickingField(false);
11177
11424
  },
11178
11425
  style: {
@@ -11205,9 +11452,7 @@ const formatFilterDate = (iso)=>{
11205
11452
  }),
11206
11453
  toolbarFilters.map((f)=>/*#__PURE__*/ jsx(FilterConditionRow, {
11207
11454
  filter: f,
11208
- columns: columns,
11209
- expanded: expandedId === f.id,
11210
- onToggle: ()=>setExpandedId((id)=>id === f.id ? null : f.id)
11455
+ columns: columns
11211
11456
  }, f.id)),
11212
11457
  /*#__PURE__*/ jsxs("button", {
11213
11458
  type: "button",
@@ -11236,8 +11481,8 @@ const formatFilterDate = (iso)=>{
11236
11481
  });
11237
11482
  }
11238
11483
 
11239
- const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
11240
- const DataTableCount = withContext$2(Primitive.span, "count");
11484
+ const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
11485
+ const DataTableCount = withContext$1(Primitive.span, "count");
11241
11486
  /**
11242
11487
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
11243
11488
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -11248,7 +11493,7 @@ const DataTableCount = withContext$2(Primitive.span, "count");
11248
11493
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
11249
11494
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
11250
11495
  * `toolbarActions` is passed.
11251
- */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
11496
+ */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
11252
11497
  /**
11253
11498
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
11254
11499
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -11298,7 +11543,7 @@ function RowCount({ shown, total, countLabel }) {
11298
11543
  }
11299
11544
 
11300
11545
  function DataTableInner(props, ref) {
11301
- 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;
11546
+ 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;
11302
11547
  const classNames = dataTable({
11303
11548
  surface,
11304
11549
  interactive
@@ -11422,7 +11667,7 @@ function DataTableInner(props, ref) {
11422
11667
  // loading/error/empty body, infinite scroll's sentinel, the add-row footer) reads
11423
11668
  // this ONE value rather than each re-deriving it, so they can never drift apart.
11424
11669
  const totalCols = columns.length + (selectable ? 1 : 0);
11425
- return /*#__PURE__*/ jsx(ClassNamesProvider$1, {
11670
+ return /*#__PURE__*/ jsx(ClassNamesProvider, {
11426
11671
  value: classNames,
11427
11672
  children: /*#__PURE__*/ jsxs(DataTableRoot, {
11428
11673
  ref: ref,
@@ -11578,7 +11823,10 @@ function DataTableInner(props, ref) {
11578
11823
  locale: locale,
11579
11824
  selectable: !!selectable,
11580
11825
  stickyFirst: stickyFirst,
11581
- stickyLast: resolvedStickyLast
11826
+ stickyLast: resolvedStickyLast,
11827
+ cellContextMenu: cellContextMenu,
11828
+ onViewEditHistory: onViewEditHistory,
11829
+ cellContextMenuItems: cellContextMenuItems
11582
11830
  })
11583
11831
  ]
11584
11832
  }, row.id)),
@@ -11617,6 +11865,10 @@ function DataTableInner(props, ref) {
11617
11865
  })
11618
11866
  ]
11619
11867
  })
11868
+ }),
11869
+ selectable && /*#__PURE__*/ jsx(DataTableBulkBar, {
11870
+ clearLabel: clearSelectionLabel,
11871
+ children: bulkActions
11620
11872
  })
11621
11873
  ]
11622
11874
  })
@@ -11670,7 +11922,7 @@ DataTableInner.displayName = "DataTable";
11670
11922
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
11671
11923
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
11672
11924
  // child without the HTML parser foster-parenting it back out.
11673
- const DataTableTable = withContext$2(DataTableTable$1, "table");
11925
+ const DataTableTable = withContext$1(DataTableTable$1, "table");
11674
11926
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
11675
11927
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
11676
11928
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -11707,15 +11959,15 @@ const DataTableTable = withContext$2(DataTableTable$1, "table");
11707
11959
  ]
11708
11960
  });
11709
11961
  }
11710
- const DataTableHeader = withContext$2(DataTableHeader$1, "header");
11711
- const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
11962
+ const DataTableHeader = withContext$1(DataTableHeader$1, "header");
11963
+ const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
11712
11964
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
11713
11965
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
11714
11966
  // for why this lives one level below the cell itself rather than on it.
11715
- const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
11967
+ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
11716
11968
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
11717
11969
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
11718
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
11970
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
11719
11971
  /**
11720
11972
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
11721
11973
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -11726,7 +11978,7 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
11726
11978
  * a second recipe call.
11727
11979
  */ function withSelectionCell(Component, baseSlot) {
11728
11980
  const StyledComponent = /*#__PURE__*/ forwardRef((props, ref)=>{
11729
- const classNames = useClassNames$1();
11981
+ const classNames = useClassNames();
11730
11982
  return /*#__PURE__*/ jsx(Component, {
11731
11983
  ref: ref,
11732
11984
  ...props,
@@ -11737,12 +11989,12 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
11737
11989
  return StyledComponent;
11738
11990
  }
11739
11991
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
11740
- const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
11992
+ const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
11741
11993
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
11742
11994
  // `<tbody>`, so it is not wrapped in `withContext`.
11743
11995
  const DataTableBody = DataTableBody$1;
11744
- const DataTableRow = withContext$2(DataTableRow$1, "row");
11745
- const DataTableCell = withContext$2(DataTableCell$1, "cell");
11996
+ const DataTableRow = withContext$1(DataTableRow$1, "row");
11997
+ const DataTableCell = withContext$1(DataTableCell$1, "cell");
11746
11998
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
11747
11999
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
11748
12000
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -11752,7 +12004,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
11752
12004
  // stays out of this slot's own base css (nothing here has a bounded height of its own
11753
12005
  // to clip), so it never introduces a second vertical scrollbar next to whatever
11754
12006
  // ambient container the whole DataTable already sits in.
11755
- const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
12007
+ const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
11756
12008
  function DataTableScrollBody({ children, scrollProps }) {
11757
12009
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
11758
12010
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -11767,7 +12019,7 @@ function DataTableScrollBody({ children, scrollProps }) {
11767
12019
  children: children
11768
12020
  });
11769
12021
  }
11770
- const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
12022
+ const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
11771
12023
  function DataTableFooter({ children, ...props }) {
11772
12024
  return /*#__PURE__*/ jsx(DataTableFooterBase, {
11773
12025
  ...props,
@@ -11776,7 +12028,7 @@ function DataTableFooter({ children, ...props }) {
11776
12028
  })
11777
12029
  });
11778
12030
  }
11779
- const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
12031
+ const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
11780
12032
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
11781
12033
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
11782
12034
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -15713,4 +15965,4 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
15713
15965
  }
15714
15966
  RecordPreviewPanel.displayName = "RecordPreviewPanel";
15715
15967
 
15716
- export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };
15968
+ export { DataTable, DataTableBulkBar, DataTableViewsControl, RecordPreview, ResizablePanel };