feat(dropdown): show-arrow prop (#656)

* feat:
-dropdown add show-arrow prop

* feat: update snapshot

* feat(dropdown): dropdown add show-arrow

* feat(dropdown): dropdown add show-arrow

* feat(dropdown): 更新快照

* feat(dropdown): 添加箭头优化代码

* feat(dropdown): 添加箭头优化代码

* feat: 箭头公用函数提出增加dropdown demo

* Apply suggestions from code review

Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
小魔王 2021-07-31 00:01:24 +08:00 committed by GitHub
parent 1256df68cf
commit d35ff882f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 181 additions and 14 deletions

View File

@ -68,6 +68,7 @@
### Feats
- `n-dropdown` add `show-arrow` prop, closes [#647](https://github.com/TuSimple/naive-ui/issues/647).
- `n-time-picker` add `actions` prop, closes [#401](https://github.com/TuSimple/naive-ui/issues/401).
- `n-mention` add `render-label` prop.
- `n-switch` add `checked`, `unchecked` slots.

View File

@ -67,6 +67,7 @@
### Feats
- `n-dropdown` 选项新增 `show-arrow`属性,关闭 [#647](https://github.com/TuSimple/naive-ui/issues/647)
- `n-time-picker` 增加 `actions` 属性, 关闭 [#401](https://github.com/TuSimple/naive-ui/issues/401)
- `n-mention` 新增 `render-label` 属性
- `n-switch` 增加 `checked`、`unchecked` 插槽

View File

@ -0,0 +1,49 @@
# Basic
Show Arrow
```html
<n-dropdown
trigger="click"
@select="handleSelect"
:options="options"
:show-arrow="true"
>
<n-button> Go For a Trip </n-button>
</n-dropdown>
```
```js
import { defineComponent } from 'vue'
import { useMessage } from 'naive-ui'
export default defineComponent({
setup () {
const message = useMessage()
return {
options: [
{
label: 'Marina Bay Sands',
key: 'marina bay sands',
disabled: true
},
{
label: "Brown's Hotel, London",
key: "brown's hotel, london"
},
{
label: 'Atlantis Bahamas, Nassau',
key: 'atlantis nahamas, nassau'
},
{
label: 'The Beverly Hills Hotel, Los Angeles',
key: 'the beverly hills hotel, los angeles'
}
],
handleSelect (key) {
message.info(key)
}
}
}
})
```

View File

@ -8,6 +8,7 @@ When you have some functions to trigger.
basic
trigger
cascade
arrow
placement
size
manual-position

View File

@ -0,0 +1,47 @@
# 显示箭头
```html
<n-dropdown
trigger="click"
@select="handleSelect"
:options="options"
:show-arrow="true"
>
<n-button>找个地方休息</n-button>
</n-dropdown>
```
```js
import { defineComponent } from 'vue'
import { useMessage } from 'naive-ui'
export default defineComponent({
setup () {
const message = useMessage()
return {
options: [
{
label: '滨海湾金沙,新加坡',
key: 'marina bay sands',
disabled: true
},
{
label: '布朗酒店,伦敦',
key: "brown's hotel, london"
},
{
label: '亚特兰蒂斯巴哈马,拿骚',
key: 'atlantis nahamas, nassau'
},
{
label: '比佛利山庄酒店,洛杉矶',
key: 'the beverly hills hotel, los angeles'
}
],
handleSelect (key) {
message.info(key)
}
}
}
})
```

View File

@ -8,6 +8,7 @@
basic
trigger
cascade
arrow
placement
size
manual-position

View File

@ -92,6 +92,8 @@ const dropdownBaseProps = {
type: String as PropType<'small' | 'medium' | 'large' | 'huge'>,
default: 'medium'
},
showArrow: Boolean,
arrowStyle: [String, Object] as PropType<string | CSSProperties>,
inverted: Boolean,
placement: {
type: String as PropType<FollowerPlacement>,
@ -389,10 +391,18 @@ export default defineComponent({
const { mergedClsPrefix } = this
const dropdownProps = {
ref: createRefSetter(ref),
class: [className, `${mergedClsPrefix}-dropdown`],
class: [
className,
`${mergedClsPrefix}-dropdown`,
{
[`${mergedClsPrefix}-popover--show-arrow`]: this.showArrow
}
],
clsPrefix: mergedClsPrefix,
tmNodes: this.tmNodes,
style: [style, this.cssVars as CSSProperties],
showArrow: this.showArrow,
arrowStyle: this.arrowStyle,
onMouseenter,
onMouseleave
}

View File

@ -6,7 +6,8 @@ import {
InjectionKey,
PropType,
provide,
Ref
Ref,
CSSProperties
} from 'vue'
import { TreeNode } from 'treemate'
import NDropdownOption from './DropdownOption'
@ -19,6 +20,7 @@ import {
DropdownIgnoredOption,
DropdownOption
} from './interface'
import { renderArrow } from '../../popover/src/PopoverBody'
export interface NDropdownMenuInjection {
showIconRef: Ref<boolean>
@ -31,6 +33,11 @@ export const dropdownMenuInjectionKey: InjectionKey<NDropdownMenuInjection> =
export default defineComponent({
name: 'DropdownMenu',
props: {
showArrow: {
type: Boolean,
required: false
},
arrowStyle: [String, Object] as PropType<string | CSSProperties>,
clsPrefix: {
type: String,
required: true
@ -78,7 +85,12 @@ export default defineComponent({
})
},
render () {
const { parentKey, clsPrefix } = this
const { parentKey, clsPrefix, showArrow, arrowStyle } = this
const arrowWrapParams = {
clsPrefix,
showArrow,
arrowStyle
}
return (
<div class={`${clsPrefix}-dropdown-menu`}>
{this.tmNodes.map((tmNode) => {
@ -104,6 +116,7 @@ export default defineComponent({
/>
)
})}
{renderArrow(arrowWrapParams)}
</div>
)
}

View File

@ -98,6 +98,17 @@ describe('n-dropdown', () => {
wrapper.unmount()
})
it('shows arrow', async () => {
const wrapper = mountDropdown()
const triggerNodeWrapper = wrapper.find('span')
expect(triggerNodeWrapper.exists()).toBe(true)
await triggerNodeWrapper.trigger('click')
expect(document.querySelector('.n-popover-arrow-wrapper')).toMatchSnapshot()
wrapper.unmount()
})
it('inverted style', async () => {
const wrapper = mountDropdown({ inverted: true })

View File

@ -6,6 +6,7 @@ exports[`n-dropdown dropdown clickoutside 1`] = `
style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);"
>
<div
class="n-dropdown-option"
>
@ -232,6 +233,8 @@ exports[`n-dropdown dropdown clickoutside 1`] = `
</div>
</div>
<!---->
</div>
`;
@ -241,6 +244,7 @@ exports[`n-dropdown inverted style 1`] = `
style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: rgb(0, 20, 40); --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: #18a058; --option-color-active: #18a058; --option-text-color: #BBB; --option-text-color-hover: #FFF; --option-text-color-active: #FFF; --option-text-color-child-active: #FFF; --prefix-color: #BBB; --suffix-color: #BBB; --group-header-text-color: #AAA;"
>
<div
class="n-dropdown-option"
>
@ -448,6 +452,8 @@ exports[`n-dropdown inverted style 1`] = `
</div>
</div>
<!---->
</div>
`;
@ -457,6 +463,7 @@ exports[`n-dropdown should work with \`render-icon\` props 1`] = `
style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);"
>
<div
class="n-dropdown-option"
>
@ -832,6 +839,8 @@ exports[`n-dropdown should work with \`render-icon\` props 1`] = `
</div>
</div>
<!---->
</div>
`;
@ -841,6 +850,7 @@ exports[`n-dropdown should work with \`render-label\` props 1`] = `
style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);"
>
<div
class="n-dropdown-option"
>
@ -1064,15 +1074,20 @@ exports[`n-dropdown should work with \`render-label\` props 1`] = `
</div>
</div>
<!---->
</div>
`;
exports[`n-dropdown shows arrow 1`] = `null`;
exports[`n-dropdown shows menu after click 1`] = `
<div
class="n-dropdown-menu n-popover n-dropdown"
style="--box-shadow: 0 3px 6px -4px rgba(0, 0, 0, .12), 0 6px 16px 0 rgba(0, 0, 0, .08), 0 9px 28px 8px rgba(0, 0, 0, .05); --bezier: cubic-bezier(.4, 0, .2, 1); --bezier-ease-in: cubic-bezier(.4, 0, 1, 1); --bezier-ease-out: cubic-bezier(0, 0, .2, 1); --font-size: 14px; --text-color: rgb(51, 54, 57); --color: #fff; --divider-color: rgb(239, 239, 245); --border-radius: 3px; --arrow-height: 6px; --arrow-offset: 10px; --arrow-offset-vertical: 10px; --padding: 4px 0; --space: 6px; --space-arrow: 10px; --option-height: 34px; --option-prefix-width: 14px; --option-icon-prefix-width: 36px; --option-suffix-width: 14px; --option-icon-suffix-width: 32px; --option-icon-size: 16px; --option-opacity-disabled: 0.5; --option-color-hover: rgb(243, 243, 245); --option-color-active: rgba(24, 160, 88, 0.1); --option-text-color: rgb(51, 54, 57); --option-text-color-hover: rgb(51, 54, 57); --option-text-color-active: #18a058; --option-text-color-child-active: #18a058; --prefix-color: rgb(51, 54, 57); --suffix-color: rgb(51, 54, 57); --group-header-text-color: rgb(158, 164, 170);"
>
<div
class="n-dropdown-option"
>
@ -1280,5 +1295,7 @@ exports[`n-dropdown shows menu after click 1`] = `
</div>
</div>
<!---->
</div>
`;

View File

@ -58,6 +58,27 @@ export const popoverBodyProps = {
maxWidth: Number
}
interface arrowWrapProps {
showArrow: boolean
arrowStyle?: string | CSSProperties
clsPrefix: string
}
export const renderArrow = ({
showArrow,
arrowStyle,
clsPrefix
}: arrowWrapProps): VNode | null => {
if (showArrow) {
return h(
'div',
{ key: '__popover-arrow__', class: `${clsPrefix}-popover-arrow-wrapper` },
h('div', { class: `${clsPrefix}-popover-arrow`, style: arrowStyle })
)
}
return null
}
export default defineComponent({
name: 'PopoverBody',
inheritAttrs: false,
@ -206,6 +227,11 @@ export default defineComponent({
const { value: mergedClsPrefix } = mergedClsPrefixRef
if (!renderBody) {
const { value: extraClass } = NPopover.extraClassRef
const arrowWrapParams = {
showArrow: props.showArrow,
arrowStyle: props.arrowStyle,
clsPrefix: mergedClsPrefix
}
contentNode = h(
'div',
mergeProps(
@ -238,17 +264,7 @@ export default defineComponent({
) : (
renderSlot(slots, 'default')
),
props.showArrow ? (
<div
class={`${mergedClsPrefix}-popover-arrow-wrapper`}
key="__popover-arrow__"
>
<div
class={`${mergedClsPrefix}-popover-arrow`}
style={props.arrowStyle}
/>
</div>
) : null
renderArrow(arrowWrapParams)
]
)
} else {