@propeller-commerce/propeller-v2-react-ui 0.10.1 → 0.11.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
@@ -12827,10 +12827,7 @@ function ItemsOverview(props) {
12827
12827
  var ItemsOverview_default = ItemsOverview;
12828
12828
  function Menu(rawProps) {
12829
12829
  const props = useInfraProps(rawProps);
12830
- const [hoveredL1Id, setHoveredL1Id] = useState(null);
12831
- const [hoveredL2Id, setHoveredL2Id] = useState(null);
12832
- const [expandedL1, setExpandedL1] = useState(null);
12833
- const [expandedL2, setExpandedL2] = useState(null);
12830
+ const [openPath, setOpenPath] = useState([]);
12834
12831
  const userKey = props.user ? "contactId" in props.user ? `c${props.user.contactId}` : `u${props.user.customerId}` : "";
12835
12832
  const hasPrefetchedTree = Array.isArray(props.tree);
12836
12833
  const { categories: fetchedCategories, loading: fetchedLoading, error: menuError, fetchMenu } = useMenu({
@@ -12867,16 +12864,139 @@ function Menu(rawProps) {
12867
12864
  });
12868
12865
  }
12869
12866
  }
12870
- function setHoveredL1(id) {
12871
- setHoveredL1Id(id);
12872
- setHoveredL2Id(null);
12867
+ function openAt(level, categoryId) {
12868
+ setOpenPath((prev) => {
12869
+ const next = prev.slice(0, level);
12870
+ if (categoryId !== null) next.push(categoryId);
12871
+ return next;
12872
+ });
12873
12873
  }
12874
- function setHoveredL2(id) {
12875
- setHoveredL2Id(id);
12874
+ function toggleAt(level, categoryId) {
12875
+ setOpenPath(
12876
+ (prev) => prev[level] === categoryId ? prev.slice(0, level) : [...prev.slice(0, level), categoryId]
12877
+ );
12878
+ }
12879
+ function isOpenAt(level, categoryId) {
12880
+ return openPath[level] === categoryId;
12876
12881
  }
12877
12882
  function getMenuStyle() {
12878
12883
  return props.menuStyle || "dropdown-vertical";
12879
12884
  }
12885
+ function getRenderableDepth() {
12886
+ const requested = Math.max(1, props.depth ?? 3);
12887
+ const cap = getMenuStyle() === "dropdown-horizontal" ? 3 : getMenuStyle() === "accordion" ? Number.POSITIVE_INFINITY : 4;
12888
+ if (process.env.NODE_ENV !== "production" && requested > cap) {
12889
+ console.warn(
12890
+ `[Menu] depth={${requested}} exceeds what menuStyle="${getMenuStyle()}" can lay out (${cap}); rendering ${cap} levels. Use menuStyle="accordion" for deeper trees.`
12891
+ );
12892
+ }
12893
+ return Math.min(requested, cap);
12894
+ }
12895
+ const maxDepth = getRenderableDepth();
12896
+ function renderChevron(cat, level, rotated = false) {
12897
+ if (getSubCategories(cat).length === 0 || level + 1 >= maxDepth) return null;
12898
+ return /* @__PURE__ */ jsx(
12899
+ "svg",
12900
+ {
12901
+ className: `propeller-menu__chevron w-3.5 h-3.5 flex-shrink-0 transition-transform ${rotated ? "rotate-180" : ""}`,
12902
+ viewBox: "0 0 24 24",
12903
+ fill: "none",
12904
+ stroke: "currentColor",
12905
+ strokeWidth: "2",
12906
+ strokeLinecap: "round",
12907
+ strokeLinejoin: "round",
12908
+ "aria-hidden": "true",
12909
+ children: rotated ? /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" }) : /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
12910
+ }
12911
+ );
12912
+ }
12913
+ function renderColumns(items, level) {
12914
+ if (!items.length || level >= maxDepth) return [];
12915
+ const openId = openPath[level];
12916
+ const openCat = items.find((c) => c.categoryId === openId);
12917
+ const column = /* @__PURE__ */ jsx(
12918
+ "ul",
12919
+ {
12920
+ className: `propeller-menu__list w-64 py-1 flex-shrink-0 ${level + 1 < maxDepth ? "border-r border-border" : ""}`,
12921
+ "data-level": level + 1,
12922
+ children: items.map((cat, idx) => /* @__PURE__ */ jsx(
12923
+ "li",
12924
+ {
12925
+ className: "propeller-menu__item",
12926
+ "data-level": level + 1,
12927
+ "data-active": isOpenAt(level, cat.categoryId) ? "true" : "false",
12928
+ onMouseEnter: () => openAt(level, cat.categoryId),
12929
+ children: /* @__PURE__ */ jsxs(
12930
+ "a",
12931
+ {
12932
+ href: getCategoryUrl(cat),
12933
+ onClick: (e) => handleItemClick(cat, e),
12934
+ className: `propeller-menu__link flex items-center justify-between px-4 py-2.5 text-sm transition-colors ${isOpenAt(level, cat.categoryId) ? "bg-accent text-accent-foreground" : "text-foreground hover:bg-accent/50"}`,
12935
+ children: [
12936
+ /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(cat) }),
12937
+ renderChevron(cat, level)
12938
+ ]
12939
+ }
12940
+ )
12941
+ },
12942
+ `l${level}-${cat.categoryId}-${idx}`
12943
+ ))
12944
+ },
12945
+ `col-${level}`
12946
+ );
12947
+ const deeper = openCat ? renderColumns(getSubCategories(openCat), level + 1) : [];
12948
+ return [column, ...deeper];
12949
+ }
12950
+ function renderAccordion(items, level) {
12951
+ if (!items.length || level >= maxDepth) return null;
12952
+ return /* @__PURE__ */ jsx(
12953
+ "ul",
12954
+ {
12955
+ className: `propeller-menu__list ${level === 0 ? "divide-y divide-border" : level === 1 ? "bg-accent/30" : "bg-accent/20"}`,
12956
+ "data-level": level + 1,
12957
+ children: items.map((cat, idx) => {
12958
+ const children = getSubCategories(cat);
12959
+ const expanded = isOpenAt(level, cat.categoryId);
12960
+ const canExpand = children.length > 0 && level + 1 < maxDepth;
12961
+ return /* @__PURE__ */ jsxs(
12962
+ "li",
12963
+ {
12964
+ className: "propeller-menu__item",
12965
+ "data-level": level + 1,
12966
+ "data-expanded": expanded ? "true" : "false",
12967
+ children: [
12968
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
12969
+ /* @__PURE__ */ jsx(
12970
+ "a",
12971
+ {
12972
+ href: getCategoryUrl(cat),
12973
+ onClick: (e) => handleItemClick(cat, e),
12974
+ className: "propeller-menu__link flex-1 px-4 py-3 text-sm text-foreground",
12975
+ style: { paddingLeft: `${1 + level}rem` },
12976
+ children: getCategoryName(cat)
12977
+ }
12978
+ ),
12979
+ canExpand ? /* @__PURE__ */ jsx(
12980
+ "button",
12981
+ {
12982
+ type: "button",
12983
+ className: "propeller-menu__toggle px-4 py-3",
12984
+ "aria-expanded": expanded,
12985
+ "aria-label": getCategoryName(cat),
12986
+ onClick: () => toggleAt(level, cat.categoryId),
12987
+ children: renderChevron(cat, level, expanded)
12988
+ }
12989
+ ) : null
12990
+ ] }),
12991
+ expanded && canExpand ? renderAccordion(children, level + 1) : null
12992
+ ]
12993
+ },
12994
+ `l${level}-${cat.categoryId}-${idx}`
12995
+ );
12996
+ })
12997
+ }
12998
+ );
12999
+ }
12880
13000
  useEffect(() => {
12881
13001
  if (hasPrefetchedTree) return;
12882
13002
  if (!props.graphqlClient) return;
@@ -12899,125 +13019,7 @@ function Menu(rawProps) {
12899
13019
  "nav",
12900
13020
  {
12901
13021
  className: `propeller-menu__nav hidden md:block ${props.menuClass || ""}`,
12902
- children: /* @__PURE__ */ jsxs("div", { className: "flex bg-popover border border-border shadow-lg", children: [
12903
- /* @__PURE__ */ jsx("ul", { className: "propeller-menu__list w-64 py-1 border-r border-border flex-shrink-0", "data-level": "1", children: menuCategories?.map((l1, idx) => /* @__PURE__ */ jsx(
12904
- "li",
12905
- {
12906
- className: "propeller-menu__item",
12907
- "data-level": "1",
12908
- "data-active": hoveredL1Id === l1.categoryId ? "true" : "false",
12909
- onMouseEnter: (event) => setHoveredL1(l1.categoryId),
12910
- children: /* @__PURE__ */ jsxs(
12911
- "a",
12912
- {
12913
- href: getCategoryUrl(l1),
12914
- onClick: (e) => handleItemClick(l1, e),
12915
- className: `propeller-menu__link flex items-center justify-between px-4 py-2.5 text-sm transition-colors ${hoveredL1Id === l1.categoryId ? "bg-accent text-accent-foreground" : "text-foreground hover:bg-accent/50"}`,
12916
- children: [
12917
- /* @__PURE__ */ jsx("span", { className: "propeller-menu__label font-medium truncate", children: getCategoryName(l1) }),
12918
- getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx(
12919
- "svg",
12920
- {
12921
- fill: "none",
12922
- stroke: "currentColor",
12923
- viewBox: "0 0 24 24",
12924
- className: "propeller-menu__chevron w-4 h-4 text-muted-foreground",
12925
- children: /* @__PURE__ */ jsx(
12926
- "path",
12927
- {
12928
- strokeLinecap: "round",
12929
- strokeLinejoin: "round",
12930
- d: "M9 5l7 7-7 7",
12931
- strokeWidth: 2
12932
- }
12933
- )
12934
- }
12935
- ) : null
12936
- ]
12937
- }
12938
- )
12939
- },
12940
- `l1-${l1.categoryId}-${idx}`
12941
- )) }),
12942
- menuCategories?.map(
12943
- (l1, idx) => hoveredL1Id === l1.categoryId && getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx(
12944
- "ul",
12945
- {
12946
- className: "propeller-menu__list w-64 py-1 border-r border-border flex-shrink-0",
12947
- "data-level": "2",
12948
- children: getSubCategories(l1)?.map((l2, idx2) => /* @__PURE__ */ jsx(
12949
- "li",
12950
- {
12951
- className: "propeller-menu__item",
12952
- "data-level": "2",
12953
- "data-active": hoveredL2Id === l2.categoryId ? "true" : "false",
12954
- onMouseEnter: (event) => setHoveredL2(l2.categoryId),
12955
- children: /* @__PURE__ */ jsxs(
12956
- "a",
12957
- {
12958
- href: getCategoryUrl(l2),
12959
- onClick: (e) => handleItemClick(l2, e),
12960
- className: `propeller-menu__link flex items-center justify-between px-4 py-2.5 text-sm transition-colors ${hoveredL2Id === l2.categoryId ? "bg-accent text-accent-foreground" : "text-foreground hover:bg-accent/50"}`,
12961
- children: [
12962
- /* @__PURE__ */ jsx("span", { className: "propeller-menu__label truncate", children: getCategoryName(l2) }),
12963
- getSubCategories(l2).length > 0 ? /* @__PURE__ */ jsx(
12964
- "svg",
12965
- {
12966
- fill: "none",
12967
- stroke: "currentColor",
12968
- viewBox: "0 0 24 24",
12969
- className: "propeller-menu__chevron w-4 h-4 text-muted-foreground",
12970
- children: /* @__PURE__ */ jsx(
12971
- "path",
12972
- {
12973
- strokeLinecap: "round",
12974
- strokeLinejoin: "round",
12975
- d: "M9 5l7 7-7 7",
12976
- strokeWidth: 2
12977
- }
12978
- )
12979
- }
12980
- ) : null
12981
- ]
12982
- }
12983
- )
12984
- },
12985
- `l2-${l2.categoryId}-${idx2}`
12986
- ))
12987
- },
12988
- `l1-sub-${l1.categoryId}-${idx}`
12989
- ) : null
12990
- ),
12991
- menuCategories?.map(
12992
- (l1, idx) => hoveredL1Id === l1.categoryId ? /* @__PURE__ */ jsx(React6.Fragment, { children: getSubCategories(l1)?.map(
12993
- (l2, idx2) => hoveredL2Id === l2.categoryId && getSubCategories(l2).length > 0 ? /* @__PURE__ */ jsx(
12994
- "ul",
12995
- {
12996
- className: "propeller-menu__list w-64 py-1 flex-shrink-0",
12997
- "data-level": "3",
12998
- children: getSubCategories(l2)?.map((l3, idx3) => /* @__PURE__ */ jsx(
12999
- "li",
13000
- {
13001
- className: "propeller-menu__item",
13002
- "data-level": "3",
13003
- children: /* @__PURE__ */ jsx(
13004
- "a",
13005
- {
13006
- className: "propeller-menu__link block px-4 py-2.5 text-sm text-foreground hover:bg-accent/50 transition-colors",
13007
- href: getCategoryUrl(l3),
13008
- onClick: (e) => handleItemClick(l3, e),
13009
- children: /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(l3) })
13010
- }
13011
- )
13012
- },
13013
- `l3-${l3.categoryId}-${idx3}`
13014
- ))
13015
- },
13016
- `l3-col-${l2.categoryId}-${idx2}`
13017
- ) : null
13018
- ) }, `l1-l3col-${l1.categoryId}-${idx}`) : null
13019
- )
13020
- ] })
13022
+ children: /* @__PURE__ */ jsx("div", { className: "flex bg-popover border border-border shadow-lg", children: renderColumns(menuCategories, 0) })
13021
13023
  }
13022
13024
  ) : null,
13023
13025
  !isLoading && !hasError && menuCategories.length > 0 && getMenuStyle() === "jumbotron" ? /* @__PURE__ */ jsxs(
@@ -13028,22 +13030,22 @@ function Menu(rawProps) {
13028
13030
  /* @__PURE__ */ jsx("div", { className: "propeller-menu__tabs flex items-center border-b border-border", children: menuCategories?.map((l1, idx) => /* @__PURE__ */ jsx(
13029
13031
  "button",
13030
13032
  {
13031
- className: `propeller-menu__tab px-5 py-3 text-sm font-medium transition-colors border-b-2 ${hoveredL1Id === l1.categoryId ? "border-primary text-primary" : "border-transparent text-foreground hover:text-primary hover:border-primary/50"}`,
13033
+ className: `propeller-menu__tab px-5 py-3 text-sm font-medium transition-colors border-b-2 ${isOpenAt(0, l1.categoryId) ? "border-primary text-primary" : "border-transparent text-foreground hover:text-primary hover:border-primary/50"}`,
13032
13034
  "data-level": "1",
13033
- "data-active": hoveredL1Id === l1.categoryId ? "true" : "false",
13034
- onMouseEnter: (event) => setHoveredL1(l1.categoryId),
13035
+ "data-active": isOpenAt(0, l1.categoryId) ? "true" : "false",
13036
+ onMouseEnter: (event) => openAt(0, l1.categoryId),
13035
13037
  onClick: (e) => handleItemClick(l1, e),
13036
13038
  children: /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(l1) })
13037
13039
  },
13038
13040
  `l1-${l1.categoryId}-${idx}`
13039
13041
  )) }),
13040
13042
  menuCategories?.map(
13041
- (l1, idx) => hoveredL1Id === l1.categoryId && getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx(
13043
+ (l1, idx) => isOpenAt(0, l1.categoryId) && getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx(
13042
13044
  "div",
13043
13045
  {
13044
13046
  className: "propeller-menu__panel bg-popover border border-border border-t-0 shadow-lg p-6",
13045
- onMouseEnter: (event) => setHoveredL1(l1.categoryId),
13046
- onMouseLeave: (event) => setHoveredL1(null),
13047
+ onMouseEnter: (event) => openAt(0, l1.categoryId),
13048
+ onMouseLeave: (event) => openAt(0, null),
13047
13049
  children: /* @__PURE__ */ jsx("div", { className: "propeller-menu__panel-grid grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6", children: getSubCategories(l1)?.map((l2, idx2) => /* @__PURE__ */ jsxs("div", { className: "propeller-menu__group", "data-level": "2", children: [
13048
13050
  /* @__PURE__ */ jsx(
13049
13051
  "a",
@@ -13070,123 +13072,7 @@ function Menu(rawProps) {
13070
13072
  ]
13071
13073
  }
13072
13074
  ) : null,
13073
- !isLoading && !hasError && menuCategories.length > 0 ? /* @__PURE__ */ jsx("nav", { className: `propeller-menu__nav md:hidden ${props.menuClass || ""}`, children: /* @__PURE__ */ jsx("ul", { className: "propeller-menu__list divide-y divide-border", "data-level": "1", children: menuCategories?.map((l1, idx) => /* @__PURE__ */ jsxs(
13074
- "li",
13075
- {
13076
- className: "propeller-menu__item",
13077
- "data-level": "1",
13078
- "data-expanded": expandedL1 === l1.categoryId ? "true" : "false",
13079
- children: [
13080
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
13081
- /* @__PURE__ */ jsx(
13082
- "a",
13083
- {
13084
- className: "propeller-menu__link flex-1 px-4 py-3 text-sm font-medium text-foreground",
13085
- href: getCategoryUrl(l1),
13086
- onClick: (e) => handleItemClick(l1, e),
13087
- children: /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(l1) })
13088
- }
13089
- ),
13090
- getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx(
13091
- "button",
13092
- {
13093
- type: "button",
13094
- className: "propeller-menu__toggle px-4 py-3 text-muted-foreground",
13095
- onClick: (event) => {
13096
- setExpandedL1(expandedL1 === l1.categoryId ? null : l1.categoryId);
13097
- },
13098
- children: /* @__PURE__ */ jsx(
13099
- "svg",
13100
- {
13101
- fill: "none",
13102
- stroke: "currentColor",
13103
- viewBox: "0 0 24 24",
13104
- className: `propeller-menu__chevron w-4 h-4 transition-transform ${expandedL1 === l1.categoryId ? "rotate-180" : ""}`,
13105
- children: /* @__PURE__ */ jsx(
13106
- "path",
13107
- {
13108
- strokeLinecap: "round",
13109
- strokeLinejoin: "round",
13110
- d: "m6 9 6 6 6-6",
13111
- strokeWidth: 2
13112
- }
13113
- )
13114
- }
13115
- )
13116
- }
13117
- ) : null
13118
- ] }),
13119
- expandedL1 === l1.categoryId && getSubCategories(l1).length > 0 ? /* @__PURE__ */ jsx("ul", { className: "propeller-menu__list bg-accent/30", "data-level": "2", children: getSubCategories(l1)?.map((l2, idx2) => /* @__PURE__ */ jsxs(
13120
- "li",
13121
- {
13122
- className: "propeller-menu__item",
13123
- "data-level": "2",
13124
- "data-expanded": expandedL2 === l2.categoryId ? "true" : "false",
13125
- children: [
13126
- /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
13127
- /* @__PURE__ */ jsx(
13128
- "a",
13129
- {
13130
- className: "propeller-menu__link flex-1 pl-8 pr-4 py-2.5 text-sm text-foreground",
13131
- href: getCategoryUrl(l2),
13132
- onClick: (e) => handleItemClick(l2, e),
13133
- children: /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(l2) })
13134
- }
13135
- ),
13136
- getSubCategories(l2).length > 0 ? /* @__PURE__ */ jsx(
13137
- "button",
13138
- {
13139
- type: "button",
13140
- className: "propeller-menu__toggle px-4 py-2.5 text-muted-foreground",
13141
- onClick: (event) => {
13142
- setExpandedL2(expandedL2 === l2.categoryId ? null : l2.categoryId);
13143
- },
13144
- children: /* @__PURE__ */ jsx(
13145
- "svg",
13146
- {
13147
- fill: "none",
13148
- stroke: "currentColor",
13149
- viewBox: "0 0 24 24",
13150
- className: `propeller-menu__chevron w-3.5 h-3.5 transition-transform ${expandedL2 === l2.categoryId ? "rotate-180" : ""}`,
13151
- children: /* @__PURE__ */ jsx(
13152
- "path",
13153
- {
13154
- strokeLinecap: "round",
13155
- strokeLinejoin: "round",
13156
- d: "m6 9 6 6 6-6",
13157
- strokeWidth: 2
13158
- }
13159
- )
13160
- }
13161
- )
13162
- }
13163
- ) : null
13164
- ] }),
13165
- expandedL2 === l2.categoryId && getSubCategories(l2).length > 0 ? /* @__PURE__ */ jsx("ul", { className: "propeller-menu__list bg-accent/20", "data-level": "3", children: getSubCategories(l2)?.map((l3, idx3) => /* @__PURE__ */ jsx(
13166
- "li",
13167
- {
13168
- className: "propeller-menu__item",
13169
- "data-level": "3",
13170
- children: /* @__PURE__ */ jsx(
13171
- "a",
13172
- {
13173
- className: "propeller-menu__link block pl-12 pr-4 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors",
13174
- href: getCategoryUrl(l3),
13175
- onClick: (e) => handleItemClick(l3, e),
13176
- children: /* @__PURE__ */ jsx("span", { className: "propeller-menu__label", children: getCategoryName(l3) })
13177
- }
13178
- )
13179
- },
13180
- `l3-mobile-${l3.categoryId}-${idx3}`
13181
- )) }) : null
13182
- ]
13183
- },
13184
- `l2-mobile-${l2.categoryId}-${idx2}`
13185
- )) }) : null
13186
- ]
13187
- },
13188
- `l1-mobile-${l1.categoryId}-${idx}`
13189
- )) }) }) : null
13075
+ !isLoading && !hasError && menuCategories.length > 0 ? /* @__PURE__ */ jsx("nav", { className: `propeller-menu__nav md:hidden ${props.menuClass || ""}`, children: renderAccordion(menuCategories, 0) }) : null
13190
13076
  ]
13191
13077
  }
13192
13078
  );