@astryxdesign/core 0.5.1 → 0.5.2-canary.11abdc7
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/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- 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/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/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/Calendar/Calendar.doc.mjs +1 -1
- 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/DateInput/DateInput.doc.mjs +1 -1
- package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
- package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
- 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/Indicator/Indicator.doc.mjs +1 -1
- package/src/InputGroup/InputGroup.doc.mjs +1 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +1 -1
- package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
- package/src/RadioList/RadioList.doc.mjs +1 -1
- package/src/Selector/Selector.doc.mjs +1 -1
- 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/Tokenizer/Tokenizer.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- 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
|
@@ -25,12 +25,14 @@ const SWIPE_ACTIVE_SCALE_MAX = 0.02;
|
|
|
25
25
|
export type ToastGestureDirection = 1 | -1;
|
|
26
26
|
|
|
27
27
|
interface GesturePoint {
|
|
28
|
+
source: 'touch' | 'pen';
|
|
28
29
|
pointerId: number;
|
|
29
30
|
clientX: number;
|
|
30
31
|
clientY: number;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
interface GestureState {
|
|
35
|
+
source: GesturePoint['source'];
|
|
34
36
|
pointerId: number;
|
|
35
37
|
startX: number;
|
|
36
38
|
startY: number;
|
|
@@ -126,6 +128,7 @@ export function useToastGesture({
|
|
|
126
128
|
}
|
|
127
129
|
const surfaceSize = Math.max(root.getBoundingClientRect().height, 1);
|
|
128
130
|
gestureRef.current = {
|
|
131
|
+
source: point.source,
|
|
129
132
|
pointerId: point.pointerId,
|
|
130
133
|
startX: point.clientX,
|
|
131
134
|
startY: point.clientY,
|
|
@@ -159,7 +162,12 @@ export function useToastGesture({
|
|
|
159
162
|
) => {
|
|
160
163
|
const state = gestureRef.current;
|
|
161
164
|
const root = rootRef.current;
|
|
162
|
-
if (
|
|
165
|
+
if (
|
|
166
|
+
!state ||
|
|
167
|
+
!root ||
|
|
168
|
+
point.source !== state.source ||
|
|
169
|
+
point.pointerId !== state.pointerId
|
|
170
|
+
) {
|
|
163
171
|
return;
|
|
164
172
|
}
|
|
165
173
|
const deltaX = point.clientX - state.startX;
|
|
@@ -209,7 +217,12 @@ export function useToastGesture({
|
|
|
209
217
|
(point: GesturePoint) => {
|
|
210
218
|
const state = gestureRef.current;
|
|
211
219
|
const root = rootRef.current;
|
|
212
|
-
if (
|
|
220
|
+
if (
|
|
221
|
+
!state ||
|
|
222
|
+
!root ||
|
|
223
|
+
point.source !== state.source ||
|
|
224
|
+
point.pointerId !== state.pointerId
|
|
225
|
+
) {
|
|
213
226
|
return;
|
|
214
227
|
}
|
|
215
228
|
const travel = Math.max(
|
|
@@ -225,6 +238,10 @@ export function useToastGesture({
|
|
|
225
238
|
gestureRef.current = null;
|
|
226
239
|
root.style.removeProperty('transition-duration');
|
|
227
240
|
if (isDismissed) {
|
|
241
|
+
// The exit animation owns the final translation. Clear the live-drag
|
|
242
|
+
// transform/fade/scale first so a standalone Toast does not remain
|
|
243
|
+
// partially translated or faded after its dismiss callback fires.
|
|
244
|
+
clearTransientStyles(root);
|
|
228
245
|
root.style.setProperty(
|
|
229
246
|
'--_toast-swipe-exit-y',
|
|
230
247
|
state.direction === 1
|
|
@@ -247,7 +264,15 @@ export function useToastGesture({
|
|
|
247
264
|
if (
|
|
248
265
|
event.pointerType === 'pen' &&
|
|
249
266
|
(event.button == null || event.button === 0) &&
|
|
250
|
-
beginGesture(
|
|
267
|
+
beginGesture(
|
|
268
|
+
{
|
|
269
|
+
source: 'pen',
|
|
270
|
+
pointerId: event.pointerId,
|
|
271
|
+
clientX: event.clientX,
|
|
272
|
+
clientY: event.clientY,
|
|
273
|
+
},
|
|
274
|
+
event.target,
|
|
275
|
+
)
|
|
251
276
|
) {
|
|
252
277
|
rootRef.current?.setPointerCapture?.(event.pointerId);
|
|
253
278
|
}
|
|
@@ -261,7 +286,12 @@ export function useToastGesture({
|
|
|
261
286
|
return;
|
|
262
287
|
}
|
|
263
288
|
moveGesture(
|
|
264
|
-
|
|
289
|
+
{
|
|
290
|
+
source: 'pen',
|
|
291
|
+
pointerId: event.pointerId,
|
|
292
|
+
clientX: event.clientX,
|
|
293
|
+
clientY: event.clientY,
|
|
294
|
+
},
|
|
265
295
|
() => event.preventDefault(),
|
|
266
296
|
() => rootRef.current?.releasePointerCapture?.(event.pointerId),
|
|
267
297
|
);
|
|
@@ -275,7 +305,12 @@ export function useToastGesture({
|
|
|
275
305
|
return;
|
|
276
306
|
}
|
|
277
307
|
rootRef.current?.releasePointerCapture?.(event.pointerId);
|
|
278
|
-
endGesture(
|
|
308
|
+
endGesture({
|
|
309
|
+
source: 'pen',
|
|
310
|
+
pointerId: event.pointerId,
|
|
311
|
+
clientX: event.clientX,
|
|
312
|
+
clientY: event.clientY,
|
|
313
|
+
});
|
|
279
314
|
},
|
|
280
315
|
[endGesture],
|
|
281
316
|
);
|
|
@@ -284,6 +319,7 @@ export function useToastGesture({
|
|
|
284
319
|
(event: ReactPointerEvent<HTMLDivElement>) => {
|
|
285
320
|
if (
|
|
286
321
|
event.pointerType === 'pen' &&
|
|
322
|
+
gestureRef.current?.source === 'pen' &&
|
|
287
323
|
gestureRef.current?.pointerId === event.pointerId
|
|
288
324
|
) {
|
|
289
325
|
resetGesture(true);
|
|
@@ -298,12 +334,14 @@ export function useToastGesture({
|
|
|
298
334
|
return;
|
|
299
335
|
}
|
|
300
336
|
const point = (touch: Touch): GesturePoint => ({
|
|
337
|
+
source: 'touch',
|
|
301
338
|
pointerId: touch.identifier,
|
|
302
339
|
clientX: touch.clientX,
|
|
303
340
|
clientY: touch.clientY,
|
|
304
341
|
});
|
|
305
342
|
const changedTouch = (event: TouchEvent) => {
|
|
306
|
-
const
|
|
343
|
+
const state = gestureRef.current;
|
|
344
|
+
const pointerId = state?.source === 'touch' ? state.pointerId : null;
|
|
307
345
|
return pointerId == null
|
|
308
346
|
? undefined
|
|
309
347
|
: [...event.changedTouches].find(
|
|
@@ -311,12 +349,23 @@ export function useToastGesture({
|
|
|
311
349
|
);
|
|
312
350
|
};
|
|
313
351
|
const handleTouchStart = (event: TouchEvent) => {
|
|
314
|
-
|
|
315
|
-
|
|
352
|
+
if (event.touches?.length !== 1) {
|
|
353
|
+
// A second contact means the browser may be handling pinch zoom or
|
|
354
|
+
// two-finger scrolling. Abandon any accepted one-finger swipe before
|
|
355
|
+
// touchmove can suppress that native gesture.
|
|
356
|
+
resetGesture(true);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const touch = event.changedTouches[0];
|
|
360
|
+
if (touch != null) {
|
|
316
361
|
beginGesture(point(touch), event.target);
|
|
317
362
|
}
|
|
318
363
|
};
|
|
319
364
|
const handleTouchMove = (event: TouchEvent) => {
|
|
365
|
+
if (event.touches?.length !== 1) {
|
|
366
|
+
resetGesture(true);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
320
369
|
const touch = changedTouch(event);
|
|
321
370
|
if (touch) {
|
|
322
371
|
moveGesture(point(touch), () => {
|
|
@@ -327,6 +376,10 @@ export function useToastGesture({
|
|
|
327
376
|
}
|
|
328
377
|
};
|
|
329
378
|
const handleTouchEnd = (event: TouchEvent) => {
|
|
379
|
+
if (event.touches.length > 0) {
|
|
380
|
+
resetGesture(true);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
330
383
|
const touch = changedTouch(event);
|
|
331
384
|
if (touch) {
|
|
332
385
|
endGesture(point(touch));
|
|
@@ -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
|
}
|