diff --git a/CHANGELOG.md b/CHANGELOG.md index ed384a33..caf26f29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## v5.2.2-holistics.2 + +### 🩹 Fixes + + - Add `fixDiagonalSubmenuProblem` prop, fix [#916](https://github.com/Akryum/floating-vue/issues/916) + +### ❤️ Contributors + +- Cuong Vuong ([@cuong-vuong-holistics](https://github.com/cuong-vuong-holistics)) + ## v5.2.2-holistics.1 ### 🚀 Performance Improvements diff --git a/README.md b/README.md index e087205c..b44f0f39 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This fork has the `@holistics/floating-vue` package that tries to fix the following issues: - **[Performance]** Disable toggling CSS class of ``: this makes the entire page reflow whenever some floating elements are shown/all are hidden ([PR #1019](https://github.com/Akryum/floating-vue/pull/1019)). Can be re-enabled via `toggleBodyClass` prop. +- **[Fix]** Properly fix diagonal submenu problem ([#916](https://github.com/Akryum/floating-vue/issues/916)) via `fixDiagonalSubmenuProblem` prop ### 📦️ Versioning and syncing diff --git a/packages/floating-vue/README.md b/packages/floating-vue/README.md index d582b430..e1181698 100644 --- a/packages/floating-vue/README.md +++ b/packages/floating-vue/README.md @@ -5,6 +5,7 @@ This fork has the `@holistics/floating-vue` package that tries to fix the following issues: - **[Performance]** Disable toggling CSS class of ``: this makes the entire page reflow whenever some floating elements are shown/all are hidden ([PR #1019](https://github.com/Akryum/floating-vue/pull/1019)). Can be re-enabled via `toggleBodyClass` prop. +- **[Fix]** Properly fix diagonal submenu problem ([#916](https://github.com/Akryum/floating-vue/issues/916)) via `fixDiagonalSubmenuProblem` prop ### 📦️ Versioning and syncing diff --git a/packages/floating-vue/package.json b/packages/floating-vue/package.json index b051a733..0a8d00ca 100644 --- a/packages/floating-vue/package.json +++ b/packages/floating-vue/package.json @@ -1,6 +1,6 @@ { "name": "@holistics/floating-vue", - "version": "5.2.2-holistics.1", + "version": "5.2.2-holistics.2", "description": "Holistics fork of Floating Vue - Easy Vue tooltips, dropdowns, menus & popovers using floating-ui", "contributors": [ "Guillaume Chau ", diff --git a/packages/floating-vue/src/components/Popper.ts b/packages/floating-vue/src/components/Popper.ts index b0655a72..ef2fa31f 100644 --- a/packages/floating-vue/src/components/Popper.ts +++ b/packages/floating-vue/src/components/Popper.ts @@ -49,6 +49,17 @@ function defaultPropFactory (prop: string) { const PROVIDE_KEY = '__floating-vue__popper' +function normalizeTriggers ( + commonTriggers: string[], + customTriggers?: string[] | ((commonTriggers: string[]) => string[]), +) { + if (customTriggers != null) { + return typeof customTriggers === 'function' ? customTriggers(commonTriggers) : customTriggers + } + + return commonTriggers +} + const createPopper = () => defineComponent({ name: 'VPopper', @@ -282,6 +293,11 @@ const createPopper = () => defineComponent({ type: Boolean, default: undefined, }, + + fixDiagonalSubmenuProblem: { + type: [Boolean, Number], + default: undefined, + }, }, emits: { @@ -362,7 +378,18 @@ const createPopper = () => defineComponent({ return this[PROVIDE_KEY]?.parentPopper }, - hasPopperShowTriggerHover () { + shouldFixDiagonalSubmenuProblem () { + return this.fixDiagonalSubmenuProblem === true || typeof this.fixDiagonalSubmenuProblem === 'number' + }, + + hasHoverableTrigger () { + if (this.shouldFixDiagonalSubmenuProblem) { + const showTriggers = normalizeTriggers(this.triggers, this.showTriggers) + const popperShowTriggers = normalizeTriggers(this.popperTriggers, this.popperShowTriggers) + + return showTriggers.includes('hover') && popperShowTriggers.includes('hover') + } + return this.popperTriggers?.includes('hover') || this.popperShowTriggers?.includes('hover') }, }, @@ -468,16 +495,24 @@ const createPopper = () => defineComponent({ } // Abort if aiming for the popper - if (this.hasPopperShowTriggerHover && this.$_isAimingPopper()) { + if (this.hasHoverableTrigger && this.$_isAimingPopper()) { if (this.parentPopper) { this.parentPopper.lockedChild = this clearTimeout(this.parentPopper.lockedChildTimer) + + let timeout = 1000 + if (this.fixDiagonalSubmenuProblem === true) { + timeout = 250 + } else if (typeof this.fixDiagonalSubmenuProblem === 'number') { + timeout = this.fixDiagonalSubmenuProblem + } + this.parentPopper.lockedChildTimer = setTimeout(() => { if (this.parentPopper.lockedChild === this) { this.parentPopper.lockedChild.hide({ skipDelay }) this.parentPopper.lockedChild = null } - }, 1000) + }, timeout) } return } @@ -492,6 +527,12 @@ const createPopper = () => defineComponent({ this.$emit('update:shown', false) }, + handlePopperNodeHovered () { + if (this.parentPopper?.lockedChild === this) { + this.parentPopper.lockedChild = null + } + }, + init () { if (!this.isDisposed) return this.isDisposed = false @@ -904,8 +945,17 @@ const createPopper = () => defineComponent({ !this.$_preventShow && this.show({ event }) } - this.$_registerTriggerListeners(this.$_targetNodes, SHOW_EVENT_MAP, this.triggers, this.showTriggers, handleShow) - this.$_registerTriggerListeners([this.$_popperNode], SHOW_EVENT_MAP, this.popperTriggers, this.popperShowTriggers, handleShow) + const showTriggers = normalizeTriggers(this.triggers, this.showTriggers) + const hideTriggers = normalizeTriggers(this.triggers, this.hideTriggers) + const popperShowTriggers = normalizeTriggers(this.popperTriggers, this.popperShowTriggers) + const popperHideTriggers = normalizeTriggers(this.popperTriggers, this.popperHideTriggers) + + this.$_registerTriggerListeners(this.$_targetNodes, SHOW_EVENT_MAP, showTriggers, handleShow) + this.$_registerTriggerListeners([this.$_popperNode], SHOW_EVENT_MAP, popperShowTriggers, handleShow) + + if (this.shouldFixDiagonalSubmenuProblem && this.hasHoverableTrigger) { + this.$_registerTriggerListeners([this.$_popperNode], SHOW_EVENT_MAP, popperShowTriggers, this.handlePopperNodeHovered) + } // Add trigger hide events @@ -916,8 +966,8 @@ const createPopper = () => defineComponent({ this.hide({ event }) } - this.$_registerTriggerListeners(this.$_targetNodes, HIDE_EVENT_MAP, this.triggers, this.hideTriggers, handleHide) - this.$_registerTriggerListeners([this.$_popperNode], HIDE_EVENT_MAP, this.popperTriggers, this.popperHideTriggers, handleHide) + this.$_registerTriggerListeners(this.$_targetNodes, HIDE_EVENT_MAP, hideTriggers, handleHide) + this.$_registerTriggerListeners([this.$_popperNode], HIDE_EVENT_MAP, popperHideTriggers, handleHide) }, $_registerEventListeners (targetNodes: Element[], eventType: string, handler: (event: Event) => void) { @@ -929,13 +979,7 @@ const createPopper = () => defineComponent({ : undefined)) }, - $_registerTriggerListeners (targetNodes: Element[], eventMap: Record, commonTriggers, customTrigger, handler: (event: Event) => void) { - let triggers = commonTriggers - - if (customTrigger != null) { - triggers = typeof customTrigger === 'function' ? customTrigger(triggers) : customTrigger - } - + $_registerTriggerListeners (targetNodes: Element[], eventMap: Record, triggers: string[], handler: (event: Event) => void) { triggers.forEach(trigger => { const eventType = eventMap[trigger] if (eventType) { diff --git a/packages/floating-vue/src/components/PopperWrapper.vue b/packages/floating-vue/src/components/PopperWrapper.vue index 764885a6..18ec8d59 100644 --- a/packages/floating-vue/src/components/PopperWrapper.vue +++ b/packages/floating-vue/src/components/PopperWrapper.vue @@ -294,6 +294,11 @@ export default defineComponent({ type: Boolean, default: undefined, }, + + fixDiagonalSubmenuProblem: { + type: [Boolean, Number], + default: undefined, + }, }, emits: {