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