{"version":3,"file":"accordion.mjs","names":[],"sources":["../../../../package/common/accordion/accordion.tsx"],"sourcesContent":["import React, { useState, useEffect, useId, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';\n\nimport { AccordionStyleType, type AccordionProps } from './accordion.types';\n\nimport s from './accordion.module.pcss';\nimport { useComponentDefaults } from '../configProvider';\nimport { isSelfActivation } from '../utils/activationKey';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nconst CLASS_BY_STYLE: Record<AccordionStyleType, string> = {\n  [AccordionStyleType.Light]: s.accordionStyleLight,\n  [AccordionStyleType.Outlined]: s.accordionStyleOutlined,\n  [AccordionStyleType.Plain]: s.accordionStylePlain,\n};\n\nconst NAVIGATION_KEYS = new Set(['ArrowDown', 'ArrowUp', 'Home', 'End']);\n\n/** Same parent only, so a list nested in a body is a set of its own. */\nfunction siblingHeaders(root: HTMLElement): HTMLElement[] {\n  return [...(root.parentElement?.children ?? [])].flatMap((sibling) => {\n    if (!sibling.classList.contains(s.accordion)) return [];\n    const header = sibling.querySelector<HTMLElement>(':scope > [role=\"button\"][aria-expanded]');\n    return header ? [header] : [];\n  });\n}\n\nfunction focusSiblingHeader(header: HTMLElement, key: string) {\n  const headers = header.parentElement ? siblingHeaders(header.parentElement) : [];\n  const index = headers.indexOf(header);\n  if (index < 0) return;\n  const last = headers.length - 1;\n  const next =\n    key === 'Home'\n      ? 0\n      : key === 'End'\n        ? last\n        : key === 'ArrowDown'\n          ? (index + 1) % headers.length\n          : (index - 1 + headers.length) % headers.length;\n  headers[next].focus();\n}\n\nfunction Accordion(props: AccordionProps): ReactElement {\n  const {\n    id = '',\n    title = '',\n    children = null,\n    expanded = null,\n    onHeaderClicked = () => {},\n    styleType = AccordionStyleType.Outlined,\n    color = ThemeColor.Neutral,\n    joined = false,\n    startOpen = false,\n    hideHeader = false,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    ...rest\n  } = useComponentDefaults('Accordion', props);\n  // The header and the body name each other through aria-controls/aria-labelledby, so both need\n  // an id that is stable across renders and unique even with several accordions on the page.\n  const generatedId = useId();\n  const headerId = `${generatedId}-header`;\n  const bodyId = `${generatedId}-body`;\n  const bodyRef = useRef<HTMLDivElement>(null);\n  const bodyInnerRef = useRef<HTMLDivElement>(null);\n\n  const [isExpanded, setIsExpanded] = useState(expanded ?? startOpen);\n  const [bodyHeight, setBodyHeight] = useState<number | null>(null);\n\n  useEffect(() => {\n    // eslint-disable-next-line react-hooks/set-state-in-effect\n    if (expanded !== null) setIsExpanded(expanded);\n  }, [expanded]);\n\n  useEffect(() => {\n    const el = bodyRef.current;\n    if (el) el.inert = !isExpanded;\n  }, [isExpanded]);\n\n  useEffect(() => {\n    const el = bodyInnerRef.current;\n    if (!el) return;\n    const observer = new ResizeObserver(() => setBodyHeight(el.offsetHeight));\n    observer.observe(el);\n    return () => observer.unobserve(el);\n  }, []);\n\n  function toggleExpanded() {\n    const next = !isExpanded;\n    onHeaderClicked(id, next);\n    if (expanded === null) setIsExpanded(next);\n  }\n\n  const bodyStyle = { height: isExpanded ? (bodyHeight ?? 'auto') : '0px' };\n  const tone = toneProps(color);\n  useToneCheck('Accordion', [color]);\n\n  return (\n    <div\n      {...rest}\n      id={id || undefined}\n      className={cx(\n        s.accordion,\n        tone.className,\n        CLASS_BY_STYLE[styleType],\n        joined && s.accordionJoined,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...tone.style, ...style, ...slotStyles?.root }}\n    >\n      {!hideHeader && (\n        <div\n          className={cx(s.accordionHeader, classNames?.header)}\n          style={slotStyles?.header}\n          onClick={toggleExpanded}\n          role='button'\n          tabIndex={0}\n          id={headerId}\n          aria-expanded={isExpanded}\n          aria-controls={bodyId}\n          onKeyDown={(event) => {\n            if (NAVIGATION_KEYS.has(event.key) && event.target === event.currentTarget) {\n              event.preventDefault();\n              focusSiblingHeader(event.currentTarget, event.key);\n              return;\n            }\n            if (!isSelfActivation(event)) return;\n            // Space scrolls the page otherwise, which is what a real <button> suppresses too.\n            event.preventDefault();\n            toggleExpanded();\n          }}\n        >\n          <div className={cx(s.accordionTitle, classNames?.title)} style={slotStyles?.title}>\n            {title}\n          </div>\n          <FontAwesomeIcon\n            icon={arrowIcon}\n            className={cx(s.accordionIcon, isExpanded && s.accordionIconActive, classNames?.icon)}\n            style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n          />\n        </div>\n      )}\n      <div\n        ref={bodyRef}\n        id={bodyId}\n        role='region'\n        aria-labelledby={hideHeader ? undefined : headerId}\n        className={cx(s.accordionBody, classNames?.body)}\n        style={{ ...bodyStyle, ...slotStyles?.body }}\n      >\n        <div\n          className={cx(s.accordionBodyInner, classNames?.bodyInner)}\n          style={slotStyles?.bodyInner}\n          ref={bodyInnerRef}\n        >\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Accordion;\n"],"mappings":"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"}