UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

707 lines (683 loc) • 17.2 kB
import { c } from 'react-compiler-runtime'; import { PlusIcon, ChevronDownIcon } from '@primer/octicons-react'; import React, { createElement, isValidElement } from 'react'; import { clsx } from 'clsx'; import { ActionList } from '../ActionList/index.js'; import { SubItem } from '../ActionList/Item.js'; import { ActionListContainerContext } from '../ActionList/ActionListContainerContext.js'; import { useId } from '../hooks/useId.js'; import useIsomorphicLayoutEffect from '../utils/useIsomorphicLayoutEffect.js'; import classes from '../ActionList/ActionList.module.css.js'; import navListClasses from './NavList.module.css.js'; import { flushSync } from 'react-dom'; import { isSlot } from '../utils/is-slot.js'; import { jsx, Fragment, jsxs } from 'react/jsx-runtime'; const Root = /*#__PURE__*/React.forwardRef((t0, ref) => { const $ = c(10); let children; let props; if ($[0] !== t0) { ({ children, ...props } = t0); $[0] = t0; $[1] = children; $[2] = props; } else { children = $[1]; props = $[2]; } let t1; if ($[3] === Symbol.for("react.memo_cache_sentinel")) { t1 = { container: "NavList" }; $[3] = t1; } else { t1 = $[3]; } let t2; if ($[4] !== children) { t2 = /*#__PURE__*/jsx(ActionListContainerContext.Provider, { value: t1, children: /*#__PURE__*/jsx(ActionList, { children: children }) }); $[4] = children; $[5] = t2; } else { t2 = $[5]; } let t3; if ($[6] !== props || $[7] !== ref || $[8] !== t2) { t3 = /*#__PURE__*/jsx("nav", { ...props, ref: ref, children: t2 }); $[6] = props; $[7] = ref; $[8] = t2; $[9] = t3; } else { t3 = $[9]; } return t3; }); Root.displayName = 'NavList'; // ---------------------------------------------------------------------------- // NavList.Item const Item = /*#__PURE__*/React.forwardRef((t0, ref) => { const $ = c(29); let ariaCurrent; let children; let defaultOpen; let props; if ($[0] !== t0) { ({ "aria-current": ariaCurrent, children, defaultOpen, ...props } = t0); $[0] = t0; $[1] = ariaCurrent; $[2] = children; $[3] = defaultOpen; $[4] = props; } else { ariaCurrent = $[1]; children = $[2]; defaultOpen = $[3]; props = $[4]; } const { depth } = React.useContext(SubNavContext); let childrenWithoutSubNavOrTrailingAction; let subNav; let t1; if ($[5] !== children || $[6] !== defaultOpen) { subNav = React.Children.toArray(children).find(_temp); let t2; if ($[10] !== children) { t2 = React.Children.toArray(children).filter(_temp2); $[10] = children; $[11] = t2; } else { t2 = $[11]; } childrenWithoutSubNavOrTrailingAction = t2; if (! /*#__PURE__*/isValidElement(subNav) && defaultOpen) { console.error("NavList.Item must have a NavList.SubNav to use defaultOpen."); } t1 = subNav && /*#__PURE__*/isValidElement(subNav); $[5] = children; $[6] = defaultOpen; $[7] = childrenWithoutSubNavOrTrailingAction; $[8] = subNav; $[9] = t1; } else { childrenWithoutSubNavOrTrailingAction = $[7]; subNav = $[8]; t1 = $[9]; } if (t1) { let t2; if ($[12] !== depth) { t2 = { "--subitem-depth": depth }; $[12] = depth; $[13] = t2; } else { t2 = $[13]; } const t3 = t2; let t4; if ($[14] !== childrenWithoutSubNavOrTrailingAction || $[15] !== defaultOpen || $[16] !== depth || $[17] !== subNav || $[18] !== t3) { t4 = /*#__PURE__*/jsx(ItemWithSubNav, { subNav: subNav, depth: depth, defaultOpen: defaultOpen, style: t3, children: childrenWithoutSubNavOrTrailingAction }); $[14] = childrenWithoutSubNavOrTrailingAction; $[15] = defaultOpen; $[16] = depth; $[17] = subNav; $[18] = t3; $[19] = t4; } else { t4 = $[19]; } return t4; } const t2 = Boolean(ariaCurrent) && ariaCurrent !== "false"; let t3; if ($[20] !== depth) { t3 = { "--subitem-depth": depth }; $[20] = depth; $[21] = t3; } else { t3 = $[21]; } const t4 = t3; let t5; if ($[22] !== ariaCurrent || $[23] !== children || $[24] !== props || $[25] !== ref || $[26] !== t2 || $[27] !== t4) { t5 = /*#__PURE__*/jsx(ActionList.LinkItem, { ref: ref, "aria-current": ariaCurrent, active: t2, style: t4, ...props, children: children }); $[22] = ariaCurrent; $[23] = children; $[24] = props; $[25] = ref; $[26] = t2; $[27] = t4; $[28] = t5; } else { t5 = $[28]; } return t5; }); Item.displayName = 'NavList.Item'; // ---------------------------------------------------------------------------- // ItemWithSubNav (internal) const ItemWithSubNavContext = /*#__PURE__*/React.createContext({ buttonId: '', subNavId: '', isOpen: false }); function ItemWithSubNav(t0) { var _ref; const $ = c(26); const { children, subNav, defaultOpen, style } = t0; const buttonId = useId(); const subNavId = useId(); const [isOpen, setIsOpen] = React.useState((_ref = defaultOpen || null) !== null && _ref !== void 0 ? _ref : false); const subNavRef = React.useRef(null); const [containsCurrentItem, setContainsCurrentItem] = React.useState(false); let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = () => { if (subNavRef.current) { const currentItem = subNavRef.current.querySelector("[aria-current]:not([aria-current=false])"); if (currentItem) { setContainsCurrentItem(true); setIsOpen(true); } } }; $[0] = t1; } else { t1 = $[0]; } let t2; if ($[1] !== buttonId || $[2] !== subNav) { t2 = [subNav, buttonId]; $[1] = buttonId; $[2] = subNav; $[3] = t2; } else { t2 = $[3]; } useIsomorphicLayoutEffect(t1, t2); let t3; if ($[4] !== buttonId || $[5] !== isOpen || $[6] !== subNavId) { t3 = { buttonId, subNavId, isOpen }; $[4] = buttonId; $[5] = isOpen; $[6] = subNavId; $[7] = t3; } else { t3 = $[7]; } const t4 = !isOpen && containsCurrentItem; let t5; if ($[8] === Symbol.for("react.memo_cache_sentinel")) { t5 = () => setIsOpen(_temp3); $[8] = t5; } else { t5 = $[8]; } let t6; if ($[9] === Symbol.for("react.memo_cache_sentinel")) { t6 = /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: /*#__PURE__*/jsx(ChevronDownIcon, { className: classes.ExpandIcon }) }); $[9] = t6; } else { t6 = $[9]; } let t7; if ($[10] !== subNav) { let t8; if ($[12] === Symbol.for("react.memo_cache_sentinel")) { t8 = { ref: subNavRef }; $[12] = t8; } else { t8 = $[12]; } t7 = /*#__PURE__*/React.cloneElement(subNav, t8); $[10] = subNav; $[11] = t7; } else { t7 = $[11]; } let t8; if ($[13] !== t7) { t8 = /*#__PURE__*/jsx(SubItem, { children: t7 }); $[13] = t7; $[14] = t8; } else { t8 = $[14]; } let t9; if ($[15] !== buttonId || $[16] !== children || $[17] !== isOpen || $[18] !== style || $[19] !== subNavId || $[20] !== t4 || $[21] !== t8) { t9 = /*#__PURE__*/jsxs(ActionList.Item, { id: buttonId, "aria-expanded": isOpen, "aria-controls": subNavId, active: t4, onSelect: t5, style: style, children: [children, t6, t8] }); $[15] = buttonId; $[16] = children; $[17] = isOpen; $[18] = style; $[19] = subNavId; $[20] = t4; $[21] = t8; $[22] = t9; } else { t9 = $[22]; } let t10; if ($[23] !== t3 || $[24] !== t9) { t10 = /*#__PURE__*/jsx(ItemWithSubNavContext.Provider, { value: t3, children: t9 }); $[23] = t3; $[24] = t9; $[25] = t10; } else { t10 = $[25]; } return t10; } // ---------------------------------------------------------------------------- // NavList.SubNav function _temp3(open) { return !open; } const SubNavContext = /*#__PURE__*/React.createContext({ depth: 0 }); // NOTE: SubNav must be a direct child of an Item const SubNav = /*#__PURE__*/React.forwardRef((t0, forwardedRef) => { const $ = c(10); const { children } = t0; const { buttonId, subNavId } = React.useContext(ItemWithSubNavContext); const { depth } = React.useContext(SubNavContext); if (!buttonId || !subNavId) { console.error("NavList.SubNav must be a child of a NavList.Item"); } if (depth > 3) { console.error("NavList.SubNav only supports four levels of nesting"); return null; } const t1 = depth + 1; let t2; if ($[0] !== t1) { t2 = { depth: t1 }; $[0] = t1; $[1] = t2; } else { t2 = $[1]; } let t3; if ($[2] !== buttonId || $[3] !== children || $[4] !== forwardedRef || $[5] !== subNavId) { t3 = /*#__PURE__*/jsx("ul", { className: classes.SubGroup, id: subNavId, "aria-labelledby": buttonId, ref: forwardedRef, children: children }); $[2] = buttonId; $[3] = children; $[4] = forwardedRef; $[5] = subNavId; $[6] = t3; } else { t3 = $[6]; } let t4; if ($[7] !== t2 || $[8] !== t3) { t4 = /*#__PURE__*/jsx(SubNavContext.Provider, { value: t2, children: t3 }); $[7] = t2; $[8] = t3; $[9] = t4; } else { t4 = $[9]; } return t4; }); SubNav.displayName = 'NavList.SubNav'; // ---------------------------------------------------------------------------- // NavList.LeadingVisual const LeadingVisual = ActionList.LeadingVisual; LeadingVisual.displayName = 'NavList.LeadingVisual'; // ---------------------------------------------------------------------------- // NavList.TrailingVisual const TrailingVisual = ActionList.TrailingVisual; TrailingVisual.displayName = 'NavList.TrailingVisual'; // ---------------------------------------------------------------------------- // NavList.Divider const Divider = ActionList.Divider; Divider.displayName = 'NavList.Divider'; // NavList.TrailingAction const TrailingAction = ActionList.TrailingAction; TrailingAction.displayName = 'NavList.TrailingAction'; // ---------------------------------------------------------------------------- // NavList.Group const Group = t0 => { const $ = c(11); let children; let props; let title; if ($[0] !== t0) { ({ title, children, ...props } = t0); $[0] = t0; $[1] = children; $[2] = props; $[3] = title; } else { children = $[1]; props = $[2]; title = $[3]; } let t1; if ($[4] === Symbol.for("react.memo_cache_sentinel")) { t1 = /*#__PURE__*/jsx(ActionList.Divider, {}); $[4] = t1; } else { t1 = $[4]; } let t2; if ($[5] !== title) { t2 = title ? /*#__PURE__*/jsx(ActionList.GroupHeading, { as: "h3", "data-component": "ActionList.GroupHeading", children: title }) : null; $[5] = title; $[6] = t2; } else { t2 = $[6]; } let t3; if ($[7] !== children || $[8] !== props || $[9] !== t2) { t3 = /*#__PURE__*/jsxs(Fragment, { children: [t1, /*#__PURE__*/jsxs(ActionList.Group, { ...props, children: [t2, children] })] }); $[7] = children; $[8] = props; $[9] = t2; $[10] = t3; } else { t3 = $[10]; } return t3; }; // ---------------------------------------------------------------------------- // NavList.GroupExpand const GroupExpand = /*#__PURE__*/React.forwardRef((t0, forwardedRef) => { const $ = c(23); let items; let props; let renderItem; let t1; let t2; if ($[0] !== t0) { ({ label: t1, pages: t2, items, renderItem, ...props } = t0); $[0] = t0; $[1] = items; $[2] = props; $[3] = renderItem; $[4] = t1; $[5] = t2; } else { items = $[1]; props = $[2]; renderItem = $[3]; t1 = $[4]; t2 = $[5]; } const label = t1 === undefined ? "Show more" : t1; const pages = t2 === undefined ? 0 : t2; const [currentPage, setCurrentPage] = React.useState(0); const groupId = useId(); const itemsPerPage = items.length / pages; const amountToShow = pages === 0 ? items.length : Math.ceil(itemsPerPage * currentPage); const focusTargetIndex = currentPage === 1 ? 0 : amountToShow - Math.floor(itemsPerPage); let t3; if ($[6] !== amountToShow || $[7] !== currentPage || $[8] !== focusTargetIndex || $[9] !== groupId || $[10] !== items || $[11] !== renderItem) { t3 = currentPage > 0 ? /*#__PURE__*/jsx(Fragment, { children: items.map((itemArr, index) => { const { text, trailingVisual: TrailingVisualIcon, leadingVisual: LeadingVisualIcon, trailingAction, ...rest } = itemArr; const { icon, label: actionLabel, ...actionProps } = trailingAction || {}; const focusTarget = index === focusTargetIndex ? groupId : undefined; if (index < amountToShow) { if (renderItem) { return renderItem({ ...itemArr, "data-expand-focus-target": focusTarget }); } return /*#__PURE__*/createElement(Item, { ...rest, key: index, "data-expand-focus-target": focusTarget }, LeadingVisualIcon ? /*#__PURE__*/jsx(LeadingVisual, { children: /*#__PURE__*/jsx(LeadingVisualIcon, {}) }) : null, text, TrailingVisualIcon ? /*#__PURE__*/jsx(TrailingVisual, { children: /*#__PURE__*/jsx(TrailingVisualIcon, {}) }) : null, trailingAction ? /*#__PURE__*/jsx(TrailingAction, { ...actionProps, icon: icon, label: actionLabel || "" }) : null); } }) }) : null; $[6] = amountToShow; $[7] = currentPage; $[8] = focusTargetIndex; $[9] = groupId; $[10] = items; $[11] = renderItem; $[12] = t3; } else { t3 = $[12]; } let t4; if ($[13] !== currentPage || $[14] !== forwardedRef || $[15] !== groupId || $[16] !== label || $[17] !== pages || $[18] !== props) { t4 = currentPage < pages || currentPage === 0 ? /*#__PURE__*/jsxs(ActionList.Item, { as: "button", "aria-expanded": "false", ref: forwardedRef, onSelect: () => { flushSync(() => { setCurrentPage(currentPage + 1); }); const focusTarget_0 = Array.from(document.querySelectorAll(`[data-expand-focus-target="${groupId}"]`)); if (focusTarget_0.length > 0) { focusTarget_0[focusTarget_0.length - 1].focus(); } }, ...props, children: [label, /*#__PURE__*/jsx(TrailingVisual, { children: /*#__PURE__*/jsx(PlusIcon, {}) })] }) : null; $[13] = currentPage; $[14] = forwardedRef; $[15] = groupId; $[16] = label; $[17] = pages; $[18] = props; $[19] = t4; } else { t4 = $[19]; } let t5; if ($[20] !== t3 || $[21] !== t4) { t5 = /*#__PURE__*/jsxs(Fragment, { children: [t3, t4] }); $[20] = t3; $[21] = t4; $[22] = t5; } else { t5 = $[22]; } return t5; }); // ---------------------------------------------------------------------------- // NavList.GroupHeading /** * This is an alternative to the `title` prop on `NavList.Group`. * It was primarily added to allow links in group headings. */ const GroupHeading = t0 => { const $ = c(10); let className; let rest; let t1; if ($[0] !== t0) { ({ as: t1, className, ...rest } = t0); $[0] = t0; $[1] = className; $[2] = rest; $[3] = t1; } else { className = $[1]; rest = $[2]; t1 = $[3]; } const as = t1 === undefined ? "h3" : t1; let t2; if ($[4] !== className) { t2 = clsx(navListClasses.GroupHeading, className); $[4] = className; $[5] = t2; } else { t2 = $[5]; } let t3; if ($[6] !== as || $[7] !== rest || $[8] !== t2) { t3 = /*#__PURE__*/jsx(ActionList.GroupHeading, { as: as, className: t2, "data-component": "NavList.GroupHeading", headingWrapElement: "li", ...rest }); $[6] = as; $[7] = rest; $[8] = t2; $[9] = t3; } else { t3 = $[9]; } return t3; }; // ---------------------------------------------------------------------------- // Export const NavList = Object.assign(Root, { Description: ActionList.Description, Item, SubNav, LeadingVisual, TrailingVisual, TrailingAction, Divider, Group, GroupExpand, GroupHeading }); function _temp(child) { return /*#__PURE__*/isValidElement(child) && (child.type === SubNav || isSlot(child, SubNav)); } function _temp2(child_0) { return /*#__PURE__*/isValidElement(child_0) ? child_0.type !== SubNav && child_0.type !== TrailingAction && !isSlot(child_0, SubNav) && !isSlot(child_0, TrailingAction) : true; } export { GroupExpand, NavList };