@neo4j-ndl/react
Version:
React implementation of Neo4j Design System
220 lines • 12.1 kB
JavaScript
"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