UNPKG

@primer/react-brand

Version:

Primer Brand is a GitHub's design system for creating React-based marketing websites and digital experiences.

343 lines (342 loc) • 11.9 kB
import e from "../node_modules/clsx/dist/clsx.js"; import { useId as t } from "../hooks/useId.js"; import { useProvidedRefOrCreate as n } from "../hooks/useRef.js"; import { Text as r } from "../Text/Text.js"; import { TriangleDownIcon as i, TriangleUpIcon as a } from "../node_modules/@primer/octicons-react/dist/index.esm.js"; import { Button as o } from "../Button/Button.js"; import { ThemeProvider as s } from "../ThemeProvider/ThemeProvider.js"; import { useWindowSize as c } from "../hooks/useWindowSize.js"; import { useOnClickOutside as l } from "../hooks/useOnClickOutside.js"; import { useKeyboardEscape as u } from "../hooks/useKeyboardEscape.js"; import { useFocusTrap as d } from "../hooks/useFocusTrap.js"; import { useContainsFocus as f } from "./useContainsFocus.js"; import '../packages/design-tokens/lib/design-tokens/css/tokens/functional/components/sub-nav/colors-with-modes-5gfdihUt.css';import '../packages/design-tokens/lib/design-tokens/css/tokens/functional/components/sub-nav/base-DIkvLW-s.css';/* empty css */ /* empty css */ import p from "./SubNav.module.js"; import m, { Children as h, createContext as g, forwardRef as _, isValidElement as v, memo as y, useCallback as b, useContext as x, useEffect as S, useMemo as C, useRef as w, useState as T } from "react"; import { jsx as E, jsxs as D } from "react/jsx-runtime"; import { createPortal as O } from "react-dom"; //#region src/SubNav/SubNav.tsx var k = { root: "SubNav-root", get button() { return `${this.root}-button`; }, get overlay() { return `${this.root}-overlay`; }, get link() { return `${this.root}-link`; }, get heading() { return `${this.root}-heading`; }, get action() { return `${this.root}-action`; }, get subMenu() { return `${this.root}-sub-menu`; } }, A = ["dropdown", "anchor"], j = g(void 0), M = () => { let e = x(j); if (!e) throw Error("useSubNavContext must be used within a SubNavProvider"); return e; }; function N({ children: t }) { let n = m.useRef(null), r = m.useRef(null), i = C(() => ({ portalRef: r }), []); return S(() => { let e = n.current, t = new IntersectionObserver(([e]) => { e.target.classList.toggle(p["SubNav__anchor-menu-outer-container--stuck"], e.intersectionRatio < 1); }, { threshold: [1] }); return e && t.observe(e), () => { e && t.unobserve(e); }; }, []), /* @__PURE__ */ D(j.Provider, { value: i, children: [t, /* @__PURE__ */ E("div", { className: p["SubNav__anchor-menu-outer-container"], ref: n, children: /* @__PURE__ */ E("div", { className: e(p["SubNav__anchor-menu-container"]), ref: r }) })] }); } var P = y(_(({ id: o, children: s, className: f, "data-testid": g, fullWidth: _, hasShadow: y }, x) => { let O = n(x), A = m.useRef(null), j = m.useRef(null), M = m.useRef(null), P = w(null), [L, V] = T(!1), H = t(), [ee, te] = T(!1), { isLarge: U } = c(); d({ containerRef: A, disabled: !L }); let W = h.toArray(s), G = b(() => { U || V(!1); }, [U]), K = b(() => { U || V((e) => !e); }, [U]); l(A, G), u(G), S(() => { let e = j.current, t = () => { if (e) { let t = e.getBoundingClientRect().top; e.style.setProperty("--subnav-available-height", `${window.innerHeight - t}px`); } }; return L && !U ? (document.body.style.overflow = "hidden", t(), window.addEventListener("resize", t)) : (document.body.style.overflow = "auto", e && e.style.removeProperty("--subnav-available-height")), () => { document.body.style.overflow = "auto", window.removeEventListener("resize", t), e && e.style.removeProperty("--subnav-available-height"); }; }, [L, U]); let q = W.find((e) => v(e) && !!e.props["aria-current"]); S(() => { te(W.some((e) => { if (v(e) && e.type === R) { let t = h.toArray(e.props.children)[1]; if (v(t) && t.type === z && t.props.variant === "anchor") return !0; } return !1; })); }, [W]); let { heading: J, subheading: Y, links: ne, action: re } = W.reduce((e, t) => { if (v(t)) if (t.type === F) e.heading = t; else if (t.type === I) e.subheading = t; else if (t.type === R) { let n = t, [r, i] = h.toArray(n.props.children), a = v(i) && i.type === z && i.props.variant === "anchor"; e.links.push(m.cloneElement(n, { ...a ? { children: [r] } : {}, onClick: n.props["aria-current"] ? G : n.props.onClick })); } else t.type === B && (e.action = t); return e; }, { heading: void 0, subheading: void 0, links: [], action: void 0 }), ie = q ? h.toArray(q.props.children) : [], X = ie[0], Z = ie[1], ae = v(Z) && Z.type === z && Z.props.variant === "anchor" ? Z : null, Q = v(Y) && Y.type === I && !!Y.props["aria-current"], $ = Y ? X : null, oe = C(() => /* @__PURE__ */ E("div", { className: e(p["SubNav__overlay-toggle"], L && p["SubNav__overlay-toggle--open"]), children: /* @__PURE__ */ E("span", { className: e(p["SubNav__overlay-toggle-content"], !$ && p["SubNav__overlay-toggle-content--end"]), children: /* @__PURE__ */ D("button", { ref: P, className: p["SubNav__overlay-toggle-label"], "data-testid": k.button, onClick: L ? G : K, "aria-expanded": L ? "true" : "false", "aria-controls": H, "aria-label": X ? `Navigation menu. Current page: ${X}` : "Navigation menu", children: [$ && /* @__PURE__ */ E(r, { as: "span", size: "100", children: $ }), E(L ? a : i, { className: p["SubNav__overlay-toggle-icon"], size: 13 })] }) }) }), [ X, G, K, H, L, $ ]); return /* @__PURE__ */ E("div", { ref: O, className: e(p.SubNav__container, Y && p["SubNav--has-sub-heading"], Y && Q && p["SubNav--subHeadingActive"], ee && p["SubNav__container--with-anchor-nav"]), children: /* @__PURE__ */ E(N, { children: /* @__PURE__ */ E("nav", { ref: j, id: o, className: e(p.SubNav, L && p["SubNav--open"], y && p["SubNav--has-shadow"], _ && p["SubNav--full-width"], f), "data-testid": g || k.root, children: /* @__PURE__ */ D("div", { ref: A, className: p["SubNav--header-container-outer"], children: [ /* @__PURE__ */ D("div", { className: p["SubNav__header-container"], children: [ J && /* @__PURE__ */ E("div", { className: p["SubNav__heading-container"], children: J }), Y && /* @__PURE__ */ E("div", { className: e(p["SubNav__heading-container"], p["SubNav__subheading-container"], Q && p["SubNav__subheading-container-active"]), children: Y }), !U && (!Y || Q) && oe, ae && ae ] }), !U && Y && !Q && oe, ne.length > 0 && /* @__PURE__ */ D("ul", { ref: M, id: H, className: e(p["SubNav__links-overlay"], L && p["SubNav__links-overlay--open"]), "data-testid": k.overlay, children: [ne, re && /* @__PURE__ */ E("li", { className: p["SubNav__action-container"], children: re })] }) ] }) }) }) }); })), F = ({ href: t, children: n, className: r, "data-testid": i, ...a }) => /* @__PURE__ */ E("a", { href: t, className: e(p.SubNav__heading, r), "data-testid": k.heading || i, ...a, children: n }), I = ({ href: t, children: n, className: r, "data-testid": i, ...a }) => /* @__PURE__ */ E("a", { href: t, className: e(p.SubNav__heading, p.SubNav__link, p.SubNav__subHeading, r), "data-testid": k.heading || i, ...a, children: n }), L = _(({ children: a, href: o, "aria-current": s, "data-testid": l, className: d, _subMenuVariant: m, variant: h, ...g }, _) => { let v = t(), { isLarge: y } = c(), [x, C] = T(!1), O = n(_), A = w(null); f(O, (e) => { e || C(!1); }); let j = b(() => C(!0), []), M = b(() => C(!1), []), N = b(() => C((e) => !e), []), P = !!s && s !== "false"; u(M), S(() => { A.current && A.current.toggleAttribute("inert", y && !x); }, [y, x]); let [F, I] = a; return /* @__PURE__ */ D("div", { className: e(p["SubNav__link--has-sub-menu"], x && p["SubNav__link--expanded"], P && p["SubNav__link--has-active-sub-menu"]), "data-testid": l || k.subMenu, ref: O, onMouseOver: j, onMouseOut: M, onFocus: () => null, onBlur: () => null, children: [ /* @__PURE__ */ E("a", { href: o, className: e(p.SubNav__link, s && p["SubNav__link--active"], d), "aria-current": s, ...g, children: /* @__PURE__ */ E(r, { as: "span", size: "100", weight: "medium", className: p["SubNav__link-label"], children: F }) }), y && /* @__PURE__ */ E("button", { className: p["SubNav__sub-menu-toggle"], onClick: N, "aria-expanded": x ? "true" : "false", "aria-controls": v, "aria-label": `${F?.toString().trim()} submenu`, children: /* @__PURE__ */ E(i, { className: p["SubNav__sub-menu-icon"], size: 16 }) }), /* @__PURE__ */ E("div", { id: v, className: p["SubNav__sub-menu-children"], ref: A, children: I }) ] }); }), R = _((t, n) => { let [i, a] = T(!1), o = h.toArray(t.children), s = o.some((e) => { if (v(e)) return e.type === z; }); if (S(() => { if (s) return; let e = t.href.replace("#", ""), n = document.getElementById(e); if (!n) return; let r = new IntersectionObserver(([e]) => { a(e.isIntersecting); }, { threshold: 0, root: null, rootMargin: "0px 0px -100%" }); return r.observe(n), () => r.disconnect(); }, [s, t.href]), s) { let e = o.some((e) => v(e) && e.type === z && e.props.variant === "anchor"); return /* @__PURE__ */ E("li", { children: /* @__PURE__ */ E(L, { ref: n, ...t, _subMenuVariant: e ? "anchor" : void 0 }) }); } let { children: c, href: l, "aria-current": u, "data-testid": d, variant: f, className: m, ...g } = t; return /* @__PURE__ */ E("li", { children: /* @__PURE__ */ E("a", { href: l, className: e(p.SubNav__link, u && p["SubNav__link--active"], i && p["SubNav__link--is-in-view"], m), "aria-current": u, "data-testid": d || k.link, ref: n, ...g, children: /* @__PURE__ */ E(r, { as: "span", size: "100", weight: "medium", className: p["SubNav__link-label"], children: c }) }) }); }); function z({ children: t, className: n, variant: r = "dropdown", ...i }) { let a = m.useContext(j), o = w(null), { isLarge: l } = c(); return S(() => { let e = (e) => { o.current && !o.current.contains(e.target) || e.target instanceof HTMLAnchorElement || e.stopPropagation(); }; return r === "anchor" && document.addEventListener("click", e, !0), () => { document.removeEventListener("click", e, !0); }; }, [r]), r === "anchor" && a?.portalRef.current ? O(/* @__PURE__ */ E("nav", { ref: o, className: e(p["SubNav__sub-menu"], p["SubNav__sub-menu--anchor"], n), role: "navigation", "aria-label": "Sub navigation", children: /* @__PURE__ */ E("ul", { className: p["SubNav__sub-menu-list"], ...i, children: m.Children.map(t, (e) => v(e) && e.type === R ? m.cloneElement(e, { onClick: (t) => { e.props.onClick?.(t); } }) : null) }) }), a.portalRef.current) : /* @__PURE__ */ E(l ? s : m.Fragment, { ...l ? { colorMode: "light" } : {}, children: /* @__PURE__ */ E("ul", { className: e(p["SubNav__sub-menu"], p[`SubNav__sub-menu--${r}`], n), ...i, children: t }) }); } function B({ children: e, href: t, variant: n = "primary", size: r = "small", ...i }) { return /* @__PURE__ */ E(o, { className: p.SubNav__action, as: "a", href: t, variant: n, "data-testid": k.action, size: r, ...i, children: e }); } var V = Object.assign(P, { Heading: F, SubHeading: I, Link: R, Action: B, SubMenu: z, testIds: k }); //#endregion export { V as SubNav, A as SubNavSubMenuVariants, M as useSubNavContext }; //# sourceMappingURL=SubNav.js.map