UNPKG

@fluentui/react-northstar

Version:
251 lines (248 loc) 10.2 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import _each from "lodash/each"; import _invoke from "lodash/invoke"; import _without from "lodash/without"; import _includes from "lodash/includes"; import _uniqueId from "lodash/uniqueId"; import { accordionBehavior } from '@fluentui/accessibility'; import * as customPropTypes from '@fluentui/react-proptypes'; import * as PropTypes from 'prop-types'; import * as React from 'react'; import { childrenExist, commonPropTypes, rtlTextContainer, createShorthand, createShorthandFactory } from '../../utils'; import { AccordionTitle } from './AccordionTitle'; import { AccordionContent } from './AccordionContent'; import { ContainerFocusHandler } from '../../utils/accessibility/FocusHandling/FocusContainer'; import { useAutoControlled, useAccessibility, useTelemetry, useFluentContext, useUnhandledProps, getElementType, useStyles } from '@fluentui/react-bindings'; export var accordionClassName = 'ui-accordion'; export var accordionSlotClassNames = { content: accordionClassName + "__content", title: accordionClassName + "__title" }; /** * An Accordion represents stacked set of content sections, with action elements to toggle the display of these sections. * * @accessibility * Implements [ARIA Accordion](https://www.w3.org/TR/wai-aria-practices-1.1/#accordion) design pattern (keyboard navigation not yet supported). */ export var Accordion = /*#__PURE__*/function () { var Accordion = /*#__PURE__*/React.forwardRef(function (props, ref) { var context = useFluentContext(); var _useTelemetry = useTelemetry(Accordion.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var expanded = props.expanded, exclusive = props.exclusive, accessibility = props.accessibility, children = props.children, className = props.className, design = props.design, styles = props.styles, variables = props.variables, panels = props.panels, renderPanelContent = props.renderPanelContent, renderPanelTitle = props.renderPanelTitle, alwaysRenderPanels = props.alwaysRenderPanels; var alwaysActiveIndex = expanded ? 0 : -1; var _useAutoControlled = useAutoControlled({ defaultValue: props.defaultActiveIndex, value: props.activeIndex, initialValue: exclusive ? alwaysActiveIndex : [alwaysActiveIndex] }), activeIndex = _useAutoControlled[0], setActiveIndex = _useAutoControlled[1]; var actionHandlers = { moveNext: function moveNext(e) { e.preventDefault(); focusHandler.moveNext(); }, movePrevious: function movePrevious(e) { e.preventDefault(); focusHandler.movePrevious(); }, moveFirst: function moveFirst(e) { e.preventDefault(); focusHandler.moveFirst(); }, moveLast: function moveLast(e) { e.preventDefault(); focusHandler.moveLast(); } }; var getA11yProps = useAccessibility(accessibility, { debugName: Accordion.displayName, actionHandlers: actionHandlers, rtl: context.rtl }); var _useStyles = useStyles(Accordion.displayName, { className: accordionClassName, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: variables }; }, rtl: context.rtl }), classes = _useStyles.classes; var _React$useState = React.useState(), focusedIndex = _React$useState[0], setfocusedIndex = _React$useState[1]; var handleNavigationFocus = function handleNavigationFocus(index) { setfocusedIndex(index); }; var getNavigationItemsSize = function getNavigationItemsSize() { return props.panels.length; }; var unhandledProps = useUnhandledProps(Accordion.handledProps, props); var ElementType = getElementType(props); var focusHandler = new ContainerFocusHandler(getNavigationItemsSize, handleNavigationFocus, true); var itemRefs = React.useMemo(function () { return Array.from({ length: panels == null ? void 0 : panels.length }, function () { return /*#__PURE__*/React.createRef(); }); }, // As we are using "panels.length" it's fine to have dependency on them // eslint-disable-next-line react-hooks/exhaustive-deps [panels == null ? void 0 : panels.length]); React.useEffect(function () { var targetComponent = itemRefs[focusedIndex] && itemRefs[focusedIndex].current; targetComponent && targetComponent.focus(); }, [itemRefs, focusedIndex]); var defaultAccordionTitleId = React.useMemo(function () { return _uniqueId('accordion-title-'); }, []); var defaultAccordionContentId = React.useMemo(function () { return _uniqueId('accordion-content-'); }, []); var computeNewIndex = function computeNewIndex(index) { if (!isIndexActionable(index)) { return activeIndex; } if (exclusive) return index === activeIndex ? -1 : index; // check to see if index is in array, and remove it, if not then add it return _includes(activeIndex, index) ? _without(activeIndex, index) : [].concat(activeIndex, [index]); }; var handleTitleOverrides = function handleTitleOverrides(predefinedProps) { return { onClick: function onClick(e, titleProps) { var index = titleProps.index; var activeIndex = computeNewIndex(index); setActiveIndex(activeIndex); setfocusedIndex(index); _invoke(props, 'onActiveIndexChange', e, Object.assign({}, props, { activeIndex: activeIndex })); _invoke(predefinedProps, 'onClick', e, titleProps); _invoke(props, 'onTitleClick', e, titleProps); }, onFocus: function onFocus(e, titleProps) { _invoke(predefinedProps, 'onFocus', e, titleProps); setfocusedIndex(predefinedProps.index); } }; }; var isIndexActive = function isIndexActive(index) { return exclusive ? activeIndex === index : _includes(activeIndex, index); }; /** * Checks if panel at index can be actioned upon. Used in the case of expanded accordion, * when at least a panel needs to stay active. Will return false if expanded prop is true, * index is active and either it's an exclusive accordion or if there are no other active * panels open besides this one. * * @param index - The index of the panel. * @returns If the panel can be set active/inactive. */ var isIndexActionable = function isIndexActionable(index) { if (!isIndexActive(index)) { return true; } return !expanded || !exclusive && activeIndex.length > 1; }; var renderPanels = function renderPanels() { var children = []; focusHandler.syncFocusedIndex(focusedIndex); _each(panels, function (panel, index) { var content = panel.content, title = panel.title; var active = isIndexActive(+index); var canBeCollapsed = isIndexActionable(+index); var titleId = title['id'] || "" + defaultAccordionTitleId + index; var contentId = content['id'] || "" + defaultAccordionContentId + index; var contentRef = itemRefs[index]; children.push(createShorthand(AccordionTitle, title, { defaultProps: function defaultProps() { return { className: accordionSlotClassNames.title, active: active, index: +index, contentRef: contentRef, canBeCollapsed: canBeCollapsed, id: titleId, accordionContentId: contentId }; }, overrideProps: handleTitleOverrides, render: renderPanelTitle })); if (alwaysRenderPanels || active) { children.push(createShorthand(AccordionContent, content, { defaultProps: function defaultProps() { return { className: accordionSlotClassNames.content, active: active, id: contentId, accordionTitleId: titleId }; }, render: renderPanelContent })); } }); return children; }; var element = /*#__PURE__*/React.createElement(ElementType, _extends({}, getA11yProps('root', Object.assign({ className: classes.root }, unhandledProps, { ref: ref })), rtlTextContainer.getAttributes({ forElements: [children] })), childrenExist(children) ? children : renderPanels()); setEnd(); return element; }); Accordion.displayName = 'Accordion'; Accordion.propTypes = Object.assign({}, commonPropTypes.createCommon({ content: false }), { activeIndex: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number])]), defaultActiveIndex: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.number])]), exclusive: PropTypes.bool, expanded: PropTypes.bool, onTitleClick: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.func]), onActiveIndexChange: PropTypes.func, panels: customPropTypes.every([customPropTypes.disallow(['children']), PropTypes.arrayOf(PropTypes.shape({ content: customPropTypes.itemShorthand, title: customPropTypes.itemShorthand }))]), renderPanelTitle: PropTypes.func, renderPanelContent: PropTypes.func }); Accordion.defaultProps = { accessibility: accordionBehavior, as: 'dl' }; Accordion.handledProps = Object.keys(Accordion.propTypes); Accordion.Title = AccordionTitle; Accordion.Content = AccordionContent; Accordion.create = createShorthandFactory({ Component: Accordion }); return Accordion; }(); //# sourceMappingURL=Accordion.js.map