@cronos-labs/ui 0.8.0 → 0.9.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/README.md +60 -16
- package/dist/Footer/index.d.ts +7 -38
- package/dist/Footer/index.js +9 -63
- package/dist/Footer/static.d.ts +16 -0
- package/dist/Footer/static.js +11 -0
- package/dist/Footer/styles.d.ts +2 -2
- package/dist/Footer/styles.js +29 -27
- package/dist/Footer/types.d.ts +41 -0
- package/dist/Footer/types.js +1 -0
- package/dist/Footer/view.d.ts +2 -0
- package/dist/Footer/view.js +55 -0
- package/dist/Header/NavDropdown.d.ts +1 -1
- package/dist/Header/NavDropdown.js +3 -3
- package/dist/Header/index.d.ts +7 -56
- package/dist/Header/index.js +9 -186
- package/dist/Header/static.d.ts +15 -0
- package/dist/Header/static.js +11 -0
- package/dist/Header/styles.d.ts +4 -1
- package/dist/Header/styles.js +27 -4
- package/dist/Header/types.d.ts +61 -0
- package/dist/Header/types.js +1 -0
- package/dist/Header/view.d.ts +2 -0
- package/dist/Header/view.js +184 -0
- package/dist/LocaleSelector/index.d.ts +8 -9
- package/dist/LocaleSelector/index.js +10 -61
- package/dist/LocaleSelector/view.d.ts +8 -0
- package/dist/LocaleSelector/view.js +59 -0
- package/dist/config/eslintConfig.d.ts +17 -0
- package/dist/config/eslintConfig.js +52 -23
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/navigation/context.d.ts +26 -0
- package/dist/navigation/context.js +18 -0
- package/dist/navigation/router.d.ts +11 -0
- package/dist/navigation/router.js +25 -0
- package/dist/navigation/static.d.ts +15 -0
- package/dist/navigation/static.js +19 -0
- package/dist/navigationContent.d.ts +6 -0
- package/dist/navigationContent.js +27 -0
- package/package.json +2 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare function LocaleSelector(
|
|
9
|
-
export {};
|
|
1
|
+
import { type LocaleSelectorProps } from './view.js';
|
|
2
|
+
export type { LocaleSelectorProps } from './view.js';
|
|
3
|
+
/**
|
|
4
|
+
* Standalone locale selector for apps with a react-router `<Router>` above it.
|
|
5
|
+
* Inside `Header`/`Footer` the view is rendered directly and shares their
|
|
6
|
+
* navigation location.
|
|
7
|
+
*/
|
|
8
|
+
export declare function LocaleSelector(props: LocaleSelectorProps): React.JSX.Element;
|
|
@@ -1,62 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export function LocaleSelector(
|
|
10
|
-
|
|
11
|
-
const selectorRef = useRef(null);
|
|
12
|
-
const locale = useCurrentLocale();
|
|
13
|
-
const location = useLocation();
|
|
14
|
-
const navigate = useNavigate();
|
|
15
|
-
const menuId = 'footer-locale-menu';
|
|
16
|
-
const currentLocaleLabel = localeConfig.locales[locale].selectorLabel;
|
|
17
|
-
const localeOptions = localeConfig.supportedLocales.map((optionLocale) => ({
|
|
18
|
-
isCurrent: optionLocale === locale,
|
|
19
|
-
label: localeConfig.locales[optionLocale].selectorLabel,
|
|
20
|
-
locale: optionLocale,
|
|
21
|
-
}));
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
setIsOpen(false);
|
|
24
|
-
}, [location.pathname, location.hash]);
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
if (!isOpen)
|
|
27
|
-
return;
|
|
28
|
-
const handlePointerDown = (event) => {
|
|
29
|
-
if (event.target instanceof Node && selectorRef.current?.contains(event.target)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
setIsOpen(false);
|
|
33
|
-
};
|
|
34
|
-
const handleKeyDown = (event) => {
|
|
35
|
-
if (event.key === 'Escape') {
|
|
36
|
-
setIsOpen(false);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
document.addEventListener('pointerdown', handlePointerDown);
|
|
40
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
41
|
-
return () => {
|
|
42
|
-
document.removeEventListener('pointerdown', handlePointerDown);
|
|
43
|
-
document.removeEventListener('keydown', handleKeyDown);
|
|
44
|
-
};
|
|
45
|
-
}, [isOpen]);
|
|
46
|
-
const buildLocaleTarget = (targetLocale) => {
|
|
47
|
-
const targetPathname = buildLocalizedPath(stripLocalePrefix(location.pathname), targetLocale);
|
|
48
|
-
return `${targetPathname}${location.search}${location.hash}`;
|
|
49
|
-
};
|
|
50
|
-
const handleLocaleSelect = (targetLocale) => {
|
|
51
|
-
const targetHref = buildLocaleTarget(targetLocale);
|
|
52
|
-
setStoredLocalePreference(targetLocale);
|
|
53
|
-
setIsOpen(false);
|
|
54
|
-
onLocaleSelect?.();
|
|
55
|
-
if (forceDocumentNavigation) {
|
|
56
|
-
window.location.assign(targetHref);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
void navigate(targetHref);
|
|
60
|
-
};
|
|
61
|
-
return (_jsxs(LocaleSelectorRoot, { ref: selectorRef, "$inlineMenu": inlineMenu, children: [_jsxs(LocaleSelectorButton, { type: "button", "aria-label": ariaLabel, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, onClick: () => setIsOpen((currentValue) => !currentValue), children: [_jsx(LocaleSelectorIcon, { "aria-hidden": "true" }), _jsx(LocaleSelectorText, { children: currentLocaleLabel }), _jsx(LocaleSelectorChevron, { "$open": isOpen, "aria-hidden": "true" })] }), isOpen ? (_jsx(LocaleSelectorMenu, { id: menuId, role: "menu", "aria-label": "Language options", "$inline": inlineMenu, "$placement": menuPlacement, children: _jsx(LocaleSelectorOptionList, { children: localeOptions.map((option) => (_jsx(LocaleSelectorOption, { type: "button", role: "menuitemradio", "aria-checked": option.isCurrent, "$active": option.isCurrent, onClick: () => handleLocaleSelect(option.locale), children: _jsx("span", { children: option.label }) }, option.label))) }) })) : null] }));
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { RouterNavigationProvider } from '../navigation/router.js';
|
|
3
|
+
import { LocaleSelectorView } from './view.js';
|
|
4
|
+
/**
|
|
5
|
+
* Standalone locale selector for apps with a react-router `<Router>` above it.
|
|
6
|
+
* Inside `Header`/`Footer` the view is rendered directly and shares their
|
|
7
|
+
* navigation location.
|
|
8
|
+
*/
|
|
9
|
+
export function LocaleSelector(props) {
|
|
10
|
+
return (_jsx(RouterNavigationProvider, { children: _jsx(LocaleSelectorView, { ...props }) }));
|
|
62
11
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface LocaleSelectorProps {
|
|
2
|
+
ariaLabel: string;
|
|
3
|
+
forceDocumentNavigation?: boolean;
|
|
4
|
+
inlineMenu?: boolean;
|
|
5
|
+
menuPlacement?: 'down' | 'up';
|
|
6
|
+
onLocaleSelect?: (() => void) | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function LocaleSelectorView({ ariaLabel, forceDocumentNavigation, inlineMenu, menuPlacement, onLocaleSelect, }: LocaleSelectorProps): React.JSX.Element;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useRef, useState } from 'react';
|
|
3
|
+
import { localeConfig } from '../locale/config.js';
|
|
4
|
+
import { setStoredLocalePreference } from '../locale/localePreference.js';
|
|
5
|
+
import { buildLocalizedPath, stripLocalePrefix } from '../locale/routes.js';
|
|
6
|
+
import { useNavigationLocation } from '../navigation/context.js';
|
|
7
|
+
import { LocaleSelectorButton, LocaleSelectorChevron, LocaleSelectorIcon, LocaleSelectorMenu, LocaleSelectorOption, LocaleSelectorOptionList, LocaleSelectorRoot, LocaleSelectorText, } from './styles.js';
|
|
8
|
+
export function LocaleSelectorView({ ariaLabel, forceDocumentNavigation = false, inlineMenu = false, menuPlacement = 'up', onLocaleSelect, }) {
|
|
9
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
const selectorRef = useRef(null);
|
|
11
|
+
const { locale, pathname, search, hash, navigate } = useNavigationLocation();
|
|
12
|
+
const menuId = useId();
|
|
13
|
+
const currentLocaleLabel = localeConfig.locales[locale].selectorLabel;
|
|
14
|
+
const localeOptions = localeConfig.supportedLocales.map((optionLocale) => ({
|
|
15
|
+
isCurrent: optionLocale === locale,
|
|
16
|
+
label: localeConfig.locales[optionLocale].selectorLabel,
|
|
17
|
+
locale: optionLocale,
|
|
18
|
+
}));
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setIsOpen(false);
|
|
21
|
+
}, [pathname, hash]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!isOpen)
|
|
24
|
+
return;
|
|
25
|
+
const handlePointerDown = (event) => {
|
|
26
|
+
if (event.target instanceof Node && selectorRef.current?.contains(event.target)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
setIsOpen(false);
|
|
30
|
+
};
|
|
31
|
+
const handleKeyDown = (event) => {
|
|
32
|
+
if (event.key === 'Escape') {
|
|
33
|
+
setIsOpen(false);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
document.addEventListener('pointerdown', handlePointerDown);
|
|
37
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener('pointerdown', handlePointerDown);
|
|
40
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
41
|
+
};
|
|
42
|
+
}, [isOpen]);
|
|
43
|
+
const buildLocaleTarget = (targetLocale) => {
|
|
44
|
+
const targetPathname = buildLocalizedPath(stripLocalePrefix(pathname), targetLocale);
|
|
45
|
+
return `${targetPathname}${search}${hash}`;
|
|
46
|
+
};
|
|
47
|
+
const handleLocaleSelect = (targetLocale) => {
|
|
48
|
+
const targetHref = buildLocaleTarget(targetLocale);
|
|
49
|
+
setStoredLocalePreference(targetLocale);
|
|
50
|
+
setIsOpen(false);
|
|
51
|
+
onLocaleSelect?.();
|
|
52
|
+
if (forceDocumentNavigation) {
|
|
53
|
+
window.location.assign(targetHref);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
navigate(targetHref);
|
|
57
|
+
};
|
|
58
|
+
return (_jsxs(LocaleSelectorRoot, { ref: selectorRef, "$inlineMenu": inlineMenu, children: [_jsxs(LocaleSelectorButton, { type: "button", "aria-label": ariaLabel, "aria-haspopup": "menu", "aria-expanded": isOpen, "aria-controls": menuId, onClick: () => setIsOpen((currentValue) => !currentValue), children: [_jsx(LocaleSelectorIcon, { "aria-hidden": "true" }), _jsx(LocaleSelectorText, { children: currentLocaleLabel }), _jsx(LocaleSelectorChevron, { "$open": isOpen, "aria-hidden": "true" })] }), isOpen ? (_jsx(LocaleSelectorMenu, { id: menuId, role: "menu", "aria-label": "Language options", "$inline": inlineMenu, "$placement": menuPlacement, children: _jsx(LocaleSelectorOptionList, { children: localeOptions.map((option) => (_jsx(LocaleSelectorOption, { type: "button", role: "menuitemradio", "aria-checked": option.isCurrent, "$active": option.isCurrent, onClick: () => handleLocaleSelect(option.locale), children: _jsx("span", { children: option.label }) }, option.label))) }) })) : null] }));
|
|
59
|
+
}
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
export interface ReactEslintConfigOptions {
|
|
2
2
|
ignores?: string[];
|
|
3
|
+
/**
|
|
4
|
+
* Globs, relative to `tsconfigRootDir`, for files that belong to no
|
|
5
|
+
* tsconfig but should still be linted — config files at the repo root, and
|
|
6
|
+
* build scripts under `scripts/`. typescript-eslint reads this from inside
|
|
7
|
+
* `projectService`, so passing it as a sibling silently does nothing.
|
|
8
|
+
*/
|
|
3
9
|
allowDefaultProject?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Globs for plain Node files — build scripts, codegen — that should be
|
|
12
|
+
* linted but are not part of the React app: Node globals instead of
|
|
13
|
+
* browser, and no type-aware rules, since they have no types to check and
|
|
14
|
+
* every value reads as `any`. Defaults to none, so a consumer opts in.
|
|
15
|
+
*
|
|
16
|
+
* These are folded into `allowDefaultProject`, which typescript-eslint
|
|
17
|
+
* refuses to match with `**` — use a single-level glob such as
|
|
18
|
+
* `scripts/*.mjs`.
|
|
19
|
+
*/
|
|
20
|
+
nodeScripts?: string[];
|
|
4
21
|
tsconfigRootDir: string;
|
|
5
22
|
}
|
|
6
23
|
export declare const createReactEslintConfig: (options: ReactEslintConfigOptions) => import("typescript-eslint").FlatConfig.ConfigArray;
|
|
@@ -4,28 +4,57 @@ import tseslint from 'typescript-eslint';
|
|
|
4
4
|
import reactHooks from 'eslint-plugin-react-hooks';
|
|
5
5
|
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
6
6
|
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
7
|
-
export const createReactEslintConfig = (options) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export const createReactEslintConfig = (options) => {
|
|
8
|
+
// Node scripts are outside every tsconfig by definition, so they need to be
|
|
9
|
+
// in the default-project allowlist as well as having type-aware rules off.
|
|
10
|
+
// Folding that in here means a consumer names them once.
|
|
11
|
+
const allowDefaultProject = [
|
|
12
|
+
...(options.allowDefaultProject ?? ['*.{js,mjs,cjs,ts}']),
|
|
13
|
+
...(options.nodeScripts ?? []),
|
|
14
|
+
];
|
|
15
|
+
return tseslint.config({
|
|
16
|
+
ignores: options.ignores ?? ['dist', 'coverage', 'node_modules', 'eslint.config.mjs'],
|
|
17
|
+
}, js.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked, {
|
|
18
|
+
languageOptions: {
|
|
19
|
+
ecmaVersion: 2023,
|
|
20
|
+
globals: {
|
|
21
|
+
...globals.browser,
|
|
22
|
+
},
|
|
23
|
+
parserOptions: {
|
|
24
|
+
// `allowDefaultProject` is an option *of* the project service, not a
|
|
25
|
+
// sibling of it. Passing `projectService: true` alongside it left
|
|
26
|
+
// the allowlist unread, so any file outside a tsconfig was simply
|
|
27
|
+
// skipped rather than linted.
|
|
28
|
+
projectService: {
|
|
29
|
+
allowDefaultProject,
|
|
30
|
+
},
|
|
31
|
+
tsconfigRootDir: options.tsconfigRootDir,
|
|
32
|
+
},
|
|
14
33
|
},
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
tsconfigRootDir: options.tsconfigRootDir,
|
|
34
|
+
plugins: {
|
|
35
|
+
'react-hooks': reactHooks,
|
|
36
|
+
'react-refresh': reactRefresh,
|
|
19
37
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
rules: {
|
|
39
|
+
...reactHooks.configs.recommended.rules,
|
|
40
|
+
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
|
41
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
42
|
+
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
// Plain Node files, opted into by the consumer. Type-aware rules are
|
|
46
|
+
// switched off for them with typescript-eslint's own
|
|
47
|
+
// `disableTypeChecked`: a .mjs build script has no types, so every value
|
|
48
|
+
// reads as `any` and the unsafe-* rules fire on correct code.
|
|
49
|
+
...(options.nodeScripts?.length
|
|
50
|
+
? [
|
|
51
|
+
{
|
|
52
|
+
files: options.nodeScripts,
|
|
53
|
+
...tseslint.configs.disableTypeChecked,
|
|
54
|
+
languageOptions: {
|
|
55
|
+
globals: { ...globals.node },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
: []), eslintConfigPrettier);
|
|
60
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,11 @@ export { Seo, type RouteSeoMeta } from './Seo/index.js';
|
|
|
11
11
|
export { LocaleSelector } from './LocaleSelector/index.js';
|
|
12
12
|
export { Header, type HeaderPresentationVariant, type HeaderPresentationState, type HeaderNavChild, type HeaderNavItem, type HeaderContent, type HeaderWaitlistCta, } from './Header/index.js';
|
|
13
13
|
export { normalizePathname } from './Header/helper.js';
|
|
14
|
+
export { HeaderStatic, type HeaderStaticProps } from './Header/static.js';
|
|
14
15
|
export { Footer, type FooterLinkItem, type FooterLinkGroup, type FooterLegalLinkItem, type FooterContent, } from './Footer/index.js';
|
|
16
|
+
export { FooterStatic, type FooterStaticProps } from './Footer/static.js';
|
|
17
|
+
export { useNavigationLocation, type NavigationLocation } from './navigation/context.js';
|
|
18
|
+
export { StaticNavigationProvider } from './navigation/static.js';
|
|
15
19
|
export { navigationContent } from './navigationContent.js';
|
|
16
20
|
export type { NavigationContent } from './navigationContent.js';
|
|
17
21
|
export { interpolateMessageTemplate } from './format.js';
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,11 @@ export { Seo } from './Seo/index.js';
|
|
|
9
9
|
export { LocaleSelector } from './LocaleSelector/index.js';
|
|
10
10
|
export { Header, } from './Header/index.js';
|
|
11
11
|
export { normalizePathname } from './Header/helper.js';
|
|
12
|
+
export { HeaderStatic } from './Header/static.js';
|
|
12
13
|
export { Footer, } from './Footer/index.js';
|
|
14
|
+
export { FooterStatic } from './Footer/static.js';
|
|
15
|
+
export { useNavigationLocation } from './navigation/context.js';
|
|
16
|
+
export { StaticNavigationProvider } from './navigation/static.js';
|
|
13
17
|
export { navigationContent } from './navigationContent.js';
|
|
14
18
|
export { interpolateMessageTemplate } from './format.js';
|
|
15
19
|
export { SectionAnchorNav } from './SectionAnchorNav/index.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AppLocale } from '../locale/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The slice of router state the chrome components read: where the page is
|
|
4
|
+
* and how to move it. `Header`, `Footer` and `LocaleSelector` consume it, so
|
|
5
|
+
* the same view code runs under react-router (see ./router.tsx) and on the
|
|
6
|
+
* static Astro sites, which have no router and pass the location as props
|
|
7
|
+
* (see ./static.tsx).
|
|
8
|
+
*/
|
|
9
|
+
export interface NavigationLocation {
|
|
10
|
+
locale: AppLocale;
|
|
11
|
+
pathname: string;
|
|
12
|
+
search: string;
|
|
13
|
+
hash: string;
|
|
14
|
+
/** Client-side navigation when a router is present; a document load otherwise. */
|
|
15
|
+
navigate: (to: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const NavigationLocationProvider: import("react").Provider<NavigationLocation | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Reads the current navigation location. Throws outside a provider: a chrome
|
|
20
|
+
* component rendered with neither a router nor explicit location props would
|
|
21
|
+
* otherwise build every href against a guessed locale.
|
|
22
|
+
*
|
|
23
|
+
* @returns the current location and a navigate function
|
|
24
|
+
* @throws {Error} when no `Header`/`Footer` wrapper or `StaticNavigationProvider` is above
|
|
25
|
+
*/
|
|
26
|
+
export declare const useNavigationLocation: () => NavigationLocation;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
const NavigationLocationContext = createContext(null);
|
|
3
|
+
export const NavigationLocationProvider = NavigationLocationContext.Provider;
|
|
4
|
+
/**
|
|
5
|
+
* Reads the current navigation location. Throws outside a provider: a chrome
|
|
6
|
+
* component rendered with neither a router nor explicit location props would
|
|
7
|
+
* otherwise build every href against a guessed locale.
|
|
8
|
+
*
|
|
9
|
+
* @returns the current location and a navigate function
|
|
10
|
+
* @throws {Error} when no `Header`/`Footer` wrapper or `StaticNavigationProvider` is above
|
|
11
|
+
*/
|
|
12
|
+
export const useNavigationLocation = () => {
|
|
13
|
+
const value = useContext(NavigationLocationContext);
|
|
14
|
+
if (value === null) {
|
|
15
|
+
throw new Error('No navigation location: render <Header>/<Footer> inside a react-router <Router>, or use <HeaderStatic>/<FooterStatic> with explicit locale and pathname.');
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
interface RouterNavigationProviderProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Location backed by react-router. The only module the chrome components
|
|
7
|
+
* reach react-router through, so a consumer that imports the static variants
|
|
8
|
+
* never pulls the router into its bundle.
|
|
9
|
+
*/
|
|
10
|
+
export declare function RouterNavigationProvider({ children, }: RouterNavigationProviderProps): React.JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
4
|
+
import { useCurrentLocale } from '../locale/hooks.js';
|
|
5
|
+
import { NavigationLocationProvider } from './context.js';
|
|
6
|
+
/**
|
|
7
|
+
* Location backed by react-router. The only module the chrome components
|
|
8
|
+
* reach react-router through, so a consumer that imports the static variants
|
|
9
|
+
* never pulls the router into its bundle.
|
|
10
|
+
*/
|
|
11
|
+
export function RouterNavigationProvider({ children, }) {
|
|
12
|
+
const locale = useCurrentLocale();
|
|
13
|
+
const location = useLocation();
|
|
14
|
+
const navigate = useNavigate();
|
|
15
|
+
const value = useMemo(() => ({
|
|
16
|
+
locale,
|
|
17
|
+
pathname: location.pathname,
|
|
18
|
+
search: location.search,
|
|
19
|
+
hash: location.hash,
|
|
20
|
+
navigate: (to) => {
|
|
21
|
+
void navigate(to);
|
|
22
|
+
},
|
|
23
|
+
}), [locale, location.hash, location.pathname, location.search, navigate]);
|
|
24
|
+
return _jsx(NavigationLocationProvider, { value: value, children: children });
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { AppLocale } from '../locale/types.js';
|
|
3
|
+
interface StaticNavigationProviderProps {
|
|
4
|
+
locale: AppLocale;
|
|
5
|
+
pathname: string;
|
|
6
|
+
search?: string | undefined;
|
|
7
|
+
hash?: string | undefined;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Location for sites without a client router: every navigation is a full
|
|
12
|
+
* document load, so `navigate` is `window.location.assign`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function StaticNavigationProvider({ locale, pathname, search, hash, children, }: StaticNavigationProviderProps): React.JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { NavigationLocationProvider } from './context.js';
|
|
4
|
+
/**
|
|
5
|
+
* Location for sites without a client router: every navigation is a full
|
|
6
|
+
* document load, so `navigate` is `window.location.assign`.
|
|
7
|
+
*/
|
|
8
|
+
export function StaticNavigationProvider({ locale, pathname, search = '', hash = '', children, }) {
|
|
9
|
+
const value = useMemo(() => ({
|
|
10
|
+
locale,
|
|
11
|
+
pathname,
|
|
12
|
+
search,
|
|
13
|
+
hash,
|
|
14
|
+
navigate: (to) => {
|
|
15
|
+
window.location.assign(to);
|
|
16
|
+
},
|
|
17
|
+
}), [hash, locale, pathname, search]);
|
|
18
|
+
return _jsx(NavigationLocationProvider, { value: value, children: children });
|
|
19
|
+
}
|
|
@@ -8,6 +8,12 @@ export interface NavigationContent {
|
|
|
8
8
|
joinWaitlist: string;
|
|
9
9
|
waitlistEmailCta: string;
|
|
10
10
|
soon: string;
|
|
11
|
+
/** Label of the Cronos Launch entry (a product name; not translated). */
|
|
12
|
+
launch: string;
|
|
13
|
+
/** Label of the Network dropdown. */
|
|
14
|
+
network: string;
|
|
15
|
+
/** Label of the skip-to-content link the Header renders when given. */
|
|
16
|
+
skipToContent: string;
|
|
11
17
|
localeSelectorAriaLabel: string;
|
|
12
18
|
};
|
|
13
19
|
footer: {
|
|
@@ -9,6 +9,9 @@ export const navigationContent = {
|
|
|
9
9
|
joinWaitlist: 'Join Waitlist',
|
|
10
10
|
waitlistEmailCta: 'Enter your email to join waitlist',
|
|
11
11
|
soon: 'Soon',
|
|
12
|
+
launch: 'Launch',
|
|
13
|
+
network: 'Network',
|
|
14
|
+
skipToContent: 'Skip to content',
|
|
12
15
|
localeSelectorAriaLabel: 'Select language',
|
|
13
16
|
},
|
|
14
17
|
footer: {
|
|
@@ -70,6 +73,9 @@ export const navigationContent = {
|
|
|
70
73
|
joinWaitlist: 'Join Waitlist',
|
|
71
74
|
waitlistEmailCta: 'Enter your email to join waitlist',
|
|
72
75
|
soon: 'Soon',
|
|
76
|
+
launch: 'Launch',
|
|
77
|
+
network: 'Network',
|
|
78
|
+
skipToContent: 'Skip to content',
|
|
73
79
|
localeSelectorAriaLabel: 'Select language',
|
|
74
80
|
},
|
|
75
81
|
footer: {
|
|
@@ -131,6 +137,9 @@ export const navigationContent = {
|
|
|
131
137
|
joinWaitlist: 'Join Waitlist',
|
|
132
138
|
waitlistEmailCta: 'Enter your email to join waitlist',
|
|
133
139
|
soon: 'Soon',
|
|
140
|
+
launch: 'Launch',
|
|
141
|
+
network: 'Network',
|
|
142
|
+
skipToContent: 'Skip to content',
|
|
134
143
|
localeSelectorAriaLabel: 'Select language',
|
|
135
144
|
},
|
|
136
145
|
footer: {
|
|
@@ -192,6 +201,9 @@ export const navigationContent = {
|
|
|
192
201
|
joinWaitlist: 'Join Waitlist',
|
|
193
202
|
waitlistEmailCta: 'Enter your email to join waitlist',
|
|
194
203
|
soon: 'Soon',
|
|
204
|
+
launch: 'Launch',
|
|
205
|
+
network: 'Network',
|
|
206
|
+
skipToContent: 'Skip to content',
|
|
195
207
|
localeSelectorAriaLabel: 'Select language',
|
|
196
208
|
},
|
|
197
209
|
footer: {
|
|
@@ -253,6 +265,9 @@ export const navigationContent = {
|
|
|
253
265
|
joinWaitlist: 'Entrar na lista',
|
|
254
266
|
waitlistEmailCta: 'Digite seu email para entrar na lista',
|
|
255
267
|
soon: 'Em breve',
|
|
268
|
+
launch: 'Launch',
|
|
269
|
+
network: 'Rede',
|
|
270
|
+
skipToContent: 'Pular para o conteúdo',
|
|
256
271
|
localeSelectorAriaLabel: 'Selecionar idioma',
|
|
257
272
|
},
|
|
258
273
|
footer: {
|
|
@@ -314,6 +329,9 @@ export const navigationContent = {
|
|
|
314
329
|
joinWaitlist: 'Gabung Daftar Tunggu',
|
|
315
330
|
waitlistEmailCta: 'Masukkan email Anda',
|
|
316
331
|
soon: 'segera',
|
|
332
|
+
launch: 'Launch',
|
|
333
|
+
network: 'Jaringan',
|
|
334
|
+
skipToContent: 'Langsung ke konten',
|
|
317
335
|
localeSelectorAriaLabel: 'Pilih bahasa',
|
|
318
336
|
},
|
|
319
337
|
footer: {
|
|
@@ -375,6 +393,9 @@ export const navigationContent = {
|
|
|
375
393
|
joinWaitlist: '대기 명단 등록',
|
|
376
394
|
waitlistEmailCta: '이메일을 입력하세요',
|
|
377
395
|
soon: '곧 출시',
|
|
396
|
+
launch: 'Launch',
|
|
397
|
+
network: '네트워크',
|
|
398
|
+
skipToContent: '본문으로 건너뛰기',
|
|
378
399
|
localeSelectorAriaLabel: '언어 선택',
|
|
379
400
|
},
|
|
380
401
|
footer: {
|
|
@@ -436,6 +457,9 @@ export const navigationContent = {
|
|
|
436
457
|
joinWaitlist: 'Únete a la lista de espera',
|
|
437
458
|
waitlistEmailCta: 'Ingresa tu correo electrónico para unirte a la lista de espera',
|
|
438
459
|
soon: 'pronto',
|
|
460
|
+
launch: 'Launch',
|
|
461
|
+
network: 'Red',
|
|
462
|
+
skipToContent: 'Saltar al contenido',
|
|
439
463
|
localeSelectorAriaLabel: 'Seleccionar idioma',
|
|
440
464
|
},
|
|
441
465
|
footer: {
|
|
@@ -497,6 +521,9 @@ export const navigationContent = {
|
|
|
497
521
|
joinWaitlist: 'Tham gia danh sách chờ',
|
|
498
522
|
waitlistEmailCta: 'Nhập email của bạn',
|
|
499
523
|
soon: 'sắp ra mắt',
|
|
524
|
+
launch: 'Launch',
|
|
525
|
+
network: 'Mạng',
|
|
526
|
+
skipToContent: 'Chuyển đến nội dung',
|
|
500
527
|
localeSelectorAriaLabel: 'Chọn ngôn ngữ',
|
|
501
528
|
},
|
|
502
529
|
footer: {
|
package/package.json
CHANGED