@handled-ai/design-system 0.15.1 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/components/badge.d.ts +1 -1
  2. package/dist/components/button.d.ts +1 -1
  3. package/dist/components/collapsible-section.d.ts +20 -0
  4. package/dist/components/collapsible-section.js +48 -0
  5. package/dist/components/collapsible-section.js.map +1 -0
  6. package/dist/components/contact-list.d.ts +3 -1
  7. package/dist/components/contact-list.js +20 -3
  8. package/dist/components/contact-list.js.map +1 -1
  9. package/dist/components/data-table-condition-filter.d.ts +37 -0
  10. package/dist/components/data-table-condition-filter.js +407 -0
  11. package/dist/components/data-table-condition-filter.js.map +1 -0
  12. package/dist/components/data-table-filter.d.ts +19 -2
  13. package/dist/components/data-table-filter.js +160 -13
  14. package/dist/components/data-table-filter.js.map +1 -1
  15. package/dist/components/data-table-toolbar.d.ts +1 -0
  16. package/dist/components/data-table.d.ts +1 -0
  17. package/dist/components/entity-panel.js +1 -1
  18. package/dist/components/entity-panel.js.map +1 -1
  19. package/dist/components/tabs.d.ts +1 -1
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/prototype/index.d.ts +1 -0
  24. package/dist/prototype/prototype-accounts-view.d.ts +1 -0
  25. package/dist/prototype/prototype-admin-view.d.ts +1 -0
  26. package/dist/prototype/prototype-config.d.ts +1 -0
  27. package/dist/prototype/prototype-inbox-view.d.ts +4 -1
  28. package/dist/prototype/prototype-inbox-view.js +6 -1
  29. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  30. package/dist/prototype/prototype-insights-view.d.ts +1 -0
  31. package/dist/prototype/prototype-shell.d.ts +1 -0
  32. package/package.json +1 -1
  33. package/src/components/__tests__/collapsible-section.test.tsx +143 -0
  34. package/src/components/__tests__/contact-list.test.tsx +116 -0
  35. package/src/components/__tests__/data-table-condition-filter.test.tsx +397 -0
  36. package/src/components/__tests__/data-table-filter-presets.test.tsx +209 -0
  37. package/src/components/__tests__/data-table-filter.test.tsx +270 -3
  38. package/src/components/__tests__/entity-metadata-grid.test.tsx +25 -0
  39. package/src/components/__tests__/virtualized-data-table.test.tsx +0 -1
  40. package/src/components/collapsible-section.tsx +62 -0
  41. package/src/components/contact-list.tsx +22 -3
  42. package/src/components/data-table-condition-filter.tsx +513 -0
  43. package/src/components/data-table-filter.tsx +201 -13
  44. package/src/components/entity-panel.tsx +1 -1
  45. package/src/index.ts +2 -0
  46. package/src/prototype/__tests__/detail-view-attention.test.tsx +101 -0
  47. package/src/prototype/prototype-inbox-view.tsx +8 -0
@@ -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?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | 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?: "default" | "secondary" | "destructive" | "outline" | "ghost" | "link" | null | undefined;
6
+ variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | 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> & {
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ interface CollapsibleSectionProps {
4
+ /** Total number of items (used in the expansion bar label). */
5
+ count: number;
6
+ /** Items to show before collapsing. Default: 5. */
7
+ maxItems?: number;
8
+ /** Children to render — the component slices React.Children.toArray(children) at maxItems. */
9
+ children: React.ReactNode;
10
+ /** Start expanded. Default: false. */
11
+ defaultExpanded?: boolean;
12
+ /** Custom label for the expansion bar. Default: "Show all {count}". */
13
+ expandLabel?: string;
14
+ /** Custom label when expanded. Default: "Show less". */
15
+ collapseLabel?: string;
16
+ className?: string;
17
+ }
18
+ declare function CollapsibleSection({ count, maxItems, children, defaultExpanded, expandLabel, collapseLabel, className, }: CollapsibleSectionProps): React.JSX.Element;
19
+
20
+ export { CollapsibleSection, type CollapsibleSectionProps };
@@ -0,0 +1,48 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import * as React from "react";
6
+ import { ChevronDown } from "lucide-react";
7
+ import { cn } from "../lib/utils.js";
8
+ function CollapsibleSection({
9
+ count,
10
+ maxItems = 5,
11
+ children,
12
+ defaultExpanded = false,
13
+ expandLabel,
14
+ collapseLabel,
15
+ className
16
+ }) {
17
+ const [expanded, setExpanded] = React.useState(defaultExpanded);
18
+ const items = React.Children.toArray(children);
19
+ const visible = expanded ? items : items.slice(0, maxItems);
20
+ const showBar = items.length > maxItems;
21
+ return /* @__PURE__ */ jsxs("div", { className, children: [
22
+ visible,
23
+ showBar && /* @__PURE__ */ jsxs(
24
+ "button",
25
+ {
26
+ type: "button",
27
+ onClick: () => setExpanded(!expanded),
28
+ className: "flex items-center justify-between w-full px-3 py-1.5 mt-1 text-xs font-medium text-muted-foreground border border-border/50 rounded-md hover:bg-muted/30 transition-colors cursor-pointer",
29
+ children: [
30
+ /* @__PURE__ */ jsx("span", { children: expanded ? collapseLabel != null ? collapseLabel : "Show less" : expandLabel != null ? expandLabel : `Show all ${count}` }),
31
+ /* @__PURE__ */ jsx(
32
+ ChevronDown,
33
+ {
34
+ className: cn(
35
+ "h-3.5 w-3.5 transition-transform duration-200",
36
+ expanded && "rotate-180"
37
+ )
38
+ }
39
+ )
40
+ ]
41
+ }
42
+ )
43
+ ] });
44
+ }
45
+ export {
46
+ CollapsibleSection
47
+ };
48
+ //# sourceMappingURL=collapsible-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/collapsible-section.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface CollapsibleSectionProps {\n /** Total number of items (used in the expansion bar label). */\n count: number\n /** Items to show before collapsing. Default: 5. */\n maxItems?: number\n /** Children to render — the component slices React.Children.toArray(children) at maxItems. */\n children: React.ReactNode\n /** Start expanded. Default: false. */\n defaultExpanded?: boolean\n /** Custom label for the expansion bar. Default: \"Show all {count}\". */\n expandLabel?: string\n /** Custom label when expanded. Default: \"Show less\". */\n collapseLabel?: string\n className?: string\n}\n\nexport function CollapsibleSection({\n count,\n maxItems = 5,\n children,\n defaultExpanded = false,\n expandLabel,\n collapseLabel,\n className,\n}: CollapsibleSectionProps) {\n const [expanded, setExpanded] = React.useState(defaultExpanded)\n\n const items = React.Children.toArray(children)\n const visible = expanded ? items : items.slice(0, maxItems)\n const showBar = items.length > maxItems\n\n return (\n <div className={className}>\n {visible}\n {showBar && (\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex items-center justify-between w-full px-3 py-1.5 mt-1 text-xs font-medium text-muted-foreground border border-border/50 rounded-md hover:bg-muted/30 transition-colors cursor-pointer\"\n >\n <span>\n {expanded\n ? (collapseLabel ?? \"Show less\")\n : (expandLabel ?? `Show all ${count}`)}\n </span>\n <ChevronDown\n className={cn(\n \"h-3.5 w-3.5 transition-transform duration-200\",\n expanded && \"rotate-180\"\n )}\n />\n </button>\n )}\n </div>\n )\n}\n"],"mappings":";AAyCQ,SAKE,KALF;AAvCR,YAAY,WAAW;AACvB,SAAS,mBAAmB;AAC5B,SAAS,UAAU;AAkBZ,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,eAAe;AAE9D,QAAM,QAAQ,MAAM,SAAS,QAAQ,QAAQ;AAC7C,QAAM,UAAU,WAAW,QAAQ,MAAM,MAAM,GAAG,QAAQ;AAC1D,QAAM,UAAU,MAAM,SAAS;AAE/B,SACE,qBAAC,SAAI,WACF;AAAA;AAAA,IACA,WACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,CAAC,QAAQ;AAAA,QACpC,WAAU;AAAA,QAEV;AAAA,8BAAC,UACE,qBACI,wCAAiB,cACjB,oCAAe,YAAY,KAAK,IACvC;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,YAAY;AAAA,cACd;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -28,7 +28,9 @@ interface ContactListProps {
28
28
  contacts: ContactItem[];
29
29
  onAdd?: () => void;
30
30
  addLabel?: string;
31
+ /** Maximum contacts to show before collapsing. Shows expansion bar when exceeded. Undefined = show all (backward compatible). */
32
+ maxItems?: number;
31
33
  }
32
- declare function ContactList({ title, count, contacts, onAdd, addLabel }: ContactListProps): React.JSX.Element;
34
+ declare function ContactList({ title, count, contacts, onAdd, addLabel, maxItems }: ContactListProps): React.JSX.Element;
33
35
 
34
36
  export { type ContactChannel, type ContactItem, ContactList, type ContactListProps };
@@ -2,7 +2,9 @@
2
2
 
3
3
  "use client";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { Plus, X } from "lucide-react";
5
+ import * as React from "react";
6
+ import { ChevronDown, Plus, X } from "lucide-react";
7
+ import { cn } from "../lib/utils.js";
6
8
  import { Badge } from "./badge.js";
7
9
  import { Button } from "./button.js";
8
10
  const badgeColors = {
@@ -62,7 +64,10 @@ function ContactRow({ contact }) {
62
64
  ] })
63
65
  ] });
64
66
  }
65
- function ContactList({ title, count, contacts, onAdd, addLabel }) {
67
+ function ContactList({ title, count, contacts, onAdd, addLabel, maxItems }) {
68
+ const [expanded, setExpanded] = React.useState(false);
69
+ const visibleContacts = maxItems != null && !expanded ? contacts.slice(0, maxItems) : contacts;
70
+ const showExpansionBar = maxItems != null && contacts.length > maxItems;
66
71
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2.5", children: [
67
72
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
68
73
  /* @__PURE__ */ jsx("h3", { className: "text-[13px] font-semibold text-foreground", children: title }),
@@ -75,7 +80,19 @@ function ContactList({ title, count, contacts, onAdd, addLabel }) {
75
80
  ] })
76
81
  ] })
77
82
  ] }),
78
- /* @__PURE__ */ jsx("div", { className: "space-y-0", children: contacts.map((contact) => /* @__PURE__ */ jsx(ContactRow, { contact }, contact.id)) })
83
+ /* @__PURE__ */ jsx("div", { className: "space-y-0", children: visibleContacts.map((contact) => /* @__PURE__ */ jsx(ContactRow, { contact }, contact.id)) }),
84
+ showExpansionBar && /* @__PURE__ */ jsxs(
85
+ "button",
86
+ {
87
+ type: "button",
88
+ onClick: () => setExpanded(!expanded),
89
+ className: "flex items-center justify-between w-full px-3 py-1.5 mt-1 text-xs font-medium text-muted-foreground border border-border/50 rounded-md hover:bg-muted/30 transition-colors",
90
+ children: [
91
+ /* @__PURE__ */ jsx("span", { children: expanded ? "Show less" : `Show all ${contacts.length} contacts` }),
92
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3.5 w-3.5 transition-transform duration-200", expanded && "rotate-180") })
93
+ ]
94
+ }
95
+ )
79
96
  ] });
80
97
  }
81
98
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/contact-list.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Plus, X } from \"lucide-react\"\nimport { Badge } from \"./badge\"\nimport { Button } from \"./button\"\n\nexport interface ContactChannel {\n type: \"linkedin\" | \"gmail\" | \"salesforce\" | \"phone\" | \"custom\"\n icon: React.ReactNode\n label?: string\n onClick?: () => void\n}\n\nexport interface ContactItem {\n id: string\n name: string\n role: string\n badge?: {\n label: string\n color?: \"indigo\" | \"green\" | \"amber\" | \"red\" | \"muted\"\n }\n channels?: ContactChannel[]\n action?: {\n label: string\n onClick?: () => void\n }\n description?: string\n onDismiss?: () => void\n}\n\nexport interface ContactListProps {\n title?: string\n count?: string\n contacts: ContactItem[]\n onAdd?: () => void\n addLabel?: string\n}\n\nconst badgeColors: Record<string, string> = {\n indigo: \"bg-indigo-50 text-indigo-700 border-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-300 dark:border-indigo-800\",\n green: \"bg-green-50 text-green-700 border-green-200 dark:bg-green-900/30 dark:text-green-300 dark:border-green-800\",\n amber: \"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:border-amber-800\",\n red: \"bg-red-50 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800\",\n muted: \"bg-muted text-muted-foreground border-border\",\n}\n\nfunction ContactRow({ contact }: { contact: ContactItem }) {\n return (\n <div className=\"flex items-center justify-between gap-3 group py-2 border-b border-border/30 last:border-0 hover:bg-muted/20 -mx-2 px-2 rounded-sm transition-colors\">\n <div className=\"flex items-center gap-2.5 min-w-0\">\n {contact.badge && (\n <Badge\n variant=\"outline\"\n className={`shadow-none px-2 py-0 text-[11px] font-medium shrink-0 ${badgeColors[contact.badge.color ?? \"muted\"]}`}\n >\n {contact.badge.label}\n </Badge>\n )}\n <span className=\"font-medium text-[13px] text-foreground truncate\">{contact.name}</span>\n <span className=\"text-muted-foreground text-[13px] shrink-0\">&middot;</span>\n <span className=\"text-muted-foreground text-[13px] truncate\">{contact.role}</span>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n {contact.channels?.map((ch, i) => (\n <button\n key={i}\n onClick={ch.onClick}\n className=\"h-7 w-7 flex items-center justify-center hover:bg-muted rounded-md transition-colors\"\n title={ch.label}\n >\n {ch.icon}\n </button>\n ))}\n {contact.action && (\n <Button\n size=\"sm\"\n className=\"bg-foreground text-background hover:bg-foreground/90 h-6 text-[11px] font-medium shadow-none ml-1\"\n onClick={contact.action.onClick}\n >\n <Plus className=\"w-3 h-3 mr-1\" />\n {contact.action.label}\n </Button>\n )}\n {contact.onDismiss && (\n <button\n onClick={contact.onDismiss}\n className=\"h-6 w-6 flex items-center justify-center text-muted-foreground/40 hover:text-foreground hover:bg-muted rounded-md transition-colors opacity-0 group-hover:opacity-100\"\n >\n <X className=\"w-3 h-3\" />\n </button>\n )}\n </div>\n </div>\n )\n}\n\nexport function ContactList({ title, count, contacts, onAdd, addLabel }: ContactListProps) {\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-[13px] font-semibold text-foreground\">{title}</h3>\n <div className=\"flex items-center gap-3\">\n {count && <span className=\"text-xs text-muted-foreground\">{count}</span>}\n {onAdd && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onAdd} className=\"h-7 text-xs font-medium hover:bg-muted/50\">\n <Plus className=\"w-3.5 h-3.5 mr-1\" /> {addLabel ?? \"Add\"}\n </Button>\n )}\n </div>\n </div>\n\n <div className=\"space-y-0\">\n {contacts.map((contact) => (\n <ContactRow key={contact.id} contact={contact} />\n ))}\n </div>\n </div>\n )\n}\n"],"mappings":";AAkDM,SAEI,KAFJ;AA/CN,SAAS,MAAM,SAAS;AACxB,SAAS,aAAa;AACtB,SAAS,cAAc;AAkCvB,MAAM,cAAsC;AAAA,EAC1C,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AACT;AAEA,SAAS,WAAW,EAAE,QAAQ,GAA6B;AA/C3D;AAgDE,SACE,qBAAC,SAAI,WAAU,wJACb;AAAA,yBAAC,SAAI,WAAU,qCACZ;AAAA,cAAQ,SACP;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,WAAW,0DAA0D,aAAY,aAAQ,MAAM,UAAd,YAAuB,OAAO,CAAC;AAAA,UAE/G,kBAAQ,MAAM;AAAA;AAAA,MACjB;AAAA,MAEF,oBAAC,UAAK,WAAU,oDAAoD,kBAAQ,MAAK;AAAA,MACjF,oBAAC,UAAK,WAAU,8CAA6C,kBAAQ;AAAA,MACrE,oBAAC,UAAK,WAAU,8CAA8C,kBAAQ,MAAK;AAAA,OAC7E;AAAA,IAEA,qBAAC,SAAI,WAAU,oCACZ;AAAA,oBAAQ,aAAR,mBAAkB,IAAI,CAAC,IAAI,MAC1B;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,GAAG;AAAA,UACZ,WAAU;AAAA,UACV,OAAO,GAAG;AAAA,UAET,aAAG;AAAA;AAAA,QALC;AAAA,MAMP;AAAA,MAED,QAAQ,UACP;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,QAAQ,OAAO;AAAA,UAExB;AAAA,gCAAC,QAAK,WAAU,gBAAe;AAAA,YAC9B,QAAQ,OAAO;AAAA;AAAA;AAAA,MAClB;AAAA,MAED,QAAQ,aACP;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,QAAQ;AAAA,UACjB,WAAU;AAAA,UAEV,8BAAC,KAAE,WAAU,WAAU;AAAA;AAAA,MACzB;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEO,SAAS,YAAY,EAAE,OAAO,OAAO,UAAU,OAAO,SAAS,GAAqB;AACzF,SACE,qBAAC,SAAI,WAAU,eACb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,0BAAC,QAAG,WAAU,6CAA6C,iBAAM;AAAA,MACjE,qBAAC,SAAI,WAAU,2BACZ;AAAA,iBAAS,oBAAC,UAAK,WAAU,iCAAiC,iBAAM;AAAA,QAChE,SACC,qBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,OAAO,WAAU,6CAC1D;AAAA,8BAAC,QAAK,WAAU,oBAAmB;AAAA,UAAE;AAAA,UAAE,8BAAY;AAAA,WACrD;AAAA,SAEJ;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAU,aACZ,mBAAS,IAAI,CAAC,YACb,oBAAC,cAA4B,WAAZ,QAAQ,EAAsB,CAChD,GACH;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/contact-list.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown, Plus, X } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\nimport { Badge } from \"./badge\"\nimport { Button } from \"./button\"\n\nexport interface ContactChannel {\n type: \"linkedin\" | \"gmail\" | \"salesforce\" | \"phone\" | \"custom\"\n icon: React.ReactNode\n label?: string\n onClick?: () => void\n}\n\nexport interface ContactItem {\n id: string\n name: string\n role: string\n badge?: {\n label: string\n color?: \"indigo\" | \"green\" | \"amber\" | \"red\" | \"muted\"\n }\n channels?: ContactChannel[]\n action?: {\n label: string\n onClick?: () => void\n }\n description?: string\n onDismiss?: () => void\n}\n\nexport interface ContactListProps {\n title?: string\n count?: string\n contacts: ContactItem[]\n onAdd?: () => void\n addLabel?: string\n /** Maximum contacts to show before collapsing. Shows expansion bar when exceeded. Undefined = show all (backward compatible). */\n maxItems?: number\n}\n\nconst badgeColors: Record<string, string> = {\n indigo: \"bg-indigo-50 text-indigo-700 border-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-300 dark:border-indigo-800\",\n green: \"bg-green-50 text-green-700 border-green-200 dark:bg-green-900/30 dark:text-green-300 dark:border-green-800\",\n amber: \"bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/30 dark:text-amber-300 dark:border-amber-800\",\n red: \"bg-red-50 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800\",\n muted: \"bg-muted text-muted-foreground border-border\",\n}\n\nfunction ContactRow({ contact }: { contact: ContactItem }) {\n return (\n <div className=\"flex items-center justify-between gap-3 group py-2 border-b border-border/30 last:border-0 hover:bg-muted/20 -mx-2 px-2 rounded-sm transition-colors\">\n <div className=\"flex items-center gap-2.5 min-w-0\">\n {contact.badge && (\n <Badge\n variant=\"outline\"\n className={`shadow-none px-2 py-0 text-[11px] font-medium shrink-0 ${badgeColors[contact.badge.color ?? \"muted\"]}`}\n >\n {contact.badge.label}\n </Badge>\n )}\n <span className=\"font-medium text-[13px] text-foreground truncate\">{contact.name}</span>\n <span className=\"text-muted-foreground text-[13px] shrink-0\">&middot;</span>\n <span className=\"text-muted-foreground text-[13px] truncate\">{contact.role}</span>\n </div>\n\n <div className=\"flex items-center gap-1 shrink-0\">\n {contact.channels?.map((ch, i) => (\n <button\n key={i}\n onClick={ch.onClick}\n className=\"h-7 w-7 flex items-center justify-center hover:bg-muted rounded-md transition-colors\"\n title={ch.label}\n >\n {ch.icon}\n </button>\n ))}\n {contact.action && (\n <Button\n size=\"sm\"\n className=\"bg-foreground text-background hover:bg-foreground/90 h-6 text-[11px] font-medium shadow-none ml-1\"\n onClick={contact.action.onClick}\n >\n <Plus className=\"w-3 h-3 mr-1\" />\n {contact.action.label}\n </Button>\n )}\n {contact.onDismiss && (\n <button\n onClick={contact.onDismiss}\n className=\"h-6 w-6 flex items-center justify-center text-muted-foreground/40 hover:text-foreground hover:bg-muted rounded-md transition-colors opacity-0 group-hover:opacity-100\"\n >\n <X className=\"w-3 h-3\" />\n </button>\n )}\n </div>\n </div>\n )\n}\n\nexport function ContactList({ title, count, contacts, onAdd, addLabel, maxItems }: ContactListProps) {\n const [expanded, setExpanded] = React.useState(false)\n\n const visibleContacts = maxItems != null && !expanded ? contacts.slice(0, maxItems) : contacts\n const showExpansionBar = maxItems != null && contacts.length > maxItems\n\n return (\n <div className=\"space-y-2.5\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"text-[13px] font-semibold text-foreground\">{title}</h3>\n <div className=\"flex items-center gap-3\">\n {count && <span className=\"text-xs text-muted-foreground\">{count}</span>}\n {onAdd && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onAdd} className=\"h-7 text-xs font-medium hover:bg-muted/50\">\n <Plus className=\"w-3.5 h-3.5 mr-1\" /> {addLabel ?? \"Add\"}\n </Button>\n )}\n </div>\n </div>\n\n <div className=\"space-y-0\">\n {visibleContacts.map((contact) => (\n <ContactRow key={contact.id} contact={contact} />\n ))}\n </div>\n\n {showExpansionBar && (\n <button\n type=\"button\"\n onClick={() => setExpanded(!expanded)}\n className=\"flex items-center justify-between w-full px-3 py-1.5 mt-1 text-xs font-medium text-muted-foreground border border-border/50 rounded-md hover:bg-muted/30 transition-colors\"\n >\n <span>{expanded ? \"Show less\" : `Show all ${contacts.length} contacts`}</span>\n <ChevronDown className={cn(\"h-3.5 w-3.5 transition-transform duration-200\", expanded && \"rotate-180\")} />\n </button>\n )}\n </div>\n )\n}\n"],"mappings":";AAqDM,SAEI,KAFJ;AAnDN,YAAY,WAAW;AACvB,SAAS,aAAa,MAAM,SAAS;AACrC,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,cAAc;AAoCvB,MAAM,cAAsC;AAAA,EAC1C,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AACT;AAEA,SAAS,WAAW,EAAE,QAAQ,GAA6B;AAlD3D;AAmDE,SACE,qBAAC,SAAI,WAAU,wJACb;AAAA,yBAAC,SAAI,WAAU,qCACZ;AAAA,cAAQ,SACP;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,WAAW,0DAA0D,aAAY,aAAQ,MAAM,UAAd,YAAuB,OAAO,CAAC;AAAA,UAE/G,kBAAQ,MAAM;AAAA;AAAA,MACjB;AAAA,MAEF,oBAAC,UAAK,WAAU,oDAAoD,kBAAQ,MAAK;AAAA,MACjF,oBAAC,UAAK,WAAU,8CAA6C,kBAAQ;AAAA,MACrE,oBAAC,UAAK,WAAU,8CAA8C,kBAAQ,MAAK;AAAA,OAC7E;AAAA,IAEA,qBAAC,SAAI,WAAU,oCACZ;AAAA,oBAAQ,aAAR,mBAAkB,IAAI,CAAC,IAAI,MAC1B;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,GAAG;AAAA,UACZ,WAAU;AAAA,UACV,OAAO,GAAG;AAAA,UAET,aAAG;AAAA;AAAA,QALC;AAAA,MAMP;AAAA,MAED,QAAQ,UACP;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,QAAQ,OAAO;AAAA,UAExB;AAAA,gCAAC,QAAK,WAAU,gBAAe;AAAA,YAC9B,QAAQ,OAAO;AAAA;AAAA;AAAA,MAClB;AAAA,MAED,QAAQ,aACP;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,QAAQ;AAAA,UACjB,WAAU;AAAA,UAEV,8BAAC,KAAE,WAAU,WAAU;AAAA;AAAA,MACzB;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEO,SAAS,YAAY,EAAE,OAAO,OAAO,UAAU,OAAO,UAAU,SAAS,GAAqB;AACnG,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AAEpD,QAAM,kBAAkB,YAAY,QAAQ,CAAC,WAAW,SAAS,MAAM,GAAG,QAAQ,IAAI;AACtF,QAAM,mBAAmB,YAAY,QAAQ,SAAS,SAAS;AAE/D,SACE,qBAAC,SAAI,WAAU,eACb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,0BAAC,QAAG,WAAU,6CAA6C,iBAAM;AAAA,MACjE,qBAAC,SAAI,WAAU,2BACZ;AAAA,iBAAS,oBAAC,UAAK,WAAU,iCAAiC,iBAAM;AAAA,QAChE,SACC,qBAAC,UAAO,SAAQ,SAAQ,MAAK,MAAK,SAAS,OAAO,WAAU,6CAC1D;AAAA,8BAAC,QAAK,WAAU,oBAAmB;AAAA,UAAE;AAAA,UAAE,8BAAY;AAAA,WACrD;AAAA,SAEJ;AAAA,OACF;AAAA,IAEA,oBAAC,SAAI,WAAU,aACZ,0BAAgB,IAAI,CAAC,YACpB,oBAAC,cAA4B,WAAZ,QAAQ,EAAsB,CAChD,GACH;AAAA,IAEC,oBACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,CAAC,QAAQ;AAAA,QACpC,WAAU;AAAA,QAEV;AAAA,8BAAC,UAAM,qBAAW,cAAc,YAAY,SAAS,MAAM,aAAY;AAAA,UACvE,oBAAC,eAAY,WAAW,GAAG,iDAAiD,YAAY,YAAY,GAAG;AAAA;AAAA;AAAA,IACzG;AAAA,KAEJ;AAEJ;","names":[]}
@@ -0,0 +1,37 @@
1
+ import * as React from 'react';
2
+
3
+ type ConditionOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "is_null" | "is_not_null";
4
+ interface ConditionFieldDef {
5
+ /** Unique field key (e.g., "Account_Balance__c") */
6
+ id: string;
7
+ /** Display label (e.g., "Account Balance") */
8
+ label: string;
9
+ /** Field data type — determines which operators are available and how the value input renders */
10
+ type: "text" | "number" | "currency" | "date";
11
+ /** Allowed operators for this field. Defaults based on type if not provided. */
12
+ operators?: ConditionOperator[];
13
+ }
14
+ interface ConditionFilterValue {
15
+ /** Stable identity — used as React key to avoid stale-state bugs on removal */
16
+ id: string;
17
+ field: string;
18
+ operator: ConditionOperator;
19
+ value: string | number | null;
20
+ }
21
+ interface DataTableConditionFilterProps {
22
+ /** Available fields the user can filter on */
23
+ fields: ConditionFieldDef[];
24
+ /** Current active conditions */
25
+ conditions: ConditionFilterValue[];
26
+ /** Called when conditions change (add, update, remove) */
27
+ onConditionsChange: (conditions: ConditionFilterValue[]) => void;
28
+ className?: string;
29
+ }
30
+ declare const OPERATOR_LABELS: Record<ConditionOperator, string>;
31
+ declare const DEFAULT_OPERATORS: Record<ConditionFieldDef["type"], ConditionOperator[]>;
32
+ /** Generate a stable unique ID for a new condition row. */
33
+ declare function generateConditionId(): string;
34
+ declare function getOperators(field: ConditionFieldDef): ConditionOperator[];
35
+ declare function DataTableConditionFilter({ fields, conditions, onConditionsChange, className, }: DataTableConditionFilterProps): React.JSX.Element;
36
+
37
+ export { type ConditionFieldDef, type ConditionFilterValue, type ConditionOperator, DEFAULT_OPERATORS, DataTableConditionFilter, type DataTableConditionFilterProps, OPERATOR_LABELS, generateConditionId, getOperators };
@@ -0,0 +1,407 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ import { jsx, jsxs } from "react/jsx-runtime";
24
+ import * as React from "react";
25
+ import {
26
+ CalendarDays,
27
+ DollarSign,
28
+ Eye,
29
+ Hash,
30
+ MoreHorizontal,
31
+ Plus,
32
+ Trash2,
33
+ Type
34
+ } from "lucide-react";
35
+ import { cn } from "../lib/utils.js";
36
+ import { Button } from "./button.js";
37
+ import { Input } from "./input.js";
38
+ import {
39
+ Select,
40
+ SelectContent,
41
+ SelectItem,
42
+ SelectTrigger,
43
+ SelectValue
44
+ } from "./select.js";
45
+ const OPERATOR_LABELS = {
46
+ eq: "=",
47
+ neq: "\u2260",
48
+ gt: ">",
49
+ gte: "\u2265",
50
+ lt: "<",
51
+ lte: "\u2264",
52
+ in: "contains",
53
+ is_null: "is empty",
54
+ is_not_null: "is not empty"
55
+ };
56
+ const NUMERIC_OPERATORS = [
57
+ "eq",
58
+ "neq",
59
+ "gt",
60
+ "gte",
61
+ "lt",
62
+ "lte",
63
+ "is_null",
64
+ "is_not_null"
65
+ ];
66
+ const DEFAULT_OPERATORS = {
67
+ text: ["eq", "neq", "is_null", "is_not_null"],
68
+ number: NUMERIC_OPERATORS,
69
+ currency: NUMERIC_OPERATORS,
70
+ date: ["gt", "gte", "lt", "lte", "eq", "neq", "is_null", "is_not_null"]
71
+ };
72
+ function generateConditionId() {
73
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
74
+ return crypto.randomUUID();
75
+ }
76
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
77
+ }
78
+ function getOperators(field) {
79
+ var _a;
80
+ return ((_a = field.operators) != null ? _a : DEFAULT_OPERATORS[field.type]).filter((op) => op !== "in");
81
+ }
82
+ function isUnaryOperator(op) {
83
+ return op === "is_null" || op === "is_not_null";
84
+ }
85
+ function getDefaultOperator(field) {
86
+ var _a;
87
+ return (_a = getOperators(field)[0]) != null ? _a : "eq";
88
+ }
89
+ function createDraftCondition(field) {
90
+ return {
91
+ id: generateConditionId(),
92
+ field: field.id,
93
+ operator: getDefaultOperator(field),
94
+ value: null
95
+ };
96
+ }
97
+ function normalizeCondition(condition, fields) {
98
+ var _a;
99
+ const field = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
100
+ if (!field) return condition;
101
+ const operators = getOperators(field);
102
+ const operator = operators.includes(condition.operator) ? condition.operator : getDefaultOperator(field);
103
+ return __spreadProps(__spreadValues({}, condition), {
104
+ field: field.id,
105
+ operator,
106
+ value: isUnaryOperator(operator) ? null : condition.value
107
+ });
108
+ }
109
+ function parseConditionValue(raw, fieldType) {
110
+ if (raw === "") return null;
111
+ if (fieldType === "number" || fieldType === "currency") {
112
+ const parsed = Number(raw);
113
+ return Number.isNaN(parsed) ? null : parsed;
114
+ }
115
+ return raw;
116
+ }
117
+ function isCompleteCondition(condition, fields) {
118
+ const field = fields.find((f) => f.id === condition.field);
119
+ if (!field) return false;
120
+ if (!getOperators(field).includes(condition.operator)) return false;
121
+ if (isUnaryOperator(condition.operator)) return true;
122
+ return condition.value !== null && condition.value !== "";
123
+ }
124
+ function getFieldIcon(type) {
125
+ if (type === "currency") return DollarSign;
126
+ if (type === "number") return Hash;
127
+ if (type === "date") return CalendarDays;
128
+ return Type;
129
+ }
130
+ function ConditionRow({
131
+ condition,
132
+ fields,
133
+ index,
134
+ onChange,
135
+ onRemove,
136
+ onCommit
137
+ }) {
138
+ var _a;
139
+ const fieldDef = (_a = fields.find((f) => f.id === condition.field)) != null ? _a : fields[0];
140
+ const operators = getOperators(fieldDef);
141
+ const isUnary = isUnaryOperator(condition.operator);
142
+ const FieldIcon = getFieldIcon(fieldDef.type);
143
+ const handleFieldChange = (newFieldId) => {
144
+ var _a2;
145
+ const newFieldDef = (_a2 = fields.find((f) => f.id === newFieldId)) != null ? _a2 : fields[0];
146
+ if (!newFieldDef) return;
147
+ onChange(__spreadProps(__spreadValues({}, condition), {
148
+ field: newFieldDef.id,
149
+ operator: getDefaultOperator(newFieldDef),
150
+ value: null
151
+ }));
152
+ };
153
+ const handleOperatorChange = (newOp) => {
154
+ onChange(__spreadProps(__spreadValues({}, condition), {
155
+ operator: newOp,
156
+ value: isUnaryOperator(newOp) ? null : condition.value
157
+ }));
158
+ };
159
+ const handleValueChange = (event) => {
160
+ onChange(__spreadProps(__spreadValues({}, condition), {
161
+ value: parseConditionValue(event.target.value, fieldDef.type)
162
+ }));
163
+ };
164
+ return /* @__PURE__ */ jsxs(
165
+ "div",
166
+ {
167
+ 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",
168
+ "data-slot": "condition-row",
169
+ children: [
170
+ /* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-muted-foreground", children: index === 0 ? "Where" : "And" }),
171
+ /* @__PURE__ */ jsxs(Select, { value: condition.field, onValueChange: handleFieldChange, children: [
172
+ /* @__PURE__ */ jsxs(SelectTrigger, { className: "h-8 w-full justify-start gap-2", size: "sm", children: [
173
+ /* @__PURE__ */ jsx(FieldIcon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
174
+ /* @__PURE__ */ jsx(SelectValue, { placeholder: fieldDef.label })
175
+ ] }),
176
+ /* @__PURE__ */ jsx(SelectContent, { children: fields.map((field) => {
177
+ const Icon = getFieldIcon(field.type);
178
+ return /* @__PURE__ */ jsx(SelectItem, { value: field.id, children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
179
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 text-muted-foreground" }),
180
+ field.label
181
+ ] }) }, field.id);
182
+ }) })
183
+ ] }),
184
+ /* @__PURE__ */ jsxs(
185
+ Select,
186
+ {
187
+ value: condition.operator,
188
+ onValueChange: (value) => handleOperatorChange(value),
189
+ children: [
190
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-full", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Operator" }) }),
191
+ /* @__PURE__ */ jsx(SelectContent, { children: operators.map((op) => /* @__PURE__ */ jsx(SelectItem, { value: op, children: OPERATOR_LABELS[op] }, op)) })
192
+ ]
193
+ }
194
+ ),
195
+ 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__ */ jsxs("div", { className: "relative flex items-center", children: [
196
+ fieldDef.type === "currency" ? /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-2 text-sm text-muted-foreground", children: "$" }) : null,
197
+ /* @__PURE__ */ jsx(
198
+ Input,
199
+ {
200
+ type: fieldDef.type === "number" || fieldDef.type === "currency" ? "number" : fieldDef.type === "date" ? "date" : "text",
201
+ value: condition.value != null ? String(condition.value) : "",
202
+ onChange: handleValueChange,
203
+ onClick: (event) => event.stopPropagation(),
204
+ onKeyDown: (event) => {
205
+ event.stopPropagation();
206
+ if (event.key === "Enter") {
207
+ onCommit();
208
+ }
209
+ },
210
+ placeholder: fieldDef.type === "currency" ? "Amount" : fieldDef.type === "number" ? "Enter number..." : fieldDef.type === "date" ? "" : "Enter value...",
211
+ className: cn("h-8", fieldDef.type === "currency" && "pl-6")
212
+ }
213
+ )
214
+ ] }),
215
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
216
+ /* @__PURE__ */ jsx(
217
+ Button,
218
+ {
219
+ type: "button",
220
+ variant: "ghost",
221
+ size: "icon",
222
+ className: "h-8 w-8 text-muted-foreground",
223
+ "aria-label": "Toggle condition visibility",
224
+ onClick: (event) => event.preventDefault(),
225
+ children: /* @__PURE__ */ jsx(Eye, { className: "size-4" })
226
+ }
227
+ ),
228
+ /* @__PURE__ */ jsx(
229
+ Button,
230
+ {
231
+ type: "button",
232
+ variant: "ghost",
233
+ size: "icon",
234
+ className: "h-8 w-8 text-muted-foreground",
235
+ "aria-label": "More condition actions",
236
+ onClick: (event) => event.preventDefault(),
237
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" })
238
+ }
239
+ ),
240
+ /* @__PURE__ */ jsx(
241
+ Button,
242
+ {
243
+ type: "button",
244
+ variant: "ghost",
245
+ size: "icon",
246
+ className: "h-8 w-8 text-muted-foreground hover:text-destructive",
247
+ onClick: onRemove,
248
+ "aria-label": "Remove condition",
249
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-4" })
250
+ }
251
+ )
252
+ ] })
253
+ ]
254
+ }
255
+ );
256
+ }
257
+ function DataTableConditionFilter({
258
+ fields,
259
+ conditions,
260
+ onConditionsChange,
261
+ className
262
+ }) {
263
+ const [drafts, setDrafts] = React.useState(
264
+ () => conditions.map((condition) => normalizeCondition(condition, fields))
265
+ );
266
+ React.useEffect(() => {
267
+ setDrafts(conditions.map((condition) => normalizeCondition(condition, fields)));
268
+ }, [conditions, fields]);
269
+ const commitDrafts = React.useCallback(
270
+ (nextDrafts = drafts) => {
271
+ onConditionsChange(
272
+ nextDrafts.map((condition) => normalizeCondition(condition, fields)).filter((condition) => isCompleteCondition(condition, fields))
273
+ );
274
+ },
275
+ [drafts, fields, onConditionsChange]
276
+ );
277
+ const handleAdd = () => {
278
+ const firstField = fields[0];
279
+ if (!firstField) return;
280
+ const committedDrafts = drafts.filter(
281
+ (condition) => isCompleteCondition(condition, fields)
282
+ );
283
+ const nextDrafts = [...committedDrafts, createDraftCondition(firstField)];
284
+ setDrafts(nextDrafts);
285
+ commitDrafts(committedDrafts);
286
+ };
287
+ const handleUpdate = (index, updated) => {
288
+ setDrafts((current) => {
289
+ const next = [...current];
290
+ next[index] = normalizeCondition(updated, fields);
291
+ return next;
292
+ });
293
+ };
294
+ const handleRemove = (index) => {
295
+ setDrafts((current) => {
296
+ const next = current.filter((_, currentIndex) => currentIndex !== index);
297
+ commitDrafts(next);
298
+ return next;
299
+ });
300
+ };
301
+ const handleClear = () => {
302
+ setDrafts([]);
303
+ onConditionsChange([]);
304
+ };
305
+ const hasAppliedConditions = conditions.length > 0;
306
+ const hasDrafts = drafts.length > 0;
307
+ return /* @__PURE__ */ jsxs(
308
+ "div",
309
+ {
310
+ className: cn(
311
+ "w-[min(760px,calc(100vw-2rem))] rounded-xl border border-border bg-background p-3 text-foreground shadow-xl",
312
+ className
313
+ ),
314
+ "data-slot": "condition-filter",
315
+ onClick: (event) => event.stopPropagation(),
316
+ onKeyDown: (event) => {
317
+ if (event.key !== "Escape") {
318
+ event.stopPropagation();
319
+ }
320
+ },
321
+ children: [
322
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-3 border-b border-border/70 pb-3", children: [
323
+ /* @__PURE__ */ jsxs("div", { children: [
324
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", children: "Filter builder" }),
325
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: "Build field, operator, and value conditions." })
326
+ ] }),
327
+ /* @__PURE__ */ jsx(
328
+ Button,
329
+ {
330
+ type: "button",
331
+ variant: "ghost",
332
+ size: "sm",
333
+ className: "h-8 text-xs text-destructive hover:text-destructive",
334
+ onClick: handleClear,
335
+ disabled: !hasAppliedConditions && !hasDrafts,
336
+ children: "Clear filters"
337
+ }
338
+ )
339
+ ] }),
340
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: drafts.map((condition, index) => /* @__PURE__ */ jsx(
341
+ ConditionRow,
342
+ {
343
+ condition,
344
+ fields,
345
+ index,
346
+ onChange: (updated) => handleUpdate(index, updated),
347
+ onRemove: () => handleRemove(index),
348
+ onCommit: () => commitDrafts()
349
+ },
350
+ condition.id
351
+ )) }),
352
+ !hasDrafts ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/80 bg-muted/20 px-3 py-5 text-center text-xs text-muted-foreground", children: "No builder filters yet. Add a filter to start a condition row." }) : null,
353
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center justify-between gap-2 border-t border-border/70 pt-3", children: [
354
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
355
+ /* @__PURE__ */ jsxs(
356
+ Button,
357
+ {
358
+ type: "button",
359
+ variant: "ghost",
360
+ size: "sm",
361
+ className: "h-8 text-xs text-muted-foreground",
362
+ onClick: handleAdd,
363
+ children: [
364
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1 size-3.5" }),
365
+ "Add filter"
366
+ ]
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsxs(
370
+ Button,
371
+ {
372
+ type: "button",
373
+ variant: "ghost",
374
+ size: "sm",
375
+ className: "h-8 text-xs text-muted-foreground opacity-60",
376
+ disabled: true,
377
+ "aria-disabled": "true",
378
+ children: [
379
+ /* @__PURE__ */ jsx(Plus, { className: "mr-1 size-3.5" }),
380
+ "Add filter group"
381
+ ]
382
+ }
383
+ )
384
+ ] }),
385
+ /* @__PURE__ */ jsx(
386
+ Button,
387
+ {
388
+ type: "button",
389
+ size: "sm",
390
+ className: "h-8 text-xs",
391
+ onClick: () => commitDrafts(),
392
+ children: "Apply"
393
+ }
394
+ )
395
+ ] })
396
+ ]
397
+ }
398
+ );
399
+ }
400
+ export {
401
+ DEFAULT_OPERATORS,
402
+ DataTableConditionFilter,
403
+ OPERATOR_LABELS,
404
+ generateConditionId,
405
+ getOperators
406
+ };
407
+ //# sourceMappingURL=data-table-condition-filter.js.map