(component): [dropdown]新增 contentClass 属性

*新增 contentClass
*新增 contentStyle
*新增 alignPoint
This commit is contained in:
sight 2022-09-16 18:55:13 +08:00
parent 106aadf788
commit 0fe5aa2644
4 changed files with 157 additions and 168 deletions

View File

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

View File

@ -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,
};
};

View File

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

View File

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