@proteos/ui 0.54.1 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/index.cjs +679 -321
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +84 -9
  4. package/dist/index.d.ts +84 -9
  5. package/dist/index.js +677 -324
  6. package/dist/index.js.map +1 -1
  7. package/package.json +4 -4
  8. package/src/components/badge/badge.stories.tsx +37 -1
  9. package/src/components/badge/badge.test.tsx +39 -2
  10. package/src/components/badge/badge.tsx +65 -1
  11. package/src/components/badge/index.ts +2 -2
  12. package/src/components/badge-group/badge-group.stories.tsx +75 -0
  13. package/src/components/badge-group/badge-group.test.tsx +121 -0
  14. package/src/components/badge-group/badge-group.tsx +208 -0
  15. package/src/components/badge-group/index.ts +7 -0
  16. package/src/components/badge-multi-select/badge-multi-select.stories.tsx +25 -0
  17. package/src/components/badge-multi-select/badge-multi-select.test.tsx +110 -1
  18. package/src/components/badge-multi-select/badge-multi-select.tsx +190 -128
  19. package/src/components/data-table/data-table.stories.tsx +53 -2
  20. package/src/components/data-table/data-table.tsx +45 -25
  21. package/src/components/field/listbox-check.tsx +44 -0
  22. package/src/components/field/use-chip-row-wrap.ts +45 -0
  23. package/src/components/multi-select/multi-select.tsx +8 -39
  24. package/src/components/record-filter/chip-value.tsx +6 -5
  25. package/src/components/record-filter/filter-value-input.tsx +11 -9
  26. package/src/components/record-filter/operators.test.ts +67 -12
  27. package/src/components/record-filter/operators.ts +43 -2
  28. package/src/components/record-filter/types.ts +6 -1
  29. package/src/components/table/table.test.tsx +88 -9
  30. package/src/components/table/table.tsx +139 -13
  31. package/src/components/tag-input/tag-input.test.tsx +67 -1
  32. package/src/components/tag-input/tag-input.tsx +98 -64
  33. package/src/index.ts +1 -0
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@ import { cn, Button, Skeleton } from './chunk-IKAX76MB.js';
2
2
  export { Button, Skeleton, buttonStyles, cn, skeletonStyles } from './chunk-IKAX76MB.js';
3
3
  import { icons, Contact, Paperclip, BookText, Banknote, UsersRound, User, Link2, Braces, Brackets, ToggleLeft, CircleDot, Calendar, Hash, Type, File, FileImage, FileText, FileSpreadsheet, FileArchive, Loader2, X, Shapes, Search, Phone, AudioLines, Video, Mic, AtSign, MessagesSquare, Camera, Send, Linkedin, MessageCircle, Mail, Pipette, Pencil, ChevronDown, GripVertical, SendHorizontal, CircleX, Download, ChevronRight as ChevronRight$1, CircleCheck, CircleAlert, Info } from 'lucide-react';
4
4
  import { tv } from 'tailwind-variants';
5
- import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, Input as Input$1, ListBox, ListBoxItem, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Button as Button$1, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, TextField, ComboBox, ComboBoxStateContext, Select as Select$1, SelectValue, ModalOverlay, Modal as Modal$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, UNSTABLE_ToastQueue, UNSTABLE_ToastRegion, UNSTABLE_Toast, UNSTABLE_ToastContent, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
5
+ import { Tabs as Tabs$1, TabPanel, Tab, TabList, MenuTrigger as MenuTrigger$1, SubmenuTrigger as SubmenuTrigger$1, DialogTrigger, Popover as Popover$1, Dialog, DropZone, Text, FileTrigger, Heading, Label, Input as Input$1, ListBox, ListBoxItem, TooltipTrigger as TooltipTrigger$1, Tooltip as Tooltip$1, Button as Button$1, Breadcrumbs as Breadcrumbs$1, Breadcrumb, Link, Checkbox as Checkbox$1, CheckboxGroup as CheckboxGroup$1, FieldError, TextField, ComboBox, ComboBoxStateContext, Select as Select$1, SelectValue, ModalOverlay, Modal as Modal$1, DatePicker as DatePicker$1, Group, DateInput, DateSegment, Menu as Menu$1, MenuItem as MenuItem$1, Disclosure, DisclosurePanel, ProgressBar as ProgressBar$1, RadioGroup as RadioGroup$1, Radio as Radio$1, Separator as Separator$1, Switch as Switch$1, TextArea, TimeField as TimeField$1, UNSTABLE_ToastQueue, UNSTABLE_ToastRegion, UNSTABLE_Toast, UNSTABLE_ToastContent, Calendar as Calendar$1, CalendarGrid, CalendarGridHeader, CalendarHeaderCell, CalendarGridBody, CalendarCell, DatePickerStateContext } from 'react-aria-components';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { lazy, createContext, useState, useRef, useMemo, useEffect, useContext, useSyncExternalStore, useCallback, useLayoutEffect, Suspense, useId, Fragment as Fragment$1 } from 'react';
7
+ import { lazy, createContext, useState, useRef, useMemo, Children, isValidElement, useLayoutEffect, useEffect, useContext, useSyncExternalStore, useCallback, Suspense, useId, Fragment as Fragment$1 } from 'react';
8
8
  import { formatAmount, parseAmount } from '@proteos/sdk';
9
9
  import { draggable, dropTargetForElements, monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
10
10
  import { extractClosestEdge, attachClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
@@ -872,6 +872,7 @@ function Badge({
872
872
  hasDot,
873
873
  color,
874
874
  icon,
875
+ actions,
875
876
  className,
876
877
  style,
877
878
  children,
@@ -893,11 +894,249 @@ function Badge({
893
894
  ),
894
895
  children: [
895
896
  Icon ? /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: cn("size-3 shrink-0", ICON_TONE[register2]) }) : null,
896
- children
897
+ children,
898
+ actions ? (
899
+ // Pulled flush with the trailing edge so the chip still reads
900
+ // symmetric around its leading dot / icon.
901
+ /* @__PURE__ */ jsx("span", { className: "inline-flex items-center shrink-0 -mr-1", children: actions })
902
+ ) : null
897
903
  ]
898
904
  }
899
905
  );
900
906
  }
907
+ function BadgeAction({ label, onPress, isDisabled = false, children }) {
908
+ return /* @__PURE__ */ jsx(
909
+ "button",
910
+ {
911
+ type: "button",
912
+ "aria-label": label,
913
+ disabled: isDisabled,
914
+ onClick: (event) => {
915
+ event.stopPropagation();
916
+ onPress();
917
+ },
918
+ className: cn(
919
+ "inline-flex items-center justify-center shrink-0",
920
+ "size-3.5 rounded-full",
921
+ "text-current/70 hover:text-current hover:bg-current/10",
922
+ "outline-none cursor-pointer",
923
+ "focus-visible:shadow-[var(--shadow-focus)]",
924
+ "disabled:opacity-45 disabled:cursor-not-allowed"
925
+ ),
926
+ children: children ?? /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
927
+ "path",
928
+ {
929
+ d: "M2 2l6 6M8 2l-6 6",
930
+ stroke: "currentColor",
931
+ strokeWidth: "1.4",
932
+ strokeLinecap: "round"
933
+ }
934
+ ) })
935
+ }
936
+ );
937
+ }
938
+ function TooltipTrigger({
939
+ delay = 500,
940
+ closeDelay = 0,
941
+ ...props
942
+ }) {
943
+ return /* @__PURE__ */ jsx(TooltipTrigger$1, { delay, closeDelay, ...props });
944
+ }
945
+ function Tooltip({ className, offset = 6, ...props }) {
946
+ return /* @__PURE__ */ jsx(
947
+ Tooltip$1,
948
+ {
949
+ ...props,
950
+ offset,
951
+ className: (renderProps) => cn(
952
+ "z-50 max-w-xs",
953
+ "rounded-md border border-rule bg-surface shadow-[var(--shadow-raised)]",
954
+ "px-2.5 py-1.5",
955
+ "font-mono text-[11.5px] text-ink-2",
956
+ "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1 entering:duration-150",
957
+ "exiting:animate-out exiting:fade-out-0 exiting:duration-100",
958
+ typeof className === "function" ? className(renderProps) : className
959
+ )
960
+ }
961
+ );
962
+ }
963
+ var badgeGroupStyles = tv({
964
+ base: "flex flex-nowrap items-center gap-1.5 w-full min-w-0",
965
+ variants: {
966
+ // The measuring pass renders every chip plus the overflow chip so their
967
+ // widths can be read; it is hidden so that pass never paints.
968
+ isMeasuring: { true: "invisible", false: "" }
969
+ },
970
+ defaultVariants: { isMeasuring: false }
971
+ });
972
+ function fitBadgeCount(metrics, available) {
973
+ const { itemWidths, overflowWidth, gap } = metrics;
974
+ const total = itemWidths.length;
975
+ if (available <= 0) return total;
976
+ let fullWidth = 0;
977
+ for (let index = 0; index < total; index += 1) {
978
+ fullWidth += (itemWidths[index] ?? 0) + (index > 0 ? gap : 0);
979
+ }
980
+ if (fullWidth <= available) return total;
981
+ let used = 0;
982
+ let count = 0;
983
+ for (let index = 0; index < total; index += 1) {
984
+ const next = used + (itemWidths[index] ?? 0) + gap;
985
+ if (next + overflowWidth > available) break;
986
+ used = next;
987
+ count = index + 1;
988
+ }
989
+ return count;
990
+ }
991
+ var badgeGroupOverflowStyles = tv({
992
+ base: [
993
+ badgeStyles({ variant: "neutral", shape: "rounded", hasDot: false }),
994
+ // badgeStyles' register still materializes an empty ::before — hide it,
995
+ // as Badge itself does when no dot should show.
996
+ "before:hidden shrink-0 cursor-pointer whitespace-nowrap",
997
+ "hover:bg-surface-2 hover:text-ink",
998
+ "outline-none focus-visible:shadow-[var(--shadow-focus)]"
999
+ ]
1000
+ });
1001
+ function BadgeGroup({ children, className, ...props }) {
1002
+ const items = Children.toArray(children);
1003
+ const signature = items.map((item, index) => isValidElement(item) ? String(item.key ?? index) : String(item)).join("");
1004
+ const rootRef = useRef(null);
1005
+ const metricsRef = useRef(null);
1006
+ const [layout, setLayout] = useState({ signature, visibleCount: null });
1007
+ if (layout.signature !== signature) {
1008
+ metricsRef.current = null;
1009
+ setLayout({ signature, visibleCount: null });
1010
+ }
1011
+ const measuring = layout.signature !== signature || layout.visibleCount === null;
1012
+ const visibleCount = measuring ? items.length : layout.visibleCount ?? items.length;
1013
+ const shown = items.slice(0, visibleCount);
1014
+ const hidden = measuring ? items.length : items.length - visibleCount;
1015
+ useLayoutEffect(() => {
1016
+ if (!measuring) return;
1017
+ const root = rootRef.current;
1018
+ if (!root) return;
1019
+ const nodes = Array.from(root.children);
1020
+ const metrics = {
1021
+ itemWidths: nodes.slice(0, items.length).map((node) => node.getBoundingClientRect().width),
1022
+ overflowWidth: nodes[items.length]?.getBoundingClientRect().width ?? 0,
1023
+ gap: Number.parseFloat(getComputedStyle(root).columnGap) || 0
1024
+ };
1025
+ metricsRef.current = metrics;
1026
+ setLayout({ signature, visibleCount: fitBadgeCount(metrics, root.clientWidth) });
1027
+ }, [measuring, signature, items.length]);
1028
+ useEffect(() => {
1029
+ const root = rootRef.current;
1030
+ if (!root || typeof ResizeObserver === "undefined") return;
1031
+ const observer = new ResizeObserver(() => {
1032
+ const metrics = metricsRef.current;
1033
+ if (!metrics) return;
1034
+ const next = fitBadgeCount(metrics, root.clientWidth);
1035
+ setLayout((prev) => prev.visibleCount === next ? prev : { ...prev, visibleCount: next });
1036
+ });
1037
+ observer.observe(root);
1038
+ return () => observer.disconnect();
1039
+ }, []);
1040
+ useEffect(() => {
1041
+ const fonts = typeof document === "undefined" ? void 0 : document.fonts;
1042
+ if (!fonts || fonts.status === "loaded") return;
1043
+ let cancelled = false;
1044
+ void fonts.ready.then(() => {
1045
+ if (cancelled) return;
1046
+ metricsRef.current = null;
1047
+ setLayout((prev) => ({ ...prev, visibleCount: null }));
1048
+ });
1049
+ return () => {
1050
+ cancelled = true;
1051
+ };
1052
+ }, []);
1053
+ return /* @__PURE__ */ jsxs(
1054
+ "div",
1055
+ {
1056
+ ...props,
1057
+ ref: rootRef,
1058
+ className: cn(badgeGroupStyles({ isMeasuring: measuring }), className),
1059
+ children: [
1060
+ shown.map((item, index) => /* @__PURE__ */ jsx(
1061
+ "span",
1062
+ {
1063
+ className: "inline-flex shrink-0",
1064
+ children: item
1065
+ },
1066
+ (isValidElement(item) ? item.key : null) ?? index
1067
+ )),
1068
+ hidden > 0 ? (
1069
+ // The span swallows the click before it reaches a clickable host row
1070
+ // (React propagation), so opening the overflow never opens the record.
1071
+ // biome-ignore lint/a11y/noStaticElementInteractions: click fence only — the button inside is the interactive element
1072
+ // biome-ignore lint/a11y/useKeyWithClickEvents: keyboard handling lives on the button inside
1073
+ /* @__PURE__ */ jsx("span", { className: "inline-flex shrink-0", onClick: (event) => event.stopPropagation(), children: /* @__PURE__ */ jsxs(Popover, { children: [
1074
+ /* @__PURE__ */ jsxs(TooltipTrigger, { children: [
1075
+ /* @__PURE__ */ jsxs(Button$1, { className: badgeGroupOverflowStyles(), children: [
1076
+ "+",
1077
+ hidden,
1078
+ " more"
1079
+ ] }),
1080
+ /* @__PURE__ */ jsx(Tooltip, { className: "max-w-sm", children: /* @__PURE__ */ jsx("span", { className: "flex flex-wrap items-center gap-1.5", children: items }) })
1081
+ ] }),
1082
+ /* @__PURE__ */ jsx(PopoverContent, { placement: "bottom start", panelClassName: "max-w-sm", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 p-2.5", children: items }) })
1083
+ ] }) })
1084
+ ) : null
1085
+ ]
1086
+ }
1087
+ );
1088
+ }
1089
+ var listboxCheckStyles = tv({
1090
+ slots: {
1091
+ box: [
1092
+ "relative inline-flex items-center justify-center shrink-0",
1093
+ "size-[16px] rounded-[3px] border border-ink-4 bg-surface",
1094
+ "transition-[background-color,border-color] duration-150",
1095
+ "group-data-[selected]:bg-ink group-data-[selected]:border-ink"
1096
+ ],
1097
+ tick: [
1098
+ "size-3 text-surface transition-opacity duration-150",
1099
+ "opacity-0 group-data-[selected]:opacity-100"
1100
+ ]
1101
+ }
1102
+ });
1103
+ function ListboxCheck() {
1104
+ const { box, tick } = listboxCheckStyles();
1105
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: box(), children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", className: tick(), "aria-hidden": true, children: /* @__PURE__ */ jsx(
1106
+ "path",
1107
+ {
1108
+ d: "M2.5 6.25 5 8.75 9.5 3.75",
1109
+ stroke: "currentColor",
1110
+ strokeWidth: "1.6",
1111
+ strokeLinecap: "round",
1112
+ strokeLinejoin: "round",
1113
+ fill: "none"
1114
+ }
1115
+ ) }) });
1116
+ }
1117
+ function useChipRowWrap(ref, isEnabled, signature) {
1118
+ const [hasWrapped, setHasWrapped] = useState(false);
1119
+ useLayoutEffect(() => {
1120
+ if (!isEnabled) return;
1121
+ const element = ref.current;
1122
+ if (!element) return;
1123
+ const measure = () => {
1124
+ const children = Array.from(element.children);
1125
+ const first = children[0]?.getBoundingClientRect();
1126
+ if (!first || first.height === 0) {
1127
+ setHasWrapped(false);
1128
+ return;
1129
+ }
1130
+ setHasWrapped(children.some((child) => child.getBoundingClientRect().top >= first.bottom - 1));
1131
+ };
1132
+ measure();
1133
+ if (typeof ResizeObserver === "undefined") return;
1134
+ const observer = new ResizeObserver(measure);
1135
+ observer.observe(element);
1136
+ return () => observer.disconnect();
1137
+ }, [ref, isEnabled, signature]);
1138
+ return hasWrapped;
1139
+ }
901
1140
  function badgeChrome(option) {
902
1141
  return {
903
1142
  ...option.variant ? { variant: option.variant } : {},
@@ -921,18 +1160,51 @@ function BadgeMultiSelect({
921
1160
  size = "md",
922
1161
  appearance,
923
1162
  autoFocus = false,
1163
+ defaultOpen = false,
1164
+ "aria-label": ariaLabel,
924
1165
  className
925
1166
  }) {
1167
+ const isCell = appearance === "cell";
926
1168
  const styles4 = fieldControlStyles({ size, invalid: isInvalid, appearance });
927
- const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
928
- const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
1169
+ const focusWithin = isCell ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
929
1170
  const [isOpen, setIsOpen] = useState(false);
930
1171
  const triggerRef = useRef(null);
1172
+ const chipsRef = useRef(null);
1173
+ const popoverRef = useRef(null);
1174
+ const closedByPointerRef = useRef(false);
931
1175
  useEffect(() => {
932
1176
  if (autoFocus) triggerRef.current?.focus();
1177
+ if (defaultOpen && !isDisabled) setIsOpen(true);
933
1178
  }, []);
1179
+ const wasOpenRef = useRef(false);
1180
+ useEffect(() => {
1181
+ if (wasOpenRef.current && !isOpen && !closedByPointerRef.current) {
1182
+ const active = document.activeElement;
1183
+ const strandedInList = !active || active === document.body || !!active.closest('[data-rac][role="listbox"]');
1184
+ if (strandedInList) triggerRef.current?.focus();
1185
+ }
1186
+ wasOpenRef.current = isOpen;
1187
+ closedByPointerRef.current = false;
1188
+ }, [isOpen]);
934
1189
  const selectedSet = new Set(selectedKeys);
935
1190
  const selectedOptions = options.filter((o) => selectedSet.has(o.id));
1191
+ const hasWrapped = useChipRowWrap(
1192
+ chipsRef,
1193
+ isCell,
1194
+ JSON.stringify(selectedOptions.map((o) => o.id))
1195
+ );
1196
+ useEffect(() => {
1197
+ if (!isOpen) return;
1198
+ const onPointerDown = (event) => {
1199
+ const target = event.target;
1200
+ if (!(target instanceof Node)) return;
1201
+ if (triggerRef.current?.contains(target) || popoverRef.current?.contains(target)) return;
1202
+ closedByPointerRef.current = true;
1203
+ setIsOpen(false);
1204
+ };
1205
+ document.addEventListener("pointerdown", onPointerDown, true);
1206
+ return () => document.removeEventListener("pointerdown", onPointerDown, true);
1207
+ }, [isOpen]);
936
1208
  const listboxOptions = hideSelectedOptions ? options.filter((o) => !selectedSet.has(o.id)) : options;
937
1209
  const remove = (id) => {
938
1210
  const next = new Set(selectedSet);
@@ -944,6 +1216,12 @@ function BadgeMultiSelect({
944
1216
  if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
945
1217
  e.preventDefault();
946
1218
  setIsOpen(true);
1219
+ return;
1220
+ }
1221
+ if (e.key === "Escape" && isOpen) {
1222
+ e.preventDefault();
1223
+ e.stopPropagation();
1224
+ setIsOpen(false);
947
1225
  }
948
1226
  };
949
1227
  return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
@@ -953,6 +1231,7 @@ function BadgeMultiSelect({
953
1231
  {
954
1232
  ref: triggerRef,
955
1233
  role: "combobox",
1234
+ "aria-label": ariaLabel,
956
1235
  "aria-expanded": isOpen,
957
1236
  "aria-haspopup": "listbox",
958
1237
  "aria-disabled": isDisabled || void 0,
@@ -964,34 +1243,53 @@ function BadgeMultiSelect({
964
1243
  "data-disabled": isDisabled || void 0,
965
1244
  className: cn(
966
1245
  styles4.box(),
967
- "flex flex-wrap items-center gap-1.5 cursor-pointer",
968
- minHeight,
969
- fieldChipPadding[size],
970
- "pr-2",
1246
+ "flex items-stretch cursor-pointer",
1247
+ // Cell editor: one table row (29px under a 32px row) while the chips
1248
+ // fit on a line; two rows (64px), popping out of the row, once they
1249
+ // wrap — the chips then scroll inside. Field mode grows with its
1250
+ // content like every other chip field.
1251
+ isCell ? hasWrapped ? "h-[64px] p-1" : "h-[29px] px-1 py-0" : [fieldChipPadding[size], fieldMinHeight[size]],
971
1252
  focusWithin
972
1253
  ),
973
1254
  children: [
974
- selectedOptions.length === 0 ? (
975
- // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
976
- // overall height so it matches the Input/Select trigger at the same
977
- // size.
978
- /* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
979
- ) : selectedOptions.map((o) => /* @__PURE__ */ jsx(
980
- RemovableBadge,
1255
+ /* @__PURE__ */ jsx(
1256
+ "div",
981
1257
  {
982
- ...badgeChrome(o),
983
- onRemove: () => remove(o.id),
984
- isDisabled,
985
- children: o.label
986
- },
987
- o.id
988
- )),
989
- /* @__PURE__ */ jsx("span", { className: "ml-auto pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
1258
+ ref: chipsRef,
1259
+ className: cn(
1260
+ "flex flex-1 flex-wrap items-center gap-1.5 min-w-0",
1261
+ isCell && (hasWrapped ? "content-start overflow-y-auto" : "content-center overflow-hidden")
1262
+ ),
1263
+ children: selectedOptions.length === 0 ? (
1264
+ // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
1265
+ // overall height so it matches the Input/Select trigger at the same
1266
+ // size.
1267
+ /* @__PURE__ */ jsx("span", { className: cn("text-ink-4 font-sans px-1", fieldTextStyles[size]), children: placeholder })
1268
+ ) : selectedOptions.map((o) => /* @__PURE__ */ jsx(
1269
+ Badge,
1270
+ {
1271
+ ...badgeChrome(o),
1272
+ className: "shrink-0",
1273
+ actions: /* @__PURE__ */ jsx(
1274
+ BadgeAction,
1275
+ {
1276
+ label: "Remove",
1277
+ onPress: () => remove(o.id),
1278
+ isDisabled
1279
+ }
1280
+ ),
1281
+ children: o.label
1282
+ },
1283
+ o.id
1284
+ ))
1285
+ }
1286
+ ),
1287
+ isCell ? null : /* @__PURE__ */ jsx("span", { className: "flex items-center pl-1.5 pr-1", children: /* @__PURE__ */ jsx(FieldCaret, {}) })
990
1288
  ]
991
1289
  }
992
1290
  ),
993
1291
  description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
994
- errorMessage && appearance !== "cell" ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
1292
+ errorMessage && !isCell ? /* @__PURE__ */ jsx("p", { className: fieldErrorStyles, children: errorMessage }) : null,
995
1293
  /* @__PURE__ */ jsx(
996
1294
  Popover$1,
997
1295
  {
@@ -1000,18 +1298,27 @@ function BadgeMultiSelect({
1000
1298
  triggerRef,
1001
1299
  offset: 6,
1002
1300
  placement: "bottom start",
1301
+ isNonModal: true,
1302
+ shouldCloseOnInteractOutside: (element) => !triggerRef.current?.contains(element),
1003
1303
  className: cn(
1004
- "w-[var(--trigger-width)]",
1005
- "rounded-md border border-rule bg-surface p-1",
1304
+ // At least as wide as the trigger, wider when a badge needs it —
1305
+ // a narrow table column must not clip its own options.
1306
+ "min-w-[var(--trigger-width)] w-max max-w-[min(28rem,calc(100vw-2rem))]",
1307
+ // Float above modals + full-screen editors, matching Select.
1308
+ "z-[1300]",
1309
+ // The popover element itself takes focus when opened programmatically
1310
+ // (defaultOpen) — no browser outline around the whole panel.
1311
+ "outline-none rounded-md border border-rule bg-surface p-1",
1006
1312
  "shadow-[var(--shadow-raised)]",
1007
1313
  "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
1008
1314
  "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
1009
1315
  ),
1010
- children: /* @__PURE__ */ jsx(
1316
+ children: /* @__PURE__ */ jsx("div", { ref: popoverRef, children: /* @__PURE__ */ jsx(
1011
1317
  ListBox,
1012
1318
  {
1013
- "aria-label": typeof label === "string" ? label : "Options",
1319
+ "aria-label": typeof label === "string" ? label : ariaLabel ?? "Options",
1014
1320
  selectionMode: "multiple",
1321
+ escapeKeyBehavior: "none",
1015
1322
  selectedKeys: selectedSet,
1016
1323
  onSelectionChange: (keys) => {
1017
1324
  if (keys === "all") {
@@ -1020,70 +1327,32 @@ function BadgeMultiSelect({
1020
1327
  }
1021
1328
  onSelectionChange(keys);
1022
1329
  },
1330
+ autoFocus: "first",
1023
1331
  className: "outline-none max-h-72 overflow-auto",
1024
1332
  renderEmptyState: () => /* @__PURE__ */ jsx("div", { className: cn("px-3 py-2", fieldHintStyles), children: "All options selected" }),
1025
- children: listboxOptions.map((option) => /* @__PURE__ */ jsx(
1333
+ children: listboxOptions.map((option) => /* @__PURE__ */ jsxs(
1026
1334
  ListBoxItem,
1027
1335
  {
1028
1336
  id: option.id,
1029
1337
  textValue: option.label,
1030
1338
  ...option.isDisabled ? { isDisabled: true } : {},
1031
1339
  className: cn(
1032
- "group flex items-center",
1033
- "rounded-[6px] px-3 py-2",
1340
+ "group flex items-center gap-2.5",
1341
+ "rounded-[6px] px-2.5 py-1.5",
1034
1342
  "cursor-pointer outline-none select-none",
1343
+ // Selection is carried by the check box, not a row fill.
1035
1344
  "data-[hovered]:bg-surface-2",
1036
1345
  "data-[focused]:bg-surface-2",
1037
- "data-[selected]:bg-surface-3",
1038
1346
  "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
1039
1347
  ),
1040
- children: /* @__PURE__ */ jsx(Badge, { ...badgeChrome(option), children: option.label })
1348
+ children: [
1349
+ /* @__PURE__ */ jsx(ListboxCheck, {}),
1350
+ /* @__PURE__ */ jsx(Badge, { ...badgeChrome(option), children: option.label })
1351
+ ]
1041
1352
  },
1042
1353
  option.id
1043
1354
  ))
1044
1355
  }
1045
- )
1046
- }
1047
- )
1048
- ] });
1049
- }
1050
- function RemovableBadge({
1051
- children,
1052
- onRemove,
1053
- isDisabled,
1054
- ...chrome
1055
- }) {
1056
- return /* @__PURE__ */ jsxs(Badge, { ...badgeChrome(chrome), children: [
1057
- /* @__PURE__ */ jsx("span", { children }),
1058
- /* @__PURE__ */ jsx(
1059
- "button",
1060
- {
1061
- type: "button",
1062
- "aria-label": "Remove",
1063
- disabled: isDisabled,
1064
- onClick: (e) => {
1065
- e.stopPropagation();
1066
- onRemove();
1067
- },
1068
- className: cn(
1069
- "inline-flex items-center justify-center shrink-0",
1070
- "size-3.5 rounded-full",
1071
- "text-current/70 hover:text-current hover:bg-current/10",
1072
- "outline-none cursor-pointer",
1073
- "focus-visible:shadow-[var(--shadow-focus)]",
1074
- "disabled:opacity-45 disabled:cursor-not-allowed",
1075
- // Pull the button flush with the right edge so the pill still feels
1076
- // symmetric around the leading dot.
1077
- "-mr-1"
1078
- ),
1079
- children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2.5", fill: "none", children: /* @__PURE__ */ jsx(
1080
- "path",
1081
- {
1082
- d: "M2 2l6 6M8 2l-6 6",
1083
- stroke: "currentColor",
1084
- strokeWidth: "1.4",
1085
- strokeLinecap: "round"
1086
- }
1087
1356
  ) })
1088
1357
  }
1089
1358
  )
@@ -2687,7 +2956,9 @@ var tableHeaderStyles = tv({
2687
2956
  "border-b border-rule"
2688
2957
  ],
2689
2958
  variants: {
2690
- sticky: { true: "sticky top-0 z-[2]", false: "" }
2959
+ // z-[4]: above an editing row (z-[3]) — sticky chrome stays on top of
2960
+ // rows scrolling underneath, popped-out editors included.
2961
+ sticky: { true: "sticky top-0 z-[4]", false: "" }
2691
2962
  },
2692
2963
  defaultVariants: { sticky: false }
2693
2964
  });
@@ -2752,11 +3023,57 @@ function TableHeaderCell({
2752
3023
  onRemove,
2753
3024
  ariaLabel,
2754
3025
  sticky = false,
3026
+ onResize,
3027
+ minWidth = 64,
2755
3028
  ...rest
2756
3029
  }) {
2757
3030
  const cellRef = useRef(null);
2758
3031
  const handleRef = useRef(null);
2759
3032
  const [dragState, setDragState] = useState({ kind: "idle" });
3033
+ const [isResizing, setIsResizing] = useState(false);
3034
+ const startResize = (event) => {
3035
+ if (!onResize || event.button !== 0) return;
3036
+ const cell = cellRef.current;
3037
+ if (!cell) return;
3038
+ event.preventDefault();
3039
+ event.stopPropagation();
3040
+ const handle = event.currentTarget;
3041
+ const startX = event.clientX;
3042
+ const startWidth = cell.getBoundingClientRect().width;
3043
+ if (typeof handle.setPointerCapture === "function") handle.setPointerCapture(event.pointerId);
3044
+ setIsResizing(true);
3045
+ document.body.style.cursor = "col-resize";
3046
+ let frame = 0;
3047
+ const report = (clientX) => {
3048
+ const next = Math.max(minWidth, Math.round(startWidth + clientX - startX));
3049
+ if (typeof requestAnimationFrame !== "function") {
3050
+ onResize(next);
3051
+ return;
3052
+ }
3053
+ cancelAnimationFrame(frame);
3054
+ frame = requestAnimationFrame(() => onResize(next));
3055
+ };
3056
+ const onMove = (move) => report(move.clientX);
3057
+ const onEnd = () => {
3058
+ handle.removeEventListener("pointermove", onMove);
3059
+ handle.removeEventListener("pointerup", onEnd);
3060
+ handle.removeEventListener("pointercancel", onEnd);
3061
+ document.body.style.cursor = "";
3062
+ setIsResizing(false);
3063
+ };
3064
+ handle.addEventListener("pointermove", onMove);
3065
+ handle.addEventListener("pointerup", onEnd);
3066
+ handle.addEventListener("pointercancel", onEnd);
3067
+ };
3068
+ const resizeByKey = (event) => {
3069
+ if (!onResize) return;
3070
+ const step = event.key === "ArrowRight" ? 8 : event.key === "ArrowLeft" ? -8 : 0;
3071
+ if (step === 0) return;
3072
+ event.preventDefault();
3073
+ event.stopPropagation();
3074
+ const current = cellRef.current?.getBoundingClientRect().width ?? minWidth;
3075
+ onResize(Math.max(minWidth, Math.round(current + step)));
3076
+ };
2760
3077
  useEffect(() => {
2761
3078
  if (!isDraggable || !columnId) return;
2762
3079
  const cell = cellRef.current;
@@ -2774,10 +3091,7 @@ function TableHeaderCell({
2774
3091
  dropTargetForElements({
2775
3092
  element: cell,
2776
3093
  canDrop: ({ source }) => isColumnDragData(source.data) && source.data.columnId !== columnId,
2777
- getData: ({ input, element }) => attachClosestEdge(
2778
- { ...data },
2779
- { input, element, allowedEdges: ["left", "right"] }
2780
- ),
3094
+ getData: ({ input, element }) => attachClosestEdge({ ...data }, { input, element, allowedEdges: ["left", "right"] }),
2781
3095
  getIsSticky: () => true,
2782
3096
  onDragEnter: ({ self }) => {
2783
3097
  const edge = extractClosestEdge(self.data);
@@ -2804,6 +3118,7 @@ function TableHeaderCell({
2804
3118
  const target = e.target;
2805
3119
  if (target.closest('[data-dnd-handle="true"]')) return;
2806
3120
  if (target.closest('[data-column-remove="true"]')) return;
3121
+ if (target.closest('[data-column-resize="true"]')) return;
2807
3122
  onSortToggle?.();
2808
3123
  };
2809
3124
  const handleKeyDown = (e) => {
@@ -2887,6 +3202,42 @@ function TableHeaderCell({
2887
3202
  dragState.edge === "left" ? "-left-px" : "-right-px"
2888
3203
  )
2889
3204
  }
3205
+ ) : null,
3206
+ onResize ? (
3207
+ // Sits astride the trailing separator: an 8px grab zone with a 2px
3208
+ // bar that lights up on hover, focus and while dragging. A focusable
3209
+ // separator is the ARIA pattern for a resizer (no semantic element
3210
+ // exists for it); its value is the column width when one is known.
3211
+ // biome-ignore lint/a11y/useSemanticElements: interactive vertical resizer — <hr> cannot be one
3212
+ /* @__PURE__ */ jsx(
3213
+ "span",
3214
+ {
3215
+ role: "separator",
3216
+ "aria-orientation": "vertical",
3217
+ "aria-label": `Resize column ${ariaLabel ?? "column"}`,
3218
+ "aria-valuemin": minWidth,
3219
+ "aria-valuenow": typeof width === "number" ? width : void 0,
3220
+ tabIndex: 0,
3221
+ "data-column-resize": "true",
3222
+ onPointerDown: startResize,
3223
+ onKeyDown: resizeByKey,
3224
+ onClick: (e) => e.stopPropagation(),
3225
+ className: cn(
3226
+ "group/resize absolute inset-y-0 -right-1 z-10 w-2 cursor-col-resize outline-none",
3227
+ "flex justify-center"
3228
+ ),
3229
+ children: /* @__PURE__ */ jsx(
3230
+ "span",
3231
+ {
3232
+ "aria-hidden": "true",
3233
+ className: cn(
3234
+ "h-full w-0.5 transition-colors",
3235
+ isResizing ? "bg-accent" : "bg-transparent group-hover/resize:bg-accent group-focus-visible/resize:bg-accent"
3236
+ )
3237
+ }
3238
+ )
3239
+ }
3240
+ )
2890
3241
  ) : null
2891
3242
  ]
2892
3243
  }
@@ -2940,12 +3291,27 @@ var tableScrollerStyles = tv({
2940
3291
  base: ["relative flex-1 min-h-0 overflow-auto", ...scrollbarChrome]
2941
3292
  });
2942
3293
  function TableScroller({ className, ref, ...props }) {
2943
- return /* @__PURE__ */ jsx("div", { role: "presentation", ref, ...props, className: cn(tableScrollerStyles(), className) });
3294
+ return /* @__PURE__ */ jsx(
3295
+ "div",
3296
+ {
3297
+ role: "presentation",
3298
+ ref,
3299
+ ...props,
3300
+ className: cn(tableScrollerStyles(), className)
3301
+ }
3302
+ );
2944
3303
  }
2945
3304
  var rowStripeBg = "bg-[color-mix(in_oklab,var(--color-ink)_var(--row-stripe),var(--color-bg))]";
2946
3305
  var rowHoverBg = "hover:bg-[color-mix(in_oklab,var(--color-ink)_var(--row-hover),var(--color-bg))]";
3306
+ var cellEditingRow = "has-[[data-cell-editing]]:z-[3]";
3307
+ var cellEditingCell = "has-[[data-cell-editing]]:z-[3] has-[[data-cell-editing]]:overflow-visible";
2947
3308
  var tableRowStyles = tv({
2948
- base: ["flex items-stretch", "border-b border-rule-2 last:border-b-0", "bg-bg"],
3309
+ base: [
3310
+ "relative flex items-stretch",
3311
+ "border-b border-rule-2 last:border-b-0",
3312
+ "bg-bg",
3313
+ cellEditingRow
3314
+ ],
2949
3315
  variants: {
2950
3316
  alt: {
2951
3317
  true: rowStripeBg,
@@ -2956,7 +3322,9 @@ var tableRowStyles = tv({
2956
3322
  false: rowHoverBg
2957
3323
  },
2958
3324
  absolute: {
2959
- true: "absolute left-0 right-0",
3325
+ // `top-0` pins the static position: a virtualized row must never
3326
+ // inherit a flow offset from a sibling that is in normal flow.
3327
+ true: "absolute top-0 left-0 right-0",
2960
3328
  false: ""
2961
3329
  }
2962
3330
  },
@@ -2987,11 +3355,12 @@ function TableRow({
2987
3355
  }
2988
3356
  var tableCellStyles = tv({
2989
3357
  base: [
2990
- "flex items-center shrink-0 min-w-0",
3358
+ "relative flex items-center shrink-0 min-w-0",
2991
3359
  "px-[14px]",
2992
3360
  "whitespace-nowrap overflow-hidden text-ellipsis",
2993
3361
  "font-sans text-[13px] tracking-[-0.005em]",
2994
- "text-ink-2"
3362
+ "text-ink-2",
3363
+ cellEditingCell
2995
3364
  ],
2996
3365
  variants: {
2997
3366
  align: {
@@ -3152,6 +3521,8 @@ function DataTable({
3152
3521
  enableColumnRemoval = false,
3153
3522
  onReorderColumn,
3154
3523
  onRemoveColumn,
3524
+ enableColumnResizing = false,
3525
+ onResizeColumn,
3155
3526
  variant = "panel",
3156
3527
  rowWrapper,
3157
3528
  extraRows,
@@ -3365,7 +3736,6 @@ function DataTable({
3365
3736
  const showEmpty = !query.isLoading && !query.error && tableRowCount === 0 && !query.isFetchingNextPage;
3366
3737
  const showInitialLoading = query.isLoading && tableRowCount === 0;
3367
3738
  const showError = !!query.error && tableRowCount === 0;
3368
- const showEndMarker = !query.isLoading && !query.isFetchingNextPage && !query.hasNextPage && tableRowCount > 0;
3369
3739
  const totalSize = virtualizer.getTotalSize();
3370
3740
  const virtualItems = virtualizer.getVirtualItems();
3371
3741
  const headerGroups = table.getHeaderGroups();
@@ -3376,84 +3746,108 @@ function DataTable({
3376
3746
  ...ariaLabel !== void 0 ? { "aria-label": ariaLabel } : {},
3377
3747
  variant,
3378
3748
  className: cn("flex-1 min-h-0", className),
3379
- children: /* @__PURE__ */ jsx(TableScroller, { ref: scrollerRef, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { minWidth: minPanelWidth }, children: [
3380
- headerGroups.map((group) => /* @__PURE__ */ jsxs(TableHeader, { sticky: true, children: [
3381
- group.headers.map((header) => {
3382
- const meta = header.column.columnDef.meta;
3383
- const align = meta?.align ?? "left";
3384
- const sticky = meta?.sticky ?? false;
3385
- const headerSize = header.getSize() || void 0;
3386
- const headerNode = header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext());
3387
- const sorted = header.column.getIsSorted();
3388
- const direction = sorted === false ? null : sorted;
3389
- const canSort = header.column.getCanSort();
3390
- const handleSort = header.column.getToggleSortingHandler();
3391
- const ariaLabel2 = typeof header.column.columnDef.header === "string" ? header.column.columnDef.header : header.column.id;
3392
- return /* @__PURE__ */ jsx(
3393
- TableHeaderCell,
3394
- {
3395
- align,
3396
- ariaLabel: ariaLabel2,
3397
- draggable: enableColumnReordering && !!header.column.id,
3398
- ...header.column.id ? { columnId: header.column.id } : {},
3399
- ...headerSize !== void 0 ? { width: headerSize } : {},
3400
- ...sticky !== false ? { sticky } : {},
3401
- ...meta?.cellClassName ? { className: meta.cellClassName } : {},
3402
- ...canSort ? {
3403
- sortDirection: direction,
3404
- onSortToggle: () => handleSort?.({})
3405
- } : {},
3406
- ...enableColumnRemoval && onRemoveColumn && header.column.id ? { onRemove: () => onRemoveColumn(header.column.id) } : {},
3407
- children: headerNode
3408
- },
3409
- header.id
3410
- );
3411
- }),
3412
- /* @__PURE__ */ jsx(TableHeaderSpacer, {})
3413
- ] }, group.id)),
3414
- bodyBefore,
3415
- showInitialLoading ? loadingState ?? /* @__PURE__ */ jsx(DataTableSkeleton, { rowHeight }) : showError ? /* @__PURE__ */ jsx("div", { role: "rowgroup", children: errorState ?? (query.error ? /* @__PURE__ */ jsx(DataTableError, { error: query.error }) : null) }) : /* @__PURE__ */ jsxs("div", { role: "rowgroup", children: [
3416
- showEmpty ? emptyState ?? /* @__PURE__ */ jsx(DataTableEmpty, {}) : /* @__PURE__ */ jsx(
3417
- "div",
3418
- {
3419
- ref: listRef,
3420
- role: "presentation",
3421
- style: { height: totalSize, position: "relative", width: "100%" },
3422
- children: virtualItems.map((vRow) => {
3423
- const item = listItems[vRow.index];
3424
- if (!item) return null;
3425
- const rowStyle = {
3426
- // Items' `start` includes the virtualizer's scrollMargin
3427
- // (the list's offset below the sticky header); subtract
3428
- // it to position relative to the list container.
3429
- transform: `translateY(${vRow.start - listOffset}px)`,
3430
- height: vRow.size
3431
- };
3432
- if (item.kind === "group") {
3433
- return /* @__PURE__ */ jsx(GroupHeaderRow, { style: rowStyle, children: item.label }, `group:${item.id}`);
3434
- }
3749
+ children: /* @__PURE__ */ jsx(
3750
+ TableScroller,
3751
+ {
3752
+ ref: scrollerRef,
3753
+ className: cn("scroll-pt-[36px]", bodyAfter ? "scroll-pb-8" : void 0),
3754
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", style: { minWidth: minPanelWidth }, children: [
3755
+ headerGroups.map((group) => /* @__PURE__ */ jsxs(TableHeader, { sticky: true, children: [
3756
+ group.headers.map((header) => {
3757
+ const meta = header.column.columnDef.meta;
3758
+ const align = meta?.align ?? "left";
3759
+ const sticky = meta?.sticky ?? false;
3760
+ const headerSize = header.getSize() || void 0;
3761
+ const headerNode = header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext());
3762
+ const sorted = header.column.getIsSorted();
3763
+ const direction = sorted === false ? null : sorted;
3764
+ const canSort = header.column.getCanSort();
3765
+ const handleSort = header.column.getToggleSortingHandler();
3766
+ const ariaLabel2 = typeof header.column.columnDef.header === "string" ? header.column.columnDef.header : header.column.id;
3435
3767
  return /* @__PURE__ */ jsx(
3436
- DataRow,
3768
+ TableHeaderCell,
3437
3769
  {
3438
- row: item.row,
3439
- index: item.dataIndex,
3440
- zebra,
3441
- onRowClick,
3442
- style: rowStyle,
3443
- columnBorders,
3444
- ...rowWrapper ? { rowWrapper } : {}
3770
+ align,
3771
+ ariaLabel: ariaLabel2,
3772
+ draggable: enableColumnReordering && !!header.column.id,
3773
+ ...header.column.id ? { columnId: header.column.id } : {},
3774
+ ...headerSize !== void 0 ? { width: headerSize } : {},
3775
+ ...sticky !== false ? { sticky } : {},
3776
+ ...meta?.cellClassName ? { className: meta.cellClassName } : {},
3777
+ ...canSort ? {
3778
+ sortDirection: direction,
3779
+ onSortToggle: () => handleSort?.({})
3780
+ } : {},
3781
+ ...enableColumnRemoval && onRemoveColumn && header.column.id ? { onRemove: () => onRemoveColumn(header.column.id) } : {},
3782
+ ...enableColumnResizing && onResizeColumn && header.column.id ? {
3783
+ onResize: (width) => onResizeColumn({ id: header.column.id, width })
3784
+ } : {},
3785
+ children: headerNode
3445
3786
  },
3446
- item.row.id
3787
+ header.id
3447
3788
  );
3448
- })
3449
- }
3450
- ),
3451
- !showEmpty && /* @__PURE__ */ jsx("div", { ref: sentinelRef, "aria-hidden": "true", style: { height: 1, width: "100%" } }),
3452
- query.isFetchingNextPage ? /* @__PURE__ */ jsx(LoaderRow, {}) : null,
3453
- showEndMarker ? /* @__PURE__ */ jsx(EndMarker, { count: tableRowCount }) : null
3454
- ] }),
3455
- bodyAfter
3456
- ] }) })
3789
+ }),
3790
+ /* @__PURE__ */ jsx(TableHeaderSpacer, {})
3791
+ ] }, group.id)),
3792
+ bodyBefore,
3793
+ showInitialLoading ? loadingState ?? /* @__PURE__ */ jsx(DataTableSkeleton, { rowHeight }) : showError ? /* @__PURE__ */ jsx("div", { role: "rowgroup", children: errorState ?? (query.error ? /* @__PURE__ */ jsx(DataTableError, { error: query.error }) : null) }) : /* @__PURE__ */ jsxs("div", { role: "rowgroup", children: [
3794
+ showEmpty ? emptyState ?? /* @__PURE__ */ jsx(DataTableEmpty, {}) : /* @__PURE__ */ jsx(
3795
+ "div",
3796
+ {
3797
+ ref: listRef,
3798
+ role: "presentation",
3799
+ style: { height: totalSize, position: "relative", width: "100%" },
3800
+ children: virtualItems.map((vRow) => {
3801
+ const item = listItems[vRow.index];
3802
+ if (!item) return null;
3803
+ const rowStyle = {
3804
+ // Items' `start` includes the virtualizer's scrollMargin
3805
+ // (the list's offset below the sticky header); subtract
3806
+ // it to position relative to the list container.
3807
+ transform: `translateY(${vRow.start - listOffset}px)`,
3808
+ height: vRow.size
3809
+ };
3810
+ if (item.kind === "group") {
3811
+ return /* @__PURE__ */ jsx(GroupHeaderRow, { style: rowStyle, children: item.label }, `group:${item.id}`);
3812
+ }
3813
+ return /* @__PURE__ */ jsx(
3814
+ DataRow,
3815
+ {
3816
+ row: item.row,
3817
+ index: item.dataIndex,
3818
+ zebra,
3819
+ onRowClick,
3820
+ style: rowStyle,
3821
+ columnBorders,
3822
+ ...rowWrapper ? { rowWrapper } : {}
3823
+ },
3824
+ item.row.id
3825
+ );
3826
+ })
3827
+ }
3828
+ ),
3829
+ !showEmpty && /* @__PURE__ */ jsx("div", { ref: sentinelRef, "aria-hidden": "true", style: { height: 1, width: "100%" } }),
3830
+ query.isFetchingNextPage ? /* @__PURE__ */ jsx(LoaderRow, {}) : null
3831
+ ] }),
3832
+ bodyAfter ? (
3833
+ // Sticky to the scroller's bottom edge; `bg-bg` covers the rows
3834
+ // sliding underneath. z-[4] — like the sticky header — stays above an
3835
+ // editing row (z-[3]), so an editor scrolled under the strip is
3836
+ // covered rather than drawn over it.
3837
+ /* @__PURE__ */ jsx(
3838
+ "div",
3839
+ {
3840
+ className: cn(
3841
+ "sticky bottom-0 z-[4] flex items-center h-8 px-0.5",
3842
+ "border-t border-rule-2 bg-bg"
3843
+ ),
3844
+ children: bodyAfter
3845
+ }
3846
+ )
3847
+ ) : null
3848
+ ] })
3849
+ }
3850
+ )
3457
3851
  }
3458
3852
  );
3459
3853
  }
@@ -3553,23 +3947,6 @@ function LoaderRow() {
3553
3947
  }
3554
3948
  );
3555
3949
  }
3556
- function EndMarker({ count }) {
3557
- return /* @__PURE__ */ jsxs(
3558
- "div",
3559
- {
3560
- className: cn(
3561
- "flex items-center justify-center",
3562
- "h-8 font-mono text-[10.5px] uppercase tracking-[0.08em]",
3563
- "text-ink-4"
3564
- ),
3565
- children: [
3566
- "end \xB7 ",
3567
- count.toLocaleString(),
3568
- " rows"
3569
- ]
3570
- }
3571
- );
3572
- }
3573
3950
 
3574
3951
  // src/components/data-table/make-actions-column.ts
3575
3952
  function makeActionsColumn({
@@ -4157,7 +4534,9 @@ function MultiSelect({
4157
4534
  placement: "bottom start",
4158
4535
  className: cn(
4159
4536
  "w-[var(--trigger-width)]",
4160
- "rounded-md border border-rule bg-surface p-1",
4537
+ // The popover element itself takes focus when opened programmatically
4538
+ // (defaultOpen) — no browser outline around the whole panel.
4539
+ "outline-none rounded-md border border-rule bg-surface p-1",
4161
4540
  "shadow-[var(--shadow-raised)]",
4162
4541
  "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1",
4163
4542
  "exiting:animate-out exiting:fade-out-0 exiting:slide-out-to-top-1"
@@ -4182,6 +4561,7 @@ function MultiSelect({
4182
4561
  {
4183
4562
  "aria-label": typeof label === "string" ? label : "Options",
4184
4563
  selectionMode: "multiple",
4564
+ escapeKeyBehavior: "none",
4185
4565
  selectedKeys: selectedSet,
4186
4566
  onSelectionChange: (keys) => {
4187
4567
  if (keys === "all") {
@@ -4204,11 +4584,10 @@ function MultiSelect({
4204
4584
  "cursor-pointer outline-none select-none",
4205
4585
  "data-[hovered]:bg-surface-2 data-[hovered]:text-ink",
4206
4586
  "data-[focused]:bg-surface-2 data-[focused]:text-ink",
4207
- "data-[selected]:bg-surface-3 data-[selected]:text-ink",
4208
4587
  "data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed"
4209
4588
  ),
4210
4589
  children: [
4211
- /* @__PURE__ */ jsx(Checkmark, {}),
4590
+ /* @__PURE__ */ jsx(ListboxCheck, {}),
4212
4591
  /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: option.content ?? option.label })
4213
4592
  ]
4214
4593
  },
@@ -4269,42 +4648,6 @@ function Chip({
4269
4648
  }
4270
4649
  );
4271
4650
  }
4272
- function Checkmark() {
4273
- return /* @__PURE__ */ jsx(
4274
- "span",
4275
- {
4276
- "aria-hidden": true,
4277
- className: cn(
4278
- "relative inline-flex items-center justify-center shrink-0",
4279
- "size-[16px] rounded-[3px] border border-ink-4 bg-surface",
4280
- "transition-[background-color,border-color] duration-150",
4281
- "group-data-[selected]:bg-ink group-data-[selected]:border-ink"
4282
- ),
4283
- children: /* @__PURE__ */ jsx(
4284
- "svg",
4285
- {
4286
- viewBox: "0 0 12 12",
4287
- className: cn(
4288
- "size-3 text-surface transition-opacity duration-150",
4289
- "opacity-0 group-data-[selected]:opacity-100"
4290
- ),
4291
- "aria-hidden": true,
4292
- children: /* @__PURE__ */ jsx(
4293
- "path",
4294
- {
4295
- d: "M2.5 6.25 5 8.75 9.5 3.75",
4296
- stroke: "currentColor",
4297
- strokeWidth: "1.6",
4298
- strokeLinecap: "round",
4299
- strokeLinejoin: "round",
4300
- fill: "none"
4301
- }
4302
- )
4303
- }
4304
- )
4305
- }
4306
- );
4307
- }
4308
4651
  var SEARCH_DEBOUNCE_MS = 250;
4309
4652
  function RecipientSelect({
4310
4653
  connectionId,
@@ -5874,7 +6217,8 @@ var OPERATORS = [
5874
6217
  "relation",
5875
6218
  "user",
5876
6219
  "principal",
5877
- "contact-address"
6220
+ "contact-address",
6221
+ "array"
5878
6222
  ]
5879
6223
  },
5880
6224
  {
@@ -5890,10 +6234,19 @@ var OPERATORS = [
5890
6234
  "relation",
5891
6235
  "user",
5892
6236
  "principal",
5893
- "contact-address"
6237
+ "contact-address",
6238
+ "array"
5894
6239
  ]
5895
6240
  },
5896
6241
  { id: "contains", label: "contains", arity: 1, types: ["string", "array", "contact-address"] },
6242
+ {
6243
+ id: "not_contains",
6244
+ label: "does not contain",
6245
+ arity: 1,
6246
+ types: ["string", "array", "contact-address"]
6247
+ },
6248
+ // Array-only: "has every one of these". On a scalar the backend rejects it.
6249
+ { id: "contains_all", label: "has all of", arity: "n", types: ["array"] },
5897
6250
  { id: "starts_with", label: "starts with", arity: 1, types: ["string", "contact-address"] },
5898
6251
  { id: "ends_with", label: "ends with", arity: 1, types: ["string", "contact-address"] },
5899
6252
  {
@@ -5956,14 +6309,33 @@ var OP_LABEL_OVERRIDES = {
5956
6309
  integer: { gt: "is greater than", lt: "is less than", eq: "equals", ne: "does not equal" },
5957
6310
  currency: { gt: "is greater than", lt: "is less than", eq: "equals", ne: "does not equal" },
5958
6311
  datetime: { eq: "is on", ne: "is not on", gt: "is after", lt: "is before" },
5959
- array: { contains: "contains", empty: "is empty", not_empty: "is not empty" },
6312
+ // Arrays filter by set membership, in the words every CRM uses for it.
6313
+ array: {
6314
+ contains: "has",
6315
+ not_contains: "does not have",
6316
+ in: "has any of",
6317
+ not_in: "has none of",
6318
+ contains_all: "has all of",
6319
+ empty: "is empty",
6320
+ not_empty: "is not empty"
6321
+ },
5960
6322
  boolean: { eq: "is", ne: "is not" }
5961
6323
  };
6324
+ var OP_ORDER_OVERRIDES = {
6325
+ array: ["contains", "not_contains", "in", "contains_all", "not_in", "empty", "not_empty"]
6326
+ };
5962
6327
  function operatorsForType(type) {
5963
- return OPERATORS.filter((op) => op.types.includes(type)).map((op) => {
6328
+ const ops = OPERATORS.filter((op) => op.types.includes(type)).map((op) => {
5964
6329
  const override = OP_LABEL_OVERRIDES[type]?.[op.id];
5965
6330
  return override ? { ...op, label: override } : op;
5966
6331
  });
6332
+ const order = OP_ORDER_OVERRIDES[type];
6333
+ if (!order) return ops;
6334
+ return [...ops].sort((a, b) => order.indexOf(a.id) - order.indexOf(b.id));
6335
+ }
6336
+ function isEnumLike(field) {
6337
+ if (field.type === "enum") return true;
6338
+ return field.type === "array" && (field.options?.length ?? 0) > 0;
5967
6339
  }
5968
6340
  function operatorById(id) {
5969
6341
  return OPERATORS.find((o) => o.id === id);
@@ -6035,7 +6407,7 @@ function ChipItem({
6035
6407
  const custom = effectiveRenderer({ field, operator, value });
6036
6408
  if (custom !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: custom });
6037
6409
  }
6038
- if (field.type === "enum") {
6410
+ if (isEnumLike(field)) {
6039
6411
  const option = findEnumOption(field, value);
6040
6412
  return /* @__PURE__ */ jsx(
6041
6413
  Badge,
@@ -6063,7 +6435,7 @@ function formatValue(field, operator, value) {
6063
6435
  if (arity === "n") {
6064
6436
  const arr = Array.isArray(value) ? value : typeof value === "string" && value.length > 0 ? value.split(",") : [];
6065
6437
  if (arr.length === 0) return "\u2014";
6066
- if (field.type === "enum") {
6438
+ if (isEnumLike(field)) {
6067
6439
  const labels = arr.map((v) => findEnumOption(field, v)?.label ?? v);
6068
6440
  return labels.length <= 2 ? labels.join(", ") : `${labels[0]}, ${labels[1]} +${labels.length - 2}`;
6069
6441
  }
@@ -6071,7 +6443,7 @@ function formatValue(field, operator, value) {
6071
6443
  }
6072
6444
  if (field.type === "boolean") return value === true || value === "true" ? "true" : "false";
6073
6445
  if (value === "" || value == null) return "\u2014";
6074
- if (field.type === "enum") {
6446
+ if (isEnumLike(field)) {
6075
6447
  return findEnumOption(field, String(value))?.label ?? String(value);
6076
6448
  }
6077
6449
  if (field.type === "number" || field.type === "integer") {
@@ -6159,13 +6531,21 @@ function TagInput({
6159
6531
  }) {
6160
6532
  const styles4 = fieldControlStyles({ size, invalid: isInvalid, appearance });
6161
6533
  const focusWithin = appearance === "cell" ? "focus-within:border-accent focus-within:shadow-none" : "focus-within:border-accent focus-within:shadow-[var(--shadow-focus)]";
6162
- const minHeight = appearance === "cell" ? "min-h-[29px]" : fieldMinHeight[size];
6534
+ const isCell = appearance === "cell";
6163
6535
  const inputRef = useRef(null);
6536
+ const chipsRef = useRef(null);
6537
+ const hasWrapped = useChipRowWrap(chipsRef, isCell, JSON.stringify(value));
6164
6538
  useEffect(() => {
6165
6539
  if (autoFocus) inputRef.current?.focus();
6166
6540
  }, []);
6167
6541
  const inputId = useId();
6168
6542
  const [draft, setDraft] = useState("");
6543
+ useLayoutEffect(() => {
6544
+ const input = inputRef.current;
6545
+ if (!isCell || !input || document.activeElement !== input) return;
6546
+ if (typeof input.scrollIntoView !== "function") return;
6547
+ input.scrollIntoView({ block: "nearest" });
6548
+ }, [isCell, value]);
6169
6549
  const commit = () => {
6170
6550
  const v = draft.trim();
6171
6551
  if (!v) return;
@@ -6182,7 +6562,10 @@ function TagInput({
6182
6562
  const onKeyDown2 = (e) => {
6183
6563
  if (e.key === "Enter" || e.key === ",") {
6184
6564
  e.preventDefault();
6185
- commit();
6565
+ if (draft.trim()) {
6566
+ e.stopPropagation();
6567
+ commit();
6568
+ }
6186
6569
  return;
6187
6570
  }
6188
6571
  if (e.key === "Backspace" && !draft && value.length > 0) {
@@ -6192,85 +6575,78 @@ function TagInput({
6192
6575
  };
6193
6576
  return /* @__PURE__ */ jsxs("div", { className: cn("grid grid-cols-1 gap-1.5", className), children: [
6194
6577
  label ? /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: fieldLabelStyles, children: label }) : null,
6195
- /* @__PURE__ */ jsxs(
6578
+ /* @__PURE__ */ jsx(
6196
6579
  "div",
6197
6580
  {
6198
6581
  "data-invalid": isInvalid || void 0,
6199
6582
  "data-disabled": isDisabled || void 0,
6200
6583
  className: cn(
6201
6584
  styles4.box(),
6202
- "flex flex-wrap items-center gap-1",
6203
- minHeight,
6204
- fieldChipPadding[size],
6585
+ "flex",
6586
+ // Cell editor: one table row (29px under a 32px row) while the chips
6587
+ // fit on a line; two rows (64px), popping out of the row, once they
6588
+ // wrap — the chips then scroll inside. Field mode grows with its
6589
+ // content.
6590
+ isCell ? hasWrapped ? "h-[64px] p-1" : "h-[29px] px-1 py-0" : [fieldChipPadding[size], fieldMinHeight[size]],
6205
6591
  focusWithin
6206
6592
  ),
6207
- children: [
6208
- value.map((tag, idx) => (
6209
- // tags are kept unique on commit, so the value alone is a stable key
6210
- /* @__PURE__ */ jsxs(
6211
- "span",
6212
- {
6213
- className: cn(
6214
- "inline-flex items-center gap-1",
6215
- "px-1.5 py-0.5 rounded-sm bg-surface-3",
6216
- "font-mono text-[12px] text-ink-2"
6217
- ),
6218
- children: [
6219
- /* @__PURE__ */ jsx("span", { className: "max-w-[10rem] truncate", children: tag }),
6220
- /* @__PURE__ */ jsx(
6221
- "button",
6222
- {
6223
- type: "button",
6224
- "aria-label": `Remove ${tag}`,
6225
- disabled: isDisabled,
6226
- onClick: () => remove(idx),
6227
- className: cn(
6228
- "inline-flex items-center justify-center",
6229
- "size-3.5 rounded-full text-ink-3 hover:text-ink hover:bg-surface-2",
6230
- "cursor-pointer outline-none",
6231
- "focus-visible:shadow-[var(--shadow-focus)]",
6232
- "disabled:opacity-45 disabled:cursor-not-allowed"
6233
- ),
6234
- children: /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 10 10", className: "size-2", fill: "none", children: /* @__PURE__ */ jsx(
6235
- "path",
6236
- {
6237
- d: "M2 2l6 6M8 2l-6 6",
6238
- stroke: "currentColor",
6239
- strokeWidth: "1.4",
6240
- strokeLinecap: "round"
6241
- }
6242
- ) })
6243
- }
6593
+ children: /* @__PURE__ */ jsxs(
6594
+ "div",
6595
+ {
6596
+ ref: chipsRef,
6597
+ className: cn(
6598
+ "flex flex-1 flex-wrap items-center gap-1.5 min-w-0",
6599
+ isCell && (hasWrapped ? "content-start overflow-y-auto" : "content-center overflow-hidden")
6600
+ ),
6601
+ children: [
6602
+ value.map((tag, idx) => (
6603
+ // tags are kept unique on commit, so the value alone is a stable key
6604
+ /* @__PURE__ */ jsx(
6605
+ Badge,
6606
+ {
6607
+ variant: "active",
6608
+ shape: "rounded",
6609
+ hasDot: false,
6610
+ className: "shrink-0",
6611
+ actions: /* @__PURE__ */ jsx(
6612
+ BadgeAction,
6613
+ {
6614
+ label: `Remove ${tag}`,
6615
+ onPress: () => remove(idx),
6616
+ isDisabled
6617
+ }
6618
+ ),
6619
+ children: /* @__PURE__ */ jsx("span", { className: "max-w-[10rem] truncate", children: tag })
6620
+ },
6621
+ tag
6622
+ )
6623
+ )),
6624
+ /* @__PURE__ */ jsx(
6625
+ "input",
6626
+ {
6627
+ ref: inputRef,
6628
+ id: inputId,
6629
+ type: inputType,
6630
+ value: draft,
6631
+ onChange: (e) => setDraft(e.target.value),
6632
+ onKeyDown: onKeyDown2,
6633
+ onBlur: commit,
6634
+ disabled: isDisabled,
6635
+ placeholder: value.length === 0 ? placeholder : "",
6636
+ className: cn(
6637
+ "flex-1 min-w-[6rem] bg-transparent outline-none",
6638
+ "font-sans text-ink placeholder:text-ink-4",
6639
+ fieldTextStyles[size],
6640
+ // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
6641
+ // overall height so it matches Input/Select at the same size.
6642
+ "px-1.5",
6643
+ "disabled:cursor-not-allowed disabled:opacity-45"
6244
6644
  )
6245
- ]
6246
- },
6247
- tag
6248
- )
6249
- )),
6250
- /* @__PURE__ */ jsx(
6251
- "input",
6252
- {
6253
- ref: inputRef,
6254
- id: inputId,
6255
- type: inputType,
6256
- value: draft,
6257
- onChange: (e) => setDraft(e.target.value),
6258
- onKeyDown: onKeyDown2,
6259
- onBlur: commit,
6260
- disabled: isDisabled,
6261
- placeholder: value.length === 0 ? placeholder : "",
6262
- className: cn(
6263
- "flex-1 min-w-[6rem] bg-transparent outline-none",
6264
- "font-sans text-ink placeholder:text-ink-4",
6265
- fieldTextStyles[size],
6266
- // No vertical padding — wrapper's p-1.5 + min-h-[size] govern the
6267
- // overall height so it matches Input/Select at the same size.
6268
- "px-1.5",
6269
- "disabled:cursor-not-allowed disabled:opacity-45"
6645
+ }
6270
6646
  )
6271
- }
6272
- )
6273
- ]
6647
+ ]
6648
+ }
6649
+ )
6274
6650
  }
6275
6651
  ),
6276
6652
  description ? /* @__PURE__ */ jsx("p", { className: fieldHintStyles, children: description }) : null,
@@ -6315,7 +6691,7 @@ function FilterValueInput({
6315
6691
  }
6316
6692
  const leaf = (() => {
6317
6693
  if (arity === "n") {
6318
- if (field.type === "enum") {
6694
+ if (isEnumLike(field)) {
6319
6695
  return /* @__PURE__ */ jsx(
6320
6696
  EnumMultiValue,
6321
6697
  {
@@ -6342,7 +6718,7 @@ function FilterValueInput({
6342
6718
  }
6343
6719
  );
6344
6720
  }
6345
- if (field.type === "enum") {
6721
+ if (isEnumLike(field)) {
6346
6722
  return /* @__PURE__ */ jsx(
6347
6723
  EnumSingleValue,
6348
6724
  {
@@ -6503,7 +6879,9 @@ function placeholderFor(field, operator) {
6503
6879
  if (field.type === "number" || field.type === "integer" || field.type === "currency") return "0";
6504
6880
  if (operator === "starts_with") return "starts with\u2026";
6505
6881
  if (operator === "ends_with") return "ends with\u2026";
6506
- if (operator === "contains") return "contains\u2026";
6882
+ if (operator === "contains") return field.type === "array" ? "has\u2026" : "contains\u2026";
6883
+ if (operator === "not_contains")
6884
+ return field.type === "array" ? "does not have\u2026" : "does not contain\u2026";
6507
6885
  return "value";
6508
6886
  }
6509
6887
  function OperatorPicker({
@@ -8170,32 +8548,7 @@ function ToastRegion({ queue, className }) {
8170
8548
  }
8171
8549
  );
8172
8550
  }
8173
- function TooltipTrigger({
8174
- delay = 500,
8175
- closeDelay = 0,
8176
- ...props
8177
- }) {
8178
- return /* @__PURE__ */ jsx(TooltipTrigger$1, { delay, closeDelay, ...props });
8179
- }
8180
- function Tooltip({ className, offset = 6, ...props }) {
8181
- return /* @__PURE__ */ jsx(
8182
- Tooltip$1,
8183
- {
8184
- ...props,
8185
- offset,
8186
- className: (renderProps) => cn(
8187
- "z-50 max-w-xs",
8188
- "rounded-md border border-rule bg-surface shadow-[var(--shadow-raised)]",
8189
- "px-2.5 py-1.5",
8190
- "font-mono text-[11.5px] text-ink-2",
8191
- "entering:animate-in entering:fade-in-0 entering:slide-in-from-top-1 entering:duration-150",
8192
- "exiting:animate-out exiting:fade-out-0 exiting:duration-100",
8193
- typeof className === "function" ? className(renderProps) : className
8194
- )
8195
- }
8196
- );
8197
- }
8198
8551
 
8199
- export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, CONVERSATION_BRIEF_OUTCOME_LABEL, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, ChannelIcon, Checkbox, CheckboxGroup, ChipValue, ColorInput, Combobox, Command, CommandHint, ConversationBriefCard, ConversationBriefPreview, ConversationBriefReview, CurrencyInput, DRAFT_OUTCOME_LABEL, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageCard, DraftMessageComposer, DraftMessageReview, EmptyState, ErrorAnimation, FIELD_LABEL_SEPARATOR, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, IconPicker, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, MAX_COMPOSER_ATTACHMENT_BYTES, Menu, MenuItem, MenuPopover, MenuTrigger, MessageComposer, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, Orb, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecipientSelect, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SELECTION_COLUMN_ID, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, ToastRegion, Tooltip, TooltipTrigger, TruncateCell, __resetDraftMessageActions, __resetDraftMessageOutcomes, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeStyles, cardStyles2 as cardStyles, channelIcon, contactPhoneNumbers, conversationBriefCardStyles, conversationBriefOutcomeFor, countLeaves, createConversationBriefClient, createDraftMessageClient, createToastQueue, defaultValueFor, draftMessageCardStyles, draftMessageComposerStyles, draftOutcomeFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isDialableConversationBrief, isDraftReviewOutcome, isEditableTarget, isPreviewableImageName, joinFieldLabel, makeActionsColumn, messageComposerStyles, namesOf, navItemStyles, normalizeHexColor, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, resolveLucideIcon, resolveLucideIconName, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, summaryFor, tableCellStyles, tableHeaderCellStyles, tabsStyles, terminalKindFor, toastStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
8552
+ export { ATTRIBUTE_TYPES, ATTRIBUTE_TYPE_ICONS, AttachmentChip, AttributePicker, AttributeType, AttributeTypeIcon, Avatar, AvatarGroup, Badge, BadgeAction, BadgeGroup, BadgeMultiSelect, BooleanCell, Breadcrumbs, COLUMN_DRAG_KIND, CONVERSATION_BRIEF_OUTCOME_LABEL, Card, CardBody, CardContent, CardEyebrow, CardFeatureTitle, CardFooter, CardHeader, CardTitle, ChannelIcon, Checkbox, CheckboxGroup, ChipValue, ColorInput, Combobox, Command, CommandHint, ConversationBriefCard, ConversationBriefPreview, ConversationBriefReview, CurrencyInput, DRAFT_OUTCOME_LABEL, DataTable, DataTableEmpty, DataTableError, DataTableSkeleton, DateCell, DatePicker, DateTimeCell, DateTimePicker, DraftMessageCard, DraftMessageComposer, DraftMessageReview, EmptyState, ErrorAnimation, FIELD_LABEL_SEPARATOR, FieldCaret, FileField, FileViewer, FileViewerFallbackCard, FilterRow, FilterValueInput, IconPicker, ImageAttachment, ImageLightbox, Input, Kbd, LinkCell, MAX_COMPOSER_ATTACHMENT_BYTES, Menu, MenuItem, MenuPopover, MenuTrigger, MessageComposer, Modal, ModalBody, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, MultiSelect, NavGroup, NavItem, NavSection, OPERATORS, OperatorPicker, Orb, PROTECTED_COMBOS, Popover, PopoverContent, PopoverPanelBody, PopoverPanelFooter, PopoverPanelHeader, PressableCard, ProgressBar, Radio, RadioGroup, RecipientSelect, RecordFilterBuilderPanel, RecordFilterButton, RecordFilterChips, RecordFilterEditor, RenderChipItemProvider, RenderValueInputProvider, SELECTION_COLUMN_ID, SegmentedControl, Select, Separator, SubmenuTrigger, SuccessAnimation, Switch, Table, TableBody, TableCell, TableHeader, TableHeaderCell, TableHeaderSpacer, TableRow, TableScroller, Tabs, TagInput, Textarea, TimeField, ToastRegion, Tooltip, TooltipTrigger, TruncateCell, __resetDraftMessageActions, __resetDraftMessageOutcomes, addLeaf as addFilterLeaf, addPendingLeaf, addSubgroup, arityOf, attachmentChipStyles, avatarGroupStyles, avatarStyles, badgeGroupOverflowStyles, badgeGroupStyles, badgeStyles, cardStyles2 as cardStyles, channelIcon, contactPhoneNumbers, conversationBriefCardStyles, conversationBriefOutcomeFor, countLeaves, createConversationBriefClient, createDraftMessageClient, createToastQueue, defaultValueFor, draftMessageCardStyles, draftMessageComposerStyles, draftOutcomeFor, emptyStateStyles, fieldControlStyles, fieldErrorStyles, fieldHintStyles, fieldLabelStyles, fileViewerFallbackCardStyles, fileViewerStyles, fitBadgeCount, formatFileSize, formatValue, iconForContentType, imageAttachmentStyles, imageLightboxStyles, isColumnDragData, isDialableConversationBrief, isDraftReviewOutcome, isEditableTarget, isPreviewableImageName, joinFieldLabel, makeActionsColumn, messageComposerStyles, namesOf, navItemStyles, normalizeHexColor, operatorById, operatorsForType, parseCombo, progressBarStyles, publishDraftMessageOutcome, rebaseRowOnFieldChange, rebaseRowOnOperatorChange, registerDraftMessageActions, removeLeaf, removeSubgroup, replaceLeaf, resolveLucideIcon, resolveLucideIconName, segmentedControlStyles, setJoiner, subscribeDraftMessageOutcome, summaryFor, tableCellStyles, tableHeaderCellStyles, tabsStyles, terminalKindFor, toastStyles, updateLeaf, updateSubgroup, useAttributeTypeIcon, useCommand, useDiscoveryActive, useDraftMessageActions, useDraftMessageOutcome, useEscapeKey, useInfiniteScrollSentinel, useRenderChipItem, useRenderValueInput, useShowDetails, viewerKindForContentType, wrapLeafInGroup };
8200
8553
  //# sourceMappingURL=index.js.map
8201
8554
  //# sourceMappingURL=index.js.map