UNPKG

@nex-ui/react

Version:

🎉 A beautiful, modern, and reliable React component library.

251 lines (248 loc) • 8.58 kB
"use client"; import { jsx, jsxs } from 'react/jsx-runtime'; import * as m from 'motion/react-m'; import { useEvent } from '@nex-ui/hooks'; import { ChevronDownOutlined } from '@nex-ui/icons'; import { LazyMotion, AnimatePresence } from 'motion/react'; import { useId, useRef, useMemo } from 'react'; import { useAccordionGroup } from './AccordionContext.mjs'; import { useDefaultProps } from '../utils/useDefaultProps.mjs'; import { useStyles } from '../utils/useStyles.mjs'; import { useSlot } from '../utils/useSlot.mjs'; import { ButtonBase } from '../buttonBase/ButtonBase.mjs'; import { motionFeatures } from '../utils/motionFeatures/index.mjs'; import { useNexUI } from '../provider/Context.mjs'; import { composeClasses } from '../utils/composeClasses.mjs'; import { accordionItemRecipe } from '../../theme/recipes/accordion.mjs'; import { getUtilityClass } from '../utils/getUtilityClass.mjs'; const contentMotionVariants = { expanded: { opacity: 1, height: 'auto', transition: { opacity: { delay: 0.1 }, ease: 'easeInOut', duration: 0.2 } }, unexpanded: { opacity: 0, height: 0, overflow: 'hidden', transition: { ease: 'easeInOut', duration: 0.2 } } }; const indicatorMotionVariants = { expanded: { rotate: 180, transition: { duration: 0.2 } }, unexpanded: { rotate: 0, transition: { duration: 0.2 } } }; const useSlotClasses = (ownerState)=>{ const { prefix } = useNexUI(); const { disabled, classes, expanded } = ownerState; return useMemo(()=>{ const accordionItemRoot = `${prefix}-accordion-item`; const slots = { root: [ 'root', disabled && 'disabled', expanded && 'expanded' ], heading: [ 'heading' ], trigger: [ 'trigger' ], content: [ 'content' ], indicator: [ 'indicator' ] }; return composeClasses(slots, getUtilityClass(accordionItemRoot), classes); }, [ classes, disabled, expanded, prefix ]); }; const useSlotAriaProps = (ownerState)=>{ const { itemKey, expanded, slotProps } = ownerState; const id = useId(); return useMemo(()=>{ const triggerProps = slotProps?.trigger || {}; const contentProps = slotProps?.content || {}; const indicatorProps = slotProps?.indicator || {}; const triggerId = triggerProps.id ?? id; const contentId = contentProps.id ?? `panel-${itemKey}-content`; const trigger = { id: triggerId, 'aria-expanded': triggerProps['aria-expanded'] ?? expanded, 'aria-controls': triggerProps['aria-controls'] ?? contentId }; const content = { id: contentId, role: contentProps.role ?? 'region', 'aria-labelledby': contentProps['aria-labelledby'] ?? triggerId }; const indicator = { 'aria-hidden': indicatorProps['aria-hidden'] ?? true }; return { trigger, content, indicator }; }, [ expanded, id, itemKey, slotProps?.content, slotProps?.indicator, slotProps?.trigger ]); }; const AccordionItem = (inProps)=>{ const defaultKey = useId(); const props = useDefaultProps({ name: 'AccordionItem', props: inProps }); const { variant, toggleExpandedKey, expandedKeys, disabledKeys, disabled: defaultDisabled, indicator: defaultIndicator, motionProps: defaultMotionProps, keepMounted: defaultKeepMounted, hideIndicator: defaultHideIndicator, indicatorMotionProps: defaultIndicatorMotionProps } = useAccordionGroup(); const { children, title, slotProps, indicatorMotionProps = defaultIndicatorMotionProps, motionProps = defaultMotionProps, hideIndicator = defaultHideIndicator, keepMounted = defaultKeepMounted, indicator = defaultIndicator, itemKey = defaultKey, disabled = disabledKeys.includes(itemKey) || defaultDisabled, ...remainingProps } = props; const expanded = expandedKeys.includes(itemKey); const ownerState = { ...props, variant, itemKey, expanded, indicator, keepMounted, hideIndicator, disabled, motionProps }; const styles = useStyles({ name: 'AccordionItem', ownerState, recipe: accordionItemRecipe }); const classes = useSlotClasses(ownerState); const slotAriaProps = useSlotAriaProps(ownerState); const animate = expanded ? 'expanded' : 'unexpanded'; const motionInitialRef = useRef(animate); const contentMotionProps = keepMounted ? { animate, initial: motionInitialRef.current, variants: contentMotionVariants } : { variants: contentMotionVariants, initial: motionInitialRef.current, animate: 'expanded', exit: 'unexpanded' }; const [AccordionItemRoot, getAccordionItemRootProps] = useSlot({ ownerState, elementType: 'div', externalForwardedProps: remainingProps, style: styles.root, classNames: classes.root }); const [AccordionItemHeading, getAccordionItemHeadingProps] = useSlot({ ownerState, elementType: 'h3', externalSlotProps: slotProps?.heading, style: styles.heading, classNames: classes.heading }); const handleClick = useEvent(()=>{ toggleExpandedKey(itemKey); }); const [AccordionItemTrigger, getAccordionItemTriggerProps] = useSlot({ ownerState, elementType: ButtonBase, externalSlotProps: slotProps?.trigger, style: styles.trigger, classNames: classes.trigger, a11y: slotAriaProps.trigger, shouldForwardComponent: false, additionalProps: { disabled, onClick: handleClick } }); const [AccordionItemContent, getAccordionItemContentProps] = useSlot({ ownerState, elementType: 'div', externalSlotProps: slotProps?.content, style: styles.content, classNames: classes.content, a11y: slotAriaProps.content }); const [AccordionItemIndicator, getAccordionItemIndicatorProps] = useSlot({ ownerState, elementType: m.span, externalSlotProps: slotProps?.indicator, style: styles.indicator, classNames: classes.indicator, a11y: slotAriaProps.indicator, additionalProps: { animate, variants: indicatorMotionVariants, initial: animate, ...indicatorMotionProps } }); return /*#__PURE__*/ jsx(LazyMotion, { features: motionFeatures, children: /*#__PURE__*/ jsxs(AccordionItemRoot, { ...getAccordionItemRootProps(), children: [ /*#__PURE__*/ jsx(AccordionItemHeading, { ...getAccordionItemHeadingProps(), children: /*#__PURE__*/ jsxs(AccordionItemTrigger, { ...getAccordionItemTriggerProps(), children: [ /*#__PURE__*/ jsx("span", { children: title }), !hideIndicator && /*#__PURE__*/ jsx(AccordionItemIndicator, { ...getAccordionItemIndicatorProps(), children: indicator ?? /*#__PURE__*/ jsx(ChevronDownOutlined, {}) }) ] }) }), /*#__PURE__*/ jsx(AnimatePresence, { children: (keepMounted || expanded) && /*#__PURE__*/ jsx(m.div, { ...contentMotionProps, ...motionProps, children: /*#__PURE__*/ jsx(AccordionItemContent, { ...getAccordionItemContentProps(), children: children }) }) }) ] }) }); }; AccordionItem.displayName = 'AccordionItem'; export { AccordionItem };