@handled-ai/design-system 0.18.10 → 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/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/data-table-condition-filter.d.ts +15 -3
- package/dist/components/data-table-condition-filter.js +199 -52
- package/dist/components/data-table-condition-filter.js.map +1 -1
- package/dist/components/data-table-filter.js +7 -8
- 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/pill.d.ts +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/components/tabs.d.ts +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 +239 -7
- 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 +278 -68
- package/src/components/data-table-filter.tsx +6 -9
- 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":[]}
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const badgeVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
|
|
7
7
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
8
8
|
declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const buttonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
6
|
+
variant?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
|
|
7
7
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
8
8
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
9
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
type ConditionOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "is_null" | "is_not_null";
|
|
4
|
+
interface ConditionOptionObject {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}
|
|
8
|
+
type ConditionFieldOption = string | ConditionOptionObject;
|
|
4
9
|
interface ConditionFieldDef {
|
|
5
10
|
/** Unique field key (e.g., "Account_Balance__c") */
|
|
6
11
|
id: string;
|
|
7
12
|
/** Display label (e.g., "Account Balance") */
|
|
8
13
|
label: string;
|
|
9
14
|
/** Field data type — determines which operators are available and how the value input renders */
|
|
10
|
-
type: "text" | "number" | "currency" | "date";
|
|
15
|
+
type: "text" | "number" | "currency" | "date" | "select" | "multi_select";
|
|
11
16
|
/** Allowed operators for this field. Defaults based on type if not provided. */
|
|
12
17
|
operators?: ConditionOperator[];
|
|
18
|
+
/** Options used by select and multi-select fields. Strings use the same label and value. */
|
|
19
|
+
options?: ConditionFieldOption[];
|
|
20
|
+
/** Show a search box for option-backed value inputs. */
|
|
21
|
+
searchable?: boolean | {
|
|
22
|
+
threshold?: number;
|
|
23
|
+
};
|
|
13
24
|
}
|
|
14
25
|
interface ConditionFilterValue {
|
|
15
26
|
/** Stable identity — used as React key to avoid stale-state bugs on removal */
|
|
16
27
|
id: string;
|
|
17
28
|
field: string;
|
|
18
29
|
operator: ConditionOperator;
|
|
19
|
-
value: string | number | null;
|
|
30
|
+
value: string | number | string[] | null;
|
|
20
31
|
}
|
|
21
32
|
interface DataTableConditionFilterProps {
|
|
22
33
|
/** Available fields the user can filter on */
|
|
@@ -28,10 +39,11 @@ interface DataTableConditionFilterProps {
|
|
|
28
39
|
className?: string;
|
|
29
40
|
}
|
|
30
41
|
declare const OPERATOR_LABELS: Record<ConditionOperator, string>;
|
|
42
|
+
declare function shouldShowOptionSearch(searchable: ConditionFieldDef["searchable"], optionCount: number, defaultThreshold?: number): boolean;
|
|
31
43
|
declare const DEFAULT_OPERATORS: Record<ConditionFieldDef["type"], ConditionOperator[]>;
|
|
32
44
|
/** Generate a stable unique ID for a new condition row. */
|
|
33
45
|
declare function generateConditionId(): string;
|
|
34
46
|
declare function getOperators(field: ConditionFieldDef): ConditionOperator[];
|
|
35
47
|
declare function DataTableConditionFilter({ fields, conditions, onConditionsChange, className, }: DataTableConditionFilterProps): React.JSX.Element;
|
|
36
48
|
|
|
37
|
-
export { type ConditionFieldDef, type ConditionFilterValue, type ConditionOperator, 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,14 +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
|
-
|
|
27
|
-
DollarSign,
|
|
26
|
+
Check,
|
|
28
27
|
Eye,
|
|
29
|
-
Hash,
|
|
30
28
|
MoreHorizontal,
|
|
31
29
|
Plus,
|
|
32
|
-
Trash2
|
|
33
|
-
Type
|
|
30
|
+
Trash2
|
|
34
31
|
} from "lucide-react";
|
|
35
32
|
import { cn } from "../lib/utils.js";
|
|
36
33
|
import { Button } from "./button.js";
|
|
@@ -49,7 +46,7 @@ const OPERATOR_LABELS = {
|
|
|
49
46
|
gte: "\u2265",
|
|
50
47
|
lt: "<",
|
|
51
48
|
lte: "\u2264",
|
|
52
|
-
in: "
|
|
49
|
+
in: "is any of",
|
|
53
50
|
is_null: "is empty",
|
|
54
51
|
is_not_null: "is not empty"
|
|
55
52
|
};
|
|
@@ -63,11 +60,21 @@ const NUMERIC_OPERATORS = [
|
|
|
63
60
|
"is_null",
|
|
64
61
|
"is_not_null"
|
|
65
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
|
+
}
|
|
66
71
|
const DEFAULT_OPERATORS = {
|
|
67
72
|
text: ["eq", "neq", "is_null", "is_not_null"],
|
|
68
73
|
number: NUMERIC_OPERATORS,
|
|
69
74
|
currency: NUMERIC_OPERATORS,
|
|
70
|
-
date: ["gt", "gte", "lt", "lte", "eq", "neq", "is_null", "is_not_null"]
|
|
75
|
+
date: ["gt", "gte", "lt", "lte", "eq", "neq", "is_null", "is_not_null"],
|
|
76
|
+
select: ["eq", "neq", "is_null", "is_not_null"],
|
|
77
|
+
multi_select: ["in", "is_null", "is_not_null"]
|
|
71
78
|
};
|
|
72
79
|
function generateConditionId() {
|
|
73
80
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
@@ -77,7 +84,7 @@ function generateConditionId() {
|
|
|
77
84
|
}
|
|
78
85
|
function getOperators(field) {
|
|
79
86
|
var _a;
|
|
80
|
-
return (
|
|
87
|
+
return (_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type];
|
|
81
88
|
}
|
|
82
89
|
function isUnaryOperator(op) {
|
|
83
90
|
return op === "is_null" || op === "is_not_null";
|
|
@@ -94,6 +101,13 @@ function createDraftCondition(field) {
|
|
|
94
101
|
value: null
|
|
95
102
|
};
|
|
96
103
|
}
|
|
104
|
+
function normalizeConditionValue(value, field, operator) {
|
|
105
|
+
if (isUnaryOperator(operator)) return null;
|
|
106
|
+
if (field.type === "multi_select") {
|
|
107
|
+
return Array.isArray(value) ? value : null;
|
|
108
|
+
}
|
|
109
|
+
return Array.isArray(value) ? null : value;
|
|
110
|
+
}
|
|
97
111
|
function normalizeCondition(condition, fields) {
|
|
98
112
|
var _a;
|
|
99
113
|
const field = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
|
|
@@ -103,7 +117,7 @@ function normalizeCondition(condition, fields) {
|
|
|
103
117
|
return __spreadProps(__spreadValues({}, condition), {
|
|
104
118
|
field: field.id,
|
|
105
119
|
operator,
|
|
106
|
-
value:
|
|
120
|
+
value: normalizeConditionValue(condition.value, field, operator)
|
|
107
121
|
});
|
|
108
122
|
}
|
|
109
123
|
function parseConditionValue(raw, fieldType) {
|
|
@@ -119,25 +133,164 @@ function isCompleteCondition(condition, fields) {
|
|
|
119
133
|
if (!field) return false;
|
|
120
134
|
if (!getOperators(field).includes(condition.operator)) return false;
|
|
121
135
|
if (isUnaryOperator(condition.operator)) return true;
|
|
122
|
-
|
|
136
|
+
if (field.type === "multi_select") {
|
|
137
|
+
return Array.isArray(condition.value) && condition.value.length > 0;
|
|
138
|
+
}
|
|
139
|
+
return condition.value !== null && condition.value !== "" && !Array.isArray(condition.value);
|
|
140
|
+
}
|
|
141
|
+
function getConditionValueSignature(value) {
|
|
142
|
+
return Array.isArray(value) ? JSON.stringify(value) : String(value);
|
|
123
143
|
}
|
|
124
144
|
function getConditionsSignature(conditions) {
|
|
125
|
-
return conditions.map((condition) => `${condition.id}:${condition.field}:${condition.operator}:${
|
|
145
|
+
return conditions.map((condition) => `${condition.id}:${condition.field}:${condition.operator}:${getConditionValueSignature(condition.value)}`).join(";");
|
|
146
|
+
}
|
|
147
|
+
function normalizeFieldOptions(field) {
|
|
148
|
+
var _a;
|
|
149
|
+
return ((_a = field.options) != null ? _a : []).map(
|
|
150
|
+
(option) => typeof option === "string" ? { label: option, value: option } : option
|
|
151
|
+
);
|
|
126
152
|
}
|
|
127
153
|
function getFieldsSignature(fields) {
|
|
128
154
|
return fields.map((field) => {
|
|
129
|
-
var _a;
|
|
130
|
-
|
|
155
|
+
var _a, _b, _c;
|
|
156
|
+
const optionsSignature = normalizeFieldOptions(field).map((option) => `${option.label}:${option.value}`).join("|");
|
|
157
|
+
const searchSignature = typeof field.searchable === "object" ? `threshold:${(_a = field.searchable.threshold) != null ? _a : ""}` : String((_b = field.searchable) != null ? _b : "");
|
|
158
|
+
return `${field.id}:${field.type}:${field.label}:${((_c = field.operators) != null ? _c : []).join("|")}:${optionsSignature}:${searchSignature}`;
|
|
131
159
|
}).join(";");
|
|
132
160
|
}
|
|
133
161
|
function getCommittedConditions(drafts, fields) {
|
|
134
162
|
return drafts.map((condition) => normalizeCondition(condition, fields)).filter((condition) => isCompleteCondition(condition, fields));
|
|
135
163
|
}
|
|
136
|
-
function
|
|
137
|
-
if (
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
164
|
+
function getInputType(fieldType) {
|
|
165
|
+
if (fieldType === "number" || fieldType === "currency") return "number";
|
|
166
|
+
if (fieldType === "date") return "date";
|
|
167
|
+
return "text";
|
|
168
|
+
}
|
|
169
|
+
function getInputPlaceholder(fieldType) {
|
|
170
|
+
if (fieldType === "currency") return "Amount";
|
|
171
|
+
if (fieldType === "number") return "Enter number...";
|
|
172
|
+
if (fieldType === "date") return "";
|
|
173
|
+
return "Enter value...";
|
|
174
|
+
}
|
|
175
|
+
function SelectConditionValueInput({
|
|
176
|
+
condition,
|
|
177
|
+
fieldDef,
|
|
178
|
+
onSelectValueChange
|
|
179
|
+
}) {
|
|
180
|
+
const [query, setQuery] = React.useState("");
|
|
181
|
+
const options = normalizeFieldOptions(fieldDef);
|
|
182
|
+
const showSearch = shouldShowOptionSearch(fieldDef.searchable, options.length);
|
|
183
|
+
const normalizedQuery = showSearch ? query.trim().toLowerCase() : "";
|
|
184
|
+
const filteredOptions = normalizedQuery ? options.filter((option) => option.label.toLowerCase().includes(normalizedQuery)) : options;
|
|
185
|
+
React.useEffect(() => {
|
|
186
|
+
setQuery("");
|
|
187
|
+
}, [fieldDef.id]);
|
|
188
|
+
return /* @__PURE__ */ jsxs(
|
|
189
|
+
Select,
|
|
190
|
+
{
|
|
191
|
+
value: typeof condition.value === "string" ? condition.value : "",
|
|
192
|
+
onValueChange: onSelectValueChange,
|
|
193
|
+
children: [
|
|
194
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-full", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select value..." }) }),
|
|
195
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
196
|
+
showSearch ? /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 border-b border-border bg-popover p-1.5", children: /* @__PURE__ */ jsx(
|
|
197
|
+
Input,
|
|
198
|
+
{
|
|
199
|
+
value: query,
|
|
200
|
+
onChange: (event) => setQuery(event.target.value),
|
|
201
|
+
onClick: (event) => event.stopPropagation(),
|
|
202
|
+
onKeyDown: (event) => {
|
|
203
|
+
if (!NAV_KEYS.has(event.key)) {
|
|
204
|
+
event.stopPropagation();
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
placeholder: "Search options...",
|
|
208
|
+
className: "h-7 text-xs"
|
|
209
|
+
}
|
|
210
|
+
) }) : null,
|
|
211
|
+
filteredOptions.length > 0 ? filteredOptions.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) : /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs text-muted-foreground", children: "No options" })
|
|
212
|
+
] })
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
function MultiSelectConditionValueInput({
|
|
218
|
+
condition,
|
|
219
|
+
fieldDef,
|
|
220
|
+
onMultiSelectValueToggle
|
|
221
|
+
}) {
|
|
222
|
+
const selectedValues = Array.isArray(condition.value) ? condition.value : [];
|
|
223
|
+
const options = normalizeFieldOptions(fieldDef);
|
|
224
|
+
return /* @__PURE__ */ jsx("div", { className: "max-h-28 overflow-y-auto rounded-md border border-border bg-background p-1", children: options.length > 0 ? options.map((option) => {
|
|
225
|
+
const checked = selectedValues.includes(option.value);
|
|
226
|
+
return /* @__PURE__ */ jsxs(
|
|
227
|
+
"button",
|
|
228
|
+
{
|
|
229
|
+
type: "button",
|
|
230
|
+
role: "checkbox",
|
|
231
|
+
"aria-checked": checked,
|
|
232
|
+
className: cn(
|
|
233
|
+
"flex w-full items-center gap-2 rounded-sm px-2 py-1 text-left text-xs hover:bg-muted",
|
|
234
|
+
checked && "text-brand-purple"
|
|
235
|
+
),
|
|
236
|
+
onClick: (event) => {
|
|
237
|
+
event.stopPropagation();
|
|
238
|
+
onMultiSelectValueToggle(option.value);
|
|
239
|
+
},
|
|
240
|
+
children: [
|
|
241
|
+
/* @__PURE__ */ jsx(
|
|
242
|
+
"span",
|
|
243
|
+
{
|
|
244
|
+
className: cn(
|
|
245
|
+
"flex h-3.5 w-3.5 items-center justify-center rounded-sm border border-border",
|
|
246
|
+
checked && "border-brand-purple bg-brand-purple text-white"
|
|
247
|
+
),
|
|
248
|
+
"aria-hidden": "true",
|
|
249
|
+
children: checked ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) : null
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: option.label })
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
option.value
|
|
256
|
+
);
|
|
257
|
+
}) : /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-xs text-muted-foreground", children: "No options" }) });
|
|
258
|
+
}
|
|
259
|
+
function ScalarConditionValueInput({
|
|
260
|
+
condition,
|
|
261
|
+
fieldDef,
|
|
262
|
+
onValueChange,
|
|
263
|
+
onCommit
|
|
264
|
+
}) {
|
|
265
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
|
|
266
|
+
fieldDef.type === "currency" ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-2 text-sm text-muted-foreground", children: "$" }) : null,
|
|
267
|
+
/* @__PURE__ */ jsx(
|
|
268
|
+
Input,
|
|
269
|
+
{
|
|
270
|
+
type: getInputType(fieldDef.type),
|
|
271
|
+
value: condition.value != null && !Array.isArray(condition.value) ? String(condition.value) : "",
|
|
272
|
+
onChange: onValueChange,
|
|
273
|
+
onClick: (event) => event.stopPropagation(),
|
|
274
|
+
onKeyDown: (event) => {
|
|
275
|
+
event.stopPropagation();
|
|
276
|
+
if (event.key === "Enter") {
|
|
277
|
+
onCommit();
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
placeholder: getInputPlaceholder(fieldDef.type),
|
|
281
|
+
className: cn("h-8", fieldDef.type === "currency" && "pl-6")
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
] });
|
|
285
|
+
}
|
|
286
|
+
function ConditionValueInput(props) {
|
|
287
|
+
if (props.fieldDef.type === "select") {
|
|
288
|
+
return /* @__PURE__ */ jsx(SelectConditionValueInput, __spreadValues({}, props));
|
|
289
|
+
}
|
|
290
|
+
if (props.fieldDef.type === "multi_select") {
|
|
291
|
+
return /* @__PURE__ */ jsx(MultiSelectConditionValueInput, __spreadValues({}, props));
|
|
292
|
+
}
|
|
293
|
+
return /* @__PURE__ */ jsx(ScalarConditionValueInput, __spreadValues({}, props));
|
|
141
294
|
}
|
|
142
295
|
function ConditionRow({
|
|
143
296
|
condition,
|
|
@@ -151,7 +304,6 @@ function ConditionRow({
|
|
|
151
304
|
const fieldDef = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
|
|
152
305
|
const operators = getOperators(fieldDef);
|
|
153
306
|
const isUnary = isUnaryOperator(condition.operator);
|
|
154
|
-
const FieldIcon = getFieldIcon(fieldDef.type);
|
|
155
307
|
const handleFieldChange = (newFieldId) => {
|
|
156
308
|
var _a2;
|
|
157
309
|
const newFieldDef = (_a2 = fields.find((f) => f.id === newFieldId)) != null ? _a2 : fields[0];
|
|
@@ -165,7 +317,7 @@ function ConditionRow({
|
|
|
165
317
|
const handleOperatorChange = (newOp) => {
|
|
166
318
|
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
167
319
|
operator: newOp,
|
|
168
|
-
value:
|
|
320
|
+
value: normalizeConditionValue(condition.value, fieldDef, newOp)
|
|
169
321
|
}));
|
|
170
322
|
};
|
|
171
323
|
const handleValueChange = (event) => {
|
|
@@ -173,6 +325,18 @@ function ConditionRow({
|
|
|
173
325
|
value: parseConditionValue(event.target.value, fieldDef.type)
|
|
174
326
|
}));
|
|
175
327
|
};
|
|
328
|
+
const handleSelectValueChange = (value) => {
|
|
329
|
+
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
330
|
+
value
|
|
331
|
+
}));
|
|
332
|
+
};
|
|
333
|
+
const handleMultiSelectValueToggle = (value) => {
|
|
334
|
+
const currentValues = Array.isArray(condition.value) ? condition.value : [];
|
|
335
|
+
const nextValues = currentValues.includes(value) ? currentValues.filter((currentValue) => currentValue !== value) : [...currentValues, value];
|
|
336
|
+
onChange(__spreadProps(__spreadValues({}, condition), {
|
|
337
|
+
value: nextValues
|
|
338
|
+
}));
|
|
339
|
+
};
|
|
176
340
|
return /* @__PURE__ */ jsxs(
|
|
177
341
|
"div",
|
|
178
342
|
{
|
|
@@ -181,17 +345,8 @@ function ConditionRow({
|
|
|
181
345
|
children: [
|
|
182
346
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: index === 0 ? "Where" : "And" }),
|
|
183
347
|
/* @__PURE__ */ jsxs(Select, { value: condition.field, onValueChange: handleFieldChange, children: [
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
|
|
186
|
-
/* @__PURE__ */ jsx(SelectValue, { placeholder: fieldDef.label })
|
|
187
|
-
] }),
|
|
188
|
-
/* @__PURE__ */ jsx(SelectContent, { children: fields.map((field) => {
|
|
189
|
-
const Icon = getFieldIcon(field.type);
|
|
190
|
-
return /* @__PURE__ */ jsx(SelectItem, { value: field.id, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
|
|
191
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
192
|
-
field.label
|
|
193
|
-
] }) }, field.id);
|
|
194
|
-
}) })
|
|
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)) })
|
|
195
350
|
] }),
|
|
196
351
|
/* @__PURE__ */ jsxs(
|
|
197
352
|
Select,
|
|
@@ -204,26 +359,17 @@ function ConditionRow({
|
|
|
204
359
|
]
|
|
205
360
|
}
|
|
206
361
|
),
|
|
207
|
-
isUnary ? /* @__PURE__ */ jsx("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", children: "No value needed" }) : /* @__PURE__ */
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (event.key === "Enter") {
|
|
219
|
-
onCommit();
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
placeholder: fieldDef.type === "currency" ? "Amount" : fieldDef.type === "number" ? "Enter number..." : fieldDef.type === "date" ? "" : "Enter value...",
|
|
223
|
-
className: cn("h-8", fieldDef.type === "currency" && "pl-6")
|
|
224
|
-
}
|
|
225
|
-
)
|
|
226
|
-
] }),
|
|
362
|
+
isUnary ? /* @__PURE__ */ jsx("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", children: "No value needed" }) : /* @__PURE__ */ jsx(
|
|
363
|
+
ConditionValueInput,
|
|
364
|
+
{
|
|
365
|
+
condition,
|
|
366
|
+
fieldDef,
|
|
367
|
+
onValueChange: handleValueChange,
|
|
368
|
+
onSelectValueChange: handleSelectValueChange,
|
|
369
|
+
onMultiSelectValueToggle: handleMultiSelectValueToggle,
|
|
370
|
+
onCommit
|
|
371
|
+
}
|
|
372
|
+
),
|
|
227
373
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
228
374
|
/* @__PURE__ */ jsx(
|
|
229
375
|
Button,
|
|
@@ -419,6 +565,7 @@ export {
|
|
|
419
565
|
DataTableConditionFilter,
|
|
420
566
|
OPERATOR_LABELS,
|
|
421
567
|
generateConditionId,
|
|
422
|
-
getOperators
|
|
568
|
+
getOperators,
|
|
569
|
+
shouldShowOptionSearch
|
|
423
570
|
};
|
|
424
571
|
//# sourceMappingURL=data-table-condition-filter.js.map
|