@stratakit/structures
Version:
Medium-sized component structures for the Strata design system
336 lines (335 loc) • 11.6 kB
JavaScript
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
};