mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-24 12:45:18 +08:00
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:
parent
1256df68cf
commit
d35ff882f2
@ -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.
|
||||
|
@ -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` 插槽
|
||||
|
49
src/dropdown/demos/enUS/arrow.demo.md
Normal file
49
src/dropdown/demos/enUS/arrow.demo.md
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -8,6 +8,7 @@ When you have some functions to trigger.
|
||||
basic
|
||||
trigger
|
||||
cascade
|
||||
arrow
|
||||
placement
|
||||
size
|
||||
manual-position
|
||||
|
47
src/dropdown/demos/zhCN/arrow.demo.md
Normal file
47
src/dropdown/demos/zhCN/arrow.demo.md
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -8,6 +8,7 @@
|
||||
basic
|
||||
trigger
|
||||
cascade
|
||||
arrow
|
||||
placement
|
||||
size
|
||||
manual-position
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
@ -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 })
|
||||
|
||||
|
@ -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>
|
||||
`;
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user