refactor(dropdown): new api

This commit is contained in:
07akioni 2019-12-03 18:36:21 +08:00
parent c6c96aa375
commit 8fd7d00446
13 changed files with 326 additions and 99 deletions

View File

@ -5,6 +5,7 @@ trigger
placement
cascade
size
manual-position
```
# Placeholder
# Placeholder

View File

@ -0,0 +1,77 @@
# Manually Positioned
```html
<div style="width: 200px; height: 200px; background-color: rgba(0, 128, 0, .5);" @contextmenu="handleContextMenu"></div>
<n-dropdown
placement="bottom-start"
trigger="manual"
manually-positioned
@select="handleSelect"
@blur="handleBlur"
:x="x"
:y="y"
v-model="showDropdown"
>
<n-dropdown-item name="gatsby">
Gatsby
</n-dropdown-item>
<n-dropdown-item name="daisy">
Daisy
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item name="nick">
Nick
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
Others
</template>
<n-dropdown-item name="jordan baker">
Jordan Baker
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item name="tom buchanan">
Tom Buchanan
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
Others
</template>
<n-dropdown-item name="chicken">
Chicken
</n-dropdown-item>
<n-dropdown-item name="beef">
Beef
</n-dropdown-item>
</n-dropdown-submenu>
</n-dropdown-submenu>
</n-dropdown>
```
```js
export default {
methods: {
handleSelect (name) {
this.showDropdown = false
this.$NMessage.info(name)
},
handleBlur () {
this.showDropdown = false
},
handleContextMenu (e) {
e.preventDefault()
this.showDropdown = false
this.$nextTick().then(() => {
this.showDropdown = true
this.x = e.clientX
this.y = e.clientY
})
}
},
data () {
return {
showDropdown: false,
x: 0,
y: 0
}
}
}
```

View File

@ -3,43 +3,85 @@
<n-dropdown
placement="bottom-start"
trigger="click"
size="medium"
:width="160"
:submenu-width="160"
size="small"
:focusable="false"
>
<template v-slot:activator>
<div>menu</div>
<n-button>Small Some</n-button>
</template>
<n-dropdown-item>
item1
<n-dropdown-item name="gatsby">
Gatsby
</n-dropdown-item>
<n-dropdown-item>
item2
<n-dropdown-item name="daisy">
Daisy
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item3
<n-dropdown-item name="nick">
Nick
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu
Others
</template>
<n-dropdown-item>
item4
<n-dropdown-item name="jordan baker">
Jordan Baker
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item5
<n-dropdown-item name="tom buchanan">
Tom Buchanan
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu2
Others
</template>
<n-dropdown-item>
item6
<n-dropdown-item name="chicken">
Chicken
</n-dropdown-item>
<n-dropdown-item>
item7
<n-dropdown-item name="beef">
Beef
</n-dropdown-item>
</n-dropdown-submenu>
</n-dropdown-submenu>
</n-dropdown>
<n-dropdown
placement="bottom-start"
trigger="click"
size="medium"
:focusable="false"
>
<template v-slot:activator>
<n-button>Medium Some</n-button>
</template>
<n-dropdown-item name="gatsby">
Gatsby
</n-dropdown-item>
<n-dropdown-item name="daisy">
Daisy
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item name="nick">
Nick
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
Others
</template>
<n-dropdown-item name="jordan baker">
Jordan Baker
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item name="tom buchanan">
Tom Buchanan
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
Others
</template>
<n-dropdown-item name="chicken">
Chicken
</n-dropdown-item>
<n-dropdown-item name="beef">
Beef
</n-dropdown-item>
</n-dropdown-submenu>
</n-dropdown-submenu>
@ -48,84 +90,48 @@
placement="bottom-start"
trigger="click"
size="large"
:focusable="false"
>
<template v-slot:activator>
<div>menu</div>
<n-button>Large Some</n-button>
</template>
<n-dropdown-item>
item1
<n-dropdown-item name="gatsby">
Gatsby
</n-dropdown-item>
<n-dropdown-item>
item2
<n-dropdown-item name="daisy">
Daisy
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item3
<n-dropdown-item name="nick">
Nick
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu
Others
</template>
<n-dropdown-item>
item4
<n-dropdown-item name="jordan baker">
Jordan Baker
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item5
<n-dropdown-item name="tom buchanan">
Tom Buchanan
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu2
Others
</template>
<n-dropdown-item>
item6
<n-dropdown-item name="chicken">
Chicken
</n-dropdown-item>
<n-dropdown-item>
item7
</n-dropdown-item>
</n-dropdown-submenu>
</n-dropdown-submenu>
</n-dropdown>
<n-dropdown
placement="bottom-start"
trigger="click"
size="huge"
>
<template v-slot:activator>
<div>menu</div>
</template>
<n-dropdown-item>
item1
</n-dropdown-item>
<n-dropdown-item>
item2
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item3
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu
</template>
<n-dropdown-item>
item4
</n-dropdown-item>
<n-dropdown-divider />
<n-dropdown-item>
item5
</n-dropdown-item>
<n-dropdown-submenu>
<template v-slot:activator>
submenu2
</template>
<n-dropdown-item>
item6
</n-dropdown-item>
<n-dropdown-item>
item7
<n-dropdown-item name="beef">
Beef
</n-dropdown-item>
</n-dropdown-submenu>
</n-dropdown-submenu>
</n-dropdown>
```
```css
.n-button {
margin: 0 8px 12px 0;
}
```

View File

@ -1,4 +1,4 @@
# Basic
# Trigger
```html
<n-dropdown @select="handleSelect" trigger="hover">
<template v-slot:activator>
@ -13,7 +13,7 @@
</n-dropdown-item>
</n-dropdown>
<n-dropdown @select="handleSelect" trigger="click">
<n-dropdown @select="handleSelect" trigger="click" :focusable="false">
<template v-slot:activator>
<n-button>I want to click!</n-button>
</template>
@ -25,6 +25,19 @@
{{ hotel }}
</n-dropdown-item>
</n-dropdown>
<n-dropdown @select="handleSelect" trigger="manual" v-model="showDropdown">
<template v-slot:activator>
<n-button @click="handleClick">Oh! By Myself!</n-button>
</template>
<n-dropdown-item
v-for="hotel in hotels"
:key="hotel"
:name="hotel.toLowerCase()"
>
{{ hotel }}
</n-dropdown-item>
</n-dropdown>
```
```js
export default {
@ -32,12 +45,16 @@ export default {
return {
hotels: [
'Marina Bay Sands, Singapore', 'Browns Hotel, London', 'Atlantis Bahamas, Nassau', 'The Beverly Hills Hotel, Los Angeles'
]
],
showDropdown: false
}
},
methods: {
handleSelect (name) {
this.$NMessage.info(name)
},
handleClick () {
this.showDropdown = !this.showDropdown
}
}
}

View File

@ -8,4 +8,5 @@ event
placement
raw-content
width
manual-position
```

View File

@ -0,0 +1,28 @@
# Manually Positioned
```html
<div style="width: 200px; height: 200px; background-color: rgba(0, 128, 0, .5);" @click="handleClick"></div>
<n-popover trigger="manual" v-model="showPopover" :x="x" :y="y" manually-positioned>
666
</n-popover>
```
```js
export default {
methods: {
handleClick(e) {
this.showPopover = false
this.$nextTick().then(() => {
this.showPopover = true
this.x = e.clientX
this.y = e.clientY
})
}
},
data () {
return {
showPopover: false,
x: 0,
y: 0
}
}
}
```

View File

@ -9,7 +9,6 @@ export default {
}
return defaultSlot[0]
} else {
console.error(`NBaseContext: default slot is empty`)
return null
}
}

View File

@ -1,5 +1,10 @@
export default {
name: 'NBasePortal',
provide () {
return {
NBasePortal: this
}
},
mounted () {
if (this.$el.parentElement && !this.elementTransferred) {
this.$el.parentElement.removeChild(this.$el)

View File

@ -49,6 +49,10 @@ export default {
submenuMinWidth: {
type: Number,
default: null
},
focusable: {
type: Boolean,
default: true
}
},
data () {
@ -86,7 +90,7 @@ export default {
}
},
mounted () {
if (this.autoFocus) {
if (this.autoFocus && this.focusable) {
this.$el.focus()
}
},
@ -138,6 +142,7 @@ export default {
},
handleBlur () {
this.controller.hide()
this.$emit('blur')
},
handleMouseEnter () {
if (this.NDropdownMenu) {
@ -161,9 +166,6 @@ export default {
keydown: this.handleKeyDown,
mouseenter: this.handleMouseEnter,
blur: this.handleBlur
},
attrs: {
tabindex: '0'
}
}, [
h(NBaseSelectOptionCollector, {

View File

@ -8,7 +8,7 @@ export default {
props: {
trigger: {
validator (value) {
return ['click', 'hover'].includes(value)
return ['click', 'hover', 'manual'].includes(value)
},
default: 'click'
},
@ -49,6 +49,26 @@ export default {
submenuWidth: {
type: Number,
default: null
},
value: {
type: Boolean,
default: false
},
manuallyPositioned: {
type: Boolean,
default: false
},
x: {
type: Number,
default: null
},
y: {
type: Number,
default: null
},
focusable: {
type: Boolean,
default: true
}
},
render (h, context) {
@ -64,6 +84,10 @@ export default {
width: context.props.width,
minWidth: context.props.minWidth,
maxWidth: context.props.maxWidth,
value: context.props.value,
manuallyPositioned: context.props.manuallyPositioned,
x: context.props.x,
y: context.props.y,
arrow: false,
raw: true,
shadow: false,
@ -75,19 +99,25 @@ export default {
},
default () {
return h(NDropdownMenu, {
attrs: {
tabindex: context.props.focusable ? '0' : '-1'
},
props: {
autoFocus: context.props.autoFocus,
size: context.props.size,
controller,
submenuWidth: context.props.submenuWidth
submenuWidth: context.props.submenuWidth,
focusable: context.props.focusable
},
on: {
blur: context.listeners.blur || (() => {}),
select: context.listeners.select || (() => {})
},
scopedSlots: context.scopedSlots
})
}
}
},
on: context.listeners
})
}
}

View File

@ -57,6 +57,10 @@ export default {
type: Boolean,
default: false
},
manuallyPositioned: {
type: Boolean,
default: false
},
detachedContainerClass: {
type: String,
default: 'n-popover-detached-content-container'
@ -67,6 +71,11 @@ export default {
clickoutside,
mousemoveoutside
},
inject: {
NBasePortal: {
default: null
}
},
data () {
return {
internalActive: false,
@ -87,6 +96,9 @@ export default {
}
},
computed: {
detached () {
return this.NBasePortal.elementTransferred
},
style () {
const style = {}
if (this.width) {

View File

@ -68,14 +68,26 @@ export default {
detachedContainerClass: {
type: String,
default: 'n-popover-detached-content-container'
},
manuallyPositioned: {
type: Boolean,
default: false
},
x: {
type: Number,
default: null
},
y: {
type: Number,
default: null
}
},
render (h, context) {
const slots = context.scopedSlots
const defaultSlot = slots.default && slots.default()
const activatorSlot = slots.activator && slots.activator()
const activatorSlot = (slots.activator && slots.activator()) || []
let activatorVNode = activatorSlot[0]
if (!activatorVNode.tag) {
if (activatorVNode && !activatorVNode.tag) {
activatorVNode = h('span', {
staticClass: 'n-popover-text-wrapper'
}, [activatorVNode])

View File

@ -61,6 +61,18 @@ export default {
return ['self', 'activator'].includes(value)
},
default: 'self'
},
x: {
type: Number,
default: null
},
y: {
type: Number,
default: null
},
manuallyPositioned: {
type: Boolean,
default: false
}
},
computed: {
@ -73,6 +85,12 @@ export default {
if (newValue) {
this.$nextTick().then(this.updatePosition)
}
},
x () {
this.$nextTick().then(this.updatePosition)
},
y () {
this.$nextTick().then(this.updatePosition)
}
},
data () {
@ -123,21 +141,39 @@ export default {
this.trackingElement.setAttribute('n-suggested-transform-origin', 'top left')
},
updatePosition (el, cb, keepOrigin = false) {
// console.log('scroll')
if (!this.active && !this.show) return
// console.log('update position', el, cb, keepOrigin)
// console.log('[placeable.updatePosition]')
this._getTrackedElement()
if (!this.manuallyPositioned) {
this._getTrackedElement()
}
this._getTrackingElement()
if (!this.trackedElement || !this.trackingElement) {
console.log('[placeable.updatePosition]: trakedElement or trackingElement not found!')
if (this.manuallyPositioned) {
if (!this.trackingElement) {
console.error('[naive-ui/placeable/updatePosition]: trackingElement not found!')
}
} else {
if (!this.trackedElement || !this.trackingElement) {
console.error('[naive-ui/placeable/updatePosition]: trakedElement or trackingElement not found!')
}
}
if (this.positionModeisAbsolute) {
this.updatePositionInAbsoluteMode()
return
}
// console.log(activator)
const activatorBoundingClientRect = this.trackedElement.getBoundingClientRect()
let activatorBoundingClientRect = null
if (!this.manuallyPositioned) {
activatorBoundingClientRect = this.trackedElement.getBoundingClientRect()
} else {
activatorBoundingClientRect = {
top: this.y,
left: this.x,
height: 0,
width: 0,
right: this.x,
bottom: this.y
}
// console.log(activatorBoundingClientRect)
}
// console.log(activatorBoundingClientRect)
// console.log(this.$refs.popoverBody)
// debugger
@ -146,6 +182,7 @@ export default {
// debugger
// console.log('scroll', activatorBoundingClientRect, contentBoundingClientRect)
const [placementTransform, suggestedTransformOrigin] = calcPlacementTransfrom(this.placement, activatorBoundingClientRect, contentBoundingClientRect)
// console.log(placementTransform)
this.trackingElement.style.position = 'absolute'
this.trackingElement.style.top = placementTransform.top
this.trackingElement.style.left = placementTransform.left