@l3mpire/ui 2.16.4 → 2.18.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.mjs CHANGED
@@ -5328,6 +5328,23 @@ function updateNodeInTree(nodes, id, updater) {
5328
5328
  return node;
5329
5329
  });
5330
5330
  }
5331
+ function toggleGroupLogicInTree(nodes, id) {
5332
+ const idx = nodes.findIndex((n) => n.id === id);
5333
+ if (idx >= 0) {
5334
+ const current = nodes[idx].logicOperator ?? "and";
5335
+ const next = current === "and" ? "or" : "and";
5336
+ return nodes.map(
5337
+ (n, i) => i === 0 ? n : { ...n, logicOperator: next }
5338
+ );
5339
+ }
5340
+ return nodes.map((node) => {
5341
+ if (isFilterGroup(node)) {
5342
+ const updated = toggleGroupLogicInTree(node.children, id);
5343
+ if (updated !== node.children) return { ...node, children: updated };
5344
+ }
5345
+ return node;
5346
+ });
5347
+ }
5331
5348
  function removeNodeFromTree(nodes, id) {
5332
5349
  const result = [];
5333
5350
  for (const node of nodes) {
@@ -7069,6 +7086,7 @@ import { Icon as Icon29, faArrowRightOutline as faArrowRightOutline2, faTrashOut
7069
7086
  import { jsx as jsx51, jsxs as jsxs45 } from "react/jsx-runtime";
7070
7087
  var KebabMenu = ({
7071
7088
  onConvertToAdvanced,
7089
+ hasAdvancedFilters = false,
7072
7090
  onDelete,
7073
7091
  open,
7074
7092
  onOpenChange,
@@ -7107,7 +7125,7 @@ var KebabMenu = ({
7107
7125
  className: "shrink-0 text-[var(--color-dropdown-item-icon)]"
7108
7126
  }
7109
7127
  ),
7110
- /* @__PURE__ */ jsx51("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)]", children: "Convert to advanced" })
7128
+ /* @__PURE__ */ jsx51("span", { className: "text-sm font-regular leading-sm text-[var(--color-dropdown-item-text)]", children: hasAdvancedFilters ? "Add to advanced filters" : "Convert to advanced" })
7111
7129
  ]
7112
7130
  }
7113
7131
  ),
@@ -7294,6 +7312,7 @@ var InteractiveFilterChip = ({
7294
7312
  onPropertyChange,
7295
7313
  onDelete,
7296
7314
  onConvertToAdvanced,
7315
+ hasAdvancedFilters = false,
7297
7316
  className
7298
7317
  }) => {
7299
7318
  const [operatorOpen, setOperatorOpen] = React46.useState(false);
@@ -7449,6 +7468,7 @@ var InteractiveFilterChip = ({
7449
7468
  open: kebabOpen,
7450
7469
  onOpenChange: setKebabOpen,
7451
7470
  onConvertToAdvanced,
7471
+ hasAdvancedFilters,
7452
7472
  onDelete,
7453
7473
  children: /* @__PURE__ */ jsx53("div", { children: /* @__PURE__ */ jsx53(
7454
7474
  FilterChipSegment,
@@ -7470,7 +7490,7 @@ InteractiveFilterChip.displayName = "InteractiveFilterChip";
7470
7490
 
7471
7491
  // src/components/ui/filter/filter-system.tsx
7472
7492
  import * as React54 from "react";
7473
- import { Icon as Icon37, faXmarkOutline as faXmarkOutline6, faPlusOutline as faPlusOutline5 } from "@l3mpire/icons";
7493
+ import { Icon as Icon37, faPlusOutline as faPlusOutline5 } from "@l3mpire/icons";
7474
7494
 
7475
7495
  // src/components/ui/filter/advanced-chip.tsx
7476
7496
  import * as React47 from "react";
@@ -7951,7 +7971,8 @@ var AdvancedPopover = ({
7951
7971
  onFiltersChange,
7952
7972
  open,
7953
7973
  onOpenChange,
7954
- children
7974
+ children,
7975
+ collisionBoundary
7955
7976
  }) => {
7956
7977
  const [addSelectorOpen, setAddSelectorOpen] = React51.useState(false);
7957
7978
  const [draftPickerOpen, setDraftPickerOpen] = React51.useState(false);
@@ -8028,12 +8049,7 @@ var AdvancedPopover = ({
8028
8049
  }
8029
8050
  };
8030
8051
  const toggleLogicOp = (id) => {
8031
- onFiltersChange(
8032
- updateNodeInTree(filters, id, (n) => ({
8033
- ...n,
8034
- logicOperator: (n.logicOperator ?? "and") === "and" ? "or" : "and"
8035
- }))
8036
- );
8052
+ onFiltersChange(toggleGroupLogicInTree(filters, id));
8037
8053
  };
8038
8054
  const handleGroupChildrenChange = (groupId, children2) => {
8039
8055
  onFiltersChange(
@@ -8107,6 +8123,7 @@ var AdvancedPopover = ({
8107
8123
  sideOffset: 4,
8108
8124
  align: "start",
8109
8125
  collisionPadding: 16,
8126
+ collisionBoundary: collisionBoundary ?? void 0,
8110
8127
  onOpenAutoFocus: (e) => e.preventDefault(),
8111
8128
  className: cn(
8112
8129
  "z-50 flex flex-col",
@@ -8114,7 +8131,7 @@ var AdvancedPopover = ({
8114
8131
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
8115
8132
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
8116
8133
  "data-[side=bottom]:slide-in-from-top-2",
8117
- "w-[min(640px,calc(100vw-32px))] min-w-[360px]"
8134
+ collisionBoundary ? "w-[min(640px,var(--radix-popover-content-available-width))] min-w-0" : "w-[min(640px,calc(100vw-32px))] min-w-[360px]"
8118
8135
  ),
8119
8136
  children: [
8120
8137
  /* @__PURE__ */ jsx58("div", { className: "flex flex-col gap-base p-base", children: filters.length > 0 ? filters.map((node, i) => renderNode(node, i)) : /* @__PURE__ */ jsx58(
@@ -8247,7 +8264,8 @@ var SummaryChip = ({
8247
8264
  children,
8248
8265
  className,
8249
8266
  open: openProp,
8250
- onOpenChange
8267
+ onOpenChange,
8268
+ collisionBoundary
8251
8269
  }) => {
8252
8270
  const [uncontrolledOpen, setUncontrolledOpen] = React52.useState(false);
8253
8271
  const isControlled = openProp !== void 0;
@@ -8325,12 +8343,7 @@ var SummaryChip = ({
8325
8343
  }
8326
8344
  };
8327
8345
  const toggleLogicOp = (id) => {
8328
- onFiltersChange(
8329
- updateNodeInTree(filters, id, (n) => ({
8330
- ...n,
8331
- logicOperator: (n.logicOperator ?? "and") === "and" ? "or" : "and"
8332
- }))
8333
- );
8346
+ onFiltersChange(toggleGroupLogicInTree(filters, id));
8334
8347
  };
8335
8348
  const handleGroupChildrenChange = (groupId, newChildren) => {
8336
8349
  onFiltersChange(
@@ -8420,6 +8433,7 @@ var SummaryChip = ({
8420
8433
  sideOffset: 4,
8421
8434
  align: "start",
8422
8435
  collisionPadding: 16,
8436
+ collisionBoundary: collisionBoundary ?? void 0,
8423
8437
  onOpenAutoFocus: (e) => e.preventDefault(),
8424
8438
  className: cn(
8425
8439
  "z-50 flex flex-col overflow-clip",
@@ -8427,7 +8441,7 @@ var SummaryChip = ({
8427
8441
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
8428
8442
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
8429
8443
  "data-[side=bottom]:slide-in-from-top-2",
8430
- "w-[min(640px,calc(100vw-32px))]"
8444
+ collisionBoundary ? "w-[min(640px,var(--radix-popover-content-available-width))]" : "w-[min(640px,calc(100vw-32px))]"
8431
8445
  ),
8432
8446
  children: [
8433
8447
  /* @__PURE__ */ jsx59("div", { className: "flex flex-col gap-base p-base", children: filters.length > 0 ? filters.map((node, i) => renderNode(node, i)) : /* @__PURE__ */ jsx59(
@@ -8541,12 +8555,14 @@ var FilterSystem = ({
8541
8555
  sortFields,
8542
8556
  mode: modeOverride,
8543
8557
  breakpoint,
8558
+ bounded = false,
8544
8559
  children,
8545
8560
  actions,
8546
8561
  className
8547
8562
  }) => {
8548
8563
  const containerRef = React54.useRef(null);
8549
8564
  const mode = useFilterBarMode(containerRef, modeOverride, breakpoint);
8565
+ const collisionBoundary = bounded ? containerRef.current : void 0;
8550
8566
  const [propertySelectorOpen, setPropertySelectorOpen] = React54.useState(false);
8551
8567
  const [advancedOpen, setAdvancedOpen] = React54.useState(false);
8552
8568
  const [summaryOpen, setSummaryOpen] = React54.useState(false);
@@ -8623,7 +8639,8 @@ var FilterSystem = ({
8623
8639
  };
8624
8640
  const getPropertyDef = (propertyId) => properties.find((p) => p.id === propertyId);
8625
8641
  const hasAdvanced = filterState.advancedFilters.length > 0;
8626
- const isMinimal = mode === "minimal";
8642
+ const isMinimal = mode === "minimal" || mode === "icon";
8643
+ const isIconOnly = mode === "icon";
8627
8644
  const handleOpenAdvanced = () => {
8628
8645
  setPropertySelectorOpen(false);
8629
8646
  requestAnimationFrame(() => {
@@ -8636,7 +8653,6 @@ var FilterSystem = ({
8636
8653
  };
8637
8654
  const advancedFilterCount = filterState.advancedFilters.length;
8638
8655
  const showAdvancedChip = hasAdvanced || advancedOpen;
8639
- const showSummaryChip = totalCount > 0 || summaryOpen;
8640
8656
  return /* @__PURE__ */ jsxs54(FilterBar, { ref: containerRef, className, children: [
8641
8657
  /* @__PURE__ */ jsxs54(FilterBarLeft, { className: "flex-nowrap flex-1 min-w-0 overflow-x-auto scrollbar-none outline-none [&>*]:shrink-0", children: [
8642
8658
  children,
@@ -8650,38 +8666,33 @@ var FilterSystem = ({
8650
8666
  iconOnly: isMinimal
8651
8667
  }
8652
8668
  ),
8653
- isMinimal ? /* @__PURE__ */ jsxs54(Fragment5, { children: [
8654
- showSummaryChip && /* @__PURE__ */ jsx60(
8655
- SummaryChip,
8656
- {
8657
- count: totalCount,
8658
- filters: [...filterState.basicFilters, ...filterState.advancedFilters],
8659
- properties,
8660
- onFiltersChange: (nodes) => {
8661
- onFilterStateChange({
8662
- ...filterState,
8663
- basicFilters: [],
8664
- advancedFilters: nodes
8665
- });
8666
- },
8667
- onClearAll: handleClearAll,
8668
- open: summaryOpen,
8669
- onOpenChange: setSummaryOpen
8670
- }
8671
- ),
8672
- !showSummaryChip && /* @__PURE__ */ jsx60(
8673
- PropertySelector,
8674
- {
8675
- properties,
8676
- onSelect: handleAddFilter,
8677
- open: propertySelectorOpen,
8678
- onOpenChange: setPropertySelectorOpen,
8679
- onAdvancedFilter: handleOpenAdvanced,
8680
- advancedFilterCount,
8681
- children: /* @__PURE__ */ jsx60(FilterBarButton, { iconOnly: true })
8682
- }
8683
- )
8684
- ] }) : (
8669
+ isMinimal ? /* @__PURE__ */ jsx60(Fragment5, { children: /* @__PURE__ */ jsx60(
8670
+ SummaryChip,
8671
+ {
8672
+ count: totalCount,
8673
+ filters: [...filterState.basicFilters, ...filterState.advancedFilters],
8674
+ properties,
8675
+ onFiltersChange: (nodes) => {
8676
+ onFilterStateChange({
8677
+ ...filterState,
8678
+ basicFilters: [],
8679
+ advancedFilters: nodes
8680
+ });
8681
+ },
8682
+ onClearAll: handleClearAll,
8683
+ open: summaryOpen,
8684
+ onOpenChange: setSummaryOpen,
8685
+ collisionBoundary,
8686
+ children: /* @__PURE__ */ jsx60(
8687
+ FilterBarButton,
8688
+ {
8689
+ iconOnly: isIconOnly,
8690
+ count: totalCount > 0 ? totalCount : void 0,
8691
+ title: buildFilterSummary(filterState.basicFilters, filterState.advancedFilters.length, properties)
8692
+ }
8693
+ )
8694
+ }
8695
+ ) }) : (
8685
8696
  /* ── DEFAULT MODE ────────────────────────────────────── */
8686
8697
  /* @__PURE__ */ jsxs54(Fragment5, { children: [
8687
8698
  showAdvancedChip && /* @__PURE__ */ jsx60(
@@ -8692,6 +8703,7 @@ var FilterSystem = ({
8692
8703
  onFiltersChange: handleAdvancedFiltersChange,
8693
8704
  open: advancedOpen,
8694
8705
  onOpenChange: setAdvancedOpen,
8706
+ collisionBoundary,
8695
8707
  children: /* @__PURE__ */ jsx60(
8696
8708
  AdvancedChip,
8697
8709
  {
@@ -8716,7 +8728,8 @@ var FilterSystem = ({
8716
8728
  onUpdate: handleUpdateFilter,
8717
8729
  onPropertyChange: (newProp) => handlePropertyChange(filter.id, newProp),
8718
8730
  onDelete: () => handleDeleteFilter(filter.id),
8719
- onConvertToAdvanced: () => handleConvertToAdvanced(filter.id)
8731
+ onConvertToAdvanced: () => handleConvertToAdvanced(filter.id),
8732
+ hasAdvancedFilters: hasAdvanced
8720
8733
  },
8721
8734
  filter.id
8722
8735
  );
@@ -8742,13 +8755,13 @@ var FilterSystem = ({
8742
8755
  )
8743
8756
  ] })
8744
8757
  ),
8745
- totalCount > 0 && /* @__PURE__ */ jsx60(
8758
+ totalCount > 0 && !isIconOnly && /* @__PURE__ */ jsx60(
8746
8759
  "button",
8747
8760
  {
8748
8761
  type: "button",
8749
8762
  onClick: handleClearAll,
8750
- 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)]",
8751
- children: isMinimal ? /* @__PURE__ */ jsx60(Icon37, { icon: faXmarkOutline6, size: "sm", className: "text-[var(--color-foreground)]" }) : /* @__PURE__ */ jsx60("span", { className: "text-sm font-semibold leading-sm text-[var(--color-foreground)]", children: "Clear" })
8763
+ className: "shrink-0 flex items-center gap-sm px-base py-sm min-h-[32px] max-h-[32px] rounded-md cursor-pointer transition-colors text-btn-ghost-brand-text-default hover:bg-btn-ghost-brand-bg-hover hover:text-btn-ghost-brand-text-hover active:bg-btn-ghost-brand-bg-pressed active:text-btn-ghost-brand-text-pressed",
8764
+ children: /* @__PURE__ */ jsx60("span", { className: "text-sm font-semibold leading-sm", children: "Clear" })
8752
8765
  }
8753
8766
  )
8754
8767
  ] }),
@@ -8756,6 +8769,20 @@ var FilterSystem = ({
8756
8769
  ] });
8757
8770
  };
8758
8771
  FilterSystem.displayName = "FilterSystem";
8772
+ function buildFilterSummary(filters, advancedCount, properties) {
8773
+ if (filters.length === 0 && advancedCount === 0) return void 0;
8774
+ const lines = [];
8775
+ for (const f of filters) {
8776
+ const prop = properties.find((p) => p.id === f.propertyId);
8777
+ if (!prop) continue;
8778
+ const val = formatFilterValue(f.value, prop.dynamicOptions);
8779
+ lines.push(val ? `${prop.label} ${f.operator} ${val}` : `${prop.label} ${f.operator ?? ""}`);
8780
+ }
8781
+ if (advancedCount > 0) {
8782
+ lines.push(`+ ${advancedCount} advanced filter${advancedCount > 1 ? "s" : ""}`);
8783
+ }
8784
+ return lines.join("\n");
8785
+ }
8759
8786
  export {
8760
8787
  AdvancedChip,
8761
8788
  AdvancedPopover,