You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

150 lines
4.0 KiB

"use client";
import Image from "next/image";
import type { HTMLAttributes, ReactNode } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { useHardwareBackHandler } from "@/hooks/use-hardware-back-handler";
import { useI18n } from "@/translations/provider";
const EXIT_ANIMATION_MS = 200;
export type FemaleConsentSheetProps = Omit<
HTMLAttributes<HTMLDivElement>,
"children" | "title"
> & {
title: ReactNode;
description?: ReactNode;
buttons?: ReactNode | ((controls: { close: () => void }) => ReactNode);
closeOnOutside?: boolean;
onClose?: () => void;
};
export function FemaleConsentSheet({
title,
description,
buttons,
closeOnOutside = true,
onClose,
className,
...props
}: FemaleConsentSheetProps) {
const { dictionary: t } = useI18n();
const [isVisible, setIsVisible] = useState(true);
const [isClosing, setIsClosing] = useState(false);
const isMountedRef = useRef(true);
useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);
const closeSheet = useCallback(() => {
if (isClosing) return;
setIsClosing(true);
window.setTimeout(() => {
if (isMountedRef.current) {
setIsVisible(false);
}
onClose?.();
}, EXIT_ANIMATION_MS);
}, [isClosing, onClose]);
useHardwareBackHandler(closeSheet, isVisible && !isClosing);
const controls = { close: closeSheet };
const resolvedButtons =
typeof buttons === "function" ? buttons(controls) : buttons;
useEffect(() => {
if (!isVisible) {
return;
}
const previousBodyOverflow = document.body.style.overflow;
const previousHtmlOverflow = document.documentElement.style.overflow;
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
return () => {
document.body.style.overflow = previousBodyOverflow;
document.documentElement.style.overflow = previousHtmlOverflow;
};
}, [isVisible]);
if (!isVisible) {
return null;
}
return (
<div
className={[
"fixed inset-0 z-50 flex items-end justify-center",
isClosing ? "flutter-scrim-exit" : "flutter-scrim",
]
.filter(Boolean)
.join(" ")}
role="dialog"
aria-modal="true"
aria-label={t["Information sheet"]}
tabIndex={-1}
onClick={(event) => {
if (closeOnOutside && event.target === event.currentTarget) {
closeSheet();
}
}}
onKeyDown={(event) => {
if (
closeOnOutside &&
event.target === event.currentTarget &&
(event.key === "Escape" || event.key === "Enter" || event.key === " ")
) {
event.preventDefault();
closeSheet();
}
}}
>
<section
{...props}
className={[
"w-full sm:max-w-[375px] rounded-t-[22px] bg-white py-5 px-4 text-left shadow-[0_20px_60px_rgba(15,23,42,0.08)]",
isClosing ? "flutter-sheet-surface-exit" : "flutter-sheet-surface",
className,
]
.filter(Boolean)
.join(" ")}
>
<div className="flex items-center justify-between gap-4">
<h2 className="text-sm font-bold leading-[1.2] tracking-[-0.03em] text-[#8B8B8B]">
{title}
</h2>
<button
type="button"
onClick={closeSheet}
aria-label="Close"
className="items-center justify-center rounded-full text-[#A0A0A0]"
>
<Image
src={"/assets/images/Vecfadsftor.svg"}
alt="close"
width={18}
height={18}
/>
</button>
</div>
{description ? (
<div className="mt-5 text-sm leading-[1.45] text-[#2B2B2B]">
{description}
</div>
) : null}
{resolvedButtons ? <div className="mt-5">{resolvedButtons}</div> : null}
</section>
</div>
);
}
export default FemaleConsentSheet;