ionbase-ui 0.86.0 → 0.88.0
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/components/MultiSelect.d.ts +90 -0
- package/dist/components/MultiSelect.d.ts.map +1 -0
- package/dist/components/MultiSelect.js +189 -0
- package/dist/components/MultiSelect.js.map +1 -0
- package/dist/components/Toolbar.d.ts +35 -0
- package/dist/components/Toolbar.d.ts.map +1 -0
- package/dist/components/Toolbar.js +81 -0
- package/dist/components/Toolbar.js.map +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/figma-descriptions.json +89 -79
- package/dist/figma-map.json +97 -2
- package/dist/meta/CheckboxGroup.json +2 -2
- package/dist/meta/Combobox.json +8 -0
- package/dist/meta/MultiSelect.json +269 -0
- package/dist/meta/Select.json +2 -1
- package/dist/meta/Toolbar.json +130 -0
- package/dist/meta/components.json +412 -4
- package/dist/meta/contrast.json +28 -0
- package/dist/meta/index.json +26 -1
- package/dist/meta/patterns/DataTable.json +21 -2
- package/dist/meta/patterns/index.json +3 -1
- package/dist/styles/index.css +2 -0
- package/dist/styles/multi-select.css +104 -0
- package/dist/styles/toolbar.css +36 -0
- package/llms.txt +2 -2
- package/package.json +1 -1
package/dist/figma-map.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "ionbase-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.0",
|
|
4
4
|
"figmaFile": "gaLbGd0QNb1fUl6BjSpfBA",
|
|
5
5
|
"figmaExported": "2026-09-25",
|
|
6
6
|
"usage": "Given a Figma node id or component name, look it up here for the React component, its import, and how each Figma property maps onto a real prop. Every mapping is verified against both the Figma export and the TypeScript API on each build.",
|
|
@@ -1260,6 +1260,35 @@
|
|
|
1260
1260
|
}
|
|
1261
1261
|
}
|
|
1262
1262
|
},
|
|
1263
|
+
"1510:666": {
|
|
1264
|
+
"figmaComponent": "Multi Select",
|
|
1265
|
+
"figmaNodeId": "1510:666",
|
|
1266
|
+
"figmaPage": "Multi Select",
|
|
1267
|
+
"component": "MultiSelect",
|
|
1268
|
+
"import": "import { MultiSelect } from 'ionbase-ui';",
|
|
1269
|
+
"docs": "components/multi-select/index.html.md",
|
|
1270
|
+
"props": {
|
|
1271
|
+
"Size": {
|
|
1272
|
+
"prop": "size",
|
|
1273
|
+
"values": {
|
|
1274
|
+
"Small": "sm",
|
|
1275
|
+
"Medium": "md",
|
|
1276
|
+
"Large": "lg"
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
"State": {
|
|
1280
|
+
"prop": "isInvalid",
|
|
1281
|
+
"when": {
|
|
1282
|
+
"Invalid": true
|
|
1283
|
+
},
|
|
1284
|
+
"note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly, and both hide the tags’ × buttons. The field is Combobox’s, which is Input’s box — the same split both record."
|
|
1285
|
+
},
|
|
1286
|
+
"Show Tags#1510:0": {
|
|
1287
|
+
"prop": "hideTags",
|
|
1288
|
+
"note": "Inverted: Show Tags off is `hideTags`, for a filter bar whose active-filters row already shows the values. With tags shown, they are the chosen `value`, labelled from `options`, and appear whenever something is chosen."
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1263
1292
|
"53:13": {
|
|
1264
1293
|
"figmaComponent": "Nav Item",
|
|
1265
1294
|
"figmaNodeId": "53:13",
|
|
@@ -2316,6 +2345,24 @@
|
|
|
2316
2345
|
}
|
|
2317
2346
|
}
|
|
2318
2347
|
},
|
|
2348
|
+
"1517:583": {
|
|
2349
|
+
"figmaComponent": "Toolbar",
|
|
2350
|
+
"figmaNodeId": "1517:583",
|
|
2351
|
+
"figmaPage": "Toolbar",
|
|
2352
|
+
"component": "Toolbar",
|
|
2353
|
+
"import": "import { Toolbar } from 'ionbase-ui';",
|
|
2354
|
+
"docs": "components/toolbar/index.html.md",
|
|
2355
|
+
"props": {
|
|
2356
|
+
"Orientation": {
|
|
2357
|
+
"prop": "orientation",
|
|
2358
|
+
"values": {
|
|
2359
|
+
"Horizontal": "horizontal",
|
|
2360
|
+
"Vertical": "vertical"
|
|
2361
|
+
},
|
|
2362
|
+
"note": "Horizontal moves with ← →, vertical with ↑ ↓. The children are the caller's: Buttons, a Divider between groups (vertical in a horizontal toolbar), and a MenuTrigger with an icon-only Button for the overflow. Name it with aria-label for what it acts on."
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
},
|
|
2319
2366
|
"801:68": {
|
|
2320
2367
|
"figmaComponent": "Tooltip",
|
|
2321
2368
|
"figmaNodeId": "801:68",
|
|
@@ -3677,6 +3724,35 @@
|
|
|
3677
3724
|
}
|
|
3678
3725
|
}
|
|
3679
3726
|
},
|
|
3727
|
+
"Multi Select": {
|
|
3728
|
+
"figmaComponent": "Multi Select",
|
|
3729
|
+
"figmaNodeId": "1510:666",
|
|
3730
|
+
"figmaPage": "Multi Select",
|
|
3731
|
+
"component": "MultiSelect",
|
|
3732
|
+
"import": "import { MultiSelect } from 'ionbase-ui';",
|
|
3733
|
+
"docs": "components/multi-select/index.html.md",
|
|
3734
|
+
"props": {
|
|
3735
|
+
"Size": {
|
|
3736
|
+
"prop": "size",
|
|
3737
|
+
"values": {
|
|
3738
|
+
"Small": "sm",
|
|
3739
|
+
"Medium": "md",
|
|
3740
|
+
"Large": "lg"
|
|
3741
|
+
}
|
|
3742
|
+
},
|
|
3743
|
+
"State": {
|
|
3744
|
+
"prop": "isInvalid",
|
|
3745
|
+
"when": {
|
|
3746
|
+
"Invalid": true
|
|
3747
|
+
},
|
|
3748
|
+
"note": "Hover, Focus and Filled are CSS or content; Disabled is isDisabled and Read-only is isReadOnly, and both hide the tags’ × buttons. The field is Combobox’s, which is Input’s box — the same split both record."
|
|
3749
|
+
},
|
|
3750
|
+
"Show Tags#1510:0": {
|
|
3751
|
+
"prop": "hideTags",
|
|
3752
|
+
"note": "Inverted: Show Tags off is `hideTags`, for a filter bar whose active-filters row already shows the values. With tags shown, they are the chosen `value`, labelled from `options`, and appear whenever something is chosen."
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
},
|
|
3680
3756
|
"Nav Item": {
|
|
3681
3757
|
"figmaComponent": "Nav Item",
|
|
3682
3758
|
"figmaNodeId": "53:13",
|
|
@@ -4733,6 +4809,24 @@
|
|
|
4733
4809
|
}
|
|
4734
4810
|
}
|
|
4735
4811
|
},
|
|
4812
|
+
"Toolbar": {
|
|
4813
|
+
"figmaComponent": "Toolbar",
|
|
4814
|
+
"figmaNodeId": "1517:583",
|
|
4815
|
+
"figmaPage": "Toolbar",
|
|
4816
|
+
"component": "Toolbar",
|
|
4817
|
+
"import": "import { Toolbar } from 'ionbase-ui';",
|
|
4818
|
+
"docs": "components/toolbar/index.html.md",
|
|
4819
|
+
"props": {
|
|
4820
|
+
"Orientation": {
|
|
4821
|
+
"prop": "orientation",
|
|
4822
|
+
"values": {
|
|
4823
|
+
"Horizontal": "horizontal",
|
|
4824
|
+
"Vertical": "vertical"
|
|
4825
|
+
},
|
|
4826
|
+
"note": "Horizontal moves with ← →, vertical with ↑ ↓. The children are the caller's: Buttons, a Divider between groups (vertical in a horizontal toolbar), and a MenuTrigger with an icon-only Button for the overflow. Name it with aria-label for what it acts on."
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
},
|
|
4736
4830
|
"Tooltip": {
|
|
4737
4831
|
"figmaComponent": "Tooltip",
|
|
4738
4832
|
"figmaNodeId": "801:68",
|
|
@@ -4855,7 +4949,8 @@
|
|
|
4855
4949
|
"Screen Frame": "The frame FullCard draws around its media slot. The component owns it, so there is nothing for a caller to place.",
|
|
4856
4950
|
"Side Menu Item": "A 32-tall side-navigation row on the Side Menu page, which predates Sidebar. It shared the name Menu Item with the real one (82:217) until 25 Sep 2026, and because the export keyed on the name it silently replaced it — the mapping, the gates and the Dev Mode block all pointed here. Sidebar Item is the side-navigation row code ships.",
|
|
4857
4951
|
"Side Menu Section Title": "The section heading of the same pre-Sidebar side menu. Menu Section Title (1468:271) is the menu's own, drawn on the Menu page; Sidebar Section is the side navigation's.",
|
|
4858
|
-
"Table/Cell Text": "TableCell covers Figma's Table Cell and Cell Text together; the two are never used apart in the design. Recorded in TableCell's contract."
|
|
4952
|
+
"Table/Cell Text": "TableCell covers Figma's Table Cell and Cell Text together; the two are never used apart in the design. Recorded in TableCell's contract.",
|
|
4953
|
+
"Multi Select Menu": "The list MultiSelect opens, with a check on each row. Not a separate export, for Combobox Menu's reason: the list and the field are one control, and a listbox with nothing to own it is not a thing a caller should be able to mount. Drawn separately because a frame cannot both hug a field and overlay a list."
|
|
4859
4954
|
},
|
|
4860
4955
|
"codeUnmapped": {
|
|
4861
4956
|
"DateRangePicker": "Drawn, but not as a set of its own: its field is Date Picker Type=Range and its calendar is Calendar Mode=Range. A Figma set maps to exactly one code component, and splitting one field into two sets to satisfy that would draw the same box twice. Read Date Picker’s Type note for the handoff.",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"when": "the options are many, or space is tight",
|
|
20
|
-
"use": "
|
|
21
|
-
"why": "past about seven options a list of boxes is longer than the form around it"
|
|
20
|
+
"use": "MultiSelect",
|
|
21
|
+
"why": "past about seven options a list of boxes is longer than the form around it; MultiSelect filters them and shows the chosen ones as tags"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"when": "there is one on/off choice",
|
package/dist/meta/Combobox.json
CHANGED
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"use": "Select",
|
|
18
18
|
"why": "the native control brings keyboard handling and the mobile platform picker for free; this rebuilds all of it"
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
"when": "more than one value may be picked",
|
|
22
|
+
"use": "MultiSelect",
|
|
23
|
+
"why": "its input holds only the filter text, the list stays open between picks, and the chosen values show as tags"
|
|
24
|
+
},
|
|
20
25
|
{
|
|
21
26
|
"when": "there are two or three mutually exclusive choices worth showing at once",
|
|
22
27
|
"use": "RadioGroup"
|
|
@@ -58,6 +63,9 @@
|
|
|
58
63
|
"requires": [
|
|
59
64
|
"`label`, or an `aria-label` when no visible label is rendered",
|
|
60
65
|
"`emptyLabel` wording that says why nothing is listed, if the default is too terse"
|
|
66
|
+
],
|
|
67
|
+
"notes": [
|
|
68
|
+
"While the list is open, React Aria hides everything outside the field and the list from assistive technology. That is the platform pattern for a combobox; a whole-page accessibility scan taken with the list open reports the hidden page, not this control."
|
|
61
69
|
]
|
|
62
70
|
},
|
|
63
71
|
"antiPatterns": [
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "MultiSelect",
|
|
3
|
+
"source": "src/components/MultiSelect.tsx",
|
|
4
|
+
"propsType": "MultiSelectProps",
|
|
5
|
+
"description": "MultiSelect — a text field that filters a list, with any number of selected\nvalues shown as removable tags beneath it.\n\nWHY IT IS ITS OWN COMPONENT, NOT `Combobox selectionMode=\"multiple\"`\n\nThe two differ in what the field holds. Combobox's input shows the chosen\nlabel — typing edits the value. Here the input only ever holds the filter\ntext, the value lives in the tags, and choosing an option leaves the list\nopen for the next one. One prop switching the meaning of the text in the\nbox is the kind of contract agents get wrong in generated code.\n\nWHAT REACT ARIA GIVES IT\n\n`useComboBoxState` and `useComboBox` in `selectionMode: 'multiple'`: the\nlistbox is `aria-multiselectable`, Enter and click toggle an option without\nclosing the list, the filter text clears after each pick, and native\n`required` is set only while nothing is chosen — \"at least one\", the same\nrule CheckboxGroup enforces.\n\nWHAT IT ADDS\n\n - The tags. A TagGroup under the field, named by the field's label, one\n tab stop, Delete or the × to remove. The selection must be visible with\n the list closed, and \"3 selected\" hides which three.\n - The value is announced with the field. React Aria points the input's\n `aria-describedby` at a value element; this fills it with the chosen\n labels joined by `Intl.ListFormat`, so \"Billing, Legal and Ops\" is read\n in the user's language with no string shipped for it.\n - Backspace in an empty field removes the last value, which is what\n every tag input on the web has taught people to expect.\n - Focus lands back in the field when the last tag is removed, instead of\n falling to <body> with the TagGroup that held it.",
|
|
6
|
+
"import": "import { MultiSelect } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "A text field that filters a list, with any number of values chosen. The choices show as removable tags beneath the field and are read with it.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"the user picks several values from a list too long to show as checkboxes — teams, regions, labels, assignees",
|
|
11
|
+
"a table filter can match more than one value — \"Status is Failing or Paused\""
|
|
12
|
+
],
|
|
13
|
+
"useInstead": [
|
|
14
|
+
{
|
|
15
|
+
"when": "exactly one value is picked",
|
|
16
|
+
"use": "Combobox",
|
|
17
|
+
"why": "Combobox's input shows the chosen label; here the input only ever holds the filter text, and the list stays open for the next pick"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"when": "there are about seven options or fewer",
|
|
21
|
+
"use": "CheckboxGroup",
|
|
22
|
+
"why": "every option visible at once beats typing to find one, and nothing is hidden behind a popover"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"when": "the values are free text the user invents, not options from a list",
|
|
26
|
+
"use": "TagGroup",
|
|
27
|
+
"why": "MultiSelect only chooses from `options`; a free-text tag input is a TagGroup beside an Input"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"composition": {
|
|
31
|
+
"order": [
|
|
32
|
+
"MultiSelect"
|
|
33
|
+
],
|
|
34
|
+
"note": "Pass the full `options` list; filtering, the tags and the announcement are the component's. In a table's filter bar, give it `aria-label` and `hideTags`, and list its values in the table's active-filters TagGroup — one removable tag per value, not the same tags twice.",
|
|
35
|
+
"example": "<MultiSelect label=\"Notify teams\" options={teams} value={teamIds} onChange={setTeamIds} placeholder=\"Search teams\" />"
|
|
36
|
+
},
|
|
37
|
+
"variants": {
|
|
38
|
+
"size": {
|
|
39
|
+
"sm": {
|
|
40
|
+
"use": "dense forms, table filters"
|
|
41
|
+
},
|
|
42
|
+
"md": {
|
|
43
|
+
"use": "the default"
|
|
44
|
+
},
|
|
45
|
+
"lg": {
|
|
46
|
+
"use": "prominent single-field forms"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"slots": {
|
|
51
|
+
"label": {
|
|
52
|
+
"accepts": "text",
|
|
53
|
+
"note": "names the field and the tags beneath it"
|
|
54
|
+
},
|
|
55
|
+
"description": {
|
|
56
|
+
"accepts": "text",
|
|
57
|
+
"note": "replaced by `errorMessage` while `isInvalid` is set"
|
|
58
|
+
},
|
|
59
|
+
"errorMessage": {
|
|
60
|
+
"accepts": "text",
|
|
61
|
+
"note": "shown only while `isInvalid` is set"
|
|
62
|
+
},
|
|
63
|
+
"emptyLabel": {
|
|
64
|
+
"accepts": "text",
|
|
65
|
+
"note": "shown in the list when the filter matches nothing"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"a11y": {
|
|
69
|
+
"role": "combobox, controlling a listbox that is aria-multiselectable",
|
|
70
|
+
"guarantees": [
|
|
71
|
+
"React Aria's multi-select combobox: arrow keys move through the list while focus stays in the input, Enter or click toggles an option, and the list stays open for the next pick",
|
|
72
|
+
"the chosen labels are on the input's `aria-describedby`, joined by `Intl.ListFormat` — read with the field in the user's language, with no string shipped for it",
|
|
73
|
+
"the chosen values are a TagGroup named by the field's label: one tab stop, arrow keys between tags, Delete or the × to remove",
|
|
74
|
+
"Backspace in an empty field removes the last value; with text in the field it only edits the text",
|
|
75
|
+
"removing the last tag returns focus to the field instead of dropping it on <body>",
|
|
76
|
+
"`isRequired` marks the field required only while nothing is chosen — at least one"
|
|
77
|
+
],
|
|
78
|
+
"requires": [
|
|
79
|
+
"`label`, or `aria-label` when there is no visible label — it names the tags as well as the field"
|
|
80
|
+
],
|
|
81
|
+
"notes": [
|
|
82
|
+
"While the list is open, React Aria hides everything outside the field and the list from assistive technology, the tags included. That is the platform pattern for a combobox, not a defect: the chosen options are marked selected in the list itself."
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"antiPatterns": [
|
|
86
|
+
{
|
|
87
|
+
"dont": "a Combobox that appends each pick to a TagGroup by hand",
|
|
88
|
+
"why": "the list closes after every pick, the input shows the last label, and nothing announces what is chosen"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"dont": "a native <select multiple>",
|
|
92
|
+
"why": "it needs Ctrl or Cmd to pick a second option, which most people never discover, and it cannot filter"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"dont": "a count in place of the values — \"3 selected\"",
|
|
96
|
+
"why": "it hides which three; the tags show them and the field reads them"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"dont": "`hideTags` with nowhere else showing the values",
|
|
100
|
+
"why": "the field shows only the filter text, so the choice is invisible until the list is opened"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"stylesheet": "src/styles/multi-select.css",
|
|
104
|
+
"tokens": [
|
|
105
|
+
"--border-disabled",
|
|
106
|
+
"--border-primary-strong",
|
|
107
|
+
"--border-stronger",
|
|
108
|
+
"--border-width-default",
|
|
109
|
+
"--border-width-thick",
|
|
110
|
+
"--font-weight-regular",
|
|
111
|
+
"--icon-on-color",
|
|
112
|
+
"--ion-shadow-raised-flush-xs",
|
|
113
|
+
"--radius-xs",
|
|
114
|
+
"--spacing-16",
|
|
115
|
+
"--spacing-2",
|
|
116
|
+
"--spacing-4",
|
|
117
|
+
"--spacing-8",
|
|
118
|
+
"--surface-default",
|
|
119
|
+
"--surface-disabled",
|
|
120
|
+
"--surface-hover",
|
|
121
|
+
"--surface-primary",
|
|
122
|
+
"--type-body-line-height"
|
|
123
|
+
],
|
|
124
|
+
"props": {
|
|
125
|
+
"options": {
|
|
126
|
+
"type": "readonly ComboboxOption[]",
|
|
127
|
+
"required": true,
|
|
128
|
+
"origin": "own",
|
|
129
|
+
"description": "The full option list. Filtering happens here, against what is typed."
|
|
130
|
+
},
|
|
131
|
+
"label": {
|
|
132
|
+
"type": "React.ReactNode",
|
|
133
|
+
"required": false,
|
|
134
|
+
"origin": "own",
|
|
135
|
+
"description": "Field label. Required for a usable control — see `a11y.requires`."
|
|
136
|
+
},
|
|
137
|
+
"aria-label": {
|
|
138
|
+
"type": "string | undefined",
|
|
139
|
+
"required": false,
|
|
140
|
+
"origin": "own",
|
|
141
|
+
"description": "Names the field, and its tags, when there is no visible `label`."
|
|
142
|
+
},
|
|
143
|
+
"description": {
|
|
144
|
+
"type": "React.ReactNode",
|
|
145
|
+
"required": false,
|
|
146
|
+
"origin": "own",
|
|
147
|
+
"description": "Helper text below the field."
|
|
148
|
+
},
|
|
149
|
+
"errorMessage": {
|
|
150
|
+
"type": "React.ReactNode",
|
|
151
|
+
"required": false,
|
|
152
|
+
"origin": "own",
|
|
153
|
+
"description": "Replaces the helper text when `isInvalid` is set."
|
|
154
|
+
},
|
|
155
|
+
"isInvalid": {
|
|
156
|
+
"type": "boolean | undefined",
|
|
157
|
+
"required": false,
|
|
158
|
+
"origin": "own"
|
|
159
|
+
},
|
|
160
|
+
"isDisabled": {
|
|
161
|
+
"type": "boolean | undefined",
|
|
162
|
+
"required": false,
|
|
163
|
+
"origin": "own"
|
|
164
|
+
},
|
|
165
|
+
"isReadOnly": {
|
|
166
|
+
"type": "boolean | undefined",
|
|
167
|
+
"required": false,
|
|
168
|
+
"origin": "own"
|
|
169
|
+
},
|
|
170
|
+
"isRequired": {
|
|
171
|
+
"type": "boolean | undefined",
|
|
172
|
+
"required": false,
|
|
173
|
+
"origin": "own",
|
|
174
|
+
"description": "At least one value must be chosen."
|
|
175
|
+
},
|
|
176
|
+
"size": {
|
|
177
|
+
"type": "MultiSelectSize | undefined",
|
|
178
|
+
"required": false,
|
|
179
|
+
"origin": "own",
|
|
180
|
+
"description": "Matches Input's `Size` variant: Small, Medium, Large.",
|
|
181
|
+
"values": [
|
|
182
|
+
"sm",
|
|
183
|
+
"md",
|
|
184
|
+
"lg"
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
"placeholder": {
|
|
188
|
+
"type": "string | undefined",
|
|
189
|
+
"required": false,
|
|
190
|
+
"origin": "own"
|
|
191
|
+
},
|
|
192
|
+
"value": {
|
|
193
|
+
"type": "readonly string[] | undefined",
|
|
194
|
+
"required": false,
|
|
195
|
+
"origin": "own",
|
|
196
|
+
"description": "The selected values (controlled), in the order they were chosen."
|
|
197
|
+
},
|
|
198
|
+
"defaultValue": {
|
|
199
|
+
"type": "readonly string[] | undefined",
|
|
200
|
+
"required": false,
|
|
201
|
+
"origin": "own",
|
|
202
|
+
"description": "The initially selected values (uncontrolled)."
|
|
203
|
+
},
|
|
204
|
+
"onChange": {
|
|
205
|
+
"type": "((value: string[]) => void) | undefined",
|
|
206
|
+
"required": false,
|
|
207
|
+
"origin": "own",
|
|
208
|
+
"description": "Receives the whole new selection."
|
|
209
|
+
},
|
|
210
|
+
"inputValue": {
|
|
211
|
+
"type": "string | undefined",
|
|
212
|
+
"required": false,
|
|
213
|
+
"origin": "own",
|
|
214
|
+
"description": "Controlled filter text. Usually only needed for remote filtering."
|
|
215
|
+
},
|
|
216
|
+
"onInputChange": {
|
|
217
|
+
"type": "((value: string) => void) | undefined",
|
|
218
|
+
"required": false,
|
|
219
|
+
"origin": "own"
|
|
220
|
+
},
|
|
221
|
+
"hideTags": {
|
|
222
|
+
"type": "boolean | undefined",
|
|
223
|
+
"required": false,
|
|
224
|
+
"origin": "own",
|
|
225
|
+
"description": "Leave the tags out, when the chosen values are already on screen as\nremovable tags — a table's active-filters row. The values are still read\nwith the field, and still marked in the list."
|
|
226
|
+
},
|
|
227
|
+
"emptyLabel": {
|
|
228
|
+
"type": "React.ReactNode",
|
|
229
|
+
"required": false,
|
|
230
|
+
"origin": "own",
|
|
231
|
+
"description": "Shown in place of the list when nothing matches."
|
|
232
|
+
},
|
|
233
|
+
"buttonLabel": {
|
|
234
|
+
"type": "string | undefined",
|
|
235
|
+
"required": false,
|
|
236
|
+
"origin": "own",
|
|
237
|
+
"description": "Accessible label for the disclosure button."
|
|
238
|
+
},
|
|
239
|
+
"name": {
|
|
240
|
+
"type": "string | undefined",
|
|
241
|
+
"required": false,
|
|
242
|
+
"origin": "own",
|
|
243
|
+
"description": "Posts every selected value under this name, for an uncontrolled form."
|
|
244
|
+
},
|
|
245
|
+
"className": {
|
|
246
|
+
"type": "string | undefined",
|
|
247
|
+
"required": false,
|
|
248
|
+
"origin": "own",
|
|
249
|
+
"description": "Class names for the field box (`.ion-input`)."
|
|
250
|
+
},
|
|
251
|
+
"wrapperClassName": {
|
|
252
|
+
"type": "string | undefined",
|
|
253
|
+
"required": false,
|
|
254
|
+
"origin": "own",
|
|
255
|
+
"description": "Class names for the `.ion-field` wrapper."
|
|
256
|
+
},
|
|
257
|
+
"id": {
|
|
258
|
+
"type": "string | undefined",
|
|
259
|
+
"required": false,
|
|
260
|
+
"origin": "own"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"propCounts": {
|
|
264
|
+
"own": 23,
|
|
265
|
+
"aria": 0,
|
|
266
|
+
"dom": 0,
|
|
267
|
+
"other": 0
|
|
268
|
+
}
|
|
269
|
+
}
|
package/dist/meta/Select.json
CHANGED
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"when": "the user may pick more than one",
|
|
30
|
-
"use": "
|
|
30
|
+
"use": "MultiSelect",
|
|
31
|
+
"why": "a native <select multiple> needs Ctrl or Cmd for a second pick and cannot filter; for a handful of options use CheckboxGroup"
|
|
31
32
|
}
|
|
32
33
|
],
|
|
33
34
|
"variants": {
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Toolbar",
|
|
3
|
+
"source": "src/components/Toolbar.tsx",
|
|
4
|
+
"propsType": "ToolbarProps",
|
|
5
|
+
"description": "Toolbar — a row of controls that act on one thing, reached as one stop.\n\nReact Aria's `useToolbar` gives it `role=\"toolbar\"` and `aria-orientation`,\narrow keys between its controls (mirrored in right-to-left), Tab that leaves\nthe whole toolbar rather than walking every button, and focus returned to the\ncontrol used last when Tab brings the user back. A toolbar inside a toolbar\nbecomes a `group`, so nesting one for grouping is safe.\n\nEvery control keeps its own tab stop in the DOM — React Aria moves focus to\nthe first or last control on Tab and lets the browser take it from there —\nso nothing here rewrites a child's `tabIndex`, and a control that mounts\nlater (the bulk actions appearing on selection) needs no registration.\n\nWHAT IT IS NOT FOR\n\nA filter bar. Tab leaves a toolbar in one press, so a search field inside one\nstrands the filters after it: Tab skips them and ← → belong to the caret.\nThat is WAI-ARIA's own caution about text fields in toolbars. A table's\nsearch and filters stay ordinary tab stops; the toolbar is the row of\nactions — the batch bar, a record's actions, an editor's formatting.",
|
|
6
|
+
"import": "import { Toolbar } from 'ionbase-ui';",
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"summary": "A row of controls that act on one thing, reached as one tab stop: arrow keys between the controls, Tab out in one press, and back to the control used last.",
|
|
9
|
+
"useWhen": [
|
|
10
|
+
"a set of actions on one selection or one record — the batch bar over a table, a run's Resume/Pause/Stop, an editor's formatting",
|
|
11
|
+
"the row has enough controls that tabbing through each one costs a keyboard user more than it gives"
|
|
12
|
+
],
|
|
13
|
+
"useInstead": [
|
|
14
|
+
{
|
|
15
|
+
"when": "the row holds a search field or filters",
|
|
16
|
+
"use": "a plain flex row",
|
|
17
|
+
"why": "Tab leaves a toolbar in one press and ← → belong to the caret, so a text field inside strands every filter after it — WAI-ARIA's own caution"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"when": "two or three actions at the top of a page",
|
|
21
|
+
"use": "PageHeader",
|
|
22
|
+
"why": "its `actions` slot is a row of ordinary tab stops, which for a few buttons is what a keyboard user expects"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"when": "the controls are one choice among options",
|
|
26
|
+
"use": "SegmentedControl",
|
|
27
|
+
"why": "that is a radio group with a selected value; a toolbar holds independent actions"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"composition": {
|
|
31
|
+
"order": [
|
|
32
|
+
"Toolbar",
|
|
33
|
+
"Button",
|
|
34
|
+
"Divider",
|
|
35
|
+
"MenuTrigger"
|
|
36
|
+
],
|
|
37
|
+
"note": "Buttons in the order they are read, a vertical Divider between groups, and a MenuTrigger with an icon-only Button last for the overflow. Name it with `aria-label` for what it acts on — \"Bulk actions\", not \"Toolbar\".",
|
|
38
|
+
"example": "<Toolbar aria-label=\"Bulk actions\"><Button size=\"sm\" variant=\"secondary\">Pause</Button><Divider orientation=\"vertical\" /><Button size=\"sm\" variant=\"destructive\">Delete 3</Button></Toolbar>"
|
|
39
|
+
},
|
|
40
|
+
"variants": {
|
|
41
|
+
"orientation": {
|
|
42
|
+
"horizontal": {
|
|
43
|
+
"use": "the default — ← → move between controls; wraps rather than overflowing"
|
|
44
|
+
},
|
|
45
|
+
"vertical": {
|
|
46
|
+
"use": "a side rail of tools — ↑ ↓ move between controls"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"slots": {
|
|
51
|
+
"children": {
|
|
52
|
+
"accepts": "Button, MenuTrigger, SegmentedControl, Divider, Toolbar",
|
|
53
|
+
"note": "a nested Toolbar becomes a named `group`, and the outer toolbar keeps the arrow keys"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"a11y": {
|
|
57
|
+
"role": "toolbar, with aria-orientation",
|
|
58
|
+
"guarantees": [
|
|
59
|
+
"React Aria's `useToolbar`: arrow keys move between controls, skipping disabled ones and separators, and are mirrored in right-to-left",
|
|
60
|
+
"Tab leaves the whole toolbar in one press, and Shift+Tab or Tab back in returns to the control used last",
|
|
61
|
+
"a text field, native select, combobox, slider or radio inside keeps its own arrow keys — the caret moves and the value changes instead of focus jumping",
|
|
62
|
+
"↓ still reaches a MenuTrigger in a horizontal toolbar, so an overflow menu opens as it does anywhere else"
|
|
63
|
+
],
|
|
64
|
+
"requires": [
|
|
65
|
+
"`aria-label` or `aria-labelledby` naming what the toolbar acts on"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"antiPatterns": [
|
|
69
|
+
{
|
|
70
|
+
"dont": "a table's search field and filters inside a Toolbar",
|
|
71
|
+
"why": "Tab from the search field leaves the toolbar, so the filters after it can only be reached by Shift+Tab and arrows"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"dont": "a Toolbar with one button",
|
|
75
|
+
"why": "a single stop gains nothing from arrow keys, and screen readers announce a toolbar the user has to learn"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"dont": "an unnamed Toolbar",
|
|
79
|
+
"why": "\"toolbar\" alone does not say what the buttons act on, and pages with two cannot be told apart"
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"stylesheet": "src/styles/toolbar.css",
|
|
83
|
+
"tokens": [
|
|
84
|
+
"--spacing-4",
|
|
85
|
+
"--spacing-8"
|
|
86
|
+
],
|
|
87
|
+
"props": {
|
|
88
|
+
"aria-label": {
|
|
89
|
+
"type": "string | undefined",
|
|
90
|
+
"required": false,
|
|
91
|
+
"origin": "own",
|
|
92
|
+
"description": "Names the toolbar — \"Bulk actions\", \"Formatting\". Required, or `aria-labelledby`.",
|
|
93
|
+
"tags": {
|
|
94
|
+
"see": "aria-labelledby."
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"aria-labelledby": {
|
|
98
|
+
"type": "string | undefined",
|
|
99
|
+
"required": false,
|
|
100
|
+
"origin": "own",
|
|
101
|
+
"description": "Identifies the element (or elements) that labels the current element.",
|
|
102
|
+
"tags": {
|
|
103
|
+
"see": "aria-describedby."
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"orientation": {
|
|
107
|
+
"type": "ToolbarOrientation | undefined",
|
|
108
|
+
"required": false,
|
|
109
|
+
"origin": "own",
|
|
110
|
+
"description": "Which arrow keys move between controls. Horizontal: ← →; vertical: ↑ ↓.",
|
|
111
|
+
"values": [
|
|
112
|
+
"vertical",
|
|
113
|
+
"horizontal"
|
|
114
|
+
],
|
|
115
|
+
"default": "horizontal"
|
|
116
|
+
},
|
|
117
|
+
"children": {
|
|
118
|
+
"type": "React.ReactNode",
|
|
119
|
+
"required": false,
|
|
120
|
+
"origin": "own",
|
|
121
|
+
"description": "Buttons, MenuTriggers, a SegmentedControl, and a vertical Divider between groups."
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"propCounts": {
|
|
125
|
+
"own": 4,
|
|
126
|
+
"aria": 0,
|
|
127
|
+
"dom": 274,
|
|
128
|
+
"other": 0
|
|
129
|
+
}
|
|
130
|
+
}
|