@agent-analytics/shared-ui 0.2.0 → 0.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/astro/Footer.astro +284 -40
- package/dist/eleventy/footer.njk +280 -40
- package/dist/eleventy/header.njk +239 -8
- package/dist/footer.js +256 -176
- package/dist/header.js +174 -30
- package/dist/index.js +1 -1
- package/dist/locales.js +154 -0
- package/dist/recipes.css +62 -0
- package/package.json +2 -1
package/dist/locales.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const DEFAULT_LOCALE = 'en';
|
|
2
|
+
export const SUPPORTED_LOCALES = ['en', 'he', 'zh'];
|
|
3
|
+
export const LOCALE_COOKIE_NAME = 'aa_locale';
|
|
4
|
+
export const LOCALE_COOKIE_DOMAIN = '.agentanalytics.sh';
|
|
5
|
+
export const LOCALE_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
6
|
+
|
|
7
|
+
export const LOCALE_META = Object.freeze({
|
|
8
|
+
en: {
|
|
9
|
+
id: 'en',
|
|
10
|
+
label: 'English',
|
|
11
|
+
nativeLabel: 'English',
|
|
12
|
+
lang: 'en',
|
|
13
|
+
dir: 'ltr',
|
|
14
|
+
},
|
|
15
|
+
he: {
|
|
16
|
+
id: 'he',
|
|
17
|
+
label: 'Hebrew',
|
|
18
|
+
nativeLabel: 'עברית',
|
|
19
|
+
lang: 'he',
|
|
20
|
+
dir: 'rtl',
|
|
21
|
+
},
|
|
22
|
+
zh: {
|
|
23
|
+
id: 'zh',
|
|
24
|
+
label: 'Chinese',
|
|
25
|
+
nativeLabel: '简体中文',
|
|
26
|
+
lang: 'zh-CN',
|
|
27
|
+
dir: 'ltr',
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export function normalizeLocale(locale) {
|
|
32
|
+
if (!locale) return DEFAULT_LOCALE;
|
|
33
|
+
const value = String(locale).trim().toLowerCase();
|
|
34
|
+
if (value === 'iw') return 'he';
|
|
35
|
+
if (value === 'zh-cn' || value === 'zh-hans' || value === 'zh-sg') return 'zh';
|
|
36
|
+
if (SUPPORTED_LOCALES.includes(value)) return value;
|
|
37
|
+
return DEFAULT_LOCALE;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getLocaleMeta(locale) {
|
|
41
|
+
return LOCALE_META[normalizeLocale(locale)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getLocalePrefix(locale) {
|
|
45
|
+
const normalized = normalizeLocale(locale);
|
|
46
|
+
return normalized === DEFAULT_LOCALE ? '' : `/${normalized}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function stripLocalePrefix(pathname = '/') {
|
|
50
|
+
const source = pathname || '/';
|
|
51
|
+
return source.replace(/^\/(?:he|zh)(?=\/|$)/, '') || '/';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function localizePath(locale, pathname = '/') {
|
|
55
|
+
const [pathAndQuery, hash = ''] = String(pathname || '/').split('#');
|
|
56
|
+
const [pathOnly, query = ''] = pathAndQuery.split('?');
|
|
57
|
+
const normalizedPath = stripLocalePrefix(
|
|
58
|
+
pathOnly.startsWith('/') ? pathOnly : `/${pathOnly}`
|
|
59
|
+
);
|
|
60
|
+
const prefix = getLocalePrefix(locale);
|
|
61
|
+
const localizedPath =
|
|
62
|
+
prefix === ''
|
|
63
|
+
? normalizedPath
|
|
64
|
+
: normalizedPath === '/'
|
|
65
|
+
? `${prefix}/`
|
|
66
|
+
: `${prefix}${normalizedPath}`;
|
|
67
|
+
|
|
68
|
+
const search = query ? `?${query}` : '';
|
|
69
|
+
const fragment = hash ? `#${hash}` : '';
|
|
70
|
+
return `${localizedPath}${search}${fragment}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function withLocaleUrl(baseUrl, locale, pathname = '/') {
|
|
74
|
+
return new URL(localizePath(locale, pathname), baseUrl).toString();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function detectPreferredLocale(languages = []) {
|
|
78
|
+
for (const language of languages) {
|
|
79
|
+
const value = String(language || '').toLowerCase();
|
|
80
|
+
if (value.startsWith('he') || value.startsWith('iw')) return 'he';
|
|
81
|
+
if (value.startsWith('zh')) return 'zh';
|
|
82
|
+
}
|
|
83
|
+
return DEFAULT_LOCALE;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function readLocaleCookie(cookieString = '') {
|
|
87
|
+
const match = String(cookieString)
|
|
88
|
+
.split(';')
|
|
89
|
+
.map((part) => part.trim())
|
|
90
|
+
.find((part) => part.startsWith(`${LOCALE_COOKIE_NAME}=`));
|
|
91
|
+
|
|
92
|
+
if (!match) return null;
|
|
93
|
+
return normalizeLocale(match.slice(`${LOCALE_COOKIE_NAME}=`.length));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function resolveClientLocale(options = {}) {
|
|
97
|
+
const cookieValue =
|
|
98
|
+
options.cookieString ??
|
|
99
|
+
(typeof document !== 'undefined' ? document.cookie : '');
|
|
100
|
+
const fromCookie = readLocaleCookie(cookieValue);
|
|
101
|
+
if (fromCookie) return fromCookie;
|
|
102
|
+
|
|
103
|
+
const languages =
|
|
104
|
+
options.languages ??
|
|
105
|
+
(typeof navigator !== 'undefined'
|
|
106
|
+
? navigator.languages || [navigator.language]
|
|
107
|
+
: []);
|
|
108
|
+
|
|
109
|
+
return detectPreferredLocale(languages);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function getLocaleCookieDomain(
|
|
113
|
+
targetLocation = typeof location !== 'undefined' ? location : null
|
|
114
|
+
) {
|
|
115
|
+
const hostname = targetLocation?.hostname || '';
|
|
116
|
+
if (hostname === 'agentanalytics.sh' || hostname.endsWith('.agentanalytics.sh')) {
|
|
117
|
+
return LOCALE_COOKIE_DOMAIN;
|
|
118
|
+
}
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function buildLocaleCookie(locale, options = {}) {
|
|
123
|
+
const normalized = normalizeLocale(locale);
|
|
124
|
+
const domain = options.domain ?? getLocaleCookieDomain();
|
|
125
|
+
const maxAge = options.maxAge ?? LOCALE_COOKIE_MAX_AGE;
|
|
126
|
+
const secure =
|
|
127
|
+
options.secure ??
|
|
128
|
+
(typeof location !== 'undefined' ? location.protocol === 'https:' : true);
|
|
129
|
+
|
|
130
|
+
const parts = [
|
|
131
|
+
`${LOCALE_COOKIE_NAME}=${normalized}`,
|
|
132
|
+
'Path=/',
|
|
133
|
+
`Max-Age=${maxAge}`,
|
|
134
|
+
'SameSite=Lax',
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
if (domain) parts.push(`Domain=${domain}`);
|
|
138
|
+
if (secure) parts.push('Secure');
|
|
139
|
+
return parts.join('; ');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function writeLocaleCookie(locale, options = {}) {
|
|
143
|
+
if (typeof document === 'undefined') return normalizeLocale(locale);
|
|
144
|
+
document.cookie = buildLocaleCookie(locale, options);
|
|
145
|
+
return normalizeLocale(locale);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function setDocumentLocale(locale, targetDocument = typeof document !== 'undefined' ? document : null) {
|
|
149
|
+
if (!targetDocument?.documentElement) return getLocaleMeta(locale);
|
|
150
|
+
const meta = getLocaleMeta(locale);
|
|
151
|
+
targetDocument.documentElement.lang = meta.lang;
|
|
152
|
+
targetDocument.documentElement.dir = meta.dir;
|
|
153
|
+
return meta;
|
|
154
|
+
}
|
package/dist/recipes.css
CHANGED
|
@@ -99,11 +99,50 @@
|
|
|
99
99
|
font-size: 14px;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
.aa-utility-header__controls {
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: 12px;
|
|
106
|
+
}
|
|
107
|
+
|
|
102
108
|
.aa-utility-header__link {
|
|
103
109
|
text-decoration: none;
|
|
104
110
|
transition: color 0.2s ease;
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
.aa-utility-header__locale {
|
|
114
|
+
display: inline-flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 6px;
|
|
117
|
+
padding: 4px;
|
|
118
|
+
border-radius: 999px;
|
|
119
|
+
border: 1px solid var(--surface-border);
|
|
120
|
+
background: var(--surface-glass);
|
|
121
|
+
box-shadow: var(--shadow-soft);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.aa-utility-header__locale-option {
|
|
125
|
+
min-height: 34px;
|
|
126
|
+
padding: 0 10px;
|
|
127
|
+
border: 0;
|
|
128
|
+
border-radius: 999px;
|
|
129
|
+
background: transparent;
|
|
130
|
+
color: var(--text-dim);
|
|
131
|
+
font: inherit;
|
|
132
|
+
line-height: 1;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
transition: background 0.18s ease, color 0.18s ease;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.aa-utility-header__locale-option:hover {
|
|
138
|
+
color: var(--text);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.aa-utility-header__locale-option.is-active {
|
|
142
|
+
background: var(--button-dark-background);
|
|
143
|
+
color: var(--button-dark-text);
|
|
144
|
+
}
|
|
145
|
+
|
|
107
146
|
.aa-utility-header__cta {
|
|
108
147
|
display: inline-flex;
|
|
109
148
|
align-items: center;
|
|
@@ -323,6 +362,20 @@
|
|
|
323
362
|
color: var(--aa-footer-hover, var(--sl-color-white, var(--text)));
|
|
324
363
|
}
|
|
325
364
|
|
|
365
|
+
[dir='rtl'] .aa-utility-header__inner,
|
|
366
|
+
[dir='rtl'] .aa-footer__grid,
|
|
367
|
+
[dir='rtl'] .aa-footer__sections {
|
|
368
|
+
direction: rtl;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
[dir='rtl'] .aa-utility-header__copy,
|
|
372
|
+
[dir='rtl'] .aa-footer__brand-copy,
|
|
373
|
+
[dir='rtl'] .aa-footer__section,
|
|
374
|
+
[dir='rtl'] .aa-footer__description,
|
|
375
|
+
[dir='rtl'] .aa-footer__copy {
|
|
376
|
+
text-align: right;
|
|
377
|
+
}
|
|
378
|
+
|
|
326
379
|
@media (max-width: 960px) {
|
|
327
380
|
.aa-footer__grid {
|
|
328
381
|
grid-template-columns: 1fr;
|
|
@@ -358,6 +411,15 @@
|
|
|
358
411
|
gap: 12px;
|
|
359
412
|
}
|
|
360
413
|
|
|
414
|
+
.aa-utility-header__controls {
|
|
415
|
+
gap: 8px;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.aa-utility-header__locale-option {
|
|
419
|
+
min-height: 32px;
|
|
420
|
+
padding: 0 8px;
|
|
421
|
+
}
|
|
422
|
+
|
|
361
423
|
.aa-utility-header__link {
|
|
362
424
|
display: none;
|
|
363
425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-analytics/shared-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Shared UI primitives, tokens, and marketing chrome for Agent Analytics properties.",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
".": "./dist/index.js",
|
|
33
33
|
"./header": "./dist/header.js",
|
|
34
34
|
"./footer": "./dist/footer.js",
|
|
35
|
+
"./locales": "./dist/locales.js",
|
|
35
36
|
"./astro/Footer.astro": "./dist/astro/Footer.astro",
|
|
36
37
|
"./eleventy/header.njk": "./dist/eleventy/header.njk",
|
|
37
38
|
"./eleventy/footer.njk": "./dist/eleventy/footer.njk",
|