@astryxdesign/core 0.5.1 → 0.5.2-canary.1917581

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  3. package/dist/BottomSheet/BottomSheet.js +1 -1
  4. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
  5. package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
  7. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
  9. package/dist/Button/Button.d.ts +2 -1
  10. package/dist/Button/Button.d.ts.map +1 -1
  11. package/dist/Button/Button.js +11 -4
  12. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  13. package/dist/Chat/ChatToolCalls.js +34 -17
  14. package/dist/Popover/Popover.d.ts +10 -6
  15. package/dist/Popover/Popover.d.ts.map +1 -1
  16. package/dist/Popover/Popover.js +141 -16
  17. package/dist/Popover/usePopover.d.ts +4 -0
  18. package/dist/Popover/usePopover.d.ts.map +1 -1
  19. package/dist/Popover/usePopover.js +52 -5
  20. package/dist/Toast/useToastGesture.d.ts.map +1 -1
  21. package/dist/Toast/useToastGesture.js +46 -9
  22. package/dist/astryx.css +10 -0
  23. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  24. package/dist/hooks/useFocusTrap.js +12 -0
  25. package/dist/i18n/InternationalizationContext.d.ts +2 -0
  26. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  27. package/dist/i18n/InternationalizationContext.js +3 -1
  28. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  29. package/dist/i18n/InternationalizationProvider.js +3 -1
  30. package/dist/i18n/resolve.d.ts +1 -1
  31. package/dist/i18n/resolve.d.ts.map +1 -1
  32. package/dist/i18n/resolve.js +37 -38
  33. package/dist/i18n/useTranslator.d.ts.map +1 -1
  34. package/dist/i18n/useTranslator.js +2 -3
  35. package/locales/af-ZA.json +0 -4
  36. package/locales/ar-SA.json +0 -4
  37. package/locales/ca-ES.json +0 -4
  38. package/locales/cs-CZ.json +0 -4
  39. package/locales/da-DK.json +0 -4
  40. package/locales/de-DE.json +0 -4
  41. package/locales/el-GR.json +0 -4
  42. package/locales/en.json +16 -0
  43. package/locales/es-ES.json +0 -4
  44. package/locales/fi-FI.json +0 -4
  45. package/locales/fr-FR.json +0 -4
  46. package/locales/he-IL.json +0 -4
  47. package/locales/hu-HU.json +0 -4
  48. package/locales/it-IT.json +0 -4
  49. package/locales/ja-JP.json +0 -4
  50. package/locales/ko-KR.json +0 -4
  51. package/locales/nl-NL.json +0 -4
  52. package/locales/no-NO.json +0 -4
  53. package/locales/pl-PL.json +0 -4
  54. package/locales/pseudo.json +12 -0
  55. package/locales/pt-BR.json +0 -4
  56. package/locales/pt-PT.json +0 -4
  57. package/locales/ro-RO.json +0 -4
  58. package/locales/ru-RU.json +0 -4
  59. package/locales/sr-SP.json +0 -4
  60. package/locales/sv-SE.json +0 -4
  61. package/locales/tr-TR.json +0 -4
  62. package/locales/uk-UA.json +0 -4
  63. package/locales/vi-VN.json +0 -4
  64. package/locales/zh-CN.json +0 -4
  65. package/locales/zh-TW.json +0 -4
  66. package/package.json +3 -3
  67. package/src/BottomSheet/BottomSheet.tsx +1 -2
  68. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
  69. package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
  70. package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
  71. package/src/Button/Button.test.tsx +10 -0
  72. package/src/Button/Button.tsx +13 -5
  73. package/src/Calendar/Calendar.doc.mjs +1 -1
  74. package/src/Chat/ChatToolCalls.test.tsx +44 -0
  75. package/src/Chat/ChatToolCalls.tsx +36 -15
  76. package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
  77. package/src/CheckboxList/CheckboxList.doc.mjs +1 -1
  78. package/src/ComplexSelector/ComplexSelector.doc.mjs +2 -2
  79. package/src/DateInput/DateInput.doc.mjs +1 -1
  80. package/src/DateRangeInput/DateRangeInput.doc.mjs +1 -1
  81. package/src/DateTimeInput/DateTimeInput.doc.mjs +1 -1
  82. package/src/Field/Field.doc.mjs +1 -1
  83. package/src/FieldStatus/FieldStatus.doc.mjs +1 -1
  84. package/src/FileInput/FileInput.doc.mjs +1 -1
  85. package/src/Indicator/Indicator.doc.mjs +1 -1
  86. package/src/InputGroup/InputGroup.doc.mjs +1 -1
  87. package/src/MultiSelector/MultiSelector.doc.mjs +1 -1
  88. package/src/NumberInput/NumberInput.doc.mjs +1 -1
  89. package/src/Popover/Popover.doc.mjs +11 -11
  90. package/src/Popover/Popover.test.tsx +397 -2
  91. package/src/Popover/Popover.tsx +228 -21
  92. package/src/Popover/usePopover.tsx +92 -15
  93. package/src/PowerSearch/PowerSearch.doc.mjs +1 -1
  94. package/src/RadioList/RadioList.doc.mjs +1 -1
  95. package/src/Selector/Selector.doc.mjs +1 -1
  96. package/src/Slider/Slider.doc.mjs +2 -2
  97. package/src/Switch/Switch.doc.mjs +1 -1
  98. package/src/TextArea/TextArea.doc.mjs +1 -1
  99. package/src/TextInput/TextInput.doc.mjs +1 -1
  100. package/src/TimeInput/TimeInput.doc.mjs +1 -1
  101. package/src/Toast/ToastViewport.test.tsx +118 -4
  102. package/src/Toast/useToastGesture.ts +61 -8
  103. package/src/Tokenizer/Tokenizer.doc.mjs +1 -1
  104. package/src/Typeahead/Typeahead.doc.mjs +1 -1
  105. package/src/hooks/useFocusTrap.test.tsx +40 -0
  106. package/src/hooks/useFocusTrap.ts +13 -0
  107. package/src/i18n/InternationalizationContext.ts +3 -0
  108. package/src/i18n/InternationalizationProvider.tsx +8 -1
  109. package/src/i18n/__tests__/resolve.test.ts +23 -1
  110. package/src/i18n/resolve.ts +46 -49
  111. package/src/i18n/useTranslator.ts +2 -7
@@ -365,6 +365,26 @@ const STATUS_STYLES: Record<
365
365
  error: styles.colorError,
366
366
  };
367
367
 
368
+ function getStatusAnnouncement(
369
+ t: ReturnType<typeof useTranslator>,
370
+ status: ChatToolCallStatus,
371
+ errorMessage?: string,
372
+ ): string {
373
+ if (status === 'error' && errorMessage != null) {
374
+ return t('@astryx.chatToolCalls.error', {message: errorMessage});
375
+ }
376
+ switch (status) {
377
+ case 'pending':
378
+ return t('@astryx.chatToolCalls.status.pending');
379
+ case 'running':
380
+ return t('@astryx.chatToolCalls.status.running');
381
+ case 'complete':
382
+ return t('@astryx.chatToolCalls.status.complete');
383
+ case 'error':
384
+ return t('@astryx.chatToolCalls.status.error');
385
+ }
386
+ }
387
+
368
388
  function getToolCallKey(call: ChatToolCallItem): string {
369
389
  return getKey(call.key, () =>
370
390
  [
@@ -390,6 +410,11 @@ function CallRow({call}: {call: ChatToolCallItem}) {
390
410
  const hasDetail = call.resultDetail != null;
391
411
  const [isDetailOpen, setIsDetailOpen] = useState(false);
392
412
  const detailId = useId();
413
+ const statusAnnouncement = getStatusAnnouncement(
414
+ t,
415
+ status,
416
+ call.errorMessage,
417
+ );
393
418
 
394
419
  const toggleDetail = hasDetail
395
420
  ? () => setIsDetailOpen(prev => !prev)
@@ -417,9 +442,9 @@ function CallRow({call}: {call: ChatToolCallItem}) {
417
442
  title={status === 'error' ? call.errorMessage : undefined}
418
443
  {...stylex.props(styles.statusIcon, STATUS_STYLES[status])}>
419
444
  {status === 'running' ? (
420
- <Spinner size="sm" shade="subtle" />
445
+ <Spinner size="sm" shade="subtle" aria-hidden="true" />
421
446
  ) : status === 'pending' ? (
422
- <Spinner size="sm" shade="subtle" />
447
+ <Spinner size="sm" shade="subtle" aria-hidden="true" />
423
448
  ) : (
424
449
  <>
425
450
  <span {...stylex.props(styles.statusIconCircle)} />
@@ -432,17 +457,7 @@ function CallRow({call}: {call: ChatToolCallItem}) {
432
457
  </span>
433
458
  </>
434
459
  )}
435
- {status === 'error' && call.errorMessage != null && (
436
- // The title attribute above is hover-only; expose the error detail
437
- // as real text so it reaches screen readers, keyboard, and touch
438
- // users. Rendering it inside the row also folds it into the
439
- // accessible name of expandable (role="button") rows.
440
- <VisuallyHidden>
441
- {t('@astryx.chatToolCalls.error', {
442
- message: call.errorMessage ?? '',
443
- })}
444
- </VisuallyHidden>
445
- )}
460
+ <VisuallyHidden>{statusAnnouncement}</VisuallyHidden>
446
461
  </span>
447
462
  <span {...stylex.props(styles.callName)}>{call.name}</span>
448
463
  {call.node != null && (
@@ -581,6 +596,11 @@ export function ChatToolCalls(props: ChatToolCallsProps) {
581
596
  // Multiple calls: latest call at surface with chevron to expand all
582
597
  const latestCall = calls[calls.length - 1];
583
598
  const latestStatus = latestCall.status ?? 'complete';
599
+ const latestStatusAnnouncement = getStatusAnnouncement(
600
+ t,
601
+ latestStatus,
602
+ latestCall.errorMessage,
603
+ );
584
604
 
585
605
  return (
586
606
  <div
@@ -620,9 +640,9 @@ export function ChatToolCalls(props: ChatToolCallsProps) {
620
640
  <span
621
641
  {...stylex.props(styles.statusIcon, STATUS_STYLES[latestStatus])}>
622
642
  {latestStatus === 'running' ? (
623
- <Spinner size="sm" shade="subtle" />
643
+ <Spinner size="sm" shade="subtle" aria-hidden="true" />
624
644
  ) : latestStatus === 'pending' ? (
625
- <Spinner size="sm" shade="subtle" />
645
+ <Spinner size="sm" shade="subtle" aria-hidden="true" />
626
646
  ) : (
627
647
  <>
628
648
  <span {...stylex.props(styles.statusIconCircle)} />
@@ -635,6 +655,7 @@ export function ChatToolCalls(props: ChatToolCallsProps) {
635
655
  </span>
636
656
  </>
637
657
  )}
658
+ <VisuallyHidden>{latestStatusAnnouncement}</VisuallyHidden>
638
659
  </span>
639
660
  <span {...stylex.props(styles.callName)}>{latestCall.name}</span>
640
661
  {latestCall.target != null && (
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'CheckboxInput',
7
7
  displayName: 'Checkbox Input',
8
8
  group: 'Checkbox',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: ["checkbox","check","toggle","tick","indeterminate","boolean","tristate"],
11
11
  props: [
12
12
  {
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'CheckboxList',
7
7
  displayName: 'Checkbox List',
8
8
  group: 'Checkbox',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  isHiddenFromOverview: true,
11
11
  keywords: ["checkboxlist","checkbox","checkboxgroup","multichoice","multiselect","checklist"],
12
12
  description: 'Checkbox group container with field integration for label, description, and status.',
@@ -15,7 +15,7 @@ export const docs = {
15
15
  name: 'ComplexSelector',
16
16
  displayName: 'Complex Selector',
17
17
  group: 'Selector',
18
- category: 'Data Input',
18
+ category: 'Form Controls',
19
19
  keywords: [
20
20
  'selector',
21
21
  'picker',
@@ -214,7 +214,7 @@ export const docsDense = {
214
214
  name: 'ComplexSelector',
215
215
  displayName: 'Complex Selector',
216
216
  group: 'Selector',
217
- category: 'Data Input',
217
+ category: 'Form Controls',
218
218
  description:
219
219
  'Input/ghost trigger + dialog-popover shell for rich custom selectors. Content gets value/onChange/close/state; content owns semantics. Use focus hooks and evaluate custom content against WCAG 2.2.',
220
220
  usage: {
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'DateInput',
7
7
  displayName: 'Date Input',
8
8
  group: 'DateInput',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'dateinput',
12
12
  'datepicker',
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'DateRangeInput',
7
7
  displayName: 'Date Range Input',
8
8
  group: 'DateInput',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'daterangepicker',
12
12
  'daterange',
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'DateTimeInput',
7
7
  displayName: 'Date Time Input',
8
8
  group: 'DateInput',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'datetimepicker',
12
12
  'datetime',
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'Field',
7
7
  displayName: 'Field',
8
8
  group: 'Field',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: ["field","formfield","formgroup","formcontrol","label","input","required","optional","helpertext","hint"],
11
11
  playground: {
12
12
  defaults: {
@@ -5,7 +5,7 @@ export const docs = {
5
5
  name: 'FieldStatus',
6
6
  displayName: 'Field Status',
7
7
  group: 'Field',
8
- category: 'Data Input',
8
+ category: 'Form Controls',
9
9
  isHiddenFromOverview: true,
10
10
  description:
11
11
  'Status message component for form field validation feedback. Messages are announced to screen readers through persistent live regions (assertive for errors, polite otherwise), so conditional mounting is safe.',
@@ -5,7 +5,7 @@
5
5
  export const docs = {
6
6
  name: 'FileInput',
7
7
  displayName: 'File Input',
8
- category: 'Data Input',
8
+ category: 'Form Controls',
9
9
  keywords: ["fileinput","file","upload","drag","drop","dropzone","attachment","browse"],
10
10
  props: [
11
11
  {
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'Indicator',
7
7
  displayName: 'Indicator',
8
8
  group: 'Indicator',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  isHiddenFromOverview: true,
11
11
  keywords: [
12
12
  'indicator',
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'InputGroup',
7
7
  displayName: 'Input Group',
8
8
  group: 'Field',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  isHiddenFromOverview: true,
11
11
  keywords: [
12
12
  'inputgroup',
@@ -6,7 +6,7 @@ export const docs = {
6
6
  name: 'MultiSelector',
7
7
  displayName: 'Multi Selector',
8
8
  group: 'Selector',
9
- category: 'Data Input',
9
+ category: 'Form Controls',
10
10
  keywords: [
11
11
  'multiselect',
12
12
  'checkbox',
@@ -5,7 +5,7 @@
5
5
  export const docs = {
6
6
  name: 'NumberInput',
7
7
  displayName: 'Number Input',
8
- category: 'Data Input',
8
+ category: 'Form Controls',
9
9
  keywords: [
10
10
  'numberinput',
11
11
  'numberfield',
@@ -64,7 +64,7 @@ export const docs = {
64
64
  {
65
65
  name: 'width',
66
66
  type: 'number | string',
67
- description: 'Width of the popover container.',
67
+ description: 'Width of the popover container. The layer still caps to the viewport with alignment-aware safe-area gutters before scrolling long content.',
68
68
  default: "'auto'",
69
69
  },
70
70
  {
@@ -101,7 +101,7 @@ export const docs = {
101
101
  {
102
102
  name: 'hasAutoFocus',
103
103
  type: 'boolean',
104
- description: 'Whether to auto-focus the first focusable element when the popover opens. Set to false for inline showcases or documentation previews.',
104
+ description: 'Whether to move focus into the popover when it opens. Keyboard activation focuses the first content control; pointer activation focuses the labeled dialog container so an action does not appear preselected. Set to false for inline showcases or documentation previews.',
105
105
  default: 'true',
106
106
  },
107
107
  {
@@ -149,8 +149,8 @@ export const docs = {
149
149
  { guidance: true, description: 'Keep popover content focused on a single task or piece of information.' },
150
150
  { guidance: true, description: 'Provide a clear way to close: either by clicking outside or with an explicit close button.' },
151
151
  { guidance: false, description: 'Nest popovers inside other popovers; it creates confusing focus and navigation.' },
152
- { guidance: false, description: 'Use a popover for content that requires heavy user input; use a Dialog instead.' },
153
- { guidance: false, description: 'Put too much content in a popover; if it needs scrolling, use a Dialog instead.' },
152
+ { guidance: false, description: 'Assume input complexity alone determines the presentation; evaluate the task’s focus, space, and interaction requirements.' },
153
+ { guidance: false, description: 'Assume scrolling alone means Popover is the wrong component; a bounded Popover may scroll while a focused anchored interaction remains appropriate.' },
154
154
  ],
155
155
  anatomy: [
156
156
  {name: 'Header', required: true, description: 'Contains the title, optional subheader, and close button.'},
@@ -220,7 +220,7 @@ export const docsZh = {
220
220
  {
221
221
  name: 'width',
222
222
  type: 'number | string',
223
- description: '弹出框容器的宽度。',
223
+ description: '弹出框容器的宽度。弹出层仍会限制在视口和安全区域留白内,长内容再滚动。',
224
224
  default: "'auto'",
225
225
  },
226
226
  {
@@ -294,8 +294,8 @@ export const docsZh = {
294
294
  { guidance: true, description: 'Keep popover content focused on a single task or piece of information.' },
295
295
  { guidance: true, description: 'Provide a clear way to close: either by clicking outside or with an explicit close button.' },
296
296
  { guidance: false, description: 'Nest popovers inside other popovers; it creates confusing focus and navigation.' },
297
- { guidance: false, description: 'Use a popover for content that requires heavy user input; use a Dialog instead.' },
298
- { guidance: false, description: 'Put too much content in a popover; if it needs scrolling, use a Dialog instead.' },
297
+ { guidance: false, description: 'Assume input complexity alone determines the presentation; evaluate the task’s focus, space, and interaction requirements.' },
298
+ { guidance: false, description: 'Assume scrolling alone means Popover is the wrong component; a bounded Popover may scroll while a focused anchored interaction remains appropriate.' },
299
299
  ],
300
300
  anatomy: [
301
301
  {name: 'Header', required: true, description: 'Contains the title, optional subheader, and close button.'},
@@ -316,8 +316,8 @@ export const docsDense = {
316
316
  { guidance: true, description: 'Keep popover content focused on a single task or piece of information.' },
317
317
  { guidance: true, description: 'Provide a clear way to close: either by clicking outside or with an explicit close button.' },
318
318
  { guidance: false, description: 'Nest popovers inside other popovers; it creates confusing focus and navigation.' },
319
- { guidance: false, description: 'Use a popover for content that requires heavy user input; use a Dialog instead.' },
320
- { guidance: false, description: 'Put too much content in a popover; if it needs scrolling, use a Dialog instead.' },
319
+ { guidance: false, description: 'Assume input complexity alone determines the presentation; evaluate the task’s focus, space, and interaction requirements.' },
320
+ { guidance: false, description: 'Assume scrolling alone means Popover is the wrong component; a bounded Popover may scroll while a focused anchored interaction remains appropriate.' },
321
321
  ],
322
322
  anatomy: [
323
323
  {name: 'Header', required: true, description: 'Contains the title, optional subheader, and close button.'},
@@ -340,11 +340,11 @@ export const docsDense = {
340
340
  isOpen: 'Whether popover shown in controlled mode.',
341
341
  onOpenChange: 'Callback fired when popover visibility changes.',
342
342
  isEnabled: 'When false, trigger interactions ignored.',
343
- width: 'Popover container width.',
343
+ width: 'Popover container width; capped to alignment-aware viewport/safe-area gutters before long content scrolls.',
344
344
  label: 'Accessible label for popover dialog.',
345
345
  hasCloseButton: 'Whether to include hidden close button for accessibility.',
346
346
  closeButtonLabel: 'Label for hidden close button.',
347
- hasAutoFocus: 'Auto-focus first element on open; false for showcases.',
347
+ hasAutoFocus: 'Move focus into the popover on open; keyboard targets the first control and pointer targets the dialog container.',
348
348
  hasLightDismiss: 'Outside click dismisses; false for explicit-dismiss surfaces (coachmarks).',
349
349
  hasEscapeDismiss: 'Escape dismisses; full effect only with hasLightDismiss=false.',
350
350
  },