wj-elements 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/light.css +6 -0
- package/dist/packages/wje-breadcrumb/breadcrumb.element.d.ts +202 -1
- package/dist/packages/wje-breadcrumbs/breadcrumbs.element.d.ts +70 -0
- package/dist/packages/wje-toolbar-action/toolbar-action.element.d.ts +6 -0
- package/dist/wje-breadcrumb.js +466 -41
- package/dist/wje-breadcrumb.js.map +1 -1
- package/dist/wje-breadcrumbs.js +152 -3
- package/dist/wje-breadcrumbs.js.map +1 -1
- package/dist/wje-menu-item.js +1 -1
- package/dist/wje-toolbar-action.js +22 -1
- package/dist/wje-toolbar-action.js.map +1 -1
- package/package.json +2 -2
package/dist/wje-breadcrumb.js
CHANGED
|
@@ -4,7 +4,15 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
import WJElement from "./wje-element.js";
|
|
5
5
|
import { WjElementUtils } from "./element-utils.js";
|
|
6
6
|
import { event } from "./event.js";
|
|
7
|
-
|
|
7
|
+
import "./wje-button.js";
|
|
8
|
+
import "./wje-dropdown.js";
|
|
9
|
+
import "./icon-CReYMzAK.js";
|
|
10
|
+
import "./wje-menu.js";
|
|
11
|
+
import "./wje-menu-item.js";
|
|
12
|
+
import "./wje-popup.js";
|
|
13
|
+
import "./wje-tooltip.js";
|
|
14
|
+
const styles = "/*\n[ WJ Breadcrumb ]\n*/\n\n:host {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n line-height: var(--wje-breadcrumb-line-height, 1.5);\n\n .native-breadcrumb {\n display: flex;\n align-items: center;\n width: 100%;\n outline: none;\n background: inherit;\n margin: var(--wje-breadcrumb-margin, var(--wje-breadcrumb-native-margin, 0));\n padding: var(--wje-breadcrumb-padding, var(--wje-breadcrumb-native-padding, 0.25rem 0.75rem));\n color: var(--wje-breadcrumb-a);\n text-decoration: none;\n font-size: var(--wje-breadcrumb-font-size);\n line-height: var(--wje-breadcrumb-native-line-height, var(--wje-breadcrumb-line-height, 1.5));\n &.hidden {\n display: none;\n }\n &.active {\n font-weight: var(--wje-breadcrumb-active-font-weight);\n font-size: var(--wje-breadcrumb-active-font-size);\n }\n &.disabled {\n cursor: default;\n opacity: 0.6;\n }\n &:hover {\n color: var(--wje-breadcrumb-a-hover);\n }\n }\n\n button {\n margin-inline: 0.75rem;\n border: 0 solid transparent;\n border-radius: 3px;\n background-color: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n }\n\n .separator {\n display: inline-flex;\n align-items: center;\n }\n}\n\n:host(.collapsed) {\n display: none;\n}\n\n:host([mobile-collapsed-indicator]) {\n flex: 1 1 auto;\n min-width: 0;\n max-width: 100%;\n overflow: hidden;\n}\n\n.breadcrumb-mobile {\n display: flex;\n align-items: center;\n min-width: 0;\n max-width: 100%;\n color: var(--wje-breadcrumb-a);\n font-size: var(--wje-breadcrumb-font-size);\n line-height: var(--wje-breadcrumb-line-height, 1.5);\n}\n\n.breadcrumb-mobile--text,\n.breadcrumb-mobile--back,\n.breadcrumb-mobile--parent-title {\n width: 100%;\n}\n\n.breadcrumb-mobile--back,\n.breadcrumb-mobile--parent-title {\n flex-direction: column;\n align-items: flex-start;\n gap: 0.125rem;\n}\n\n.breadcrumb-mobile-dropdown,\n.breadcrumb-mobile__menu {\n min-width: 0;\n max-width: 100%;\n}\n\n.breadcrumb-mobile__menu {\n --wje-button-margin-inline: 0;\n --wje-padding-top: 0;\n --wje-padding-bottom: 0;\n --wje-padding-start: 0;\n --wje-padding-end: 0;\n gap: 0.35rem;\n color: var(--wje-breadcrumb-a);\n}\n\n.breadcrumb-mobile__back,\n.breadcrumb-mobile__parent,\n.breadcrumb-mobile__title {\n min-width: 0;\n max-width: 100%;\n overflow: hidden;\n color: inherit;\n font: inherit;\n line-height: inherit;\n text-align: start;\n text-decoration: none;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.breadcrumb-mobile__back,\n.breadcrumb-mobile__parent {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0;\n border: 0;\n background: transparent;\n color: var(--wje-breadcrumb-a);\n cursor: default;\n font-size: var(--wje-breadcrumb-mobile-parent-font-size, 0.8125rem);\n opacity: 0.72;\n}\n\nbutton.breadcrumb-mobile__back,\nbutton.breadcrumb-mobile__parent,\nbutton.breadcrumb-mobile__title {\n margin-inline: 0;\n cursor: pointer;\n}\n\nbutton.breadcrumb-mobile__back:hover,\nbutton.breadcrumb-mobile__parent:hover,\nbutton.breadcrumb-mobile__title:hover,\n.breadcrumb-mobile__menu:hover {\n color: var(--wje-breadcrumb-a-hover);\n}\n\n.breadcrumb-mobile__title {\n display: block;\n font-weight: var(--wje-breadcrumb-active-font-weight, 600);\n font-size: var(--wje-breadcrumb-active-font-size, inherit);\n}\n\n.breadcrumb-mobile__label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.breadcrumb-mobile__icon {\n flex: 0 0 auto;\n}\n\n::slotted([slot='start']) {\n margin-inline: 0 0.5rem;\n}\n\n::slotted([slot='end']) {\n margin-inline: 0.5rem 0;\n}\n";
|
|
15
|
+
const MOBILE_COLLAPSED_VARIANTS = /* @__PURE__ */ new Set(["TEXT", "BACK", "PARENT-TITLE", "MENU-TITLE", "SHEET"]);
|
|
8
16
|
class Breadcrumb extends WJElement {
|
|
9
17
|
/**
|
|
10
18
|
* Breadcrumb constructor method.
|
|
@@ -79,9 +87,15 @@ class Breadcrumb extends WJElement {
|
|
|
79
87
|
* @returns {string} The collapsed variant value in uppercase.
|
|
80
88
|
*/
|
|
81
89
|
get collapsedVariant() {
|
|
90
|
+
return this.collapsedVariantName.toUpperCase();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get collapsed variant token.
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
get collapsedVariantName() {
|
|
82
97
|
var _a, _b;
|
|
83
|
-
|
|
84
|
-
return variant.toUpperCase();
|
|
98
|
+
return String(((_a = this.parentElement) == null ? void 0 : _a.collapsedVariant) || ((_b = this.parentElement) == null ? void 0 : _b.variant) || this._collapsedVariant || "button").trim();
|
|
85
99
|
}
|
|
86
100
|
/**
|
|
87
101
|
* Get CSS stylesheet for the Breadcrumb element.
|
|
@@ -97,7 +111,7 @@ class Breadcrumb extends WJElement {
|
|
|
97
111
|
* @returns {Array<string>} - The observed attributes array for the Breadcrumb element.
|
|
98
112
|
*/
|
|
99
113
|
static get observedAttributes() {
|
|
100
|
-
return ["show-collapsed-indicator", "collapsed", "last", "href", "target", "rel", "download", "disabled"];
|
|
114
|
+
return ["show-collapsed-indicator", "collapsed", "last", "href", "target", "rel", "download", "disabled", "title", "aria-label"];
|
|
101
115
|
}
|
|
102
116
|
/**
|
|
103
117
|
* Handles attribute changes for the custom element and updates its behavior or appearance accordingly.
|
|
@@ -115,6 +129,7 @@ class Breadcrumb extends WJElement {
|
|
|
115
129
|
(_a = super.attributeChangedCallback) == null ? void 0 : _a.call(this, name, oldValue, newValue);
|
|
116
130
|
const isOn = newValue !== null && WjElementUtils.stringToBoolean(newValue);
|
|
117
131
|
this._showCollapsedIndicator = isOn;
|
|
132
|
+
this.classList.toggle("collapsed", this.hasAttribute("collapsed") && !isOn);
|
|
118
133
|
this.refresh();
|
|
119
134
|
} else if (name === "last") {
|
|
120
135
|
(_b = super.attributeChangedCallback) == null ? void 0 : _b.call(this, name, oldValue, newValue);
|
|
@@ -165,7 +180,7 @@ class Breadcrumb extends WJElement {
|
|
|
165
180
|
fragment.append(this.drawCollapsedIndicator());
|
|
166
181
|
native.classList.add("hidden");
|
|
167
182
|
}
|
|
168
|
-
if (this.
|
|
183
|
+
if (this.shouldRenderSeparator()) {
|
|
169
184
|
let separator = document.createElement("span");
|
|
170
185
|
separator.classList.add("separator");
|
|
171
186
|
separator.setAttribute("part", "separator");
|
|
@@ -180,6 +195,34 @@ class Breadcrumb extends WJElement {
|
|
|
180
195
|
}
|
|
181
196
|
return fragment;
|
|
182
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Returns whether the separator should be visible after this breadcrumb.
|
|
200
|
+
* @returns {boolean}
|
|
201
|
+
*/
|
|
202
|
+
shouldRenderSeparator() {
|
|
203
|
+
return this.showSeparator && this.hasVisibleBreadcrumbAfter();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Returns whether any later breadcrumb is still visible in the rendered trail.
|
|
207
|
+
* @returns {boolean}
|
|
208
|
+
*/
|
|
209
|
+
hasVisibleBreadcrumbAfter() {
|
|
210
|
+
var _a, _b;
|
|
211
|
+
const breadcrumbs = (_b = (_a = this.parentElement) == null ? void 0 : _a.getBreadcrumbs) == null ? void 0 : _b.call(_a);
|
|
212
|
+
if (!Array.isArray(breadcrumbs)) return true;
|
|
213
|
+
const index = breadcrumbs.indexOf(this);
|
|
214
|
+
if (index === -1) return true;
|
|
215
|
+
return breadcrumbs.slice(index + 1).some((breadcrumb) => this.isBreadcrumbVisibleInTrail(breadcrumb));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Returns whether a breadcrumb is visible in the rendered trail.
|
|
219
|
+
* @param {Element} breadcrumb Breadcrumb element to inspect.
|
|
220
|
+
* @returns {boolean}
|
|
221
|
+
*/
|
|
222
|
+
isBreadcrumbVisibleInTrail(breadcrumb) {
|
|
223
|
+
var _a, _b;
|
|
224
|
+
return !(((_a = breadcrumb == null ? void 0 : breadcrumb.hasAttribute) == null ? void 0 : _a.call(breadcrumb, "collapsed")) && !((_b = breadcrumb == null ? void 0 : breadcrumb.hasAttribute) == null ? void 0 : _b.call(breadcrumb, "show-collapsed-indicator")));
|
|
225
|
+
}
|
|
183
226
|
/**
|
|
184
227
|
* Synchronizes host attributes to the internal anchor.
|
|
185
228
|
*/
|
|
@@ -201,6 +244,13 @@ class Breadcrumb extends WJElement {
|
|
|
201
244
|
native.removeAttribute(attr);
|
|
202
245
|
}
|
|
203
246
|
});
|
|
247
|
+
["title", "aria-label"].forEach((attr) => {
|
|
248
|
+
if (this.hasAttribute(attr)) {
|
|
249
|
+
native.setAttribute(attr, this.getAttribute(attr));
|
|
250
|
+
} else {
|
|
251
|
+
native.removeAttribute(attr);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
204
254
|
native.classList.toggle("active", Boolean(this.active));
|
|
205
255
|
native.classList.toggle("disabled", isDisabled);
|
|
206
256
|
if (this.active) native.setAttribute("aria-current", "page");
|
|
@@ -241,17 +291,25 @@ class Breadcrumb extends WJElement {
|
|
|
241
291
|
/**
|
|
242
292
|
* Renders the collapsed indicator based on the current collapsed variant.
|
|
243
293
|
* If the collapsed variant is 'DROPDOWN', it invokes the collapseDropdown method.
|
|
244
|
-
*
|
|
294
|
+
* Mobile breakpoint variants render compact breadcrumb summaries, otherwise the default button is used.
|
|
245
295
|
* @returns {any} The rendered collapsed indicator, either as a dropdown or a button.
|
|
246
296
|
*/
|
|
247
297
|
drawCollapsedIndicator() {
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
298
|
+
const variant = this.collapsedVariant;
|
|
299
|
+
if (variant === "DROPDOWN") {
|
|
300
|
+
return this.collapseDropdown();
|
|
301
|
+
}
|
|
302
|
+
if (this.isMobileCollapsedVariant() && this.isBreakpointMenuIndicator()) {
|
|
303
|
+
return this.collapseMobileVariant(variant);
|
|
253
304
|
}
|
|
254
|
-
return
|
|
305
|
+
return this.collapseButton();
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Returns whether the current collapsed variant is a compact mobile layout.
|
|
309
|
+
* @returns {boolean}
|
|
310
|
+
*/
|
|
311
|
+
isMobileCollapsedVariant() {
|
|
312
|
+
return MOBILE_COLLAPSED_VARIANTS.has(this.collapsedVariant);
|
|
255
313
|
}
|
|
256
314
|
/**
|
|
257
315
|
* Creates and returns a dropdown UI component for collapsed breadcrumbs.
|
|
@@ -262,51 +320,418 @@ class Breadcrumb extends WJElement {
|
|
|
262
320
|
* @returns {HTMLElement} A configured dropdown element containing a button as trigger and a menu with breadcrumb items.
|
|
263
321
|
*/
|
|
264
322
|
collapseDropdown() {
|
|
265
|
-
|
|
266
|
-
|
|
323
|
+
const isBreakpointMenuIndicator = this.isBreakpointMenuIndicator();
|
|
324
|
+
const breadcrumbs = this.getCollapsedMenuBreadcrumbs(isBreakpointMenuIndicator);
|
|
325
|
+
const trigger = this.createCollapsedDropdownTrigger(isBreakpointMenuIndicator);
|
|
326
|
+
return this.createBreadcrumbDropdown({
|
|
327
|
+
breadcrumbs,
|
|
328
|
+
trigger,
|
|
329
|
+
placement: isBreakpointMenuIndicator ? "bottom-start" : "bottom"
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Creates a dropdown containing breadcrumb menu items.
|
|
334
|
+
* @param {object} options Configuration for the dropdown shell.
|
|
335
|
+
* @param {Array<HTMLElement>} options.breadcrumbs Breadcrumb elements to mirror in the menu.
|
|
336
|
+
* @param {HTMLElement} options.trigger Element assigned to the dropdown trigger slot.
|
|
337
|
+
* @param {string} options.placement Floating UI placement token for the popup.
|
|
338
|
+
* @returns {HTMLElement}
|
|
339
|
+
*/
|
|
340
|
+
createBreadcrumbDropdown({ breadcrumbs, trigger, placement = "bottom" }) {
|
|
341
|
+
const dropdown = document.createElement("wje-dropdown");
|
|
342
|
+
dropdown.setAttribute("placement", placement);
|
|
267
343
|
dropdown.setAttribute("offset", "10");
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
344
|
+
const menu = this.createCollapsedBreadcrumbMenu(breadcrumbs);
|
|
345
|
+
dropdown.append(trigger, menu);
|
|
346
|
+
return dropdown;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Creates the menu used by collapsed dropdown variants.
|
|
350
|
+
* @param {Array<HTMLElement>} breadcrumbs Breadcrumb items represented by menu items.
|
|
351
|
+
* @returns {HTMLElement}
|
|
352
|
+
*/
|
|
353
|
+
createCollapsedBreadcrumbMenu(breadcrumbs) {
|
|
354
|
+
const menu = document.createElement("wje-menu");
|
|
273
355
|
menu.setAttribute("variant", "context");
|
|
356
|
+
menu.style.setProperty("--wje-menu-item-justify-content", "flex-start");
|
|
274
357
|
menu.addEventListener("click", (e) => {
|
|
275
358
|
var _a;
|
|
276
359
|
e.stopPropagation();
|
|
277
360
|
(_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e);
|
|
278
361
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
menuItem
|
|
282
|
-
menuItem.__breadcrumb =
|
|
362
|
+
breadcrumbs.forEach((breadcrumb) => {
|
|
363
|
+
const menuItem = document.createElement("wje-menu-item");
|
|
364
|
+
this.populateCollapsedMenuItem(menuItem, breadcrumb);
|
|
365
|
+
menuItem.__breadcrumb = breadcrumb;
|
|
283
366
|
menuItem.addEventListener("wje-menu-item:click", (e) => {
|
|
284
|
-
var _a, _b
|
|
367
|
+
var _a, _b;
|
|
285
368
|
(_a = e.preventDefault) == null ? void 0 : _a.call(e);
|
|
286
369
|
e.stopPropagation();
|
|
287
370
|
(_b = e.stopImmediatePropagation) == null ? void 0 : _b.call(e);
|
|
288
|
-
|
|
289
|
-
if (!breadcrumb) return;
|
|
290
|
-
const native = breadcrumb.native || ((_c = breadcrumb.context) == null ? void 0 : _c.querySelector("a.native-breadcrumb"));
|
|
291
|
-
if (native && typeof native.click === "function") {
|
|
292
|
-
native.click();
|
|
293
|
-
} else if (typeof breadcrumb.click === "function") {
|
|
294
|
-
breadcrumb.click();
|
|
295
|
-
} else {
|
|
296
|
-
breadcrumb.dispatchEvent(
|
|
297
|
-
new MouseEvent("click", {
|
|
298
|
-
bubbles: true,
|
|
299
|
-
composed: true,
|
|
300
|
-
cancelable: true
|
|
301
|
-
})
|
|
302
|
-
);
|
|
303
|
-
}
|
|
371
|
+
this.forwardBreadcrumbClick(e.currentTarget.__breadcrumb);
|
|
304
372
|
});
|
|
305
373
|
menu.append(menuItem);
|
|
306
374
|
});
|
|
307
|
-
|
|
375
|
+
return menu;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Returns breadcrumbs that should be exposed in a collapsed menu.
|
|
379
|
+
* @param {boolean} isBreakpointMenuIndicator Whether this trigger controls the full breakpoint menu.
|
|
380
|
+
* @returns {Array<HTMLElement>}
|
|
381
|
+
*/
|
|
382
|
+
getCollapsedMenuBreadcrumbs(isBreakpointMenuIndicator) {
|
|
383
|
+
var _a, _b;
|
|
384
|
+
const breadcrumbs = this.getBreadcrumbs();
|
|
385
|
+
if (isBreakpointMenuIndicator) {
|
|
386
|
+
return ((_a = breadcrumbs == null ? void 0 : breadcrumbs.getBreadcrumbs) == null ? void 0 : _a.call(breadcrumbs)) || [];
|
|
387
|
+
}
|
|
388
|
+
return ((_b = breadcrumbs == null ? void 0 : breadcrumbs.getBreadcrumbsCollapsed) == null ? void 0 : _b.call(breadcrumbs)) || [];
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Renders one of the compact mobile breakpoint variants.
|
|
392
|
+
* @param {string} variant Normalized uppercase variant token.
|
|
393
|
+
* @returns {HTMLElement}
|
|
394
|
+
*/
|
|
395
|
+
collapseMobileVariant(variant) {
|
|
396
|
+
if (variant === "TEXT") return this.collapseMobileText();
|
|
397
|
+
if (variant === "BACK") return this.collapseMobileBack();
|
|
398
|
+
if (variant === "PARENT-TITLE") return this.collapseMobileParentTitle();
|
|
399
|
+
if (variant === "MENU-TITLE") return this.collapseMobileMenuTitle();
|
|
400
|
+
if (variant === "SHEET") return this.collapseMobileSheet();
|
|
401
|
+
return this.collapseButton();
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Renders only the current breadcrumb title.
|
|
405
|
+
* @returns {HTMLElement}
|
|
406
|
+
*/
|
|
407
|
+
collapseMobileText() {
|
|
408
|
+
const current = this.getCurrentBreadcrumb();
|
|
409
|
+
const container = this.createMobileContainer("breadcrumb-mobile--text");
|
|
410
|
+
const title = this.createMobileBreadcrumbControl(current, "breadcrumb-mobile__title", {
|
|
411
|
+
ariaCurrent: true,
|
|
412
|
+
actionable: this.isBreadcrumbActionable(current)
|
|
413
|
+
});
|
|
414
|
+
container.append(title);
|
|
415
|
+
return container;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Renders a parent back action above the current title.
|
|
419
|
+
* @returns {HTMLElement}
|
|
420
|
+
*/
|
|
421
|
+
collapseMobileBack() {
|
|
422
|
+
const current = this.getCurrentBreadcrumb();
|
|
423
|
+
const parent = this.getParentBreadcrumb();
|
|
424
|
+
const container = this.createMobileContainer("breadcrumb-mobile--back");
|
|
425
|
+
if (parent) {
|
|
426
|
+
const back = this.createMobileBreadcrumbControl(parent, "breadcrumb-mobile__back", {
|
|
427
|
+
ariaLabel: "Go to parent",
|
|
428
|
+
icon: "arrow-left",
|
|
429
|
+
actionable: this.isBreadcrumbActionable(parent)
|
|
430
|
+
});
|
|
431
|
+
container.append(back);
|
|
432
|
+
}
|
|
433
|
+
const title = this.createMobileBreadcrumbControl(current, "breadcrumb-mobile__title", {
|
|
434
|
+
ariaCurrent: true,
|
|
435
|
+
actionable: false
|
|
436
|
+
});
|
|
437
|
+
container.append(title);
|
|
438
|
+
return container;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Renders parent context above the current title.
|
|
442
|
+
* @returns {HTMLElement}
|
|
443
|
+
*/
|
|
444
|
+
collapseMobileParentTitle() {
|
|
445
|
+
const current = this.getCurrentBreadcrumb();
|
|
446
|
+
const parent = this.getParentBreadcrumb();
|
|
447
|
+
const container = this.createMobileContainer("breadcrumb-mobile--parent-title");
|
|
448
|
+
if (parent) {
|
|
449
|
+
const parentControl = this.createMobileBreadcrumbControl(parent, "breadcrumb-mobile__parent", {
|
|
450
|
+
actionable: this.isBreadcrumbActionable(parent)
|
|
451
|
+
});
|
|
452
|
+
container.append(parentControl);
|
|
453
|
+
}
|
|
454
|
+
const title = this.createMobileBreadcrumbControl(current, "breadcrumb-mobile__title", {
|
|
455
|
+
ariaCurrent: true,
|
|
456
|
+
actionable: false
|
|
457
|
+
});
|
|
458
|
+
container.append(title);
|
|
459
|
+
return container;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Renders a menu trigger with the current title.
|
|
463
|
+
* @returns {HTMLElement}
|
|
464
|
+
*/
|
|
465
|
+
collapseMobileMenuTitle() {
|
|
466
|
+
const trigger = this.createMobileDropdownTrigger({
|
|
467
|
+
icon: "dots",
|
|
468
|
+
label: this.getBreadcrumbLabel(this.getCurrentBreadcrumb()),
|
|
469
|
+
ariaLabel: "Show breadcrumb menu",
|
|
470
|
+
className: "breadcrumb-mobile--menu-title"
|
|
471
|
+
});
|
|
472
|
+
return this.createMobileDropdown(trigger);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Renders the future sheet variant using the dropdown fallback for now.
|
|
476
|
+
* @returns {HTMLElement}
|
|
477
|
+
*/
|
|
478
|
+
collapseMobileSheet() {
|
|
479
|
+
const trigger = this.createMobileDropdownTrigger({
|
|
480
|
+
icon: "chevron-down",
|
|
481
|
+
iconPosition: "end",
|
|
482
|
+
label: this.getBreadcrumbLabel(this.getCurrentBreadcrumb()),
|
|
483
|
+
ariaLabel: "Show breadcrumb path",
|
|
484
|
+
className: "breadcrumb-mobile--sheet"
|
|
485
|
+
});
|
|
486
|
+
return this.createMobileDropdown(trigger);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Creates a dropdown fallback for mobile menu-like variants.
|
|
490
|
+
* @param {HTMLElement} trigger Element that opens the fallback dropdown.
|
|
491
|
+
* @returns {HTMLElement}
|
|
492
|
+
*/
|
|
493
|
+
createMobileDropdown(trigger) {
|
|
494
|
+
const dropdown = this.createBreadcrumbDropdown({
|
|
495
|
+
breadcrumbs: this.getTrailBreadcrumbs(),
|
|
496
|
+
trigger,
|
|
497
|
+
placement: "bottom-start"
|
|
498
|
+
});
|
|
499
|
+
dropdown.classList.add("breadcrumb-mobile-dropdown");
|
|
308
500
|
return dropdown;
|
|
309
501
|
}
|
|
502
|
+
/**
|
|
503
|
+
* Creates a compact mobile dropdown trigger.
|
|
504
|
+
* @param {object} options Visual settings for the compact trigger.
|
|
505
|
+
* @param {string} options.icon Icon shown next to the title.
|
|
506
|
+
* @param {string} options.iconPosition Whether the icon renders at the start or end.
|
|
507
|
+
* @param {string} options.label Current breadcrumb label shown beside the icon.
|
|
508
|
+
* @param {string} options.ariaLabel Accessible name for the dropdown trigger.
|
|
509
|
+
* @param {string} options.className Variant-specific class applied to the trigger.
|
|
510
|
+
* @returns {HTMLElement}
|
|
511
|
+
*/
|
|
512
|
+
createMobileDropdownTrigger({ icon, iconPosition = "start", label, ariaLabel, className }) {
|
|
513
|
+
const trigger = document.createElement("wje-button");
|
|
514
|
+
trigger.setAttribute("slot", "trigger");
|
|
515
|
+
trigger.setAttribute("fill", "link");
|
|
516
|
+
trigger.setAttribute("aria-label", ariaLabel);
|
|
517
|
+
trigger.classList.add("breadcrumb-mobile", "breadcrumb-mobile__menu", className);
|
|
518
|
+
const iconElement = document.createElement("wje-icon");
|
|
519
|
+
iconElement.setAttribute("name", icon);
|
|
520
|
+
iconElement.setAttribute("slot", iconPosition === "end" ? "end" : "start");
|
|
521
|
+
iconElement.classList.add("breadcrumb-mobile__icon");
|
|
522
|
+
const title = document.createElement("span");
|
|
523
|
+
title.classList.add("breadcrumb-mobile__title");
|
|
524
|
+
title.setAttribute("aria-current", "page");
|
|
525
|
+
title.textContent = label;
|
|
526
|
+
if (label) title.setAttribute("title", label);
|
|
527
|
+
if (iconPosition === "end") {
|
|
528
|
+
trigger.append(title, iconElement);
|
|
529
|
+
} else {
|
|
530
|
+
trigger.append(iconElement, title);
|
|
531
|
+
}
|
|
532
|
+
return trigger;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Creates a wrapper for compact mobile breadcrumb variants.
|
|
536
|
+
* @param {string} className Variant class.
|
|
537
|
+
* @returns {HTMLElement}
|
|
538
|
+
*/
|
|
539
|
+
createMobileContainer(className) {
|
|
540
|
+
const container = document.createElement("div");
|
|
541
|
+
container.classList.add("breadcrumb-mobile", className);
|
|
542
|
+
return container;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Creates a text or button control for a breadcrumb.
|
|
546
|
+
* @param {HTMLElement|null} breadcrumb Source breadcrumb.
|
|
547
|
+
* @param {string} className Class applied to the created control.
|
|
548
|
+
* @param {object} options Behavior and accessibility settings for the control.
|
|
549
|
+
* @param {boolean} options.actionable Whether clicks should forward to the source breadcrumb.
|
|
550
|
+
* @param {boolean} options.ariaCurrent Whether the control marks the current page.
|
|
551
|
+
* @param {string} options.ariaLabel Accessible name for action controls.
|
|
552
|
+
* @param {string} options.icon Optional icon shown before the label.
|
|
553
|
+
* @returns {HTMLElement}
|
|
554
|
+
*/
|
|
555
|
+
createMobileBreadcrumbControl(breadcrumb, className, { actionable = false, ariaCurrent = false, ariaLabel, icon } = {}) {
|
|
556
|
+
const label = this.getBreadcrumbLabel(breadcrumb);
|
|
557
|
+
const control = document.createElement(actionable ? "button" : "span");
|
|
558
|
+
control.classList.add(className);
|
|
559
|
+
if (actionable) {
|
|
560
|
+
control.setAttribute("type", "button");
|
|
561
|
+
control.addEventListener("click", (e) => {
|
|
562
|
+
var _a;
|
|
563
|
+
e.preventDefault();
|
|
564
|
+
e.stopPropagation();
|
|
565
|
+
(_a = e.stopImmediatePropagation) == null ? void 0 : _a.call(e);
|
|
566
|
+
this.forwardBreadcrumbClick(breadcrumb);
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
if (ariaCurrent) control.setAttribute("aria-current", "page");
|
|
570
|
+
if (ariaLabel && actionable) control.setAttribute("aria-label", ariaLabel);
|
|
571
|
+
if (label) control.setAttribute("title", label);
|
|
572
|
+
if (icon) {
|
|
573
|
+
const iconElement = document.createElement("wje-icon");
|
|
574
|
+
iconElement.setAttribute("name", icon);
|
|
575
|
+
iconElement.classList.add("breadcrumb-mobile__icon");
|
|
576
|
+
control.append(iconElement);
|
|
577
|
+
}
|
|
578
|
+
const text = document.createElement("span");
|
|
579
|
+
text.classList.add("breadcrumb-mobile__label");
|
|
580
|
+
text.textContent = label;
|
|
581
|
+
control.append(text);
|
|
582
|
+
return control;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Returns the full trail of sibling breadcrumbs.
|
|
586
|
+
* @returns {Array<HTMLElement>}
|
|
587
|
+
*/
|
|
588
|
+
getTrailBreadcrumbs() {
|
|
589
|
+
var _a, _b;
|
|
590
|
+
return ((_b = (_a = this.getBreadcrumbs()) == null ? void 0 : _a.getBreadcrumbs) == null ? void 0 : _b.call(_a)) || [];
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Returns the current breadcrumb item.
|
|
594
|
+
* @returns {HTMLElement|null}
|
|
595
|
+
*/
|
|
596
|
+
getCurrentBreadcrumb() {
|
|
597
|
+
const breadcrumbs = this.getTrailBreadcrumbs();
|
|
598
|
+
return breadcrumbs[breadcrumbs.length - 1] || null;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Returns the parent breadcrumb item.
|
|
602
|
+
* @returns {HTMLElement|null}
|
|
603
|
+
*/
|
|
604
|
+
getParentBreadcrumb() {
|
|
605
|
+
const breadcrumbs = this.getTrailBreadcrumbs();
|
|
606
|
+
return breadcrumbs.length > 1 ? breadcrumbs[breadcrumbs.length - 2] : null;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Returns whether a breadcrumb has a known action to forward.
|
|
610
|
+
* @param {HTMLElement|null} breadcrumb Breadcrumb to inspect.
|
|
611
|
+
* @returns {boolean}
|
|
612
|
+
*/
|
|
613
|
+
isBreadcrumbActionable(breadcrumb) {
|
|
614
|
+
var _a, _b, _c, _d;
|
|
615
|
+
if (!breadcrumb || breadcrumb.disabled || ((_a = breadcrumb.hasAttribute) == null ? void 0 : _a.call(breadcrumb, "disabled"))) return false;
|
|
616
|
+
return Boolean(
|
|
617
|
+
((_b = breadcrumb.hasAttribute) == null ? void 0 : _b.call(breadcrumb, "href")) || ((_c = breadcrumb.hasAttribute) == null ? void 0 : _c.call(breadcrumb, "onclick")) || breadcrumb.__wjeBreadcrumbManaged || ((_d = breadcrumb.hasAttribute) == null ? void 0 : _d.call(breadcrumb, "data-wje-breadcrumbs-item"))
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Forwards a compact control or menu item click to the original breadcrumb.
|
|
622
|
+
* @param {HTMLElement|null} breadcrumb Source breadcrumb.
|
|
623
|
+
*/
|
|
624
|
+
forwardBreadcrumbClick(breadcrumb) {
|
|
625
|
+
var _a;
|
|
626
|
+
if (!breadcrumb) return;
|
|
627
|
+
const native = breadcrumb.native || ((_a = breadcrumb.context) == null ? void 0 : _a.querySelector("a.native-breadcrumb"));
|
|
628
|
+
if (native && typeof native.click === "function") {
|
|
629
|
+
native.click();
|
|
630
|
+
} else if (typeof breadcrumb.click === "function") {
|
|
631
|
+
breadcrumb.click();
|
|
632
|
+
} else {
|
|
633
|
+
breadcrumb.dispatchEvent(
|
|
634
|
+
new MouseEvent("click", {
|
|
635
|
+
bubbles: true,
|
|
636
|
+
composed: true,
|
|
637
|
+
cancelable: true
|
|
638
|
+
})
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Creates the dropdown trigger for collapsed breadcrumbs.
|
|
644
|
+
* @param {boolean} isBreakpointMenuIndicator Whether this trigger controls the full breakpoint menu.
|
|
645
|
+
* @returns {HTMLElement}
|
|
646
|
+
*/
|
|
647
|
+
createCollapsedDropdownTrigger(isBreakpointMenuIndicator) {
|
|
648
|
+
var _a, _b;
|
|
649
|
+
if (isBreakpointMenuIndicator) {
|
|
650
|
+
const customTrigger = (_b = (_a = this.getBreadcrumbs()) == null ? void 0 : _a.getBreakpointCollapseTrigger) == null ? void 0 : _b.call(_a);
|
|
651
|
+
if (customTrigger) {
|
|
652
|
+
const trigger = customTrigger.cloneNode(true);
|
|
653
|
+
trigger.setAttribute("slot", "trigger");
|
|
654
|
+
if (!trigger.hasAttribute("aria-label")) {
|
|
655
|
+
trigger.setAttribute("aria-label", "Show breadcrumb menu");
|
|
656
|
+
}
|
|
657
|
+
return trigger;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
let button = document.createElement("wje-button");
|
|
661
|
+
button.setAttribute("slot", "trigger");
|
|
662
|
+
button.setAttribute("fill", "link");
|
|
663
|
+
button.setAttribute("aria-label", "Show more breadcrumbs");
|
|
664
|
+
button.innerHTML = `<wje-icon name="${this.getCollapsedDropdownIcon(isBreakpointMenuIndicator)}"></wje-icon>`;
|
|
665
|
+
return button;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Resolves the icon used by the default dropdown trigger.
|
|
669
|
+
* @param {boolean} isBreakpointMenuIndicator Whether this trigger controls the full breakpoint menu.
|
|
670
|
+
* @returns {string}
|
|
671
|
+
*/
|
|
672
|
+
getCollapsedDropdownIcon(isBreakpointMenuIndicator) {
|
|
673
|
+
var _a;
|
|
674
|
+
if (!isBreakpointMenuIndicator) return "dots";
|
|
675
|
+
return ((_a = this.getBreadcrumbs()) == null ? void 0 : _a.breakpointCollapseIcon) || "menu";
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Copies breadcrumb content into a collapsed menu item.
|
|
679
|
+
* @param {HTMLElement} menuItem Menu item receiving the content.
|
|
680
|
+
* @param {HTMLElement} breadcrumb Source breadcrumb.
|
|
681
|
+
*/
|
|
682
|
+
populateCollapsedMenuItem(menuItem, breadcrumb) {
|
|
683
|
+
const nodes = Array.from(breadcrumb.childNodes).map((node) => node.cloneNode(true));
|
|
684
|
+
menuItem.replaceChildren(...nodes);
|
|
685
|
+
if (menuItem.textContent.trim()) return;
|
|
686
|
+
const label = this.getBreadcrumbLabel(breadcrumb);
|
|
687
|
+
if (label) {
|
|
688
|
+
menuItem.append(document.createTextNode(label));
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Resolves a readable label for a breadcrumb item.
|
|
693
|
+
* @param {HTMLElement|null} breadcrumb Source breadcrumb.
|
|
694
|
+
* @returns {string}
|
|
695
|
+
*/
|
|
696
|
+
getBreadcrumbLabel(breadcrumb) {
|
|
697
|
+
if (!breadcrumb) return "";
|
|
698
|
+
return this.getCollapsedMenuItemLabel(breadcrumb);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Resolves a readable label for icon-only collapsed menu items.
|
|
702
|
+
* @param {HTMLElement} breadcrumb Source breadcrumb.
|
|
703
|
+
* @returns {string}
|
|
704
|
+
*/
|
|
705
|
+
getCollapsedMenuItemLabel(breadcrumb) {
|
|
706
|
+
var _a;
|
|
707
|
+
const explicitLabel = breadcrumb.getAttribute("aria-label") || breadcrumb.getAttribute("title");
|
|
708
|
+
if (explicitLabel == null ? void 0 : explicitLabel.trim()) return explicitLabel.trim();
|
|
709
|
+
const text = breadcrumb.textContent.trim();
|
|
710
|
+
if (text) return text;
|
|
711
|
+
const iconName = (_a = breadcrumb.querySelector('wje-icon[slot="start"]')) == null ? void 0 : _a.getAttribute("name");
|
|
712
|
+
if (iconName === "home") return "Home";
|
|
713
|
+
const href = breadcrumb.getAttribute("href");
|
|
714
|
+
const path = (href == null ? void 0 : href.split(/[?#]/)[0]) || "";
|
|
715
|
+
const segment = path.split("/").filter(Boolean).pop();
|
|
716
|
+
return segment ? this.humanizeCollapsedMenuItemLabel(segment) : "";
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Turns a URL segment into a readable menu label.
|
|
720
|
+
* @param {string} value URL segment.
|
|
721
|
+
* @returns {string}
|
|
722
|
+
*/
|
|
723
|
+
humanizeCollapsedMenuItemLabel(value) {
|
|
724
|
+
return decodeURIComponent(value).replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Returns whether this collapsed indicator represents the full breakpoint menu.
|
|
728
|
+
* @returns {boolean}
|
|
729
|
+
*/
|
|
730
|
+
isBreakpointMenuIndicator() {
|
|
731
|
+
var _a;
|
|
732
|
+
const breadcrumbs = this.parentElement;
|
|
733
|
+
return this.hasAttribute("show-collapsed-indicator") && this.hasAttribute("collapsed") && ((_a = breadcrumbs == null ? void 0 : breadcrumbs.isBreakpointMenuCollapseActive) == null ? void 0 : _a.call(breadcrumbs));
|
|
734
|
+
}
|
|
310
735
|
/**
|
|
311
736
|
* Creates a button element that expands hidden breadcrumbs when clicked.
|
|
312
737
|
* The button is set with appropriate attributes and event listeners to handle
|