From c55f1e981422eff909eaebf7918c1482ac9e20c8 Mon Sep 17 00:00:00 2001 From: CatsAndMice Date: Wed, 6 Oct 2021 14:59:02 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E8=A7=A3=E5=86=B3git=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/popover/__tests__/popover.spec.ts | 96 +++++++++ devui/popover/index.ts | 2 +- devui/popover/src/debounce.ts | 9 + devui/popover/src/popover.scss | 34 ++-- devui/popover/src/popover.tsx | 41 ++-- docs/components/popover/index.md | 260 +++++++----------------- 6 files changed, 220 insertions(+), 222 deletions(-) create mode 100644 devui/popover/__tests__/popover.spec.ts create mode 100644 devui/popover/src/debounce.ts diff --git a/devui/popover/__tests__/popover.spec.ts b/devui/popover/__tests__/popover.spec.ts new file mode 100644 index 00000000..699d8922 --- /dev/null +++ b/devui/popover/__tests__/popover.spec.ts @@ -0,0 +1,96 @@ +import {shallowMount } from '@vue/test-utils'; +import DPopover from '../src/popover' +describe('DPopover', () => { + it('visible', () => { + const wrapper = shallowMount(DPopover, { + props: { + visible: true + } + }) + expect(wrapper.props().visible).toBeTruthy() + expect(wrapper.classes().includes('devui-popover-isVisible')).toBeTruthy() + }) + + it('position', () => { + const left = 'left' + const wrapper = shallowMount(DPopover, { + props: { + position: left + } + }) + expect(wrapper.props().position).toBe(left) + expect(wrapper.classes().includes(left)).toBeTruthy() + }) + + it('content', () => { + const content = '自定义内容' + const wrapper = shallowMount(DPopover, { + props: { + content + } + }) + expect(wrapper.props().content).toBe(content) + expect(wrapper.find('.devui-popover-content').text()).toBe(content) + }) + + it('trigger click', async () => { + const wrapper = shallowMount(DPopover) + const isVisible = () => expect(wrapper.classes().includes('devui-popover-isVisible')) + isVisible().toBeFalsy() + await wrapper.find('.devui-popover-reference').trigger('click') + isVisible().toBeTruthy(); + }) + + it('trigger mouse', async () => { + const wrapper = shallowMount(DPopover, { + props: { + trigger: 'hover' + } + }) + const isVisible = () => expect(wrapper.classes().includes('devui-popover-isVisible')) + isVisible().toBeFalsy() + wrapper.find('.devui-popover-reference').trigger('onMouseenter') + wrapper.vm.$nextTick(() => { + isVisible().toBeTruthy() + }) + }) + + it('zIndex', () => { + const zIndex = 1000 + const wrapper = shallowMount(DPopover, { + props: { + zIndex + } + }) + expect(wrapper.props().zIndex).toBe(zIndex); + }) + + it('popType', () => { + const wrapper = shallowMount(DPopover, { + props: { + popType: 'warning' + } + }) + expect(wrapper.find('.devui-popover-icon').attributes().name).toBe('warning-o') + }) + + it('showAnimation', () => { + const wrapper = shallowMount(DPopover, { + props: { + showAnimation: false, + } + }) + expect(wrapper.classes().includes('devui-popover-animation')).toBeFalsy() + }) + + it('popMaxWidth', () => { + const popMaxWidth = 30 + const wrapper = shallowMount(DPopover, { + props: { + popMaxWidth + } + }) + expect(wrapper.props().popMaxWidth).toBe(popMaxWidth) + }) + +}) \ No newline at end of file diff --git a/devui/popover/index.ts b/devui/popover/index.ts index 8325a36c..6d9070c3 100644 --- a/devui/popover/index.ts +++ b/devui/popover/index.ts @@ -10,7 +10,7 @@ export { Popover } export default { title: 'Popover 悬浮提示', category: '反馈', - status: '开发中', // TODO: 组件若开发完成则填入"已完成",并删除该注释 + status: '已完成', install(app: App): void { app.use(Popover as any); } diff --git a/devui/popover/src/debounce.ts b/devui/popover/src/debounce.ts new file mode 100644 index 00000000..a9e0115b --- /dev/null +++ b/devui/popover/src/debounce.ts @@ -0,0 +1,9 @@ +export default ( callBack,wait: number) => { + let time = null + return () => { + time && clearTimeout(time); + time = setTimeout(() => { + callBack?.() + }, wait) + } +} \ No newline at end of file diff --git a/devui/popover/src/popover.scss b/devui/popover/src/popover.scss index 60f8e1d3..3c6a1f10 100644 --- a/devui/popover/src/popover.scss +++ b/devui/popover/src/popover.scss @@ -15,14 +15,13 @@ position: relative; @include some-display; - &::after { + .after { content: ''; width: 12px; display: none; height: 12px; transform: rotate(45deg); position: absolute; - z-index: 1060; background-color: $devui-feedback-overlay-bg; } @@ -31,7 +30,7 @@ @include some-display; } - &::after { + .after { @include some-display; } } @@ -39,6 +38,7 @@ .devui-popover-content { position: absolute; display: none; + overflow: hidden; padding: 5px 14px; align-items: center; flex-wrap: wrap; @@ -71,8 +71,8 @@ @include some-animation; } - &::after { - $devui-popover-animation-wait:0.1s; + .after { + $devui-popover-animation-wait:0.02s; opacity: 0; @include some-animation; @@ -112,7 +112,7 @@ transform: translate(-100%, -50%); } - &::after { + .after { @include left-postion--after; } } @@ -122,7 +122,7 @@ @include left-postion--content; } - &::after { + .after { @include left-postion--after; top: var(--devui-popover-offset); @@ -138,7 +138,7 @@ bottom: 0; } - &::after { + .after { @include left-postion--after; bottom: var(--devui-popover-offset); @@ -168,7 +168,7 @@ transform: translate(-50%, -100%); } - &::after { + .after { left: 50%; @include top-postion--after; } @@ -179,7 +179,7 @@ @include top-postion--content; } - &::after { + .after { @include top-postion--after; left: var(--devui-popover-offset); @@ -196,7 +196,7 @@ right: 0; } - &::after { + .after { @include top-postion--after; right: var(--devui-popover-offset); @@ -224,7 +224,7 @@ transform: translate(100%, -50%); } - &::after { + .after { @include right-postion--after; top: 50%; @@ -239,7 +239,7 @@ top: 0; } - &::after { + .after { @include right-postion--after; } } @@ -252,7 +252,7 @@ top: auto; } - &::after { + .after { @include right-postion--after; bottom: var(--devui-popover-offset); @@ -284,7 +284,7 @@ transform: translate(-50%, 100%); } - &::after { + .after { @include bottom-postion--after; left: 50%; @@ -298,7 +298,7 @@ @include bottom-postion--content; } - &::after { + .after { @include bottom-postion--after; } } @@ -311,7 +311,7 @@ right: 0; } - &::after { + .after { @include bottom-postion--after; left: auto; diff --git a/devui/popover/src/popover.tsx b/devui/popover/src/popover.tsx index 9fbd12ca..9d7bbd97 100644 --- a/devui/popover/src/popover.tsx +++ b/devui/popover/src/popover.tsx @@ -1,4 +1,5 @@ import { defineComponent, toRefs, ref, CSSProperties, reactive } from 'vue' +import debounce from './debounce'; import clickoutsideDirective from '../../shared/devui-directive/clickoutside' import './popover.scss' @@ -42,29 +43,41 @@ export default defineComponent({ type: Number as () => CSSProperties, default: 1060 }, + popType: { type: String as () => popType, default: 'default' }, + showAnimation: { type: Boolean, default: true }, + mouseEnterDelay: { type: Number, default: 150 }, + mouseLeaveDelay: { type: Number, default: 100 + }, + + popMaxWidth: { + type: Number, + default: undefined + }, + + popoverStyle: { + type: Object, + default: () => ({}) } }, setup(props, ctx) { - let enter = null - let leave = null const visible = ref(props.visible); - const { position, content, zIndex, trigger, popType, mouseEnterDelay, mouseLeaveDelay, showAnimation } = toRefs(props); + const { position, content, zIndex, trigger, popType, popoverStyle, mouseEnterDelay, mouseLeaveDelay, showAnimation, popMaxWidth } = toRefs(props); const isClick = trigger.value === 'click' const iconType = reactive(popTypeClass[popType.value]) const event = function () { @@ -75,28 +88,21 @@ export default defineComponent({ visible.value = true } const onClick = isClick ? event : null; - const onMouseenter = isClick ? null : () => { - enter && clearTimeout(enter); - enter = setTimeout(() => { - visible.value = true - }, mouseEnterDelay.value) - } - const onMouseleave = isClick ? null : () => { - leave && clearTimeout(leave) - leave = setTimeout(() => { - visible.value = false - }, mouseLeaveDelay.value) - } + const enter = debounce(() => { visible.value = true }, mouseEnterDelay.value) + const leave = debounce(() => { visible.value = false }, mouseLeaveDelay.value) + const onMouseenter = isClick ? null : enter + const onMouseleave = isClick ? null : leave const hiddenContext = function () { visible.value = false } - return () => { const { slots } = ctx; const style: CSSProperties = { - zIndex: zIndex.value + zIndex: zIndex.value, + ...popoverStyle.value } + popMaxWidth.value && (style.maxWidth = `${popMaxWidth.value}px`) return (
+
{slots.reference?.()}
diff --git a/docs/components/popover/index.md b/docs/components/popover/index.md index d6abd794..5fff421e 100644 --- a/docs/components/popover/index.md +++ b/docs/components/popover/index.md @@ -6,8 +6,10 @@ ### 基本用法 当Popover弹出时,会基于`reference`插槽的内容进行定位。 - -
+:::demo +```vue + + ``` +::: ### 自定义内容 自定义`reference`插槽的内容与弹出提示内容。 -
- +:::demo + +```vue + + + ``` +::: + ### 弹出位置 总共支持12个弹出位置。 -
- - - - - - - - - - - - -
- -
- - - - - - - - - - - - -
- -
- - - - - - - - - - - - -
+:::demo -
- - - - - - - - - - - - -
- -```html -
- +```vue +