UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

745 lines (722 loc) • 18.8 kB
import { c } from 'react-compiler-runtime'; import { PlusIcon, ChevronDownIcon } from '@primer/octicons-react'; import React, { createElement, isValidElement } from 'react'; import { ActionList } from '../ActionList/index.js'; import { SubItem } from '../ActionList/Item.js'; import { ActionListContainerContext } from '../ActionList/ActionListContainerContext.js'; import '@styled-system/css'; import merge from 'deepmerge'; import { defaultSxProp } from '../utils/defaultSxProp.js'; import { useId } from '../hooks/useId.js'; import useIsomorphicLayoutEffect from '../utils/useIsomorphicLayoutEffect.js'; import classes from '../ActionList/ActionList.module.css.js'; import { flushSync } from 'react-dom'; import { BoxWithFallback } from '../internal/components/BoxWithFallback.js'; import { jsx, Fragment, jsxs } from 'react/jsx-runtime'; import Box from '../Box/Box.js'; 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(BoxWithFallback, { as: "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(31); let ariaCurrent; let children; let defaultOpen; let props; let t1; if ($[0] !== t0) { ({ "aria-current": ariaCurrent, children, defaultOpen, sx: t1, ...props } = t0); $[0] = t0; $[1] = ariaCurrent; $[2] = children; $[3] = defaultOpen; $[4] = props; $[5] = t1; } else { ariaCurrent = $[1]; children = $[2]; defaultOpen = $[3]; props = $[4]; t1 = $[5]; } const sxProp = t1 === undefined ? defaultSxProp : t1; const { depth } = React.useContext(SubNavContext); let childrenWithoutSubNavOrTrailingAction; let subNav; let t2; if ($[6] !== children || $[7] !== defaultOpen) { subNav = React.Children.toArray(children).find(_temp); let t3; if ($[11] !== children) { t3 = React.Children.toArray(children).filter(_temp2); $[11] = children; $[12] = t3; } else { t3 = $[12]; } childrenWithoutSubNavOrTrailingAction = t3; if (! /*#__PURE__*/isValidElement(subNav) && defaultOpen) { console.error("NavList.Item must have a NavList.SubNav to use defaultOpen."); } t2 = subNav && /*#__PURE__*/isValidElement(subNav); $[6] = children; $[7] = defaultOpen; $[8] = childrenWithoutSubNavOrTrailingAction; $[9] = subNav; $[10] = t2; } else { childrenWithoutSubNavOrTrailingAction = $[8]; subNav = $[9]; t2 = $[10]; } if (t2) { let t3; if ($[13] !== depth) { t3 = { "--subitem-depth": depth }; $[13] = depth; $[14] = t3; } else { t3 = $[14]; } const t4 = t3; let t5; if ($[15] !== childrenWithoutSubNavOrTrailingAction || $[16] !== defaultOpen || $[17] !== depth || $[18] !== subNav || $[19] !== sxProp || $[20] !== t4) { t5 = /*#__PURE__*/jsx(ItemWithSubNav, { subNav: subNav, depth: depth, defaultOpen: defaultOpen, sx: sxProp, style: t4, children: childrenWithoutSubNavOrTrailingAction }); $[15] = childrenWithoutSubNavOrTrailingAction; $[16] = defaultOpen; $[17] = depth; $[18] = subNav; $[19] = sxProp; $[20] = t4; $[21] = t5; } else { t5 = $[21]; } return t5; } const t3 = Boolean(ariaCurrent) && ariaCurrent !== "false"; let t4; if ($[22] !== depth) { t4 = { "--subitem-depth": depth }; $[22] = depth; $[23] = t4; } else { t4 = $[23]; } const t5 = t4; let t6; if ($[24] !== ariaCurrent || $[25] !== children || $[26] !== props || $[27] !== ref || $[28] !== t3 || $[29] !== t5) { t6 = /*#__PURE__*/jsx(ActionList.LinkItem, { ref: ref, "aria-current": ariaCurrent, active: t3, style: t5, ...props, children: children }); $[24] = ariaCurrent; $[25] = children; $[26] = props; $[27] = ref; $[28] = t3; $[29] = t5; $[30] = t6; } else { t6 = $[30]; } return t6; }); Item.displayName = 'NavList.Item'; // ---------------------------------------------------------------------------- // ItemWithSubNav (internal) const ItemWithSubNavContext = /*#__PURE__*/React.createContext({ buttonId: '', subNavId: '', isOpen: false }); function ItemWithSubNav({ children, subNav, depth: _depth, defaultOpen, style, sx: sxProp = defaultSxProp }) { var _ref; 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); useIsomorphicLayoutEffect(() => { if (subNavRef.current) { // Check if SubNav contains current item // valid values: page, step, location, date, time, true and false const currentItem = subNavRef.current.querySelector('[aria-current]:not([aria-current=false])'); if (currentItem) { setContainsCurrentItem(true); setIsOpen(true); } } }, [subNav, buttonId]); if (sxProp !== defaultSxProp) { return /*#__PURE__*/jsx(ItemWithSubNavContext.Provider, { value: { buttonId, subNavId, isOpen }, children: /*#__PURE__*/jsxs(ActionList.Item, { id: buttonId, "aria-expanded": isOpen, "aria-controls": subNavId, active: !isOpen && containsCurrentItem, onSelect: () => setIsOpen(open => !open), style: style, sx: sxProp, children: [children, /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: /*#__PURE__*/jsx(ChevronDownIcon, { className: classes.ExpandIcon }) }), /*#__PURE__*/jsx(SubItem, { children: /*#__PURE__*/React.cloneElement(subNav, { ref: subNavRef }) })] }) }); } return /*#__PURE__*/jsx(ItemWithSubNavContext.Provider, { value: { buttonId, subNavId, isOpen }, children: /*#__PURE__*/jsxs(ActionList.Item, { id: buttonId, "aria-expanded": isOpen, "aria-controls": subNavId, active: !isOpen && containsCurrentItem, onSelect: () => setIsOpen(open_0 => !open_0), style: style, children: [children, /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: /*#__PURE__*/jsx(ChevronDownIcon, { className: classes.ExpandIcon }) }), /*#__PURE__*/jsx(SubItem, { children: /*#__PURE__*/React.cloneElement(subNav, { ref: subNavRef }) })] }) }); } ItemWithSubNav.displayName = "ItemWithSubNav"; // ---------------------------------------------------------------------------- // NavList.SubNav 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(21); const { children, sx: t1 } = t0; const sxProp = t1 === undefined ? defaultSxProp : t1; 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; } if (sxProp !== defaultSxProp) { const t2 = depth + 1; let t3; if ($[0] !== t2) { t3 = { depth: t2 }; $[0] = t2; $[1] = t3; } else { t3 = $[1]; } let t4; if ($[2] !== buttonId || $[3] !== children || $[4] !== forwardedRef || $[5] !== subNavId || $[6] !== sxProp) { t4 = /*#__PURE__*/jsx(Box, { as: "ul", id: subNavId, "aria-labelledby": buttonId, className: classes.SubGroup, ref: forwardedRef, sx: sxProp, children: children }); $[2] = buttonId; $[3] = children; $[4] = forwardedRef; $[5] = subNavId; $[6] = sxProp; $[7] = t4; } else { t4 = $[7]; } let t5; if ($[8] !== t3 || $[9] !== t4) { t5 = /*#__PURE__*/jsx(SubNavContext.Provider, { value: t3, children: t4 }); $[8] = t3; $[9] = t4; $[10] = t5; } else { t5 = $[10]; } return t5; } const t2 = depth + 1; let t3; if ($[11] !== t2) { t3 = { depth: t2 }; $[11] = t2; $[12] = t3; } else { t3 = $[12]; } let t4; if ($[13] !== buttonId || $[14] !== children || $[15] !== forwardedRef || $[16] !== subNavId) { t4 = /*#__PURE__*/jsx("ul", { className: classes.SubGroup, id: subNavId, "aria-labelledby": buttonId, ref: forwardedRef, children: children }); $[13] = buttonId; $[14] = children; $[15] = forwardedRef; $[16] = subNavId; $[17] = t4; } else { t4 = $[17]; } let t5; if ($[18] !== t3 || $[19] !== t4) { t5 = /*#__PURE__*/jsx(SubNavContext.Provider, { value: t3, children: t4 }); $[18] = t3; $[19] = t4; $[20] = t5; } else { t5 = $[20]; } return t5; }); 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 defaultSx = {}; const Group = t0 => { const $ = c(18); let children; let props; let t1; let title; if ($[0] !== t0) { ({ title, children, sx: t1, ...props } = t0); $[0] = t0; $[1] = children; $[2] = props; $[3] = t1; $[4] = title; } else { children = $[1]; props = $[2]; t1 = $[3]; title = $[4]; } const sxProp = t1 === undefined ? defaultSx : t1; if (sxProp !== defaultSx) { let t2; if ($[5] !== title) { t2 = title ? /*#__PURE__*/jsx(ActionList.GroupHeading, { children: title }) : null; $[5] = title; $[6] = t2; } else { t2 = $[6]; } let t3; if ($[7] !== children || $[8] !== sxProp || $[9] !== t2) { t3 = /*#__PURE__*/jsxs(Box, { sx: sxProp, as: "li", "data-component": "ActionList.Group", children: [t2, children] }); $[7] = children; $[8] = sxProp; $[9] = t2; $[10] = t3; } else { t3 = $[10]; } return t3; } let t2; if ($[11] === Symbol.for("react.memo_cache_sentinel")) { t2 = /*#__PURE__*/jsx(ActionList.Divider, {}); $[11] = t2; } else { t2 = $[11]; } let t3; if ($[12] !== title) { t3 = title ? /*#__PURE__*/jsx(ActionList.GroupHeading, { as: "h3", "data-component": "ActionList.GroupHeading", children: title }) : null; $[12] = title; $[13] = t3; } else { t3 = $[13]; } let t4; if ($[14] !== children || $[15] !== props || $[16] !== t3) { t4 = /*#__PURE__*/jsxs(Fragment, { children: [t2, /*#__PURE__*/jsxs(ActionList.Group, { ...props, children: [t3, children] })] }); $[14] = children; $[15] = props; $[16] = t3; $[17] = t4; } else { t4 = $[17]; } return t4; }; // ---------------------------------------------------------------------------- // 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 rest; let t1; let t2; if ($[0] !== t0) { ({ as: t1, sx: t2, ...rest } = t0); $[0] = t0; $[1] = rest; $[2] = t1; $[3] = t2; } else { rest = $[1]; t1 = $[2]; t2 = $[3]; } const as = t1 === undefined ? "h3" : t1; const sxProp = t2 === undefined ? defaultSxProp : t2; let t3; if ($[4] !== sxProp) { t3 = merge({ "> a {": { color: "var(--fgColor-default)", textDecoration: "inherit", ":hover": { textDecoration: "underline" } } }, sxProp); $[4] = sxProp; $[5] = t3; } else { t3 = $[5]; } let t4; if ($[6] !== as || $[7] !== rest || $[8] !== t3) { t4 = /*#__PURE__*/jsx(ActionList.GroupHeading, { as: as, sx: t3, "data-component": "NavList.GroupHeading", headingWrapElement: "li", ...rest }); $[6] = as; $[7] = rest; $[8] = t3; $[9] = t4; } else { t4 = $[9]; } return t4; }; // ---------------------------------------------------------------------------- // Export const NavList = Object.assign(Root, { Item, SubNav, LeadingVisual, TrailingVisual, TrailingAction, Divider, Group, GroupExpand, GroupHeading }); function _temp(child) { return /*#__PURE__*/isValidElement(child) && child.type === SubNav; } function _temp2(child_0) { return /*#__PURE__*/isValidElement(child_0) ? child_0.type !== SubNav && child_0.type !== TrailingAction : true; } export { GroupExpand, NavList };