diff --git a/estore/src/components/AdminColorFilter.tsx b/estore/src/components/AdminColorFilter.tsx index 45d9532..fa690c2 100644 --- a/estore/src/components/AdminColorFilter.tsx +++ b/estore/src/components/AdminColorFilter.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react' import { BASE } from '@/lib/basepath' +import { useLockBodyScroll } from '@/lib/useLockBodyScroll' interface ColorEntry { name: string @@ -24,6 +25,7 @@ interface Props { } export default function AdminColorFilter({ disabledColors, onSave, onClose }: Props) { + useLockBodyScroll() const [families, setFamilies] = useState([]) const [disabled, setDisabled] = useState>(() => new Set(disabledColors)) const [openFamily, setOpenFamily] = useState(null) diff --git a/estore/src/components/CartDrawer.tsx b/estore/src/components/CartDrawer.tsx index 9c89f05..1e2d05b 100644 --- a/estore/src/components/CartDrawer.tsx +++ b/estore/src/components/CartDrawer.tsx @@ -53,6 +53,13 @@ const STEP_ORDER: Step[] = ['cart', 'delivery', 'info', 'payment'] export default function CartDrawer() { const { entries, drawerOpen, closeDrawer, removeEntry, updateQuantity, clearCart, totalItems } = useCart() + useEffect(() => { + if (!drawerOpen) return + const prev = document.body.style.overflow + document.body.style.overflow = 'hidden' + return () => { document.body.style.overflow = prev } + }, [drawerOpen]) + const [editingEntry, setEditingEntry] = useState(null) const [step, setStep] = useState('cart') diff --git a/estore/src/components/ColorPicker.tsx b/estore/src/components/ColorPicker.tsx index 2684134..9afddfe 100644 --- a/estore/src/components/ColorPicker.tsx +++ b/estore/src/components/ColorPicker.tsx @@ -6,6 +6,7 @@ import { useCart } from '@/context/CartContext' import { BASE } from '@/lib/basepath' import type { CartEntry } from '@/context/CartContext' import { fmt } from '@/lib/format' +import { useLockBodyScroll } from '@/lib/useLockBodyScroll' interface ColorEntry { name: string @@ -29,6 +30,7 @@ interface Props { } export default function ColorPicker({ product, maxColors, onClose, editingEntry }: Props) { + useLockBodyScroll() const { addToCart, updateEntry } = useCart() const [families, setFamilies] = useState([]) const [openFamily, setOpenFamily] = useState(null) diff --git a/estore/src/components/GuidedTour.tsx b/estore/src/components/GuidedTour.tsx index 9193c15..d7a91aa 100644 --- a/estore/src/components/GuidedTour.tsx +++ b/estore/src/components/GuidedTour.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect, useCallback } from 'react' +import { useLockBodyScroll } from '@/lib/useLockBodyScroll' interface TourStep { target: string | null // CSS selector, or null = centered modal @@ -62,6 +63,7 @@ interface Props { } export default function GuidedTour({ onDone, onStart }: Props) { + useLockBodyScroll() const [step, setStep] = useState(0) const [targetRect, setTargetRect] = useState(null) diff --git a/estore/src/components/WelcomeModal.tsx b/estore/src/components/WelcomeModal.tsx index 8e434d6..05ec5bc 100644 --- a/estore/src/components/WelcomeModal.tsx +++ b/estore/src/components/WelcomeModal.tsx @@ -1,5 +1,7 @@ 'use client' +import { useLockBodyScroll } from '@/lib/useLockBodyScroll' + interface Props { onTour: () => void onDismiss: () => void @@ -13,6 +15,7 @@ const HOW_IT_WORKS = [ ] export default function WelcomeModal({ onTour, onDismiss }: Props) { + useLockBodyScroll() return ( <> {/* Backdrop */} diff --git a/estore/src/lib/useLockBodyScroll.ts b/estore/src/lib/useLockBodyScroll.ts new file mode 100644 index 0000000..7fb4c28 --- /dev/null +++ b/estore/src/lib/useLockBodyScroll.ts @@ -0,0 +1,10 @@ +import { useEffect } from 'react' + +/** Locks body scroll while the calling component is mounted. */ +export function useLockBodyScroll() { + useEffect(() => { + const prev = document.body.style.overflow + document.body.style.overflow = 'hidden' + return () => { document.body.style.overflow = prev } + }, []) +}