From 75694aefde5bf2442c219ecae7c5a767d7c525de Mon Sep 17 00:00:00 2001 From: Guillaume Chau Date: Wed, 23 Mar 2022 10:44:41 +0100 Subject: [PATCH] feat: nested poppers auto lock --- .../.vuepress/components/SubMenuExample.vue | 87 +++++++++++++++++++ packages/docs/src/guide/component.md | 6 ++ .../floating-vue/src/components/Popper.ts | 62 ++++++++++++- 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 packages/docs/src/.vuepress/components/SubMenuExample.vue diff --git a/packages/docs/src/.vuepress/components/SubMenuExample.vue b/packages/docs/src/.vuepress/components/SubMenuExample.vue new file mode 100644 index 00000000..ce7a7e51 --- /dev/null +++ b/packages/docs/src/.vuepress/components/SubMenuExample.vue @@ -0,0 +1,87 @@ + diff --git a/packages/docs/src/guide/component.md b/packages/docs/src/guide/component.md index f2044afc..867db2cb 100644 --- a/packages/docs/src/guide/component.md +++ b/packages/docs/src/guide/component.md @@ -266,6 +266,12 @@ Close all the poppers in the page with the `all` modifier: Close All ``` +## Sub menu + +Nesting poppers inside other popper will automatically prevent the parents from hiding when any child is shown: + + + ## Disable popper Disabling a popper will prevent it from being shown. diff --git a/packages/floating-vue/src/components/Popper.ts b/packages/floating-vue/src/components/Popper.ts index b7b850ff..989baa4a 100644 --- a/packages/floating-vue/src/components/Popper.ts +++ b/packages/floating-vue/src/components/Popper.ts @@ -32,6 +32,8 @@ function defaultPropFactory (prop: string) { } } +const PROVIDE_KEY = '__floating-vue__popper' + export default () => ({ name: 'VPopper', @@ -240,6 +242,18 @@ export default () => ({ }, }, + provide () { + return { + [PROVIDE_KEY]: { + parentPopper: this, + }, + } + }, + + inject: { + [PROVIDE_KEY]: { default: null }, + }, + data () { return { isShown: false, @@ -263,6 +277,7 @@ export default () => ({ }, transformOrigin: null, }, + shownChildren: new Set(), } }, @@ -293,6 +308,10 @@ export default () => ({ result: this.positioningDisabled ? null : this.result, } }, + + parentPopper () { + return this[PROVIDE_KEY]?.parentPopper + }, }, watch: { @@ -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') @@ -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') @@ -427,6 +452,8 @@ export default () => ({ this.isMounted = false this.isShown = false + this.$_updateParentShownChildren(false) + this.$_swapTargetAttrs('data-original-title', 'title') this.$emit('dispose') @@ -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 @@ -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) @@ -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 @@ -847,6 +885,12 @@ export default () => ({ this.$_preventShow = false }, 300) } + + let parent = this.parentPopper + while (parent) { + parent.$_handleGlobalClose(event, touch) + parent = parent.parentPopper + } }, $_detachPopperNode () { @@ -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 () {