UNPKG

@stratakit/structures

Version:

Medium-sized component structures for the Strata design system

489 lines (488 loc) 15.3 kB
import { c as _c } from "react-compiler-runtime"; import { jsx, jsxs } from "react/jsx-runtime"; import * as React from "react"; import { CompositeItem } from "@ariakit/react/composite"; import { Role } from "@ariakit/react/role"; import { Toolbar, ToolbarItem } from "@ariakit/react/toolbar"; import { IconButton } from "@stratakit/bricks"; import { GhostAligner, IconButtonPresentation } from "@stratakit/bricks/secret-internals"; import { Icon } from "@stratakit/foundations"; import { forwardRef, useEventHandlers } from "@stratakit/foundations/secret-internals"; import cx from "classnames"; import { ChevronDown, MoreHorizontal, StatusIcon } from "./~utils.icons.js"; import * as ListItem from "./~utils.ListItem.js"; import * as DropdownMenu from "./DropdownMenu.js"; const TreeItemErrorContext = React.createContext(void 0); const TreeItemInlineActionsContext = React.createContext(void 0); const TreeItemActionsContext = React.createContext(void 0); const TreeItemDecorationsContext = React.createContext(void 0); const TreeItemIconContext = React.createContext(void 0); const TreeItemDecorationIdContext = React.createContext(void 0); const TreeItemLabelContext = React.createContext(void 0); const TreeItemLabelIdContext = React.createContext(void 0); const TreeItemDescriptionContext = React.createContext(void 0); const TreeItemDescriptionIdContext = React.createContext(void 0); const TreeItem = React.memo(forwardRef((props, forwardedRef) => { const { selected, onSelectedChange, expanded, onExpandedChange, icon: _icon, unstable_decorations: _unstable_decorations, label: _label, description: _description, inlineActions: _inlineActions, actions: _actions, error: _error, onClick: onClickProp, onKeyDown: onKeyDownProp, ...rest } = props; const onExpanderClick = useEventHandlers(() => { if (expanded === void 0) return; onExpandedChange?.(!expanded); }); const handleClick = (event) => { if (selected !== void 0) { event.stopPropagation(); onSelectedChange?.(!selected); return; } if (expanded === void 0) return; event.stopPropagation(); onExpandedChange?.(!expanded); }; const handleKeyDown = (event_0) => { if (event_0.altKey || event_0.ctrlKey || event_0.metaKey || event_0.shiftKey) { return; } if (expanded === void 0) return; if (event_0.key === "ArrowRight" || event_0.key === "ArrowLeft") { event_0.preventDefault(); onExpandedChange?.(event_0.key === "ArrowRight"); } }; return /* @__PURE__ */ jsx(TreeItemRootProvider, { ...props, children: /* @__PURE__ */ jsx(TreeItemRoot, { ...rest, expanded, selected, onClick: useEventHandlers(onClickProp, handleClick), onKeyDown: useEventHandlers(onKeyDownProp, handleKeyDown), ref: forwardedRef, children: React.useMemo(() => /* @__PURE__ */ jsx(TreeItemNode, { onExpanderClick, expanded, selected }), [onExpanderClick, expanded, selected]) }) }); })); function TreeItemRootProvider(props) { const $ = _c(12); const { inlineActions, actions, label, description, icon: iconProp, unstable_decorations: decorations, error } = props; const labelId = React.useId(); const descriptionId = React.useId(); const decorationId = React.useId(); let t0; if ($[0] !== actions || $[1] !== decorationId || $[2] !== decorations || $[3] !== description || $[4] !== descriptionId || $[5] !== error || $[6] !== iconProp || $[7] !== inlineActions || $[8] !== label || $[9] !== labelId || $[10] !== props.children) { const icon = error ? jsx(StatusIcon, { tone: "attention" }) : iconProp; const hasDecoration = icon || decorations; t0 = jsx(TreeItemErrorContext.Provider, { value: error, children: jsx(TreeItemInlineActionsContext.Provider, { value: inlineActions, children: jsx(TreeItemActionsContext.Provider, { value: actions, children: jsx(TreeItemDisplayActionsMenuContext.Provider, { value: actions ? actions.length > 0 : false, children: jsx(TreeItemDecorationIdContext.Provider, { value: hasDecoration ? decorationId : void 0, children: jsx(TreeItemDecorationsContext.Provider, { value: decorations, children: jsx(TreeItemIconContext.Provider, { value: icon, children: jsx(TreeItemLabelIdContext.Provider, { value: labelId, children: jsx(TreeItemLabelContext.Provider, { value: label, children: jsx(TreeItemDescriptionContext.Provider, { value: description, children: jsx(TreeItemDescriptionIdContext.Provider, { value: description ? descriptionId : void 0, children: props.children }) }) }) }) }) }) }) }) }) }) }); $[0] = actions; $[1] = decorationId; $[2] = decorations; $[3] = description; $[4] = descriptionId; $[5] = error; $[6] = iconProp; $[7] = inlineActions; $[8] = label; $[9] = labelId; $[10] = props.children; $[11] = t0; } else { t0 = $[11]; } return t0; } const TreeItemRoot = React.memo(forwardRef((props, forwardedRef) => { const { style: styleProp, "aria-level": level, selected, expanded, ...rest } = props; const labelId = React.useContext(TreeItemLabelIdContext); const decorationId = React.useContext(TreeItemDecorationIdContext); const descriptionId = React.useContext(TreeItemDescriptionIdContext); const error = React.useContext(TreeItemErrorContext); const errorId = typeof error === "string" ? error : void 0; const describedBy = React.useMemo(() => { const ids = []; if (descriptionId) ids.push(descriptionId); if (decorationId) ids.push(decorationId); if (errorId) ids.push(errorId); return ids.length > 0 ? ids.join(" ") : void 0; }, [decorationId, descriptionId, errorId]); const style = React.useMemo(() => ({ ...styleProp, "--\u{1F95D}TreeItem-level": level }), [styleProp, level]); return /* @__PURE__ */ jsx(CompositeItem, { render: /* @__PURE__ */ jsx(Role, { ...rest, role: "treeitem", "aria-expanded": expanded, "aria-selected": selected, "aria-labelledby": labelId, "aria-describedby": describedBy, "aria-level": level, className: cx("\u{1F95D}TreeItem", props.className), style, ref: forwardedRef }), children: props.children }); })); function useRenderActions() { const $ = _c(5); const ref = React.useRef(null); const [renderActions, setRenderActions] = React.useState(false); let t0; let t1; if ($[0] !== renderActions) { t0 = () => { const el = ref.current; if (!el || renderActions) { return; } const observer = new IntersectionObserver((entries) => { for (const entry of entries) { if (!entry.isIntersecting) { continue; } React.startTransition(() => { setRenderActions(true); }); } }); observer.observe(el); return () => { observer.disconnect(); }; }; t1 = [renderActions]; $[0] = renderActions; $[1] = t0; $[2] = t1; } else { t0 = $[1]; t1 = $[2]; } React.useEffect(t0, t1); let t2; if ($[3] !== renderActions) { t2 = [ref, renderActions]; $[3] = renderActions; $[4] = t2; } else { t2 = $[4]; } return t2; } const TreeItemNode = React.memo((props) => { const $ = _c(7); const { expanded, selected, onExpanderClick } = props; const error = React.useContext(TreeItemErrorContext); const [ref, renderActions] = useRenderActions(); const t0 = error ? true : void 0; let t1; if ($[0] !== expanded || $[1] !== onExpanderClick || $[2] !== ref || $[3] !== renderActions || $[4] !== selected || $[5] !== t0) { t1 = jsxs(ListItem.Root, { "data-_sk-expanded": expanded, "data-_sk-selected": selected, "data-_sk-error": t0, className: "\u{1F95D}TreeItemNode", role: void 0, ref, children: [jsx(TreeItemDecorations, { onExpanderClick }), jsx(TreeItemContent, {}), jsx(TreeItemDescription, {}), renderActions && jsx(TreeItemActions, {})] }); $[0] = expanded; $[1] = onExpanderClick; $[2] = ref; $[3] = renderActions; $[4] = selected; $[5] = t0; $[6] = t1; } else { t1 = $[6]; } return t1; }); const TreeItemDecorations = React.memo((props) => { return /* @__PURE__ */ jsxs(ListItem.Decoration, { children: [/* @__PURE__ */ jsx(TreeItemExpander, { onClick: props.onExpanderClick }), /* @__PURE__ */ jsx(TreeItemDecoration, {})] }); }); function TreeItemDecoration() { const $ = _c(4); const decorationId = React.useContext(TreeItemDecorationIdContext); const decorations = React.useContext(TreeItemDecorationsContext); const icon = React.useContext(TreeItemIconContext); let t0; if ($[0] !== decorationId || $[1] !== decorations || $[2] !== icon) { t0 = icon || decorations ? jsx(Role, { className: "\u{1F95D}TreeItemDecoration", id: decorationId, render: React.isValidElement(icon) ? icon : typeof icon === "string" ? jsx(Icon, { href: icon }) : void 0, children: !icon ? decorations : null }) : null; $[0] = decorationId; $[1] = decorations; $[2] = icon; $[3] = t0; } else { t0 = $[3]; } return t0; } const TreeItemContent = React.memo(() => { const $ = _c(3); const labelId = React.useContext(TreeItemLabelIdContext); const label = React.useContext(TreeItemLabelContext); let t0; if ($[0] !== label || $[1] !== labelId) { t0 = jsx(ListItem.Content, { id: labelId, className: "\u{1F95D}TreeItemContent", children: label }); $[0] = label; $[1] = labelId; $[2] = t0; } else { t0 = $[2]; } return t0; }); const TreeItemDescription = React.memo(() => { const $ = _c(3); const description = React.useContext(TreeItemDescriptionContext); const descriptionId = React.useContext(TreeItemDescriptionIdContext); let t0; if ($[0] !== description || $[1] !== descriptionId) { t0 = description ? jsx(ListItem.Content, { id: descriptionId, className: "\u{1F95D}TreeItemDescription", children: description }) : void 0; $[0] = description; $[1] = descriptionId; $[2] = t0; } else { t0 = $[2]; } return t0; }); const TreeItemActions = React.memo(forwardRef((props, forwardedRef) => { return /* @__PURE__ */ jsxs(ListItem.Decoration, { ...props, onClick: useEventHandlers(props.onClick, (e) => e.stopPropagation()), onKeyDown: useEventHandlers(props.onKeyDown, (e_0) => e_0.stopPropagation()), className: cx("\u{1F95D}TreeItemActionsContainer", props.className), ref: forwardedRef, render: /* @__PURE__ */ jsx(Toolbar, { focusLoop: false }), children: [/* @__PURE__ */ jsx(TreeItemInlineActionsRenderer, {}), /* @__PURE__ */ jsx(TreeItemActionMenu, {})] }); })); function TreeItemInlineActionsRenderer() { const $ = _c(2); const actions = React.useContext(TreeItemInlineActionsContext) ?? []; let t0; if ($[0] !== actions) { t0 = actions.slice(0, 2); $[0] = actions; $[1] = t0; } else { t0 = $[1]; } const actionsToDisplay = t0; return actionsToDisplay; } const arrowKeys = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"]; const TreeItemActionsMenuContext = React.createContext(false); const TreeItemDisplayActionsMenuContext = React.createContext(false); const TreeItemActionMenu = React.memo(forwardRef((props, forwardedRef) => { const [open, _setOpen] = React.useState(false); const isArrowKeyPressed = React.useRef(false); const displayMenu = React.useContext(TreeItemDisplayActionsMenuContext); const setOpen = React.useCallback((value) => { if (value && !isArrowKeyPressed.current) { _setOpen(true); } else { _setOpen(false); } }, []); if (!displayMenu) return null; return /* @__PURE__ */ jsxs(DropdownMenu.Provider, { open, setOpen, placement: "right-start", children: [/* @__PURE__ */ jsx(DropdownMenu.Button, { ...props, onKeyDown: (e) => { if (arrowKeys.includes(e.key)) { isArrowKeyPressed.current = true; } queueMicrotask(() => { isArrowKeyPressed.current = false; }); }, render: /* @__PURE__ */ jsx(TreeItemInlineAction, { label: "More", icon: /* @__PURE__ */ jsx(MoreHorizontal, {}) }), ref: forwardedRef }), /* @__PURE__ */ jsx(TreeItemMenuActionsContent, {})] }); })); function TreeItemMenuActionsContent() { return /* @__PURE__ */ jsx(TreeItemActionsMenuContext.Provider, { value: true, children: /* @__PURE__ */ jsx(DropdownMenu.Content, { children: /* @__PURE__ */ jsx(TreeItemActionsRenderer, {}) }) }); } function TreeItemActionsRenderer() { const actions = React.useContext(TreeItemActionsContext) ?? []; return actions; } const TreeItemAction = React.memo(forwardRef((props, forwardedRef) => { const { label, icon, dot, ...rest } = props; const actionsMenuContext = React.useContext(TreeItemActionsMenuContext); if (actionsMenuContext) { return /* @__PURE__ */ jsx(DropdownMenu.Item, { ...rest, label, icon, unstable_dot: dot, ref: forwardedRef }); } return /* @__PURE__ */ jsx(TreeItemInlineAction, { ...props, ref: forwardedRef }); })); const TreeItemInlineAction = React.memo(forwardRef((props, forwardedRef) => { const error = React.useContext(TreeItemErrorContext); const generatedId = React.useId(); const { id = generatedId, visible = error ? true : void 0, // visible by default during error state label, icon, dot, ...rest } = props; return /* @__PURE__ */ jsx(IconButton, { id, label, icon, inert: visible === false ? "true" : void 0, ...rest, render: /* @__PURE__ */ jsx(ToolbarItem, { render: props.render }), dot, variant: "ghost", className: cx("\u{1F95D}TreeItemAction", props.className), "data-_sk-visible": visible, ref: forwardedRef }); })); const TreeItemExpander = forwardRef((props, forwardedRef) => { const descriptionId = React.useContext(TreeItemDescriptionIdContext); return /* @__PURE__ */ jsx(GhostAligner, { align: descriptionId ? "block" : void 0, children: /* @__PURE__ */ jsx(IconButtonPresentation, { "aria-hidden": "true", ...props, onClick: useEventHandlers(props.onClick, (e) => e.stopPropagation()), className: cx("\u{1F95D}TreeItemExpander", props.className), variant: "ghost", ref: forwardedRef, children: /* @__PURE__ */ jsx(ChevronDown, {}) }) }); }); export { TreeItemAction as Action, TreeItem as Root };