vue-sidebar-menu
Version:
A Vue.js Sidebar Menu Component
711 lines (710 loc) • 23.3 kB
JavaScript
import { defineComponent as V, getCurrentInstance as xe, computed as m, resolveComponent as Ne, createElementBlock as S, createBlock as x, openBlock as v, normalizeProps as W, mergeProps as $, renderSlot as O, withCtx as H, createElementVNode as A, resolveDynamicComponent as X, createTextVNode as Le, toDisplayString as me, inject as Oe, provide as Se, toRefs as de, ref as R, watch as Me, toHandlers as Ye, unref as p, createCommentVNode as D, Transition as _e, normalizeStyle as ve, normalizeClass as pe, withModifiers as Fe, guardReactiveProps as ke, Fragment as $e, renderList as Te, nextTick as De, onMounted as We, onUnmounted as je, createVNode as ze, withDirectives as qe, vShow as Ve, reactive as Ge } from "vue";
const Je = ["href", "onClick"], Ke = {
compatConfig: {
MODE: 3,
inheritAttrs: !1
}
}, Qe = /* @__PURE__ */ V({
...Ke,
__name: "SidebarMenuLink",
props: {
item: {}
},
setup(e) {
const n = e, l = xe()?.appContext.config.globalProperties.$router, o = m(() => !!(!n.item.href || n.item.external || !l));
return (a, i) => {
const u = Ne("router-link");
return o.value ? (v(), S("a", W($({ key: 0 }, a.$attrs)), [
O(a.$slots, "default")
], 16)) : (v(), x(u, {
key: 1,
custom: "",
to: a.$attrs.href
}, {
default: H(({ href: h, navigate: s }) => [
A("a", $(a.$attrs, {
href: h,
onClick: s
}), [
O(a.$slots, "default")
], 16, Je)
]),
_: 3
}, 8, ["to"]));
};
}
}), Xe = {
compatConfig: { MODE: 3 }
}, Ze = /* @__PURE__ */ V({
...Xe,
__name: "SidebarMenuIcon",
props: {
icon: {}
},
setup(e) {
const n = e, l = m(() => ({
class: [
"vsm--icon",
typeof n.icon == "object" ? n.icon.class : n.icon
],
"aria-hidden": !0,
...typeof n.icon == "object" ? n.icon.attributes : {}
}));
return (o, a) => typeof e.icon == "object" && e.icon.text ? (v(), x(X(e.icon.element ? e.icon.element : "i"), W($({ key: 0 }, l.value)), {
default: H(() => [
Le(me(e.icon.text), 1)
]),
_: 1
}, 16)) : typeof e.icon == "object" ? (v(), x(X(e.icon.element ? e.icon.element : "i"), W($({ key: 1 }, l.value)), null, 16)) : (v(), S("i", W($({ key: 2 }, l.value)), null, 16));
}
}), et = {
compatConfig: { MODE: 3 }
}, tt = /* @__PURE__ */ V({
...et,
__name: "SidebarMenuBadge",
props: {
badge: {}
},
setup(e) {
const n = e, l = m(() => ({
class: ["vsm--badge", n.badge.class],
...n.badge.attributes
}));
return (o, a) => e.badge.text ? (v(), x(X(e.badge.element ? e.badge.element : "span"), W($({ key: 0 }, l.value)), {
default: H(() => [
Le(me(e.badge.text), 1)
]),
_: 1
}, 16)) : (v(), x(X(e.badge.element ? e.badge.element : "span"), W($({ key: 1 }, l.value)), null, 16));
}
});
function ot(e) {
const n = Symbol(
e.name || "SidebarInjectionState"
);
return [(...a) => {
const i = e(...a);
return Se(n, i), i;
}, () => Oe(n)];
}
const [nt, Ie] = ot(
(e, n, l, o, a, i, u, h, s, g, P, T) => ({
getSidebarProps: e,
getSidebarRef: n,
getIsCollapsed: l,
getMobileItem: o,
getMobileItemRect: a,
getCurrentRoute: i,
updateIsCollapsed: u,
setMobileItem: h,
unsetMobileItem: s,
clearMobileItemTimeout: g,
onRouteChange: P,
emitItemClick: T
})
);
function lt(e, n) {
const { matched: l } = e, { length: o } = l, a = l[o - 1], i = n.matched;
if (!a || !i.length) return -1;
const u = i.findIndex(
Ae.bind(null, a)
);
if (u > -1) return u;
const h = He(l[o - 2]);
return o > 1 && He(a) === h && i[i.length - 1].path !== h ? i.findIndex(
Ae.bind(null, l[o - 2])
) : u;
}
function at(e, n) {
if (Object.keys(e).length !== Object.keys(n).length) return !1;
for (const l in e)
if (!st(e[l], n[l])) return !1;
return !0;
}
function it(e, n) {
for (const l in n) {
const o = n[l], a = e[l];
if (typeof o == "string") {
if (o !== a) return !1;
} else if (!Array.isArray(a) || a.length !== o.length || o.some((i, u) => i !== a[u]))
return !1;
}
return !0;
}
function He(e) {
return e ? e.aliasOf ? e.aliasOf.path : e.path : "";
}
function Ae(e, n) {
return (e.aliasOf || e) === (n.aliasOf || n);
}
function st(e, n) {
return Array.isArray(e) ? Pe(e, n) : Array.isArray(n) ? Pe(n, e) : e === n;
}
function Pe(e, n) {
return Array.isArray(n) ? e.length === n.length && e.every((l, o) => l === n[o]) : e.length === 1 && e[0] === n;
}
const rt = { class: "vsm--dropdown" }, ut = {
compatConfig: { MODE: 3 }
}, ct = /* @__PURE__ */ V({
...ut,
__name: "SidebarMenuItemLink",
props: {
item: {},
level: {},
activeShow: {}
},
emits: ["update-active-show"],
setup(e, { emit: n }) {
const l = e, o = n, {
getSidebarRef: a,
getSidebarProps: i,
getIsCollapsed: u,
getMobileItem: h,
getMobileItemRect: s,
getCurrentRoute: g,
setMobileItem: P,
unsetMobileItem: T,
clearMobileItemTimeout: G,
emitItemClick: j
} = Ie(), { linkComponentName: Y } = de(i), { item: r, activeShow: Z, level: z } = de(l), F = R(void 0), c = m(() => z.value === 1), C = (t) => {
F.value = t;
}, E = xe()?.appContext.config.globalProperties.$router, J = Oe("SidebarMenuScroll"), ie = R(!1), ee = R(!1), L = (t, y) => {
if (t.isActive && typeof t.isActive == "function") {
const w = t.isActive(t);
if (typeof w == "boolean") return w;
}
if (!t.href || t.external) return !1;
if (E) {
const w = E.resolve(t.href), k = E.currentRoute.value, M = lt(w, k);
return y || t.exact ? M > -1 && M === k.matched.length - 1 && at(k.params, w.params) : M > -1 && it(k.params, w.params);
} else
return t.href === g.value;
}, se = (t) => t ? t.some((y) => re(y) && (L(y) || se(y.child))) : !1, te = m(
() => L(r.value) || se(r.value.child)
), oe = m(() => L(r.value, !0)), he = (t) => {
(!r.value.href || r.value.disabled) && (t.preventDefault(), r.value.disabled) || (K(t, t.currentTarget.parentElement), B.value && (!r.value.href || te.value) && (I.value = !I.value), j(t, r.value));
}, fe = (t) => {
t.stopPropagation(), t.preventDefault(), !r.value.disabled && (B.value && (I.value = !I.value), j(t, r.value));
}, d = (t) => {
r.value.disabled || (t.stopPropagation(), ee.value = !0);
}, f = (t) => {
t.stopPropagation(), ee.value = !1;
}, _ = (t) => {
r.value.disabled || (i.disableHover ? b.value && B.value && G() : (G(), K(t, t.currentTarget)));
}, U = () => {
i.disableHover && !B.value || b.value && T(!1, i.disableHover ? void 0 : 300);
}, K = (t, y) => {
b.value || u.value && setTimeout(() => {
c.value && !b.value && P({ item: r.value, itemEl: y }), t.type === "click" && !B.value && T(!1, c.value ? void 0 : 300);
}, 0);
}, ge = (t) => {
t.style.height = t.scrollHeight + "px";
}, be = (t) => {
if (t.style.height = "auto", !u.value) {
if (i.smoothScroll) {
const y = a.value?.children[0], w = t.parentElement;
if (y && w) {
const k = w.getBoundingClientRect().bottom, M = y.getBoundingClientRect().bottom;
k > M && De(
() => w.scrollIntoView({
behavior: "smooth",
block: "nearest"
})
);
}
}
J();
}
}, we = (t) => {
if (u.value && c.value) {
t.style.display = "none";
return;
}
t.style.height = t.scrollHeight + "px";
}, ye = () => {
u.value || J();
};
function re(t) {
return !("header" in t) && !("component" in t);
}
const I = m({
get: () => B.value ? u.value && c.value ? ne.value : i.showChild ? !0 : i.showOneChild && c.value || i.showOneChild === "deep" ? r.value.id === Z.value : ie.value : !1,
set: (t) => {
(i.showOneChild && c.value || i.showOneChild === "deep") && o("update-active-show", t ? r.value.id : void 0), ie.value = t;
}
}), ne = m(
() => u.value && c.value ? b.value : ee.value
), B = m(() => "child" in r.value && !!r.value.child?.length), Q = m(() => [
"vsm--link",
`vsm--link_level-${z.value}`,
{
"vsm--link_mobile": b.value,
"vsm--link_hover": ne.value,
"vsm--link_active": te.value,
"vsm--link_disabled": r.value.disabled,
"vsm--link_open": I.value
},
r.value.class
]), le = m(() => {
const t = r.value.href ? r.value.href : "#", y = r.value.external ? "_blank" : "_self", w = r.value.disabled ? -1 : null, k = oe.value ? "page" : null, M = B.value ? I.value : null, Re = M ? `vsm-${l.item.id}` : null;
return {
href: t,
target: y,
tabindex: w,
"aria-current": k,
"aria-expanded": M,
"aria-controls": Re,
...r.value.attributes
};
}), N = m(() => ({ id: `vsm-${r.value.id}` })), ae = m(() => [
"vsm--item",
{ "vsm--item_mobile": b.value }
]), b = m(() => l.item.id === h.value?.id), q = (t) => i.rtl ? { right: t } : { left: t }, ue = m(() => ({
position: "absolute",
"max-height": `${s.value.maxHeight}px`,
width: `${s.value.maxWidth}px`,
"overflow-y": "auto",
"z-index": "20",
...s.value.dropup ? { bottom: `${s.value.dropup}px` } : { top: `${s.value.top + s.value.height}px` },
...q(i.widthCollapsed)
})), ce = m(() => ({
position: "absolute",
top: `${s.value.top}px`,
...q(i.widthCollapsed),
width: `${s.value.maxWidth}px`,
height: `${s.value.height}px`,
"padding-left": `${s.value.padding[0]}px`,
"padding-right": `${s.value.padding[1]}px`,
"z-index": "20"
})), Ce = m(() => ({
position: "absolute",
top: `${s.value.top}px`,
...i.rtl ? { right: "0px" } : { left: "0px" },
width: `${s.value.maxWidth + parseInt(i.widthCollapsed)}px`,
height: `${s.value.height}px`,
"z-index": "10"
}));
return Me(
() => te.value,
(t) => {
t && (I.value = !0);
},
{ immediate: !0 }
), (t, y) => (v(), S("li", $({
class: ae.value,
onMouseover: d,
onMouseout: f
}, Ye(
p(u) && c.value ? { mouseenter: _, mouseleave: U } : {},
!0
)), [
(v(), x(X(p(Y) ? p(Y) : Qe), $({
item: p(r),
class: Q.value
}, le.value, { onClick: he }), {
default: H(() => [
p(u) && c.value ? (v(), x(_e, {
key: 0,
name: "slide-animation"
}, {
default: H(() => [
ne.value ? (v(), S("div", {
key: 0,
class: "vsm--mobile-bg",
style: ve(Ce.value)
}, null, 4)) : D("", !0)
]),
_: 1
})) : D("", !0),
p(r).icon ? (v(), x(Ze, {
key: 1,
icon: p(r).icon
}, null, 8, ["icon"])) : D("", !0),
A("div", {
class: pe([
"vsm--title",
p(u) && c.value && !b.value && "vsm--title_hidden"
]),
style: ve(b.value && ce.value)
}, [
A("span", null, me(p(r).title), 1),
p(r).badge ? (v(), x(tt, {
key: 0,
badge: p(r).badge
}, null, 8, ["badge"])) : D("", !0),
B.value ? (v(), S("div", {
key: 1,
class: pe(["vsm--arrow", { "vsm--arrow_open": I.value }]),
onClick: Fe(fe, ["stop"])
}, [
O(t.$slots, "dropdown-icon", W(ke({ isOpen: I.value, toggle: fe })))
], 2)) : D("", !0)
], 6)
]),
_: 3
}, 16, ["item", "class"])),
B.value ? (v(), x(_e, {
key: 0,
appear: b.value,
name: "expand",
onEnter: ge,
onAfterEnter: be,
onBeforeLeave: we,
onAfterLeave: ye
}, {
default: H(() => [
I.value ? (v(), S("div", $({
key: 0,
class: ["vsm--child", b.value && "vsm--child_mobile"],
style: b.value && ue.value
}, N.value), [
A("ul", rt, [
(v(!0), S($e, null, Te(p(r).child, (w) => (v(), x(Ue, {
key: w.id,
item: w,
level: p(z) + 1,
"active-show": F.value,
onUpdateActiveShow: C
}, {
"dropdown-icon": H(({
isOpen: k,
toggle: M
}) => [
O(t.$slots, "dropdown-icon", $({ ref_for: !0 }, { isOpen: k, toggle: M }))
]),
_: 3
}, 8, ["item", "level", "active-show"]))), 128))
])
], 16)) : D("", !0)
]),
_: 3
}, 8, ["appear"])) : D("", !0)
], 16));
}
}), dt = { key: 0 }, vt = {
compatConfig: { MODE: 3 }
}, Ue = /* @__PURE__ */ V({
...vt,
__name: "SidebarMenuItem",
props: {
item: {},
level: {},
activeShow: {}
},
emits: ["update-active-show"],
setup(e) {
const n = e, { getIsCollapsed: l } = Ie(), { item: o, activeShow: a, level: i } = de(n), u = m(() => l.value ? n.item.hidden && n.item.hiddenOnCollapse === void 0 ? !0 : n.item.hiddenOnCollapse === !0 : n.item.hidden === !0);
return (h, s) => u.value ? D("", !0) : (v(), S($e, { key: 0 }, [
"component" in p(o) ? (v(), S("li", dt, [
(v(), x(X(p(o).component), W(ke(p(o).props)), null, 16))
])) : "header" in p(o) ? (v(), S("li", $({
key: 1,
class: ["vsm--header", p(o).class]
}, p(o).attributes), me(p(o).header), 17)) : (v(), x(ct, {
key: 2,
item: p(o),
level: p(i),
"active-show": p(a),
onUpdateActiveShow: s[0] || (s[0] = (g) => h.$emit("update-active-show", g))
}, {
"dropdown-icon": H(({
isOpen: g,
toggle: P
}) => [
O(h.$slots, "dropdown-icon", W(ke({ isOpen: g, toggle: P })))
]),
_: 3
}, 8, ["item", "level", "active-show"]))
], 64));
}
}), pt = {
compatConfig: { MODE: 3 }
}, mt = /* @__PURE__ */ V({
...pt,
__name: "SidebarMenuScroll",
setup(e) {
const { getIsCollapsed: n } = Ie(), l = R(null), o = R(null), a = R(null), i = R(!1);
let u = 0, h = !1;
const s = R(!1), g = () => {
l.value && De(() => {
z();
});
}, P = () => {
requestAnimationFrame(g);
}, T = (c) => {
if (!l.value || !o.value || !a.value) return;
const C = Math.abs(
o.value.getBoundingClientRect().y - c.clientY
), E = a.value.offsetHeight / 2;
F(C - E);
}, G = (c) => {
!l.value || !o.value || !a.value || (c.stopImmediatePropagation(), i.value = !0, window.addEventListener("mousemove", j), window.addEventListener("mouseup", Y), u = a.value.offsetHeight - (c.clientY - a.value.getBoundingClientRect().y));
}, j = (c) => {
if (!l.value || !o.value || !a.value || !i.value) return;
const C = c.clientY - o.value.getBoundingClientRect().y, E = a.value.offsetHeight - u;
s.value = !0, F(C - E);
}, Y = (c) => {
i.value = !1, u = 0, h || (s.value = !1), window.removeEventListener("mousemove", j), window.removeEventListener("mouseup", Y);
}, r = (c) => {
s.value = !0, h = !0;
}, Z = (c) => {
h = !1, i.value || (s.value = !1);
}, z = () => {
if (!l.value || !o.value || !a.value) return;
const c = l.value.clientHeight * 100 / l.value.scrollHeight, C = c < 100 ? c : 0, E = l.value.scrollTop * 100 / l.value.clientHeight || 0;
a.value.style.height = `${C}%`, a.value.style.transform = `translateY(${E}%)`;
}, F = (c) => {
if (!l.value || !o.value || !a.value) return;
const C = c * 100 / o.value.offsetHeight;
l.value.scrollTop = C * l.value.scrollHeight / 100;
};
return Me(
() => n.value,
() => {
g();
}
), We(() => {
g(), window.addEventListener("resize", g);
}), je(() => {
window.removeEventListener("resize", g);
}), Se("SidebarMenuScroll", g), (c, C) => (v(), S("div", {
class: "vsm--scroll-wrapper",
onMouseenter: r,
onMouseleave: Z
}, [
A("div", {
ref_key: "scrollRef",
ref: l,
class: "vsm--scroll",
onScroll: P
}, [
O(c.$slots, "default", {}, void 0, !0)
], 544),
ze(_e, null, {
default: H(() => [
qe(A("div", {
ref_key: "scrollBarRef",
ref: o,
class: "vsm--scroll-bar",
onMousedown: T
}, [
A("div", {
ref_key: "scrollThumbRef",
ref: a,
class: pe(["vsm--scroll-thumb", [i.value && "vsm--scroll-thumb_clicking"]]),
onMousedown: G
}, null, 34)
], 544), [
[Ve, s.value]
])
]),
_: 1
})
], 32));
}
}), ht = (e, n) => {
const l = e.__vccOpts || e;
for (const [o, a] of n)
l[o] = a;
return l;
}, ft = /* @__PURE__ */ ht(mt, [["__scopeId", "data-v-2640f004"]]), gt = { class: "vsm--wrapper" }, bt = ["aria-label"], wt = {
compatConfig: { MODE: 3 }
}, yt = /* @__PURE__ */ V({
...wt,
__name: "SidebarMenu",
props: {
menu: {},
collapsed: { type: Boolean },
width: { default: "290px" },
widthCollapsed: { default: "65px" },
showOneChild: { type: [Boolean, String] },
showChild: { type: Boolean },
rtl: { type: Boolean },
relative: { type: Boolean },
hideToggle: { type: Boolean },
theme: {},
disableHover: { type: Boolean },
linkComponentName: {},
smoothScroll: { type: Boolean }
},
emits: ["item-click", "update:collapsed"],
setup(e, { expose: n, emit: l }) {
const o = e, a = l, { collapsed: i } = de(o), u = R(null), h = R(i.value), s = Ge({
item: null,
rect: {
top: 0,
height: 0,
padding: [0, 0],
maxHeight: 0,
maxWidth: 0,
dropup: 0
},
timeout: null
}), g = R(""), P = R(void 0), T = m(() => s.item), G = m(() => s.rect), j = m(
() => h.value ? o.widthCollapsed : o.width
), Y = m(() => [
"v-sidebar-menu",
h.value ? "vsm_collapsed" : "vsm_expanded",
o.theme && `vsm_${o.theme}`,
o.rtl && "vsm_rtl",
o.relative && "vsm_relative"
]);
let r = 0;
const Z = () => `${Date.now()}${r++}`;
function z(d) {
return d ? d.map((f) => {
const _ = { ...f, id: f.id ?? Z() };
return _.child && (_.child = z(_.child)), _;
}) : [];
}
const F = m(() => z(o.menu)), c = () => {
s.timeout && (clearTimeout(s.timeout), s.timeout = null);
}, C = (d) => {
s.item = d;
}, E = (d) => {
for (const f in d)
s.rect[f] = d[f];
}, J = (d = !0, f = 800) => {
if (T.value) {
if (c(), d) {
C(null);
return;
}
s.timeout = setTimeout(() => C(null), f);
}
}, ie = (d) => {
if (!u.value) return;
const {
top: f,
bottom: _,
height: U
} = d.getBoundingClientRect(), { left: K, right: ge } = u.value.getBoundingClientRect(), be = u.value.firstElementChild, { bottom: we, height: ye } = be.getBoundingClientRect(), re = d.offsetParent, I = re.offsetTop, { top: ne, height: B } = re.getBoundingClientRect();
let Q = window.innerHeight, le = window.innerWidth, N = 0, ae = le, b = Math.max(
parseInt(o.width) - parseInt(o.widthCollapsed),
0
);
const q = u.value.parentElement;
if (q && o.relative) {
Q = q.clientHeight, le = q.clientWidth;
const Be = q.getBoundingClientRect();
N = Be.top, ae = Be.right;
}
const ue = o.rtl ? le - (ae - K) : ae - ge;
b = ue <= b ? ue : b;
const ce = window.getComputedStyle(d), Ce = parseInt(ce.paddingLeft || "0", 10), t = parseInt(ce.paddingRight || "0", 10), y = f - ne, w = we - f - (ye - (B + I));
let k = Q - (_ - N);
const M = Math.min(
window.innerHeight,
window.innerHeight - N,
Q,
Q + N
), Ee = M - (Math.max(_, 0) - Math.max(N, 0)) < M * 0.25 ? w : 0;
return k = Ee ? f - N : k, {
top: y,
height: U,
padding: [Ce, t],
maxWidth: b,
maxHeight: k,
dropup: Ee
};
}, ee = ({
item: d,
itemEl: f
}) => {
c();
const _ = f.children[0], U = ie(_);
U && (C(d), E(U));
}, L = () => {
g.value = window.location.pathname + window.location.search + window.location.hash;
}, se = (d, f) => {
a("item-click", d, f);
}, te = (d) => {
P.value = d;
}, oe = (d) => {
h.value = d;
}, he = () => {
J(), oe(!h.value), a("update:collapsed", h.value);
};
return xe()?.appContext.config.globalProperties.$router || (We(() => {
L(), window.addEventListener("hashchange", L);
}), je(() => {
window.removeEventListener("hashchange", L);
})), Me(
() => i.value,
(d) => {
J(), oe(d);
}
), nt(
o,
u,
h,
T,
G,
g,
oe,
ee,
J,
c,
L,
se
), Se("onRouteChange", L), n({
onRouteChange: L
}), (d, f) => (v(), S("div", {
ref_key: "sidebarRef",
ref: u,
class: pe(Y.value),
style: ve({ "max-width": j.value })
}, [
A("div", gt, [
O(d.$slots, "header"),
ze(ft, null, {
default: H(() => [
A("ul", {
class: "vsm--menu",
style: ve({ width: j.value })
}, [
(v(!0), S($e, null, Te(F.value, (_) => (v(), x(Ue, {
key: _.id,
item: _,
level: 1,
"active-show": P.value,
onUpdateActiveShow: te
}, {
"dropdown-icon": H(({
isOpen: U,
toggle: K
}) => [
O(d.$slots, "dropdown-icon", $({ ref_for: !0 }, { isOpen: U, toggle: K }), () => [
f[0] || (f[0] = A("span", { class: "vsm--arrow_default" }, null, -1))
])
]),
_: 3
}, 8, ["item", "active-show"]))), 128))
], 4)
]),
_: 3
}),
O(d.$slots, "footer")
]),
e.hideToggle ? D("", !0) : (v(), S("button", {
key: 0,
class: "vsm--toggle-btn",
"aria-label": p(i) ? "Expand sidebar" : "Collapse sidebar",
onClick: he
}, [
O(d.$slots, "toggle-icon", {}, () => [
f[1] || (f[1] = A("span", { class: "vsm--toggle-btn_default" }, null, -1))
])
], 8, bt))
], 6));
}
}), _t = {
install(e) {
e.component("SidebarMenu", yt);
}
};
export {
yt as SidebarMenu,
_t as default
};
//# sourceMappingURL=vue-sidebar-menu.esm.js.map