feat(popover): trigger prop support 'focus', closes #477

This commit is contained in:
07akioni 2021-07-25 14:34:13 +08:00
parent a78c6de0d3
commit c11fd7658d
8 changed files with 65 additions and 23 deletions

View File

@ -7,9 +7,10 @@
- `n-time-picker` add `actions` prop, closes [#401](https://github.com/TuSimple/naive-ui/issues/401).
- `n-switch` add `checked`, `unchecked` slots.
- `n-switch` add `loading` prop, closes [#301](https://github.com/TuSimple/naive-ui/issues/301).
- `n-select` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-tree-select` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-cascader` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-select` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300).
- `n-tree-select` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300).
- `n-cascader` pressing arrow down can open menu, ref [#300](https://github.com/TuSimple/naive-ui/issues/300).
- `n-popover`'s `trigger` prop support `'focus'`, closes [#477](https://github.com/TuSimple/naive-ui/issues/477).
### Fixes

View File

@ -10,6 +10,7 @@
- `n-select` 按下箭头会打开菜单,有关 [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-tree-select` 按下箭头会打开菜单,有关 [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-cascader` 按下箭头会打开菜单,有关 [#300](https://github.com/TuSimple/naive-ui/issues/300)
- `n-popover``trigger` 属性支持 `'focus'`,关闭 [#477](https://github.com/TuSimple/naive-ui/issues/477)
### Fixes

View File

@ -37,7 +37,7 @@ header
| show-arrow | `boolean` | `true` | Whether to show arrow if set. |
| show | `boolean` | `undefined` | Whether to show arrow. |
| title | `string` | `undefined` | Popover title. |
| trigger | `'hover' \| 'click' \| 'manual'` | `'hover'` | The popover trigger type. |
| trigger | `'hover' \| 'click' \| 'focus' \| 'manual'` | `'hover'` | The popover trigger type. |
| width | `number \| 'trigger'` | `undefined` | `'trigger'` means popover's witdh will follow its trigger's width. |
| x | `number` | `undefined` | The CSS `left` pixel value when popover manually positioned (x, y need to be set together). |
| y | `number` | `undefined` | The CSS `top` pixel value when popover manually positioned (x, y need to be set together). |

View File

@ -6,19 +6,25 @@
<template #trigger>
<n-button>Hover</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="click">
<template #trigger>
<n-button> Click </n-button>
<n-button>Click</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="focus">
<template #trigger>
<n-button>Click</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="manual" :show="showPopover">
<template #trigger>
<n-button @click="showPopover = !showPopover"> Manual </n-button>
<n-button @click="showPopover = !showPopover">Manual</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
</n-space>
```

View File

@ -38,7 +38,7 @@ header
| show-arrow | `boolean` | `true` | 是否显示箭头 |
| show | `boolean` | `undefined` | 是否展示 popover |
| title | `string` | `undefined` | popover 的 title 信息 |
| trigger | `'hover' \| 'click' \| 'manual'` | `'hover'` | popover 的触发方式 |
| trigger | `'hover' \| 'click' \| 'focus' \| 'manual'` | `'hover'` | popover 的触发方式 |
| width | `number \| 'trigger'` | `undefined` | `'trigger'` 表示 popover 的宽度会和它的触发元素一致 |
| x | `number` | `undefined` | 手动控制位置时弹出内容的 CSS `left` 的像素值xy 都设置才能生效) |
| y | `number` | `undefined` | 手动控制位置时弹出内容的 CSS `top` 的像素值xy 都设置才能生效) |

View File

@ -6,19 +6,25 @@
<template #trigger>
<n-button>悬浮</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="click">
<template #trigger>
<n-button> 点击 </n-button>
<n-button>点击</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="focus">
<template #trigger>
<n-button>聚焦</n-button>
</template>
<span>I wish they all could be California girls</span>
</n-popover>
<n-popover trigger="manual" :show="showPopover">
<template #trigger>
<n-button @click="showPopover = !showPopover"> 手动 </n-button>
<n-button @click="showPopover = !showPopover">手动</n-button>
</template>
<span> I wish they all could be California girls </span>
<span>I wish they all could be California girls</span>
</n-popover>
</n-space>
```

View File

@ -30,23 +30,34 @@ const bodyPropKeys = Object.keys(popoverBodyProps) as Array<
keyof typeof popoverBodyProps
>
const triggerEventMap = {
focus: ['onFocus', 'onBlur'],
click: ['onClick'],
hover: ['onMouseenter', 'onMouseleave'],
manual: []
} as const
function appendEvents (
vNode: VNode,
trigger: PopoverTrigger,
events: {
onClick: (e: MouseEvent) => void
onMouseenter: (e: MouseEvent) => void
onMouseleave: (e: MouseEvent) => void
onFocus: (e: FocusEvent) => void
onBlur: (e: FocusEvent) => void
}
): void {
Object.entries(events).forEach(([key, handler]) => {
triggerEventMap[trigger].forEach((eventName) => {
if (!vNode.props) vNode.props = {}
else {
vNode.props = Object.assign({}, vNode.props)
}
const originalHandler = vNode.props[key]
if (!originalHandler) vNode.props[key] = handler
const originalHandler = vNode.props[eventName]
const handler = events[eventName]
if (!originalHandler) vNode.props[eventName] = handler
else {
vNode.props[key] = (...args: unknown[]) => {
vNode.props[eventName] = (...args: unknown[]) => {
originalHandler(...args)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(handler as any)(...args)
@ -263,6 +274,20 @@ export default defineComponent({
hideTimerIdRef.value = null
}
}
function handleFocus (): void {
const mergedDisabled = getMergedDisabled()
if (props.trigger === 'focus' && !mergedDisabled) {
if (getMergedShow()) return
doUpdateShow(true)
}
}
function handleBlur (): void {
const mergedDisabled = getMergedDisabled()
if (props.trigger === 'focus' && !mergedDisabled) {
if (!getMergedShow()) return
doUpdateShow(false)
}
}
function handleMouseEnter (): void {
const mergedDisabled = getMergedDisabled()
if (props.trigger === 'hover' && !mergedDisabled) {
@ -352,6 +377,8 @@ export default defineComponent({
handleClick,
handleMouseEnter,
handleMouseLeave,
handleFocus,
handleBlur,
setTriggerVNode (v: VNode | null) {
triggerVNode = v
},
@ -373,11 +400,12 @@ export default defineComponent({
triggerVNode.type === textVNodeType
? h('span', [triggerVNode])
: triggerVNode
appendEvents(triggerVNode, {
appendEvents(triggerVNode, this.trigger, {
onClick: this.handleClick,
onMouseenter: this.handleMouseEnter,
onMouseleave: this.handleMouseLeave
onMouseleave: this.handleMouseLeave,
onFocus: this.handleFocus,
onBlur: this.handleBlur
})
}
this.setTriggerVNode(triggerVNode)

View File

@ -1,6 +1,6 @@
import { Ref, InjectionKey, CSSProperties, VNode } from 'vue'
export type PopoverTrigger = 'click' | 'hover' | 'manual'
export type PopoverTrigger = 'click' | 'hover' | 'focus' | 'manual'
export interface PopoverInst {
syncPosition: () => void