Browse Source

fix(sheets): resolve bottom sheet bounce and double-reopen lifecycle bug on exit

master
mortezaei 12 hours ago
parent
commit
3560001496
  1. 27
      src/app/globals.css
  2. 5
      src/app/questions-list/[slug]/answer-pace-sheet.tsx
  3. 5
      src/app/questions-list/sections-request.tsx
  4. 48
      src/components/Componentes/currency-sheet.tsx
  5. 52
      src/components/Componentes/help-modal.tsx
  6. 54
      src/components/Componentes/information-sheet.tsx
  7. 2
      src/components/Componentes/question-birthplace.tsx
  8. 2
      src/components/Componentes/question-date-sheet.tsx
  9. 2
      src/components/Componentes/question-phone.tsx
  10. 2
      src/components/Componentes/question-sheet.tsx
  11. 46
      src/components/Componentes/support-sheet.tsx
  12. 5
      src/components/Componentes/test-completed-sheet.tsx
  13. 5
      src/components/Componentes/test-exit-sheet.tsx
  14. 29
      src/components/Componentes/test-questions-flow.tsx

27
src/app/globals.css

@ -268,6 +268,15 @@ body.section-overlay-open .app-shell {
} }
} }
@keyframes flutter-bottomsheet-exit {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(0, 100%, 0);
}
}
@keyframes flutter-scrim-enter { @keyframes flutter-scrim-enter {
0% { 0% {
opacity: 0; opacity: 0;
@ -277,15 +286,24 @@ body.section-overlay-open .app-shell {
} }
} }
@keyframes flutter-scrim-exit {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.flutter-sheet-surface { .flutter-sheet-surface {
animation: flutter-bottomsheet-enter 250ms cubic-bezier(0, 0, 0.2, 1) forwards; animation: flutter-bottomsheet-enter 250ms cubic-bezier(0, 0, 0.2, 1) forwards;
will-change: transform; will-change: transform;
} }
.flutter-sheet-surface-exit { .flutter-sheet-surface-exit {
transform: translate3d(0, 100%, 0) !important;
transition: transform 200ms cubic-bezier(0.4, 0, 1, 1) !important;
animation: flutter-bottomsheet-exit 200ms cubic-bezier(0.4, 0, 1, 1) forwards !important;
will-change: transform; will-change: transform;
pointer-events: none;
} }
.flutter-scrim { .flutter-scrim {
@ -295,9 +313,10 @@ body.section-overlay-open .app-shell {
} }
.flutter-scrim-exit { .flutter-scrim-exit {
opacity: 0 !important;
transition: opacity 200ms cubic-bezier(0.4, 0, 1, 1) !important;
background-color: rgba(0, 0, 0, 0.54) !important;
animation: flutter-scrim-exit 200ms cubic-bezier(0.4, 0, 1, 1) forwards !important;
will-change: opacity; will-change: opacity;
pointer-events: none;
} }
/* ── Section Overlay Slide-in Panel (Exact Flutter Najm Matching: 350ms in, 200ms out, easeInOut) ── */ /* ── Section Overlay Slide-in Panel (Exact Flutter Najm Matching: 350ms in, 200ms out, easeInOut) ── */

5
src/app/questions-list/[slug]/answer-pace-sheet.tsx

@ -55,12 +55,9 @@ export default function AnswerPaceSheet({
setIsOpen(false); setIsOpen(false);
}; };
if (!isOpen) {
return null;
}
return ( return (
<InformationSheet <InformationSheet
isOpen={isOpen}
icon="play" icon="play"
title={title} title={title}
description={description} description={description}

5
src/app/questions-list/sections-request.tsx

@ -77,15 +77,12 @@ export default function SectionsRequest({
controlledOnClose?.(); controlledOnClose?.();
}; };
if (!isSheetOpen) {
return null;
}
const termsTitle = t["terms & conditions"] || "Terms & Conditions"; const termsTitle = t["terms & conditions"] || "Terms & Conditions";
const gotItLabel = t["Got it"] || "Got it"; const gotItLabel = t["Got it"] || "Got it";
return ( return (
<InformationSheet <InformationSheet
isOpen={isSheetOpen}
icon={null} icon={null}
showCloseButton={false} showCloseButton={false}
title={({ close }) => ( title={({ close }) => (

48
src/components/Componentes/currency-sheet.tsx

@ -24,29 +24,65 @@ export function CurrencySheet({
title, title,
}: CurrencySheetProps) { }: CurrencySheetProps) {
const { dictionary: t, locale } = useI18n(); const { dictionary: t, locale } = useI18n();
const [isRendered, setIsRendered] = useState(isOpen);
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const isClosingRef = useRef(false);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const listRef = useRef<HTMLDivElement>(null); const listRef = useRef<HTMLDivElement>(null);
const sheetRef = useRef<HTMLElement>(null); const sheetRef = useRef<HTMLElement>(null);
const isMountedRef = useRef(true);
const prevIsOpenRef = useRef(isOpen);
const timerRef = useRef<number | null>(null);
useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);
const isRtl = locale === "fa" || locale === "ar" || locale === "ur"; const isRtl = locale === "fa" || locale === "ar" || locale === "ur";
const defaultTitle = isRtl ? "انتخاب ارز" : "Select Currency"; const defaultTitle = isRtl ? "انتخاب ارز" : "Select Currency";
const sheetTitle = title || defaultTitle; const sheetTitle = title || defaultTitle;
const closeSheet = useCallback(() => { const closeSheet = useCallback(() => {
if (isClosingRef.current) return;
isClosingRef.current = true;
setIsClosing(true); setIsClosing(true);
window.setTimeout(() => {
onClose();
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => {
if (isMountedRef.current) {
setIsRendered(false);
setIsClosing(false); setIsClosing(false);
isClosingRef.current = false;
setSearchQuery(""); setSearchQuery("");
}
onClose();
}, EXIT_ANIMATION_MS); }, EXIT_ANIMATION_MS);
}, [onClose]); }, [onClose]);
useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet });
// When isOpen changes from outside
useEffect(() => {
if (prevIsOpenRef.current !== isOpen) {
prevIsOpenRef.current = isOpen;
if (isOpen) {
if (timerRef.current) clearTimeout(timerRef.current);
isClosingRef.current = false;
setIsClosing(false);
setIsRendered(true);
} else if (!isClosingRef.current && isRendered) {
closeSheet();
}
}
}, [isOpen, isRendered, closeSheet]);
useSheetScrollLock(isRendered, { onBack: closeSheet });
// Escape key handler // Escape key handler
useEffect(() => { useEffect(() => {
if (!isOpen) return;
if (!isRendered) return;
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") { if (e.key === "Escape") {
closeSheet(); closeSheet();
@ -54,7 +90,7 @@ export function CurrencySheet({
}; };
window.addEventListener("keydown", handleKeyDown); window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown);
}, [isOpen, closeSheet]);
}, [isRendered, closeSheet]);
const filteredCurrencies = useMemo(() => { const filteredCurrencies = useMemo(() => {
const q = searchQuery.toLowerCase().trim(); const q = searchQuery.toLowerCase().trim();
@ -87,7 +123,7 @@ export function CurrencySheet({
? "لم يتم العثور على عملات" ? "لم يتم العثور على عملات"
: "No currencies found"; : "No currencies found";
if (!isOpen && !isClosing) return null;
if (!isRendered) return null;
return createPortal( return createPortal(
<div <div

52
src/components/Componentes/help-modal.tsx

@ -26,23 +26,52 @@ export function HelpModal({
}: HelpModalProps) { }: HelpModalProps) {
const { dictionary: t } = useI18n(); const { dictionary: t } = useI18n();
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const [isRendered, setIsRendered] = useState(isOpen);
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const isClosingRef = useRef(false);
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
const prevIsOpenRef = useRef(isOpen);
const timerRef = useRef<number | null>(null);
useEffect(() => { useEffect(() => {
isMountedRef.current = true; isMountedRef.current = true;
setMounted(true); setMounted(true);
return () => { return () => {
isMountedRef.current = false; isMountedRef.current = false;
if (timerRef.current) clearTimeout(timerRef.current);
}; };
}, []); }, []);
// When isOpen changes from outside, reset isClosing
const closeSheet = useCallback(() => {
if (isClosingRef.current) return;
isClosingRef.current = true;
setIsClosing(true);
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => {
if (isMountedRef.current) {
setIsRendered(false);
setIsClosing(false);
isClosingRef.current = false;
}
onClose();
}, EXIT_ANIMATION_MS);
}, [onClose]);
// When isOpen changes from outside
useEffect(() => { useEffect(() => {
if (prevIsOpenRef.current !== isOpen) {
prevIsOpenRef.current = isOpen;
if (isOpen) { if (isOpen) {
if (timerRef.current) clearTimeout(timerRef.current);
isClosingRef.current = false;
setIsClosing(false); setIsClosing(false);
setIsRendered(true);
} else if (!isClosingRef.current && isRendered) {
closeSheet();
} }
}, [isOpen]);
}
}, [isOpen, isRendered, closeSheet]);
const resolvedTitle = "Tips"; const resolvedTitle = "Tips";
const resolvedDescription = const resolvedDescription =
@ -53,22 +82,11 @@ export function HelpModal({
"Psychologically, this practice fosters a sense of empathy and contentment, which can reduce financial stress. Socially, lending strengthens neighborhood bonds and creates support networks that can lead to economic opportunities. This hadith encourages believers to lend dough, bread, and fire to increase their sustenance."; "Psychologically, this practice fosters a sense of empathy and contentment, which can reduce financial stress. Socially, lending strengthens neighborhood bonds and creates support networks that can lead to economic opportunities. This hadith encourages believers to lend dough, bread, and fire to increase their sustenance.";
const resolvedButtonText = buttonText ?? t["Got it"] ?? "Got it"; const resolvedButtonText = buttonText ?? t["Got it"] ?? "Got it";
const closeSheet = useCallback(() => {
if (isClosing) return;
setIsClosing(true);
window.setTimeout(() => {
if (isMountedRef.current) {
setIsClosing(false);
}
onClose();
}, EXIT_ANIMATION_MS);
}, [isClosing, onClose]);
useHardwareBackHandler(closeSheet, isOpen && !isClosing);
useHardwareBackHandler(closeSheet, isRendered && !isClosing);
// Lock body scroll // Lock body scroll
useEffect(() => { useEffect(() => {
if (!isOpen || !mounted) return;
if (!isRendered || !mounted) return;
const previousBodyOverflow = document.body.style.overflow; const previousBodyOverflow = document.body.style.overflow;
const previousHtmlOverflow = document.documentElement.style.overflow; const previousHtmlOverflow = document.documentElement.style.overflow;
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
@ -77,9 +95,9 @@ export function HelpModal({
document.body.style.overflow = previousBodyOverflow; document.body.style.overflow = previousBodyOverflow;
document.documentElement.style.overflow = previousHtmlOverflow; document.documentElement.style.overflow = previousHtmlOverflow;
}; };
}, [isOpen, mounted]);
}, [isRendered, mounted]);
if (!isOpen || !mounted) return null;
if (!isRendered || !mounted) return null;
return createPortal( return createPortal(
<div <div

54
src/components/Componentes/information-sheet.tsx

@ -3,6 +3,7 @@
import Image, { type StaticImageData } from "next/image"; import Image, { type StaticImageData } from "next/image";
import type { HTMLAttributes, ReactNode } from "react"; import type { HTMLAttributes, ReactNode } from "react";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import Button from "./button"; import Button from "./button";
import { LoadingSkeleton } from "./loading-skeleton"; import { LoadingSkeleton } from "./loading-skeleton";
import { LoadingThreeDot } from "./loading-three-dot"; import { LoadingThreeDot } from "./loading-three-dot";
@ -41,6 +42,7 @@ export type InformationSheetProps = Omit<
HTMLAttributes<HTMLDivElement>, HTMLAttributes<HTMLDivElement>,
"children" | "title" "children" | "title"
> & { > & {
isOpen?: boolean;
icon?: InformationSheetIcon | null; icon?: InformationSheetIcon | null;
title?: ReactNode | ((controls: { close: () => void }) => ReactNode); title?: ReactNode | ((controls: { close: () => void }) => ReactNode);
description?: ReactNode; description?: ReactNode;
@ -182,6 +184,7 @@ function resolveIcon(icon: InformationSheetIcon | null | undefined) {
} }
export function InformationSheet({ export function InformationSheet({
isOpen = true,
icon, icon,
title, title,
description, description,
@ -200,36 +203,63 @@ export function InformationSheet({
locale === "ur" || locale === "ur" ||
locale === "he" || locale === "he" ||
locale === "ks"; locale === "ks";
const [isVisible, setIsVisible] = useState(true);
const [mounted, setMounted] = useState(false);
const [isRendered, setIsRendered] = useState(isOpen);
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const isClosingRef = useRef(false);
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
const prevIsOpenRef = useRef(isOpen);
const timerRef = useRef<number | null>(null);
useEffect(() => { useEffect(() => {
isMountedRef.current = true; isMountedRef.current = true;
setMounted(true);
return () => { return () => {
isMountedRef.current = false; isMountedRef.current = false;
if (timerRef.current) clearTimeout(timerRef.current);
}; };
}, []); }, []);
const resolvedIcon = resolveIcon(icon);
const closeSheet = useCallback(() => { const closeSheet = useCallback(() => {
if (isClosing) {
if (isClosingRef.current) {
return; return;
} }
isClosingRef.current = true;
setIsClosing(true); setIsClosing(true);
window.setTimeout(() => {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => {
if (isMountedRef.current) { if (isMountedRef.current) {
setIsVisible(false);
setIsRendered(false);
setIsClosing(false);
isClosingRef.current = false;
} }
onClose?.(); onClose?.();
}, EXIT_ANIMATION_MS); }, EXIT_ANIMATION_MS);
}, [isClosing, onClose]);
}, [onClose]);
// Synchronize external isOpen prop changes safely without bounce
useEffect(() => {
if (prevIsOpenRef.current !== isOpen) {
prevIsOpenRef.current = isOpen;
if (isOpen) {
if (timerRef.current) clearTimeout(timerRef.current);
isClosingRef.current = false;
setIsClosing(false);
setIsRendered(true);
} else if (!isClosingRef.current && isRendered) {
closeSheet();
}
}
}, [isOpen, isRendered, closeSheet]);
const resolvedIcon = resolveIcon(icon);
useHardwareBackHandler(() => { useHardwareBackHandler(() => {
closeSheet(); closeSheet();
return true; return true;
}, isVisible && !isClosing);
}, isRendered && !isClosing);
const controls = { close: closeSheet }; const controls = { close: closeSheet };
const resolvedTitle = typeof title === "function" ? title(controls) : title; const resolvedTitle = typeof title === "function" ? title(controls) : title;
@ -243,7 +273,7 @@ export function InformationSheet({
); );
useEffect(() => { useEffect(() => {
if (!isVisible) {
if (!isRendered) {
return; return;
} }
@ -257,13 +287,13 @@ export function InformationSheet({
document.body.style.overflow = previousBodyOverflow; document.body.style.overflow = previousBodyOverflow;
document.documentElement.style.overflow = previousHtmlOverflow; document.documentElement.style.overflow = previousHtmlOverflow;
}; };
}, [isVisible]);
}, [isRendered]);
if (!isVisible) {
if (!isRendered || !mounted) {
return null; return null;
} }
return (
const content = (
<div <div
className={[ className={[
"fixed inset-0 z-50 flex items-end justify-center overflow-y-auto", "fixed inset-0 z-50 flex items-end justify-center overflow-y-auto",
@ -396,6 +426,8 @@ export function InformationSheet({
</section> </section>
</div> </div>
); );
return createPortal(content, document.body);
} }
export default InformationSheet; export default InformationSheet;

2
src/components/Componentes/question-birthplace.tsx

@ -249,7 +249,7 @@ export function QuestionBirthplace({
setIsClosing(false); setIsClosing(false);
}, [disabled]); }, [disabled]);
useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet });
useSheetScrollLock(isOpen, { onBack: closeSheet });
// Handle escape key // Handle escape key
useEffect(() => { useEffect(() => {

2
src/components/Componentes/question-date-sheet.tsx

@ -181,7 +181,7 @@ export function QuestionDateSheet({
window.setTimeout(onClose, EXIT_ANIMATION_MS); window.setTimeout(onClose, EXIT_ANIMATION_MS);
}, [onClose]); }, [onClose]);
useSheetScrollLock(!isClosing, { onBack: closeSheet });
useSheetScrollLock(true, { onBack: closeSheet });
useEffect(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {

2
src/components/Componentes/question-phone.tsx

@ -586,7 +586,7 @@ export function QuestionPhone({
); );
}, [countryList, searchQuery]); }, [countryList, searchQuery]);
useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet });
useSheetScrollLock(isOpen, { onBack: closeSheet });
useEffect(() => { useEffect(() => {
if (!isOpen) return; if (!isOpen) return;

2
src/components/Componentes/question-sheet.tsx

@ -81,7 +81,7 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) {
setIsClosing(false); setIsClosing(false);
}, [disabled, selectedList]); }, [disabled, selectedList]);
useSheetScrollLock(isOpen && !isClosing, { onBack: closeSheet });
useSheetScrollLock(isOpen, { onBack: closeSheet });
// Handle escape key // Handle escape key
useEffect(() => { useEffect(() => {

46
src/components/Componentes/support-sheet.tsx

@ -17,40 +17,58 @@ export type SupportSheetProps = {
export function SupportSheet({ isOpen, onClose }: SupportSheetProps) { export function SupportSheet({ isOpen, onClose }: SupportSheetProps) {
const { dictionary: t } = useI18n(); const { dictionary: t } = useI18n();
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const [isRendered, setIsRendered] = useState(isOpen);
const [isClosing, setIsClosing] = useState(false); const [isClosing, setIsClosing] = useState(false);
const isClosingRef = useRef(false);
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
const prevIsOpenRef = useRef(isOpen);
const timerRef = useRef<number | null>(null);
useEffect(() => { useEffect(() => {
isMountedRef.current = true; isMountedRef.current = true;
setMounted(true); setMounted(true);
return () => { return () => {
isMountedRef.current = false; isMountedRef.current = false;
if (timerRef.current) clearTimeout(timerRef.current);
}; };
}, []); }, []);
// When isOpen changes from outside, reset isClosing
useEffect(() => {
if (isOpen) {
setIsClosing(false);
}
}, [isOpen]);
const closeSheet = useCallback(() => { const closeSheet = useCallback(() => {
if (isClosing) return;
if (isClosingRef.current) return;
isClosingRef.current = true;
setIsClosing(true); setIsClosing(true);
window.setTimeout(() => {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = window.setTimeout(() => {
if (isMountedRef.current) { if (isMountedRef.current) {
setIsRendered(false);
setIsClosing(false); setIsClosing(false);
isClosingRef.current = false;
} }
onClose(); onClose();
}, EXIT_ANIMATION_MS); }, EXIT_ANIMATION_MS);
}, [isClosing, onClose]);
}, [onClose]);
// When isOpen changes from outside
useEffect(() => {
if (prevIsOpenRef.current !== isOpen) {
prevIsOpenRef.current = isOpen;
if (isOpen) {
if (timerRef.current) clearTimeout(timerRef.current);
isClosingRef.current = false;
setIsClosing(false);
setIsRendered(true);
} else if (!isClosingRef.current && isRendered) {
closeSheet();
}
}
}, [isOpen, isRendered, closeSheet]);
useHardwareBackHandler(closeSheet, isOpen && !isClosing);
useHardwareBackHandler(closeSheet, isRendered && !isClosing);
// Lock body scroll // Lock body scroll
useEffect(() => { useEffect(() => {
if (!isOpen || !mounted) return;
if (!isRendered || !mounted) return;
const previousBodyOverflow = document.body.style.overflow; const previousBodyOverflow = document.body.style.overflow;
const previousHtmlOverflow = document.documentElement.style.overflow; const previousHtmlOverflow = document.documentElement.style.overflow;
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
@ -59,7 +77,7 @@ export function SupportSheet({ isOpen, onClose }: SupportSheetProps) {
document.body.style.overflow = previousBodyOverflow; document.body.style.overflow = previousBodyOverflow;
document.documentElement.style.overflow = previousHtmlOverflow; document.documentElement.style.overflow = previousHtmlOverflow;
}; };
}, [isOpen, mounted]);
}, [isRendered, mounted]);
const handleSupportContact = () => { const handleSupportContact = () => {
if (typeof window !== "undefined" && "HabibApp" in window) { if (typeof window !== "undefined" && "HabibApp" in window) {
@ -84,7 +102,7 @@ export function SupportSheet({ isOpen, onClose }: SupportSheetProps) {
closeSheet(); closeSheet();
}; };
if (!isOpen || !mounted) return null;
if (!isRendered || !mounted) return null;
return createPortal( return createPortal(
<div <div

5
src/components/Componentes/test-completed-sheet.tsx

@ -19,10 +19,6 @@ export function TestCompletedSheet({
}: TestCompletedSheetProps) { }: TestCompletedSheetProps) {
const { dictionary: t, locale } = useI18n(); const { dictionary: t, locale } = useI18n();
if (!isOpen) {
return null;
}
const isFa = locale === "fa"; const isFa = locale === "fa";
const sheetTitle = const sheetTitle =
@ -39,6 +35,7 @@ export function TestCompletedSheet({
return ( return (
<InformationSheet <InformationSheet
isOpen={isOpen}
icon="check" icon="check"
title={sheetTitle} title={sheetTitle}
description={ description={

5
src/components/Componentes/test-exit-sheet.tsx

@ -19,10 +19,6 @@ export function TestExitSheet({
}: TestExitSheetProps) { }: TestExitSheetProps) {
const { dictionary: t, locale } = useI18n(); const { dictionary: t, locale } = useI18n();
if (!isOpen) {
return null;
}
const isFa = locale === "fa"; const isFa = locale === "fa";
const tr = t as Record<string, string>; const tr = t as Record<string, string>;
@ -50,6 +46,7 @@ export function TestExitSheet({
return ( return (
<InformationSheet <InformationSheet
isOpen={isOpen}
icon="warning" icon="warning"
title={title} title={title}
description={ description={

29
src/components/Componentes/test-questions-flow.tsx

@ -131,7 +131,9 @@ export default function TestQuestionsFlow({
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
try { try {
window.localStorage.removeItem("marriage:tests:personality_test:draft"); window.localStorage.removeItem("marriage:tests:personality_test:draft");
window.localStorage.removeItem("marriage:tests:glasser_5_needs_test:draft");
window.localStorage.removeItem(
"marriage:tests:glasser_5_needs_test:draft",
);
} catch {} } catch {}
} }
setAnswers({}); setAnswers({});
@ -190,8 +192,12 @@ export default function TestQuestionsFlow({
} }
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
try { try {
window.localStorage.removeItem("marriage:tests:personality_test:draft");
window.localStorage.removeItem("marriage:tests:glasser_5_needs_test:draft");
window.localStorage.removeItem(
"marriage:tests:personality_test:draft",
);
window.localStorage.removeItem(
"marriage:tests:glasser_5_needs_test:draft",
);
} catch {} } catch {}
} }
if (onClose) { if (onClose) {
@ -287,22 +293,24 @@ export default function TestQuestionsFlow({
key={q.id} key={q.id}
aria-hidden={offset !== 0} aria-hidden={offset !== 0}
className={[ className={[
"absolute inset-0 flex flex-col overflow-y-auto pt-4 pb-2 transition-transform duration-250 ease-[cubic-bezier(0.25,1,0.5,1)]",
offset === 0 ? "pointer-events-auto" : "pointer-events-none",
"absolute inset-0 flex flex-col overflow-y-auto pt-2 pb-4 transition-transform duration-250 ease-[cubic-bezier(0.25,1,0.5,1)]",
offset === 0
? "pointer-events-auto"
: "pointer-events-none",
].join(" ")} ].join(" ")}
style={{ style={{
transform: `translate3d(${offset * 100}%, 0, 0)`, transform: `translate3d(${offset * 100}%, 0, 0)`,
}} }}
> >
{/* Question Title */} {/* Question Title */}
<h2
className="text-[17px] sm:text-[18px] font-bold text-[#1F2024] leading-[1.6] text-center px-3 mb-2 flex items-center justify-center shrink-0 min-h-[90px] sm:min-h-[100px]"
>
<span className="w-full">{q.text}</span>
<div className="w-full min-h-[100px] sm:min-h-[120px] flex items-center justify-center my-3 sm:my-4 shrink-0 px-2">
<h2 className="text-[17px] sm:text-[18px] font-bold text-[#1F2024] leading-[1.6] text-center w-full">
{q.text}
</h2> </h2>
</div>
{/* Answer Options Stack */} {/* Answer Options Stack */}
<div className="flex-1 flex flex-col justify-center gap-3.5 my-auto pb-3">
<div className="w-full flex flex-col gap-3 pb-3">
{options.map((option) => { {options.map((option) => {
const isSelected = qSelectedValue === option.value; const isSelected = qSelectedValue === option.value;
@ -422,4 +430,3 @@ export default function TestQuestionsFlow({
</> </>
); );
} }
Loading…
Cancel
Save