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.
 
 
 
 
 

224 lines
6.7 KiB

"use client";
import type { HTMLAttributes } from "react";
import { useEffect, useId, useState } from "react";
import { useI18n } from "@/translations/provider";
const EXIT_ANIMATION_MS = 220;
export type CallResultSheetProps = Omit<
HTMLAttributes<HTMLDivElement>,
"title" | "onSubmit"
> & {
closeOnOutside?: boolean;
onClose?: () => void;
onSubmit?: (value: string) => void;
onOtherReasonsClick?: () => void;
};
export function CallResultSheet({
closeOnOutside = true,
onClose,
onSubmit,
onOtherReasonsClick,
className,
...props
}: CallResultSheetProps) {
const { dictionary: t } = useI18n();
const callResultOptions = t.sheets.callOptions;
const groupId = useId();
const [isVisible, setIsVisible] = useState(true);
const [isEntering, setIsEntering] = useState(true);
const [isClosing, setIsClosing] = useState(false);
const [selectedReason, setSelectedReason] = useState<string>(
callResultOptions[0],
);
const closeSheet = () => {
if (isClosing) {
return;
}
setIsClosing(true);
};
useEffect(() => {
const frameId = window.requestAnimationFrame(() => {
setIsEntering(false);
});
return () => {
window.cancelAnimationFrame(frameId);
};
}, []);
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]);
useEffect(() => {
if (!isClosing) {
return;
}
const timeoutId = window.setTimeout(() => {
setIsVisible(false);
onClose?.();
}, EXIT_ANIMATION_MS);
return () => {
window.clearTimeout(timeoutId);
};
}, [isClosing, onClose]);
if (!isVisible) {
return null;
}
return (
<div
className={[
"fixed inset-0 z-50 flex items-end justify-center transition-all duration-[220ms]",
isClosing || isEntering
? "bg-[#171717]/0 opacity-0"
: "bg-[#171717]/55 opacity-100",
]
.filter(Boolean)
.join(" ")}
role="dialog"
aria-modal="true"
aria-label={t.sheets.callResult}
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-[34px] bg-[#F9F8F8] p-3.5 text-center shadow-[0_20px_60px_rgba(15,23,42,0.08)] transition-transform duration-[220ms] ease-out will-change-transform",
isClosing || isEntering ? "translate-y-full" : "translate-y-0",
className,
]
.filter(Boolean)
.join(" ")}
>
<div className="mx-auto flex flex-col items-center">
<h2 className="text-[18px] leading-[1.2] font-bold tracking-[-0.03em] text-[#171717]">
{t.sheets.callResult}
</h2>
<div
className="mt-5 w-full"
role="radiogroup"
aria-labelledby={groupId}
>
<span id={groupId} className="sr-only">
{t.sheets.selectCallResult}
</span>
<div className="flex flex-col gap-[14px]">
{callResultOptions.map((option) => {
const checked = selectedReason === option;
return (
<label
key={option}
className="flex cursor-pointer items-center gap-3 rounded-[14px] bg-[#ECECEC] px-[14px] py-[18px] text-left"
>
<input
checked={checked}
className="sr-only"
name="call-result"
type="radio"
value={option}
onChange={() => {
setSelectedReason(option);
if (
option ===
callResultOptions[callResultOptions.length - 1]
) {
onOtherReasonsClick?.();
closeSheet();
}
}}
/>
<span
aria-hidden="true"
className={[
"flex h-6 w-6 shrink-0 items-center justify-center rounded-full border",
checked ? "border-[#F0445B]" : "border-[#9E9E9E]",
]
.filter(Boolean)
.join(" ")}
>
{checked ? (
<span className="h-3 w-3 rounded-full bg-[#F0445B]" />
) : null}
</span>
<span className="text-[16px] leading-none font-semibold text-[#262626]">
{option}
</span>
</label>
);
})}
</div>
</div>
<div className="mt-8 grid w-full grid-cols-[1fr_2fr] gap-3">
<button
type="button"
className="appearance-none border-0 bg-transparent p-0 text-left"
onClick={closeSheet}
>
<div className="inline-flex w-full items-center justify-center rounded-[14px] border border-[#9A9A9A] bg-[#F7F7F7] px-4 py-[18px] text-[16px] font-bold text-[#8B8B8B] shadow-[inset_0_1px_0_rgba(255,255,255,0.8)] transition-opacity active:opacity-90">
{t.common.cancel}
</div>
</button>
<button
type="button"
className="appearance-none border-0 bg-transparent p-0 text-left"
onClick={() => {
onSubmit?.(selectedReason);
closeSheet();
}}
>
<div className="inline-flex w-full items-center justify-center rounded-[14px] bg-[#F0445B] px-4 py-[18px] text-[16px] font-semibold text-white shadow-[0_10px_18px_rgba(240,68,91,0.28)] transition-opacity active:opacity-90">
{t.common.submit}
</div>
</button>
</div>
</div>
</section>
</div>
);
}
export default CallResultSheet;