Files
web/components/HotelReservation/EnterDetails/SectionAccordion/index.tsx
2024-12-06 09:12:13 +01:00

156 lines
4.6 KiB
TypeScript

"use client"
import { useCallback, useEffect, useRef, useState } from "react"
import { useIntl } from "react-intl"
import { useEnterDetailsStore } from "@/stores/enter-details"
import { CheckIcon, ChevronDownIcon } from "@/components/Icons"
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./sectionAccordion.module.css"
import type { SectionAccordionProps } from "@/types/components/hotelReservation/selectRate/sectionAccordion"
import { StepEnum } from "@/types/enums/step"
function useScrollToActiveSection(
step: StepEnum,
steps: StepEnum[],
isActive: boolean
) {
const handleScroll = useCallback(() => {
const isMobile = window.matchMedia("(max-width: 768px)").matches
if (!isMobile) return
const currentElement = document.querySelector<HTMLElement>(
`[data-step="${step}"]`
)
const prevOpenElement =
document.querySelector<HTMLElement>(`[data-open="true"]`)
const currentStepIndex = steps.indexOf(step)
const prevStep = prevOpenElement?.dataset.step as StepEnum
const prevStepIndex = steps.indexOf(prevStep)
if (currentElement) {
const BOOKING_WIDGET_OFFSET = 71
const prevElementContent = prevOpenElement?.querySelector("header + div")
let collapsedSpace = 0
if (prevElementContent && prevStepIndex < currentStepIndex) {
collapsedSpace = prevElementContent.clientHeight
}
const currentElementTop =
currentElement.getBoundingClientRect().top + window.scrollY
const scrollTarget = Math.round(
currentElementTop - BOOKING_WIDGET_OFFSET - collapsedSpace
)
window.scrollTo({
top: scrollTarget,
behavior: "smooth",
})
}
}, [step, steps])
useEffect(() => {
if (!isActive) return
handleScroll()
}, [isActive, handleScroll])
}
export default function SectionAccordion({
children,
header,
label,
step,
}: React.PropsWithChildren<SectionAccordionProps>) {
const intl = useIntl()
const currentStep = useEnterDetailsStore((state) => state.currentStep)
const steps = useEnterDetailsStore((state) => state.steps)
const [isComplete, setIsComplete] = useState(false)
const [isOpen, setIsOpen] = useState(false)
const isValid = useEnterDetailsStore((state) => state.isValid[step])
const navigate = useEnterDetailsStore((state) => state.actions.navigate)
const { bedType, breakfast } = useEnterDetailsStore((state) => ({
bedType: state.bedType,
breakfast: state.breakfast,
}))
const [title, setTitle] = useState(label)
const noBreakfastTitle = intl.formatMessage({ id: "No breakfast" })
const breakfastTitle = intl.formatMessage({ id: "Breakfast buffet" })
useScrollToActiveSection(step, steps, currentStep === step)
useEffect(() => {
if (step === StepEnum.selectBed && bedType) {
setTitle(bedType.description)
}
// If breakfast step, check if an option has been selected
if (step === StepEnum.breakfast && breakfast !== undefined) {
if (breakfast === false) {
setTitle(noBreakfastTitle)
} else {
setTitle(breakfastTitle)
}
}
}, [bedType, breakfast, setTitle, step, breakfastTitle, noBreakfastTitle])
useEffect(() => {
setIsComplete(isValid)
}, [isValid, setIsComplete])
useEffect(() => {
setIsOpen(currentStep === step)
}, [currentStep, setIsOpen, step])
function onModify() {
navigate(step)
}
const textColor =
isComplete || isOpen ? "uiTextHighContrast" : "baseTextDisabled"
return (
<div className={styles.accordion} data-open={isOpen} data-step={step}>
<div className={styles.iconWrapper}>
<div className={styles.circle} data-checked={isComplete}>
{isComplete ? (
<CheckIcon color="white" height="16" width="16" />
) : null}
</div>
</div>
<header className={styles.header}>
<button
onClick={onModify}
disabled={!isComplete}
className={styles.modifyButton}
>
<Footnote
className={styles.title}
asChild
textTransform="uppercase"
type="label"
color={textColor}
>
<h2>{header}</h2>
</Footnote>
<Subtitle className={styles.selection} type="two" color={textColor}>
{title}
</Subtitle>
{isComplete && !isOpen && (
<ChevronDownIcon className={styles.button} color="burgundy" />
)}
</button>
</header>
<div className={styles.content}>
<div className={styles.contentWrapper}>{children}</div>
</div>
</div>
)
}