Skip to content

Commit

Permalink
fix(VsModal,VsDrawer): fix overlay scroll locks
Browse files Browse the repository at this point in the history
  • Loading branch information
smithoo committed Jan 7, 2025
1 parent d0db7bd commit b0e993c
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 32 deletions.
26 changes: 22 additions & 4 deletions packages/vlossom/src/components/vs-drawer/VsDrawer.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<Transition name="drawer" :duration="MODAL_DURATION">
<div
ref="drawerRef"
v-show="isOpen"
:class="['vs-drawer', colorSchemeClass, { 'vs-dimmed': dimmed }]"
:style="computedStyleSet"
Expand Down Expand Up @@ -36,7 +37,7 @@ import {
type Ref,
type ComputedRef,
} from 'vue';
import { useColorScheme, useLayout, useStyleSet, useOverlay } from '@/composables';
import { useColorScheme, useLayout, useStyleSet, useOverlay, useScrollLock } from '@/composables';
import {
VsComponent,
Placement,
Expand Down Expand Up @@ -88,18 +89,19 @@ export default defineComponent({
id,
callbacks,
dimClose,
dimmed,
fixed,
open,
placement,
size,
escClose,
scrollLock,
} = toRefs(props);
const { colorSchemeClass } = useColorScheme(name, colorScheme);
const { computedStyleSet: drawerStyleSet } = useStyleSet<VsDrawerStyleSet>(name, styleSet);
const drawerRef: Ref<HTMLElement | null> = ref(null);
const focusTrapRef: Ref<Focusable | null> = ref(null);
const positionStyle = computed(() => {
Expand Down Expand Up @@ -138,7 +140,6 @@ export default defineComponent({
});
const initialOpen = open.value || modelValue.value;
const scrollLock = computed(() => dimmed.value && fixed.value);
const computedCallbacks = computed(() => {
return {
...callbacks.value,
Expand All @@ -150,7 +151,7 @@ export default defineComponent({
},
};
});
const { isOpen, close } = useOverlay(id, initialOpen, scrollLock, computedCallbacks, escClose);
const { isOpen, close } = useOverlay(id, initialOpen, computedCallbacks, escClose);
// only for vs-layout children
const { getDefaultLayoutProvide } = useLayout();
Expand Down Expand Up @@ -205,11 +206,27 @@ export default defineComponent({
}
}
const parentElement = computed(() => {
if (fixed.value) {
return document.body;
}
return drawerRef.value?.parentElement || null;
});
watch(modelValue, (o) => {
isOpen.value = o;
});
watch(isOpen, (o) => {
if (scrollLock.value) {
if (o) {
useScrollLock(parentElement.value).lock();
} else {
useScrollLock(parentElement.value).unlock();
}
}
emit('update:modelValue', o);
emit(o ? 'open' : 'close');
});
Expand All @@ -223,6 +240,7 @@ export default defineComponent({
MODAL_DURATION,
focusTrapRef,
layoutStyles,
drawerRef,
};
},
});
Expand Down
2 changes: 2 additions & 0 deletions packages/vlossom/src/components/vs-modal/VsModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default defineComponent({
escClose,
focusLock,
initialFocusRef,
scrollLock,
size,
} = toRefs(props);
Expand Down Expand Up @@ -85,6 +86,7 @@ export default defineComponent({
focusLock: focusLock.value,
id: modalId.value,
initialFocusRef: initialFocusRef.value,
scrollLock: scrollLock.value,
size: size.value,
});
} else {
Expand Down
5 changes: 2 additions & 3 deletions packages/vlossom/src/components/vs-modal/VsModalNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default defineComponent({
},
emits: ['open', 'close'],
setup(props, { emit, slots }) {
const { colorScheme, styleSet, id, dimClose, dimmed, escClose, size, callbacks, container } = toRefs(props);
const { colorScheme, styleSet, id, dimClose, escClose, size, callbacks, container } = toRefs(props);
const { colorSchemeClass } = useColorScheme(name, colorScheme);
Expand Down Expand Up @@ -100,7 +100,6 @@ export default defineComponent({
});
const initialOpen = true;
const scrollLock = computed(() => dimmed.value && fixed.value);
const computedCallbacks = computed(() => {
return {
...callbacks.value,
Expand All @@ -109,7 +108,7 @@ export default defineComponent({
},
};
});
const { overlayId, isOpen, close } = useOverlay(id, initialOpen, scrollLock, computedCallbacks, escClose);
const { overlayId, isOpen, close } = useOverlay(id, initialOpen, computedCallbacks, escClose);
const hasHeader = computed(() => !!slots['header']);
const headerId = computed(() => `vs-modal-header-${overlayId.value}`);
Expand Down
24 changes: 22 additions & 2 deletions packages/vlossom/src/components/vs-modal/VsModalView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
</template>

<script lang="ts">
import { computed, defineComponent, toRefs } from 'vue';
import { computed, ComputedRef, defineComponent, toRefs, watch } from 'vue';
import { store } from '@/stores';
import { useContentRenderer } from '@/composables';
import { useContentRenderer, useScrollLock } from '@/composables';
import VsModalNode from '@/components/vs-modal/VsModalNode.vue';
import VsContentRenderer from '@/components/vs-content-renderer/VsContentRenderer.vue';
Expand All @@ -50,6 +50,26 @@ export default defineComponent({
const isFixed = computed(() => container.value === 'body');
const containerElement: ComputedRef<HTMLElement | null> = computed(() => {
if (container.value === 'body') {
return document.body;
}
return document.querySelector(container.value);
});
const needScrollLock = computed(() => {
return modals.value.some((modal) => modal.scrollLock);
});
watch(needScrollLock, (lock) => {
if (lock) {
useScrollLock(containerElement.value).lock();
} else {
useScrollLock(containerElement.value).unlock();
}
});
return { modals, getRenderedContent, wrapperId, isFixed };
},
});
Expand Down
11 changes: 0 additions & 11 deletions packages/vlossom/src/composables/overlay-composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import { computed, Ref, ref, watch } from 'vue';
import { store } from '@/stores';
import { utils } from '@/utils';
import { MODAL_DURATION, OverlayCallbacks } from '@/declaration';
import { useBodyScroll } from './scroll-lock-composable';

export function useOverlay(
id: Ref<string>,
initialOpen: boolean,
scrollLock: Ref<boolean>,
callbacks: Ref<OverlayCallbacks> = ref({}),
escClose: Ref<boolean>,
) {
Expand Down Expand Up @@ -37,25 +35,16 @@ export function useOverlay(
};
});

const bodyScroll = useBodyScroll();
watch(
isOpen,
(o) => {
if (o) {
if (scrollLock.value) {
bodyScroll.lock();
}

store.overlay.push(overlayId.value, computedCallbacks);
} else {
closing.value = true;
store.overlay.remove(overlayId.value);

setTimeout(() => {
if (scrollLock.value) {
bodyScroll.unlock();
}

closing.value = false;
}, MODAL_DURATION);
}
Expand Down
30 changes: 18 additions & 12 deletions packages/vlossom/src/composables/scroll-lock-composable.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
import { ref } from 'vue';
import { SCROLLBAR_WIDTH } from '@/declaration';

export function useBodyScroll() {
export function useScrollLock(element: HTMLElement | null) {
const originalOverflow = ref('');
const originalPaddingRight = ref('0');
const originalPaddingBottom = ref('0');

function lock() {
if (!element) {
return;
}
setTimeout(() => {
originalOverflow.value = document.body.style.overflow;
originalPaddingRight.value = document.body.style.paddingRight;
originalPaddingBottom.value = document.body.style.paddingBottom;
originalOverflow.value = element.style.overflow;
originalPaddingRight.value = element.style.paddingRight;
originalPaddingBottom.value = element.style.paddingBottom;

document.body.style.overflow = 'hidden';
element.style.overflow = 'hidden';

if (document.body.scrollHeight > window.innerHeight) {
document.body.style.paddingRight = SCROLLBAR_WIDTH;
if (element.scrollHeight > element.clientHeight) {
element.style.paddingRight = SCROLLBAR_WIDTH;
}

if (document.body.scrollWidth > window.innerWidth) {
document.body.style.paddingBottom = SCROLLBAR_WIDTH;
if (element.scrollWidth > element.clientWidth) {
element.style.paddingBottom = SCROLLBAR_WIDTH;
}
}, 10);
}

function unlock() {
if (!element) {
return;
}
setTimeout(() => {
document.body.style.overflow = originalOverflow.value;
document.body.style.paddingRight = originalPaddingRight.value;
document.body.style.paddingBottom = originalPaddingBottom.value;
element.style.overflow = originalOverflow.value;
element.style.paddingRight = originalPaddingRight.value;
element.style.paddingBottom = originalPaddingBottom.value;
}, 10);
}

Expand Down
1 change: 1 addition & 0 deletions packages/vlossom/src/declaration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export interface ModalOptions<T> {
focusLock?: boolean;
id?: string;
initialFocusRef?: HTMLElement | null;
scrollLock?: boolean;
size?: string | number | { width?: string | number; height?: string | number };
}

Expand Down
1 change: 1 addition & 0 deletions packages/vlossom/src/models/overlay-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export function getOverlayProps<T>() {
type: Object as PropType<HTMLElement | null>,
default: null,
},
scrollLock: { type: Boolean, default: false },
};
}

0 comments on commit b0e993c

Please sign in to comment.