ionbase-ui 0.51.0 → 0.69.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.
Files changed (174) hide show
  1. package/dist/components/Accordion.d.ts +54 -0
  2. package/dist/components/Accordion.d.ts.map +1 -0
  3. package/dist/components/Accordion.js +68 -0
  4. package/dist/components/Accordion.js.map +1 -0
  5. package/dist/components/AgentActivity.d.ts +2 -1
  6. package/dist/components/AgentActivity.d.ts.map +1 -1
  7. package/dist/components/AgentActivity.js +4 -17
  8. package/dist/components/AgentActivity.js.map +1 -1
  9. package/dist/components/Breadcrumb.d.ts +45 -0
  10. package/dist/components/Breadcrumb.d.ts.map +1 -0
  11. package/dist/components/Breadcrumb.js +31 -0
  12. package/dist/components/Breadcrumb.js.map +1 -0
  13. package/dist/components/Calendar.d.ts +20 -0
  14. package/dist/components/Calendar.d.ts.map +1 -0
  15. package/dist/components/Calendar.js +151 -0
  16. package/dist/components/Calendar.js.map +1 -0
  17. package/dist/components/CalendarPopover.d.ts +26 -0
  18. package/dist/components/CalendarPopover.d.ts.map +1 -0
  19. package/dist/components/CalendarPopover.js +45 -0
  20. package/dist/components/CalendarPopover.js.map +1 -0
  21. package/dist/components/Checkbox.d.ts +2 -1
  22. package/dist/components/Checkbox.d.ts.map +1 -1
  23. package/dist/components/Checkbox.js +4 -2
  24. package/dist/components/Checkbox.js.map +1 -1
  25. package/dist/components/Combobox.d.ts +86 -0
  26. package/dist/components/Combobox.d.ts.map +1 -0
  27. package/dist/components/Combobox.js +261 -0
  28. package/dist/components/Combobox.js.map +1 -0
  29. package/dist/components/DateField.d.ts +15 -0
  30. package/dist/components/DateField.d.ts.map +1 -0
  31. package/dist/components/DateField.js +74 -0
  32. package/dist/components/DateField.js.map +1 -0
  33. package/dist/components/DatePicker.d.ts +81 -0
  34. package/dist/components/DatePicker.d.ts.map +1 -0
  35. package/dist/components/DatePicker.js +102 -0
  36. package/dist/components/DatePicker.js.map +1 -0
  37. package/dist/components/DateRangePicker.d.ts +117 -0
  38. package/dist/components/DateRangePicker.d.ts.map +1 -0
  39. package/dist/components/DateRangePicker.js +152 -0
  40. package/dist/components/DateRangePicker.js.map +1 -0
  41. package/dist/components/Drawer.d.ts +52 -0
  42. package/dist/components/Drawer.d.ts.map +1 -0
  43. package/dist/components/Drawer.js +66 -0
  44. package/dist/components/Drawer.js.map +1 -0
  45. package/dist/components/FileUpload.d.ts +79 -0
  46. package/dist/components/FileUpload.d.ts.map +1 -0
  47. package/dist/components/FileUpload.js +218 -0
  48. package/dist/components/FileUpload.js.map +1 -0
  49. package/dist/components/Header.d.ts +10 -0
  50. package/dist/components/Header.d.ts.map +1 -1
  51. package/dist/components/Header.js +18 -3
  52. package/dist/components/Header.js.map +1 -1
  53. package/dist/components/NumberInput.d.ts +63 -0
  54. package/dist/components/NumberInput.d.ts.map +1 -0
  55. package/dist/components/NumberInput.js +102 -0
  56. package/dist/components/NumberInput.js.map +1 -0
  57. package/dist/components/ProgressBar.d.ts +46 -0
  58. package/dist/components/ProgressBar.d.ts.map +1 -0
  59. package/dist/components/ProgressBar.js +52 -0
  60. package/dist/components/ProgressBar.js.map +1 -0
  61. package/dist/components/PromptInput.d.ts +89 -0
  62. package/dist/components/PromptInput.d.ts.map +1 -0
  63. package/dist/components/PromptInput.js +130 -0
  64. package/dist/components/PromptInput.js.map +1 -0
  65. package/dist/components/SegmentedControl.d.ts +63 -0
  66. package/dist/components/SegmentedControl.d.ts.map +1 -0
  67. package/dist/components/SegmentedControl.js +79 -0
  68. package/dist/components/SegmentedControl.js.map +1 -0
  69. package/dist/components/Select.d.ts +9 -2
  70. package/dist/components/Select.d.ts.map +1 -1
  71. package/dist/components/Select.js.map +1 -1
  72. package/dist/components/Sidebar.d.ts +109 -0
  73. package/dist/components/Sidebar.d.ts.map +1 -0
  74. package/dist/components/Sidebar.js +106 -0
  75. package/dist/components/Sidebar.js.map +1 -0
  76. package/dist/components/Skeleton.d.ts +39 -0
  77. package/dist/components/Skeleton.d.ts.map +1 -0
  78. package/dist/components/Skeleton.js +50 -0
  79. package/dist/components/Skeleton.js.map +1 -0
  80. package/dist/components/Spinner.d.ts +45 -0
  81. package/dist/components/Spinner.d.ts.map +1 -0
  82. package/dist/components/Spinner.js +44 -0
  83. package/dist/components/Spinner.js.map +1 -0
  84. package/dist/components/Stepper.d.ts +73 -0
  85. package/dist/components/Stepper.d.ts.map +1 -0
  86. package/dist/components/Stepper.js +82 -0
  87. package/dist/components/Stepper.js.map +1 -0
  88. package/dist/components/Toggle.d.ts +2 -1
  89. package/dist/components/Toggle.d.ts.map +1 -1
  90. package/dist/components/Toggle.js +4 -2
  91. package/dist/components/Toggle.js.map +1 -1
  92. package/dist/components/ToolCall.d.ts +66 -0
  93. package/dist/components/ToolCall.d.ts.map +1 -0
  94. package/dist/components/ToolCall.js +78 -0
  95. package/dist/components/ToolCall.js.map +1 -0
  96. package/dist/components/agent-status.d.ts +14 -0
  97. package/dist/components/agent-status.d.ts.map +1 -0
  98. package/dist/components/agent-status.js +16 -0
  99. package/dist/components/agent-status.js.map +1 -0
  100. package/dist/components/index.d.ts +33 -0
  101. package/dist/components/index.d.ts.map +1 -1
  102. package/dist/components/index.js +16 -0
  103. package/dist/components/index.js.map +1 -1
  104. package/dist/components/iso-date.d.ts +53 -0
  105. package/dist/components/iso-date.d.ts.map +1 -0
  106. package/dist/components/iso-date.js +102 -0
  107. package/dist/components/iso-date.js.map +1 -0
  108. package/dist/components/resolve-selection.d.ts +36 -0
  109. package/dist/components/resolve-selection.d.ts.map +1 -0
  110. package/dist/components/resolve-selection.js +14 -0
  111. package/dist/components/resolve-selection.js.map +1 -0
  112. package/dist/figma-descriptions.json +255 -145
  113. package/dist/figma-map.json +3205 -1860
  114. package/dist/meta/Accordion.json +123 -0
  115. package/dist/meta/AccordionItem.json +90 -0
  116. package/dist/meta/AgentActivityStep.json +2 -2
  117. package/dist/meta/Breadcrumb.json +87 -0
  118. package/dist/meta/BreadcrumbItem.json +78 -0
  119. package/dist/meta/Checkbox.json +18 -1
  120. package/dist/meta/Combobox.json +265 -0
  121. package/dist/meta/DatePicker.json +270 -0
  122. package/dist/meta/DateRangePicker.json +284 -0
  123. package/dist/meta/Drawer.json +234 -0
  124. package/dist/meta/EmptyState.json +1 -1
  125. package/dist/meta/FileUpload.json +246 -0
  126. package/dist/meta/Header.json +32 -2
  127. package/dist/meta/NumberInput.json +407 -0
  128. package/dist/meta/PhoneInput.json +39 -39
  129. package/dist/meta/Popover.json +3 -3
  130. package/dist/meta/ProgressBar.json +166 -0
  131. package/dist/meta/PromptInput.json +212 -0
  132. package/dist/meta/SegmentedControl.json +217 -0
  133. package/dist/meta/SegmentedControlItem.json +122 -0
  134. package/dist/meta/Select.json +4 -3
  135. package/dist/meta/Sidebar.json +150 -0
  136. package/dist/meta/SidebarItem.json +184 -0
  137. package/dist/meta/SidebarSection.json +131 -0
  138. package/dist/meta/Skeleton.json +120 -0
  139. package/dist/meta/Spinner.json +123 -0
  140. package/dist/meta/Stepper.json +135 -0
  141. package/dist/meta/StepperStep.json +148 -0
  142. package/dist/meta/Tabs.json +5 -0
  143. package/dist/meta/Toggle.json +18 -1
  144. package/dist/meta/ToolCall.json +192 -0
  145. package/dist/meta/Tooltip.json +3 -3
  146. package/dist/meta/components.json +6759 -2705
  147. package/dist/meta/contrast.json +11075 -3098
  148. package/dist/meta/index.json +266 -8
  149. package/dist/meta/patterns/AgentRun.json +27 -0
  150. package/dist/meta/patterns/Form.json +9 -0
  151. package/dist/meta/patterns/PageShell.json +43 -3
  152. package/dist/meta/patterns/Wizard.json +20 -1
  153. package/dist/meta/patterns/index.json +10 -1
  154. package/dist/styles/accordion.css +102 -0
  155. package/dist/styles/breadcrumb.css +72 -0
  156. package/dist/styles/combobox.css +154 -0
  157. package/dist/styles/date-picker.css +420 -0
  158. package/dist/styles/date-range-picker.css +280 -0
  159. package/dist/styles/drawer.css +181 -0
  160. package/dist/styles/empty-state.css +16 -4
  161. package/dist/styles/file-upload.css +236 -0
  162. package/dist/styles/header.css +26 -0
  163. package/dist/styles/index.css +23 -0
  164. package/dist/styles/number-input.css +83 -0
  165. package/dist/styles/progress-bar.css +107 -0
  166. package/dist/styles/prompt-input.css +127 -0
  167. package/dist/styles/segmented-control.css +182 -0
  168. package/dist/styles/sidebar.css +307 -0
  169. package/dist/styles/skeleton.css +76 -0
  170. package/dist/styles/spinner.css +70 -0
  171. package/dist/styles/stepper.css +250 -0
  172. package/dist/styles/tool-call.css +202 -0
  173. package/llms.txt +2 -2
  174. package/package.json +2 -1
@@ -0,0 +1,166 @@
1
+ {
2
+ "name": "ProgressBar",
3
+ "source": "src/components/ProgressBar.tsx",
4
+ "propsType": "ProgressBarProps",
5
+ "description": "ProgressBar — a determinate or indeterminate measure of work.\n\nDETERMINATE AND INDETERMINATE ARE THE SAME COMPONENT ON PURPOSE\n\nWork that starts unmeasurable and becomes measurable is the common case — a\nupload that does not know its size until the first chunk lands. Splitting\nthem into two components would make that transition a swap, which remounts\nthe node and loses the live region with it.\n\nOmitting `value` is what makes it indeterminate. `aria-valuenow` is then\nomitted too, which is exactly what the ARIA spec asks for and is the detail\nhand-rolled progress bars get wrong most often — a bar reporting\n`aria-valuenow=\"0\"` forever tells a screen reader the work is stuck at zero,\nnot that it is unmeasured.\n\nWHY `label` IS REQUIRED\n\n`role=\"progressbar\"` announces a number. Without a name the user hears \"42\npercent\" with no indication of what is at 42 percent, which is worse than\nsilence because it sounds like information.",
6
+ "import": "import { ProgressBar } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "A determinate or indeterminate measure of work, with the name of the work attached. Omitting `value` is what makes it indeterminate.",
9
+ "useWhen": [
10
+ "work reports a fraction complete — an upload, an import, a multi-step job",
11
+ "work will become measurable but is not yet, and the bar should not remount when it does",
12
+ "a quota or usage figure is better read as a bar than a number"
13
+ ],
14
+ "useInstead": [
15
+ {
16
+ "when": "the wait is short and unmeasurable",
17
+ "use": "Spinner",
18
+ "why": "an indeterminate bar occupies a block of layout to say what a spinner says inline"
19
+ },
20
+ {
21
+ "when": "the region is being replaced by content",
22
+ "use": "Skeleton"
23
+ }
24
+ ],
25
+ "variants": {
26
+ "intent": {
27
+ "primary": {
28
+ "use": "the default — work proceeding normally"
29
+ },
30
+ "success": {
31
+ "use": "work that finished, where the bar stays on screen as a record"
32
+ },
33
+ "warning": {
34
+ "use": "a quota approaching its limit",
35
+ "note": "warning describes the VALUE, not a failure of the bar"
36
+ },
37
+ "error": {
38
+ "use": "a quota exceeded, or a job that failed part-way",
39
+ "note": "the bar is still reporting correctly — reserve this for the measured thing being bad"
40
+ }
41
+ },
42
+ "size": {
43
+ "sm": {
44
+ "use": "inside a row or a dense table cell"
45
+ },
46
+ "md": {
47
+ "use": "the default"
48
+ }
49
+ }
50
+ },
51
+ "a11y": {
52
+ "requires": [
53
+ "`label` — it is required, because role=\"progressbar\" announces a number and a number with no noun sounds like information while carrying none",
54
+ "`valueText` when a percentage is not what a person wants read aloud — \"3 of 12 files\""
55
+ ],
56
+ "notes": [
57
+ "indeterminate omits aria-valuenow entirely, as the ARIA spec asks; a bar reporting aria-valuenow=0 forever says the work is stuck rather than unmeasured",
58
+ "value is clamped to 0..max, so a caller cannot render a bar past its own end"
59
+ ]
60
+ },
61
+ "antiPatterns": [
62
+ {
63
+ "dont": "<ProgressBar label=\"Loading\" value={0} /> while the total is unknown",
64
+ "do": "<ProgressBar label=\"Loading\" />",
65
+ "why": "value={0} announces \"0 percent\" indefinitely. Omitting value is how you say \"not measurable yet\""
66
+ },
67
+ {
68
+ "dont": "swapping a Spinner for a ProgressBar once the size is known",
69
+ "do": "one ProgressBar, with `value` appearing when it becomes known",
70
+ "why": "the swap remounts the node and takes the live region with it, so the transition is silent to a screen reader"
71
+ },
72
+ {
73
+ "dont": "intent=\"error\" because the request failed",
74
+ "do": "leave the bar and put the failure in an Alert",
75
+ "why": "intent describes the value being measured. A failed request is not a red measurement, it is a different message"
76
+ }
77
+ ],
78
+ "stylesheet": "src/styles/progress-bar.css",
79
+ "tokens": [
80
+ "--border-width-default",
81
+ "--font-family-sans",
82
+ "--ion-duration-base",
83
+ "--ion-duration-slow",
84
+ "--ion-ease-in-out",
85
+ "--ion-ease-out",
86
+ "--ion-progress-bar-fill",
87
+ "--ion-progress-bar-height",
88
+ "--ion-progress-bar-pct",
89
+ "--radius-full",
90
+ "--spacing-4",
91
+ "--spacing-8",
92
+ "--surface-error",
93
+ "--surface-muted",
94
+ "--surface-primary",
95
+ "--surface-success",
96
+ "--surface-warning",
97
+ "--text-default",
98
+ "--text-secondary",
99
+ "--type-body-sm",
100
+ "--type-body-sm-line-height"
101
+ ],
102
+ "props": {
103
+ "label": {
104
+ "type": "string",
105
+ "required": true,
106
+ "origin": "own",
107
+ "description": "What is progressing. Required — a bare bar announces a number and no noun."
108
+ },
109
+ "value": {
110
+ "type": "number | undefined",
111
+ "required": false,
112
+ "origin": "own",
113
+ "description": "0 to `max`. Omit for an indeterminate bar."
114
+ },
115
+ "max": {
116
+ "type": "number | undefined",
117
+ "required": false,
118
+ "origin": "own",
119
+ "description": "Defaults to 100."
120
+ },
121
+ "intent": {
122
+ "type": "ProgressBarIntent | undefined",
123
+ "required": false,
124
+ "origin": "own",
125
+ "values": [
126
+ "primary",
127
+ "success",
128
+ "warning",
129
+ "error"
130
+ ]
131
+ },
132
+ "size": {
133
+ "type": "ProgressBarSize | undefined",
134
+ "required": false,
135
+ "origin": "own",
136
+ "values": [
137
+ "sm",
138
+ "md"
139
+ ]
140
+ },
141
+ "isLabelVisible": {
142
+ "type": "boolean | undefined",
143
+ "required": false,
144
+ "origin": "own",
145
+ "description": "Show the label as text above the track."
146
+ },
147
+ "isValueVisible": {
148
+ "type": "boolean | undefined",
149
+ "required": false,
150
+ "origin": "own",
151
+ "description": "Show the percentage beside the label. Determinate bars only."
152
+ },
153
+ "valueText": {
154
+ "type": "string | undefined",
155
+ "required": false,
156
+ "origin": "own",
157
+ "description": "Spoken instead of the percentage — \"3 of 12 files\". A percentage is rarely\nthe thing a person wants read aloud."
158
+ }
159
+ },
160
+ "propCounts": {
161
+ "own": 8,
162
+ "aria": 0,
163
+ "dom": 277,
164
+ "other": 0
165
+ }
166
+ }
@@ -0,0 +1,212 @@
1
+ {
2
+ "name": "PromptInput",
3
+ "source": "src/components/PromptInput.tsx",
4
+ "propsType": "PromptInputProps",
5
+ "description": "PromptInput — where a person writes to an agent.\n\nWHY IT IS NOT A TEXTAREA AND A BUTTON\n\nEvery composer is assembled that way, and every one re-implements the same\nfour bugs:\n\n Enter during IME composition sends half a word. Japanese, Chinese and\n Korean input confirm a candidate with Enter; a handler that does not check\n `isComposing` sends the message mid-character. That excludes whole\n languages from the product, silently, and nobody on an English keyboard\n ever sees it.\n\n The prompt is lost when sending fails. Clearing on submit is right —\n waiting for the server makes the composer feel broken — but clearing and\n never restoring destroys what may have been a long message. Uncontrolled,\n a rejected `onSubmit` promise puts the text back.\n\n The stop control lives somewhere else. While a run is going, the send\n button's place is exactly where the user's pointer and attention already\n are, so that is where `AgentStop` goes — the same control, the same\n guarantees, not a second stop button with different ones.\n\n The keyboard contract is invisible. \"Enter sends, Shift+Enter for a new\n line\" is announced through `aria-describedby`, because a screen-reader\n user who presses Enter expecting a newline has just sent a message.\n\nWHAT IT DOES NOT DO\n\nIt does not upload, pick models or render messages. `attachments` and\n`actions` are slots, because what a product attaches and which tools it\noffers are the product's decisions.",
6
+ "import": "import { PromptInput } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "Where a person writes to an agent: a growing text field, a send control that becomes the stop control while a run is going, and slots for attachments and tools.",
9
+ "useWhen": [
10
+ "a chat or assistant surface where the user's message starts a run",
11
+ "a command bar that sends free text to an agent"
12
+ ],
13
+ "useInstead": [
14
+ {
15
+ "when": "the text is a form value submitted with other fields",
16
+ "use": "Textarea",
17
+ "why": "Enter-to-send inside a form fights the form's own submit, and a single field of a larger form is not a composer"
18
+ },
19
+ {
20
+ "when": "the input is a single-line search",
21
+ "use": "Input",
22
+ "why": "a search box does not grow, does not stop runs, and Enter means search rather than send"
23
+ },
24
+ {
25
+ "when": "the run needs a stop control and was not started from this composer",
26
+ "use": "AgentStop",
27
+ "why": "the composer's stop belongs to the run it sent; a run started elsewhere keeps its stop beside its own activity"
28
+ }
29
+ ],
30
+ "variants": {
31
+ "submitKey": {
32
+ "enter": {
33
+ "use": "the default — chat. Enter sends, Shift+Enter is a new line"
34
+ },
35
+ "mod-enter": {
36
+ "use": "long messages, where a stray Enter is expensive. Cmd/Ctrl+Enter sends and Enter is a new line"
37
+ }
38
+ }
39
+ },
40
+ "slots": {
41
+ "actions": {
42
+ "accepts": "Button",
43
+ "note": "attach, a model picker, a tool toggle — at the start of the toolbar. Icon-only buttons need their own aria-label"
44
+ },
45
+ "attachments": {
46
+ "accepts": "Badge",
47
+ "note": "what is attached, above the text. Each removable item needs a remove control named for the file"
48
+ }
49
+ },
50
+ "a11y": {
51
+ "requires": [
52
+ "`label` — the accessible name of the field. A placeholder is not one: it disappears on the first keystroke"
53
+ ],
54
+ "guarantees": [
55
+ "the send key is described through aria-describedby, so a screen-reader user knows Enter sends before pressing it",
56
+ "Enter pressed while an IME is composing never sends — Japanese, Chinese and Korean input confirm candidates with Enter",
57
+ "while `isRunning` with `onStop`, the send control is replaced in place by AgentStop, with its guarantees: it keeps its place while stopping and announces once",
58
+ "focus stays in the field after sending, so the next message can be typed straight away"
59
+ ],
60
+ "notes": [
61
+ "the field stays editable while a run is going; only submitting is refused",
62
+ "uncontrolled, the field clears on send and a rejected onSubmit promise restores the text — unless the user has already started typing something new"
63
+ ]
64
+ },
65
+ "antiPatterns": [
66
+ {
67
+ "dont": "<Textarea> beside a <Button> with an onKeyDown that checks for Enter",
68
+ "do": "<PromptInput label=\"Message\" onSubmit={send} />",
69
+ "why": "the hand-built version sends mid-character for IME users and loses the prompt when sending fails"
70
+ },
71
+ {
72
+ "dont": "a separate AgentStop elsewhere on screen as well as `isRunning` with `onStop`",
73
+ "why": "two stop controls for one run raises the question of whether they do different things"
74
+ },
75
+ {
76
+ "dont": "`isRunning` without `onStop`",
77
+ "why": "the send control disables and nothing replaces it — the user can see the agent working and has no way to stop it"
78
+ },
79
+ {
80
+ "dont": "controlled `value` cleared in onSubmit before the request settles",
81
+ "why": "controlled, restoring the text on failure is the caller's job; clearing early and not restoring loses the message"
82
+ }
83
+ ],
84
+ "stylesheet": "src/styles/prompt-input.css",
85
+ "tokens": [
86
+ "--border-default",
87
+ "--border-disabled",
88
+ "--border-focus",
89
+ "--border-strong",
90
+ "--border-width-default",
91
+ "--font-family-sans",
92
+ "--ion-duration-base",
93
+ "--ion-ease-out",
94
+ "--radius-lg",
95
+ "--spacing-32",
96
+ "--spacing-4",
97
+ "--spacing-8",
98
+ "--surface-default",
99
+ "--surface-disabled",
100
+ "--text-default",
101
+ "--text-disabled",
102
+ "--text-tertiary",
103
+ "--type-body",
104
+ "--type-body-line-height"
105
+ ],
106
+ "props": {
107
+ "label": {
108
+ "type": "string",
109
+ "required": true,
110
+ "origin": "own",
111
+ "description": "The accessible name of the text field. Required, and not a placeholder:\nplaceholder text disappears the moment the user types, and is not reliably\nannounced as a name."
112
+ },
113
+ "value": {
114
+ "type": "string | undefined",
115
+ "required": false,
116
+ "origin": "own",
117
+ "description": "Controlled text. Pass `onChange` with it."
118
+ },
119
+ "defaultValue": {
120
+ "type": "string | undefined",
121
+ "required": false,
122
+ "origin": "own",
123
+ "description": "Uncontrolled starting text."
124
+ },
125
+ "onChange": {
126
+ "type": "((value: string) => void) | undefined",
127
+ "required": false,
128
+ "origin": "own"
129
+ },
130
+ "onSubmit": {
131
+ "type": "((value: string) => void | Promise<unknown>) | undefined",
132
+ "required": false,
133
+ "origin": "own",
134
+ "description": "Fires with the text as typed. Uncontrolled, the field clears straight away;\nif the returned promise rejects, the text is put back — a prompt lost to a\nnetwork error is the one thing a composer must never do."
135
+ },
136
+ "submitKey": {
137
+ "type": "PromptInputSubmitKey | undefined",
138
+ "required": false,
139
+ "origin": "own",
140
+ "description": "Which key sends. `enter` sends on Enter with Shift+Enter for a new line —\na chat. `mod-enter` sends on Cmd/Ctrl+Enter and leaves Enter for new lines —\na composer where messages are long and a stray Enter is expensive.",
141
+ "values": [
142
+ "enter",
143
+ "mod-enter"
144
+ ]
145
+ },
146
+ "isRunning": {
147
+ "type": "boolean | undefined",
148
+ "required": false,
149
+ "origin": "own",
150
+ "description": "A run is in progress. The send control becomes an `AgentStop` in the same\nplace, and submitting is refused. The field stays editable, so the user can\ndraft the next message while the agent works."
151
+ },
152
+ "onStop": {
153
+ "type": "(() => void) | undefined",
154
+ "required": false,
155
+ "origin": "own",
156
+ "description": "Called by the stop control while `isRunning`."
157
+ },
158
+ "isStopping": {
159
+ "type": "boolean | undefined",
160
+ "required": false,
161
+ "origin": "own",
162
+ "description": "Passed to the stop control once the stop has been requested."
163
+ },
164
+ "isDisabled": {
165
+ "type": "boolean | undefined",
166
+ "required": false,
167
+ "origin": "own"
168
+ },
169
+ "minRows": {
170
+ "type": "number | undefined",
171
+ "required": false,
172
+ "origin": "own",
173
+ "description": "Rows the field starts at."
174
+ },
175
+ "maxRows": {
176
+ "type": "number | undefined",
177
+ "required": false,
178
+ "origin": "own",
179
+ "description": "Rows the field grows to before it scrolls."
180
+ },
181
+ "actions": {
182
+ "type": "React.ReactNode",
183
+ "required": false,
184
+ "origin": "own",
185
+ "description": "Controls at the start of the toolbar — attach, a model picker, a tool\ntoggle. Icon-only controls here need their own `aria-label`."
186
+ },
187
+ "attachments": {
188
+ "type": "React.ReactNode",
189
+ "required": false,
190
+ "origin": "own",
191
+ "description": "Rendered above the text — attached files, a quoted message."
192
+ },
193
+ "sendLabel": {
194
+ "type": "string | undefined",
195
+ "required": false,
196
+ "origin": "own",
197
+ "description": "Accessible name of the send control."
198
+ },
199
+ "className": {
200
+ "type": "string | undefined",
201
+ "required": false,
202
+ "origin": "own",
203
+ "description": "Class names for the outer box."
204
+ }
205
+ },
206
+ "propCounts": {
207
+ "own": 16,
208
+ "aria": 0,
209
+ "dom": 284,
210
+ "other": 0
211
+ }
212
+ }
@@ -0,0 +1,217 @@
1
+ {
2
+ "name": "SegmentedControl",
3
+ "source": "src/components/SegmentedControl.tsx",
4
+ "propsType": "SegmentedControlProps",
5
+ "description": "SegmentedControl — pick one of a few options, and see all of them.\n\nNOT TABS, ALTHOUGH IT LOOKS LIKE THE PILL TYPE, and the difference is the\ncontract. Tabs switch between panels: `tablist`, `tab`, `tabpanel`, and each\ntab controls a region. This sets a VALUE — a view mode, a time range, a\nunit — and controls no panel. It is a `radiogroup`, so it has one tab stop,\narrow keys move and select, and it submits with a form like any radio.\n\nBuilt from real radio inputs, visually hidden, not from buttons with\n`aria-pressed`. A row of toggle buttons is a tab stop per option and no\nmutual exclusion that assistive tech can announce; radios give \"2 of 3,\nselected\" for free.\n\nThe look is Tabs' pill track, token for token, so the two cannot drift\napart visually — which is exactly why the intent file spends its words on\nwhen to use which.",
6
+ "import": "import { SegmentedControl } from 'ionbase-ui';",
7
+ "status": "stable",
8
+ "summary": "Pick one of two to five options, all visible at once. A radio group drawn as a segmented track — it sets a value and controls no panel.",
9
+ "useWhen": [
10
+ "switching how the same content is shown — List / Board / Calendar",
11
+ "a short, fixed set of mutually exclusive settings — Day / Week / Month, °C / °F",
12
+ "a filter with a few exclusive choices where seeing every option matters"
13
+ ],
14
+ "useInstead": [
15
+ {
16
+ "when": "each option shows a different panel of content",
17
+ "use": "Tabs",
18
+ "why": "Tabs wires each tab to its panel with tablist/tabpanel semantics. A SegmentedControl controls no region, so a screen-reader user is never told which content changed"
19
+ },
20
+ {
21
+ "when": "there are more than five options, or the labels are long",
22
+ "use": "Select",
23
+ "why": "segments do not wrap; a track that overflows hides the options it exists to show"
24
+ },
25
+ {
26
+ "when": "the choice sits in a form with a visible question and descriptions per option",
27
+ "use": "RadioGroup",
28
+ "why": "same semantics, but room for a label and helper text on each option"
29
+ },
30
+ {
31
+ "when": "the setting is a single on/off",
32
+ "use": "Toggle"
33
+ },
34
+ {
35
+ "when": "the options trigger actions rather than set a value — Bold, Italic",
36
+ "use": "Button",
37
+ "why": "an action is not a selection; a radio group announces a state that does not exist"
38
+ }
39
+ ],
40
+ "composition": {
41
+ "order": [
42
+ "SegmentedControl",
43
+ "SegmentedControlItem"
44
+ ],
45
+ "example": "<SegmentedControl label=\"View\" defaultValue=\"list\"><SegmentedControlItem value=\"list\">List</SegmentedControlItem><SegmentedControlItem value=\"board\">Board</SegmentedControlItem></SegmentedControl>"
46
+ },
47
+ "variants": {
48
+ "size": {
49
+ "sm": {
50
+ "use": "toolbars and dense panel headers"
51
+ },
52
+ "md": {
53
+ "use": "the default"
54
+ },
55
+ "lg": {
56
+ "use": "a primary view switch at the top of a page"
57
+ }
58
+ }
59
+ },
60
+ "slots": {
61
+ "children": {
62
+ "accepts": "SegmentedControlItem",
63
+ "note": "two to five. One item is not a choice; six is a Select"
64
+ }
65
+ },
66
+ "a11y": {
67
+ "role": "radiogroup / radio",
68
+ "requires": [
69
+ "`label` — the group's accessible name. Hidden by default; `showLabel` renders it above the track"
70
+ ],
71
+ "guarantees": [
72
+ "one tab stop for the whole control; arrow keys move and select, skipping disabled segments",
73
+ "built from real radio inputs, so it announces \"2 of 3, selected\" and submits with a form under `name`",
74
+ "a disabled selected segment keeps its selected surface — a disabled control still has a value"
75
+ ],
76
+ "notes": [
77
+ "it looks exactly like Tabs' pill type on purpose, and behaves differently on purpose. Choose by what changes, not by how it looks"
78
+ ]
79
+ },
80
+ "antiPatterns": [
81
+ {
82
+ "dont": "a SegmentedControl that swaps the panel below it",
83
+ "do": "<Tabs type=\"pill\">",
84
+ "why": "the panel relationship is exactly what tabpanel semantics announce; without it the content changes silently"
85
+ },
86
+ {
87
+ "dont": "no defaultValue or value",
88
+ "why": "a segmented track with nothing selected reads as broken, not as a choice still to make"
89
+ },
90
+ {
91
+ "dont": "icon-only segments with no aria-label",
92
+ "do": "<SegmentedControlItem value=\"grid\" icon={<Grid />} aria-label=\"Grid view\" />",
93
+ "why": "each segment is a radio, and an unnamed radio is announced as just \"radio button\""
94
+ }
95
+ ],
96
+ "stylesheet": "src/styles/segmented-control.css",
97
+ "tokens": [
98
+ "--border-width-thick",
99
+ "--font-family-sans",
100
+ "--font-weight-medium",
101
+ "--icon-size-md",
102
+ "--icon-size-sm",
103
+ "--ion-duration-base",
104
+ "--ion-ease-out",
105
+ "--ion-segmented-font-size",
106
+ "--ion-segmented-icon-size",
107
+ "--ion-segmented-item-gap",
108
+ "--ion-segmented-item-height",
109
+ "--ion-segmented-item-padding-x",
110
+ "--ion-segmented-line-height",
111
+ "--radius-lg",
112
+ "--radius-md",
113
+ "--ring-focus",
114
+ "--spacing-12",
115
+ "--spacing-16",
116
+ "--spacing-2",
117
+ "--spacing-20",
118
+ "--spacing-24",
119
+ "--spacing-32",
120
+ "--spacing-4",
121
+ "--spacing-40",
122
+ "--spacing-6",
123
+ "--spacing-8",
124
+ "--surface-default",
125
+ "--surface-hover",
126
+ "--surface-muted",
127
+ "--surface-page",
128
+ "--text-default",
129
+ "--text-disabled",
130
+ "--text-tertiary",
131
+ "--type-body",
132
+ "--type-body-lg",
133
+ "--type-body-lg-line-height",
134
+ "--type-body-line-height",
135
+ "--type-body-sm",
136
+ "--type-body-sm-line-height"
137
+ ],
138
+ "props": {
139
+ "label": {
140
+ "type": "string",
141
+ "required": true,
142
+ "origin": "own",
143
+ "description": "The accessible name of the group. Required: \"List, Board, Calendar\" means\nnothing until something says they are views."
144
+ },
145
+ "showLabel": {
146
+ "type": "boolean | undefined",
147
+ "required": false,
148
+ "origin": "own",
149
+ "description": "Show `label` above the control, as a form field does. Hidden by default.",
150
+ "default": false
151
+ },
152
+ "value": {
153
+ "type": "string | undefined",
154
+ "required": false,
155
+ "origin": "own",
156
+ "description": "Controlled selected value. Pass `onChange` with it."
157
+ },
158
+ "defaultValue": {
159
+ "type": "string | undefined",
160
+ "required": false,
161
+ "origin": "own",
162
+ "description": "Uncontrolled starting value. One option should always be selected — a\nsegmented control with nothing chosen reads as broken, not as \"no choice\"."
163
+ },
164
+ "onChange": {
165
+ "type": "((value: string) => void) | undefined",
166
+ "required": false,
167
+ "origin": "own"
168
+ },
169
+ "size": {
170
+ "type": "SegmentedControlSize | undefined",
171
+ "required": false,
172
+ "origin": "own",
173
+ "values": [
174
+ "sm",
175
+ "md",
176
+ "lg"
177
+ ],
178
+ "default": "md"
179
+ },
180
+ "isFullWidth": {
181
+ "type": "boolean | undefined",
182
+ "required": false,
183
+ "origin": "own",
184
+ "description": "Stretch to the container, with equal-width segments.",
185
+ "default": false
186
+ },
187
+ "isDisabled": {
188
+ "type": "boolean | undefined",
189
+ "required": false,
190
+ "origin": "own",
191
+ "default": false
192
+ },
193
+ "name": {
194
+ "type": "string | undefined",
195
+ "required": false,
196
+ "origin": "own",
197
+ "description": "Submitted with a form under this name."
198
+ },
199
+ "children": {
200
+ "type": "React.ReactNode",
201
+ "required": false,
202
+ "origin": "own",
203
+ "description": "`SegmentedControlItem` elements. Two to five of them."
204
+ },
205
+ "className": {
206
+ "type": "string | undefined",
207
+ "required": false,
208
+ "origin": "own"
209
+ }
210
+ },
211
+ "propCounts": {
212
+ "own": 11,
213
+ "aria": 0,
214
+ "dom": 0,
215
+ "other": 0
216
+ }
217
+ }