✨(component): [dropdown]新增 contentClass 属性
*新增 contentClass *新增 contentStyle *新增 alignPoint
This commit is contained in:
parent
106aadf788
commit
0fe5aa2644
@ -44,7 +44,7 @@ import {
|
||||
DropdownContext,
|
||||
} from "./interface";
|
||||
import TeleportWrapper from "../_components/teleportWrapper.vue";
|
||||
import { useFirstElement } from "./useFirstElement";
|
||||
import { useFirstElement, isScrollElement, getScrollElements } from "./util";
|
||||
import RenderFunction from "../_components/renderFunction";
|
||||
import { transformPlacement } from "./util";
|
||||
|
||||
@ -67,11 +67,11 @@ export interface LayDropdownProps {
|
||||
mouseEnterDelay?: number;
|
||||
mouseLeaveDelay?: number;
|
||||
focusDelay?: number;
|
||||
// 未完善,暂不开放
|
||||
alignPoint?: boolean;
|
||||
popupContainer?: string | undefined;
|
||||
contentClass?: string | Array<string | object> | object;
|
||||
contentStyle?: StyleValue;
|
||||
// wip
|
||||
popupContainer?: string | undefined;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<LayDropdownProps>(), {
|
||||
@ -445,25 +445,6 @@ const getContentOffset = (
|
||||
}
|
||||
};
|
||||
|
||||
const isScrollElement = (element: HTMLElement) => {
|
||||
return (
|
||||
element.scrollHeight > element.offsetHeight ||
|
||||
element.scrollWidth > element.offsetWidth
|
||||
);
|
||||
};
|
||||
|
||||
const getScrollElements = (container: HTMLElement | undefined) => {
|
||||
const scrollElements: HTMLElement[] = [];
|
||||
let element: HTMLElement | undefined = container;
|
||||
while (element && element !== document.documentElement) {
|
||||
if (isScrollElement(element)) {
|
||||
scrollElements.push(element);
|
||||
}
|
||||
element = element.parentElement ?? undefined;
|
||||
}
|
||||
return scrollElements;
|
||||
};
|
||||
|
||||
const handleScroll = useThrottleFn(() => {
|
||||
if (openState.value) {
|
||||
updateContentStyle();
|
||||
|
@ -1,103 +0,0 @@
|
||||
import { Component, onMounted, onUpdated, ref, VNode, VNodeTypes } from "vue";
|
||||
|
||||
export interface SlotChildren {
|
||||
value?: VNode[];
|
||||
}
|
||||
|
||||
// Quoted from arco-vue
|
||||
// https://github.com/arco-design/arco-design-vue/blob/main/packages/web-vue/components/_utils/vue-utils.ts
|
||||
export enum ShapeFlags {
|
||||
ELEMENT = 1,
|
||||
FUNCTIONAL_COMPONENT = 1 << 1,
|
||||
STATEFUL_COMPONENT = 1 << 2,
|
||||
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT,
|
||||
TEXT_CHILDREN = 1 << 3,
|
||||
ARRAY_CHILDREN = 1 << 4,
|
||||
SLOTS_CHILDREN = 1 << 5,
|
||||
TELEPORT = 1 << 6,
|
||||
SUSPENSE = 1 << 7,
|
||||
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
|
||||
COMPONENT_KEPT_ALIVE = 1 << 9,
|
||||
}
|
||||
|
||||
export const isElement = (vn: VNode) => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.ELEMENT);
|
||||
};
|
||||
|
||||
export const isComponent = (
|
||||
vn: VNode,
|
||||
type?: VNodeTypes
|
||||
): type is Component => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.COMPONENT);
|
||||
};
|
||||
|
||||
export const isArrayChildren = (
|
||||
vn: VNode,
|
||||
children: VNode["children"]
|
||||
): children is VNode[] => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.ARRAY_CHILDREN);
|
||||
};
|
||||
|
||||
export const getChildrenArray = (vn: VNode): VNode[] | undefined => {
|
||||
if (isArrayChildren(vn, vn.children)) {
|
||||
return vn.children;
|
||||
}
|
||||
if (Array.isArray(vn)) {
|
||||
return vn;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getFirstElementFromVNode = (
|
||||
vn: VNode
|
||||
): HTMLElement | undefined => {
|
||||
if (isElement(vn)) {
|
||||
return vn.el as HTMLElement;
|
||||
}
|
||||
if (isComponent(vn)) {
|
||||
if ((vn.el as Node)?.nodeType === 1) {
|
||||
return vn.el as HTMLElement;
|
||||
}
|
||||
if (vn.component?.subTree) {
|
||||
const ele = getFirstElementFromVNode(vn.component.subTree);
|
||||
if (ele) return ele;
|
||||
}
|
||||
} else {
|
||||
const children = getChildrenArray(vn);
|
||||
return getFirstElementFromChildren(children);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getFirstElementFromChildren = (
|
||||
children: VNode[] | undefined
|
||||
): HTMLElement | undefined => {
|
||||
if (children && children.length > 0) {
|
||||
for (const child of children) {
|
||||
const element = getFirstElementFromVNode(child);
|
||||
if (element) return element;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const useFirstElement = () => {
|
||||
const children: SlotChildren = {};
|
||||
const firstElement = ref<HTMLElement>();
|
||||
|
||||
const getFirstElement = () => {
|
||||
const element = getFirstElementFromChildren(children.value);
|
||||
if (element !== firstElement.value) {
|
||||
firstElement.value = element;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => getFirstElement());
|
||||
|
||||
onUpdated(() => getFirstElement());
|
||||
|
||||
return {
|
||||
children,
|
||||
firstElement,
|
||||
};
|
||||
};
|
@ -1,5 +1,128 @@
|
||||
import { DropdownPlacement } from "./interface";
|
||||
|
||||
import { Component, onMounted, onUpdated, ref, VNode, VNodeTypes } from "vue";
|
||||
|
||||
export interface SlotChildren {
|
||||
value?: VNode[];
|
||||
}
|
||||
|
||||
// Quoted from arco-vue
|
||||
// https://github.com/arco-design/arco-design-vue/blob/main/packages/web-vue/components/_utils/vue-utils.ts
|
||||
export enum ShapeFlags {
|
||||
ELEMENT = 1,
|
||||
FUNCTIONAL_COMPONENT = 1 << 1,
|
||||
STATEFUL_COMPONENT = 1 << 2,
|
||||
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT,
|
||||
TEXT_CHILDREN = 1 << 3,
|
||||
ARRAY_CHILDREN = 1 << 4,
|
||||
SLOTS_CHILDREN = 1 << 5,
|
||||
TELEPORT = 1 << 6,
|
||||
SUSPENSE = 1 << 7,
|
||||
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8,
|
||||
COMPONENT_KEPT_ALIVE = 1 << 9,
|
||||
}
|
||||
|
||||
export const isScrollElement = (element: HTMLElement) => {
|
||||
return (
|
||||
element.scrollHeight > element.offsetHeight ||
|
||||
element.scrollWidth > element.offsetWidth
|
||||
);
|
||||
};
|
||||
|
||||
export const getScrollElements = (container: HTMLElement | undefined) => {
|
||||
const scrollElements: HTMLElement[] = [];
|
||||
let element: HTMLElement | undefined = container;
|
||||
while (element && element !== document.documentElement) {
|
||||
if (isScrollElement(element)) {
|
||||
scrollElements.push(element);
|
||||
}
|
||||
element = element.parentElement ?? undefined;
|
||||
}
|
||||
return scrollElements;
|
||||
};
|
||||
|
||||
export const isElement = (vn: VNode) => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.ELEMENT);
|
||||
};
|
||||
|
||||
export const isComponent = (
|
||||
vn: VNode,
|
||||
type?: VNodeTypes
|
||||
): type is Component => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.COMPONENT);
|
||||
};
|
||||
|
||||
export const isArrayChildren = (
|
||||
vn: VNode,
|
||||
children: VNode["children"]
|
||||
): children is VNode[] => {
|
||||
return Boolean(vn && vn.shapeFlag & ShapeFlags.ARRAY_CHILDREN);
|
||||
};
|
||||
|
||||
export const getChildrenArray = (vn: VNode): VNode[] | undefined => {
|
||||
if (isArrayChildren(vn, vn.children)) {
|
||||
return vn.children;
|
||||
}
|
||||
if (Array.isArray(vn)) {
|
||||
return vn;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getFirstElementFromVNode = (
|
||||
vn: VNode
|
||||
): HTMLElement | undefined => {
|
||||
if (isElement(vn)) {
|
||||
return vn.el as HTMLElement;
|
||||
}
|
||||
if (isComponent(vn)) {
|
||||
if ((vn.el as Node)?.nodeType === 1) {
|
||||
return vn.el as HTMLElement;
|
||||
}
|
||||
if (vn.component?.subTree) {
|
||||
const ele = getFirstElementFromVNode(vn.component.subTree);
|
||||
if (ele) return ele;
|
||||
}
|
||||
} else {
|
||||
const children = getChildrenArray(vn);
|
||||
return getFirstElementFromChildren(children);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const getFirstElementFromChildren = (
|
||||
children: VNode[] | undefined
|
||||
): HTMLElement | undefined => {
|
||||
if (children && children.length > 0) {
|
||||
for (const child of children) {
|
||||
const element = getFirstElementFromVNode(child);
|
||||
if (element) return element;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const useFirstElement = () => {
|
||||
const children: SlotChildren = {};
|
||||
const firstElement = ref<HTMLElement>();
|
||||
|
||||
const getFirstElement = () => {
|
||||
const element = getFirstElementFromChildren(children.value);
|
||||
if (element !== firstElement.value) {
|
||||
firstElement.value = element;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => getFirstElement());
|
||||
|
||||
onUpdated(() => getFirstElement());
|
||||
|
||||
return {
|
||||
children,
|
||||
firstElement,
|
||||
};
|
||||
};
|
||||
|
||||
export const transformPlacement = (
|
||||
placement: DropdownPlacement
|
||||
): DropdownPlacement => {
|
||||
|
@ -471,62 +471,48 @@ export default {
|
||||
|
||||
:::
|
||||
|
||||
::: title 其它属性
|
||||
::: title 跟随鼠标
|
||||
:::
|
||||
|
||||
::: demo
|
||||
|
||||
<template>
|
||||
<lay-space>
|
||||
<lay-dropdown placement="bottom-start" autoFitWidth updateAtScroll>
|
||||
<lay-button type="primary">autoFitWidth</lay-button>
|
||||
<lay-dropdown ref="dropdownRefEl" :trigger="['click','contextMenu']" alignPoint>
|
||||
<div style="width:500px;height:300px;background-color:#eee"></div>
|
||||
<template #content>
|
||||
<lay-dropdown-menu>
|
||||
<lay-dropdown-menu-item>选项一</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项二1111111111111111111111</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
|
||||
</lay-dropdown-menu>
|
||||
<lay-dropdown-menu>
|
||||
<lay-dropdown-menu-item>选项一</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项二</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
|
||||
</lay-dropdown-menu>
|
||||
</template>
|
||||
</lay-dropdown>
|
||||
<lay-dropdown placement="bottom-start" :autoFitMinWidth="false" updateAtScroll>
|
||||
<lay-button type="primary">关闭 autoFitMinWidth</lay-button>
|
||||
<template #content>
|
||||
<lay-dropdown-menu>
|
||||
<lay-dropdown-menu-item>选项一</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项二111111111</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
|
||||
</lay-dropdown-menu>
|
||||
</template>
|
||||
</lay-dropdown>
|
||||
<lay-dropdown placement="bottom-start" updateAtScroll>
|
||||
<lay-button type="primary">updateAtScroll</lay-button>
|
||||
<template #content>
|
||||
<lay-dropdown-menu>
|
||||
<lay-dropdown-menu-item>选项一</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项二111111111</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
|
||||
</lay-dropdown-menu>
|
||||
</template>
|
||||
</lay-dropdown>
|
||||
<lay-dropdown placement="bottom-start" updateAtScroll :contentOffset="8">
|
||||
<lay-button type="primary">contentOffset: 8px</lay-button>
|
||||
<template #content>
|
||||
<lay-dropdown-menu>
|
||||
<lay-dropdown-menu-item>选项一</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项二</lay-dropdown-menu-item>
|
||||
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
|
||||
</lay-dropdown-menu>
|
||||
</template>
|
||||
</lay-dropdown>
|
||||
</lay-space>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const dropdownRefEl = ref(null);
|
||||
const handleScroll = () => {
|
||||
dropdownRefEl.value.hide();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const scrollEl = document.querySelector(".layui-body")
|
||||
scrollEl.addEventListener("scroll", handleScroll)
|
||||
})
|
||||
return {
|
||||
dropdownRefEl
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
:::
|
||||
|
||||
|
||||
::: title Dropdown 属性
|
||||
:::
|
||||
|
||||
@ -550,7 +536,9 @@ import { ref, computed } from 'vue'
|
||||
| mouseEnterDelay | mouseEnter 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
||||
| mouseLeaveDelay | mouseLeave 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
||||
| focusDelay | focus 事件延迟触发的时间, trigger focus 有效 | `number` | `150` | - |
|
||||
|
||||
| alignPoint |跟随鼠标|`boolean`|`false`|`true` `false`|
|
||||
| contentClass| 弹出内容的类名 | `string`| -| -|
|
||||
| contentStyle| 弹出内容的样式 | `string` | - | -|
|
||||
|
||||
:::
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user