@primer/react
Version:
An implementation of GitHub's Primer Design System using React
649 lines (644 loc) • 16.6 kB
JavaScript
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 };