114 lines
2.6 KiB
TypeScript
114 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useId, useRef, useState } from "react";
|
|
import { CaretDownIcon } from "@phosphor-icons/react/dist/ssr";
|
|
import styles from "./FaqSection.module.css";
|
|
|
|
export type FaqItem = {
|
|
question: string;
|
|
answer: string;
|
|
};
|
|
|
|
type FaqSectionProps = {
|
|
title: string;
|
|
items: FaqItem[];
|
|
};
|
|
|
|
type AccordionItemProps = {
|
|
item: FaqItem;
|
|
isOpen: boolean;
|
|
buttonId: string;
|
|
panelId: string;
|
|
onToggle: () => void;
|
|
};
|
|
|
|
function AccordionItem({
|
|
item,
|
|
isOpen,
|
|
buttonId,
|
|
panelId,
|
|
onToggle,
|
|
}: AccordionItemProps) {
|
|
const panelRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
const handleToggle = () => {
|
|
const panel = panelRef.current;
|
|
if (!panel) return;
|
|
|
|
if (panel.style.maxHeight) {
|
|
panel.style.maxHeight = "";
|
|
} else {
|
|
panel.style.maxHeight = panel.scrollHeight + "px";
|
|
}
|
|
|
|
onToggle();
|
|
};
|
|
|
|
return (
|
|
<div className={styles.item}>
|
|
<h3 className={styles.itemHeading}>
|
|
<button
|
|
id={buttonId}
|
|
type="button"
|
|
className={styles.trigger}
|
|
aria-expanded={isOpen}
|
|
aria-controls={panelId}
|
|
onClick={handleToggle}
|
|
>
|
|
<span className={styles.triggerText}>{item.question}</span>
|
|
|
|
<span className={styles.iconWrap} aria-hidden="true">
|
|
<CaretDownIcon size={14} weight="bold" />
|
|
</span>
|
|
</button>
|
|
</h3>
|
|
|
|
<div
|
|
id={panelId}
|
|
ref={panelRef}
|
|
role="region"
|
|
aria-labelledby={buttonId}
|
|
className={styles.panel}
|
|
>
|
|
<div className={styles.panelInner}>
|
|
<p className={styles.answer}>{item.answer}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function FaqSection({ title, items }: FaqSectionProps) {
|
|
const baseId = useId();
|
|
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
|
|
|
return (
|
|
<section className={styles.section} aria-labelledby="faq-heading">
|
|
<div className={styles.inner}>
|
|
<div className={styles.content}>
|
|
<h2 id="faq-heading" className={styles.heading}>
|
|
{title}
|
|
</h2>
|
|
</div>
|
|
|
|
<div className={styles.accordion}>
|
|
{items.map((item, index) => {
|
|
const isOpen = openIndex === index;
|
|
|
|
return (
|
|
<AccordionItem
|
|
key={item.question}
|
|
item={item}
|
|
isOpen={isOpen}
|
|
buttonId={`${baseId}-btn-${index}`}
|
|
panelId={`${baseId}-panel-${index}`}
|
|
onToggle={() => setOpenIndex(isOpen ? null : index)}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|