Skip to content

Commit

Permalink
feat: nested poppers auto lock
Browse files Browse the repository at this point in the history
  • Loading branch information
Akryum committed Mar 23, 2022
1 parent 3181330 commit 75694ae
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 1 deletion.
87 changes: 87 additions & 0 deletions packages/docs/src/.vuepress/components/SubMenuExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<template>
<div class="example flex justify-center items-center gap-6">
<VMenu placement="bottom-start">
<button class="border border-gray-300 rounded px-4 py-2">
Hover me
</button>

<template #popper>
<div class="px-4 py-1">
Sub menus:
</div>

<VMenu
v-for="n in 5"
:key="n"
placement="right-start"
instant-move
>
<button class="rounded hover:bg-green-100 px-4 py-2">
Sub menu >
</button>

<template #popper>
<VMenu
v-for="n in 5"
:key="n"
placement="right-start"
instant-move
>
<button class="rounded hover:bg-green-100 px-4 py-2">
Option {{ n }} >
</button>

<template #popper>
<div class="px-6 py-2">
Hello
</div>
</template>
</VMenu>
</template>
</VMenu>
</template>
</VMenu>

<VDropdown placement="bottom-start">
<button class="border border-gray-300 rounded px-4 py-2">
Click me
</button>

<template #popper>
<div class="px-4 py-1">
Sub menus:
</div>

<VDropdown
v-for="n in 5"
:key="n"
placement="right-start"
instant-move
>
<button class="rounded hover:bg-green-100 px-4 py-2">
Sub menu >
</button>

<template #popper>
<VDropdown
v-for="n in 5"
:key="n"
placement="right-start"
instant-move
>
<button class="rounded hover:bg-green-100 px-4 py-2">
Option {{ n }} >
</button>

<template #popper>
<div class="px-6 py-2">
Hello
</div>
</template>
</VDropdown>
</template>
</VDropdown>
</template>
</VDropdown>
</div>
</template>
6 changes: 6 additions & 0 deletions packages/docs/src/guide/component.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ Close all the poppers in the page with the `all` modifier:
<a v-close-popper.all>Close All</a>
```

## Sub menu

Nesting poppers inside other popper will automatically prevent the parents from hiding when any child is shown:

<SubMenuExample />

## Disable popper

Disabling a popper will prevent it from being shown.
Expand Down
62 changes: 61 additions & 1 deletion packages/floating-vue/src/components/Popper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ function defaultPropFactory (prop: string) {
}
}

const PROVIDE_KEY = '__floating-vue__popper'

export default () => ({
name: 'VPopper',

Expand Down Expand Up @@ -240,6 +242,18 @@ export default () => ({
},
},

provide () {
return {
[PROVIDE_KEY]: {
parentPopper: this,
},
}
},

inject: {
[PROVIDE_KEY]: { default: null },
},

data () {
return {
isShown: false,
Expand All @@ -263,6 +277,7 @@ export default () => ({
},
transformOrigin: null,
},
shownChildren: new Set(),
}
},

Expand Down Expand Up @@ -293,6 +308,10 @@ export default () => ({
result: this.positioningDisabled ? null : this.result,
}
},

parentPopper () {
return this[PROVIDE_KEY]?.parentPopper
},
},

watch: {
Expand Down Expand Up @@ -369,6 +388,7 @@ export default () => ({

methods: {
show ({ event = null, skipDelay = false, force = false } = {}) {
this.$_pendingHide = false
if (force || !this.disabled) {
this.$_scheduleShow(event, skipDelay)
this.$emit('show')
Expand All @@ -384,6 +404,11 @@ export default () => ({

hide ({ event = null, skipDelay = false } = {}) {
if (this.$_hideInProgress) return
if (this.shownChildren.size > 0) {
this.$_pendingHide = true
return
}
this.$_pendingHide = false
this.$_scheduleHide(event, skipDelay)

this.$emit('hide')
Expand Down Expand Up @@ -427,6 +452,8 @@ export default () => ({
this.isMounted = false
this.isShown = false

this.$_updateParentShownChildren(false)

this.$_swapTargetAttrs('data-original-title', 'title')

this.$emit('dispose')
Expand Down Expand Up @@ -574,10 +601,11 @@ export default () => ({
},

$_scheduleShow (event = null, skipDelay = false) {
this.$_updateParentShownChildren(true)
this.$_hideInProgress = false
clearTimeout(this.$_scheduleTimer)

if (hidingPopper && this.instantMove && hidingPopper.instantMove) {
if (hidingPopper && this.instantMove && hidingPopper.instantMove && hidingPopper !== this.parentPopper) {
hidingPopper.$_applyHide(true)
this.$_applyShow(true)
return
Expand All @@ -591,6 +619,11 @@ export default () => ({
},

$_scheduleHide (event = null, skipDelay = false) {
if (this.shownChildren.size > 0) {
this.$_pendingHide = true
return
}
this.$_updateParentShownChildren(false)
this.$_hideInProgress = true
clearTimeout(this.$_scheduleTimer)

Expand Down Expand Up @@ -683,6 +716,11 @@ export default () => ({
},

async $_applyHide (skipTransition = false) {
if (this.shownChildren.size > 0) {
this.$_pendingHide = true
this.$_hideInProgress = false
return
}
clearTimeout(this.$_scheduleTimer)

// Already hidden
Expand Down Expand Up @@ -847,6 +885,12 @@ export default () => ({
this.$_preventShow = false
}, 300)
}

let parent = this.parentPopper
while (parent) {
parent.$_handleGlobalClose(event, touch)
parent = parent.parentPopper
}
},

$_detachPopperNode () {
Expand Down Expand Up @@ -875,6 +919,22 @@ export default () => ({
}
}
},

$_updateParentShownChildren (value) {
let parent = this.parentPopper
while (parent) {
if (value) {
parent.shownChildren.add(this.randomId)
} else {
parent.shownChildren.delete(this.randomId)

if (parent.$_pendingHide) {
parent.hide()
}
}
parent = parent.parentPopper
}
},
},

render () {
Expand Down

0 comments on commit 75694ae

Please sign in to comment.