✨(component): [dropdown]新增 contentClass 属性
*新增 contentClass *新增 contentStyle *新增 alignPoint
This commit is contained in:
parent
106aadf788
commit
0fe5aa2644
@ -44,7 +44,7 @@ import {
|
|||||||
DropdownContext,
|
DropdownContext,
|
||||||
} from "./interface";
|
} from "./interface";
|
||||||
import TeleportWrapper from "../_components/teleportWrapper.vue";
|
import TeleportWrapper from "../_components/teleportWrapper.vue";
|
||||||
import { useFirstElement } from "./useFirstElement";
|
import { useFirstElement, isScrollElement, getScrollElements } from "./util";
|
||||||
import RenderFunction from "../_components/renderFunction";
|
import RenderFunction from "../_components/renderFunction";
|
||||||
import { transformPlacement } from "./util";
|
import { transformPlacement } from "./util";
|
||||||
|
|
||||||
@ -67,11 +67,11 @@ export interface LayDropdownProps {
|
|||||||
mouseEnterDelay?: number;
|
mouseEnterDelay?: number;
|
||||||
mouseLeaveDelay?: number;
|
mouseLeaveDelay?: number;
|
||||||
focusDelay?: number;
|
focusDelay?: number;
|
||||||
// 未完善,暂不开放
|
|
||||||
alignPoint?: boolean;
|
alignPoint?: boolean;
|
||||||
popupContainer?: string | undefined;
|
|
||||||
contentClass?: string | Array<string | object> | object;
|
contentClass?: string | Array<string | object> | object;
|
||||||
contentStyle?: StyleValue;
|
contentStyle?: StyleValue;
|
||||||
|
// wip
|
||||||
|
popupContainer?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<LayDropdownProps>(), {
|
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(() => {
|
const handleScroll = useThrottleFn(() => {
|
||||||
if (openState.value) {
|
if (openState.value) {
|
||||||
updateContentStyle();
|
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 { 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 = (
|
export const transformPlacement = (
|
||||||
placement: DropdownPlacement
|
placement: DropdownPlacement
|
||||||
): DropdownPlacement => {
|
): DropdownPlacement => {
|
||||||
|
@ -471,62 +471,48 @@ export default {
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: title 其它属性
|
::: title 跟随鼠标
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: demo
|
::: demo
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<lay-space>
|
<lay-dropdown ref="dropdownRefEl" :trigger="['click','contextMenu']" alignPoint>
|
||||||
<lay-dropdown placement="bottom-start" autoFitWidth updateAtScroll>
|
<div style="width:500px;height:300px;background-color:#eee"></div>
|
||||||
<lay-button type="primary">autoFitWidth</lay-button>
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<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>选项二1111111111111111111111</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-item>
|
||||||
</lay-dropdown-menu>
|
</lay-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</lay-dropdown>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import { ref, computed } from 'vue'
|
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>
|
</script>
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
::: title Dropdown 属性
|
::: title Dropdown 属性
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@ -550,7 +536,9 @@ import { ref, computed } from 'vue'
|
|||||||
| mouseEnterDelay | mouseEnter 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
| mouseEnterDelay | mouseEnter 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
||||||
| mouseLeaveDelay | mouseLeave 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
| mouseLeaveDelay | mouseLeave 事件延迟触发的时间, trigger hover 有效 | `number` | `150` | - |
|
||||||
| focusDelay | focus 事件延迟触发的时间, trigger focus 有效 | `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