UNPKG

@carbon/react

Version:

React components for the Carbon Design System

106 lines (102 loc) 3.7 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import cx from 'classnames'; import PropTypes from 'prop-types'; import React, { useRef, useState, isValidElement } from 'react'; import { usePrefix } from '../../internal/usePrefix.js'; import { Escape } from '../../internal/keyboard/keys.js'; import { match } from '../../internal/keyboard/match.js'; import { useWindowEvent } from '../../internal/useEvent.js'; import { useMergedRefs } from '../../internal/useMergedRefs.js'; import Switcher from './Switcher.js'; import { noopFn } from '../../internal/noopFn.js'; const HeaderPanel = /*#__PURE__*/React.forwardRef(function HeaderPanel({ children, className: customClassName, expanded, addFocusListeners = true, onHeaderPanelFocus = noopFn, href, ...rest }, ref) { const prefix = usePrefix(); const headerPanelReference = useRef(null); const headerPanelRef = useMergedRefs([headerPanelReference, ref]); const controlled = useRef(expanded !== undefined).current; const [expandedState, setExpandedState] = useState(expanded); const expandedProp = controlled ? expanded : expandedState; const [lastClickedElement, setLastClickedElement] = useState(null); const className = cx(`${prefix}--header-panel`, { [`${prefix}--header-panel--expanded`]: expandedProp, [customClassName]: !!customClassName }); const eventHandlers = {}; if (addFocusListeners) { eventHandlers.onBlur = event => { if (!event.currentTarget.contains(event.relatedTarget) && !lastClickedElement?.classList?.contains(`${prefix}--switcher__item-link`)) { setExpandedState(false); setLastClickedElement(null); if (expanded) { onHeaderPanelFocus(); } } }; eventHandlers.onKeyDown = event => { if (match(event, Escape)) { setExpandedState(false); onHeaderPanelFocus(); if (href) { window.location.href = href; } } }; } useWindowEvent('click', event => { const target = event.target; if (!(target instanceof HTMLElement)) return; setLastClickedElement(target); const isChildASwitcher = /*#__PURE__*/isValidElement(children) && typeof children.type !== 'string' && children.type === Switcher; if (isChildASwitcher && !target.closest(`.${prefix}--header-panel--expanded`) && !target.closest(`.${prefix}--header__action`) && !headerPanelReference?.current?.classList.contains(`${prefix}--switcher`) && expanded) { setExpandedState(false); onHeaderPanelFocus(); } }); return /*#__PURE__*/React.createElement("div", _extends({}, rest, { className: className, ref: headerPanelRef }, eventHandlers), children); }); HeaderPanel.propTypes = { /** * Specify whether focus and blur listeners are added. They are by default. */ addFocusListeners: PropTypes.bool, /** * The content that will render inside of the `HeaderPanel` */ children: PropTypes.node, /** * Optionally provide a custom class to apply to the underlying `<li>` node */ className: PropTypes.string, /** * Specify whether the panel is expanded */ expanded: PropTypes.bool, /** * Provide the `href` to the id of the element on your package that could * be target. */ href: PropTypes.string, /** * An optional listener that is called a callback to collapse the HeaderPanel */ onHeaderPanelFocus: PropTypes.func }; HeaderPanel.displayName = 'HeaderPanel'; export { HeaderPanel as default };