From 883ba1adba3ab6f03fbe1696902ee094b03667f9 Mon Sep 17 00:00:00 2001 From: Kael Date: Mon, 23 May 2022 01:50:52 +1000 Subject: [PATCH] feat(VOverlay): add closeOnBack prop fixes #15130 --- .../src/locale/en/v-overlay.json | 1 + .../src/components/VOverlay/VOverlay.tsx | 26 ++++++++++++------- .../src/components/VTooltip/VTooltip.tsx | 1 + packages/vuetify/src/composables/router.tsx | 11 ++++---- .../vuetify/src/composables/toggleScope.ts | 15 +++++++++++ 5 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 packages/vuetify/src/composables/toggleScope.ts diff --git a/packages/api-generator/src/locale/en/v-overlay.json b/packages/api-generator/src/locale/en/v-overlay.json index cf62f3ab415..d82b877fa51 100644 --- a/packages/api-generator/src/locale/en/v-overlay.json +++ b/packages/api-generator/src/locale/en/v-overlay.json @@ -1,6 +1,7 @@ { "props": { "absolute": "Applies **position: absolute** to the content element.", + "closeOnBack": "Closes the overlay content when the browser's back button is pressed or `$router.back()` is called, cancelling the original navigation. `persistent` overlays will cancel navigation and animate as if they were clicked outside instead of closing.", "contained": "Limits the size of the component and scrim to its offset parent. Implies `absolute` and `attach`.", "opacity": "Sets the overlay opacity", "zIndex": "The z-index used for the component" diff --git a/packages/vuetify/src/components/VOverlay/VOverlay.tsx b/packages/vuetify/src/components/VOverlay/VOverlay.tsx index 7209ff033c2..78473b95e2d 100644 --- a/packages/vuetify/src/components/VOverlay/VOverlay.tsx +++ b/packages/vuetify/src/components/VOverlay/VOverlay.tsx @@ -7,7 +7,8 @@ import { makePositionStrategyProps, usePositionStrategies } from './positionStra import { makeScrollStrategyProps, useScrollStrategies } from './scrollStrategies' import { makeThemeProps, provideTheme } from '@/composables/theme' import { makeTransitionProps, MaybeTransition } from '@/composables/transition' -import { useBackButton } from '@/composables/router' +import { useBackButton, useRouter } from '@/composables/router' +import { useToggleScope } from '@/composables/toggleScope' import { useBackgroundColor } from '@/composables/color' import { useProxiedModel } from '@/composables/proxiedModel' import { useRtl } from '@/composables/rtl' @@ -83,6 +84,10 @@ export const VOverlay = genericComponent { props: { absolute: Boolean, attach: [Boolean, String, Object] as PropType, + closeOnBack: { + type: Boolean, + default: true, + }, contained: Boolean, contentClass: null, noClickAnimation: Boolean, @@ -163,14 +168,17 @@ export const VOverlay = genericComponent { } } - useBackButton(next => { - if (isTop.value && isActive.value) { - next(false) - if (!props.persistent) isActive.value = false - else animateClick() - } else { - next() - } + const router = useRouter() + useToggleScope(() => props.closeOnBack, () => { + useBackButton(router, next => { + if (isTop.value && isActive.value) { + next(false) + if (!props.persistent) isActive.value = false + else animateClick() + } else { + next() + } + }) }) const top = ref() diff --git a/packages/vuetify/src/components/VTooltip/VTooltip.tsx b/packages/vuetify/src/components/VTooltip/VTooltip.tsx index 64d40f51fed..4a2c467cc35 100644 --- a/packages/vuetify/src/components/VTooltip/VTooltip.tsx +++ b/packages/vuetify/src/components/VTooltip/VTooltip.tsx @@ -94,6 +94,7 @@ export const VTooltip = genericComponent { persistent open-on-click={ false } open-on-hover + close-on-back={ false } role="tooltip" eager activatorProps={{ diff --git a/packages/vuetify/src/composables/router.tsx b/packages/vuetify/src/composables/router.tsx index 1cbf70bc525..1e63ffdeed1 100644 --- a/packages/vuetify/src/composables/router.tsx +++ b/packages/vuetify/src/composables/router.tsx @@ -2,8 +2,8 @@ import { getCurrentInstance, propsFactory } from '@/util' import { computed, - onBeforeUnmount, - onMounted, + nextTick, + onScopeDispose, resolveDynamicComponent, toRef, } from 'vue' @@ -74,13 +74,12 @@ export const makeRouterProps = propsFactory({ }, 'router') let inTransition = false -export function useBackButton (cb: (next: NavigationGuardNext) => void) { - const router = useRouter() +export function useBackButton (router: Router | undefined, cb: (next: NavigationGuardNext) => void) { let popped = false let removeBefore: (() => void) | undefined let removeAfter: (() => void) | undefined - onMounted(() => { + nextTick(() => { window.addEventListener('popstate', onPopstate) removeBefore = router?.beforeEach((to, from, next) => { if (!inTransition) { @@ -94,7 +93,7 @@ export function useBackButton (cb: (next: NavigationGuardNext) => void) { inTransition = false }) }) - onBeforeUnmount(() => { + onScopeDispose(() => { window.removeEventListener('popstate', onPopstate) removeBefore?.() removeAfter?.() diff --git a/packages/vuetify/src/composables/toggleScope.ts b/packages/vuetify/src/composables/toggleScope.ts new file mode 100644 index 00000000000..dae9c51ce02 --- /dev/null +++ b/packages/vuetify/src/composables/toggleScope.ts @@ -0,0 +1,15 @@ +import { effectScope, watch } from 'vue' +import type { EffectScope, WatchSource } from 'vue' + +export function useToggleScope (source: WatchSource, cb: () => void) { + let scope: EffectScope | undefined + watch(source, active => { + if (active && !scope) { + scope = effectScope() + scope.run(cb) + } else { + scope?.stop() + scope = undefined + } + }, { immediate: true }) +}