@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
package/dist/index.js
CHANGED
|
@@ -1696,6 +1696,8 @@ class YumeMenu extends HTMLElement {
|
|
|
1696
1696
|
this._onAnchorClick = this._onAnchorClick.bind(this);
|
|
1697
1697
|
this._onDocumentClick = this._onDocumentClick.bind(this);
|
|
1698
1698
|
this._onScrollOrResize = this._onScrollOrResize.bind(this);
|
|
1699
|
+
this._isReady = false;
|
|
1700
|
+
this._slottedHandlers = new Map();
|
|
1699
1701
|
}
|
|
1700
1702
|
|
|
1701
1703
|
connectedCallback() {
|
|
@@ -1712,10 +1714,13 @@ class YumeMenu extends HTMLElement {
|
|
|
1712
1714
|
this.style.zIndex = "1000";
|
|
1713
1715
|
this.style.display = "none";
|
|
1714
1716
|
if (this.visible) this._updatePosition();
|
|
1717
|
+
|
|
1718
|
+
this._isReady = true;
|
|
1715
1719
|
}
|
|
1716
1720
|
|
|
1717
1721
|
disconnectedCallback() {
|
|
1718
1722
|
this._teardownAnchor();
|
|
1723
|
+
this._teardownSlottedItems();
|
|
1719
1724
|
|
|
1720
1725
|
document.removeEventListener("click", this._onDocumentClick);
|
|
1721
1726
|
window.removeEventListener("scroll", this._onScrollOrResize, true);
|
|
@@ -1735,6 +1740,13 @@ class YumeMenu extends HTMLElement {
|
|
|
1735
1740
|
if (name === "visible" || name === "direction") {
|
|
1736
1741
|
this._updatePosition();
|
|
1737
1742
|
}
|
|
1743
|
+
|
|
1744
|
+
if (name === "visible" && this._isReady) {
|
|
1745
|
+
this.dispatchEvent(new CustomEvent(this.visible ? "open" : "close", {
|
|
1746
|
+
bubbles: true,
|
|
1747
|
+
composed: true,
|
|
1748
|
+
}));
|
|
1749
|
+
}
|
|
1738
1750
|
}
|
|
1739
1751
|
|
|
1740
1752
|
// -------------------------------------------------------------------------
|
|
@@ -1749,6 +1761,16 @@ class YumeMenu extends HTMLElement {
|
|
|
1749
1761
|
get direction() { return this.getAttribute("direction") || "down"; }
|
|
1750
1762
|
set direction(val) { this.setAttribute("direction", val); }
|
|
1751
1763
|
|
|
1764
|
+
/**
|
|
1765
|
+
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
1766
|
+
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
1767
|
+
*/
|
|
1768
|
+
get history() { return this.getAttribute("history"); }
|
|
1769
|
+
set history(val) {
|
|
1770
|
+
if (val != null) this.setAttribute("history", val);
|
|
1771
|
+
else this.removeAttribute("history");
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1752
1774
|
/** Menu items array (JSON attribute). */
|
|
1753
1775
|
get items() {
|
|
1754
1776
|
try {
|
|
@@ -1761,16 +1783,6 @@ class YumeMenu extends HTMLElement {
|
|
|
1761
1783
|
this.setAttribute("items", Array.isArray(val) ? JSON.stringify(val) : (val ?? "[]"));
|
|
1762
1784
|
}
|
|
1763
1785
|
|
|
1764
|
-
/**
|
|
1765
|
-
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
1766
|
-
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
1767
|
-
*/
|
|
1768
|
-
get history() { return this.getAttribute("history"); }
|
|
1769
|
-
set history(val) {
|
|
1770
|
-
if (val != null) this.setAttribute("history", val);
|
|
1771
|
-
else this.removeAttribute("history");
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
1786
|
/** Size: "small" | "medium" | "large" (default "medium"). */
|
|
1775
1787
|
get size() {
|
|
1776
1788
|
const sz = this.getAttribute("size");
|
|
@@ -1794,63 +1806,94 @@ class YumeMenu extends HTMLElement {
|
|
|
1794
1806
|
render() {
|
|
1795
1807
|
this.shadowRoot.innerHTML = "";
|
|
1796
1808
|
|
|
1797
|
-
const style =
|
|
1798
|
-
|
|
1799
|
-
this.
|
|
1809
|
+
const style = createElement("style", {}, [this._buildStyles()]);
|
|
1810
|
+
|
|
1811
|
+
const root = this._createMenuList(this.items);
|
|
1812
|
+
root.classList.add("menu");
|
|
1813
|
+
root.setAttribute("role", "menu");
|
|
1814
|
+
root.setAttribute("part", "menu");
|
|
1800
1815
|
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
rootUl.setAttribute("part", "menu");
|
|
1816
|
+
const childSlot = createElement("slot");
|
|
1817
|
+
childSlot.addEventListener("slotchange", () => this._processSlottedItems());
|
|
1818
|
+
root.appendChild(childSlot);
|
|
1805
1819
|
|
|
1806
|
-
this.shadowRoot.appendChild(
|
|
1820
|
+
this.shadowRoot.appendChild(style);
|
|
1821
|
+
this.shadowRoot.appendChild(root);
|
|
1822
|
+
this._processSlottedItems();
|
|
1807
1823
|
}
|
|
1808
1824
|
|
|
1809
1825
|
// -------------------------------------------------------------------------
|
|
1810
1826
|
// Private
|
|
1811
1827
|
// -------------------------------------------------------------------------
|
|
1812
1828
|
|
|
1829
|
+
_activateItem(item) {
|
|
1830
|
+
if (item.children?.length > 0) return;
|
|
1831
|
+
|
|
1832
|
+
this._dispatchSelect({
|
|
1833
|
+
value: item.value ?? item.text,
|
|
1834
|
+
item,
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
const href = item.href ?? item.url;
|
|
1838
|
+
if (href) this._navigateTo(href);
|
|
1839
|
+
|
|
1840
|
+
this.visible = false;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
_activateSlottedItem(el) {
|
|
1844
|
+
this._dispatchSelect({
|
|
1845
|
+
value: el.dataset.value ?? el.textContent.trim(),
|
|
1846
|
+
element: el,
|
|
1847
|
+
});
|
|
1848
|
+
this.visible = false;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1813
1851
|
_buildStyles() {
|
|
1814
1852
|
const paddingVar = `var(--component-button-padding-${this.size}, 0.5rem)`;
|
|
1815
1853
|
return `
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
list-style: none;
|
|
1819
|
-
margin: 0;
|
|
1820
|
-
padding: 0;
|
|
1854
|
+
.menu,
|
|
1855
|
+
.submenu {
|
|
1821
1856
|
background: var(--component-menu-background, #0c0c0d);
|
|
1822
1857
|
border: var(--component-menu-border-width, 1px) solid var(--component-menu-border-color, #37383a);
|
|
1823
1858
|
border-radius: var(--component-menu-border-radius, 4px);
|
|
1824
1859
|
box-shadow: var(--component-menu-shadow, 0 2px 8px rgba(0, 0, 0, 0.15));
|
|
1825
1860
|
min-width: 150px;
|
|
1861
|
+
display: flex;
|
|
1862
|
+
flex-direction: column;
|
|
1826
1863
|
}
|
|
1827
1864
|
|
|
1828
|
-
|
|
1865
|
+
.menuitem,
|
|
1866
|
+
::slotted(:not([slot])) {
|
|
1829
1867
|
cursor: pointer;
|
|
1830
1868
|
padding: ${paddingVar};
|
|
1831
|
-
display: flex;
|
|
1832
|
-
align-items: center;
|
|
1833
|
-
justify-content: space-between;
|
|
1834
1869
|
white-space: nowrap;
|
|
1835
1870
|
color: var(--component-menu-color, #f7f7fa);
|
|
1836
1871
|
font-size: var(--font-size-button, 1em);
|
|
1872
|
+
box-sizing: border-box;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
.menuitem {
|
|
1876
|
+
display: flex;
|
|
1877
|
+
align-items: center;
|
|
1878
|
+
justify-content: space-between;
|
|
1837
1879
|
position: relative;
|
|
1838
1880
|
}
|
|
1839
1881
|
|
|
1840
|
-
|
|
1882
|
+
.menuitem:hover,
|
|
1883
|
+
::slotted(:not([slot]):hover) {
|
|
1841
1884
|
background: var(--component-menu-hover-background, #292a2b);
|
|
1842
1885
|
}
|
|
1843
1886
|
|
|
1844
|
-
|
|
1887
|
+
.menuitem.selected {
|
|
1845
1888
|
background: var(--component-menu-selected-background);
|
|
1846
1889
|
color: var(--component-menu-selected-color);
|
|
1847
1890
|
}
|
|
1848
1891
|
|
|
1849
|
-
|
|
1892
|
+
.menuitem.selected:hover {
|
|
1850
1893
|
background: var(--component-menu-selected-background);
|
|
1851
1894
|
}
|
|
1852
1895
|
|
|
1853
|
-
|
|
1896
|
+
.submenu {
|
|
1854
1897
|
position: absolute;
|
|
1855
1898
|
top: 0;
|
|
1856
1899
|
left: 100%;
|
|
@@ -1858,8 +1901,8 @@ class YumeMenu extends HTMLElement {
|
|
|
1858
1901
|
z-index: var(--component-menu-z-index, 1001);
|
|
1859
1902
|
}
|
|
1860
1903
|
|
|
1861
|
-
|
|
1862
|
-
display:
|
|
1904
|
+
.menuitem:hover > .submenu {
|
|
1905
|
+
display: flex;
|
|
1863
1906
|
}
|
|
1864
1907
|
|
|
1865
1908
|
.submenu-indicator {
|
|
@@ -1869,13 +1912,11 @@ class YumeMenu extends HTMLElement {
|
|
|
1869
1912
|
opacity: 0.6;
|
|
1870
1913
|
}
|
|
1871
1914
|
|
|
1872
|
-
.submenu-indicator svg {
|
|
1873
|
-
width: 16px;
|
|
1874
|
-
height: 16px;
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
1915
|
.item-content {
|
|
1878
1916
|
flex: 1;
|
|
1917
|
+
display: inline-flex;
|
|
1918
|
+
align-items: center;
|
|
1919
|
+
gap: 0.5rem;
|
|
1879
1920
|
}
|
|
1880
1921
|
`;
|
|
1881
1922
|
}
|
|
@@ -1888,91 +1929,140 @@ class YumeMenu extends HTMLElement {
|
|
|
1888
1929
|
});
|
|
1889
1930
|
}
|
|
1890
1931
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1932
|
+
_computeMenuOffset(direction, anchorRect, menuRect, vw, vh) {
|
|
1933
|
+
if (direction === "right") {
|
|
1934
|
+
let top = anchorRect.top;
|
|
1935
|
+
let left = anchorRect.right;
|
|
1936
|
+
if (left + menuRect.width > vw) left = anchorRect.left - menuRect.width;
|
|
1937
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1938
|
+
return { top, left };
|
|
1939
|
+
}
|
|
1893
1940
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
const contentWrapper = document.createElement("span");
|
|
1903
|
-
contentWrapper.className = "item-content";
|
|
1904
|
-
|
|
1905
|
-
if (item["icon-template"]) {
|
|
1906
|
-
const iconTpl = this._findTemplate(item["icon-template"]);
|
|
1907
|
-
if (iconTpl)
|
|
1908
|
-
contentWrapper.appendChild(iconTpl.content.cloneNode(true));
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
if (item.template) {
|
|
1912
|
-
const textTpl = this._findTemplate(item.template);
|
|
1913
|
-
if (textTpl) {
|
|
1914
|
-
contentWrapper.appendChild(textTpl.content.cloneNode(true));
|
|
1915
|
-
} else {
|
|
1916
|
-
contentWrapper.append(item.text);
|
|
1917
|
-
}
|
|
1918
|
-
} else {
|
|
1919
|
-
contentWrapper.append(item.text);
|
|
1920
|
-
}
|
|
1941
|
+
if (direction === "up") {
|
|
1942
|
+
let top = anchorRect.top - menuRect.height;
|
|
1943
|
+
let left = anchorRect.left;
|
|
1944
|
+
if (top < 0) top = anchorRect.bottom;
|
|
1945
|
+
if (left + menuRect.width > vw) left = vw - menuRect.width - 10;
|
|
1946
|
+
return { top, left };
|
|
1947
|
+
}
|
|
1921
1948
|
|
|
1922
|
-
|
|
1949
|
+
if (direction === "left") {
|
|
1950
|
+
let top = anchorRect.top;
|
|
1951
|
+
let left = anchorRect.left - menuRect.width;
|
|
1952
|
+
if (left < 0) left = anchorRect.right;
|
|
1953
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1954
|
+
return { top, left };
|
|
1955
|
+
}
|
|
1923
1956
|
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
});
|
|
1932
|
-
const cancelled = !this.dispatchEvent(event);
|
|
1933
|
-
if (cancelled) return;
|
|
1934
|
-
if (this.getAttribute("history") !== "false") {
|
|
1935
|
-
history.pushState({}, "", item.url);
|
|
1936
|
-
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
1937
|
-
} else {
|
|
1938
|
-
window.location.href = item.url;
|
|
1939
|
-
}
|
|
1940
|
-
});
|
|
1941
|
-
}
|
|
1957
|
+
// "down" (default)
|
|
1958
|
+
let top = anchorRect.bottom;
|
|
1959
|
+
let left = anchorRect.left;
|
|
1960
|
+
if (top + menuRect.height > vh) top = anchorRect.top - menuRect.height;
|
|
1961
|
+
if (left + menuRect.width > vw) left = vw - menuRect.width - 10;
|
|
1962
|
+
return { top, left };
|
|
1963
|
+
}
|
|
1942
1964
|
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
}
|
|
1965
|
+
_createItemContent(item) {
|
|
1966
|
+
const wrapper = createElement("span", { class: "item-content" });
|
|
1967
|
+
|
|
1968
|
+
if (item.icon) {
|
|
1969
|
+
wrapper.appendChild(createElement("y-icon", { name: item.icon, size: this.size }));
|
|
1970
|
+
} else if (item["icon-template"]) {
|
|
1971
|
+
YumeMenu._warnTemplateFieldDeprecated();
|
|
1972
|
+
const tpl = this._findTemplate(item["icon-template"]);
|
|
1973
|
+
if (tpl) wrapper.appendChild(tpl.content.cloneNode(true));
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
if (item.template) {
|
|
1977
|
+
YumeMenu._warnTemplateFieldDeprecated();
|
|
1978
|
+
const tpl = this._findTemplate(item.template);
|
|
1979
|
+
if (tpl) wrapper.appendChild(tpl.content.cloneNode(true));
|
|
1980
|
+
else wrapper.append(item.text ?? "");
|
|
1981
|
+
} else {
|
|
1982
|
+
wrapper.append(item.text ?? "");
|
|
1983
|
+
}
|
|
1948
1984
|
|
|
1949
|
-
|
|
1950
|
-
const indicator = document.createElement("span");
|
|
1951
|
-
indicator.className = "submenu-indicator";
|
|
1952
|
-
indicator.innerHTML = chevronRight;
|
|
1953
|
-
li.appendChild(indicator);
|
|
1985
|
+
if (!item.slot) return wrapper;
|
|
1954
1986
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
}
|
|
1987
|
+
const slotEl = createElement("slot", { name: item.slot });
|
|
1988
|
+
slotEl.appendChild(wrapper);
|
|
1989
|
+
return slotEl;
|
|
1990
|
+
}
|
|
1960
1991
|
|
|
1961
|
-
|
|
1992
|
+
_createMenuItem(item) {
|
|
1993
|
+
const isSelected = !!item.selected;
|
|
1994
|
+
const partValue = isSelected ? "menuitem selected" : "menuitem";
|
|
1995
|
+
|
|
1996
|
+
const itemEl = createElement("div", {
|
|
1997
|
+
class: partValue,
|
|
1998
|
+
role: "menuitem",
|
|
1999
|
+
part: partValue,
|
|
2000
|
+
"aria-current": isSelected ? "true" : "false",
|
|
2001
|
+
tabindex: "0",
|
|
1962
2002
|
});
|
|
1963
2003
|
|
|
1964
|
-
|
|
2004
|
+
itemEl.appendChild(this._createItemContent(item));
|
|
2005
|
+
|
|
2006
|
+
if (item.url && !item.href) YumeMenu._warnUrlDeprecated();
|
|
2007
|
+
|
|
2008
|
+
itemEl.addEventListener("click", () => this._activateItem(item));
|
|
2009
|
+
|
|
2010
|
+
if (item.children?.length > 0) {
|
|
2011
|
+
itemEl.appendChild(this._createSubmenuIndicator());
|
|
2012
|
+
|
|
2013
|
+
const submenu = this._createMenuList(item.children);
|
|
2014
|
+
submenu.classList.add("submenu");
|
|
2015
|
+
submenu.setAttribute("role", "menu");
|
|
2016
|
+
itemEl.appendChild(submenu);
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
return itemEl;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
_createMenuList(items) {
|
|
2023
|
+
const container = createElement("div");
|
|
2024
|
+
items.forEach((item) => container.appendChild(this._createMenuItem(item)));
|
|
2025
|
+
return container;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
_createSubmenuIndicator() {
|
|
2029
|
+
return createElement("span", { class: "submenu-indicator" }, [
|
|
2030
|
+
createElement("y-icon", { name: "chevron-right", size: this.size }),
|
|
2031
|
+
]);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
_dispatchSelect(detail) {
|
|
2035
|
+
this.dispatchEvent(new CustomEvent("select", {
|
|
2036
|
+
detail,
|
|
2037
|
+
bubbles: true,
|
|
2038
|
+
composed: true,
|
|
2039
|
+
}));
|
|
1965
2040
|
}
|
|
1966
2041
|
|
|
1967
2042
|
_findTemplate(name) {
|
|
1968
2043
|
return this.querySelector(`template[slot="${name}"]`);
|
|
1969
2044
|
}
|
|
1970
2045
|
|
|
2046
|
+
_navigateTo(href) {
|
|
2047
|
+
const event = new CustomEvent("navigate", {
|
|
2048
|
+
bubbles: true,
|
|
2049
|
+
composed: true,
|
|
2050
|
+
cancelable: true,
|
|
2051
|
+
detail: { href },
|
|
2052
|
+
});
|
|
2053
|
+
if (!this.dispatchEvent(event)) return;
|
|
2054
|
+
|
|
2055
|
+
if (this.getAttribute("history") === "false") {
|
|
2056
|
+
window.location.href = href;
|
|
2057
|
+
} else {
|
|
2058
|
+
history.pushState({}, "", href);
|
|
2059
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1971
2063
|
_onAnchorClick(e) {
|
|
1972
2064
|
e.stopPropagation();
|
|
1973
|
-
if (!this.visible)
|
|
1974
|
-
YumeMenu._closeAll(this);
|
|
1975
|
-
}
|
|
2065
|
+
if (!this.visible) YumeMenu._closeAll(this);
|
|
1976
2066
|
this.visible = !this.visible;
|
|
1977
2067
|
}
|
|
1978
2068
|
|
|
@@ -1987,9 +2077,34 @@ class YumeMenu extends HTMLElement {
|
|
|
1987
2077
|
if (this.visible) this._updatePosition();
|
|
1988
2078
|
}
|
|
1989
2079
|
|
|
2080
|
+
_processSlottedItems() {
|
|
2081
|
+
const slot = this.shadowRoot.querySelector(".menu > slot");
|
|
2082
|
+
if (!slot) return;
|
|
2083
|
+
|
|
2084
|
+
const assigned = new Set(slot.assignedElements());
|
|
2085
|
+
|
|
2086
|
+
for (const [el, handler] of this._slottedHandlers) {
|
|
2087
|
+
if (assigned.has(el)) continue;
|
|
2088
|
+
el.removeEventListener("click", handler);
|
|
2089
|
+
this._slottedHandlers.delete(el);
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
for (const el of assigned) {
|
|
2093
|
+
if (this._slottedHandlers.has(el)) continue;
|
|
2094
|
+
|
|
2095
|
+
if (!el.hasAttribute("role")) el.setAttribute("role", "menuitem");
|
|
2096
|
+
if (el.tabIndex < 0) el.tabIndex = 0;
|
|
2097
|
+
|
|
2098
|
+
const handler = () => this._activateSlottedItem(el);
|
|
2099
|
+
el.addEventListener("click", handler);
|
|
2100
|
+
this._slottedHandlers.set(el, handler);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
|
|
1990
2104
|
_setupAnchor() {
|
|
1991
2105
|
const id = this.anchor;
|
|
1992
2106
|
if (!id) return;
|
|
2107
|
+
|
|
1993
2108
|
const root = this.getRootNode();
|
|
1994
2109
|
this._anchorResolveDispose = resolveAnchor(
|
|
1995
2110
|
this,
|
|
@@ -2013,6 +2128,13 @@ class YumeMenu extends HTMLElement {
|
|
|
2013
2128
|
}
|
|
2014
2129
|
}
|
|
2015
2130
|
|
|
2131
|
+
_teardownSlottedItems() {
|
|
2132
|
+
for (const [el, handler] of this._slottedHandlers) {
|
|
2133
|
+
el.removeEventListener("click", handler);
|
|
2134
|
+
}
|
|
2135
|
+
this._slottedHandlers.clear();
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2016
2138
|
_updatePosition() {
|
|
2017
2139
|
if (!this.visible || !this._anchorEl) {
|
|
2018
2140
|
this.style.display = "none";
|
|
@@ -2021,7 +2143,7 @@ class YumeMenu extends HTMLElement {
|
|
|
2021
2143
|
|
|
2022
2144
|
const anchorRect = this._anchorEl.getBoundingClientRect();
|
|
2023
2145
|
|
|
2024
|
-
//
|
|
2146
|
+
// Measure menu off-screen so we know its size before placing it.
|
|
2025
2147
|
this.style.visibility = "hidden";
|
|
2026
2148
|
this.style.display = "block";
|
|
2027
2149
|
const menuRect = this.getBoundingClientRect();
|
|
@@ -2029,58 +2151,38 @@ class YumeMenu extends HTMLElement {
|
|
|
2029
2151
|
|
|
2030
2152
|
const vw = window.innerWidth;
|
|
2031
2153
|
const vh = window.innerHeight;
|
|
2154
|
+
const { top, left } = this._computeMenuOffset(
|
|
2155
|
+
this.direction,
|
|
2156
|
+
anchorRect,
|
|
2157
|
+
menuRect,
|
|
2158
|
+
vw,
|
|
2159
|
+
vh,
|
|
2160
|
+
);
|
|
2032
2161
|
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
if (this.direction === "right") {
|
|
2036
|
-
top = anchorRect.top;
|
|
2037
|
-
left = anchorRect.right;
|
|
2038
|
-
|
|
2039
|
-
if (left + menuRect.width > vw) {
|
|
2040
|
-
left = anchorRect.left - menuRect.width;
|
|
2041
|
-
}
|
|
2042
|
-
if (top + menuRect.height > vh) {
|
|
2043
|
-
top = anchorRect.top - menuRect.height;
|
|
2044
|
-
}
|
|
2045
|
-
} else if (this.direction === "up") {
|
|
2046
|
-
top = anchorRect.top - menuRect.height;
|
|
2047
|
-
left = anchorRect.left;
|
|
2048
|
-
|
|
2049
|
-
if (top < 0) {
|
|
2050
|
-
top = anchorRect.bottom;
|
|
2051
|
-
}
|
|
2052
|
-
if (left + menuRect.width > vw) {
|
|
2053
|
-
left = vw - menuRect.width - 10;
|
|
2054
|
-
}
|
|
2055
|
-
} else if (this.direction === "left") {
|
|
2056
|
-
top = anchorRect.top;
|
|
2057
|
-
left = anchorRect.left - menuRect.width;
|
|
2162
|
+
const clampedTop = Math.max(0, Math.min(top, vh - menuRect.height));
|
|
2163
|
+
const clampedLeft = Math.max(0, Math.min(left, vw - menuRect.width));
|
|
2058
2164
|
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
top = anchorRect.top - menuRect.height;
|
|
2064
|
-
}
|
|
2065
|
-
} else {
|
|
2066
|
-
// "down" (default)
|
|
2067
|
-
top = anchorRect.bottom;
|
|
2068
|
-
left = anchorRect.left;
|
|
2069
|
-
|
|
2070
|
-
if (top + menuRect.height > vh) {
|
|
2071
|
-
top = anchorRect.top - menuRect.height;
|
|
2072
|
-
}
|
|
2073
|
-
if (left + menuRect.width > vw) {
|
|
2074
|
-
left = vw - menuRect.width - 10;
|
|
2075
|
-
}
|
|
2076
|
-
}
|
|
2165
|
+
this.style.top = `${clampedTop}px`;
|
|
2166
|
+
this.style.left = `${clampedLeft}px`;
|
|
2167
|
+
this.style.display = "block";
|
|
2168
|
+
}
|
|
2077
2169
|
|
|
2078
|
-
|
|
2079
|
-
|
|
2170
|
+
static _warnTemplateFieldDeprecated() {
|
|
2171
|
+
if (YumeMenu._templateFieldDeprecationWarned) return;
|
|
2172
|
+
YumeMenu._templateFieldDeprecationWarned = true;
|
|
2173
|
+
// eslint-disable-next-line no-console
|
|
2174
|
+
console.warn(
|
|
2175
|
+
"[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.",
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2080
2178
|
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2179
|
+
static _warnUrlDeprecated() {
|
|
2180
|
+
if (YumeMenu._urlDeprecationWarned) return;
|
|
2181
|
+
YumeMenu._urlDeprecationWarned = true;
|
|
2182
|
+
// eslint-disable-next-line no-console
|
|
2183
|
+
console.warn(
|
|
2184
|
+
"[y-menu] item.url is deprecated; use item.href instead. Support for item.url will be removed in a future release.",
|
|
2185
|
+
);
|
|
2084
2186
|
}
|
|
2085
2187
|
}
|
|
2086
2188
|
|
|
@@ -2138,6 +2240,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
2138
2240
|
this._idCounter = 0;
|
|
2139
2241
|
this._mql = null;
|
|
2140
2242
|
this._isMobile = false;
|
|
2243
|
+
this._mobileOutsideClick = null;
|
|
2141
2244
|
}
|
|
2142
2245
|
|
|
2143
2246
|
connectedCallback() {
|
|
@@ -2147,6 +2250,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
2147
2250
|
|
|
2148
2251
|
disconnectedCallback() {
|
|
2149
2252
|
this._teardownMediaQuery();
|
|
2253
|
+
this._teardownMobileOutsideClick();
|
|
2150
2254
|
}
|
|
2151
2255
|
|
|
2152
2256
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
@@ -2162,12 +2266,26 @@ class YumeAppbar extends HTMLElement {
|
|
|
2162
2266
|
// -------------------------------------------------------------------------
|
|
2163
2267
|
|
|
2164
2268
|
/** Whether the sidebar is currently collapsed. */
|
|
2165
|
-
get collapsed() {
|
|
2269
|
+
get collapsed() {
|
|
2270
|
+
return this.hasAttribute("collapsed");
|
|
2271
|
+
}
|
|
2166
2272
|
set collapsed(val) {
|
|
2167
2273
|
if (val) this.setAttribute("collapsed", "");
|
|
2168
2274
|
else this.removeAttribute("collapsed");
|
|
2169
2275
|
}
|
|
2170
2276
|
|
|
2277
|
+
/**
|
|
2278
|
+
* Navigation mode: omit for pushState (SPA-friendly), set to "false" for full-page navigation.
|
|
2279
|
+
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
2280
|
+
*/
|
|
2281
|
+
get history() {
|
|
2282
|
+
return this.getAttribute("history");
|
|
2283
|
+
}
|
|
2284
|
+
set history(val) {
|
|
2285
|
+
if (val != null) this.setAttribute("history", val);
|
|
2286
|
+
else this.removeAttribute("history");
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2171
2289
|
/** Nav items array parsed from the "items" attribute. */
|
|
2172
2290
|
get items() {
|
|
2173
2291
|
try {
|
|
@@ -2176,47 +2294,53 @@ class YumeAppbar extends HTMLElement {
|
|
|
2176
2294
|
return [];
|
|
2177
2295
|
}
|
|
2178
2296
|
}
|
|
2179
|
-
set items(val) {
|
|
2297
|
+
set items(val) {
|
|
2298
|
+
this.setAttribute("items", JSON.stringify(val));
|
|
2299
|
+
}
|
|
2180
2300
|
|
|
2181
2301
|
/**
|
|
2182
2302
|
* Direction menus pop out from nav buttons:
|
|
2183
2303
|
* "right", "down", or unset (auto: vertical → right, horizontal → down).
|
|
2184
2304
|
*/
|
|
2185
|
-
get menuDirection() {
|
|
2305
|
+
get menuDirection() {
|
|
2306
|
+
return this.getAttribute("menu-direction") || "";
|
|
2307
|
+
}
|
|
2186
2308
|
set menuDirection(val) {
|
|
2187
2309
|
if (val) this.setAttribute("menu-direction", val);
|
|
2188
2310
|
else this.removeAttribute("menu-direction");
|
|
2189
2311
|
}
|
|
2190
2312
|
|
|
2191
2313
|
/** Whether the appbar is currently rendering in mobile mode. */
|
|
2192
|
-
get mobile() {
|
|
2314
|
+
get mobile() {
|
|
2315
|
+
return this._isMobile;
|
|
2316
|
+
}
|
|
2193
2317
|
|
|
2194
2318
|
/**
|
|
2195
2319
|
* Override the mobile breakpoint (in pixels) for this instance.
|
|
2196
2320
|
* Falls back to the CSS variable --component-appbar-mobile-breakpoint (default 768).
|
|
2197
2321
|
*/
|
|
2198
|
-
get mobileBreakpoint() {
|
|
2322
|
+
get mobileBreakpoint() {
|
|
2323
|
+
return this.getAttribute("mobile-breakpoint") || "";
|
|
2324
|
+
}
|
|
2199
2325
|
set mobileBreakpoint(val) {
|
|
2200
2326
|
if (val) this.setAttribute("mobile-breakpoint", val);
|
|
2201
2327
|
else this.removeAttribute("mobile-breakpoint");
|
|
2202
2328
|
}
|
|
2203
2329
|
|
|
2204
2330
|
/** Layout orientation: "vertical" | "horizontal" (default "vertical"). */
|
|
2205
|
-
get orientation() {
|
|
2206
|
-
|
|
2331
|
+
get orientation() {
|
|
2332
|
+
return this.getAttribute("orientation") || "vertical";
|
|
2333
|
+
}
|
|
2334
|
+
set orientation(val) {
|
|
2335
|
+
this.setAttribute("orientation", val);
|
|
2336
|
+
}
|
|
2207
2337
|
|
|
2208
2338
|
/** Size variant: "small" | "medium" | "large" (default "medium"). */
|
|
2209
|
-
get size() {
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
* Regardless of this setting, a cancelable "navigate" event is always dispatched first.
|
|
2215
|
-
*/
|
|
2216
|
-
get history() { return this.getAttribute("history"); }
|
|
2217
|
-
set history(val) {
|
|
2218
|
-
if (val != null) this.setAttribute("history", val);
|
|
2219
|
-
else this.removeAttribute("history");
|
|
2339
|
+
get size() {
|
|
2340
|
+
return this.getAttribute("size") || "medium";
|
|
2341
|
+
}
|
|
2342
|
+
set size(val) {
|
|
2343
|
+
this.setAttribute("size", val);
|
|
2220
2344
|
}
|
|
2221
2345
|
|
|
2222
2346
|
/** Sticky position: "start" | "end" | false. */
|
|
@@ -2233,201 +2357,88 @@ class YumeAppbar extends HTMLElement {
|
|
|
2233
2357
|
// Public
|
|
2234
2358
|
// -------------------------------------------------------------------------
|
|
2235
2359
|
|
|
2360
|
+
render() {
|
|
2361
|
+
if (this._isMobile) this._renderMobile();
|
|
2362
|
+
else this._renderDesktop();
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2236
2365
|
/** Toggles the collapsed state of the sidebar. */
|
|
2237
2366
|
toggle() {
|
|
2238
2367
|
this.collapsed = !this.collapsed;
|
|
2239
2368
|
}
|
|
2240
2369
|
|
|
2241
|
-
render() {
|
|
2242
|
-
if (this._isMobile) {
|
|
2243
|
-
this._renderMobile();
|
|
2244
|
-
} else {
|
|
2245
|
-
this._renderDesktop();
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
2370
|
// -------------------------------------------------------------------------
|
|
2250
2371
|
// Private
|
|
2251
2372
|
// -------------------------------------------------------------------------
|
|
2252
2373
|
|
|
2253
|
-
|
|
2254
|
-
const
|
|
2255
|
-
btn.setAttribute("color", "base");
|
|
2256
|
-
btn.setAttribute("style-type", "flat");
|
|
2257
|
-
btn.setAttribute("size", cfg.buttonSize);
|
|
2258
|
-
btn.setAttribute("aria-label", isCollapsed ? "Expand sidebar" : "Collapse sidebar");
|
|
2259
|
-
btn.className = "collapse-btn";
|
|
2260
|
-
|
|
2261
|
-
const icon = document.createElement("span");
|
|
2262
|
-
icon.slot = "left-icon";
|
|
2263
|
-
icon.innerHTML = isCollapsed ? expandRight : expandLeft;
|
|
2264
|
-
btn.appendChild(icon);
|
|
2265
|
-
|
|
2266
|
-
if (!isCollapsed) {
|
|
2267
|
-
btn.appendChild(document.createTextNode("Collapse"));
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
btn.addEventListener("click", this._onCollapseClick);
|
|
2271
|
-
return btn;
|
|
2272
|
-
}
|
|
2273
|
-
|
|
2274
|
-
_buildHeader() {
|
|
2275
|
-
const header = document.createElement("div");
|
|
2276
|
-
header.className = "appbar-header";
|
|
2277
|
-
header.setAttribute("part", "header");
|
|
2278
|
-
|
|
2279
|
-
const headerContent = document.createElement("div");
|
|
2280
|
-
headerContent.className = "header-content";
|
|
2281
|
-
|
|
2282
|
-
const logoWrapper = document.createElement("div");
|
|
2283
|
-
logoWrapper.className = "logo-wrapper";
|
|
2284
|
-
logoWrapper.appendChild(this._makeSlot("logo"));
|
|
2285
|
-
headerContent.appendChild(logoWrapper);
|
|
2374
|
+
_buildBody(cfg, isCollapsed, menuDir) {
|
|
2375
|
+
const body = createElement("div", { class: "appbar-body", part: "body" });
|
|
2286
2376
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
header.appendChild(this._makeSlot("header"));
|
|
2294
|
-
return header;
|
|
2295
|
-
}
|
|
2296
|
-
|
|
2297
|
-
_buildNavItem(item, cfg, isVertical, isCollapsed, menuDir) {
|
|
2298
|
-
const hasChildren = item.children?.length > 0;
|
|
2299
|
-
const wrapper = document.createElement("div");
|
|
2300
|
-
const btn = document.createElement("y-button");
|
|
2301
|
-
const btnId = this._uid("appbar-btn");
|
|
2302
|
-
|
|
2303
|
-
wrapper.className = "nav-item";
|
|
2304
|
-
btn.id = btnId;
|
|
2305
|
-
btn.setAttribute("color", this._isItemActive(item) ? "primary" : "base");
|
|
2306
|
-
btn.setAttribute("style-type", "flat");
|
|
2307
|
-
btn.setAttribute("size", cfg.buttonSize);
|
|
2308
|
-
|
|
2309
|
-
if (item.icon) {
|
|
2310
|
-
if (item.icon.trim().startsWith("<")) {
|
|
2311
|
-
const iconEl = document.createElement("span");
|
|
2312
|
-
iconEl.slot = "left-icon";
|
|
2313
|
-
iconEl.setAttribute("part", "icon");
|
|
2314
|
-
iconEl.innerHTML = item.icon;
|
|
2315
|
-
btn.appendChild(iconEl);
|
|
2316
|
-
} else {
|
|
2317
|
-
const iconEl = document.createElement("y-icon");
|
|
2318
|
-
iconEl.slot = "left-icon";
|
|
2319
|
-
iconEl.setAttribute("part", "icon");
|
|
2320
|
-
iconEl.setAttribute("name", item.icon);
|
|
2321
|
-
iconEl.setAttribute("size", cfg.iconSize);
|
|
2322
|
-
btn.appendChild(iconEl);
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
if (item.text && !isCollapsed) {
|
|
2327
|
-
btn.appendChild(document.createTextNode(item.text));
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
if (hasChildren && !isCollapsed) {
|
|
2331
|
-
const arrow = document.createElement("span");
|
|
2332
|
-
arrow.slot = "right-icon";
|
|
2333
|
-
arrow.innerHTML = isVertical ? chevronRight : chevronDown;
|
|
2334
|
-
btn.appendChild(arrow);
|
|
2335
|
-
}
|
|
2336
|
-
|
|
2337
|
-
if (item.href && !hasChildren) {
|
|
2338
|
-
btn.addEventListener("click", () => {
|
|
2339
|
-
const event = new CustomEvent("navigate", {
|
|
2340
|
-
bubbles: true,
|
|
2341
|
-
composed: true,
|
|
2342
|
-
cancelable: true,
|
|
2343
|
-
detail: { href: item.href },
|
|
2344
|
-
});
|
|
2345
|
-
const cancelled = !this.dispatchEvent(event);
|
|
2346
|
-
if (cancelled) return;
|
|
2347
|
-
if (this.getAttribute("history") !== "false") {
|
|
2348
|
-
history.pushState({}, "", item.href);
|
|
2349
|
-
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
2350
|
-
} else {
|
|
2351
|
-
window.location.href = item.href;
|
|
2352
|
-
}
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
if (item.slot) {
|
|
2357
|
-
const slot = this._makeSlot(item.slot);
|
|
2358
|
-
slot.appendChild(btn);
|
|
2359
|
-
wrapper.appendChild(slot);
|
|
2360
|
-
} else {
|
|
2361
|
-
wrapper.appendChild(btn);
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
if (hasChildren) {
|
|
2365
|
-
const menuEl = document.createElement("y-menu");
|
|
2366
|
-
menuEl.setAttribute("anchor", btnId);
|
|
2367
|
-
menuEl.setAttribute("direction", menuDir);
|
|
2368
|
-
menuEl.setAttribute("size", cfg.buttonSize);
|
|
2369
|
-
menuEl.items = item.children;
|
|
2370
|
-
wrapper.appendChild(menuEl);
|
|
2371
|
-
}
|
|
2372
|
-
|
|
2373
|
-
return wrapper;
|
|
2374
|
-
}
|
|
2377
|
+
this.items.forEach((item) => {
|
|
2378
|
+
body.appendChild(
|
|
2379
|
+
this._buildNavItem(item, cfg, isCollapsed, menuDir),
|
|
2380
|
+
);
|
|
2381
|
+
});
|
|
2382
|
+
body.appendChild(createElement("slot", {}));
|
|
2375
2383
|
|
|
2376
|
-
|
|
2377
|
-
const attr = this.mobileBreakpoint;
|
|
2378
|
-
if (attr) {
|
|
2379
|
-
const px = parseInt(attr, 10);
|
|
2380
|
-
if (!isNaN(px) && px > 0) return px;
|
|
2381
|
-
}
|
|
2382
|
-
const cssVal = getComputedStyle(document.documentElement)
|
|
2383
|
-
.getPropertyValue("--component-appbar-mobile-breakpoint")
|
|
2384
|
-
.trim();
|
|
2385
|
-
if (cssVal) {
|
|
2386
|
-
const px = parseInt(cssVal, 10);
|
|
2387
|
-
if (!isNaN(px) && px > 0) return px;
|
|
2388
|
-
}
|
|
2389
|
-
return 768;
|
|
2384
|
+
return body;
|
|
2390
2385
|
}
|
|
2391
2386
|
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2387
|
+
_buildCollapseButton(cfg, isCollapsed) {
|
|
2388
|
+
const icon = createElement("y-icon", {
|
|
2389
|
+
slot: "left-icon",
|
|
2390
|
+
name: isCollapsed ? "expand-right" : "expand-left",
|
|
2391
|
+
size: cfg.iconSize,
|
|
2392
|
+
});
|
|
2396
2393
|
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2394
|
+
const children = isCollapsed ? [icon] : [icon, "Collapse"];
|
|
2395
|
+
const btn = createElement(
|
|
2396
|
+
"y-button",
|
|
2397
|
+
{
|
|
2398
|
+
class: "collapse-btn",
|
|
2399
|
+
color: "base",
|
|
2400
|
+
"style-type": "flat",
|
|
2401
|
+
size: cfg.buttonSize,
|
|
2402
|
+
"aria-label": isCollapsed
|
|
2403
|
+
? "Expand sidebar"
|
|
2404
|
+
: "Collapse sidebar",
|
|
2405
|
+
},
|
|
2406
|
+
children,
|
|
2407
|
+
);
|
|
2406
2408
|
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
slot.name = name;
|
|
2410
|
-
return slot;
|
|
2409
|
+
btn.addEventListener("click", this._onCollapseClick);
|
|
2410
|
+
return btn;
|
|
2411
2411
|
}
|
|
2412
2412
|
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2413
|
+
_buildDesktopBar(cfg, isVertical, isCollapsed, menuDir) {
|
|
2414
|
+
const classes = ["appbar", isVertical ? "vertical" : "horizontal"];
|
|
2415
|
+
if (isCollapsed) classes.push("collapsed");
|
|
2416
2416
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2417
|
+
const bar = createElement("div", {
|
|
2418
|
+
class: classes.join(" "),
|
|
2419
|
+
role: "navigation",
|
|
2420
|
+
});
|
|
2421
2421
|
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2422
|
+
// Layout-specific sizing tokens consumed by the stylesheet.
|
|
2423
|
+
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))`;
|
|
2424
|
+
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
2425
|
+
bar.style.setProperty("--_appbar-collapsed-width", cfg.collapsedWidth);
|
|
2426
|
+
bar.style.setProperty("--_appbar-body-gap", cfg.bodyGap);
|
|
2427
|
+
bar.style.setProperty(
|
|
2428
|
+
"--_button-padding",
|
|
2429
|
+
`var(--component-button-padding-${cfg.buttonSize})`,
|
|
2430
|
+
);
|
|
2431
|
+
bar.style.setProperty("--_icon-col-width", iconColWidth);
|
|
2428
2432
|
|
|
2429
|
-
|
|
2430
|
-
|
|
2433
|
+
bar.appendChild(this._buildHeader());
|
|
2434
|
+
bar.appendChild(this._buildBody(cfg, isCollapsed, menuDir));
|
|
2435
|
+
bar.appendChild(this._buildFooter(cfg, isVertical, isCollapsed));
|
|
2436
|
+
|
|
2437
|
+
return bar;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
_buildDesktopStyles() {
|
|
2441
|
+
return `
|
|
2431
2442
|
:host {
|
|
2432
2443
|
display: block;
|
|
2433
2444
|
font-family: var(--font-family-body, sans-serif);
|
|
@@ -2655,55 +2666,148 @@ class YumeAppbar extends HTMLElement {
|
|
|
2655
2666
|
::slotted(*) {
|
|
2656
2667
|
display: block;
|
|
2657
2668
|
}
|
|
2669
|
+
.appbar.vertical ::slotted(:not([slot])) {
|
|
2670
|
+
width: 100%;
|
|
2671
|
+
}
|
|
2672
|
+
.appbar.horizontal ::slotted(:not([slot])) {
|
|
2673
|
+
display: inline-flex;
|
|
2674
|
+
align-items: center;
|
|
2675
|
+
}
|
|
2676
|
+
.appbar.vertical.collapsed ::slotted(:not([slot])) {
|
|
2677
|
+
width: var(--_icon-col-width);
|
|
2678
|
+
overflow: hidden;
|
|
2679
|
+
}
|
|
2658
2680
|
span[slot="left-icon"] svg,
|
|
2659
2681
|
span[slot="right-icon"] svg {
|
|
2660
2682
|
width: var(--component-icon-size-large, 1.25em);
|
|
2661
2683
|
height: var(--component-icon-size-large, 1.25em);
|
|
2662
2684
|
}
|
|
2663
2685
|
`;
|
|
2664
|
-
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
_buildFooter(cfg, isVertical, isCollapsed) {
|
|
2689
|
+
const footer = createElement("div", { class: "appbar-footer", part: "footer" });
|
|
2690
|
+
footer.appendChild(createElement("slot", { name: "footer" }));
|
|
2691
|
+
|
|
2692
|
+
if (isVertical) {
|
|
2693
|
+
footer.appendChild(this._buildCollapseButton(cfg, isCollapsed));
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
return footer;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
_buildHeader() {
|
|
2700
|
+
const logoWrapper = createElement("div", { class: "logo-wrapper" }, [
|
|
2701
|
+
createElement("slot", { name: "logo" }),
|
|
2702
|
+
]);
|
|
2703
|
+
const titleWrapper = createElement("div", { class: "header-title" }, [
|
|
2704
|
+
createElement("slot", { name: "title" }),
|
|
2705
|
+
]);
|
|
2706
|
+
const headerContent = createElement("div", { class: "header-content" }, [
|
|
2707
|
+
logoWrapper,
|
|
2708
|
+
titleWrapper,
|
|
2709
|
+
]);
|
|
2710
|
+
|
|
2711
|
+
return createElement("div", { class: "appbar-header", part: "header" }, [
|
|
2712
|
+
headerContent,
|
|
2713
|
+
createElement("slot", { name: "header" }),
|
|
2714
|
+
]);
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
_buildItemIcon(iconValue, cfg) {
|
|
2718
|
+
// Raw SVG markup is preserved as a public escape hatch; everything else
|
|
2719
|
+
// routes through y-icon for consistent sizing/theming.
|
|
2720
|
+
if (iconValue.trim().startsWith("<")) {
|
|
2721
|
+
const span = createElement("span", { slot: "left-icon", part: "icon" });
|
|
2722
|
+
span.innerHTML = iconValue;
|
|
2723
|
+
return span;
|
|
2724
|
+
}
|
|
2725
|
+
return createElement("y-icon", {
|
|
2726
|
+
slot: "left-icon",
|
|
2727
|
+
part: "icon",
|
|
2728
|
+
name: iconValue,
|
|
2729
|
+
size: cfg.iconSize,
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2665
2732
|
|
|
2666
|
-
|
|
2667
|
-
bar
|
|
2668
|
-
if (isCollapsed) bar.classList.add("collapsed");
|
|
2669
|
-
bar.setAttribute("role", "navigation");
|
|
2733
|
+
_buildMobileBar(cfg) {
|
|
2734
|
+
const bar = createElement("div", { class: "appbar", role: "navigation" });
|
|
2670
2735
|
bar.style.setProperty("--_appbar-padding", cfg.padding);
|
|
2671
|
-
bar.style.setProperty("--_appbar-collapsed-width", cfg.collapsedWidth);
|
|
2672
|
-
bar.style.setProperty("--_appbar-body-gap", cfg.bodyGap);
|
|
2673
|
-
bar.style.setProperty("--_button-padding", `var(--component-button-padding-${cfg.buttonSize})`);
|
|
2674
|
-
bar.style.setProperty(
|
|
2675
|
-
"--_icon-col-width",
|
|
2676
|
-
`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))`,
|
|
2677
|
-
);
|
|
2678
2736
|
|
|
2679
|
-
bar.appendChild(this.
|
|
2737
|
+
bar.appendChild(this._buildMobileStart(cfg));
|
|
2738
|
+
bar.appendChild(this._buildMobileCenter());
|
|
2739
|
+
bar.appendChild(this._buildMobileEnd());
|
|
2680
2740
|
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2741
|
+
return bar;
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
_buildMobileCenter() {
|
|
2745
|
+
return createElement("div", { class: "mobile-center" }, [
|
|
2746
|
+
createElement("slot", { name: "logo" }),
|
|
2747
|
+
createElement("slot", { name: "title" }),
|
|
2748
|
+
]);
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
_buildMobileEnd() {
|
|
2752
|
+
return createElement("div", { class: "mobile-end", part: "footer" }, [
|
|
2753
|
+
createElement("slot", { name: "footer" }),
|
|
2754
|
+
]);
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
_buildMobileStart(cfg) {
|
|
2758
|
+
const menuBtnId = this._uid("appbar-mobile-menu");
|
|
2759
|
+
const panelId = this._uid("appbar-mobile-panel");
|
|
2760
|
+
|
|
2761
|
+
const menuBtn = createElement(
|
|
2762
|
+
"y-button",
|
|
2763
|
+
{
|
|
2764
|
+
id: menuBtnId,
|
|
2765
|
+
color: "base",
|
|
2766
|
+
"style-type": "flat",
|
|
2767
|
+
size: cfg.buttonSize,
|
|
2768
|
+
"aria-label": "Open menu",
|
|
2769
|
+
"aria-controls": panelId,
|
|
2770
|
+
"aria-expanded": "false",
|
|
2771
|
+
},
|
|
2772
|
+
[
|
|
2773
|
+
createElement("y-icon", {
|
|
2774
|
+
slot: "left-icon",
|
|
2775
|
+
name: "menu",
|
|
2776
|
+
size: cfg.iconSize,
|
|
2777
|
+
}),
|
|
2778
|
+
],
|
|
2779
|
+
);
|
|
2780
|
+
|
|
2781
|
+
const panel = createElement("div", { id: panelId, class: "mobile-panel" });
|
|
2684
2782
|
this.items.forEach((item) => {
|
|
2685
|
-
|
|
2783
|
+
panel.appendChild(this._buildNavItem(item, cfg, false, "down"));
|
|
2686
2784
|
});
|
|
2687
|
-
|
|
2785
|
+
panel.appendChild(createElement("slot", {}));
|
|
2688
2786
|
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
if (isVertical) {
|
|
2694
|
-
footer.appendChild(this._buildCollapseButton(cfg, isCollapsed));
|
|
2695
|
-
}
|
|
2696
|
-
bar.appendChild(footer);
|
|
2787
|
+
const closePanel = () => {
|
|
2788
|
+
panel.classList.remove("open");
|
|
2789
|
+
menuBtn.setAttribute("aria-expanded", "false");
|
|
2790
|
+
};
|
|
2697
2791
|
|
|
2698
|
-
|
|
2699
|
-
|
|
2792
|
+
menuBtn.addEventListener("click", (e) => {
|
|
2793
|
+
e.stopPropagation();
|
|
2794
|
+
const open = panel.classList.toggle("open");
|
|
2795
|
+
menuBtn.setAttribute("aria-expanded", open ? "true" : "false");
|
|
2796
|
+
});
|
|
2700
2797
|
|
|
2701
|
-
|
|
2702
|
-
this._initRender();
|
|
2703
|
-
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
2798
|
+
panel.addEventListener("navigate", closePanel);
|
|
2704
2799
|
|
|
2705
|
-
|
|
2706
|
-
|
|
2800
|
+
this._mobileOutsideClick = (e) => {
|
|
2801
|
+
if (e.composedPath().includes(this)) return;
|
|
2802
|
+
closePanel();
|
|
2803
|
+
};
|
|
2804
|
+
document.addEventListener("pointerdown", this._mobileOutsideClick);
|
|
2805
|
+
|
|
2806
|
+
return createElement("div", { class: "mobile-start" }, [menuBtn, panel]);
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
_buildMobileStyles() {
|
|
2810
|
+
return `
|
|
2707
2811
|
:host {
|
|
2708
2812
|
display: block;
|
|
2709
2813
|
font-family: var(--font-family-body, sans-serif);
|
|
@@ -2744,6 +2848,7 @@ class YumeAppbar extends HTMLElement {
|
|
|
2744
2848
|
display: flex;
|
|
2745
2849
|
align-items: center;
|
|
2746
2850
|
flex-shrink: 0;
|
|
2851
|
+
position: relative;
|
|
2747
2852
|
}
|
|
2748
2853
|
|
|
2749
2854
|
.mobile-center {
|
|
@@ -2760,8 +2865,38 @@ class YumeAppbar extends HTMLElement {
|
|
|
2760
2865
|
flex-shrink: 0;
|
|
2761
2866
|
}
|
|
2762
2867
|
|
|
2763
|
-
.mobile-
|
|
2868
|
+
.mobile-panel {
|
|
2869
|
+
position: absolute;
|
|
2870
|
+
top: 100%;
|
|
2871
|
+
left: 0;
|
|
2872
|
+
margin-top: 4px;
|
|
2873
|
+
background: var(--component-appbar-background, #0c0c0d);
|
|
2874
|
+
border: var(--component-appbar-border-width, var(--component-sidebar-border-width, 2px)) solid var(--component-appbar-border-color, #37383a);
|
|
2875
|
+
border-radius: var(--component-appbar-border-radius, var(--component-sidebar-border-radius, 4px));
|
|
2876
|
+
padding: var(--_appbar-padding);
|
|
2877
|
+
display: none;
|
|
2878
|
+
flex-direction: column;
|
|
2879
|
+
gap: 2px;
|
|
2880
|
+
min-width: 180px;
|
|
2881
|
+
z-index: var(--component-appbar-z-index, 100);
|
|
2882
|
+
}
|
|
2883
|
+
.mobile-panel.open {
|
|
2884
|
+
display: flex;
|
|
2885
|
+
}
|
|
2886
|
+
.mobile-panel .nav-item {
|
|
2887
|
+
display: flex;
|
|
2888
|
+
width: 100%;
|
|
2889
|
+
}
|
|
2890
|
+
.mobile-panel .nav-item y-button {
|
|
2764
2891
|
display: block;
|
|
2892
|
+
width: 100%;
|
|
2893
|
+
}
|
|
2894
|
+
.mobile-panel .nav-item y-button::part(button) {
|
|
2895
|
+
width: 100%;
|
|
2896
|
+
justify-content: flex-start;
|
|
2897
|
+
}
|
|
2898
|
+
.mobile-panel ::slotted(:not([slot])) {
|
|
2899
|
+
width: 100%;
|
|
2765
2900
|
}
|
|
2766
2901
|
|
|
2767
2902
|
::slotted(*) {
|
|
@@ -2773,56 +2908,147 @@ class YumeAppbar extends HTMLElement {
|
|
|
2773
2908
|
height: var(--component-icon-size-large, 1.25em);
|
|
2774
2909
|
}
|
|
2775
2910
|
`;
|
|
2776
|
-
|
|
2911
|
+
}
|
|
2777
2912
|
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2913
|
+
_buildNavItem(item, cfg, isCollapsed, menuDir) {
|
|
2914
|
+
const hasChildren = item.children?.length > 0;
|
|
2915
|
+
const showLabel = item.text && !isCollapsed;
|
|
2916
|
+
const showArrow = hasChildren && !isCollapsed;
|
|
2917
|
+
const btnId = this._uid("appbar-btn");
|
|
2782
2918
|
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2919
|
+
const btn = createElement("y-button", {
|
|
2920
|
+
id: btnId,
|
|
2921
|
+
color: this._isItemActive(item) ? "primary" : "base",
|
|
2922
|
+
"style-type": "flat",
|
|
2923
|
+
size: cfg.buttonSize,
|
|
2924
|
+
});
|
|
2786
2925
|
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2926
|
+
if (item.icon) btn.appendChild(this._buildItemIcon(item.icon, cfg));
|
|
2927
|
+
if (showLabel) btn.appendChild(document.createTextNode(item.text));
|
|
2928
|
+
if (showArrow) {
|
|
2929
|
+
btn.appendChild(
|
|
2930
|
+
createElement("y-icon", {
|
|
2931
|
+
slot: "right-icon",
|
|
2932
|
+
name: `chevron-${menuDir}`,
|
|
2933
|
+
size: cfg.iconSize,
|
|
2934
|
+
}),
|
|
2935
|
+
);
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
if (item.href && !hasChildren) {
|
|
2939
|
+
btn.addEventListener("click", () => this._navigateTo(item.href));
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
const wrapper = createElement("div", { class: "nav-item" });
|
|
2943
|
+
if (item.slot) {
|
|
2944
|
+
const slot = createElement("slot", { name: item.slot });
|
|
2945
|
+
slot.appendChild(btn);
|
|
2946
|
+
wrapper.appendChild(slot);
|
|
2947
|
+
} else {
|
|
2948
|
+
wrapper.appendChild(btn);
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
if (hasChildren) {
|
|
2952
|
+
const menuEl = createElement("y-menu", {
|
|
2953
|
+
anchor: btnId,
|
|
2954
|
+
direction: menuDir,
|
|
2955
|
+
size: cfg.buttonSize,
|
|
2956
|
+
});
|
|
2957
|
+
menuEl.items = item.children;
|
|
2958
|
+
wrapper.appendChild(menuEl);
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
return wrapper;
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2964
|
+
_getBreakpointPx() {
|
|
2965
|
+
const attr = this.mobileBreakpoint;
|
|
2966
|
+
if (attr) {
|
|
2967
|
+
const px = parseInt(attr, 10);
|
|
2968
|
+
if (!isNaN(px) && px > 0) return px;
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
const cssVal = getComputedStyle(document.documentElement)
|
|
2972
|
+
.getPropertyValue("--component-appbar-mobile-breakpoint")
|
|
2973
|
+
.trim();
|
|
2974
|
+
if (cssVal) {
|
|
2975
|
+
const px = parseInt(cssVal, 10);
|
|
2976
|
+
if (!isNaN(px) && px > 0) return px;
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
return 768;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
_initRender() {
|
|
2983
|
+
this._teardownMobileOutsideClick();
|
|
2984
|
+
this.shadowRoot.innerHTML = "";
|
|
2985
|
+
this._idCounter = 0;
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
_isItemActive(item) {
|
|
2989
|
+
if (item.selected) return true;
|
|
2990
|
+
if (!item.href) return false;
|
|
2991
|
+
|
|
2992
|
+
const loc = window.location;
|
|
2993
|
+
const current = loc.pathname + loc.search + loc.hash;
|
|
2994
|
+
|
|
2995
|
+
return item.href === current || item.href === loc.href;
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
_navigateTo(href) {
|
|
2999
|
+
const event = new CustomEvent("navigate", {
|
|
3000
|
+
bubbles: true,
|
|
3001
|
+
composed: true,
|
|
3002
|
+
cancelable: true,
|
|
3003
|
+
detail: { href },
|
|
3004
|
+
});
|
|
3005
|
+
if (!this.dispatchEvent(event)) return;
|
|
3006
|
+
|
|
3007
|
+
if (this.getAttribute("history") === "false") {
|
|
3008
|
+
window.location.href = href;
|
|
3009
|
+
} else {
|
|
3010
|
+
history.pushState({}, "", href);
|
|
3011
|
+
window.dispatchEvent(new PopStateEvent("popstate", { state: {} }));
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
_onCollapseClick() {
|
|
3016
|
+
this.toggle();
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
_onMediaChange(e) {
|
|
3020
|
+
this._isMobile = e.matches;
|
|
3021
|
+
this.render();
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
_renderDesktop() {
|
|
3025
|
+
this._initRender();
|
|
3026
|
+
|
|
3027
|
+
const isVertical = this.orientation === "vertical";
|
|
3028
|
+
const isCollapsed = this.collapsed && isVertical;
|
|
3029
|
+
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
3030
|
+
const menuDir = this.menuDirection || (isVertical ? "right" : "down");
|
|
3031
|
+
|
|
3032
|
+
const style = createElement("style", {}, [this._buildDesktopStyles()]);
|
|
3033
|
+
const bar = this._buildDesktopBar(
|
|
3034
|
+
cfg,
|
|
3035
|
+
isVertical,
|
|
3036
|
+
isCollapsed,
|
|
3037
|
+
menuDir,
|
|
3038
|
+
);
|
|
3039
|
+
|
|
3040
|
+
this.shadowRoot.appendChild(style);
|
|
3041
|
+
this.shadowRoot.appendChild(bar);
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
_renderMobile() {
|
|
3045
|
+
this._initRender();
|
|
3046
|
+
const cfg = SIZE_CONFIG[this.size] || SIZE_CONFIG.medium;
|
|
3047
|
+
|
|
3048
|
+
const style = createElement("style", {}, [this._buildMobileStyles()]);
|
|
3049
|
+
const bar = this._buildMobileBar(cfg);
|
|
2825
3050
|
|
|
3051
|
+
this.shadowRoot.appendChild(style);
|
|
2826
3052
|
this.shadowRoot.appendChild(bar);
|
|
2827
3053
|
}
|
|
2828
3054
|
|
|
@@ -2839,26 +3065,15 @@ class YumeAppbar extends HTMLElement {
|
|
|
2839
3065
|
}
|
|
2840
3066
|
|
|
2841
3067
|
_teardownMediaQuery() {
|
|
2842
|
-
if (this._mql)
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
}
|
|
3068
|
+
if (!this._mql) return;
|
|
3069
|
+
this._mql.removeEventListener("change", this._onMediaChange);
|
|
3070
|
+
this._mql = null;
|
|
2846
3071
|
}
|
|
2847
3072
|
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
_toMenuItems(items) {
|
|
2853
|
-
return items.map((item) => {
|
|
2854
|
-
const mi = { text: item.text || "" };
|
|
2855
|
-
if (item.href) mi.url = item.href;
|
|
2856
|
-
if (item.icon) mi.icon = item.icon;
|
|
2857
|
-
if (item.children?.length) {
|
|
2858
|
-
mi.children = this._toMenuItems(item.children);
|
|
2859
|
-
}
|
|
2860
|
-
return mi;
|
|
2861
|
-
});
|
|
3073
|
+
_teardownMobileOutsideClick() {
|
|
3074
|
+
if (!this._mobileOutsideClick) return;
|
|
3075
|
+
document.removeEventListener("pointerdown", this._mobileOutsideClick);
|
|
3076
|
+
this._mobileOutsideClick = null;
|
|
2862
3077
|
}
|
|
2863
3078
|
|
|
2864
3079
|
_uid(prefix) {
|
|
@@ -3369,6 +3584,7 @@ class YumeBadge extends HTMLElement {
|
|
|
3369
3584
|
constructor() {
|
|
3370
3585
|
super();
|
|
3371
3586
|
this.attachShadow({ mode: "open" });
|
|
3587
|
+
this._onSlotChange = this._onSlotChange.bind(this);
|
|
3372
3588
|
this.render();
|
|
3373
3589
|
}
|
|
3374
3590
|
|
|
@@ -3411,21 +3627,24 @@ class YumeBadge extends HTMLElement {
|
|
|
3411
3627
|
render() {
|
|
3412
3628
|
const [badgeColor, badgeTextColor] = this._getBadgeColors(this.color);
|
|
3413
3629
|
const { fontSize, padding, minSize } = this._getSizeAttributes(this.size);
|
|
3414
|
-
const
|
|
3415
|
-
const positionCSS = hasTarget
|
|
3416
|
-
? this._getBadgePosition(this.position, this.alignment)
|
|
3417
|
-
: "";
|
|
3630
|
+
const positionCSS = this._getBadgePosition(this.position, this.alignment);
|
|
3418
3631
|
|
|
3419
3632
|
this.shadowRoot.innerHTML = `
|
|
3420
3633
|
<style>
|
|
3421
3634
|
:host {
|
|
3422
|
-
position: ${hasTarget ? "relative" : "static"};
|
|
3423
3635
|
display: inline-flex;
|
|
3424
3636
|
align-items: center;
|
|
3425
3637
|
}
|
|
3638
|
+
.root {
|
|
3639
|
+
position: static;
|
|
3640
|
+
display: inline-flex;
|
|
3641
|
+
align-items: center;
|
|
3642
|
+
}
|
|
3643
|
+
.root.has-target {
|
|
3644
|
+
position: relative;
|
|
3645
|
+
}
|
|
3426
3646
|
.badge {
|
|
3427
|
-
position:
|
|
3428
|
-
${positionCSS}
|
|
3647
|
+
position: static;
|
|
3429
3648
|
background: ${badgeColor};
|
|
3430
3649
|
color: ${badgeTextColor};
|
|
3431
3650
|
font-size: ${fontSize};
|
|
@@ -3440,14 +3659,24 @@ class YumeBadge extends HTMLElement {
|
|
|
3440
3659
|
height: ${minSize};
|
|
3441
3660
|
z-index: 20;
|
|
3442
3661
|
}
|
|
3662
|
+
.root.has-target .badge {
|
|
3663
|
+
position: absolute;
|
|
3664
|
+
${positionCSS}
|
|
3665
|
+
}
|
|
3443
3666
|
::slotted(*) {
|
|
3444
3667
|
position: relative;
|
|
3445
3668
|
display: inline-block;
|
|
3446
3669
|
}
|
|
3447
3670
|
</style>
|
|
3448
|
-
|
|
3449
|
-
|
|
3671
|
+
<div class="root" part="root">
|
|
3672
|
+
<slot></slot>
|
|
3673
|
+
<div class="badge" part="badge">${this.value}</div>
|
|
3674
|
+
</div>
|
|
3450
3675
|
`;
|
|
3676
|
+
|
|
3677
|
+
const slot = this.shadowRoot.querySelector("slot");
|
|
3678
|
+
slot.addEventListener("slotchange", this._onSlotChange);
|
|
3679
|
+
this._onSlotChange();
|
|
3451
3680
|
}
|
|
3452
3681
|
|
|
3453
3682
|
// -------------------------------------------------------------------------
|
|
@@ -3499,12 +3728,14 @@ class YumeBadge extends HTMLElement {
|
|
|
3499
3728
|
return sizeMap[size] || sizeMap.small;
|
|
3500
3729
|
}
|
|
3501
3730
|
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3731
|
+
_onSlotChange() {
|
|
3732
|
+
const slot = this.shadowRoot.querySelector("slot");
|
|
3733
|
+
const root = this.shadowRoot.querySelector(".root");
|
|
3734
|
+
if (!slot || !root) return;
|
|
3735
|
+
const hasElement = slot
|
|
3736
|
+
.assignedNodes({ flatten: true })
|
|
3737
|
+
.some((n) => n.nodeType === Node.ELEMENT_NODE);
|
|
3738
|
+
root.classList.toggle("has-target", hasElement);
|
|
3508
3739
|
}
|
|
3509
3740
|
}
|
|
3510
3741
|
|
|
@@ -16584,4 +16815,4 @@ if (
|
|
|
16584
16815
|
document.head.appendChild(style);
|
|
16585
16816
|
}
|
|
16586
16817
|
|
|
16587
|
-
export { YumeAppbar, YumeAvatar, YumeBadge, YumeBanner, YumeBreadcrumbs, YumeButton, YumeButtonGroup, YumeCard, YumeCheckbox, YumeColor, YumeColorpicker, YumeDate, YumeDatepicker, YumeDock, YumeGallery, YumeIcon, YumeInput, YumePanel, YumePanelBar, YumeProgress, YumeRadio, YumeRating, YumeSelect, YumeSlider, YumeStack, YumeStepper, YumeTable, YumeTabs, YumeTag, YumeTextarea, YumeTheme, YumeToast, YumeTooltip, getIcon, registerIcon, registerIcons };
|
|
16818
|
+
export { YumeAppbar, YumeAvatar, YumeBadge, YumeBanner, YumeBreadcrumbs, YumeButton, YumeButtonGroup, YumeCard, YumeCheckbox, YumeColor, YumeColorpicker, YumeDate, YumeDatepicker, YumeDock, YumeGallery, YumeIcon, YumeInput, YumeMenu, YumePanel, YumePanelBar, YumeProgress, YumeRadio, YumeRating, YumeSelect, YumeSlider, YumeStack, YumeStepper, YumeTable, YumeTabs, YumeTag, YumeTextarea, YumeTheme, YumeToast, YumeTooltip, getIcon, registerIcon, registerIcons };
|