@jsswift/ui 1.0.53 → 2.0.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.
Files changed (40) hide show
  1. package/dist/css/animation.css +36 -36
  2. package/dist/css/base.css +374 -374
  3. package/dist/css/docs.css +54 -54
  4. package/dist/css/fonts-nunito.css +2 -2
  5. package/dist/css/min-ui.css +1 -1
  6. package/dist/css/responsive-base.css +557 -0
  7. package/dist/css/responsive.css +3225 -3225
  8. package/dist/css/ui-components.css +2902 -2930
  9. package/dist/css/ui-light.css +9908 -0
  10. package/dist/css/ui-light.min.css +1 -0
  11. package/dist/css/ui.css +6530 -6558
  12. package/dist/css/ui.min.css +1 -1
  13. package/dist/min-ui.js +12 -12
  14. package/dist/ui.js +4354 -9379
  15. package/dist/ui.min.js +12 -12
  16. package/package.json +4 -2
  17. package/src/00-bootstrap.js +305 -305
  18. package/src/01-foundation-helpers.js +328 -325
  19. package/src/10-primitives-layout.js +242 -242
  20. package/src/20-display-content.js +76 -76
  21. package/src/21-list-content.js +21 -21
  22. package/src/22-banner-content.js +376 -376
  23. package/src/23-tooltip.js +9 -9
  24. package/src/31-form-advanced.js +141 -141
  25. package/src/40-navigation.js +81 -81
  26. package/src/41-tab-panel.js +37 -37
  27. package/src/50-feedback.js +52 -52
  28. package/src/51-feedback-service.js +166 -166
  29. package/src/60-shell-app.js +1082 -1082
  30. package/src/61-form-service.js +5 -5
  31. package/src/70-dialog.js +34 -34
  32. package/src/71-menu-overlays.js +73 -73
  33. package/src/80-data.js +38 -38
  34. package/src/css/README.md +2 -0
  35. package/src/css/animation.css +36 -36
  36. package/src/css/base.css +374 -374
  37. package/src/css/docs.css +54 -54
  38. package/src/css/fonts-nunito.css +2 -2
  39. package/src/css/responsive.css +3225 -3225
  40. package/src/css/ui-components.css +2902 -2930
@@ -1,207 +1,207 @@
1
- UI.Header = (...args) => {
2
- const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
3
- const slots = rawProps.slots || {};
4
- const props = { ...rawProps };
5
- applyCommonProps(props);
6
-
7
- const hasOwn = (key) => Object.prototype.hasOwnProperty.call(rawProps, key);
8
- const currentStateKey = rawProps.drawerStateKey ?? drawerStateKey;
9
- if (currentStateKey) {
10
- drawerStateKey = currentStateKey;
11
- drawerOpen = readDrawerOpen(currentStateKey);
12
- }
13
-
14
- const isDrawerOpen = () => readDrawerOpen(currentStateKey);
15
- const openDrawer = () => setDrawerOpen(true, currentStateKey);
16
- const closeDrawer = () => setDrawerOpen(false, currentStateKey);
17
- const toggleDrawer = () => setDrawerOpen(!isDrawerOpen(), currentStateKey);
18
- const ctx = {
19
- props: rawProps,
20
- stateKey: currentStateKey,
21
- isDrawerOpen,
22
- openDrawer,
23
- closeDrawer,
24
- toggleDrawer,
25
- toggleAside: toggleDrawer
26
- };
27
- const appendResolvedValue = (host, value) => {
28
- if (value == null || value === false) return;
29
- if (Array.isArray(value)) {
30
- value.forEach((item) => appendResolvedValue(host, item));
31
- return;
32
- }
33
- if (value?.nodeType) {
34
- host.appendChild(value);
35
- return;
36
- }
37
- host.appendChild(document.createTextNode(String(value)));
38
- };
39
- const renderPropNodes = (name, fallback, map = (value) => value) => {
40
- const slot = JSswift.ui.getSlot(slots, name);
41
- if (slot !== null && slot !== undefined) {
42
- return renderSlotToArray(slots, name, ctx, null);
43
- }
44
- if (typeof fallback === "function") {
45
- const inlineNames = new Set(["eyebrow", "title", "subtitle"]);
46
- const host = _[inlineNames.has(name) ? "span" : "div"]({ class: `cms-header-slot-${name}` });
47
- JSswift.reactive.effect(() => {
48
- const nextValue = map(fallback(ctx));
49
- const normalized = flattenSlotValue(JSswift.ui.slot(nextValue));
50
- host.replaceChildren();
51
- if (Array.isArray(normalized)) normalized.forEach((item) => appendResolvedValue(host, item));
52
- else appendResolvedValue(host, normalized);
53
- }, `UI.Header:${name}`);
54
- return [host];
55
- }
56
- return renderSlotToArray(slots, name, ctx, map(fallback));
57
- };
58
-
59
- const renderIconValue = (value, as = "icon", sizeFallback = rawProps.iconSize || rawProps.size || "md") => {
60
- if (value == null || value === false) return null;
61
- if (typeof value === "string") return UI.Icon({ name: value, size: sizeFallback });
62
- return JSswift.ui.slot(value, { as });
63
- };
64
- const renderDrawerToggleValue = (open) => {
65
- const value = open ? (rawProps.drawerOpenIcon ?? "✕") : (rawProps.drawerCloseIcon ?? "☰");
66
- if (value == null || value === false) return null;
67
- if (typeof value === "string") return value;
68
- return JSswift.ui.slot(value, { as: open ? "drawerOpenIcon" : "drawerCloseIcon" });
69
- };
70
-
71
- const toggleIconHost = _.span({ class: "cms-header-toggle-icon" });
72
- const paintToggleIcon = (open) => {
73
- const nodes = renderSlotToArray(null, "default", ctx, renderDrawerToggleValue(open));
74
- toggleIconHost.replaceChildren(...(nodes.length ? nodes : [""]));
75
- };
76
- paintToggleIcon(isDrawerOpen());
77
-
78
- const autoLeft = UI.Btn({
79
- class: "cms-header-toggle",
80
- outline: true,
81
- size: rawProps.toggleSize ?? rawProps.size ?? "sm",
82
- onClick: toggleDrawer,
83
- "aria-label": rawProps.toggleLabel || "Toggle navigation"
84
- }, toggleIconHost);
85
- if (rawProps.left == null && rawProps.left !== false && !JSswift.ui.getSlot(slots, "left")) {
86
- drawerToggleIcons.add({ update: paintToggleIcon });
87
- }
88
-
89
- const leftFallback = rawProps.left === false
90
- ? null
91
- : (rawProps.left != null ? rawProps.left : autoLeft);
92
- const titleFallback = hasOwn("title") ? rawProps.title : (rawProps.label ?? "App");
93
- const subtitleFallback = rawProps.subtitle ?? rawProps.description ?? "";
94
- const rightFallback = hasOwn("right") ? rawProps.right : rawProps.end;
95
- const contentFallback = hasOwn("content")
96
- ? rawProps.content
97
- : (hasOwn("body") ? rawProps.body : (children?.length ? children : null));
98
-
99
- const startNodes = [
100
- ...renderPropNodes("left", leftFallback),
101
- ...renderPropNodes("start", null)
102
- ];
103
- const iconNodes = renderPropNodes("icon", rawProps.icon, renderIconValue);
104
- const eyebrowNodes = renderPropNodes("eyebrow", rawProps.eyebrow ?? rawProps.kicker);
105
- const titleNodes = renderPropNodes("title", titleFallback);
106
- const subtitleNodes = renderPropNodes("subtitle", subtitleFallback);
107
- const metaNodes = renderPropNodes("meta", rawProps.meta);
108
- const contentNodes = (() => {
109
- const explicit = renderPropNodes("content", null);
110
- return explicit.length ? explicit : renderPropNodes("default", contentFallback);
111
- })();
112
- const customCenterNodes = [
113
- ...renderPropNodes("center", null),
114
- ...renderPropNodes("body", null)
115
- ];
116
- const rightNodes = [
117
- ...renderPropNodes("right", rightFallback),
118
- ...renderPropNodes("end", null)
119
- ];
120
- const actionNodes = renderPropNodes("actions", rawProps.actions);
121
-
122
- const structuredCenter = _.div(
123
- { class: uiClass(["cms-header-body", rawProps.bodyClass, rawProps.centerClass]) },
124
- _.div(
125
- { class: "cms-header-heading" },
126
- iconNodes.length ? _.div({ class: "cms-header-icon" }, ...iconNodes) : null,
127
- _.div(
128
- { class: "cms-header-copy" },
129
- eyebrowNodes.length ? _.div({ class: uiClass(["cms-header-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
130
- titleNodes.length ? _.div({ class: uiClass(["cms-header-title", rawProps.titleClass]) }, ...titleNodes) : null,
131
- subtitleNodes.length ? _.div({ class: uiClass(["cms-header-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
132
- contentNodes.length ? _.div({ class: uiClass(["cms-header-content", rawProps.contentClass]) }, ...contentNodes) : null
133
- ),
134
- metaNodes.length ? _.div({ class: uiClass(["cms-header-meta", rawProps.metaClass]) }, ...metaNodes) : null
135
- )
136
- );
137
-
138
- const endContent = [
139
- ...rightNodes,
140
- ...(actionNodes.length ? [_.div({ class: uiClass(["cms-header-actions", rawProps.actionsClass]) }, ...actionNodes)] : [])
141
- ];
142
-
143
- const p = JSswift.omit(props, [
144
- "actions", "actionsClass", "body", "bodyClass", "centerClass", "content", "contentClass",
145
- "description", "divider", "drawerCloseIcon", "drawerOpenIcon", "drawerStateKey", "elevated",
146
- "end", "eyebrow", "eyebrowClass", "icon", "iconSize", "kicker", "label", "left", "meta",
147
- "metaClass", "right", "slots", "stack", "sticky", "subtitle", "subtitleClass", "title",
148
- "titleClass", "toggleLabel", "toggleSize", "gap", "minHeight", "startClass", "endClass"
149
- ]);
150
- p.class = uiClass([
151
- "cms-panel",
152
- "cms-header",
153
- "cms-singularity",
154
- uiWhen(rawProps.sticky !== false, "sticky"),
155
- uiWhen(rawProps.stack, "stack"),
156
- uiWhen(rawProps.elevated, "elevated"),
157
- uiWhen(rawProps.divider !== false, "divider"),
158
- props.class
159
- ]);
160
- p.style = { ...(props.style || {}) };
161
- if (rawProps.gap != null) p.style["--cms-header-gap"] = toCssSize(uiUnwrap(rawProps.gap));
162
- if (rawProps.minHeight != null) p.style.minHeight = toCssSize(uiUnwrap(rawProps.minHeight));
163
-
164
- const el = _.div(
165
- p,
166
- ...(startNodes.length ? [_.div({ class: uiClass(["cms-header-start", rawProps.startClass]) }, ...startNodes)] : []),
167
- _.div(
168
- { class: "cms-header-main" },
169
- ...(customCenterNodes.length
170
- ? [_.div({ class: uiClass(["cms-header-body", rawProps.bodyClass, rawProps.centerClass]) }, ...customCenterNodes)]
171
- : [structuredCenter]),
172
- ...(endContent.length ? [_.div({ class: uiClass(["cms-header-end", rawProps.endClass]) }, ...endContent)] : [])
173
- )
174
- );
175
-
176
- setPropertyProps(el, rawProps);
177
- return el;
178
- };
179
-
180
-
1
+ UI.Header = (...args) => {
2
+ const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
3
+ const slots = rawProps.slots || {};
4
+ const props = { ...rawProps };
5
+ applyCommonProps(props);
6
+
7
+ const hasOwn = (key) => Object.prototype.hasOwnProperty.call(rawProps, key);
8
+ const currentStateKey = rawProps.drawerStateKey ?? drawerStateKey;
9
+ if (currentStateKey) {
10
+ drawerStateKey = currentStateKey;
11
+ drawerOpen = readDrawerOpen(currentStateKey);
12
+ }
13
+
14
+ const isDrawerOpen = () => readDrawerOpen(currentStateKey);
15
+ const openDrawer = () => setDrawerOpen(true, currentStateKey);
16
+ const closeDrawer = () => setDrawerOpen(false, currentStateKey);
17
+ const toggleDrawer = () => setDrawerOpen(!isDrawerOpen(), currentStateKey);
18
+ const ctx = {
19
+ props: rawProps,
20
+ stateKey: currentStateKey,
21
+ isDrawerOpen,
22
+ openDrawer,
23
+ closeDrawer,
24
+ toggleDrawer,
25
+ toggleAside: toggleDrawer
26
+ };
27
+ const appendResolvedValue = (host, value) => {
28
+ if (value == null || value === false) return;
29
+ if (Array.isArray(value)) {
30
+ value.forEach((item) => appendResolvedValue(host, item));
31
+ return;
32
+ }
33
+ if (value?.nodeType) {
34
+ host.appendChild(value);
35
+ return;
36
+ }
37
+ host.appendChild(document.createTextNode(String(value)));
38
+ };
39
+ const renderPropNodes = (name, fallback, map = (value) => value) => {
40
+ const slot = JSswift.ui.getSlot(slots, name);
41
+ if (slot !== null && slot !== undefined) {
42
+ return renderSlotToArray(slots, name, ctx, null);
43
+ }
44
+ if (typeof fallback === "function") {
45
+ const inlineNames = new Set(["eyebrow", "title", "subtitle"]);
46
+ const host = _[inlineNames.has(name) ? "span" : "div"]({ class: `jss-header-slot-${name}` });
47
+ JSswift.reactive.effect(() => {
48
+ const nextValue = map(fallback(ctx));
49
+ const normalized = flattenSlotValue(JSswift.ui.slot(nextValue));
50
+ host.replaceChildren();
51
+ if (Array.isArray(normalized)) normalized.forEach((item) => appendResolvedValue(host, item));
52
+ else appendResolvedValue(host, normalized);
53
+ }, `UI.Header:${name}`);
54
+ return [host];
55
+ }
56
+ return renderSlotToArray(slots, name, ctx, map(fallback));
57
+ };
58
+
59
+ const renderIconValue = (value, as = "icon", sizeFallback = rawProps.iconSize || rawProps.size || "md") => {
60
+ if (value == null || value === false) return null;
61
+ if (typeof value === "string") return UI.Icon({ name: value, size: sizeFallback });
62
+ return JSswift.ui.slot(value, { as });
63
+ };
64
+ const renderDrawerToggleValue = (open) => {
65
+ const value = open ? (rawProps.drawerOpenIcon ?? "✕") : (rawProps.drawerCloseIcon ?? "☰");
66
+ if (value == null || value === false) return null;
67
+ if (typeof value === "string") return value;
68
+ return JSswift.ui.slot(value, { as: open ? "drawerOpenIcon" : "drawerCloseIcon" });
69
+ };
70
+
71
+ const toggleIconHost = _.span({ class: "jss-header-toggle-icon" });
72
+ const paintToggleIcon = (open) => {
73
+ const nodes = renderSlotToArray(null, "default", ctx, renderDrawerToggleValue(open));
74
+ toggleIconHost.replaceChildren(...(nodes.length ? nodes : [""]));
75
+ };
76
+ paintToggleIcon(isDrawerOpen());
77
+
78
+ const autoLeft = UI.Btn({
79
+ class: "jss-header-toggle",
80
+ outline: true,
81
+ size: rawProps.toggleSize ?? rawProps.size ?? "sm",
82
+ onClick: toggleDrawer,
83
+ "aria-label": rawProps.toggleLabel || "Toggle navigation"
84
+ }, toggleIconHost);
85
+ if (rawProps.left == null && rawProps.left !== false && !JSswift.ui.getSlot(slots, "left")) {
86
+ drawerToggleIcons.add({ update: paintToggleIcon });
87
+ }
88
+
89
+ const leftFallback = rawProps.left === false
90
+ ? null
91
+ : (rawProps.left != null ? rawProps.left : autoLeft);
92
+ const titleFallback = hasOwn("title") ? rawProps.title : (rawProps.label ?? "App");
93
+ const subtitleFallback = rawProps.subtitle ?? rawProps.description ?? "";
94
+ const rightFallback = hasOwn("right") ? rawProps.right : rawProps.end;
95
+ const contentFallback = hasOwn("content")
96
+ ? rawProps.content
97
+ : (hasOwn("body") ? rawProps.body : (children?.length ? children : null));
98
+
99
+ const startNodes = [
100
+ ...renderPropNodes("left", leftFallback),
101
+ ...renderPropNodes("start", null)
102
+ ];
103
+ const iconNodes = renderPropNodes("icon", rawProps.icon, renderIconValue);
104
+ const eyebrowNodes = renderPropNodes("eyebrow", rawProps.eyebrow ?? rawProps.kicker);
105
+ const titleNodes = renderPropNodes("title", titleFallback);
106
+ const subtitleNodes = renderPropNodes("subtitle", subtitleFallback);
107
+ const metaNodes = renderPropNodes("meta", rawProps.meta);
108
+ const contentNodes = (() => {
109
+ const explicit = renderPropNodes("content", null);
110
+ return explicit.length ? explicit : renderPropNodes("default", contentFallback);
111
+ })();
112
+ const customCenterNodes = [
113
+ ...renderPropNodes("center", null),
114
+ ...renderPropNodes("body", null)
115
+ ];
116
+ const rightNodes = [
117
+ ...renderPropNodes("right", rightFallback),
118
+ ...renderPropNodes("end", null)
119
+ ];
120
+ const actionNodes = renderPropNodes("actions", rawProps.actions);
121
+
122
+ const structuredCenter = _.div(
123
+ { class: uiClass(["jss-header-body", rawProps.bodyClass, rawProps.centerClass]) },
124
+ _.div(
125
+ { class: "jss-header-heading" },
126
+ iconNodes.length ? _.div({ class: "jss-header-icon" }, ...iconNodes) : null,
127
+ _.div(
128
+ { class: "jss-header-copy" },
129
+ eyebrowNodes.length ? _.div({ class: uiClass(["jss-header-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
130
+ titleNodes.length ? _.div({ class: uiClass(["jss-header-title", rawProps.titleClass]) }, ...titleNodes) : null,
131
+ subtitleNodes.length ? _.div({ class: uiClass(["jss-header-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
132
+ contentNodes.length ? _.div({ class: uiClass(["jss-header-content", rawProps.contentClass]) }, ...contentNodes) : null
133
+ ),
134
+ metaNodes.length ? _.div({ class: uiClass(["jss-header-meta", rawProps.metaClass]) }, ...metaNodes) : null
135
+ )
136
+ );
137
+
138
+ const endContent = [
139
+ ...rightNodes,
140
+ ...(actionNodes.length ? [_.div({ class: uiClass(["jss-header-actions", rawProps.actionsClass]) }, ...actionNodes)] : [])
141
+ ];
142
+
143
+ const p = JSswift.omit(props, [
144
+ "actions", "actionsClass", "body", "bodyClass", "centerClass", "content", "contentClass",
145
+ "description", "divider", "drawerCloseIcon", "drawerOpenIcon", "drawerStateKey", "elevated",
146
+ "end", "eyebrow", "eyebrowClass", "icon", "iconSize", "kicker", "label", "left", "meta",
147
+ "metaClass", "right", "slots", "stack", "sticky", "subtitle", "subtitleClass", "title",
148
+ "titleClass", "toggleLabel", "toggleSize", "gap", "minHeight", "startClass", "endClass"
149
+ ]);
150
+ p.class = uiClass([
151
+ "jss-panel",
152
+ "jss-header",
153
+ "jss-singularity",
154
+ uiWhen(rawProps.sticky !== false, "sticky"),
155
+ uiWhen(rawProps.stack, "stack"),
156
+ uiWhen(rawProps.elevated, "elevated"),
157
+ uiWhen(rawProps.divider !== false, "divider"),
158
+ props.class
159
+ ]);
160
+ p.style = { ...(props.style || {}) };
161
+ if (rawProps.gap != null) p.style["--jss-header-gap"] = toCssSize(uiUnwrap(rawProps.gap));
162
+ if (rawProps.minHeight != null) p.style.minHeight = toCssSize(uiUnwrap(rawProps.minHeight));
163
+
164
+ const el = _.div(
165
+ p,
166
+ ...(startNodes.length ? [_.div({ class: uiClass(["jss-header-start", rawProps.startClass]) }, ...startNodes)] : []),
167
+ _.div(
168
+ { class: "jss-header-main" },
169
+ ...(customCenterNodes.length
170
+ ? [_.div({ class: uiClass(["jss-header-body", rawProps.bodyClass, rawProps.centerClass]) }, ...customCenterNodes)]
171
+ : [structuredCenter]),
172
+ ...(endContent.length ? [_.div({ class: uiClass(["jss-header-end", rawProps.endClass]) }, ...endContent)] : [])
173
+ )
174
+ );
175
+
176
+ setPropertyProps(el, rawProps);
177
+ return el;
178
+ };
179
+
180
+
181
181
  UI.Drawer = (...args) => {
182
182
  const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
183
183
  const slots = rawProps.slots || {};
184
184
  const props = { ...rawProps };
185
185
  const hasOwn = (key) => Object.prototype.hasOwnProperty.call(rawProps, key);
186
- const currentStateKey = rawProps.stateKey ?? drawerStateKey;
187
- if (currentStateKey) {
188
- drawerStateKey = currentStateKey;
189
- drawerOpen = readDrawerOpen(currentStateKey);
190
- }
191
-
192
- const isDrawerOpen = () => readDrawerOpen(currentStateKey);
193
- const openDrawer = () => setDrawerOpen(true, currentStateKey);
194
- const closeDrawer = () => setDrawerOpen(false, currentStateKey);
195
- const toggleDrawer = () => setDrawerOpen(!isDrawerOpen(), currentStateKey);
196
- const ctx = {
197
- props: rawProps,
198
- stateKey: currentStateKey,
199
- isDrawerOpen,
200
- openDrawer,
201
- closeDrawer,
202
- toggleDrawer,
203
- toggleAside: toggleDrawer
204
- };
186
+ const currentStateKey = rawProps.stateKey ?? drawerStateKey;
187
+ if (currentStateKey) {
188
+ drawerStateKey = currentStateKey;
189
+ drawerOpen = readDrawerOpen(currentStateKey);
190
+ }
191
+
192
+ const isDrawerOpen = () => readDrawerOpen(currentStateKey);
193
+ const openDrawer = () => setDrawerOpen(true, currentStateKey);
194
+ const closeDrawer = () => setDrawerOpen(false, currentStateKey);
195
+ const toggleDrawer = () => setDrawerOpen(!isDrawerOpen(), currentStateKey);
196
+ const ctx = {
197
+ props: rawProps,
198
+ stateKey: currentStateKey,
199
+ isDrawerOpen,
200
+ openDrawer,
201
+ closeDrawer,
202
+ toggleDrawer,
203
+ toggleAside: toggleDrawer
204
+ };
205
205
 
206
206
  const store = JSswift?.store;
207
207
  const canStore = !!(store?.get && store?.set);
@@ -211,38 +211,38 @@ UI.Drawer = (...args) => {
211
211
  let activeItemKey = canStore ? (store.get(activeStateKey, null) ?? null) : null;
212
212
  const activeLeafRefs = new Map();
213
213
  const activeGroupRefs = new Map();
214
-
215
- const normalizeList = (value) => {
216
- const raw = uiUnwrap(value);
217
- if (raw == null || raw === false) return [];
218
- if (Array.isArray(raw)) return raw.flat(Infinity);
219
- return [raw];
220
- };
221
- const renderArea = (names, fallback, localCtx = ctx) => {
222
- const list = Array.isArray(names) ? names : [names];
223
- for (const name of list) {
224
- if (JSswift.ui.getSlot(slots, name) != null) {
225
- return renderSlotToArray(slots, name, localCtx, fallback);
226
- }
227
- }
228
- return fallback == null ? [] : renderSlotToArray(null, "default", localCtx, uiUnwrap(fallback));
229
- };
230
- const hasArea = (names) => {
231
- const list = Array.isArray(names) ? names : [names];
232
- return list.some((name) => JSswift.ui.getSlot(slots, name) != null);
233
- };
234
-
235
- const isExternalLink = (it) => {
236
- if (typeof uiUnwrap(it.external) === "boolean") return !!uiUnwrap(it.external);
237
- const href = uiUnwrap(it.href || it.to || it.link || "");
238
- return /^(https?:)?\/\//.test(href);
239
- };
240
- const closeOnSelect = rawProps.closeOnSelect ?? true;
241
- const shouldClose = (it) => !!closeOnSelect && uiUnwrap(it.keepOpen) !== true;
242
- const itemIconSize = rawProps.itemIconSize ?? rawProps.size ?? "md";
243
- const groupOpenIcon = rawProps.groupOpenIcon ?? "arrow_drop_up";
244
- const groupCloseIcon = rawProps.groupCloseIcon ?? "arrow_drop_down";
245
-
214
+
215
+ const normalizeList = (value) => {
216
+ const raw = uiUnwrap(value);
217
+ if (raw == null || raw === false) return [];
218
+ if (Array.isArray(raw)) return raw.flat(Infinity);
219
+ return [raw];
220
+ };
221
+ const renderArea = (names, fallback, localCtx = ctx) => {
222
+ const list = Array.isArray(names) ? names : [names];
223
+ for (const name of list) {
224
+ if (JSswift.ui.getSlot(slots, name) != null) {
225
+ return renderSlotToArray(slots, name, localCtx, fallback);
226
+ }
227
+ }
228
+ return fallback == null ? [] : renderSlotToArray(null, "default", localCtx, uiUnwrap(fallback));
229
+ };
230
+ const hasArea = (names) => {
231
+ const list = Array.isArray(names) ? names : [names];
232
+ return list.some((name) => JSswift.ui.getSlot(slots, name) != null);
233
+ };
234
+
235
+ const isExternalLink = (it) => {
236
+ if (typeof uiUnwrap(it.external) === "boolean") return !!uiUnwrap(it.external);
237
+ const href = uiUnwrap(it.href || it.to || it.link || "");
238
+ return /^(https?:)?\/\//.test(href);
239
+ };
240
+ const closeOnSelect = rawProps.closeOnSelect ?? true;
241
+ const shouldClose = (it) => !!closeOnSelect && uiUnwrap(it.keepOpen) !== true;
242
+ const itemIconSize = rawProps.itemIconSize ?? rawProps.size ?? "md";
243
+ const groupOpenIcon = rawProps.groupOpenIcon ?? "arrow_drop_up";
244
+ const groupCloseIcon = rawProps.groupCloseIcon ?? "arrow_drop_down";
245
+
246
246
  const isStoredActive = (key) => !!key && activeItemKey === key;
247
247
  const isBranchActive = (key) => !!key && !!activeItemKey && (activeItemKey === key || activeItemKey.startsWith(`${key}::`));
248
248
  const syncActiveClasses = () => {
@@ -269,8 +269,8 @@ UI.Drawer = (...args) => {
269
269
  if (!href || isExternalLink(it)) return false;
270
270
  try {
271
271
  if (href.startsWith("#")) return window.location.hash === href;
272
- const fullPath = `${window.location.pathname}${window.location.search}${window.location.hash}`;
273
- return href === fullPath || href === window.location.pathname;
272
+ const fullPath = `${window.location.pathname}${window.location.search}${window.location.hash}`;
273
+ return href === fullPath || href === window.location.pathname;
274
274
  } catch {
275
275
  return false;
276
276
  }
@@ -289,139 +289,139 @@ UI.Drawer = (...args) => {
289
289
  return hasActiveChildren(nested, level + 1, path.concat(keyPart));
290
290
  });
291
291
  };
292
- const iconNodes = (icon, localCtx = {}) => {
293
- const raw = uiUnwrap(icon);
294
- if (raw == null || raw === false) return [];
295
- const slotValue = typeof raw === "string"
296
- ? UI.Icon({ name: raw, size: localCtx.iconSize ?? itemIconSize })
297
- : JSswift.ui.slot(raw, { ...localCtx, as: "icon" });
298
- return renderSlotToArray(null, "default", localCtx, slotValue);
299
- };
300
- const wrapIconNodes = (icon, side, localCtx = {}) => {
301
- const nodes = iconNodes(icon, localCtx);
302
- return nodes.length ? [_.span({ class: uiClass(["cms-drawer-item-icon", side]) }, ...nodes)] : [];
303
- };
304
-
305
- const getItemIcons = (it) => {
306
- const side = uiUnwrap(it.iconPosition) || "left";
307
- const leftIcon = Object.prototype.hasOwnProperty.call(it, "iconLeft") ? it.iconLeft : (side !== "right" ? it.icon : null);
308
- const rightIcon = Object.prototype.hasOwnProperty.call(it, "iconRight") ? it.iconRight : (side === "right" ? it.icon : null);
309
- return {
310
- left: leftIcon,
311
- right: rightIcon
312
- };
313
- };
314
-
315
- const itemKeyPart = (it, label, level, idx) => uiUnwrap(it.key || it.id || it.to || it.href || label || `item-${level}-${idx}`);
316
-
317
- const readGroupOpen = (key, fallback) => {
318
- if (!canStore) return fallback;
319
- if (Object.prototype.hasOwnProperty.call(groupState, key)) return !!groupState[key];
320
- return fallback;
321
- };
322
-
323
- const writeGroupOpen = (key, open) => {
324
- if (!canStore) return;
325
- groupState = { ...groupState, [key]: !!open };
326
- store.set(groupStateKey, groupState);
327
- };
328
-
329
- const buildStructuredHeader = () => {
330
- const headerCtx = { ...ctx, area: "header" };
331
- const icon = renderArea(["headerIcon", "icon"], rawProps.icon, headerCtx);
332
- const eyebrow = renderArea(["eyebrow"], rawProps.eyebrow, headerCtx);
333
- const title = renderArea(["title"], rawProps.title, headerCtx);
334
- const subtitle = renderArea(["subtitle"], rawProps.subtitle, headerCtx);
335
- const meta = renderArea(["meta"], rawProps.meta, headerCtx);
336
- const content = renderArea(["content"], rawProps.content, headerCtx);
337
- const actions = renderArea(["actions"], rawProps.actions, headerCtx);
338
- const extra = hasOwn("header") ? renderSlotToArray(null, "default", headerCtx, uiUnwrap(rawProps.header)) : [];
339
- const hasStructured = icon.length || eyebrow.length || title.length || subtitle.length || meta.length || content.length || actions.length;
340
- if (!hasStructured && !extra.length) return [];
341
- if (!hasStructured) return extra;
342
- return [
343
- _.div(
344
- { class: uiClass(["cms-drawer-header", rawProps.headerClass]) },
345
- ...(icon.length ? [_.div({ class: "cms-drawer-header-icon" }, ...icon)] : []),
346
- _.div(
347
- { class: "cms-drawer-header-body" },
348
- ...(eyebrow.length ? [_.div({ class: "cms-drawer-header-eyebrow" }, ...eyebrow)] : []),
349
- ...(title.length ? [_.div({ class: "cms-drawer-header-title" }, ...title)] : []),
350
- ...(subtitle.length ? [_.div({ class: "cms-drawer-header-subtitle" }, ...subtitle)] : []),
351
- ...(meta.length ? [_.div({ class: "cms-drawer-header-meta" }, ...meta)] : []),
352
- ...(content.length ? [_.div({ class: "cms-drawer-header-content" }, ...content)] : []),
353
- ...(extra.length ? [_.div({ class: "cms-drawer-header-extra" }, ...extra)] : [])
354
- ),
355
- ...(actions.length ? [_.div({ class: "cms-drawer-header-actions" }, ...actions)] : [])
356
- )
357
- ];
358
- };
359
- const invokeItemHandler = (it, itemCtx, e) => {
360
- const itemResult = it.onClick?.(e, itemCtx);
361
- if (itemResult === false || e?.defaultPrevented) return false;
362
- const rootResult = rawProps.onSelect?.(it, itemCtx, e);
363
- if (rootResult === false || e?.defaultPrevented) return false;
364
- return true;
365
- };
366
- const renderEntryNodes = (it, itemCtx, extras = {}) => {
367
- const slotPrefix = itemCtx.hasChildren ? "group" : "item";
368
- const labelNodes = renderSlotToArray(slots, `${slotPrefix}Label`, itemCtx, itemCtx.label);
369
- const noteNodes = renderSlotToArray(slots, `${slotPrefix}Note`, itemCtx, itemCtx.note);
370
- const badgeNodes = renderSlotToArray(slots, `${slotPrefix}Badge`, itemCtx, itemCtx.badge);
371
- const asideNodes = renderSlotToArray(slots, `${slotPrefix}Aside`, itemCtx, itemCtx.aside);
372
- const contentNodes = renderSlotToArray(slots, `${slotPrefix}Content`, itemCtx, itemCtx.content);
373
- const mainChildren = [];
374
-
375
- if (labelNodes.length || noteNodes.length || badgeNodes.length || asideNodes.length) {
376
- mainChildren.push(
377
- _.div(
378
- { class: "cms-drawer-item-top" },
379
- _.div(
380
- { class: "cms-drawer-item-copy" },
381
- ...(labelNodes.length ? [_.div({ class: "cms-drawer-item-label" }, ...labelNodes)] : []),
382
- ...(noteNodes.length ? [_.div({ class: "cms-drawer-item-note" }, ...noteNodes)] : [])
383
- ),
384
- ...(badgeNodes.length || asideNodes.length
385
- ? [_.div(
386
- { class: "cms-drawer-item-meta" },
387
- ...(badgeNodes.length ? [_.div({ class: "cms-drawer-item-badge" }, ...badgeNodes)] : []),
388
- ...(asideNodes.length ? [_.div({ class: "cms-drawer-item-aside" }, ...asideNodes)] : [])
389
- )]
390
- : [])
391
- )
392
- );
393
- }
394
- if (contentNodes.length) mainChildren.push(_.div({ class: "cms-drawer-item-content" }, ...contentNodes));
395
-
396
- return [
397
- ...(extras.start || []),
398
- ...wrapIconNodes(itemCtx.iconLeft, "left", itemCtx),
399
- ...(mainChildren.length ? [_.div({ class: "cms-drawer-item-main" }, ...mainChildren)] : []),
400
- ...wrapIconNodes(itemCtx.iconRight, "right", itemCtx),
401
- ...(extras.end || [])
402
- ];
403
- };
404
- const renderItems = (list = [], level = 0, path = []) => {
405
- return normalizeList(list).map((entry, idx) => {
406
- const it = uiUnwrap(entry);
407
- if (it == null || it === false) return null;
408
-
409
- if (it.nodeType || typeof it !== "object" || Array.isArray(it)) {
410
- return _.div({
411
- class: "cms-drawer-custom",
412
- "data-level": String(level)
413
- }, ...renderSlotToArray(null, "default", { ...ctx, item: it, level, index: idx, path }, it));
414
- }
415
-
416
- if (uiUnwrap(it.hidden) === true || uiUnwrap(it.visible) === false) return null;
417
-
418
- const childItems = normalizeList(it.items || it.children || it.sub);
419
- const href = uiUnwrap(it.href || it.to || it.link || null);
420
- const rawLabel = uiUnwrap(it.label ?? it.title ?? it.text ?? href ?? null);
421
- const label = rawLabel ?? `item-${level}-${idx}`;
422
- const note = uiUnwrap(it.note ?? it.subtitle ?? it.description ?? null);
423
- const badge = uiUnwrap(it.badge ?? null);
424
- const aside = uiUnwrap(it.aside ?? it.meta ?? null);
292
+ const iconNodes = (icon, localCtx = {}) => {
293
+ const raw = uiUnwrap(icon);
294
+ if (raw == null || raw === false) return [];
295
+ const slotValue = typeof raw === "string"
296
+ ? UI.Icon({ name: raw, size: localCtx.iconSize ?? itemIconSize })
297
+ : JSswift.ui.slot(raw, { ...localCtx, as: "icon" });
298
+ return renderSlotToArray(null, "default", localCtx, slotValue);
299
+ };
300
+ const wrapIconNodes = (icon, side, localCtx = {}) => {
301
+ const nodes = iconNodes(icon, localCtx);
302
+ return nodes.length ? [_.span({ class: uiClass(["jss-drawer-item-icon", side]) }, ...nodes)] : [];
303
+ };
304
+
305
+ const getItemIcons = (it) => {
306
+ const side = uiUnwrap(it.iconPosition) || "left";
307
+ const leftIcon = Object.prototype.hasOwnProperty.call(it, "iconLeft") ? it.iconLeft : (side !== "right" ? it.icon : null);
308
+ const rightIcon = Object.prototype.hasOwnProperty.call(it, "iconRight") ? it.iconRight : (side === "right" ? it.icon : null);
309
+ return {
310
+ left: leftIcon,
311
+ right: rightIcon
312
+ };
313
+ };
314
+
315
+ const itemKeyPart = (it, label, level, idx) => uiUnwrap(it.key || it.id || it.to || it.href || label || `item-${level}-${idx}`);
316
+
317
+ const readGroupOpen = (key, fallback) => {
318
+ if (!canStore) return fallback;
319
+ if (Object.prototype.hasOwnProperty.call(groupState, key)) return !!groupState[key];
320
+ return fallback;
321
+ };
322
+
323
+ const writeGroupOpen = (key, open) => {
324
+ if (!canStore) return;
325
+ groupState = { ...groupState, [key]: !!open };
326
+ store.set(groupStateKey, groupState);
327
+ };
328
+
329
+ const buildStructuredHeader = () => {
330
+ const headerCtx = { ...ctx, area: "header" };
331
+ const icon = renderArea(["headerIcon", "icon"], rawProps.icon, headerCtx);
332
+ const eyebrow = renderArea(["eyebrow"], rawProps.eyebrow, headerCtx);
333
+ const title = renderArea(["title"], rawProps.title, headerCtx);
334
+ const subtitle = renderArea(["subtitle"], rawProps.subtitle, headerCtx);
335
+ const meta = renderArea(["meta"], rawProps.meta, headerCtx);
336
+ const content = renderArea(["content"], rawProps.content, headerCtx);
337
+ const actions = renderArea(["actions"], rawProps.actions, headerCtx);
338
+ const extra = hasOwn("header") ? renderSlotToArray(null, "default", headerCtx, uiUnwrap(rawProps.header)) : [];
339
+ const hasStructured = icon.length || eyebrow.length || title.length || subtitle.length || meta.length || content.length || actions.length;
340
+ if (!hasStructured && !extra.length) return [];
341
+ if (!hasStructured) return extra;
342
+ return [
343
+ _.div(
344
+ { class: uiClass(["jss-drawer-header", rawProps.headerClass]) },
345
+ ...(icon.length ? [_.div({ class: "jss-drawer-header-icon" }, ...icon)] : []),
346
+ _.div(
347
+ { class: "jss-drawer-header-body" },
348
+ ...(eyebrow.length ? [_.div({ class: "jss-drawer-header-eyebrow" }, ...eyebrow)] : []),
349
+ ...(title.length ? [_.div({ class: "jss-drawer-header-title" }, ...title)] : []),
350
+ ...(subtitle.length ? [_.div({ class: "jss-drawer-header-subtitle" }, ...subtitle)] : []),
351
+ ...(meta.length ? [_.div({ class: "jss-drawer-header-meta" }, ...meta)] : []),
352
+ ...(content.length ? [_.div({ class: "jss-drawer-header-content" }, ...content)] : []),
353
+ ...(extra.length ? [_.div({ class: "jss-drawer-header-extra" }, ...extra)] : [])
354
+ ),
355
+ ...(actions.length ? [_.div({ class: "jss-drawer-header-actions" }, ...actions)] : [])
356
+ )
357
+ ];
358
+ };
359
+ const invokeItemHandler = (it, itemCtx, e) => {
360
+ const itemResult = it.onClick?.(e, itemCtx);
361
+ if (itemResult === false || e?.defaultPrevented) return false;
362
+ const rootResult = rawProps.onSelect?.(it, itemCtx, e);
363
+ if (rootResult === false || e?.defaultPrevented) return false;
364
+ return true;
365
+ };
366
+ const renderEntryNodes = (it, itemCtx, extras = {}) => {
367
+ const slotPrefix = itemCtx.hasChildren ? "group" : "item";
368
+ const labelNodes = renderSlotToArray(slots, `${slotPrefix}Label`, itemCtx, itemCtx.label);
369
+ const noteNodes = renderSlotToArray(slots, `${slotPrefix}Note`, itemCtx, itemCtx.note);
370
+ const badgeNodes = renderSlotToArray(slots, `${slotPrefix}Badge`, itemCtx, itemCtx.badge);
371
+ const asideNodes = renderSlotToArray(slots, `${slotPrefix}Aside`, itemCtx, itemCtx.aside);
372
+ const contentNodes = renderSlotToArray(slots, `${slotPrefix}Content`, itemCtx, itemCtx.content);
373
+ const mainChildren = [];
374
+
375
+ if (labelNodes.length || noteNodes.length || badgeNodes.length || asideNodes.length) {
376
+ mainChildren.push(
377
+ _.div(
378
+ { class: "jss-drawer-item-top" },
379
+ _.div(
380
+ { class: "jss-drawer-item-copy" },
381
+ ...(labelNodes.length ? [_.div({ class: "jss-drawer-item-label" }, ...labelNodes)] : []),
382
+ ...(noteNodes.length ? [_.div({ class: "jss-drawer-item-note" }, ...noteNodes)] : [])
383
+ ),
384
+ ...(badgeNodes.length || asideNodes.length
385
+ ? [_.div(
386
+ { class: "jss-drawer-item-meta" },
387
+ ...(badgeNodes.length ? [_.div({ class: "jss-drawer-item-badge" }, ...badgeNodes)] : []),
388
+ ...(asideNodes.length ? [_.div({ class: "jss-drawer-item-aside" }, ...asideNodes)] : [])
389
+ )]
390
+ : [])
391
+ )
392
+ );
393
+ }
394
+ if (contentNodes.length) mainChildren.push(_.div({ class: "jss-drawer-item-content" }, ...contentNodes));
395
+
396
+ return [
397
+ ...(extras.start || []),
398
+ ...wrapIconNodes(itemCtx.iconLeft, "left", itemCtx),
399
+ ...(mainChildren.length ? [_.div({ class: "jss-drawer-item-main" }, ...mainChildren)] : []),
400
+ ...wrapIconNodes(itemCtx.iconRight, "right", itemCtx),
401
+ ...(extras.end || [])
402
+ ];
403
+ };
404
+ const renderItems = (list = [], level = 0, path = []) => {
405
+ return normalizeList(list).map((entry, idx) => {
406
+ const it = uiUnwrap(entry);
407
+ if (it == null || it === false) return null;
408
+
409
+ if (it.nodeType || typeof it !== "object" || Array.isArray(it)) {
410
+ return _.div({
411
+ class: "jss-drawer-custom",
412
+ "data-level": String(level)
413
+ }, ...renderSlotToArray(null, "default", { ...ctx, item: it, level, index: idx, path }, it));
414
+ }
415
+
416
+ if (uiUnwrap(it.hidden) === true || uiUnwrap(it.visible) === false) return null;
417
+
418
+ const childItems = normalizeList(it.items || it.children || it.sub);
419
+ const href = uiUnwrap(it.href || it.to || it.link || null);
420
+ const rawLabel = uiUnwrap(it.label ?? it.title ?? it.text ?? href ?? null);
421
+ const label = rawLabel ?? `item-${level}-${idx}`;
422
+ const note = uiUnwrap(it.note ?? it.subtitle ?? it.description ?? null);
423
+ const badge = uiUnwrap(it.badge ?? null);
424
+ const aside = uiUnwrap(it.aside ?? it.meta ?? null);
425
425
  const content = uiUnwrap(it.content ?? null);
426
426
  const keyPart = itemKeyPart(it, label, level, idx);
427
427
  const itemStateKey = path.concat(keyPart).join("::");
@@ -433,89 +433,89 @@ UI.Drawer = (...args) => {
433
433
  const itemCtx = {
434
434
  ...ctx,
435
435
  item: it,
436
- index: idx,
437
- level,
438
- path,
439
- label,
440
- note,
441
- badge,
442
- aside,
443
- content,
444
- itemStateKey,
445
- iconLeft: itemIconLeft,
446
- iconRight: itemIconRight,
447
- iconSize: uiUnwrap(it.iconSize) || itemIconSize,
448
- active: isActive,
449
- disabled,
450
- hasChildren: false
451
- };
452
- const dividerLike = uiUnwrap(it.divider) === true || it.type === "divider" || it.type === "separator";
453
- const sectionLike = uiUnwrap(it.section) === true || it.type === "section" || it.type === "heading" || it.type === "label";
454
- const contentOnly = it.type === "content" || it.type === "custom" || (!href && !childItems.length && !uiUnwrap(it.button) && it.type !== "button" && rawLabel == null && content != null);
455
-
456
- if (dividerLike) {
457
- return _.div({
458
- class: uiClass(["cms-drawer-separator", it.class]),
459
- "data-level": String(level)
460
- });
461
- }
462
-
463
- if (sectionLike) {
464
- const sectionNodes = renderSlotToArray(slots, "sectionLabel", itemCtx, label || content);
465
- return _.div({
466
- class: uiClass(["cms-drawer-section-label", it.class]),
467
- "data-level": String(level)
468
- }, ...sectionNodes);
469
- }
470
-
471
- if (contentOnly) {
472
- const customNodes = renderSlotToArray(slots, "item", itemCtx, content ?? label);
473
- return _.div({
474
- class: uiClass(["cms-drawer-custom", tone ? `cms-drawer-tone-${tone}` : "", it.class]),
475
- style: it.style,
476
- "data-level": String(level)
477
- }, ...customNodes);
436
+ index: idx,
437
+ level,
438
+ path,
439
+ label,
440
+ note,
441
+ badge,
442
+ aside,
443
+ content,
444
+ itemStateKey,
445
+ iconLeft: itemIconLeft,
446
+ iconRight: itemIconRight,
447
+ iconSize: uiUnwrap(it.iconSize) || itemIconSize,
448
+ active: isActive,
449
+ disabled,
450
+ hasChildren: false
451
+ };
452
+ const dividerLike = uiUnwrap(it.divider) === true || it.type === "divider" || it.type === "separator";
453
+ const sectionLike = uiUnwrap(it.section) === true || it.type === "section" || it.type === "heading" || it.type === "label";
454
+ const contentOnly = it.type === "content" || it.type === "custom" || (!href && !childItems.length && !uiUnwrap(it.button) && it.type !== "button" && rawLabel == null && content != null);
455
+
456
+ if (dividerLike) {
457
+ return _.div({
458
+ class: uiClass(["jss-drawer-separator", it.class]),
459
+ "data-level": String(level)
460
+ });
461
+ }
462
+
463
+ if (sectionLike) {
464
+ const sectionNodes = renderSlotToArray(slots, "sectionLabel", itemCtx, label || content);
465
+ return _.div({
466
+ class: uiClass(["jss-drawer-section-label", it.class]),
467
+ "data-level": String(level)
468
+ }, ...sectionNodes);
469
+ }
470
+
471
+ if (contentOnly) {
472
+ const customNodes = renderSlotToArray(slots, "item", itemCtx, content ?? label);
473
+ return _.div({
474
+ class: uiClass(["jss-drawer-custom", tone ? `jss-drawer-tone-${tone}` : "", it.class]),
475
+ style: it.style,
476
+ "data-level": String(level)
477
+ }, ...customNodes);
478
478
  }
479
479
 
480
480
  if (childItems.length) {
481
481
  const baseActiveInGroup = baseIsActive || hasActiveChildren(childItems, level + 1, path.concat(keyPart));
482
482
  const activeInGroup = baseActiveInGroup || isBranchActive(itemStateKey);
483
483
  let open = readGroupOpen(itemStateKey, !!uiUnwrap(it.expanded) || !!uiUnwrap(it.open) || activeInGroup);
484
- const toggleIconEl = _.span({ class: "cms-drawer-group-icon" });
484
+ const toggleIconEl = _.span({ class: "jss-drawer-group-icon" });
485
485
  const groupCtx = {
486
486
  ...itemCtx,
487
- active: activeInGroup,
488
- hasChildren: true,
489
- isOpen: () => open,
490
- setOpen: (value) => setOpen(value),
491
- toggle: () => setOpen(!open)
492
- };
493
- const paintToggleIcon = () => {
494
- const icon = open ? (it.openIcon ?? groupOpenIcon) : (it.closeIcon ?? groupCloseIcon);
495
- const side = uiUnwrap(
496
- open
497
- ? (it.iconSidePosition || it.openIconSide || it.openIconPosition || "right")
498
- : (it.iconSidePosition || it.closeIconSide || it.closeIconPosition || "right")
499
- ) === "left" ? "left" : "right";
500
- toggleIconEl.innerHTML = "";
501
- toggleIconEl.className = `cms-drawer-group-icon ${side}`;
502
- iconNodes(icon, { ...groupCtx, as: "toggleIcon" }).forEach((node) => toggleIconEl.appendChild(node));
503
- };
504
- const toggleBtn = _.button({
505
- type: "button",
506
- class: uiClass([
507
- "cms-drawer-group-toggle",
508
- "cms-drawer-entry",
509
- open ? "open" : "",
510
- activeInGroup ? "active" : "",
511
- disabled ? "is-disabled" : "",
512
- tone ? `cms-drawer-tone-${tone}` : "",
513
- it.class
514
- ]),
515
- style: it.style,
516
- "data-level": String(level),
517
- "aria-expanded": String(open),
518
- "aria-disabled": disabled ? "true" : null,
487
+ active: activeInGroup,
488
+ hasChildren: true,
489
+ isOpen: () => open,
490
+ setOpen: (value) => setOpen(value),
491
+ toggle: () => setOpen(!open)
492
+ };
493
+ const paintToggleIcon = () => {
494
+ const icon = open ? (it.openIcon ?? groupOpenIcon) : (it.closeIcon ?? groupCloseIcon);
495
+ const side = uiUnwrap(
496
+ open
497
+ ? (it.iconSidePosition || it.openIconSide || it.openIconPosition || "right")
498
+ : (it.iconSidePosition || it.closeIconSide || it.closeIconPosition || "right")
499
+ ) === "left" ? "left" : "right";
500
+ toggleIconEl.innerHTML = "";
501
+ toggleIconEl.className = `jss-drawer-group-icon ${side}`;
502
+ iconNodes(icon, { ...groupCtx, as: "toggleIcon" }).forEach((node) => toggleIconEl.appendChild(node));
503
+ };
504
+ const toggleBtn = _.button({
505
+ type: "button",
506
+ class: uiClass([
507
+ "jss-drawer-group-toggle",
508
+ "jss-drawer-entry",
509
+ open ? "open" : "",
510
+ activeInGroup ? "active" : "",
511
+ disabled ? "is-disabled" : "",
512
+ tone ? `jss-drawer-tone-${tone}` : "",
513
+ it.class
514
+ ]),
515
+ style: it.style,
516
+ "data-level": String(level),
517
+ "aria-expanded": String(open),
518
+ "aria-disabled": disabled ? "true" : null,
519
519
  onClick: (e) => {
520
520
  if (disabled) {
521
521
  e.preventDefault();
@@ -527,24 +527,24 @@ UI.Drawer = (...args) => {
527
527
  }, ...renderEntryNodes(it, groupCtx, {
528
528
  start: uiUnwrap(it.iconSidePosition || it.openIconSide || it.closeIconSide || it.openIconPosition || it.closeIconPosition || "right") === "left" ? [toggleIconEl] : [],
529
529
  end: uiUnwrap(it.iconSidePosition || it.openIconSide || it.closeIconSide || it.openIconPosition || it.closeIconPosition || "right") === "right" ? [toggleIconEl] : []
530
- }));
531
- const customGroupHeader = renderSlotToArray(slots, "group", groupCtx, null);
532
- const groupItems = _.div({ class: "cms-drawer-group-items" }, ...renderItems(childItems, level + 1, path.concat(keyPart)));
533
- const groupWrap = _.div({
534
- class: uiClass([
535
- "cms-drawer-group",
536
- open ? "open" : "",
537
- activeInGroup ? "active" : "",
538
- tone ? `cms-drawer-tone-${tone}` : ""
539
- ]),
540
- "data-level": String(level)
541
- }, ...(customGroupHeader.length ? customGroupHeader : [toggleBtn]), groupItems);
530
+ }));
531
+ const customGroupHeader = renderSlotToArray(slots, "group", groupCtx, null);
532
+ const groupItems = _.div({ class: "jss-drawer-group-items" }, ...renderItems(childItems, level + 1, path.concat(keyPart)));
533
+ const groupWrap = _.div({
534
+ class: uiClass([
535
+ "jss-drawer-group",
536
+ open ? "open" : "",
537
+ activeInGroup ? "active" : "",
538
+ tone ? `jss-drawer-tone-${tone}` : ""
539
+ ]),
540
+ "data-level": String(level)
541
+ }, ...(customGroupHeader.length ? customGroupHeader : [toggleBtn]), groupItems);
542
542
  const setOpen = (value) => {
543
543
  open = !!value;
544
544
  groupWrap.classList.toggle("open", open);
545
545
  toggleBtn.classList.toggle("open", open);
546
546
  toggleBtn.setAttribute("aria-expanded", String(open));
547
- paintToggleIcon();
547
+ paintToggleIcon();
548
548
  writeGroupOpen(itemStateKey, open);
549
549
  };
550
550
  activeGroupRefs.set(itemStateKey, {
@@ -555,25 +555,25 @@ UI.Drawer = (...args) => {
555
555
  paintToggleIcon();
556
556
  return groupWrap;
557
557
  }
558
-
559
- const customItem = renderSlotToArray(slots, "item", itemCtx, null);
560
- const isButtonItem = uiUnwrap(it.button) === true || it.type === "button" || (!href && typeof it.onClick === "function");
561
- const sharedProps = {
562
- class: uiClass([
563
- "cms-drawer-entry",
564
- isButtonItem ? "cms-drawer-btn" : "cms-drawer-link",
565
- isActive ? "active" : "",
566
- disabled ? "is-disabled" : "",
567
- tone ? `cms-drawer-tone-${tone}` : "",
568
- it.class
569
- ]),
570
- style: it.style,
571
- "data-level": String(level),
572
- "aria-current": isActive ? "page" : null,
573
- "aria-disabled": disabled ? "true" : null
574
- };
575
- const entryNodes = customItem.length ? customItem : renderEntryNodes(it, itemCtx);
576
-
558
+
559
+ const customItem = renderSlotToArray(slots, "item", itemCtx, null);
560
+ const isButtonItem = uiUnwrap(it.button) === true || it.type === "button" || (!href && typeof it.onClick === "function");
561
+ const sharedProps = {
562
+ class: uiClass([
563
+ "jss-drawer-entry",
564
+ isButtonItem ? "jss-drawer-btn" : "jss-drawer-link",
565
+ isActive ? "active" : "",
566
+ disabled ? "is-disabled" : "",
567
+ tone ? `jss-drawer-tone-${tone}` : "",
568
+ it.class
569
+ ]),
570
+ style: it.style,
571
+ "data-level": String(level),
572
+ "aria-current": isActive ? "page" : null,
573
+ "aria-disabled": disabled ? "true" : null
574
+ };
575
+ const entryNodes = customItem.length ? customItem : renderEntryNodes(it, itemCtx);
576
+
577
577
  if (isButtonItem) {
578
578
  const entryEl = _.button({
579
579
  ...sharedProps,
@@ -592,27 +592,27 @@ UI.Drawer = (...args) => {
592
592
  activeLeafRefs.set(itemStateKey, { el: entryEl, baseActive: baseIsActive });
593
593
  return entryEl;
594
594
  }
595
-
596
- const external = isExternalLink(it);
597
- const target = external ? (uiUnwrap(it.target) || "_blank") : uiUnwrap(it.target);
598
- const rel = external ? (uiUnwrap(it.rel) || "noopener noreferrer") : uiUnwrap(it.rel);
599
-
595
+
596
+ const external = isExternalLink(it);
597
+ const target = external ? (uiUnwrap(it.target) || "_blank") : uiUnwrap(it.target);
598
+ const rel = external ? (uiUnwrap(it.rel) || "noopener noreferrer") : uiUnwrap(it.rel);
599
+
600
600
  const entryEl = _.a({
601
601
  ...sharedProps,
602
602
  href: href || "#",
603
603
  target: target || null,
604
604
  rel: rel || null,
605
- tabIndex: disabled ? -1 : null,
606
- onClick: (e) => {
607
- if (disabled) {
608
- e.preventDefault();
609
- return;
610
- }
611
- if (!invokeItemHandler(it, itemCtx, e)) return;
612
- if (!external && it.to && app.router?.navigate) {
613
- e.preventDefault();
614
- app.router.navigate(it.to);
615
- }
605
+ tabIndex: disabled ? -1 : null,
606
+ onClick: (e) => {
607
+ if (disabled) {
608
+ e.preventDefault();
609
+ return;
610
+ }
611
+ if (!invokeItemHandler(it, itemCtx, e)) return;
612
+ if (!external && it.to && app.router?.navigate) {
613
+ e.preventDefault();
614
+ app.router.navigate(it.to);
615
+ }
616
616
  if (shouldClose(it)) closeDrawer();
617
617
  }
618
618
  }, ...entryNodes);
@@ -620,54 +620,54 @@ UI.Drawer = (...args) => {
620
620
  return entryEl;
621
621
  }).filter(Boolean);
622
622
  };
623
-
624
- const beforeNodes = renderArea(["before", "beforeItems"], rawProps.before ?? rawProps.beforeItems);
625
- const bodyCustom = hasArea(["body"]) ? renderArea(["body"], null) : [];
626
- const itemNodes = renderItems(rawProps.items || []);
627
- const defaultNodes = renderArea(["default"], children);
628
- const afterNodes = renderArea(["after", "afterItems"], rawProps.after ?? rawProps.afterItems);
629
- const bodyNodes = bodyCustom.length ? bodyCustom : [...beforeNodes, ...itemNodes, ...defaultNodes, ...afterNodes];
630
- const emptyFallback = hasOwn("empty") || hasOwn("emptyText")
631
- ? (rawProps.empty ?? _.div({ class: uiClass(["cms-drawer-empty", rawProps.emptyClass]) }, rawProps.emptyText || "Nessun contenuto"))
632
- : _.div({ class: uiClass(["cms-drawer-empty", rawProps.emptyClass]) }, "Nessun contenuto");
633
- const headerNodes = renderArea(["header"], buildStructuredHeader());
634
- const footerNodes = renderArea(["footer"], rawProps.footer);
635
- const finalBodyNodes = bodyNodes.length ? bodyNodes : renderArea(["empty"], emptyFallback);
636
-
637
- const p = JSswift.omit(props, [
638
- "items", "header", "footer", "before", "beforeItems", "after", "afterItems",
639
- "title", "subtitle", "eyebrow", "icon", "content", "meta", "actions",
640
- "empty", "emptyText", "closeOnSelect", "groupOpenIcon", "groupCloseIcon",
641
- "stateKey", "sticky", "slots", "headerClass", "bodyClass", "footerClass",
642
- "emptyClass", "itemIconSize", "gap", "indent", "padding", "minHeight",
643
- "maxHeight", "width", "onSelect"
644
- ]);
645
- p.class = uiClass([
646
- "cms-panel",
647
- "cms-drawer",
648
- drawerOpen ? "open" : "",
649
- uiWhen(rawProps.sticky, "sticky"),
650
- props.class
651
- ]);
652
- p.style = { ...(props.style || {}) };
653
- if (rawProps.gap != null) p.style["--cms-drawer-gap"] = toCssSize(uiUnwrap(rawProps.gap));
654
- if (rawProps.indent != null) p.style["--cms-drawer-indent"] = toCssSize(uiUnwrap(rawProps.indent));
655
- if (rawProps.padding != null) p.style["--cms-drawer-padding"] = toCssSize(uiUnwrap(rawProps.padding));
656
- if (rawProps.width != null) p.style.width = toCssSize(uiUnwrap(rawProps.width));
657
- if (rawProps.minHeight != null) p.style.minHeight = toCssSize(uiUnwrap(rawProps.minHeight));
658
- if (rawProps.maxHeight != null) p.style.maxHeight = toCssSize(uiUnwrap(rawProps.maxHeight));
659
-
660
- const drawerEl = _.div(
661
- p,
662
- ...(headerNodes.length ? headerNodes : []),
663
- _.div({ class: uiClass(["cms-drawer-body", rawProps.bodyClass]) }, ...finalBodyNodes),
664
- ...(footerNodes.length ? [_.div({ class: uiClass(["cms-drawer-footer", rawProps.footerClass]) }, ...footerNodes)] : [])
665
- );
666
- const drawerSet = drawerElsByKey.get(currentStateKey) || new Set();
667
- drawerSet.add(drawerEl);
668
- drawerElsByKey.set(currentStateKey, drawerSet);
669
- setDrawerOpen(drawerOpen, currentStateKey);
670
- drawerEl.isDrawerOpen = isDrawerOpen;
623
+
624
+ const beforeNodes = renderArea(["before", "beforeItems"], rawProps.before ?? rawProps.beforeItems);
625
+ const bodyCustom = hasArea(["body"]) ? renderArea(["body"], null) : [];
626
+ const itemNodes = renderItems(rawProps.items || []);
627
+ const defaultNodes = renderArea(["default"], children);
628
+ const afterNodes = renderArea(["after", "afterItems"], rawProps.after ?? rawProps.afterItems);
629
+ const bodyNodes = bodyCustom.length ? bodyCustom : [...beforeNodes, ...itemNodes, ...defaultNodes, ...afterNodes];
630
+ const emptyFallback = hasOwn("empty") || hasOwn("emptyText")
631
+ ? (rawProps.empty ?? _.div({ class: uiClass(["jss-drawer-empty", rawProps.emptyClass]) }, rawProps.emptyText || "Nessun contenuto"))
632
+ : _.div({ class: uiClass(["jss-drawer-empty", rawProps.emptyClass]) }, "Nessun contenuto");
633
+ const headerNodes = renderArea(["header"], buildStructuredHeader());
634
+ const footerNodes = renderArea(["footer"], rawProps.footer);
635
+ const finalBodyNodes = bodyNodes.length ? bodyNodes : renderArea(["empty"], emptyFallback);
636
+
637
+ const p = JSswift.omit(props, [
638
+ "items", "header", "footer", "before", "beforeItems", "after", "afterItems",
639
+ "title", "subtitle", "eyebrow", "icon", "content", "meta", "actions",
640
+ "empty", "emptyText", "closeOnSelect", "groupOpenIcon", "groupCloseIcon",
641
+ "stateKey", "sticky", "slots", "headerClass", "bodyClass", "footerClass",
642
+ "emptyClass", "itemIconSize", "gap", "indent", "padding", "minHeight",
643
+ "maxHeight", "width", "onSelect"
644
+ ]);
645
+ p.class = uiClass([
646
+ "jss-panel",
647
+ "jss-drawer",
648
+ drawerOpen ? "open" : "",
649
+ uiWhen(rawProps.sticky, "sticky"),
650
+ props.class
651
+ ]);
652
+ p.style = { ...(props.style || {}) };
653
+ if (rawProps.gap != null) p.style["--jss-drawer-gap"] = toCssSize(uiUnwrap(rawProps.gap));
654
+ if (rawProps.indent != null) p.style["--jss-drawer-indent"] = toCssSize(uiUnwrap(rawProps.indent));
655
+ if (rawProps.padding != null) p.style["--jss-drawer-padding"] = toCssSize(uiUnwrap(rawProps.padding));
656
+ if (rawProps.width != null) p.style.width = toCssSize(uiUnwrap(rawProps.width));
657
+ if (rawProps.minHeight != null) p.style.minHeight = toCssSize(uiUnwrap(rawProps.minHeight));
658
+ if (rawProps.maxHeight != null) p.style.maxHeight = toCssSize(uiUnwrap(rawProps.maxHeight));
659
+
660
+ const drawerEl = _.div(
661
+ p,
662
+ ...(headerNodes.length ? headerNodes : []),
663
+ _.div({ class: uiClass(["jss-drawer-body", rawProps.bodyClass]) }, ...finalBodyNodes),
664
+ ...(footerNodes.length ? [_.div({ class: uiClass(["jss-drawer-footer", rawProps.footerClass]) }, ...footerNodes)] : [])
665
+ );
666
+ const drawerSet = drawerElsByKey.get(currentStateKey) || new Set();
667
+ drawerSet.add(drawerEl);
668
+ drawerElsByKey.set(currentStateKey, drawerSet);
669
+ setDrawerOpen(drawerOpen, currentStateKey);
670
+ drawerEl.isDrawerOpen = isDrawerOpen;
671
671
  drawerEl.openDrawer = openDrawer;
672
672
  drawerEl.closeDrawer = closeDrawer;
673
673
  drawerEl.toggleDrawer = toggleDrawer;
@@ -676,548 +676,548 @@ UI.Drawer = (...args) => {
676
676
  syncActiveClasses();
677
677
  return drawerEl;
678
678
  };
679
-
680
-
681
- UI.Page = (...args) => {
682
- const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
683
- const slots = rawProps.slots || {};
684
- const props = { ...rawProps };
685
-
686
- const isSectionNode = (node, name) => {
687
- return !!(node && node.nodeType === 1 && node.classList?.contains(`cms-page-${name}`));
688
- };
689
- const renderIconFallback = (value) => {
690
- if (value == null) return null;
691
- if (typeof value === "string") return UI.Icon({ name: value, size: rawProps.iconSize || rawProps.size || "xl" });
692
- return JSswift.ui.slot(value, { as: "icon" });
693
- };
694
-
695
- const ctx = {
696
- dense: !!uiUnwrap(rawProps.dense),
697
- flat: !!uiUnwrap(rawProps.flat),
698
- centered: !!uiUnwrap(rawProps.centered),
699
- narrow: !!uiUnwrap(rawProps.narrow)
700
- };
701
- const appendResolvedValue = (host, value) => {
702
- if (value == null || value === false) return;
703
- if (Array.isArray(value)) {
704
- value.forEach((item) => appendResolvedValue(host, item));
705
- return;
706
- }
707
- if (value?.nodeType) {
708
- host.appendChild(value);
709
- return;
710
- }
711
- host.appendChild(document.createTextNode(String(value)));
712
- };
713
- const renderPropNodes = (name, fallback, map = (value) => value) => {
714
- const slot = JSswift.ui.getSlot(slots, name);
715
- if (slot !== null && slot !== undefined) {
716
- return renderSlotToArray(slots, name, ctx, null);
717
- }
718
- if (typeof fallback === "function") {
719
- const inlineNames = new Set(["eyebrow", "title", "subtitle"]);
720
- const host = _[inlineNames.has(name) ? "span" : "div"]({ class: `cms-page-slot-${name}` });
721
- JSswift.reactive.effect(() => {
722
- const nextValue = map(fallback(ctx));
723
- const normalized = flattenSlotValue(JSswift.ui.slot(nextValue));
724
- host.replaceChildren();
725
- if (Array.isArray(normalized)) normalized.forEach((item) => appendResolvedValue(host, item));
726
- else appendResolvedValue(host, normalized);
727
- }, `UI.Page:${name}`);
728
- return [host];
729
- }
730
- return renderSlotToArray(slots, name, ctx, map(fallback));
731
- };
732
-
733
- const defaultNodes = renderSlotToArray(slots, "default", ctx, children?.length ? children : []);
734
- const sectionNodes = {
735
- hero: [],
736
- header: [],
737
- body: [],
738
- footer: [],
739
- actions: []
740
- };
741
- const looseNodes = [];
742
-
743
- defaultNodes.forEach((node) => {
744
- if (isSectionNode(node, "hero")) sectionNodes.hero.push(node);
745
- else if (isSectionNode(node, "header")) sectionNodes.header.push(node);
746
- else if (isSectionNode(node, "body")) sectionNodes.body.push(node);
747
- else if (isSectionNode(node, "footer")) sectionNodes.footer.push(node);
748
- else if (isSectionNode(node, "actions")) sectionNodes.actions.push(node);
749
- else looseNodes.push(node);
750
- });
751
-
752
- const iconNodes = renderPropNodes("icon", rawProps.icon, renderIconFallback);
753
- const heroNodes = renderPropNodes("hero", rawProps.hero ?? rawProps.banner);
754
- const eyebrowNodes = renderPropNodes("eyebrow", rawProps.eyebrow ?? rawProps.kicker);
755
- const titleNodes = renderPropNodes("title", rawProps.title);
756
- const subtitleNodes = renderPropNodes("subtitle", rawProps.subtitle ?? rawProps.description);
757
- const headerNodes = renderPropNodes("header", rawProps.header);
758
- const asideNodes = renderPropNodes("aside", rawProps.aside);
759
- const bodyNodes = renderPropNodes("body", rawProps.body ?? rawProps.content);
760
- const footerNodes = renderPropNodes("footer", rawProps.footer);
761
- const actionsNodes = renderPropNodes("actions", rawProps.actions);
762
-
763
- const generatedHero = heroNodes.length
764
- ? _.div({ class: uiClass(["cms-page-hero", rawProps.heroClass]) }, ...heroNodes)
765
- : null;
766
-
767
- const hasStructuredHeader = iconNodes.length || eyebrowNodes.length || titleNodes.length || subtitleNodes.length || headerNodes.length || asideNodes.length;
768
- const generatedHeader = hasStructuredHeader
769
- ? _.div(
770
- { class: uiClass(["cms-page-header", rawProps.headerClass]) },
771
- _.div(
772
- { class: "cms-page-head" },
773
- iconNodes.length ? _.div({ class: uiClass(["cms-page-icon", rawProps.iconClass]) }, ...iconNodes) : null,
774
- _.div(
775
- { class: "cms-page-head-main" },
776
- eyebrowNodes.length ? _.div({ class: uiClass(["cms-page-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
777
- titleNodes.length ? _.div({ class: uiClass(["cms-page-title", rawProps.titleClass]) }, ...titleNodes) : null,
778
- subtitleNodes.length ? _.div({ class: uiClass(["cms-page-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
779
- headerNodes.length ? _.div({ class: uiClass(["cms-page-header-content", rawProps.headerContentClass]) }, ...headerNodes) : null
780
- ),
781
- asideNodes.length ? _.div({ class: uiClass(["cms-page-aside", rawProps.asideClass]) }, ...asideNodes) : null
782
- )
783
- )
784
- : null;
785
-
786
- const mergedBodyNodes = [...bodyNodes, ...looseNodes];
787
- const generatedBody = mergedBodyNodes.length
788
- ? _.div({ class: uiClass(["cms-page-body", rawProps.bodyClass]) }, ...mergedBodyNodes)
789
- : null;
790
-
791
- const mergedActionNodes = [...actionsNodes, ...sectionNodes.actions];
792
- const generatedFooter = (footerNodes.length || mergedActionNodes.length)
793
- ? _.div(
794
- { class: uiClass(["cms-page-footer", rawProps.footerClass]) },
795
- ...footerNodes,
796
- mergedActionNodes.length ? _.div({ class: "cms-page-actions" }, ...mergedActionNodes) : null
797
- )
798
- : null;
799
-
800
- const hasHero = !!(heroNodes.length || sectionNodes.hero.length);
801
- const hasHeader = !!(hasStructuredHeader || sectionNodes.header.length);
802
- const p = JSswift.omit(props, [
803
- "actions", "aside", "asideClass", "banner", "body", "bodyClass", "centered", "content",
804
- "dense", "description", "eyebrow", "eyebrowClass", "flat", "footer", "footerClass",
805
- "gap", "header", "headerClass", "headerContentClass", "headerGap", "hero", "heroClass",
806
- "heroPadding", "icon", "iconClass", "iconSize", "kicker", "maxWidth", "minHeight",
807
- "narrow", "padding", "size", "slots", "subtitle", "subtitleClass", "title", "titleClass"
808
- ]);
809
- p.class = uiClass([
810
- "cms-panel",
811
- "cms-page",
812
- uiWhen(rawProps.dense, "dense"),
813
- uiWhen(rawProps.flat, "cms-page-flat"),
814
- uiWhen(rawProps.centered, "cms-page-centered"),
815
- uiWhen(rawProps.narrow, "cms-page-narrow"),
816
- uiWhen(hasHero, "cms-page-has-hero"),
817
- uiWhen(hasHeader, "cms-page-has-header"),
818
- props.class
819
- ]);
820
- p.style = { ...(props.style || {}) };
821
-
822
- if (rawProps.gap != null || uiIsReactive(rawProps.gap)) {
823
- p.style["--cms-page-gap"] = uiStyleValue(rawProps.gap, toCssSize);
824
- }
825
- if (rawProps.padding != null || uiIsReactive(rawProps.padding)) {
826
- p.style["--cms-page-padding"] = uiStyleValue(rawProps.padding, toCssSize);
827
- }
828
- if (rawProps.maxWidth != null || uiIsReactive(rawProps.maxWidth)) {
829
- p.style["--cms-page-max-width"] = uiStyleValue(rawProps.maxWidth, toCssSize);
830
- }
831
- if (rawProps.minHeight != null || uiIsReactive(rawProps.minHeight)) {
832
- p.style["--cms-page-min-height"] = uiStyleValue(rawProps.minHeight, toCssSize);
833
- }
834
- if (rawProps.headerGap != null || uiIsReactive(rawProps.headerGap)) {
835
- p.style["--cms-page-header-gap"] = uiStyleValue(rawProps.headerGap, toCssSize);
836
- }
837
- if (rawProps.heroPadding != null || uiIsReactive(rawProps.heroPadding)) {
838
- p.style["--cms-page-hero-padding"] = uiStyleValue(rawProps.heroPadding, toCssSize);
839
- }
840
-
841
- const el = _.div(
842
- p,
843
- generatedHero,
844
- ...sectionNodes.hero,
845
- generatedHeader,
846
- ...sectionNodes.header,
847
- generatedBody,
848
- ...sectionNodes.body,
849
- generatedFooter,
850
- ...sectionNodes.footer
851
- );
852
-
853
- setPropertyProps(el, rawProps);
854
- return el;
855
- };
856
-
857
-
858
- UI.AppShell = (...args) => {
859
- const { props, children } = JSswift.uiNormalizeArgs(args);
860
- const slots = props.slots || {};
861
- const hasOwn = (obj, key) => !!obj && Object.prototype.hasOwnProperty.call(obj, key);
862
-
863
- const noDrawer = uiUnwrap(props.noDrawer) === true || props.drawer === false;
864
- const reverse = uiUnwrap(props.reverse) === true;
865
- const stack = uiUnwrap(props.stack) === true;
866
- const currentStateKey = props.drawerStateKey ?? props.stateKey ?? drawerStateKey;
867
-
868
- if (currentStateKey) {
869
- drawerStateKey = currentStateKey;
870
- drawerOpen = readDrawerOpen(currentStateKey);
871
- }
872
-
873
- const shellCtx = {
874
- props,
875
- noDrawer,
876
- stateKey: currentStateKey,
877
- isDrawerOpen: () => readDrawerOpen(currentStateKey),
878
- openDrawer: () => setDrawerOpen(true, currentStateKey),
879
- closeDrawer: () => setDrawerOpen(false, currentStateKey),
880
- toggleDrawer: () => setDrawerOpen(!readDrawerOpen(currentStateKey), currentStateKey)
881
- };
882
-
883
- const headerProps = (props.headerProps && typeof props.headerProps === "object") ? { ...props.headerProps } : {};
884
- let headerFallback = props.header;
885
- if (!hasOwn(props, "header") && (
886
- Object.keys(headerProps).length ||
887
- hasOwn(props, "title") ||
888
- hasOwn(props, "subtitle") ||
889
- hasOwn(props, "left") ||
890
- hasOwn(props, "right")
891
- )) {
892
- if (!hasOwn(headerProps, "title") && hasOwn(props, "title")) headerProps.title = props.title;
893
- if (!hasOwn(headerProps, "subtitle") && hasOwn(props, "subtitle")) headerProps.subtitle = props.subtitle;
894
- if (!hasOwn(headerProps, "left") && hasOwn(props, "left")) headerProps.left = props.left;
895
- if (!hasOwn(headerProps, "right") && hasOwn(props, "right")) headerProps.right = props.right;
896
- if (!hasOwn(headerProps, "drawerStateKey")) headerProps.drawerStateKey = currentStateKey;
897
- if (noDrawer && !hasOwn(headerProps, "left")) headerProps.left = false;
898
- headerFallback = UI.Header(headerProps);
899
- }
900
-
901
- const drawerProps = (props.drawerProps && typeof props.drawerProps === "object") ? { ...props.drawerProps } : {};
902
- const drawerItems = hasOwn(props, "drawerItems") ? props.drawerItems : props.items;
903
- const drawerHeader = hasOwn(props, "drawerHeader") ? props.drawerHeader : undefined;
904
- let drawerFallback = props.drawer;
905
- if (!hasOwn(props, "drawer") && !noDrawer && (
906
- Object.keys(drawerProps).length ||
907
- drawerItems != null ||
908
- drawerHeader != null
909
- )) {
910
- if (!hasOwn(drawerProps, "items") && drawerItems != null) drawerProps.items = drawerItems;
911
- if (!hasOwn(drawerProps, "header") && drawerHeader != null) drawerProps.header = drawerHeader;
912
- if (!hasOwn(drawerProps, "stateKey")) drawerProps.stateKey = currentStateKey;
913
- drawerFallback = UI.Drawer(drawerProps);
914
- }
915
-
916
- const pageProps = (props.pageProps && typeof props.pageProps === "object") ? { ...props.pageProps } : {};
917
- const pageContentFallback = hasOwn(props, "content") ? props.content : children;
918
- const defaultPageContent = renderSlotToArray(slots, "default", shellCtx, pageContentFallback);
919
- let pageFallback = props.page;
920
- if (!hasOwn(props, "page") && (Object.keys(pageProps).length || defaultPageContent.length)) {
921
- pageFallback = UI.Page(pageProps, ...defaultPageContent);
922
- }
923
-
924
- const footerProps = (props.footerProps && typeof props.footerProps === "object") ? { ...props.footerProps } : {};
925
- const footerContent = hasOwn(props, "footerContent") ? props.footerContent : undefined;
926
- let footerFallback = props.footer;
927
- if (!hasOwn(props, "footer") && (Object.keys(footerProps).length || footerContent != null)) {
928
- footerFallback = UI.Footer(footerProps, ...renderSlotToArray(null, "default", shellCtx, footerContent));
929
- }
930
-
931
- const headerNodes = renderSlotToArray(slots, "header", shellCtx, headerFallback);
932
- const drawerNodes = noDrawer ? [] : renderSlotToArray(slots, "drawer", shellCtx, drawerFallback);
933
- const pageNodes = renderSlotToArray(slots, "page", shellCtx, pageFallback);
934
- const footerNodes = renderSlotToArray(slots, "footer", shellCtx, footerFallback);
935
-
936
- const cls = uiClass([
937
- "cms-app",
938
- "cms-app-shell",
939
- uiWhen(noDrawer, "no-drawer"),
940
- uiWhen(reverse, "is-reverse"),
941
- uiWhen(stack, "is-stack"),
942
- uiWhen(props.flush, "is-flush"),
943
- uiWhen(props.divider, "is-divider"),
944
- props.class
945
- ]);
946
-
947
- const p = JSswift.omit(props, [
948
- "header", "drawer", "page", "footer", "content",
949
- "title", "subtitle", "left", "right",
950
- "items", "drawerItems", "drawerHeader",
951
- "headerProps", "drawerProps", "pageProps", "footerProps",
952
- "footerContent",
953
- "noDrawer", "reverse", "stack", "flush", "divider",
954
- "drawerStateKey", "stateKey",
955
- "drawerWidth", "gap", "padding",
956
- "slots"
957
- ]);
958
- p.class = cls;
959
- p.style = { ...(props.style || {}) };
960
-
961
- const drawerWidth = uiStyleValue(props.drawerWidth, toCssSize);
962
- if (drawerWidth != null) p.style["--cms-app-shell-drawer-width"] = drawerWidth;
963
- const gap = uiStyleValue(props.gap, toCssSize);
964
- if (gap != null) p.style["--cms-app-shell-gap"] = gap;
965
- const padding = uiStyleValue(props.padding, toCssSize);
966
- if (padding != null) p.style["--cms-app-shell-padding"] = padding;
967
-
968
- const headerWrap = headerNodes.length
969
- ? _.div({ class: "cms-app-shell-header-slot" }, ...headerNodes)
970
- : null;
971
- const drawerWrap = drawerNodes.length
972
- ? _.div({ class: "cms-app-shell-drawer-slot" }, ...drawerNodes)
973
- : null;
974
- const pageWrap = pageNodes.length
975
- ? _.div({ class: "cms-app-shell-page-slot" }, ...pageNodes)
976
- : null;
977
- const footerWrap = footerNodes.length
978
- ? _.div({ class: "cms-app-shell-footer-slot" }, ...footerNodes)
979
- : null;
980
-
981
- const bodyNodes = reverse
982
- ? [pageWrap, drawerWrap]
983
- : [drawerWrap, pageWrap];
984
-
985
- const root = _.div(
986
- p,
987
- ...(headerWrap ? [headerWrap] : []),
988
- _.div({ class: "cms-app-shell-body" }, ...bodyNodes.filter(Boolean)),
989
- ...(footerWrap ? [footerWrap] : [])
990
- );
991
-
992
- root.openDrawer = shellCtx.openDrawer;
993
- root.closeDrawer = shellCtx.closeDrawer;
994
- root.toggleDrawer = shellCtx.toggleDrawer;
995
- root.isDrawerOpen = shellCtx.isDrawerOpen;
996
- root.header = headerWrap;
997
- root.drawer = drawerWrap;
998
- root.page = pageWrap;
999
- root.footer = footerWrap;
1000
-
1001
- setPropertyProps(root, props);
1002
- return root;
1003
- };
1004
-
1005
-
1006
- UI.Parallax = function Parallax(...args) {
1007
- const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
1008
- const slots = rawProps.slots || {};
1009
- const props = { ...rawProps };
1010
-
1011
- if (props.color != null && props.textColor == null) props.textColor = props.color;
1012
- delete props.color;
1013
- applyCommonProps(props);
1014
-
1015
- const stateClass = uiComputed(rawProps.state, () => {
1016
- const state = normalizeState(uiUnwrap(rawProps.state));
1017
- return state ? `cms-state-${state}` : "";
1018
- });
1019
- const mapColorValue = (value) => {
1020
- if (value == null || value === false || value === "") return "";
1021
- const text = String(value);
1022
- return isTokenCSS(text) ? `var(--cms-${text})` : text;
1023
- };
1024
- const getBackgroundImage = () => {
1025
- const background = uiUnwrap(rawProps.background);
1026
- if (background) return String(background);
1027
- const source = uiUnwrap(rawProps.image) || uiUnwrap(rawProps.src) || "";
1028
- if (!source) return "linear-gradient(120deg, #1b2c5a, #2f6d8f, #2a8f5c)";
1029
- const text = String(source);
1030
- if (/^url\(/i.test(text) || /gradient\(/i.test(text) || /^var\(/i.test(text)) return text;
1031
- return `url(${text})`;
1032
- };
1033
- const getNumericValue = (value, fallback) => {
1034
- const num = Number(uiUnwrap(value));
1035
- return Number.isFinite(num) ? num : fallback;
1036
- };
1037
-
1038
- const ctx = {
1039
- speed: () => uiUnwrap(rawProps.speed) ?? 0.18,
1040
- state: () => uiUnwrap(rawProps.state) || "",
1041
- disabled: () => !!uiUnwrap(rawProps.disabled)
1042
- };
1043
-
1044
- const badgeNodes = renderSlotToArray(slots, "badge", ctx, rawProps.badge);
1045
- const backgroundNodes = renderSlotToArray(slots, "background", ctx, rawProps.backgroundContent);
1046
- const eyebrowNodes = renderSlotToArray(slots, "eyebrow", ctx, rawProps.eyebrow ?? rawProps.kicker);
1047
- const titleNodes = renderSlotToArray(slots, "title", ctx, rawProps.title);
1048
- const subtitleNodes = renderSlotToArray(slots, "subtitle", ctx, rawProps.subtitle);
1049
- const headerNodes = renderSlotToArray(slots, "header", ctx, rawProps.header);
1050
- const asideNodes = renderSlotToArray(slots, "aside", ctx, rawProps.aside);
1051
- const mediaNodes = renderSlotToArray(slots, "media", ctx, rawProps.media);
1052
- const contentNodes = renderSlotToArray(slots, "content", ctx, rawProps.content ?? rawProps.body);
1053
- const defaultNodes = renderSlotToArray(slots, "default", ctx, children?.length ? children : []);
1054
- const footerNodes = renderSlotToArray(slots, "footer", ctx, rawProps.footer);
1055
- const actionsNodes = renderSlotToArray(slots, "actions", ctx, rawProps.actions);
1056
- const mergedBodyNodes = [...contentNodes, ...defaultNodes];
1057
-
1058
- const generatedBadge = badgeNodes.length
1059
- ? _.div({ class: uiClass(["cms-parallax-badge", rawProps.badgeClass]) }, ...badgeNodes)
1060
- : null;
1061
- const hasStructuredHeader = eyebrowNodes.length || titleNodes.length || subtitleNodes.length || headerNodes.length || asideNodes.length;
1062
- const generatedHeader = hasStructuredHeader
1063
- ? _.div(
1064
- { class: uiClass(["cms-parallax-header", rawProps.headerClass]) },
1065
- _.div(
1066
- { class: "cms-parallax-head" },
1067
- _.div(
1068
- { class: "cms-parallax-head-main" },
1069
- eyebrowNodes.length ? _.div({ class: uiClass(["cms-parallax-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
1070
- titleNodes.length ? _.div({ class: uiClass(["cms-parallax-title", rawProps.titleClass]) }, ...titleNodes) : null,
1071
- subtitleNodes.length ? _.div({ class: uiClass(["cms-parallax-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
1072
- headerNodes.length ? _.div({ class: uiClass(["cms-parallax-header-content", rawProps.headerContentClass]) }, ...headerNodes) : null
1073
- ),
1074
- asideNodes.length ? _.div({ class: uiClass(["cms-parallax-aside", rawProps.asideClass]) }, ...asideNodes) : null
1075
- )
1076
- )
1077
- : null;
1078
- const generatedMedia = mediaNodes.length
1079
- ? _.div({ class: uiClass(["cms-parallax-media", rawProps.mediaClass]) }, ...mediaNodes)
1080
- : null;
1081
- const generatedBody = mergedBodyNodes.length
1082
- ? _.div({ class: uiClass(["cms-parallax-body", rawProps.bodyClass]) }, ...mergedBodyNodes)
1083
- : null;
1084
- const generatedFooter = (footerNodes.length || actionsNodes.length)
1085
- ? _.div(
1086
- { class: uiClass(["cms-parallax-footer", rawProps.footerClass]) },
1087
- footerNodes.length ? _.div({ class: "cms-parallax-footer-content" }, ...footerNodes) : null,
1088
- actionsNodes.length ? _.div({ class: "cms-parallax-actions" }, ...actionsNodes) : null
1089
- )
1090
- : null;
1091
-
1092
- const wrapProps = JSswift.omit(props, [
1093
- "actions", "align", "aside", "asideClass", "background", "backgroundContent", "backgroundContentClass",
1094
- "badge", "badgeClass", "bgClass", "bgPosition", "bgRepeat", "bgSize", "body", "bodyClass", "color",
1095
- "content", "contentClass", "contentMaxWidth", "disabled", "eyebrow", "eyebrowClass", "footer",
1096
- "footerClass", "gap", "header", "headerClass", "headerContentClass", "height", "image", "imageAlt",
1097
- "imageClass", "innerClass", "justify", "kicker", "maxOffset", "media", "mediaClass", "minHeight",
1098
- "overlay", "padding", "slots", "speed", "src", "startTop", "state", "subtitle", "subtitleClass",
1099
- "textColor", "title", "titleClass"
1100
- ]);
1101
- wrapProps.class = uiClass([
1102
- "cms-parallax",
1103
- "cms-singularity",
1104
- stateClass,
1105
- uiWhen(rawProps.disabled, "cms-parallax-static"),
1106
- uiWhen(generatedBadge, "cms-parallax-has-badge"),
1107
- uiWhen(hasStructuredHeader, "cms-parallax-has-header"),
1108
- props.class
1109
- ]);
1110
- wrapProps.style = { ...(props.style || {}) };
1111
- if (rawProps.height != null) wrapProps.style["--cms-parallax-height"] = uiStyleValue(rawProps.height, toCssSize);
1112
- if (rawProps.minHeight != null) wrapProps.style["--cms-parallax-min-height"] = uiStyleValue(rawProps.minHeight, toCssSize);
1113
- if (rawProps.padding != null) wrapProps.style["--cms-parallax-padding"] = uiStyleValue(rawProps.padding, toCssSize);
1114
- if (rawProps.gap != null) wrapProps.style["--cms-parallax-gap"] = uiStyleValue(rawProps.gap, toCssSize);
1115
- if (rawProps.contentMaxWidth != null) wrapProps.style["--cms-parallax-content-max-width"] = uiStyleValue(rawProps.contentMaxWidth, toCssSize);
1116
- if (rawProps.justify != null) wrapProps.style["--cms-parallax-justify"] = uiStyleValue(rawProps.justify);
1117
- if (rawProps.align != null) wrapProps.style["--cms-parallax-align"] = uiStyleValue(rawProps.align);
1118
- if (rawProps.color != null) wrapProps.style["--cms-parallax-color"] = uiStyleValue(rawProps.color, mapColorValue);
1119
- if (rawProps.overlay != null) wrapProps.style["--cms-parallax-overlay"] = uiStyleValue(rawProps.overlay);
1120
- if (rawProps.bgPosition != null) wrapProps.style["--cms-parallax-position"] = uiStyleValue(rawProps.bgPosition);
1121
- if (rawProps.bgSize != null) wrapProps.style["--cms-parallax-size"] = uiStyleValue(rawProps.bgSize);
1122
- if (rawProps.bgRepeat != null) wrapProps.style["--cms-parallax-repeat"] = uiStyleValue(rawProps.bgRepeat);
1123
- wrapProps.style["--cms-parallax-image"] = (uiIsReactive(rawProps.background) || uiIsReactive(rawProps.image) || uiIsReactive(rawProps.src))
1124
- ? () => getBackgroundImage()
1125
- : getBackgroundImage();
1126
-
1127
- const bg = _.div(
1128
- { class: uiClass(["cms-parallax-bg", rawProps.bgClass]) },
1129
- backgroundNodes.length
1130
- ? _.div({ class: uiClass(["cms-parallax-bg-content", rawProps.backgroundContentClass]) }, ...backgroundNodes)
1131
- : null
1132
- );
1133
- const content = _.div(
1134
- { class: uiClass(["cms-parallax-content", rawProps.contentClass, rawProps.innerClass]) },
1135
- generatedBadge,
1136
- generatedHeader,
1137
- generatedMedia,
1138
- generatedBody,
1139
- generatedFooter
1140
- );
1141
-
1142
- const wrap = _.div(wrapProps, bg, content);
1143
-
1144
- let disposed = false;
1145
- let ticking = false;
1146
- let resizeObserver = null;
1147
-
1148
- const cleanup = () => {
1149
- if (disposed) return;
1150
- disposed = true;
1151
- if (typeof window !== "undefined") {
1152
- window.removeEventListener("scroll", scheduleUpdate);
1153
- window.removeEventListener("resize", scheduleUpdate);
1154
- }
1155
- resizeObserver?.disconnect?.();
1156
- resizeObserver = null;
1157
- };
1158
-
1159
- const update = () => {
1160
- if (disposed) return;
1161
- if (!wrap.isConnected) {
1162
- cleanup();
1163
- return;
1164
- }
1165
- if (uiUnwrap(rawProps.disabled)) {
1166
- bg.style.transform = "translate3d(0, 0, 0)";
1167
- wrap.style.setProperty("--cms-parallax-offset", "0px");
1168
- return;
1169
- }
1170
- const rect = wrap.getBoundingClientRect();
1171
- const speed = getNumericValue(rawProps.speed, 0.18);
1172
- const maxOffset = Math.abs(getNumericValue(rawProps.maxOffset, 96));
1173
- const explicitStartTop = uiUnwrap(rawProps.startTop);
1174
- const rawOffset = explicitStartTop != null && explicitStartTop !== ""
1175
- ? (rect.top - getNumericValue(rawProps.startTop, 0)) * speed
1176
- : (((typeof window !== "undefined" ? window.innerHeight || document.documentElement.clientHeight || 0 : 0) / 2) - (rect.top + (rect.height / 2))) * speed;
1177
- const offset = maxOffset > 0 ? Math.max(-maxOffset, Math.min(maxOffset, rawOffset)) : rawOffset;
1178
- const cssOffset = `${offset.toFixed(2)}px`;
1179
- bg.style.transform = `translate3d(0, ${cssOffset}, 0)`;
1180
- wrap.style.setProperty("--cms-parallax-offset", cssOffset);
1181
- };
1182
-
1183
- function scheduleUpdate() {
1184
- if (disposed || ticking) return;
1185
- ticking = true;
1186
- requestAnimationFrame(() => {
1187
- ticking = false;
1188
- update();
1189
- });
1190
- }
1191
-
1192
- if (typeof window !== "undefined") {
1193
- window.addEventListener("scroll", scheduleUpdate, { passive: true });
1194
- window.addEventListener("resize", scheduleUpdate);
1195
- if (typeof ResizeObserver !== "undefined") {
1196
- resizeObserver = new ResizeObserver(() => { scheduleUpdate(); });
1197
- resizeObserver.observe(wrap);
1198
- }
1199
- requestAnimationFrame(() => { scheduleUpdate(); });
1200
- setTimeout(() => { scheduleUpdate(); }, 90);
1201
- }
1202
-
1203
- wrap.refresh = () => {
1204
- scheduleUpdate();
1205
- return wrap;
1206
- };
1207
- wrap.update = wrap.refresh;
1208
- wrap.destroy = () => {
1209
- cleanup();
1210
- wrap.remove();
1211
- return null;
1212
- };
1213
- wrap._dispose = cleanup;
1214
-
1215
- setPropertyProps(wrap, rawProps);
1216
- return wrap;
1217
- };
1218
-
1219
-
1220
- // Example:
1221
- // _.Parallax({ src: "/assets/hero.jpg", height: "280px", speed: 0.2 }, _.h2("Hello"));
1222
-
1223
- // -------------------------------
679
+
680
+
681
+ UI.Page = (...args) => {
682
+ const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
683
+ const slots = rawProps.slots || {};
684
+ const props = { ...rawProps };
685
+
686
+ const isSectionNode = (node, name) => {
687
+ return !!(node && node.nodeType === 1 && node.classList?.contains(`jss-page-${name}`));
688
+ };
689
+ const renderIconFallback = (value) => {
690
+ if (value == null) return null;
691
+ if (typeof value === "string") return UI.Icon({ name: value, size: rawProps.iconSize || rawProps.size || "xl" });
692
+ return JSswift.ui.slot(value, { as: "icon" });
693
+ };
694
+
695
+ const ctx = {
696
+ dense: !!uiUnwrap(rawProps.dense),
697
+ flat: !!uiUnwrap(rawProps.flat),
698
+ centered: !!uiUnwrap(rawProps.centered),
699
+ narrow: !!uiUnwrap(rawProps.narrow)
700
+ };
701
+ const appendResolvedValue = (host, value) => {
702
+ if (value == null || value === false) return;
703
+ if (Array.isArray(value)) {
704
+ value.forEach((item) => appendResolvedValue(host, item));
705
+ return;
706
+ }
707
+ if (value?.nodeType) {
708
+ host.appendChild(value);
709
+ return;
710
+ }
711
+ host.appendChild(document.createTextNode(String(value)));
712
+ };
713
+ const renderPropNodes = (name, fallback, map = (value) => value) => {
714
+ const slot = JSswift.ui.getSlot(slots, name);
715
+ if (slot !== null && slot !== undefined) {
716
+ return renderSlotToArray(slots, name, ctx, null);
717
+ }
718
+ if (typeof fallback === "function") {
719
+ const inlineNames = new Set(["eyebrow", "title", "subtitle"]);
720
+ const host = _[inlineNames.has(name) ? "span" : "div"]({ class: `jss-page-slot-${name}` });
721
+ JSswift.reactive.effect(() => {
722
+ const nextValue = map(fallback(ctx));
723
+ const normalized = flattenSlotValue(JSswift.ui.slot(nextValue));
724
+ host.replaceChildren();
725
+ if (Array.isArray(normalized)) normalized.forEach((item) => appendResolvedValue(host, item));
726
+ else appendResolvedValue(host, normalized);
727
+ }, `UI.Page:${name}`);
728
+ return [host];
729
+ }
730
+ return renderSlotToArray(slots, name, ctx, map(fallback));
731
+ };
732
+
733
+ const defaultNodes = renderSlotToArray(slots, "default", ctx, children?.length ? children : []);
734
+ const sectionNodes = {
735
+ hero: [],
736
+ header: [],
737
+ body: [],
738
+ footer: [],
739
+ actions: []
740
+ };
741
+ const looseNodes = [];
742
+
743
+ defaultNodes.forEach((node) => {
744
+ if (isSectionNode(node, "hero")) sectionNodes.hero.push(node);
745
+ else if (isSectionNode(node, "header")) sectionNodes.header.push(node);
746
+ else if (isSectionNode(node, "body")) sectionNodes.body.push(node);
747
+ else if (isSectionNode(node, "footer")) sectionNodes.footer.push(node);
748
+ else if (isSectionNode(node, "actions")) sectionNodes.actions.push(node);
749
+ else looseNodes.push(node);
750
+ });
751
+
752
+ const iconNodes = renderPropNodes("icon", rawProps.icon, renderIconFallback);
753
+ const heroNodes = renderPropNodes("hero", rawProps.hero ?? rawProps.banner);
754
+ const eyebrowNodes = renderPropNodes("eyebrow", rawProps.eyebrow ?? rawProps.kicker);
755
+ const titleNodes = renderPropNodes("title", rawProps.title);
756
+ const subtitleNodes = renderPropNodes("subtitle", rawProps.subtitle ?? rawProps.description);
757
+ const headerNodes = renderPropNodes("header", rawProps.header);
758
+ const asideNodes = renderPropNodes("aside", rawProps.aside);
759
+ const bodyNodes = renderPropNodes("body", rawProps.body ?? rawProps.content);
760
+ const footerNodes = renderPropNodes("footer", rawProps.footer);
761
+ const actionsNodes = renderPropNodes("actions", rawProps.actions);
762
+
763
+ const generatedHero = heroNodes.length
764
+ ? _.div({ class: uiClass(["jss-page-hero", rawProps.heroClass]) }, ...heroNodes)
765
+ : null;
766
+
767
+ const hasStructuredHeader = iconNodes.length || eyebrowNodes.length || titleNodes.length || subtitleNodes.length || headerNodes.length || asideNodes.length;
768
+ const generatedHeader = hasStructuredHeader
769
+ ? _.div(
770
+ { class: uiClass(["jss-page-header", rawProps.headerClass]) },
771
+ _.div(
772
+ { class: "jss-page-head" },
773
+ iconNodes.length ? _.div({ class: uiClass(["jss-page-icon", rawProps.iconClass]) }, ...iconNodes) : null,
774
+ _.div(
775
+ { class: "jss-page-head-main" },
776
+ eyebrowNodes.length ? _.div({ class: uiClass(["jss-page-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
777
+ titleNodes.length ? _.div({ class: uiClass(["jss-page-title", rawProps.titleClass]) }, ...titleNodes) : null,
778
+ subtitleNodes.length ? _.div({ class: uiClass(["jss-page-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
779
+ headerNodes.length ? _.div({ class: uiClass(["jss-page-header-content", rawProps.headerContentClass]) }, ...headerNodes) : null
780
+ ),
781
+ asideNodes.length ? _.div({ class: uiClass(["jss-page-aside", rawProps.asideClass]) }, ...asideNodes) : null
782
+ )
783
+ )
784
+ : null;
785
+
786
+ const mergedBodyNodes = [...bodyNodes, ...looseNodes];
787
+ const generatedBody = mergedBodyNodes.length
788
+ ? _.div({ class: uiClass(["jss-page-body", rawProps.bodyClass]) }, ...mergedBodyNodes)
789
+ : null;
790
+
791
+ const mergedActionNodes = [...actionsNodes, ...sectionNodes.actions];
792
+ const generatedFooter = (footerNodes.length || mergedActionNodes.length)
793
+ ? _.div(
794
+ { class: uiClass(["jss-page-footer", rawProps.footerClass]) },
795
+ ...footerNodes,
796
+ mergedActionNodes.length ? _.div({ class: "jss-page-actions" }, ...mergedActionNodes) : null
797
+ )
798
+ : null;
799
+
800
+ const hasHero = !!(heroNodes.length || sectionNodes.hero.length);
801
+ const hasHeader = !!(hasStructuredHeader || sectionNodes.header.length);
802
+ const p = JSswift.omit(props, [
803
+ "actions", "aside", "asideClass", "banner", "body", "bodyClass", "centered", "content",
804
+ "dense", "description", "eyebrow", "eyebrowClass", "flat", "footer", "footerClass",
805
+ "gap", "header", "headerClass", "headerContentClass", "headerGap", "hero", "heroClass",
806
+ "heroPadding", "icon", "iconClass", "iconSize", "kicker", "maxWidth", "minHeight",
807
+ "narrow", "padding", "size", "slots", "subtitle", "subtitleClass", "title", "titleClass"
808
+ ]);
809
+ p.class = uiClass([
810
+ "jss-panel",
811
+ "jss-page",
812
+ uiWhen(rawProps.dense, "dense"),
813
+ uiWhen(rawProps.flat, "jss-page-flat"),
814
+ uiWhen(rawProps.centered, "jss-page-centered"),
815
+ uiWhen(rawProps.narrow, "jss-page-narrow"),
816
+ uiWhen(hasHero, "jss-page-has-hero"),
817
+ uiWhen(hasHeader, "jss-page-has-header"),
818
+ props.class
819
+ ]);
820
+ p.style = { ...(props.style || {}) };
821
+
822
+ if (rawProps.gap != null || uiIsReactive(rawProps.gap)) {
823
+ p.style["--jss-page-gap"] = uiStyleValue(rawProps.gap, toCssSize);
824
+ }
825
+ if (rawProps.padding != null || uiIsReactive(rawProps.padding)) {
826
+ p.style["--jss-page-padding"] = uiStyleValue(rawProps.padding, toCssSize);
827
+ }
828
+ if (rawProps.maxWidth != null || uiIsReactive(rawProps.maxWidth)) {
829
+ p.style["--jss-page-max-width"] = uiStyleValue(rawProps.maxWidth, toCssSize);
830
+ }
831
+ if (rawProps.minHeight != null || uiIsReactive(rawProps.minHeight)) {
832
+ p.style["--jss-page-min-height"] = uiStyleValue(rawProps.minHeight, toCssSize);
833
+ }
834
+ if (rawProps.headerGap != null || uiIsReactive(rawProps.headerGap)) {
835
+ p.style["--jss-page-header-gap"] = uiStyleValue(rawProps.headerGap, toCssSize);
836
+ }
837
+ if (rawProps.heroPadding != null || uiIsReactive(rawProps.heroPadding)) {
838
+ p.style["--jss-page-hero-padding"] = uiStyleValue(rawProps.heroPadding, toCssSize);
839
+ }
840
+
841
+ const el = _.div(
842
+ p,
843
+ generatedHero,
844
+ ...sectionNodes.hero,
845
+ generatedHeader,
846
+ ...sectionNodes.header,
847
+ generatedBody,
848
+ ...sectionNodes.body,
849
+ generatedFooter,
850
+ ...sectionNodes.footer
851
+ );
852
+
853
+ setPropertyProps(el, rawProps);
854
+ return el;
855
+ };
856
+
857
+
858
+ UI.AppShell = (...args) => {
859
+ const { props, children } = JSswift.uiNormalizeArgs(args);
860
+ const slots = props.slots || {};
861
+ const hasOwn = (obj, key) => !!obj && Object.prototype.hasOwnProperty.call(obj, key);
862
+
863
+ const noDrawer = uiUnwrap(props.noDrawer) === true || props.drawer === false;
864
+ const reverse = uiUnwrap(props.reverse) === true;
865
+ const stack = uiUnwrap(props.stack) === true;
866
+ const currentStateKey = props.drawerStateKey ?? props.stateKey ?? drawerStateKey;
867
+
868
+ if (currentStateKey) {
869
+ drawerStateKey = currentStateKey;
870
+ drawerOpen = readDrawerOpen(currentStateKey);
871
+ }
872
+
873
+ const shellCtx = {
874
+ props,
875
+ noDrawer,
876
+ stateKey: currentStateKey,
877
+ isDrawerOpen: () => readDrawerOpen(currentStateKey),
878
+ openDrawer: () => setDrawerOpen(true, currentStateKey),
879
+ closeDrawer: () => setDrawerOpen(false, currentStateKey),
880
+ toggleDrawer: () => setDrawerOpen(!readDrawerOpen(currentStateKey), currentStateKey)
881
+ };
882
+
883
+ const headerProps = (props.headerProps && typeof props.headerProps === "object") ? { ...props.headerProps } : {};
884
+ let headerFallback = props.header;
885
+ if (!hasOwn(props, "header") && (
886
+ Object.keys(headerProps).length ||
887
+ hasOwn(props, "title") ||
888
+ hasOwn(props, "subtitle") ||
889
+ hasOwn(props, "left") ||
890
+ hasOwn(props, "right")
891
+ )) {
892
+ if (!hasOwn(headerProps, "title") && hasOwn(props, "title")) headerProps.title = props.title;
893
+ if (!hasOwn(headerProps, "subtitle") && hasOwn(props, "subtitle")) headerProps.subtitle = props.subtitle;
894
+ if (!hasOwn(headerProps, "left") && hasOwn(props, "left")) headerProps.left = props.left;
895
+ if (!hasOwn(headerProps, "right") && hasOwn(props, "right")) headerProps.right = props.right;
896
+ if (!hasOwn(headerProps, "drawerStateKey")) headerProps.drawerStateKey = currentStateKey;
897
+ if (noDrawer && !hasOwn(headerProps, "left")) headerProps.left = false;
898
+ headerFallback = UI.Header(headerProps);
899
+ }
900
+
901
+ const drawerProps = (props.drawerProps && typeof props.drawerProps === "object") ? { ...props.drawerProps } : {};
902
+ const drawerItems = hasOwn(props, "drawerItems") ? props.drawerItems : props.items;
903
+ const drawerHeader = hasOwn(props, "drawerHeader") ? props.drawerHeader : undefined;
904
+ let drawerFallback = props.drawer;
905
+ if (!hasOwn(props, "drawer") && !noDrawer && (
906
+ Object.keys(drawerProps).length ||
907
+ drawerItems != null ||
908
+ drawerHeader != null
909
+ )) {
910
+ if (!hasOwn(drawerProps, "items") && drawerItems != null) drawerProps.items = drawerItems;
911
+ if (!hasOwn(drawerProps, "header") && drawerHeader != null) drawerProps.header = drawerHeader;
912
+ if (!hasOwn(drawerProps, "stateKey")) drawerProps.stateKey = currentStateKey;
913
+ drawerFallback = UI.Drawer(drawerProps);
914
+ }
915
+
916
+ const pageProps = (props.pageProps && typeof props.pageProps === "object") ? { ...props.pageProps } : {};
917
+ const pageContentFallback = hasOwn(props, "content") ? props.content : children;
918
+ const defaultPageContent = renderSlotToArray(slots, "default", shellCtx, pageContentFallback);
919
+ let pageFallback = props.page;
920
+ if (!hasOwn(props, "page") && (Object.keys(pageProps).length || defaultPageContent.length)) {
921
+ pageFallback = UI.Page(pageProps, ...defaultPageContent);
922
+ }
923
+
924
+ const footerProps = (props.footerProps && typeof props.footerProps === "object") ? { ...props.footerProps } : {};
925
+ const footerContent = hasOwn(props, "footerContent") ? props.footerContent : undefined;
926
+ let footerFallback = props.footer;
927
+ if (!hasOwn(props, "footer") && (Object.keys(footerProps).length || footerContent != null)) {
928
+ footerFallback = UI.Footer(footerProps, ...renderSlotToArray(null, "default", shellCtx, footerContent));
929
+ }
930
+
931
+ const headerNodes = renderSlotToArray(slots, "header", shellCtx, headerFallback);
932
+ const drawerNodes = noDrawer ? [] : renderSlotToArray(slots, "drawer", shellCtx, drawerFallback);
933
+ const pageNodes = renderSlotToArray(slots, "page", shellCtx, pageFallback);
934
+ const footerNodes = renderSlotToArray(slots, "footer", shellCtx, footerFallback);
935
+
936
+ const cls = uiClass([
937
+ "jss-app",
938
+ "jss-app-shell",
939
+ uiWhen(noDrawer, "no-drawer"),
940
+ uiWhen(reverse, "is-reverse"),
941
+ uiWhen(stack, "is-stack"),
942
+ uiWhen(props.flush, "is-flush"),
943
+ uiWhen(props.divider, "is-divider"),
944
+ props.class
945
+ ]);
946
+
947
+ const p = JSswift.omit(props, [
948
+ "header", "drawer", "page", "footer", "content",
949
+ "title", "subtitle", "left", "right",
950
+ "items", "drawerItems", "drawerHeader",
951
+ "headerProps", "drawerProps", "pageProps", "footerProps",
952
+ "footerContent",
953
+ "noDrawer", "reverse", "stack", "flush", "divider",
954
+ "drawerStateKey", "stateKey",
955
+ "drawerWidth", "gap", "padding",
956
+ "slots"
957
+ ]);
958
+ p.class = cls;
959
+ p.style = { ...(props.style || {}) };
960
+
961
+ const drawerWidth = uiStyleValue(props.drawerWidth, toCssSize);
962
+ if (drawerWidth != null) p.style["--jss-app-shell-drawer-width"] = drawerWidth;
963
+ const gap = uiStyleValue(props.gap, toCssSize);
964
+ if (gap != null) p.style["--jss-app-shell-gap"] = gap;
965
+ const padding = uiStyleValue(props.padding, toCssSize);
966
+ if (padding != null) p.style["--jss-app-shell-padding"] = padding;
967
+
968
+ const headerWrap = headerNodes.length
969
+ ? _.div({ class: "jss-app-shell-header-slot" }, ...headerNodes)
970
+ : null;
971
+ const drawerWrap = drawerNodes.length
972
+ ? _.div({ class: "jss-app-shell-drawer-slot" }, ...drawerNodes)
973
+ : null;
974
+ const pageWrap = pageNodes.length
975
+ ? _.div({ class: "jss-app-shell-page-slot" }, ...pageNodes)
976
+ : null;
977
+ const footerWrap = footerNodes.length
978
+ ? _.div({ class: "jss-app-shell-footer-slot" }, ...footerNodes)
979
+ : null;
980
+
981
+ const bodyNodes = reverse
982
+ ? [pageWrap, drawerWrap]
983
+ : [drawerWrap, pageWrap];
984
+
985
+ const root = _.div(
986
+ p,
987
+ ...(headerWrap ? [headerWrap] : []),
988
+ _.div({ class: "jss-app-shell-body" }, ...bodyNodes.filter(Boolean)),
989
+ ...(footerWrap ? [footerWrap] : [])
990
+ );
991
+
992
+ root.openDrawer = shellCtx.openDrawer;
993
+ root.closeDrawer = shellCtx.closeDrawer;
994
+ root.toggleDrawer = shellCtx.toggleDrawer;
995
+ root.isDrawerOpen = shellCtx.isDrawerOpen;
996
+ root.header = headerWrap;
997
+ root.drawer = drawerWrap;
998
+ root.page = pageWrap;
999
+ root.footer = footerWrap;
1000
+
1001
+ setPropertyProps(root, props);
1002
+ return root;
1003
+ };
1004
+
1005
+
1006
+ UI.Parallax = function Parallax(...args) {
1007
+ const { props: rawProps, children } = JSswift.uiNormalizeArgs(args);
1008
+ const slots = rawProps.slots || {};
1009
+ const props = { ...rawProps };
1010
+
1011
+ if (props.color != null && props.textColor == null) props.textColor = props.color;
1012
+ delete props.color;
1013
+ applyCommonProps(props);
1014
+
1015
+ const stateClass = uiComputed(rawProps.state, () => {
1016
+ const state = normalizeState(uiUnwrap(rawProps.state));
1017
+ return state ? `jss-state-${state}` : "";
1018
+ });
1019
+ const mapColorValue = (value) => {
1020
+ if (value == null || value === false || value === "") return "";
1021
+ const text = String(value);
1022
+ return isTokenCSS(text) ? `var(--jss-${text})` : text;
1023
+ };
1024
+ const getBackgroundImage = () => {
1025
+ const background = uiUnwrap(rawProps.background);
1026
+ if (background) return String(background);
1027
+ const source = uiUnwrap(rawProps.image) || uiUnwrap(rawProps.src) || "";
1028
+ if (!source) return "linear-gradient(120deg, #1b2c5a, #2f6d8f, #2a8f5c)";
1029
+ const text = String(source);
1030
+ if (/^url\(/i.test(text) || /gradient\(/i.test(text) || /^var\(/i.test(text)) return text;
1031
+ return `url(${text})`;
1032
+ };
1033
+ const getNumericValue = (value, fallback) => {
1034
+ const num = Number(uiUnwrap(value));
1035
+ return Number.isFinite(num) ? num : fallback;
1036
+ };
1037
+
1038
+ const ctx = {
1039
+ speed: () => uiUnwrap(rawProps.speed) ?? 0.18,
1040
+ state: () => uiUnwrap(rawProps.state) || "",
1041
+ disabled: () => !!uiUnwrap(rawProps.disabled)
1042
+ };
1043
+
1044
+ const badgeNodes = renderSlotToArray(slots, "badge", ctx, rawProps.badge);
1045
+ const backgroundNodes = renderSlotToArray(slots, "background", ctx, rawProps.backgroundContent);
1046
+ const eyebrowNodes = renderSlotToArray(slots, "eyebrow", ctx, rawProps.eyebrow ?? rawProps.kicker);
1047
+ const titleNodes = renderSlotToArray(slots, "title", ctx, rawProps.title);
1048
+ const subtitleNodes = renderSlotToArray(slots, "subtitle", ctx, rawProps.subtitle);
1049
+ const headerNodes = renderSlotToArray(slots, "header", ctx, rawProps.header);
1050
+ const asideNodes = renderSlotToArray(slots, "aside", ctx, rawProps.aside);
1051
+ const mediaNodes = renderSlotToArray(slots, "media", ctx, rawProps.media);
1052
+ const contentNodes = renderSlotToArray(slots, "content", ctx, rawProps.content ?? rawProps.body);
1053
+ const defaultNodes = renderSlotToArray(slots, "default", ctx, children?.length ? children : []);
1054
+ const footerNodes = renderSlotToArray(slots, "footer", ctx, rawProps.footer);
1055
+ const actionsNodes = renderSlotToArray(slots, "actions", ctx, rawProps.actions);
1056
+ const mergedBodyNodes = [...contentNodes, ...defaultNodes];
1057
+
1058
+ const generatedBadge = badgeNodes.length
1059
+ ? _.div({ class: uiClass(["jss-parallax-badge", rawProps.badgeClass]) }, ...badgeNodes)
1060
+ : null;
1061
+ const hasStructuredHeader = eyebrowNodes.length || titleNodes.length || subtitleNodes.length || headerNodes.length || asideNodes.length;
1062
+ const generatedHeader = hasStructuredHeader
1063
+ ? _.div(
1064
+ { class: uiClass(["jss-parallax-header", rawProps.headerClass]) },
1065
+ _.div(
1066
+ { class: "jss-parallax-head" },
1067
+ _.div(
1068
+ { class: "jss-parallax-head-main" },
1069
+ eyebrowNodes.length ? _.div({ class: uiClass(["jss-parallax-eyebrow", rawProps.eyebrowClass]) }, ...eyebrowNodes) : null,
1070
+ titleNodes.length ? _.div({ class: uiClass(["jss-parallax-title", rawProps.titleClass]) }, ...titleNodes) : null,
1071
+ subtitleNodes.length ? _.div({ class: uiClass(["jss-parallax-subtitle", rawProps.subtitleClass]) }, ...subtitleNodes) : null,
1072
+ headerNodes.length ? _.div({ class: uiClass(["jss-parallax-header-content", rawProps.headerContentClass]) }, ...headerNodes) : null
1073
+ ),
1074
+ asideNodes.length ? _.div({ class: uiClass(["jss-parallax-aside", rawProps.asideClass]) }, ...asideNodes) : null
1075
+ )
1076
+ )
1077
+ : null;
1078
+ const generatedMedia = mediaNodes.length
1079
+ ? _.div({ class: uiClass(["jss-parallax-media", rawProps.mediaClass]) }, ...mediaNodes)
1080
+ : null;
1081
+ const generatedBody = mergedBodyNodes.length
1082
+ ? _.div({ class: uiClass(["jss-parallax-body", rawProps.bodyClass]) }, ...mergedBodyNodes)
1083
+ : null;
1084
+ const generatedFooter = (footerNodes.length || actionsNodes.length)
1085
+ ? _.div(
1086
+ { class: uiClass(["jss-parallax-footer", rawProps.footerClass]) },
1087
+ footerNodes.length ? _.div({ class: "jss-parallax-footer-content" }, ...footerNodes) : null,
1088
+ actionsNodes.length ? _.div({ class: "jss-parallax-actions" }, ...actionsNodes) : null
1089
+ )
1090
+ : null;
1091
+
1092
+ const wrapProps = JSswift.omit(props, [
1093
+ "actions", "align", "aside", "asideClass", "background", "backgroundContent", "backgroundContentClass",
1094
+ "badge", "badgeClass", "bgClass", "bgPosition", "bgRepeat", "bgSize", "body", "bodyClass", "color",
1095
+ "content", "contentClass", "contentMaxWidth", "disabled", "eyebrow", "eyebrowClass", "footer",
1096
+ "footerClass", "gap", "header", "headerClass", "headerContentClass", "height", "image", "imageAlt",
1097
+ "imageClass", "innerClass", "justify", "kicker", "maxOffset", "media", "mediaClass", "minHeight",
1098
+ "overlay", "padding", "slots", "speed", "src", "startTop", "state", "subtitle", "subtitleClass",
1099
+ "textColor", "title", "titleClass"
1100
+ ]);
1101
+ wrapProps.class = uiClass([
1102
+ "jss-parallax",
1103
+ "jss-singularity",
1104
+ stateClass,
1105
+ uiWhen(rawProps.disabled, "jss-parallax-static"),
1106
+ uiWhen(generatedBadge, "jss-parallax-has-badge"),
1107
+ uiWhen(hasStructuredHeader, "jss-parallax-has-header"),
1108
+ props.class
1109
+ ]);
1110
+ wrapProps.style = { ...(props.style || {}) };
1111
+ if (rawProps.height != null) wrapProps.style["--jss-parallax-height"] = uiStyleValue(rawProps.height, toCssSize);
1112
+ if (rawProps.minHeight != null) wrapProps.style["--jss-parallax-min-height"] = uiStyleValue(rawProps.minHeight, toCssSize);
1113
+ if (rawProps.padding != null) wrapProps.style["--jss-parallax-padding"] = uiStyleValue(rawProps.padding, toCssSize);
1114
+ if (rawProps.gap != null) wrapProps.style["--jss-parallax-gap"] = uiStyleValue(rawProps.gap, toCssSize);
1115
+ if (rawProps.contentMaxWidth != null) wrapProps.style["--jss-parallax-content-max-width"] = uiStyleValue(rawProps.contentMaxWidth, toCssSize);
1116
+ if (rawProps.justify != null) wrapProps.style["--jss-parallax-justify"] = uiStyleValue(rawProps.justify);
1117
+ if (rawProps.align != null) wrapProps.style["--jss-parallax-align"] = uiStyleValue(rawProps.align);
1118
+ if (rawProps.color != null) wrapProps.style["--jss-parallax-color"] = uiStyleValue(rawProps.color, mapColorValue);
1119
+ if (rawProps.overlay != null) wrapProps.style["--jss-parallax-overlay"] = uiStyleValue(rawProps.overlay);
1120
+ if (rawProps.bgPosition != null) wrapProps.style["--jss-parallax-position"] = uiStyleValue(rawProps.bgPosition);
1121
+ if (rawProps.bgSize != null) wrapProps.style["--jss-parallax-size"] = uiStyleValue(rawProps.bgSize);
1122
+ if (rawProps.bgRepeat != null) wrapProps.style["--jss-parallax-repeat"] = uiStyleValue(rawProps.bgRepeat);
1123
+ wrapProps.style["--jss-parallax-image"] = (uiIsReactive(rawProps.background) || uiIsReactive(rawProps.image) || uiIsReactive(rawProps.src))
1124
+ ? () => getBackgroundImage()
1125
+ : getBackgroundImage();
1126
+
1127
+ const bg = _.div(
1128
+ { class: uiClass(["jss-parallax-bg", rawProps.bgClass]) },
1129
+ backgroundNodes.length
1130
+ ? _.div({ class: uiClass(["jss-parallax-bg-content", rawProps.backgroundContentClass]) }, ...backgroundNodes)
1131
+ : null
1132
+ );
1133
+ const content = _.div(
1134
+ { class: uiClass(["jss-parallax-content", rawProps.contentClass, rawProps.innerClass]) },
1135
+ generatedBadge,
1136
+ generatedHeader,
1137
+ generatedMedia,
1138
+ generatedBody,
1139
+ generatedFooter
1140
+ );
1141
+
1142
+ const wrap = _.div(wrapProps, bg, content);
1143
+
1144
+ let disposed = false;
1145
+ let ticking = false;
1146
+ let resizeObserver = null;
1147
+
1148
+ const cleanup = () => {
1149
+ if (disposed) return;
1150
+ disposed = true;
1151
+ if (typeof window !== "undefined") {
1152
+ window.removeEventListener("scroll", scheduleUpdate);
1153
+ window.removeEventListener("resize", scheduleUpdate);
1154
+ }
1155
+ resizeObserver?.disconnect?.();
1156
+ resizeObserver = null;
1157
+ };
1158
+
1159
+ const update = () => {
1160
+ if (disposed) return;
1161
+ if (!wrap.isConnected) {
1162
+ cleanup();
1163
+ return;
1164
+ }
1165
+ if (uiUnwrap(rawProps.disabled)) {
1166
+ bg.style.transform = "translate3d(0, 0, 0)";
1167
+ wrap.style.setProperty("--jss-parallax-offset", "0px");
1168
+ return;
1169
+ }
1170
+ const rect = wrap.getBoundingClientRect();
1171
+ const speed = getNumericValue(rawProps.speed, 0.18);
1172
+ const maxOffset = Math.abs(getNumericValue(rawProps.maxOffset, 96));
1173
+ const explicitStartTop = uiUnwrap(rawProps.startTop);
1174
+ const rawOffset = explicitStartTop != null && explicitStartTop !== ""
1175
+ ? (rect.top - getNumericValue(rawProps.startTop, 0)) * speed
1176
+ : (((typeof window !== "undefined" ? window.innerHeight || document.documentElement.clientHeight || 0 : 0) / 2) - (rect.top + (rect.height / 2))) * speed;
1177
+ const offset = maxOffset > 0 ? Math.max(-maxOffset, Math.min(maxOffset, rawOffset)) : rawOffset;
1178
+ const cssOffset = `${offset.toFixed(2)}px`;
1179
+ bg.style.transform = `translate3d(0, ${cssOffset}, 0)`;
1180
+ wrap.style.setProperty("--jss-parallax-offset", cssOffset);
1181
+ };
1182
+
1183
+ function scheduleUpdate() {
1184
+ if (disposed || ticking) return;
1185
+ ticking = true;
1186
+ requestAnimationFrame(() => {
1187
+ ticking = false;
1188
+ update();
1189
+ });
1190
+ }
1191
+
1192
+ if (typeof window !== "undefined") {
1193
+ window.addEventListener("scroll", scheduleUpdate, { passive: true });
1194
+ window.addEventListener("resize", scheduleUpdate);
1195
+ if (typeof ResizeObserver !== "undefined") {
1196
+ resizeObserver = new ResizeObserver(() => { scheduleUpdate(); });
1197
+ resizeObserver.observe(wrap);
1198
+ }
1199
+ requestAnimationFrame(() => { scheduleUpdate(); });
1200
+ setTimeout(() => { scheduleUpdate(); }, 90);
1201
+ }
1202
+
1203
+ wrap.refresh = () => {
1204
+ scheduleUpdate();
1205
+ return wrap;
1206
+ };
1207
+ wrap.update = wrap.refresh;
1208
+ wrap.destroy = () => {
1209
+ cleanup();
1210
+ wrap.remove();
1211
+ return null;
1212
+ };
1213
+ wrap._dispose = cleanup;
1214
+
1215
+ setPropertyProps(wrap, rawProps);
1216
+ return wrap;
1217
+ };
1218
+
1219
+
1220
+ // Example:
1221
+ // _.Parallax({ src: "/assets/hero.jpg", height: "280px", speed: 0.2 }, _.h2("Hello"));
1222
+
1223
+ // -------------------------------