@nanisoft/prism-ui 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/blocks/site-footer/site-footer.d.ts +23 -8
- package/dist/blocks/site-footer/site-footer.d.ts.map +1 -1
- package/dist/blocks/site-footer/site-footer.js +10 -4
- package/dist/blocks/site-navbar/index.d.ts +1 -1
- package/dist/blocks/site-navbar/index.d.ts.map +1 -1
- package/dist/blocks/site-navbar/mobile-nav.d.ts +25 -4
- package/dist/blocks/site-navbar/mobile-nav.d.ts.map +1 -1
- package/dist/blocks/site-navbar/mobile-nav.js +11 -4
- package/dist/blocks/site-navbar/nav-menu.d.ts +50 -0
- package/dist/blocks/site-navbar/nav-menu.d.ts.map +1 -0
- package/dist/blocks/site-navbar/nav-menu.js +44 -0
- package/dist/blocks/site-navbar/navbar-controls.d.ts +9 -3
- package/dist/blocks/site-navbar/navbar-controls.d.ts.map +1 -1
- package/dist/blocks/site-navbar/site-navbar.d.ts +54 -13
- package/dist/blocks/site-navbar/site-navbar.d.ts.map +1 -1
- package/dist/blocks/site-navbar/site-navbar.js +45 -10
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/calendar.js +7 -2
- package/dist/components/ui/mini-calendar.js +1 -1
- package/dist/components/ui/product-mark.d.ts +26 -7
- package/dist/components/ui/product-mark.d.ts.map +1 -1
- package/dist/components/ui/product-mark.js +6 -4
- package/dist/components/ui/pulse-series.d.ts.map +1 -1
- package/dist/components/ui/pulse-series.js +1 -1
- package/dist/components/ui/sparkline.d.ts.map +1 -1
- package/dist/components/ui/sparkline.js +1 -2
- package/dist/styles.css +173 -0
- package/dist/theming/index.d.ts +1 -1
- package/dist/theming/index.d.ts.map +1 -1
- package/dist/theming/index.js +1 -1
- package/package.json +2 -2
|
@@ -66,12 +66,26 @@ export type SiteFooterSocial = {
|
|
|
66
66
|
*/
|
|
67
67
|
export type SiteFooterProps = {
|
|
68
68
|
/**
|
|
69
|
-
* The product this footer belongs to: the id, name and
|
|
70
|
-
* is drawn from. The footer renders the mark rather than taking
|
|
71
|
-
* because the brand lockup is the one part of a footer that is never
|
|
72
|
-
* product-specific and every consumer would otherwise reimplement it
|
|
69
|
+
* The product this footer belongs to: the id, name, pack and drawn mark its
|
|
70
|
+
* `ProductMark` is drawn from. The footer renders the mark rather than taking
|
|
71
|
+
* a slot, because the brand lockup is the one part of a footer that is never
|
|
72
|
+
* product-specific and every consumer would otherwise reimplement it; a
|
|
73
|
+
* product whose identity is a drawing carries it here as data, beside the
|
|
74
|
+
* name, exactly as the bar's lockup does.
|
|
73
75
|
*/
|
|
74
|
-
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack'>;
|
|
76
|
+
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack' | 'mark'>;
|
|
77
|
+
/**
|
|
78
|
+
* A slot for the sentence or two a footer carries under its mark: what this
|
|
79
|
+
* site is, who publishes it, what it is built with.
|
|
80
|
+
*
|
|
81
|
+
* A slot rather than a string, because the same position holds one paragraph
|
|
82
|
+
* on one site and two labelled ones on another, and any headings are the
|
|
83
|
+
* caller's document outline rather than this Block's to level. It renders
|
|
84
|
+
* under the mark at the measure, with no colour of its own: muted text is the
|
|
85
|
+
* caller's decision about its own copy, and a Block that coloured a node it
|
|
86
|
+
* did not render would be styling it could not see.
|
|
87
|
+
*/
|
|
88
|
+
about?: ReactNode;
|
|
75
89
|
/**
|
|
76
90
|
* The groups of destinations: the sections of this site, its documentation, and
|
|
77
91
|
* anywhere else the site sends a reader. Omit it for a footer that carries only
|
|
@@ -109,8 +123,9 @@ export type SiteFooterProps = {
|
|
|
109
123
|
className?: string;
|
|
110
124
|
};
|
|
111
125
|
/**
|
|
112
|
-
* The footer of a product site: a brand lockup,
|
|
113
|
-
* that leave the site, and an optional
|
|
126
|
+
* The footer of a product site: a brand lockup, an optional word from the site
|
|
127
|
+
* under it, grouped destinations, the links that leave the site, and an optional
|
|
128
|
+
* slot for the legal line.
|
|
114
129
|
*
|
|
115
130
|
* It is a Block and not a Component because it is a region of a page: it
|
|
116
131
|
* composes a `ProductMark` and two sets of links and owns the block they sit in.
|
|
@@ -163,6 +178,6 @@ export type SiteFooterProps = {
|
|
|
163
178
|
* imports no router: a consumer renders it from a server file and the links are
|
|
164
179
|
* plain anchors the consumer's own router can intercept.
|
|
165
180
|
*/
|
|
166
|
-
export declare function SiteFooter({ product, columns, social, legal, headingLevel, className, }: SiteFooterProps): import("react").JSX.Element;
|
|
181
|
+
export declare function SiteFooter({ product, about, columns, social, legal, headingLevel, className, }: SiteFooterProps): import("react").JSX.Element;
|
|
167
182
|
export default SiteFooter;
|
|
168
183
|
//# sourceMappingURL=site-footer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-footer.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-footer/site-footer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACrF,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAG/D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CACjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B
|
|
1
|
+
{"version":3,"file":"site-footer.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-footer/site-footer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACrF,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAG/D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;CACjC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;;;OAOG;IACH,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,CAAA;IAChE;;;;;;;;;;OAUG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACrC;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,KAAK,EACL,OAAO,EACP,MAAM,EACN,KAAK,EACL,YAAmB,EACnB,SAAS,GACV,EAAE,eAAe,+BA+EjB;AAED,eAAe,UAAU,CAAA"}
|
|
@@ -2,8 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { ProductMark } from '../../components/ui/product-mark.js';
|
|
3
3
|
import { cn } from '../../lib/utils.js';
|
|
4
4
|
/**
|
|
5
|
-
* The footer of a product site: a brand lockup,
|
|
6
|
-
* that leave the site, and an optional
|
|
5
|
+
* The footer of a product site: a brand lockup, an optional word from the site
|
|
6
|
+
* under it, grouped destinations, the links that leave the site, and an optional
|
|
7
|
+
* slot for the legal line.
|
|
7
8
|
*
|
|
8
9
|
* It is a Block and not a Component because it is a region of a page: it
|
|
9
10
|
* composes a `ProductMark` and two sets of links and owns the block they sit in.
|
|
@@ -56,8 +57,13 @@ import { cn } from '../../lib/utils.js';
|
|
|
56
57
|
* imports no router: a consumer renders it from a server file and the links are
|
|
57
58
|
* plain anchors the consumer's own router can intercept.
|
|
58
59
|
*/
|
|
59
|
-
export function SiteFooter({ product, columns, social, legal, headingLevel = 'h2', className, }) {
|
|
60
|
+
export function SiteFooter({ product, about, columns, social, legal, headingLevel = 'h2', className, }) {
|
|
61
|
+
/*
|
|
62
|
+
* The lockup is the same `ProductMark` the bar draws, so a product's drawn
|
|
63
|
+
* mark, when it has one, is the one drawing in both places and a reader who
|
|
64
|
+
* scrolled from the bar meets the same sign at the foot.
|
|
65
|
+
*/
|
|
60
66
|
const ColumnTitle = headingLevel;
|
|
61
|
-
return (_jsx("footer", { "data-slot": "site-footer", className: cn('border-border bg-background w-full border-t', className), children: _jsxs("div", { className: "mx-auto flex w-full max-w-page flex-col gap-10 px-6 py-12 lg:px-8", children: [_jsxs("div", { className: "flex flex-col gap-10 md:flex-row md:justify-between", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, size: "lg" }), social && social.length > 0 ? (_jsx("ul", { className: "flex flex-wrap items-center gap-4", children: social.map((link) => (_jsx("li", { children: _jsxs("a", { href: link.href, rel: link.newTab === false ? undefined : 'noopener noreferrer', target: link.newTab === false ? undefined : '_blank', className: "text-muted-foreground hover:text-foreground flex min-h-6 items-center gap-2 rounded-sm text-sm font-medium transition-colors duration-fast ease-out", children: [link.icon ? (_jsx("span", { "aria-hidden": true, className: "inline-flex items-center", children: link.icon })) : null, link.label] }) }, link.href))) })) : null] }), columns && columns.length > 0 ? (_jsx("div", { className: "grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: columns.map((column) => (_jsxs("nav", { "aria-label": column.title, className: "flex flex-col gap-3", children: [_jsx(ColumnTitle, { className: "text-sm font-semibold", children: column.title }), _jsx("ul", { className: "flex flex-col gap-2", children: column.links.map((link) => (_jsx("li", { children: _jsx("a", { href: link.href, rel: link.newTab ? 'noopener noreferrer' : undefined, target: link.newTab ? '_blank' : undefined, className: "text-muted-foreground hover:text-foreground flex min-h-6 items-center rounded-sm text-sm transition-colors duration-fast ease-out", children: link.label }) }, link.href))) })] }, column.title))) })) : null] }), legal ? _jsx("div", { className: "text-muted-foreground text-sm", children: legal }) : null] }) }));
|
|
67
|
+
return (_jsx("footer", { "data-slot": "site-footer", className: cn('border-border bg-background w-full border-t', className), children: _jsxs("div", { className: "mx-auto flex w-full max-w-page flex-col gap-10 px-6 py-12 lg:px-8", children: [_jsxs("div", { className: "flex flex-col gap-10 md:flex-row md:justify-between", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, mark: product.mark, size: "lg" }), about ? (_jsx("div", { "data-slot": "site-footer-about", className: "max-w-measure flex flex-col gap-2", children: about })) : null, social && social.length > 0 ? (_jsx("ul", { className: "flex flex-wrap items-center gap-4", children: social.map((link) => (_jsx("li", { children: _jsxs("a", { href: link.href, rel: link.newTab === false ? undefined : 'noopener noreferrer', target: link.newTab === false ? undefined : '_blank', className: "text-muted-foreground hover:text-foreground flex min-h-6 items-center gap-2 rounded-sm text-sm font-medium transition-colors duration-fast ease-out", children: [link.icon ? (_jsx("span", { "aria-hidden": true, className: "inline-flex items-center", children: link.icon })) : null, link.label] }) }, link.href))) })) : null] }), columns && columns.length > 0 ? (_jsx("div", { className: "grid gap-8 sm:grid-cols-2 lg:grid-cols-3", children: columns.map((column) => (_jsxs("nav", { "aria-label": column.title, className: "flex flex-col gap-3", children: [_jsx(ColumnTitle, { className: "text-sm font-semibold", children: column.title }), _jsx("ul", { className: "flex flex-col gap-2", children: column.links.map((link) => (_jsx("li", { children: _jsx("a", { href: link.href, rel: link.newTab ? 'noopener noreferrer' : undefined, target: link.newTab ? '_blank' : undefined, className: "text-muted-foreground hover:text-foreground flex min-h-6 items-center rounded-sm text-sm transition-colors duration-fast ease-out", children: link.label }) }, link.href))) })] }, column.title))) })) : null] }), legal ? _jsx("div", { className: "text-muted-foreground text-sm", children: legal }) : null] }) }));
|
|
62
68
|
}
|
|
63
69
|
export default SiteFooter;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { SiteNavbar, default } from './site-navbar.js';
|
|
2
|
-
export type { SiteNavbarProps, SiteNavLink, SiteNavSearch, SiteNavTheme, SiteNavMode, SiteNavMobileLabels, } from './site-navbar.js';
|
|
2
|
+
export type { SiteNavbarProps, SiteNavLink, SiteNavGroup, SiteNavEntry, SiteNavSearch, SiteNavTheme, SiteNavMode, SiteNavMobileLabels, } from './site-navbar.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACnD,YAAY,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,mBAAmB,GACpB,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACnD,YAAY,EACV,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,mBAAmB,GACpB,MAAM,eAAe,CAAA"}
|
|
@@ -5,15 +5,33 @@ export type MobileNavLink = {
|
|
|
5
5
|
label: string;
|
|
6
6
|
/** Where the link goes. */
|
|
7
7
|
href: string;
|
|
8
|
-
/** Marks
|
|
8
|
+
/** Marks the link as the page the reader is on. */
|
|
9
9
|
current?: boolean;
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* A group of destinations in the mobile panel, as a label above its links.
|
|
13
|
+
*
|
|
14
|
+
* The row renders a group as a menu because a row is short of room and a group
|
|
15
|
+
* is one control there; this panel is not short of room, so the same group is a
|
|
16
|
+
* named run of the list rather than a second thing to open. A reader below the
|
|
17
|
+
* row's threshold therefore meets every destination flat: no route is behind
|
|
18
|
+
* two disclosures, and the group's name still tells them which destinations
|
|
19
|
+
* belong together.
|
|
20
|
+
*/
|
|
21
|
+
export type MobileNavGroup = {
|
|
22
|
+
/** The group's own name, which is a label and not a link. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** The destinations under the label, in the order a reader should meet them. */
|
|
25
|
+
children: readonly MobileNavLink[];
|
|
26
|
+
};
|
|
27
|
+
/** One entry in the mobile panel: a link, or a group of them. */
|
|
28
|
+
export type MobileNavEntry = MobileNavLink | MobileNavGroup;
|
|
11
29
|
/** The props the mobile navigation takes. */
|
|
12
30
|
export type MobileNavProps = {
|
|
13
31
|
/** The product this navigation belongs to, drawn as the mark above the list. */
|
|
14
|
-
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack'>;
|
|
32
|
+
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack' | 'mark'>;
|
|
15
33
|
/** Every destination, in the order a reader should meet them. */
|
|
16
|
-
links: readonly
|
|
34
|
+
links: readonly MobileNavEntry[];
|
|
17
35
|
/** The accessible name of the panel, which is also the trigger's name. */
|
|
18
36
|
label: string;
|
|
19
37
|
/** The word on the trigger while the panel is closed. */
|
|
@@ -42,7 +60,10 @@ export type MobileNavProps = {
|
|
|
42
60
|
*
|
|
43
61
|
* The links are full width with a 44 pixel minimum height, because below the row's
|
|
44
62
|
* threshold this is a touch target and not a line of text, and the coarse-pointer
|
|
45
|
-
* floor in the design system asks for exactly that.
|
|
63
|
+
* floor in the design system asks for exactly that. A group's label is a
|
|
64
|
+
* `role="group"` named by the label, so a reader who navigates the panel by
|
|
65
|
+
* landmark or by list is told which links belong together rather than meeting a
|
|
66
|
+
* longer list with a break in it.
|
|
46
67
|
*/
|
|
47
68
|
export declare function MobileNav({ product, links, label, openLabel, closeLabel, }: MobileNavProps): import("react").JSX.Element;
|
|
48
69
|
//# sourceMappingURL=mobile-nav.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-nav.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/mobile-nav.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAExE,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG;IAC1B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,
|
|
1
|
+
{"version":3,"file":"mobile-nav.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/mobile-nav.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAExE,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG;IAC1B,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb,gFAAgF;IAChF,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAA;CACnC,CAAA;AAED,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,cAAc,CAAA;AAE3D,6CAA6C;AAC7C,MAAM,MAAM,cAAc,GAAG;IAC3B,gFAAgF;IAChF,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,CAAA;IAChE,iEAAiE;IACjE,KAAK,EAAE,SAAS,cAAc,EAAE,CAAA;IAChC,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,KAAK,EACL,SAAS,EACT,UAAU,GACX,EAAE,cAAc,+BA0DhB"}
|
|
@@ -25,11 +25,18 @@ import { ProductMark } from '../../components/ui/product-mark.js';
|
|
|
25
25
|
*
|
|
26
26
|
* The links are full width with a 44 pixel minimum height, because below the row's
|
|
27
27
|
* threshold this is a touch target and not a line of text, and the coarse-pointer
|
|
28
|
-
* floor in the design system asks for exactly that.
|
|
28
|
+
* floor in the design system asks for exactly that. A group's label is a
|
|
29
|
+
* `role="group"` named by the label, so a reader who navigates the panel by
|
|
30
|
+
* landmark or by list is told which links belong together rather than meeting a
|
|
31
|
+
* longer list with a break in it.
|
|
29
32
|
*/
|
|
30
33
|
export function MobileNav({ product, links, label, openLabel, closeLabel, }) {
|
|
31
34
|
const [open, setOpen] = useState(false);
|
|
32
|
-
return (_jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsx(SheetTrigger, { "data-slot": "site-navbar-mobile-trigger", "aria-label": open ? closeLabel : openLabel, className: "border-border bg-card text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring flex size-11 shrink-0 items-center justify-center rounded-full border transition-colors focus-visible:ring-[3px] focus-visible:outline-none lg:hidden", children: open ? _jsx(X, { "aria-hidden": true, className: "size-5" }) : _jsx(Menu, { "aria-hidden": true, className: "size-5" }) }), _jsxs(SheetContent, { side: "right", className: "w-80", children: [_jsx(SheetTitle, { className: "sr-only", children: label }), _jsxs("nav", { "aria-label": label, className: "flex flex-col gap-1", children: [_jsx("div", { className: "mb-4 flex items-center", children: _jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, size: "md" }) }), links.map((
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
return (_jsxs(Sheet, { open: open, onOpenChange: setOpen, children: [_jsx(SheetTrigger, { "data-slot": "site-navbar-mobile-trigger", "aria-label": open ? closeLabel : openLabel, className: "border-border bg-card text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring flex size-11 shrink-0 items-center justify-center rounded-full border transition-colors focus-visible:ring-[3px] focus-visible:outline-none lg:hidden", children: open ? _jsx(X, { "aria-hidden": true, className: "size-5" }) : _jsx(Menu, { "aria-hidden": true, className: "size-5" }) }), _jsxs(SheetContent, { side: "right", className: "w-80", children: [_jsx(SheetTitle, { className: "sr-only", children: label }), _jsxs("nav", { "aria-label": label, className: "flex flex-col gap-1", children: [_jsx("div", { className: "mb-4 flex items-center", children: _jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, mark: product.mark, size: "md" }) }), links.map((entry) => 'children' in entry ? (_jsxs("div", { role: "group", "aria-label": entry.label, className: "mt-5 flex flex-col gap-1", children: [_jsx("span", { className: "text-muted-foreground px-3 pb-2 text-sm font-semibold tracking-tight", children: entry.label }), entry.children.map((link) => (_jsx(PanelLink, { link: link, onNavigate: () => setOpen(false) }, link.href)))] }, entry.label)) : (_jsx(PanelLink, { link: entry, onNavigate: () => setOpen(false) }, entry.href)))] })] })] }));
|
|
36
|
+
}
|
|
37
|
+
/** One link in the panel, extracted because the group and the root render the same one. */
|
|
38
|
+
function PanelLink({ link, onNavigate }) {
|
|
39
|
+
return (_jsx("a", { href: link.href, "aria-current": link.current ? 'page' : undefined, onClick: onNavigate, className: link.current
|
|
40
|
+
? 'bg-accent text-accent-foreground focus-visible:border-ring focus-visible:ring-ring flex min-h-11 items-center rounded-lg px-3 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none'
|
|
41
|
+
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring flex min-h-11 items-center rounded-lg px-3 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: link.label }));
|
|
35
42
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SiteNavLink } from './site-navbar.js';
|
|
2
|
+
/** One destination inside the menu, with the current flag already resolved. */
|
|
3
|
+
export type NavMenuLink = SiteNavLink & {
|
|
4
|
+
/** Marks this destination as the page the reader is on. */
|
|
5
|
+
current: boolean;
|
|
6
|
+
};
|
|
7
|
+
/** The props the navigation menu takes. */
|
|
8
|
+
export type NavMenuProps = {
|
|
9
|
+
/** The word on the trigger, which is the group's own name. */
|
|
10
|
+
label: string;
|
|
11
|
+
/** The group's destinations, in the order a reader should meet them. */
|
|
12
|
+
links: readonly NavMenuLink[];
|
|
13
|
+
/**
|
|
14
|
+
* Marks the group as holding the page the reader is on, which the bar
|
|
15
|
+
* resolves against its own `currentPath` before it crosses the client
|
|
16
|
+
* boundary. The trigger is filled rather than merely tinted, so a reader who
|
|
17
|
+
* cannot distinguish the fill still finds the page named inside the menu.
|
|
18
|
+
*/
|
|
19
|
+
current: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* One group of the site's own destinations, as a menu.
|
|
23
|
+
*
|
|
24
|
+
* A bar whose navigation is a row of links clubs into groups the moment a site
|
|
25
|
+
* outgrows the row: five destinations that are all "the parts of the library"
|
|
26
|
+
* beside four that are each a body of knowledge is a row a reader scans
|
|
27
|
+
* word by word rather than recognises at a glance. This is the second half of
|
|
28
|
+
* that row, and it is the one control in the row, because a group is not a
|
|
29
|
+
* destination: it is a place other destinations live behind.
|
|
30
|
+
*
|
|
31
|
+
* **The items are links, not commands, for the reason the sites menu states.**
|
|
32
|
+
* A menu item that navigated would be a command wearing a link's clothes, and
|
|
33
|
+
* a reader who middle-clicks a row that only looks like a link gets no new tab.
|
|
34
|
+
* Each item is rendered as the anchor itself rather than wrapping one, so the
|
|
35
|
+
* row is one tab stop and one target.
|
|
36
|
+
*
|
|
37
|
+
* **The menu is a closed group, and a closed group shows nothing.** A reader
|
|
38
|
+
* with scripting off sees the trigger and cannot open it, which is why the bar
|
|
39
|
+
* renders a group as a menu only on the row above the panel threshold: below
|
|
40
|
+
* it, the sheet lists every destination flat, and the bar's caller keeps
|
|
41
|
+
* whatever routes it cannot afford to hide out of a group. The trigger is
|
|
42
|
+
* filled when the reader is inside the group, so the place they are is on the
|
|
43
|
+
* row even with the menu shut.
|
|
44
|
+
*
|
|
45
|
+
* **The chevron rotates on the open state and on nothing else.** Something
|
|
46
|
+
* happened and the reader can see that it did, at the authored duration, and
|
|
47
|
+
* the rotation is the only motion here.
|
|
48
|
+
*/
|
|
49
|
+
export declare function NavMenu({ label, links, current }: NavMenuProps): import("react").JSX.Element;
|
|
50
|
+
//# sourceMappingURL=nav-menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-menu.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/nav-menu.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,+EAA+E;AAC/E,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG;IACtC,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,2CAA2C;AAC3C,MAAM,MAAM,YAAY,GAAG;IACzB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAA;IACb,wEAAwE;IACxE,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,YAAY,+BA6C9D"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../../components/ui/dropdown-menu.js';
|
|
5
|
+
/**
|
|
6
|
+
* One group of the site's own destinations, as a menu.
|
|
7
|
+
*
|
|
8
|
+
* A bar whose navigation is a row of links clubs into groups the moment a site
|
|
9
|
+
* outgrows the row: five destinations that are all "the parts of the library"
|
|
10
|
+
* beside four that are each a body of knowledge is a row a reader scans
|
|
11
|
+
* word by word rather than recognises at a glance. This is the second half of
|
|
12
|
+
* that row, and it is the one control in the row, because a group is not a
|
|
13
|
+
* destination: it is a place other destinations live behind.
|
|
14
|
+
*
|
|
15
|
+
* **The items are links, not commands, for the reason the sites menu states.**
|
|
16
|
+
* A menu item that navigated would be a command wearing a link's clothes, and
|
|
17
|
+
* a reader who middle-clicks a row that only looks like a link gets no new tab.
|
|
18
|
+
* Each item is rendered as the anchor itself rather than wrapping one, so the
|
|
19
|
+
* row is one tab stop and one target.
|
|
20
|
+
*
|
|
21
|
+
* **The menu is a closed group, and a closed group shows nothing.** A reader
|
|
22
|
+
* with scripting off sees the trigger and cannot open it, which is why the bar
|
|
23
|
+
* renders a group as a menu only on the row above the panel threshold: below
|
|
24
|
+
* it, the sheet lists every destination flat, and the bar's caller keeps
|
|
25
|
+
* whatever routes it cannot afford to hide out of a group. The trigger is
|
|
26
|
+
* filled when the reader is inside the group, so the place they are is on the
|
|
27
|
+
* row even with the menu shut.
|
|
28
|
+
*
|
|
29
|
+
* **The chevron rotates on the open state and on nothing else.** Something
|
|
30
|
+
* happened and the reader can see that it did, at the authored duration, and
|
|
31
|
+
* the rotation is the only motion here.
|
|
32
|
+
*/
|
|
33
|
+
export function NavMenu({ label, links, current }) {
|
|
34
|
+
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { "data-slot": "site-navbar-nav-trigger", "data-current": current ? 'true' : undefined, className: current
|
|
35
|
+
? 'group data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground bg-accent text-accent-foreground focus-visible:border-ring focus-visible:ring-ring gap-1 rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none'
|
|
36
|
+
: 'group data-[popup-open]:bg-accent data-[popup-open]:text-accent-foreground text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring gap-1 rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: [label, _jsx(ChevronDown, { "aria-hidden": true, className: "group-data-[popup-open]:rotate-180 size-4 transition-transform duration-base ease-out" })] }), _jsx(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-44", children: links.map((link) => (_jsx(DropdownMenuItem, {
|
|
37
|
+
/*
|
|
38
|
+
* Rendered AS the link rather than wrapping one. A focusable menu
|
|
39
|
+
* item with an anchor inside it is two tab stops for one row, and a
|
|
40
|
+
* reader who middle-clicks a row that only looks like a link gets no
|
|
41
|
+
* new tab and no way to tell that the row was a link at all.
|
|
42
|
+
*/
|
|
43
|
+
render: _jsx("a", { href: link.href, target: link.newTab ? '_blank' : undefined, rel: link.newTab ? 'noopener noreferrer' : undefined, "aria-current": link.current ? 'page' : undefined, "data-current": link.current ? 'true' : undefined }), className: "data-[current]:bg-accent data-[current]:text-accent-foreground data-[current]:font-medium", children: link.label }, link.href))) })] }));
|
|
44
|
+
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
1
2
|
import type { Mode, PackId } from '../../theming/index.js';
|
|
2
3
|
import type { SearchDialogMessages } from '../../components/ui/search-dialog.js';
|
|
3
4
|
import { type ThemePack } from './theme-menu.js';
|
|
4
5
|
import type { SwitcherProduct } from '../../components/ui/product-switcher.js';
|
|
5
|
-
import type {
|
|
6
|
+
import type { MobileNavEntry } from './mobile-nav.js';
|
|
6
7
|
/** What the control cluster was told to render. Every field is optional. */
|
|
7
8
|
export type NavbarControlsProps = {
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* The product whose mark heads the mobile panel. The drawn `mark` crosses
|
|
11
|
+
* this client boundary as rendered content, the same way a slot does, so a
|
|
12
|
+
* consumer's drawing stays server-drawn while the panel around it is not.
|
|
13
|
+
*/
|
|
9
14
|
product: {
|
|
10
15
|
id: string;
|
|
11
16
|
name: string;
|
|
12
17
|
pack?: PackId | null;
|
|
18
|
+
mark?: ReactNode;
|
|
13
19
|
};
|
|
14
20
|
/** The site's own destinations, for the panel below the row's threshold. */
|
|
15
|
-
nav: readonly
|
|
21
|
+
nav: readonly MobileNavEntry[];
|
|
16
22
|
/** The accessible name of the panel and of its trigger. */
|
|
17
23
|
navLabel: string;
|
|
18
24
|
/** The set of sites, or nothing for a site that is the only one. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navbar-controls.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/navbar-controls.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AAI7E,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"navbar-controls.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/navbar-controls.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AAI7E,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;OAIG;IACH,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;IAC7E,4EAA4E;IAC5E,GAAG,EAAE,SAAS,cAAc,EAAE,CAAA;IAC9B,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uEAAuE;IACvE,MAAM,CAAC,EAAE;QACP,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,EAAE,oBAAoB,CAAA;KAC/B,CAAA;IACD,+DAA+D;IAC/D,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;KAAE,CAAA;IACvD,6EAA6E;IAC7E,IAAI,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAChD,2CAA2C;IAC3C,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAA;IACnB,oDAAoD;IACpD,WAAW,EAAE,IAAI,CAAA;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAsDxD"}
|
|
@@ -31,7 +31,41 @@ export type SiteNavLink = {
|
|
|
31
31
|
* external: a cross-origin destination is not by itself a reason to open a tab.
|
|
32
32
|
*/
|
|
33
33
|
newTab?: boolean;
|
|
34
|
+
/** Forbidden: a destination carrying children is a `SiteNavGroup`, not a link. */
|
|
35
|
+
children?: never;
|
|
34
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* A group of destinations in the bar's own navigation, rendered as one trigger
|
|
39
|
+
* that opens a menu of them.
|
|
40
|
+
*
|
|
41
|
+
* The group is a place in the row rather than a route: it carries no `href`, and
|
|
42
|
+
* a caller whose group has an index page of its own lists that page among the
|
|
43
|
+
* `children` rather than on the group, so nothing is inventing a route. That is
|
|
44
|
+
* the same shape the site's own sidebar holds a folder to, and for the same
|
|
45
|
+
* reason: a group is where destinations live, not one.
|
|
46
|
+
*
|
|
47
|
+
* **A group hides its destinations until it is opened**, on the row above the
|
|
48
|
+
* panel threshold and nowhere else. The bar's narrow-viewport panel lists every
|
|
49
|
+
* destination flat, so a reader below the threshold meets no group at all, and a
|
|
50
|
+
* caller who cannot afford to hide a route behind a click leaves it out of a
|
|
51
|
+
* group: the search index, the sitemap and the page's own links are outside the
|
|
52
|
+
* bar and unaffected either way.
|
|
53
|
+
*/
|
|
54
|
+
export type SiteNavGroup = {
|
|
55
|
+
/** The word on the trigger, which is the group's own name. */
|
|
56
|
+
label: string;
|
|
57
|
+
/** The destinations behind the trigger, in the order a reader should meet them. */
|
|
58
|
+
children: readonly SiteNavLink[];
|
|
59
|
+
/**
|
|
60
|
+
* Marks the group as holding the page the reader is on, for a caller that
|
|
61
|
+
* composes the bar per page and marks each entry itself. A caller that passes
|
|
62
|
+
* `currentPath` leaves this off and the bar resolves it against the group's
|
|
63
|
+
* children.
|
|
64
|
+
*/
|
|
65
|
+
current?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** One entry in the bar's own navigation: a link, or a group of them. */
|
|
68
|
+
export type SiteNavEntry = SiteNavLink | SiteNavGroup;
|
|
35
69
|
/** The search the bar offers, which is a URL rather than an index. */
|
|
36
70
|
export type SiteNavSearch = {
|
|
37
71
|
/**
|
|
@@ -79,12 +113,12 @@ export type SiteNavMobileLabels = {
|
|
|
79
113
|
/** The props a SiteNavbar takes. */
|
|
80
114
|
export type SiteNavbarProps = {
|
|
81
115
|
/**
|
|
82
|
-
* The product this bar belongs to: the id, name and
|
|
83
|
-
* drawn from, and the mark that heads the mobile panel. The
|
|
84
|
-
* itself rather than a slot, because a bar whose brand
|
|
85
|
-
* every consumer has to reimplement.
|
|
116
|
+
* The product this bar belongs to: the id, name, pack and drawn mark its
|
|
117
|
+
* `ProductMark` is drawn from, and the mark that heads the mobile panel. The
|
|
118
|
+
* bar renders the mark itself rather than a slot, because a bar whose brand
|
|
119
|
+
* lockup is a slot is a bar every consumer has to reimplement.
|
|
86
120
|
*/
|
|
87
|
-
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack'>;
|
|
121
|
+
product: Pick<ProductMarkProps, 'id' | 'name' | 'pack' | 'mark'>;
|
|
88
122
|
/** Where the brand lockup goes. @defaultValue '/' */
|
|
89
123
|
homeHref?: string;
|
|
90
124
|
/**
|
|
@@ -99,8 +133,13 @@ export type SiteNavbarProps = {
|
|
|
99
133
|
* control whose icon is a guess until it is pressed once.
|
|
100
134
|
*/
|
|
101
135
|
defaultMode: Mode;
|
|
102
|
-
/**
|
|
103
|
-
|
|
136
|
+
/**
|
|
137
|
+
* This site's own destinations: links, groups of links, or both in the order
|
|
138
|
+
* a reader should meet them. A link renders as itself; a group renders as one
|
|
139
|
+
* trigger opening its children as a menu. Omit for a bar that carries only
|
|
140
|
+
* the lockup.
|
|
141
|
+
*/
|
|
142
|
+
nav?: readonly SiteNavEntry[];
|
|
104
143
|
/**
|
|
105
144
|
* The accessible name of the site's navigation. Required, and required rather
|
|
106
145
|
* than defaulted because it is a word a reader hears: a Block that ships no copy
|
|
@@ -234,12 +273,14 @@ export type SiteNavbarProps = {
|
|
|
234
273
|
* with no release. This package ships no `will-change` anywhere, and the bar is not
|
|
235
274
|
* the place to start one.
|
|
236
275
|
*
|
|
237
|
-
* **It is a server Component
|
|
238
|
-
*
|
|
239
|
-
* single boundary, because a control's state is a reader's state
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
276
|
+
* **It is a server Component whose client islands are the ones a reader opens.** The
|
|
277
|
+
* lockup and the row's links are rendered on the server and ship no JavaScript; the
|
|
278
|
+
* controls are a single boundary, because a control's state is a reader's state, and
|
|
279
|
+
* a group in the navigation adds one island of its own for the same reason. A site
|
|
280
|
+
* that passes no `search`, `sites`, `theme`, `mode` or `nav`, and groups none of
|
|
281
|
+
* its destinations, renders the whole bar on the server and ships none, which is
|
|
282
|
+
* the arrangement a static site wants and the reason those props are optional
|
|
283
|
+
* rather than required.
|
|
243
284
|
*/
|
|
244
285
|
export declare function SiteNavbar({ product, homeHref, defaultPack, defaultMode, nav, navLabel, currentPath, sites, currentSiteId, sitesLabel, search, theme, mode, mobileLabels, actions, sticky, className, }: SiteNavbarProps): import("react").JSX.Element;
|
|
245
286
|
export default SiteNavbar;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-navbar.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/site-navbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAA;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AAE7E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"site-navbar.d.ts","sourceRoot":"","sources":["../../../src/blocks/site-navbar/site-navbar.tsx"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { ProductMark } from '../../components/ui/product-mark.js';
|
|
3
3
|
import { cn } from '../../lib/utils.js';
|
|
4
4
|
import { NavbarControls } from './navbar-controls.js';
|
|
5
|
+
import { NavMenu } from './nav-menu.js';
|
|
5
6
|
/**
|
|
6
7
|
* The bar at the top of a product site, with the controls a reader needs on every
|
|
7
8
|
* page of it.
|
|
@@ -73,18 +74,52 @@ import { NavbarControls } from './navbar-controls.js';
|
|
|
73
74
|
* with no release. This package ships no `will-change` anywhere, and the bar is not
|
|
74
75
|
* the place to start one.
|
|
75
76
|
*
|
|
76
|
-
* **It is a server Component
|
|
77
|
-
*
|
|
78
|
-
* single boundary, because a control's state is a reader's state
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
77
|
+
* **It is a server Component whose client islands are the ones a reader opens.** The
|
|
78
|
+
* lockup and the row's links are rendered on the server and ship no JavaScript; the
|
|
79
|
+
* controls are a single boundary, because a control's state is a reader's state, and
|
|
80
|
+
* a group in the navigation adds one island of its own for the same reason. A site
|
|
81
|
+
* that passes no `search`, `sites`, `theme`, `mode` or `nav`, and groups none of
|
|
82
|
+
* its destinations, renders the whole bar on the server and ships none, which is
|
|
83
|
+
* the arrangement a static site wants and the reason those props are optional
|
|
84
|
+
* rather than required.
|
|
82
85
|
*/
|
|
83
86
|
export function SiteNavbar({ product, homeHref = '/', defaultPack, defaultMode, nav, navLabel, currentPath, sites, currentSiteId, sitesLabel, search, theme, mode, mobileLabels, actions, sticky = true, className, }) {
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
const entries = nav ?? [];
|
|
88
|
+
/*
|
|
89
|
+
* The current mark reads the route as a place rather than as a string, so a
|
|
90
|
+
* reader on a page inside a destination is still on it: `/components/button`
|
|
91
|
+
* marks Components on the row, which is the question a reader asking "where
|
|
92
|
+
* am I" is asking. The boundary is the slash, so a Section whose segment is a
|
|
93
|
+
* prefix of another's (`/content` beside a hypothetical `/content-x`) never
|
|
94
|
+
* claims the other's pages.
|
|
95
|
+
*/
|
|
96
|
+
const under = (href) => currentPath !== undefined && (currentPath === href || currentPath.startsWith(`${href}/`));
|
|
97
|
+
const leafCurrent = (link) => link.current ?? under(link.href);
|
|
98
|
+
/**
|
|
99
|
+
* A group is current when the reader is on it or inside any of its children.
|
|
100
|
+
* A group is told apart from a link by `href`, which a link must carry and a
|
|
101
|
+
* group must not, so the narrowing is on the one key the two never share.
|
|
102
|
+
*/
|
|
103
|
+
const entryCurrent = (entry) => entry.current ??
|
|
104
|
+
('href' in entry
|
|
105
|
+
? under(entry.href)
|
|
106
|
+
: entry.children.some((child) => leafCurrent(child)));
|
|
107
|
+
return (_jsx("header", { "data-slot": "site-navbar", className: cn('border-border bg-background w-full border-b', sticky && 'sticky top-0 z-20', className), children: _jsxs("div", { className: "mx-auto flex h-14 w-full max-w-page items-center gap-2 px-6", children: [_jsx("a", { href: homeHref, className: "focus-visible:border-ring focus-visible:ring-ring shrink-0 rounded-sm focus-visible:ring-[3px] focus-visible:outline-none", children: _jsx(ProductMark, { id: product.id, name: product.name, pack: product.pack, mark: product.mark, size: "sm" }) }), entries.length > 0 ? (_jsx("nav", { "aria-label": navLabel, className: "hidden items-center lg:flex", children: entries.map((entry) =>
|
|
108
|
+
/*
|
|
109
|
+
* A group is the one control in the row, because it is the one
|
|
110
|
+
* entry that is a place rather than a destination, and its menu is
|
|
111
|
+
* a client island for the same reason the controls cluster is:
|
|
112
|
+
* whether it is open is a reader's state. The links around it stay
|
|
113
|
+
* server-rendered anchors, so a bar whose row is all links still
|
|
114
|
+
* ships a navigation with no JavaScript in it.
|
|
115
|
+
*/
|
|
116
|
+
'href' in entry ? (_jsx("a", { href: entry.href, "aria-current": entryCurrent(entry) ? 'page' : undefined, rel: entry.newTab ? 'noopener noreferrer' : undefined, target: entry.newTab ? '_blank' : undefined, className: entryCurrent(entry)
|
|
87
117
|
? 'bg-accent text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none'
|
|
88
|
-
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children:
|
|
118
|
+
: 'text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring rounded-md px-2 py-1.5 text-sm font-medium transition-colors focus-visible:ring-[3px] focus-visible:outline-none', children: entry.label }, entry.href)) : (_jsx(NavMenu, { label: entry.label, current: entryCurrent(entry), links: entry.children.map((child) => ({ ...child, current: leafCurrent(child) })) }, entry.label))) })) : null, _jsx(NavbarControls, { product: product, nav: entries.map((entry) => 'href' in entry
|
|
119
|
+
? { ...entry, current: entryCurrent(entry) }
|
|
120
|
+
: {
|
|
121
|
+
label: entry.label,
|
|
122
|
+
children: entry.children.map((child) => ({ ...child, current: leafCurrent(child) })),
|
|
123
|
+
}), navLabel: navLabel, sites: sites, currentSiteId: currentSiteId, sitesLabel: sitesLabel, search: search, theme: theme, mode: mode, mobileLabels: mobileLabels, defaultPack: defaultPack, defaultMode: defaultMode, actions: actions })] }) }));
|
|
89
124
|
}
|
|
90
125
|
export default SiteNavbar;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/components/ui/calendar.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS;IACtC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAA;AAED,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,aAAa,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,IAAI,CAAA;IACX,mFAAmF;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IACjC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../../src/components/ui/calendar.tsx"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS;IACtC,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;CACP,CAAA;AAED,sCAAsC;AACtC,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,gEAAgE;IAChE,aAAa,EAAE,gBAAgB,CAAA;IAC/B;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;OAOG;IACH,KAAK,EAAE,IAAI,CAAA;IACX,mFAAmF;IACnF,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;IACnB,kFAAkF;IAClF,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;IACjC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,IAAI,CAAA;IACZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mBAAmB;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,iBAAS,QAAQ,CAAC,EAChB,KAAK,EACL,UAAU,EACV,aAAa,EACb,YAAgB,EAChB,aAAa,EACb,SAAS,EACT,KAAK,EACL,aAAa,EACb,KAAY,EACZ,aAAa,EACb,cAAc,EACd,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,IAAI,EACJ,IAAI,EACJ,SAAS,GACV,EAAE,aAAa,+BAyPf;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA"}
|
|
@@ -35,7 +35,12 @@ const DAY_CELL =
|
|
|
35
35
|
// was added to it, and the fuller grid kept the 36px cell that is the same cell at
|
|
36
36
|
// a different size. A month grid is the densest control in the package and the one
|
|
37
37
|
// most worked with a thumb, so it is the last place the floor can be argued about.
|
|
38
|
-
|
|
38
|
+
//
|
|
39
|
+
// The cell is centred by the table, not by a `flex` utility: a `display: flex` on
|
|
40
|
+
// a `<th>` or a `<td>` takes the cell out of the table's column layout, and the
|
|
41
|
+
// month drew as one stacked column at four times its height before the utility
|
|
42
|
+
// came off.
|
|
43
|
+
'text-foreground hover:bg-accent hover:text-accent-foreground h-9 cursor-pointer rounded-md p-0 align-middle text-center text-sm tabular-nums outline-none transition-colors duration-fast ease-out pointer-coarse:h-11 focus-visible:ring-ring focus-visible:ring-[3px]';
|
|
39
44
|
/**
|
|
40
45
|
* The two paging controls.
|
|
41
46
|
*
|
|
@@ -226,7 +231,7 @@ function Calendar({ label, monthLabel, weekdayLabels, firstWeekday = 0, previous
|
|
|
226
231
|
const focusedIso = iso(focused);
|
|
227
232
|
const chosenIso = value === null ? null : iso(value);
|
|
228
233
|
const todayIso = today === undefined ? null : iso(today);
|
|
229
|
-
return (_jsxs("div", { "data-slot": "calendar", className: cn('bg-popover text-popover-foreground w-64 rounded-md border p-3 shadow-md', className), children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "calendar-previous", "aria-label": previousLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, -1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronLeftIcon, { className: "size-4" }) }), _jsx("div", { "data-slot": "calendar-caption", "aria-live": "polite", className: "text-foreground text-sm font-medium tabular-nums", children: monthLabel }), _jsx("button", { type: "button", "data-slot": "calendar-next", "aria-label": nextLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, 1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronRightIcon, { className: "size-4" }) })] }), _jsx("div", { ref: gridRef, children: _jsxs("table", { "data-slot": "calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels.map((weekday, column) => (_jsx("th", { scope: "col", "data-slot": "calendar-weekday", className: "text-muted-foreground
|
|
234
|
+
return (_jsxs("div", { "data-slot": "calendar", className: cn('bg-popover text-popover-foreground w-64 rounded-md border p-3 shadow-md', className), children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "calendar-previous", "aria-label": previousLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, -1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronLeftIcon, { className: "size-4" }) }), _jsx("div", { "data-slot": "calendar-caption", "aria-live": "polite", className: "text-foreground text-sm font-medium tabular-nums", children: monthLabel }), _jsx("button", { type: "button", "data-slot": "calendar-next", "aria-label": nextLabel, disabled: disabled, onClick: () => onMonthChange(shiftMonth(month, 1)), className: cn(NAV_CONTROL, 'disabled:pointer-events-none disabled:opacity-50'), children: _jsx(ChevronRightIcon, { className: "size-4" }) })] }), _jsx("div", { ref: gridRef, children: _jsxs("table", { "data-slot": "calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("thead", { children: _jsx("tr", { children: weekdayLabels.map((weekday, column) => (_jsx("th", { scope: "col", "data-slot": "calendar-weekday", className: "text-muted-foreground h-8 p-0 align-middle text-center text-xs font-medium", children: weekday }, `${weekday}-${column}`))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
|
|
230
235
|
const key = iso(date);
|
|
231
236
|
if (!sameMonth(date, month)) {
|
|
232
237
|
// The cells that pad the month out. They are hidden from the
|
|
@@ -243,7 +243,7 @@ function MiniCalendar({ selected, onSelect, month, defaultMonth, onMonthChange,
|
|
|
243
243
|
default:
|
|
244
244
|
}
|
|
245
245
|
};
|
|
246
|
-
return (_jsx("div", { ref: gridRef, "data-slot": "mini-calendar", className: cn('bg-card text-card-foreground w-64 rounded-md border p-3 shadow-sm', className), children: _jsxs("table", { "data-slot": "mini-calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("caption", { "data-slot": "mini-calendar-caption", className: "mb-2", children: _jsxs("span", { className: "flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "mini-calendar-previous", "aria-label": previousLabel, onClick: () => move(shiftMonth(shown, -1)), className: NAV_CONTROL, children: _jsx(ChevronLeftIcon, { className: "size-4", "aria-hidden": "true" }) }), _jsx("span", { "data-slot": "mini-calendar-month", "aria-live": "polite", className: "text-foreground text-sm font-medium", children: monthText }), _jsx("button", { type: "button", "data-slot": "mini-calendar-next", "aria-label": nextLabel, onClick: () => move(shiftMonth(shown, 1)), className: NAV_CONTROL, children: _jsx(ChevronRightIcon, { className: "size-4", "aria-hidden": "true" }) })] }) }), _jsx("thead", { children: _jsx("tr", { children: headings.map((heading) => (_jsx("th", { scope: "col", abbr: heading.long, "data-slot": "mini-calendar-weekday", className: "text-muted-foreground
|
|
246
|
+
return (_jsx("div", { ref: gridRef, "data-slot": "mini-calendar", className: cn('bg-card text-card-foreground w-64 rounded-md border p-3 shadow-sm', className), children: _jsxs("table", { "data-slot": "mini-calendar-grid", role: "grid", "aria-label": label, className: "w-full border-collapse tabular-nums", children: [_jsx("caption", { "data-slot": "mini-calendar-caption", className: "mb-2", children: _jsxs("span", { className: "flex items-center justify-between gap-1", children: [_jsx("button", { type: "button", "data-slot": "mini-calendar-previous", "aria-label": previousLabel, onClick: () => move(shiftMonth(shown, -1)), className: NAV_CONTROL, children: _jsx(ChevronLeftIcon, { className: "size-4", "aria-hidden": "true" }) }), _jsx("span", { "data-slot": "mini-calendar-month", "aria-live": "polite", className: "text-foreground text-sm font-medium", children: monthText }), _jsx("button", { type: "button", "data-slot": "mini-calendar-next", "aria-label": nextLabel, onClick: () => move(shiftMonth(shown, 1)), className: NAV_CONTROL, children: _jsx(ChevronRightIcon, { className: "size-4", "aria-hidden": "true" }) })] }) }), _jsx("thead", { children: _jsx("tr", { children: headings.map((heading) => (_jsx("th", { scope: "col", abbr: heading.long, "data-slot": "mini-calendar-weekday", className: "text-muted-foreground h-8 p-0 align-middle text-center text-xs font-medium", children: heading.short }, heading.long))) }) }), _jsx("tbody", { children: Array.from({ length: 6 }, (_, row) => (_jsx("tr", { children: cells.slice(row * 7, row * 7 + 7).map((date) => {
|
|
247
247
|
const key = iso(date);
|
|
248
248
|
if (!sameMonth(date, shown)) {
|
|
249
249
|
// The cells that pad the month out. Hidden from the accessibility
|