mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-03-13 13:59:04 +08:00
fix(dropdown): scrollable prop not work (#3099)
* fix(dropdown): scrollable prop not work * fix(dropdown): teleport to menu node when scrollable * feat(dropdown): showSubmenu when hover scrollbar * fix(dropdown): use popoverBody in teleport * fix(docs): conflicts Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
parent
1dd6f421f0
commit
4388c109d2
@ -9,6 +9,8 @@ Show an arrow with the dropdown.
|
||||
trigger="click"
|
||||
:options="options"
|
||||
:show-arrow="true"
|
||||
scrollable
|
||||
style="max-height: 100px"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<n-button>Go For a Trip</n-button>
|
||||
|
@ -7,6 +7,8 @@
|
||||
trigger="click"
|
||||
:options="options"
|
||||
:show-arrow="true"
|
||||
scrollable
|
||||
style="max-height: 100px"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<n-button>找个地方休息</n-button>
|
||||
|
@ -5,14 +5,27 @@
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-dropdown
|
||||
:options="options"
|
||||
placement="bottom-start"
|
||||
trigger="click"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<n-button>人物和食物</n-button>
|
||||
</n-dropdown>
|
||||
<n-space>
|
||||
<n-dropdown
|
||||
:options="options"
|
||||
placement="bottom-start"
|
||||
trigger="click"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<n-button>人物和食物</n-button>
|
||||
</n-dropdown>
|
||||
|
||||
<n-dropdown
|
||||
:options="options"
|
||||
placement="bottom-start"
|
||||
trigger="click"
|
||||
scrollable
|
||||
style="max-height: 100px"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<n-button>人物和食物-scrollable</n-button>
|
||||
</n-dropdown>
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -57,7 +70,6 @@ const options = [
|
||||
{
|
||||
label: '其他',
|
||||
key: 'others2',
|
||||
disabled: true,
|
||||
children: [
|
||||
{
|
||||
label: '鸡肉',
|
||||
|
@ -431,6 +431,7 @@ export default defineComponent({
|
||||
style: [style, this.cssVars as any],
|
||||
showArrow: this.showArrow,
|
||||
arrowStyle: this.arrowStyle,
|
||||
scrollable: this.scrollable,
|
||||
onMouseenter,
|
||||
onMouseleave
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
} from 'vue'
|
||||
import { TreeNode } from 'treemate'
|
||||
import { renderArrow } from '../../popover/src/PopoverBody'
|
||||
import { NxScrollbar } from '../../_internal/scrollbar'
|
||||
import NDropdownDivider from './DropdownDivider'
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import NDropdownGroup from './DropdownGroup'
|
||||
@ -42,6 +43,7 @@ export interface NDropdownMenuInjection {
|
||||
export default defineComponent({
|
||||
name: 'DropdownMenu',
|
||||
props: {
|
||||
scrollable: Boolean,
|
||||
showArrow: Boolean,
|
||||
arrowStyle: [String, Object] as PropType<string | CSSProperties>,
|
||||
clsPrefix: {
|
||||
@ -99,42 +101,57 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const { parentKey, clsPrefix } = this
|
||||
const { parentKey, clsPrefix, scrollable } = this
|
||||
const menuOptionsNode = this.tmNodes.map((tmNode) => {
|
||||
const { rawNode } = tmNode
|
||||
if (isRenderNode(rawNode)) {
|
||||
return (
|
||||
<NDropdownRenderOption
|
||||
tmNode={tmNode as unknown as TreeNode<DropdownRenderOption>}
|
||||
key={tmNode.key}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (isDividerNode(rawNode)) {
|
||||
return <NDropdownDivider clsPrefix={clsPrefix} key={tmNode.key} />
|
||||
}
|
||||
if (isGroupNode(rawNode)) {
|
||||
return (
|
||||
<NDropdownGroup
|
||||
clsPrefix={clsPrefix}
|
||||
tmNode={tmNode}
|
||||
parentKey={parentKey}
|
||||
key={tmNode.key}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<NDropdownOption
|
||||
clsPrefix={clsPrefix}
|
||||
tmNode={tmNode}
|
||||
parentKey={parentKey}
|
||||
key={tmNode.key}
|
||||
props={rawNode.props}
|
||||
scrollable={scrollable}
|
||||
/>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<div class={`${clsPrefix}-dropdown-menu`} ref="bodyRef">
|
||||
{this.tmNodes.map((tmNode) => {
|
||||
const { rawNode } = tmNode
|
||||
if (isRenderNode(rawNode)) {
|
||||
return (
|
||||
<NDropdownRenderOption
|
||||
tmNode={tmNode as unknown as TreeNode<DropdownRenderOption>}
|
||||
key={tmNode.key}
|
||||
/>
|
||||
)
|
||||
}
|
||||
if (isDividerNode(rawNode)) {
|
||||
return <NDropdownDivider clsPrefix={clsPrefix} key={tmNode.key} />
|
||||
}
|
||||
if (isGroupNode(rawNode)) {
|
||||
return (
|
||||
<NDropdownGroup
|
||||
clsPrefix={clsPrefix}
|
||||
tmNode={tmNode}
|
||||
parentKey={parentKey}
|
||||
key={tmNode.key}
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<NDropdownOption
|
||||
clsPrefix={clsPrefix}
|
||||
tmNode={tmNode}
|
||||
parentKey={parentKey}
|
||||
key={tmNode.key}
|
||||
props={rawNode.props}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<div
|
||||
class={`${clsPrefix}-dropdown-menu ${
|
||||
scrollable ? clsPrefix + '-dropdown-menu--scrollable' : ''
|
||||
} `}
|
||||
ref="bodyRef"
|
||||
>
|
||||
{scrollable ? (
|
||||
<NxScrollbar contentClass={`${clsPrefix}-dropdown-menu__content`}>
|
||||
{{
|
||||
default: () => menuOptionsNode
|
||||
}}
|
||||
</NxScrollbar>
|
||||
) : (
|
||||
menuOptionsNode
|
||||
)}
|
||||
{this.showArrow
|
||||
? renderArrow({
|
||||
clsPrefix,
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
dropdownInjectionKey,
|
||||
dropdownOptionInjectionKey
|
||||
} from './context'
|
||||
import { popoverBodyInjectionKey } from '../../popover/src/interface'
|
||||
import { isSubmenuNode } from './utils'
|
||||
import { TreeNode } from 'treemate'
|
||||
import {
|
||||
@ -57,7 +58,8 @@ export default defineComponent({
|
||||
type: String as PropType<FollowerPlacement>,
|
||||
default: 'right-start'
|
||||
},
|
||||
props: Object as PropType<HTMLAttributes>
|
||||
props: Object as PropType<HTMLAttributes>,
|
||||
scrollable: Boolean
|
||||
},
|
||||
setup (props) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
@ -80,6 +82,7 @@ export default defineComponent({
|
||||
const NDropdownOption = inject(dropdownOptionInjectionKey, null)
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const NDropdownMenu = inject(dropdownMenuInjectionKey)!
|
||||
const NPopoverBody = inject(popoverBodyInjectionKey) as Ref<HTMLElement>
|
||||
const rawNodeRef = computed(() => props.tmNode.rawNode)
|
||||
const hasSubmenuRef = computed(() => {
|
||||
const { value: childrenField } = childrenFieldRef
|
||||
@ -145,9 +148,13 @@ export default defineComponent({
|
||||
function handleMouseLeave (e: MouseEvent): void {
|
||||
if (!mergedShowRef.value) return
|
||||
const { relatedTarget } = e
|
||||
const { clsPrefix } = props
|
||||
if (
|
||||
relatedTarget &&
|
||||
!happensIn({ target: relatedTarget }, 'dropdownOption')
|
||||
!happensIn({ target: relatedTarget }, 'dropdownOption') &&
|
||||
(relatedTarget as HTMLElement).className.indexOf(
|
||||
`${clsPrefix}-scrollbar-rail__scrollbar`
|
||||
)
|
||||
) {
|
||||
hoverKeyRef.value = null
|
||||
}
|
||||
@ -164,12 +171,14 @@ export default defineComponent({
|
||||
NDropdown.doUpdateShow(false)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
labelField: labelFieldRef,
|
||||
renderLabel: renderLabelRef,
|
||||
renderIcon: renderIconRef,
|
||||
siblingHasIcon: NDropdownMenu.showIconRef,
|
||||
siblingHasSubmenu: NDropdownMenu.hasSubmenuRef,
|
||||
popoverBody: NPopoverBody,
|
||||
animated: animatedRef,
|
||||
mergedShowSubmenu: computed(() => {
|
||||
return deferredShowSubmenuRef.value && !parentEnteringSubmenuRef.value
|
||||
@ -218,7 +227,9 @@ export default defineComponent({
|
||||
renderIcon,
|
||||
renderOption,
|
||||
nodeProps,
|
||||
props
|
||||
props,
|
||||
scrollable,
|
||||
popoverBody
|
||||
} = this
|
||||
const submenuVNode = mergedShowSubmenu ? (
|
||||
<NDropdownMenu
|
||||
@ -290,7 +301,8 @@ export default defineComponent({
|
||||
<VFollower
|
||||
show={this.mergedShowSubmenu}
|
||||
placement={this.placement}
|
||||
teleportDisabled
|
||||
to={scrollable ? popoverBody : undefined}
|
||||
teleportDisabled={!scrollable}
|
||||
>
|
||||
{{
|
||||
default: () => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { c, cB, cM, cE } from '../../../_utils/cssr'
|
||||
import { c, cB, cM, cE, cNotM } from '../../../_utils/cssr'
|
||||
import { fadeInScaleUpTransition } from '../../../_styles/transitions/fade-in-scale-up.cssr'
|
||||
|
||||
// vars:
|
||||
@ -29,7 +29,6 @@ import { fadeInScaleUpTransition } from '../../../_styles/transitions/fade-in-sc
|
||||
|
||||
export default cB('dropdown-menu', `
|
||||
transform-origin: inherit;
|
||||
padding: var(--n-padding);
|
||||
background-color: var(--n-color);
|
||||
border-radius: var(--n-border-radius);
|
||||
box-shadow: var(--n-box-shadow);
|
||||
@ -174,5 +173,19 @@ export default cB('dropdown-menu', `
|
||||
cB('dropdown-menu-wrapper', `
|
||||
transform-origin: inherit;
|
||||
width: fit-content;
|
||||
`)
|
||||
`),
|
||||
c('>', [
|
||||
cB('scrollbar', `
|
||||
height: inherit;
|
||||
max-height: inherit;
|
||||
`)
|
||||
]),
|
||||
cNotM('scrollable', `
|
||||
padding: var(--n-padding);
|
||||
`),
|
||||
cM('scrollable', [
|
||||
cE('content', `
|
||||
padding: var(--n-padding);
|
||||
`)
|
||||
])
|
||||
])
|
||||
|
Loading…
x
Reference in New Issue
Block a user