@l3mpire/ui 2.11.0 → 2.13.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
@@ -328,7 +328,7 @@ var buttonVariants = (0, import_class_variance_authority2.cva)(
328
328
  },
329
329
  size: {
330
330
  sm: [
331
- "h-6 p-xs gap-xs",
331
+ "h-6 py-xs gap-xs",
332
332
  "text-xs",
333
333
  "min-w-16 rounded-base"
334
334
  ],
@@ -423,13 +423,13 @@ var buttonVariants = (0, import_class_variance_authority2.cva)(
423
423
  appearance: "ghost",
424
424
  intent: "brand",
425
425
  class: [
426
- "bg-btn-ghost-neutral-bg-default",
427
- "text-btn-ghost-neutral-text-default",
428
- "border-btn-ghost-neutral-border-default",
429
- "hover:bg-btn-ghost-neutral-bg-hover",
430
- "hover:text-btn-ghost-neutral-text-hover",
431
- "active:bg-btn-ghost-neutral-bg-pressed",
432
- "active:text-btn-ghost-neutral-text-pressed"
426
+ "bg-btn-ghost-brand-bg-default",
427
+ "text-btn-ghost-brand-text-default",
428
+ "border-btn-ghost-brand-border-default",
429
+ "hover:bg-btn-ghost-brand-bg-hover",
430
+ "hover:text-btn-ghost-brand-text-hover",
431
+ "active:bg-btn-ghost-brand-bg-pressed",
432
+ "active:text-btn-ghost-brand-text-pressed"
433
433
  ]
434
434
  },
435
435
  // ── Ghost + Alert ──────────────────────────────────────────────────
@@ -487,13 +487,17 @@ var Button = React2.forwardRef(
487
487
  const isDisabled = disabled || loading;
488
488
  const isIconOnly = iconOnlyProp ?? !children;
489
489
  const iconSize = iconSizeMap[size ?? "md"];
490
+ const smPadding = size === "sm" && !isIconOnly ? cn(
491
+ leftIcon || loading ? "pl-xs" : "pl-sm",
492
+ rightIcon ? "pr-xs" : "pr-sm"
493
+ ) : "";
490
494
  const variantClasses = buttonVariants({
491
495
  appearance,
492
496
  intent,
493
497
  size,
494
498
  iconOnly: isIconOnly || void 0,
495
499
  fullWidth,
496
- className
500
+ className: cn(smPadding, className)
497
501
  });
498
502
  if (asChild) {
499
503
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_slot.Slot, { ref, className: cn(variantClasses), ...props, children });
@@ -5127,7 +5131,6 @@ var OPERATORS_BY_TYPE = {
5127
5131
  "is on or before",
5128
5132
  "is on or after",
5129
5133
  "is between",
5130
- "is relative",
5131
5134
  "is empty",
5132
5135
  "is not empty"
5133
5136
  ],
@@ -5146,7 +5149,7 @@ var OPERATORS_BY_TYPE = {
5146
5149
  var DEFAULT_OPERATOR_BY_TYPE = {
5147
5150
  text: "contains",
5148
5151
  number: "=",
5149
- date: "is relative",
5152
+ date: "is between",
5150
5153
  enum: "is",
5151
5154
  tags: "contains",
5152
5155
  boolean: "is true",
@@ -5174,7 +5177,6 @@ function getValueInputType(type, operator) {
5174
5177
  return operator === "is between" ? "NumberRange" : "NumberInput";
5175
5178
  if (type === "date") {
5176
5179
  if (operator === "is between") return "DateRange";
5177
- if (operator === "is relative") return "PresetTags";
5178
5180
  return "DatePicker";
5179
5181
  }
5180
5182
  if (type === "enum")
@@ -5466,7 +5468,7 @@ var import_jsx_runtime40 = require("react/jsx-runtime");
5466
5468
  var SaveViewButton = React40.forwardRef(
5467
5469
  ({ className, label = "Save view", onSave, onDropdown, ...props }, ref) => {
5468
5470
  const sharedStyle = [
5469
- "flex items-center justify-center",
5471
+ "relative flex items-center justify-center",
5470
5472
  "min-h-[32px] max-h-[32px]",
5471
5473
  "bg-gradient-to-t from-[var(--color-btn-solid-brand-bg-default)] from-[10%] to-[var(--color-btn-solid-brand-bg-gradient-to-default)]",
5472
5474
  "border border-[var(--color-btn-solid-brand-border-default)]",
@@ -5616,2111 +5618,2525 @@ var OperatorList = ({
5616
5618
  };
5617
5619
  OperatorList.displayName = "OperatorList";
5618
5620
 
5619
- // src/components/ui/filter/value-input.tsx
5621
+ // src/components/ui/filter/value-inputs/shared.ts
5622
+ var inputClasses = [
5623
+ "w-full px-base py-sm rounded-base border border-[var(--color-input)]",
5624
+ "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5625
+ "placeholder:text-[var(--color-muted-foreground)]",
5626
+ "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-0"
5627
+ ].join(" ");
5628
+ var halfInputClasses = [
5629
+ "flex-1 px-base py-sm rounded-base border border-[var(--color-input)]",
5630
+ "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5631
+ "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5632
+ ].join(" ");
5633
+ var applyBtnClasses = "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity";
5634
+
5635
+ // src/components/ui/filter/value-inputs/text-value-input.tsx
5620
5636
  var import_jsx_runtime42 = require("react/jsx-runtime");
5621
- var RELATIVE_DATE_PRESETS = [
5622
- { group: "Past", options: ["Today", "Yesterday", "Last 7 days", "Last 14 days", "Last 30 days", "Last 90 days"] },
5623
- { group: "Current", options: ["This week", "This month", "This quarter", "This year"] },
5624
- { group: "Future", options: ["Tomorrow", "Next 7 days", "Next 14 days", "Next 30 days", "Next week", "Next month", "Next quarter"] }
5625
- ];
5626
- var ValueInput = ({
5627
- dataType,
5628
- operator,
5637
+ var TextValueInput = ({
5629
5638
  value,
5630
5639
  onChange,
5631
5640
  onSubmit,
5632
- options = [],
5633
5641
  className
5634
5642
  }) => {
5635
- const inputType = getValueInputType(dataType, operator);
5636
- if (!inputType) return null;
5637
5643
  const handleKeyDown = (e) => {
5638
5644
  if (e.key === "Enter") onSubmit?.();
5639
5645
  };
5640
- switch (inputType) {
5641
- case "TextInput":
5642
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5643
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5644
- "input",
5645
- {
5646
- type: "text",
5647
- value: value ?? "",
5648
- onChange: (e) => onChange(e.target.value),
5649
- onKeyDown: handleKeyDown,
5650
- placeholder: "Enter value...",
5651
- autoFocus: true,
5652
- className: cn(
5653
- "w-full px-base py-sm rounded-base border border-[var(--color-input)]",
5654
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5655
- "placeholder:text-[var(--color-muted-foreground)]",
5656
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-0"
5657
- )
5658
- }
5659
- ),
5660
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5661
- "button",
5662
- {
5663
- type: "button",
5664
- onClick: onSubmit,
5665
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5666
- children: "Apply"
5667
- }
5668
- )
5669
- ] });
5670
- case "NumberInput":
5671
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5672
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5673
- "input",
5674
- {
5675
- type: "number",
5676
- value: value ?? "",
5677
- onChange: (e) => onChange(e.target.value ? Number(e.target.value) : null),
5678
- onKeyDown: handleKeyDown,
5679
- placeholder: "Enter number...",
5680
- autoFocus: true,
5681
- className: cn(
5682
- "w-full px-base py-sm rounded-base border border-[var(--color-input)]",
5683
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5684
- "placeholder:text-[var(--color-muted-foreground)]",
5685
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-0"
5686
- )
5687
- }
5688
- ),
5689
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5690
- "button",
5691
- {
5692
- type: "button",
5693
- onClick: onSubmit,
5694
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5695
- children: "Apply"
5696
- }
5697
- )
5698
- ] });
5699
- case "NumberRange": {
5700
- const rangeVal = value ?? [0, 0];
5701
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5702
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-base", children: [
5703
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5704
- "input",
5705
- {
5706
- type: "number",
5707
- value: rangeVal[0] ?? "",
5708
- onChange: (e) => onChange([Number(e.target.value), rangeVal[1]]),
5709
- placeholder: "Min",
5710
- autoFocus: true,
5711
- className: cn(
5712
- "flex-1 px-base py-sm rounded-base border border-[var(--color-input)]",
5713
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5714
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5715
- )
5716
- }
5717
- ),
5718
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-sm text-[var(--color-muted-foreground)]", children: "and" }),
5719
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5720
- "input",
5721
- {
5722
- type: "number",
5723
- value: rangeVal[1] ?? "",
5724
- onChange: (e) => onChange([rangeVal[0], Number(e.target.value)]),
5725
- placeholder: "Max",
5726
- className: cn(
5727
- "flex-1 px-base py-sm rounded-base border border-[var(--color-input)]",
5728
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5729
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5730
- )
5731
- }
5732
- )
5733
- ] }),
5734
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5735
- "button",
5736
- {
5737
- type: "button",
5738
- onClick: onSubmit,
5739
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5740
- children: "Apply"
5741
- }
5742
- )
5743
- ] });
5744
- }
5745
- case "PresetTags":
5746
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cn("flex flex-col gap-base p-base max-w-[280px]", className), children: RELATIVE_DATE_PRESETS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-xs", children: [
5747
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)] uppercase px-xs", children: group.group }),
5748
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex flex-wrap gap-xs", children: group.options.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5749
- "button",
5750
- {
5751
- type: "button",
5752
- onClick: () => {
5753
- onChange(preset);
5754
- onSubmit?.();
5755
- },
5756
- className: cn(
5757
- "px-base py-2xs rounded-base border cursor-pointer transition-colors text-sm font-regular leading-sm",
5758
- value === preset ? "border-[var(--color-ring)] bg-[var(--color-primary)] text-[var(--color-primary-foreground)]" : "border-[var(--color-input)] bg-[var(--color-background)] text-[var(--color-foreground)] hover:bg-[var(--color-accent)]"
5759
- ),
5760
- children: preset
5761
- },
5762
- preset
5763
- )) })
5764
- ] }, group.group)) });
5765
- case "SingleSelect":
5766
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: cn("flex flex-col gap-xs p-base max-h-[250px] overflow-y-auto", className), children: options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5767
- "button",
5646
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5647
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5648
+ "input",
5649
+ {
5650
+ type: "text",
5651
+ value: value ?? "",
5652
+ onChange: (e) => onChange(e.target.value),
5653
+ onKeyDown: handleKeyDown,
5654
+ placeholder: "Enter value...",
5655
+ autoFocus: true,
5656
+ className: inputClasses
5657
+ }
5658
+ ),
5659
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
5660
+ ] });
5661
+ };
5662
+ TextValueInput.displayName = "TextValueInput";
5663
+
5664
+ // src/components/ui/filter/value-inputs/number-value-input.tsx
5665
+ var import_jsx_runtime43 = require("react/jsx-runtime");
5666
+ var NumberValueInput = ({
5667
+ value,
5668
+ onChange,
5669
+ onSubmit,
5670
+ className
5671
+ }) => {
5672
+ const handleKeyDown = (e) => {
5673
+ if (e.key === "Enter") onSubmit?.();
5674
+ };
5675
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5676
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5677
+ "input",
5678
+ {
5679
+ type: "number",
5680
+ value: value ?? "",
5681
+ onChange: (e) => onChange(e.target.value ? Number(e.target.value) : null),
5682
+ onKeyDown: handleKeyDown,
5683
+ placeholder: "Enter number...",
5684
+ autoFocus: true,
5685
+ className: inputClasses
5686
+ }
5687
+ ),
5688
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
5689
+ ] });
5690
+ };
5691
+ NumberValueInput.displayName = "NumberValueInput";
5692
+ var NumberRangeValueInput = ({
5693
+ value,
5694
+ onChange,
5695
+ onSubmit,
5696
+ className
5697
+ }) => {
5698
+ const rangeVal = value ?? [0, 0];
5699
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5700
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center gap-base", children: [
5701
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5702
+ "input",
5768
5703
  {
5769
- type: "button",
5770
- onClick: () => {
5771
- onChange(opt);
5772
- onSubmit?.();
5773
- },
5774
- className: cn(
5775
- "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
5776
- "hover:bg-[var(--color-dropdown-item-hover)]",
5777
- value === opt && "bg-[var(--color-dropdown-item-hover)]"
5778
- ),
5779
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)]", children: opt })
5780
- },
5781
- opt
5782
- )) });
5783
- case "MultiSelect": {
5784
- const selected = Array.isArray(value) ? value : [];
5785
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-xs p-base", className), children: [
5786
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex flex-col max-h-[200px] overflow-y-auto", children: options.map((opt) => {
5787
- const isSelected = selected.includes(opt);
5788
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
5789
- "button",
5790
- {
5791
- type: "button",
5792
- onClick: () => {
5793
- const next = isSelected ? selected.filter((s) => s !== opt) : [...selected, opt];
5794
- onChange(next);
5795
- },
5796
- className: cn(
5797
- "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
5798
- "hover:bg-[var(--color-dropdown-item-hover)]"
5799
- ),
5800
- children: [
5801
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5802
- "span",
5803
- {
5804
- className: cn(
5805
- "flex items-center justify-center size-4 rounded-xs border transition-colors",
5806
- isSelected ? "bg-[var(--color-primary)] border-[var(--color-primary)]" : "border-[var(--color-input)] bg-[var(--color-background)]"
5807
- ),
5808
- children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("path", { d: "M2 5L4 7L8 3", stroke: "white", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
5809
- }
5810
- ),
5811
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)]", children: opt })
5812
- ]
5813
- },
5814
- opt
5815
- );
5816
- }) }),
5817
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5818
- "button",
5819
- {
5820
- type: "button",
5821
- onClick: onSubmit,
5822
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5823
- children: "Apply"
5824
- }
5825
- )
5826
- ] });
5827
- }
5828
- // DatePicker, DateRange, RelationPicker, MultiRelationPicker
5829
- // Stub as text inputs for now — will wire to actual DatePicker/relation components
5830
- case "DatePicker":
5831
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5832
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5833
- "input",
5834
- {
5835
- type: "date",
5836
- value: value instanceof Date ? value.toISOString().split("T")[0] : value ?? "",
5837
- onChange: (e) => onChange(e.target.value ? new Date(e.target.value) : null),
5838
- autoFocus: true,
5839
- className: cn(
5840
- "w-full px-base py-sm rounded-base border border-[var(--color-input)]",
5841
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5842
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5843
- )
5844
- }
5845
- ),
5846
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5847
- "button",
5848
- {
5849
- type: "button",
5850
- onClick: onSubmit,
5851
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5852
- children: "Apply"
5853
- }
5854
- )
5855
- ] });
5856
- case "DateRange":
5857
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5858
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-base", children: [
5859
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5860
- "input",
5861
- {
5862
- type: "date",
5863
- autoFocus: true,
5864
- className: cn(
5865
- "flex-1 px-base py-sm rounded-base border border-[var(--color-input)]",
5866
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5867
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5868
- )
5869
- }
5870
- ),
5871
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-sm text-[var(--color-muted-foreground)]", children: "to" }),
5872
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5873
- "input",
5874
- {
5875
- type: "date",
5876
- className: cn(
5877
- "flex-1 px-base py-sm rounded-base border border-[var(--color-input)]",
5878
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5879
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5880
- )
5881
- }
5882
- )
5883
- ] }),
5884
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5885
- "button",
5886
- {
5887
- type: "button",
5888
- onClick: onSubmit,
5889
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5890
- children: "Apply"
5891
- }
5892
- )
5893
- ] });
5894
- case "RelationPicker":
5895
- case "MultiRelationPicker":
5896
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
5897
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5898
- "input",
5899
- {
5900
- type: "text",
5901
- value: value ?? "",
5902
- onChange: (e) => onChange(e.target.value),
5903
- onKeyDown: handleKeyDown,
5904
- placeholder: "Search...",
5905
- autoFocus: true,
5906
- className: cn(
5907
- "w-full px-base py-sm rounded-base border border-[var(--color-input)]",
5908
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
5909
- "placeholder:text-[var(--color-muted-foreground)]",
5910
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)]"
5911
- )
5912
- }
5913
- ),
5914
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5915
- "button",
5916
- {
5917
- type: "button",
5918
- onClick: onSubmit,
5919
- className: "self-end px-md py-sm text-sm font-semibold leading-sm text-[var(--color-primary-foreground)] bg-[var(--color-primary)] rounded-base cursor-pointer hover:opacity-90 transition-opacity",
5920
- children: "Apply"
5921
- }
5922
- )
5923
- ] });
5924
- default:
5925
- return null;
5926
- }
5704
+ type: "number",
5705
+ value: rangeVal[0] ?? "",
5706
+ onChange: (e) => onChange([Number(e.target.value), rangeVal[1]]),
5707
+ placeholder: "Min",
5708
+ autoFocus: true,
5709
+ className: halfInputClasses
5710
+ }
5711
+ ),
5712
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-sm text-[var(--color-muted-foreground)]", children: "and" }),
5713
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5714
+ "input",
5715
+ {
5716
+ type: "number",
5717
+ value: rangeVal[1] ?? "",
5718
+ onChange: (e) => onChange([rangeVal[0], Number(e.target.value)]),
5719
+ placeholder: "Max",
5720
+ className: halfInputClasses
5721
+ }
5722
+ )
5723
+ ] }),
5724
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
5725
+ ] });
5927
5726
  };
5928
- ValueInput.displayName = "ValueInput";
5727
+ NumberRangeValueInput.displayName = "NumberRangeValueInput";
5929
5728
 
5930
- // src/components/ui/filter/property-selector.tsx
5729
+ // src/components/ui/filter/value-inputs/date-value-input.tsx
5730
+ var React42 = __toESM(require("react"));
5731
+
5732
+ // src/components/ui/date-picker.tsx
5931
5733
  var React41 = __toESM(require("react"));
5932
5734
  var PopoverPrimitive5 = __toESM(require("@radix-ui/react-popover"));
5933
5735
  var import_icons27 = require("@l3mpire/icons");
5934
- var import_jsx_runtime43 = require("react/jsx-runtime");
5935
- var PropertySelector = ({
5936
- properties,
5937
- onSelect,
5938
- open,
5939
- onOpenChange,
5940
- children
5941
- }) => {
5942
- const [activeGroup, setActiveGroup] = React41.useState(null);
5943
- const [search, setSearch] = React41.useState("");
5944
- React41.useEffect(() => {
5945
- if (!open) {
5946
- setActiveGroup(null);
5947
- setSearch("");
5948
- }
5949
- }, [open]);
5950
- const groups = React41.useMemo(() => {
5951
- const map = /* @__PURE__ */ new Map();
5952
- for (const prop of properties) {
5953
- const existing = map.get(prop.group);
5954
- if (existing) {
5955
- existing.count++;
5956
- } else {
5957
- map.set(prop.group, {
5958
- group: prop.group,
5959
- groupLabel: prop.groupLabel,
5960
- groupIcon: prop.icon,
5961
- count: 1
5962
- });
5963
- }
5964
- }
5965
- return Array.from(map.values());
5966
- }, [properties]);
5967
- const filteredProperties = React41.useMemo(() => {
5968
- if (!activeGroup) return [];
5969
- const groupProps = properties.filter((p) => p.group === activeGroup);
5970
- if (!search) return groupProps;
5971
- const lower = search.toLowerCase();
5972
- return groupProps.filter((p) => p.label.toLowerCase().includes(lower));
5973
- }, [properties, activeGroup, search]);
5974
- const activeGroupInfo = groups.find((g) => g.group === activeGroup);
5975
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(PopoverPrimitive5.Root, { open, onOpenChange, children: [
5976
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PopoverPrimitive5.Trigger, { asChild: true, children }),
5977
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PopoverPrimitive5.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5978
- PopoverPrimitive5.Content,
5979
- {
5980
- sideOffset: 4,
5981
- align: "start",
5982
- className: cn(
5983
- "z-50 flex flex-col gap-xs overflow-clip p-xs",
5984
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
5985
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
5986
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
5987
- "data-[side=bottom]:slide-in-from-top-2",
5988
- "min-w-[230px]"
5989
- ),
5990
- children: activeGroup === null ? (
5991
- /* ── Level 1: Categories ─────────────────────────────────── */
5992
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex flex-col", children: groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
5993
- "button",
5994
- {
5995
- type: "button",
5996
- onClick: () => setActiveGroup(g.group),
5997
- className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
5998
- children: [
5999
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6000
- import_icons27.Icon,
6001
- {
6002
- icon: g.groupIcon,
6003
- size: "sm",
6004
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6005
- }
6006
- ),
6007
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: g.groupLabel }),
6008
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: g.count }),
6009
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6010
- import_icons27.Icon,
6011
- {
6012
- icon: import_icons27.faChevronRightOutline,
6013
- size: "xs",
6014
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6015
- }
6016
- )
6017
- ]
6018
- },
6019
- g.group
6020
- )) })
6021
- ) : (
6022
- /* ── Level 2: Properties ─────────────────────────────────── */
6023
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-xs", children: [
6024
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
6025
- "button",
6026
- {
6027
- type: "button",
6028
- onClick: () => {
6029
- setActiveGroup(null);
6030
- setSearch("");
6031
- },
6032
- className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6033
- children: [
6034
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6035
- import_icons27.Icon,
6036
- {
6037
- icon: import_icons27.faChevronLeftOutline,
6038
- size: "sm",
6039
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6040
- }
6041
- ),
6042
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-1 text-xs font-semibold leading-xs text-[var(--color-muted-foreground)] text-left truncate", children: activeGroupInfo?.groupLabel })
6043
- ]
6044
- }
6045
- ),
6046
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center gap-base px-md py-base border border-[var(--color-input)] rounded-md", children: [
6047
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6048
- import_icons27.Icon,
6049
- {
6050
- icon: import_icons27.faMagnifyingGlassOutline,
6051
- size: "sm",
6052
- className: "shrink-0 text-[var(--color-muted-foreground)]"
6053
- }
6054
- ),
6055
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6056
- "input",
6057
- {
6058
- type: "text",
6059
- value: search,
6060
- onChange: (e) => setSearch(e.target.value),
6061
- placeholder: "Search...",
6062
- autoFocus: true,
6063
- className: "flex-1 text-sm font-regular leading-sm text-[var(--color-foreground)] bg-transparent outline-none placeholder:text-[var(--color-muted-foreground)]"
6064
- }
6065
- )
6066
- ] }),
6067
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col max-h-[300px] overflow-y-auto", children: [
6068
- filteredProperties.map((prop) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
6069
- "button",
6070
- {
6071
- type: "button",
6072
- onClick: () => {
6073
- onSelect(prop);
6074
- onOpenChange?.(false);
6075
- },
6076
- className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6077
- children: [
6078
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
6079
- import_icons27.Icon,
6080
- {
6081
- icon: prop.icon,
6082
- size: "sm",
6083
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6084
- }
6085
- ),
6086
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: prop.label })
6087
- ]
6088
- },
6089
- prop.id
6090
- )),
6091
- filteredProperties.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "p-base text-sm text-[var(--color-muted-foreground)]", children: "No results" })
6092
- ] })
6093
- ] })
6094
- )
5736
+ var import_jsx_runtime44 = require("react/jsx-runtime");
5737
+ function getDaysInMonth(year, month) {
5738
+ return new Date(year, month + 1, 0).getDate();
5739
+ }
5740
+ function getWeekdayIndex(date) {
5741
+ return (date.getDay() + 6) % 7;
5742
+ }
5743
+ function isSameDay(a, b) {
5744
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
5745
+ }
5746
+ function isInRange(date, from, to) {
5747
+ const t = date.getTime();
5748
+ return t >= from.getTime() && t <= to.getTime();
5749
+ }
5750
+ function startOfDay(d) {
5751
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
5752
+ }
5753
+ var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
5754
+ var MONTH_NAMES = [
5755
+ "January",
5756
+ "February",
5757
+ "March",
5758
+ "April",
5759
+ "May",
5760
+ "June",
5761
+ "July",
5762
+ "August",
5763
+ "September",
5764
+ "October",
5765
+ "November",
5766
+ "December"
5767
+ ];
5768
+ var DatePickerContext = React41.createContext(
5769
+ null
5770
+ );
5771
+ function useDatePickerContext() {
5772
+ const ctx = React41.useContext(DatePickerContext);
5773
+ if (!ctx)
5774
+ throw new Error("DatePicker compound components must be used within <DatePicker>");
5775
+ return ctx;
5776
+ }
5777
+ var DatePicker = React41.forwardRef(
5778
+ ({
5779
+ className,
5780
+ mode = "single",
5781
+ value,
5782
+ onValueChange,
5783
+ defaultMonth,
5784
+ defaultYear,
5785
+ children,
5786
+ ...props
5787
+ }, ref) => {
5788
+ const today = React41.useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
5789
+ const initialDate = React41.useMemo(() => {
5790
+ if (value) {
5791
+ if (value instanceof Date) return value;
5792
+ return value.from;
6095
5793
  }
6096
- ) })
6097
- ] });
5794
+ return today;
5795
+ }, []);
5796
+ const [month, setMonth] = React41.useState(
5797
+ defaultMonth ?? initialDate.getMonth()
5798
+ );
5799
+ const [year, setYear] = React41.useState(
5800
+ defaultYear ?? initialDate.getFullYear()
5801
+ );
5802
+ const [hoveredDate, setHoveredDate] = React41.useState();
5803
+ const goToPrevMonth = React41.useCallback(() => {
5804
+ setMonth((m) => {
5805
+ if (m === 0) {
5806
+ setYear((y) => y - 1);
5807
+ return 11;
5808
+ }
5809
+ return m - 1;
5810
+ });
5811
+ }, []);
5812
+ const goToNextMonth = React41.useCallback(() => {
5813
+ setMonth((m) => {
5814
+ if (m === 11) {
5815
+ setYear((y) => y + 1);
5816
+ return 0;
5817
+ }
5818
+ return m + 1;
5819
+ });
5820
+ }, []);
5821
+ const onSelect = React41.useCallback(
5822
+ (date) => {
5823
+ if (mode === "single") {
5824
+ onValueChange?.(date);
5825
+ return;
5826
+ }
5827
+ if (!value || value instanceof Date) {
5828
+ onValueChange?.({ from: date });
5829
+ return;
5830
+ }
5831
+ const range = value;
5832
+ if (range.to || date.getTime() < range.from.getTime()) {
5833
+ onValueChange?.({ from: date });
5834
+ } else {
5835
+ onValueChange?.({ from: range.from, to: date });
5836
+ }
5837
+ },
5838
+ [mode, value, onValueChange]
5839
+ );
5840
+ const ctxValue = React41.useMemo(
5841
+ () => ({
5842
+ mode,
5843
+ selected: value,
5844
+ onSelect,
5845
+ month,
5846
+ year,
5847
+ setMonth,
5848
+ setYear,
5849
+ goToPrevMonth,
5850
+ goToNextMonth,
5851
+ today,
5852
+ hoveredDate,
5853
+ setHoveredDate
5854
+ }),
5855
+ [
5856
+ mode,
5857
+ value,
5858
+ onSelect,
5859
+ month,
5860
+ year,
5861
+ goToPrevMonth,
5862
+ goToNextMonth,
5863
+ today,
5864
+ hoveredDate
5865
+ ]
5866
+ );
5867
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(DatePickerContext.Provider, { value: ctxValue, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5868
+ "div",
5869
+ {
5870
+ ref,
5871
+ className: cn(
5872
+ "flex flex-col overflow-clip",
5873
+ "bg-datepicker-bg border border-datepicker-border rounded-md shadow-lg",
5874
+ className
5875
+ ),
5876
+ ...props,
5877
+ children
5878
+ }
5879
+ ) });
5880
+ }
5881
+ );
5882
+ DatePicker.displayName = "DatePicker";
5883
+ function defaultFormatDate(date) {
5884
+ return date.toLocaleDateString("en-US", {
5885
+ month: "short",
5886
+ day: "numeric",
5887
+ year: "numeric"
5888
+ });
5889
+ }
5890
+ var DatePickerSelects = React41.forwardRef(({ className, formatDate = defaultFormatDate, ...props }, ref) => {
5891
+ const { selected } = useDatePickerContext();
5892
+ const fromDate = selected instanceof Date ? selected : selected?.from;
5893
+ const toDate = selected instanceof Date ? void 0 : selected?.to;
5894
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5895
+ "div",
5896
+ {
5897
+ ref,
5898
+ className: cn("flex flex-col items-start pt-lg px-lg", className),
5899
+ ...props,
5900
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-base w-full", children: [
5901
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex-1 flex items-center gap-base min-w-0 px-base py-sm bg-gradient-to-t from-[var(--color-select-bg-default)] to-[var(--color-select-bg-gradient-to)] border border-[var(--color-select-border-default)] rounded-base shadow-sm", children: [
5902
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-datepicker-header-text truncate", children: fromDate ? formatDate(fromDate) : "Start date" }),
5903
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_icons27.Icon, { icon: import_icons27.faCalendarOutline, size: "sm", className: "shrink-0 text-datepicker-header-text" })
5904
+ ] }),
5905
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5906
+ import_icons27.Icon,
5907
+ {
5908
+ icon: import_icons27.faArrowRightOutline,
5909
+ size: "sm",
5910
+ className: "shrink-0 text-datepicker-header-weekday"
5911
+ }
5912
+ ),
5913
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex-1 flex items-center gap-base min-w-0 px-base py-sm bg-gradient-to-t from-[var(--color-select-bg-default)] to-[var(--color-select-bg-gradient-to)] border border-[var(--color-select-border-default)] rounded-base shadow-sm", children: [
5914
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-datepicker-header-text truncate", children: toDate ? formatDate(toDate) : "End date" }),
5915
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_icons27.Icon, { icon: import_icons27.faCalendarOutline, size: "sm", className: "shrink-0 text-datepicker-header-text" })
5916
+ ] })
5917
+ ] })
5918
+ }
5919
+ );
5920
+ });
5921
+ DatePickerSelects.displayName = "DatePickerSelects";
5922
+ var DatePickerDay = ({ date, isOutside }) => {
5923
+ const { mode, selected, onSelect, today, hoveredDate, setHoveredDate } = useDatePickerContext();
5924
+ const isToday = isSameDay(date, today);
5925
+ const isSelected = selected instanceof Date ? isSameDay(date, selected) : selected?.from ? isSameDay(date, selected.from) || (selected.to ? isSameDay(date, selected.to) : false) : false;
5926
+ const isRangeStart = mode === "range" && selected && !(selected instanceof Date) && selected.from && isSameDay(date, selected.from);
5927
+ const isRangeEnd = mode === "range" && selected && !(selected instanceof Date) && selected.to && isSameDay(date, selected.to);
5928
+ const inRange = mode === "range" && selected && !(selected instanceof Date) && selected.from && selected.to && !isSelected && isInRange(date, selected.from, selected.to);
5929
+ const inPreviewRange = mode === "range" && selected && !(selected instanceof Date) && selected.from && !selected.to && hoveredDate && !isSelected && hoveredDate.getTime() > selected.from.getTime() && isInRange(date, selected.from, hoveredDate);
5930
+ const isInRangeOrPreview = inRange || inPreviewRange;
5931
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
5932
+ "button",
5933
+ {
5934
+ type: "button",
5935
+ onClick: () => !isOutside && onSelect(date),
5936
+ onMouseEnter: () => mode === "range" && setHoveredDate(date),
5937
+ onMouseLeave: () => mode === "range" && setHoveredDate(void 0),
5938
+ disabled: isOutside,
5939
+ className: cn(
5940
+ "relative flex flex-col items-center justify-center w-9 rounded-full p-2 cursor-pointer transition-colors",
5941
+ "text-sm font-semibold leading-sm text-center",
5942
+ // Default
5943
+ !isOutside && !isSelected && !isInRangeOrPreview && "text-datepicker-day-text-default hover:bg-datepicker-day-bg-hover",
5944
+ // Outside month (disabled)
5945
+ isOutside && "text-datepicker-day-text-disabled cursor-default",
5946
+ // Selected
5947
+ isSelected && "bg-datepicker-day-bg-selected text-datepicker-day-text-selected",
5948
+ // In range
5949
+ isInRangeOrPreview && "bg-datepicker-day-bg-range text-datepicker-day-text-range",
5950
+ // Range start/end get full rounded; in-range items could be less rounded
5951
+ (isRangeStart || isRangeEnd) && "rounded-full"
5952
+ ),
5953
+ children: [
5954
+ date.getDate(),
5955
+ isToday && !isOutside && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1.5 rounded-full bg-datepicker-day-today" })
5956
+ ]
5957
+ }
5958
+ );
6098
5959
  };
6099
- PropertySelector.displayName = "PropertySelector";
6100
-
6101
- // src/components/ui/filter/kebab-menu.tsx
6102
- var PopoverPrimitive6 = __toESM(require("@radix-ui/react-popover"));
6103
- var import_icons28 = require("@l3mpire/icons");
6104
- var import_jsx_runtime44 = require("react/jsx-runtime");
6105
- var KebabMenu = ({
6106
- onConvertToAdvanced,
6107
- onDelete,
6108
- open,
6109
- onOpenChange,
6110
- children
6111
- }) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(PopoverPrimitive6.Root, { open, onOpenChange, children: [
6112
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PopoverPrimitive6.Trigger, { asChild: true, children }),
6113
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PopoverPrimitive6.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6114
- PopoverPrimitive6.Content,
5960
+ var DatePickerCalendar = React41.forwardRef(({ className, header, ...props }, ref) => {
5961
+ const { month, year, goToPrevMonth, goToNextMonth } = useDatePickerContext();
5962
+ const weeks = React41.useMemo(() => {
5963
+ const firstDay = new Date(year, month, 1);
5964
+ const startOffset = getWeekdayIndex(firstDay);
5965
+ const daysInMonth = getDaysInMonth(year, month);
5966
+ const daysInPrevMonth = getDaysInMonth(
5967
+ month === 0 ? year - 1 : year,
5968
+ month === 0 ? 11 : month - 1
5969
+ );
5970
+ const days = [];
5971
+ for (let i = startOffset - 1; i >= 0; i--) {
5972
+ const d = daysInPrevMonth - i;
5973
+ days.push({
5974
+ date: new Date(
5975
+ month === 0 ? year - 1 : year,
5976
+ month === 0 ? 11 : month - 1,
5977
+ d
5978
+ ),
5979
+ isOutside: true
5980
+ });
5981
+ }
5982
+ for (let d = 1; d <= daysInMonth; d++) {
5983
+ days.push({ date: new Date(year, month, d), isOutside: false });
5984
+ }
5985
+ const remaining = 42 - days.length;
5986
+ for (let d = 1; d <= remaining; d++) {
5987
+ days.push({
5988
+ date: new Date(
5989
+ month === 11 ? year + 1 : year,
5990
+ month === 11 ? 0 : month + 1,
5991
+ d
5992
+ ),
5993
+ isOutside: true
5994
+ });
5995
+ }
5996
+ const result = [];
5997
+ for (let i = 0; i < days.length; i += 7) {
5998
+ result.push(days.slice(i, i + 7));
5999
+ }
6000
+ return result;
6001
+ }, [month, year]);
6002
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6003
+ "div",
6115
6004
  {
6116
- sideOffset: 4,
6117
- align: "end",
6118
- className: cn(
6119
- "z-50 flex flex-col p-xs overflow-clip",
6120
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6121
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6122
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6123
- "data-[side=bottom]:slide-in-from-top-2",
6124
- "min-w-[210px]"
6125
- ),
6005
+ ref,
6006
+ className: cn("flex flex-col", className),
6007
+ ...props,
6126
6008
  children: [
6127
- onConvertToAdvanced && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6128
- "button",
6129
- {
6130
- type: "button",
6131
- onClick: () => {
6132
- onConvertToAdvanced();
6133
- onOpenChange?.(false);
6134
- },
6135
- className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6136
- children: [
6009
+ header,
6010
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col gap-lg p-lg", children: [
6011
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center justify-between", children: [
6012
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "text-base font-semibold leading-base text-datepicker-header-text", children: [
6013
+ MONTH_NAMES[month],
6014
+ " ",
6015
+ year
6016
+ ] }),
6017
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-xs", children: [
6137
6018
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6138
- import_icons28.Icon,
6019
+ "button",
6139
6020
  {
6140
- icon: import_icons28.faArrowRightOutline,
6141
- size: "sm",
6142
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6021
+ type: "button",
6022
+ onClick: goToPrevMonth,
6023
+ className: "flex items-center justify-center p-xs rounded-base hover:bg-datepicker-day-bg-hover transition-colors cursor-pointer",
6024
+ "aria-label": "Previous month",
6025
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_icons27.Icon, { icon: import_icons27.faChevronLeftOutline, size: "xs", className: "text-datepicker-header-nav" })
6143
6026
  }
6144
6027
  ),
6145
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)]", children: "Convert to advanced" })
6146
- ]
6147
- }
6148
- ),
6149
- onConvertToAdvanced && onDelete && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "h-px mx-base my-xs bg-[var(--color-border)]" }),
6150
- onDelete && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6151
- "button",
6152
- {
6153
- type: "button",
6154
- onClick: () => {
6155
- onDelete();
6156
- onOpenChange?.(false);
6157
- },
6158
- className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6159
- children: [
6160
6028
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6161
- import_icons28.Icon,
6029
+ "button",
6162
6030
  {
6163
- icon: import_icons28.faTrashOutline,
6164
- size: "sm",
6165
- className: "shrink-0 text-[var(--color-destructive)]"
6031
+ type: "button",
6032
+ onClick: goToNextMonth,
6033
+ className: "flex items-center justify-center p-xs rounded-base hover:bg-datepicker-day-bg-hover transition-colors cursor-pointer",
6034
+ "aria-label": "Next month",
6035
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_icons27.Icon, { icon: import_icons27.faChevronRightOutline, size: "xs", className: "text-datepicker-header-nav" })
6166
6036
  }
6167
- ),
6168
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-destructive)]", children: "Delete filter" })
6169
- ]
6170
- }
6171
- )
6037
+ )
6038
+ ] })
6039
+ ] }),
6040
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col", children: [
6041
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "grid grid-cols-7 gap-base py-sm", children: WEEKDAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6042
+ "span",
6043
+ {
6044
+ className: "w-9 text-center text-xs font-regular leading-xs text-datepicker-header-weekday",
6045
+ children: day
6046
+ },
6047
+ day
6048
+ )) }),
6049
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex flex-col", children: weeks.map((week, wi) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "grid grid-cols-7 gap-base", children: week.map((day, di) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6050
+ DatePickerDay,
6051
+ {
6052
+ date: day.date,
6053
+ isOutside: day.isOutside
6054
+ },
6055
+ di
6056
+ )) }, wi)) })
6057
+ ] })
6058
+ ] })
6172
6059
  ]
6173
6060
  }
6174
- ) })
6175
- ] });
6176
- KebabMenu.displayName = "KebabMenu";
6177
-
6178
- // src/components/ui/filter/filter-editor.tsx
6179
- var React42 = __toESM(require("react"));
6180
- var PopoverPrimitive7 = __toESM(require("@radix-ui/react-popover"));
6181
- var import_icons29 = require("@l3mpire/icons");
6182
- var import_jsx_runtime45 = require("react/jsx-runtime");
6183
- var FilterEditor = ({
6184
- propertyDef,
6185
- condition,
6186
- mode,
6187
- onUpdate,
6188
- onClose,
6189
- open,
6190
- onOpenChange,
6191
- children
6192
- }) => {
6193
- const [view, setView] = React42.useState(
6194
- mode === "add" ? "value" : "operator"
6195
- );
6196
- const [localOperator, setLocalOperator] = React42.useState(
6197
- condition.operator
6198
- );
6199
- const [localValue, setLocalValue] = React42.useState(
6200
- condition.value
6201
6061
  );
6202
- React42.useEffect(() => {
6203
- if (open) {
6204
- setView(mode === "add" ? "value" : "operator");
6205
- setLocalOperator(condition.operator);
6206
- setLocalValue(condition.value);
6207
- }
6208
- }, [open, mode, condition.operator, condition.value]);
6209
- const handleOperatorSelect = (op) => {
6210
- setLocalOperator(op);
6211
- if (isNoValueOperator(op)) {
6212
- onUpdate({ ...condition, operator: op, value: null });
6213
- onOpenChange?.(false);
6214
- onClose();
6215
- } else {
6216
- if (op !== localOperator) {
6217
- setLocalValue(null);
6062
+ });
6063
+ DatePickerCalendar.displayName = "DatePickerCalendar";
6064
+ var DatePickerSuggestions = React41.forwardRef(
6065
+ ({ className, suggestions, formatDate = defaultFormatDate, ...props }, ref) => {
6066
+ const { onSelect, mode } = useDatePickerContext();
6067
+ const onValueChange = React41.useContext(DatePickerContext) ? void 0 : void 0;
6068
+ const ctx = useDatePickerContext();
6069
+ const handleClick = (suggestion) => {
6070
+ const val = suggestion.getValue();
6071
+ if (val instanceof Date) {
6072
+ ctx.onSelect(val);
6073
+ } else {
6074
+ ctx.onSelect(val.from);
6075
+ if (val.to) {
6076
+ setTimeout(() => ctx.onSelect(val.to), 0);
6077
+ }
6218
6078
  }
6219
- setView("value");
6220
- }
6221
- };
6222
- const handleSubmit = () => {
6223
- onUpdate({ ...condition, operator: localOperator, value: localValue });
6224
- onOpenChange?.(false);
6225
- onClose();
6226
- };
6227
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(PopoverPrimitive7.Root, { open, onOpenChange, children: [
6228
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PopoverPrimitive7.Trigger, { asChild: true, children }),
6229
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PopoverPrimitive7.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
6230
- PopoverPrimitive7.Content,
6079
+ };
6080
+ const formatSuggestionDate = (suggestion) => {
6081
+ const val = suggestion.getValue();
6082
+ if (val instanceof Date) {
6083
+ return formatDate(val);
6084
+ }
6085
+ const from = formatDate(val.from);
6086
+ const to = val.to ? formatDate(val.to) : "";
6087
+ return to ? `${from} - ${to}` : from;
6088
+ };
6089
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6090
+ "div",
6231
6091
  {
6232
- sideOffset: 4,
6233
- align: "start",
6092
+ ref,
6234
6093
  className: cn(
6235
- "z-50 flex flex-col overflow-clip",
6236
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6237
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6238
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6239
- "data-[side=bottom]:slide-in-from-top-2",
6240
- "min-w-[240px]"
6094
+ "flex flex-col border-l border-datepicker-border self-stretch shrink-0",
6095
+ className
6241
6096
  ),
6097
+ ...props,
6242
6098
  children: [
6243
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-base px-base pt-base pb-xs border-b border-[var(--color-border)]", children: [
6244
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
6245
- import_icons29.Icon,
6246
- {
6247
- icon: propertyDef.icon,
6248
- size: "sm",
6249
- className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6250
- }
6251
- ),
6252
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: propertyDef.label }),
6253
- localOperator && view === "value" && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
6254
- "button",
6255
- {
6256
- type: "button",
6257
- onClick: () => setView("operator"),
6258
- className: "ml-auto text-xs font-regular text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] cursor-pointer transition-colors",
6259
- children: [
6260
- localOperator,
6261
- " \u25BE"
6262
- ]
6263
- }
6264
- )
6265
- ] }),
6266
- view === "operator" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "p-xs", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
6267
- OperatorList,
6268
- {
6269
- dataType: propertyDef.type,
6270
- activeOperator: localOperator,
6271
- onSelect: handleOperatorSelect
6272
- }
6273
- ) }) : localOperator && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
6274
- ValueInput,
6099
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "pt-lg px-base", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex items-center p-base rounded-base", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-1 text-xs font-semibold leading-xs text-datepicker-suggestion-heading uppercase truncate", children: "Suggestions" }) }) }),
6100
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex flex-1 flex-col p-base min-w-[222px]", children: suggestions.map((suggestion, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
6101
+ "button",
6275
6102
  {
6276
- dataType: propertyDef.type,
6277
- operator: localOperator,
6278
- value: localValue,
6279
- onChange: setLocalValue,
6280
- onSubmit: handleSubmit,
6281
- options: propertyDef.options
6282
- }
6283
- )
6103
+ type: "button",
6104
+ onClick: () => handleClick(suggestion),
6105
+ className: "flex items-center gap-sm p-base rounded-base hover:bg-datepicker-suggestion-hover transition-colors cursor-pointer text-left",
6106
+ children: [
6107
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-sm font-regular leading-sm text-datepicker-suggestion-text truncate shrink-0", children: suggestion.label }),
6108
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-xs font-regular leading-xs text-datepicker-suggestion-date truncate", children: formatSuggestionDate(suggestion) })
6109
+ ]
6110
+ },
6111
+ i
6112
+ )) })
6284
6113
  ]
6285
6114
  }
6286
- ) })
6287
- ] });
6288
- };
6289
- FilterEditor.displayName = "FilterEditor";
6290
-
6291
- // src/components/ui/filter/interactive-filter-chip.tsx
6292
- var React43 = __toESM(require("react"));
6293
- var PopoverPrimitive8 = __toESM(require("@radix-ui/react-popover"));
6294
- var import_jsx_runtime46 = require("react/jsx-runtime");
6295
- function formatFilterValue(value) {
6296
- if (value == null) return void 0;
6297
- if (typeof value === "boolean") return value ? "Yes" : "No";
6298
- if (value instanceof Date) {
6299
- return value.toLocaleDateString("en-US", {
6300
- month: "short",
6301
- day: "numeric",
6302
- year: "numeric"
6303
- });
6304
- }
6305
- if (Array.isArray(value)) {
6306
- if (value.length === 0) return void 0;
6307
- if (value.length === 2 && typeof value[0] === "number") {
6308
- return `${value[0]} \u2013 ${value[1]}`;
6309
- }
6310
- return value[0];
6311
- }
6312
- return String(value);
6313
- }
6314
- function getBadgeCount(value) {
6315
- if (Array.isArray(value) && value.length > 1 && typeof value[0] === "string") {
6316
- return value.length;
6115
+ );
6317
6116
  }
6318
- return void 0;
6319
- }
6320
- var SegmentPopover = ({
6321
- open,
6322
- onOpenChange,
6323
- trigger,
6324
- children,
6325
- align = "start",
6326
- minWidth = "240px"
6327
- }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(PopoverPrimitive8.Root, { open, onOpenChange, children: [
6328
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PopoverPrimitive8.Trigger, { asChild: true, children: trigger }),
6329
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(PopoverPrimitive8.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6330
- PopoverPrimitive8.Content,
6117
+ );
6118
+ DatePickerSuggestions.displayName = "DatePickerSuggestions";
6119
+ var DatePickerFooter = React41.forwardRef(
6120
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6121
+ "div",
6331
6122
  {
6332
- sideOffset: 4,
6333
- align,
6123
+ ref,
6334
6124
  className: cn(
6335
- "z-50 flex flex-col overflow-clip",
6336
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6337
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6338
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6339
- "data-[side=bottom]:slide-in-from-top-2"
6125
+ "flex items-center justify-between p-lg",
6126
+ "border-t border-datepicker-footer-border",
6127
+ "bg-datepicker-bg",
6128
+ className
6340
6129
  ),
6341
- style: { minWidth },
6130
+ ...props,
6342
6131
  children
6343
6132
  }
6344
- ) })
6345
- ] });
6346
- var InteractiveFilterChip = ({
6347
- propertyDef,
6348
- condition,
6349
- properties,
6350
- mode = "edit",
6351
- autoOpen = false,
6352
- onUpdate,
6353
- onPropertyChange,
6354
- onDelete,
6355
- onConvertToAdvanced,
6133
+ )
6134
+ );
6135
+ DatePickerFooter.displayName = "DatePickerFooter";
6136
+ var DatePickerPanel = React41.forwardRef(
6137
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6138
+ "div",
6139
+ {
6140
+ ref,
6141
+ className: cn("flex items-start", className),
6142
+ ...props,
6143
+ children
6144
+ }
6145
+ )
6146
+ );
6147
+ DatePickerPanel.displayName = "DatePickerPanel";
6148
+ var DatePickerRoot = PopoverPrimitive5.Root;
6149
+ var DatePickerTrigger = PopoverPrimitive5.Trigger;
6150
+ var DatePickerPopover = React41.forwardRef(({ className, sideOffset = 4, align = "start", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PopoverPrimitive5.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
6151
+ PopoverPrimitive5.Content,
6152
+ {
6153
+ ref,
6154
+ sideOffset,
6155
+ align,
6156
+ className: cn(
6157
+ "z-50",
6158
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6159
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6160
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
6161
+ className
6162
+ ),
6163
+ ...props,
6164
+ children
6165
+ }
6166
+ ) }));
6167
+ DatePickerPopover.displayName = "DatePickerPopover";
6168
+ function getDefaultSuggestions(referenceDate) {
6169
+ const now = referenceDate ?? /* @__PURE__ */ new Date();
6170
+ const today = startOfDay(now);
6171
+ const dayOfWeek = getWeekdayIndex(today);
6172
+ const startOfThisWeek = new Date(today);
6173
+ startOfThisWeek.setDate(today.getDate() - dayOfWeek);
6174
+ const endOfThisWeek = new Date(startOfThisWeek);
6175
+ endOfThisWeek.setDate(startOfThisWeek.getDate() + 6);
6176
+ const startOfThisMonth = new Date(today.getFullYear(), today.getMonth(), 1);
6177
+ const endOfThisMonth = new Date(
6178
+ today.getFullYear(),
6179
+ today.getMonth() + 1,
6180
+ 0
6181
+ );
6182
+ const startOfThisYear = new Date(today.getFullYear(), 0, 1);
6183
+ const endOfThisYear = new Date(today.getFullYear(), 11, 31);
6184
+ const startOfLastWeek = new Date(startOfThisWeek);
6185
+ startOfLastWeek.setDate(startOfThisWeek.getDate() - 7);
6186
+ const endOfLastWeek = new Date(startOfThisWeek);
6187
+ endOfLastWeek.setDate(startOfThisWeek.getDate() - 1);
6188
+ const startOfLastMonth = new Date(
6189
+ today.getFullYear(),
6190
+ today.getMonth() - 1,
6191
+ 1
6192
+ );
6193
+ const endOfLastMonth = new Date(today.getFullYear(), today.getMonth(), 0);
6194
+ const startOfLastYear = new Date(today.getFullYear() - 1, 0, 1);
6195
+ const endOfLastYear = new Date(today.getFullYear() - 1, 11, 31);
6196
+ return [
6197
+ { label: "Today", getValue: () => today },
6198
+ {
6199
+ label: "This week",
6200
+ getValue: () => ({ from: startOfThisWeek, to: endOfThisWeek })
6201
+ },
6202
+ {
6203
+ label: "This month",
6204
+ getValue: () => ({ from: startOfThisMonth, to: endOfThisMonth })
6205
+ },
6206
+ {
6207
+ label: "This year",
6208
+ getValue: () => ({ from: startOfThisYear, to: endOfThisYear })
6209
+ },
6210
+ {
6211
+ label: "Last week",
6212
+ getValue: () => ({ from: startOfLastWeek, to: endOfLastWeek })
6213
+ },
6214
+ {
6215
+ label: "Last month",
6216
+ getValue: () => ({ from: startOfLastMonth, to: endOfLastMonth })
6217
+ },
6218
+ {
6219
+ label: "Last year",
6220
+ getValue: () => ({ from: startOfLastYear, to: endOfLastYear })
6221
+ }
6222
+ ];
6223
+ }
6224
+
6225
+ // src/components/ui/filter/value-inputs/date-value-input.tsx
6226
+ var import_jsx_runtime45 = require("react/jsx-runtime");
6227
+ var RELATIVE_DATE_PRESETS = [
6228
+ { group: "Past", options: ["Today", "Yesterday", "Last 7 days", "Last 14 days", "Last 30 days", "Last 90 days"] },
6229
+ { group: "Current", options: ["This week", "This month", "This quarter", "This year"] },
6230
+ { group: "Future", options: ["Tomorrow", "Next 7 days", "Next 14 days", "Next 30 days", "Next week", "Next month", "Next quarter"] }
6231
+ ];
6232
+ var DateCalendarValueInput = ({
6233
+ operator,
6234
+ value,
6235
+ onChange,
6236
+ onSubmit,
6356
6237
  className
6357
6238
  }) => {
6358
- const [operatorOpen, setOperatorOpen] = React43.useState(false);
6359
- const [valueOpen, setValueOpen] = React43.useState(false);
6360
- const [propertyOpen, setPropertyOpen] = React43.useState(false);
6361
- const [kebabOpen, setKebabOpen] = React43.useState(false);
6362
- React43.useEffect(() => {
6363
- if (autoOpen && condition.operator && !isNoValueOperator(condition.operator)) {
6364
- const t = setTimeout(() => setValueOpen(true), 50);
6365
- return () => clearTimeout(t);
6239
+ const isRange = operator === "is between";
6240
+ const pickerValue = React42.useMemo(() => {
6241
+ if (isRange) {
6242
+ if (Array.isArray(value) && value.length === 2) {
6243
+ const [from, to] = value;
6244
+ return { from, to };
6245
+ }
6246
+ return void 0;
6366
6247
  }
6367
- }, []);
6368
- const handleOperatorSelect = (op) => {
6369
- if (isNoValueOperator(op)) {
6370
- onUpdate({ ...condition, operator: op, value: null });
6371
- setOperatorOpen(false);
6248
+ return value instanceof Date ? value : void 0;
6249
+ }, [value, isRange]);
6250
+ const handleValueChange = (v) => {
6251
+ if (v instanceof Date) {
6252
+ onChange(v);
6253
+ if (!isRange) onSubmit?.();
6372
6254
  } else {
6373
- const resetValue = op !== condition.operator ? null : condition.value;
6374
- onUpdate({ ...condition, operator: op, value: resetValue });
6375
- setOperatorOpen(false);
6376
- if (resetValue == null) {
6377
- setTimeout(() => setValueOpen(true), 100);
6255
+ const range = v;
6256
+ if (range.from && range.to) {
6257
+ onChange([range.from, range.to]);
6258
+ } else if (range.from) {
6259
+ onChange([range.from, range.from]);
6378
6260
  }
6379
6261
  }
6380
6262
  };
6381
- const handleValueChange = (val) => {
6382
- onUpdate({ ...condition, value: val });
6383
- };
6384
- const handleValueSubmit = () => {
6385
- setValueOpen(false);
6263
+ const suggestions = React42.useMemo(() => getDefaultSuggestions(), []);
6264
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cn("flex flex-col", className), children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
6265
+ DatePicker,
6266
+ {
6267
+ mode: isRange ? "range" : "single",
6268
+ value: pickerValue,
6269
+ onValueChange: handleValueChange,
6270
+ children: [
6271
+ isRange && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(DatePickerSelects, {}),
6272
+ isRange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(DatePickerPanel, { children: [
6273
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(DatePickerCalendar, {}),
6274
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(DatePickerSuggestions, { suggestions })
6275
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(DatePickerCalendar, {}),
6276
+ isRange && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(DatePickerFooter, { children: [
6277
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", {}),
6278
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
6279
+ ] })
6280
+ ]
6281
+ }
6282
+ ) });
6283
+ };
6284
+ DateCalendarValueInput.displayName = "DateCalendarValueInput";
6285
+ var PresetTagsValueInput = ({
6286
+ value,
6287
+ onChange,
6288
+ onSubmit,
6289
+ className
6290
+ }) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: cn("flex flex-col gap-base p-base max-w-[280px]", className), children: RELATIVE_DATE_PRESETS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-col gap-xs", children: [
6291
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)] uppercase px-xs", children: group.group }),
6292
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "flex flex-wrap gap-xs", children: group.options.map((preset) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
6293
+ "button",
6294
+ {
6295
+ type: "button",
6296
+ onClick: () => {
6297
+ onChange(preset);
6298
+ onSubmit?.();
6299
+ },
6300
+ className: cn(
6301
+ "px-base py-2xs rounded-base border cursor-pointer transition-colors text-sm font-regular leading-sm",
6302
+ value === preset ? "border-[var(--color-ring)] bg-[var(--color-primary)] text-[var(--color-primary-foreground)]" : "border-[var(--color-input)] bg-[var(--color-background)] text-[var(--color-foreground)] hover:bg-[var(--color-accent)]"
6303
+ ),
6304
+ children: preset
6305
+ },
6306
+ preset
6307
+ )) })
6308
+ ] }, group.group)) });
6309
+ PresetTagsValueInput.displayName = "PresetTagsValueInput";
6310
+
6311
+ // src/components/ui/filter/value-inputs/select-value-input.tsx
6312
+ var import_icons28 = require("@l3mpire/icons");
6313
+ var import_jsx_runtime46 = require("react/jsx-runtime");
6314
+ var DynamicOptionRow = ({
6315
+ option,
6316
+ selected,
6317
+ multi,
6318
+ onClick
6319
+ }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
6320
+ "button",
6321
+ {
6322
+ type: "button",
6323
+ onClick,
6324
+ className: cn(
6325
+ "flex items-start gap-base p-base rounded-base cursor-pointer transition-colors text-left",
6326
+ "hover:bg-[var(--color-dropdown-item-hover)]",
6327
+ selected && "bg-[var(--color-dropdown-item-hover)]"
6328
+ ),
6329
+ children: [
6330
+ multi && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6331
+ "span",
6332
+ {
6333
+ className: cn(
6334
+ "mt-[2px] flex items-center justify-center size-4 rounded-xs border transition-colors shrink-0",
6335
+ selected ? "bg-[var(--color-primary)] border-[var(--color-primary)]" : "border-[var(--color-input)] bg-[var(--color-background)]"
6336
+ ),
6337
+ children: selected && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6338
+ "path",
6339
+ {
6340
+ d: "M2 5L4 7L8 3",
6341
+ stroke: "white",
6342
+ strokeWidth: "1.5",
6343
+ strokeLinecap: "round",
6344
+ strokeLinejoin: "round"
6345
+ }
6346
+ ) })
6347
+ }
6348
+ ),
6349
+ option.icon && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6350
+ import_icons28.Icon,
6351
+ {
6352
+ icon: option.icon,
6353
+ size: "sm",
6354
+ className: "shrink-0 mt-[1px] text-[var(--color-interactive-text-primary-dark-default,var(--color-primary))]"
6355
+ }
6356
+ ),
6357
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "flex-1 flex flex-col gap-2xs min-w-0", children: [
6358
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] truncate", children: option.label }),
6359
+ option.description && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs font-regular leading-xs text-[var(--color-muted-foreground)]", children: option.description })
6360
+ ] })
6361
+ ]
6362
+ }
6363
+ );
6364
+ var DynamicOptionsDivider = () => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "h-px bg-[var(--color-dropdown-border)] mx-xs my-xs" });
6365
+ var SingleSelectValueInput = ({
6366
+ value,
6367
+ onChange,
6368
+ onSubmit,
6369
+ options,
6370
+ dynamicOptions,
6371
+ className
6372
+ }) => {
6373
+ const pick = (v) => {
6374
+ onChange(v);
6375
+ onSubmit?.();
6386
6376
  };
6387
- const hasOperator = !!condition.operator;
6388
- const displayValue = formatFilterValue(condition.value);
6389
- const hasValue = hasOperator && displayValue != null;
6390
- const badgeCount = getBadgeCount(condition.value);
6391
6377
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
6392
6378
  "div",
6393
6379
  {
6394
6380
  className: cn(
6395
- "inline-flex items-center overflow-clip",
6396
- "bg-filter-chip-bg border border-filter-chip-border rounded-md shadow-sm",
6381
+ "flex flex-col gap-xs p-base max-h-[280px] overflow-y-auto",
6397
6382
  className
6398
6383
  ),
6399
6384
  children: [
6400
- properties ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6401
- PropertySelector,
6402
- {
6403
- properties,
6404
- onSelect: (prop) => {
6405
- onPropertyChange?.(prop);
6406
- setPropertyOpen(false);
6407
- },
6408
- open: propertyOpen,
6409
- onOpenChange: setPropertyOpen,
6410
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6411
- FilterChipSegment,
6412
- {
6413
- segmentType: "property",
6414
- hasBorder: true,
6415
- icon: propertyDef.icon,
6416
- label: propertyDef.label,
6417
- onClick: () => setPropertyOpen(true)
6418
- }
6419
- ) })
6420
- }
6421
- ) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6422
- FilterChipSegment,
6423
- {
6424
- segmentType: "property",
6425
- hasBorder: true,
6426
- icon: propertyDef.icon,
6427
- label: propertyDef.label
6428
- }
6429
- ),
6430
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6431
- SegmentPopover,
6432
- {
6433
- open: operatorOpen,
6434
- onOpenChange: setOperatorOpen,
6435
- minWidth: "180px",
6436
- trigger: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6437
- FilterChipSegment,
6438
- {
6439
- segmentType: hasOperator ? "operator" : "placeholder",
6440
- hasBorder: true,
6441
- label: hasOperator ? condition.operator : "Select condition",
6442
- onClick: () => setOperatorOpen(true)
6443
- }
6444
- ) }),
6445
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "p-xs", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6446
- OperatorList,
6447
- {
6448
- dataType: propertyDef.type,
6449
- activeOperator: condition.operator,
6450
- onSelect: handleOperatorSelect
6451
- }
6452
- ) })
6453
- }
6454
- ),
6455
- hasOperator && condition.operator && !isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6456
- SegmentPopover,
6457
- {
6458
- open: valueOpen,
6459
- onOpenChange: setValueOpen,
6460
- minWidth: "240px",
6461
- trigger: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6462
- FilterChipSegment,
6463
- {
6464
- segmentType: hasValue ? "value" : "placeholder",
6465
- hasBorder: true,
6466
- label: hasValue ? displayValue : "Enter value",
6467
- badgeCount,
6468
- onClick: () => setValueOpen(true)
6469
- }
6470
- ) }),
6471
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6472
- ValueInput,
6473
- {
6474
- dataType: propertyDef.type,
6475
- operator: condition.operator,
6476
- value: condition.value,
6477
- onChange: handleValueChange,
6478
- onSubmit: handleValueSubmit,
6479
- options: propertyDef.options
6480
- }
6481
- )
6482
- }
6483
- ),
6484
- hasOperator && condition.operator && isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6485
- FilterChipSegment,
6385
+ dynamicOptions?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6386
+ DynamicOptionRow,
6486
6387
  {
6487
- segmentType: "value",
6488
- hasBorder: true,
6489
- label: condition.operator
6490
- }
6491
- ),
6492
- hasOperator && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6493
- KebabMenu,
6388
+ option: opt,
6389
+ selected: value === opt.value,
6390
+ multi: false,
6391
+ onClick: () => pick(opt.value)
6392
+ },
6393
+ opt.value
6394
+ )),
6395
+ dynamicOptions && dynamicOptions.length > 0 && options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(DynamicOptionsDivider, {}),
6396
+ options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6397
+ "button",
6494
6398
  {
6495
- open: kebabOpen,
6496
- onOpenChange: setKebabOpen,
6497
- onConvertToAdvanced,
6498
- onDelete,
6499
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6500
- FilterChipSegment,
6501
- {
6502
- segmentType: "button",
6503
- onKebabClick: (e) => {
6504
- e.stopPropagation();
6505
- setKebabOpen(true);
6506
- }
6507
- }
6508
- ) })
6509
- }
6510
- )
6399
+ type: "button",
6400
+ onClick: () => pick(opt),
6401
+ className: cn(
6402
+ "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
6403
+ "hover:bg-[var(--color-dropdown-item-hover)]",
6404
+ value === opt && "bg-[var(--color-dropdown-item-hover)]"
6405
+ ),
6406
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)]", children: opt })
6407
+ },
6408
+ opt
6409
+ ))
6511
6410
  ]
6512
6411
  }
6513
6412
  );
6514
6413
  };
6515
- InteractiveFilterChip.displayName = "InteractiveFilterChip";
6516
-
6517
- // src/components/ui/filter/filter-system.tsx
6518
- var React49 = __toESM(require("react"));
6519
- var import_icons34 = require("@l3mpire/icons");
6414
+ SingleSelectValueInput.displayName = "SingleSelectValueInput";
6415
+ var MultiSelectValueInput = ({
6416
+ value,
6417
+ onChange,
6418
+ onSubmit,
6419
+ options,
6420
+ dynamicOptions,
6421
+ className
6422
+ }) => {
6423
+ const selected = Array.isArray(value) ? value : [];
6424
+ const toggle = (v) => {
6425
+ const next = selected.includes(v) ? selected.filter((s) => s !== v) : [...selected, v];
6426
+ onChange(next);
6427
+ };
6428
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: cn("flex flex-col gap-xs p-base", className), children: [
6429
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-col max-h-[240px] overflow-y-auto", children: [
6430
+ dynamicOptions?.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6431
+ DynamicOptionRow,
6432
+ {
6433
+ option: opt,
6434
+ selected: selected.includes(opt.value),
6435
+ multi: true,
6436
+ onClick: () => toggle(opt.value)
6437
+ },
6438
+ opt.value
6439
+ )),
6440
+ dynamicOptions && dynamicOptions.length > 0 && options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(DynamicOptionsDivider, {}),
6441
+ options.map((opt) => {
6442
+ const isSelected = selected.includes(opt);
6443
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
6444
+ "button",
6445
+ {
6446
+ type: "button",
6447
+ onClick: () => toggle(opt),
6448
+ className: cn(
6449
+ "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
6450
+ "hover:bg-[var(--color-dropdown-item-hover)]"
6451
+ ),
6452
+ children: [
6453
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6454
+ "span",
6455
+ {
6456
+ className: cn(
6457
+ "flex items-center justify-center size-4 rounded-xs border transition-colors",
6458
+ isSelected ? "bg-[var(--color-primary)] border-[var(--color-primary)]" : "border-[var(--color-input)] bg-[var(--color-background)]"
6459
+ ),
6460
+ children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
6461
+ "path",
6462
+ {
6463
+ d: "M2 5L4 7L8 3",
6464
+ stroke: "white",
6465
+ strokeWidth: "1.5",
6466
+ strokeLinecap: "round",
6467
+ strokeLinejoin: "round"
6468
+ }
6469
+ ) })
6470
+ }
6471
+ ),
6472
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)]", children: opt })
6473
+ ]
6474
+ },
6475
+ opt
6476
+ );
6477
+ })
6478
+ ] }),
6479
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
6480
+ ] });
6481
+ };
6482
+ MultiSelectValueInput.displayName = "MultiSelectValueInput";
6520
6483
 
6521
- // src/components/ui/filter/advanced-chip.tsx
6522
- var React44 = __toESM(require("react"));
6523
- var import_icons30 = require("@l3mpire/icons");
6484
+ // src/components/ui/filter/value-inputs/relation-value-input.tsx
6524
6485
  var import_jsx_runtime47 = require("react/jsx-runtime");
6525
- var btnBase = [
6526
- "flex items-center justify-center",
6527
- "min-h-[32px] max-h-[32px]",
6528
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6529
- "border border-[var(--color-btn-outlined-neutral-border-default)] shadow-sm",
6530
- "cursor-pointer transition-colors",
6531
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
6532
- ];
6533
- var AdvancedChip = React44.forwardRef(
6534
- ({ className, count, onClear, onClick, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cn("inline-flex items-center", className), children: [
6535
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
6536
- "button",
6486
+ var RelationValueInput = ({
6487
+ value,
6488
+ onChange,
6489
+ onSubmit,
6490
+ className
6491
+ }) => {
6492
+ const handleKeyDown = (e) => {
6493
+ if (e.key === "Enter") onSubmit?.();
6494
+ };
6495
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cn("flex flex-col gap-base p-base", className), children: [
6496
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
6497
+ "input",
6537
6498
  {
6538
- ref,
6539
- type: "button",
6540
- onClick,
6541
- className: cn(
6542
- btnBase,
6543
- "gap-sm px-base py-sm min-w-[80px]",
6544
- "rounded-l-md -mr-px"
6545
- ),
6546
- ...props,
6547
- children: [
6548
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-sm font-semibold leading-sm whitespace-nowrap text-[var(--color-foreground)]", children: "Advanced filters" }),
6549
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "flex items-center p-2xs rounded-xs bg-filter-chip-badge-bg", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-[10px] font-semibold leading-2xs text-filter-chip-badge-text", children: count }) })
6550
- ]
6499
+ type: "text",
6500
+ value: value ?? "",
6501
+ onChange: (e) => onChange(e.target.value),
6502
+ onKeyDown: handleKeyDown,
6503
+ placeholder: "Search...",
6504
+ autoFocus: true,
6505
+ className: inputClasses
6551
6506
  }
6552
6507
  ),
6553
- onClear && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
6554
- "button",
6555
- {
6556
- type: "button",
6557
- onClick: (e) => {
6558
- e.stopPropagation();
6559
- onClear();
6560
- },
6561
- className: cn(
6562
- btnBase,
6563
- "p-sm",
6564
- "rounded-r-md -ml-px"
6565
- ),
6566
- "aria-label": "Clear all advanced filters",
6567
- children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_icons30.Icon, { icon: import_icons30.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" })
6568
- }
6569
- )
6570
- ] })
6571
- );
6572
- AdvancedChip.displayName = "AdvancedChip";
6573
-
6574
- // src/components/ui/filter/advanced-popover.tsx
6575
- var React46 = __toESM(require("react"));
6576
- var PopoverPrimitive10 = __toESM(require("@radix-ui/react-popover"));
6577
- var import_icons32 = require("@l3mpire/icons");
6508
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: onSubmit, className: applyBtnClasses, children: "Apply" })
6509
+ ] });
6510
+ };
6511
+ RelationValueInput.displayName = "RelationValueInput";
6578
6512
 
6579
- // src/components/ui/filter/advanced-row.tsx
6580
- var React45 = __toESM(require("react"));
6581
- var PopoverPrimitive9 = __toESM(require("@radix-ui/react-popover"));
6582
- var import_icons31 = require("@l3mpire/icons");
6513
+ // src/components/ui/filter/value-input.tsx
6583
6514
  var import_jsx_runtime48 = require("react/jsx-runtime");
6584
- var selectBtnStyle = [
6585
- "flex items-center gap-base",
6586
- "px-base py-sm",
6587
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6588
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
6589
- "cursor-pointer transition-colors",
6590
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
6591
- ];
6592
- var AdvancedRow = ({
6593
- connector,
6594
- onConnectorToggle,
6595
- propertyDef,
6596
- condition,
6597
- properties,
6598
- onUpdate,
6599
- onPropertyChange,
6600
- onDelete
6515
+ var ValueInput = ({
6516
+ dataType,
6517
+ operator,
6518
+ value,
6519
+ onChange,
6520
+ onSubmit,
6521
+ options = [],
6522
+ dynamicOptions,
6523
+ className
6601
6524
  }) => {
6602
- const [operatorOpen, setOperatorOpen] = React45.useState(false);
6603
- const [propertyOpen, setPropertyOpen] = React45.useState(false);
6604
- const handleOperatorSelect = (op) => {
6605
- if (isNoValueOperator(op)) {
6606
- onUpdate({ ...condition, operator: op, value: null });
6607
- } else {
6608
- const resetValue = op !== condition.operator ? null : condition.value;
6609
- onUpdate({ ...condition, operator: op, value: resetValue });
6525
+ const inputType = getValueInputType(dataType, operator);
6526
+ if (!inputType) return null;
6527
+ switch (inputType) {
6528
+ case "TextInput":
6529
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TextValueInput, { value, onChange, onSubmit, className });
6530
+ case "NumberInput":
6531
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(NumberValueInput, { value, onChange, onSubmit, className });
6532
+ case "NumberRange":
6533
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(NumberRangeValueInput, { value, onChange, onSubmit, className });
6534
+ case "SingleSelect":
6535
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6536
+ SingleSelectValueInput,
6537
+ {
6538
+ value,
6539
+ onChange,
6540
+ onSubmit,
6541
+ options,
6542
+ dynamicOptions,
6543
+ className
6544
+ }
6545
+ );
6546
+ case "MultiSelect":
6547
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6548
+ MultiSelectValueInput,
6549
+ {
6550
+ value,
6551
+ onChange,
6552
+ onSubmit,
6553
+ options,
6554
+ dynamicOptions,
6555
+ className
6556
+ }
6557
+ );
6558
+ case "DatePicker":
6559
+ case "DateRange":
6560
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6561
+ DateCalendarValueInput,
6562
+ {
6563
+ operator,
6564
+ value,
6565
+ onChange,
6566
+ onSubmit,
6567
+ className
6568
+ }
6569
+ );
6570
+ case "RelationPicker":
6571
+ case "MultiRelationPicker":
6572
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(RelationValueInput, { value, onChange, onSubmit, className });
6573
+ default:
6574
+ return null;
6575
+ }
6576
+ };
6577
+ ValueInput.displayName = "ValueInput";
6578
+
6579
+ // src/components/ui/filter/property-selector.tsx
6580
+ var React43 = __toESM(require("react"));
6581
+ var PopoverPrimitive6 = __toESM(require("@radix-ui/react-popover"));
6582
+ var import_icons29 = require("@l3mpire/icons");
6583
+ var import_jsx_runtime49 = require("react/jsx-runtime");
6584
+ var AdvancedFilterFooter = ({ onClick, count }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
6585
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "h-px bg-[var(--color-dropdown-border)] mx-xs" }),
6586
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6587
+ "button",
6588
+ {
6589
+ type: "button",
6590
+ onPointerDown: (e) => e.preventDefault(),
6591
+ onClick,
6592
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6593
+ children: [
6594
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6595
+ import_icons29.Icon,
6596
+ {
6597
+ icon: import_icons29.faFilterOutline,
6598
+ size: "sm",
6599
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6600
+ }
6601
+ ),
6602
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: "Advanced filter" }),
6603
+ count > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-xs font-regular leading-xs text-[var(--color-muted-foreground)]", children: [
6604
+ count,
6605
+ " ",
6606
+ count === 1 ? "rule" : "rules"
6607
+ ] })
6608
+ ]
6610
6609
  }
6611
- setOperatorOpen(false);
6612
- };
6613
- const handleValueChange = (val) => {
6614
- onUpdate({ ...condition, value: val });
6610
+ )
6611
+ ] });
6612
+ var PropertySelector = ({
6613
+ properties,
6614
+ onSelect,
6615
+ open,
6616
+ onOpenChange,
6617
+ children,
6618
+ onAdvancedFilter,
6619
+ advancedFilterCount = 0
6620
+ }) => {
6621
+ const handleAdvancedClick = (e) => {
6622
+ e.stopPropagation();
6623
+ e.preventDefault();
6624
+ onAdvancedFilter?.();
6615
6625
  };
6616
- const displayValue = condition.value == null ? "" : typeof condition.value === "string" ? condition.value : String(condition.value);
6617
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-base w-full min-w-0", children: [
6618
- connector === "Where" ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "shrink-0 w-[64px] flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: "Where" }) }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
6619
- "button",
6626
+ const showAdvancedFooter = !!onAdvancedFilter;
6627
+ const [activeGroup, setActiveGroup] = React43.useState(null);
6628
+ const [search, setSearch] = React43.useState("");
6629
+ React43.useEffect(() => {
6630
+ if (!open) {
6631
+ setActiveGroup(null);
6632
+ setSearch("");
6633
+ }
6634
+ }, [open]);
6635
+ const groups = React43.useMemo(() => {
6636
+ const map = /* @__PURE__ */ new Map();
6637
+ for (const prop of properties) {
6638
+ const existing = map.get(prop.group);
6639
+ if (existing) {
6640
+ existing.count++;
6641
+ } else {
6642
+ map.set(prop.group, {
6643
+ group: prop.group,
6644
+ groupLabel: prop.groupLabel,
6645
+ groupIcon: prop.icon,
6646
+ count: 1
6647
+ });
6648
+ }
6649
+ }
6650
+ return Array.from(map.values());
6651
+ }, [properties]);
6652
+ const globalSearchResults = React43.useMemo(() => {
6653
+ if (!search || activeGroup) return [];
6654
+ const lower = search.toLowerCase();
6655
+ return properties.filter((p) => p.label.toLowerCase().includes(lower));
6656
+ }, [properties, search, activeGroup]);
6657
+ const filteredGroups = React43.useMemo(() => {
6658
+ if (!search || activeGroup) return groups;
6659
+ const lower = search.toLowerCase();
6660
+ return groups.filter(
6661
+ (g) => g.groupLabel.toLowerCase().includes(lower) || properties.some(
6662
+ (p) => p.group === g.group && p.label.toLowerCase().includes(lower)
6663
+ )
6664
+ );
6665
+ }, [groups, properties, search, activeGroup]);
6666
+ const filteredProperties = React43.useMemo(() => {
6667
+ if (!activeGroup) return [];
6668
+ const groupProps = properties.filter((p) => p.group === activeGroup);
6669
+ if (!search) return groupProps;
6670
+ const lower = search.toLowerCase();
6671
+ return groupProps.filter((p) => p.label.toLowerCase().includes(lower));
6672
+ }, [properties, activeGroup, search]);
6673
+ const activeGroupInfo = groups.find((g) => g.group === activeGroup);
6674
+ const showGlobalResults = search.length > 0 && !activeGroup && globalSearchResults.length > 0;
6675
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(PopoverPrimitive6.Root, { open, onOpenChange, children: [
6676
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PopoverPrimitive6.Trigger, { asChild: true, children }),
6677
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PopoverPrimitive6.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6678
+ PopoverPrimitive6.Content,
6620
6679
  {
6621
- type: "button",
6622
- onClick: onConnectorToggle,
6680
+ sideOffset: 4,
6681
+ align: "start",
6682
+ onCloseAutoFocus: (e) => e.preventDefault(),
6623
6683
  className: cn(
6624
- "shrink-0 flex items-center justify-center gap-xs",
6625
- "min-w-[64px] min-h-[24px] max-h-[24px] p-xs",
6626
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6627
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-base shadow-sm",
6628
- "cursor-pointer transition-colors text-xs font-semibold leading-xs text-[var(--color-foreground)]",
6629
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
6684
+ "z-50 flex flex-col gap-xs overflow-clip p-xs",
6685
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6686
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6687
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6688
+ "data-[side=bottom]:slide-in-from-top-2",
6689
+ "min-w-[230px]"
6630
6690
  ),
6631
6691
  children: [
6632
- connector,
6633
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: import_icons31.faRefreshOutline, size: "xs", className: "text-[var(--color-foreground)]" })
6634
- ]
6635
- }
6636
- ),
6637
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(PopoverPrimitive9.Root, { open: propertyOpen, onOpenChange: setPropertyOpen, children: [
6638
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PopoverPrimitive9.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
6639
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: propertyDef.icon, size: "sm", className: "shrink-0 text-[var(--color-muted-foreground)]" }),
6640
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate", children: [
6641
- propertyDef.groupLabel,
6642
- " > ",
6643
- propertyDef.label
6644
- ] }),
6645
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: import_icons31.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
6646
- ] }) }),
6647
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PopoverPrimitive9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6648
- PopoverPrimitive9.Content,
6649
- {
6650
- sideOffset: 4,
6651
- align: "start",
6652
- className: cn(
6653
- "z-50 flex flex-col p-xs overflow-clip max-h-[300px] overflow-y-auto",
6654
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6655
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6656
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
6657
- "min-w-[200px]"
6658
- ),
6659
- children: properties.map((p) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
6660
- "button",
6661
- {
6662
- type: "button",
6663
- onClick: () => {
6664
- onPropertyChange(p);
6665
- setPropertyOpen(false);
6666
- },
6667
- className: cn(
6668
- "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
6669
- "hover:bg-[var(--color-dropdown-item-hover)]",
6670
- p.id === condition.propertyId && "bg-[var(--color-dropdown-item-hover)]"
6692
+ activeGroup === null ? (
6693
+ /* ── Level 1: Search + Categories ───────────────────────── */
6694
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-col gap-xs", children: [
6695
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-base px-md py-base border border-[var(--color-input)] rounded-md", children: [
6696
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6697
+ import_icons29.Icon,
6698
+ {
6699
+ icon: import_icons29.faMagnifyingGlassOutline,
6700
+ size: "sm",
6701
+ className: "shrink-0 text-[var(--color-muted-foreground)]"
6702
+ }
6703
+ ),
6704
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6705
+ "input",
6706
+ {
6707
+ type: "text",
6708
+ value: search,
6709
+ onChange: (e) => setSearch(e.target.value),
6710
+ placeholder: "Search...",
6711
+ autoFocus: true,
6712
+ className: "flex-1 text-sm font-regular leading-sm text-[var(--color-foreground)] bg-transparent outline-none placeholder:text-[var(--color-muted-foreground)]"
6713
+ }
6714
+ )
6715
+ ] }),
6716
+ showGlobalResults ? (
6717
+ /* ── Global search results (flat property list) ─────── */
6718
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex flex-col max-h-[300px] overflow-y-auto", children: globalSearchResults.map((prop) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6719
+ "button",
6720
+ {
6721
+ type: "button",
6722
+ onClick: () => {
6723
+ onSelect(prop);
6724
+ onOpenChange?.(false);
6725
+ },
6726
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6727
+ children: [
6728
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6729
+ import_icons29.Icon,
6730
+ {
6731
+ icon: prop.icon,
6732
+ size: "sm",
6733
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6734
+ }
6735
+ ),
6736
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: prop.label }),
6737
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-xs font-regular leading-xs text-[var(--color-muted-foreground)]", children: prop.groupLabel })
6738
+ ]
6739
+ },
6740
+ prop.id
6741
+ )) })
6742
+ ) : (
6743
+ /* ── Group list ─────────────────────────────────────── */
6744
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-col", children: [
6745
+ filteredGroups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6746
+ "button",
6747
+ {
6748
+ type: "button",
6749
+ onClick: () => {
6750
+ setActiveGroup(g.group);
6751
+ setSearch("");
6752
+ },
6753
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6754
+ children: [
6755
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6756
+ import_icons29.Icon,
6757
+ {
6758
+ icon: g.groupIcon,
6759
+ size: "sm",
6760
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6761
+ }
6762
+ ),
6763
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: g.groupLabel }),
6764
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-xs font-semibold leading-xs text-[var(--color-muted-foreground)]", children: g.count }),
6765
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6766
+ import_icons29.Icon,
6767
+ {
6768
+ icon: import_icons29.faChevronRightOutline,
6769
+ size: "xs",
6770
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6771
+ }
6772
+ )
6773
+ ]
6774
+ },
6775
+ g.group
6776
+ )),
6777
+ filteredGroups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "p-base text-sm text-[var(--color-muted-foreground)]", children: "No results" })
6778
+ ] })
6779
+ )
6780
+ ] })
6781
+ ) : (
6782
+ /* ── Level 2: Properties ─────────────────────────────────── */
6783
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-col gap-xs", children: [
6784
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6785
+ "button",
6786
+ {
6787
+ type: "button",
6788
+ onClick: () => {
6789
+ setActiveGroup(null);
6790
+ setSearch("");
6791
+ },
6792
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6793
+ children: [
6794
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6795
+ import_icons29.Icon,
6796
+ {
6797
+ icon: import_icons29.faChevronLeftOutline,
6798
+ size: "sm",
6799
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6800
+ }
6801
+ ),
6802
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-1 text-xs font-semibold leading-xs text-[var(--color-muted-foreground)] text-left truncate", children: activeGroupInfo?.groupLabel })
6803
+ ]
6804
+ }
6671
6805
  ),
6672
- children: [
6673
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: p.icon, size: "sm", className: "shrink-0 text-[var(--color-dropdown-item-icon)]" }),
6674
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] truncate", children: p.label })
6675
- ]
6676
- },
6677
- p.id
6678
- ))
6679
- }
6680
- ) })
6681
- ] }),
6682
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(PopoverPrimitive9.Root, { open: operatorOpen, onOpenChange: setOperatorOpen, children: [
6683
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PopoverPrimitive9.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("button", { type: "button", className: cn(selectBtnStyle, "min-w-0"), children: [
6684
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-foreground)] whitespace-nowrap truncate text-left", children: condition.operator ?? "Select" }),
6685
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: import_icons31.faChevronDownOutline, size: "xs", className: "shrink-0 text-[var(--color-foreground)]" })
6686
- ] }) }),
6687
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PopoverPrimitive9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6688
- PopoverPrimitive9.Content,
6689
- {
6690
- sideOffset: 4,
6691
- align: "start",
6692
- className: cn(
6693
- "z-50 flex flex-col p-xs overflow-clip",
6694
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6695
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6696
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
6697
- "min-w-[160px]"
6806
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-base px-md py-base border border-[var(--color-input)] rounded-md", children: [
6807
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6808
+ import_icons29.Icon,
6809
+ {
6810
+ icon: import_icons29.faMagnifyingGlassOutline,
6811
+ size: "sm",
6812
+ className: "shrink-0 text-[var(--color-muted-foreground)]"
6813
+ }
6814
+ ),
6815
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6816
+ "input",
6817
+ {
6818
+ type: "text",
6819
+ value: search,
6820
+ onChange: (e) => setSearch(e.target.value),
6821
+ placeholder: "Search...",
6822
+ autoFocus: true,
6823
+ className: "flex-1 text-sm font-regular leading-sm text-[var(--color-foreground)] bg-transparent outline-none placeholder:text-[var(--color-muted-foreground)]"
6824
+ }
6825
+ )
6826
+ ] }),
6827
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-col max-h-[300px] overflow-y-auto", children: [
6828
+ filteredProperties.map((prop) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6829
+ "button",
6830
+ {
6831
+ type: "button",
6832
+ onClick: () => {
6833
+ onSelect(prop);
6834
+ onOpenChange?.(false);
6835
+ },
6836
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6837
+ children: [
6838
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6839
+ import_icons29.Icon,
6840
+ {
6841
+ icon: prop.icon,
6842
+ size: "sm",
6843
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6844
+ }
6845
+ ),
6846
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)] text-left truncate", children: prop.label })
6847
+ ]
6848
+ },
6849
+ prop.id
6850
+ )),
6851
+ filteredProperties.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "p-base text-sm text-[var(--color-muted-foreground)]", children: "No results" })
6852
+ ] })
6853
+ ] })
6698
6854
  ),
6699
- children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6700
- OperatorList,
6855
+ showAdvancedFooter && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6856
+ AdvancedFilterFooter,
6701
6857
  {
6702
- dataType: propertyDef.type,
6703
- activeOperator: condition.operator,
6704
- onSelect: handleOperatorSelect
6858
+ onClick: handleAdvancedClick,
6859
+ count: advancedFilterCount
6705
6860
  }
6706
6861
  )
6707
- }
6708
- ) })
6709
- ] }),
6710
- condition.operator && !isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6711
- "input",
6712
- {
6713
- type: "text",
6714
- value: displayValue,
6715
- onChange: (e) => handleValueChange(e.target.value),
6716
- placeholder: "Placeholder",
6717
- className: cn(
6718
- "flex-1 min-w-[80px] px-base py-sm rounded-md",
6719
- "border border-[var(--color-input)]",
6720
- "bg-[var(--color-background)] text-sm font-regular leading-sm text-[var(--color-foreground)]",
6721
- "placeholder:text-[var(--color-muted-foreground)]",
6722
- "focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-0"
6723
- )
6724
- }
6725
- ),
6726
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
6727
- "button",
6728
- {
6729
- type: "button",
6730
- onClick: onDelete,
6731
- className: "ml-auto shrink-0 flex items-center justify-center p-sm rounded-md cursor-pointer transition-colors hover:bg-[var(--color-accent)]",
6732
- "aria-label": "Remove filter",
6733
- children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_icons31.Icon, { icon: import_icons31.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" })
6862
+ ]
6734
6863
  }
6735
- )
6864
+ ) })
6736
6865
  ] });
6737
6866
  };
6738
- AdvancedRow.displayName = "AdvancedRow";
6867
+ PropertySelector.displayName = "PropertySelector";
6739
6868
 
6740
- // src/components/ui/filter/advanced-popover.tsx
6741
- var import_jsx_runtime49 = require("react/jsx-runtime");
6742
- var outlinedBtn = [
6743
- "flex items-center gap-sm px-base py-sm",
6744
- "min-h-[32px] max-h-[32px] min-w-[80px]",
6745
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6746
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
6747
- "cursor-pointer transition-colors text-sm font-semibold leading-sm text-[var(--color-foreground)]",
6748
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
6749
- ];
6750
- var AdvancedPopover = ({
6751
- filters,
6752
- properties,
6753
- onFiltersChange,
6869
+ // src/components/ui/filter/kebab-menu.tsx
6870
+ var PopoverPrimitive7 = __toESM(require("@radix-ui/react-popover"));
6871
+ var import_icons30 = require("@l3mpire/icons");
6872
+ var import_jsx_runtime50 = require("react/jsx-runtime");
6873
+ var KebabMenu = ({
6874
+ onConvertToAdvanced,
6875
+ onDelete,
6876
+ open,
6877
+ onOpenChange,
6878
+ children
6879
+ }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(PopoverPrimitive7.Root, { open, onOpenChange, children: [
6880
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(PopoverPrimitive7.Trigger, { asChild: true, children }),
6881
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(PopoverPrimitive7.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
6882
+ PopoverPrimitive7.Content,
6883
+ {
6884
+ sideOffset: 4,
6885
+ align: "end",
6886
+ className: cn(
6887
+ "z-50 flex flex-col p-xs overflow-clip",
6888
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6889
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6890
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6891
+ "data-[side=bottom]:slide-in-from-top-2",
6892
+ "min-w-[210px]"
6893
+ ),
6894
+ children: [
6895
+ onConvertToAdvanced && /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
6896
+ "button",
6897
+ {
6898
+ type: "button",
6899
+ onClick: () => {
6900
+ onConvertToAdvanced();
6901
+ onOpenChange?.(false);
6902
+ },
6903
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6904
+ children: [
6905
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6906
+ import_icons30.Icon,
6907
+ {
6908
+ icon: import_icons30.faArrowRightOutline,
6909
+ size: "sm",
6910
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6911
+ }
6912
+ ),
6913
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)]", children: "Convert to advanced" })
6914
+ ]
6915
+ }
6916
+ ),
6917
+ onConvertToAdvanced && onDelete && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "h-px mx-base my-xs bg-[var(--color-border)]" }),
6918
+ onDelete && /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
6919
+ "button",
6920
+ {
6921
+ type: "button",
6922
+ onClick: () => {
6923
+ onDelete();
6924
+ onOpenChange?.(false);
6925
+ },
6926
+ className: "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors hover:bg-[var(--color-dropdown-item-hover)]",
6927
+ children: [
6928
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6929
+ import_icons30.Icon,
6930
+ {
6931
+ icon: import_icons30.faTrashOutline,
6932
+ size: "sm",
6933
+ className: "shrink-0 text-[var(--color-destructive)]"
6934
+ }
6935
+ ),
6936
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-sm font-regular leading-sm text-[var(--color-destructive)]", children: "Delete filter" })
6937
+ ]
6938
+ }
6939
+ )
6940
+ ]
6941
+ }
6942
+ ) })
6943
+ ] });
6944
+ KebabMenu.displayName = "KebabMenu";
6945
+
6946
+ // src/components/ui/filter/filter-editor.tsx
6947
+ var React44 = __toESM(require("react"));
6948
+ var PopoverPrimitive8 = __toESM(require("@radix-ui/react-popover"));
6949
+ var import_icons31 = require("@l3mpire/icons");
6950
+ var import_jsx_runtime51 = require("react/jsx-runtime");
6951
+ var FilterEditor = ({
6952
+ propertyDef,
6953
+ condition,
6954
+ mode,
6955
+ onUpdate,
6956
+ onClose,
6754
6957
  open,
6755
6958
  onOpenChange,
6756
6959
  children
6757
6960
  }) => {
6758
- const [addSelectorOpen, setAddSelectorOpen] = React46.useState(false);
6759
- const [logicOps, setLogicOps] = React46.useState({});
6760
- const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
6761
- const handleUpdateFilter = (updated) => {
6762
- onFiltersChange(filters.map((f) => f.id === updated.id ? updated : f));
6763
- };
6764
- const handleDeleteFilter = (id) => {
6765
- onFiltersChange(filters.filter((f) => f.id !== id));
6766
- };
6767
- const handlePropertyChange = (filterId, newProp) => {
6768
- const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
6769
- onFiltersChange(
6770
- filters.map((f) => f.id === filterId ? { ...newCondition, id: filterId } : f)
6771
- );
6772
- };
6773
- const handleAddFilter = (property) => {
6774
- const newFilter = createFilterWithDefaults(property.id, property.type);
6775
- onFiltersChange([...filters, newFilter]);
6776
- setAddSelectorOpen(false);
6961
+ const [view, setView] = React44.useState(
6962
+ mode === "add" ? "value" : "operator"
6963
+ );
6964
+ const [localOperator, setLocalOperator] = React44.useState(
6965
+ condition.operator
6966
+ );
6967
+ const [localValue, setLocalValue] = React44.useState(
6968
+ condition.value
6969
+ );
6970
+ React44.useEffect(() => {
6971
+ if (open) {
6972
+ setView(mode === "add" ? "value" : "operator");
6973
+ setLocalOperator(condition.operator);
6974
+ setLocalValue(condition.value);
6975
+ }
6976
+ }, [open, mode, condition.operator, condition.value]);
6977
+ const handleOperatorSelect = (op) => {
6978
+ setLocalOperator(op);
6979
+ if (isNoValueOperator(op)) {
6980
+ onUpdate({ ...condition, operator: op, value: null });
6981
+ onOpenChange?.(false);
6982
+ onClose();
6983
+ } else {
6984
+ if (op !== localOperator) {
6985
+ setLocalValue(null);
6986
+ }
6987
+ setView("value");
6988
+ }
6777
6989
  };
6778
- const handleClearAll = () => {
6779
- onFiltersChange([]);
6990
+ const handleSubmit = () => {
6991
+ onUpdate({ ...condition, operator: localOperator, value: localValue });
6780
6992
  onOpenChange?.(false);
6993
+ onClose();
6781
6994
  };
6782
- const toggleLogicOp = (filterId) => {
6783
- setLogicOps((prev) => ({
6784
- ...prev,
6785
- [filterId]: (prev[filterId] ?? "and") === "and" ? "or" : "and"
6786
- }));
6787
- };
6788
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(PopoverPrimitive10.Root, { open, onOpenChange, children: [
6789
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PopoverPrimitive10.Trigger, { asChild: true, children }),
6790
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
6791
- PopoverPrimitive10.Content,
6995
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(PopoverPrimitive8.Root, { open, onOpenChange, children: [
6996
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(PopoverPrimitive8.Trigger, { asChild: true, children }),
6997
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(PopoverPrimitive8.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
6998
+ PopoverPrimitive8.Content,
6792
6999
  {
6793
7000
  sideOffset: 4,
6794
7001
  align: "start",
6795
- collisionPadding: 16,
6796
7002
  className: cn(
6797
- "z-50 flex flex-col",
6798
- "bg-[var(--color-background)] rounded-md shadow-lg",
7003
+ "z-50 flex flex-col overflow-clip",
7004
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6799
7005
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6800
7006
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6801
7007
  "data-[side=bottom]:slide-in-from-top-2",
6802
- "w-[min(520px,calc(100vw-32px))]"
7008
+ "min-w-[240px]"
6803
7009
  ),
6804
7010
  children: [
6805
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-col gap-base p-base", children: [
6806
- filters.map((filter, i) => {
6807
- const propDef = getPropertyDef(filter.propertyId);
6808
- if (!propDef) return null;
6809
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6810
- AdvancedRow,
6811
- {
6812
- connector: i === 0 ? "Where" : (logicOps[filter.id] ?? "and") === "and" ? "And" : "Or",
6813
- onConnectorToggle: i > 0 ? () => toggleLogicOp(filter.id) : void 0,
6814
- propertyDef: propDef,
6815
- condition: filter,
6816
- properties,
6817
- onUpdate: handleUpdateFilter,
6818
- onPropertyChange: (p) => handlePropertyChange(filter.id, p),
6819
- onDelete: () => handleDeleteFilter(filter.id)
6820
- },
6821
- filter.id
6822
- );
6823
- }),
6824
- filters.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "py-base text-sm text-[var(--color-muted-foreground)]", children: "No advanced filters yet." })
6825
- ] }),
6826
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
6827
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
6828
- PropertySelector,
7011
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-base px-base pt-base pb-xs border-b border-[var(--color-border)]", children: [
7012
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7013
+ import_icons31.Icon,
6829
7014
  {
6830
- properties,
6831
- onSelect: handleAddFilter,
6832
- open: addSelectorOpen,
6833
- onOpenChange: setAddSelectorOpen,
6834
- children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("button", { type: "button", className: cn(outlinedBtn), children: [
6835
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_icons32.Icon, { icon: import_icons32.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
6836
- "Add filter"
6837
- ] })
7015
+ icon: propertyDef.icon,
7016
+ size: "sm",
7017
+ className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
6838
7018
  }
6839
7019
  ),
6840
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
7020
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: propertyDef.label }),
7021
+ localOperator && view === "value" && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
6841
7022
  "button",
6842
7023
  {
6843
7024
  type: "button",
6844
- onClick: handleClearAll,
6845
- className: "text-sm font-semibold leading-sm text-[var(--color-foreground)] cursor-pointer transition-colors hover:opacity-70 px-base py-sm",
6846
- children: "Clear all filters"
7025
+ onClick: () => setView("operator"),
7026
+ className: "ml-auto text-xs font-regular text-[var(--color-muted-foreground)] hover:text-[var(--color-foreground)] cursor-pointer transition-colors",
7027
+ children: [
7028
+ localOperator,
7029
+ " \u25BE"
7030
+ ]
6847
7031
  }
6848
7032
  )
6849
- ] })
7033
+ ] }),
7034
+ view === "operator" ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "p-xs", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7035
+ OperatorList,
7036
+ {
7037
+ dataType: propertyDef.type,
7038
+ activeOperator: localOperator,
7039
+ onSelect: handleOperatorSelect
7040
+ }
7041
+ ) }) : localOperator && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7042
+ ValueInput,
7043
+ {
7044
+ dataType: propertyDef.type,
7045
+ operator: localOperator,
7046
+ value: localValue,
7047
+ onChange: setLocalValue,
7048
+ onSubmit: handleSubmit,
7049
+ options: propertyDef.options,
7050
+ dynamicOptions: propertyDef.dynamicOptions
7051
+ }
7052
+ )
6850
7053
  ]
6851
7054
  }
6852
7055
  ) })
6853
7056
  ] });
6854
7057
  };
6855
- AdvancedPopover.displayName = "AdvancedPopover";
7058
+ FilterEditor.displayName = "FilterEditor";
6856
7059
 
6857
- // src/components/ui/filter/summary-chip.tsx
6858
- var React47 = __toESM(require("react"));
6859
- var PopoverPrimitive11 = __toESM(require("@radix-ui/react-popover"));
6860
- var import_icons33 = require("@l3mpire/icons");
6861
- var import_jsx_runtime50 = require("react/jsx-runtime");
6862
- var SummaryChip = ({
6863
- count,
6864
- filters,
6865
- properties,
6866
- onFiltersChange,
6867
- onClearAll,
7060
+ // src/components/ui/filter/interactive-filter-chip.tsx
7061
+ var React45 = __toESM(require("react"));
7062
+ var PopoverPrimitive9 = __toESM(require("@radix-ui/react-popover"));
7063
+ 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
+ var SegmentPopover = ({
7094
+ open,
7095
+ onOpenChange,
7096
+ trigger,
6868
7097
  children,
7098
+ align = "start",
7099
+ minWidth = "240px"
7100
+ }) => /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(PopoverPrimitive9.Root, { open, onOpenChange, children: [
7101
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(PopoverPrimitive9.Trigger, { asChild: true, children: trigger }),
7102
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(PopoverPrimitive9.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7103
+ PopoverPrimitive9.Content,
7104
+ {
7105
+ sideOffset: 4,
7106
+ align,
7107
+ className: cn(
7108
+ "z-50 flex flex-col overflow-clip",
7109
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7110
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7111
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7112
+ "data-[side=bottom]:slide-in-from-top-2"
7113
+ ),
7114
+ style: { minWidth },
7115
+ children
7116
+ }
7117
+ ) })
7118
+ ] });
7119
+ var InteractiveFilterChip = ({
7120
+ propertyDef,
7121
+ condition,
7122
+ properties,
7123
+ mode = "edit",
7124
+ autoOpen = false,
7125
+ onUpdate,
7126
+ onPropertyChange,
7127
+ onDelete,
7128
+ onConvertToAdvanced,
6869
7129
  className
6870
7130
  }) => {
6871
- const [open, setOpen] = React47.useState(false);
6872
- const [addSelectorOpen, setAddSelectorOpen] = React47.useState(false);
6873
- const [logicOps, setLogicOps] = React47.useState({});
6874
- const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
6875
- const handleUpdateFilter = (updated) => {
6876
- onFiltersChange(filters.map((f) => f.id === updated.id ? updated : f));
6877
- };
6878
- const handleDeleteFilter = (id) => {
6879
- const next = filters.filter((f) => f.id !== id);
6880
- onFiltersChange(next);
6881
- if (next.length === 0) setOpen(false);
7131
+ const [operatorOpen, setOperatorOpen] = React45.useState(false);
7132
+ const [valueOpen, setValueOpen] = React45.useState(false);
7133
+ const [propertyOpen, setPropertyOpen] = React45.useState(false);
7134
+ const [kebabOpen, setKebabOpen] = React45.useState(false);
7135
+ const [pendingValueOpen, setPendingValueOpen] = React45.useState(false);
7136
+ const autoOpenHandled = React45.useRef(false);
7137
+ React45.useEffect(() => {
7138
+ if (autoOpen && !autoOpenHandled.current && condition.operator && !isNoValueOperator(condition.operator)) {
7139
+ autoOpenHandled.current = true;
7140
+ setValueOpen(true);
7141
+ }
7142
+ }, [autoOpen, condition.operator]);
7143
+ React45.useEffect(() => {
7144
+ if (!operatorOpen && pendingValueOpen) {
7145
+ setPendingValueOpen(false);
7146
+ setValueOpen(true);
7147
+ }
7148
+ }, [operatorOpen, pendingValueOpen]);
7149
+ const handleOperatorSelect = (op) => {
7150
+ if (isNoValueOperator(op)) {
7151
+ onUpdate({ ...condition, operator: op, value: null });
7152
+ setOperatorOpen(false);
7153
+ } else {
7154
+ const resetValue = op !== condition.operator ? null : condition.value;
7155
+ onUpdate({ ...condition, operator: op, value: resetValue });
7156
+ setOperatorOpen(false);
7157
+ if (resetValue == null) {
7158
+ setPendingValueOpen(true);
7159
+ }
7160
+ }
6882
7161
  };
6883
- const handlePropertyChange = (filterId, newProp) => {
6884
- const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
6885
- onFiltersChange(
6886
- filters.map((f) => f.id === filterId ? { ...newCondition, id: filterId } : f)
6887
- );
7162
+ const handleValueChange = (val) => {
7163
+ onUpdate({ ...condition, value: val });
6888
7164
  };
6889
- const handleAddFilter = (property) => {
6890
- const newFilter = createFilterWithDefaults(property.id, property.type);
6891
- onFiltersChange([...filters, newFilter]);
6892
- setAddSelectorOpen(false);
7165
+ const handleValueSubmit = () => {
7166
+ setValueOpen(false);
6893
7167
  };
6894
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(PopoverPrimitive11.Root, { open, onOpenChange: setOpen, children: [
6895
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(PopoverPrimitive11.Trigger, { asChild: true, children: children ?? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
7168
+ const hasOperator = !!condition.operator;
7169
+ const displayValue = formatFilterValue(condition.value);
7170
+ const hasValue = hasOperator && displayValue != null;
7171
+ const badgeCount = getBadgeCount(condition.value);
7172
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
7173
+ "div",
7174
+ {
7175
+ className: cn(
7176
+ "inline-flex items-center overflow-clip",
7177
+ "bg-filter-chip-bg border border-filter-chip-border rounded-md shadow-sm",
7178
+ className
7179
+ ),
7180
+ children: [
7181
+ properties ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7182
+ PropertySelector,
7183
+ {
7184
+ properties,
7185
+ onSelect: (prop) => {
7186
+ onPropertyChange?.(prop);
7187
+ setPropertyOpen(false);
7188
+ },
7189
+ open: propertyOpen,
7190
+ onOpenChange: setPropertyOpen,
7191
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7192
+ FilterChipSegment,
7193
+ {
7194
+ segmentType: "property",
7195
+ hasBorder: true,
7196
+ icon: propertyDef.icon,
7197
+ label: propertyDef.label,
7198
+ onClick: () => setPropertyOpen(true)
7199
+ }
7200
+ ) })
7201
+ }
7202
+ ) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7203
+ FilterChipSegment,
7204
+ {
7205
+ segmentType: "property",
7206
+ hasBorder: true,
7207
+ icon: propertyDef.icon,
7208
+ label: propertyDef.label
7209
+ }
7210
+ ),
7211
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7212
+ SegmentPopover,
7213
+ {
7214
+ open: operatorOpen,
7215
+ onOpenChange: setOperatorOpen,
7216
+ minWidth: "180px",
7217
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7218
+ FilterChipSegment,
7219
+ {
7220
+ segmentType: hasOperator ? "operator" : "placeholder",
7221
+ hasBorder: true,
7222
+ label: hasOperator ? condition.operator : "Select condition",
7223
+ onClick: () => setOperatorOpen(true)
7224
+ }
7225
+ ) }),
7226
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "p-xs", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7227
+ OperatorList,
7228
+ {
7229
+ dataType: propertyDef.type,
7230
+ activeOperator: condition.operator,
7231
+ onSelect: handleOperatorSelect
7232
+ }
7233
+ ) })
7234
+ }
7235
+ ),
7236
+ hasOperator && condition.operator && !isNoValueOperator(condition.operator) && (() => {
7237
+ const inputType = getValueInputType(propertyDef.type, condition.operator);
7238
+ const dateWide = inputType === "DatePicker" || inputType === "DateRange";
7239
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7240
+ SegmentPopover,
7241
+ {
7242
+ open: valueOpen,
7243
+ onOpenChange: setValueOpen,
7244
+ minWidth: dateWide ? "auto" : "240px",
7245
+ trigger: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7246
+ FilterChipSegment,
7247
+ {
7248
+ segmentType: hasValue ? "value" : "placeholder",
7249
+ hasBorder: true,
7250
+ label: hasValue ? displayValue : "Enter value",
7251
+ badgeCount,
7252
+ onClick: () => setValueOpen(true)
7253
+ }
7254
+ ) }),
7255
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7256
+ ValueInput,
7257
+ {
7258
+ dataType: propertyDef.type,
7259
+ operator: condition.operator,
7260
+ value: condition.value,
7261
+ onChange: handleValueChange,
7262
+ onSubmit: handleValueSubmit,
7263
+ options: propertyDef.options,
7264
+ dynamicOptions: propertyDef.dynamicOptions
7265
+ }
7266
+ )
7267
+ }
7268
+ );
7269
+ })(),
7270
+ hasOperator && condition.operator && isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7271
+ FilterChipSegment,
7272
+ {
7273
+ segmentType: "value",
7274
+ hasBorder: true,
7275
+ label: condition.operator
7276
+ }
7277
+ ),
7278
+ hasOperator && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7279
+ KebabMenu,
7280
+ {
7281
+ open: kebabOpen,
7282
+ onOpenChange: setKebabOpen,
7283
+ onConvertToAdvanced,
7284
+ onDelete,
7285
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7286
+ FilterChipSegment,
7287
+ {
7288
+ segmentType: "button",
7289
+ onKebabClick: (e) => {
7290
+ e.stopPropagation();
7291
+ setKebabOpen(true);
7292
+ }
7293
+ }
7294
+ ) })
7295
+ }
7296
+ )
7297
+ ]
7298
+ }
7299
+ );
7300
+ };
7301
+ InteractiveFilterChip.displayName = "InteractiveFilterChip";
7302
+
7303
+ // src/components/ui/filter/filter-system.tsx
7304
+ var React51 = __toESM(require("react"));
7305
+ var import_icons36 = require("@l3mpire/icons");
7306
+
7307
+ // src/components/ui/filter/advanced-chip.tsx
7308
+ var React46 = __toESM(require("react"));
7309
+ var import_icons32 = require("@l3mpire/icons");
7310
+ var import_jsx_runtime53 = require("react/jsx-runtime");
7311
+ var btnBase = [
7312
+ "flex items-center justify-center",
7313
+ "min-h-[32px] max-h-[32px]",
7314
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7315
+ "border border-[var(--color-btn-outlined-neutral-border-default)] shadow-sm",
7316
+ "cursor-pointer transition-colors",
7317
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7318
+ ];
7319
+ var AdvancedChip = React46.forwardRef(
7320
+ ({ className, count, onClear, onClick, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: cn("inline-flex items-center", className), children: [
7321
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
6896
7322
  "button",
6897
7323
  {
7324
+ ref,
6898
7325
  type: "button",
7326
+ onClick,
6899
7327
  className: cn(
6900
- "inline-flex items-center gap-sm px-base py-sm",
6901
- "min-h-[32px] max-h-[32px]",
6902
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6903
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
6904
- "cursor-pointer transition-colors",
6905
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]",
6906
- className
7328
+ btnBase,
7329
+ "gap-sm px-base py-sm min-w-[80px]",
7330
+ "rounded-l-md -mr-px"
6907
7331
  ),
7332
+ ...props,
6908
7333
  children: [
6909
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6910
- import_icons33.Icon,
6911
- {
6912
- icon: import_icons33.faSlidersOutline,
6913
- size: "sm",
6914
- className: "shrink-0 text-[var(--color-foreground)]"
6915
- }
6916
- ),
6917
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-sm font-semibold leading-sm whitespace-nowrap text-[var(--color-foreground)]", children: "Filters" }),
6918
- count > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex items-center p-2xs rounded-xs bg-filter-chip-badge-bg", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-[10px] font-semibold leading-2xs text-filter-chip-badge-text", children: count }) })
7334
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-sm font-semibold leading-sm whitespace-nowrap text-[var(--color-foreground)]", children: "Advanced filters" }),
7335
+ count > 0 && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "flex items-center p-2xs rounded-xs bg-filter-chip-badge-bg", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "text-[10px] font-semibold leading-2xs text-filter-chip-badge-text", children: count }) })
6919
7336
  ]
6920
7337
  }
6921
- ) }),
6922
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(PopoverPrimitive11.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
6923
- PopoverPrimitive11.Content,
7338
+ ),
7339
+ onClear && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
7340
+ "button",
6924
7341
  {
6925
- sideOffset: 4,
6926
- align: "start",
6927
- collisionPadding: 16,
7342
+ type: "button",
7343
+ onClick: (e) => {
7344
+ e.stopPropagation();
7345
+ onClear();
7346
+ },
6928
7347
  className: cn(
6929
- "z-50 flex flex-col overflow-clip",
6930
- "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
6931
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
6932
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
6933
- "data-[side=bottom]:slide-in-from-top-2",
6934
- "w-[min(520px,calc(100vw-32px))]"
7348
+ btnBase,
7349
+ "p-sm",
7350
+ "rounded-r-md -ml-px"
6935
7351
  ),
6936
- children: [
6937
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex flex-col gap-base p-base", children: [
6938
- filters.map((filter, i) => {
6939
- const propDef = getPropertyDef(filter.propertyId);
6940
- if (!propDef) return null;
6941
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6942
- AdvancedRow,
6943
- {
6944
- connector: i === 0 ? "Where" : (logicOps[filter.id] ?? "and") === "and" ? "And" : "Or",
6945
- onConnectorToggle: i > 0 ? () => setLogicOps((prev) => ({ ...prev, [filter.id]: (prev[filter.id] ?? "and") === "and" ? "or" : "and" })) : void 0,
6946
- propertyDef: propDef,
6947
- condition: filter,
6948
- properties,
6949
- onUpdate: handleUpdateFilter,
6950
- onPropertyChange: (p) => handlePropertyChange(filter.id, p),
6951
- onDelete: () => handleDeleteFilter(filter.id)
6952
- },
6953
- filter.id
6954
- );
6955
- }),
6956
- filters.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "py-base text-sm text-[var(--color-muted-foreground)]", children: "No active filters." })
6957
- ] }),
6958
- /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center justify-between p-base border-t border-[var(--color-border)]", children: [
6959
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6960
- PropertySelector,
6961
- {
6962
- properties,
6963
- onSelect: handleAddFilter,
6964
- open: addSelectorOpen,
6965
- onOpenChange: setAddSelectorOpen,
6966
- children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
6967
- "button",
6968
- {
6969
- type: "button",
6970
- className: cn(
6971
- "flex items-center gap-sm px-base py-sm",
6972
- "min-h-[32px] max-h-[32px] min-w-[80px]",
6973
- "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
6974
- "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
6975
- "cursor-pointer transition-colors text-sm font-semibold leading-sm text-[var(--color-foreground)]",
6976
- "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
6977
- ),
6978
- children: [
6979
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_icons33.Icon, { icon: import_icons33.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" }),
6980
- "Add filter"
6981
- ]
6982
- }
6983
- )
6984
- }
6985
- ),
6986
- filters.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6987
- "button",
6988
- {
6989
- type: "button",
6990
- onClick: () => {
6991
- onClearAll();
6992
- setOpen(false);
6993
- },
6994
- className: "text-sm font-semibold leading-sm text-[var(--color-foreground)] cursor-pointer transition-colors hover:opacity-70 px-base py-sm",
6995
- children: "Clear all filters"
6996
- }
6997
- )
6998
- ] })
6999
- ]
7352
+ "aria-label": "Clear all advanced filters",
7353
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_icons32.Icon, { icon: import_icons32.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" })
7000
7354
  }
7001
- ) })
7002
- ] });
7003
- };
7004
- SummaryChip.displayName = "SummaryChip";
7355
+ )
7356
+ ] })
7357
+ );
7358
+ AdvancedChip.displayName = "AdvancedChip";
7005
7359
 
7006
- // src/components/ui/filter/use-filter-bar-mode.ts
7360
+ // src/components/ui/filter/advanced-popover.tsx
7007
7361
  var React48 = __toESM(require("react"));
7008
- function useFilterBarMode(ref, override) {
7009
- const [mode, setMode] = React48.useState("default");
7010
- React48.useEffect(() => {
7011
- if (override) return;
7012
- const el = ref.current;
7013
- if (!el) return;
7014
- const observer = new ResizeObserver((entries) => {
7015
- const width = entries[0]?.contentRect.width ?? 0;
7016
- setMode(width > 600 ? "default" : "minimal");
7017
- });
7018
- observer.observe(el);
7019
- return () => observer.disconnect();
7020
- }, [ref, override]);
7021
- return override ?? mode;
7022
- }
7362
+ var PopoverPrimitive11 = __toESM(require("@radix-ui/react-popover"));
7363
+ var import_icons34 = require("@l3mpire/icons");
7023
7364
 
7024
- // src/components/ui/filter/filter-system.tsx
7025
- var import_jsx_runtime51 = require("react/jsx-runtime");
7026
- var FilterSystem = ({
7365
+ // src/components/ui/filter/advanced-row.tsx
7366
+ var React47 = __toESM(require("react"));
7367
+ var PopoverPrimitive10 = __toESM(require("@radix-ui/react-popover"));
7368
+ var import_icons33 = require("@l3mpire/icons");
7369
+ var import_jsx_runtime54 = require("react/jsx-runtime");
7370
+ var selectBtnStyle = [
7371
+ "flex items-center gap-base",
7372
+ "px-base py-sm",
7373
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7374
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7375
+ "cursor-pointer transition-colors",
7376
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7377
+ ];
7378
+ var AdvancedRow = ({
7379
+ connector,
7380
+ onConnectorToggle,
7381
+ propertyDef,
7382
+ condition,
7027
7383
  properties,
7028
- filterState,
7029
- onFilterStateChange,
7030
- sortFields,
7031
- mode: modeOverride,
7032
- children,
7033
- actions,
7034
- className
7035
- }) => {
7036
- const containerRef = React49.useRef(null);
7037
- const mode = useFilterBarMode(containerRef, modeOverride);
7038
- const [propertySelectorOpen, setPropertySelectorOpen] = React49.useState(false);
7039
- const [advancedOpen, setAdvancedOpen] = React49.useState(false);
7040
- const [pendingFilterId, setPendingFilterId] = React49.useState(null);
7041
- const allFilters = [...filterState.basicFilters, ...filterState.advancedFilters];
7042
- const totalCount = allFilters.length;
7043
- const handleAddFilter = (property) => {
7044
- const newFilter = createFilterWithDefaults(property.id, property.type);
7045
- if (newFilter.operator && isNoValueOperator(newFilter.operator)) {
7046
- onFilterStateChange({
7047
- ...filterState,
7048
- basicFilters: [...filterState.basicFilters, newFilter]
7049
- });
7050
- return;
7051
- }
7052
- setPendingFilterId(newFilter.id);
7053
- onFilterStateChange({
7054
- ...filterState,
7055
- basicFilters: [...filterState.basicFilters, newFilter]
7056
- });
7057
- };
7058
- const handleUpdateFilter = (updated) => {
7059
- onFilterStateChange({
7060
- ...filterState,
7061
- basicFilters: filterState.basicFilters.map(
7062
- (f) => f.id === updated.id ? updated : f
7063
- )
7064
- });
7065
- if (pendingFilterId === updated.id) {
7066
- setPendingFilterId(null);
7067
- }
7068
- };
7069
- const handleDeleteFilter = (id) => {
7070
- onFilterStateChange({
7071
- ...filterState,
7072
- basicFilters: filterState.basicFilters.filter((f) => f.id !== id)
7073
- });
7074
- };
7075
- const handlePropertyChange = (filterId, newProp) => {
7076
- const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
7077
- onFilterStateChange({
7078
- ...filterState,
7079
- basicFilters: filterState.basicFilters.map(
7080
- (f) => f.id === filterId ? { ...newCondition, id: filterId } : f
7081
- )
7082
- });
7083
- if (newCondition.operator && !isNoValueOperator(newCondition.operator)) {
7084
- setPendingFilterId(filterId);
7085
- }
7086
- };
7087
- const handleConvertToAdvanced = (id) => {
7088
- const filter = filterState.basicFilters.find((f) => f.id === id);
7089
- if (!filter) return;
7090
- onFilterStateChange({
7091
- ...filterState,
7092
- basicFilters: filterState.basicFilters.filter((f) => f.id !== id),
7093
- advancedFilters: [...filterState.advancedFilters, filter]
7094
- });
7095
- };
7096
- const handleAdvancedFiltersChange = (filters) => {
7097
- onFilterStateChange({ ...filterState, advancedFilters: filters });
7098
- };
7099
- const handleClearAdvanced = () => {
7100
- onFilterStateChange({ ...filterState, advancedFilters: [] });
7101
- };
7102
- const handleClearAll = () => {
7103
- onFilterStateChange({
7104
- ...filterState,
7105
- basicFilters: [],
7106
- advancedFilters: []
7107
- });
7384
+ onUpdate,
7385
+ onPropertyChange,
7386
+ onDelete
7387
+ }) => {
7388
+ const [operatorOpen, setOperatorOpen] = React47.useState(false);
7389
+ const [propertyOpen, setPropertyOpen] = React47.useState(false);
7390
+ const handleOperatorSelect = (op) => {
7391
+ if (isNoValueOperator(op)) {
7392
+ onUpdate({ ...condition, operator: op, value: null });
7393
+ } else {
7394
+ const resetValue = op !== condition.operator ? null : condition.value;
7395
+ onUpdate({ ...condition, operator: op, value: resetValue });
7396
+ }
7397
+ setOperatorOpen(false);
7108
7398
  };
7109
- const handleSortChange = (field, direction) => {
7110
- onFilterStateChange({ ...filterState, sort: { field, direction } });
7399
+ const handleValueChange = (val) => {
7400
+ onUpdate({ ...condition, value: val });
7111
7401
  };
7112
- const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
7113
- const hasAdvanced = filterState.advancedFilters.length > 0;
7114
- const isMinimal = mode === "minimal";
7115
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(FilterBar, { ref: containerRef, className, children: [
7116
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(FilterBarLeft, { className: "flex-nowrap flex-1 min-w-0 overflow-x-auto scrollbar-none outline-none [&>*]:shrink-0", children: [
7117
- children,
7118
- sortFields && filterState.sort && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7119
- SortButton,
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)(
7405
+ "button",
7406
+ {
7407
+ type: "button",
7408
+ onClick: onConnectorToggle,
7409
+ className: cn(
7410
+ "shrink-0 flex items-center justify-center gap-xs",
7411
+ "min-w-[64px] min-h-[24px] max-h-[24px] p-xs",
7412
+ "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
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-base shadow-sm",
7414
+ "cursor-pointer transition-colors text-xs font-semibold leading-xs text-[var(--color-foreground)]",
7415
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]"
7416
+ ),
7417
+ children: [
7418
+ connector,
7419
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_icons33.Icon, { icon: import_icons33.faRefreshOutline, size: "xs", className: "text-[var(--color-foreground)]" })
7420
+ ]
7421
+ }
7422
+ ),
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)]" })
7432
+ ] }) }),
7433
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7434
+ PopoverPrimitive10.Content,
7120
7435
  {
7121
- fields: sortFields,
7122
- activeField: filterState.sort.field,
7123
- direction: filterState.sort.direction,
7124
- onChange: handleSortChange,
7125
- iconOnly: isMinimal
7126
- }
7127
- ),
7128
- isMinimal ? totalCount > 0 ? (
7129
- /* Has filters → SummaryChip (interactive popover) */
7130
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7131
- SummaryChip,
7132
- {
7133
- count: totalCount,
7134
- filters: allFilters,
7135
- properties,
7136
- onFiltersChange: (filters) => {
7137
- onFilterStateChange({
7138
- ...filterState,
7139
- basicFilters: filters,
7140
- advancedFilters: []
7141
- });
7142
- },
7143
- onClearAll: handleClearAll
7144
- }
7145
- )
7146
- ) : (
7147
- /* No filters → same FilterBarButton as default, icon-only */
7148
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7149
- PropertySelector,
7150
- {
7151
- properties,
7152
- onSelect: handleAddFilter,
7153
- open: propertySelectorOpen,
7154
- onOpenChange: setPropertySelectorOpen,
7155
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(FilterBarButton, { iconOnly: true })
7156
- }
7157
- )
7158
- ) : (
7159
- /* ── DEFAULT MODE ────────────────────────────────────── */
7160
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
7161
- hasAdvanced && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7162
- AdvancedPopover,
7163
- {
7164
- filters: filterState.advancedFilters,
7165
- properties,
7166
- onFiltersChange: handleAdvancedFiltersChange,
7167
- open: advancedOpen,
7168
- onOpenChange: setAdvancedOpen,
7169
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7170
- AdvancedChip,
7171
- {
7172
- count: filterState.advancedFilters.length,
7173
- onClick: () => setAdvancedOpen(true),
7174
- onClear: handleClearAdvanced
7175
- }
7176
- )
7177
- }
7436
+ sideOffset: 4,
7437
+ align: "start",
7438
+ className: cn(
7439
+ "z-50 flex flex-col p-xs overflow-clip max-h-[300px] overflow-y-auto",
7440
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7441
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7442
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
7443
+ "min-w-[200px]"
7178
7444
  ),
7179
- filterState.basicFilters.map((filter) => {
7180
- const propDef = getPropertyDef(filter.propertyId);
7181
- if (!propDef) return null;
7182
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7183
- InteractiveFilterChip,
7184
- {
7185
- propertyDef: propDef,
7186
- condition: filter,
7187
- properties,
7188
- mode: pendingFilterId === filter.id ? "add" : "edit",
7189
- autoOpen: pendingFilterId === filter.id,
7190
- onUpdate: handleUpdateFilter,
7191
- onPropertyChange: (newProp) => handlePropertyChange(filter.id, newProp),
7192
- onDelete: () => handleDeleteFilter(filter.id),
7193
- onConvertToAdvanced: () => handleConvertToAdvanced(filter.id)
7445
+ children: properties.map((p) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
7446
+ "button",
7447
+ {
7448
+ type: "button",
7449
+ onClick: () => {
7450
+ onPropertyChange(p);
7451
+ setPropertyOpen(false);
7194
7452
  },
7195
- filter.id
7196
- );
7197
- }),
7198
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7199
- PropertySelector,
7453
+ className: cn(
7454
+ "flex items-center gap-base p-base rounded-base cursor-pointer transition-colors text-left",
7455
+ "hover:bg-[var(--color-dropdown-item-hover)]",
7456
+ p.id === condition.propertyId && "bg-[var(--color-dropdown-item-hover)]"
7457
+ ),
7458
+ 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 })
7461
+ ]
7462
+ },
7463
+ p.id
7464
+ ))
7465
+ }
7466
+ ) })
7467
+ ] }),
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)]" })
7472
+ ] }) }),
7473
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PopoverPrimitive10.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7474
+ PopoverPrimitive10.Content,
7475
+ {
7476
+ sideOffset: 4,
7477
+ align: "start",
7478
+ className: cn(
7479
+ "z-50 flex flex-col p-xs overflow-clip",
7480
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7481
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7482
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0",
7483
+ "min-w-[160px]"
7484
+ ),
7485
+ children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7486
+ OperatorList,
7200
7487
  {
7201
- properties,
7202
- onSelect: handleAddFilter,
7203
- open: propertySelectorOpen,
7204
- onOpenChange: setPropertySelectorOpen,
7205
- children: totalCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7206
- "button",
7207
- {
7208
- type: "button",
7209
- 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)]",
7210
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_icons34.Icon, { icon: import_icons34.faPlusOutline, size: "sm", className: "text-[var(--color-foreground)]" })
7211
- }
7212
- ) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(FilterBarButton, {})
7488
+ dataType: propertyDef.type,
7489
+ activeOperator: condition.operator,
7490
+ onSelect: handleOperatorSelect
7213
7491
  }
7214
7492
  )
7215
- ] })
7216
- ),
7217
- totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
7218
- "button",
7219
- {
7220
- type: "button",
7221
- onClick: handleClearAll,
7222
- 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)]",
7223
- children: isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_icons34.Icon, { icon: import_icons34.faXmarkOutline, size: "sm", className: "text-[var(--color-foreground)]" }) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: "Clear" })
7224
7493
  }
7225
- )
7494
+ ) })
7226
7495
  ] }),
7227
- actions && /* @__PURE__ */ (0, import_jsx_runtime51.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 })
7228
- ] });
7229
- };
7230
- FilterSystem.displayName = "FilterSystem";
7231
-
7232
- // src/components/ui/date-picker.tsx
7233
- var React50 = __toESM(require("react"));
7234
- var PopoverPrimitive12 = __toESM(require("@radix-ui/react-popover"));
7235
- var import_icons35 = require("@l3mpire/icons");
7236
- var import_jsx_runtime52 = require("react/jsx-runtime");
7237
- function getDaysInMonth(year, month) {
7238
- return new Date(year, month + 1, 0).getDate();
7239
- }
7240
- function getWeekdayIndex(date) {
7241
- return (date.getDay() + 6) % 7;
7242
- }
7243
- function isSameDay(a, b) {
7244
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
7245
- }
7246
- function isInRange(date, from, to) {
7247
- const t = date.getTime();
7248
- return t >= from.getTime() && t <= to.getTime();
7249
- }
7250
- function startOfDay(d) {
7251
- return new Date(d.getFullYear(), d.getMonth(), d.getDate());
7252
- }
7253
- var WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
7254
- var MONTH_NAMES = [
7255
- "January",
7256
- "February",
7257
- "March",
7258
- "April",
7259
- "May",
7260
- "June",
7261
- "July",
7262
- "August",
7263
- "September",
7264
- "October",
7265
- "November",
7266
- "December"
7267
- ];
7268
- var DatePickerContext = React50.createContext(
7269
- null
7270
- );
7271
- function useDatePickerContext() {
7272
- const ctx = React50.useContext(DatePickerContext);
7273
- if (!ctx)
7274
- throw new Error("DatePicker compound components must be used within <DatePicker>");
7275
- return ctx;
7276
- }
7277
- var DatePicker = React50.forwardRef(
7278
- ({
7279
- className,
7280
- mode = "single",
7281
- value,
7282
- onValueChange,
7283
- defaultMonth,
7284
- defaultYear,
7285
- children,
7286
- ...props
7287
- }, ref) => {
7288
- const today = React50.useMemo(() => startOfDay(/* @__PURE__ */ new Date()), []);
7289
- const initialDate = React50.useMemo(() => {
7290
- if (value) {
7291
- if (value instanceof Date) return value;
7292
- return value.from;
7293
- }
7294
- return today;
7295
- }, []);
7296
- const [month, setMonth] = React50.useState(
7297
- defaultMonth ?? initialDate.getMonth()
7298
- );
7299
- const [year, setYear] = React50.useState(
7300
- defaultYear ?? initialDate.getFullYear()
7301
- );
7302
- const [hoveredDate, setHoveredDate] = React50.useState();
7303
- const goToPrevMonth = React50.useCallback(() => {
7304
- setMonth((m) => {
7305
- if (m === 0) {
7306
- setYear((y) => y - 1);
7307
- return 11;
7308
- }
7309
- return m - 1;
7310
- });
7311
- }, []);
7312
- const goToNextMonth = React50.useCallback(() => {
7313
- setMonth((m) => {
7314
- if (m === 11) {
7315
- setYear((y) => y + 1);
7316
- return 0;
7317
- }
7318
- return m + 1;
7319
- });
7320
- }, []);
7321
- const onSelect = React50.useCallback(
7322
- (date) => {
7323
- if (mode === "single") {
7324
- onValueChange?.(date);
7325
- return;
7326
- }
7327
- if (!value || value instanceof Date) {
7328
- onValueChange?.({ from: date });
7329
- return;
7330
- }
7331
- const range = value;
7332
- if (range.to || date.getTime() < range.from.getTime()) {
7333
- onValueChange?.({ from: date });
7334
- } else {
7335
- onValueChange?.({ from: range.from, to: date });
7336
- }
7337
- },
7338
- [mode, value, onValueChange]
7496
+ condition.operator && !isNoValueOperator(condition.operator) && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7497
+ "input",
7498
+ {
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
+ )
7510
+ }
7511
+ ),
7512
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
7513
+ "button",
7514
+ {
7515
+ 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)]" })
7520
+ }
7521
+ )
7522
+ ] });
7523
+ };
7524
+ AdvancedRow.displayName = "AdvancedRow";
7525
+
7526
+ // src/components/ui/filter/advanced-popover.tsx
7527
+ var import_jsx_runtime55 = require("react/jsx-runtime");
7528
+ var outlinedBtn = [
7529
+ "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)]"
7535
+ ];
7536
+ var AdvancedPopover = ({
7537
+ filters,
7538
+ properties,
7539
+ onFiltersChange,
7540
+ open,
7541
+ onOpenChange,
7542
+ children
7543
+ }) => {
7544
+ const [addSelectorOpen, setAddSelectorOpen] = React48.useState(false);
7545
+ const [draftPickerOpen, setDraftPickerOpen] = React48.useState(false);
7546
+ 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)
7339
7557
  );
7340
- const ctxValue = React50.useMemo(
7341
- () => ({
7342
- mode,
7343
- selected: value,
7344
- onSelect,
7345
- month,
7346
- year,
7347
- setMonth,
7348
- setYear,
7349
- goToPrevMonth,
7350
- goToNextMonth,
7351
- today,
7352
- hoveredDate,
7353
- setHoveredDate
7354
- }),
7355
- [
7356
- mode,
7357
- value,
7358
- onSelect,
7359
- month,
7360
- year,
7361
- goToPrevMonth,
7362
- goToNextMonth,
7363
- today,
7364
- hoveredDate
7365
- ]
7558
+ };
7559
+ const handleAddFilter = (property) => {
7560
+ const newFilter = createFilterWithDefaults(property.id, property.type);
7561
+ onFiltersChange([...filters, newFilter]);
7562
+ setAddSelectorOpen(false);
7563
+ };
7564
+ const handleClearAll = () => {
7565
+ onFiltersChange([]);
7566
+ onOpenChange?.(false);
7567
+ };
7568
+ const toggleLogicOp = (filterId) => {
7569
+ onFiltersChange(
7570
+ filters.map(
7571
+ (f) => f.id === filterId ? { ...f, logicOperator: (f.logicOperator ?? "and") === "and" ? "or" : "and" } : f
7572
+ )
7366
7573
  );
7367
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(DatePickerContext.Provider, { value: ctxValue, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7368
- "div",
7574
+ };
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,
7369
7579
  {
7370
- ref,
7580
+ sideOffset: 4,
7581
+ align: "start",
7582
+ collisionPadding: 16,
7583
+ onOpenAutoFocus: (e) => e.preventDefault(),
7371
7584
  className: cn(
7372
- "flex flex-col overflow-clip",
7373
- "bg-datepicker-bg border border-datepicker-border rounded-md shadow-lg",
7374
- className
7585
+ "z-50 flex flex-col",
7586
+ "bg-[var(--color-background)] rounded-md shadow-lg",
7587
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7588
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7589
+ "data-[side=bottom]:slide-in-from-top-2",
7590
+ "w-[min(520px,calc(100vw-32px))]"
7375
7591
  ),
7376
- ...props,
7377
- children
7592
+ 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)(
7616
+ PropertySelector,
7617
+ {
7618
+ properties,
7619
+ 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
+ )
7646
+ }
7647
+ )
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)(
7665
+ "button",
7666
+ {
7667
+ type: "button",
7668
+ 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"
7671
+ }
7672
+ )
7673
+ ] })
7674
+ ]
7378
7675
  }
7379
- ) });
7380
- }
7381
- );
7382
- DatePicker.displayName = "DatePicker";
7383
- function defaultFormatDate(date) {
7384
- return date.toLocaleDateString("en-US", {
7385
- month: "short",
7386
- day: "numeric",
7387
- year: "numeric"
7388
- });
7389
- }
7390
- var DatePickerSelects = React50.forwardRef(({ className, formatDate = defaultFormatDate, ...props }, ref) => {
7391
- const { selected } = useDatePickerContext();
7392
- const fromDate = selected instanceof Date ? selected : selected?.from;
7393
- const toDate = selected instanceof Date ? void 0 : selected?.to;
7394
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7395
- "div",
7396
- {
7397
- ref,
7398
- className: cn("flex flex-col items-start pt-lg px-lg", className),
7399
- ...props,
7400
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-base w-full", children: [
7401
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex-1 flex items-center gap-base min-w-0 px-base py-sm bg-gradient-to-t from-[var(--color-select-bg-default)] to-[var(--color-select-bg-gradient-to)] border border-[var(--color-select-border-default)] rounded-base shadow-sm", children: [
7402
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-datepicker-header-text truncate", children: fromDate ? formatDate(fromDate) : "Start date" }),
7403
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_icons35.Icon, { icon: import_icons35.faCalendarOutline, size: "sm", className: "shrink-0 text-datepicker-header-text" })
7404
- ] }),
7405
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7406
- import_icons35.Icon,
7407
- {
7408
- icon: import_icons35.faArrowRightOutline,
7409
- size: "sm",
7410
- className: "shrink-0 text-datepicker-header-weekday"
7411
- }
7412
- ),
7413
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex-1 flex items-center gap-base min-w-0 px-base py-sm bg-gradient-to-t from-[var(--color-select-bg-default)] to-[var(--color-select-bg-gradient-to)] border border-[var(--color-select-border-default)] rounded-base shadow-sm", children: [
7414
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "flex-1 text-sm font-regular leading-sm text-datepicker-header-text truncate", children: toDate ? formatDate(toDate) : "End date" }),
7415
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_icons35.Icon, { icon: import_icons35.faCalendarOutline, size: "sm", className: "shrink-0 text-datepicker-header-text" })
7416
- ] })
7417
- ] })
7418
- }
7419
- );
7420
- });
7421
- DatePickerSelects.displayName = "DatePickerSelects";
7422
- var DatePickerDay = ({ date, isOutside }) => {
7423
- const { mode, selected, onSelect, today, hoveredDate, setHoveredDate } = useDatePickerContext();
7424
- const isToday = isSameDay(date, today);
7425
- const isSelected = selected instanceof Date ? isSameDay(date, selected) : selected?.from ? isSameDay(date, selected.from) || (selected.to ? isSameDay(date, selected.to) : false) : false;
7426
- const isRangeStart = mode === "range" && selected && !(selected instanceof Date) && selected.from && isSameDay(date, selected.from);
7427
- const isRangeEnd = mode === "range" && selected && !(selected instanceof Date) && selected.to && isSameDay(date, selected.to);
7428
- const inRange = mode === "range" && selected && !(selected instanceof Date) && selected.from && selected.to && !isSelected && isInRange(date, selected.from, selected.to);
7429
- const inPreviewRange = mode === "range" && selected && !(selected instanceof Date) && selected.from && !selected.to && hoveredDate && !isSelected && hoveredDate.getTime() > selected.from.getTime() && isInRange(date, selected.from, hoveredDate);
7430
- const isInRangeOrPreview = inRange || inPreviewRange;
7431
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
7432
- "button",
7433
- {
7434
- type: "button",
7435
- onClick: () => !isOutside && onSelect(date),
7436
- onMouseEnter: () => mode === "range" && setHoveredDate(date),
7437
- onMouseLeave: () => mode === "range" && setHoveredDate(void 0),
7438
- disabled: isOutside,
7439
- className: cn(
7440
- "relative flex flex-col items-center justify-center w-9 rounded-full p-2 cursor-pointer transition-colors",
7441
- "text-sm font-semibold leading-sm text-center",
7442
- // Default
7443
- !isOutside && !isSelected && !isInRangeOrPreview && "text-datepicker-day-text-default hover:bg-datepicker-day-bg-hover",
7444
- // Outside month (disabled)
7445
- isOutside && "text-datepicker-day-text-disabled cursor-default",
7446
- // Selected
7447
- isSelected && "bg-datepicker-day-bg-selected text-datepicker-day-text-selected",
7448
- // In range
7449
- isInRangeOrPreview && "bg-datepicker-day-bg-range text-datepicker-day-text-range",
7450
- // Range start/end get full rounded; in-range items could be less rounded
7451
- (isRangeStart || isRangeEnd) && "rounded-full"
7452
- ),
7453
- children: [
7454
- date.getDate(),
7455
- isToday && !isOutside && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "absolute bottom-0.5 left-1/2 -translate-x-1/2 size-1.5 rounded-full bg-datepicker-day-today" })
7456
- ]
7457
- }
7458
- );
7676
+ ) })
7677
+ ] });
7459
7678
  };
7460
- var DatePickerCalendar = React50.forwardRef(({ className, header, ...props }, ref) => {
7461
- const { month, year, goToPrevMonth, goToNextMonth } = useDatePickerContext();
7462
- const weeks = React50.useMemo(() => {
7463
- const firstDay = new Date(year, month, 1);
7464
- const startOffset = getWeekdayIndex(firstDay);
7465
- const daysInMonth = getDaysInMonth(year, month);
7466
- const daysInPrevMonth = getDaysInMonth(
7467
- month === 0 ? year - 1 : year,
7468
- month === 0 ? 11 : month - 1
7679
+ AdvancedPopover.displayName = "AdvancedPopover";
7680
+
7681
+ // 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");
7686
+ var SummaryChip = ({
7687
+ count,
7688
+ filters,
7689
+ properties,
7690
+ onFiltersChange,
7691
+ onClearAll,
7692
+ children,
7693
+ className,
7694
+ open: openProp,
7695
+ onOpenChange
7696
+ }) => {
7697
+ const [uncontrolledOpen, setUncontrolledOpen] = React49.useState(false);
7698
+ const isControlled = openProp !== void 0;
7699
+ const open = isControlled ? openProp : uncontrolledOpen;
7700
+ const setOpen = (v) => {
7701
+ if (!isControlled) setUncontrolledOpen(v);
7702
+ onOpenChange?.(v);
7703
+ };
7704
+ const [addSelectorOpen, setAddSelectorOpen] = React49.useState(false);
7705
+ const [draftPickerOpen, setDraftPickerOpen] = React49.useState(false);
7706
+ const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
7707
+ const handleUpdateFilter = (updated) => {
7708
+ onFiltersChange(filters.map((f) => f.id === updated.id ? updated : f));
7709
+ };
7710
+ const handleDeleteFilter = (id) => {
7711
+ const next = filters.filter((f) => f.id !== id);
7712
+ onFiltersChange(next);
7713
+ if (next.length === 0) setOpen(false);
7714
+ };
7715
+ const handlePropertyChange = (filterId, newProp) => {
7716
+ const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
7717
+ onFiltersChange(
7718
+ filters.map((f) => f.id === filterId ? { ...newCondition, id: filterId } : f)
7469
7719
  );
7470
- const days = [];
7471
- for (let i = startOffset - 1; i >= 0; i--) {
7472
- const d = daysInPrevMonth - i;
7473
- days.push({
7474
- date: new Date(
7475
- month === 0 ? year - 1 : year,
7476
- month === 0 ? 11 : month - 1,
7477
- d
7720
+ };
7721
+ const handleAddFilter = (property) => {
7722
+ const newFilter = createFilterWithDefaults(property.id, property.type);
7723
+ onFiltersChange([...filters, newFilter]);
7724
+ setAddSelectorOpen(false);
7725
+ };
7726
+ const toggleLogicOp = (filterId) => {
7727
+ onFiltersChange(
7728
+ filters.map(
7729
+ (f) => f.id === filterId ? { ...f, logicOperator: (f.logicOperator ?? "and") === "and" ? "or" : "and" } : f
7730
+ )
7731
+ );
7732
+ };
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)(
7735
+ "button",
7736
+ {
7737
+ type: "button",
7738
+ className: cn(
7739
+ "inline-flex items-center gap-sm px-base py-sm",
7740
+ "min-h-[32px] max-h-[32px]",
7741
+ "bg-gradient-to-t from-[var(--color-btn-outlined-neutral-bg-default)] from-[10%] to-[var(--color-btn-outlined-neutral-bg-gradient-to-default)]",
7742
+ "border border-[var(--color-btn-outlined-neutral-border-default)] rounded-md shadow-sm",
7743
+ "cursor-pointer transition-colors",
7744
+ "hover:from-[var(--color-btn-outlined-neutral-bg-hover)] hover:to-[var(--color-btn-outlined-neutral-bg-gradient-to-hover)]",
7745
+ className
7478
7746
  ),
7479
- isOutside: true
7480
- });
7481
- }
7482
- for (let d = 1; d <= daysInMonth; d++) {
7483
- days.push({ date: new Date(year, month, d), isOutside: false });
7484
- }
7485
- const remaining = 42 - days.length;
7486
- for (let d = 1; d <= remaining; d++) {
7487
- days.push({
7488
- date: new Date(
7489
- month === 11 ? year + 1 : year,
7490
- month === 11 ? 0 : month + 1,
7491
- d
7747
+ 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 }) })
7758
+ ]
7759
+ }
7760
+ ) }),
7761
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(PopoverPrimitive12.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
7762
+ PopoverPrimitive12.Content,
7763
+ {
7764
+ sideOffset: 4,
7765
+ align: "start",
7766
+ collisionPadding: 16,
7767
+ onOpenAutoFocus: (e) => e.preventDefault(),
7768
+ className: cn(
7769
+ "z-50 flex flex-col overflow-clip",
7770
+ "bg-[var(--color-dropdown-bg)] border border-[var(--color-dropdown-border)] rounded-md shadow-lg",
7771
+ "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7772
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7773
+ "data-[side=bottom]:slide-in-from-top-2",
7774
+ "w-[min(520px,calc(100vw-32px))]"
7492
7775
  ),
7493
- isOutside: true
7776
+ 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)(
7800
+ PropertySelector,
7801
+ {
7802
+ properties,
7803
+ 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
+ )
7830
+ }
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)(
7863
+ "button",
7864
+ {
7865
+ type: "button",
7866
+ onClick: () => {
7867
+ onClearAll();
7868
+ setOpen(false);
7869
+ },
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"
7872
+ }
7873
+ )
7874
+ ] })
7875
+ ]
7876
+ }
7877
+ ) })
7878
+ ] });
7879
+ };
7880
+ SummaryChip.displayName = "SummaryChip";
7881
+
7882
+ // src/components/ui/filter/use-filter-bar-mode.ts
7883
+ var React50 = __toESM(require("react"));
7884
+ var DEFAULT_BREAKPOINT = 600;
7885
+ function useFilterBarMode(ref, override, breakpoint = DEFAULT_BREAKPOINT) {
7886
+ const [mode, setMode] = React50.useState("default");
7887
+ React50.useEffect(() => {
7888
+ if (override) return;
7889
+ const el = ref.current;
7890
+ if (!el) return;
7891
+ const observer = new ResizeObserver((entries) => {
7892
+ const width = entries[0]?.contentRect.width ?? 0;
7893
+ setMode(width > breakpoint ? "default" : "minimal");
7894
+ });
7895
+ observer.observe(el);
7896
+ return () => observer.disconnect();
7897
+ }, [ref, override, breakpoint]);
7898
+ return override ?? mode;
7899
+ }
7900
+
7901
+ // src/components/ui/filter/filter-system.tsx
7902
+ var import_jsx_runtime57 = require("react/jsx-runtime");
7903
+ var FilterSystem = ({
7904
+ properties,
7905
+ filterState,
7906
+ onFilterStateChange,
7907
+ sortFields,
7908
+ mode: modeOverride,
7909
+ breakpoint,
7910
+ children,
7911
+ actions,
7912
+ className
7913
+ }) => {
7914
+ const containerRef = React51.useRef(null);
7915
+ 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;
7922
+ const handleAddFilter = (property) => {
7923
+ const newFilter = createFilterWithDefaults(property.id, property.type);
7924
+ if (newFilter.operator && isNoValueOperator(newFilter.operator)) {
7925
+ onFilterStateChange({
7926
+ ...filterState,
7927
+ basicFilters: [...filterState.basicFilters, newFilter]
7494
7928
  });
7929
+ return;
7495
7930
  }
7496
- const result = [];
7497
- for (let i = 0; i < days.length; i += 7) {
7498
- result.push(days.slice(i, i + 7));
7931
+ setPendingFilterId(newFilter.id);
7932
+ onFilterStateChange({
7933
+ ...filterState,
7934
+ basicFilters: [...filterState.basicFilters, newFilter]
7935
+ });
7936
+ };
7937
+ const handleUpdateFilter = (updated) => {
7938
+ onFilterStateChange({
7939
+ ...filterState,
7940
+ basicFilters: filterState.basicFilters.map(
7941
+ (f) => f.id === updated.id ? updated : f
7942
+ )
7943
+ });
7944
+ if (pendingFilterId === updated.id) {
7945
+ setPendingFilterId(null);
7499
7946
  }
7500
- return result;
7501
- }, [month, year]);
7502
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
7503
- "div",
7504
- {
7505
- ref,
7506
- className: cn("flex flex-col", className),
7507
- ...props,
7508
- children: [
7509
- header,
7510
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-col gap-lg p-lg", children: [
7511
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center justify-between", children: [
7512
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "text-base font-semibold leading-base text-datepicker-header-text", children: [
7513
- MONTH_NAMES[month],
7514
- " ",
7515
- year
7516
- ] }),
7517
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-xs", children: [
7518
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7519
- "button",
7947
+ };
7948
+ const handleDeleteFilter = (id) => {
7949
+ onFilterStateChange({
7950
+ ...filterState,
7951
+ basicFilters: filterState.basicFilters.filter((f) => f.id !== id)
7952
+ });
7953
+ };
7954
+ const handlePropertyChange = (filterId, newProp) => {
7955
+ const newCondition = createFilterWithDefaults(newProp.id, newProp.type);
7956
+ onFilterStateChange({
7957
+ ...filterState,
7958
+ basicFilters: filterState.basicFilters.map(
7959
+ (f) => f.id === filterId ? { ...newCondition, id: filterId } : f
7960
+ )
7961
+ });
7962
+ if (newCondition.operator && !isNoValueOperator(newCondition.operator)) {
7963
+ setPendingFilterId(filterId);
7964
+ }
7965
+ };
7966
+ const handleConvertToAdvanced = (id) => {
7967
+ const filter = filterState.basicFilters.find((f) => f.id === id);
7968
+ if (!filter) return;
7969
+ onFilterStateChange({
7970
+ ...filterState,
7971
+ basicFilters: filterState.basicFilters.filter((f) => f.id !== id),
7972
+ advancedFilters: [...filterState.advancedFilters, filter]
7973
+ });
7974
+ };
7975
+ const handleAdvancedFiltersChange = (filters) => {
7976
+ onFilterStateChange({ ...filterState, advancedFilters: filters });
7977
+ };
7978
+ const handleClearAdvanced = () => {
7979
+ onFilterStateChange({ ...filterState, advancedFilters: [] });
7980
+ };
7981
+ const handleClearAll = () => {
7982
+ onFilterStateChange({
7983
+ ...filterState,
7984
+ basicFilters: [],
7985
+ advancedFilters: []
7986
+ });
7987
+ };
7988
+ const handleSortChange = (field, direction) => {
7989
+ onFilterStateChange({ ...filterState, sort: { field, direction } });
7990
+ };
7991
+ const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
7992
+ const hasAdvanced = filterState.advancedFilters.length > 0;
7993
+ const isMinimal = mode === "minimal";
7994
+ const handleOpenAdvanced = () => {
7995
+ setPropertySelectorOpen(false);
7996
+ requestAnimationFrame(() => {
7997
+ if (isMinimal) {
7998
+ setSummaryOpen(true);
7999
+ } else {
8000
+ setAdvancedOpen(true);
8001
+ }
8002
+ });
8003
+ };
8004
+ const advancedFilterCount = filterState.advancedFilters.length;
8005
+ const showAdvancedChip = hasAdvanced || advancedOpen;
8006
+ 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: [
8009
+ children,
8010
+ sortFields && filterState.sort && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8011
+ SortButton,
8012
+ {
8013
+ fields: sortFields,
8014
+ activeField: filterState.sort.field,
8015
+ direction: filterState.sort.direction,
8016
+ onChange: handleSortChange,
8017
+ iconOnly: isMinimal
8018
+ }
8019
+ ),
8020
+ isMinimal ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
8021
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8022
+ "div",
8023
+ {
8024
+ className: showSummaryChip ? "inline-flex" : "inline-flex w-0 overflow-hidden opacity-0 pointer-events-none",
8025
+ "aria-hidden": !showSummaryChip || void 0,
8026
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8027
+ SummaryChip,
8028
+ {
8029
+ count: totalCount,
8030
+ filters: allFilters,
8031
+ properties,
8032
+ onFiltersChange: (filters) => {
8033
+ onFilterStateChange({
8034
+ ...filterState,
8035
+ basicFilters: filters,
8036
+ advancedFilters: []
8037
+ });
8038
+ },
8039
+ onClearAll: handleClearAll,
8040
+ open: summaryOpen,
8041
+ onOpenChange: setSummaryOpen
8042
+ }
8043
+ )
8044
+ }
8045
+ ),
8046
+ !showSummaryChip && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8047
+ PropertySelector,
8048
+ {
8049
+ properties,
8050
+ onSelect: handleAddFilter,
8051
+ open: propertySelectorOpen,
8052
+ onOpenChange: setPropertySelectorOpen,
8053
+ onAdvancedFilter: handleOpenAdvanced,
8054
+ advancedFilterCount,
8055
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(FilterBarButton, { iconOnly: true })
8056
+ }
8057
+ )
8058
+ ] }) : (
8059
+ /* ── DEFAULT MODE ────────────────────────────────────── */
8060
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
8061
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8062
+ AdvancedPopover,
8063
+ {
8064
+ filters: filterState.advancedFilters,
8065
+ properties,
8066
+ onFiltersChange: handleAdvancedFiltersChange,
8067
+ open: advancedOpen,
8068
+ onOpenChange: setAdvancedOpen,
8069
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8070
+ "div",
7520
8071
  {
7521
- type: "button",
7522
- onClick: goToPrevMonth,
7523
- className: "flex items-center justify-center p-xs rounded-base hover:bg-datepicker-day-bg-hover transition-colors cursor-pointer",
7524
- "aria-label": "Previous month",
7525
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_icons35.Icon, { icon: import_icons35.faChevronLeftOutline, size: "xs", className: "text-datepicker-header-nav" })
8072
+ className: showAdvancedChip ? "inline-flex" : "inline-flex w-0 overflow-hidden opacity-0 pointer-events-none",
8073
+ "aria-hidden": !showAdvancedChip || void 0,
8074
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8075
+ AdvancedChip,
8076
+ {
8077
+ count: filterState.advancedFilters.length,
8078
+ onClick: () => setAdvancedOpen(true),
8079
+ onClear: handleClearAdvanced
8080
+ }
8081
+ )
7526
8082
  }
7527
- ),
7528
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
8083
+ )
8084
+ }
8085
+ ),
8086
+ filterState.basicFilters.map((filter) => {
8087
+ const propDef = getPropertyDef(filter.propertyId);
8088
+ if (!propDef) return null;
8089
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8090
+ InteractiveFilterChip,
8091
+ {
8092
+ propertyDef: propDef,
8093
+ condition: filter,
8094
+ properties,
8095
+ mode: pendingFilterId === filter.id ? "add" : "edit",
8096
+ autoOpen: pendingFilterId === filter.id,
8097
+ onUpdate: handleUpdateFilter,
8098
+ onPropertyChange: (newProp) => handlePropertyChange(filter.id, newProp),
8099
+ onDelete: () => handleDeleteFilter(filter.id),
8100
+ onConvertToAdvanced: () => handleConvertToAdvanced(filter.id)
8101
+ },
8102
+ filter.id
8103
+ );
8104
+ }),
8105
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8106
+ PropertySelector,
8107
+ {
8108
+ properties,
8109
+ onSelect: handleAddFilter,
8110
+ open: propertySelectorOpen,
8111
+ onOpenChange: setPropertySelectorOpen,
8112
+ onAdvancedFilter: handleOpenAdvanced,
8113
+ advancedFilterCount,
8114
+ children: totalCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
7529
8115
  "button",
7530
8116
  {
7531
8117
  type: "button",
7532
- onClick: goToNextMonth,
7533
- className: "flex items-center justify-center p-xs rounded-base hover:bg-datepicker-day-bg-hover transition-colors cursor-pointer",
7534
- "aria-label": "Next month",
7535
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_icons35.Icon, { icon: import_icons35.faChevronRightOutline, size: "xs", className: "text-datepicker-header-nav" })
8118
+ 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)]" })
7536
8120
  }
7537
- )
7538
- ] })
7539
- ] }),
7540
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-col", children: [
7541
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "grid grid-cols-7 gap-base py-sm", children: WEEKDAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7542
- "span",
7543
- {
7544
- className: "w-9 text-center text-xs font-regular leading-xs text-datepicker-header-weekday",
7545
- children: day
7546
- },
7547
- day
7548
- )) }),
7549
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-col", children: weeks.map((week, wi) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "grid grid-cols-7 gap-base", children: week.map((day, di) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7550
- DatePickerDay,
7551
- {
7552
- date: day.date,
7553
- isOutside: day.isOutside
7554
- },
7555
- di
7556
- )) }, wi)) })
7557
- ] })
8121
+ ) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(FilterBarButton, {})
8122
+ }
8123
+ )
7558
8124
  ] })
7559
- ]
7560
- }
7561
- );
7562
- });
7563
- DatePickerCalendar.displayName = "DatePickerCalendar";
7564
- var DatePickerSuggestions = React50.forwardRef(
7565
- ({ className, suggestions, formatDate = defaultFormatDate, ...props }, ref) => {
7566
- const { onSelect, mode } = useDatePickerContext();
7567
- const onValueChange = React50.useContext(DatePickerContext) ? void 0 : void 0;
7568
- const ctx = useDatePickerContext();
7569
- const handleClick = (suggestion) => {
7570
- const val = suggestion.getValue();
7571
- if (val instanceof Date) {
7572
- ctx.onSelect(val);
7573
- } else {
7574
- ctx.onSelect(val.from);
7575
- if (val.to) {
7576
- setTimeout(() => ctx.onSelect(val.to), 0);
7577
- }
7578
- }
7579
- };
7580
- const formatSuggestionDate = (suggestion) => {
7581
- const val = suggestion.getValue();
7582
- if (val instanceof Date) {
7583
- return formatDate(val);
7584
- }
7585
- const from = formatDate(val.from);
7586
- const to = val.to ? formatDate(val.to) : "";
7587
- return to ? `${from} - ${to}` : from;
7588
- };
7589
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
7590
- "div",
7591
- {
7592
- ref,
7593
- className: cn(
7594
- "flex flex-col border-l border-datepicker-border self-stretch shrink-0",
7595
- className
7596
- ),
7597
- ...props,
7598
- children: [
7599
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "pt-lg px-base", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex items-center p-base rounded-base", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "flex-1 text-xs font-semibold leading-xs text-datepicker-suggestion-heading uppercase truncate", children: "Suggestions" }) }) }),
7600
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-1 flex-col p-base min-w-[222px]", children: suggestions.map((suggestion, i) => /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
7601
- "button",
7602
- {
7603
- type: "button",
7604
- onClick: () => handleClick(suggestion),
7605
- className: "flex items-center gap-sm p-base rounded-base hover:bg-datepicker-suggestion-hover transition-colors cursor-pointer text-left",
7606
- children: [
7607
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "text-sm font-regular leading-sm text-datepicker-suggestion-text truncate shrink-0", children: suggestion.label }),
7608
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "text-xs font-regular leading-xs text-datepicker-suggestion-date truncate", children: formatSuggestionDate(suggestion) })
7609
- ]
7610
- },
7611
- i
7612
- )) })
7613
- ]
7614
- }
7615
- );
7616
- }
7617
- );
7618
- DatePickerSuggestions.displayName = "DatePickerSuggestions";
7619
- var DatePickerFooter = React50.forwardRef(
7620
- ({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7621
- "div",
7622
- {
7623
- ref,
7624
- className: cn(
7625
- "flex items-center justify-between p-lg",
7626
- "border-t border-datepicker-footer-border",
7627
- "bg-datepicker-bg",
7628
- className
7629
8125
  ),
7630
- ...props,
7631
- children
7632
- }
7633
- )
7634
- );
7635
- DatePickerFooter.displayName = "DatePickerFooter";
7636
- var DatePickerPanel = React50.forwardRef(
7637
- ({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7638
- "div",
7639
- {
7640
- ref,
7641
- className: cn("flex items-start", className),
7642
- ...props,
7643
- children
7644
- }
7645
- )
7646
- );
7647
- DatePickerPanel.displayName = "DatePickerPanel";
7648
- var DatePickerRoot = PopoverPrimitive12.Root;
7649
- var DatePickerTrigger = PopoverPrimitive12.Trigger;
7650
- var DatePickerPopover = React50.forwardRef(({ className, sideOffset = 4, align = "start", children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(PopoverPrimitive12.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
7651
- PopoverPrimitive12.Content,
7652
- {
7653
- ref,
7654
- sideOffset,
7655
- align,
7656
- className: cn(
7657
- "z-50",
7658
- "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
7659
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
7660
- "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
7661
- className
7662
- ),
7663
- ...props,
7664
- children
7665
- }
7666
- ) }));
7667
- DatePickerPopover.displayName = "DatePickerPopover";
7668
- function getDefaultSuggestions(referenceDate) {
7669
- const now = referenceDate ?? /* @__PURE__ */ new Date();
7670
- const today = startOfDay(now);
7671
- const dayOfWeek = getWeekdayIndex(today);
7672
- const startOfThisWeek = new Date(today);
7673
- startOfThisWeek.setDate(today.getDate() - dayOfWeek);
7674
- const endOfThisWeek = new Date(startOfThisWeek);
7675
- endOfThisWeek.setDate(startOfThisWeek.getDate() + 6);
7676
- const startOfThisMonth = new Date(today.getFullYear(), today.getMonth(), 1);
7677
- const endOfThisMonth = new Date(
7678
- today.getFullYear(),
7679
- today.getMonth() + 1,
7680
- 0
7681
- );
7682
- const startOfThisYear = new Date(today.getFullYear(), 0, 1);
7683
- const endOfThisYear = new Date(today.getFullYear(), 11, 31);
7684
- const startOfLastWeek = new Date(startOfThisWeek);
7685
- startOfLastWeek.setDate(startOfThisWeek.getDate() - 7);
7686
- const endOfLastWeek = new Date(startOfThisWeek);
7687
- endOfLastWeek.setDate(startOfThisWeek.getDate() - 1);
7688
- const startOfLastMonth = new Date(
7689
- today.getFullYear(),
7690
- today.getMonth() - 1,
7691
- 1
7692
- );
7693
- const endOfLastMonth = new Date(today.getFullYear(), today.getMonth(), 0);
7694
- const startOfLastYear = new Date(today.getFullYear() - 1, 0, 1);
7695
- const endOfLastYear = new Date(today.getFullYear() - 1, 11, 31);
7696
- return [
7697
- { label: "Today", getValue: () => today },
7698
- {
7699
- label: "This week",
7700
- getValue: () => ({ from: startOfThisWeek, to: endOfThisWeek })
7701
- },
7702
- {
7703
- label: "This month",
7704
- getValue: () => ({ from: startOfThisMonth, to: endOfThisMonth })
7705
- },
7706
- {
7707
- label: "This year",
7708
- getValue: () => ({ from: startOfThisYear, to: endOfThisYear })
7709
- },
7710
- {
7711
- label: "Last week",
7712
- getValue: () => ({ from: startOfLastWeek, to: endOfLastWeek })
7713
- },
7714
- {
7715
- label: "Last month",
7716
- getValue: () => ({ from: startOfLastMonth, to: endOfLastMonth })
7717
- },
7718
- {
7719
- label: "Last year",
7720
- getValue: () => ({ from: startOfLastYear, to: endOfLastYear })
7721
- }
7722
- ];
7723
- }
8126
+ totalCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8127
+ "button",
8128
+ {
8129
+ type: "button",
8130
+ onClick: handleClearAll,
8131
+ 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" })
8133
+ }
8134
+ )
8135
+ ] }),
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 })
8137
+ ] });
8138
+ };
8139
+ FilterSystem.displayName = "FilterSystem";
7724
8140
  // Annotate the CommonJS export names for ESM import in node:
7725
8141
  0 && (module.exports = {
7726
8142
  AdvancedChip,