@waggylabs/yumekit 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/components/y-appbar/y-appbar.d.ts +22 -15
- package/dist/components/y-appbar.d.ts +22 -15
- package/dist/components/y-appbar.js +659 -454
- package/dist/components/y-badge/y-badge.d.ts +1 -1
- package/dist/components/y-badge.d.ts +1 -1
- package/dist/components/y-badge.js +31 -15
- package/dist/components/y-menu/y-menu.d.ts +57 -1
- package/dist/components/y-menu.d.ts +57 -1
- package/dist/components/y-menu.js +477 -158
- package/dist/index.js +693 -462
- package/dist/yumekit.min.js +1 -1
- package/llm.txt +43 -12
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { contrastTextColor, resolveAnchor } from '../../modules/helpers.js';
|
|
1
|
+
import { contrastTextColor, createElement, resolveAnchor } from '../../modules/helpers.js';
|
|
2
2
|
import { getIcon } from '../../icons/registry.js';
|
|
3
3
|
|
|
4
4
|
class YumeButton extends HTMLElement {
|
|
@@ -782,16 +782,6 @@ if (!customElements.get("y-icon")) {
|
|
|
782
782
|
customElements.define("y-icon", YumeIcon);
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
var chevronRight = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"9 18 15 12 9 6\"/>\n</svg>\n";
|
|
786
|
-
|
|
787
|
-
var chevronDown = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\"/>\n</svg>\n";
|
|
788
|
-
|
|
789
|
-
var expandLeft = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"11 17 6 12 11 7\"/>\n <polyline points=\"18 17 13 12 18 7\"/>\n</svg>\n";
|
|
790
|
-
|
|
791
|
-
var expandRight = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"13 17 18 12 13 7\"/>\n <polyline points=\"6 17 11 12 6 7\"/>\n</svg>\n";
|
|
792
|
-
|
|
793
|
-
var menu = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/>\n <line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/>\n <line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/>\n</svg>\n";
|
|
794
|
-
|
|
795
785
|
class YumeMenu extends HTMLElement {
|
|
796
786
|
static get observedAttributes() {
|
|
797
787
|
return ["items", "anchor", "visible", "direction", "size", "history"];
|
|
@@ -807,6 +797,8 @@ class YumeMenu extends HTMLElement {
|
|
|
807
797
|
this._onAnchorClick = this._onAnchorClick.bind(this);
|
|
808
798
|
this._onDocumentClick = this._onDocumentClick.bind(this);
|
|
809
799
|
this._onScrollOrResize = this._onScrollOrResize.bind(this);
|
|
800
|
+
this._isReady = false;
|
|
801
|
+
this._slottedHandlers = new Map();
|
|
810
802
|
}
|
|
811
803
|
|
|
812
804
|
connectedCallback() {
|
|
@@ -823,10 +815,13 @@ class YumeMenu extends HTMLElement {
|
|
|
823
815
|
this.style.zIndex = "1000";
|
|
824
816
|
this.style.display = "none";
|
|
825
817
|
if (this.visible) this._updatePosition();
|
|
818
|
+
|
|
819
|
+
this._isReady = true;
|
|
826
820
|
}
|
|
827
821
|
|
|
828
822
|
disconnectedCallback() {
|
|
829
823
|
this._teardownAnchor();
|
|
824
|
+
this._teardownSlottedItems();
|
|
830
825
|
|
|
831
826
|
document.removeEventListener("click", this._onDocumentClick);
|
|
832
827
|
window.removeEventListener("scroll", this._onScrollOrResize, true);
|
|
@@ -846,6 +841,13 @@ class YumeMenu extends HTMLElement {
|
|
|
846
841
|
if (name === "visible" || name === "direction") {
|
|
847
842
|
this._updatePosition();
|
|
848
843
|
}
|
|
844
|
+
|
|
845
|
+
if (name === "visible" && this._isReady) {
|
|
846
|
+
this.dispatchEvent(new CustomEvent(this.visible ? "open" : "close", {
|
|
847
|
+
bubbles: true,
|
|
848
|
+
composed: true,
|
|
849
|
+
}));
|
|
850
|
+
}
|
|
849
851
|
}
|
|
850
852
|
|
|
851
853
|
// -------------------------------------------------------------------------
|
|
@@ -860,6 +862,16 @@ class YumeMenu extends HTMLElement {
|
|
|
860
862
|
get direction() { return this.getAttribute("direction") || "down"; }
|
|
861
863
|
set direction(val) { this.setAttribute("direction", val); }
|
|
862
864
|
|
|
865
|
+
/**
|
|
866
|
+
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
867
|
+
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
868
|
+
*/
|
|
869
|
+
get history() { return this.getAttribute("history"); }
|
|
870
|
+
set history(val) {
|
|
871
|
+
if (val != null) this.setAttribute("history", val);
|
|
872
|
+
else this.removeAttribute("history");
|
|
873
|
+
}
|
|
874
|
+
|
|
863
875
|
/** Menu items array (JSON attribute). */
|
|
864
876
|
get items() {
|
|
865
877
|
try {
|
|
@@ -872,16 +884,6 @@ class YumeMenu extends HTMLElement {
|
|
|
872
884
|
this.setAttribute("items", Array.isArray(val) ? JSON.stringify(val) : (val ?? "[]"));
|
|
873
885
|
}
|
|
874
886
|
|
|
875
|
-
/**
|
|
876
|
-
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
877
|
-
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
878
|
-
*/
|
|
879
|
-
get history() { return this.getAttribute("history"); }
|
|
880
|
-
set history(val) {
|
|
881
|
-
if (val != null) this.setAttribute("history", val);
|
|
882
|
-
else this.removeAttribute("history");
|
|
883
|
-
}
|
|
884
|
-
|
|
885
887
|
/** Size: "small" | "medium" | "large" (default "medium"). */
|
|
886
888
|
get size() {
|
|
887
889
|
const sz = this.getAttribute("size");
|
|
@@ -905,63 +907,94 @@ class YumeMenu extends HTMLElement {
|
|
|
905
907
|
render() {
|
|
906
908
|
this.shadowRoot.innerHTML = "";
|
|
907
909
|
|
|
908
|
-
const style =
|
|
909
|
-
style.textContent = this._buildStyles();
|
|
910
|
-
this.shadowRoot.appendChild(style);
|
|
910
|
+
const style = createElement("style", {}, [this._buildStyles()]);
|
|
911
911
|
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
912
|
+
const root = this._createMenuList(this.items);
|
|
913
|
+
root.classList.add("menu");
|
|
914
|
+
root.setAttribute("role", "menu");
|
|
915
|
+
root.setAttribute("part", "menu");
|
|
916
916
|
|
|
917
|
-
|
|
917
|
+
const childSlot = createElement("slot");
|
|
918
|
+
childSlot.addEventListener("slotchange", () => this._processSlottedItems());
|
|
919
|
+
root.appendChild(childSlot);
|
|
920
|
+
|
|
921
|
+
this.shadowRoot.appendChild(style);
|
|
922
|
+
this.shadowRoot.appendChild(root);
|
|
923
|
+
this._processSlottedItems();
|
|
918
924
|
}
|
|
919
925
|
|
|
920
926
|
// -------------------------------------------------------------------------
|
|
921
927
|
// Private
|
|
922
928
|
// -------------------------------------------------------------------------
|
|
923
929
|
|
|
930
|
+
_activateItem(item) {
|
|
931
|
+
if (item.children?.length > 0) return;
|
|
932
|
+
|
|
933
|
+
this._dispatchSelect({
|
|
934
|
+
value: item.value ?? item.text,
|
|
935
|
+
item,
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
const href = item.href ?? item.url;
|
|
939
|
+
if (href) this._navigateTo(href);
|
|
940
|
+
|
|
941
|
+
this.visible = false;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
_activateSlottedItem(el) {
|
|
945
|
+
this._dispatchSelect({
|
|
946
|
+
value: el.dataset.value ?? el.textContent.trim(),
|
|
947
|
+
element: el,
|
|
948
|
+
});
|
|
949
|
+
this.visible = false;
|
|
950
|
+
}
|
|
951
|
+
|
|
924
952
|
_buildStyles() {
|
|
925
953
|
const paddingVar = `var(--component-button-padding-${this.size}, 0.5rem)`;
|
|
926
954
|
return `
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
list-style: none;
|
|
930
|
-
margin: 0;
|
|
931
|
-
padding: 0;
|
|
955
|
+
.menu,
|
|
956
|
+
.submenu {
|
|
932
957
|
background: var(--component-menu-background, #0c0c0d);
|
|
933
958
|
border: var(--component-menu-border-width, 1px) solid var(--component-menu-border-color, #37383a);
|
|
934
959
|
border-radius: var(--component-menu-border-radius, 4px);
|
|
935
960
|
box-shadow: var(--component-menu-shadow, 0 2px 8px rgba(0, 0, 0, 0.15));
|
|
936
961
|
min-width: 150px;
|
|
962
|
+
display: flex;
|
|
963
|
+
flex-direction: column;
|
|
937
964
|
}
|
|
938
965
|
|
|
939
|
-
|
|
966
|
+
.menuitem,
|
|
967
|
+
::slotted(:not([slot])) {
|
|
940
968
|
cursor: pointer;
|
|
941
969
|
padding: ${paddingVar};
|
|
942
|
-
display: flex;
|
|
943
|
-
align-items: center;
|
|
944
|
-
justify-content: space-between;
|
|
945
970
|
white-space: nowrap;
|
|
946
971
|
color: var(--component-menu-color, #f7f7fa);
|
|
947
972
|
font-size: var(--font-size-button, 1em);
|
|
973
|
+
box-sizing: border-box;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.menuitem {
|
|
977
|
+
display: flex;
|
|
978
|
+
align-items: center;
|
|
979
|
+
justify-content: space-between;
|
|
948
980
|
position: relative;
|
|
949
981
|
}
|
|
950
982
|
|
|
951
|
-
|
|
983
|
+
.menuitem:hover,
|
|
984
|
+
::slotted(:not([slot]):hover) {
|
|
952
985
|
background: var(--component-menu-hover-background, #292a2b);
|
|
953
986
|
}
|
|
954
987
|
|
|
955
|
-
|
|
988
|
+
.menuitem.selected {
|
|
956
989
|
background: var(--component-menu-selected-background);
|
|
957
990
|
color: var(--component-menu-selected-color);
|
|
958
991
|
}
|
|
959
992
|
|
|
960
|
-
|
|
993
|
+
.menuitem.selected:hover {
|
|
961
994
|
background: var(--component-menu-selected-background);
|
|
962
995
|
}
|
|
963
996
|
|
|
964
|
-
|
|
997
|
+
.submenu {
|
|
965
998
|
position: absolute;
|
|
966
999
|
top: 0;
|
|
967
1000
|
left: 100%;
|
|
@@ -969,8 +1002,8 @@ class YumeMenu extends HTMLElement {
|
|
|
969
1002
|
z-index: var(--component-menu-z-index, 1001);
|
|
970
1003
|
}
|
|
971
1004
|
|
|
972
|
-
|
|
973
|
-
display:
|
|
1005
|
+
.menuitem:hover > .submenu {
|
|
1006
|
+
display: flex;
|
|
974
1007
|
}
|
|
975
1008
|
|
|
976
1009
|
.submenu-indicator {
|
|
@@ -980,13 +1013,11 @@ class YumeMenu extends HTMLElement {
|
|
|
980
1013
|
opacity: 0.6;
|
|
981
1014
|
}
|
|
982
1015
|
|
|
983
|
-
.submenu-indicator svg {
|
|
984
|
-
width: 16px;
|
|
985
|
-
height: 16px;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
1016
|
.item-content {
|
|
989
1017
|
flex: 1;
|
|
1018
|
+
display: inline-flex;
|
|
1019
|
+
align-items: center;
|
|
1020
|
+
gap: 0.5rem;
|
|
990
1021
|
}
|
|
991
1022
|
`;
|
|
992
1023
|
}
|
|
@@ -999,91 +1030,140 @@ class YumeMenu extends HTMLElement {
|
|
|
999
1030
|
});
|
|
1000
1031
|
}
|
|
1001
1032
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
li.setAttribute("aria-current", item.selected ? "true" : "false");
|
|
1011
|
-
li.tabIndex = 0;
|
|
1012
|
-
|
|
1013
|
-
const contentWrapper = document.createElement("span");
|
|
1014
|
-
contentWrapper.className = "item-content";
|
|
1015
|
-
|
|
1016
|
-
if (item["icon-template"]) {
|
|
1017
|
-
const iconTpl = this._findTemplate(item["icon-template"]);
|
|
1018
|
-
if (iconTpl)
|
|
1019
|
-
contentWrapper.appendChild(iconTpl.content.cloneNode(true));
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
if (item.template) {
|
|
1023
|
-
const textTpl = this._findTemplate(item.template);
|
|
1024
|
-
if (textTpl) {
|
|
1025
|
-
contentWrapper.appendChild(textTpl.content.cloneNode(true));
|
|
1026
|
-
} else {
|
|
1027
|
-
contentWrapper.append(item.text);
|
|
1028
|
-
}
|
|
1029
|
-
} else {
|
|
1030
|
-
contentWrapper.append(item.text);
|
|
1031
|
-
}
|
|
1033
|
+
_computeMenuOffset(direction, anchorRect, menuRect, vw, vh) {
|
|
1034
|
+
if (direction === "right") {
|
|
1035
|
+
let top = anchorRect.top;
|
|
1036
|
+
let left = anchorRect.right;
|
|
1037
|
+
if (left + menuRect.width > vw) left = anchorRect.left - menuRect.width;
|
|
1038
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1039
|
+
return { top, left };
|
|
1040
|
+
}
|
|
1032
1041
|
|
|
1033
|
-
|
|
1042
|
+
if (direction === "up") {
|
|
1043
|
+
let top = anchorRect.top - menuRect.height;
|
|
1044
|
+
let left = anchorRect.left;
|
|
1045
|
+
if (top < 0) top = anchorRect.bottom;
|
|
1046
|
+
if (left + menuRect.width > vw) left = vw - menuRect.width - 10;
|
|
1047
|
+
return { top, left };
|
|
1048
|
+
}
|
|
1034
1049
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
});
|
|
1043
|
-
const cancelled = !this.dispatchEvent(event);
|
|
1044
|
-
if (cancelled) return;
|
|
1045
|
-
if (this.getAttribute("history") !== "false") {
|
|
1046
|
-
history.pushState({}, "", item.url);
|
|
1047
|
-
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
1048
|
-
} else {
|
|
1049
|
-
window.location.href = item.url;
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1050
|
+
if (direction === "left") {
|
|
1051
|
+
let top = anchorRect.top;
|
|
1052
|
+
let left = anchorRect.left - menuRect.width;
|
|
1053
|
+
if (left < 0) left = anchorRect.right;
|
|
1054
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1055
|
+
return { top, left };
|
|
1056
|
+
}
|
|
1053
1057
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1058
|
+
// "down" (default)
|
|
1059
|
+
let top = anchorRect.bottom;
|
|
1060
|
+
let left = anchorRect.left;
|
|
1061
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1062
|
+
if (left + menuRect.width > vw) left = vw - menuRect.width - 10;
|
|
1063
|
+
return { top, left };
|
|
1064
|
+
}
|
|
1059
1065
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
indicator.className = "submenu-indicator";
|
|
1063
|
-
indicator.innerHTML = chevronRight;
|
|
1064
|
-
li.appendChild(indicator);
|
|
1066
|
+
_createItemContent(item) {
|
|
1067
|
+
const wrapper = createElement("span", { class: "item-content" });
|
|
1065
1068
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1069
|
+
if (item.icon) {
|
|
1070
|
+
wrapper.appendChild(createElement("y-icon", { name: item.icon, size: this.size }));
|
|
1071
|
+
} else if (item["icon-template"]) {
|
|
1072
|
+
YumeMenu._warnTemplateFieldDeprecated();
|
|
1073
|
+
const tpl = this._findTemplate(item["icon-template"]);
|
|
1074
|
+
if (tpl) wrapper.appendChild(tpl.content.cloneNode(true));
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
if (item.template) {
|
|
1078
|
+
YumeMenu._warnTemplateFieldDeprecated();
|
|
1079
|
+
const tpl = this._findTemplate(item.template);
|
|
1080
|
+
if (tpl) wrapper.appendChild(tpl.content.cloneNode(true));
|
|
1081
|
+
else wrapper.append(item.text ?? "");
|
|
1082
|
+
} else {
|
|
1083
|
+
wrapper.append(item.text ?? "");
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if (!item.slot) return wrapper;
|
|
1087
|
+
|
|
1088
|
+
const slotEl = createElement("slot", { name: item.slot });
|
|
1089
|
+
slotEl.appendChild(wrapper);
|
|
1090
|
+
return slotEl;
|
|
1091
|
+
}
|
|
1071
1092
|
|
|
1072
|
-
|
|
1093
|
+
_createMenuItem(item) {
|
|
1094
|
+
const isSelected = !!item.selected;
|
|
1095
|
+
const partValue = isSelected ? "menuitem selected" : "menuitem";
|
|
1096
|
+
|
|
1097
|
+
const itemEl = createElement("div", {
|
|
1098
|
+
class: partValue,
|
|
1099
|
+
role: "menuitem",
|
|
1100
|
+
part: partValue,
|
|
1101
|
+
"aria-current": isSelected ? "true" : "false",
|
|
1102
|
+
tabindex: "0",
|
|
1073
1103
|
});
|
|
1074
1104
|
|
|
1075
|
-
|
|
1105
|
+
itemEl.appendChild(this._createItemContent(item));
|
|
1106
|
+
|
|
1107
|
+
if (item.url && !item.href) YumeMenu._warnUrlDeprecated();
|
|
1108
|
+
|
|
1109
|
+
itemEl.addEventListener("click", () => this._activateItem(item));
|
|
1110
|
+
|
|
1111
|
+
if (item.children?.length > 0) {
|
|
1112
|
+
itemEl.appendChild(this._createSubmenuIndicator());
|
|
1113
|
+
|
|
1114
|
+
const submenu = this._createMenuList(item.children);
|
|
1115
|
+
submenu.classList.add("submenu");
|
|
1116
|
+
submenu.setAttribute("role", "menu");
|
|
1117
|
+
itemEl.appendChild(submenu);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
return itemEl;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
_createMenuList(items) {
|
|
1124
|
+
const container = createElement("div");
|
|
1125
|
+
items.forEach((item) => container.appendChild(this._createMenuItem(item)));
|
|
1126
|
+
return container;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
_createSubmenuIndicator() {
|
|
1130
|
+
return createElement("span", { class: "submenu-indicator" }, [
|
|
1131
|
+
createElement("y-icon", { name: "chevron-right", size: this.size }),
|
|
1132
|
+
]);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
_dispatchSelect(detail) {
|
|
1136
|
+
this.dispatchEvent(new CustomEvent("select", {
|
|
1137
|
+
detail,
|
|
1138
|
+
bubbles: true,
|
|
1139
|
+
composed: true,
|
|
1140
|
+
}));
|
|
1076
1141
|
}
|
|
1077
1142
|
|
|
1078
1143
|
_findTemplate(name) {
|
|
1079
1144
|
return this.querySelector(`template[slot="${name}"]`);
|
|
1080
1145
|
}
|
|
1081
1146
|
|
|
1147
|
+
_navigateTo(href) {
|
|
1148
|
+
const event = new CustomEvent("navigate", {
|
|
1149
|
+
bubbles: true,
|
|
1150
|
+
composed: true,
|
|
1151
|
+
cancelable: true,
|
|
1152
|
+
detail: { href },
|
|
1153
|
+
});
|
|
1154
|
+
if (!this.dispatchEvent(event)) return;
|
|
1155
|
+
|
|
1156
|
+
if (this.getAttribute("history") === "false") {
|
|
1157
|
+
window.location.href = href;
|
|
1158
|
+
} else {
|
|
1159
|
+
history.pushState({}, "", href);
|
|
1160
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1082
1164
|
_onAnchorClick(e) {
|
|
1083
1165
|
e.stopPropagation();
|
|
1084
|
-
if (!this.visible)
|
|
1085
|
-
YumeMenu._closeAll(this);
|
|
1086
|
-
}
|
|
1166
|
+
if (!this.visible) YumeMenu._closeAll(this);
|
|
1087
1167
|
this.visible = !this.visible;
|
|
1088
1168
|
}
|
|
1089
1169
|
|
|
@@ -1098,9 +1178,34 @@ class YumeMenu extends HTMLElement {
|
|
|
1098
1178
|
if (this.visible) this._updatePosition();
|
|
1099
1179
|
}
|
|
1100
1180
|
|
|
1181
|
+
_processSlottedItems() {
|
|
1182
|
+
const slot = this.shadowRoot.querySelector(".menu > slot");
|
|
1183
|
+
if (!slot) return;
|
|
1184
|
+
|
|
1185
|
+
const assigned = new Set(slot.assignedElements());
|
|
1186
|
+
|
|
1187
|
+
for (const [el, handler] of this._slottedHandlers) {
|
|
1188
|
+
if (assigned.has(el)) continue;
|
|
1189
|
+
el.removeEventListener("click", handler);
|
|
1190
|
+
this._slottedHandlers.delete(el);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
for (const el of assigned) {
|
|
1194
|
+
if (this._slottedHandlers.has(el)) continue;
|
|
1195
|
+
|
|
1196
|
+
if (!el.hasAttribute("role")) el.setAttribute("role", "menuitem");
|
|
1197
|
+
if (el.tabIndex < 0) el.tabIndex = 0;
|
|
1198
|
+
|
|
1199
|
+
const handler = () => this._activateSlottedItem(el);
|
|
1200
|
+
el.addEventListener("click", handler);
|
|
1201
|
+
this._slottedHandlers.set(el, handler);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1101
1205
|
_setupAnchor() {
|
|
1102
1206
|
const id = this.anchor;
|
|
1103
1207
|
if (!id) return;
|
|
1208
|
+
|
|
1104
1209
|
const root = this.getRootNode();
|
|
1105
1210
|
this._anchorResolveDispose = resolveAnchor(
|
|
1106
1211
|
this,
|
|
@@ -1124,6 +1229,13 @@ class YumeMenu extends HTMLElement {
|
|
|
1124
1229
|
}
|
|
1125
1230
|
}
|
|
1126
1231
|
|
|
1232
|
+
_teardownSlottedItems() {
|
|
1233
|
+
for (const [el, handler] of this._slottedHandlers) {
|
|
1234
|
+
el.removeEventListener("click", handler);
|
|
1235
|
+
}
|
|
1236
|
+
this._slottedHandlers.clear();
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1127
1239
|
_updatePosition() {
|
|
1128
1240
|
if (!this.visible || !this._anchorEl) {
|
|
1129
1241
|
this.style.display = "none";
|
|
@@ -1132,7 +1244,7 @@ class YumeMenu extends HTMLElement {
|
|
|
1132
1244
|
|
|
1133
1245
|
const anchorRect = this._anchorEl.getBoundingClientRect();
|
|
1134
1246
|
|
|
1135
|
-
//
|
|
1247
|
+
// Measure menu off-screen so we know its size before placing it.
|
|
1136
1248
|
this.style.visibility = "hidden";
|
|
1137
1249
|
this.style.display = "block";
|
|
1138
1250
|
const menuRect = this.getBoundingClientRect();
|
|
@@ -1140,58 +1252,38 @@ class YumeMenu extends HTMLElement {
|
|
|
1140
1252
|
|
|
1141
1253
|
const vw = window.innerWidth;
|
|
1142
1254
|
const vh = window.innerHeight;
|
|
1255
|
+
const { top, left } = this._computeMenuOffset(
|
|
1256
|
+
this.direction,
|
|
1257
|
+
anchorRect,
|
|
1258
|
+
menuRect,
|
|
1259
|
+
vw,
|
|
1260
|
+
vh,
|
|
1261
|
+
);
|
|
1143
1262
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
if (this.direction === "right") {
|
|
1147
|
-
top = anchorRect.top;
|
|
1148
|
-
left = anchorRect.right;
|
|
1149
|
-
|
|
1150
|
-
if (left + menuRect.width > vw) {
|
|
1151
|
-
left = anchorRect.left - menuRect.width;
|
|
1152
|
-
}
|
|
1153
|
-
if (top + menuRect.height > vh) {
|
|
1154
|
-
top = anchorRect.top - menuRect.height;
|
|
1155
|
-
}
|
|
1156
|
-
} else if (this.direction === "up") {
|
|
1157
|
-
top = anchorRect.top - menuRect.height;
|
|
1158
|
-
left = anchorRect.left;
|
|
1159
|
-
|
|
1160
|
-
if (top < 0) {
|
|
1161
|
-
top = anchorRect.bottom;
|
|
1162
|
-
}
|
|
1163
|
-
if (left + menuRect.width > vw) {
|
|
1164
|
-
left = vw - menuRect.width - 10;
|
|
1165
|
-
}
|
|
1166
|
-
} else if (this.direction === "left") {
|
|
1167
|
-
top = anchorRect.top;
|
|
1168
|
-
left = anchorRect.left - menuRect.width;
|
|
1169
|
-
|
|
1170
|
-
if (left < 0) {
|
|
1171
|
-
left = anchorRect.right;
|
|
1172
|
-
}
|
|
1173
|
-
if (top + menuRect.height > vh) {
|
|
1174
|
-
top = anchorRect.top - menuRect.height;
|
|
1175
|
-
}
|
|
1176
|
-
} else {
|
|
1177
|
-
// "down" (default)
|
|
1178
|
-
top = anchorRect.bottom;
|
|
1179
|
-
left = anchorRect.left;
|
|
1263
|
+
const clampedTop = Math.max(0, Math.min(top, vh - menuRect.height));
|
|
1264
|
+
const clampedLeft = Math.max(0, Math.min(left, vw - menuRect.width));
|
|
1180
1265
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
left = vw - menuRect.width - 10;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1266
|
+
this.style.top = `${clampedTop}px`;
|
|
1267
|
+
this.style.left = `${clampedLeft}px`;
|
|
1268
|
+
this.style.display = "block";
|
|
1269
|
+
}
|
|
1188
1270
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1271
|
+
static _warnTemplateFieldDeprecated() {
|
|
1272
|
+
if (YumeMenu._templateFieldDeprecationWarned) return;
|
|
1273
|
+
YumeMenu._templateFieldDeprecationWarned = true;
|
|
1274
|
+
// eslint-disable-next-line no-console
|
|
1275
|
+
console.warn(
|
|
1276
|
+
"[y-menu] item.template and item['icon-template'] are deprecated; use item.icon (icon name) and item.slot (named slot) instead. Support will be removed in a future release.",
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1191
1279
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1280
|
+
static _warnUrlDeprecated() {
|
|
1281
|
+
if (YumeMenu._urlDeprecationWarned) return;
|
|
1282
|
+
YumeMenu._urlDeprecationWarned = true;
|
|
1283
|
+
// eslint-disable-next-line no-console
|
|
1284
|
+
console.warn(
|
|
1285
|
+
"[y-menu] item.url is deprecated; use item.href instead. Support for item.url will be removed in a future release.",
|
|
1286
|
+
);
|
|
1195
1287
|
}
|
|
1196
1288
|
}
|
|
1197
1289
|
|
|
@@ -1249,6 +1341,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1249
1341
|
this._idCounter = 0;
|
|
1250
1342
|
this._mql = null;
|
|
1251
1343
|
this._isMobile = false;
|
|
1344
|
+
this._mobileOutsideClick = null;
|
|
1252
1345
|
}
|
|
1253
1346
|
|
|
1254
1347
|
connectedCallback() {
|
|
@@ -1258,6 +1351,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1258
1351
|
|
|
1259
1352
|
disconnectedCallback() {
|
|
1260
1353
|
this._teardownMediaQuery();
|
|
1354
|
+
this._teardownMobileOutsideClick();
|
|
1261
1355
|
}
|
|
1262
1356
|
|
|
1263
1357
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
@@ -1273,12 +1367,26 @@ class YumeAppbar extends HTMLElement {
|
|
|
1273
1367
|
// -------------------------------------------------------------------------
|
|
1274
1368
|
|
|
1275
1369
|
/** Whether the sidebar is currently collapsed. */
|
|
1276
|
-
get collapsed() {
|
|
1370
|
+
get collapsed() {
|
|
1371
|
+
return this.hasAttribute("collapsed");
|
|
1372
|
+
}
|
|
1277
1373
|
set collapsed(val) {
|
|
1278
1374
|
if (val) this.setAttribute("collapsed", "");
|
|
1279
1375
|
else this.removeAttribute("collapsed");
|
|
1280
1376
|
}
|
|
1281
1377
|
|
|
1378
|
+
/**
|
|
1379
|
+
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
1380
|
+
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
1381
|
+
*/
|
|
1382
|
+
get history() {
|
|
1383
|
+
return this.getAttribute("history");
|
|
1384
|
+
}
|
|
1385
|
+
set history(val) {
|
|
1386
|
+
if (val != null) this.setAttribute("history", val);
|
|
1387
|
+
else this.removeAttribute("history");
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1282
1390
|
/** Nav items array parsed from the "items" attribute. */
|
|
1283
1391
|
get items() {
|
|
1284
1392
|
try {
|
|
@@ -1287,47 +1395,53 @@ class YumeAppbar extends HTMLElement {
|
|
|
1287
1395
|
return [];
|
|
1288
1396
|
}
|
|
1289
1397
|
}
|
|
1290
|
-
set items(val) {
|
|
1398
|
+
set items(val) {
|
|
1399
|
+
this.setAttribute("items", JSON.stringify(val));
|
|
1400
|
+
}
|
|
1291
1401
|
|
|
1292
1402
|
/**
|
|
1293
1403
|
* Direction menus pop out from nav buttons:
|
|
1294
1404
|
* "right", "down", or unset (auto: vertical → right, horizontal → down).
|
|
1295
1405
|
*/
|
|
1296
|
-
get menuDirection() {
|
|
1406
|
+
get menuDirection() {
|
|
1407
|
+
return this.getAttribute("menu-direction") || "";
|
|
1408
|
+
}
|
|
1297
1409
|
set menuDirection(val) {
|
|
1298
1410
|
if (val) this.setAttribute("menu-direction", val);
|
|
1299
1411
|
else this.removeAttribute("menu-direction");
|
|
1300
1412
|
}
|
|
1301
1413
|
|
|
1302
1414
|
/** Whether the appbar is currently rendering in mobile mode. */
|
|
1303
|
-
get mobile() {
|
|
1415
|
+
get mobile() {
|
|
1416
|
+
return this._isMobile;
|
|
1417
|
+
}
|
|
1304
1418
|
|
|
1305
1419
|
/**
|
|
1306
1420
|
* Override the mobile breakpoint (in pixels) for this instance.
|
|
1307
1421
|
* Falls back to the CSS variable --component-appbar-mobile-breakpoint (default 768).
|
|
1308
1422
|
*/
|
|
1309
|
-
get mobileBreakpoint() {
|
|
1423
|
+
get mobileBreakpoint() {
|
|
1424
|
+
return this.getAttribute("mobile-breakpoint") || "";
|
|
1425
|
+
}
|
|
1310
1426
|
set mobileBreakpoint(val) {
|
|
1311
1427
|
if (val) this.setAttribute("mobile-breakpoint", val);
|
|
1312
1428
|
else this.removeAttribute("mobile-breakpoint");
|
|
1313
1429
|
}
|
|
1314
1430
|
|
|
1315
1431
|
/** Layout orientation: "vertical" | "horizontal" (default "vertical"). */
|
|
1316
|
-
get orientation() {
|
|
1317
|
-
|
|
1432
|
+
get orientation() {
|
|
1433
|
+
return this.getAttribute("orientation") || "vertical";
|
|
1434
|
+
}
|
|
1435
|
+
set orientation(val) {
|
|
1436
|
+
this.setAttribute("orientation", val);
|
|
1437
|
+
}
|
|
1318
1438
|
|
|
1319
1439
|
/** Size variant: "small" | "medium" | "large" (default "medium"). */
|
|
1320
|
-
get size() {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
1326
|
-
*/
|
|
1327
|
-
get history() { return this.getAttribute("history"); }
|
|
1328
|
-
set history(val) {
|
|
1329
|
-
if (val != null) this.setAttribute("history", val);
|
|
1330
|
-
else this.removeAttribute("history");
|
|
1440
|
+
get size() {
|
|
1441
|
+
return this.getAttribute("size") || "medium";
|
|
1442
|
+
}
|
|
1443
|
+
set size(val) {
|
|
1444
|
+
this.setAttribute("size", val);
|
|
1331
1445
|
}
|
|
1332
1446
|
|
|
1333
1447
|
/** Sticky position: "start" | "end" | false. */
|
|
@@ -1344,201 +1458,88 @@ class YumeAppbar extends HTMLElement {
|
|
|
1344
1458
|
// Public
|
|
1345
1459
|
// -------------------------------------------------------------------------
|
|
1346
1460
|
|
|
1461
|
+
render() {
|
|
1462
|
+
if (this._isMobile) this._renderMobile();
|
|
1463
|
+
else this._renderDesktop();
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1347
1466
|
/** Toggles the collapsed state of the sidebar. */
|
|
1348
1467
|
toggle() {
|
|
1349
1468
|
this.collapsed = !this.collapsed;
|
|
1350
1469
|
}
|
|
1351
1470
|
|
|
1352
|
-
render() {
|
|
1353
|
-
if (this._isMobile) {
|
|
1354
|
-
this._renderMobile();
|
|
1355
|
-
} else {
|
|
1356
|
-
this._renderDesktop();
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
1471
|
// -------------------------------------------------------------------------
|
|
1361
1472
|
// Private
|
|
1362
1473
|
// -------------------------------------------------------------------------
|
|
1363
1474
|
|
|
1364
|
-
|
|
1365
|
-
const
|
|
1366
|
-
btn.setAttribute("color", "base");
|
|
1367
|
-
btn.setAttribute("style-type", "flat");
|
|
1368
|
-
btn.setAttribute("size", cfg.buttonSize);
|
|
1369
|
-
btn.setAttribute("aria-label", isCollapsed ? "Expand sidebar" : "Collapse sidebar");
|
|
1370
|
-
btn.className = "collapse-btn";
|
|
1371
|
-
|
|
1372
|
-
const icon = document.createElement("span");
|
|
1373
|
-
icon.slot = "left-icon";
|
|
1374
|
-
icon.innerHTML = isCollapsed ? expandRight : expandLeft;
|
|
1375
|
-
btn.appendChild(icon);
|
|
1376
|
-
|
|
1377
|
-
if (!isCollapsed) {
|
|
1378
|
-
btn.appendChild(document.createTextNode("Collapse"));
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
btn.addEventListener("click", this._onCollapseClick);
|
|
1382
|
-
return btn;
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
_buildHeader() {
|
|
1386
|
-
const header = document.createElement("div");
|
|
1387
|
-
header.className = "appbar-header";
|
|
1388
|
-
header.setAttribute("part", "header");
|
|
1389
|
-
|
|
1390
|
-
const headerContent = document.createElement("div");
|
|
1391
|
-
headerContent.className = "header-content";
|
|
1392
|
-
|
|
1393
|
-
const logoWrapper = document.createElement("div");
|
|
1394
|
-
logoWrapper.className = "logo-wrapper";
|
|
1395
|
-
logoWrapper.appendChild(this._makeSlot("logo"));
|
|
1396
|
-
headerContent.appendChild(logoWrapper);
|
|
1475
|
+
_buildBody(cfg, isCollapsed, menuDir) {
|
|
1476
|
+
const body = createElement("div", { class: "appbar-body", part: "body" });
|
|
1397
1477
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1478
|
+
this.items.forEach((item) => {
|
|
1479
|
+
body.appendChild(
|
|
1480
|
+
this._buildNavItem(item, cfg, isCollapsed, menuDir),
|
|
1481
|
+
);
|
|
1482
|
+
});
|
|
1483
|
+
body.appendChild(createElement("slot", {}));
|
|
1402
1484
|
|
|
1403
|
-
|
|
1404
|
-
header.appendChild(this._makeSlot("header"));
|
|
1405
|
-
return header;
|
|
1485
|
+
return body;
|
|
1406
1486
|
}
|
|
1407
1487
|
|
|
1408
|
-
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
wrapper.className = "nav-item";
|
|
1415
|
-
btn.id = btnId;
|
|
1416
|
-
btn.setAttribute("color", this._isItemActive(item) ? "primary" : "base");
|
|
1417
|
-
btn.setAttribute("style-type", "flat");
|
|
1418
|
-
btn.setAttribute("size", cfg.buttonSize);
|
|
1419
|
-
|
|
1420
|
-
if (item.icon) {
|
|
1421
|
-
if (item.icon.trim().startsWith("<")) {
|
|
1422
|
-
const iconEl = document.createElement("span");
|
|
1423
|
-
iconEl.slot = "left-icon";
|
|
1424
|
-
iconEl.setAttribute("part", "icon");
|
|
1425
|
-
iconEl.innerHTML = item.icon;
|
|
1426
|
-
btn.appendChild(iconEl);
|
|
1427
|
-
} else {
|
|
1428
|
-
const iconEl = document.createElement("y-icon");
|
|
1429
|
-
iconEl.slot = "left-icon";
|
|
1430
|
-
iconEl.setAttribute("part", "icon");
|
|
1431
|
-
iconEl.setAttribute("name", item.icon);
|
|
1432
|
-
iconEl.setAttribute("size", cfg.iconSize);
|
|
1433
|
-
btn.appendChild(iconEl);
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
if (item.text && !isCollapsed) {
|
|
1438
|
-
btn.appendChild(document.createTextNode(item.text));
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
if (hasChildren && !isCollapsed) {
|
|
1442
|
-
const arrow = document.createElement("span");
|
|
1443
|
-
arrow.slot = "right-icon";
|
|
1444
|
-
arrow.innerHTML = isVertical ? chevronRight : chevronDown;
|
|
1445
|
-
btn.appendChild(arrow);
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
if (item.href && !hasChildren) {
|
|
1449
|
-
btn.addEventListener("click", () => {
|
|
1450
|
-
const event = new CustomEvent("navigate", {
|
|
1451
|
-
bubbles: true,
|
|
1452
|
-
composed: true,
|
|
1453
|
-
cancelable: true,
|
|
1454
|
-
detail: { href: item.href },
|
|
1455
|
-
});
|
|
1456
|
-
const cancelled = !this.dispatchEvent(event);
|
|
1457
|
-
if (cancelled) return;
|
|
1458
|
-
if (this.getAttribute("history") !== "false") {
|
|
1459
|
-
history.pushState({}, "", item.href);
|
|
1460
|
-
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
1461
|
-
} else {
|
|
1462
|
-
window.location.href = item.href;
|
|
1463
|
-
}
|
|
1464
|
-
});
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
if (item.slot) {
|
|
1468
|
-
const slot = this._makeSlot(item.slot);
|
|
1469
|
-
slot.appendChild(btn);
|
|
1470
|
-
wrapper.appendChild(slot);
|
|
1471
|
-
} else {
|
|
1472
|
-
wrapper.appendChild(btn);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
if (hasChildren) {
|
|
1476
|
-
const menuEl = document.createElement("y-menu");
|
|
1477
|
-
menuEl.setAttribute("anchor", btnId);
|
|
1478
|
-
menuEl.setAttribute("direction", menuDir);
|
|
1479
|
-
menuEl.setAttribute("size", cfg.buttonSize);
|
|
1480
|
-
menuEl.items = item.children;
|
|
1481
|
-
wrapper.appendChild(menuEl);
|
|
1482
|
-
}
|
|
1488
|
+
_buildCollapseButton(cfg, isCollapsed) {
|
|
1489
|
+
const icon = createElement("y-icon", {
|
|
1490
|
+
slot: "left-icon",
|
|
1491
|
+
name: isCollapsed ? "expand-right" : "expand-left",
|
|
1492
|
+
size: cfg.iconSize,
|
|
1493
|
+
});
|
|
1483
1494
|
|
|
1484
|
-
|
|
1485
|
-
|
|
1495
|
+
const children = isCollapsed ? [icon] : [icon, "Collapse"];
|
|
1496
|
+
const btn = createElement(
|
|
1497
|
+
"y-button",
|
|
1498
|
+
{
|
|
1499
|
+
class: "collapse-btn",
|
|
1500
|
+
color: "base",
|
|
1501
|
+
"style-type": "flat",
|
|
1502
|
+
size: cfg.buttonSize,
|
|
1503
|
+
"aria-label": isCollapsed
|
|
1504
|
+
? "Expand sidebar"
|
|
1505
|
+
: "Collapse sidebar",
|
|
1506
|
+
},
|
|
1507
|
+
children,
|
|
1508
|
+
);
|
|
1486
1509
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
if (attr) {
|
|
1490
|
-
const px = parseInt(attr, 10);
|
|
1491
|
-
if (!isNaN(px) && px > 0) return px;
|
|
1492
|
-
}
|
|
1493
|
-
const cssVal = getComputedStyle(document.documentElement)
|
|
1494
|
-
.getPropertyValue("--component-appbar-mobile-breakpoint")
|
|
1495
|
-
.trim();
|
|
1496
|
-
if (cssVal) {
|
|
1497
|
-
const px = parseInt(cssVal, 10);
|
|
1498
|
-
if (!isNaN(px) && px > 0) return px;
|
|
1499
|
-
}
|
|
1500
|
-
return 768;
|
|
1510
|
+
btn.addEventListener("click", this._onCollapseClick);
|
|
1511
|
+
return btn;
|
|
1501
1512
|
}
|
|
1502
1513
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1514
|
+
_buildDesktopBar(cfg, isVertical, isCollapsed, menuDir) {
|
|
1515
|
+
const classes = ["appbar", isVertical ? "vertical" : "horizontal"];
|
|
1516
|
+
if (isCollapsed) classes.push("collapsed");
|
|
1507
1517
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
const current = loc.pathname + loc.search + loc.hash;
|
|
1513
|
-
return item.href === current || item.href === loc.href;
|
|
1514
|
-
}
|
|
1515
|
-
return false;
|
|
1516
|
-
}
|
|
1518
|
+
const bar = createElement("div", {
|
|
1519
|
+
class: classes.join(" "),
|
|
1520
|
+
role: "navigation",
|
|
1521
|
+
});
|
|
1517
1522
|
|
|
1518
|
-
|
|
1519
|
-
const
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
+
// Layout-specific sizing tokens consumed by the stylesheet.
|
|
1524
|
+
const iconColWidth = `calc(${cfg.collapsedWidth} - 2 * var(--_appbar-padding) - 2 * var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) - 2 * var(--component-button-border-width, 1px))`;
|
|
1525
|
+
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
1526
|
+
bar.style.setProperty("--_appbar-collapsed-width", cfg.collapsedWidth);
|
|
1527
|
+
bar.style.setProperty("--_appbar-body-gap", cfg.bodyGap);
|
|
1528
|
+
bar.style.setProperty(
|
|
1529
|
+
"--_button-padding",
|
|
1530
|
+
`var(--component-button-padding-${cfg.buttonSize})`,
|
|
1531
|
+
);
|
|
1532
|
+
bar.style.setProperty("--_icon-col-width", iconColWidth);
|
|
1523
1533
|
|
|
1524
|
-
|
|
1525
|
-
this.
|
|
1526
|
-
|
|
1534
|
+
bar.appendChild(this._buildHeader());
|
|
1535
|
+
bar.appendChild(this._buildBody(cfg, isCollapsed, menuDir));
|
|
1536
|
+
bar.appendChild(this._buildFooter(cfg, isVertical, isCollapsed));
|
|
1527
1537
|
|
|
1528
|
-
|
|
1529
|
-
this._isMobile = e.matches;
|
|
1530
|
-
this.render();
|
|
1538
|
+
return bar;
|
|
1531
1539
|
}
|
|
1532
1540
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
const isVertical = this.orientation === "vertical";
|
|
1536
|
-
const isCollapsed = this.collapsed && isVertical;
|
|
1537
|
-
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
1538
|
-
const menuDir = this.menuDirection || (isVertical ? "right" : "down");
|
|
1539
|
-
|
|
1540
|
-
const style = document.createElement("style");
|
|
1541
|
-
style.textContent = `
|
|
1541
|
+
_buildDesktopStyles() {
|
|
1542
|
+
return `
|
|
1542
1543
|
:host {
|
|
1543
1544
|
display: block;
|
|
1544
1545
|
font-family: var(--font-family-body, sans-serif);
|
|
@@ -1766,55 +1767,148 @@ class YumeAppbar extends HTMLElement {
|
|
|
1766
1767
|
::slotted(*) {
|
|
1767
1768
|
display: block;
|
|
1768
1769
|
}
|
|
1770
|
+
.appbar.vertical ::slotted(:not([slot])) {
|
|
1771
|
+
width: 100%;
|
|
1772
|
+
}
|
|
1773
|
+
.appbar.horizontal ::slotted(:not([slot])) {
|
|
1774
|
+
display: inline-flex;
|
|
1775
|
+
align-items: center;
|
|
1776
|
+
}
|
|
1777
|
+
.appbar.vertical.collapsed ::slotted(:not([slot])) {
|
|
1778
|
+
width: var(--_icon-col-width);
|
|
1779
|
+
overflow: hidden;
|
|
1780
|
+
}
|
|
1769
1781
|
span[slot="left-icon"] svg,
|
|
1770
1782
|
span[slot="right-icon"] svg {
|
|
1771
1783
|
width: var(--component-icon-size-large, 1.25em);
|
|
1772
1784
|
height: var(--component-icon-size-large, 1.25em);
|
|
1773
1785
|
}
|
|
1774
1786
|
`;
|
|
1775
|
-
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
_buildFooter(cfg, isVertical, isCollapsed) {
|
|
1790
|
+
const footer = createElement("div", { class: "appbar-footer", part: "footer" });
|
|
1791
|
+
footer.appendChild(createElement("slot", { name: "footer" }));
|
|
1792
|
+
|
|
1793
|
+
if (isVertical) {
|
|
1794
|
+
footer.appendChild(this._buildCollapseButton(cfg, isCollapsed));
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
return footer;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
_buildHeader() {
|
|
1801
|
+
const logoWrapper = createElement("div", { class: "logo-wrapper" }, [
|
|
1802
|
+
createElement("slot", { name: "logo" }),
|
|
1803
|
+
]);
|
|
1804
|
+
const titleWrapper = createElement("div", { class: "header-title" }, [
|
|
1805
|
+
createElement("slot", { name: "title" }),
|
|
1806
|
+
]);
|
|
1807
|
+
const headerContent = createElement("div", { class: "header-content" }, [
|
|
1808
|
+
logoWrapper,
|
|
1809
|
+
titleWrapper,
|
|
1810
|
+
]);
|
|
1811
|
+
|
|
1812
|
+
return createElement("div", { class: "appbar-header", part: "header" }, [
|
|
1813
|
+
headerContent,
|
|
1814
|
+
createElement("slot", { name: "header" }),
|
|
1815
|
+
]);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
_buildItemIcon(iconValue, cfg) {
|
|
1819
|
+
// Raw SVG markup is preserved as a public escape hatch; everything else
|
|
1820
|
+
// routes through y-icon for consistent sizing/theming.
|
|
1821
|
+
if (iconValue.trim().startsWith("<")) {
|
|
1822
|
+
const span = createElement("span", { slot: "left-icon", part: "icon" });
|
|
1823
|
+
span.innerHTML = iconValue;
|
|
1824
|
+
return span;
|
|
1825
|
+
}
|
|
1826
|
+
return createElement("y-icon", {
|
|
1827
|
+
slot: "left-icon",
|
|
1828
|
+
part: "icon",
|
|
1829
|
+
name: iconValue,
|
|
1830
|
+
size: cfg.iconSize,
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1776
1833
|
|
|
1777
|
-
|
|
1778
|
-
bar
|
|
1779
|
-
if (isCollapsed) bar.classList.add("collapsed");
|
|
1780
|
-
bar.setAttribute("role", "navigation");
|
|
1834
|
+
_buildMobileBar(cfg) {
|
|
1835
|
+
const bar = createElement("div", { class: "appbar", role: "navigation" });
|
|
1781
1836
|
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
1782
|
-
bar.style.setProperty("--_appbar-collapsed-width", cfg.collapsedWidth);
|
|
1783
|
-
bar.style.setProperty("--_appbar-body-gap", cfg.bodyGap);
|
|
1784
|
-
bar.style.setProperty("--_button-padding", `var(--component-button-padding-${cfg.buttonSize})`);
|
|
1785
|
-
bar.style.setProperty(
|
|
1786
|
-
"--_icon-col-width",
|
|
1787
|
-
`calc(${cfg.collapsedWidth} - 2 * var(--_appbar-padding) - 2 * var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) - 2 * var(--component-button-border-width, 1px))`,
|
|
1788
|
-
);
|
|
1789
1837
|
|
|
1790
|
-
bar.appendChild(this.
|
|
1838
|
+
bar.appendChild(this._buildMobileStart(cfg));
|
|
1839
|
+
bar.appendChild(this._buildMobileCenter());
|
|
1840
|
+
bar.appendChild(this._buildMobileEnd());
|
|
1841
|
+
|
|
1842
|
+
return bar;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
_buildMobileCenter() {
|
|
1846
|
+
return createElement("div", { class: "mobile-center" }, [
|
|
1847
|
+
createElement("slot", { name: "logo" }),
|
|
1848
|
+
createElement("slot", { name: "title" }),
|
|
1849
|
+
]);
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
_buildMobileEnd() {
|
|
1853
|
+
return createElement("div", { class: "mobile-end", part: "footer" }, [
|
|
1854
|
+
createElement("slot", { name: "footer" }),
|
|
1855
|
+
]);
|
|
1856
|
+
}
|
|
1791
1857
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1858
|
+
_buildMobileStart(cfg) {
|
|
1859
|
+
const menuBtnId = this._uid("appbar-mobile-menu");
|
|
1860
|
+
const panelId = this._uid("appbar-mobile-panel");
|
|
1861
|
+
|
|
1862
|
+
const menuBtn = createElement(
|
|
1863
|
+
"y-button",
|
|
1864
|
+
{
|
|
1865
|
+
id: menuBtnId,
|
|
1866
|
+
color: "base",
|
|
1867
|
+
"style-type": "flat",
|
|
1868
|
+
size: cfg.buttonSize,
|
|
1869
|
+
"aria-label": "Open menu",
|
|
1870
|
+
"aria-controls": panelId,
|
|
1871
|
+
"aria-expanded": "false",
|
|
1872
|
+
},
|
|
1873
|
+
[
|
|
1874
|
+
createElement("y-icon", {
|
|
1875
|
+
slot: "left-icon",
|
|
1876
|
+
name: "menu",
|
|
1877
|
+
size: cfg.iconSize,
|
|
1878
|
+
}),
|
|
1879
|
+
],
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
const panel = createElement("div", { id: panelId, class: "mobile-panel" });
|
|
1795
1883
|
this.items.forEach((item) => {
|
|
1796
|
-
|
|
1884
|
+
panel.appendChild(this._buildNavItem(item, cfg, false, "down"));
|
|
1797
1885
|
});
|
|
1798
|
-
|
|
1886
|
+
panel.appendChild(createElement("slot", {}));
|
|
1799
1887
|
|
|
1800
|
-
const
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
if (isVertical) {
|
|
1805
|
-
footer.appendChild(this._buildCollapseButton(cfg, isCollapsed));
|
|
1806
|
-
}
|
|
1807
|
-
bar.appendChild(footer);
|
|
1888
|
+
const closePanel = () => {
|
|
1889
|
+
panel.classList.remove("open");
|
|
1890
|
+
menuBtn.setAttribute("aria-expanded", "false");
|
|
1891
|
+
};
|
|
1808
1892
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1893
|
+
menuBtn.addEventListener("click", (e) => {
|
|
1894
|
+
e.stopPropagation();
|
|
1895
|
+
const open = panel.classList.toggle("open");
|
|
1896
|
+
menuBtn.setAttribute("aria-expanded", open ? "true" : "false");
|
|
1897
|
+
});
|
|
1811
1898
|
|
|
1812
|
-
|
|
1813
|
-
this._initRender();
|
|
1814
|
-
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
1899
|
+
panel.addEventListener("navigate", closePanel);
|
|
1815
1900
|
|
|
1816
|
-
|
|
1817
|
-
|
|
1901
|
+
this._mobileOutsideClick = (e) => {
|
|
1902
|
+
if (e.composedPath().includes(this)) return;
|
|
1903
|
+
closePanel();
|
|
1904
|
+
};
|
|
1905
|
+
document.addEventListener("pointerdown", this._mobileOutsideClick);
|
|
1906
|
+
|
|
1907
|
+
return createElement("div", { class: "mobile-start" }, [menuBtn, panel]);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
_buildMobileStyles() {
|
|
1911
|
+
return `
|
|
1818
1912
|
:host {
|
|
1819
1913
|
display: block;
|
|
1820
1914
|
font-family: var(--font-family-body, sans-serif);
|
|
@@ -1855,6 +1949,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
1855
1949
|
display: flex;
|
|
1856
1950
|
align-items: center;
|
|
1857
1951
|
flex-shrink: 0;
|
|
1952
|
+
position: relative;
|
|
1858
1953
|
}
|
|
1859
1954
|
|
|
1860
1955
|
.mobile-center {
|
|
@@ -1871,8 +1966,38 @@ class YumeAppbar extends HTMLElement {
|
|
|
1871
1966
|
flex-shrink: 0;
|
|
1872
1967
|
}
|
|
1873
1968
|
|
|
1874
|
-
.mobile-
|
|
1969
|
+
.mobile-panel {
|
|
1970
|
+
position: absolute;
|
|
1971
|
+
top: 100%;
|
|
1972
|
+
left: 0;
|
|
1973
|
+
margin-top: 4px;
|
|
1974
|
+
background: var(--component-appbar-background, #0c0c0d);
|
|
1975
|
+
border: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #37383a);
|
|
1976
|
+
border-radius: var(--component-appbar-border-radius, var(--component-sidebar-border-radius, 4px));
|
|
1977
|
+
padding: var(--_appbar-padding);
|
|
1978
|
+
display: none;
|
|
1979
|
+
flex-direction: column;
|
|
1980
|
+
gap: 2px;
|
|
1981
|
+
min-width: 180px;
|
|
1982
|
+
z-index: var(--component-appbar-z-index, 100);
|
|
1983
|
+
}
|
|
1984
|
+
.mobile-panel.open {
|
|
1985
|
+
display: flex;
|
|
1986
|
+
}
|
|
1987
|
+
.mobile-panel .nav-item {
|
|
1988
|
+
display: flex;
|
|
1989
|
+
width: 100%;
|
|
1990
|
+
}
|
|
1991
|
+
.mobile-panel .nav-item y-button {
|
|
1875
1992
|
display: block;
|
|
1993
|
+
width: 100%;
|
|
1994
|
+
}
|
|
1995
|
+
.mobile-panel .nav-item y-button::part(button) {
|
|
1996
|
+
width: 100%;
|
|
1997
|
+
justify-content: flex-start;
|
|
1998
|
+
}
|
|
1999
|
+
.mobile-panel ::slotted(:not([slot])) {
|
|
2000
|
+
width: 100%;
|
|
1876
2001
|
}
|
|
1877
2002
|
|
|
1878
2003
|
::slotted(*) {
|
|
@@ -1884,56 +2009,147 @@ class YumeAppbar extends HTMLElement {
|
|
|
1884
2009
|
height: var(--component-icon-size-large, 1.25em);
|
|
1885
2010
|
}
|
|
1886
2011
|
`;
|
|
1887
|
-
|
|
2012
|
+
}
|
|
1888
2013
|
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
2014
|
+
_buildNavItem(item, cfg, isCollapsed, menuDir) {
|
|
2015
|
+
const hasChildren = item.children?.length > 0;
|
|
2016
|
+
const showLabel = item.text && !isCollapsed;
|
|
2017
|
+
const showArrow = hasChildren && !isCollapsed;
|
|
2018
|
+
const btnId = this._uid("appbar-btn");
|
|
2019
|
+
|
|
2020
|
+
const btn = createElement("y-button", {
|
|
2021
|
+
id: btnId,
|
|
2022
|
+
color: this._isItemActive(item) ? "primary" : "base",
|
|
2023
|
+
"style-type": "flat",
|
|
2024
|
+
size: cfg.buttonSize,
|
|
2025
|
+
});
|
|
1893
2026
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2027
|
+
if (item.icon) btn.appendChild(this._buildItemIcon(item.icon, cfg));
|
|
2028
|
+
if (showLabel) btn.appendChild(document.createTextNode(item.text));
|
|
2029
|
+
if (showArrow) {
|
|
2030
|
+
btn.appendChild(
|
|
2031
|
+
createElement("y-icon", {
|
|
2032
|
+
slot: "right-icon",
|
|
2033
|
+
name: `chevron-${menuDir}`,
|
|
2034
|
+
size: cfg.iconSize,
|
|
2035
|
+
}),
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
1897
2038
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2039
|
+
if (item.href && !hasChildren) {
|
|
2040
|
+
btn.addEventListener("click", () => this._navigateTo(item.href));
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
const wrapper = createElement("div", { class: "nav-item" });
|
|
2044
|
+
if (item.slot) {
|
|
2045
|
+
const slot = createElement("slot", { name: item.slot });
|
|
2046
|
+
slot.appendChild(btn);
|
|
2047
|
+
wrapper.appendChild(slot);
|
|
2048
|
+
} else {
|
|
2049
|
+
wrapper.appendChild(btn);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
if (hasChildren) {
|
|
2053
|
+
const menuEl = createElement("y-menu", {
|
|
2054
|
+
anchor: btnId,
|
|
2055
|
+
direction: menuDir,
|
|
2056
|
+
size: cfg.buttonSize,
|
|
2057
|
+
});
|
|
2058
|
+
menuEl.items = item.children;
|
|
2059
|
+
wrapper.appendChild(menuEl);
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
return wrapper;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
_getBreakpointPx() {
|
|
2066
|
+
const attr = this.mobileBreakpoint;
|
|
2067
|
+
if (attr) {
|
|
2068
|
+
const px = parseInt(attr, 10);
|
|
2069
|
+
if (!isNaN(px) && px > 0) return px;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
const cssVal = getComputedStyle(document.documentElement)
|
|
2073
|
+
.getPropertyValue("--component-appbar-mobile-breakpoint")
|
|
2074
|
+
.trim();
|
|
2075
|
+
if (cssVal) {
|
|
2076
|
+
const px = parseInt(cssVal, 10);
|
|
2077
|
+
if (!isNaN(px) && px > 0) return px;
|
|
1920
2078
|
}
|
|
1921
|
-
bar.appendChild(startSection);
|
|
1922
|
-
|
|
1923
|
-
/* ── Center: logo + title ── */
|
|
1924
|
-
const centerSection = document.createElement("div");
|
|
1925
|
-
centerSection.className = "mobile-center";
|
|
1926
|
-
centerSection.appendChild(this._makeSlot("logo"));
|
|
1927
|
-
centerSection.appendChild(this._makeSlot("title"));
|
|
1928
|
-
bar.appendChild(centerSection);
|
|
1929
|
-
|
|
1930
|
-
/* ── Right: footer slot ── */
|
|
1931
|
-
const endSection = document.createElement("div");
|
|
1932
|
-
endSection.className = "mobile-end";
|
|
1933
|
-
endSection.setAttribute("part", "footer");
|
|
1934
|
-
endSection.appendChild(this._makeSlot("footer"));
|
|
1935
|
-
bar.appendChild(endSection);
|
|
1936
2079
|
|
|
2080
|
+
return 768;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
_initRender() {
|
|
2084
|
+
this._teardownMobileOutsideClick();
|
|
2085
|
+
this.shadowRoot.innerHTML = "";
|
|
2086
|
+
this._idCounter = 0;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
_isItemActive(item) {
|
|
2090
|
+
if (item.selected) return true;
|
|
2091
|
+
if (!item.href) return false;
|
|
2092
|
+
|
|
2093
|
+
const loc = window.location;
|
|
2094
|
+
const current = loc.pathname + loc.search + loc.hash;
|
|
2095
|
+
|
|
2096
|
+
return item.href === current || item.href === loc.href;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
_navigateTo(href) {
|
|
2100
|
+
const event = new CustomEvent("navigate", {
|
|
2101
|
+
bubbles: true,
|
|
2102
|
+
composed: true,
|
|
2103
|
+
cancelable: true,
|
|
2104
|
+
detail: { href },
|
|
2105
|
+
});
|
|
2106
|
+
if (!this.dispatchEvent(event)) return;
|
|
2107
|
+
|
|
2108
|
+
if (this.getAttribute("history") === "false") {
|
|
2109
|
+
window.location.href = href;
|
|
2110
|
+
} else {
|
|
2111
|
+
history.pushState({}, "", href);
|
|
2112
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
_onCollapseClick() {
|
|
2117
|
+
this.toggle();
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
_onMediaChange(e) {
|
|
2121
|
+
this._isMobile = e.matches;
|
|
2122
|
+
this.render();
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
_renderDesktop() {
|
|
2126
|
+
this._initRender();
|
|
2127
|
+
|
|
2128
|
+
const isVertical = this.orientation === "vertical";
|
|
2129
|
+
const isCollapsed = this.collapsed && isVertical;
|
|
2130
|
+
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
2131
|
+
const menuDir = this.menuDirection || (isVertical ? "right" : "down");
|
|
2132
|
+
|
|
2133
|
+
const style = createElement("style", {}, [this._buildDesktopStyles()]);
|
|
2134
|
+
const bar = this._buildDesktopBar(
|
|
2135
|
+
cfg,
|
|
2136
|
+
isVertical,
|
|
2137
|
+
isCollapsed,
|
|
2138
|
+
menuDir,
|
|
2139
|
+
);
|
|
2140
|
+
|
|
2141
|
+
this.shadowRoot.appendChild(style);
|
|
2142
|
+
this.shadowRoot.appendChild(bar);
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
_renderMobile() {
|
|
2146
|
+
this._initRender();
|
|
2147
|
+
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
2148
|
+
|
|
2149
|
+
const style = createElement("style", {}, [this._buildMobileStyles()]);
|
|
2150
|
+
const bar = this._buildMobileBar(cfg);
|
|
2151
|
+
|
|
2152
|
+
this.shadowRoot.appendChild(style);
|
|
1937
2153
|
this.shadowRoot.appendChild(bar);
|
|
1938
2154
|
}
|
|
1939
2155
|
|
|
@@ -1950,26 +2166,15 @@ class YumeAppbar extends HTMLElement {
|
|
|
1950
2166
|
}
|
|
1951
2167
|
|
|
1952
2168
|
_teardownMediaQuery() {
|
|
1953
|
-
if (this._mql)
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
}
|
|
2169
|
+
if (!this._mql) return;
|
|
2170
|
+
this._mql.removeEventListener("change", this._onMediaChange);
|
|
2171
|
+
this._mql = null;
|
|
1957
2172
|
}
|
|
1958
2173
|
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
_toMenuItems(items) {
|
|
1964
|
-
return items.map((item) => {
|
|
1965
|
-
const mi = { text: item.text || "" };
|
|
1966
|
-
if (item.href) mi.url = item.href;
|
|
1967
|
-
if (item.icon) mi.icon = item.icon;
|
|
1968
|
-
if (item.children?.length) {
|
|
1969
|
-
mi.children = this._toMenuItems(item.children);
|
|
1970
|
-
}
|
|
1971
|
-
return mi;
|
|
1972
|
-
});
|
|
2174
|
+
_teardownMobileOutsideClick() {
|
|
2175
|
+
if (!this._mobileOutsideClick) return;
|
|
2176
|
+
document.removeEventListener("pointerdown", this._mobileOutsideClick);
|
|
2177
|
+
this._mobileOutsideClick = null;
|
|
1973
2178
|
}
|
|
1974
2179
|
|
|
1975
2180
|
_uid(prefix) {
|