@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
JavaScript
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