@sonic-equipment/ui 260.0.0 → 260.0.2
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/buttons/link/link.d.ts +1 -0
- package/dist/buttons/link/link.js +2 -2
- package/dist/country-language-selector/connected-country-language-selector.d.ts +1 -0
- package/dist/country-language-selector/connected-country-language-selector.js +32 -6
- package/dist/shared/routing/route-provider.d.ts +1 -0
- package/dist/shared/routing/route-utils.d.ts +2 -1
- package/dist/shared/routing/route-utils.js +2 -1
- package/dist/shared/routing/types.d.ts +2 -0
- package/dist/shared/routing/use-route-link.d.ts +1 -1
- package/dist/shared/routing/use-route-link.js +3 -3
- package/dist/shared/utils/css.js +9 -53
- package/package.json +1 -1
|
@@ -5,8 +5,8 @@ import clsx from 'clsx';
|
|
|
5
5
|
import { useRouteLink } from '../../shared/routing/use-route-link.js';
|
|
6
6
|
import styles from './link.module.css.js';
|
|
7
7
|
|
|
8
|
-
const Link = forwardRef(({ children, className: _className, color, hasUnderline, href, hrefLang, id, isDisabled, lang, onClick, onKeyUp, rel, role, route, tabIndex, target, title, ...rest }, ref) => {
|
|
9
|
-
const { getRouteLinkProps, RouteLinkElement } = useRouteLink();
|
|
8
|
+
const Link = forwardRef(({ children, className: _className, color, hasUnderline, href, hrefLang, id, ignoreLocalePrefix, isDisabled, lang, onClick, onKeyUp, rel, role, route, tabIndex, target, title, ...rest }, ref) => {
|
|
9
|
+
const { getRouteLinkProps, RouteLinkElement } = useRouteLink(ignoreLocalePrefix);
|
|
10
10
|
const className = clsx(styles['link'], hasUnderline && styles['has-underline'], color && styles[color], _className);
|
|
11
11
|
if (href) {
|
|
12
12
|
return (jsx(RouteLinkElement, { ref: ref, className: className, "data-disabled": isDisabled ? true : undefined, hrefLang: hrefLang, id: id, lang: lang, onClick: onClick, onKeyUp: onKeyUp, rel: rel, role: role, tabIndex: isDisabled ? -1 : tabIndex, target: target, title: title, ...getRouteLinkProps(href, route), ...rest, children: children }));
|
|
@@ -2,6 +2,7 @@ import { CountryCode, LanguageCode } from '../intl/types';
|
|
|
2
2
|
export interface ConnectedCountryLanguageSelectorProps {
|
|
3
3
|
className?: string;
|
|
4
4
|
countries?: CountryCode[];
|
|
5
|
+
currentPath?: string;
|
|
5
6
|
isOpen?: boolean;
|
|
6
7
|
selectedCountry?: CountryCode;
|
|
7
8
|
selectedLanguage?: LanguageCode;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import { useCookie } from '../cookies/use-cookie.js';
|
|
5
5
|
import { useCountriesLanguages } from '../country-selector/use-countries-languages.js';
|
|
6
|
+
import { isCountryLanguageCode } from '../intl/types.js';
|
|
6
7
|
import { useFormattedMessage } from '../intl/use-formatted-message.js';
|
|
7
8
|
import { useIntl } from '../intl/use-intl.js';
|
|
8
9
|
import { useNavigate } from '../shared/routing/use-navigate.js';
|
|
10
|
+
import { useRouter } from '../shared/routing/use-router.js';
|
|
11
|
+
import { environment } from '../shared/utils/environment.js';
|
|
9
12
|
import { CountryLanguageSelector } from './country-language-selector.js';
|
|
10
13
|
|
|
11
14
|
/* eslint-disable @eslint-react/hooks-extra/no-direct-set-state-in-use-effect */
|
|
@@ -28,6 +31,7 @@ function ConnectedCountryLanguageSelector({ className, countries: _countries, is
|
|
|
28
31
|
});
|
|
29
32
|
const [isOpen, setIsOpen] = useState(_isOpen ?? !CLSelectorCookie?.includes('closed=true'));
|
|
30
33
|
const { countries } = useCountriesLanguages({});
|
|
34
|
+
const router = useRouter();
|
|
31
35
|
// options for the selector menu
|
|
32
36
|
const menuOptions = {
|
|
33
37
|
other: t('Choose your country or region…'),
|
|
@@ -65,16 +69,36 @@ function ConnectedCountryLanguageSelector({ className, countries: _countries, is
|
|
|
65
69
|
const optionKey = `${languageCode}-${countryCode}`;
|
|
66
70
|
const optionCountryLabel = `clselector.country.${countryCode}.${languageCode}`;
|
|
67
71
|
const optionLanguageLabel = `clselector.language.${languageCode}.${languageCode}`;
|
|
68
|
-
// add option key/value to menu options
|
|
69
|
-
menuOptions[optionKey] = (jsx("span", { lang: languageCode, children: `${t(optionCountryLabel)} - ${t(optionLanguageLabel)}` }));
|
|
70
72
|
// add option key to group's options
|
|
71
73
|
options.push(optionKey);
|
|
74
|
+
const { alternativeHrefs, pathname } = router.url;
|
|
75
|
+
let path;
|
|
76
|
+
if (alternativeHrefs && alternativeHrefs[optionKey]) {
|
|
77
|
+
path = `/${optionKey}${alternativeHrefs[optionKey]}`;
|
|
78
|
+
}
|
|
79
|
+
else if (!alternativeHrefs ||
|
|
80
|
+
Object.keys(alternativeHrefs).length === 0) {
|
|
81
|
+
const currentCountryLanguage = pathname.split('/')[1]?.toLowerCase();
|
|
82
|
+
path = isCountryLanguageCode(currentCountryLanguage)
|
|
83
|
+
? pathname
|
|
84
|
+
.toLowerCase()
|
|
85
|
+
.replace(`${currentCountryLanguage}`, `${optionKey}`)
|
|
86
|
+
: `/${optionKey}${pathname}`;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
path = `/${optionKey}`;
|
|
90
|
+
}
|
|
91
|
+
// add option key/value to menu options
|
|
92
|
+
menuOptions[optionKey] =
|
|
93
|
+
environment === 'next-production' ? (jsx("span", { lang: languageCode, children: `${t(optionCountryLabel)} - ${t(optionLanguageLabel)}` })) : (
|
|
94
|
+
// in development, show the path to help identify the correct option
|
|
95
|
+
jsxs("span", { lang: languageCode, children: [`${t(optionCountryLabel)} - ${t(optionLanguageLabel)}`, jsx("code", { style: { fontSize: '12px' }, children: path })] }));
|
|
72
96
|
// add to option data
|
|
73
97
|
optionData.push({
|
|
74
98
|
country: countryCode,
|
|
75
99
|
key: optionKey,
|
|
76
100
|
language: languageCode,
|
|
77
|
-
path
|
|
101
|
+
path,
|
|
78
102
|
});
|
|
79
103
|
});
|
|
80
104
|
// push new group to groups
|
|
@@ -105,7 +129,9 @@ function ConnectedCountryLanguageSelector({ className, countries: _countries, is
|
|
|
105
129
|
setIsOpen(false);
|
|
106
130
|
if (selectedOption === 'other') {
|
|
107
131
|
setCLSelectorCookie('closed=true');
|
|
108
|
-
navigate(pathToCLSelectionList
|
|
132
|
+
navigate(pathToCLSelectionList, {
|
|
133
|
+
ignoreLocalePrefix: true,
|
|
134
|
+
});
|
|
109
135
|
}
|
|
110
136
|
else {
|
|
111
137
|
const option = optionData.find(option => option.key === selectedOption);
|
|
@@ -113,7 +139,7 @@ function ConnectedCountryLanguageSelector({ className, countries: _countries, is
|
|
|
113
139
|
return;
|
|
114
140
|
const { country, language, path } = option;
|
|
115
141
|
setCLSelectorCookie(`closed=true;language=${language};country=${country}`);
|
|
116
|
-
navigate(path);
|
|
142
|
+
navigate(path, { ignoreLocalePrefix: true, reload: true });
|
|
117
143
|
}
|
|
118
144
|
};
|
|
119
145
|
// react to CountryLanguageSelectorButton clicked
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
function buildHref({ basePathname, href, }) {
|
|
1
|
+
function buildHref({ basePathname, href, ignoreLocalePrefix, }) {
|
|
2
2
|
if (!href.startsWith('http') &&
|
|
3
3
|
basePathname &&
|
|
4
4
|
basePathname !== '/' &&
|
|
5
|
+
!ignoreLocalePrefix &&
|
|
5
6
|
!href.startsWith(`/${basePathname}`)) {
|
|
6
7
|
return `/${basePathname.replace(/^\//, '')}/${href.replace(/^\//, '')}`;
|
|
7
8
|
}
|
|
@@ -24,6 +24,7 @@ export interface RouteContextValue {
|
|
|
24
24
|
navigate: NavigateFunction;
|
|
25
25
|
paths: Paths;
|
|
26
26
|
url: {
|
|
27
|
+
alternativeHrefs?: Record<string, string>;
|
|
27
28
|
basePathname?: string;
|
|
28
29
|
href: string;
|
|
29
30
|
pathname: string;
|
|
@@ -36,6 +37,7 @@ export interface RouteLinkElementProps extends React.HTMLProps<HTMLAnchorElement
|
|
|
36
37
|
route?: NavigateOptions;
|
|
37
38
|
}
|
|
38
39
|
export interface NavigateOptions {
|
|
40
|
+
ignoreLocalePrefix?: boolean;
|
|
39
41
|
prefetch?: boolean;
|
|
40
42
|
reload?: boolean;
|
|
41
43
|
replace?: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NavigateOptions } from './types';
|
|
2
|
-
export declare function useRouteLink(): {
|
|
2
|
+
export declare function useRouteLink(ignoreLocalePrefix?: boolean): {
|
|
3
3
|
RouteLinkElement: React.ElementType<import("./types").RouteLinkElementProps>;
|
|
4
4
|
getRouteLinkProps: (href: string, route?: NavigateOptions) => {
|
|
5
5
|
href: string;
|
|
@@ -5,18 +5,18 @@ import { useLocation } from './use-location.js';
|
|
|
5
5
|
import { useOnNavigate } from './use-on-navigate.js';
|
|
6
6
|
import { useRouteLinkElement } from './use-route-link-element.js';
|
|
7
7
|
|
|
8
|
-
function useRouteLink() {
|
|
8
|
+
function useRouteLink(ignoreLocalePrefix) {
|
|
9
9
|
const { basePathname } = useLocation();
|
|
10
10
|
const RouteLinkElement = useRouteLinkElement();
|
|
11
11
|
const triggerCallbacks = useOnNavigate();
|
|
12
12
|
const getRouteLinkProps = useCallback((href, route) => {
|
|
13
|
-
href = buildHref({ basePathname, href });
|
|
13
|
+
href = buildHref({ basePathname, href, ignoreLocalePrefix });
|
|
14
14
|
return {
|
|
15
15
|
href,
|
|
16
16
|
onNavigate: () => triggerCallbacks(href, route),
|
|
17
17
|
route,
|
|
18
18
|
};
|
|
19
|
-
}, [basePathname, triggerCallbacks]);
|
|
19
|
+
}, [basePathname, triggerCallbacks, ignoreLocalePrefix]);
|
|
20
20
|
return {
|
|
21
21
|
RouteLinkElement,
|
|
22
22
|
getRouteLinkProps,
|
package/dist/shared/utils/css.js
CHANGED
|
@@ -1,61 +1,17 @@
|
|
|
1
|
-
function evaluateCssCalc(expression, options = {}) {
|
|
2
|
-
const baseFontSize = options.baseFontSize ?? 16;
|
|
3
|
-
const percentBase = options.percentBase ?? 100;
|
|
4
|
-
const expr = expression
|
|
5
|
-
.replace(/^calc\(/, '')
|
|
6
|
-
.replace(/\)$/, '')
|
|
7
|
-
.trim();
|
|
8
|
-
const tokens = expr.split(/([*+/-])/).map(token => token.trim());
|
|
9
|
-
const toPx = (value = '0px') => {
|
|
10
|
-
const match = value.match(/^([+-]?[\d.]+)(px|rem|em|%)?$/);
|
|
11
|
-
if (!match)
|
|
12
|
-
throw new Error(`Unsupported or invalid token: "${value}"`);
|
|
13
|
-
const num = Number.parseFloat(match[1] || '0');
|
|
14
|
-
const unit = match[2] || 'px';
|
|
15
|
-
switch (unit) {
|
|
16
|
-
case 'px':
|
|
17
|
-
return num;
|
|
18
|
-
case 'rem':
|
|
19
|
-
case 'em':
|
|
20
|
-
return num * baseFontSize;
|
|
21
|
-
case '%':
|
|
22
|
-
return (num / 100) * percentBase;
|
|
23
|
-
default:
|
|
24
|
-
throw new Error(`Unsupported unit: ${unit}`);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
// Evaluate left-to-right (no operator precedence)
|
|
28
|
-
let result = toPx(tokens[0]);
|
|
29
|
-
for (let i = 1; i < tokens.length; i += 2) {
|
|
30
|
-
const operator = tokens[i];
|
|
31
|
-
const nextValue = toPx(tokens[i + 1]);
|
|
32
|
-
switch (operator) {
|
|
33
|
-
case '+':
|
|
34
|
-
result += nextValue;
|
|
35
|
-
break;
|
|
36
|
-
case '-':
|
|
37
|
-
result -= nextValue;
|
|
38
|
-
break;
|
|
39
|
-
case '*':
|
|
40
|
-
result *= nextValue;
|
|
41
|
-
break;
|
|
42
|
-
case '/':
|
|
43
|
-
result /= nextValue;
|
|
44
|
-
break;
|
|
45
|
-
default:
|
|
46
|
-
throw new Error(`Unsupported operator: ${operator}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
1
|
function getCssPropertyValue(property) {
|
|
52
|
-
if (typeof document === 'undefined')
|
|
2
|
+
if (typeof document === 'undefined' || !getComputedStyle)
|
|
53
3
|
return;
|
|
54
|
-
const rawValue = getComputedStyle
|
|
4
|
+
const rawValue = getComputedStyle(document.body).getPropertyValue(property);
|
|
55
5
|
if (!rawValue)
|
|
56
6
|
return undefined;
|
|
57
7
|
if (/calc/i.test(rawValue)) {
|
|
58
|
-
|
|
8
|
+
const el = document.createElement('div');
|
|
9
|
+
el.style.cssText = 'position:fixed;visibility:hidden;pointer-events:none';
|
|
10
|
+
el.style.top = rawValue;
|
|
11
|
+
document.body.append(el);
|
|
12
|
+
const px = Number.parseFloat(getComputedStyle(el).top);
|
|
13
|
+
el.remove();
|
|
14
|
+
return Number.isNaN(px) ? undefined : `${px}px`;
|
|
59
15
|
}
|
|
60
16
|
if (rawValue && !/\D/.test(rawValue)) {
|
|
61
17
|
const valueAsNumber = Number.parseFloat(rawValue);
|