@handled-ai/design-system 0.18.11 → 0.18.12
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/components/account-contacts-popover.d.ts +5 -1
- package/dist/components/account-contacts-popover.js +25 -4
- package/dist/components/account-contacts-popover.js.map +1 -1
- package/dist/components/data-table-condition-filter.d.ts +2 -1
- package/dist/components/data-table-condition-filter.js +23 -41
- package/dist/components/data-table-condition-filter.js.map +1 -1
- package/dist/components/data-table-filter.js +8 -9
- package/dist/components/data-table-filter.js.map +1 -1
- package/dist/components/entity-panel.d.ts +2 -1
- package/dist/components/entity-panel.js +52 -45
- package/dist/components/entity-panel.js.map +1 -1
- package/dist/components/score-why-chips.d.ts +1 -1
- package/dist/components/signal-priority-popover.d.ts +1 -1
- package/dist/components/signal-priority-popover.js +4 -4
- package/dist/components/signal-priority-popover.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/prototype/index.d.ts +1 -1
- package/dist/prototype/prototype-accounts-view.d.ts +1 -1
- package/dist/prototype/prototype-admin-view.d.ts +1 -1
- package/dist/prototype/prototype-config.d.ts +1 -1
- package/dist/prototype/prototype-inbox-view.d.ts +5 -3
- package/dist/prototype/prototype-inbox-view.js +11 -5
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/dist/prototype/prototype-insights-view.d.ts +1 -1
- package/dist/prototype/prototype-shell.d.ts +1 -1
- package/dist/{signal-priority-popover-BT6CPYNs.d.ts → signal-priority-popover-BEDoPsNE.d.ts} +6 -0
- package/package.json +1 -2
- package/src/components/__tests__/account-contacts-popover.test.tsx +79 -0
- package/src/components/__tests__/data-table-condition-filter.test.tsx +96 -0
- package/src/components/__tests__/data-table-filter.test.tsx +45 -0
- package/src/components/__tests__/entity-panel-header.test.tsx +44 -0
- package/src/components/__tests__/signal-priority-popover.test.tsx +30 -0
- package/src/components/account-contacts-popover.tsx +29 -1
- package/src/components/data-table-condition-filter.tsx +32 -47
- package/src/components/data-table-filter.tsx +7 -10
- package/src/components/entity-panel.tsx +56 -40
- package/src/components/signal-priority-popover.tsx +15 -4
- package/src/prototype/__tests__/detail-view-title-slots.test.tsx +15 -0
- package/src/prototype/prototype-config.ts +2 -0
- package/src/prototype/prototype-inbox-view.tsx +17 -5
|
@@ -12,11 +12,15 @@ interface AccountContactsPopoverProps {
|
|
|
12
12
|
onSelectTo?: (contact: SuggestedContact) => void;
|
|
13
13
|
onSelectCc?: (contact: SuggestedContact) => void;
|
|
14
14
|
onSelectBcc?: (contact: SuggestedContact) => void;
|
|
15
|
+
/** Label for the default contact row action. Defaults to "Add" or "Switch" when onSelectSwitch is provided. */
|
|
16
|
+
defaultSelectLabel?: string;
|
|
17
|
+
/** Optional replacement-selection callback. When provided, row clicks call this instead of additive onSelect/onSelectTo. */
|
|
18
|
+
onSelectSwitch?: (contact: SuggestedContact) => void;
|
|
15
19
|
onViewAll?: () => void;
|
|
16
20
|
onOpenRecentActivity?: () => void;
|
|
17
21
|
trigger: React.ReactNode;
|
|
18
22
|
iconMap?: SuggestedActionsIconMap;
|
|
19
23
|
}
|
|
20
|
-
declare function AccountContactsPopover({ contacts, onSelect, onSelectTo, onSelectCc, onSelectBcc, onViewAll, onOpenRecentActivity, trigger, iconMap, }: AccountContactsPopoverProps): React.JSX.Element;
|
|
24
|
+
declare function AccountContactsPopover({ contacts, onSelect, onSelectTo, onSelectCc, onSelectBcc, defaultSelectLabel, onSelectSwitch, onViewAll, onOpenRecentActivity, trigger, iconMap, }: AccountContactsPopoverProps): React.JSX.Element;
|
|
21
25
|
|
|
22
26
|
export { AccountContactsPopover, type AccountContactsPopoverProps, BrandIcon };
|
|
@@ -24,6 +24,8 @@ function AccountContactsPopover({
|
|
|
24
24
|
onSelectTo,
|
|
25
25
|
onSelectCc,
|
|
26
26
|
onSelectBcc,
|
|
27
|
+
defaultSelectLabel,
|
|
28
|
+
onSelectSwitch,
|
|
27
29
|
onViewAll,
|
|
28
30
|
onOpenRecentActivity,
|
|
29
31
|
trigger,
|
|
@@ -32,6 +34,15 @@ function AccountContactsPopover({
|
|
|
32
34
|
const [open, setOpen] = React.useState(false);
|
|
33
35
|
const triggerRef = React.useRef(null);
|
|
34
36
|
const [popoverStyle, setPopoverStyle] = React.useState({});
|
|
37
|
+
const resolvedDefaultSelectLabel = defaultSelectLabel != null ? defaultSelectLabel : onSelectSwitch ? "Switch" : void 0;
|
|
38
|
+
const handleDefaultSelect = React.useCallback((contact) => {
|
|
39
|
+
if (onSelectSwitch) {
|
|
40
|
+
onSelectSwitch(contact);
|
|
41
|
+
} else {
|
|
42
|
+
(onSelectTo != null ? onSelectTo : onSelect)(contact);
|
|
43
|
+
}
|
|
44
|
+
setOpen(false);
|
|
45
|
+
}, [onSelect, onSelectSwitch, onSelectTo]);
|
|
35
46
|
React.useEffect(() => {
|
|
36
47
|
if (open && triggerRef.current) {
|
|
37
48
|
const rect = triggerRef.current.getBoundingClientRect();
|
|
@@ -59,10 +70,8 @@ function AccountContactsPopover({
|
|
|
59
70
|
"div",
|
|
60
71
|
{
|
|
61
72
|
role: "button",
|
|
62
|
-
onClick: () =>
|
|
63
|
-
|
|
64
|
-
setOpen(false);
|
|
65
|
-
},
|
|
73
|
+
onClick: () => handleDefaultSelect(c),
|
|
74
|
+
"aria-label": resolvedDefaultSelectLabel ? `${resolvedDefaultSelectLabel} ${c.name}` : void 0,
|
|
66
75
|
className: "flex items-center gap-3 w-full px-3 py-2 text-left hover:bg-muted/50 transition-colors cursor-pointer",
|
|
67
76
|
children: [
|
|
68
77
|
/* @__PURE__ */ jsx("div", { className: "w-7 h-7 rounded-full bg-muted flex items-center justify-center text-[10px] font-medium text-muted-foreground shrink-0", children: c.name.split(" ").map((n) => n[0]).join("") }),
|
|
@@ -95,6 +104,18 @@ function AccountContactsPopover({
|
|
|
95
104
|
)
|
|
96
105
|
] }),
|
|
97
106
|
/* @__PURE__ */ jsxs("div", { className: "ml-2 flex items-center gap-1.5 shrink-0", children: [
|
|
107
|
+
resolvedDefaultSelectLabel && /* @__PURE__ */ jsx(
|
|
108
|
+
"button",
|
|
109
|
+
{
|
|
110
|
+
type: "button",
|
|
111
|
+
onClick: (e) => {
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
handleDefaultSelect(c);
|
|
114
|
+
},
|
|
115
|
+
className: "h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40",
|
|
116
|
+
children: resolvedDefaultSelectLabel
|
|
117
|
+
}
|
|
118
|
+
),
|
|
98
119
|
onSelectTo && /* @__PURE__ */ jsx(
|
|
99
120
|
"button",
|
|
100
121
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/account-contacts-popover.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n Clock,\n ExternalLink,\n} from \"lucide-react\"\nimport type { SuggestedContact, SuggestedActionsIconMap } from \"./suggested-actions\"\n\n// ---------------------------------------------------------------------------\n// BrandIcon\n// ---------------------------------------------------------------------------\n\nexport function BrandIcon({ src, alt, className }: { src: string; alt: string; className?: string }) {\n return (\n <img\n src={src}\n alt={alt}\n className={`${className ?? \"\"} object-contain`}\n draggable={false}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// AccountContactsPopover\n// ---------------------------------------------------------------------------\n\nexport interface AccountContactsPopoverProps {\n contacts: SuggestedContact[]\n onSelect: (contact: SuggestedContact) => void\n onSelectTo?: (contact: SuggestedContact) => void\n onSelectCc?: (contact: SuggestedContact) => void\n onSelectBcc?: (contact: SuggestedContact) => void\n onViewAll?: () => void\n onOpenRecentActivity?: () => void\n trigger: React.ReactNode\n iconMap?: SuggestedActionsIconMap\n}\n\nexport function AccountContactsPopover({\n contacts,\n onSelect,\n onSelectTo,\n onSelectCc,\n onSelectBcc,\n onViewAll,\n onOpenRecentActivity,\n trigger,\n iconMap,\n}: AccountContactsPopoverProps) {\n const [open, setOpen] = React.useState(false)\n const triggerRef = React.useRef<HTMLDivElement>(null)\n const [popoverStyle, setPopoverStyle] = React.useState<React.CSSProperties>({})\n\n React.useEffect(() => {\n if (open && triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect()\n const popoverWidth = Math.min(448, window.innerWidth - 32)\n let left = rect.right - popoverWidth\n if (left < 16) left = 16\n if (left + popoverWidth > window.innerWidth - 16) left = window.innerWidth - 16 - popoverWidth\n const popoverHeight = 320;\n const spaceBelow = window.innerHeight - rect.bottom - 8;\n const spaceAbove = rect.top - 8;\n const placeAbove = spaceBelow < popoverHeight && spaceAbove > spaceBelow;\n const top = placeAbove ? rect.top - popoverHeight - 4 : rect.bottom + 4;\n setPopoverStyle({ position: \"fixed\", top: Math.max(8, top), left, maxHeight: placeAbove ? spaceAbove : spaceBelow })\n }\n }, [open])\n\n return (\n <div>\n <div ref={triggerRef} onClick={() => setOpen(!open)}>{trigger}</div>\n {open && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setOpen(false)} />\n <div style={popoverStyle} className=\"fixed bg-background border border-border rounded-lg shadow-xl z-50 w-[28rem] max-w-[calc(100vw-2rem)] py-2 animate-in fade-in slide-in-from-top-1 duration-150 overflow-y-auto\">\n <div className=\"px-3 py-1.5 text-[11px] font-medium text-muted-foreground/60 uppercase tracking-wide\">\n Account Contacts\n </div>\n <div className=\"max-h-48 overflow-y-auto\">\n {contacts.map((c, i) => (\n <div\n key={i}\n role=\"button\"\n onClick={() => { (onSelectTo ?? onSelect)(c); setOpen(false) }}\n className=\"flex items-center gap-3 w-full px-3 py-2 text-left hover:bg-muted/50 transition-colors cursor-pointer\"\n >\n <div className=\"w-7 h-7 rounded-full bg-muted flex items-center justify-center text-[10px] font-medium text-muted-foreground shrink-0\">\n {c.name.split(\" \").map((n) => n[0]).join(\"\")}\n </div>\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <div className=\"truncate text-sm font-medium text-foreground\">{c.name}</div>\n <div className=\"truncate text-xs text-muted-foreground leading-tight\">\n {c.role} · {c.email ?? c.emails?.[0] ?? c.phone ?? c.phones?.[0] ?? \"\"}\n </div>\n {c.lastActivity && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onOpenRecentActivity?.()\n setOpen(false)\n }}\n className=\"mt-1.5 flex max-w-full items-center gap-1.5 overflow-hidden rounded-md border border-border/70 bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <Clock className=\"w-3 h-3 shrink-0\" />\n <span className=\"shrink-0 font-medium\">Last activity</span>\n <span className=\"shrink-0 text-muted-foreground/60\">·</span>\n <span className=\"shrink-0\">{c.lastActivity.date}</span>\n <span className=\"shrink-0 text-muted-foreground/60\">·</span>\n <span className=\"truncate capitalize\">{c.lastActivity.type}</span>\n </button>\n )}\n </div>\n <div className=\"ml-2 flex items-center gap-1.5 shrink-0\">\n {onSelectTo && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectTo(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n To\n </button>\n )}\n {onSelectCc && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectCc(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n Cc\n </button>\n )}\n {onSelectBcc && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectBcc(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n Bcc\n </button>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n if (c.salesforceUrl) {\n window.open(c.salesforceUrl, \"_blank\", \"noopener,noreferrer\")\n } else {\n onViewAll?.()\n }\n }}\n className=\"h-7 w-7 inline-flex items-center justify-center rounded-md border border-border bg-background hover:bg-muted/40 transition-colors shrink-0\"\n aria-label={`Open ${c.name} in Salesforce`}\n >\n {iconMap?.salesforce ? (\n <BrandIcon src={iconMap.salesforce} alt=\"Salesforce\" className=\"w-3.5 h-3.5\" />\n ) : (\n <ExternalLink className=\"w-3.5 h-3.5 text-muted-foreground\" />\n )}\n </button>\n </div>\n </div>\n ))}\n </div>\n {onViewAll && (\n <>\n <div className=\"h-px bg-border mx-3 my-1\" />\n <button\n onClick={() => { onViewAll(); setOpen(false) }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-left text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ExternalLink className=\"w-3 h-3\" />\n View all contacts\n </button>\n </>\n )}\n </div>\n </>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/account-contacts-popover.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n Clock,\n ExternalLink,\n} from \"lucide-react\"\nimport type { SuggestedContact, SuggestedActionsIconMap } from \"./suggested-actions\"\n\n// ---------------------------------------------------------------------------\n// BrandIcon\n// ---------------------------------------------------------------------------\n\nexport function BrandIcon({ src, alt, className }: { src: string; alt: string; className?: string }) {\n return (\n <img\n src={src}\n alt={alt}\n className={`${className ?? \"\"} object-contain`}\n draggable={false}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// AccountContactsPopover\n// ---------------------------------------------------------------------------\n\nexport interface AccountContactsPopoverProps {\n contacts: SuggestedContact[]\n onSelect: (contact: SuggestedContact) => void\n onSelectTo?: (contact: SuggestedContact) => void\n onSelectCc?: (contact: SuggestedContact) => void\n onSelectBcc?: (contact: SuggestedContact) => void\n /** Label for the default contact row action. Defaults to \"Add\" or \"Switch\" when onSelectSwitch is provided. */\n defaultSelectLabel?: string\n /** Optional replacement-selection callback. When provided, row clicks call this instead of additive onSelect/onSelectTo. */\n onSelectSwitch?: (contact: SuggestedContact) => void\n onViewAll?: () => void\n onOpenRecentActivity?: () => void\n trigger: React.ReactNode\n iconMap?: SuggestedActionsIconMap\n}\n\nexport function AccountContactsPopover({\n contacts,\n onSelect,\n onSelectTo,\n onSelectCc,\n onSelectBcc,\n defaultSelectLabel,\n onSelectSwitch,\n onViewAll,\n onOpenRecentActivity,\n trigger,\n iconMap,\n}: AccountContactsPopoverProps) {\n const [open, setOpen] = React.useState(false)\n const triggerRef = React.useRef<HTMLDivElement>(null)\n const [popoverStyle, setPopoverStyle] = React.useState<React.CSSProperties>({})\n const resolvedDefaultSelectLabel = defaultSelectLabel ?? (onSelectSwitch ? \"Switch\" : undefined)\n const handleDefaultSelect = React.useCallback((contact: SuggestedContact) => {\n if (onSelectSwitch) {\n onSelectSwitch(contact)\n } else {\n (onSelectTo ?? onSelect)(contact)\n }\n setOpen(false)\n }, [onSelect, onSelectSwitch, onSelectTo])\n\n React.useEffect(() => {\n if (open && triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect()\n const popoverWidth = Math.min(448, window.innerWidth - 32)\n let left = rect.right - popoverWidth\n if (left < 16) left = 16\n if (left + popoverWidth > window.innerWidth - 16) left = window.innerWidth - 16 - popoverWidth\n const popoverHeight = 320;\n const spaceBelow = window.innerHeight - rect.bottom - 8;\n const spaceAbove = rect.top - 8;\n const placeAbove = spaceBelow < popoverHeight && spaceAbove > spaceBelow;\n const top = placeAbove ? rect.top - popoverHeight - 4 : rect.bottom + 4;\n setPopoverStyle({ position: \"fixed\", top: Math.max(8, top), left, maxHeight: placeAbove ? spaceAbove : spaceBelow })\n }\n }, [open])\n\n return (\n <div>\n <div ref={triggerRef} onClick={() => setOpen(!open)}>{trigger}</div>\n {open && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setOpen(false)} />\n <div style={popoverStyle} className=\"fixed bg-background border border-border rounded-lg shadow-xl z-50 w-[28rem] max-w-[calc(100vw-2rem)] py-2 animate-in fade-in slide-in-from-top-1 duration-150 overflow-y-auto\">\n <div className=\"px-3 py-1.5 text-[11px] font-medium text-muted-foreground/60 uppercase tracking-wide\">\n Account Contacts\n </div>\n <div className=\"max-h-48 overflow-y-auto\">\n {contacts.map((c, i) => (\n <div\n key={i}\n role=\"button\"\n onClick={() => handleDefaultSelect(c)}\n aria-label={resolvedDefaultSelectLabel ? `${resolvedDefaultSelectLabel} ${c.name}` : undefined}\n className=\"flex items-center gap-3 w-full px-3 py-2 text-left hover:bg-muted/50 transition-colors cursor-pointer\"\n >\n <div className=\"w-7 h-7 rounded-full bg-muted flex items-center justify-center text-[10px] font-medium text-muted-foreground shrink-0\">\n {c.name.split(\" \").map((n) => n[0]).join(\"\")}\n </div>\n <div className=\"flex-1 min-w-0 overflow-hidden\">\n <div className=\"truncate text-sm font-medium text-foreground\">{c.name}</div>\n <div className=\"truncate text-xs text-muted-foreground leading-tight\">\n {c.role} · {c.email ?? c.emails?.[0] ?? c.phone ?? c.phones?.[0] ?? \"\"}\n </div>\n {c.lastActivity && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onOpenRecentActivity?.()\n setOpen(false)\n }}\n className=\"mt-1.5 flex max-w-full items-center gap-1.5 overflow-hidden rounded-md border border-border/70 bg-muted/30 px-2 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <Clock className=\"w-3 h-3 shrink-0\" />\n <span className=\"shrink-0 font-medium\">Last activity</span>\n <span className=\"shrink-0 text-muted-foreground/60\">·</span>\n <span className=\"shrink-0\">{c.lastActivity.date}</span>\n <span className=\"shrink-0 text-muted-foreground/60\">·</span>\n <span className=\"truncate capitalize\">{c.lastActivity.type}</span>\n </button>\n )}\n </div>\n <div className=\"ml-2 flex items-center gap-1.5 shrink-0\">\n {resolvedDefaultSelectLabel && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n handleDefaultSelect(c)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n {resolvedDefaultSelectLabel}\n </button>\n )}\n {onSelectTo && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectTo(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n To\n </button>\n )}\n {onSelectCc && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectCc(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n Cc\n </button>\n )}\n {onSelectBcc && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n onSelectBcc(c)\n setOpen(false)\n }}\n className=\"h-6 rounded border border-border bg-background px-1.5 text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted/40\"\n >\n Bcc\n </button>\n )}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n if (c.salesforceUrl) {\n window.open(c.salesforceUrl, \"_blank\", \"noopener,noreferrer\")\n } else {\n onViewAll?.()\n }\n }}\n className=\"h-7 w-7 inline-flex items-center justify-center rounded-md border border-border bg-background hover:bg-muted/40 transition-colors shrink-0\"\n aria-label={`Open ${c.name} in Salesforce`}\n >\n {iconMap?.salesforce ? (\n <BrandIcon src={iconMap.salesforce} alt=\"Salesforce\" className=\"w-3.5 h-3.5\" />\n ) : (\n <ExternalLink className=\"w-3.5 h-3.5 text-muted-foreground\" />\n )}\n </button>\n </div>\n </div>\n ))}\n </div>\n {onViewAll && (\n <>\n <div className=\"h-px bg-border mx-3 my-1\" />\n <button\n onClick={() => { onViewAll(); setOpen(false) }}\n className=\"flex items-center gap-2 w-full px-3 py-2 text-left text-xs text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ExternalLink className=\"w-3 h-3\" />\n View all contacts\n </button>\n </>\n )}\n </div>\n </>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -39,10 +39,11 @@ interface DataTableConditionFilterProps {
|
|
|
39
39
|
className?: string;
|
|
40
40
|
}
|
|
41
41
|
declare const OPERATOR_LABELS: Record<ConditionOperator, string>;
|
|
42
|
+
declare function shouldShowOptionSearch(searchable: ConditionFieldDef["searchable"], optionCount: number, defaultThreshold?: number): boolean;
|
|
42
43
|
declare const DEFAULT_OPERATORS: Record<ConditionFieldDef["type"], ConditionOperator[]>;
|
|
43
44
|
/** Generate a stable unique ID for a new condition row. */
|
|
44
45
|
declare function generateConditionId(): string;
|
|
45
46
|
declare function getOperators(field: ConditionFieldDef): ConditionOperator[];
|
|
46
47
|
declare function DataTableConditionFilter({ fields, conditions, onConditionsChange, className, }: DataTableConditionFilterProps): React.JSX.Element;
|
|
47
48
|
|
|
48
|
-
export { type ConditionFieldDef, type ConditionFieldOption, type ConditionFilterValue, type ConditionOperator, type ConditionOptionObject, DEFAULT_OPERATORS, DataTableConditionFilter, type DataTableConditionFilterProps, OPERATOR_LABELS, generateConditionId, getOperators };
|
|
49
|
+
export { type ConditionFieldDef, type ConditionFieldOption, type ConditionFilterValue, type ConditionOperator, type ConditionOptionObject, DEFAULT_OPERATORS, DataTableConditionFilter, type DataTableConditionFilterProps, OPERATOR_LABELS, generateConditionId, getOperators, shouldShowOptionSearch };
|
|
@@ -23,15 +23,11 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
|
23
23
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
24
24
|
import * as React from "react";
|
|
25
25
|
import {
|
|
26
|
-
CalendarDays,
|
|
27
26
|
Check,
|
|
28
|
-
DollarSign,
|
|
29
27
|
Eye,
|
|
30
|
-
Hash,
|
|
31
28
|
MoreHorizontal,
|
|
32
29
|
Plus,
|
|
33
|
-
Trash2
|
|
34
|
-
Type
|
|
30
|
+
Trash2
|
|
35
31
|
} from "lucide-react";
|
|
36
32
|
import { cn } from "../lib/utils.js";
|
|
37
33
|
import { Button } from "./button.js";
|
|
@@ -64,6 +60,14 @@ const NUMERIC_OPERATORS = [
|
|
|
64
60
|
"is_null",
|
|
65
61
|
"is_not_null"
|
|
66
62
|
];
|
|
63
|
+
const NAV_KEYS = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "Enter", "Escape", "Tab"]);
|
|
64
|
+
function shouldShowOptionSearch(searchable, optionCount, defaultThreshold = 8) {
|
|
65
|
+
var _a;
|
|
66
|
+
if (searchable === true) return true;
|
|
67
|
+
if (searchable === false) return false;
|
|
68
|
+
const threshold = typeof searchable === "object" ? (_a = searchable.threshold) != null ? _a : defaultThreshold : defaultThreshold;
|
|
69
|
+
return optionCount > threshold;
|
|
70
|
+
}
|
|
67
71
|
const DEFAULT_OPERATORS = {
|
|
68
72
|
text: ["eq", "neq", "is_null", "is_not_null"],
|
|
69
73
|
number: NUMERIC_OPERATORS,
|
|
@@ -157,17 +161,6 @@ function getFieldsSignature(fields) {
|
|
|
157
161
|
function getCommittedConditions(drafts, fields) {
|
|
158
162
|
return drafts.map((condition) => normalizeCondition(condition, fields)).filter((condition) => isCompleteCondition(condition, fields));
|
|
159
163
|
}
|
|
160
|
-
const FIELD_ICON_BY_TYPE = {
|
|
161
|
-
text: Type,
|
|
162
|
-
number: Hash,
|
|
163
|
-
currency: DollarSign,
|
|
164
|
-
date: CalendarDays,
|
|
165
|
-
select: MoreHorizontal,
|
|
166
|
-
multi_select: MoreHorizontal
|
|
167
|
-
};
|
|
168
|
-
function getFieldIcon(type) {
|
|
169
|
-
return FIELD_ICON_BY_TYPE[type];
|
|
170
|
-
}
|
|
171
164
|
function getInputType(fieldType) {
|
|
172
165
|
if (fieldType === "number" || fieldType === "currency") return "number";
|
|
173
166
|
if (fieldType === "date") return "date";
|
|
@@ -179,15 +172,6 @@ function getInputPlaceholder(fieldType) {
|
|
|
179
172
|
if (fieldType === "date") return "";
|
|
180
173
|
return "Enter value...";
|
|
181
174
|
}
|
|
182
|
-
function shouldShowOptionSearch(fieldDef, optionCount) {
|
|
183
|
-
var _a;
|
|
184
|
-
if (fieldDef.searchable === true) return true;
|
|
185
|
-
if (fieldDef.searchable === false) return false;
|
|
186
|
-
if (typeof fieldDef.searchable === "object") {
|
|
187
|
-
return optionCount >= ((_a = fieldDef.searchable.threshold) != null ? _a : 8);
|
|
188
|
-
}
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
175
|
function SelectConditionValueInput({
|
|
192
176
|
condition,
|
|
193
177
|
fieldDef,
|
|
@@ -195,9 +179,12 @@ function SelectConditionValueInput({
|
|
|
195
179
|
}) {
|
|
196
180
|
const [query, setQuery] = React.useState("");
|
|
197
181
|
const options = normalizeFieldOptions(fieldDef);
|
|
198
|
-
const
|
|
182
|
+
const showSearch = shouldShowOptionSearch(fieldDef.searchable, options.length);
|
|
183
|
+
const normalizedQuery = showSearch ? query.trim().toLowerCase() : "";
|
|
199
184
|
const filteredOptions = normalizedQuery ? options.filter((option) => option.label.toLowerCase().includes(normalizedQuery)) : options;
|
|
200
|
-
|
|
185
|
+
React.useEffect(() => {
|
|
186
|
+
setQuery("");
|
|
187
|
+
}, [fieldDef.id]);
|
|
201
188
|
return /* @__PURE__ */ jsxs(
|
|
202
189
|
Select,
|
|
203
190
|
{
|
|
@@ -212,7 +199,11 @@ function SelectConditionValueInput({
|
|
|
212
199
|
value: query,
|
|
213
200
|
onChange: (event) => setQuery(event.target.value),
|
|
214
201
|
onClick: (event) => event.stopPropagation(),
|
|
215
|
-
onKeyDown: (event) =>
|
|
202
|
+
onKeyDown: (event) => {
|
|
203
|
+
if (!NAV_KEYS.has(event.key)) {
|
|
204
|
+
event.stopPropagation();
|
|
205
|
+
}
|
|
206
|
+
},
|
|
216
207
|
placeholder: "Search options...",
|
|
217
208
|
className: "h-7 text-xs"
|
|
218
209
|
}
|
|
@@ -313,7 +304,6 @@ function ConditionRow({
|
|
|
313
304
|
const fieldDef = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
|
|
314
305
|
const operators = getOperators(fieldDef);
|
|
315
306
|
const isUnary = isUnaryOperator(condition.operator);
|
|
316
|
-
const FieldIcon = getFieldIcon(fieldDef.type);
|
|
317
307
|
const handleFieldChange = (newFieldId) => {
|
|
318
308
|
var _a2;
|
|
319
309
|
const newFieldDef = (_a2 = fields.find((f) => f.id === newFieldId)) != null ? _a2 : fields[0];
|
|
@@ -355,17 +345,8 @@ function ConditionRow({
|
|
|
355
345
|
children: [
|
|
356
346
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: index === 0 ? "Where" : "And" }),
|
|
357
347
|
/* @__PURE__ */ jsxs(Select, { value: condition.field, onValueChange: handleFieldChange, children: [
|
|
358
|
-
/* @__PURE__ */
|
|
359
|
-
|
|
360
|
-
/* @__PURE__ */ jsx(SelectValue, { placeholder: fieldDef.label })
|
|
361
|
-
] }),
|
|
362
|
-
/* @__PURE__ */ jsx(SelectContent, { children: fields.map((field) => {
|
|
363
|
-
const Icon = getFieldIcon(field.type);
|
|
364
|
-
return /* @__PURE__ */ jsx(SelectItem, { value: field.id, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
|
|
365
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
366
|
-
field.label
|
|
367
|
-
] }) }, field.id);
|
|
368
|
-
}) })
|
|
348
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-full justify-start gap-2", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: fieldDef.label }) }),
|
|
349
|
+
/* @__PURE__ */ jsx(SelectContent, { children: fields.map((field) => /* @__PURE__ */ jsx(SelectItem, { value: field.id, children: field.label }, field.id)) })
|
|
369
350
|
] }),
|
|
370
351
|
/* @__PURE__ */ jsxs(
|
|
371
352
|
Select,
|
|
@@ -584,6 +565,7 @@ export {
|
|
|
584
565
|
DataTableConditionFilter,
|
|
585
566
|
OPERATOR_LABELS,
|
|
586
567
|
generateConditionId,
|
|
587
|
-
getOperators
|
|
568
|
+
getOperators,
|
|
569
|
+
shouldShowOptionSearch
|
|
588
570
|
};
|
|
589
571
|
//# sourceMappingURL=data-table-condition-filter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/data-table-condition-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n CalendarDays,\n Check,\n DollarSign,\n Eye,\n Hash,\n MoreHorizontal,\n Plus,\n Trash2,\n Type,\n type LucideIcon,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Input } from \"./input\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./select\"\n\n// ── Types ──────────────────────────────────────────────────────\n\nexport type ConditionOperator =\n | \"eq\"\n | \"neq\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"in\"\n | \"is_null\"\n | \"is_not_null\"\n\nexport interface ConditionOptionObject {\n label: string\n value: string\n}\n\nexport type ConditionFieldOption = string | ConditionOptionObject\n\nexport interface ConditionFieldDef {\n /** Unique field key (e.g., \"Account_Balance__c\") */\n id: string\n /** Display label (e.g., \"Account Balance\") */\n label: string\n /** Field data type — determines which operators are available and how the value input renders */\n type: \"text\" | \"number\" | \"currency\" | \"date\" | \"select\" | \"multi_select\"\n /** Allowed operators for this field. Defaults based on type if not provided. */\n operators?: ConditionOperator[]\n /** Options used by select and multi-select fields. Strings use the same label and value. */\n options?: ConditionFieldOption[]\n /** Show a search box for option-backed value inputs. */\n searchable?: boolean | { threshold?: number }\n}\n\nexport interface ConditionFilterValue {\n /** Stable identity — used as React key to avoid stale-state bugs on removal */\n id: string\n field: string\n operator: ConditionOperator\n value: string | number | string[] | null\n}\n\ninterface DataTableConditionFilterProps {\n /** Available fields the user can filter on */\n fields: ConditionFieldDef[]\n /** Current active conditions */\n conditions: ConditionFilterValue[]\n /** Called when conditions change (add, update, remove) */\n onConditionsChange: (conditions: ConditionFilterValue[]) => void\n className?: string\n}\n\n// ── Constants ──────────────────────────────────────────────────\n\nconst OPERATOR_LABELS: Record<ConditionOperator, string> = {\n eq: \"=\",\n neq: \"≠\",\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n in: \"is any of\",\n is_null: \"is empty\",\n is_not_null: \"is not empty\",\n}\n\nconst NUMERIC_OPERATORS: ConditionOperator[] = [\n \"eq\",\n \"neq\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"is_null\",\n \"is_not_null\",\n]\n\nconst DEFAULT_OPERATORS: Record<ConditionFieldDef[\"type\"], ConditionOperator[]> = {\n text: [\"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n number: NUMERIC_OPERATORS,\n currency: NUMERIC_OPERATORS,\n date: [\"gt\", \"gte\", \"lt\", \"lte\", \"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n select: [\"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n multi_select: [\"in\", \"is_null\", \"is_not_null\"],\n}\n\n/** Generate a stable unique ID for a new condition row. */\nfunction generateConditionId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ── Helpers ────────────────────────────────────────────────────\n\nfunction getOperators(field: ConditionFieldDef): ConditionOperator[] {\n return field.operators ?? DEFAULT_OPERATORS[field.type]\n}\n\nfunction isUnaryOperator(op: ConditionOperator): boolean {\n return op === \"is_null\" || op === \"is_not_null\"\n}\n\nfunction getDefaultOperator(field: ConditionFieldDef): ConditionOperator {\n return getOperators(field)[0] ?? \"eq\"\n}\n\nfunction createDraftCondition(field: ConditionFieldDef): ConditionFilterValue {\n return {\n id: generateConditionId(),\n field: field.id,\n operator: getDefaultOperator(field),\n value: null,\n }\n}\n\nfunction normalizeConditionValue(\n value: ConditionFilterValue[\"value\"],\n field: ConditionFieldDef,\n operator: ConditionOperator,\n): ConditionFilterValue[\"value\"] {\n if (isUnaryOperator(operator)) return null\n if (field.type === \"multi_select\") {\n return Array.isArray(value) ? value : null\n }\n return Array.isArray(value) ? null : value\n}\n\nfunction normalizeCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): ConditionFilterValue {\n const field = fields.find((f) => f.id === condition.field) ?? fields[0]\n if (!field) return condition\n\n const operators = getOperators(field)\n const operator = operators.includes(condition.operator)\n ? condition.operator\n : getDefaultOperator(field)\n\n return {\n ...condition,\n field: field.id,\n operator,\n value: normalizeConditionValue(condition.value, field, operator),\n }\n}\n\nfunction parseConditionValue(\n raw: string,\n fieldType: ConditionFieldDef[\"type\"],\n): string | number | null {\n if (raw === \"\") return null\n if (fieldType === \"number\" || fieldType === \"currency\") {\n const parsed = Number(raw)\n return Number.isNaN(parsed) ? null : parsed\n }\n return raw\n}\n\nfunction isCompleteCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): boolean {\n const field = fields.find((f) => f.id === condition.field)\n if (!field) return false\n if (!getOperators(field).includes(condition.operator)) return false\n if (isUnaryOperator(condition.operator)) return true\n if (field.type === \"multi_select\") {\n return Array.isArray(condition.value) && condition.value.length > 0\n }\n return condition.value !== null && condition.value !== \"\" && !Array.isArray(condition.value)\n}\n\nfunction getConditionValueSignature(value: ConditionFilterValue[\"value\"]): string {\n return Array.isArray(value) ? JSON.stringify(value) : String(value)\n}\n\nfunction getConditionsSignature(conditions: ConditionFilterValue[]): string {\n return conditions\n .map((condition) => `${condition.id}:${condition.field}:${condition.operator}:${getConditionValueSignature(condition.value)}`)\n .join(\";\")\n}\n\nfunction normalizeFieldOptions(field: ConditionFieldDef): ConditionOptionObject[] {\n return (field.options ?? []).map((option) =>\n typeof option === \"string\" ? { label: option, value: option } : option,\n )\n}\n\nfunction getFieldsSignature(fields: ConditionFieldDef[]): string {\n return fields\n .map((field) => {\n const optionsSignature = normalizeFieldOptions(field)\n .map((option) => `${option.label}:${option.value}`)\n .join(\"|\")\n const searchSignature =\n typeof field.searchable === \"object\"\n ? `threshold:${field.searchable.threshold ?? \"\"}`\n : String(field.searchable ?? \"\")\n return `${field.id}:${field.type}:${field.label}:${(field.operators ?? []).join(\"|\")}:${optionsSignature}:${searchSignature}`\n })\n .join(\";\")\n}\n\nfunction getCommittedConditions(\n drafts: ConditionFilterValue[],\n fields: ConditionFieldDef[],\n): ConditionFilterValue[] {\n return drafts\n .map((condition) => normalizeCondition(condition, fields))\n .filter((condition) => isCompleteCondition(condition, fields))\n}\n\nconst FIELD_ICON_BY_TYPE: Record<ConditionFieldDef[\"type\"], LucideIcon> = {\n text: Type,\n number: Hash,\n currency: DollarSign,\n date: CalendarDays,\n select: MoreHorizontal,\n multi_select: MoreHorizontal,\n}\n\nfunction getFieldIcon(type: ConditionFieldDef[\"type\"]): LucideIcon {\n return FIELD_ICON_BY_TYPE[type]\n}\n\n// ── Condition Row ──────────────────────────────────────────────\n\nfunction getInputType(fieldType: ConditionFieldDef[\"type\"]): \"text\" | \"number\" | \"date\" {\n if (fieldType === \"number\" || fieldType === \"currency\") return \"number\"\n if (fieldType === \"date\") return \"date\"\n return \"text\"\n}\n\nfunction getInputPlaceholder(fieldType: ConditionFieldDef[\"type\"]): string {\n if (fieldType === \"currency\") return \"Amount\"\n if (fieldType === \"number\") return \"Enter number...\"\n if (fieldType === \"date\") return \"\"\n return \"Enter value...\"\n}\n\ninterface ConditionValueInputProps {\n condition: ConditionFilterValue\n fieldDef: ConditionFieldDef\n onValueChange: (event: React.ChangeEvent<HTMLInputElement>) => void\n onSelectValueChange: (value: string) => void\n onMultiSelectValueToggle: (value: string) => void\n onCommit: () => void\n}\n\nfunction shouldShowOptionSearch(\n fieldDef: ConditionFieldDef,\n optionCount: number,\n): boolean {\n if (fieldDef.searchable === true) return true\n if (fieldDef.searchable === false) return false\n if (typeof fieldDef.searchable === \"object\") {\n return optionCount >= (fieldDef.searchable.threshold ?? 8)\n }\n return false\n}\n\nfunction SelectConditionValueInput({\n condition,\n fieldDef,\n onSelectValueChange,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onSelectValueChange\">) {\n const [query, setQuery] = React.useState(\"\")\n const options = normalizeFieldOptions(fieldDef)\n const normalizedQuery = query.trim().toLowerCase()\n const filteredOptions = normalizedQuery\n ? options.filter((option) => option.label.toLowerCase().includes(normalizedQuery))\n : options\n const showSearch = shouldShowOptionSearch(fieldDef, options.length)\n\n return (\n <Select\n value={typeof condition.value === \"string\" ? condition.value : \"\"}\n onValueChange={onSelectValueChange}\n >\n <SelectTrigger className=\"h-8 w-full\" size=\"sm\">\n <SelectValue placeholder=\"Select value...\" />\n </SelectTrigger>\n <SelectContent>\n {showSearch ? (\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-1.5\">\n <Input\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n placeholder=\"Search options...\"\n className=\"h-7 text-xs\"\n />\n </div>\n ) : null}\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))\n ) : (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">No options</div>\n )}\n </SelectContent>\n </Select>\n )\n}\n\nfunction MultiSelectConditionValueInput({\n condition,\n fieldDef,\n onMultiSelectValueToggle,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onMultiSelectValueToggle\">) {\n const selectedValues = Array.isArray(condition.value) ? condition.value : []\n const options = normalizeFieldOptions(fieldDef)\n\n return (\n <div className=\"max-h-28 overflow-y-auto rounded-md border border-border bg-background p-1\">\n {options.length > 0 ? (\n options.map((option) => {\n const checked = selectedValues.includes(option.value)\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-xs hover:bg-muted\",\n checked && \"text-brand-purple\",\n )}\n onClick={(event) => {\n event.stopPropagation()\n onMultiSelectValueToggle(option.value)\n }}\n >\n <span\n className={cn(\n \"flex h-3.5 w-3.5 items-center justify-center rounded-sm border border-border\",\n checked && \"border-brand-purple bg-brand-purple text-white\",\n )}\n aria-hidden=\"true\"\n >\n {checked ? <Check className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n </button>\n )\n })\n ) : (\n <div className=\"px-2 py-1 text-xs text-muted-foreground\">No options</div>\n )}\n </div>\n )\n}\n\nfunction ScalarConditionValueInput({\n condition,\n fieldDef,\n onValueChange,\n onCommit,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onValueChange\" | \"onCommit\">) {\n return (\n <div className=\"relative flex items-center\">\n {fieldDef.type === \"currency\" ? (\n <span className=\"pointer-events-none absolute left-2 text-sm text-muted-foreground\">\n $\n </span>\n ) : null}\n <Input\n type={getInputType(fieldDef.type)}\n value={\n condition.value != null && !Array.isArray(condition.value)\n ? String(condition.value)\n : \"\"\n }\n onChange={onValueChange}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n onCommit()\n }\n }}\n placeholder={getInputPlaceholder(fieldDef.type)}\n className={cn(\"h-8\", fieldDef.type === \"currency\" && \"pl-6\")}\n />\n </div>\n )\n}\n\nfunction ConditionValueInput(props: ConditionValueInputProps) {\n if (props.fieldDef.type === \"select\") {\n return <SelectConditionValueInput {...props} />\n }\n\n if (props.fieldDef.type === \"multi_select\") {\n return <MultiSelectConditionValueInput {...props} />\n }\n\n return <ScalarConditionValueInput {...props} />\n}\n\ninterface ConditionRowProps {\n condition: ConditionFilterValue\n fields: ConditionFieldDef[]\n index: number\n onChange: (updated: ConditionFilterValue) => void\n onRemove: () => void\n onCommit: () => void\n}\n\nfunction ConditionRow({\n condition,\n fields,\n index,\n onChange,\n onRemove,\n onCommit,\n}: ConditionRowProps) {\n const fieldDef = fields.find((f) => f.id === condition.field) ?? fields[0]\n const operators = getOperators(fieldDef)\n const isUnary = isUnaryOperator(condition.operator)\n const FieldIcon = getFieldIcon(fieldDef.type)\n\n const handleFieldChange = (newFieldId: string) => {\n const newFieldDef = fields.find((f) => f.id === newFieldId) ?? fields[0]\n if (!newFieldDef) return\n onChange({\n ...condition,\n field: newFieldDef.id,\n operator: getDefaultOperator(newFieldDef),\n value: null,\n })\n }\n\n const handleOperatorChange = (newOp: ConditionOperator) => {\n onChange({\n ...condition,\n operator: newOp,\n value: normalizeConditionValue(condition.value, fieldDef, newOp),\n })\n }\n\n const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange({\n ...condition,\n value: parseConditionValue(event.target.value, fieldDef.type),\n })\n }\n\n const handleSelectValueChange = (value: string) => {\n onChange({\n ...condition,\n value,\n })\n }\n\n const handleMultiSelectValueToggle = (value: string) => {\n const currentValues = Array.isArray(condition.value) ? condition.value : []\n const nextValues = currentValues.includes(value)\n ? currentValues.filter((currentValue) => currentValue !== value)\n : [...currentValues, value]\n\n onChange({\n ...condition,\n value: nextValues,\n })\n }\n\n\n return (\n <div\n className=\"grid grid-cols-[52px_minmax(150px,1fr)_120px_minmax(140px,1fr)_auto] items-center gap-2 rounded-lg border border-border/70 bg-background p-2 shadow-sm\"\n data-slot=\"condition-row\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">\n {index === 0 ? \"Where\" : \"And\"}\n </div>\n\n <Select value={condition.field} onValueChange={handleFieldChange}>\n <SelectTrigger className=\"h-8 w-full justify-start gap-2\" size=\"sm\">\n <FieldIcon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <SelectValue placeholder={fieldDef.label} />\n </SelectTrigger>\n <SelectContent>\n {fields.map((field) => {\n const Icon = getFieldIcon(field.type)\n return (\n <SelectItem key={field.id} value={field.id}>\n <span className=\"inline-flex items-center gap-2\">\n <Icon className=\"h-3.5 w-3.5 text-muted-foreground\" />\n {field.label}\n </span>\n </SelectItem>\n )\n })}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) => handleOperatorChange(value as ConditionOperator)}\n >\n <SelectTrigger className=\"h-8 w-full\" size=\"sm\">\n <SelectValue placeholder=\"Operator\" />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isUnary ? (\n <div className=\"h-8 rounded-md border border-dashed border-border/70 bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground\">\n No value needed\n </div>\n ) : (\n <ConditionValueInput\n condition={condition}\n fieldDef={fieldDef}\n onValueChange={handleValueChange}\n onSelectValueChange={handleSelectValueChange}\n onMultiSelectValueToggle={handleMultiSelectValueToggle}\n onCommit={onCommit}\n />\n )}\n\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"Toggle condition visibility\"\n onClick={(event) => event.preventDefault()}\n >\n <Eye className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"More condition actions\"\n onClick={(event) => event.preventDefault()}\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground hover:text-destructive\"\n onClick={onRemove}\n aria-label=\"Remove condition\"\n >\n <Trash2 className=\"size-4\" />\n </Button>\n </div>\n </div>\n )\n}\n\n// ── Main Component ─────────────────────────────────────────────\n\nfunction DataTableConditionFilter({\n fields,\n conditions,\n onConditionsChange,\n className,\n}: DataTableConditionFilterProps) {\n const [drafts, setDrafts] = React.useState<ConditionFilterValue[]>(() =>\n conditions.map((condition) => normalizeCondition(condition, fields)),\n )\n\n const fieldsSignature = React.useMemo(() => getFieldsSignature(fields), [fields])\n const conditionsSignature = React.useMemo(() => getConditionsSignature(conditions), [conditions])\n const fieldsRef = React.useRef(fields)\n\n React.useEffect(() => {\n setDrafts(conditions.map((condition) => normalizeCondition(condition, fieldsRef.current)))\n }, [conditionsSignature])\n\n React.useEffect(() => {\n if (fieldsRef.current !== fields) {\n fieldsRef.current = fields\n setDrafts((current) => current.map((condition) => normalizeCondition(condition, fields)))\n }\n // Depend on a structural signature so inline-but-equivalent field arrays do\n // not wipe in-progress drafts before Apply.\n }, [fieldsSignature, fields])\n\n const commitDrafts = React.useCallback(\n (nextDrafts: ConditionFilterValue[] = drafts) => {\n onConditionsChange(getCommittedConditions(nextDrafts, fields))\n },\n [drafts, fields, onConditionsChange],\n )\n\n const handleAdd = () => {\n const firstField = fields[0]\n if (!firstField) return\n const committedDrafts = getCommittedConditions(drafts, fields)\n const nextDrafts = [...committedDrafts, createDraftCondition(firstField)]\n setDrafts(nextDrafts)\n onConditionsChange(committedDrafts)\n }\n\n const handleUpdate = (index: number, updated: ConditionFilterValue) => {\n setDrafts((current) => {\n const next = [...current]\n next[index] = normalizeCondition(updated, fields)\n return next\n })\n }\n\n const handleRemove = (index: number) => {\n setDrafts((current) => {\n const next = current.filter((_, currentIndex) => currentIndex !== index)\n commitDrafts(next)\n return next\n })\n }\n\n const handleClear = () => {\n setDrafts([])\n onConditionsChange([])\n }\n\n const hasAppliedConditions = conditions.length > 0\n const hasDrafts = drafts.length > 0\n\n return (\n <div\n className={cn(\n \"w-[min(760px,calc(100vw-2rem))] rounded-xl border border-border bg-background p-3 text-foreground shadow-xl\",\n className,\n )}\n data-slot=\"condition-filter\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") {\n event.stopPropagation()\n }\n }}\n >\n <div className=\"mb-3 flex items-center justify-between gap-3 border-b border-border/70 pb-3\">\n <div>\n <div className=\"text-sm font-semibold\">Filter builder</div>\n <div className=\"text-xs text-muted-foreground\">\n Build field, operator, and value conditions.\n </div>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-destructive hover:text-destructive\"\n onClick={handleClear}\n disabled={!hasAppliedConditions && !hasDrafts}\n >\n Clear filters\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-2\">\n {drafts.map((condition, index) => (\n <ConditionRow\n key={condition.id}\n condition={condition}\n fields={fields}\n index={index}\n onChange={(updated) => handleUpdate(index, updated)}\n onRemove={() => handleRemove(index)}\n onCommit={() => commitDrafts()}\n />\n ))}\n </div>\n\n {!hasDrafts ? (\n <div className=\"rounded-lg border border-dashed border-border/80 bg-muted/20 px-3 py-5 text-center text-xs text-muted-foreground\">\n No builder filters yet. Add a filter to start a condition row.\n </div>\n ) : null}\n\n <div className=\"mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-border/70 pt-3\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground\"\n onClick={handleAdd}\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground opacity-60\"\n disabled\n aria-disabled=\"true\"\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter group\n </Button>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-8 text-xs\"\n onClick={() => commitDrafts()}\n >\n Apply\n </Button>\n </div>\n </div>\n )\n}\n\nexport {\n DataTableConditionFilter,\n OPERATOR_LABELS,\n DEFAULT_OPERATORS,\n generateConditionId,\n getOperators,\n}\nexport type { DataTableConditionFilterProps }\n"],"mappings":"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","names":["_a"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/data-table-condition-filter.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n Check,\n Eye,\n MoreHorizontal,\n Plus,\n Trash2,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Button } from \"./button\"\nimport { Input } from \"./input\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./select\"\n\n// ── Types ──────────────────────────────────────────────────────\n\nexport type ConditionOperator =\n | \"eq\"\n | \"neq\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"in\"\n | \"is_null\"\n | \"is_not_null\"\n\nexport interface ConditionOptionObject {\n label: string\n value: string\n}\n\nexport type ConditionFieldOption = string | ConditionOptionObject\n\nexport interface ConditionFieldDef {\n /** Unique field key (e.g., \"Account_Balance__c\") */\n id: string\n /** Display label (e.g., \"Account Balance\") */\n label: string\n /** Field data type — determines which operators are available and how the value input renders */\n type: \"text\" | \"number\" | \"currency\" | \"date\" | \"select\" | \"multi_select\"\n /** Allowed operators for this field. Defaults based on type if not provided. */\n operators?: ConditionOperator[]\n /** Options used by select and multi-select fields. Strings use the same label and value. */\n options?: ConditionFieldOption[]\n /** Show a search box for option-backed value inputs. */\n searchable?: boolean | { threshold?: number }\n}\n\nexport interface ConditionFilterValue {\n /** Stable identity — used as React key to avoid stale-state bugs on removal */\n id: string\n field: string\n operator: ConditionOperator\n value: string | number | string[] | null\n}\n\ninterface DataTableConditionFilterProps {\n /** Available fields the user can filter on */\n fields: ConditionFieldDef[]\n /** Current active conditions */\n conditions: ConditionFilterValue[]\n /** Called when conditions change (add, update, remove) */\n onConditionsChange: (conditions: ConditionFilterValue[]) => void\n className?: string\n}\n\n// ── Constants ──────────────────────────────────────────────────\n\nconst OPERATOR_LABELS: Record<ConditionOperator, string> = {\n eq: \"=\",\n neq: \"≠\",\n gt: \">\",\n gte: \"≥\",\n lt: \"<\",\n lte: \"≤\",\n in: \"is any of\",\n is_null: \"is empty\",\n is_not_null: \"is not empty\",\n}\n\nconst NUMERIC_OPERATORS: ConditionOperator[] = [\n \"eq\",\n \"neq\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"is_null\",\n \"is_not_null\",\n]\n\nconst NAV_KEYS = new Set([\"ArrowDown\", \"ArrowUp\", \"Enter\", \"Escape\", \"Tab\"])\n\nexport function shouldShowOptionSearch(\n searchable: ConditionFieldDef[\"searchable\"],\n optionCount: number,\n defaultThreshold = 8,\n): boolean {\n if (searchable === true) return true\n if (searchable === false) return false\n const threshold =\n typeof searchable === \"object\"\n ? (searchable.threshold ?? defaultThreshold)\n : defaultThreshold\n return optionCount > threshold\n}\n\nconst DEFAULT_OPERATORS: Record<ConditionFieldDef[\"type\"], ConditionOperator[]> = {\n text: [\"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n number: NUMERIC_OPERATORS,\n currency: NUMERIC_OPERATORS,\n date: [\"gt\", \"gte\", \"lt\", \"lte\", \"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n select: [\"eq\", \"neq\", \"is_null\", \"is_not_null\"],\n multi_select: [\"in\", \"is_null\", \"is_not_null\"],\n}\n\n/** Generate a stable unique ID for a new condition row. */\nfunction generateConditionId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ── Helpers ────────────────────────────────────────────────────\n\nfunction getOperators(field: ConditionFieldDef): ConditionOperator[] {\n return field.operators ?? DEFAULT_OPERATORS[field.type]\n}\n\nfunction isUnaryOperator(op: ConditionOperator): boolean {\n return op === \"is_null\" || op === \"is_not_null\"\n}\n\nfunction getDefaultOperator(field: ConditionFieldDef): ConditionOperator {\n return getOperators(field)[0] ?? \"eq\"\n}\n\nfunction createDraftCondition(field: ConditionFieldDef): ConditionFilterValue {\n return {\n id: generateConditionId(),\n field: field.id,\n operator: getDefaultOperator(field),\n value: null,\n }\n}\n\nfunction normalizeConditionValue(\n value: ConditionFilterValue[\"value\"],\n field: ConditionFieldDef,\n operator: ConditionOperator,\n): ConditionFilterValue[\"value\"] {\n if (isUnaryOperator(operator)) return null\n if (field.type === \"multi_select\") {\n return Array.isArray(value) ? value : null\n }\n return Array.isArray(value) ? null : value\n}\n\nfunction normalizeCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): ConditionFilterValue {\n const field = fields.find((f) => f.id === condition.field) ?? fields[0]\n if (!field) return condition\n\n const operators = getOperators(field)\n const operator = operators.includes(condition.operator)\n ? condition.operator\n : getDefaultOperator(field)\n\n return {\n ...condition,\n field: field.id,\n operator,\n value: normalizeConditionValue(condition.value, field, operator),\n }\n}\n\nfunction parseConditionValue(\n raw: string,\n fieldType: ConditionFieldDef[\"type\"],\n): string | number | null {\n if (raw === \"\") return null\n if (fieldType === \"number\" || fieldType === \"currency\") {\n const parsed = Number(raw)\n return Number.isNaN(parsed) ? null : parsed\n }\n return raw\n}\n\nfunction isCompleteCondition(\n condition: ConditionFilterValue,\n fields: ConditionFieldDef[],\n): boolean {\n const field = fields.find((f) => f.id === condition.field)\n if (!field) return false\n if (!getOperators(field).includes(condition.operator)) return false\n if (isUnaryOperator(condition.operator)) return true\n if (field.type === \"multi_select\") {\n return Array.isArray(condition.value) && condition.value.length > 0\n }\n return condition.value !== null && condition.value !== \"\" && !Array.isArray(condition.value)\n}\n\nfunction getConditionValueSignature(value: ConditionFilterValue[\"value\"]): string {\n return Array.isArray(value) ? JSON.stringify(value) : String(value)\n}\n\nfunction getConditionsSignature(conditions: ConditionFilterValue[]): string {\n return conditions\n .map((condition) => `${condition.id}:${condition.field}:${condition.operator}:${getConditionValueSignature(condition.value)}`)\n .join(\";\")\n}\n\nfunction normalizeFieldOptions(field: ConditionFieldDef): ConditionOptionObject[] {\n return (field.options ?? []).map((option) =>\n typeof option === \"string\" ? { label: option, value: option } : option,\n )\n}\n\nfunction getFieldsSignature(fields: ConditionFieldDef[]): string {\n return fields\n .map((field) => {\n const optionsSignature = normalizeFieldOptions(field)\n .map((option) => `${option.label}:${option.value}`)\n .join(\"|\")\n const searchSignature =\n typeof field.searchable === \"object\"\n ? `threshold:${field.searchable.threshold ?? \"\"}`\n : String(field.searchable ?? \"\")\n return `${field.id}:${field.type}:${field.label}:${(field.operators ?? []).join(\"|\")}:${optionsSignature}:${searchSignature}`\n })\n .join(\";\")\n}\n\nfunction getCommittedConditions(\n drafts: ConditionFilterValue[],\n fields: ConditionFieldDef[],\n): ConditionFilterValue[] {\n return drafts\n .map((condition) => normalizeCondition(condition, fields))\n .filter((condition) => isCompleteCondition(condition, fields))\n}\n\n// ── Condition Row ──────────────────────────────────────────────\n\nfunction getInputType(fieldType: ConditionFieldDef[\"type\"]): \"text\" | \"number\" | \"date\" {\n if (fieldType === \"number\" || fieldType === \"currency\") return \"number\"\n if (fieldType === \"date\") return \"date\"\n return \"text\"\n}\n\nfunction getInputPlaceholder(fieldType: ConditionFieldDef[\"type\"]): string {\n if (fieldType === \"currency\") return \"Amount\"\n if (fieldType === \"number\") return \"Enter number...\"\n if (fieldType === \"date\") return \"\"\n return \"Enter value...\"\n}\n\ninterface ConditionValueInputProps {\n condition: ConditionFilterValue\n fieldDef: ConditionFieldDef\n onValueChange: (event: React.ChangeEvent<HTMLInputElement>) => void\n onSelectValueChange: (value: string) => void\n onMultiSelectValueToggle: (value: string) => void\n onCommit: () => void\n}\n\nfunction SelectConditionValueInput({\n condition,\n fieldDef,\n onSelectValueChange,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onSelectValueChange\">) {\n const [query, setQuery] = React.useState(\"\")\n const options = normalizeFieldOptions(fieldDef)\n const showSearch = shouldShowOptionSearch(fieldDef.searchable, options.length)\n const normalizedQuery = showSearch ? query.trim().toLowerCase() : \"\"\n const filteredOptions = normalizedQuery\n ? options.filter((option) => option.label.toLowerCase().includes(normalizedQuery))\n : options\n\n React.useEffect(() => {\n setQuery(\"\")\n }, [fieldDef.id])\n\n return (\n <Select\n value={typeof condition.value === \"string\" ? condition.value : \"\"}\n onValueChange={onSelectValueChange}\n >\n <SelectTrigger className=\"h-8 w-full\" size=\"sm\">\n <SelectValue placeholder=\"Select value...\" />\n </SelectTrigger>\n <SelectContent>\n {showSearch ? (\n <div className=\"sticky top-0 z-10 border-b border-border bg-popover p-1.5\">\n <Input\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (!NAV_KEYS.has(event.key)) {\n event.stopPropagation()\n }\n }}\n placeholder=\"Search options...\"\n className=\"h-7 text-xs\"\n />\n </div>\n ) : null}\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))\n ) : (\n <div className=\"px-2 py-1.5 text-xs text-muted-foreground\">No options</div>\n )}\n </SelectContent>\n </Select>\n )\n}\n\nfunction MultiSelectConditionValueInput({\n condition,\n fieldDef,\n onMultiSelectValueToggle,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onMultiSelectValueToggle\">) {\n const selectedValues = Array.isArray(condition.value) ? condition.value : []\n const options = normalizeFieldOptions(fieldDef)\n\n return (\n <div className=\"max-h-28 overflow-y-auto rounded-md border border-border bg-background p-1\">\n {options.length > 0 ? (\n options.map((option) => {\n const checked = selectedValues.includes(option.value)\n return (\n <button\n key={option.value}\n type=\"button\"\n role=\"checkbox\"\n aria-checked={checked}\n className={cn(\n \"flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-xs hover:bg-muted\",\n checked && \"text-brand-purple\",\n )}\n onClick={(event) => {\n event.stopPropagation()\n onMultiSelectValueToggle(option.value)\n }}\n >\n <span\n className={cn(\n \"flex h-3.5 w-3.5 items-center justify-center rounded-sm border border-border\",\n checked && \"border-brand-purple bg-brand-purple text-white\",\n )}\n aria-hidden=\"true\"\n >\n {checked ? <Check className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{option.label}</span>\n </button>\n )\n })\n ) : (\n <div className=\"px-2 py-1 text-xs text-muted-foreground\">No options</div>\n )}\n </div>\n )\n}\n\nfunction ScalarConditionValueInput({\n condition,\n fieldDef,\n onValueChange,\n onCommit,\n}: Pick<ConditionValueInputProps, \"condition\" | \"fieldDef\" | \"onValueChange\" | \"onCommit\">) {\n return (\n <div className=\"relative flex items-center\">\n {fieldDef.type === \"currency\" ? (\n <span className=\"pointer-events-none absolute left-2 text-sm text-muted-foreground\">\n $\n </span>\n ) : null}\n <Input\n type={getInputType(fieldDef.type)}\n value={\n condition.value != null && !Array.isArray(condition.value)\n ? String(condition.value)\n : \"\"\n }\n onChange={onValueChange}\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n event.stopPropagation()\n if (event.key === \"Enter\") {\n onCommit()\n }\n }}\n placeholder={getInputPlaceholder(fieldDef.type)}\n className={cn(\"h-8\", fieldDef.type === \"currency\" && \"pl-6\")}\n />\n </div>\n )\n}\n\nfunction ConditionValueInput(props: ConditionValueInputProps) {\n if (props.fieldDef.type === \"select\") {\n return <SelectConditionValueInput {...props} />\n }\n\n if (props.fieldDef.type === \"multi_select\") {\n return <MultiSelectConditionValueInput {...props} />\n }\n\n return <ScalarConditionValueInput {...props} />\n}\n\ninterface ConditionRowProps {\n condition: ConditionFilterValue\n fields: ConditionFieldDef[]\n index: number\n onChange: (updated: ConditionFilterValue) => void\n onRemove: () => void\n onCommit: () => void\n}\n\nfunction ConditionRow({\n condition,\n fields,\n index,\n onChange,\n onRemove,\n onCommit,\n}: ConditionRowProps) {\n const fieldDef = fields.find((f) => f.id === condition.field) ?? fields[0]\n const operators = getOperators(fieldDef)\n const isUnary = isUnaryOperator(condition.operator)\n const handleFieldChange = (newFieldId: string) => {\n const newFieldDef = fields.find((f) => f.id === newFieldId) ?? fields[0]\n if (!newFieldDef) return\n onChange({\n ...condition,\n field: newFieldDef.id,\n operator: getDefaultOperator(newFieldDef),\n value: null,\n })\n }\n\n const handleOperatorChange = (newOp: ConditionOperator) => {\n onChange({\n ...condition,\n operator: newOp,\n value: normalizeConditionValue(condition.value, fieldDef, newOp),\n })\n }\n\n const handleValueChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n onChange({\n ...condition,\n value: parseConditionValue(event.target.value, fieldDef.type),\n })\n }\n\n const handleSelectValueChange = (value: string) => {\n onChange({\n ...condition,\n value,\n })\n }\n\n const handleMultiSelectValueToggle = (value: string) => {\n const currentValues = Array.isArray(condition.value) ? condition.value : []\n const nextValues = currentValues.includes(value)\n ? currentValues.filter((currentValue) => currentValue !== value)\n : [...currentValues, value]\n\n onChange({\n ...condition,\n value: nextValues,\n })\n }\n\n\n return (\n <div\n className=\"grid grid-cols-[52px_minmax(150px,1fr)_120px_minmax(140px,1fr)_auto] items-center gap-2 rounded-lg border border-border/70 bg-background p-2 shadow-sm\"\n data-slot=\"condition-row\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">\n {index === 0 ? \"Where\" : \"And\"}\n </div>\n\n <Select value={condition.field} onValueChange={handleFieldChange}>\n <SelectTrigger className=\"h-8 w-full justify-start gap-2\" size=\"sm\">\n <SelectValue placeholder={fieldDef.label} />\n </SelectTrigger>\n <SelectContent>\n {fields.map((field) => (\n <SelectItem key={field.id} value={field.id}>\n {field.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) => handleOperatorChange(value as ConditionOperator)}\n >\n <SelectTrigger className=\"h-8 w-full\" size=\"sm\">\n <SelectValue placeholder=\"Operator\" />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isUnary ? (\n <div className=\"h-8 rounded-md border border-dashed border-border/70 bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground\">\n No value needed\n </div>\n ) : (\n <ConditionValueInput\n condition={condition}\n fieldDef={fieldDef}\n onValueChange={handleValueChange}\n onSelectValueChange={handleSelectValueChange}\n onMultiSelectValueToggle={handleMultiSelectValueToggle}\n onCommit={onCommit}\n />\n )}\n\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"Toggle condition visibility\"\n onClick={(event) => event.preventDefault()}\n >\n <Eye className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground\"\n aria-label=\"More condition actions\"\n onClick={(event) => event.preventDefault()}\n >\n <MoreHorizontal className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-8 w-8 text-muted-foreground hover:text-destructive\"\n onClick={onRemove}\n aria-label=\"Remove condition\"\n >\n <Trash2 className=\"size-4\" />\n </Button>\n </div>\n </div>\n )\n}\n\n// ── Main Component ─────────────────────────────────────────────\n\nfunction DataTableConditionFilter({\n fields,\n conditions,\n onConditionsChange,\n className,\n}: DataTableConditionFilterProps) {\n const [drafts, setDrafts] = React.useState<ConditionFilterValue[]>(() =>\n conditions.map((condition) => normalizeCondition(condition, fields)),\n )\n\n const fieldsSignature = React.useMemo(() => getFieldsSignature(fields), [fields])\n const conditionsSignature = React.useMemo(() => getConditionsSignature(conditions), [conditions])\n const fieldsRef = React.useRef(fields)\n\n React.useEffect(() => {\n setDrafts(conditions.map((condition) => normalizeCondition(condition, fieldsRef.current)))\n }, [conditionsSignature])\n\n React.useEffect(() => {\n if (fieldsRef.current !== fields) {\n fieldsRef.current = fields\n setDrafts((current) => current.map((condition) => normalizeCondition(condition, fields)))\n }\n // Depend on a structural signature so inline-but-equivalent field arrays do\n // not wipe in-progress drafts before Apply.\n }, [fieldsSignature, fields])\n\n const commitDrafts = React.useCallback(\n (nextDrafts: ConditionFilterValue[] = drafts) => {\n onConditionsChange(getCommittedConditions(nextDrafts, fields))\n },\n [drafts, fields, onConditionsChange],\n )\n\n const handleAdd = () => {\n const firstField = fields[0]\n if (!firstField) return\n const committedDrafts = getCommittedConditions(drafts, fields)\n const nextDrafts = [...committedDrafts, createDraftCondition(firstField)]\n setDrafts(nextDrafts)\n onConditionsChange(committedDrafts)\n }\n\n const handleUpdate = (index: number, updated: ConditionFilterValue) => {\n setDrafts((current) => {\n const next = [...current]\n next[index] = normalizeCondition(updated, fields)\n return next\n })\n }\n\n const handleRemove = (index: number) => {\n setDrafts((current) => {\n const next = current.filter((_, currentIndex) => currentIndex !== index)\n commitDrafts(next)\n return next\n })\n }\n\n const handleClear = () => {\n setDrafts([])\n onConditionsChange([])\n }\n\n const hasAppliedConditions = conditions.length > 0\n const hasDrafts = drafts.length > 0\n\n return (\n <div\n className={cn(\n \"w-[min(760px,calc(100vw-2rem))] rounded-xl border border-border bg-background p-3 text-foreground shadow-xl\",\n className,\n )}\n data-slot=\"condition-filter\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => {\n if (event.key !== \"Escape\") {\n event.stopPropagation()\n }\n }}\n >\n <div className=\"mb-3 flex items-center justify-between gap-3 border-b border-border/70 pb-3\">\n <div>\n <div className=\"text-sm font-semibold\">Filter builder</div>\n <div className=\"text-xs text-muted-foreground\">\n Build field, operator, and value conditions.\n </div>\n </div>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-destructive hover:text-destructive\"\n onClick={handleClear}\n disabled={!hasAppliedConditions && !hasDrafts}\n >\n Clear filters\n </Button>\n </div>\n\n <div className=\"flex flex-col gap-2\">\n {drafts.map((condition, index) => (\n <ConditionRow\n key={condition.id}\n condition={condition}\n fields={fields}\n index={index}\n onChange={(updated) => handleUpdate(index, updated)}\n onRemove={() => handleRemove(index)}\n onCommit={() => commitDrafts()}\n />\n ))}\n </div>\n\n {!hasDrafts ? (\n <div className=\"rounded-lg border border-dashed border-border/80 bg-muted/20 px-3 py-5 text-center text-xs text-muted-foreground\">\n No builder filters yet. Add a filter to start a condition row.\n </div>\n ) : null}\n\n <div className=\"mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-border/70 pt-3\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground\"\n onClick={handleAdd}\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 text-xs text-muted-foreground opacity-60\"\n disabled\n aria-disabled=\"true\"\n >\n <Plus className=\"mr-1 size-3.5\" />\n Add filter group\n </Button>\n </div>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"h-8 text-xs\"\n onClick={() => commitDrafts()}\n >\n Apply\n </Button>\n </div>\n </div>\n )\n}\n\nexport {\n DataTableConditionFilter,\n OPERATOR_LABELS,\n DEFAULT_OPERATORS,\n generateConditionId,\n getOperators,\n}\nexport type { DataTableConditionFilterProps }\n"],"mappings":"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","names":["_a"]}
|
|
@@ -27,7 +27,8 @@ import { Popover as PopoverPrimitive } from "radix-ui";
|
|
|
27
27
|
import { cn } from "../lib/utils.js";
|
|
28
28
|
import { Button } from "./button.js";
|
|
29
29
|
import {
|
|
30
|
-
DataTableConditionFilter
|
|
30
|
+
DataTableConditionFilter,
|
|
31
|
+
shouldShowOptionSearch
|
|
31
32
|
} from "./data-table-condition-filter.js";
|
|
32
33
|
import {
|
|
33
34
|
DropdownMenu,
|
|
@@ -120,7 +121,7 @@ function DataTableFilter({
|
|
|
120
121
|
children: [
|
|
121
122
|
/* @__PURE__ */ jsx(ListFilter, { className: "h-3.5 w-3.5" }),
|
|
122
123
|
"Filter",
|
|
123
|
-
activeCount > 0 ? /* @__PURE__ */ jsx("span", { className: "rounded bg-muted px-1.5 py-0 text-[10px] font-semibold", children: activeCount }) : null
|
|
124
|
+
activeCount > 0 ? /* @__PURE__ */ jsx("span", { className: "rounded bg-muted px-1.5 py-0 text-[10px] font-semibold text-foreground", children: activeCount }) : null
|
|
124
125
|
]
|
|
125
126
|
}
|
|
126
127
|
) }),
|
|
@@ -169,13 +170,11 @@ function DataTableFilter({
|
|
|
169
170
|
const filteredOptions = subQuery ? category.options.filter(
|
|
170
171
|
(opt) => getOptionLabel(opt).toLowerCase().includes(subQuery)
|
|
171
172
|
) : category.options;
|
|
172
|
-
const shouldShowSubmenuSearch = (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return category.options.length > threshold;
|
|
178
|
-
})();
|
|
173
|
+
const shouldShowSubmenuSearch = shouldShowOptionSearch(
|
|
174
|
+
category.searchable,
|
|
175
|
+
category.options.length,
|
|
176
|
+
optionSearchThreshold
|
|
177
|
+
);
|
|
179
178
|
return /* @__PURE__ */ jsxs(
|
|
180
179
|
DropdownMenuSub,
|
|
181
180
|
{
|