@propeller-commerce/propeller-v2-react-ui 0.11.0 → 0.11.2
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/CHANGELOG.md +65 -0
- package/dist/index.cjs +48 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -6
- package/dist/index.d.ts +74 -6
- package/dist/index.js +48 -6
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,71 @@ once it reaches 1.0. Until then (the `0.x` line) the public API may change
|
|
|
8
8
|
between minor versions; breaking changes are called out below and in
|
|
9
9
|
[MIGRATION.md](./MIGRATION.md).
|
|
10
10
|
|
|
11
|
+
## [0.11.2] - 2026-07-31
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`<ProductSlider>` dropped `onLoginClick`, so its log-in action did
|
|
16
|
+
nothing.** In a semi-closed portal `ProductCard` replaces add-to-cart with
|
|
17
|
+
`<LoginToOrderButton>`, and the card resolves `onLoginClick` from the grid
|
|
18
|
+
config. `ProductGrid` puts it there; the slider never did — it forwards every
|
|
19
|
+
other card callback (`onProceedToCheckout`, `onRequestQuoteClick`,
|
|
20
|
+
`afterAddToCart`, …) but this one was missed when the semi-closed gating
|
|
21
|
+
landed in 0.8.0. Anonymous visitors got a button that looked clickable and
|
|
22
|
+
went nowhere, on every slider — cross-sells, up-sells and CMS product blocks.
|
|
23
|
+
It is now forwarded like the rest.
|
|
24
|
+
|
|
25
|
+
## [0.11.1] - 2026-07-30
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **`menuStyle="accordion"` rendered nothing above `md`.** The accordion is the
|
|
30
|
+
mobile drawer for every style, so its `<nav>` carried `md:hidden` and rendered
|
|
31
|
+
unconditionally — while the desktop branches were gated on `menuStyle`.
|
|
32
|
+
Selecting it therefore matched no desktop branch and hid the mobile one,
|
|
33
|
+
leaving an empty panel. It now renders at every breakpoint when chosen
|
|
34
|
+
explicitly, and stays `md:hidden` as the mobile presentation of the others.
|
|
35
|
+
This is what 0.11.0's depth warning already recommended for deep trees.
|
|
36
|
+
- **`menuStyle` values with no renderer failed silently.** An unrecognised
|
|
37
|
+
style matched no branch and produced an empty panel with nothing to indicate
|
|
38
|
+
why. It now falls back to `accordion` — the style that can display any tree —
|
|
39
|
+
and warns in development, pointing at `renderMenu`.
|
|
40
|
+
- **The accordion's expand control didn't look clickable.** Tailwind v4 resets
|
|
41
|
+
buttons to `cursor: default`, so the chevron — the only control that expands a
|
|
42
|
+
branch — gave no affordance. Added `cursor-pointer` plus a hover colour shift,
|
|
43
|
+
and the same to the jumbotron tabs, which had it too.
|
|
44
|
+
- **A deep flyout could run off the viewport with no way to reach it.** The
|
|
45
|
+
columns row now has `max-w-[100vw] overflow-x-auto`, so a tree wider than the
|
|
46
|
+
screen degrades to a scroll instead of being unreachable.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- **`renderMenu` — render the menu yourself.** The built-in styles are three
|
|
51
|
+
arrangements of the same tree; this is the escape hatch for a fourth. It
|
|
52
|
+
receives a `MenuRenderContext` carrying the categories, the open-path state
|
|
53
|
+
and the same helpers the built-ins use (`getCategoryUrl` honouring
|
|
54
|
+
`getUrl`/`configuration`, `handleItemClick` firing `onMenuItemClick`,
|
|
55
|
+
`openAt`/`toggleAt`/`isOpenAt`), so a custom menu gets working open/close
|
|
56
|
+
behaviour and URL building without reimplementing them and drifting. Return
|
|
57
|
+
`null` to fall through to the `menuStyle` rendering. `MenuStyle` and
|
|
58
|
+
`MenuRenderContext` are exported.
|
|
59
|
+
- **First tests for `Menu`** (9 cases), covering the failures above: they all
|
|
60
|
+
passed type-checking and rendered *something*, so only a DOM assertion catches
|
|
61
|
+
them.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **`dropdown-horizontal` removed.** It was referenced only in the depth-cap
|
|
66
|
+
logic and never had a renderer, so it advertised a style that drew nothing.
|
|
67
|
+
Callers using it now hit the unknown-style fallback (accordion + warning); use
|
|
68
|
+
`renderMenu` for a genuinely custom layout.
|
|
69
|
+
- **`dropdown-vertical` caps at 5 levels, up from 4.** Each level is a 256px
|
|
70
|
+
column, so five total 1280px — within a 1440px desktop. Paired with the
|
|
71
|
+
overflow guard above for narrower screens.
|
|
72
|
+
- **`menuStyle` is typed `MenuStyle | (string & {})`** rather than `string`: the
|
|
73
|
+
built-ins autocomplete while a custom value still type-checks for use with
|
|
74
|
+
`renderMenu`.
|
|
75
|
+
|
|
11
76
|
## [0.11.0] - 2026-07-30
|
|
12
77
|
|
|
13
78
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -12845,6 +12845,7 @@ function ItemsOverview(props) {
|
|
|
12845
12845
|
] });
|
|
12846
12846
|
}
|
|
12847
12847
|
var ItemsOverview_default = ItemsOverview;
|
|
12848
|
+
var RENDERED_STYLES = ["dropdown-vertical", "jumbotron", "accordion"];
|
|
12848
12849
|
function Menu(rawProps) {
|
|
12849
12850
|
const props = useInfraProps(rawProps);
|
|
12850
12851
|
const [openPath, setOpenPath] = React6.useState([]);
|
|
@@ -12900,11 +12901,20 @@ function Menu(rawProps) {
|
|
|
12900
12901
|
return openPath[level] === categoryId;
|
|
12901
12902
|
}
|
|
12902
12903
|
function getMenuStyle() {
|
|
12903
|
-
|
|
12904
|
+
const requested = props.menuStyle || "dropdown-vertical";
|
|
12905
|
+
if (!RENDERED_STYLES.includes(requested)) {
|
|
12906
|
+
if (process.env.NODE_ENV !== "production") {
|
|
12907
|
+
console.warn(
|
|
12908
|
+
`[Menu] menuStyle="${requested}" has no built-in renderer (available: ${RENDERED_STYLES.join(", ")}); falling back to "accordion". Pass \`renderMenu\` to draw a custom style.`
|
|
12909
|
+
);
|
|
12910
|
+
}
|
|
12911
|
+
return "accordion";
|
|
12912
|
+
}
|
|
12913
|
+
return requested;
|
|
12904
12914
|
}
|
|
12905
12915
|
function getRenderableDepth() {
|
|
12906
12916
|
const requested = Math.max(1, props.depth ?? 3);
|
|
12907
|
-
const cap = getMenuStyle() === "
|
|
12917
|
+
const cap = getMenuStyle() === "accordion" ? Number.POSITIVE_INFINITY : 5;
|
|
12908
12918
|
if (process.env.NODE_ENV !== "production" && requested > cap) {
|
|
12909
12919
|
console.warn(
|
|
12910
12920
|
`[Menu] depth={${requested}} exceeds what menuStyle="${getMenuStyle()}" can lay out (${cap}); rendering ${cap} levels. Use menuStyle="accordion" for deeper trees.`
|
|
@@ -13000,7 +13010,7 @@ function Menu(rawProps) {
|
|
|
13000
13010
|
"button",
|
|
13001
13011
|
{
|
|
13002
13012
|
type: "button",
|
|
13003
|
-
className: "propeller-menu__toggle px-4 py-3",
|
|
13013
|
+
className: "propeller-menu__toggle cursor-pointer px-4 py-3 text-muted-foreground transition-colors hover:text-foreground",
|
|
13004
13014
|
"aria-expanded": expanded,
|
|
13005
13015
|
"aria-label": getCategoryName(cat),
|
|
13006
13016
|
onClick: () => toggleAt(level, cat.categoryId),
|
|
@@ -13022,6 +13032,31 @@ function Menu(rawProps) {
|
|
|
13022
13032
|
if (!props.graphqlClient) return;
|
|
13023
13033
|
fetchMenu(props.categoryId, userKey);
|
|
13024
13034
|
}, [hasPrefetchedTree, props.graphqlClient, props.categoryId, props.language, userKey, fetchMenu]);
|
|
13035
|
+
const customMenu = props.renderMenu ? props.renderMenu({
|
|
13036
|
+
categories: menuCategories,
|
|
13037
|
+
isLoading,
|
|
13038
|
+
hasError,
|
|
13039
|
+
maxDepth,
|
|
13040
|
+
getSubCategories,
|
|
13041
|
+
getCategoryName,
|
|
13042
|
+
getCategoryUrl,
|
|
13043
|
+
handleItemClick,
|
|
13044
|
+
openPath,
|
|
13045
|
+
isOpenAt,
|
|
13046
|
+
openAt,
|
|
13047
|
+
toggleAt
|
|
13048
|
+
}) : null;
|
|
13049
|
+
if (customMenu !== null && customMenu !== void 0) {
|
|
13050
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13051
|
+
"div",
|
|
13052
|
+
{
|
|
13053
|
+
className: `propeller-menu ${props.className || ""}`,
|
|
13054
|
+
"data-variant": props.menuStyle || "custom",
|
|
13055
|
+
"data-loading": isLoading ? "true" : "false",
|
|
13056
|
+
children: customMenu
|
|
13057
|
+
}
|
|
13058
|
+
);
|
|
13059
|
+
}
|
|
13025
13060
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13026
13061
|
"div",
|
|
13027
13062
|
{
|
|
@@ -13039,7 +13074,7 @@ function Menu(rawProps) {
|
|
|
13039
13074
|
"nav",
|
|
13040
13075
|
{
|
|
13041
13076
|
className: `propeller-menu__nav hidden md:block ${props.menuClass || ""}`,
|
|
13042
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex bg-popover border border-border shadow-lg", children: renderColumns(menuCategories, 0) })
|
|
13077
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-w-[100vw] overflow-x-auto bg-popover border border-border shadow-lg", children: renderColumns(menuCategories, 0) })
|
|
13043
13078
|
}
|
|
13044
13079
|
) : null,
|
|
13045
13080
|
!isLoading && !hasError && menuCategories.length > 0 && getMenuStyle() === "jumbotron" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -13050,7 +13085,7 @@ function Menu(rawProps) {
|
|
|
13050
13085
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-menu__tabs flex items-center border-b border-border", children: menuCategories?.map((l1, idx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
13051
13086
|
"button",
|
|
13052
13087
|
{
|
|
13053
|
-
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"}`,
|
|
13088
|
+
className: `propeller-menu__tab cursor-pointer 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"}`,
|
|
13054
13089
|
"data-level": "1",
|
|
13055
13090
|
"data-active": isOpenAt(0, l1.categoryId) ? "true" : "false",
|
|
13056
13091
|
onMouseEnter: (event) => openAt(0, l1.categoryId),
|
|
@@ -13092,7 +13127,13 @@ function Menu(rawProps) {
|
|
|
13092
13127
|
]
|
|
13093
13128
|
}
|
|
13094
13129
|
) : null,
|
|
13095
|
-
!isLoading && !hasError && menuCategories.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
13130
|
+
!isLoading && !hasError && menuCategories.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
13131
|
+
"nav",
|
|
13132
|
+
{
|
|
13133
|
+
className: `propeller-menu__nav ${getMenuStyle() === "accordion" ? "block" : "md:hidden"} ${props.menuClass || ""}`,
|
|
13134
|
+
children: renderAccordion(menuCategories, 0)
|
|
13135
|
+
}
|
|
13136
|
+
) : null
|
|
13096
13137
|
]
|
|
13097
13138
|
}
|
|
13098
13139
|
);
|
|
@@ -15996,6 +16037,7 @@ function ProductSlider(rawProps) {
|
|
|
15996
16037
|
afterAddToCart: props.afterAddToCart,
|
|
15997
16038
|
onProceedToCheckout: props.onProceedToCheckout,
|
|
15998
16039
|
onRequestQuoteClick: props.onRequestQuoteClick,
|
|
16040
|
+
onLoginClick: props.onLoginClick,
|
|
15999
16041
|
onToggleFavorite: (item, isFav) => props.onToggleFavorite?.(item, isFav),
|
|
16000
16042
|
onProductClick: (p) => handleProductClick(p),
|
|
16001
16043
|
onClusterClick: (c) => handleClusterClick(c),
|