@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.
- package/dist/index.cjs +679 -321
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -9
- package/dist/index.d.ts +84 -9
- package/dist/index.js +677 -324
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/badge/badge.stories.tsx +37 -1
- package/src/components/badge/badge.test.tsx +39 -2
- package/src/components/badge/badge.tsx +65 -1
- package/src/components/badge/index.ts +2 -2
- package/src/components/badge-group/badge-group.stories.tsx +75 -0
- package/src/components/badge-group/badge-group.test.tsx +121 -0
- package/src/components/badge-group/badge-group.tsx +208 -0
- package/src/components/badge-group/index.ts +7 -0
- package/src/components/badge-multi-select/badge-multi-select.stories.tsx +25 -0
- package/src/components/badge-multi-select/badge-multi-select.test.tsx +110 -1
- package/src/components/badge-multi-select/badge-multi-select.tsx +190 -128
- package/src/components/data-table/data-table.stories.tsx +53 -2
- package/src/components/data-table/data-table.tsx +45 -25
- package/src/components/field/listbox-check.tsx +44 -0
- package/src/components/field/use-chip-row-wrap.ts +45 -0
- package/src/components/multi-select/multi-select.tsx +8 -39
- package/src/components/record-filter/chip-value.tsx +6 -5
- package/src/components/record-filter/filter-value-input.tsx +11 -9
- package/src/components/record-filter/operators.test.ts +67 -12
- package/src/components/record-filter/operators.ts +43 -2
- package/src/components/record-filter/types.ts +6 -1
- package/src/components/table/table.test.tsx +88 -9
- package/src/components/table/table.tsx +139 -13
- package/src/components/tag-input/tag-input.test.tsx +67 -1
- package/src/components/tag-input/tag-input.tsx +98 -64
- 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,
|
|
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,
|
|
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 =
|
|
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
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
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
|
-
|
|
975
|
-
|
|
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
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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 &&
|
|
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
|
-
|
|
1005
|
-
|
|
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__ */
|
|
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-
|
|
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:
|
|
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
|
-
|
|
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(
|
|
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: [
|
|
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
|
-
|
|
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(
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
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
|
-
|
|
3768
|
+
TableHeaderCell,
|
|
3437
3769
|
{
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
...
|
|
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
|
-
|
|
3787
|
+
header.id
|
|
3447
3788
|
);
|
|
3448
|
-
})
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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__ */
|
|
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
|
|
6203
|
-
|
|
6204
|
-
|
|
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
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
"
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
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
|
|
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
|
|
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
|