@cronos-labs/ui 0.7.1 → 0.8.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/Footer/index.d.ts +3 -0
- package/dist/Footer/index.js +4 -1
- package/dist/Footer/styles.d.ts +6 -0
- package/dist/Footer/styles.js +33 -0
- package/dist/FooterWaitlist/styles.js +12 -2
- package/dist/Header/NavDropdown.d.ts +5 -1
- package/dist/Header/NavDropdown.js +2 -2
- package/dist/Header/index.d.ts +9 -0
- package/dist/Header/index.js +22 -6
- package/dist/Header/styles.d.ts +14 -2
- package/dist/Header/styles.js +69 -0
- package/package.json +1 -1
package/dist/Footer/index.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ export interface FooterLinkItem {
|
|
|
3
3
|
target: string;
|
|
4
4
|
external: boolean;
|
|
5
5
|
localizeTarget?: boolean;
|
|
6
|
+
/** Renders the link greyed and non-interactive, with `badge` beside it if given. */
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
badge?: string;
|
|
6
9
|
}
|
|
7
10
|
export interface FooterLinkGroup {
|
|
8
11
|
group: string;
|
package/dist/Footer/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { buildLocalizedPath } from '../locale/routes.js';
|
|
|
5
5
|
import { useCurrentLocale } from '../locale/hooks.js';
|
|
6
6
|
import { desktopViewportQuery } from '../tokens.js';
|
|
7
7
|
import { useOptionalBrand } from '../brand/index.js';
|
|
8
|
-
import { FooterColumn, FooterColumnHeading, FooterContainer, FooterCopyright, FooterColumnsGrid, FooterDesktopLocaleSlot, FooterDisclaimer, FooterDisclaimerStack, FooterGrid, FooterLegalButton, FooterLegalDivider, FooterLegalLink as FooterLegalLinkStyled, FooterLegalMeta, FooterLegalNav, FooterLegalText, FooterLocaleSlot, FooterLink as FooterLinkStyled, FooterLinkList, FooterMobileCopyright, FooterMobileDivider, FooterMobileLegalDividerTrack, FooterMobileLegalMeta, FooterMobileLegalNav, FooterShell, FooterWaitlistSlot, FooterWordmark, FooterWordmarkImage, } from './styles.js';
|
|
8
|
+
import { FooterColumn, FooterColumnHeading, FooterContainer, FooterCopyright, FooterColumnsGrid, FooterDesktopLocaleSlot, FooterDisclaimer, FooterDisclaimerStack, FooterGrid, FooterLegalButton, FooterLegalDivider, FooterLegalLink as FooterLegalLinkStyled, FooterLegalMeta, FooterLegalNav, FooterLegalText, FooterLocaleSlot, FooterLink as FooterLinkStyled, FooterLinkBadge, FooterLinkDisabled, FooterLinkList, FooterMobileCopyright, FooterMobileDivider, FooterMobileLegalDividerTrack, FooterMobileLegalMeta, FooterMobileLegalNav, FooterShell, FooterWaitlistSlot, FooterWordmark, FooterWordmarkImage, } from './styles.js';
|
|
9
9
|
const buildFooterHref = (target, locale, localize = true) => {
|
|
10
10
|
if (target.startsWith('/') && localize) {
|
|
11
11
|
return buildLocalizedPath(target, locale);
|
|
@@ -38,6 +38,9 @@ export function Footer({ brandWordmarkSrc, columns, content, forceDocumentNaviga
|
|
|
38
38
|
};
|
|
39
39
|
}, []);
|
|
40
40
|
const renderFooterLink = (link) => {
|
|
41
|
+
if (link.disabled) {
|
|
42
|
+
return (_jsxs(FooterLinkDisabled, { "aria-disabled": "true", "aria-label": `${link.label} ${link.badge ?? ''}`.trim(), children: [_jsx("span", { children: link.label }), link.badge ? _jsx(FooterLinkBadge, { children: link.badge }) : null] }, link.target));
|
|
43
|
+
}
|
|
41
44
|
return (_jsx(FooterLinkStyled, { href: buildFooterHref(link.target, locale, link.localizeTarget ?? true), ...getExternalLinkProps(link.external), children: _jsx("span", { children: link.label }) }, link.target));
|
|
42
45
|
};
|
|
43
46
|
const renderLegalLink = (link) => {
|
package/dist/Footer/styles.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export declare const FooterLinkList: import("styled-components/dist/types").ISty
|
|
|
40
40
|
export declare const FooterLink: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "style"> & {
|
|
41
41
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
42
42
|
}> & string;
|
|
43
|
+
export declare const FooterLinkDisabled: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
44
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
45
|
+
}> & string;
|
|
46
|
+
export declare const FooterLinkBadge: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
47
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
48
|
+
}> & string;
|
|
43
49
|
export declare const FooterLegalMeta: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style"> & {
|
|
44
50
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
45
51
|
}> & string;
|
package/dist/Footer/styles.js
CHANGED
|
@@ -172,6 +172,39 @@ export const FooterLink = styled.a `
|
|
|
172
172
|
font-size: ${({ theme }) => theme.typography.fontSize.body};
|
|
173
173
|
}
|
|
174
174
|
`;
|
|
175
|
+
/* A greyed column link: the link's type, dimmed, with no hover. */
|
|
176
|
+
export const FooterLinkDisabled = styled.span `
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 8px;
|
|
180
|
+
width: fit-content;
|
|
181
|
+
color: ${({ theme }) => theme.colors.ink};
|
|
182
|
+
font-size: 0.875rem;
|
|
183
|
+
font-weight: ${({ theme }) => theme.fontWeight.medium};
|
|
184
|
+
line-height: 1.5;
|
|
185
|
+
opacity: 0.5;
|
|
186
|
+
cursor: default;
|
|
187
|
+
|
|
188
|
+
${({ theme }) => theme.mediaQueries.lg} {
|
|
189
|
+
font-size: ${({ theme }) => theme.typography.fontSize.body};
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
export const FooterLinkBadge = styled.span `
|
|
193
|
+
display: inline-flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
justify-content: center;
|
|
196
|
+
height: 18px;
|
|
197
|
+
padding: 0 8px;
|
|
198
|
+
border-radius: ${({ theme }) => theme.borderRadius.pill};
|
|
199
|
+
background: rgb(0 0 0 / 0.1);
|
|
200
|
+
color: currentColor;
|
|
201
|
+
font-size: 0.5625rem;
|
|
202
|
+
font-weight: ${({ theme }) => theme.fontWeight.black};
|
|
203
|
+
letter-spacing: 0.1em;
|
|
204
|
+
line-height: 1;
|
|
205
|
+
text-transform: uppercase;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
`;
|
|
175
208
|
export const FooterLegalMeta = styled.div `
|
|
176
209
|
display: flex;
|
|
177
210
|
flex-wrap: wrap;
|
|
@@ -273,10 +273,20 @@ export const WaitlistSocialBlock = styled.div `
|
|
|
273
273
|
margin-top: ${({ $variant }) => ($variant === 'footer' ? '10px' : '14px')};
|
|
274
274
|
}
|
|
275
275
|
`;
|
|
276
|
+
/* Seven 32px icons. The row wraps and closes up to 16px below 768px:
|
|
277
|
+
at 24px it is 368px wide, wider than a 360px phone's 328px column, and
|
|
278
|
+
as it could not shrink it widened every ancestor up to the footer, so
|
|
279
|
+
the whole footer ran past the viewport and the page scrolled sideways. */
|
|
276
280
|
export const WaitlistSocialLinks = styled.div `
|
|
277
|
-
display:
|
|
281
|
+
display: flex;
|
|
282
|
+
flex-wrap: wrap;
|
|
278
283
|
align-items: center;
|
|
279
|
-
gap:
|
|
284
|
+
gap: 16px;
|
|
285
|
+
min-width: 0;
|
|
286
|
+
|
|
287
|
+
${({ theme }) => theme.mediaQueries.sm} {
|
|
288
|
+
gap: 24px;
|
|
289
|
+
}
|
|
280
290
|
`;
|
|
281
291
|
export const WaitlistSocialLink = styled.a `
|
|
282
292
|
display: inline-flex;
|
|
@@ -2,10 +2,14 @@ import type { HeaderPresentationVariant } from './index.js';
|
|
|
2
2
|
export interface NavDropdownItem {
|
|
3
3
|
key: string;
|
|
4
4
|
label: string;
|
|
5
|
-
|
|
5
|
+
/** Absent on a disabled item. */
|
|
6
|
+
href?: string | undefined;
|
|
6
7
|
ariaLabel?: string | undefined;
|
|
7
8
|
external?: boolean | undefined;
|
|
8
9
|
onClick?: ((event: React.MouseEvent<HTMLAnchorElement>) => void) | undefined;
|
|
10
|
+
/** Greyed and non-interactive, with `badge` beside the label if given. */
|
|
11
|
+
disabled?: boolean | undefined;
|
|
12
|
+
badge?: string | undefined;
|
|
9
13
|
}
|
|
10
14
|
interface NavDropdownProps {
|
|
11
15
|
label: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { useLocation } from 'react-router-dom';
|
|
4
|
-
import { NavDropdown as NavDropdownRoot, NavDropdownChevron, NavDropdownLabel, NavDropdownList, NavDropdownMenu, NavDropdownOption, NavDropdownTrigger, } from './styles.js';
|
|
4
|
+
import { NavDropdown as NavDropdownRoot, NavDropdownChevron, NavDropdownDisabledOption, NavDropdownLabel, NavDropdownList, NavDropdownMenu, NavDropdownOption, NavDropdownOptionBadge, NavDropdownTrigger, } from './styles.js';
|
|
5
5
|
const buildMenuId = (label) => `header-nav-dropdown-${label.toLowerCase().replace(/\s+/gu, '-')}`;
|
|
6
6
|
export function NavDropdown({ label, items, variant, contrast, }) {
|
|
7
7
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -32,7 +32,7 @@ export function NavDropdown({ label, items, variant, contrast, }) {
|
|
|
32
32
|
document.removeEventListener('keydown', handleKeyDown);
|
|
33
33
|
};
|
|
34
34
|
}, [isOpen]);
|
|
35
|
-
return (_jsxs(NavDropdownRoot, { ref: rootRef, onMouseEnter: () => setIsOpen(true), onMouseLeave: () => setIsOpen(false), children: [_jsxs(NavDropdownTrigger, { type: "button", "$variant": variant, "$contrast": contrast, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, onClick: () => setIsOpen((currentValue) => !currentValue), children: [_jsx(NavDropdownLabel, { "$contrast": contrast, children: label }), _jsx(NavDropdownChevron, { "$open": isOpen, "aria-hidden": "true" })] }), _jsx(NavDropdownMenu, { "$open": isOpen, id: menuId, role: "menu", "aria-label": label, children: _jsx(NavDropdownList, { children: items.map((item) => (_jsx(NavDropdownOption, { role: "menuitem", href: item.href, "aria-label": item.ariaLabel, onClick: (event) => {
|
|
35
|
+
return (_jsxs(NavDropdownRoot, { ref: rootRef, onMouseEnter: () => setIsOpen(true), onMouseLeave: () => setIsOpen(false), children: [_jsxs(NavDropdownTrigger, { type: "button", "$variant": variant, "$contrast": contrast, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, onClick: () => setIsOpen((currentValue) => !currentValue), children: [_jsx(NavDropdownLabel, { "$contrast": contrast, children: label }), _jsx(NavDropdownChevron, { "$open": isOpen, "aria-hidden": "true" })] }), _jsx(NavDropdownMenu, { "$open": isOpen, id: menuId, role: "menu", "aria-label": label, children: _jsx(NavDropdownList, { children: items.map((item) => item.disabled ? (_jsxs(NavDropdownDisabledOption, { role: "menuitem", "aria-disabled": "true", "aria-label": `${item.label} ${item.badge ?? ''}`.trim(), children: [item.label, item.badge ? _jsx(NavDropdownOptionBadge, { children: item.badge }) : null] }, item.key)) : (_jsx(NavDropdownOption, { role: "menuitem", href: item.href, "aria-label": item.ariaLabel, onClick: (event) => {
|
|
36
36
|
setIsOpen(false);
|
|
37
37
|
item.onClick?.(event);
|
|
38
38
|
}, ...(item.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: item.label }, item.key))) }) })] }));
|
package/dist/Header/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export interface HeaderNavChild {
|
|
|
10
10
|
label: string;
|
|
11
11
|
route?: string;
|
|
12
12
|
external?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Renders the child greyed and non-interactive, with `badge` beside it if
|
|
15
|
+
* given — for a destination that is not open yet. A disabled child needs
|
|
16
|
+
* no `route`.
|
|
17
|
+
*/
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
badge?: string;
|
|
13
20
|
}
|
|
14
21
|
export interface HeaderNavItem {
|
|
15
22
|
label: string;
|
|
@@ -29,6 +36,8 @@ export interface HeaderContent {
|
|
|
29
36
|
export interface HeaderWaitlistCta {
|
|
30
37
|
href: string;
|
|
31
38
|
onClick?: (event: React.MouseEvent<HTMLAnchorElement>, ctaLocation: 'nav_desktop' | 'nav_mobile') => void;
|
|
39
|
+
/** Renders the CTA greyed and non-interactive, keeping its place and label. */
|
|
40
|
+
disabled?: boolean;
|
|
32
41
|
}
|
|
33
42
|
interface HeaderProps {
|
|
34
43
|
/** Overrides the wordmark from `<BrandProvider>`. Optional when one is present. */
|
package/dist/Header/index.js
CHANGED
|
@@ -8,11 +8,11 @@ import { desktopViewportQuery } from '../tokens.js';
|
|
|
8
8
|
import { useOptionalBrand } from '../brand/index.js';
|
|
9
9
|
import { normalizePathname } from './helper.js';
|
|
10
10
|
import { NavDropdown } from './NavDropdown.js';
|
|
11
|
-
import { BrandWordmarkMask, HeaderAction, HeaderActionArrow, HeaderBrandLink, HeaderFrame, HeaderMobileNav, HeaderMobileNavDisabledItem, HeaderMobileNavGroup, HeaderMobileNavGroupChevron, HeaderMobileNavGroupPanel, HeaderMobileNavGroupPanelInner, HeaderMobileNavGroupToggle, HeaderMobileNavSubLink, HeaderMobileNavSubList, HeaderMobileNavLink, HeaderMobileOverlay, HeaderMobileLocaleSlot, HeaderMobileSheet, HeaderMobileSoonBadge, HeaderMobileToggle, HeaderMobileToggleIcon, HeaderMobileWaitlistArrow, HeaderMobileWaitlistLabel, HeaderMobileWaitlistLink, HeaderShell, Nav, NavBadge, NavBadgeLabel, Wordmark, } from './styles.js';
|
|
11
|
+
import { BrandWordmarkMask, HeaderAction, HeaderActionArrow, HeaderBrandLink, HeaderFrame, HeaderMobileNav, HeaderMobileNavDisabledItem, HeaderMobileNavGroup, HeaderMobileNavGroupChevron, HeaderMobileNavGroupPanel, HeaderMobileNavGroupPanelInner, HeaderMobileNavGroupToggle, HeaderMobileNavSubDisabledItem, HeaderMobileNavSubLink, HeaderMobileNavSubList, HeaderMobileNavLink, HeaderMobileOverlay, HeaderMobileLocaleSlot, HeaderMobileSheet, HeaderMobileSoonBadge, HeaderMobileToggle, HeaderMobileToggleIcon, HeaderMobileWaitlistArrow, HeaderMobileWaitlistLabel, HeaderMobileWaitlistLink, HeaderShell, Nav, NavBadge, NavBadgeLabel, Wordmark, } from './styles.js';
|
|
12
12
|
function MobileNavGroup({ label, items }) {
|
|
13
13
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
14
14
|
const panelId = `mobile-nav-group-${label.toLowerCase().replace(/\s+/gu, '-')}`;
|
|
15
|
-
return (_jsxs(HeaderMobileNavGroup, { children: [_jsxs(HeaderMobileNavGroupToggle, { type: "button", "aria-expanded": isExpanded, "aria-controls": panelId, onClick: () => setIsExpanded((currentValue) => !currentValue), children: [label, _jsx(HeaderMobileNavGroupChevron, { "$open": isExpanded, "aria-hidden": "true" })] }), _jsx(HeaderMobileNavGroupPanel, { "$open": isExpanded, id: panelId, children: _jsx(HeaderMobileNavGroupPanelInner, { "$open": isExpanded, children: _jsx(HeaderMobileNavSubList, { "aria-hidden": !isExpanded, children: items.map((item) => (_jsx(HeaderMobileNavSubLink, { href: item.href, "aria-label": item.ariaLabel, tabIndex: isExpanded ? undefined : -1, onClick: item.onClick, ...(item.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: item.label }, item.key))) }) }) })] }));
|
|
15
|
+
return (_jsxs(HeaderMobileNavGroup, { children: [_jsxs(HeaderMobileNavGroupToggle, { type: "button", "aria-expanded": isExpanded, "aria-controls": panelId, onClick: () => setIsExpanded((currentValue) => !currentValue), children: [label, _jsx(HeaderMobileNavGroupChevron, { "$open": isExpanded, "aria-hidden": "true" })] }), _jsx(HeaderMobileNavGroupPanel, { "$open": isExpanded, id: panelId, children: _jsx(HeaderMobileNavGroupPanelInner, { "$open": isExpanded, children: _jsx(HeaderMobileNavSubList, { "aria-hidden": !isExpanded, children: items.map((item) => item.disabled ? (_jsxs(HeaderMobileNavSubDisabledItem, { "aria-disabled": "true", "aria-label": `${item.label} ${item.badge ?? ''}`.trim(), children: [item.label, item.badge ? _jsx(HeaderMobileSoonBadge, { children: item.badge }) : null] }, item.key)) : (_jsx(HeaderMobileNavSubLink, { href: item.href, "aria-label": item.ariaLabel, tabIndex: isExpanded ? undefined : -1, onClick: item.onClick, ...(item.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: item.label }, item.key))) }) }) })] }));
|
|
16
16
|
}
|
|
17
17
|
export function Header({ brandWordmarkSrc, content, forceDocumentNavigation = false, homeHref, isExternalHref, mobilePresentation, navItems, presentation, shouldAnimatePresentation, waitlistCta, }) {
|
|
18
18
|
const brand = useOptionalBrand();
|
|
@@ -100,8 +100,16 @@ export function Header({ brandWordmarkSrc, content, forceDocumentNavigation = fa
|
|
|
100
100
|
const label = item.label;
|
|
101
101
|
if (item.children && item.children.length > 0) {
|
|
102
102
|
const dropdownItems = item.children
|
|
103
|
-
.filter((child) => Boolean(child.route))
|
|
103
|
+
.filter((child) => Boolean(child.route) || child.disabled)
|
|
104
104
|
.map((child) => {
|
|
105
|
+
if (child.disabled || !child.route) {
|
|
106
|
+
return {
|
|
107
|
+
key: child.key,
|
|
108
|
+
label: child.label,
|
|
109
|
+
disabled: true,
|
|
110
|
+
badge: child.badge,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
105
113
|
const childHref = resolveHref(child.route);
|
|
106
114
|
return {
|
|
107
115
|
key: child.key,
|
|
@@ -129,14 +137,22 @@ export function Header({ brandWordmarkSrc, content, forceDocumentNavigation = fa
|
|
|
129
137
|
: (event) => {
|
|
130
138
|
handleInternalNavigation(event, href);
|
|
131
139
|
}, ...(item.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: content_ }, item.label));
|
|
132
|
-
}) }), _jsx(HeaderMobileToggle, { type: "button", "aria-label": isMobileMenuOpen ? 'Close navigation menu' : 'Open navigation menu', "aria-expanded": isMobileMenuOpen, "aria-controls": "mobile-navigation", onClick: () => setIsMobileMenuOpen((currentValue) => !currentValue), children: _jsx(HeaderMobileToggleIcon, { "$open": isMobileMenuOpen, children: _jsx("span", {}) }) }), waitlistCta ? (_jsxs(HeaderAction, { "$variant": presentation.variant, href: resolveHref(waitlistCta.href), onClick: forceDocumentNavigation
|
|
140
|
+
}) }), _jsx(HeaderMobileToggle, { type: "button", "aria-label": isMobileMenuOpen ? 'Close navigation menu' : 'Open navigation menu', "aria-expanded": isMobileMenuOpen, "aria-controls": "mobile-navigation", onClick: () => setIsMobileMenuOpen((currentValue) => !currentValue), children: _jsx(HeaderMobileToggleIcon, { "$open": isMobileMenuOpen, children: _jsx("span", {}) }) }), waitlistCta?.disabled ? (_jsxs(HeaderAction, { as: "span", "$variant": presentation.variant, "$disabled": true, "aria-disabled": "true", children: [content.joinWaitlist, _jsx(HeaderActionArrow, { "aria-hidden": "true", children: "\u2192" })] })) : waitlistCta ? (_jsxs(HeaderAction, { "$variant": presentation.variant, href: resolveHref(waitlistCta.href), onClick: forceDocumentNavigation
|
|
133
141
|
? undefined
|
|
134
142
|
: (event) => handleWaitlistCtaClick(event, 'nav_desktop'), children: [content.joinWaitlist, _jsx(HeaderActionArrow, { "aria-hidden": "true", children: "\u2192" })] })) : null] }), isMobileMenuOpen ? (_jsx(HeaderMobileOverlay, { onClick: () => setIsMobileMenuOpen(false), children: _jsxs(HeaderMobileSheet, { id: "mobile-navigation", role: "dialog", "aria-modal": "true", "aria-label": "Navigation menu", onClick: (event) => event.stopPropagation(), children: [_jsx(HeaderMobileNav, { "aria-label": "Mobile navigation", children: enabledNavItems.map((item) => {
|
|
135
143
|
const label = item.label;
|
|
136
144
|
if (item.children && item.children.length > 0) {
|
|
137
145
|
const groupItems = item.children
|
|
138
|
-
.filter((child) => Boolean(child.route))
|
|
146
|
+
.filter((child) => Boolean(child.route) || child.disabled)
|
|
139
147
|
.map((child) => {
|
|
148
|
+
if (child.disabled || !child.route) {
|
|
149
|
+
return {
|
|
150
|
+
key: child.key,
|
|
151
|
+
label: child.label,
|
|
152
|
+
disabled: true,
|
|
153
|
+
badge: child.badge,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
140
156
|
const childHref = resolveHref(child.route);
|
|
141
157
|
return {
|
|
142
158
|
key: child.key,
|
|
@@ -165,7 +181,7 @@ export function Header({ brandWordmarkSrc, content, forceDocumentNavigation = fa
|
|
|
165
181
|
setIsMobileMenuOpen(false);
|
|
166
182
|
handleInternalNavigation(event, href);
|
|
167
183
|
}, ...(item.external ? { target: '_blank', rel: 'noreferrer' } : {}), children: [label, item.badge ? (_jsx(HeaderMobileSoonBadge, { children: item.badge })) : null] }, item.label));
|
|
168
|
-
}) }), _jsx(HeaderMobileLocaleSlot, { children: _jsx(LocaleSelector, { ariaLabel: content.localeSelectorAriaLabel, forceDocumentNavigation: forceDocumentNavigation, inlineMenu: true, menuPlacement: "down", onLocaleSelect: () => setIsMobileMenuOpen(false) }) }), waitlistCta ? (_jsxs(HeaderMobileWaitlistLink, { href: resolveHref(waitlistCta.href), onClick: forceDocumentNavigation
|
|
184
|
+
}) }), _jsx(HeaderMobileLocaleSlot, { children: _jsx(LocaleSelector, { ariaLabel: content.localeSelectorAriaLabel, forceDocumentNavigation: forceDocumentNavigation, inlineMenu: true, menuPlacement: "down", onLocaleSelect: () => setIsMobileMenuOpen(false) }) }), waitlistCta?.disabled ? (_jsxs(HeaderMobileWaitlistLink, { as: "span", "$disabled": true, "aria-disabled": "true", children: [_jsx(HeaderMobileWaitlistLabel, { children: content.waitlistEmailCta }), _jsx(HeaderMobileWaitlistArrow, { "aria-hidden": "true", children: "\u2192" })] })) : waitlistCta ? (_jsxs(HeaderMobileWaitlistLink, { href: resolveHref(waitlistCta.href), onClick: forceDocumentNavigation
|
|
169
185
|
? () => setIsMobileMenuOpen(false)
|
|
170
186
|
: (event) => handleWaitlistCtaClick(event, 'nav_mobile'), children: [_jsx(HeaderMobileWaitlistLabel, { children: content.waitlistEmailCta }), _jsx(HeaderMobileWaitlistArrow, { "aria-hidden": "true", children: "\u2192" })] })) : null] }) })) : null] }));
|
|
171
187
|
}
|
package/dist/Header/styles.d.ts
CHANGED
|
@@ -77,6 +77,12 @@ export declare const NavDropdownList: import("styled-components/dist/types").ISt
|
|
|
77
77
|
export declare const NavDropdownOption: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "style"> & {
|
|
78
78
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
79
79
|
}> & string;
|
|
80
|
+
export declare const NavDropdownDisabledOption: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
81
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
82
|
+
}> & string;
|
|
83
|
+
export declare const NavDropdownOptionBadge: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
84
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
85
|
+
}> & string;
|
|
80
86
|
export declare const NavBadge: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
81
87
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
82
88
|
}> & string;
|
|
@@ -87,6 +93,7 @@ export declare const HeaderAction: import("styled-components/dist/types").IStyle
|
|
|
87
93
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
88
94
|
}, {
|
|
89
95
|
$variant: HeaderPresentationVariant;
|
|
96
|
+
$disabled?: boolean | undefined;
|
|
90
97
|
}>> & string;
|
|
91
98
|
export declare const HeaderActionArrow: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
92
99
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
@@ -138,6 +145,9 @@ export declare const HeaderMobileNavSubList: import("styled-components/dist/type
|
|
|
138
145
|
export declare const HeaderMobileNavSubLink: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "style"> & {
|
|
139
146
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
140
147
|
}> & string;
|
|
148
|
+
export declare const HeaderMobileNavSubDisabledItem: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
149
|
+
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
150
|
+
}> & string;
|
|
141
151
|
export declare const HeaderMobileNavDisabledItem: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
142
152
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
143
153
|
}> & string;
|
|
@@ -147,9 +157,11 @@ export declare const HeaderMobileSoonBadge: import("styled-components/dist/types
|
|
|
147
157
|
export declare const HeaderMobileLocaleSlot: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "style"> & {
|
|
148
158
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
149
159
|
}> & string;
|
|
150
|
-
export declare const HeaderMobileWaitlistLink: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "style"> & {
|
|
160
|
+
export declare const HeaderMobileWaitlistLink: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Merged<Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "style"> & {
|
|
151
161
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
152
|
-
}
|
|
162
|
+
}, {
|
|
163
|
+
$disabled?: boolean | undefined;
|
|
164
|
+
}>> & string;
|
|
153
165
|
export declare const HeaderMobileWaitlistLabel: import("styled-components/dist/types").IStyledComponentBase<"web", Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "style"> & {
|
|
154
166
|
style?: import("react").CSSProperties | import("styled-components/dist/types").CSSPropertiesWithVars | undefined;
|
|
155
167
|
}> & string;
|
package/dist/Header/styles.js
CHANGED
|
@@ -398,6 +398,43 @@ export const NavDropdownOption = styled.a `
|
|
|
398
398
|
outline-offset: -3px;
|
|
399
399
|
}
|
|
400
400
|
`;
|
|
401
|
+
/* A greyed dropdown entry: the option's shape without its hover or link
|
|
402
|
+
semantics, the badge in the disabled nav item's muted pill. */
|
|
403
|
+
export const NavDropdownDisabledOption = styled.span `
|
|
404
|
+
display: flex;
|
|
405
|
+
align-items: center;
|
|
406
|
+
gap: 8px;
|
|
407
|
+
width: 100%;
|
|
408
|
+
min-height: 44px;
|
|
409
|
+
padding: 12px;
|
|
410
|
+
border-radius: 8px;
|
|
411
|
+
color: inherit;
|
|
412
|
+
font-size: 1rem;
|
|
413
|
+
font-weight: ${({ theme }) => theme.fontWeight.semibold};
|
|
414
|
+
letter-spacing: -0.02em;
|
|
415
|
+
line-height: ${({ theme }) => theme.typography.lineHeight.tight};
|
|
416
|
+
text-align: left;
|
|
417
|
+
white-space: nowrap;
|
|
418
|
+
opacity: 0.5;
|
|
419
|
+
cursor: default;
|
|
420
|
+
`;
|
|
421
|
+
export const NavDropdownOptionBadge = styled.span `
|
|
422
|
+
display: inline-flex;
|
|
423
|
+
align-items: center;
|
|
424
|
+
justify-content: center;
|
|
425
|
+
min-width: 44px;
|
|
426
|
+
height: 18px;
|
|
427
|
+
padding: 0 8px;
|
|
428
|
+
border-radius: ${({ theme }) => theme.borderRadius.pill};
|
|
429
|
+
background: rgb(0 0 0 / 0.08);
|
|
430
|
+
color: currentColor;
|
|
431
|
+
font-size: 0.5625rem;
|
|
432
|
+
font-weight: ${({ theme }) => theme.fontWeight.black};
|
|
433
|
+
letter-spacing: 0.1em;
|
|
434
|
+
line-height: ${({ theme }) => theme.typography.lineHeight.tight};
|
|
435
|
+
text-transform: uppercase;
|
|
436
|
+
white-space: nowrap;
|
|
437
|
+
`;
|
|
401
438
|
export const NavBadge = styled.span `
|
|
402
439
|
display: inline-flex;
|
|
403
440
|
align-items: center;
|
|
@@ -458,6 +495,20 @@ export const HeaderAction = styled.a `
|
|
|
458
495
|
outline-offset: 3px;
|
|
459
496
|
}
|
|
460
497
|
|
|
498
|
+
/* Disabled: rendered as a span, so no hover lift and no focus ring;
|
|
499
|
+
dimmed in place so the layout does not move when it goes live. */
|
|
500
|
+
${({ $disabled }) => $disabled
|
|
501
|
+
? `
|
|
502
|
+
opacity: 0.55;
|
|
503
|
+
cursor: default;
|
|
504
|
+
|
|
505
|
+
&:hover {
|
|
506
|
+
opacity: 0.55;
|
|
507
|
+
transform: none;
|
|
508
|
+
}
|
|
509
|
+
`
|
|
510
|
+
: ''}
|
|
511
|
+
|
|
461
512
|
${({ theme }) => theme.mediaQueries.lg} {
|
|
462
513
|
display: inline-flex;
|
|
463
514
|
min-width: ${({ theme }) => theme.layout.headerActionWidth};
|
|
@@ -698,6 +749,22 @@ export const HeaderMobileNavSubLink = styled.a `
|
|
|
698
749
|
outline-offset: -4px;
|
|
699
750
|
}
|
|
700
751
|
`;
|
|
752
|
+
/* A greyed entry inside an expanded mobile group: the sub-link's shape,
|
|
753
|
+
dimmed, with no hover. */
|
|
754
|
+
export const HeaderMobileNavSubDisabledItem = styled.span `
|
|
755
|
+
display: flex;
|
|
756
|
+
align-items: center;
|
|
757
|
+
gap: 6px;
|
|
758
|
+
min-height: 40px;
|
|
759
|
+
padding: 8px 12px;
|
|
760
|
+
color: ${({ theme }) => theme.colors.textDarkSubtle};
|
|
761
|
+
font-size: ${({ theme }) => theme.typography.fontSize.bodySm};
|
|
762
|
+
font-weight: ${({ theme }) => theme.fontWeight.semibold};
|
|
763
|
+
letter-spacing: -0.02em;
|
|
764
|
+
line-height: ${({ theme }) => theme.typography.lineHeight.compact};
|
|
765
|
+
opacity: 0.55;
|
|
766
|
+
cursor: default;
|
|
767
|
+
`;
|
|
701
768
|
export const HeaderMobileNavDisabledItem = styled.span `
|
|
702
769
|
display: inline-flex;
|
|
703
770
|
align-items: center;
|
|
@@ -751,6 +818,8 @@ export const HeaderMobileWaitlistLink = styled.a `
|
|
|
751
818
|
color: ${({ theme }) => theme.colors.textDarkSubtle};
|
|
752
819
|
text-decoration: none;
|
|
753
820
|
overflow: hidden;
|
|
821
|
+
opacity: ${({ $disabled }) => ($disabled ? 0.55 : 1)};
|
|
822
|
+
cursor: ${({ $disabled }) => ($disabled ? 'default' : 'pointer')};
|
|
754
823
|
|
|
755
824
|
&:focus-visible {
|
|
756
825
|
outline: 2px solid ${({ theme }) => theme.colors.ink};
|