@astryxdesign/core 0.5.2-canary.c9c8564 → 0.5.2-canary.e03511b
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/BottomSheet/BottomSheet.d.ts +3 -1
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheet.js +4 -3
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts +2 -2
- package/dist/BottomSheet/BottomSheetEdgeTint.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetEdgeTint.js +7 -10
- package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
- package/dist/Button/Button.d.ts +2 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +11 -4
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +2 -2
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +34 -17
- package/dist/ContextMenu/ContextMenu.d.ts +14 -3
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +149 -26
- package/dist/ContextMenu/index.d.ts +1 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts +24 -5
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +340 -26
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuItem.js +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +6 -2
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +43 -14
- package/dist/DropdownMenu/MenuBottomSheet.d.ts +20 -0
- package/dist/DropdownMenu/MenuBottomSheet.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheet.js +36 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts +10 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -0
- package/dist/DropdownMenu/MenuBottomSheetActionList.js +111 -0
- package/dist/DropdownMenu/index.d.ts +2 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/menuWidth.d.ts +14 -0
- package/dist/DropdownMenu/menuWidth.d.ts.map +1 -0
- package/dist/DropdownMenu/menuWidth.js +35 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts +10 -0
- package/dist/DropdownMenu/useMenuOverflow.d.ts.map +1 -0
- package/dist/DropdownMenu/useMenuOverflow.js +60 -0
- package/dist/MoreMenu/MoreMenu.d.ts +8 -2
- package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
- package/dist/MoreMenu/MoreMenu.js +2 -0
- package/dist/MultiSelector/MultiSelector.d.ts +11 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +97 -68
- package/dist/MultiSelector/index.d.ts +1 -1
- package/dist/MultiSelector/index.d.ts.map +1 -1
- package/dist/Popover/Popover.d.ts +10 -6
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +141 -16
- package/dist/Popover/usePopover.d.ts +4 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +52 -5
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +106 -80
- package/dist/Selector/SelectorBottomSheet.d.ts +21 -0
- package/dist/Selector/SelectorBottomSheet.d.ts.map +1 -0
- package/dist/Selector/SelectorBottomSheet.js +83 -0
- package/dist/Selector/index.d.ts +1 -1
- package/dist/Selector/index.d.ts.map +1 -1
- package/dist/Selector/selectorPresentation.stylex.d.ts +16 -0
- package/dist/Selector/selectorPresentation.stylex.d.ts.map +1 -0
- package/dist/Selector/selectorPresentation.stylex.js +21 -0
- package/dist/Selector/useSelectorPresentation.d.ts +32 -0
- package/dist/Selector/useSelectorPresentation.d.ts.map +1 -0
- package/dist/Selector/useSelectorPresentation.js +90 -0
- package/dist/Toast/useToastGesture.d.ts.map +1 -1
- package/dist/Toast/useToastGesture.js +46 -9
- package/dist/astryx.css +19 -1
- package/dist/hooks/useAdaptivePresentation.d.ts +5 -0
- package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -0
- package/dist/hooks/useAdaptivePresentation.js +19 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts +7 -0
- package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -0
- package/dist/hooks/useFocusReturnVisibility.js +35 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +12 -0
- package/locales/en.json +20 -0
- package/locales/pseudo.json +15 -0
- package/package.json +2 -2
- package/src/BottomSheet/BottomSheet.doc.mjs +37 -0
- package/src/BottomSheet/BottomSheet.spec.md +177 -0
- package/src/BottomSheet/BottomSheet.test.tsx +29 -0
- package/src/BottomSheet/BottomSheet.tsx +7 -4
- package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +4 -6
- package/src/BottomSheet/BottomSheetEdgeTint.tsx +7 -10
- package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -2
- package/src/Button/Button.doc.mjs +56 -0
- package/src/Button/Button.test.tsx +10 -0
- package/src/Button/Button.tsx +13 -5
- package/src/ButtonGroup/ButtonGroup.doc.mjs +47 -0
- package/src/Carousel/Carousel.test.tsx +83 -0
- package/src/Carousel/Carousel.tsx +8 -2
- package/src/Chat/ChatToolCalls.test.tsx +44 -0
- package/src/Chat/ChatToolCalls.tsx +36 -15
- package/src/CheckboxList/CheckboxList.doc.mjs +172 -31
- package/src/CheckboxList/CheckboxList.spec.md +214 -0
- package/src/CommandPalette/CommandPalette.doc.mjs +72 -0
- package/src/CommandPalette/CommandPalette.spec.md +223 -0
- package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
- package/src/ComplexSelector/ComplexSelector.spec.md +189 -0
- package/src/ContextMenu/ContextMenu.doc.mjs +61 -1
- package/src/ContextMenu/ContextMenu.spec.md +206 -0
- package/src/ContextMenu/ContextMenu.test.tsx +131 -1
- package/src/ContextMenu/ContextMenu.tsx +199 -32
- package/src/ContextMenu/index.ts +1 -0
- package/src/Divider/Divider.doc.mjs +24 -0
- package/src/Divider/Divider.spec.md +150 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +305 -40
- package/src/DropdownMenu/DropdownMenu.spec.md +245 -0
- package/src/DropdownMenu/DropdownMenu.test.tsx +518 -7
- package/src/DropdownMenu/DropdownMenu.tsx +459 -28
- package/src/DropdownMenu/DropdownMenuContext.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuItem.tsx +2 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +4 -2
- package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +124 -0
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +72 -17
- package/src/DropdownMenu/MenuBottomSheet.tsx +46 -0
- package/src/DropdownMenu/MenuBottomSheetActionList.tsx +141 -0
- package/src/DropdownMenu/index.ts +2 -0
- package/src/DropdownMenu/menuWidth.ts +51 -0
- package/src/DropdownMenu/useMenuOverflow.ts +77 -0
- package/src/FieldStatus/FieldStatus.doc.mjs +21 -0
- package/src/FieldStatus/FieldStatus.spec.md +212 -0
- package/src/Grid/Grid.doc.mjs +19 -0
- package/src/Grid/Grid.spec.md +161 -0
- package/src/Icon/Icon.doc.mjs +196 -48
- package/src/Icon/Icon.spec.md +191 -0
- package/src/IconButton/IconButton.doc.mjs +38 -0
- package/src/Kbd/Kbd.doc.mjs +18 -0
- package/src/Kbd/Kbd.spec.md +141 -0
- package/src/Layout/Layout.doc.mjs +32 -0
- package/src/Layout/Layout.spec.md +199 -0
- package/src/Lightbox/Lightbox.doc.mjs +149 -33
- package/src/Lightbox/Lightbox.spec.md +197 -0
- package/src/Markdown/Markdown.doc.mjs +60 -0
- package/src/Markdown/Markdown.spec.md +200 -0
- package/src/MobileNav/MobileNav.doc.mjs +140 -32
- package/src/MobileNav/MobileNav.spec.md +208 -0
- package/src/MoreMenu/MoreMenu.doc.mjs +59 -0
- package/src/MoreMenu/MoreMenu.spec.md +221 -0
- package/src/MoreMenu/MoreMenu.test.tsx +47 -0
- package/src/MoreMenu/MoreMenu.tsx +13 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +128 -0
- package/src/MultiSelector/MultiSelector.spec.md +266 -0
- package/src/MultiSelector/MultiSelector.test.tsx +127 -0
- package/src/MultiSelector/MultiSelector.tsx +121 -73
- package/src/MultiSelector/index.ts +1 -0
- package/src/NavIcon/NavIcon.doc.mjs +17 -0
- package/src/NavIcon/NavIcon.spec.md +133 -0
- package/src/NavMenu/NavHeadingMenu.spec.md +205 -0
- package/src/NavMenu/NavMenu.doc.mjs +37 -0
- package/src/Outline/Outline.doc.mjs +35 -0
- package/src/Outline/Outline.spec.md +161 -0
- package/src/OverflowList/OverflowList.doc.mjs +24 -0
- package/src/OverflowList/OverflowList.spec.md +156 -0
- package/src/Pagination/Pagination.doc.mjs +74 -0
- package/src/Pagination/Pagination.spec.md +225 -0
- package/src/Popover/Popover.doc.mjs +11 -11
- package/src/Popover/Popover.test.tsx +397 -2
- package/src/Popover/Popover.tsx +228 -21
- package/src/Popover/usePopover.tsx +92 -15
- package/src/ProgressBar/ProgressBar.doc.mjs +198 -38
- package/src/ProgressBar/ProgressBar.spec.md +260 -0
- package/src/Resizable/Resizable.doc.mjs +59 -16
- package/src/Resizable/Resizable.spec.md +194 -0
- package/src/Section/Section.doc.mjs +17 -0
- package/src/Section/Section.spec.md +138 -0
- package/src/SegmentedControl/SegmentedControl.doc.mjs +83 -0
- package/src/SegmentedControl/SegmentedControl.spec.md +151 -0
- package/src/Selector/Selector.doc.mjs +12 -0
- package/src/Selector/Selector.spec.md +202 -0
- package/src/Selector/Selector.test.tsx +135 -0
- package/src/Selector/Selector.tsx +141 -82
- package/src/Selector/SelectorBottomSheet.tsx +97 -0
- package/src/Selector/index.ts +1 -0
- package/src/Selector/selectorPresentation.stylex.ts +20 -0
- package/src/Selector/useSelectorPresentation.ts +147 -0
- package/src/Skeleton/Skeleton.doc.mjs +12 -0
- package/src/Skeleton/Skeleton.spec.md +126 -0
- package/src/Slider/Slider.doc.mjs +184 -30
- package/src/Slider/Slider.spec.md +223 -0
- package/src/Stack/Stack.doc.mjs +22 -0
- package/src/Stack/Stack.spec.md +154 -0
- package/src/StatusDot/StatusDot.doc.mjs +17 -0
- package/src/StatusDot/StatusDot.spec.md +137 -0
- package/src/Switch/Switch.doc.mjs +40 -0
- package/src/Switch/Switch.spec.md +174 -0
- package/src/Table/Table.doc.mjs +102 -24
- package/src/Table/Table.spec.md +283 -0
- package/src/Text/Text.doc.mjs +190 -43
- package/src/Text/Text.spec.md +205 -0
- package/src/TextArea/TextArea.doc.mjs +255 -45
- package/src/TextArea/TextArea.spec.md +204 -0
- package/src/Toast/ToastViewport.test.tsx +118 -4
- package/src/Toast/useToastGesture.ts +61 -8
- package/src/ToggleButton/ToggleButton.doc.mjs +56 -0
- package/src/Toolbar/Toolbar.doc.mjs +19 -0
- package/src/Toolbar/Toolbar.spec.md +173 -0
- package/src/Tooltip/Tooltip.doc.mjs +17 -0
- package/src/Tooltip/Tooltip.spec.md +141 -0
- package/src/TreeList/TreeList.doc.mjs +58 -0
- package/src/TreeList/TreeList.spec.md +220 -0
- package/src/Typeahead/Typeahead.doc.mjs +83 -1
- package/src/Typeahead/Typeahead.spec.md +250 -0
- package/src/hooks/useAdaptivePresentation.ts +35 -0
- package/src/hooks/useFocusReturnVisibility.ts +45 -0
- package/src/hooks/useFocusTrap.test.tsx +40 -0
- package/src/hooks/useFocusTrap.ts +13 -0
- package/src/theme/themingTargets.test.ts +91 -25
|
@@ -1,18 +1,95 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
+
/** @type {import('@astryxdesign/cli/authoring').ComponentAnatomyElement[]} */
|
|
4
|
+
const anatomy = [
|
|
5
|
+
{
|
|
6
|
+
name: 'Label',
|
|
7
|
+
required: true,
|
|
8
|
+
description: 'Text identifying the multi-line field.',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: 'Description',
|
|
12
|
+
required: false,
|
|
13
|
+
description: 'Helper text between the label and the input.',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'Input container',
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'Painted boundary containing the text area and its overlays.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'Text area',
|
|
22
|
+
required: true,
|
|
23
|
+
description: 'Multi-line control that displays and edits the value.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'Placeholder',
|
|
27
|
+
required: false,
|
|
28
|
+
description: 'Hint text shown inside the empty text area.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Start icon',
|
|
32
|
+
required: false,
|
|
33
|
+
description:
|
|
34
|
+
'Astryx Icon rendered at the start when startIcon is a semantic name or icon component.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'Custom start content',
|
|
38
|
+
required: false,
|
|
39
|
+
description:
|
|
40
|
+
'Caller-provided ReactNode rendered at the start instead of an Astryx Icon.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Spinner',
|
|
44
|
+
required: false,
|
|
45
|
+
description: 'Loading indicator shown at the end of the input container.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'Status icon',
|
|
49
|
+
required: false,
|
|
50
|
+
description: 'Error, warning, or success icon shown inside the input.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'Character counter',
|
|
54
|
+
required: false,
|
|
55
|
+
description:
|
|
56
|
+
'Current and maximum character counts shown inside the input container.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Field status message',
|
|
60
|
+
required: false,
|
|
61
|
+
description:
|
|
62
|
+
'Attached or detached error, warning, or success message associated with the field.',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'Tooltip status message',
|
|
66
|
+
required: false,
|
|
67
|
+
description:
|
|
68
|
+
'Tooltip surface presenting the status message for the tooltip variant.',
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
3
72
|
/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */
|
|
4
73
|
|
|
5
74
|
export const docs = {
|
|
6
75
|
name: 'TextArea',
|
|
7
76
|
displayName: 'Text Area',
|
|
8
77
|
category: 'Form Controls',
|
|
9
|
-
keywords: [
|
|
78
|
+
keywords: [
|
|
79
|
+
'textarea',
|
|
80
|
+
'textfield',
|
|
81
|
+
'multiline',
|
|
82
|
+
'comment',
|
|
83
|
+
'message',
|
|
84
|
+
'autoresize',
|
|
85
|
+
'autosize',
|
|
86
|
+
'charlimit',
|
|
87
|
+
],
|
|
10
88
|
props: [
|
|
11
89
|
{
|
|
12
90
|
name: 'ref',
|
|
13
91
|
type: 'React.Ref<HTMLTextAreaElement>',
|
|
14
|
-
description:
|
|
15
|
-
'Ref forwarded to the underlying <textarea> element.',
|
|
92
|
+
description: 'Ref forwarded to the underlying <textarea> element.',
|
|
16
93
|
},
|
|
17
94
|
{
|
|
18
95
|
name: 'label',
|
|
@@ -147,7 +224,8 @@ export const docs = {
|
|
|
147
224
|
{
|
|
148
225
|
name: 'size',
|
|
149
226
|
type: "'sm' | 'md' | 'lg'",
|
|
150
|
-
description:
|
|
227
|
+
description:
|
|
228
|
+
'Size of the textarea, affecting internal padding. Height is controlled by rows, not size.',
|
|
151
229
|
default: "'md'",
|
|
152
230
|
},
|
|
153
231
|
{
|
|
@@ -186,12 +264,21 @@ export const docs = {
|
|
|
186
264
|
],
|
|
187
265
|
theming: {
|
|
188
266
|
targets: [
|
|
189
|
-
{
|
|
267
|
+
{
|
|
268
|
+
className: 'astryx-text-area',
|
|
269
|
+
visualProps: ['size', 'status'],
|
|
270
|
+
states: ['disabled', 'readonly'],
|
|
271
|
+
},
|
|
190
272
|
{className: 'astryx-text-area-control'},
|
|
191
273
|
{className: 'astryx-text-area-counter'},
|
|
192
274
|
// Still emitted beside the names above, so themes written against
|
|
193
275
|
// them keep working. Drop in the next major.
|
|
194
|
-
{
|
|
276
|
+
{
|
|
277
|
+
className: 'astryx-textarea',
|
|
278
|
+
visualProps: ['size', 'status'],
|
|
279
|
+
states: ['disabled', 'readonly'],
|
|
280
|
+
deprecatedFor: 'text-area',
|
|
281
|
+
},
|
|
195
282
|
],
|
|
196
283
|
vars: [
|
|
197
284
|
{
|
|
@@ -203,21 +290,58 @@ export const docs = {
|
|
|
203
290
|
},
|
|
204
291
|
],
|
|
205
292
|
derived: [
|
|
206
|
-
{
|
|
293
|
+
{
|
|
294
|
+
property: 'paddingInline',
|
|
295
|
+
vars: ['--_textarea-inline-padding'],
|
|
296
|
+
replaces: true,
|
|
297
|
+
},
|
|
207
298
|
],
|
|
208
299
|
},
|
|
209
300
|
usage: {
|
|
301
|
+
anatomy,
|
|
210
302
|
description:
|
|
211
303
|
'TextArea is a multi-line text input for collecting longer-form content like comments, descriptions, or messages. Use it when the expected input spans multiple lines. For shorter, single-line values, use TextInput.',
|
|
212
304
|
bestPractices: [
|
|
213
|
-
{
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
{
|
|
219
|
-
|
|
220
|
-
|
|
305
|
+
{
|
|
306
|
+
guidance: true,
|
|
307
|
+
description:
|
|
308
|
+
'Provide a visible label so users know what to enter. If the label must be hidden, set isLabelHidden with a descriptive label for screen readers.',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
guidance: true,
|
|
312
|
+
description:
|
|
313
|
+
'Set maxLength with a character counter when there is a defined limit; it helps users stay within bounds before they submit.',
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
guidance: true,
|
|
317
|
+
description:
|
|
318
|
+
'Use the status prop to surface validation feedback inline: show success when input is valid, warning for soft limits, and error for hard failures.',
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
guidance: true,
|
|
322
|
+
description:
|
|
323
|
+
'Add a description or placeholder to clarify expected content, like "Describe the issue in detail," but never rely on placeholder alone as the only label.',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
guidance: false,
|
|
327
|
+
description:
|
|
328
|
+
'Avoid using TextArea for short, single-line values like names or emails; use TextInput instead.',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
guidance: false,
|
|
332
|
+
description:
|
|
333
|
+
"Don't rely solely on placeholder text to communicate the purpose of the field; placeholders disappear on focus and are not accessible labels.",
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
guidance: false,
|
|
337
|
+
description:
|
|
338
|
+
"Don't show a status message without also setting the status type; the colored border and icon are what draw the user's attention to the message.",
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
guidance: false,
|
|
342
|
+
description:
|
|
343
|
+
"Don't wrap a disabled TextArea in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.",
|
|
344
|
+
},
|
|
221
345
|
],
|
|
222
346
|
},
|
|
223
347
|
};
|
|
@@ -252,7 +376,8 @@ export const docsZh = {
|
|
|
252
376
|
{
|
|
253
377
|
name: 'changeAction',
|
|
254
378
|
type: '(value: string, e: ChangeEvent<HTMLTextAreaElement>) => void | Promise<void>',
|
|
255
|
-
description:
|
|
379
|
+
description:
|
|
380
|
+
'在 React transition 内于 onChange 之后触发的异步操作。通过 useOptimistic 启用乐观更新。',
|
|
256
381
|
},
|
|
257
382
|
{
|
|
258
383
|
name: 'isLabelHidden',
|
|
@@ -274,7 +399,8 @@ export const docsZh = {
|
|
|
274
399
|
{
|
|
275
400
|
name: 'isRequired',
|
|
276
401
|
type: 'boolean',
|
|
277
|
-
description:
|
|
402
|
+
description:
|
|
403
|
+
'在标签旁显示"必填"指示器并设置 aria-required。与 isOptional 互斥。',
|
|
278
404
|
default: 'false',
|
|
279
405
|
},
|
|
280
406
|
{
|
|
@@ -316,12 +442,14 @@ export const docsZh = {
|
|
|
316
442
|
{
|
|
317
443
|
name: 'maxLength',
|
|
318
444
|
type: 'number',
|
|
319
|
-
description:
|
|
445
|
+
description:
|
|
446
|
+
'允许的最大字符数。设置后,在文本域下方显示字符计数器(当前/最大)。不原生强制限制:超出时计数器显示错误样式。',
|
|
320
447
|
},
|
|
321
448
|
{
|
|
322
449
|
name: 'status',
|
|
323
450
|
type: "{ type: 'warning' | 'error' | 'success'; message?: string }",
|
|
324
|
-
description:
|
|
451
|
+
description:
|
|
452
|
+
'应用彩色边框和图标的状态指示器。可选消息显示在文本域下方的浮动框中。',
|
|
325
453
|
},
|
|
326
454
|
{
|
|
327
455
|
name: 'statusVariant',
|
|
@@ -381,17 +509,27 @@ export const docsZh = {
|
|
|
381
509
|
{
|
|
382
510
|
name: 'xstyle',
|
|
383
511
|
type: 'StyleXStyles',
|
|
384
|
-
description:
|
|
512
|
+
description:
|
|
513
|
+
'StyleX 样式,用于布局自定义(边距、定位、尺寸)。必须是 stylex.create() 的值,而非内联样式对象。',
|
|
385
514
|
},
|
|
386
515
|
],
|
|
387
516
|
theming: {
|
|
388
517
|
targets: [
|
|
389
|
-
{
|
|
518
|
+
{
|
|
519
|
+
className: 'astryx-text-area',
|
|
520
|
+
visualProps: ['size', 'status'],
|
|
521
|
+
states: ['disabled', 'readonly'],
|
|
522
|
+
},
|
|
390
523
|
{className: 'astryx-text-area-control'},
|
|
391
524
|
{className: 'astryx-text-area-counter'},
|
|
392
525
|
// Still emitted beside the names above, so themes written against
|
|
393
526
|
// them keep working. Drop in the next major.
|
|
394
|
-
{
|
|
527
|
+
{
|
|
528
|
+
className: 'astryx-textarea',
|
|
529
|
+
visualProps: ['size', 'status'],
|
|
530
|
+
states: ['disabled', 'readonly'],
|
|
531
|
+
deprecatedFor: 'text-area',
|
|
532
|
+
},
|
|
395
533
|
],
|
|
396
534
|
vars: [
|
|
397
535
|
{
|
|
@@ -403,21 +541,57 @@ export const docsZh = {
|
|
|
403
541
|
},
|
|
404
542
|
],
|
|
405
543
|
derived: [
|
|
406
|
-
{
|
|
544
|
+
{
|
|
545
|
+
property: 'paddingInline',
|
|
546
|
+
vars: ['--_textarea-inline-padding'],
|
|
547
|
+
replaces: true,
|
|
548
|
+
},
|
|
407
549
|
],
|
|
408
550
|
},
|
|
409
551
|
usage: {
|
|
410
552
|
description:
|
|
411
553
|
'TextArea is a multi-line text input for collecting longer-form content like comments, descriptions, or messages. Use it when the expected input spans multiple lines. For shorter, single-line values, use TextInput.',
|
|
412
554
|
bestPractices: [
|
|
413
|
-
{
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
{
|
|
419
|
-
|
|
420
|
-
|
|
555
|
+
{
|
|
556
|
+
guidance: true,
|
|
557
|
+
description:
|
|
558
|
+
'Provide a visible label so users know what to enter. If the label must be hidden, set isLabelHidden with a descriptive label for screen readers.',
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
guidance: true,
|
|
562
|
+
description:
|
|
563
|
+
'Set maxLength with a character counter when there is a defined limit; it helps users stay within bounds before they submit.',
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
guidance: true,
|
|
567
|
+
description:
|
|
568
|
+
'Use the status prop to surface validation feedback inline: show success when input is valid, warning for soft limits, and error for hard failures.',
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
guidance: true,
|
|
572
|
+
description:
|
|
573
|
+
'Add a description or placeholder to clarify expected content, like "Describe the issue in detail," but never rely on placeholder alone as the only label.',
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
guidance: false,
|
|
577
|
+
description:
|
|
578
|
+
'Avoid using TextArea for short, single-line values like names or emails; use TextInput instead.',
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
guidance: false,
|
|
582
|
+
description:
|
|
583
|
+
"Don't rely solely on placeholder text to communicate the purpose of the field; placeholders disappear on focus and are not accessible labels.",
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
guidance: false,
|
|
587
|
+
description:
|
|
588
|
+
"Don't show a status message without also setting the status type; the colored border and icon are what draw the user's attention to the message.",
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
guidance: false,
|
|
592
|
+
description:
|
|
593
|
+
"Don't wrap a disabled TextArea in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead.",
|
|
594
|
+
},
|
|
421
595
|
],
|
|
422
596
|
},
|
|
423
597
|
};
|
|
@@ -429,14 +603,44 @@ export const docsDense = {
|
|
|
429
603
|
description:
|
|
430
604
|
'Multi-line input for comments, descriptions, messages. Use when input spans multiple lines; use TextInput for single-line.',
|
|
431
605
|
bestPractices: [
|
|
432
|
-
{
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
{
|
|
438
|
-
|
|
439
|
-
|
|
606
|
+
{
|
|
607
|
+
guidance: true,
|
|
608
|
+
description:
|
|
609
|
+
'Visible label or isLabelHidden with descriptive label for screen readers.',
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
guidance: true,
|
|
613
|
+
description: 'Set maxLength for character counter when a limit exists.',
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
guidance: true,
|
|
617
|
+
description:
|
|
618
|
+
'Use status prop for inline validation: success, warning, error.',
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
guidance: true,
|
|
622
|
+
description:
|
|
623
|
+
'Add description or placeholder for context; never placeholder alone as label.',
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
guidance: false,
|
|
627
|
+
description: 'Avoid TextArea for single-line values; use TextInput.',
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
guidance: false,
|
|
631
|
+
description:
|
|
632
|
+
"Don't use placeholder as only label; disappears on focus, not accessible.",
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
guidance: false,
|
|
636
|
+
description:
|
|
637
|
+
"Don't show status message without status type; border and icon draw attention.",
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
guidance: false,
|
|
641
|
+
description:
|
|
642
|
+
"Don't wrap a disabled TextArea in Tooltip to explain the disabled state; use the disabledMessage prop instead.",
|
|
643
|
+
},
|
|
440
644
|
],
|
|
441
645
|
},
|
|
442
646
|
propDescriptions: {
|
|
@@ -444,11 +648,13 @@ export const docsDense = {
|
|
|
444
648
|
label: 'Label text for textarea; always rendered for a11y.',
|
|
445
649
|
value: 'Current textarea value.',
|
|
446
650
|
onChange: 'Fired on textarea value change.',
|
|
447
|
-
changeAction:
|
|
651
|
+
changeAction:
|
|
652
|
+
'Async action after onChange in React transition. Enables useOptimistic.',
|
|
448
653
|
isLabelHidden: 'Visually hides label; keeps screen reader access.',
|
|
449
654
|
description: 'Helper text between label+textarea.',
|
|
450
655
|
isOptional: 'Shows "Optional" indicator. Mutually exclusive w/ isRequired.',
|
|
451
|
-
isRequired:
|
|
656
|
+
isRequired:
|
|
657
|
+
'Shows "Required" indicator+sets aria-required. Mutually exclusive w/ isOptional.',
|
|
452
658
|
isDisabled: 'Disables textarea, prevents interaction.',
|
|
453
659
|
isReadOnly:
|
|
454
660
|
'Read-only: value visible + still submits, but not editable. Unlike isDisabled: not dimmed, stays in tab order.',
|
|
@@ -457,9 +663,12 @@ export const docsDense = {
|
|
|
457
663
|
isLoading: 'Loading state w/ spinner inside input.',
|
|
458
664
|
placeholder: 'Placeholder when textarea empty.',
|
|
459
665
|
rows: 'Visible text rows.',
|
|
460
|
-
maxLength:
|
|
461
|
-
|
|
462
|
-
|
|
666
|
+
maxLength:
|
|
667
|
+
'Max chars allowed. Shows counter (current/max) inside the container, bottom-right beneath the text. No native enforcement; over-limit shows red + a warning icon and is announced to screen readers.',
|
|
668
|
+
status:
|
|
669
|
+
'Colored border+icon status. Optional floating message below textarea.',
|
|
670
|
+
statusVariant:
|
|
671
|
+
'How status message is placed: attached overlaps below input; detached floats below w/ spacing; tooltip hides the box and shows it on the status icon.',
|
|
463
672
|
labelTooltip: 'Tooltip in info icon at label end.',
|
|
464
673
|
startIcon: 'Icon inside leading edge of textarea wrapper.',
|
|
465
674
|
hasSpellCheck: 'Enables/disables browser spell checking.',
|
|
@@ -469,6 +678,7 @@ export const docsDense = {
|
|
|
469
678
|
htmlName: 'HTML name attr for form submissions.',
|
|
470
679
|
onFocus: 'Callback on focus.',
|
|
471
680
|
onBlur: 'Callback on blur.',
|
|
472
|
-
xstyle:
|
|
681
|
+
xstyle:
|
|
682
|
+
'StyleX styles for layout customization. Must be stylex.create() value, not inline style.',
|
|
473
683
|
},
|
|
474
684
|
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
template_version: 3
|
|
4
|
+
kind: component
|
|
5
|
+
id: component:TextArea
|
|
6
|
+
authority: draft
|
|
7
|
+
archive_reason: null
|
|
8
|
+
superseded_by: null
|
|
9
|
+
approved_by: null
|
|
10
|
+
approved_at: null
|
|
11
|
+
owners: [cixzhang]
|
|
12
|
+
review_triggers: [theming]
|
|
13
|
+
verified_by:
|
|
14
|
+
[
|
|
15
|
+
packages/core/src/TextArea/TextArea.test.tsx,
|
|
16
|
+
packages/core/src/theme/themingTargets.test.ts,
|
|
17
|
+
scripts/check-knowledge.mjs,
|
|
18
|
+
]
|
|
19
|
+
families: []
|
|
20
|
+
design_specs: []
|
|
21
|
+
architecture: [architecture:component-theming-surface]
|
|
22
|
+
contributing: []
|
|
23
|
+
system_specs: []
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# TextArea component contract
|
|
27
|
+
|
|
28
|
+
## Intent
|
|
29
|
+
|
|
30
|
+
TextArea presents a labeled multi-line text field. This draft records its current
|
|
31
|
+
consumer anatomy and the theming ownership of parts rendered by TextArea, Field,
|
|
32
|
+
FieldStatus, and Spinner. It changes no runtime behavior or public API.
|
|
33
|
+
|
|
34
|
+
## Compatibility and migration
|
|
35
|
+
|
|
36
|
+
- Released default preserved: `yes`
|
|
37
|
+
- Compatibility class: additive documentation only; runtime, DOM, styling,
|
|
38
|
+
targets, aliases, and public API remain unchanged
|
|
39
|
+
- Controlled/uncontrolled behavior: unchanged; TextArea remains controlled
|
|
40
|
+
- Migration decision: none
|
|
41
|
+
|
|
42
|
+
Consumer migration instructions belong in consumer docs and release notes.
|
|
43
|
+
|
|
44
|
+
## Ownership boundary
|
|
45
|
+
|
|
46
|
+
**Owns**
|
|
47
|
+
|
|
48
|
+
- The painted input container, native text area, and character counter represented
|
|
49
|
+
by the existing `text-area`, `text-area-control`, and `text-area-counter`
|
|
50
|
+
targets.
|
|
51
|
+
|
|
52
|
+
**Does not own / non-goals**
|
|
53
|
+
|
|
54
|
+
- Label and attached/detached validation-message presentation — owned by
|
|
55
|
+
`component:Field` and `component:FieldStatus`.
|
|
56
|
+
- The tooltip-variant status surface — owned by `component:Tooltip`.
|
|
57
|
+
- Standard start-icon presentation and the shared on-field status icon — owned
|
|
58
|
+
by `component:Icon` and `component:Field`, respectively.
|
|
59
|
+
- Loading-indicator presentation — owned by `component:Spinner`.
|
|
60
|
+
- Caller-provided custom start content outside Icon's supported source forms.
|
|
61
|
+
- The deprecated `textarea` alias as separate anatomy.
|
|
62
|
+
|
|
63
|
+
## Public concepts
|
|
64
|
+
|
|
65
|
+
No new public concept is introduced. Consumer props, states, and usage remain
|
|
66
|
+
documented in `TextArea.doc.mjs`.
|
|
67
|
+
|
|
68
|
+
## Behavioral and layout contract
|
|
69
|
+
|
|
70
|
+
| ID | Candidate invariant | Basis | Draft review state |
|
|
71
|
+
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------------------------------------------------- |
|
|
72
|
+
| FR1 | The current render places the native text area inside its painted input container. | Current source, docs, and focused tests | Verified current behavior; no new behavior decided |
|
|
73
|
+
| FR2 | The input container, native control, and conditional character counter carry the three current local targets. | Current source and public docs | Verified current behavior; no target change |
|
|
74
|
+
| FR3 | Label, standard start icon, loading indicator, status icon, and status surfaces continue to use their shared Field, Icon, Spinner, FieldStatus, and Tooltip owners. | Current source | Verified conditional composition boundary |
|
|
75
|
+
| FR4 | Placeholder text remains part of the native control; the over-limit glyph remains part of the character counter. | Current source, docs, and focused tests | Verified current grouping |
|
|
76
|
+
|
|
77
|
+
### Allowed variation
|
|
78
|
+
|
|
79
|
+
- Value, placeholder, row count, size, status, disabled/read-only state, and
|
|
80
|
+
optional slots remain current capabilities rather than separate target names.
|
|
81
|
+
- Start-icon content may be an Icon-supported value or caller-supplied ReactNode.
|
|
82
|
+
|
|
83
|
+
### Representative states
|
|
84
|
+
|
|
85
|
+
| State | Required invariant | Allowed variation |
|
|
86
|
+
| -------------------- | ---------------------------------------------------------------------- | ----------------------------------- |
|
|
87
|
+
| Default editable | Label, input container, and native text area render. | Value, placeholder, rows, and size |
|
|
88
|
+
| With character limit | Character counter renders inside the input container. | Count and over-limit state |
|
|
89
|
+
| Busy or status | Shared Spinner or status presentation renders in its current location. | Status variant and message presence |
|
|
90
|
+
|
|
91
|
+
### Transformation and precedence order
|
|
92
|
+
|
|
93
|
+
- No new value, layout, status, or styling precedence rule is introduced.
|
|
94
|
+
|
|
95
|
+
### Performance and resources
|
|
96
|
+
|
|
97
|
+
- No new performance or resource rule is introduced.
|
|
98
|
+
|
|
99
|
+
## Accessibility contract
|
|
100
|
+
|
|
101
|
+
This draft does not change or extend TextArea's existing label, description,
|
|
102
|
+
status, counter, busy state, disabled/read-only state, or announcement behavior.
|
|
103
|
+
|
|
104
|
+
## Design relationships
|
|
105
|
+
|
|
106
|
+
| Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
|
|
107
|
+
| --------------------- | -------------------------------------------------------------------- | ------------------------------- | -------------- | ------------------ |
|
|
108
|
+
| Input container | Presents the current painted field boundary. | Current source and public docs | Supporting | FR1, FR2 |
|
|
109
|
+
| Text area | Presents and edits the current multi-line value. | Current source and public docs | Prominent | FR1, FR2 |
|
|
110
|
+
| Character counter | Presents current and maximum character counts. | Current source and public docs | Supporting | FR2, FR4 |
|
|
111
|
+
| Shared field feedback | Presents current label, standard icons, status, and loading content. | Current shared-component source | Supporting | FR3 |
|
|
112
|
+
|
|
113
|
+
### Theming anatomy
|
|
114
|
+
|
|
115
|
+
<!-- anatomy-theming:v1 -->
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"Label": {
|
|
120
|
+
"delegatesTo": {"owner": "component:Field", "target": "field-label"}
|
|
121
|
+
},
|
|
122
|
+
"Description": {
|
|
123
|
+
"none": {
|
|
124
|
+
"reason": "unsettled: No current public target reaches the stable Description; future exposure still needs an owner decision"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"Input container": {"target": "text-area"},
|
|
128
|
+
"Text area": {"target": "text-area-control"},
|
|
129
|
+
"Placeholder": {"inherits": "text-area-control"},
|
|
130
|
+
"Start icon": {
|
|
131
|
+
"delegatesTo": {"owner": "component:Icon", "target": "icon"}
|
|
132
|
+
},
|
|
133
|
+
"Custom start content": {
|
|
134
|
+
"none": {
|
|
135
|
+
"reason": "intentional: Custom start content is caller-provided ReactNode content outside TextArea's public theming ownership"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"Spinner": {
|
|
139
|
+
"delegatesTo": {"owner": "component:Spinner", "target": "spinner"}
|
|
140
|
+
},
|
|
141
|
+
"Status icon": {
|
|
142
|
+
"delegatesTo": {
|
|
143
|
+
"owner": "component:Field",
|
|
144
|
+
"target": "input-status-icon"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"Character counter": {"target": "text-area-counter"},
|
|
148
|
+
"Field status message": {
|
|
149
|
+
"delegatesTo": {
|
|
150
|
+
"owner": "component:FieldStatus",
|
|
151
|
+
"target": "field-status"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"Tooltip status message": {
|
|
155
|
+
"delegatesTo": {"owner": "component:Tooltip", "target": "tooltip"}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`Description` remains stable consumer anatomy, but no current public target
|
|
161
|
+
reaches it; future exposure remains unsettled. Semantic names and icon component
|
|
162
|
+
values render through Icon and delegate to its `icon` target; arbitrary ReactNode
|
|
163
|
+
start content is caller-provided and intentionally stays outside TextArea's
|
|
164
|
+
public theming ownership. Attached and detached status messages delegate to
|
|
165
|
+
FieldStatus, while the tooltip variant delegates its rendered surface to Tooltip.
|
|
166
|
+
The deprecated `textarea` alias is compatibility evidence only and does not
|
|
167
|
+
appear in the map.
|
|
168
|
+
|
|
169
|
+
## Family and system relationships
|
|
170
|
+
|
|
171
|
+
- `architecture:component-theming-surface` owns anatomy qualification, factual
|
|
172
|
+
`none` dispositions, composition-preserving ownership, and alias exclusion.
|
|
173
|
+
- Field, FieldStatus, Icon, Spinner, and Tooltip retain their existing public
|
|
174
|
+
target contracts when composed by TextArea.
|
|
175
|
+
|
|
176
|
+
## Verification map
|
|
177
|
+
|
|
178
|
+
| Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
|
|
179
|
+
| ------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
|
|
180
|
+
| FR1, FR4 | `TextArea.test.tsx` structure and counter suites | Default, placeholder, counter, over limit | Removing or regrouping stable parts breaks existing role, content, or DOM assertions. | `audit:TextArea/anatomy` |
|
|
181
|
+
| FR2 | `TextArea.test.tsx` root-target suite and `themingTargets.test.ts` | Current and deprecated root names; current targets | Removing root compatibility classes fails focused coverage; source/docs drift fails the target guard. | `audit:TextArea/theming` |
|
|
182
|
+
| FR3 | `TextArea.test.tsx`, `useInputStatusIcon.test.tsx`, and source inspection of `renderIconSlot` | Standard/custom start content; Spinner; attached/detached/tooltip status | Removing or rerouting composed content breaks existing icon, spinner, tooltip, message, or association assertions. | `audit:TextArea/anatomy` |
|
|
183
|
+
| Theming anatomy map | `scripts/check-knowledge.mjs` | Canonical anatomy and current local targets | Canonical-key drift, invalid dispositions or target spelling, alias-backed local claims, or an unclaimed current local target fails validation. | `audit:TextArea/theming` |
|
|
184
|
+
|
|
185
|
+
The focused TextArea suite pins the current and deprecated root classes, but does
|
|
186
|
+
not separately assert the exact `text-area-control` or `text-area-counter` class
|
|
187
|
+
placement. The source/metadata target guard covers those local declarations. No
|
|
188
|
+
current repository check resolves `delegatesTo` owner/target pairs; the pairs in
|
|
189
|
+
this draft were verified manually, so semantic delegation drift remains a
|
|
190
|
+
validation gap.
|
|
191
|
+
|
|
192
|
+
## Decision log
|
|
193
|
+
|
|
194
|
+
None. This draft records current facts and introduces no component-local design
|
|
195
|
+
or API decision.
|
|
196
|
+
|
|
197
|
+
## Open questions
|
|
198
|
+
|
|
199
|
+
None.
|
|
200
|
+
|
|
201
|
+
## Content boundary
|
|
202
|
+
|
|
203
|
+
This file does not duplicate consumer prop tables, examples, implementation
|
|
204
|
+
steps, or shared-component contracts. It links to their owners.
|