UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

649 lines (644 loc) • 16.6 kB
import { c } from 'react-compiler-runtime'; import React, { forwardRef, useRef, useState, useMemo, useCallback, useContext, useSyncExternalStore } from 'react'; import { KebabHorizontalIcon } from '@primer/octicons-react'; import { ActionList } from '../ActionList/index.js'; import { IconButton } from '../Button/IconButton.js'; import { useFocusZone } from '../hooks/useFocusZone.js'; import styles from './ActionBar.module.css.js'; import { clsx } from 'clsx'; import { createDescendantRegistry } from '../utils/descendant-registry.js'; import { jsx, jsxs } from 'react/jsx-runtime'; import { useMergedRefs } from '../hooks/useMergedRefs.js'; import { FocusKeys } from '@primer/behaviors'; import { ActionMenu } from '../ActionMenu/ActionMenu.js'; const ActionBarContext = /*#__PURE__*/React.createContext({ size: 'medium' }); /* small (28px), medium (32px), large (40px) */ const ActionBarItemsRegistry = createDescendantRegistry(); const FOCUSABLE_ITEM_SELECTOR = ':is(button, a, input, [tabindex]):not(:disabled):not([data-overflowing]):not([data-more-button-inactive])'; const renderMenuItem = (item, index) => { if (item.type === 'divider') { return /*#__PURE__*/jsx(ActionList.Divider, {}, index); } const { label, onClick, disabled, trailingVisual: TrailingIcon, leadingVisual: LeadingIcon, items, variant } = item; if (items && items.length > 0) { return /*#__PURE__*/jsxs(ActionMenu, { children: [/*#__PURE__*/jsx(ActionMenu.Anchor, { children: /*#__PURE__*/jsxs(ActionList.Item, { disabled: disabled, variant: variant, children: [LeadingIcon ? /*#__PURE__*/jsx(ActionList.LeadingVisual, { children: /*#__PURE__*/jsx(LeadingIcon, {}) }) : null, label, TrailingIcon ? /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: typeof TrailingIcon === 'string' ? /*#__PURE__*/jsx("span", { children: TrailingIcon }) : /*#__PURE__*/jsx(TrailingIcon, {}) }) : null] }) }), /*#__PURE__*/jsx(ActionMenu.Overlay, { children: /*#__PURE__*/jsx(ActionList, { children: items.map((subItem, subIndex) => renderMenuItem(subItem, subIndex)) }) })] }, label); } return /*#__PURE__*/jsxs(ActionList.Item, { onSelect: onClick, disabled: disabled, variant: variant, children: [LeadingIcon ? /*#__PURE__*/jsx(ActionList.LeadingVisual, { children: /*#__PURE__*/jsx(LeadingIcon, {}) }) : null, label, TrailingIcon ? /*#__PURE__*/jsx(ActionList.TrailingVisual, { children: typeof TrailingIcon === 'string' ? /*#__PURE__*/jsx("span", { children: TrailingIcon }) : /*#__PURE__*/jsx(TrailingIcon, {}) }) : null] }, label); }; renderMenuItem.displayName = "renderMenuItem"; const ActionBar = t0 => { const $ = c(40); const { size: t1, children, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, flush: t2, className, gap: t3 } = t0; const size = t1 === undefined ? "medium" : t1; const flush = t2 === undefined ? false : t2; const gap = t3 === undefined ? "condensed" : t3; const [childRegistry, setChildRegistry] = ActionBarItemsRegistry.useRegistryState(); let t4; if ($[0] !== childRegistry) { t4 = childRegistry && Array.from(childRegistry.entries()).filter(_temp); $[0] = childRegistry; $[1] = t4; } else { t4 = $[1]; } const overflowItems = t4; let t5; if ($[2] === Symbol.for("react.memo_cache_sentinel")) { t5 = { bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd, focusOutBehavior: "wrap", focusableElementFilter: _temp2 }; $[2] = t5; } else { t5 = $[2]; } let t6; if ($[3] !== overflowItems) { t6 = [overflowItems]; $[3] = overflowItems; $[4] = t6; } else { t6 = $[4]; } const { containerRef } = useFocusZone(t5, t6); let t7; if ($[5] !== size) { t7 = { size }; $[5] = size; $[6] = t7; } else { t7 = $[6]; } let t8; if ($[7] !== className) { t8 = clsx(className, styles.Nav); $[7] = className; $[8] = t8; } else { t8 = $[8]; } const t9 = containerRef; const t10 = overflowItems ? overflowItems.length > 0 : undefined; let t11; if ($[9] === Symbol.for("react.memo_cache_sentinel")) { t11 = /*#__PURE__*/jsx("div", { className: styles.OverflowSpacer }); $[9] = t11; } else { t11 = $[9]; } let t12; if ($[10] !== children || $[11] !== setChildRegistry) { t12 = /*#__PURE__*/jsxs("div", { className: styles.OverflowContainer, children: [t11, /*#__PURE__*/jsx(ActionBarItemsRegistry.Provider, { setRegistry: setChildRegistry, children: children })] }); $[10] = children; $[11] = setChildRegistry; $[12] = t12; } else { t12 = $[12]; } const t13 = `More ${ariaLabel} items ${overflowItems === null || overflowItems === void 0 ? void 0 : overflowItems.length}`; const t14 = overflowItems !== null && overflowItems !== void 0 && overflowItems.length ? undefined : true; let t15; if ($[13] !== size || $[14] !== t13 || $[15] !== t14) { t15 = /*#__PURE__*/jsx(ActionMenu.Anchor, { children: /*#__PURE__*/jsx(IconButton, { variant: "invisible", "aria-label": t13, icon: KebabHorizontalIcon, className: styles.MoreButton, "data-more-button-inactive": t14, size: size }) }); $[13] = size; $[14] = t13; $[15] = t14; $[16] = t15; } else { t15 = $[16]; } let t16; if ($[17] !== overflowItems) { t16 = overflowItems === null || overflowItems === void 0 ? void 0 : overflowItems.map(_temp4); $[17] = overflowItems; $[18] = t16; } else { t16 = $[18]; } let t17; if ($[19] !== t16) { t17 = /*#__PURE__*/jsx(ActionMenu.Overlay, { children: /*#__PURE__*/jsx(ActionList, { children: t16 }) }); $[19] = t16; $[20] = t17; } else { t17 = $[20]; } let t18; if ($[21] !== t15 || $[22] !== t17) { t18 = /*#__PURE__*/jsxs(ActionMenu, { children: [t15, t17] }); $[21] = t15; $[22] = t17; $[23] = t18; } else { t18 = $[23]; } let t19; if ($[24] !== ariaLabel || $[25] !== ariaLabelledBy || $[26] !== gap || $[27] !== size || $[28] !== t10 || $[29] !== t12 || $[30] !== t18 || $[31] !== t9) { t19 = /*#__PURE__*/jsxs("div", { ref: t9, role: "toolbar", className: styles.List, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-gap": gap, "data-size": size, "data-has-overflow": t10, children: [t12, t18] }); $[24] = ariaLabel; $[25] = ariaLabelledBy; $[26] = gap; $[27] = size; $[28] = t10; $[29] = t12; $[30] = t18; $[31] = t9; $[32] = t19; } else { t19 = $[32]; } let t20; if ($[33] !== flush || $[34] !== t19 || $[35] !== t8) { t20 = /*#__PURE__*/jsx("div", { className: t8, "data-component": "ActionBar", "data-flush": flush, children: t19 }); $[33] = flush; $[34] = t19; $[35] = t8; $[36] = t20; } else { t20 = $[36]; } let t21; if ($[37] !== t20 || $[38] !== t7) { t21 = /*#__PURE__*/jsx(ActionBarContext.Provider, { value: t7, children: t20 }); $[37] = t20; $[38] = t7; $[39] = t21; } else { t21 = $[39]; } return t21; }; function useActionBarItem(ref, registryProps) { var _useContext; const $ = c(8); const isGroupOverflowing = (_useContext = useContext(ActionBarGroupContext)) === null || _useContext === void 0 ? void 0 : _useContext.isOverflowing; const isInGroup = isGroupOverflowing !== undefined; let t0; if ($[0] !== isInGroup || $[1] !== ref) { t0 = onChange => { if (isInGroup) { return _temp5; } const observer = new IntersectionObserver(() => onChange(), { threshold: 0.75 }); if (ref.current) { observer.observe(ref.current); } return () => observer.disconnect(); }; $[0] = isInGroup; $[1] = ref; $[2] = t0; } else { t0 = $[2]; } const subscribeIntersectionObserver = t0; let t1; if ($[3] !== ref) { t1 = () => ref.current ? ref.current.offsetTop > 0 : false; $[3] = ref; $[4] = t1; } else { t1 = $[4]; } const isItemOverflowing = useSyncExternalStore(subscribeIntersectionObserver, t1, _temp6); const isOverflowing = isGroupOverflowing || isItemOverflowing; ActionBarItemsRegistry.useRegisterDescendant(isOverflowing ? registryProps : null); const t2 = isOverflowing ? "" : undefined; let t3; if ($[5] !== isOverflowing || $[6] !== t2) { t3 = { isOverflowing, dataOverflowingAttr: t2 }; $[5] = isOverflowing; $[6] = t2; $[7] = t3; } else { t3 = $[7]; } return t3; } function _temp6() { return false; } function _temp5() {} const ActionBarIconButton = /*#__PURE__*/forwardRef(({ disabled, onClick, ...props }, forwardedRef) => { const ref = useRef(null); const mergedRef = useMergedRefs(forwardedRef, ref); const { size } = React.useContext(ActionBarContext); const { ['aria-label']: ariaLabel, icon } = props; const { dataOverflowingAttr } = useActionBarItem(ref, useMemo(() => ({ type: 'action', label: ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : '', icon, disabled: !!disabled, onClick: onClick }), [ariaLabel, icon, disabled, onClick])); const clickHandler = useCallback(event => { if (disabled) return; onClick === null || onClick === void 0 ? void 0 : onClick(event); }, [disabled, onClick]); return /*#__PURE__*/jsx(IconButton, { "aria-disabled": disabled, ref: mergedRef, size: size, onClick: clickHandler, ...props, variant: "invisible", "data-overflowing": dataOverflowingAttr }); }); const ActionBarGroupContext = /*#__PURE__*/React.createContext(null); const ActionBarGroup = /*#__PURE__*/forwardRef((t0, forwardedRef) => { const $ = c(10); const { children } = t0; const backupRef = useRef(null); const ref = forwardedRef !== null && forwardedRef !== void 0 ? forwardedRef : backupRef; let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = { type: "group" }; $[0] = t1; } else { t1 = $[0]; } const { dataOverflowingAttr, isOverflowing } = useActionBarItem(ref, t1); let t2; if ($[1] !== isOverflowing) { t2 = { isOverflowing }; $[1] = isOverflowing; $[2] = t2; } else { t2 = $[2]; } let t3; if ($[3] !== children || $[4] !== dataOverflowingAttr || $[5] !== ref) { t3 = /*#__PURE__*/jsx("div", { className: styles.Group, "data-component": "ActionBar.Group", ref: ref, "data-overflowing": dataOverflowingAttr, children: children }); $[3] = children; $[4] = dataOverflowingAttr; $[5] = ref; $[6] = t3; } else { t3 = $[6]; } let t4; if ($[7] !== t2 || $[8] !== t3) { t4 = /*#__PURE__*/jsx(ActionBarGroupContext.Provider, { value: t2, children: t3 }); $[7] = t2; $[8] = t3; $[9] = t4; } else { t4 = $[9]; } return t4; }); const ActionBarMenu = /*#__PURE__*/forwardRef((t0, forwardedRef) => { const $ = c(31); let ariaLabel; let icon; let items; let overflowIcon; let props; let returnFocusRef; if ($[0] !== t0) { ({ "aria-label": ariaLabel, icon, overflowIcon, items, returnFocusRef, ...props } = t0); $[0] = t0; $[1] = ariaLabel; $[2] = icon; $[3] = items; $[4] = overflowIcon; $[5] = props; $[6] = returnFocusRef; } else { ariaLabel = $[1]; icon = $[2]; items = $[3]; overflowIcon = $[4]; props = $[5]; returnFocusRef = $[6]; } const backupRef = useRef(null); const ref = forwardedRef !== null && forwardedRef !== void 0 ? forwardedRef : backupRef; const [menuOpen, setMenuOpen] = useState(false); const t1 = overflowIcon ? overflowIcon : icon; let t2; if ($[7] !== ariaLabel || $[8] !== items || $[9] !== returnFocusRef || $[10] !== t1) { t2 = { type: "menu", label: ariaLabel, icon: t1, returnFocusRef, items }; $[7] = ariaLabel; $[8] = items; $[9] = returnFocusRef; $[10] = t1; $[11] = t2; } else { t2 = $[11]; } const { dataOverflowingAttr } = useActionBarItem(ref, t2); let t3; if ($[12] !== ariaLabel || $[13] !== dataOverflowingAttr || $[14] !== icon || $[15] !== props) { t3 = /*#__PURE__*/jsx(ActionMenu.Anchor, { children: /*#__PURE__*/jsx(IconButton, { variant: "invisible", "aria-label": ariaLabel, icon: icon, ...props, "data-overflowing": dataOverflowingAttr, "data-component": "ActionBar.Menu.IconButton" }) }); $[12] = ariaLabel; $[13] = dataOverflowingAttr; $[14] = icon; $[15] = props; $[16] = t3; } else { t3 = $[16]; } let t4; if ($[17] !== returnFocusRef) { t4 = returnFocusRef && { returnFocusRef }; $[17] = returnFocusRef; $[18] = t4; } else { t4 = $[18]; } let t5; if ($[19] !== items) { t5 = items.map(_temp7); $[19] = items; $[20] = t5; } else { t5 = $[20]; } let t6; if ($[21] !== t5) { t6 = /*#__PURE__*/jsx(ActionList, { children: t5 }); $[21] = t5; $[22] = t6; } else { t6 = $[22]; } let t7; if ($[23] !== t4 || $[24] !== t6) { t7 = /*#__PURE__*/jsx(ActionMenu.Overlay, { ...t4, children: t6 }); $[23] = t4; $[24] = t6; $[25] = t7; } else { t7 = $[25]; } let t8; if ($[26] !== menuOpen || $[27] !== ref || $[28] !== t3 || $[29] !== t7) { t8 = /*#__PURE__*/jsxs(ActionMenu, { anchorRef: ref, open: menuOpen, onOpenChange: setMenuOpen, children: [t3, t7] }); $[26] = menuOpen; $[27] = ref; $[28] = t3; $[29] = t7; $[30] = t8; } else { t8 = $[30]; } return t8; }); const VerticalDivider = () => { const $ = c(3); const ref = useRef(null); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = { type: "divider" }; $[0] = t0; } else { t0 = $[0]; } const { dataOverflowingAttr } = useActionBarItem(ref, t0); let t1; if ($[1] !== dataOverflowingAttr) { t1 = /*#__PURE__*/jsx("div", { ref: ref, "data-component": "ActionBar.VerticalDivider", "aria-hidden": "true", className: styles.Divider, "data-overflowing": dataOverflowingAttr }); $[1] = dataOverflowingAttr; $[2] = t1; } else { t1 = $[2]; } return t1; }; function _temp(entry) { return entry[1] !== null; } function _temp2(element) { return element.matches(FOCUSABLE_ITEM_SELECTOR); } function _temp3(item, index) { return renderMenuItem(item, index); } function _temp4(t0) { const [id, menuItem] = t0; if (menuItem.type === "divider") { return /*#__PURE__*/jsx(ActionList.Divider, {}, id); } if (menuItem.type === "action") { const { onClick, icon: Icon, label, disabled } = menuItem; return /*#__PURE__*/jsxs(ActionList.Item, { onSelect: event => { typeof onClick === "function" && onClick(event); }, disabled: disabled, children: [/*#__PURE__*/jsx(ActionList.LeadingVisual, { children: /*#__PURE__*/jsx(Icon, {}) }), label] }, label); } if (menuItem.type === "menu") { const menuItems = menuItem.items; const { icon: Icon_0, label: label_0, returnFocusRef } = menuItem; return /*#__PURE__*/jsxs(ActionMenu, { children: [/*#__PURE__*/jsx(ActionMenu.Anchor, { children: /*#__PURE__*/jsxs(ActionList.Item, { children: [Icon_0 !== "none" ? /*#__PURE__*/jsx(ActionList.LeadingVisual, { children: /*#__PURE__*/jsx(Icon_0, {}) }) : null, label_0] }) }), /*#__PURE__*/jsx(ActionMenu.Overlay, { ...(returnFocusRef && { returnFocusRef }), children: /*#__PURE__*/jsx(ActionList, { children: menuItems.map(_temp3) }) })] }, id); } } function _temp7(item, index) { return renderMenuItem(item, index); } export { ActionBar, ActionBarGroup, ActionBarIconButton, ActionBarMenu, VerticalDivider };