♻️(component): [subMenu]重构 subMenu
subMenu 重构,默认插槽渲染行为和 dropdown一致,支持更多场景 点击 menuItem 关闭悬浮菜单 popupMenu 支持侧边,横向菜单 dropdown 监听窗口 resize
This commit is contained in:
parent
8ea3ae78b0
commit
a31b33fca7
@ -621,6 +621,7 @@ onMounted(() => {
|
|||||||
item.addEventListener("scroll", handleScroll);
|
item.addEventListener("scroll", handleScroll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
window.addEventListener("resize", handleScroll);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@ -633,6 +634,7 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
removeContentResizeObserver();
|
removeContentResizeObserver();
|
||||||
removeTriggerResizeObserver();
|
removeTriggerResizeObserver();
|
||||||
|
window.removeEventListener("resize", handleScroll);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -664,7 +666,7 @@ defineExpose({ show, hide, toggle });
|
|||||||
:renderFunc="onlyChildRenderFunc"
|
:renderFunc="onlyChildRenderFunc"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
></RenderFunction>
|
></RenderFunction>
|
||||||
<TeleportWrapper :to="popupContainer">
|
<TeleportWrapper :to="popupContainer" :disabled="disabled">
|
||||||
<div
|
<div
|
||||||
v-if="openState"
|
v-if="openState"
|
||||||
ref="contentRef"
|
ref="contentRef"
|
||||||
|
@ -6,6 +6,7 @@ export default {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ComputedRef, inject, ref, Ref, useSlots } from "vue";
|
import { computed, ComputedRef, inject, ref, Ref, useSlots } from "vue";
|
||||||
|
import { DropdownContext, dropdownInjectionKey } from "../dropdown/interface";
|
||||||
import useLevel from "../menu/useLevel";
|
import useLevel from "../menu/useLevel";
|
||||||
import LayTooltip from "../tooltip/index.vue";
|
import LayTooltip from "../tooltip/index.vue";
|
||||||
|
|
||||||
@ -21,8 +22,14 @@ const selectedKey: Ref<string> = inject("selectedKey") as Ref<string>;
|
|||||||
const isTree = inject("isTree") as ComputedRef<boolean>;
|
const isTree = inject("isTree") as ComputedRef<boolean>;
|
||||||
const isCollapse = inject("isCollapse") as ComputedRef<boolean | string>;
|
const isCollapse = inject("isCollapse") as ComputedRef<boolean | string>;
|
||||||
const theme = inject("menuTheme") as Ref<string>;
|
const theme = inject("menuTheme") as Ref<string>;
|
||||||
|
const dropdownCtx = inject<DropdownContext | undefined>(
|
||||||
|
dropdownInjectionKey,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
const selectHandle = function () {
|
const selectHandle = function () {
|
||||||
selectedKey.value = props.id;
|
selectedKey.value = props.id;
|
||||||
|
dropdownCtx?.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
const needTooltip = computed(
|
const needTooltip = computed(
|
||||||
|
@ -16,24 +16,42 @@ export interface LaySubMenuPopupProps {
|
|||||||
}
|
}
|
||||||
const props = defineProps<LaySubMenuPopupProps>();
|
const props = defineProps<LaySubMenuPopupProps>();
|
||||||
|
|
||||||
|
const { level } = useLevel();
|
||||||
|
const isTree: Ref<boolean> = inject("isTree") as Ref<boolean>;
|
||||||
const openKeys: Ref<string[]> = inject("openKeys") as Ref<string[]>;
|
const openKeys: Ref<string[]> = inject("openKeys") as Ref<string[]>;
|
||||||
const theme = inject("menuTheme") as Ref<string>;
|
const theme = inject("menuTheme") as Ref<string>;
|
||||||
const computedTheme = computed(() => {
|
|
||||||
return theme.value === "light" ? "-light" : "";
|
|
||||||
});
|
|
||||||
|
|
||||||
const isOpen = computed(() => {
|
const isOpen = computed(() => {
|
||||||
return openKeys.value.includes(props.id);
|
return openKeys.value.includes(props.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const computedTheme = computed(() => {
|
||||||
|
if (isTree.value) {
|
||||||
|
return theme.value === "light" ? "-light" : "";
|
||||||
|
}
|
||||||
|
return theme.value === "light" ? "" : "-light";
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedExpandIcon = computed(() => {
|
||||||
|
if (isTree.value) return "layui-icon-right";
|
||||||
|
return level.value === 2 ? "layui-icon-down" : "layui-icon-right";
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedPlacement = computed(() => {
|
||||||
|
return !isTree.value && level.value === 2 ? "bottom-start" : "right-start";
|
||||||
|
});
|
||||||
|
|
||||||
|
const computedTrigger = computed(() => {
|
||||||
|
return !isTree.value && level.value === 2 ? "click" : "hover";
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<lay-dropdown
|
<lay-dropdown
|
||||||
trigger="hover"
|
:trigger="computedTrigger"
|
||||||
placement="right-start"
|
:placement="computedPlacement"
|
||||||
:autoFitMinWidth="false"
|
:autoFitMinWidth="false"
|
||||||
:contentOffset="3"
|
:contentOffset="3"
|
||||||
popupContainer="body"
|
|
||||||
updateAtScroll
|
updateAtScroll
|
||||||
class="layui-sub-menu-popup"
|
class="layui-sub-menu-popup"
|
||||||
>
|
>
|
||||||
@ -54,7 +72,7 @@ const isOpen = computed(() => {
|
|||||||
<!-- 扩展 -->
|
<!-- 扩展 -->
|
||||||
<span v-if="$slots.expandIcon" class="layui-nav-more">
|
<span v-if="$slots.expandIcon" class="layui-nav-more">
|
||||||
<slot name="expandIcon">
|
<slot name="expandIcon">
|
||||||
<lay-icon type="layui-icon-right"></lay-icon>
|
<lay-icon :type="computedExpandIcon" />
|
||||||
</slot>
|
</slot>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
@ -5,17 +5,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import { computed, inject, ref, Ref, useSlots, watchEffect } from "vue";
|
||||||
computed,
|
|
||||||
inject,
|
|
||||||
onBeforeUnmount,
|
|
||||||
ref,
|
|
||||||
Ref,
|
|
||||||
useSlots,
|
|
||||||
watch,
|
|
||||||
watchEffect,
|
|
||||||
} from "vue";
|
|
||||||
import { onClickOutside } from "@vueuse/core";
|
|
||||||
import LayTransition from "../transition/index.vue";
|
import LayTransition from "../transition/index.vue";
|
||||||
import SubMenuPopup from "./SubMenuPopup.vue";
|
import SubMenuPopup from "./SubMenuPopup.vue";
|
||||||
import { provideLevel, default as useLevel } from "../menu/useLevel";
|
import { provideLevel, default as useLevel } from "../menu/useLevel";
|
||||||
@ -43,15 +33,15 @@ const isOpen = computed(() => {
|
|||||||
return openKeys.value.includes(props.id);
|
return openKeys.value.includes(props.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
const subMenuRef = ref<HTMLElement>();
|
|
||||||
const position = ref<String>();
|
|
||||||
const nextLevel = computed(() => level.value + 1);
|
const nextLevel = computed(() => level.value + 1);
|
||||||
|
|
||||||
provideLevel(nextLevel);
|
provideLevel(nextLevel);
|
||||||
|
|
||||||
const needPopup = ref(false);
|
const needPopup = ref(false);
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const _isCollapse = isCollapse.value === true || isCollapse.value === "true";
|
if (isTree.value) {
|
||||||
|
const _isCollapse =
|
||||||
|
isCollapse.value === true || isCollapse.value === "true";
|
||||||
if (_isCollapse && level.value === 1) {
|
if (_isCollapse && level.value === 1) {
|
||||||
// 动画结束后改变
|
// 动画结束后改变
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -60,11 +50,8 @@ watchEffect(() => {
|
|||||||
} else {
|
} else {
|
||||||
needPopup.value = isTree.value && _isCollapse;
|
needPopup.value = isTree.value && _isCollapse;
|
||||||
}
|
}
|
||||||
});
|
} else if (slots.default && slots.default().length > 0) {
|
||||||
|
needPopup.value = true;
|
||||||
watch(isOpen, () => {
|
|
||||||
if (isOpen.value && position.value !== "left-nav") {
|
|
||||||
setTimeout(setPosition, 0);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,33 +66,6 @@ const openHandle = function () {
|
|||||||
openKeys.value = newOpenKeys;
|
openKeys.value = newOpenKeys;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPosition = function () {
|
|
||||||
if (!isTree.value || !subMenuRef.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const offsetWidth = subMenuRef.value.offsetWidth;
|
|
||||||
if (
|
|
||||||
window.innerWidth <
|
|
||||||
subMenuRef.value.getBoundingClientRect().left + offsetWidth + 10
|
|
||||||
) {
|
|
||||||
position.value = "left-nav";
|
|
||||||
} else {
|
|
||||||
position.value = "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onClickOutside(subMenuRef, (event: PointerEvent) => {
|
|
||||||
if (!isTree.value) {
|
|
||||||
let index = openKeys.value.indexOf(props.id);
|
|
||||||
if (index != -1) {
|
|
||||||
openKeys.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("resize", setPosition);
|
|
||||||
onBeforeUnmount(() => window.removeEventListener("resize", setPosition));
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -141,15 +101,6 @@ onBeforeUnmount(() => window.removeEventListener("resize", setPosition));
|
|||||||
</div>
|
</div>
|
||||||
</lay-transition>
|
</lay-transition>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
|
||||||
<dl
|
|
||||||
ref="subMenuRef"
|
|
||||||
class="layui-nav-child layui-anim layui-anim-upbit"
|
|
||||||
:class="[{ 'layui-show': isOpen }, position]"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</dl>
|
|
||||||
</template>
|
|
||||||
</li>
|
</li>
|
||||||
<SubMenuPopup v-else :id="id">
|
<SubMenuPopup v-else :id="id">
|
||||||
<template v-if="slots.icon" #icon>
|
<template v-if="slots.icon" #icon>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user