mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-03-31 14:20:53 +08:00
feat(dropdown): submenu positioning
This commit is contained in:
parent
68f82f6966
commit
ee21dd7056
@ -1,6 +1,6 @@
|
||||
# Basic
|
||||
```html
|
||||
<n-dropdown @select="handleSelect">
|
||||
<n-dropdown @select="handleSelect" :focusable="false">
|
||||
<template v-slot:activator>
|
||||
<n-button>I want to go sleep!</n-button>
|
||||
</template>
|
||||
|
@ -7,14 +7,4 @@ cascade
|
||||
width
|
||||
size
|
||||
manual-position
|
||||
```
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
# Placeholder
|
||||
```
|
@ -125,7 +125,7 @@ export default {
|
||||
) {
|
||||
this.pendingSubMenuInstance.menuActivated = true
|
||||
this.$nextTick().then(() => {
|
||||
this.activeMenuInstance = this.pendingSubMenuInstance.$refs.dropdownMenu
|
||||
this.activeMenuInstance = this.pendingSubMenuInstance.$refs.content
|
||||
this.$nextTick().then(() => {
|
||||
this.activeMenuInstance.$refs.selectMenu.next()
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<n-dropdown-item
|
||||
ref="selectOption"
|
||||
ref="activator"
|
||||
:label="label"
|
||||
name="n-dropdown-submenu-item"
|
||||
:value="value"
|
||||
@ -9,7 +9,6 @@
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<div
|
||||
ref="activator"
|
||||
class="n-dropdown-submenu-activator"
|
||||
>
|
||||
<slot name="activator">
|
||||
@ -23,8 +22,8 @@
|
||||
name="n-fade-in-scale-up--transition"
|
||||
>
|
||||
<n-dropdown-menu
|
||||
v-if="menuActivated && menuPendingToBeActivated"
|
||||
ref="dropdownMenu"
|
||||
v-if="active"
|
||||
ref="content"
|
||||
:style="style"
|
||||
:theme="synthesizedTheme"
|
||||
class="n-dropdown-submenu"
|
||||
@ -42,6 +41,7 @@ import NDropdownItem from './DropdownItem'
|
||||
import themeable from '../../../mixins/themeable'
|
||||
import NIcon from '../../Icon'
|
||||
import iosArrowForward from '../../../icons/ios-arrow-forward'
|
||||
import placeable from '../../../mixins/placeable'
|
||||
|
||||
export default {
|
||||
name: 'NDropdownSubmenu',
|
||||
@ -51,7 +51,7 @@ export default {
|
||||
NIcon,
|
||||
iosArrowForward
|
||||
},
|
||||
mixins: [themeable],
|
||||
mixins: [themeable, placeable],
|
||||
provide () {
|
||||
return {
|
||||
NDropdownSubmenu: this
|
||||
@ -96,6 +96,14 @@ export default {
|
||||
maxWidth: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
positionMode: {
|
||||
type: String,
|
||||
default: 'absolute'
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'right-start'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
@ -106,6 +114,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active () {
|
||||
return this.menuActivated && this.menuPendingToBeActivated
|
||||
},
|
||||
synthesizedStyleWidth () {
|
||||
if (this.NDropdownMenu.inheritedSubmenuWidth) {
|
||||
return this.NDropdownMenu.inheritedSubmenuWidth + 'px'
|
||||
|
@ -7,7 +7,11 @@ export default {
|
||||
render (h, context) {
|
||||
if (context.props.withoutScrollbar) return context.scopedSlots.default()
|
||||
else {
|
||||
return h(NScrollbar, context)
|
||||
return h(NScrollbar, {
|
||||
props: context.props,
|
||||
scopedSlots: context.scopedSlots,
|
||||
on: context.listeners
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ import getScrollParent from '../utils/dom/getScrollParent'
|
||||
import calcPlacementTransfrom from '../utils/dom/calcPlacementTransform'
|
||||
|
||||
function getActivatorEl (componentInstance) {
|
||||
return componentInstance.currentActivatorEl || componentInstance.$refs.activator.$el || componentInstance.$refs.activator
|
||||
return componentInstance.$refs.activator.$el || componentInstance.$refs.activator
|
||||
}
|
||||
|
||||
function getContentEl (componentInstance) {
|
||||
return componentInstance.$refs.content.$el || componentInstance.$refs.content
|
||||
}
|
||||
|
||||
function sortOrigin (origin) {
|
||||
@ -16,6 +20,45 @@ function sortOrigin (origin) {
|
||||
return origin
|
||||
}
|
||||
|
||||
function getPositionInAbsoluteMode (placement, origin) {
|
||||
let position = {
|
||||
top: null,
|
||||
bottom: null,
|
||||
left: null,
|
||||
right: null
|
||||
}
|
||||
if (placement === 'bottom-start') {
|
||||
if (~origin.indexOf('top')) {
|
||||
position.top = '100%'
|
||||
}
|
||||
if (~origin.indexOf('bottom')) {
|
||||
position.bottom = '100%'
|
||||
}
|
||||
if (~origin.indexOf('left')) {
|
||||
position.left = '0'
|
||||
}
|
||||
if (~origin.indexOf('right')) {
|
||||
position.right = '0'
|
||||
}
|
||||
} else if (placement === 'right-start') {
|
||||
if (~origin.indexOf('top')) {
|
||||
position.top = '0'
|
||||
}
|
||||
if (~origin.indexOf('bottom')) {
|
||||
position.bottom = '0'
|
||||
}
|
||||
if (~origin.indexOf('left')) {
|
||||
position.left = '100%'
|
||||
}
|
||||
if (~origin.indexOf('right')) {
|
||||
position.right = '100%'
|
||||
}
|
||||
} else {
|
||||
console.error('[naive-ui/placeable/getPositionInAbsoluteMode]: placement not implemented')
|
||||
}
|
||||
return position
|
||||
}
|
||||
|
||||
/**
|
||||
* Make $refs.content trace $refs.activator, set $refs.contentInner width by the way
|
||||
*
|
||||
@ -102,7 +145,9 @@ export default {
|
||||
},
|
||||
mounted () {
|
||||
this._getTrackingElement()
|
||||
this.trackingElement.style.position = 'absolute'
|
||||
if (this.trackingElement) {
|
||||
this.trackingElement.style.position = 'absolute'
|
||||
}
|
||||
this.$nextTick().then(() => {
|
||||
this.registerScrollListeners()
|
||||
this.registerResizeListener()
|
||||
@ -116,7 +161,7 @@ export default {
|
||||
methods: {
|
||||
_getTrackingElement () {
|
||||
if (this.$refs && this.$refs.content) {
|
||||
this.trackingElement = this.$refs.content
|
||||
this.trackingElement = getContentEl(this)
|
||||
} else if (this.getTrackingElement) {
|
||||
this.trackingElement = this.getTrackingElement()
|
||||
}
|
||||
@ -131,14 +176,14 @@ export default {
|
||||
/**
|
||||
* Need to be fulfilled!
|
||||
*/
|
||||
updatePositionInAbsoluteMode () {
|
||||
updatePositionInAbsoluteMode (position, transformOrigin) {
|
||||
this.trackingElement.style.position = 'absolute'
|
||||
this.trackingElement.style.top = '100%'
|
||||
this.trackingElement.style.left = '0%'
|
||||
this.trackingElement.style.right = null
|
||||
this.trackingElement.style.bottom = null
|
||||
this.trackingElement.style.transformOrigin = 'top left'
|
||||
this.trackingElement.setAttribute('n-suggested-transform-origin', 'top left')
|
||||
this.trackingElement.style.top = position.top
|
||||
this.trackingElement.style.left = position.left
|
||||
this.trackingElement.style.right = position.right
|
||||
this.trackingElement.style.bottom = position.bottom
|
||||
this.trackingElement.style.transformOrigin = transformOrigin
|
||||
this.trackingElement.setAttribute('n-suggested-transform-origin', transformOrigin)
|
||||
},
|
||||
updatePosition (el, cb, keepOrigin = false) {
|
||||
if (!this.active && !this.show) return
|
||||
@ -155,10 +200,7 @@ export default {
|
||||
console.error('[naive-ui/placeable/updatePosition]: trakedElement or trackingElement not found!')
|
||||
}
|
||||
}
|
||||
if (this.positionModeisAbsolute) {
|
||||
this.updatePositionInAbsoluteMode()
|
||||
return
|
||||
}
|
||||
|
||||
// console.log(activator)
|
||||
let activatorBoundingClientRect = null
|
||||
if (!this.manuallyPositioned) {
|
||||
@ -177,11 +219,24 @@ export default {
|
||||
// console.log(activatorBoundingClientRect)
|
||||
// console.log(this.$refs.popoverBody)
|
||||
// debugger
|
||||
const contentBoundingClientRect = this.trackingElement.getBoundingClientRect()
|
||||
const contentBoundingClientRect = {
|
||||
width: this.trackingElement.offsetWidth,
|
||||
height: this.trackingElement.offsetHeight
|
||||
}
|
||||
|
||||
// console.log(contentBoundingClientRect.width, contentBoundingClientRect.height)
|
||||
// console.log(contentBoundingClientRect2.width, contentBoundingClientRect2.height)
|
||||
// console.log(contentBoundingClientRect)
|
||||
// debugger
|
||||
// console.log('scroll', activatorBoundingClientRect, contentBoundingClientRect)
|
||||
const [placementTransform, suggestedTransformOrigin] = calcPlacementTransfrom(this.placement, activatorBoundingClientRect, contentBoundingClientRect)
|
||||
// console.log(this.trackingElement, this.positionMode, this.positionModeisAbsolute)
|
||||
if (this.positionModeisAbsolute) {
|
||||
const position = getPositionInAbsoluteMode(this.placement, suggestedTransformOrigin)
|
||||
// console.log(suggestedTransformOrigin, position)
|
||||
this.updatePositionInAbsoluteMode(position, suggestedTransformOrigin)
|
||||
return
|
||||
}
|
||||
// console.log(placementTransform)
|
||||
this.trackingElement.style.position = 'absolute'
|
||||
this.trackingElement.style.top = placementTransform.top
|
||||
|
@ -29,9 +29,23 @@ export default function calcPlacementTransform (placement, activatorRect, conten
|
||||
contentLeft = activatorRect.left - contentRect.width
|
||||
suggesetedTransfromOrigin = 'bottom right'
|
||||
} else if (placement === 'right-start') {
|
||||
contentTop = activatorRect.top
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
suggesetedTransfromOrigin = 'top left'
|
||||
console.log(activatorRect, contentRect)
|
||||
const toWindowBottom = window.innerHeight - activatorRect.top - contentRect.height
|
||||
const toWindowRight = window.innerWidth - activatorRect.right - contentRect.width
|
||||
if (toWindowBottom < 0) {
|
||||
contentBottom = window.innerHeight - activatorRect.bottom
|
||||
suggesetedTransfromOrigin = 'bottom'
|
||||
} else {
|
||||
contentTop = activatorRect.top
|
||||
suggesetedTransfromOrigin = 'top'
|
||||
}
|
||||
if (toWindowRight < 0) {
|
||||
contentRight = window.innerWidth - activatorRect.left
|
||||
suggesetedTransfromOrigin += ' right'
|
||||
} else {
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
suggesetedTransfromOrigin += ' left'
|
||||
}
|
||||
} else if (placement === 'right') {
|
||||
contentTop = activatorRect.top + activatorRect.height / 2 - contentRect.height / 2
|
||||
contentLeft = activatorRect.left + activatorRect.width
|
||||
|
@ -79,8 +79,8 @@
|
||||
@include fade-in-scale-up-transition($origin: (left top));
|
||||
margin-top: 0;
|
||||
position: absolute !important;
|
||||
left: calc(100% + 6px);
|
||||
top: -4px;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
@include b(base-select-menu) {
|
||||
outline: none;
|
||||
|
Loading…
x
Reference in New Issue
Block a user