♻️(component): [subMenu]重构 subMenu

subMenu 重构,默认插槽渲染行为和 dropdown一致,支持更多场景
点击 menuItem 关闭悬浮菜单
popupMenu 支持侧边,横向菜单
dropdown 监听窗口
resize
This commit is contained in:
sight 2022-08-30 01:18:06 +08:00
parent 8ea3ae78b0
commit a31b33fca7
4 changed files with 48 additions and 70 deletions

View File

@ -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"

View File

@ -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(

View File

@ -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>

View File

@ -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,28 +33,25 @@ 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) {
if (_isCollapse && level.value === 1) { const _isCollapse =
// isCollapse.value === true || isCollapse.value === "true";
setTimeout(() => { if (_isCollapse && level.value === 1) {
//
setTimeout(() => {
needPopup.value = isTree.value && _isCollapse;
}, 200);
} else {
needPopup.value = isTree.value && _isCollapse; needPopup.value = isTree.value && _isCollapse;
}, 200); }
} else { } else if (slots.default && slots.default().length > 0) {
needPopup.value = isTree.value && _isCollapse; 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>