@primer/react
Version:
An implementation of GitHub's Primer Design System using React
542 lines (541 loc) • 16.8 kB
JavaScript
import { useMergedRefs } from "../hooks/useMergedRefs.js";
import { IconButton } from "../Button/IconButton.js";
import { ButtonComponent } from "../Button/Button.js";
import { FocusKeys, useFocusZone } from "../hooks/useFocusZone.js";
import { ActionList } from "../ActionList/index.js";
import { ActionMenu } from "../ActionMenu/ActionMenu.js";
import { createDescendantRegistry } from "../utils/descendant-registry.js";
import ActionBar_module_css_default from "./ActionBar.module.css.js";
import { c } from "react-compiler-runtime";
import { clsx } from "clsx";
import { jsx, jsxs } from "react/jsx-runtime";
import React, { forwardRef, useCallback, useContext, useMemo, useRef, useState, useSyncExternalStore } from "react";
import { KebabHorizontalIcon } from "@primer/octicons-react";
//#region src/ActionBar/ActionBar.tsx
const ActionBarContext = /*#__PURE__*/ React.createContext({ size: "medium" });
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,
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,
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 === void 0 ? "medium" : t1;
const flush = t2 === void 0 ? false : t2;
const gap = t3 === void 0 ? "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, ActionBar_module_css_default.Nav);
$[7] = className;
$[8] = t8;
} else t8 = $[8];
const t9 = containerRef;
const t10 = overflowItems ? overflowItems.length > 0 : void 0;
let t11;
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
t11 = /*#__PURE__*/ jsx("div", { className: ActionBar_module_css_default.OverflowSpacer });
$[9] = t11;
} else t11 = $[9];
let t12;
if ($[10] !== children || $[11] !== setChildRegistry) {
t12 = /*#__PURE__*/ jsxs("div", {
className: ActionBar_module_css_default.OverflowContainer,
children: [t11, /*#__PURE__*/ jsx(ActionBarItemsRegistry.Provider, {
setRegistry: setChildRegistry,
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 ? void 0 : 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: ActionBar_module_css_default.MoreButton,
"data-more-button-inactive": t14,
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: ActionBar_module_css_default.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 !== void 0;
let t0;
if ($[0] !== isInGroup || $[1] !== ref) {
t0 = (onChange) => {
if (isInGroup) return _temp5;
const observer = new IntersectionObserver(() => onChange(), { threshold: .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 ? "" : void 0;
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
}), [
ariaLabel,
icon,
disabled,
onClick
]));
return /*#__PURE__*/ jsx(IconButton, {
"aria-disabled": disabled,
ref: mergedRef,
size,
onClick: useCallback((event) => {
if (disabled) return;
onClick === null || onClick === void 0 || onClick(event);
}, [disabled, onClick]),
...props,
variant: "invisible",
"data-overflowing": dataOverflowingAttr
});
});
const ActionBarButton = /*#__PURE__*/ forwardRef((t0, forwardedRef) => {
const $ = c(19);
let disabled;
let onClick;
let props;
if ($[0] !== t0) {
({disabled, onClick, ...props} = t0);
$[0] = t0;
$[1] = disabled;
$[2] = onClick;
$[3] = props;
} else {
disabled = $[1];
onClick = $[2];
props = $[3];
}
const ref = useRef(null);
const mergedRef = useMergedRefs(forwardedRef, ref);
const { size } = React.useContext(ActionBarContext);
const { children, leadingVisual } = props;
const t1 = typeof leadingVisual === "function" ? leadingVisual : void 0;
const t2 = !!disabled;
const t3 = onClick;
let t4;
if ($[4] !== children || $[5] !== t1 || $[6] !== t2 || $[7] !== t3) {
t4 = {
type: "action",
label: children,
icon: t1,
disabled: t2,
onClick: t3
};
$[4] = children;
$[5] = t1;
$[6] = t2;
$[7] = t3;
$[8] = t4;
} else t4 = $[8];
const { dataOverflowingAttr } = useActionBarItem(ref, t4);
let t5;
if ($[9] !== disabled || $[10] !== onClick) {
t5 = (event) => {
var _onClick;
if (disabled) return;
(_onClick = onClick) === null || _onClick === void 0 || _onClick(event);
};
$[9] = disabled;
$[10] = onClick;
$[11] = t5;
} else t5 = $[11];
const clickHandler = t5;
let t6;
if ($[12] !== clickHandler || $[13] !== dataOverflowingAttr || $[14] !== disabled || $[15] !== mergedRef || $[16] !== props || $[17] !== size) {
t6 = /*#__PURE__*/ jsx(ButtonComponent, {
"aria-disabled": disabled,
ref: mergedRef,
size,
onClick: clickHandler,
...props,
variant: "invisible",
"data-overflowing": dataOverflowingAttr
});
$[12] = clickHandler;
$[13] = dataOverflowingAttr;
$[14] = disabled;
$[15] = mergedRef;
$[16] = props;
$[17] = size;
$[18] = t6;
} else t6 = $[18];
return t6;
});
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: ActionBar_module_css_default.Group,
"data-component": "ActionBar.Group",
ref,
"data-overflowing": dataOverflowingAttr,
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,
...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,
"data-component": "ActionBar.VerticalDivider",
"aria-hidden": "true",
className: ActionBar_module_css_default.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,
children: [Icon ? /*#__PURE__*/ jsx(ActionList.LeadingVisual, { children: /*#__PURE__*/ jsx(Icon, {}) }) : null, label]
}, id);
}
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);
}
//#endregion
export { ActionBar, ActionBarButton, ActionBarGroup, ActionBarIconButton, ActionBarMenu, VerticalDivider };