UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

220 lines 12.1 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Accordion = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ const classnames_1 = __importDefault(require("classnames")); const react_1 = require("react"); const utils_1 = require("../_common/utils"); const divider_1 = require("../divider"); const ChevronDownIconOutline_1 = require("../icons/generated/heroIcons/ChevronDownIconOutline"); const typography_1 = require("../typography"); const accordion_context_1 = require("./accordion-context"); var AccordionSelector; (function (AccordionSelector) { AccordionSelector["Item"] = ".ndl-accordion-item"; AccordionSelector["ItemNotDisabled"] = ".ndl-accordion-item:not(.ndl-accordion-item-disabled)"; })(AccordionSelector || (AccordionSelector = {})); const getAccordionItem = (accordionElement, selector, direction = 'next') => { const itemParent = accordionElement.closest(AccordionSelector.Item); if (!itemParent) { return null; } return (0, utils_1.findUntil)(direction, itemParent, selector); }; const AccordionComponent = function AccordionComponent(_a) { var { children, as, isMultiple, onChange, className, style, htmlAttributes, ref } = _a, restProps = __rest(_a, ["children", "as", "isMultiple", "onChange", "className", "style", "htmlAttributes", "ref"]); const accordionRef = (0, react_1.useRef)(null); (0, react_1.useImperativeHandle)(ref, () => accordionRef.current); const Component = as || 'div'; // The following function includes code needed to be // able to navigate with the arrow keys (not tab). const handleKeyDown = (event) => { const accordionElement = accordionRef.current; if (!accordionElement) { return; } const activeElement = document.activeElement; if (event.key === 'ArrowDown') { event.preventDefault(); event.stopPropagation(); const newFocusedElement = getAccordionItem(activeElement, AccordionSelector.ItemNotDisabled, 'next'); const newHeader = newFocusedElement === null || newFocusedElement === void 0 ? void 0 : newFocusedElement.firstElementChild; if (newHeader && newHeader instanceof HTMLElement) { focusAccordionItem(newHeader); } } else if (event.key === 'ArrowUp') { event.preventDefault(); event.stopPropagation(); const newFocusedElement = getAccordionItem(activeElement, AccordionSelector.ItemNotDisabled, 'prev'); const newHeader = newFocusedElement === null || newFocusedElement === void 0 ? void 0 : newFocusedElement.firstElementChild; if (newHeader && newHeader instanceof HTMLElement) { focusAccordionItem(newHeader); } } else if (event.key === ' ' || event.key === 'Enter') { event.preventDefault(); event.stopPropagation(); activeElement.click(); } }; const focusAccordionItem = (accordionHtmlElement) => { const accordionButton = accordionHtmlElement .getElementsByClassName('ndl-accordion-item-header-button') .item(0); if (accordionButton !== null && accordionButton instanceof HTMLElement) { accordionButton.focus(); } }; const classes = (0, classnames_1.default)('ndl-accordion', className); // Destructure and omit keys from DOM spread const _b = restProps, { expandedItemIds, expandedItemId } = _b, restAccordionProps = __rest(_b, ["expandedItemIds", "expandedItemId"]); const contextValue = isMultiple ? { expandedItemIds, isMultiple, onChange, } : { expandedItemId, isMultiple, onChange, }; return ((0, jsx_runtime_1.jsx)(Component, Object.assign({ className: classes, style: style, ref: accordionRef, onKeyDown: handleKeyDown, role: "presentation" }, restAccordionProps, htmlAttributes, { children: (0, jsx_runtime_1.jsx)(accordion_context_1.AccordionContext.Provider, { value: contextValue, children: children }) }))); }; const createItemId = (type, id) => `ndl-accordionitem${type}id-${id}`; const BaseAccordionItem = ({ itemId, children, title, className = '', arrowPosition = 'leading', isDisabled = false, onExpandedChange, htmlAttributes, style, as, titleTypographyVariant = 'subheading-medium', hasDivider = true, trailingContent, headingLevel = 2, }) => { const contentRef = (0, react_1.useRef)(null); const innerContentRef = (0, react_1.useRef)(null); const itemElementId = createItemId('item', itemId); const headerElementId = createItemId('header', itemId); const buttonElementId = createItemId('button', itemId); const panelElementId = createItemId('panel', itemId); const Component = as || 'div'; const context = (0, accordion_context_1.useAccordionContext)(); const { isMultiple } = context; const isExpanded = isMultiple ? context.expandedItemIds.includes(itemId) : context.expandedItemId === itemId; const handleOnClick = (0, react_1.useCallback)(() => { var _a, _b; if (isDisabled) { return; } // Custom callback to call. if (onExpandedChange !== undefined) { onExpandedChange(!isExpanded); } if (isMultiple) { const { expandedItemIds, onChange } = context; // Multiple expanded. if (isExpanded) { // Remove from list. const newArray = expandedItemIds.filter((activeId) => activeId !== itemId); onChange(newArray); } else if (!isExpanded) { // Add to list. const newArray = [...expandedItemIds]; newArray.push(itemId); onChange(newArray); } } else { const { onChange } = context; // Single expanded. if (isExpanded) { // Set null. onChange(null); } else if (!isExpanded) { // Set to id. onChange(itemId); } } // The W3 WAI-ARIA states that focus can only happen on the header not the header button. (_b = (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.focus(); }, [isExpanded, isMultiple, isDisabled, itemId, onExpandedChange, context]); const classes = (0, classnames_1.default)('ndl-accordion-item', className, { 'ndl-accordion-item-disabled': isDisabled, 'ndl-accordion-item-expanded': isExpanded, }); const headerClasses = (0, classnames_1.default)('ndl-accordion-item-header', { 'ndl-accordion-item-header-disabled': isDisabled, }); const iconClasses = (0, classnames_1.default)('ndl-accordion-item-header-icon-wrapper', { 'ndl-accordion-item-header-icon-wrapper-leading': arrowPosition === 'leading', }); const buttonClasses = (0, classnames_1.default)('ndl-accordion-item-header-button', { 'ndl-accordion-item-header-button-disabled': isDisabled, }); const titleClasses = (0, classnames_1.default)('ndl-accordion-item-header-button-title', { 'ndl-accordion-item-header-button-title': isDisabled, 'ndl-accordion-item-header-button-title-leading': arrowPosition === 'leading', }); const contentClasses = (0, classnames_1.default)('ndl-accordion-item-content', { 'ndl-accordion-item-content-expanded': isExpanded, 'ndl-accordion-item-content-leading': arrowPosition === 'leading', }); return ((0, jsx_runtime_1.jsxs)(Component, Object.assign({ className: classes, style: style, id: itemElementId }, htmlAttributes, { children: [(0, jsx_runtime_1.jsxs)("div", { className: headerClasses, id: headerElementId, children: [(0, jsx_runtime_1.jsx)(typography_1.Typography, { as: `h${headingLevel}`, variant: titleTypographyVariant, className: "ndl-accordion-item-heading", children: (0, jsx_runtime_1.jsx)("button", { type: "button", id: buttonElementId, onClick: handleOnClick, className: buttonClasses, "aria-expanded": isExpanded, "aria-disabled": isDisabled, "aria-controls": panelElementId, disabled: isDisabled, children: (0, jsx_runtime_1.jsxs)("span", { className: iconClasses, children: [(0, jsx_runtime_1.jsx)("span", { className: titleClasses, children: title }), (0, jsx_runtime_1.jsx)(ChevronDownIconOutline_1.ChevronDownIconOutline, { className: (0, classnames_1.default)('ndl-accordion-item-header-icon', { '-n-rotate-180': isExpanded, }) })] }) }) }), trailingContent && ((0, jsx_runtime_1.jsx)(typography_1.Typography, { as: "div", variant: "body-medium", children: trailingContent }))] }), (0, jsx_runtime_1.jsx)("div", { id: panelElementId, ref: contentRef, className: contentClasses, "aria-hidden": !isExpanded, "aria-labelledby": buttonElementId, role: "region", children: (0, jsx_runtime_1.jsx)("div", { ref: innerContentRef, className: "ndl-accordion-item-content-inner", onKeyDown: (event) => event.stopPropagation(), children: (0, jsx_runtime_1.jsx)(typography_1.Typography, { variant: "body-medium", className: "n-text-neutral-text-weak", as: "div", children: children }) }) }), hasDivider && (0, jsx_runtime_1.jsx)(divider_1.Divider, {})] }))); }; /** * @remarks * Where `T` is of the type `string | number | undefined`. * * `TypographyVariants` is of the type `'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'subheading-large' | 'subheading-medium' | 'subheading-small' | 'body-large' | 'body-medium' | 'body-small' | 'code' | 'label'`. */ const AccordionItem = (_a) => { var { className } = _a, restProps = __rest(_a, ["className"]); const classes = (0, classnames_1.default)('ndl-accordion-item-classic', className); return (0, jsx_runtime_1.jsx)(BaseAccordionItem, Object.assign({}, restProps, { className: classes })); }; AccordionItem.displayName = 'Accordion.Item'; const CleanItem = (_a) => { var { className } = _a, restProps = __rest(_a, ["className"]); const classes = (0, classnames_1.default)('ndl-accordion-item-clean', className); return ((0, jsx_runtime_1.jsx)(BaseAccordionItem, Object.assign({}, restProps, { className: classes, hasDivider: false }))); }; CleanItem.displayName = 'Accordion.CleanItem'; const Accordion = Object.assign(AccordionComponent, { CleanItem: CleanItem, Item: AccordionItem, }); exports.Accordion = Accordion; //# sourceMappingURL=Accordion.js.map