Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 1x 38x 38x 38x 38x 38x 38x 38x 1x 1x | import {forwardRef} from 'react'
import {isFragment} from 'react-is'
import cx from 'classnames'
import PropTypes from 'prop-types'
import {combineProps, inject} from '@s-ui/react-primitive-injector'
import Poly from '@s-ui/react-primitive-polymorphic-element'
import {useAccordionContext} from './context/index.js'
import useMeasure from './hook/useMeasure.js'
import AccordionItemPanelDefaultChildren from './AccordionItemPanelDefaultChildren.js'
import {BASE_CLASS_ELEMENT, BASE_CLASS_ITEM_PANEL, BASE_CLASS_ITEM_PANEL_CONTENT} from './settings.js'
const AccordionItemPanel = forwardRef(
(
{
as,
id,
headerId,
content,
children = <AccordionItemPanelDefaultChildren />,
isExpanded,
maxHeight: maxHeightProp,
value,
animationDuration: animationDurationProp,
disabled,
...props
},
forwardedRef
) => {
const [contentRef, {height}] = useMeasure()
const {
values,
animationDuration: animationDurationContext,
maxHeight: maxHeightContext
} = useAccordionContext({isExpanded, value})
const maxHeight = maxHeightProp !== undefined ? maxHeightProp : maxHeightContext
const animationDuration = animationDurationProp || animationDurationContext
const accordionItemPanelClassName = cx(
BASE_CLASS_ITEM_PANEL,
BASE_CLASS_ELEMENT,
`${BASE_CLASS_ITEM_PANEL}--${values.includes(value) ? 'expanded' : 'collapsed'}`
)
return (
<div
id={id}
role="region"
ref={forwardedRef}
aria-labelledby={headerId}
className={accordionItemPanelClassName}
aria-disabled={disabled}
style={{
// grid-template-rows animation is layout-safe: siblings are pushed down
// correctly in all browsers including iOS Safari ≥16.4.
// max-height animation was replaced because it requires transform/will-change
// hacks to avoid Safari repaint corruption, and those hacks break document flow.
display: 'grid',
gridTemplateRows: values.includes(value) ? '1fr' : '0fr',
transition: `grid-template-rows ${animationDuration}ms ${
values.includes(value) ? 'ease-out' : 'ease-in'
}, opacity 0s linear ${values.includes(value) ? 0 : animationDuration}ms, border-top-width 0s linear ${
values.includes(value) ? 0 : animationDuration
}ms`
}}
{...props}
>
<Poly
as={as}
// overflow:hidden + min-height:0 must be on the direct grid child so that
// grid-template-rows:0fr can collapse it to zero height. These cannot live
// inside the !isFragment spread because isFragment is a function (always truthy),
// making !isFragment always false — the spread never executes.
style={{
// overflow must be 'hidden' while collapsed so 0fr clips the content.
// Once expanded, switch to 'visible' so popovers/dropdowns inside the
// panel can overflow outside its bounds (e.g. make/model picker on desktop).
overflow: values.includes(value) ? 'visible' : 'hidden',
minHeight: 0
}}
{...{
...(!isFragment && {
className: `${BASE_CLASS_ITEM_PANEL_CONTENT}Wrapper`
})
}}
>
<div
className={`${BASE_CLASS_ITEM_PANEL_CONTENT}WrapperRef`}
ref={contentRef}
style={{
overflow: values.includes(value) ? 'visible' : 'hidden',
minHeight: 0,
// maxHeight prop caps panel height and enables scroll — applied here (not on
// the outer grid wrapper) so it constrains content without affecting the animation
...(maxHeight !== 0 && {
maxHeight,
overflowY: height > maxHeight ? 'scroll' : 'hidden'
})
}}
>
{inject(children, [
{
props: {
...(content && {children: content}),
isExpanded,
values,
value,
disabled
},
proviso: () => true,
combine: combineProps
}
])}
</div>
</Poly>
</div>
)
}
)
AccordionItemPanel.displayName = 'AccordionItemPanel'
AccordionItemPanel.propTypes = {
/** The elementType of the wrapper **/
as: PropTypes.elementType,
/** The animation duration in ms **/
animationDuration: PropTypes.number,
/** child element **/
children: PropTypes.node,
/** panel inner content **/
content: PropTypes.node,
/** element enabled or not **/
disabled: PropTypes.bool,
/** unique identifier **/
id: PropTypes.string,
/** a required string indicating the button id controlling the panel **/
headerId: PropTypes.string.isRequired,
/** controlled expanded accordion item behavior */
isExpanded: PropTypes.bool,
/** the max height limit a panel can reach when its expanded **/
maxHeight: PropTypes.number,
/** the unique value of the element **/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
}
export default AccordionItemPanel
|