@astryxdesign/core 0.5.1 → 0.5.2-canary.0a6a1eb
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/CHANGELOG.md +19 -0
- package/dist/BottomSheet/BottomSheet.d.ts +3 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -3
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/Button/Button.d.ts +2 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -4
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +34 -17
- package/dist/ContextMenu/ContextMenu.d.ts +14 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +149 -26
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +340 -26
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/menuWidth.d.ts +14 -0
- package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
- package/dist/DropdownMenu/menuWidth.js +35 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
- package/dist/DropdownMenu/useMenuOverflow.js +60 -0
- package/dist/MoreMenu/MoreMenu.d.ts +8 -2
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +2 -0
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +97 -68
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/Popover/Popover.d.ts +10 -6
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +141 -16
- package/dist/Popover/usePopover.d.ts +4 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +52 -5
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +106 -80
- package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
- package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
- package/dist/Selector/SelectorBottomSheet.js +83 -0
- package/dist/Selector/index.d.ts +1 -1
- package/dist/Selector/index.d.ts.map +1 -1
- package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
- package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
- package/dist/Selector/selectorPresentation.stylex.js +21 -0
- package/dist/Selector/useSelectorPresentation.d.ts +32 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
- package/dist/Selector/useSelectorPresentation.js +90 -0
- package/dist/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- package/dist/astryx.css +19 -1
- package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
- package/dist/hooks/useAdaptivePresentation.js +19 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
- package/dist/hooks/useFocusReturnVisibility.js +35 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +12 -0
- package/dist/i18n/InternationalizationContext.d.ts +2 -0
- package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
- package/dist/i18n/InternationalizationContext.js +3 -1
- package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
- package/dist/i18n/InternationalizationProvider.js +3 -1
- package/dist/i18n/resolve.d.ts +1 -1
- package/dist/i18n/resolve.d.ts.map +1 -1
- package/dist/i18n/resolve.js +37 -38
- package/dist/i18n/useTranslator.d.ts.map +1 -1
- package/dist/i18n/useTranslator.js +2 -3
- package/locales/af-ZA.json +0 -4
- package/locales/ar-SA.json +0 -4
- package/locales/ca-ES.json +0 -4
- package/locales/cs-CZ.json +0 -4
- package/locales/da-DK.json +0 -4
- package/locales/de-DE.json +0 -4
- package/locales/el-GR.json +0 -4
- package/locales/en.json +20 -0
- package/locales/es-ES.json +0 -4
- package/locales/fi-FI.json +0 -4
- package/locales/fr-FR.json +0 -4
- package/locales/he-IL.json +0 -4
- package/locales/hu-HU.json +0 -4
- package/locales/it-IT.json +0 -4
- package/locales/ja-JP.json +0 -4
- package/locales/ko-KR.json +0 -4
- package/locales/nl-NL.json +0 -4
- package/locales/no-NO.json +0 -4
- package/locales/pl-PL.json +0 -4
- package/locales/pseudo.json +15 -0
- package/locales/pt-BR.json +0 -4
- package/locales/pt-PT.json +0 -4
- package/locales/ro-RO.json +0 -4
- package/locales/ru-RU.json +0 -4
- package/locales/sr-SP.json +0 -4
- package/locales/sv-SE.json +0 -4
- package/locales/tr-TR.json +0 -4
- package/locales/uk-UA.json +0 -4
- package/locales/vi-VN.json +0 -4
- package/locales/zh-CN.json +0 -4
- package/locales/zh-TW.json +0 -4
- package/package.json +3 -3
- package/src/BottomSheet/BottomSheet.doc.mjs +6 -0
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +7 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/Button/Button.test.tsx +10 -0
- package/src/Button/Button.tsx +13 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatToolCalls.test.tsx +44 -0
- package/src/Chat/ChatToolCalls.tsx +36 -15
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
- package/src/ComplexSelector/ComplexSelector.doc.mjs +2 -2
- package/src/ContextMenu/ContextMenu.doc.mjs +13 -1
- package/src/ContextMenu/ContextMenu.test.tsx +131 -1
- package/src/ContextMenu/ContextMenu.tsx +199 -32
- package/src/ContextMenu/index.ts +1 -0
- package/src/DateInput/DateInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenu.doc.mjs +201 -40
- package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
- package/src/DropdownMenu/DropdownMenu.tsx +459 -28
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
- package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
- package/src/DropdownMenu/index.ts +2 -0
- package/src/DropdownMenu/menuWidth.ts +51 -0
- package/src/DropdownMenu/useMenuOverflow.ts +77 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/FieldStatus/FieldStatus.doc.mjs +1 -1
- package/src/FileInput/FileInput.doc.mjs +1 -1
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/Indicator/Indicator.doc.mjs +1 -1
- package/src/InputGroup/InputGroup.doc.mjs +1 -1
- package/src/MoreMenu/MoreMenu.doc.mjs +17 -0
- package/src/MoreMenu/MoreMenu.test.tsx +47 -0
- package/src/MoreMenu/MoreMenu.tsx +13 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +17 -1
- package/src/MultiSelector/MultiSelector.test.tsx +127 -0
- package/src/MultiSelector/MultiSelector.tsx +121 -73
- package/src/MultiSelector/index.ts +1 -0
- package/src/NumberInput/NumberInput.doc.mjs +1 -1
- package/src/Popover/Popover.doc.mjs +11 -11
- package/src/Popover/Popover.test.tsx +397 -2
- package/src/Popover/Popover.tsx +228 -21
- package/src/Popover/usePopover.tsx +92 -15
- package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
- package/src/RadioList/RadioList.doc.mjs +1 -1
- package/src/SegmentedControl/SegmentedControl.doc.mjs +56 -0
- package/src/Selector/Selector.doc.mjs +13 -1
- package/src/Selector/Selector.test.tsx +135 -0
- package/src/Selector/Selector.tsx +141 -82
- package/src/Selector/SelectorBottomSheet.tsx +97 -0
- package/src/Selector/index.ts +1 -0
- package/src/Selector/selectorPresentation.stylex.ts +20 -0
- package/src/Selector/useSelectorPresentation.ts +147 -0
- package/src/Slider/Slider.doc.mjs +2 -2
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/TimeInput/TimeInput.doc.mjs +1 -1
- package/src/Toast/ToastViewport.test.tsx +118 -4
- package/src/Toast/useToastGesture.ts +61 -8
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- package/src/hooks/useAdaptivePresentation.ts +35 -0
- package/src/hooks/useFocusReturnVisibility.ts +45 -0
- package/src/hooks/useFocusTrap.test.tsx +40 -0
- package/src/hooks/useFocusTrap.ts +13 -0
- package/src/i18n/InternationalizationContext.ts +3 -0
- package/src/i18n/InternationalizationProvider.tsx +8 -1
- package/src/i18n/__tests__/resolve.test.ts +23 -1
- package/src/i18n/resolve.ts +46 -49
- package/src/i18n/useTranslator.ts +2 -7
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @file useFocusReturnVisibility.ts
|
|
7
|
+
* @input Uses the shared interaction-modality tracker
|
|
8
|
+
* @output Preserves returned focus while suppressing pointer-only focus rings
|
|
9
|
+
* @position Internal focus-return policy for adaptive overlays
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {useCallback, useEffect, useState} from 'react';
|
|
13
|
+
import {
|
|
14
|
+
getInteractionModality,
|
|
15
|
+
trackInteractionModality,
|
|
16
|
+
} from '../utils/interactionModality';
|
|
17
|
+
|
|
18
|
+
export function useFocusReturnVisibility() {
|
|
19
|
+
const [isFocusRingSuppressed, setIsFocusRingSuppressed] = useState(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
trackInteractionModality();
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const prepareFocusReturn = useCallback(() => {
|
|
26
|
+
setIsFocusRingSuppressed(getInteractionModality() === 'pointer');
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
const resetFocusReturn = useCallback(() => {
|
|
30
|
+
setIsFocusRingSuppressed(false);
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
const onFocusReturnTargetFocus = useCallback(() => {
|
|
34
|
+
if (getInteractionModality() === 'keyboard') {
|
|
35
|
+
setIsFocusRingSuppressed(false);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
isFocusRingSuppressed,
|
|
41
|
+
onFocusReturnTargetFocus,
|
|
42
|
+
prepareFocusReturn,
|
|
43
|
+
resetFocusReturn,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -193,6 +193,46 @@ describe('useFocusTrap tabbable model (infra-8)', () => {
|
|
|
193
193
|
expect(screen.getByTestId('first')).toHaveFocus();
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
it('moves from a programmatic container focus target into the tabbable cycle', () => {
|
|
197
|
+
render(
|
|
198
|
+
<Trap>
|
|
199
|
+
<button type="button" data-testid="first">
|
|
200
|
+
First
|
|
201
|
+
</button>
|
|
202
|
+
<button type="button" data-testid="last">
|
|
203
|
+
Last
|
|
204
|
+
</button>
|
|
205
|
+
</Trap>,
|
|
206
|
+
);
|
|
207
|
+
const trap = screen.getByTestId('trap');
|
|
208
|
+
trap.tabIndex = -1;
|
|
209
|
+
trap.focus();
|
|
210
|
+
|
|
211
|
+
fireEvent.keyDown(document, {key: 'Tab'});
|
|
212
|
+
|
|
213
|
+
expect(screen.getByTestId('first')).toHaveFocus();
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('moves backward from a programmatic container focus target to the last tabbable control', () => {
|
|
217
|
+
render(
|
|
218
|
+
<Trap>
|
|
219
|
+
<button type="button" data-testid="first">
|
|
220
|
+
First
|
|
221
|
+
</button>
|
|
222
|
+
<button type="button" data-testid="last">
|
|
223
|
+
Last
|
|
224
|
+
</button>
|
|
225
|
+
</Trap>,
|
|
226
|
+
);
|
|
227
|
+
const trap = screen.getByTestId('trap');
|
|
228
|
+
trap.tabIndex = -1;
|
|
229
|
+
trap.focus();
|
|
230
|
+
|
|
231
|
+
fireEvent.keyDown(document, {key: 'Tab', shiftKey: true});
|
|
232
|
+
|
|
233
|
+
expect(screen.getByTestId('last')).toHaveFocus();
|
|
234
|
+
});
|
|
235
|
+
|
|
196
236
|
it('keeps a programmatic focus target when the trap has no tabbable controls', () => {
|
|
197
237
|
render(
|
|
198
238
|
<Trap>
|
|
@@ -375,6 +375,19 @@ export function useFocusTrap<T extends HTMLElement = HTMLElement>(
|
|
|
375
375
|
const first = focusable[0];
|
|
376
376
|
const last = focusable[focusable.length - 1];
|
|
377
377
|
|
|
378
|
+
if (document.activeElement === container) {
|
|
379
|
+
// Programmatic initial focus can sit on the trap container itself
|
|
380
|
+
// (for example a dialog panel with no content controls). Move into
|
|
381
|
+
// the tabbable cycle instead of letting Tab escape to the page.
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
if (event.shiftKey) {
|
|
384
|
+
last.focus();
|
|
385
|
+
} else {
|
|
386
|
+
first.focus();
|
|
387
|
+
}
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
|
|
378
391
|
if (event.shiftKey) {
|
|
379
392
|
// Shift+Tab: if on first element, wrap to last
|
|
380
393
|
if (document.activeElement === first) {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import {createContext} from 'react';
|
|
26
|
+
import {getResolve} from './resolve';
|
|
26
27
|
import type {Locale, MessagesByLocale, Overrides} from './types';
|
|
27
28
|
|
|
28
29
|
export interface InternationalizationContextValue {
|
|
@@ -30,6 +31,7 @@ export interface InternationalizationContextValue {
|
|
|
30
31
|
direction: 'ltr' | 'rtl';
|
|
31
32
|
messages: MessagesByLocale;
|
|
32
33
|
overrides?: Overrides;
|
|
34
|
+
translate: ReturnType<typeof getResolve>;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -41,5 +43,6 @@ export const InternationalizationContext =
|
|
|
41
43
|
locale: 'en',
|
|
42
44
|
direction: 'ltr',
|
|
43
45
|
messages: {},
|
|
46
|
+
translate: getResolve('en', {}),
|
|
44
47
|
});
|
|
45
48
|
InternationalizationContext.displayName = 'InternationalizationContext';
|
|
@@ -23,6 +23,7 @@ import {useMemo, type ReactNode} from 'react';
|
|
|
23
23
|
import {InternationalizationContext} from './InternationalizationContext';
|
|
24
24
|
import {getLocaleDirection} from './getLocaleDirection';
|
|
25
25
|
import type {Locale, MessagesByLocale, Overrides} from './types';
|
|
26
|
+
import {getResolve} from './resolve';
|
|
26
27
|
|
|
27
28
|
export interface InternationalizationProviderProps {
|
|
28
29
|
/**
|
|
@@ -85,7 +86,13 @@ export function InternationalizationProvider({
|
|
|
85
86
|
}: InternationalizationProviderProps) {
|
|
86
87
|
const direction = dir ?? getLocaleDirection(locale);
|
|
87
88
|
const value = useMemo(
|
|
88
|
-
() => ({
|
|
89
|
+
() => ({
|
|
90
|
+
locale,
|
|
91
|
+
direction,
|
|
92
|
+
messages: messages ?? {},
|
|
93
|
+
overrides,
|
|
94
|
+
translate: getResolve(locale, messages ?? {}, overrides),
|
|
95
|
+
}),
|
|
89
96
|
[locale, direction, messages, overrides],
|
|
90
97
|
);
|
|
91
98
|
return (
|
|
@@ -9,9 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {describe, expect, test, beforeEach, vi} from 'vitest';
|
|
12
|
-
import {__resetForTests,
|
|
12
|
+
import {__resetForTests, getResolve, resolveLocaleChain} from '../resolve';
|
|
13
13
|
import type {Catalog, MessagesByLocale, Overrides} from '../types';
|
|
14
14
|
|
|
15
|
+
const resolve = (
|
|
16
|
+
...[key, values, locale, messages, overrides]: [
|
|
17
|
+
...Parameters<ReturnType<typeof getResolve>>,
|
|
18
|
+
...Parameters<typeof getResolve>,
|
|
19
|
+
]
|
|
20
|
+
) => getResolve(locale, messages, overrides)(key, values);
|
|
21
|
+
|
|
15
22
|
// Reset caches between tests so warn-once and formatter cache don't bleed.
|
|
16
23
|
beforeEach(() => {
|
|
17
24
|
__resetForTests();
|
|
@@ -209,6 +216,21 @@ describe('resolve — overrides', () => {
|
|
|
209
216
|
// Should fall through to en since fr has no messages and es override doesn't apply
|
|
210
217
|
expect(out).toBe('Go to next page');
|
|
211
218
|
});
|
|
219
|
+
|
|
220
|
+
test('override `pt` should take precedence over message `pt-BR`', () => {
|
|
221
|
+
const overrides: Overrides = {
|
|
222
|
+
pt: {key: 'override'},
|
|
223
|
+
};
|
|
224
|
+
const messages: MessagesByLocale = {
|
|
225
|
+
'pt-BR': {
|
|
226
|
+
key: {
|
|
227
|
+
defaultMessage: 'shipped',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
const out = resolve('key', undefined, 'pt-BR', messages, overrides);
|
|
232
|
+
expect(out).toBe('override');
|
|
233
|
+
});
|
|
212
234
|
});
|
|
213
235
|
|
|
214
236
|
describe('resolve — ICU MessageFormat', () => {
|
package/src/i18n/resolve.ts
CHANGED
|
@@ -73,74 +73,71 @@ export function resolveLocaleChain(locale: Locale): Locale[] {
|
|
|
73
73
|
return chain;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function
|
|
77
|
-
key: string,
|
|
76
|
+
function getLookup(
|
|
78
77
|
locale: Locale,
|
|
79
78
|
messages: MessagesByLocale,
|
|
80
|
-
overrides
|
|
81
|
-
): string
|
|
79
|
+
overrides?: Overrides,
|
|
80
|
+
): Record<string, string> {
|
|
81
|
+
const lookup: Record<string, string> = {};
|
|
82
82
|
const chain = resolveLocaleChain(locale);
|
|
83
|
-
|
|
84
83
|
// 1 + 2. Overrides (most specific to least specific in the chain)
|
|
85
84
|
if (overrides !== undefined) {
|
|
86
85
|
for (const tag of chain) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if (overrides[tag]) {
|
|
87
|
+
for (const [key, value] of Object.entries(overrides[tag])) {
|
|
88
|
+
if (lookup[key] === undefined && value !== null) {
|
|
89
|
+
lookup[key] = value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
}
|
|
93
|
-
|
|
94
|
-
// 3 + 4. Shipped catalogs (most specific to least specific)
|
|
95
95
|
for (const tag of chain) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
if (messages[tag]) {
|
|
97
|
+
for (const [key, value] of Object.entries(messages[tag])) {
|
|
98
|
+
if (lookup[key] === undefined && value?.defaultMessage !== null) {
|
|
99
|
+
lookup[key] = value?.defaultMessage;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
const enEntry = EN_CATALOG[key];
|
|
104
|
-
if (enEntry !== undefined) {
|
|
105
|
-
return enEntry.defaultMessage;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 6. Nothing found
|
|
109
|
-
return null;
|
|
105
|
+
return lookup;
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
export function
|
|
113
|
-
key: string,
|
|
114
|
-
values: Record<string, unknown> | undefined,
|
|
108
|
+
export function getResolve(
|
|
115
109
|
locale: Locale,
|
|
116
110
|
messages: MessagesByLocale,
|
|
117
|
-
overrides
|
|
118
|
-
)
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
111
|
+
overrides?: Overrides,
|
|
112
|
+
) {
|
|
113
|
+
const lookup = getLookup(locale, messages, overrides);
|
|
114
|
+
|
|
115
|
+
return (key: string, values: Record<string, unknown> | undefined) => {
|
|
116
|
+
const result = lookup[key] ?? EN_CATALOG[key]?.defaultMessage;
|
|
117
|
+
if (result === undefined) {
|
|
118
|
+
// Fires ONLY when a key is missing from every source including the
|
|
119
|
+
// shipped `en` catalog — a real bug (typo, stale catalog, deleted key).
|
|
120
|
+
// Fallback to `en` from a non-en locale is expected and stays silent,
|
|
121
|
+
// matching the FormatJS / i18next default.
|
|
122
|
+
warnOnce(
|
|
123
|
+
`astryx-i18n:${locale}::${key}`,
|
|
124
|
+
'astryx-i18n',
|
|
125
|
+
`missing key: ${key} (locale: ${locale})`,
|
|
126
|
+
);
|
|
127
|
+
return key;
|
|
128
|
+
}
|
|
133
129
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
130
|
+
if (values === undefined) {
|
|
131
|
+
// Static string — skip the parser entirely for the common case
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
138
134
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
const formatted = getFormatter(result, locale).format(values);
|
|
136
|
+
// IntlMessageFormat.format returns string | (string | React elements) — we
|
|
137
|
+
// only ever pass string values so it will be a string; assert for the type
|
|
138
|
+
// system.
|
|
139
|
+
return formatted as string;
|
|
140
|
+
};
|
|
144
141
|
}
|
|
145
142
|
|
|
146
143
|
/**
|
|
@@ -19,9 +19,8 @@
|
|
|
19
19
|
* - /packages/core/src/i18n/t.client.ts
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import {use
|
|
22
|
+
import {use} from 'react';
|
|
23
23
|
import {InternationalizationContext} from './InternationalizationContext';
|
|
24
|
-
import {resolve} from './resolve';
|
|
25
24
|
|
|
26
25
|
export type TranslatorFn = (
|
|
27
26
|
key: string,
|
|
@@ -42,9 +41,5 @@ export type TranslatorFn = (
|
|
|
42
41
|
*/
|
|
43
42
|
export function useTranslator(): TranslatorFn {
|
|
44
43
|
const ctx = use(InternationalizationContext);
|
|
45
|
-
return
|
|
46
|
-
(key: string, values?: Record<string, unknown>) =>
|
|
47
|
-
resolve(key, values, ctx.locale, ctx.messages, ctx.overrides),
|
|
48
|
-
[ctx.locale, ctx.messages, ctx.overrides],
|
|
49
|
-
);
|
|
44
|
+
return ctx.translate;
|
|
50
45
|
}
|