UNPKG

@stratakit/structures

Version:

Medium-sized component structures for the Strata design system

336 lines (335 loc) 11.6 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import * as React from "react"; import { Button } from "@ariakit/react/button"; import { Role } from "@ariakit/react/role"; import { Tooltip, VisuallyHidden } from "@stratakit/bricks"; import { Icon } from "@stratakit/foundations"; import { forwardRef, useEventHandlers, useMergedRefs, useSafeContext, useUnreactiveCallback } from "@stratakit/foundations/secret-internals"; import cx from "classnames"; import { createStore, useStore } from "zustand"; import { combine } from "zustand/middleware"; import { useWarnOnInteractiveDescendants } from "./~hooks.js"; import { useInit } from "./~utils.useInit.js"; function createNavigationRailStore(initialState) { return createStore( combine(initialState, (set) => ({ setExpanded: (expanded) => set({ expanded }) })) ); } const NavigationRailContext = React.createContext(void 0); function NavigationRailProvider(props) { const { defaultExpanded, expanded, setExpanded: setExpandedProp } = props; DEV: { if (expanded !== void 0 && !setExpandedProp) { throw new Error( "If you provide the `expanded` prop, you must also provide the `setExpanded` prop." ); } } const [store] = React.useState( () => createNavigationRailStore({ expanded: expanded ?? defaultExpanded }) ); const setExpanded = useUnreactiveCallback(setExpandedProp ?? (() => { })); React.useEffect( function synchronizeWithProps() { if (expanded === void 0) return; store.setState({ expanded, setExpanded }); }, [store, expanded, setExpanded] ); return /* @__PURE__ */ jsx(NavigationRailContext.Provider, { value: store, children: props.children }); } function useNavigationRailState(selectorFn) { const store = useSafeContext(NavigationRailContext); return useStore(store, selectorFn); } const NavigationRailRoot = forwardRef( (props, forwardedRef) => { useInit(); const { defaultExpanded = false, expanded, setExpanded, ...rest } = props; return /* @__PURE__ */ jsx( NavigationRailProvider, { defaultExpanded, expanded, setExpanded, children: /* @__PURE__ */ jsx(NavigationRailRootInner, { ...rest, ref: forwardedRef }) } ); } ); DEV: NavigationRailRoot.displayName = "NavigationRail.Root"; const NavigationRailRootInner = forwardRef( (props, forwardedRef) => { const expanded = useNavigationRailState((state) => state.expanded); return /* @__PURE__ */ jsx( Role.nav, { ...props, className: cx("\u{1F95D}NavigationRail", props.className), "data-_sk-expanded": expanded ? "true" : void 0, ref: forwardedRef } ); } ); DEV: NavigationRailRootInner.displayName = "NavigationRailRootInner"; const NavigationRailHeader = forwardRef( (props, forwardedRef) => { const expanded = useNavigationRailState((state) => state.expanded); return /* @__PURE__ */ jsx( Role.header, { ...props, className: cx("\u{1F95D}NavigationRailHeader", props.className), "data-_sk-expanded": expanded ? "true" : void 0, ref: forwardedRef } ); } ); DEV: NavigationRailHeader.displayName = "NavigationRail.Header"; const NavigationRailToggleButton = forwardRef((props, forwardedRef) => { const { label = "Expand navigation", ...rest } = props; const expanded = useNavigationRailState((state) => state.expanded); const setExpanded = useNavigationRailState((state) => state.setExpanded); return /* @__PURE__ */ jsxs( Button, { "aria-pressed": expanded ? "true" : "false", ...rest, className: cx("\u{1F95D}NavigationRailToggleButton", props.className), ref: forwardedRef, onClick: useEventHandlers(props.onClick, () => setExpanded(!expanded)), children: [ /* @__PURE__ */ jsx("svg", { width: "12", height: "12", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx( "path", { fill: "currentColor", d: "M5.405 2.845a.75.75 0 1 0-1.06 1.06L6.439 6 4.345 8.095a.75.75 0 0 0 1.06 1.06L8.03 6.53a.75.75 0 0 0 0-1.06L5.405 2.845Z" } ) }), /* @__PURE__ */ jsx(VisuallyHidden, { children: label }) ] } ); }); DEV: NavigationRailToggleButton.displayName = "NavigationRail.ToggleButton"; const NavigationRailContent = forwardRef( (props, forwardedRef) => { return /* @__PURE__ */ jsx( Role.div, { ...props, className: cx("\u{1F95D}NavigationRailContent", props.className), ref: forwardedRef } ); } ); DEV: NavigationRailContent.displayName = "NavigationRail.Content"; const NavigationRailList = forwardRef( (props, forwardedRef) => { return /* @__PURE__ */ jsx( Role, { role: "list", ...props, className: cx("\u{1F95D}NavigationRailList", props.className), ref: forwardedRef } ); } ); DEV: NavigationRailList.displayName = "NavigationRail.List"; const NavigationRailListItem = forwardRef( (props, forwardedRef) => { return /* @__PURE__ */ jsx( Role.div, { role: "listitem", ...props, className: cx("\u{1F95D}NavigationRailListItem", props.className), ref: forwardedRef } ); } ); DEV: NavigationRailListItem.displayName = "NavigationRail.ListItem"; function createNavigationRailItemActionStore() { return createStore((set) => ({ label: void 0, setLabel: (label) => set({ label }), suffix: void 0, setSuffix: (suffix) => set({ suffix }) })); } const NavigationRailItemActionContext = React.createContext(void 0); function NavigationRailItemActionProvider(props) { const [store] = React.useState(() => createNavigationRailItemActionStore()); return /* @__PURE__ */ jsx(NavigationRailItemActionContext.Provider, { value: store, children: props.children }); } function useNavigationRailItemActionState(selectorFn) { const store = useSafeContext(NavigationRailItemActionContext); return useStore(store, selectorFn); } const NavigationRailItemActionRoot = forwardRef((props, forwardedRef) => { return /* @__PURE__ */ jsx(NavigationRailItemActionProvider, { children: /* @__PURE__ */ jsx(NavigationRailItemActionRootInner, { ...props, ref: forwardedRef }) }); }); DEV: NavigationRailItemActionRoot.displayName = "NavigationRailItemActionRoot"; const NavigationRailItemActionRootInner = forwardRef((props, forwardedRef) => { const expanded = useNavigationRailState((state) => state.expanded); const label = useNavigationRailItemActionState((state) => state.label); const suffix = useNavigationRailItemActionState((state) => state.suffix); const action = /* @__PURE__ */ jsx( Role, { ...props, className: cx("\u{1F95D}NavigationRailItemAction", props.className), ref: forwardedRef } ); if (expanded) return action; return /* @__PURE__ */ jsx( Tooltip, { content: /* @__PURE__ */ jsxs(Fragment, { children: [ label, suffix ] }), placement: "right", type: "none", children: action } ); }); DEV: NavigationRailItemActionRootInner.displayName = "NavigationRailItemActionRootInner"; const NavigationRailItemAction = forwardRef((props, forwardedRef) => { const { label, icon, suffix, ...rest } = props; DEV: if (!label || !icon) throw new Error("label and icon are required"); return /* @__PURE__ */ jsxs(NavigationRailItemActionRoot, { ...rest, ref: forwardedRef, children: [ /* @__PURE__ */ jsx(NavigationRailItemActionIcon, { icon }), /* @__PURE__ */ jsx(NavigationRailItemActionLabel, { children: label }), suffix && /* @__PURE__ */ jsx(NavigationRailItemActionSuffix, { children: suffix }) ] }); }); DEV: NavigationRailItemAction.displayName = "NavigationRailItemAction"; const NavigationRailItemActionIcon = forwardRef((props, forwardedRef) => { const { icon, ...rest } = props; return /* @__PURE__ */ jsx( Icon, { size: "large", href: typeof icon === "string" ? icon : void 0, render: React.isValidElement(icon) ? icon : void 0, ...rest, ref: forwardedRef } ); }); DEV: NavigationRailItemActionIcon.displayName = "NavigationRailItemActionIcon"; const NavigationRailItemActionLabel = forwardRef((props, forwardedRef) => { const expanded = useNavigationRailState((state) => state.expanded); const setLabel = useNavigationRailItemActionState((state) => state.setLabel); React.useEffect(() => { setLabel(props.children); }, [props.children, setLabel]); return /* @__PURE__ */ jsx( Role.span, { ...props, className: cx("\u{1F95D}NavigationRailItemActionLabel", props.className), render: expanded ? props.render : /* @__PURE__ */ jsx(VisuallyHidden, { render: props.render }), ref: forwardedRef } ); }); DEV: NavigationRailItemActionLabel.displayName = "NavigationRailItemActionLabel"; const NavigationRailItemActionSuffix = forwardRef((props, forwardedRef) => { const expanded = useNavigationRailState((state) => state.expanded); const setSuffix = useNavigationRailItemActionState( (state) => state.setSuffix ); React.useEffect(() => { setSuffix(props.children); }, [props.children, setSuffix]); const ref = React.useRef(void 0); DEV: useWarnOnInteractiveDescendants( ref, `NavigationRail: interactive elements (e.g. buttons or links) should not be used in the "suffix" prop of "NavigationRail.Anchor" and "NavigationRail.Button". If you have a use case for trailing actions, please open an issue: https://github.com/iTwin/stratakit/issues` ); return /* @__PURE__ */ jsx( Role.span, { ...props, className: cx("\u{1F95D}NavigationRailItemActionSuffix", props.className), render: expanded ? props.render : /* @__PURE__ */ jsx(VisuallyHidden, { render: props.render }), ref: useMergedRefs(forwardedRef, ref) } ); }); DEV: NavigationRailItemActionSuffix.displayName = "NavigationRailItemActionSuffix"; const NavigationRailAnchor = forwardRef( (props, forwardedRef) => { const { label, icon, suffix, active, ...rest } = props; return /* @__PURE__ */ jsx( NavigationRailItemAction, { label, icon, suffix, "aria-current": active ? "true" : void 0, render: /* @__PURE__ */ jsx(Role.a, { ...rest, ref: forwardedRef }) } ); } ); DEV: NavigationRailAnchor.displayName = "NavigationRail.Anchor"; const NavigationRailButton = forwardRef( (props, forwardedRef) => { const { label, icon, suffix, ...rest } = props; return /* @__PURE__ */ jsx( NavigationRailItemAction, { label, icon, suffix, render: /* @__PURE__ */ jsx(Role.button, { ...rest, ref: forwardedRef }) } ); } ); DEV: NavigationRailButton.displayName = "NavigationRail.Button"; const NavigationRailFooter = forwardRef( (props, forwardedRef) => { return /* @__PURE__ */ jsx( Role, { render: /* @__PURE__ */ jsx("footer", {}), ...props, className: cx("\u{1F95D}NavigationRailFooter", props.className), ref: forwardedRef } ); } ); DEV: NavigationRailFooter.displayName = "NavigationRail.Footer"; export { NavigationRailAnchor as Anchor, NavigationRailButton as Button, NavigationRailContent as Content, NavigationRailFooter as Footer, NavigationRailHeader as Header, NavigationRailList as List, NavigationRailListItem as ListItem, NavigationRailRoot as Root, NavigationRailToggleButton as ToggleButton };