Fix modal scrolling

This commit is contained in:
Joey Yakimowich-Payne 2026-01-13 23:56:21 -07:00
commit 683cd039e7
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
6 changed files with 27 additions and 0 deletions

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { X, Plus, Trash2, Triangle, Diamond, Circle, Square, Clock } from 'lucide-react'; import { X, Plus, Trash2, Triangle, Diamond, Circle, Square, Clock } from 'lucide-react';
import { Question, AnswerOption } from '../types'; import { Question, AnswerOption } from '../types';
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
interface QuestionEditModalProps { interface QuestionEditModalProps {
question: Question; question: Question;
@ -39,6 +40,8 @@ export const QuestionEditModal: React.FC<QuestionEditModalProps> = ({
const [options, setOptions] = useState<AnswerOption[]>(question.options); const [options, setOptions] = useState<AnswerOption[]>(question.options);
const [timeLimit, setTimeLimit] = useState(question.timeLimit); const [timeLimit, setTimeLimit] = useState(question.timeLimit);
useBodyScrollLock(true);
const handleOptionTextChange = (index: number, newText: string) => { const handleOptionTextChange = (index: number, newText: string) => {
setOptions(prev => prev.map((opt, i) => setOptions(prev => prev.map((opt, i) =>
i === index ? { ...opt, text: newText } : opt i === index ? { ...opt, text: newText } : opt

View file

@ -6,6 +6,7 @@ import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSo
import { Quiz, Question } from '../types'; import { Quiz, Question } from '../types';
import { SortableQuestionCard } from './SortableQuestionCard'; import { SortableQuestionCard } from './SortableQuestionCard';
import { QuestionEditModal } from './QuestionEditModal'; import { QuestionEditModal } from './QuestionEditModal';
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
interface QuizEditorProps { interface QuizEditorProps {
@ -31,6 +32,8 @@ export const QuizEditor: React.FC<QuizEditorProps> = ({
const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null);
const [titleEditing, setTitleEditing] = useState(false); const [titleEditing, setTitleEditing] = useState(false);
useBodyScrollLock(!!showDeleteConfirm);
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor, { useSensor(PointerSensor, {
activationConstraint: { distance: 8 } activationConstraint: { distance: 8 }

View file

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { X, Trash2, Play, BrainCircuit, PenTool, Loader2, Calendar } from 'lucide-react'; import { X, Trash2, Play, BrainCircuit, PenTool, Loader2, Calendar } from 'lucide-react';
import { QuizListItem } from '../types'; import { QuizListItem } from '../types';
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
interface QuizLibraryProps { interface QuizLibraryProps {
isOpen: boolean; isOpen: boolean;
@ -31,6 +32,8 @@ export const QuizLibrary: React.FC<QuizLibraryProps> = ({
const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null); const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);
const isAnyOperationInProgress = loading || !!loadingQuizId || !!deletingQuizId; const isAnyOperationInProgress = loading || !!loadingQuizId || !!deletingQuizId;
useBodyScrollLock(isOpen);
const handleDeleteClick = (e: React.MouseEvent, id: string) => { const handleDeleteClick = (e: React.MouseEvent, id: string) => {
e.stopPropagation(); e.stopPropagation();
setConfirmDeleteId(id); setConfirmDeleteId(id);

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Save, Copy, X } from 'lucide-react'; import { Save, Copy, X } from 'lucide-react';
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
interface SaveOptionsModalProps { interface SaveOptionsModalProps {
isOpen: boolean; isOpen: boolean;
@ -17,6 +18,8 @@ export const SaveOptionsModal: React.FC<SaveOptionsModalProps> = ({
onOverwrite, onOverwrite,
isSaving isSaving
}) => { }) => {
useBodyScrollLock(isOpen);
if (!isOpen) return null; if (!isOpen) return null;
return ( return (

View file

@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Save, X, Loader2, BrainCircuit } from 'lucide-react'; import { Save, X, Loader2, BrainCircuit } from 'lucide-react';
import { useBodyScrollLock } from '../hooks/useBodyScrollLock';
interface SaveQuizPromptProps { interface SaveQuizPromptProps {
isOpen: boolean; isOpen: boolean;
@ -17,6 +18,8 @@ export const SaveQuizPrompt: React.FC<SaveQuizPromptProps> = ({
}) => { }) => {
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
useBodyScrollLock(isOpen);
const handleSave = async () => { const handleSave = async () => {
setIsSaving(true); setIsSaving(true);
try { try {

View file

@ -0,0 +1,12 @@
import { useEffect } from 'react';
export const useBodyScrollLock = (isLocked: boolean) => {
useEffect(() => {
if (isLocked) {
document.body.style.overflow = 'hidden';
}
return () => {
document.body.style.overflow = '';
};
}, [isLocked]);
};