@astryxdesign/core 0.4.2 → 0.4.3-canary.00bed29
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 +45 -0
- package/README.md +79 -45
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +3 -19
- package/dist/Banner/Banner.d.ts +9 -2
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +84 -33
- package/dist/Banner/index.d.ts +14 -12
- package/dist/Banner/index.d.ts.map +1 -1
- package/dist/Banner/index.js +10 -8
- package/dist/BottomSheet/BottomSheet.d.ts +66 -0
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheet.js +392 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts +60 -0
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetPanel.js +398 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts +61 -0
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetSwitcher.js +410 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts +21 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts.map +1 -0
- package/dist/BottomSheet/BottomSheetSwitcherContext.js +13 -0
- package/dist/BottomSheet/index.d.ts +11 -0
- package/dist/BottomSheet/index.d.ts.map +1 -0
- package/dist/BottomSheet/index.js +12 -0
- package/dist/BottomSheet/snapOffsets.d.ts +72 -0
- package/dist/BottomSheet/snapOffsets.d.ts.map +1 -0
- package/dist/BottomSheet/snapOffsets.js +130 -0
- package/dist/BottomSheet/useMobileKeyboard.d.ts +35 -0
- package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -0
- package/dist/BottomSheet/useMobileKeyboard.js +373 -0
- package/dist/BottomSheet/useSheetGestures.d.ts +180 -0
- package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -0
- package/dist/BottomSheet/useSheetGestures.js +942 -0
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +3 -4
- package/dist/Chat/ChatTokenizedText.js +1 -1
- package/dist/ComplexSelector/ComplexSelector.d.ts +38 -4
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +98 -31
- package/dist/ComplexSelector/index.d.ts +2 -2
- package/dist/ComplexSelector/index.d.ts.map +1 -1
- package/dist/ComplexSelector/index.js +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -2
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +9 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +31 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +9 -3
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +55 -12
- package/dist/MobileNav/MobileNav.d.ts +12 -0
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +102 -30
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +21 -7
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +9 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +4 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
- package/dist/PowerSearch/formatFilterValue.d.ts.map +1 -1
- package/dist/PowerSearch/formatFilterValue.js +2 -4
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +14 -3
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +5 -1
- package/dist/Table/columnUtils.d.ts.map +1 -1
- package/dist/Table/columnUtils.js +4 -1
- package/dist/TextArea/TextArea.d.ts +6 -3
- package/dist/TextArea/TextArea.d.ts.map +1 -1
- package/dist/TextArea/TextArea.js +17 -6
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +8 -1
- package/dist/Tokenizer/Tokenizer.js +11 -5
- package/dist/Tooltip/useTooltip.d.ts.map +1 -1
- package/dist/Tooltip/useTooltip.js +4 -1
- package/dist/TreeList/TreeList.js +2 -1
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +5 -3
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +10 -1
- package/dist/astryx.css +25 -2
- package/dist/hooks/index.d.ts +8 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +8 -1
- package/dist/hooks/useFocusTrap.d.ts +0 -11
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +1 -11
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/theme/defineTheme.d.ts +30 -7
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +36 -47
- package/dist/theme/expandColorScale.d.ts +17 -2
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +63 -33
- package/dist/theme/mergeComponents.d.ts +20 -0
- package/dist/theme/mergeComponents.d.ts.map +1 -0
- package/dist/theme/mergeComponents.js +56 -0
- package/dist/theme/onMediaTokens.d.ts +6 -1
- package/dist/theme/onMediaTokens.d.ts.map +1 -1
- package/dist/theme/onMediaTokens.js +11 -3
- package/dist/utils/characters.d.ts +27 -0
- package/dist/utils/characters.d.ts.map +1 -0
- package/dist/utils/characters.js +83 -0
- package/dist/utils/ime.d.ts +5 -0
- package/dist/utils/ime.d.ts.map +1 -0
- package/dist/utils/ime.js +63 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -1
- package/locales/en.json +48 -0
- package/locales/pseudo.json +36 -0
- package/package.json +9 -8
- package/src/Avatar/Avatar.test.tsx +6 -1
- package/src/Avatar/Avatar.tsx +3 -21
- package/src/Banner/Banner.doc.mjs +12 -10
- package/src/Banner/Banner.test.tsx +108 -0
- package/src/Banner/Banner.tsx +124 -38
- package/src/Banner/index.ts +15 -13
- package/src/BottomSheet/BottomSheet.doc.mjs +266 -0
- package/src/BottomSheet/BottomSheet.test.tsx +2114 -0
- package/src/BottomSheet/BottomSheet.tsx +545 -0
- package/src/BottomSheet/BottomSheetPanel.test.tsx +240 -0
- package/src/BottomSheet/BottomSheetPanel.tsx +547 -0
- package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +219 -0
- package/src/BottomSheet/BottomSheetSwitcher.test.tsx +850 -0
- package/src/BottomSheet/BottomSheetSwitcher.tsx +628 -0
- package/src/BottomSheet/BottomSheetSwitcherContext.ts +51 -0
- package/src/BottomSheet/index.ts +15 -0
- package/src/BottomSheet/snapOffsets.test.ts +187 -0
- package/src/BottomSheet/snapOffsets.ts +159 -0
- package/src/BottomSheet/useMobileKeyboard.ts +499 -0
- package/src/BottomSheet/useSheetGestures.test.ts +1014 -0
- package/src/BottomSheet/useSheetGestures.ts +1407 -0
- package/src/Chat/ChatComposerInput.tsx +3 -4
- package/src/Chat/ChatTokenizedText.tsx +1 -1
- package/src/ComplexSelector/ComplexSelector.doc.mjs +55 -6
- package/src/ComplexSelector/ComplexSelector.test.tsx +197 -6
- package/src/ComplexSelector/ComplexSelector.tsx +156 -28
- package/src/ComplexSelector/index.ts +3 -1
- package/src/ContextMenu/ContextMenu.tsx +4 -2
- package/src/DateInput/DateInput.test.tsx +21 -0
- package/src/DateInput/DateInput.tsx +9 -1
- package/src/DateTimeInput/DateTimeInput.doc.mjs +4 -0
- package/src/DateTimeInput/DateTimeInput.test.tsx +137 -0
- package/src/DateTimeInput/DateTimeInput.tsx +39 -2
- package/src/Dialog/Dialog.tsx +2 -1
- package/src/FileInput/FileInput.test.tsx +53 -9
- package/src/Lightbox/Lightbox.test.tsx +49 -28
- package/src/Lightbox/Lightbox.tsx +7 -3
- package/src/Markdown/parser.test.ts +53 -0
- package/src/Markdown/parser.ts +53 -12
- package/src/MobileNav/MobileNav.tsx +128 -37
- package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +426 -0
- package/src/MobileNav/MobileNavCloseTiming.test.ts +58 -0
- package/src/MobileNav/MobileNavCloseVisibility.test.tsx +172 -0
- package/src/MultiSelector/MultiSelector.test.tsx +195 -51
- package/src/MultiSelector/MultiSelector.tsx +22 -8
- package/src/NumberInput/NumberInput.test.tsx +18 -0
- package/src/NumberInput/NumberInput.tsx +9 -1
- package/src/PowerSearch/PowerSearch.test.tsx +48 -3
- package/src/PowerSearch/PowerSearch.tsx +4 -1
- package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
- package/src/PowerSearch/formatFilterValue.test.ts +22 -0
- package/src/PowerSearch/formatFilterValue.ts +2 -4
- package/src/Selector/Selector.test.tsx +85 -26
- package/src/Selector/Selector.tsx +13 -4
- package/src/Switch/Switch.test.tsx +16 -0
- package/src/Switch/Switch.tsx +7 -0
- package/src/Table/Table.test.tsx +6 -0
- package/src/Table/columnUtils.ts +3 -1
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextArea/TextArea.test.tsx +72 -0
- package/src/TextArea/TextArea.tsx +26 -8
- package/src/TimeInput/TimeInput.test.tsx +23 -0
- package/src/TimeInput/TimeInput.tsx +8 -0
- package/src/Tokenizer/Tokenizer.test.tsx +50 -32
- package/src/Tokenizer/Tokenizer.tsx +5 -5
- package/src/Tooltip/useTooltip.tsx +4 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/TreeList/TreeList.tsx +1 -1
- package/src/Typeahead/BaseTypeahead.tsx +10 -3
- package/src/Typeahead/Typeahead.test.tsx +116 -7
- package/src/Typeahead/Typeahead.tsx +10 -1
- package/src/__tests__/stubMatchMedia.test.ts +79 -0
- package/src/__tests__/stubMatchMedia.ts +95 -0
- package/src/hooks/index.ts +9 -5
- package/src/hooks/useFocusTrap.ts +1 -14
- package/src/hooks/useInputStatusIcon.test.tsx +19 -0
- package/src/hooks/useInputStatusIcon.tsx +7 -0
- package/src/index.ts +1 -0
- package/src/theme/defineTheme.test.ts +127 -0
- package/src/theme/defineTheme.ts +70 -55
- package/src/theme/expandColorScale.test.ts +138 -0
- package/src/theme/expandColorScale.ts +89 -44
- package/src/theme/extensibleAxes.test.ts +365 -0
- package/src/theme/mergeComponents.ts +59 -0
- package/src/theme/onMediaTokens.ts +9 -2
- package/src/utils/characters.test.ts +141 -0
- package/src/utils/characters.ts +88 -0
- package/src/utils/ime.test.ts +32 -0
- package/src/utils/ime.ts +69 -0
- package/src/utils/index.ts +4 -0
- package/dist/astryx.umd.js +0 -147
- package/dist/astryx.umd.js.map +0 -7
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number of characters in a string — the count a person would give, where one
|
|
3
|
+
* emoji, flag, or accented letter is one character. Replacement for `.length`
|
|
4
|
+
* on user-visible strings, which counts internal storage units instead.
|
|
5
|
+
*/
|
|
6
|
+
export declare function characterCount(str: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* The first character of a string, or '' when empty. Replacement for
|
|
9
|
+
* `.charAt(0)`, which can return half of an emoji. Reads only the first
|
|
10
|
+
* character, so cost does not scale with the length of the string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function firstCharacter(str: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Truncate to at most `max` characters, ellipsis included: strings within
|
|
15
|
+
* `max` characters pass through unchanged; longer strings are cut so the
|
|
16
|
+
* result — content plus `ellipsis` — is exactly `max` characters (or just the
|
|
17
|
+
* ellipsis, when `max` is smaller than the ellipsis itself). Replacement for
|
|
18
|
+
* `str.slice(0, n) + '…'`, which can cut an emoji in half.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```
|
|
22
|
+
* truncateCharacters('abcdefghij', 5) // 'abcd…'
|
|
23
|
+
* truncateCharacters('abcdefghij', 8, '...') // 'abcde...'
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function truncateCharacters(str: string, max: number, ellipsis?: string): string;
|
|
27
|
+
//# sourceMappingURL=characters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"characters.d.ts","sourceRoot":"","sources":["../../src/utils/characters.ts"],"names":[],"mappings":"AAqCA;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKlD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,SAAM,GACb,MAAM,CAOR"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file characters.ts
|
|
5
|
+
* @input Strings that may contain characters made of several code units
|
|
6
|
+
* (emoji, flags, joined emoji sequences, accented letters)
|
|
7
|
+
* @output Exports characterCount, firstCharacter, truncateCharacters —
|
|
8
|
+
* replacements for .length / .charAt(0) / .slice(0, n) that measure
|
|
9
|
+
* and cut user-visible strings the way a person reads them
|
|
10
|
+
* @position Shared utility; consumed by Avatar, TextArea, PowerSearch, Table
|
|
11
|
+
*
|
|
12
|
+
* SYNC: When modified, update:
|
|
13
|
+
* - /packages/core/src/utils/characters.test.ts
|
|
14
|
+
* - /packages/core/src/utils/index.ts
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reuse a single segmenter when the runtime supports Intl.Segmenter.
|
|
19
|
+
* `grapheme` is the Intl granularity name for a user-perceived character.
|
|
20
|
+
*/
|
|
21
|
+
const characterSegmenter = typeof Intl.Segmenter === 'function' ? new Intl.Segmenter(undefined, {
|
|
22
|
+
granularity: 'grapheme'
|
|
23
|
+
}) : null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Split a string into user-perceived characters. The code-point fallback
|
|
27
|
+
* keeps surrogate pairs intact but may split joined emoji sequences and flag
|
|
28
|
+
* pairs on runtimes without Intl.Segmenter.
|
|
29
|
+
*/
|
|
30
|
+
function splitCharacters(str) {
|
|
31
|
+
if (characterSegmenter) {
|
|
32
|
+
return [...characterSegmenter.segment(str)].map(s => s.segment);
|
|
33
|
+
}
|
|
34
|
+
return [...str];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Number of characters in a string — the count a person would give, where one
|
|
39
|
+
* emoji, flag, or accented letter is one character. Replacement for `.length`
|
|
40
|
+
* on user-visible strings, which counts internal storage units instead.
|
|
41
|
+
*/
|
|
42
|
+
export function characterCount(str) {
|
|
43
|
+
if (str === '') {
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
return splitCharacters(str).length;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The first character of a string, or '' when empty. Replacement for
|
|
51
|
+
* `.charAt(0)`, which can return half of an emoji. Reads only the first
|
|
52
|
+
* character, so cost does not scale with the length of the string.
|
|
53
|
+
*/
|
|
54
|
+
export function firstCharacter(str) {
|
|
55
|
+
if (characterSegmenter) {
|
|
56
|
+
const first = characterSegmenter.segment(str)[Symbol.iterator]().next();
|
|
57
|
+
return first.done ? '' : first.value.segment;
|
|
58
|
+
}
|
|
59
|
+
const codePoint = str.codePointAt(0);
|
|
60
|
+
return codePoint == null ? '' : String.fromCodePoint(codePoint);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Truncate to at most `max` characters, ellipsis included: strings within
|
|
65
|
+
* `max` characters pass through unchanged; longer strings are cut so the
|
|
66
|
+
* result — content plus `ellipsis` — is exactly `max` characters (or just the
|
|
67
|
+
* ellipsis, when `max` is smaller than the ellipsis itself). Replacement for
|
|
68
|
+
* `str.slice(0, n) + '…'`, which can cut an emoji in half.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```
|
|
72
|
+
* truncateCharacters('abcdefghij', 5) // 'abcd…'
|
|
73
|
+
* truncateCharacters('abcdefghij', 8, '...') // 'abcde...'
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function truncateCharacters(str, max, ellipsis = '…') {
|
|
77
|
+
const characters = splitCharacters(str);
|
|
78
|
+
if (characters.length <= max) {
|
|
79
|
+
return str;
|
|
80
|
+
}
|
|
81
|
+
const keep = Math.max(max - characterCount(ellipsis), 0);
|
|
82
|
+
return characters.slice(0, keep).join('') + ellipsis;
|
|
83
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ime.d.ts","sourceRoot":"","sources":["../../src/utils/ime.ts"],"names":[],"mappings":"AA6DA,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAIV"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file ime.ts
|
|
5
|
+
* @input Receives a keyboard event (React SyntheticEvent's `nativeEvent`, or a
|
|
6
|
+
* native DOM KeyboardEvent) with optional `isComposing` / `keyCode`.
|
|
7
|
+
* @output Exports `isImeKeyEvent`, the canonical predicate for "this keydown is
|
|
8
|
+
* part of an in-progress IME composition and must not be treated as a command".
|
|
9
|
+
* @position Shared low-level utility; consumed by every editable/overlay surface
|
|
10
|
+
* that handles Enter/Escape/arrows in `onKeyDown` (Typeahead, PowerSearch,
|
|
11
|
+
* Dialog, ContextMenu, Tooltip, Chat composer, focus-trap Escape, ...).
|
|
12
|
+
*
|
|
13
|
+
* ── Why this exists (read once, here — do not re-explain at call sites) ──
|
|
14
|
+
*
|
|
15
|
+
* When a CJK user (Korean / Japanese / Chinese) is composing text via an IME,
|
|
16
|
+
* the browser fires a `keydown` event to COMMIT or CANCEL the pending
|
|
17
|
+
* composition (Enter commits the highlighted candidate; Escape cancels it;
|
|
18
|
+
* ArrowUp/Down/Home/End navigate the candidate window). Crucially this
|
|
19
|
+
* `keydown` fires BEFORE the `compositionend` event that actually writes the
|
|
20
|
+
* committed text into the field. So a naive `onKeyDown` handler sees a bare
|
|
21
|
+
* "Enter" or "Escape" and misreads the composition-commit/cancel as an
|
|
22
|
+
* application command — accepting a typeahead suggestion, submitting a chat
|
|
23
|
+
* message, closing a dialog/menu/tooltip, or saving a filter — mid-composition.
|
|
24
|
+
* The fix is to detect the composing keydown and early-return before running
|
|
25
|
+
* any command logic.
|
|
26
|
+
*
|
|
27
|
+
* Two signals, both needed:
|
|
28
|
+
*
|
|
29
|
+
* 1. `event.isComposing === true` — the modern, spec'd signal
|
|
30
|
+
* (https://www.w3.org/TR/uievents/#dom-keyboardevent-iscomposing). This is
|
|
31
|
+
* the primary check and is reliable in current evergreen browsers.
|
|
32
|
+
*
|
|
33
|
+
* 2. `event.keyCode === 229` — the legacy fallback. `229` is the sentinel
|
|
34
|
+
* keyCode browsers report for "the key event is being processed by an IME".
|
|
35
|
+
* It is still load-bearing: some IMEs and older Safari fire the composing
|
|
36
|
+
* `keydown` with `isComposing` NOT yet set to `true`, but DO report
|
|
37
|
+
* keyCode 229. Keeping both makes the guard robust across the browser
|
|
38
|
+
* matrix we support. Do not drop the 229 fallback without a browser-matrix
|
|
39
|
+
* audit.
|
|
40
|
+
*
|
|
41
|
+
* ── Which event object to pass ──
|
|
42
|
+
*
|
|
43
|
+
* React's SyntheticEvent for KeyboardEvent *does* surface `isComposing`, but to
|
|
44
|
+
* avoid any cross-browser normalization gap prefer passing the *native* event
|
|
45
|
+
* (`e.nativeEvent`) from React handlers — that is what BaseTypeahead,
|
|
46
|
+
* PowerSearch, and the Chat composer do. Native DOM listeners (Dialog,
|
|
47
|
+
* ContextMenu, Tooltip, focus-trap) pass the DOM `KeyboardEvent` directly.
|
|
48
|
+
* The parameter is intentionally structurally typed so both shapes are accepted.
|
|
49
|
+
*
|
|
50
|
+
* Note: `keyCode` is deprecated on the DOM `KeyboardEvent` type but is still
|
|
51
|
+
* present at runtime; we read it defensively via the optional structural field.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The sentinel `keyCode` browsers report while a key event is being processed
|
|
56
|
+
* by an IME (the composing keydown that fires before `compositionend`). See the
|
|
57
|
+
* file header for why this legacy signal is still load-bearing alongside
|
|
58
|
+
* `isComposing`.
|
|
59
|
+
*/
|
|
60
|
+
const IME_PROCESSING_KEY_CODE = 229;
|
|
61
|
+
export function isImeKeyEvent(event) {
|
|
62
|
+
return event.isComposing === true || event.keyCode === IME_PROCESSING_KEY_CODE;
|
|
63
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { parseISOTime, formatISOTime, formatDisplayTime12h, formatDisplayTime24h
|
|
|
13
13
|
export type { ISOTimeString, ParsedTime } from './timeParser';
|
|
14
14
|
export { parseStyleKey } from './parseStyleKey';
|
|
15
15
|
export { getKey, type Key, type KeyFallback } from './getKey';
|
|
16
|
+
export { characterCount, firstCharacter, truncateCharacters } from './characters';
|
|
16
17
|
export { mergeProps } from './mergeProps';
|
|
17
18
|
export { mergeRefs } from './mergeRefs';
|
|
18
19
|
export { isFocusDetached } from './focusReturn';
|
|
@@ -30,4 +31,5 @@ export type { RGBA } from './color';
|
|
|
30
31
|
export { devWarn, devError, warnOnce, formatDevMessage } from './devWarning';
|
|
31
32
|
export { rtlStyles } from './rtlStyles';
|
|
32
33
|
export { focusOutlineStyles, focusOutlineProps } from './focusOutline.stylex';
|
|
34
|
+
export { isImeKeyEvent } from './ime';
|
|
33
35
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAC,UAAU,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACtD,YAAY,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAC,IAAI,EAAC,MAAM,SAAS,CAAC;AAElC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAItC,OAAO,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,YAAY,EAAC,SAAS,EAAC,MAAM,SAAS,CAAC;AAEvC,YAAY,EAAC,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAEhF,OAAO,EACL,cAAc,EACd,SAAS,EACT,QAAQ,EACR,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,8BAA8B,EAC9B,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAC,aAAa,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAC,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAC,MAAM,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAC,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAC,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAC,UAAU,EAAC,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,eAAe,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAC,UAAU,EAAE,mBAAmB,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EACV,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,UAAU,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACtD,YAAY,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAC,aAAa,EAAE,eAAe,EAAC,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAC,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,YAAY,EAAC,SAAS,EAAE,mBAAmB,EAAC,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAC,IAAI,EAAC,MAAM,SAAS,CAAC;AAElC,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AAItC,OAAO,EAAC,kBAAkB,EAAE,iBAAiB,EAAC,MAAM,uBAAuB,CAAC;AAE5E,OAAO,EAAC,aAAa,EAAC,MAAM,OAAO,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { plainDateCreate, plainDateFromISO, plainDateToISO, plainDateToDate, pla
|
|
|
12
12
|
export { parseISOTime, formatISOTime, formatDisplayTime12h, formatDisplayTime24h, parseTimeInput, compareTime, isTimeInRange, clampTime, adjustTime, createISOTimeString } from "./timeParser.js";
|
|
13
13
|
export { parseStyleKey } from "./parseStyleKey.js";
|
|
14
14
|
export { getKey } from "./getKey.js";
|
|
15
|
+
export { characterCount, firstCharacter, truncateCharacters } from "./characters.js";
|
|
15
16
|
export { mergeProps } from "./mergeProps.js";
|
|
16
17
|
export { mergeRefs } from "./mergeRefs.js";
|
|
17
18
|
export { isFocusDetached } from "./focusReturn.js";
|
|
@@ -27,4 +28,5 @@ export { rtlStyles } from "./rtlStyles.js";
|
|
|
27
28
|
|
|
28
29
|
// The shared focus ring, exported for @astryxdesign/lab — same reason
|
|
29
30
|
// rtlStyles is: a style that must be identical across packages, not copied.
|
|
30
|
-
export { focusOutlineStyles, focusOutlineProps } from "./focusOutline.stylex.js";
|
|
31
|
+
export { focusOutlineStyles, focusOutlineProps } from "./focusOutline.stylex.js";
|
|
32
|
+
export { isImeKeyEvent } from "./ime.js";
|
package/locales/en.json
CHANGED
|
@@ -1134,5 +1134,53 @@
|
|
|
1134
1134
|
"@astryx.commandPalette.footer.close": {
|
|
1135
1135
|
"defaultMessage": "Close",
|
|
1136
1136
|
"description": "Keyboard hint in the CommandPalette footer, paired with the Escape key icon. Tells users Escape dismisses the palette."
|
|
1137
|
+
},
|
|
1138
|
+
"@astryx.multiSelector.selectionCleared": {
|
|
1139
|
+
"defaultMessage": "Selection cleared",
|
|
1140
|
+
"description": "Polite screen-reader announcement after the last selected option in a MultiSelector is deselected. Live-region text, never shown on screen; past participle (\"has been cleared\")."
|
|
1141
|
+
},
|
|
1142
|
+
"@astryx.multiSelector.allSelected": {
|
|
1143
|
+
"defaultMessage": "All selected",
|
|
1144
|
+
"description": "Polite screen-reader announcement after every option in a MultiSelector becomes selected. Live-region text, never shown on screen; \"all\" is a determiner (as in \"all the options\")."
|
|
1145
|
+
},
|
|
1146
|
+
"@astryx.multiSelector.selectionCount": {
|
|
1147
|
+
"defaultMessage": "{count, number} of {total, number} selected",
|
|
1148
|
+
"description": "Polite screen-reader announcement of how many MultiSelector options are selected out of the total, spoken after each toggle. Live-region text, never shown on screen; example: `3 of 12 selected`."
|
|
1149
|
+
},
|
|
1150
|
+
"@astryx.multiSelector.emptySearchResults": {
|
|
1151
|
+
"defaultMessage": "No results found",
|
|
1152
|
+
"description": "Polite screen-reader announcement when a MultiSelector search query matches no options. Live-region text, never shown on screen; neutral tone (not error-y)."
|
|
1153
|
+
},
|
|
1154
|
+
"@astryx.multiSelector.resultCount": {
|
|
1155
|
+
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
|
|
1156
|
+
"description": "Polite screen-reader announcement of how many MultiSelector options match the search query, spoken as the user types. Live-region text, never shown on screen; example: `1 result`, `12 results`."
|
|
1157
|
+
},
|
|
1158
|
+
"@astryx.selector.emptySearchResults": {
|
|
1159
|
+
"defaultMessage": "No results found",
|
|
1160
|
+
"description": "Polite screen-reader announcement when a Selector search query matches no options. Live-region text, never shown on screen; neutral tone (not error-y)."
|
|
1161
|
+
},
|
|
1162
|
+
"@astryx.selector.resultCount": {
|
|
1163
|
+
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
|
|
1164
|
+
"description": "Polite screen-reader announcement of how many Selector options match the search query, spoken as the user types. Live-region text, never shown on screen; example: `1 result`, `12 results`."
|
|
1165
|
+
},
|
|
1166
|
+
"@astryx.typeahead.resultCount": {
|
|
1167
|
+
"defaultMessage": "{count, number} {count, plural, one {result} other {results}}",
|
|
1168
|
+
"description": "Polite screen-reader announcement of how many suggestions a Typeahead search returned. Live-region text, never shown on screen; example: `1 result`, `12 results`."
|
|
1169
|
+
},
|
|
1170
|
+
"@astryx.tokenizer.tokenAdded": {
|
|
1171
|
+
"defaultMessage": "Added {label}",
|
|
1172
|
+
"description": "Polite screen-reader announcement after a token is added to a Tokenizer, either picked from the suggestions or newly created. Live-region text, never shown on screen; `{label}` is the token's visible text."
|
|
1173
|
+
},
|
|
1174
|
+
"@astryx.tokenizer.tokenRemoved": {
|
|
1175
|
+
"defaultMessage": "Removed {label}",
|
|
1176
|
+
"description": "Polite screen-reader announcement after a token is removed from a Tokenizer, by its × button or by Backspace. Live-region text, never shown on screen; `{label}` is the token's visible text."
|
|
1177
|
+
},
|
|
1178
|
+
"@astryx.lightbox.imagePosition": {
|
|
1179
|
+
"defaultMessage": "Image {index, number} of {total, number}",
|
|
1180
|
+
"description": "Polite screen-reader announcement when a Lightbox moves to a media item that has no alt text. Live-region text, never shown on screen; `{index}` is 1-based, example: `Image 3 of 12`."
|
|
1181
|
+
},
|
|
1182
|
+
"@astryx.lightbox.mediaPosition": {
|
|
1183
|
+
"defaultMessage": "{alt}, {index, number} of {total, number}",
|
|
1184
|
+
"description": "Polite screen-reader announcement when a Lightbox moves to a media item that has alt text, composing that alt with the item's position. Live-region text, never shown on screen; example: `Sunset over the bay, 3 of 12`."
|
|
1137
1185
|
}
|
|
1138
1186
|
}
|
package/locales/pseudo.json
CHANGED
|
@@ -850,5 +850,41 @@
|
|
|
850
850
|
},
|
|
851
851
|
"@astryx.commandPalette.footer.close": {
|
|
852
852
|
"defaultMessage": "⟦Çłóšé⟧"
|
|
853
|
+
},
|
|
854
|
+
"@astryx.multiSelector.selectionCleared": {
|
|
855
|
+
"defaultMessage": "⟦Šéłéçţíóñ çłéàřéð⟧"
|
|
856
|
+
},
|
|
857
|
+
"@astryx.multiSelector.allSelected": {
|
|
858
|
+
"defaultMessage": "⟦Àłł šéłéçţéð⟧"
|
|
859
|
+
},
|
|
860
|
+
"@astryx.multiSelector.selectionCount": {
|
|
861
|
+
"defaultMessage": "⟦{count, number} óƒ {total, number} šéłéçţéð⟧"
|
|
862
|
+
},
|
|
863
|
+
"@astryx.multiSelector.emptySearchResults": {
|
|
864
|
+
"defaultMessage": "⟦Ñó řéšúłţš ƒóúñð⟧"
|
|
865
|
+
},
|
|
866
|
+
"@astryx.multiSelector.resultCount": {
|
|
867
|
+
"defaultMessage": "⟦{count, number} {count, plural, one {result} other {results}}⟧"
|
|
868
|
+
},
|
|
869
|
+
"@astryx.selector.emptySearchResults": {
|
|
870
|
+
"defaultMessage": "⟦Ñó řéšúłţš ƒóúñð⟧"
|
|
871
|
+
},
|
|
872
|
+
"@astryx.selector.resultCount": {
|
|
873
|
+
"defaultMessage": "⟦{count, number} {count, plural, one {result} other {results}}⟧"
|
|
874
|
+
},
|
|
875
|
+
"@astryx.typeahead.resultCount": {
|
|
876
|
+
"defaultMessage": "⟦{count, number} {count, plural, one {result} other {results}}⟧"
|
|
877
|
+
},
|
|
878
|
+
"@astryx.tokenizer.tokenAdded": {
|
|
879
|
+
"defaultMessage": "⟦Àððéð {label}⟧"
|
|
880
|
+
},
|
|
881
|
+
"@astryx.tokenizer.tokenRemoved": {
|
|
882
|
+
"defaultMessage": "⟦Řéɱóṽéð {label}⟧"
|
|
883
|
+
},
|
|
884
|
+
"@astryx.lightbox.imagePosition": {
|
|
885
|
+
"defaultMessage": "⟦Íɱàĝé {index, number} óƒ {total, number}⟧"
|
|
886
|
+
},
|
|
887
|
+
"@astryx.lightbox.mediaPosition": {
|
|
888
|
+
"defaultMessage": "⟦{alt}, {index, number} óƒ {total, number}⟧"
|
|
853
889
|
}
|
|
854
890
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3-canary.00bed29",
|
|
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",
|
|
@@ -27,8 +27,6 @@
|
|
|
27
27
|
"type": "module",
|
|
28
28
|
"main": "./dist/index.js",
|
|
29
29
|
"types": "./dist/index.d.ts",
|
|
30
|
-
"unpkg": "./dist/astryx.umd.js",
|
|
31
|
-
"jsdelivr": "./dist/astryx.umd.js",
|
|
32
30
|
"sideEffects": [
|
|
33
31
|
"**/*.stylex.ts",
|
|
34
32
|
"**/*.stylex.js",
|
|
@@ -49,7 +47,6 @@
|
|
|
49
47
|
"types": "./src/astryx.css.d.ts",
|
|
50
48
|
"default": "./dist/astryx.css"
|
|
51
49
|
},
|
|
52
|
-
"./astryx.umd.js": "./dist/astryx.umd.js",
|
|
53
50
|
"./tailwind-theme.css": {
|
|
54
51
|
"types": "./src/tailwind-theme.css.d.ts",
|
|
55
52
|
"default": "./src/tailwind-theme.css"
|
|
@@ -122,6 +119,11 @@
|
|
|
122
119
|
"types": "./dist/Blockquote/index.d.ts",
|
|
123
120
|
"default": "./dist/Blockquote/index.js"
|
|
124
121
|
},
|
|
122
|
+
"./BottomSheet": {
|
|
123
|
+
"source": "./src/BottomSheet/index.ts",
|
|
124
|
+
"types": "./dist/BottomSheet/index.d.ts",
|
|
125
|
+
"default": "./dist/BottomSheet/index.js"
|
|
126
|
+
},
|
|
125
127
|
"./Breadcrumbs": {
|
|
126
128
|
"source": "./src/Breadcrumbs/index.ts",
|
|
127
129
|
"types": "./dist/Breadcrumbs/index.d.ts",
|
|
@@ -654,6 +656,7 @@
|
|
|
654
656
|
"react-dom": ">=19.0.0"
|
|
655
657
|
},
|
|
656
658
|
"devDependencies": {
|
|
659
|
+
"@astryxdesign/cli": "0.4.3-canary.00bed29",
|
|
657
660
|
"@babel/cli": "^8.0.4",
|
|
658
661
|
"@babel/core": "^7.29.7",
|
|
659
662
|
"@babel/preset-react": "^8.0.1",
|
|
@@ -662,19 +665,17 @@
|
|
|
662
665
|
"@testing-library/dom": "^10.0.0",
|
|
663
666
|
"@testing-library/jest-dom": "^6.6.0",
|
|
664
667
|
"@testing-library/react": "^16.3.2",
|
|
665
|
-
"rimraf": "^6.0.1"
|
|
666
|
-
"@astryxdesign/cli": "0.4.2"
|
|
668
|
+
"rimraf": "^6.0.1"
|
|
667
669
|
},
|
|
668
670
|
"dependencies": {
|
|
669
671
|
"intl-messageformat": "^11.2.9"
|
|
670
672
|
},
|
|
671
673
|
"scripts": {
|
|
672
674
|
"postinstall": "node scripts/postinstall.mjs",
|
|
673
|
-
"build": "rimraf dist && pnpm build:i18n && pnpm build:esm && tsc --project tsconfig.build.json && pnpm build:css &&
|
|
675
|
+
"build": "rimraf dist && pnpm build:i18n && pnpm build:esm && tsc --project tsconfig.build.json && pnpm build:css && node ../../scripts/check-no-dev-jsx.mjs && node ../../scripts/check-fully-specified.mjs",
|
|
674
676
|
"build:i18n": "node scripts/build-pseudo-locale.mjs",
|
|
675
677
|
"build:esm": "babel src --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.ts,**/*.test.tsx,**/__tests__/**,**/*.perf.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css,**/*.css.d.ts\" --config-file ./babel.config.json",
|
|
676
678
|
"build:css": "node ../../scripts/build-css.mjs",
|
|
677
|
-
"build:umd": "node ../../scripts/build-umd.mjs",
|
|
678
679
|
"dev": "babel src --watch --out-dir dist --extensions .ts,.tsx --out-file-extension .js --ignore \"**/*.test.*,**/*.doc.mjs,**/*.d.ts,**/*.css*\" --config-file ./babel.config.json",
|
|
679
680
|
"test": "vitest run --root ../..",
|
|
680
681
|
"test:watch": "vitest --root ../..",
|
|
@@ -91,11 +91,16 @@ describe('Avatar', () => {
|
|
|
91
91
|
expect(screen.getByTestId('avatar')).toHaveTextContent('😀A');
|
|
92
92
|
});
|
|
93
93
|
|
|
94
|
-
it('preserves a complete
|
|
94
|
+
it('preserves a complete character when generating initials', () => {
|
|
95
95
|
render(<Avatar name="🇬🇧 Ada" data-testid="avatar" />);
|
|
96
96
|
expect(screen.getByTestId('avatar')).toHaveTextContent('🇬🇧A');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('keeps a ZWJ family emoji intact when generating initials', () => {
|
|
100
|
+
render(<Avatar name="👨👩👧👦 Ada" data-testid="avatar" />);
|
|
101
|
+
expect(screen.getByTestId('avatar')).toHaveTextContent('👨👩👧👦A');
|
|
102
|
+
});
|
|
103
|
+
|
|
99
104
|
it('retries a new src after a previous src failed to load', () => {
|
|
100
105
|
const {rerender} = render(
|
|
101
106
|
<Avatar name="Ada" src="https://example.com/broken.jpg" />,
|
package/src/Avatar/Avatar.tsx
CHANGED
|
@@ -31,6 +31,7 @@ import {AvatarSizeContext} from './AvatarSizeContext';
|
|
|
31
31
|
import {useAvatarGroup} from '../AvatarGroup/AvatarGroupContext';
|
|
32
32
|
import {mergeProps, mergeRefs} from '../utils';
|
|
33
33
|
import {themeProps} from '../utils/themeProps';
|
|
34
|
+
import {firstCharacter} from '../utils/characters';
|
|
34
35
|
import {focusOutlineProps} from '../utils/focusOutline.stylex';
|
|
35
36
|
import {useTooltip} from '../Tooltip/useTooltip';
|
|
36
37
|
import {useDevWarning} from '../hooks/useDevWarning';
|
|
@@ -317,25 +318,6 @@ export interface AvatarProps extends BaseProps<HTMLDivElement> {
|
|
|
317
318
|
onClick?: React.MouseEventHandler<HTMLElement>;
|
|
318
319
|
}
|
|
319
320
|
|
|
320
|
-
/**
|
|
321
|
-
* Reuse a single segmenter when the runtime supports Intl.Segmenter.
|
|
322
|
-
*/
|
|
323
|
-
const graphemeSegmenter =
|
|
324
|
-
typeof Intl.Segmenter === 'function'
|
|
325
|
-
? new Intl.Segmenter(undefined, {granularity: 'grapheme'})
|
|
326
|
-
: null;
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Return the first user-perceived character, with a code-point fallback.
|
|
330
|
-
*/
|
|
331
|
-
function firstGrapheme(word: string): string {
|
|
332
|
-
if (graphemeSegmenter) {
|
|
333
|
-
return [...graphemeSegmenter.segment(word)][0]?.segment ?? '';
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return [...word][0] ?? '';
|
|
337
|
-
}
|
|
338
|
-
|
|
339
321
|
/**
|
|
340
322
|
* Generates initials from a name string.
|
|
341
323
|
* Takes the first letter of the first two words.
|
|
@@ -351,10 +333,10 @@ function getInitials(name: string): string {
|
|
|
351
333
|
return '';
|
|
352
334
|
}
|
|
353
335
|
if (words.length === 1) {
|
|
354
|
-
return
|
|
336
|
+
return firstCharacter(words[0]).toUpperCase();
|
|
355
337
|
}
|
|
356
338
|
return (
|
|
357
|
-
|
|
339
|
+
firstCharacter(words[0]) + firstCharacter(words[words.length - 1])
|
|
358
340
|
).toUpperCase();
|
|
359
341
|
}
|
|
360
342
|
|
|
@@ -17,11 +17,13 @@ export const docs = {
|
|
|
17
17
|
{guidance: true, description: 'Keep titles short and scannable: "Payment failed" not "There was a problem processing your most recent payment."'},
|
|
18
18
|
{guidance: false, description: 'Use Banner for short-lived messages that disappear on their own; use Toast instead.'},
|
|
19
19
|
{guidance: false, description: 'Stack multiple banners with the same status; combine related messages into one banner.'},
|
|
20
|
+
{guidance: true, description: 'Error and warning banners render as role="alert"; info and success render as role="status". Mount an alert banner in response to an event rather than on first paint, so assistive tech has a change to report.'},
|
|
21
|
+
{guidance: false, description: 'Rely on the status color or icon alone to carry meaning; say which status it is in the title text, because the icon is decorative to a screen reader.'},
|
|
20
22
|
],
|
|
21
23
|
anatomy: [
|
|
22
24
|
{name: 'Icon', required: true, description: 'Automatically set based on the status (info, warning, error, success).'},
|
|
23
|
-
{name: 'Title', required:
|
|
24
|
-
{name: 'Description', required: false, description: 'Additional detail below the title.
|
|
25
|
+
{name: 'Title', required: true, description: 'The main message. Always required.'},
|
|
26
|
+
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
25
27
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
26
28
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
27
29
|
{name: 'Collapsible content', required: false, description: 'Extra detail that expands below the banner header, like a list of errors.'},
|
|
@@ -68,7 +70,7 @@ export const docs = {
|
|
|
68
70
|
name: 'endContent',
|
|
69
71
|
type: 'ReactNode',
|
|
70
72
|
description:
|
|
71
|
-
'Action content rendered in the header area, end-aligned.
|
|
73
|
+
'Action content rendered in the header area, end-aligned. Wraps to its own row below the text when the header is too narrow to hold both.',
|
|
72
74
|
slotElements: [
|
|
73
75
|
{__element: 'Icon', props: {icon: 'chevronDown', size: 'sm'}},
|
|
74
76
|
{__element: 'Badge', props: {label: '3'}},
|
|
@@ -123,7 +125,7 @@ export const docs = {
|
|
|
123
125
|
{className: 'astryx-banner-content', visualProps: ['container', 'status']},
|
|
124
126
|
],
|
|
125
127
|
vars: [
|
|
126
|
-
{name: '--_banner-radius', description: 'Border radius
|
|
128
|
+
{name: '--_banner-radius', description: 'Border radius of the card container (header, content area and the elevated root silhouette)', default: 'var(--radius-container)', private: true},
|
|
127
129
|
],
|
|
128
130
|
derived: [
|
|
129
131
|
{property: 'borderRadius', vars: ['--_banner-radius']},
|
|
@@ -148,8 +150,8 @@ export const docsZh = {
|
|
|
148
150
|
],
|
|
149
151
|
anatomy: [
|
|
150
152
|
{name: 'Icon', required: true, description: 'Automatically set based on the status (info, warning, error, success).'},
|
|
151
|
-
{name: 'Title', required:
|
|
152
|
-
{name: 'Description', required: false, description: 'Additional detail below the title.
|
|
153
|
+
{name: 'Title', required: true, description: 'The main message. Always required.'},
|
|
154
|
+
{name: 'Description', required: false, description: 'Additional detail below the title.'},
|
|
153
155
|
{name: 'Action button', required: false, description: 'A button for the user to act on the message, like "Review" or "Retry".'},
|
|
154
156
|
{name: 'Dismiss button', required: false, description: 'Lets the user close the banner. Enabled by setting isDismissable.'},
|
|
155
157
|
{name: 'Collapsible content', required: false, description: 'Extra detail that expands below the banner header, like a list of errors.'},
|
|
@@ -162,7 +164,7 @@ export const docsZh = {
|
|
|
162
164
|
{name: 'icon', type: 'ReactNode', description: '覆盖默认的状态图标。'},
|
|
163
165
|
{name: 'isDismissable', type: 'boolean', description: '横幅是否可被用户关闭。', default: 'false'},
|
|
164
166
|
{name: 'onDismiss', type: '() => void', description: '点击关闭按钮时调用;无论是否提供此回调,横幅都会自动隐藏。'},
|
|
165
|
-
{name: 'endContent', type: 'ReactNode', description: '
|
|
167
|
+
{name: 'endContent', type: 'ReactNode', description: '渲染在头部区域末端对齐的操作内容,通常是按钮或链接。头部过窄时会整体换行到文本下方,自成一行。'},
|
|
166
168
|
{name: 'container', type: "'card' | 'section'", description: '视觉变体:card 带圆角;section 无圆角全宽,适用于页面级场景。', default: "'card'"},
|
|
167
169
|
{name: 'elevation', type: "'none' | 'low' | 'med' | 'high'", description: '静止阴影深度。用于悬浮于内容之上的浮动横幅;none 为默认内联横幅。', default: "'none'"},
|
|
168
170
|
{name: 'children', type: 'ReactNode', description: '渲染在彩色头部下方卡片背景区域的内容。'},
|
|
@@ -198,7 +200,7 @@ export const docsZh = {
|
|
|
198
200
|
},
|
|
199
201
|
],
|
|
200
202
|
vars: [
|
|
201
|
-
{name: '--_banner-radius', description: 'Border radius
|
|
203
|
+
{name: '--_banner-radius', description: 'Border radius of the card container (header, content area and the elevated root silhouette)', default: 'var(--radius-container)', private: true},
|
|
202
204
|
],
|
|
203
205
|
derived: [
|
|
204
206
|
{property: 'borderRadius', vars: ['--_banner-radius']},
|
|
@@ -222,7 +224,7 @@ export const docsDense = {
|
|
|
222
224
|
],
|
|
223
225
|
anatomy: [
|
|
224
226
|
{name: 'Icon', required: true, description: 'Set automatically from status.'},
|
|
225
|
-
{name: 'Title', required:
|
|
227
|
+
{name: 'Title', required: true, description: 'Main message text.'},
|
|
226
228
|
{name: 'Description', required: false, description: 'Detail below title.'},
|
|
227
229
|
{name: 'Action button', required: false, description: 'CTA like Review or Retry.'},
|
|
228
230
|
{name: 'Dismiss button', required: false, description: 'Close button via isDismissable.'},
|
|
@@ -236,7 +238,7 @@ export const docsDense = {
|
|
|
236
238
|
icon: 'override default status icon',
|
|
237
239
|
isDismissable: 'user can dismiss banner',
|
|
238
240
|
onDismiss: 'dismiss callback; banner self-hides regardless',
|
|
239
|
-
endContent: 'end-aligned action in header, typically button/link',
|
|
241
|
+
endContent: 'end-aligned action in header, typically button/link; wraps to its own row when the header is too narrow',
|
|
240
242
|
container: 'card=border-radius; section=full-width no radius for page-level',
|
|
241
243
|
elevation: 'resting shadow depth: none|low|med|high; raise for a floating banner',
|
|
242
244
|
children: 'content in card-bg area below colored header',
|