@l3mpire/ui 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5187,6 +5187,130 @@ function getValueInputType(type, operator) {
5187
5187
  return ["is any of", "is none of"].includes(operator) ? "MultiRelationPicker" : "RelationPicker";
5188
5188
  return null;
5189
5189
  }
5190
+ function formatFilterValue(value) {
5191
+ if (value == null) return void 0;
5192
+ if (typeof value === "boolean") return value ? "Yes" : "No";
5193
+ if (value instanceof Date) {
5194
+ return value.toLocaleDateString("en-US", {
5195
+ month: "short",
5196
+ day: "numeric",
5197
+ year: "numeric"
5198
+ });
5199
+ }
5200
+ if (Array.isArray(value)) {
5201
+ if (value.length === 0) return void 0;
5202
+ if (value.length === 2 && typeof value[0] === "number") {
5203
+ return `${value[0]} \u2013 ${value[1]}`;
5204
+ }
5205
+ if (value.length === 2 && value[0] instanceof Date) {
5206
+ const fmt = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
5207
+ return `${fmt(value[0])} \u2013 ${value[1] instanceof Date ? fmt(value[1]) : "\u2026"}`;
5208
+ }
5209
+ return String(value[0]);
5210
+ }
5211
+ return String(value);
5212
+ }
5213
+ function getBadgeCount(value) {
5214
+ if (Array.isArray(value) && value.length > 1 && typeof value[0] === "string") {
5215
+ return value.length;
5216
+ }
5217
+ return void 0;
5218
+ }
5219
+ function isFilterGroup(node) {
5220
+ return node.type === "group";
5221
+ }
5222
+ function createEmptyGroup() {
5223
+ return {
5224
+ id: crypto.randomUUID(),
5225
+ type: "group",
5226
+ children: []
5227
+ };
5228
+ }
5229
+ function duplicateNode(node) {
5230
+ if (isFilterGroup(node)) {
5231
+ return {
5232
+ ...node,
5233
+ id: crypto.randomUUID(),
5234
+ children: node.children.map(duplicateNode)
5235
+ };
5236
+ }
5237
+ return { ...node, id: crypto.randomUUID() };
5238
+ }
5239
+ function wrapInGroup(condition) {
5240
+ return {
5241
+ id: crypto.randomUUID(),
5242
+ type: "group",
5243
+ logicOperator: condition.logicOperator,
5244
+ children: [{ ...condition, logicOperator: void 0 }]
5245
+ };
5246
+ }
5247
+ function unwrapGroup(group) {
5248
+ const first = group.children.find((c) => !isFilterGroup(c));
5249
+ if (!first) return null;
5250
+ return { ...first, logicOperator: group.logicOperator };
5251
+ }
5252
+ function updateNodeInTree(nodes, id, updater) {
5253
+ return nodes.map((node) => {
5254
+ if (node.id === id) return updater(node);
5255
+ if (isFilterGroup(node)) {
5256
+ const updated = updateNodeInTree(node.children, id, updater);
5257
+ if (updated !== node.children) return { ...node, children: updated };
5258
+ }
5259
+ return node;
5260
+ });
5261
+ }
5262
+ function removeNodeFromTree(nodes, id) {
5263
+ const result = [];
5264
+ for (const node of nodes) {
5265
+ if (node.id === id) continue;
5266
+ if (isFilterGroup(node)) {
5267
+ const updated = removeNodeFromTree(node.children, id);
5268
+ result.push(updated !== node.children ? { ...node, children: updated } : node);
5269
+ } else {
5270
+ result.push(node);
5271
+ }
5272
+ }
5273
+ return result;
5274
+ }
5275
+ function insertAfterInTree(nodes, afterId, newNode) {
5276
+ const result = [];
5277
+ let inserted = false;
5278
+ for (const node of nodes) {
5279
+ result.push(node);
5280
+ if (node.id === afterId) {
5281
+ result.push(newNode);
5282
+ inserted = true;
5283
+ } else if (!inserted && isFilterGroup(node)) {
5284
+ const updated = insertAfterInTree(node.children, afterId, newNode);
5285
+ if (updated.length !== node.children.length) {
5286
+ result[result.length - 1] = { ...node, children: updated };
5287
+ inserted = true;
5288
+ }
5289
+ }
5290
+ }
5291
+ return result;
5292
+ }
5293
+ function replaceNodeInTree(nodes, id, replacement) {
5294
+ return nodes.map((node) => {
5295
+ if (node.id === id) return replacement;
5296
+ if (isFilterGroup(node)) {
5297
+ const updated = replaceNodeInTree(node.children, id, replacement);
5298
+ if (updated !== node.children) return { ...node, children: updated };
5299
+ }
5300
+ return node;
5301
+ });
5302
+ }
5303
+ function countConditions(nodes) {
5304
+ let count = 0;
5305
+ for (const node of nodes) {
5306
+ if (isFilterGroup(node)) {
5307
+ count += countConditions(node.children);
5308
+ } else {
5309
+ count++;
5310
+ }
5311
+ }
5312
+ return count;
5313
+ }
5190
5314
 
5191
5315
  // src/components/ui/filter/filter-bar.tsx
5192
5316
  var React37 = __toESM(require("react"));
@@ -7061,35 +7185,6 @@ FilterEditor.displayName = "FilterEditor";
7061
7185
  var React45 = __toESM(require("react"));
7062
7186
  var PopoverPrimitive9 = __toESM(require("@radix-ui/react-popover"));
7063
7187
  var import_jsx_runtime52 = require("react/jsx-runtime");
7064
- function formatFilterValue(value) {
7065
- if (value == null) return void 0;
7066
- if (typeof value === "boolean") return value ? "Yes" : "No";
7067
- if (value instanceof Date) {
7068
- return value.toLocaleDateString("en-US", {
7069
- month: "short",
7070
- day: "numeric",
7071
- year: "numeric"
7072
- });
7073
- }
7074
- if (Array.isArray(value)) {
7075
- if (value.length === 0) return void 0;
7076
- if (value.length === 2 && typeof value[0] === "number") {
7077
- return `${value[0]} \u2013 ${value[1]}`;
7078
- }
7079
- if (value.length === 2 && value[0] instanceof Date) {
7080
- const fmt = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
7081
- return `${fmt(value[0])} \u2013 ${value[1] instanceof Date ? fmt(value[1]) : "\u2026"}`;
7082
- }
7083
- return String(value[0]);
7084
- }
7085
- return String(value);
7086
- }
7087
- function getBadgeCount(value) {
7088
- if (Array.isArray(value) && value.length > 1 && typeof value[0] === "string") {
7089
- return value.length;
7090
- }
7091
- return void 0;
7092
- }
7093
7188
  var SegmentPopover = ({
7094
7189
  open,
7095
7190
  onOpenChange,
@@ -7301,8 +7396,8 @@ var InteractiveFilterChip = ({
7301
7396
  InteractiveFilterChip.displayName = "InteractiveFilterChip";
7302
7397
 
7303
7398
  // src/components/ui/filter/filter-system.tsx
7304
- var React51 = __toESM(require("react"));
7305
- var import_icons36 = require("@l3mpire/icons");
7399
+ var React53 = __toESM(require("react"));
7400
+ var import_icons38 = require("@l3mpire/icons");
7306
7401
 
7307
7402
  // src/components/ui/filter/advanced-chip.tsx
7308
7403
  var React46 = __toESM(require("react"));
@@ -7358,15 +7453,121 @@ var AdvancedChip = React46.forwardRef(
7358
7453
  AdvancedChip.displayName = "AdvancedChip";
7359
7454
 
7360
7455
  // src/components/ui/filter/advanced-popover.tsx
7456
+ var React50 = __toESM(require("react"));
7457
+ var PopoverPrimitive12 = __toESM(require("@radix-ui/react-popover"));
7458
+ var import_icons36 = require("@l3mpire/icons");
7459
+
7460
+ // src/components/ui/filter/advanced-row.tsx
7361
7461
  var React48 = __toESM(require("react"));
7362
7462
  var PopoverPrimitive11 = __toESM(require("@radix-ui/react-popover"));
7363
7463
  var import_icons34 = require("@l3mpire/icons");
7364
7464
 
7365
- // src/components/ui/filter/advanced-row.tsx
7465
+ // src/components/ui/filter/filter-node-actions.tsx
7366
7466
  var React47 = __toESM(require("react"));
7367
7467
  var PopoverPrimitive10 = __toESM(require("@radix-ui/react-popover"));
7368
7468
  var import_icons33 = require("@l3mpire/icons");
7369
7469
  var import_jsx_runtime54 = require("react/jsx-runtime");
7470
+ var FilterNodeActions = ({
7471
+ nodeType,
7472
+ onDuplicate,
7473
+ onConvert,
7474
+ onDelete
7475
+ }) => {
7476
+ const [open, setOpen] = React47.useState(false);
7477
+ const items = [
7478
+ {
7479
+ label: "Duplicate",
7480
+ icon: import_icons33.faCopyOutline,
7481
+ action: onDuplicate
7482
+ },
7483
+ {
7484
+ label: nodeType === "condition" ? "Turn into group" : "Turn into filter",
7485
+ icon: nodeType === "condition" ? import_icons33.faFolderOutline : import_icons33.faFilterOutline,
7486
+ action: onConvert
7487
+ },
7488
+ {
7489
+ label: "Delete",
7490
+ icon: import_icons33.faTrashOutline,
7491
+ action: onDelete,
7492
+ destructive: true
7493
+ }
7494
+ ];
7495
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(PopoverPrimitive10.Root, { open, onOpenChange: setOpen, children: [
7496
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7497
+ "button",
7498
+ {
7499
+ type: "button",
7500
+ className: "shrink-0 flex items-center justify-center p-sm rounded-md cursor-pointer transition-colors hover:bg-[var(--color-accent)]",
7501
+ "aria-label": "More actions",
7502
+ children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7503
+ import_icons33.Icon,
7504
+ {
7505
+ icon: import_icons33.faEllipsisOutline,
7506
+ size: "sm",
7507
+ className: "text-[var(--color-foreground)]"
7508
+ }
7509
+ )
7510
+ }
7511
+ ) }),
7512
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7513
+ PopoverPrimitive10.Content,
7514
+ {
7515
+ sideOffset: 4,
7516
+ align: "end",
7517
+ className: cn(
7518
+ "z-50 flex flex-col p-xs overflow-clip",
7519
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7520
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7521
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7522
+ "min-w-[180px]"
7523
+ ),
7524
+ children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
7525
+ "button",
7526
+ {
7527
+ type: "button",
7528
+ onClick: () => {
7529
+ item.action();
7530
+ setOpen(false);
7531
+ },
7532
+ className: cn(
7533
+ "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
7534
+ "hover:bg-[var(--color-dropdown-item-hover)]",
7535
+ item.destructive && "text-[var(--color-destructive,#ef4444)]"
7536
+ ),
7537
+ children: [
7538
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7539
+ import_icons33.Icon,
7540
+ {
7541
+ icon: item.icon,
7542
+ size: "sm",
7543
+ className: cn(
7544
+ "shrink-0",
7545
+ item.destructive ? "text-[var(--color-destructive,#ef4444)]" : "text-[var(--color-dropdown-item-icon)]"
7546
+ )
7547
+ }
7548
+ ),
7549
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7550
+ "span",
7551
+ {
7552
+ className: cn(
7553
+ "text-sm font-regular leading-sm",
7554
+ item.destructive ? "text-[var(--color-destructive,#ef4444)]" : "text-[var(--color-dropdown-item-text)]"
7555
+ ),
7556
+ children: item.label
7557
+ }
7558
+ )
7559
+ ]
7560
+ },
7561
+ item.label
7562
+ ))
7563
+ }
7564
+ ) })
7565
+ ] });
7566
+ };
7567
+ FilterNodeActions.displayName = "FilterNodeActions";
7568
+
7569
+ // src/components/ui/filter/advanced-row.tsx
7570
+ var import_jsx_runtime55 = require("react/jsx-runtime");
7370
7571
  var selectBtnStyle = [
7371
7572
  "flex items-center gap-base",
7372
7573
  "px-base py-sm",
@@ -7383,10 +7584,13 @@ var AdvancedRow = ({
7383
7584
  properties,
7384
7585
  onUpdate,
7385
7586
  onPropertyChange,
7386
- onDelete
7587
+ onDelete,
7588
+ onDuplicate,
7589
+ onTurnIntoGroup
7387
7590
  }) => {
7388
- const [operatorOpen, setOperatorOpen] = React47.useState(false);
7389
- const [propertyOpen, setPropertyOpen] = React47.useState(false);
7591
+ const [operatorOpen, setOperatorOpen] = React48.useState(false);
7592
+ const [propertyOpen, setPropertyOpen] = React48.useState(false);
7593
+ const [valueOpen, setValueOpen] = React48.useState(false);
7390
7594
  const handleOperatorSelect = (op) => {
7391
7595
  if (isNoValueOperator(op)) {
7392
7596
  onUpdate({ ...condition, operator: op, value: null });
@@ -7399,16 +7603,18 @@ var AdvancedRow = ({
7399
7603
  const handleValueChange = (val) => {
7400
7604
  onUpdate({ ...condition, value: val });
7401
7605
  };
7402
- const displayValue = condition.value == null ? "" : typeof condition.value === "string" ? condition.value : String(condition.value);
7403
- return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
7404
- connector === "Where" ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
7606
+ const displayValue = formatFilterValue(condition.value);
7607
+ const badgeCount = getBadgeCount(condition.value);
7608
+ const hasValue = displayValue != null;
7609
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
7610
+ connector === "Where" ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
7405
7611
  "button",
7406
7612
  {
7407
7613
  type: "button",
7408
7614
  onClick: onConnectorToggle,
7409
7615
  className: cn(
7410
7616
  "shrink-0 flex items-center justify-center gap-xs",
7411
- "min-w-[64px] min-h-[24px] max-h-[24px] p-xs",
7617
+ "min-w-[64px] min-h-[32px] max-h-[32px] px-base py-sm",
7412
7618
  "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7413
7619
  "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-base shadow-sm",
7414
7620
  "cursor-pointer transition-colors text-xs font-semibold leading-xs text-[var(--color-foreground)]",
@@ -7416,22 +7622,18 @@ var AdvancedRow = ({
7416
7622
  ),
7417
7623
  children: [
7418
7624
  connector,
7419
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: import_icons33.faRefreshOutline, size: "xs", className: "text-[var(--color-foreground)]" })
7625
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: import_icons34.faRefreshOutline, size: "xs", className: "text-[var(--color-foreground)]" })
7420
7626
  ]
7421
7627
  }
7422
7628
  ),
7423
- /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(PopoverPrimitive10.Root, { open: propertyOpen, onOpenChange: setPropertyOpen, children: [
7424
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
7425
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: propertyDef.icon, size: "sm", className: "shrink-0 text-[var(--color-muted-foreground)]" }),
7426
- /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate", children: [
7427
- propertyDef.groupLabel,
7428
- " > ",
7429
- propertyDef.label
7430
- ] }),
7431
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: import_icons33.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
7629
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(PopoverPrimitive11.Root, { open: propertyOpen, onOpenChange: setPropertyOpen, children: [
7630
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
7631
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: propertyDef.icon, size: "sm", className: "shrink-0 text-[var(--color-muted-foreground)]" }),
7632
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate", children: propertyDef.label }),
7633
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: import_icons34.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
7432
7634
  ] }) }),
7433
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7434
- PopoverPrimitive10.Content,
7635
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7636
+ PopoverPrimitive11.Content,
7435
7637
  {
7436
7638
  sideOffset: 4,
7437
7639
  align: "start",
@@ -7442,7 +7644,7 @@ var AdvancedRow = ({
7442
7644
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
7443
7645
  "min-w-[200px]"
7444
7646
  ),
7445
- children: properties.map((p) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
7647
+ children: properties.map((p) => /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
7446
7648
  "button",
7447
7649
  {
7448
7650
  type: "button",
@@ -7456,8 +7658,8 @@ var AdvancedRow = ({
7456
7658
  p.id === condition.propertyId && "bg-[var(--color-dropdown-item-hover)]"
7457
7659
  ),
7458
7660
  children: [
7459
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: p.icon, size: "sm", className: "shrink-0 text-[var(--color-dropdown-item-icon)]" }),
7460
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] truncate", children: p.label })
7661
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: p.icon, size: "sm", className: "shrink-0 text-[var(--color-dropdown-item-icon)]" }),
7662
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] truncate", children: p.label })
7461
7663
  ]
7462
7664
  },
7463
7665
  p.id
@@ -7465,13 +7667,13 @@ var AdvancedRow = ({
7465
7667
  }
7466
7668
  ) })
7467
7669
  ] }),
7468
- /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(PopoverPrimitive10.Root, { open: operatorOpen, onOpenChange: setOperatorOpen, children: [
7469
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
7470
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate text-left", children: condition.operator ?? "Select" }),
7471
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: import_icons33.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
7670
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(PopoverPrimitive11.Root, { open: operatorOpen, onOpenChange: setOperatorOpen, children: [
7671
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
7672
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate text-left", children: condition.operator ?? "Select" }),
7673
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: import_icons34.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
7472
7674
  ] }) }),
7473
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7474
- PopoverPrimitive10.Content,
7675
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7676
+ PopoverPrimitive11.Content,
7475
7677
  {
7476
7678
  sideOffset: 4,
7477
7679
  align: "start",
@@ -7482,7 +7684,7 @@ var AdvancedRow = ({
7482
7684
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
7483
7685
  "min-w-[160px]"
7484
7686
  ),
7485
- children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7687
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7486
7688
  OperatorList,
7487
7689
  {
7488
7690
  dataType: propertyDef.type,
@@ -7493,45 +7695,170 @@ var AdvancedRow = ({
7493
7695
  }
7494
7696
  ) })
7495
7697
  ] }),
7496
- condition.operator && !isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7497
- "input",
7698
+ condition.operator && !isNoValueOperator(condition.operator) && (() => {
7699
+ const inputType = getValueInputType(propertyDef.type, condition.operator);
7700
+ const dateWide = inputType === "DatePicker" || inputType === "DateRange";
7701
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(PopoverPrimitive11.Root, { open: valueOpen, onOpenChange: setValueOpen, children: [
7702
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
7703
+ "button",
7704
+ {
7705
+ type: "button",
7706
+ className: cn(selectBtnStyle, "flex-1 min-w-[80px] justify-between"),
7707
+ children: [
7708
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7709
+ "span",
7710
+ {
7711
+ className: cn(
7712
+ "text-sm font-regular leading-sm whitespace-nowrap truncate text-left",
7713
+ hasValue ? "text-[var(--color-foreground)]" : "text-[var(--color-muted-foreground)]"
7714
+ ),
7715
+ children: hasValue ? displayValue : "Enter a value"
7716
+ }
7717
+ ),
7718
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "flex items-center gap-xs shrink-0", children: [
7719
+ badgeCount != null && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: [
7720
+ "+",
7721
+ badgeCount - 1
7722
+ ] }),
7723
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7724
+ import_icons34.Icon,
7725
+ {
7726
+ icon: import_icons34.faChevronDownOutline,
7727
+ size: "xs",
7728
+ className: "text-[var(--color-foreground)]"
7729
+ }
7730
+ )
7731
+ ] })
7732
+ ]
7733
+ }
7734
+ ) }),
7735
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7736
+ PopoverPrimitive11.Content,
7737
+ {
7738
+ sideOffset: 4,
7739
+ align: "start",
7740
+ className: cn(
7741
+ "z-50 flex flex-col overflow-clip",
7742
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7743
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7744
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0"
7745
+ ),
7746
+ style: { minWidth: dateWide ? "auto" : "240px" },
7747
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7748
+ ValueInput,
7749
+ {
7750
+ dataType: propertyDef.type,
7751
+ operator: condition.operator,
7752
+ value: condition.value,
7753
+ onChange: handleValueChange,
7754
+ onSubmit: () => setValueOpen(false),
7755
+ options: propertyDef.options,
7756
+ dynamicOptions: propertyDef.dynamicOptions
7757
+ }
7758
+ )
7759
+ }
7760
+ ) })
7761
+ ] });
7762
+ })(),
7763
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7764
+ FilterNodeActions,
7498
7765
  {
7499
- type: "text",
7500
- value: displayValue,
7501
- onChange: (e) => handleValueChange(e.target.value),
7502
- placeholder: "Placeholder",
7503
- className: cn(
7504
- "flex-1 min-w-[80px] px-base py-sm rounded-md",
7505
- "border border-[var(--color-input)]",
7506
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
7507
- "placeholder:text-[var(--color-muted-foreground)]",
7508
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-0"
7509
- )
7766
+ nodeType: "condition",
7767
+ onDuplicate: onDuplicate ?? (() => {
7768
+ }),
7769
+ onConvert: onTurnIntoGroup ?? (() => {
7770
+ }),
7771
+ onDelete
7510
7772
  }
7511
- ),
7512
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7773
+ )
7774
+ ] });
7775
+ };
7776
+ AdvancedRow.displayName = "AdvancedRow";
7777
+
7778
+ // src/components/ui/filter/advanced-group.tsx
7779
+ var React49 = __toESM(require("react"));
7780
+ var import_icons35 = require("@l3mpire/icons");
7781
+ var import_jsx_runtime56 = require("react/jsx-runtime");
7782
+ var AdvancedGroup = ({
7783
+ connector,
7784
+ onConnectorToggle,
7785
+ onDuplicate,
7786
+ onTurnIntoFilter,
7787
+ onDelete,
7788
+ onAddFilter,
7789
+ properties,
7790
+ children
7791
+ }) => {
7792
+ const [addOpen, setAddOpen] = React49.useState(false);
7793
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-start gap-base w-full min-w-0", children: [
7794
+ connector === "Where" ? /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end pt-base", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }) : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end pt-base", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7513
7795
  "button",
7514
7796
  {
7515
7797
  type: "button",
7516
- onClick: onDelete,
7517
- className: "ml-auto shrink-0 flex items-center justify-center p-sm rounded-md cursor-pointer transition-colors hover:bg-[var(--color-accent)]",
7518
- "aria-label": "Remove filter",
7519
- children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: import_icons33.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" })
7798
+ onClick: onConnectorToggle,
7799
+ className: cn(
7800
+ "flex items-center justify-center gap-xs",
7801
+ "min-w-[64px] min-h-[32px] max-h-[32px] px-base py-sm",
7802
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7803
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-base shadow-sm",
7804
+ "cursor-pointer transition-colors text-xs font-semibold leading-xs text-[var(--color-foreground)]",
7805
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7806
+ ),
7807
+ children: [
7808
+ connector,
7809
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_icons35.Icon, { icon: import_icons35.faRefreshOutline, size: "xs", className: "text-[var(--color-foreground)]" })
7810
+ ]
7520
7811
  }
7521
- )
7812
+ ) }),
7813
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex-1 min-w-0 flex flex-col gap-base p-base border border-[var(--color-border)] rounded-md bg-[var(--color-secondary,var(--color-accent))]", children: [
7814
+ children,
7815
+ onAddFilter && properties && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7816
+ PropertySelector,
7817
+ {
7818
+ properties,
7819
+ onSelect: (prop) => {
7820
+ onAddFilter(prop);
7821
+ setAddOpen(false);
7822
+ },
7823
+ open: addOpen,
7824
+ onOpenChange: setAddOpen,
7825
+ children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7826
+ "button",
7827
+ {
7828
+ type: "button",
7829
+ className: "flex items-center gap-sm px-base py-sm text-sm font-semibold leading-sm text-[var(--color-muted-foreground)] cursor-pointer transition-colors rounded-md hover:bg-[var(--color-accent)] hover:text-[var(--color-foreground)] w-fit",
7830
+ children: [
7831
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_icons35.Icon, { icon: import_icons35.faPlusOutline, size: "sm" }),
7832
+ "Add filter"
7833
+ ]
7834
+ }
7835
+ )
7836
+ }
7837
+ )
7838
+ ] }),
7839
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "shrink-0 pt-base", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7840
+ FilterNodeActions,
7841
+ {
7842
+ nodeType: "group",
7843
+ onDuplicate,
7844
+ onConvert: onTurnIntoFilter,
7845
+ onDelete
7846
+ }
7847
+ ) })
7522
7848
  ] });
7523
7849
  };
7524
- AdvancedRow.displayName = "AdvancedRow";
7850
+ AdvancedGroup.displayName = "AdvancedGroup";
7525
7851
 
7526
7852
  // src/components/ui/filter/advanced-popover.tsx
7527
- var import_jsx_runtime55 = require("react/jsx-runtime");
7528
- var outlinedBtn = [
7853
+ var import_jsx_runtime57 = (
7854
+ /* Draft row in empty group */
7855
+ require("react/jsx-runtime")
7856
+ );
7857
+ var ghostBtn = [
7529
7858
  "flex items-center gap-sm px-base py-sm",
7530
- "min-h-[32px] max-h-[32px] min-w-[80px]",
7531
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7532
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7533
- "cursor-pointer transition-colors text-sm font-semibold leading-sm text-[var(--color-foreground)]",
7534
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7859
+ "min-h-[32px]",
7860
+ "cursor-pointer transition-colors text-sm font-semibold leading-sm",
7861
+ "rounded-md hover:bg-[var(--color-accent)]"
7535
7862
  ];
7536
7863
  var AdvancedPopover = ({
7537
7864
  filters,
@@ -7541,41 +7868,153 @@ var AdvancedPopover = ({
7541
7868
  onOpenChange,
7542
7869
  children
7543
7870
  }) => {
7544
- const [addSelectorOpen, setAddSelectorOpen] = React48.useState(false);
7545
- const [draftPickerOpen, setDraftPickerOpen] = React48.useState(false);
7871
+ const [addSelectorOpen, setAddSelectorOpen] = React50.useState(false);
7872
+ const [draftPickerOpen, setDraftPickerOpen] = React50.useState(false);
7873
+ const [groupSelectorOpen, setGroupSelectorOpen] = React50.useState(false);
7546
7874
  const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
7547
- const handleUpdateFilter = (updated) => {
7548
- onFiltersChange(filters.map((f) => f.id === updated.id ? updated : f));
7549
- };
7550
- const handleDeleteFilter = (id) => {
7551
- onFiltersChange(filters.filter((f) => f.id !== id));
7552
- };
7553
- const handlePropertyChange = (filterId, newProp) => {
7554
- const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
7555
- onFiltersChange(
7556
- filters.map((f) => f.id === filterId ? { ...newCondition, id: filterId } : f)
7557
- );
7558
- };
7559
7875
  const handleAddFilter = (property) => {
7560
7876
  const newFilter = createFilterWithDefaults(property.id, property.type);
7561
7877
  onFiltersChange([...filters, newFilter]);
7562
7878
  setAddSelectorOpen(false);
7563
7879
  };
7880
+ const handleAddGroup = () => {
7881
+ const group = createEmptyGroup();
7882
+ onFiltersChange([...filters, group]);
7883
+ setGroupSelectorOpen(false);
7884
+ };
7564
7885
  const handleClearAll = () => {
7565
7886
  onFiltersChange([]);
7566
7887
  onOpenChange?.(false);
7567
7888
  };
7568
- const toggleLogicOp = (filterId) => {
7889
+ const handleUpdateNode = (id, updated) => {
7890
+ onFiltersChange(updateNodeInTree(filters, id, () => updated));
7891
+ };
7892
+ const handleDeleteNode = (id) => {
7893
+ onFiltersChange(removeNodeFromTree(filters, id));
7894
+ };
7895
+ const handleDuplicateNode = (id) => {
7896
+ const find = (nodes) => {
7897
+ for (const n of nodes) {
7898
+ if (n.id === id) return n;
7899
+ if (isFilterGroup(n)) {
7900
+ const found = find(n.children);
7901
+ if (found) return found;
7902
+ }
7903
+ }
7904
+ };
7905
+ const node = find(filters);
7906
+ if (!node) return;
7907
+ const clone = duplicateNode(node);
7908
+ clone.logicOperator = node.logicOperator ?? "and";
7909
+ onFiltersChange(insertAfterInTree(filters, id, clone));
7910
+ };
7911
+ const handleWrapInGroup = (id) => {
7912
+ const find = (nodes) => {
7913
+ for (const n of nodes) {
7914
+ if (n.id === id && !isFilterGroup(n)) return n;
7915
+ if (isFilterGroup(n)) {
7916
+ const found = find(n.children);
7917
+ if (found) return found;
7918
+ }
7919
+ }
7920
+ };
7921
+ const condition = find(filters);
7922
+ if (!condition) return;
7923
+ const group = wrapInGroup(condition);
7924
+ onFiltersChange(replaceNodeInTree(filters, id, group));
7925
+ };
7926
+ const handleUnwrapGroup = (id) => {
7927
+ const find = (nodes) => {
7928
+ for (const n of nodes) {
7929
+ if (n.id === id && isFilterGroup(n)) return n;
7930
+ if (isFilterGroup(n)) {
7931
+ const found = find(n.children);
7932
+ if (found) return found;
7933
+ }
7934
+ }
7935
+ };
7936
+ const group = find(filters);
7937
+ if (!group) return;
7938
+ const replacement = unwrapGroup(group);
7939
+ if (replacement) {
7940
+ onFiltersChange(replaceNodeInTree(filters, id, replacement));
7941
+ } else {
7942
+ onFiltersChange(removeNodeFromTree(filters, id));
7943
+ }
7944
+ };
7945
+ const toggleLogicOp = (id) => {
7946
+ onFiltersChange(
7947
+ updateNodeInTree(filters, id, (n) => ({
7948
+ ...n,
7949
+ logicOperator: (n.logicOperator ?? "and") === "and" ? "or" : "and"
7950
+ }))
7951
+ );
7952
+ };
7953
+ const handleGroupChildrenChange = (groupId, children2) => {
7569
7954
  onFiltersChange(
7570
- filters.map(
7571
- (f) => f.id === filterId ? { ...f, logicOperator: (f.logicOperator ?? "and") === "and" ? "or" : "and" } : f
7955
+ updateNodeInTree(
7956
+ filters,
7957
+ groupId,
7958
+ (n) => isFilterGroup(n) ? { ...n, children: children2 } : n
7572
7959
  )
7573
7960
  );
7574
7961
  };
7575
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(PopoverPrimitive11.Root, { open, onOpenChange, children: [
7576
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Trigger, { asChild: true, children }),
7577
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PopoverPrimitive11.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
7578
- PopoverPrimitive11.Content,
7962
+ const renderNode = (node, index) => {
7963
+ const connector = index === 0 ? "Where" : (node.logicOperator ?? "and") === "and" ? "And" : "Or";
7964
+ if (isFilterGroup(node)) {
7965
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
7966
+ AdvancedGroup,
7967
+ {
7968
+ connector,
7969
+ onConnectorToggle: index > 0 ? () => toggleLogicOp(node.id) : void 0,
7970
+ onDuplicate: () => handleDuplicateNode(node.id),
7971
+ onTurnIntoFilter: () => handleUnwrapGroup(node.id),
7972
+ onDelete: () => handleDeleteNode(node.id),
7973
+ properties,
7974
+ onAddFilter: (prop) => {
7975
+ const newFilter = createFilterWithDefaults(prop.id, prop.type);
7976
+ handleGroupChildrenChange(node.id, [...node.children, newFilter]);
7977
+ },
7978
+ children: node.children.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
7979
+ DraftRow,
7980
+ {
7981
+ properties,
7982
+ onSelect: (prop) => {
7983
+ const newFilter = createFilterWithDefaults(prop.id, prop.type);
7984
+ handleGroupChildrenChange(node.id, [newFilter]);
7985
+ }
7986
+ }
7987
+ ) : node.children.map((child, i) => renderNode(child, i))
7988
+ },
7989
+ node.id
7990
+ );
7991
+ }
7992
+ const propDef = getPropertyDef(node.propertyId);
7993
+ if (!propDef) return null;
7994
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
7995
+ AdvancedRow,
7996
+ {
7997
+ connector,
7998
+ onConnectorToggle: index > 0 ? () => toggleLogicOp(node.id) : void 0,
7999
+ propertyDef: propDef,
8000
+ condition: node,
8001
+ properties,
8002
+ onUpdate: (updated) => handleUpdateNode(node.id, updated),
8003
+ onPropertyChange: (p) => {
8004
+ const newCondition = createFilterWithDefaults(p.id, p.type);
8005
+ handleUpdateNode(node.id, { ...newCondition, id: node.id });
8006
+ },
8007
+ onDelete: () => handleDeleteNode(node.id),
8008
+ onDuplicate: () => handleDuplicateNode(node.id),
8009
+ onTurnIntoGroup: () => handleWrapInGroup(node.id)
8010
+ },
8011
+ node.id
8012
+ );
8013
+ };
8014
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(PopoverPrimitive12.Root, { open, onOpenChange, children: [
8015
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(PopoverPrimitive12.Trigger, { asChild: true, children }),
8016
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(PopoverPrimitive12.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
8017
+ PopoverPrimitive12.Content,
7579
8018
  {
7580
8019
  sideOffset: 4,
7581
8020
  align: "start",
@@ -7587,87 +8026,56 @@ var AdvancedPopover = ({
7587
8026
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7588
8027
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7589
8028
  "data-[side=bottom]:slide-in-from-top-2",
7590
- "w-[min(520px,calc(100vw-32px))]"
8029
+ "w-[min(640px,calc(100vw-32px))]"
7591
8030
  ),
7592
8031
  children: [
7593
- /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex flex-col gap-base p-base", children: [
7594
- filters.map((filter, i) => {
7595
- const propDef = getPropertyDef(filter.propertyId);
7596
- if (!propDef) return null;
7597
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7598
- AdvancedRow,
7599
- {
7600
- connector: i === 0 ? "Where" : (filter.logicOperator ?? "and") === "and" ? "And" : "Or",
7601
- onConnectorToggle: i > 0 ? () => toggleLogicOp(filter.id) : void 0,
7602
- propertyDef: propDef,
7603
- condition: filter,
7604
- properties,
7605
- onUpdate: handleUpdateFilter,
7606
- onPropertyChange: (p) => handlePropertyChange(filter.id, p),
7607
- onDelete: () => handleDeleteFilter(filter.id)
7608
- },
7609
- filter.id
7610
- );
7611
- }),
7612
- filters.length === 0 && /* ── Draft row: inline "Select property" placeholder ──── */
7613
- /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
7614
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }),
7615
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
8032
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "flex flex-col gap-base p-base", children: filters.length > 0 ? filters.map((node, i) => renderNode(node, i)) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8033
+ DraftRow,
8034
+ {
8035
+ properties,
8036
+ onSelect: handleAddFilter,
8037
+ open: draftPickerOpen,
8038
+ onOpenChange: setDraftPickerOpen
8039
+ }
8040
+ ) }),
8041
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
8042
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex items-center gap-sm", children: [
8043
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
7616
8044
  PropertySelector,
7617
8045
  {
7618
8046
  properties,
7619
8047
  onSelect: handleAddFilter,
7620
- open: draftPickerOpen,
7621
- onOpenChange: setDraftPickerOpen,
7622
- children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
7623
- "button",
7624
- {
7625
- type: "button",
7626
- className: cn(
7627
- "flex items-center gap-base px-base py-sm min-w-0",
7628
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7629
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7630
- "cursor-pointer transition-colors",
7631
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7632
- ),
7633
- children: [
7634
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-muted-foreground)] whitespace-nowrap", children: "Select property" }),
7635
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7636
- import_icons34.Icon,
7637
- {
7638
- icon: import_icons34.faChevronDownOutline,
7639
- size: "xs",
7640
- className: "shrink-0 text-[var(--color-foreground)]"
7641
- }
7642
- )
7643
- ]
7644
- }
7645
- )
8048
+ open: addSelectorOpen,
8049
+ onOpenChange: setAddSelectorOpen,
8050
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("button", { type: "button", className: cn(ghostBtn, "text-[var(--color-foreground)]"), children: [
8051
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_icons36.Icon, { icon: import_icons36.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
8052
+ "Add filter"
8053
+ ] })
8054
+ }
8055
+ ),
8056
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
8057
+ "button",
8058
+ {
8059
+ type: "button",
8060
+ onClick: handleAddGroup,
8061
+ className: cn(ghostBtn, "text-[var(--color-foreground)]"),
8062
+ children: [
8063
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_icons36.Icon, { icon: import_icons36.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
8064
+ "Add filters group"
8065
+ ]
7646
8066
  }
7647
8067
  )
7648
- ] })
7649
- ] }),
7650
- /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
7651
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
7652
- PropertySelector,
7653
- {
7654
- properties,
7655
- onSelect: handleAddFilter,
7656
- open: addSelectorOpen,
7657
- onOpenChange: setAddSelectorOpen,
7658
- children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("button", { type: "button", className: cn(outlinedBtn), children: [
7659
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_icons34.Icon, { icon: import_icons34.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
7660
- "Add filter"
7661
- ] })
7662
- }
7663
- ),
7664
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
8068
+ ] }),
8069
+ filters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
7665
8070
  "button",
7666
8071
  {
7667
8072
  type: "button",
7668
8073
  onClick: handleClearAll,
7669
- className: "text-sm font-semibold leading-sm text-[var(--color-foreground)] cursor-pointer transition-colors hover:opacity-70 px-base py-sm",
7670
- children: "Clear all filters"
8074
+ className: cn(ghostBtn, "text-[var(--color-destructive,#ef4444)]"),
8075
+ children: [
8076
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_icons36.Icon, { icon: import_icons36.faXmarkOutline, size: "sm", className: "text-[var(--color-destructive,#ef4444)]" }),
8077
+ "Clear filters"
8078
+ ]
7671
8079
  }
7672
8080
  )
7673
8081
  ] })
@@ -7677,12 +8085,71 @@ var AdvancedPopover = ({
7677
8085
  ] });
7678
8086
  };
7679
8087
  AdvancedPopover.displayName = "AdvancedPopover";
8088
+ var DraftRow = ({
8089
+ properties,
8090
+ onSelect,
8091
+ open: openProp,
8092
+ onOpenChange
8093
+ }) => {
8094
+ const [internalOpen, setInternalOpen] = React50.useState(false);
8095
+ const isControlled = openProp !== void 0;
8096
+ const open = isControlled ? openProp : internalOpen;
8097
+ const setOpen = (v) => {
8098
+ if (!isControlled) setInternalOpen(v);
8099
+ onOpenChange?.(v);
8100
+ };
8101
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
8102
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }),
8103
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8104
+ PropertySelector,
8105
+ {
8106
+ properties,
8107
+ onSelect: (prop) => {
8108
+ onSelect(prop);
8109
+ setOpen(false);
8110
+ },
8111
+ open,
8112
+ onOpenChange: setOpen,
8113
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
8114
+ "button",
8115
+ {
8116
+ type: "button",
8117
+ className: cn(
8118
+ "flex items-center gap-base px-base py-sm min-w-0",
8119
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
8120
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
8121
+ "cursor-pointer transition-colors",
8122
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
8123
+ ),
8124
+ children: [
8125
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-muted-foreground)] whitespace-nowrap", children: "Select property" }),
8126
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8127
+ import_icons36.Icon,
8128
+ {
8129
+ icon: import_icons36.faChevronDownOutline,
8130
+ size: "xs",
8131
+ className: "shrink-0 text-[var(--color-foreground)]"
8132
+ }
8133
+ )
8134
+ ]
8135
+ }
8136
+ )
8137
+ }
8138
+ )
8139
+ ] });
8140
+ };
7680
8141
 
7681
8142
  // src/components/ui/filter/summary-chip.tsx
7682
- var React49 = __toESM(require("react"));
7683
- var PopoverPrimitive12 = __toESM(require("@radix-ui/react-popover"));
7684
- var import_icons35 = require("@l3mpire/icons");
7685
- var import_jsx_runtime56 = require("react/jsx-runtime");
8143
+ var React51 = __toESM(require("react"));
8144
+ var PopoverPrimitive13 = __toESM(require("@radix-ui/react-popover"));
8145
+ var import_icons37 = require("@l3mpire/icons");
8146
+ var import_jsx_runtime58 = require("react/jsx-runtime");
8147
+ var ghostBtn2 = [
8148
+ "flex items-center gap-sm px-base py-sm",
8149
+ "min-h-[32px]",
8150
+ "cursor-pointer transition-colors text-sm font-semibold leading-sm",
8151
+ "rounded-md hover:bg-[var(--color-accent)]"
8152
+ ];
7686
8153
  var SummaryChip = ({
7687
8154
  count,
7688
8155
  filters,
@@ -7694,44 +8161,152 @@ var SummaryChip = ({
7694
8161
  open: openProp,
7695
8162
  onOpenChange
7696
8163
  }) => {
7697
- const [uncontrolledOpen, setUncontrolledOpen] = React49.useState(false);
8164
+ const [uncontrolledOpen, setUncontrolledOpen] = React51.useState(false);
7698
8165
  const isControlled = openProp !== void 0;
7699
8166
  const open = isControlled ? openProp : uncontrolledOpen;
7700
8167
  const setOpen = (v) => {
7701
8168
  if (!isControlled) setUncontrolledOpen(v);
7702
8169
  onOpenChange?.(v);
7703
8170
  };
7704
- const [addSelectorOpen, setAddSelectorOpen] = React49.useState(false);
7705
- const [draftPickerOpen, setDraftPickerOpen] = React49.useState(false);
8171
+ const [addSelectorOpen, setAddSelectorOpen] = React51.useState(false);
8172
+ const [draftPickerOpen, setDraftPickerOpen] = React51.useState(false);
7706
8173
  const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
7707
- const handleUpdateFilter = (updated) => {
7708
- onFiltersChange(filters.map((f) => f.id === updated.id ? updated : f));
8174
+ const handleAddFilter = (property) => {
8175
+ const newFilter = createFilterWithDefaults(property.id, property.type);
8176
+ onFiltersChange([...filters, newFilter]);
8177
+ setAddSelectorOpen(false);
7709
8178
  };
7710
- const handleDeleteFilter = (id) => {
7711
- const next = filters.filter((f) => f.id !== id);
8179
+ const handleAddGroup = () => {
8180
+ onFiltersChange([...filters, createEmptyGroup()]);
8181
+ };
8182
+ const handleUpdateNode = (id, updated) => {
8183
+ onFiltersChange(updateNodeInTree(filters, id, () => updated));
8184
+ };
8185
+ const handleDeleteNode = (id) => {
8186
+ const next = removeNodeFromTree(filters, id);
7712
8187
  onFiltersChange(next);
7713
8188
  if (next.length === 0) setOpen(false);
7714
8189
  };
7715
- const handlePropertyChange = (filterId, newProp) => {
7716
- const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
8190
+ const handleDuplicateNode = (id) => {
8191
+ const find = (nodes) => {
8192
+ for (const n of nodes) {
8193
+ if (n.id === id) return n;
8194
+ if (isFilterGroup(n)) {
8195
+ const f = find(n.children);
8196
+ if (f) return f;
8197
+ }
8198
+ }
8199
+ };
8200
+ const node = find(filters);
8201
+ if (!node) return;
8202
+ const clone = duplicateNode(node);
8203
+ clone.logicOperator = node.logicOperator ?? "and";
8204
+ onFiltersChange(insertAfterInTree(filters, id, clone));
8205
+ };
8206
+ const handleWrapInGroup = (id) => {
8207
+ const find = (nodes) => {
8208
+ for (const n of nodes) {
8209
+ if (n.id === id && !isFilterGroup(n)) return n;
8210
+ if (isFilterGroup(n)) {
8211
+ const f = find(n.children);
8212
+ if (f) return f;
8213
+ }
8214
+ }
8215
+ };
8216
+ const condition = find(filters);
8217
+ if (!condition) return;
8218
+ onFiltersChange(replaceNodeInTree(filters, id, wrapInGroup(condition)));
8219
+ };
8220
+ const handleUnwrapGroup = (id) => {
8221
+ const find = (nodes) => {
8222
+ for (const n of nodes) {
8223
+ if (n.id === id && isFilterGroup(n)) return n;
8224
+ if (isFilterGroup(n)) {
8225
+ const f = find(n.children);
8226
+ if (f) return f;
8227
+ }
8228
+ }
8229
+ };
8230
+ const group = find(filters);
8231
+ if (!group) return;
8232
+ const replacement = unwrapGroup(group);
8233
+ if (replacement) {
8234
+ onFiltersChange(replaceNodeInTree(filters, id, replacement));
8235
+ } else {
8236
+ onFiltersChange(removeNodeFromTree(filters, id));
8237
+ }
8238
+ };
8239
+ const toggleLogicOp = (id) => {
7717
8240
  onFiltersChange(
7718
- filters.map((f) => f.id === filterId ? { ...newCondition, id: filterId } : f)
8241
+ updateNodeInTree(filters, id, (n) => ({
8242
+ ...n,
8243
+ logicOperator: (n.logicOperator ?? "and") === "and" ? "or" : "and"
8244
+ }))
7719
8245
  );
7720
8246
  };
7721
- const handleAddFilter = (property) => {
7722
- const newFilter = createFilterWithDefaults(property.id, property.type);
7723
- onFiltersChange([...filters, newFilter]);
7724
- setAddSelectorOpen(false);
7725
- };
7726
- const toggleLogicOp = (filterId) => {
8247
+ const handleGroupChildrenChange = (groupId, newChildren) => {
7727
8248
  onFiltersChange(
7728
- filters.map(
7729
- (f) => f.id === filterId ? { ...f, logicOperator: (f.logicOperator ?? "and") === "and" ? "or" : "and" } : f
8249
+ updateNodeInTree(
8250
+ filters,
8251
+ groupId,
8252
+ (n) => isFilterGroup(n) ? { ...n, children: newChildren } : n
7730
8253
  )
7731
8254
  );
7732
8255
  };
7733
- return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(PopoverPrimitive12.Root, { open, onOpenChange: setOpen, children: [
7734
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(PopoverPrimitive12.Trigger, { asChild: true, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
8256
+ const renderNode = (node, index) => {
8257
+ const connector = index === 0 ? "Where" : (node.logicOperator ?? "and") === "and" ? "And" : "Or";
8258
+ if (isFilterGroup(node)) {
8259
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8260
+ AdvancedGroup,
8261
+ {
8262
+ connector,
8263
+ onConnectorToggle: index > 0 ? () => toggleLogicOp(node.id) : void 0,
8264
+ onDuplicate: () => handleDuplicateNode(node.id),
8265
+ onTurnIntoFilter: () => handleUnwrapGroup(node.id),
8266
+ onDelete: () => handleDeleteNode(node.id),
8267
+ properties,
8268
+ onAddFilter: (prop) => {
8269
+ const newFilter = createFilterWithDefaults(prop.id, prop.type);
8270
+ handleGroupChildrenChange(node.id, [...node.children, newFilter]);
8271
+ },
8272
+ children: node.children.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8273
+ DraftRow2,
8274
+ {
8275
+ properties,
8276
+ onSelect: (prop) => {
8277
+ const newFilter = createFilterWithDefaults(prop.id, prop.type);
8278
+ handleGroupChildrenChange(node.id, [newFilter]);
8279
+ }
8280
+ }
8281
+ ) : node.children.map((child, i) => renderNode(child, i))
8282
+ },
8283
+ node.id
8284
+ );
8285
+ }
8286
+ const propDef = getPropertyDef(node.propertyId);
8287
+ if (!propDef) return null;
8288
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8289
+ AdvancedRow,
8290
+ {
8291
+ connector,
8292
+ onConnectorToggle: index > 0 ? () => toggleLogicOp(node.id) : void 0,
8293
+ propertyDef: propDef,
8294
+ condition: node,
8295
+ properties,
8296
+ onUpdate: (updated) => handleUpdateNode(node.id, updated),
8297
+ onPropertyChange: (p) => {
8298
+ const newCondition = createFilterWithDefaults(p.id, p.type);
8299
+ handleUpdateNode(node.id, { ...newCondition, id: node.id });
8300
+ },
8301
+ onDelete: () => handleDeleteNode(node.id),
8302
+ onDuplicate: () => handleDuplicateNode(node.id),
8303
+ onTurnIntoGroup: () => handleWrapInGroup(node.id)
8304
+ },
8305
+ node.id
8306
+ );
8307
+ };
8308
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(PopoverPrimitive13.Root, { open, onOpenChange: setOpen, children: [
8309
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(PopoverPrimitive13.Trigger, { asChild: true, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
7735
8310
  "button",
7736
8311
  {
7737
8312
  type: "button",
@@ -7745,21 +8320,14 @@ var SummaryChip = ({
7745
8320
  className
7746
8321
  ),
7747
8322
  children: [
7748
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7749
- import_icons35.Icon,
7750
- {
7751
- icon: import_icons35.faSlidersOutline,
7752
- size: "sm",
7753
- className: "shrink-0 text-[var(--color-foreground)]"
7754
- }
7755
- ),
7756
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "text-sm font-semibold leading-sm whitespace-nowrap text-[var(--color-foreground)]", children: "Filters" }),
7757
- count > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "flex items-center p-2xs rounded-xs bg-filter-chip-badge-bg", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "text-[10px] font-semibold leading-2xs text-filter-chip-badge-text", children: count }) })
8323
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_icons37.Icon, { icon: import_icons37.faSlidersOutline, size: "sm", className: "shrink-0 text-[var(--color-foreground)]" }),
8324
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-sm font-semibold leading-sm whitespace-nowrap text-[var(--color-foreground)]", children: "Filters" }),
8325
+ count > 0 && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "flex items-center p-2xs rounded-xs bg-filter-chip-badge-bg", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-[10px] font-semibold leading-2xs text-filter-chip-badge-text", children: count }) })
7758
8326
  ]
7759
8327
  }
7760
8328
  ) }),
7761
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(PopoverPrimitive12.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7762
- PopoverPrimitive12.Content,
8329
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(PopoverPrimitive13.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
8330
+ PopoverPrimitive13.Content,
7763
8331
  {
7764
8332
  sideOffset: 4,
7765
8333
  align: "start",
@@ -7771,95 +8339,39 @@ var SummaryChip = ({
7771
8339
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7772
8340
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7773
8341
  "data-[side=bottom]:slide-in-from-top-2",
7774
- "w-[min(520px,calc(100vw-32px))]"
8342
+ "w-[min(640px,calc(100vw-32px))]"
7775
8343
  ),
7776
8344
  children: [
7777
- /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex flex-col gap-base p-base", children: [
7778
- filters.map((filter, i) => {
7779
- const propDef = getPropertyDef(filter.propertyId);
7780
- if (!propDef) return null;
7781
- return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7782
- AdvancedRow,
7783
- {
7784
- connector: i === 0 ? "Where" : (filter.logicOperator ?? "and") === "and" ? "And" : "Or",
7785
- onConnectorToggle: i > 0 ? () => toggleLogicOp(filter.id) : void 0,
7786
- propertyDef: propDef,
7787
- condition: filter,
7788
- properties,
7789
- onUpdate: handleUpdateFilter,
7790
- onPropertyChange: (p) => handlePropertyChange(filter.id, p),
7791
- onDelete: () => handleDeleteFilter(filter.id)
7792
- },
7793
- filter.id
7794
- );
7795
- }),
7796
- filters.length === 0 && /* ── Draft row: inline "Select property" placeholder ──── */
7797
- /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
7798
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }),
7799
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
8345
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "flex flex-col gap-base p-base", children: filters.length > 0 ? filters.map((node, i) => renderNode(node, i)) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8346
+ DraftRow2,
8347
+ {
8348
+ properties,
8349
+ onSelect: handleAddFilter,
8350
+ open: draftPickerOpen,
8351
+ onOpenChange: setDraftPickerOpen
8352
+ }
8353
+ ) }),
8354
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
8355
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "flex items-center gap-sm", children: [
8356
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
7800
8357
  PropertySelector,
7801
8358
  {
7802
8359
  properties,
7803
8360
  onSelect: handleAddFilter,
7804
- open: draftPickerOpen,
7805
- onOpenChange: setDraftPickerOpen,
7806
- children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7807
- "button",
7808
- {
7809
- type: "button",
7810
- className: cn(
7811
- "flex items-center gap-base px-base py-sm min-w-0",
7812
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7813
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7814
- "cursor-pointer transition-colors",
7815
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7816
- ),
7817
- children: [
7818
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-muted-foreground)] whitespace-nowrap", children: "Select property" }),
7819
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7820
- import_icons35.Icon,
7821
- {
7822
- icon: import_icons35.faChevronDownOutline,
7823
- size: "xs",
7824
- className: "shrink-0 text-[var(--color-foreground)]"
7825
- }
7826
- )
7827
- ]
7828
- }
7829
- )
8361
+ open: addSelectorOpen,
8362
+ onOpenChange: setAddSelectorOpen,
8363
+ children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", className: cn(ghostBtn2, "text-[var(--color-foreground)]"), children: [
8364
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_icons37.Icon, { icon: import_icons37.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
8365
+ "Add filter"
8366
+ ] })
7830
8367
  }
7831
- )
7832
- ] })
7833
- ] }),
7834
- /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
7835
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
7836
- PropertySelector,
7837
- {
7838
- properties,
7839
- onSelect: handleAddFilter,
7840
- open: addSelectorOpen,
7841
- onOpenChange: setAddSelectorOpen,
7842
- children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7843
- "button",
7844
- {
7845
- type: "button",
7846
- className: cn(
7847
- "flex items-center gap-sm px-base py-sm",
7848
- "min-h-[32px] max-h-[32px] min-w-[80px]",
7849
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7850
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7851
- "cursor-pointer transition-colors text-sm font-semibold leading-sm text-[var(--color-foreground)]",
7852
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7853
- ),
7854
- children: [
7855
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_icons35.Icon, { icon: import_icons35.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
7856
- "Add filter"
7857
- ]
7858
- }
7859
- )
7860
- }
7861
- ),
7862
- filters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
8368
+ ),
8369
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("button", { type: "button", onClick: handleAddGroup, className: cn(ghostBtn2, "text-[var(--color-foreground)]"), children: [
8370
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_icons37.Icon, { icon: import_icons37.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
8371
+ "Add filters group"
8372
+ ] })
8373
+ ] }),
8374
+ filters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
7863
8375
  "button",
7864
8376
  {
7865
8377
  type: "button",
@@ -7867,8 +8379,11 @@ var SummaryChip = ({
7867
8379
  onClearAll();
7868
8380
  setOpen(false);
7869
8381
  },
7870
- className: "text-sm font-semibold leading-sm text-[var(--color-foreground)] cursor-pointer transition-colors hover:opacity-70 px-base py-sm",
7871
- children: "Clear all filters"
8382
+ className: cn(ghostBtn2, "text-[var(--color-destructive,#ef4444)]"),
8383
+ children: [
8384
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_icons37.Icon, { icon: import_icons37.faXmarkOutline, size: "sm", className: "text-[var(--color-destructive,#ef4444)]" }),
8385
+ "Clear filters"
8386
+ ]
7872
8387
  }
7873
8388
  )
7874
8389
  ] })
@@ -7878,13 +8393,44 @@ var SummaryChip = ({
7878
8393
  ] });
7879
8394
  };
7880
8395
  SummaryChip.displayName = "SummaryChip";
8396
+ var DraftRow2 = ({ properties, onSelect, open: openProp, onOpenChange }) => {
8397
+ const [internalOpen, setInternalOpen] = React51.useState(false);
8398
+ const isCtrl = openProp !== void 0;
8399
+ const open = isCtrl ? openProp : internalOpen;
8400
+ const setOpen = (v) => {
8401
+ if (!isCtrl) setInternalOpen(v);
8402
+ onOpenChange?.(v);
8403
+ };
8404
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
8405
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }),
8406
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(PropertySelector, { properties, onSelect: (p) => {
8407
+ onSelect(p);
8408
+ setOpen(false);
8409
+ }, open, onOpenChange: setOpen, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
8410
+ "button",
8411
+ {
8412
+ type: "button",
8413
+ className: cn(
8414
+ "flex items-center gap-base px-base py-sm min-w-0",
8415
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
8416
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm cursor-pointer transition-colors",
8417
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
8418
+ ),
8419
+ children: [
8420
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-muted-foreground)] whitespace-nowrap", children: "Select property" }),
8421
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_icons37.Icon, { icon: import_icons37.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
8422
+ ]
8423
+ }
8424
+ ) })
8425
+ ] });
8426
+ };
7881
8427
 
7882
8428
  // src/components/ui/filter/use-filter-bar-mode.ts
7883
- var React50 = __toESM(require("react"));
8429
+ var React52 = __toESM(require("react"));
7884
8430
  var DEFAULT_BREAKPOINT = 600;
7885
8431
  function useFilterBarMode(ref, override, breakpoint = DEFAULT_BREAKPOINT) {
7886
- const [mode, setMode] = React50.useState("default");
7887
- React50.useEffect(() => {
8432
+ const [mode, setMode] = React52.useState("default");
8433
+ React52.useEffect(() => {
7888
8434
  if (override) return;
7889
8435
  const el = ref.current;
7890
8436
  if (!el) return;
@@ -7899,7 +8445,7 @@ function useFilterBarMode(ref, override, breakpoint = DEFAULT_BREAKPOINT) {
7899
8445
  }
7900
8446
 
7901
8447
  // src/components/ui/filter/filter-system.tsx
7902
- var import_jsx_runtime57 = require("react/jsx-runtime");
8448
+ var import_jsx_runtime59 = require("react/jsx-runtime");
7903
8449
  var FilterSystem = ({
7904
8450
  properties,
7905
8451
  filterState,
@@ -7911,14 +8457,13 @@ var FilterSystem = ({
7911
8457
  actions,
7912
8458
  className
7913
8459
  }) => {
7914
- const containerRef = React51.useRef(null);
8460
+ const containerRef = React53.useRef(null);
7915
8461
  const mode = useFilterBarMode(containerRef, modeOverride, breakpoint);
7916
- const [propertySelectorOpen, setPropertySelectorOpen] = React51.useState(false);
7917
- const [advancedOpen, setAdvancedOpen] = React51.useState(false);
7918
- const [summaryOpen, setSummaryOpen] = React51.useState(false);
7919
- const [pendingFilterId, setPendingFilterId] = React51.useState(null);
7920
- const allFilters = [...filterState.basicFilters, ...filterState.advancedFilters];
7921
- const totalCount = allFilters.length;
8462
+ const [propertySelectorOpen, setPropertySelectorOpen] = React53.useState(false);
8463
+ const [advancedOpen, setAdvancedOpen] = React53.useState(false);
8464
+ const [summaryOpen, setSummaryOpen] = React53.useState(false);
8465
+ const [pendingFilterId, setPendingFilterId] = React53.useState(null);
8466
+ const totalCount = filterState.basicFilters.length + countConditions(filterState.advancedFilters);
7922
8467
  const handleAddFilter = (property) => {
7923
8468
  const newFilter = createFilterWithDefaults(property.id, property.type);
7924
8469
  if (newFilter.operator && isNoValueOperator(newFilter.operator)) {
@@ -8004,10 +8549,10 @@ var FilterSystem = ({
8004
8549
  const advancedFilterCount = filterState.advancedFilters.length;
8005
8550
  const showAdvancedChip = hasAdvanced || advancedOpen;
8006
8551
  const showSummaryChip = totalCount > 0 || summaryOpen;
8007
- return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(FilterBar, { ref: containerRef, className, children: [
8008
- /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(FilterBarLeft, { className: "flex-nowrap flex-1 min-w-0 overflow-x-auto scrollbar-none outline-none [&>*]:shrink-0", children: [
8552
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(FilterBar, { ref: containerRef, className, children: [
8553
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(FilterBarLeft, { className: "flex-nowrap flex-1 min-w-0 overflow-x-auto scrollbar-none outline-none [&>*]:shrink-0", children: [
8009
8554
  children,
8010
- sortFields && filterState.sort && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8555
+ sortFields && filterState.sort && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8011
8556
  SortButton,
8012
8557
  {
8013
8558
  fields: sortFields,
@@ -8017,23 +8562,23 @@ var FilterSystem = ({
8017
8562
  iconOnly: isMinimal
8018
8563
  }
8019
8564
  ),
8020
- isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
8021
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8565
+ isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
8566
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8022
8567
  "div",
8023
8568
  {
8024
8569
  className: showSummaryChip ? "inline-flex" : "inline-flex w-0 overflow-hidden opacity-0 pointer-events-none",
8025
8570
  "aria-hidden": !showSummaryChip || void 0,
8026
- children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8571
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8027
8572
  SummaryChip,
8028
8573
  {
8029
8574
  count: totalCount,
8030
- filters: allFilters,
8575
+ filters: [...filterState.basicFilters, ...filterState.advancedFilters],
8031
8576
  properties,
8032
- onFiltersChange: (filters) => {
8577
+ onFiltersChange: (nodes) => {
8033
8578
  onFilterStateChange({
8034
8579
  ...filterState,
8035
- basicFilters: filters,
8036
- advancedFilters: []
8580
+ basicFilters: [],
8581
+ advancedFilters: nodes
8037
8582
  });
8038
8583
  },
8039
8584
  onClearAll: handleClearAll,
@@ -8043,7 +8588,7 @@ var FilterSystem = ({
8043
8588
  )
8044
8589
  }
8045
8590
  ),
8046
- !showSummaryChip && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8591
+ !showSummaryChip && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8047
8592
  PropertySelector,
8048
8593
  {
8049
8594
  properties,
@@ -8052,13 +8597,13 @@ var FilterSystem = ({
8052
8597
  onOpenChange: setPropertySelectorOpen,
8053
8598
  onAdvancedFilter: handleOpenAdvanced,
8054
8599
  advancedFilterCount,
8055
- children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(FilterBarButton, { iconOnly: true })
8600
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(FilterBarButton, { iconOnly: true })
8056
8601
  }
8057
8602
  )
8058
8603
  ] }) : (
8059
8604
  /* ── DEFAULT MODE ────────────────────────────────────── */
8060
- /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
8061
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8605
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_jsx_runtime59.Fragment, { children: [
8606
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8062
8607
  AdvancedPopover,
8063
8608
  {
8064
8609
  filters: filterState.advancedFilters,
@@ -8066,12 +8611,12 @@ var FilterSystem = ({
8066
8611
  onFiltersChange: handleAdvancedFiltersChange,
8067
8612
  open: advancedOpen,
8068
8613
  onOpenChange: setAdvancedOpen,
8069
- children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8614
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8070
8615
  "div",
8071
8616
  {
8072
8617
  className: showAdvancedChip ? "inline-flex" : "inline-flex w-0 overflow-hidden opacity-0 pointer-events-none",
8073
8618
  "aria-hidden": !showAdvancedChip || void 0,
8074
- children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8619
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8075
8620
  AdvancedChip,
8076
8621
  {
8077
8622
  count: filterState.advancedFilters.length,
@@ -8086,7 +8631,7 @@ var FilterSystem = ({
8086
8631
  filterState.basicFilters.map((filter) => {
8087
8632
  const propDef = getPropertyDef(filter.propertyId);
8088
8633
  if (!propDef) return null;
8089
- return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8634
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8090
8635
  InteractiveFilterChip,
8091
8636
  {
8092
8637
  propertyDef: propDef,
@@ -8102,7 +8647,7 @@ var FilterSystem = ({
8102
8647
  filter.id
8103
8648
  );
8104
8649
  }),
8105
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8650
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8106
8651
  PropertySelector,
8107
8652
  {
8108
8653
  properties,
@@ -8111,29 +8656,29 @@ var FilterSystem = ({
8111
8656
  onOpenChange: setPropertySelectorOpen,
8112
8657
  onAdvancedFilter: handleOpenAdvanced,
8113
8658
  advancedFilterCount,
8114
- children: totalCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8659
+ children: totalCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8115
8660
  "button",
8116
8661
  {
8117
8662
  type: "button",
8118
8663
  className: "shrink-0 inline-flex items-center justify-center size-8 rounded-md border border-[var(--color-btn-outlined-neutral-border-default)] bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)] shadow-sm cursor-pointer transition-colors hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]",
8119
- children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_icons36.Icon, { icon: import_icons36.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" })
8664
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_icons38.Icon, { icon: import_icons38.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" })
8120
8665
  }
8121
- ) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(FilterBarButton, {})
8666
+ ) : /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(FilterBarButton, {})
8122
8667
  }
8123
8668
  )
8124
8669
  ] })
8125
8670
  ),
8126
- totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8671
+ totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8127
8672
  "button",
8128
8673
  {
8129
8674
  type: "button",
8130
8675
  onClick: handleClearAll,
8131
8676
  className: "shrink-0 flex items-center gap-sm px-base py-sm min-h-[32px] max-h-[32px] rounded-md cursor-pointer transition-colors hover:bg-[var(--color-accent)]",
8132
- children: isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_icons36.Icon, { icon: import_icons36.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" }) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: "Clear" })
8677
+ children: isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_icons38.Icon, { icon: import_icons38.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" }) : /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: "Clear" })
8133
8678
  }
8134
8679
  )
8135
8680
  ] }),
8136
- actions && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(FilterBarRight, { className: "shrink-0 -ml-2xl pl-2xl relative z-10 bg-[linear-gradient(to_right,transparent_0px,var(--filter-bar-bg,var(--color-background,#fff))_24px)]", children: actions })
8681
+ actions && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(FilterBarRight, { className: "shrink-0 -ml-2xl pl-2xl relative z-10 bg-[linear-gradient(to_right,transparent_0px,var(--filter-bar-bg,var(--color-background,#fff))_24px)]", children: actions })
8137
8682
  ] });
8138
8683
  };
8139
8684
  FilterSystem.displayName = "FilterSystem";