@astryxdesign/core 0.5.0-canary.86c10d7 → 0.5.0-canary.8985bb9
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/MultiSelector/MultiSelector.d.ts +18 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +46 -5
- package/dist/NumberInput/numberParser.d.ts +16 -3
- package/dist/NumberInput/numberParser.d.ts.map +1 -1
- package/dist/NumberInput/numberParser.js +26 -9
- package/dist/Selector/Selector.d.ts +17 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +71 -13
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +34 -2
- package/locales/en.json +10 -2
- package/locales/pseudo.json +6 -0
- package/package.json +2 -2
- package/src/MultiSelector/MultiSelector.doc.mjs +22 -1
- package/src/MultiSelector/MultiSelector.test.tsx +171 -0
- package/src/MultiSelector/MultiSelector.tsx +92 -5
- package/src/NumberInput/numberParser.docblock.test.ts +234 -0
- package/src/NumberInput/numberParser.test.ts +12 -0
- package/src/NumberInput/numberParser.ts +26 -9
- package/src/Selector/Selector.doc.mjs +14 -0
- package/src/Selector/Selector.test.tsx +217 -0
- package/src/Selector/Selector.tsx +123 -14
- package/src/Slider/Slider.test.tsx +106 -1
- package/src/Slider/Slider.tsx +52 -1
- package/src/raw-import.d.ts +14 -0
|
@@ -175,6 +175,23 @@ export interface MultiSelectorProps<T extends MultiSelectorOptionType = MultiSel
|
|
|
175
175
|
* @default 'Search...'
|
|
176
176
|
*/
|
|
177
177
|
searchPlaceholder?: string;
|
|
178
|
+
/**
|
|
179
|
+
* Content shown in the panel when there are no options to show, and
|
|
180
|
+
* announced in a polite live region when the panel opens. Not shown while
|
|
181
|
+
* `isLoading` — the options have not arrived yet.
|
|
182
|
+
* @default 'No options'
|
|
183
|
+
*/
|
|
184
|
+
emptyText?: ReactNode;
|
|
185
|
+
/**
|
|
186
|
+
* Content shown in the panel when a search query matches no options, and
|
|
187
|
+
* announced in a polite live region at the same time.
|
|
188
|
+
*
|
|
189
|
+
* The panel message is `role="presentation"`, so the live region is the only
|
|
190
|
+
* route to assistive tech: a string is announced verbatim, a richer node
|
|
191
|
+
* falls back to the default text since it cannot be spoken.
|
|
192
|
+
* @default 'No results found'
|
|
193
|
+
*/
|
|
194
|
+
emptySearchText?: ReactNode;
|
|
178
195
|
/**
|
|
179
196
|
* How to display selected items in the trigger.
|
|
180
197
|
* - 'count': "3 selected"
|
|
@@ -236,7 +253,7 @@ export interface MultiSelectorProps<T extends MultiSelectorOptionType = MultiSel
|
|
|
236
253
|
* />
|
|
237
254
|
* ```
|
|
238
255
|
*/
|
|
239
|
-
export declare function MultiSelector<T extends MultiSelectorOptionType>({ label, isLabelHidden, description, isOptional, isRequired, isDisabled, disabledMessage, options, value, onChange, changeAction, isLoading, placeholder: placeholderFromProps, size: sizeProp, variant, status, statusVariant, labelTooltip, startIcon, hasClear, hasSelectAll, selectAllLabel: selectAllLabelFromProps, hasSearch, searchPlaceholder: searchPlaceholderFromProps, triggerDisplay, formatValue, maxBadges, renderOption, indicatorPosition, isDefaultOpen, 'data-testid': testId, htmlName, width, xstyle, className, style, }: MultiSelectorProps<T>): React.JSX.Element;
|
|
256
|
+
export declare function MultiSelector<T extends MultiSelectorOptionType>({ label, isLabelHidden, description, isOptional, isRequired, isDisabled, disabledMessage, options, value, onChange, changeAction, isLoading, placeholder: placeholderFromProps, size: sizeProp, variant, status, statusVariant, labelTooltip, startIcon, hasClear, hasSelectAll, selectAllLabel: selectAllLabelFromProps, hasSearch, searchPlaceholder: searchPlaceholderFromProps, emptyText: emptyTextFromProps, emptySearchText: emptySearchTextFromProps, triggerDisplay, formatValue, maxBadges, renderOption, indicatorPosition, isDefaultOpen, 'data-testid': testId, htmlName, width, xstyle, className, style, }: MultiSelectorProps<T>): React.JSX.Element;
|
|
240
257
|
export declare namespace MultiSelector {
|
|
241
258
|
var displayName: string;
|
|
242
259
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"MultiSelector.d.ts","sourceRoot":"","sources":["../../src/MultiSelector/MultiSelector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -275,6 +275,8 @@ export function MultiSelector({
|
|
|
275
275
|
selectAllLabel: selectAllLabelFromProps,
|
|
276
276
|
hasSearch = false,
|
|
277
277
|
searchPlaceholder: searchPlaceholderFromProps,
|
|
278
|
+
emptyText: emptyTextFromProps,
|
|
279
|
+
emptySearchText: emptySearchTextFromProps,
|
|
278
280
|
triggerDisplay = 'count',
|
|
279
281
|
formatValue,
|
|
280
282
|
maxBadges = 3,
|
|
@@ -296,6 +298,8 @@ export function MultiSelector({
|
|
|
296
298
|
const placeholder = placeholderFromProps ?? t('@astryx.multiSelector.selectPlaceholder');
|
|
297
299
|
const selectAllLabel = selectAllLabelFromProps ?? t('@astryx.multiSelector.selectAll');
|
|
298
300
|
const searchPlaceholder = searchPlaceholderFromProps ?? t('@astryx.multiSelector.searchPlaceholder');
|
|
301
|
+
const emptyText = emptyTextFromProps ?? t('@astryx.multiSelector.empty');
|
|
302
|
+
const emptySearchText = emptySearchTextFromProps ?? t('@astryx.multiSelector.emptySearchResults');
|
|
299
303
|
const size = useSize(sizeProp, 'md');
|
|
300
304
|
const effectiveStatusVariant = variant === 'ghost' && statusVariant === 'attached' ? 'detached' : statusVariant;
|
|
301
305
|
const triggerId = useId();
|
|
@@ -348,6 +352,13 @@ export function MultiSelector({
|
|
|
348
352
|
// Announce selection-count changes politely (comboboxes-7 announce path).
|
|
349
353
|
// Toggling options / select-all previously produced no audible feedback.
|
|
350
354
|
const announce = useAnnounce();
|
|
355
|
+
|
|
356
|
+
// The panel's empty message is role="presentation" and reaches assistive tech
|
|
357
|
+
// only through this live region, so the region has to speak whatever the
|
|
358
|
+
// panel shows. A ReactNode override cannot be spoken; fall back to the
|
|
359
|
+
// catalog copy for that case rather than announcing nothing.
|
|
360
|
+
const emptyAnnouncement = typeof emptyText === 'string' ? emptyText : t('@astryx.multiSelector.empty');
|
|
361
|
+
const emptySearchAnnouncement = typeof emptySearchText === 'string' ? emptySearchText : t('@astryx.multiSelector.emptySearchResults');
|
|
351
362
|
const announceSelection = useCallback(nextValue => {
|
|
352
363
|
const total = selectableItems.length;
|
|
353
364
|
const selectableSet = new Set(selectableItems.map(item => item.value));
|
|
@@ -454,11 +465,38 @@ export function MultiSelector({
|
|
|
454
465
|
announce('');
|
|
455
466
|
return;
|
|
456
467
|
}
|
|
468
|
+
// While isLoading the panel deliberately shows nothing, so announcing a
|
|
469
|
+
// result would put a claim in the one channel the screen has gone quiet
|
|
470
|
+
// for.
|
|
471
|
+
if (isLoading) {
|
|
472
|
+
announce('');
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
457
475
|
const count = filterOptionsByQuery(selectableItems, nextQuery).length;
|
|
458
|
-
announce(count === 0 ?
|
|
476
|
+
announce(count === 0 ? emptySearchAnnouncement : t('@astryx.multiSelector.resultCount', {
|
|
459
477
|
count
|
|
460
478
|
}));
|
|
461
|
-
}, [announce, selectableItems, t]);
|
|
479
|
+
}, [announce, isLoading, selectableItems, emptySearchAnnouncement, t]);
|
|
480
|
+
|
|
481
|
+
// The panel's empty message is role="presentation", so this region is the
|
|
482
|
+
// only route to assistive tech. It has to watch the STATE rather than the
|
|
483
|
+
// open event: the panel can become empty either on open or when a fetch
|
|
484
|
+
// lands with nothing in it, and an open-only announcement leaves the second
|
|
485
|
+
// case silent while the message sits on screen. The ref makes it fire once
|
|
486
|
+
// per arrival at that state rather than on every re-render.
|
|
487
|
+
const announcedEmptyRef = useRef(null);
|
|
488
|
+
useEffect(() => {
|
|
489
|
+
const isPanelEmpty = popover.isOpen && !isLoading && searchQuery === '' && selectableItems.length === 0;
|
|
490
|
+
if (!isPanelEmpty) {
|
|
491
|
+
announcedEmptyRef.current = null;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (announcedEmptyRef.current === emptyAnnouncement) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
announcedEmptyRef.current = emptyAnnouncement;
|
|
498
|
+
announce(emptyAnnouncement);
|
|
499
|
+
}, [popover.isOpen, isLoading, searchQuery, selectableItems.length, emptyAnnouncement, announce]);
|
|
462
500
|
|
|
463
501
|
// Handle toggle
|
|
464
502
|
// Clear all selected values. Shared by the clear button and the keyboard
|
|
@@ -800,13 +838,16 @@ export function MultiSelector({
|
|
|
800
838
|
// message out of the listbox's accessibility tree (role="listbox" only
|
|
801
839
|
// permits option/group children); the no-results outcome is announced
|
|
802
840
|
// via the result-count live region instead.
|
|
803
|
-
|
|
841
|
+
// While isLoading the options have not arrived yet, so asserting either
|
|
842
|
+
// message would be a claim the component cannot make; the trigger's
|
|
843
|
+
// spinner covers it.
|
|
844
|
+
if (realItemCount === 0 && !isLoading) {
|
|
804
845
|
elements.push(/*#__PURE__*/_jsx("div", {
|
|
805
846
|
role: "presentation",
|
|
806
847
|
...mergeProps(themeProps('multi-selector-empty-state'), {
|
|
807
848
|
className: "x1b2ylru x2b8uid xv1l7n4 x9ynric xcr08ib"
|
|
808
849
|
}),
|
|
809
|
-
children:
|
|
850
|
+
children: searchQuery ? emptySearchText : emptyText
|
|
810
851
|
}, "empty"));
|
|
811
852
|
return elements;
|
|
812
853
|
}
|
|
@@ -877,7 +918,7 @@ export function MultiSelector({
|
|
|
877
918
|
}
|
|
878
919
|
flushPending();
|
|
879
920
|
return elements;
|
|
880
|
-
}, [options, renderItem, sortedItems, searchQuery, hasSelectAll]);
|
|
921
|
+
}, [options, renderItem, sortedItems, searchQuery, hasSelectAll, isLoading, emptyText, emptySearchText]);
|
|
881
922
|
|
|
882
923
|
// The detached message box renders its own leading status icon, so the
|
|
883
924
|
// on-field icon would duplicate it — keep the chevron indicator instead.
|
|
@@ -7,9 +7,21 @@
|
|
|
7
7
|
* People paste numbers out of spreadsheets, and a spreadsheet writes them
|
|
8
8
|
* grouped, signed, and decorated. This reads those back. Everything locale
|
|
9
9
|
* specific — the grouping and decimal separators, the group sizes, the digit
|
|
10
|
-
* script — is derived from Intl and Unicode rather than a table, and
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* script — is derived from Intl and Unicode rather than a table, and text that
|
|
11
|
+
* no reading fits returns null so the field stays visibly invalid instead of
|
|
12
|
+
* committing a guess. Where both readings are well formed the locale's wins,
|
|
13
|
+
* the way utils/dateParser.ts settles an ambiguous date by locale preference.
|
|
14
|
+
* Grouping is well formed only when every group fits the locale's own sizes:
|
|
15
|
+
* the last exactly the primary size, each inner one exactly the secondary, the
|
|
16
|
+
* first no longer than that. So de-DE `1.234` is 1234, while `1.2345` (last
|
|
17
|
+
* group too long) and `1234.567` (too much ahead of it) keep the full stop as
|
|
18
|
+
* a decimal point. Groups of three fit every locale, so a separator repeated
|
|
19
|
+
* with three-digit groups is grouping in all of them and `1,234,567` reads the
|
|
20
|
+
* same everywhere; for a space or apostrophe, which no locale writes as a
|
|
21
|
+
* decimal point, one occurrence is enough, so en-US `1 234` is 1234.
|
|
22
|
+
*
|
|
23
|
+
* That paragraph is executable: numberParser.docblock.test.ts implements it as
|
|
24
|
+
* code, quote by quote, and fails when the parser and the words disagree.
|
|
13
25
|
*
|
|
14
26
|
* Which characters may separate digits at all is the one thing that is NOT
|
|
15
27
|
* read off the input: SEPARATOR_CHARS below is a bounded alphabet, the same
|
|
@@ -18,6 +30,7 @@
|
|
|
18
30
|
* SYNC: When modified, update these files to stay in sync:
|
|
19
31
|
* - /packages/core/src/NumberInput/NumberInput.tsx
|
|
20
32
|
* - /packages/core/src/NumberInput/numberParser.test.ts
|
|
33
|
+
* - /packages/core/src/NumberInput/numberParser.docblock.test.ts
|
|
21
34
|
*/
|
|
22
35
|
import type { Locale } from '../i18n';
|
|
23
36
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numberParser.d.ts","sourceRoot":"","sources":["../../src/NumberInput/numberParser.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"numberParser.d.ts","sourceRoot":"","sources":["../../src/NumberInput/numberParser.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,SAAS,CAAC;AAiPpC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CA8Ef;AAaD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3E"}
|
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|
* People paste numbers out of spreadsheets, and a spreadsheet writes them
|
|
10
10
|
* grouped, signed, and decorated. This reads those back. Everything locale
|
|
11
11
|
* specific — the grouping and decimal separators, the group sizes, the digit
|
|
12
|
-
* script — is derived from Intl and Unicode rather than a table, and
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* script — is derived from Intl and Unicode rather than a table, and text that
|
|
13
|
+
* no reading fits returns null so the field stays visibly invalid instead of
|
|
14
|
+
* committing a guess. Where both readings are well formed the locale's wins,
|
|
15
|
+
* the way utils/dateParser.ts settles an ambiguous date by locale preference.
|
|
16
|
+
* Grouping is well formed only when every group fits the locale's own sizes:
|
|
17
|
+
* the last exactly the primary size, each inner one exactly the secondary, the
|
|
18
|
+
* first no longer than that. So de-DE `1.234` is 1234, while `1.2345` (last
|
|
19
|
+
* group too long) and `1234.567` (too much ahead of it) keep the full stop as
|
|
20
|
+
* a decimal point. Groups of three fit every locale, so a separator repeated
|
|
21
|
+
* with three-digit groups is grouping in all of them and `1,234,567` reads the
|
|
22
|
+
* same everywhere; for a space or apostrophe, which no locale writes as a
|
|
23
|
+
* decimal point, one occurrence is enough, so en-US `1 234` is 1234.
|
|
24
|
+
*
|
|
25
|
+
* That paragraph is executable: numberParser.docblock.test.ts implements it as
|
|
26
|
+
* code, quote by quote, and fails when the parser and the words disagree.
|
|
15
27
|
*
|
|
16
28
|
* Which characters may separate digits at all is the one thing that is NOT
|
|
17
29
|
* read off the input: SEPARATOR_CHARS below is a bounded alphabet, the same
|
|
@@ -20,6 +32,7 @@
|
|
|
20
32
|
* SYNC: When modified, update these files to stay in sync:
|
|
21
33
|
* - /packages/core/src/NumberInput/NumberInput.tsx
|
|
22
34
|
* - /packages/core/src/NumberInput/numberParser.test.ts
|
|
35
|
+
* - /packages/core/src/NumberInput/numberParser.docblock.test.ts
|
|
23
36
|
*/
|
|
24
37
|
|
|
25
38
|
const symbolsByLocale = new Map();
|
|
@@ -64,15 +77,19 @@ const INVISIBLES = /[\u200B-\u200D\u200E\u200F\u061C\u2066-\u2069\uFEFF]/g;
|
|
|
64
77
|
const MINUS_SIGNS = '\u2212\u2012\u2013-';
|
|
65
78
|
|
|
66
79
|
/**
|
|
67
|
-
* Every character a locale writes between digits
|
|
68
|
-
*
|
|
69
|
-
*
|
|
80
|
+
* Every character a locale writes between digits. Sweeping every language ICU
|
|
81
|
+
* resolves, in every region, yields seven: comma, full stop, apostrophe,
|
|
82
|
+
* space (the whole space family folds to it under NFKC), the Arabic decimal
|
|
83
|
+
* and thousands separators, and U+060C — which only nqo writes, and which
|
|
84
|
+
* this alphabet leaves out. U+2019 is here in its place: no locale writes it,
|
|
85
|
+
* but the corpus pins it as the Swiss spelling.
|
|
70
86
|
*
|
|
71
87
|
* The alphabet is bounded on purpose. Read the candidates off the input
|
|
72
88
|
* instead and any repeated character is grouping, so a hyphenated ID commits
|
|
73
|
-
* as a number.
|
|
89
|
+
* as a number. It bounds characters and not shapes: a full stop is a real
|
|
90
|
+
* separator, so `192.168.100.200` is four well-formed groups.
|
|
74
91
|
*/
|
|
75
|
-
const SEPARATOR_CHARS = ",.' \u2019\
|
|
92
|
+
const SEPARATOR_CHARS = ",.' \u2019\u066B\u066C";
|
|
76
93
|
const NUMBER_BODY = new RegExp(`^[0-9${SEPARATOR_CHARS}]+$`);
|
|
77
94
|
/** No grouping and at most one full stop: the format `Number()` reads. */
|
|
78
95
|
const MACHINE_NUMBER = /^(?:\d+\.?\d*|\.\d+)$/;
|
|
@@ -167,7 +184,7 @@ function readUnder(core, group, decimal, primaryGroupSize, secondaryGroupSize, m
|
|
|
167
184
|
* beats the decimal-point reading.
|
|
168
185
|
*/
|
|
169
186
|
function isGroupOnlySeparator(separator) {
|
|
170
|
-
return /^[\s'\u2019
|
|
187
|
+
return /^[\s'\u2019]$/.test(separator);
|
|
171
188
|
}
|
|
172
189
|
function toPlainNumber(core, locale) {
|
|
173
190
|
if (!NUMBER_BODY.test(core)) {
|
|
@@ -189,6 +189,23 @@ interface SelectorPropsBase<T extends SelectorOptionType = SelectorOptionType> e
|
|
|
189
189
|
* @default 'Search...'
|
|
190
190
|
*/
|
|
191
191
|
searchPlaceholder?: string;
|
|
192
|
+
/**
|
|
193
|
+
* Content shown in the panel when there are no options to show, and
|
|
194
|
+
* announced in a polite live region when the panel opens. Not shown while
|
|
195
|
+
* `isLoading` — the options have not arrived yet.
|
|
196
|
+
* @default 'No options'
|
|
197
|
+
*/
|
|
198
|
+
emptyText?: ReactNode;
|
|
199
|
+
/**
|
|
200
|
+
* Content shown in the panel when a search query matches no options, and
|
|
201
|
+
* announced in a polite live region at the same time.
|
|
202
|
+
*
|
|
203
|
+
* The panel message is `role="presentation"`, so the live region is the only
|
|
204
|
+
* route to assistive tech: a string is announced verbatim, a richer node
|
|
205
|
+
* falls back to the default text since it cannot be spoken.
|
|
206
|
+
* @default 'No results found'
|
|
207
|
+
*/
|
|
208
|
+
emptySearchText?: ReactNode;
|
|
192
209
|
/**
|
|
193
210
|
* Position placement relative to the trigger.
|
|
194
211
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Selector.d.ts","sourceRoot":"","sources":["../../src/Selector/Selector.tsx"],"names":[],"mappings":"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"}
|
|
@@ -308,6 +308,8 @@ export function Selector(props) {
|
|
|
308
308
|
indicatorPosition = 'end',
|
|
309
309
|
hasSearch = false,
|
|
310
310
|
searchPlaceholder: searchPlaceholderFromProps,
|
|
311
|
+
emptyText: emptyTextFromProps,
|
|
312
|
+
emptySearchText: emptySearchTextFromProps,
|
|
311
313
|
placement,
|
|
312
314
|
isDefaultOpen = false,
|
|
313
315
|
'data-testid': testId,
|
|
@@ -324,6 +326,8 @@ export function Selector(props) {
|
|
|
324
326
|
});
|
|
325
327
|
const placeholder = placeholderFromProps ?? t('@astryx.selector.placeholder');
|
|
326
328
|
const searchPlaceholder = searchPlaceholderFromProps ?? t('@astryx.selector.searchPlaceholder');
|
|
329
|
+
const emptyText = emptyTextFromProps ?? t('@astryx.selector.empty');
|
|
330
|
+
const emptySearchText = emptySearchTextFromProps ?? t('@astryx.selector.emptySearchResults');
|
|
327
331
|
const hasClear = hasClearProp === true;
|
|
328
332
|
const size = useSize(sizeProp, 'md');
|
|
329
333
|
const effectiveStatusVariant = variant === 'ghost' && statusVariant === 'attached' ? 'detached' : statusVariant;
|
|
@@ -343,6 +347,9 @@ export function Selector(props) {
|
|
|
343
347
|
const searchRef = useRef(null);
|
|
344
348
|
const inputGroup = useInputGroup();
|
|
345
349
|
const [searchQuery, setSearchQuery] = useState('');
|
|
350
|
+
// Mirrors searchQuery for the seed path, which runs before focus reaches the
|
|
351
|
+
// input and so cannot read the rendered state.
|
|
352
|
+
const searchQueryRef = useRef('');
|
|
346
353
|
// A typed query shows the search row's clear (✕) button, which becomes
|
|
347
354
|
// the next tab stop after the search input.
|
|
348
355
|
const hasQuery = searchQuery.length > 0;
|
|
@@ -351,6 +358,13 @@ export function Selector(props) {
|
|
|
351
358
|
const isBusy = isLoading || optimisticValue !== normalizedValue;
|
|
352
359
|
const announce = useAnnounce();
|
|
353
360
|
|
|
361
|
+
// The panel's empty message is role="presentation" and reaches assistive tech
|
|
362
|
+
// only through this live region, so the region has to speak whatever the
|
|
363
|
+
// panel shows. A ReactNode override cannot be spoken; fall back to the
|
|
364
|
+
// catalog copy for that case rather than announcing nothing.
|
|
365
|
+
const emptyAnnouncement = typeof emptyText === 'string' ? emptyText : t('@astryx.selector.empty');
|
|
366
|
+
const emptySearchAnnouncement = typeof emptySearchText === 'string' ? emptySearchText : t('@astryx.selector.emptySearchResults');
|
|
367
|
+
|
|
354
368
|
// Disabled-reason tooltip. Disabled controls swallow pointer events, so the
|
|
355
369
|
// tooltip listeners attach to the trigger container (which already exists)
|
|
356
370
|
// and the trigger button stays perceivable via aria-disabled instead of the
|
|
@@ -398,6 +412,7 @@ export function Selector(props) {
|
|
|
398
412
|
// Layer for dropdown positioning
|
|
399
413
|
const handleLayerHide = useCallback(() => {
|
|
400
414
|
setSearchQuery('');
|
|
415
|
+
searchQueryRef.current = '';
|
|
401
416
|
resetTypeaheadRef.current();
|
|
402
417
|
// Clear any lingering result count when the popover closes so stale status
|
|
403
418
|
// text does not linger in the a11y tree.
|
|
@@ -425,22 +440,55 @@ export function Selector(props) {
|
|
|
425
440
|
// eslint-disable-next-line @eslint-react/exhaustive-deps -- mount-only: isDefaultOpen is not reactive
|
|
426
441
|
}, []);
|
|
427
442
|
|
|
428
|
-
// Announce the filtered result count from the query-change
|
|
443
|
+
// Announce the filtered result count from the query-change handlers (matching
|
|
429
444
|
// BaseTypeahead) rather than a reactive effect: computing the count for the
|
|
430
445
|
// next query here fires the announcement exactly once per keystroke and does
|
|
431
|
-
// not re-speak on unrelated re-renders.
|
|
432
|
-
|
|
433
|
-
|
|
446
|
+
// not re-speak on unrelated re-renders. Split out from handleSearchChange so
|
|
447
|
+
// the type-to-open seed below reaches the same live region — a query the user
|
|
448
|
+
// typed is a query however it arrived.
|
|
449
|
+
const announceSearchResults = useCallback(nextQuery => {
|
|
434
450
|
if (nextQuery.length === 0) {
|
|
435
451
|
// Emptying the query clears the region rather than announcing a count.
|
|
436
452
|
announce('');
|
|
437
453
|
return;
|
|
438
454
|
}
|
|
455
|
+
// While isLoading the panel deliberately shows nothing, so announcing a
|
|
456
|
+
// result would put a claim in the one channel the screen has gone quiet
|
|
457
|
+
// for.
|
|
458
|
+
if (isLoading) {
|
|
459
|
+
announce('');
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
439
462
|
const count = filterOptionsByQuery(selectableItems, nextQuery).length;
|
|
440
|
-
announce(count === 0 ?
|
|
463
|
+
announce(count === 0 ? emptySearchAnnouncement : t('@astryx.selector.resultCount', {
|
|
441
464
|
count
|
|
442
465
|
}));
|
|
443
|
-
}, [announce, selectableItems, t]);
|
|
466
|
+
}, [announce, isLoading, selectableItems, emptySearchAnnouncement, t]);
|
|
467
|
+
const handleSearchChange = useCallback(nextQuery => {
|
|
468
|
+
setSearchQuery(nextQuery);
|
|
469
|
+
searchQueryRef.current = nextQuery;
|
|
470
|
+
announceSearchResults(nextQuery);
|
|
471
|
+
}, [announceSearchResults]);
|
|
472
|
+
|
|
473
|
+
// The panel's empty message is role="presentation", so this region is the
|
|
474
|
+
// only route to assistive tech. It has to watch the STATE rather than the
|
|
475
|
+
// open event: the panel can become empty either on open or when a fetch
|
|
476
|
+
// lands with nothing in it, and an open-only announcement leaves the second
|
|
477
|
+
// case silent while the message sits on screen. The ref makes it fire once
|
|
478
|
+
// per arrival at that state rather than on every re-render.
|
|
479
|
+
const announcedEmptyRef = useRef(null);
|
|
480
|
+
useEffect(() => {
|
|
481
|
+
const isPanelEmpty = popover.isOpen && !isLoading && searchQuery === '' && selectableItems.length === 0;
|
|
482
|
+
if (!isPanelEmpty) {
|
|
483
|
+
announcedEmptyRef.current = null;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (announcedEmptyRef.current === emptyAnnouncement) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
announcedEmptyRef.current = emptyAnnouncement;
|
|
490
|
+
announce(emptyAnnouncement);
|
|
491
|
+
}, [popover.isOpen, isLoading, searchQuery, selectableItems.length, emptyAnnouncement, announce]);
|
|
444
492
|
|
|
445
493
|
// Calculate offset to position selected item over trigger. Explicit
|
|
446
494
|
// placement opts out of the selector-specific overlay behavior and uses the
|
|
@@ -477,10 +525,17 @@ export function Selector(props) {
|
|
|
477
525
|
}, [onChange, changeAction, startTransition, setOptimisticValue]);
|
|
478
526
|
|
|
479
527
|
// Type-to-find appends to the query rather than replacing it: characters
|
|
480
|
-
// typed before focus reaches the search input must not be dropped.
|
|
528
|
+
// typed before focus reaches the search input must not be dropped. The ref
|
|
529
|
+
// is what makes that safe without a state updater — it advances
|
|
530
|
+
// synchronously, so a second character seeded in the same tick still sees
|
|
531
|
+
// the first, and the announcement can be computed here rather than inside a
|
|
532
|
+
// setState callback.
|
|
481
533
|
const appendSearchQuery = useCallback(char => {
|
|
482
|
-
|
|
483
|
-
|
|
534
|
+
const nextQuery = searchQueryRef.current + char;
|
|
535
|
+
searchQueryRef.current = nextQuery;
|
|
536
|
+
setSearchQuery(nextQuery);
|
|
537
|
+
announceSearchResults(nextQuery);
|
|
538
|
+
}, [announceSearchResults]);
|
|
484
539
|
const commitValue = useCallback(newValue => {
|
|
485
540
|
onChange?.(newValue);
|
|
486
541
|
if (changeAction) {
|
|
@@ -732,8 +787,11 @@ export function Selector(props) {
|
|
|
732
787
|
const renderOptions = useCallback(() => {
|
|
733
788
|
const isSearching = hasSearch && Boolean(searchQuery);
|
|
734
789
|
|
|
735
|
-
// Nothing
|
|
736
|
-
|
|
790
|
+
// Nothing to show — either the query matched nothing, or no options were
|
|
791
|
+
// given at all. Both render the same slot with different copy. While
|
|
792
|
+
// isLoading the options have not arrived yet, so asserting either would be
|
|
793
|
+
// a claim the component cannot make; the trigger's spinner covers it.
|
|
794
|
+
if (filteredItems.length === 0 && !isLoading) {
|
|
737
795
|
// role="presentation" keeps the message out of the listbox's
|
|
738
796
|
// accessibility tree (role="listbox" only permits option/group
|
|
739
797
|
// children); the no-results outcome is announced via the
|
|
@@ -743,7 +801,7 @@ export function Selector(props) {
|
|
|
743
801
|
...mergeProps(themeProps('selector-empty-state'), {
|
|
744
802
|
className: "x1b2ylru x2b8uid xv1l7n4 x9ynric xcr08ib"
|
|
745
803
|
}),
|
|
746
|
-
children:
|
|
804
|
+
children: isSearching ? emptySearchText : emptyText
|
|
747
805
|
}, "empty")];
|
|
748
806
|
}
|
|
749
807
|
let flatIndex = 0;
|
|
@@ -805,7 +863,7 @@ export function Selector(props) {
|
|
|
805
863
|
}
|
|
806
864
|
}
|
|
807
865
|
return elements;
|
|
808
|
-
}, [options, renderItem, hasSearch, searchQuery, filteredItems]);
|
|
866
|
+
}, [options, renderItem, hasSearch, searchQuery, filteredItems, isLoading, emptyText, emptySearchText]);
|
|
809
867
|
|
|
810
868
|
// The detached message box renders its own leading status icon, so the
|
|
811
869
|
// on-field icon would duplicate it — keep the chevron indicator instead.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../src/Slider/Slider.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Slider.d.ts","sourceRoot":"","sources":["../../src/Slider/Slider.tsx"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAQhD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAQ9C,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAC3C,SAAS,CAAC,cAAc,CAAC,EACzB,UAAU,CACX;IACC,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,uCAAuC;IACvC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,WAAW,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IACxC,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAC3C,8DAA8D;IAC9D,KAAK,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,iEAAiE;IACjE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,8CAA8C;IAC9C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAC7C,iEAAiE;IACjE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAChD,8CAA8C;IAC9C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAoS/D;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,EAAC,GAAG,EAAE,GAAG,KAAK,EAAC,EAAE,WAAW,+BAgqBlD;yBAhqBe,MAAM"}
|
package/dist/Slider/Slider.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - /apps/storybook/stories/Slider.stories.tsx
|
|
16
16
|
* - /packages/cli/assets/templates/blocks/components/Slider/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
|
-
import { useId, useMemo, useRef, useState, useCallback } from 'react';
|
|
18
|
+
import { useEffect, useId, useMemo, useRef, useState, useCallback } from 'react';
|
|
19
19
|
import * as stylex from '@stylexjs/stylex';
|
|
20
20
|
import "../theme/tokens.stylex.js";
|
|
21
21
|
import { colorVars, spacingVars, radiusVars, durationVars, easeVars, typographyVars, typeScaleVars } from "../theme/tokens.stylex.js";
|
|
@@ -25,6 +25,7 @@ import { useTooltip } from "../Tooltip/index.js";
|
|
|
25
25
|
import { VisuallyHidden } from "../VisuallyHidden/index.js";
|
|
26
26
|
import { mergeProps, rtlStyles } from "../utils/index.js";
|
|
27
27
|
import { focusOutlineStyles } from "../utils/focusOutline.stylex.js";
|
|
28
|
+
import { getInteractionModality, trackInteractionModality } from "../utils/interactionModality.js";
|
|
28
29
|
import { isRtlElement } from "../hooks/isRtlElement.js";
|
|
29
30
|
import { themeProps } from "../utils/themeProps.js";
|
|
30
31
|
import { useMergedRefs } from "../hooks/useMergedRefs.js";
|
|
@@ -249,6 +250,24 @@ export function Slider({
|
|
|
249
250
|
const draggingThumbRef = useRef(null);
|
|
250
251
|
const [draggingThumb, setDraggingThumb] = useState(null);
|
|
251
252
|
|
|
253
|
+
// A thumb is a div[role="slider"], and `handlePointerDown` focuses it from
|
|
254
|
+
// script after preventDefault — which Chromium treats as focus-visible, so
|
|
255
|
+
// dragging with a mouse drew the keyboard ring (measured: `:focus-visible`
|
|
256
|
+
// true on pointerdown). Gate the ring on how the user last interacted; the
|
|
257
|
+
// CSS condition stays `:focus-visible`, this only narrows it.
|
|
258
|
+
const [keyboardFocusThumb, setKeyboardFocusThumb] = useState(null);
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
trackInteractionModality();
|
|
261
|
+
}, []);
|
|
262
|
+
const handleThumbFocus = useCallback((thumbIndex, _e) => {
|
|
263
|
+
// A disabled thumb draws no ring even when it stays focusable for its
|
|
264
|
+
// reason tooltip, so there is nothing to track for one.
|
|
265
|
+
setKeyboardFocusThumb(!isDisabled && getInteractionModality() === 'keyboard' ? thumbIndex : null);
|
|
266
|
+
}, [isDisabled]);
|
|
267
|
+
const handleThumbBlur = useCallback(_e => {
|
|
268
|
+
setKeyboardFocusThumb(null);
|
|
269
|
+
}, []);
|
|
270
|
+
|
|
252
271
|
// Disabled-reason tooltip. This is a *separate* useTooltip instance from the
|
|
253
272
|
// per-thumb value bubble (the `<Tooltip>` component below): it anchors to the
|
|
254
273
|
// track container and fires on hover/focus of the whole control. Disabled
|
|
@@ -387,6 +406,10 @@ export function Slider({
|
|
|
387
406
|
const thumbs = track.querySelectorAll('[role="slider"]');
|
|
388
407
|
thumbs[thumbIndex]?.focus();
|
|
389
408
|
}
|
|
409
|
+
// Also clear it explicitly: focusing an already-focused thumb fires no
|
|
410
|
+
// focus event, so a thumb the user had tabbed to would keep its ring
|
|
411
|
+
// through the drag.
|
|
412
|
+
setKeyboardFocusThumb(null);
|
|
390
413
|
if (typeof e.currentTarget.setPointerCapture === 'function') {
|
|
391
414
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
392
415
|
}
|
|
@@ -411,6 +434,13 @@ export function Slider({
|
|
|
411
434
|
if (isDisabled) {
|
|
412
435
|
return;
|
|
413
436
|
}
|
|
437
|
+
// Unlike a text field, a thumb has no caret to show where input is
|
|
438
|
+
// going, so a keypress after a mouse drag must bring the ring back.
|
|
439
|
+
// Ask the utility rather than assuming: a modifier chord (⌘R, ⌃C) is
|
|
440
|
+
// not navigation and must not re-ring a thumb the mouse is holding.
|
|
441
|
+
if (getInteractionModality() === 'keyboard') {
|
|
442
|
+
setKeyboardFocusThumb(thumbIndex);
|
|
443
|
+
}
|
|
414
444
|
const currentVal = values[thumbIndex];
|
|
415
445
|
let newVal;
|
|
416
446
|
switch (e.key) {
|
|
@@ -517,10 +547,12 @@ export function Slider({
|
|
|
517
547
|
"aria-labelledby": !isRange ? labelID : undefined,
|
|
518
548
|
"aria-describedby": ariaDescribedBy,
|
|
519
549
|
onKeyDown: e => handleKeyDown(thumbIndex, e),
|
|
550
|
+
onFocus: e => handleThumbFocus(thumbIndex, e),
|
|
551
|
+
onBlur: handleThumbBlur,
|
|
520
552
|
...mergeProps(themeProps('slider-thumb', {
|
|
521
553
|
orientation,
|
|
522
554
|
disabled: isDisabled ? 'disabled' : null
|
|
523
|
-
}), stylex.props(styles.thumb, isHorizontal ? styles.thumbHorizontal : rtlStyles.centerInline('50%'), !isDisabled && styles.thumbHover, !isDisabled && focusOutlineStyles.focusVisible, isDisabled && styles.thumbDisabled), undefined, positionStyle)
|
|
555
|
+
}), stylex.props(styles.thumb, isHorizontal ? styles.thumbHorizontal : rtlStyles.centerInline('50%'), !isDisabled && styles.thumbHover, !isDisabled && keyboardFocusThumb === thumbIndex && focusOutlineStyles.focusVisible, isDisabled && styles.thumbDisabled), undefined, positionStyle)
|
|
524
556
|
}, thumbIndex);
|
|
525
557
|
if (useValueTooltip) {
|
|
526
558
|
return /*#__PURE__*/_jsx(Tooltip, {
|
package/locales/en.json
CHANGED
|
@@ -511,6 +511,10 @@
|
|
|
511
511
|
"defaultMessage": "Search options",
|
|
512
512
|
"description": "Screen-reader-only accessible name for that same search input inside a MultiSelector."
|
|
513
513
|
},
|
|
514
|
+
"@astryx.multiSelector.empty": {
|
|
515
|
+
"defaultMessage": "No options",
|
|
516
|
+
"description": "Shown in a MultiSelector's dropdown panel when it was given no options at all, and announced in a polite live region on open. Very short (2 words); neutral tone, not error-y."
|
|
517
|
+
},
|
|
514
518
|
"@astryx.multiSelector.selectAllPartiallySelected": {
|
|
515
519
|
"defaultMessage": "{label}, partially selected",
|
|
516
520
|
"description": "Accessible name for the MultiSelector select-all option while only some options are selected. `{label}` is the visible select-all label (e.g. \"Select all\"). ARIA forbids aria-selected=\"mixed\" on options, so the indeterminate state is conveyed through the name instead. \"Partially\" = some but not all."
|
|
@@ -527,6 +531,10 @@
|
|
|
527
531
|
"defaultMessage": "Search options",
|
|
528
532
|
"description": "\"Options\" = the list of choices in the dropdown. Screen-reader-only accessible name for the search input inside a Selector."
|
|
529
533
|
},
|
|
534
|
+
"@astryx.selector.empty": {
|
|
535
|
+
"defaultMessage": "No options",
|
|
536
|
+
"description": "Shown in a Selector's dropdown panel when it was given no options at all, and announced in a polite live region on open. Very short (2 words); neutral tone, not error-y."
|
|
537
|
+
},
|
|
530
538
|
"@astryx.sideNav.label": {
|
|
531
539
|
"defaultMessage": "Side navigation",
|
|
532
540
|
"description": "Screen-reader-only accessible name for the primary vertical sidebar nav (usually on the left)."
|
|
@@ -1205,7 +1213,7 @@
|
|
|
1205
1213
|
},
|
|
1206
1214
|
"@astryx.multiSelector.emptySearchResults": {
|
|
1207
1215
|
"defaultMessage": "No results found",
|
|
1208
|
-
"description": "
|
|
1216
|
+
"description": "Shown in a MultiSelector's dropdown panel when the search query matches no options, and announced in a polite live region at the same time. Short; neutral tone (not error-y)."
|
|
1209
1217
|
},
|
|
1210
1218
|
"@astryx.multiSelector.resultCount": {
|
|
1211
1219
|
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
|
|
@@ -1213,7 +1221,7 @@
|
|
|
1213
1221
|
},
|
|
1214
1222
|
"@astryx.selector.emptySearchResults": {
|
|
1215
1223
|
"defaultMessage": "No results found",
|
|
1216
|
-
"description": "
|
|
1224
|
+
"description": "Shown in a Selector's dropdown panel when the search query matches no options, and announced in a polite live region at the same time. Short; neutral tone (not error-y)."
|
|
1217
1225
|
},
|
|
1218
1226
|
"@astryx.selector.resultCount": {
|
|
1219
1227
|
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
|
package/locales/pseudo.json
CHANGED
|
@@ -383,6 +383,9 @@
|
|
|
383
383
|
"@astryx.multiSelector.searchOptions": {
|
|
384
384
|
"defaultMessage": "⟦Šéàřçĥ óþţíóñš⟧"
|
|
385
385
|
},
|
|
386
|
+
"@astryx.multiSelector.empty": {
|
|
387
|
+
"defaultMessage": "⟦Ñó óþţíóñš⟧"
|
|
388
|
+
},
|
|
386
389
|
"@astryx.multiSelector.selectAllPartiallySelected": {
|
|
387
390
|
"defaultMessage": "⟦{label}, þàřţíàłłý šéłéçţéð⟧"
|
|
388
391
|
},
|
|
@@ -395,6 +398,9 @@
|
|
|
395
398
|
"@astryx.selector.searchOptions": {
|
|
396
399
|
"defaultMessage": "⟦Šéàřçĥ óþţíóñš⟧"
|
|
397
400
|
},
|
|
401
|
+
"@astryx.selector.empty": {
|
|
402
|
+
"defaultMessage": "⟦Ñó óþţíóñš⟧"
|
|
403
|
+
},
|
|
398
404
|
"@astryx.sideNav.label": {
|
|
399
405
|
"defaultMessage": "⟦Šíðé ñàṽíĝàţíóñ⟧"
|
|
400
406
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.5.0-canary.
|
|
3
|
+
"version": "0.5.0-canary.8985bb9",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -661,7 +661,7 @@
|
|
|
661
661
|
"react-dom": ">=19.0.0"
|
|
662
662
|
},
|
|
663
663
|
"devDependencies": {
|
|
664
|
-
"@astryxdesign/cli": "0.5.0-canary.
|
|
664
|
+
"@astryxdesign/cli": "0.5.0-canary.8985bb9",
|
|
665
665
|
"@babel/cli": "^8.0.4",
|
|
666
666
|
"@babel/core": "^7.29.7",
|
|
667
667
|
"@babel/preset-react": "^8.0.1",
|