@ratio-ui/core 0.1.0 → 0.2.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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -1
  3. package/dist/catalogue.json +1719 -43
  4. package/dist/components/app-shell/AppShell.d.ts +22 -0
  5. package/dist/components/app-shell/AppShell.js +39 -0
  6. package/dist/components/app-shell/context.js +5 -0
  7. package/dist/components/app-shell/index.d.ts +2 -0
  8. package/dist/components/app-shell/index.js +1 -0
  9. package/dist/components/avatar/Avatar.d.ts +22 -0
  10. package/dist/components/avatar/Avatar.js +38 -0
  11. package/dist/components/avatar/index.d.ts +2 -0
  12. package/dist/components/avatar/index.js +1 -0
  13. package/dist/components/button/Button.d.ts +37 -0
  14. package/dist/components/button/Button.js +41 -0
  15. package/dist/components/button/index.d.ts +2 -0
  16. package/dist/components/button/index.js +1 -0
  17. package/dist/components/card/Card.d.ts +33 -0
  18. package/dist/components/card/Card.js +53 -0
  19. package/dist/components/card/index.d.ts +2 -0
  20. package/dist/components/card/index.js +1 -0
  21. package/dist/components/checkbox/Checkbox.d.ts +10 -0
  22. package/dist/components/checkbox/Checkbox.js +28 -0
  23. package/dist/components/checkbox/index.d.ts +2 -0
  24. package/dist/components/checkbox/index.js +1 -0
  25. package/dist/components/data-table/DataTable.d.ts +65 -0
  26. package/dist/components/data-table/DataTable.js +141 -0
  27. package/dist/components/data-table/index.d.ts +2 -0
  28. package/dist/components/data-table/index.js +1 -0
  29. package/dist/components/definition/Definition.d.ts +14 -0
  30. package/dist/components/definition/Definition.js +29 -0
  31. package/dist/components/definition/index.d.ts +2 -0
  32. package/dist/components/definition/index.js +1 -0
  33. package/dist/components/dialog/Dialog.d.ts +44 -0
  34. package/dist/components/dialog/Dialog.js +79 -0
  35. package/dist/components/dialog/index.d.ts +2 -0
  36. package/dist/components/dialog/index.js +1 -0
  37. package/dist/components/dropdown-menu/DropdownMenu.d.ts +73 -0
  38. package/dist/components/dropdown-menu/DropdownMenu.js +116 -0
  39. package/dist/components/dropdown-menu/index.d.ts +2 -0
  40. package/dist/components/dropdown-menu/index.js +1 -0
  41. package/dist/components/empty-state/EmptyState.d.ts +15 -0
  42. package/dist/components/empty-state/EmptyState.js +30 -0
  43. package/dist/components/empty-state/index.d.ts +2 -0
  44. package/dist/components/empty-state/index.js +1 -0
  45. package/dist/components/filter-tabs/FilterTabs.d.ts +28 -0
  46. package/dist/components/filter-tabs/FilterTabs.js +37 -0
  47. package/dist/components/filter-tabs/index.d.ts +2 -0
  48. package/dist/components/filter-tabs/index.js +1 -0
  49. package/dist/components/global-search/GlobalSearch.d.ts +51 -0
  50. package/dist/components/global-search/GlobalSearch.js +144 -0
  51. package/dist/components/global-search/index.d.ts +2 -0
  52. package/dist/components/global-search/index.js +1 -0
  53. package/dist/components/icon/registry.d.ts +3 -2
  54. package/dist/components/icon/registry.js +53 -1
  55. package/dist/components/input/Input.d.ts +19 -0
  56. package/dist/components/input/Input.js +26 -0
  57. package/dist/components/input/index.d.ts +2 -0
  58. package/dist/components/input/index.js +1 -0
  59. package/dist/components/input-group/InputGroup.d.ts +42 -0
  60. package/dist/components/input-group/InputGroup.js +83 -0
  61. package/dist/components/input-group/index.d.ts +2 -0
  62. package/dist/components/input-group/index.js +1 -0
  63. package/dist/components/kbd/Kbd.d.ts +17 -0
  64. package/dist/components/kbd/Kbd.js +21 -0
  65. package/dist/components/kbd/index.d.ts +2 -0
  66. package/dist/components/kbd/index.js +1 -0
  67. package/dist/components/link-provider/LinkProvider.d.ts +29 -0
  68. package/dist/components/link-provider/LinkProvider.js +15 -0
  69. package/dist/components/link-provider/RatioLink.js +18 -0
  70. package/dist/components/link-provider/context.js +4 -0
  71. package/dist/components/link-provider/index.d.ts +2 -0
  72. package/dist/components/link-provider/index.js +1 -0
  73. package/dist/components/metric-strip/MetricStrip.d.ts +23 -0
  74. package/dist/components/metric-strip/MetricStrip.js +39 -0
  75. package/dist/components/metric-strip/index.d.ts +2 -0
  76. package/dist/components/metric-strip/index.js +1 -0
  77. package/dist/components/page-header/PageHeader.d.ts +37 -0
  78. package/dist/components/page-header/PageHeader.js +62 -0
  79. package/dist/components/page-header/index.d.ts +2 -0
  80. package/dist/components/page-header/index.js +1 -0
  81. package/dist/components/pagination/Pagination.d.ts +18 -0
  82. package/dist/components/pagination/Pagination.js +59 -0
  83. package/dist/components/pagination/index.d.ts +2 -0
  84. package/dist/components/pagination/index.js +1 -0
  85. package/dist/components/popover/Popover.d.ts +27 -0
  86. package/dist/components/popover/Popover.js +47 -0
  87. package/dist/components/popover/index.d.ts +2 -0
  88. package/dist/components/popover/index.js +1 -0
  89. package/dist/components/search-field/SearchField.d.ts +23 -0
  90. package/dist/components/search-field/SearchField.js +66 -0
  91. package/dist/components/search-field/index.d.ts +2 -0
  92. package/dist/components/search-field/index.js +1 -0
  93. package/dist/components/side-nav/SideNav.d.ts +64 -0
  94. package/dist/components/side-nav/SideNav.js +194 -0
  95. package/dist/components/side-nav/index.d.ts +2 -0
  96. package/dist/components/side-nav/index.js +1 -0
  97. package/dist/components/skeleton/Skeleton.d.ts +13 -0
  98. package/dist/components/skeleton/Skeleton.js +21 -0
  99. package/dist/components/skeleton/index.d.ts +2 -0
  100. package/dist/components/skeleton/index.js +1 -0
  101. package/dist/components/spinner/Spinner.d.ts +11 -0
  102. package/dist/components/spinner/Spinner.js +16 -0
  103. package/dist/components/spinner/index.d.ts +2 -0
  104. package/dist/components/spinner/index.js +1 -0
  105. package/dist/components/status-pill/StatusPill.d.ts +14 -0
  106. package/dist/components/status-pill/StatusPill.js +19 -0
  107. package/dist/components/status-pill/index.d.ts +2 -0
  108. package/dist/components/status-pill/index.js +1 -0
  109. package/dist/components/table/Table.d.ts +46 -0
  110. package/dist/components/table/Table.js +87 -0
  111. package/dist/components/table/index.d.ts +2 -0
  112. package/dist/components/table/index.js +1 -0
  113. package/dist/components/table-loader/TableLoader.d.ts +31 -0
  114. package/dist/components/table-loader/TableLoader.js +35 -0
  115. package/dist/components/table-loader/index.d.ts +2 -0
  116. package/dist/components/table-loader/index.js +1 -0
  117. package/dist/components/tabs/Tabs.d.ts +23 -0
  118. package/dist/components/tabs/Tabs.js +41 -0
  119. package/dist/components/tabs/index.d.ts +2 -0
  120. package/dist/components/tabs/index.js +1 -0
  121. package/dist/components/toaster/Toaster.d.ts +32 -0
  122. package/dist/components/toaster/Toaster.js +89 -0
  123. package/dist/components/toaster/index.d.ts +2 -0
  124. package/dist/components/toaster/index.js +1 -0
  125. package/dist/components/tooltip/Tooltip.d.ts +18 -0
  126. package/dist/components/tooltip/Tooltip.js +35 -0
  127. package/dist/components/tooltip/index.d.ts +2 -0
  128. package/dist/components/tooltip/index.js +1 -0
  129. package/dist/components/top-bar/StoreSwitcher.d.ts +26 -0
  130. package/dist/components/top-bar/StoreSwitcher.js +48 -0
  131. package/dist/components/top-bar/TopBar.d.ts +24 -0
  132. package/dist/components/top-bar/TopBar.js +53 -0
  133. package/dist/components/top-bar/index.d.ts +3 -0
  134. package/dist/components/top-bar/index.js +3 -0
  135. package/dist/index.d.ts +65 -1
  136. package/dist/index.js +35 -2
  137. package/dist/lib/cn.js +32 -27
  138. package/dist/lib/format.d.ts +31 -0
  139. package/dist/lib/format.js +65 -0
  140. package/dist/styles.css +1 -1
  141. package/package.json +129 -4
@@ -1,6 +1,814 @@
1
1
  {
2
- "version": "0.1.0",
2
+ "version": "0.2.0",
3
3
  "components": [
4
+ {
5
+ "name": "AppShell",
6
+ "import": "import { AppShell } from '@ratio-ui/core'",
7
+ "description": "The frame of every page: the side nav at the left, the top bar above and the content below it. It fills the height of its container, so the app gives it a height and the content scrolls beside the side nav and under the top bar. The parts are slots, so the shell does not depend on their components. On a phone the side nav is a drawer: the menu button in TopBar opens it, and Escape or choosing a row closes it.",
8
+ "props": [
9
+ {
10
+ "name": "sideNav",
11
+ "type": "ReactNode",
12
+ "required": false,
13
+ "description": "The side navigation, for example SideNav. It sits at the left of the top bar and the content."
14
+ },
15
+ {
16
+ "name": "topBar",
17
+ "type": "ReactNode",
18
+ "required": false,
19
+ "description": "The top bar, for example TopBar. It spans the width beside the side nav."
20
+ },
21
+ {
22
+ "name": "navOpen",
23
+ "type": "boolean",
24
+ "required": false,
25
+ "description": "Whether the side navigation drawer is open on a phone. Set it with onNavOpenChange to control the drawer."
26
+ },
27
+ {
28
+ "name": "defaultNavOpen",
29
+ "type": "boolean",
30
+ "required": false,
31
+ "default": "false",
32
+ "description": "Whether the drawer starts open on a phone when navOpen is not set. Defaults to false."
33
+ },
34
+ {
35
+ "name": "onNavOpenChange",
36
+ "type": "((open: boolean) => void)",
37
+ "required": false,
38
+ "description": "Called when the drawer asks to open or close on a phone: the menu button in TopBar, Escape, or choosing a row."
39
+ },
40
+ {
41
+ "name": "children",
42
+ "type": "ReactNode",
43
+ "required": false,
44
+ "description": "The page content, centred in a column that is 1152px wide at most."
45
+ }
46
+ ],
47
+ "usage": {
48
+ "whenToUse": "- The frame of an admin page: side nav, top bar and content\n- Content that should be at most 1152px wide and centred",
49
+ "whenNotToUse": "- A page without the admin chrome, such as sign in\n- Spacing inside the content: use Stack",
50
+ "example": "<AppShell sideNav={<SideNav />} topBar={<TopBar />}>\n <PageHeader title=\"Products\" />\n</AppShell>"
51
+ }
52
+ },
53
+ {
54
+ "name": "Avatar",
55
+ "import": "import { Avatar } from '@ratio-ui/core'",
56
+ "description": "A picture or the initials of a person or a store: round for people, square for stores.",
57
+ "props": [
58
+ {
59
+ "name": "name",
60
+ "type": "string",
61
+ "required": true,
62
+ "description": "The full name of the person or store. It is the accessible label, and its first letters are the initials."
63
+ },
64
+ {
65
+ "name": "src",
66
+ "type": "string",
67
+ "required": false,
68
+ "description": "The address of a picture. The initials show until it loads and when it fails."
69
+ },
70
+ {
71
+ "name": "shape",
72
+ "type": "\"round\" | \"square\"",
73
+ "required": false,
74
+ "default": "round",
75
+ "description": "Round for a person, square for a store. Defaults to round."
76
+ },
77
+ {
78
+ "name": "size",
79
+ "type": "\"default\" | \"lg\" | \"sm\"",
80
+ "required": false,
81
+ "default": "default",
82
+ "description": "Defaults to default, which is 32px."
83
+ },
84
+ {
85
+ "name": "ref",
86
+ "type": "Ref<HTMLSpanElement>",
87
+ "required": false,
88
+ "description": "Receives the rendered element."
89
+ }
90
+ ],
91
+ "usage": {
92
+ "whenToUse": "- The identity of a person (round) or a store (square), in the top bar, account menu or a list\n- A picture with the initials as fallback while it loads or when it fails",
93
+ "whenNotToUse": "- A generic icon or logo that is not an identity: use Icon\n- A status marker: use StatusPill",
94
+ "example": "<Avatar name=\"My Store\" shape=\"square\" />\n<Avatar name=\"Kavya Singh\" />"
95
+ }
96
+ },
97
+ {
98
+ "name": "Button",
99
+ "import": "import { Button } from '@ratio-ui/core'",
100
+ "description": "The design system's 28px button in five looks: main, secondary, ghost, link and destructive. Hover and pressed change the fill only, disabled is 50% opacity and the colours fade in 120ms. Put an Icon beside the label, or use iconOnly with an aria-label. It has no size prop: 28px is the only height.",
101
+ "props": [
102
+ {
103
+ "name": "iconOnly",
104
+ "type": "boolean",
105
+ "required": false,
106
+ "default": "false",
107
+ "description": "Draws a 28px square for one icon. Needs aria-label, because there is no text to name the button."
108
+ },
109
+ {
110
+ "name": "children",
111
+ "type": "ReactNode",
112
+ "required": false,
113
+ "description": "The label, optionally with an Icon beside it. One Icon."
114
+ },
115
+ {
116
+ "name": "variant",
117
+ "type": "\"destructive\" | \"ghost\" | \"link\" | \"main\" | \"secondary\"",
118
+ "required": false,
119
+ "default": "main",
120
+ "description": "The look of the button. Defaults to main."
121
+ },
122
+ {
123
+ "name": "ref",
124
+ "type": "Ref<HTMLButtonElement>",
125
+ "required": false,
126
+ "description": "Gives the button back to the caller's ref."
127
+ },
128
+ {
129
+ "name": "aria-label",
130
+ "type": "string",
131
+ "required": true,
132
+ "description": "The accessible name of an icon-only button."
133
+ }
134
+ ],
135
+ "usage": {
136
+ "whenToUse": "- The main action of a view, once per page header or table toolbar: variant main\n- A second action beside it, such as Export: variant secondary\n- Quiet actions like Cancel: ghost. Navigation inside running text or a card: link\n- A delete or remove action: destructive, followed by a confirm step\n- A single icon action, such as add or close: iconOnly with an aria-label",
137
+ "whenNotToUse": "- A choice among options that filters a list: use FilterTabs\n- Going to another page from a menu: use the menu's own items\n- A different height: Button is 28px only, with no size prop\n- Styling: Button takes no `className` and no `style`",
138
+ "example": "<Stack gap={8}>\n <Button><Icon name=\"plus\" />Add customer</Button>\n <Button variant=\"secondary\">Export</Button>\n <Button iconOnly variant=\"ghost\" aria-label=\"Add\"><Icon name=\"plus\" /></Button>\n</Stack>"
139
+ }
140
+ },
141
+ {
142
+ "name": "Card",
143
+ "import": "import { Card } from '@ratio-ui/core'",
144
+ "description": "A container for a panel of content: a 1px hairline border on white, no shadow and 24px of padding. The design system has no cards, so this is only the hairline container.",
145
+ "props": [],
146
+ "usage": {
147
+ "whenToUse": "- A panel of related content that needs a boundary, such as a settings group or a metric with its notes\n- With a title in B4 and, optionally, a description, content and a footer of actions",
148
+ "whenNotToUse": "- Page content that needs no boundary: it sits directly on white\n- A menu or popup: those have no shadow either, but they are not cards",
149
+ "example": "<Card>\n <CardHeader>\n <CardTitle>Payout account</CardTitle>\n <CardDescription>Where settlements are paid</CardDescription>\n </CardHeader>\n <CardContent>HDFC ending 4021</CardContent>\n</Card>"
150
+ },
151
+ "parts": [
152
+ {
153
+ "name": "CardHeader",
154
+ "import": "import { CardHeader } from '@ratio-ui/core'",
155
+ "description": "The top of a card: its title and description, stacked.",
156
+ "props": []
157
+ },
158
+ {
159
+ "name": "CardTitle",
160
+ "import": "import { CardTitle } from '@ratio-ui/core'",
161
+ "description": "The title of a card, set in the B4 text style.",
162
+ "props": [
163
+ {
164
+ "name": "as",
165
+ "type": "\"div\" | \"h2\" | \"h3\" | \"h4\"",
166
+ "required": false,
167
+ "default": "h3",
168
+ "description": "The heading element. Defaults to h3."
169
+ }
170
+ ]
171
+ },
172
+ {
173
+ "name": "CardDescription",
174
+ "import": "import { CardDescription } from '@ratio-ui/core'",
175
+ "description": "A line under the title that says what the card holds.",
176
+ "props": []
177
+ },
178
+ {
179
+ "name": "CardContent",
180
+ "import": "import { CardContent } from '@ratio-ui/core'",
181
+ "description": "The body of a card.",
182
+ "props": []
183
+ },
184
+ {
185
+ "name": "CardFooter",
186
+ "import": "import { CardFooter } from '@ratio-ui/core'",
187
+ "description": "The foot of a card, a row for its actions.",
188
+ "props": []
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "name": "Checkbox",
194
+ "import": "import { Checkbox } from '@ratio-ui/core'",
195
+ "description": "A 14px box with a 4px radius, drawn with a hairline ring and filled in the primary colour when checked. Set indeterminate for a select-all that stands for some rows. An invisible 24px area around the box takes the tap.",
196
+ "props": [],
197
+ "usage": {
198
+ "whenToUse": "- A row selector in a table or list, one box per row\n- A select-all in a table header: set `indeterminate` while only some rows are selected\n- A yes or no option in a form, or a list of channels where several can be on",
199
+ "whenNotToUse": "- A setting that applies at once: use a switch\n- One choice among several: use radio buttons or FilterTabs\n- Styling: Checkbox takes no `className` and no `style`",
200
+ "example": "<Checkbox aria-label=\"Select row\" checked={selected} onCheckedChange={setSelected} />"
201
+ }
202
+ },
203
+ {
204
+ "name": "DataTable",
205
+ "import": "import { DataTable } from '@ratio-ui/core'",
206
+ "description": "A data table on Table with TanStack Table v9: a selection column, server-side sorting and paging, alignment, one-line truncation, row links, and empty and loading slots. The parent owns the state (sort, page, selection) and passes it in. On phones it keeps the columns marked essential and hides the rest.",
207
+ "props": [
208
+ {
209
+ "name": "columns",
210
+ "type": "DataTableColumn<T>[]",
211
+ "required": true,
212
+ "description": "The columns, in order."
213
+ },
214
+ {
215
+ "name": "data",
216
+ "type": "T[]",
217
+ "required": true,
218
+ "description": "The rows of the page on show, already sorted by the parent."
219
+ },
220
+ {
221
+ "name": "getRowId",
222
+ "type": "(row: T) => string",
223
+ "required": true,
224
+ "description": "A stable id for a row."
225
+ },
226
+ {
227
+ "name": "selectedIds",
228
+ "type": "string[]",
229
+ "required": false,
230
+ "description": "Ids of the selected rows. Pass it with `onSelectedIdsChange` to show a checkbox column."
231
+ },
232
+ {
233
+ "name": "onSelectedIdsChange",
234
+ "type": "((ids: string[]) => void)",
235
+ "required": false,
236
+ "description": "Called with the ids that are selected after a change."
237
+ },
238
+ {
239
+ "name": "getRowLabel",
240
+ "type": "((row: T) => string)",
241
+ "required": false,
242
+ "description": "The label of one row's checkbox: \"RYZE - 2mg\", which the table prefixes with \"Select \". Defaults to \"Select row N\"."
243
+ },
244
+ {
245
+ "name": "sort",
246
+ "type": "DataTableSort | null",
247
+ "required": false,
248
+ "default": "null",
249
+ "description": "The sort the parent applied, or null for none."
250
+ },
251
+ {
252
+ "name": "onSortChange",
253
+ "type": "((sort: DataTableSort | null) => void)",
254
+ "required": false,
255
+ "description": "Called with the next sort, or null to clear it. The parent sorts and passes new data."
256
+ },
257
+ {
258
+ "name": "page",
259
+ "type": "number",
260
+ "required": false,
261
+ "description": "The page on show, counting from 1. Pass with `pageSize`, `total` and `onPageChange` to show the footer."
262
+ },
263
+ {
264
+ "name": "pageSize",
265
+ "type": "number",
266
+ "required": false,
267
+ "description": "How many results one page holds."
268
+ },
269
+ {
270
+ "name": "total",
271
+ "type": "number",
272
+ "required": false,
273
+ "description": "How many results there are in all, across every page."
274
+ },
275
+ {
276
+ "name": "onPageChange",
277
+ "type": "((page: number) => void)",
278
+ "required": false,
279
+ "description": "Called with the page to show."
280
+ },
281
+ {
282
+ "name": "rowHref",
283
+ "type": "((row: T) => string)",
284
+ "required": false,
285
+ "description": "Where a row leads. The first data cell of the row becomes a link to it. On a router, LinkProvider draws it with the app's own link."
286
+ },
287
+ {
288
+ "name": "empty",
289
+ "type": "ReactNode",
290
+ "required": false,
291
+ "description": "Shown in place of the rows when there are none and the table is not loading. Usually an EmptyState."
292
+ },
293
+ {
294
+ "name": "loading",
295
+ "type": "ReactNode",
296
+ "required": false,
297
+ "description": "Shown in place of the whole table while loading, usually a TableLoader. A falsy value shows the table."
298
+ }
299
+ ],
300
+ "usage": {
301
+ "whenToUse": "- A list of records with selection, server-side sorting and paging: products, variants, inventory\n- A first load: pass a TableLoader as `loading` and it replaces the table\n- Rows that link to a detail page, with an empty and a loading state\n- Phones: mark the columns a list needs as `essential` and the rest are hidden",
302
+ "whenNotToUse": "- Static content composed by hand: use Table\n- Client-side sorting or paging: the parent owns the sort and the page, and passes already sorted data\n- Styling: it takes no `className` and no `style`",
303
+ "example": "<DataTable\n aria-label=\"Products\"\n columns={[\n { id: 'name', header: 'Product', cell: (p) => p.name, essential: true, sortable: true, truncate: true },\n { id: 'stock', header: 'Stock', cell: (p) => p.stock, numeric: true },\n ]}\n data={products}\n getRowId={(p) => p.id}\n rowHref={(p) => `/products/${p.id}`}\n sort={sort}\n onSortChange={setSort}\n page={page}\n pageSize={20}\n total={84}\n onPageChange={setPage}\n empty=\"No products match this filter\"\n/>"
304
+ }
305
+ },
306
+ {
307
+ "name": "Definition",
308
+ "import": "import { Definition } from '@ratio-ui/core'",
309
+ "description": "A term that explains itself. It is a span with the button role and a dotted underline, so it takes the font of the text around it, so keyboard users reach it with Tab and open it with Enter or Space, touch users open it with a tap, and mouse users with hover or click. It does not use the browser's own title tooltip, which keyboard and touch users cannot open. Escape closes it.",
310
+ "props": [
311
+ {
312
+ "name": "children",
313
+ "type": "ReactNode",
314
+ "required": true,
315
+ "description": "The term, shown in the text with a dotted underline."
316
+ },
317
+ {
318
+ "name": "definition",
319
+ "type": "ReactNode",
320
+ "required": true,
321
+ "description": "The explanation shown when the term is opened. Keep it to a sentence."
322
+ }
323
+ ],
324
+ "usage": {
325
+ "whenToUse": "- A term in a heading or label that needs a one-sentence explanation, such as a metric title\n- Where keyboard and touch users must be able to read the explanation: it opens with Tab and Enter, with a tap, and on hover",
326
+ "whenNotToUse": "- An icon-only hint: use Tooltip\n- Long or interactive content: use Popover\n- Text that the task cannot be finished without: show it on the screen",
327
+ "example": "<Definition definition=\"Gross merchandise value, before returns\">GMV</Definition>"
328
+ }
329
+ },
330
+ {
331
+ "name": "Dialog",
332
+ "import": "import { Dialog } from '@ratio-ui/core'",
333
+ "description": "The root of a dialog. It holds the open state and renders nothing itself. Pass `open` and `onOpenChange` to control it, or `defaultOpen` to leave it uncontrolled. The page behind is inert while it is open, with no scrim over it.",
334
+ "props": [],
335
+ "usage": {
336
+ "whenToUse": "- A short task that needs the user's full attention before going back to the page, such as adding a product or defining a field\n- A confirm step before a destructive action\n- Work that fits in 480px: the panel is fixed at that width and has no scrim",
337
+ "whenNotToUse": "- A message after an action: use toast()\n- A long form or a multi-step flow: give it a page\n- Help text on a control: use a Tooltip or Popover",
338
+ "example": "<Dialog>\n <DialogTrigger>Add product</DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Add product</DialogTitle>\n <DialogDescription>Fill in the basics</DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <DialogClose>Cancel</DialogClose>\n </DialogFooter>\n </DialogContent>\n</Dialog>"
339
+ },
340
+ "parts": [
341
+ {
342
+ "name": "DialogClose",
343
+ "import": "import { DialogClose } from '@ratio-ui/core'",
344
+ "description": "A button that closes the dialog, such as Cancel in the footer.",
345
+ "props": [
346
+ {
347
+ "name": "render",
348
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogCloseState>",
349
+ "required": false,
350
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
351
+ }
352
+ ]
353
+ },
354
+ {
355
+ "name": "DialogContent",
356
+ "import": "import { DialogContent } from '@ratio-ui/core'",
357
+ "description": "The panel: 480px wide, white, drawn with the ring outline and no shadow, with no scrim behind it. Put DialogHeader, the form and DialogFooter inside.",
358
+ "props": [
359
+ {
360
+ "name": "showCloseButton",
361
+ "type": "boolean",
362
+ "required": false,
363
+ "default": "true",
364
+ "description": "Whether the corner close button shows. Defaults to true."
365
+ },
366
+ {
367
+ "name": "closeLabel",
368
+ "type": "string",
369
+ "required": false,
370
+ "default": "Close",
371
+ "description": "The accessible name of the corner close button. Defaults to Close."
372
+ },
373
+ {
374
+ "name": "render",
375
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogPopupState>",
376
+ "required": false,
377
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
378
+ }
379
+ ]
380
+ },
381
+ {
382
+ "name": "DialogDescription",
383
+ "import": "import { DialogDescription } from '@ratio-ui/core'",
384
+ "description": "One line that says what the dialog is for, in subdued body text.",
385
+ "props": [
386
+ {
387
+ "name": "render",
388
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogDescriptionState>",
389
+ "required": false,
390
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
391
+ }
392
+ ]
393
+ },
394
+ {
395
+ "name": "DialogFooter",
396
+ "import": "import { DialogFooter } from '@ratio-ui/core'",
397
+ "description": "The row of actions at the bottom, aligned to the end.",
398
+ "props": []
399
+ },
400
+ {
401
+ "name": "DialogHeader",
402
+ "import": "import { DialogHeader } from '@ratio-ui/core'",
403
+ "description": "The title and description, stacked.",
404
+ "props": []
405
+ },
406
+ {
407
+ "name": "DialogTitle",
408
+ "import": "import { DialogTitle } from '@ratio-ui/core'",
409
+ "description": "The dialog's name, in the b4 style. It names the dialog for assistive technology.",
410
+ "props": [
411
+ {
412
+ "name": "render",
413
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTitleState>",
414
+ "required": false,
415
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
416
+ }
417
+ ]
418
+ },
419
+ {
420
+ "name": "DialogTrigger",
421
+ "import": "import { DialogTrigger } from '@ratio-ui/core'",
422
+ "description": "The button that opens the dialog.",
423
+ "props": [
424
+ {
425
+ "name": "render",
426
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, DialogTriggerState>",
427
+ "required": false,
428
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
429
+ }
430
+ ]
431
+ }
432
+ ]
433
+ },
434
+ {
435
+ "name": "DropdownMenu",
436
+ "import": "import { DropdownMenu } from '@ratio-ui/core'",
437
+ "description": "The root of a menu of actions that opens from a trigger. Compose it with DropdownMenuTrigger and DropdownMenuContent.",
438
+ "props": [],
439
+ "usage": {
440
+ "whenToUse": "- A short list of actions behind one control: \"More actions\", a store menu, a \"…\" button\n- Actions that belong in groups, with a label per group, a hairline between groups, or a submenu\n- Items that show a key hint, a check (on or off) or one choice out of a few",
441
+ "whenNotToUse": "- Choosing a value for a form field: use a select\n- Content that is not a list of actions, such as notifications or a form: use Popover\n- A destructive action without a confirm step: the destructive item only colours it\n- Styling: the parts take no `className` and no `style`, and the menu has no shadow",
442
+ "example": "<DropdownMenu>\n <DropdownMenuTrigger>More actions</DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuGroup>\n <DropdownMenuLabel>Product</DropdownMenuLabel>\n <DropdownMenuItem>\n Edit\n <DropdownMenuShortcut>⌘E</DropdownMenuShortcut>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n <DropdownMenuSeparator />\n <DropdownMenuItem variant=\"destructive\">Delete</DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>"
443
+ },
444
+ "parts": [
445
+ {
446
+ "name": "DropdownMenuCheckboxItem",
447
+ "import": "import { DropdownMenuCheckboxItem } from '@ratio-ui/core'",
448
+ "description": "An item that toggles an option. A check shows at its start while it is on and keeps the menu open, so several options can be changed in one visit.",
449
+ "props": [
450
+ {
451
+ "name": "render",
452
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuCheckboxItemState>",
453
+ "required": false,
454
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
455
+ }
456
+ ]
457
+ },
458
+ {
459
+ "name": "DropdownMenuContent",
460
+ "import": "import { DropdownMenuContent } from '@ratio-ui/core'",
461
+ "description": "The menu panel: a white popup with a hairline ring, 8px corners and no shadow. It opens in a portal.",
462
+ "props": [
463
+ {
464
+ "name": "side",
465
+ "type": "\"bottom\" | \"inline-end\" | \"inline-start\" | \"left\" | \"right\" | \"top\"",
466
+ "required": false,
467
+ "default": "bottom",
468
+ "description": "Which side of the trigger the menu opens on. Defaults to bottom."
469
+ },
470
+ {
471
+ "name": "align",
472
+ "type": "\"center\" | \"end\" | \"start\"",
473
+ "required": false,
474
+ "default": "start",
475
+ "description": "How the menu lines up with the trigger along that side. Defaults to start."
476
+ },
477
+ {
478
+ "name": "sideOffset",
479
+ "type": "number",
480
+ "required": false,
481
+ "default": "4",
482
+ "description": "The gap between the trigger and the menu in px. Defaults to 4."
483
+ },
484
+ {
485
+ "name": "alignOffset",
486
+ "type": "number",
487
+ "required": false,
488
+ "default": "0",
489
+ "description": "A shift along the side in px. Defaults to 0."
490
+ },
491
+ {
492
+ "name": "render",
493
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuPopupState>",
494
+ "required": false,
495
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
496
+ }
497
+ ]
498
+ },
499
+ {
500
+ "name": "DropdownMenuGroup",
501
+ "import": "import { DropdownMenuGroup } from '@ratio-ui/core'",
502
+ "description": "Groups items under one DropdownMenuLabel, so assistive technology announces the label with the group.",
503
+ "props": [
504
+ {
505
+ "name": "render",
506
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuGroupState>",
507
+ "required": false,
508
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
509
+ }
510
+ ]
511
+ },
512
+ {
513
+ "name": "DropdownMenuItem",
514
+ "import": "import { DropdownMenuItem } from '@ratio-ui/core'",
515
+ "description": "One action in the menu. It runs onClick and closes the menu; disabled is drawn at half opacity and cannot be chosen.",
516
+ "props": [
517
+ {
518
+ "name": "variant",
519
+ "type": "\"default\" | \"destructive\"",
520
+ "required": false,
521
+ "default": "default",
522
+ "description": "What the item means. destructive is drawn in the destructive token; a delete action also needs a confirm step. Defaults to default."
523
+ },
524
+ {
525
+ "name": "render",
526
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuItemState>",
527
+ "required": false,
528
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
529
+ }
530
+ ]
531
+ },
532
+ {
533
+ "name": "DropdownMenuLabel",
534
+ "import": "import { DropdownMenuLabel } from '@ratio-ui/core'",
535
+ "description": "The caption of a group, in the design system's label style: 12px, uppercase, subdued. Place it inside DropdownMenuGroup.",
536
+ "props": [
537
+ {
538
+ "name": "render",
539
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuGroupLabelState>",
540
+ "required": false,
541
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
542
+ }
543
+ ]
544
+ },
545
+ {
546
+ "name": "DropdownMenuRadioGroup",
547
+ "import": "import { DropdownMenuRadioGroup } from '@ratio-ui/core'",
548
+ "description": "Holds radio items and the one chosen value.",
549
+ "props": [
550
+ {
551
+ "name": "render",
552
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuRadioGroupState>",
553
+ "required": false,
554
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
555
+ }
556
+ ]
557
+ },
558
+ {
559
+ "name": "DropdownMenuRadioItem",
560
+ "import": "import { DropdownMenuRadioItem } from '@ratio-ui/core'",
561
+ "description": "One choice of a DropdownMenuRadioGroup. A dot shows at its start while it is the chosen value.",
562
+ "props": [
563
+ {
564
+ "name": "render",
565
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuRadioItemState>",
566
+ "required": false,
567
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
568
+ }
569
+ ]
570
+ },
571
+ {
572
+ "name": "DropdownMenuSeparator",
573
+ "import": "import { DropdownMenuSeparator } from '@ratio-ui/core'",
574
+ "description": "A hairline between groups of items.",
575
+ "props": [
576
+ {
577
+ "name": "render",
578
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SeparatorState>",
579
+ "required": false,
580
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
581
+ }
582
+ ]
583
+ },
584
+ {
585
+ "name": "DropdownMenuShortcut",
586
+ "import": "import { DropdownMenuShortcut } from '@ratio-ui/core'",
587
+ "description": "A key hint pushed to the end of an item. It is a hint only: the host binds the key.",
588
+ "props": [
589
+ {
590
+ "name": "children",
591
+ "type": "ReactNode",
592
+ "required": true,
593
+ "description": "The key hint, such as ⌘E. It is text only and does not bind a key."
594
+ }
595
+ ]
596
+ },
597
+ {
598
+ "name": "DropdownMenuSub",
599
+ "import": "import { DropdownMenuSub } from '@ratio-ui/core'",
600
+ "description": "The root of a nested menu. Hold a DropdownMenuSubTrigger and a DropdownMenuSubContent.",
601
+ "props": []
602
+ },
603
+ {
604
+ "name": "DropdownMenuSubContent",
605
+ "import": "import { DropdownMenuSubContent } from '@ratio-ui/core'",
606
+ "description": "The panel of a submenu. It opens to the side of its trigger.",
607
+ "props": [
608
+ {
609
+ "name": "side",
610
+ "type": "\"bottom\" | \"inline-end\" | \"inline-start\" | \"left\" | \"right\" | \"top\"",
611
+ "required": false,
612
+ "default": "right",
613
+ "description": "Which side of the trigger the menu opens on. Defaults to bottom."
614
+ },
615
+ {
616
+ "name": "align",
617
+ "type": "\"center\" | \"end\" | \"start\"",
618
+ "required": false,
619
+ "description": "How the menu lines up with the trigger along that side. Defaults to start."
620
+ },
621
+ {
622
+ "name": "sideOffset",
623
+ "type": "number",
624
+ "required": false,
625
+ "default": "0",
626
+ "description": "The gap between the trigger and the menu in px. Defaults to 4."
627
+ },
628
+ {
629
+ "name": "alignOffset",
630
+ "type": "number",
631
+ "required": false,
632
+ "default": "-6",
633
+ "description": "A shift along the side in px. Defaults to 0."
634
+ },
635
+ {
636
+ "name": "render",
637
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuPopupState>",
638
+ "required": false,
639
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
640
+ }
641
+ ]
642
+ },
643
+ {
644
+ "name": "DropdownMenuSubTrigger",
645
+ "import": "import { DropdownMenuSubTrigger } from '@ratio-ui/core'",
646
+ "description": "The item that opens a submenu, on hover, click or ArrowRight. A chevron at its end marks it.",
647
+ "props": [
648
+ {
649
+ "name": "render",
650
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuSubmenuTriggerState>",
651
+ "required": false,
652
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
653
+ }
654
+ ]
655
+ },
656
+ {
657
+ "name": "DropdownMenuTrigger",
658
+ "import": "import { DropdownMenuTrigger } from '@ratio-ui/core'",
659
+ "description": "The control that opens the menu. It is a button, and carries aria-expanded and aria-haspopup.",
660
+ "props": [
661
+ {
662
+ "name": "render",
663
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, MenuTriggerState>",
664
+ "required": false,
665
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
666
+ }
667
+ ]
668
+ }
669
+ ]
670
+ },
671
+ {
672
+ "name": "EmptyState",
673
+ "import": "import { EmptyState } from '@ratio-ui/core'",
674
+ "description": "What a list, table or panel shows when it has nothing to show: one clause and an optional action, centred in a hairline container.",
675
+ "props": [
676
+ {
677
+ "name": "title",
678
+ "type": "ReactNode",
679
+ "required": true,
680
+ "description": "One clause that says why nothing is shown, in sentence case with no period."
681
+ },
682
+ {
683
+ "name": "description",
684
+ "type": "ReactNode",
685
+ "required": false,
686
+ "description": "An optional second clause that says what to do next."
687
+ },
688
+ {
689
+ "name": "action",
690
+ "type": "ReactNode",
691
+ "required": false,
692
+ "description": "An optional action, usually one Button."
693
+ }
694
+ ],
695
+ "usage": {
696
+ "whenToUse": "- A list, table or panel with nothing to show, such as a filter that matches no products\n- With one action when the user can fix it, such as clearing the filter",
697
+ "whenNotToUse": "- A load that is still running: use Skeleton\n- An error: say what failed in one clause, in the error colour, not here",
698
+ "example": "<EmptyState title=\"No products match this filter\" action={<Button>Clear filter</Button>} />"
699
+ }
700
+ },
701
+ {
702
+ "name": "FilterTabs",
703
+ "import": "import { FilterTabs } from '@ratio-ui/core'",
704
+ "description": "A single choice that filters a list, such as All, Active and Inactive above a table: the design system's Tab chips in a row with an 8px gap. One item is always chosen. For sections of a page use Tabs instead.",
705
+ "props": [
706
+ {
707
+ "name": "items",
708
+ "type": "FilterTabsItem[]",
709
+ "required": true,
710
+ "description": "The choices, in order."
711
+ },
712
+ {
713
+ "name": "value",
714
+ "type": "string",
715
+ "required": false,
716
+ "description": "The chosen value when controlled."
717
+ },
718
+ {
719
+ "name": "defaultValue",
720
+ "type": "string",
721
+ "required": false,
722
+ "description": "The chosen value at the start when uncontrolled. Defaults to the first item."
723
+ },
724
+ {
725
+ "name": "onValueChange",
726
+ "type": "((value: string) => void)",
727
+ "required": false,
728
+ "description": "Called with the new value when another item is chosen. Choosing the chosen item again does nothing."
729
+ },
730
+ {
731
+ "name": "disabled",
732
+ "type": "boolean",
733
+ "required": false,
734
+ "description": "Disables every item. Defaults to false."
735
+ }
736
+ ],
737
+ "usage": {
738
+ "whenToUse": "- A single choice that filters the list below it, such as All, Active and Inactive above the products table\n- A short set of choices, one chosen at all times, shown as the design system's chip tabs in a row",
739
+ "whenNotToUse": "- Sections of a page with their own content: use Tabs\n- Choices that can be combined or cleared: use checkboxes or a menu\n- Many choices: the chips wrap onto more lines, so use a select",
740
+ "example": "<FilterTabs\n aria-label=\"Status\"\n items={[\n { value: 'all', label: 'All' },\n { value: 'active', label: 'Active' },\n { value: 'draft', label: 'Draft' },\n ]}\n defaultValue=\"all\"\n onValueChange={setStatus}\n/>"
741
+ }
742
+ },
743
+ {
744
+ "name": "GlobalSearch",
745
+ "import": "import { GlobalSearch } from '@ratio-ui/core'",
746
+ "description": "The search in the top bar. Typing opens a panel of grouped results that the app supplies, an empty field shows recent items, and the arrow keys, Enter and Escape work the list. Cmd+K or Ctrl+K focuses the field from anywhere.",
747
+ "props": [
748
+ {
749
+ "name": "label",
750
+ "type": "string",
751
+ "required": true,
752
+ "description": "The accessible name of the field, such as \"Search the dashboard\"."
753
+ },
754
+ {
755
+ "name": "value",
756
+ "type": "string",
757
+ "required": true,
758
+ "description": "The text typed in the field."
759
+ },
760
+ {
761
+ "name": "onValueChange",
762
+ "type": "(value: string) => void",
763
+ "required": true,
764
+ "description": "Called on every change of the text. Debounce and fetch in the app."
765
+ },
766
+ {
767
+ "name": "groups",
768
+ "type": "GlobalSearchGroup[]",
769
+ "required": true,
770
+ "description": "The results for the current text, grouped. The app supplies them; nothing is filtered here."
771
+ },
772
+ {
773
+ "name": "recent",
774
+ "type": "GlobalSearchResult[]",
775
+ "required": false,
776
+ "default": "[]",
777
+ "description": "Recent items, shown under \"Recent\" while the field is empty."
778
+ },
779
+ {
780
+ "name": "onSelect",
781
+ "type": "((result: GlobalSearchResult) => void)",
782
+ "required": false,
783
+ "description": "Called when a result is chosen by click or Enter."
784
+ },
785
+ {
786
+ "name": "onShowMore",
787
+ "type": "((group: GlobalSearchGroup) => void)",
788
+ "required": false,
789
+ "description": "Called when a \"+N more\" row is chosen, with its group."
790
+ },
791
+ {
792
+ "name": "placeholder",
793
+ "type": "string",
794
+ "required": false,
795
+ "default": "Search",
796
+ "description": "The placeholder of the field. Defaults to \"Search\"."
797
+ },
798
+ {
799
+ "name": "emptyText",
800
+ "type": "string",
801
+ "required": false,
802
+ "default": "No results",
803
+ "description": "The copy when the text matches nothing. Defaults to \"No results\"."
804
+ }
805
+ ],
806
+ "usage": {
807
+ "whenToUse": "- The search in the top bar that finds products, orders, customers and pages across the dashboard\n- Results that the app fetches and groups itself, with recent items while the field is empty",
808
+ "whenNotToUse": "- Narrowing the rows of one listing: use SearchField\n- Picking a value for a form field: use a select",
809
+ "example": "<GlobalSearch\n label=\"Search the dashboard\"\n value={query}\n onValueChange={setQuery}\n groups={[{ id: 'products', label: 'Products', total: 8, items: products }]}\n recent={recentItems}\n onSelect={(result) => navigate(result.id)}\n onShowMore={(group) => navigate(`/search?group=${group.id}&q=${query}`)}\n/>"
810
+ }
811
+ },
4
812
  {
5
813
  "name": "Icon",
6
814
  "import": "import { Icon } from '@ratio-ui/core'",
@@ -8,87 +816,595 @@
8
816
  "props": [
9
817
  {
10
818
  "name": "name",
11
- "type": "\"alert-triangle\" | \"check\" | \"chevron-left\" | \"chevron-right\" | \"export\" | \"info-circle\" | \"nav-caret-down\" | \"nav-chevron\" | \"nav-home\" | \"nav-product\" | \"nav-product-2\" | \"nav-shortcut\" | \"plus\" | \"search\" | \"x\"",
819
+ "type": "\"alert-triangle\" | \"check\" | \"chevron-left\" | \"chevron-right\" | \"export\" | \"import\" | \"info-circle\" | \"menu\" | \"more\" | \"nav-analytics\" | \"nav-apps\" | \"nav-caret-down\" | \"nav-chevron\" | \"nav-content\" | \"nav-customers\" | \"nav-discounts\" | \"nav-growth\" | \"nav-home\" | \"nav-markets\" | \"nav-orders\" | \"nav-product\" | \"nav-product-2\" | \"nav-sales-channels\" | \"nav-settings\" | \"nav-shortcut\" | \"plus\" | \"search\" | \"x\"",
820
+ "required": true,
821
+ "description": "Which icon to draw, by name."
822
+ },
823
+ {
824
+ "name": "size",
825
+ "type": "8 | 12 | 16",
826
+ "required": false,
827
+ "default": "12",
828
+ "description": "The square box in px: 8, 12 or 16. Defaults to 12, the size inside controls."
829
+ },
830
+ {
831
+ "name": "tone",
832
+ "type": "\"current\" | \"icon\" | \"primary\" | \"subdued\"",
833
+ "required": false,
834
+ "default": "current",
835
+ "description": "The colour. current, the default, takes the colour of the text around the icon; the other tones set their own."
836
+ },
837
+ {
838
+ "name": "label",
839
+ "type": "string",
840
+ "required": false,
841
+ "description": "The accessible name. With a label the icon is an image that assistive technology announces under that name. Without one it is decoration, hidden from assistive technology, and the text beside it has to say what it means."
842
+ }
843
+ ],
844
+ "usage": {
845
+ "whenToUse": "- A glyph beside a label inside a control: the plus on a main button, the magnifier in a search field, the export tray on a secondary button. Use size 12\n- The side navigation: the nav glyphs at size 16 (nav-home and nav-product are the design system's own; nav-orders, nav-customers, nav-growth, nav-discounts, nav-content, nav-markets, nav-analytics, nav-sales-channels, nav-apps and nav-settings are Tabler stand-ins for the dashboard's sections, question 7), and the disclosure caret (nav-chevron or nav-caret-down, one glyph under two names) at size 8\n- Pagination: chevron-left and chevron-right at size 8\n- A mark that Tabler supplies: check, x, alert-triangle, info-circle, import, more (three dots) and menu (the phone menu button). They are drawn with a 1.5px line at every size, so they sit beside the design system's own glyphs without looking heavier\n- Meaning that lives only in the icon: pass label. The icon is then an image that assistive technology announces under that name. Without a label it is decoration and is hidden from assistive technology\n- Colour: tone current, the default, takes the colour of the text around the icon. icon is the design system's icon ink, subdued is its subdued text ink and primary is the accent\n- Chevron geometry (design question 9): the design system has no vector for its pagination chevrons, only a 1.5px CSS caret in an 8px box, so chevron-left and chevron-right stand in. Each is the outline of a line 1.5 units wide with round ends, two legs at 45 degrees, each 2.5 units across and 2.5 down, through (2.75, 1.5), (5.25, 4) and (2.75, 6.5) in an 8 unit box. They scale like every filled glyph, so draw them at size 8, and expect them to be replaced when the designer sends real vectors",
846
+ "whenNotToUse": "- Decoration that adds no meaning. An icon next to text that already says the same thing is hidden from assistive technology, so leave the label off, and leave the icon out when it adds nothing\n- An icon the registry does not have. Components accept no className or style, and icons come from the registry only, so a new one is a registry entry: the design system's glyph first, Tabler for the rest\n- An icon-only control without a name. Pass label, or give the control its own accessible name\n- A size other than 8, 12 or 16, or a colour other than the four tones. There is no red, no destructive colour and no third accent: an error is marked with the alert-triangle glyph beside its message, not with a colour\n- Emoji, or unicode arrows and symbols, as icons. Inter's web subsets do not hold them and the design system bans them",
847
+ "example": "import { Icon } from '@ratio-ui/core'\n\n// Beside text that names it: decoration, hidden from assistive technology\n<button type=\"button\">\n <Icon name=\"plus\" />\n Add product\n</button>\n\n// Alone: the label is the accessible name\n<Icon name=\"search\" tone=\"icon\" label=\"Search\" />\n\n// Pagination chevrons are drawn for an 8px box\n<Icon name=\"chevron-right\" size={8} />"
848
+ }
849
+ },
850
+ {
851
+ "name": "Inline",
852
+ "import": "import { Inline } from '@ratio-ui/core'",
853
+ "description": "Lays its children out in a row with a fixed space between them. It takes no className and no style: the gap is a step of the design system's spacing scale, and the layout is the only styling it has.",
854
+ "props": [
855
+ {
856
+ "name": "gap",
857
+ "type": "0 | 1 | 2 | 4 | 6 | 7 | 8 | 11 | 12 | 14 | 24 | 32 | 80",
858
+ "required": true,
859
+ "description": "The space between children, a step of the design system's scale in px: 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 24, 32 or 80."
860
+ },
861
+ {
862
+ "name": "align",
863
+ "type": "\"baseline\" | \"center\" | \"end\" | \"start\" | \"stretch\"",
864
+ "required": false,
865
+ "default": "center",
866
+ "description": "How children line up across the row, which is the vertical axis. Defaults to center."
867
+ },
868
+ {
869
+ "name": "justify",
870
+ "type": "\"between\" | \"center\" | \"end\" | \"start\"",
871
+ "required": false,
872
+ "default": "start",
873
+ "description": "How children spread along the row, which is the horizontal axis. Defaults to start."
874
+ },
875
+ {
876
+ "name": "wrap",
877
+ "type": "boolean",
878
+ "required": false,
879
+ "default": "false",
880
+ "description": "Whether children move to a new line when the row is full. Defaults to false, which keeps one line."
881
+ },
882
+ {
883
+ "name": "as",
884
+ "type": "\"aside\" | \"div\" | \"fieldset\" | \"footer\" | \"form\" | \"header\" | \"li\" | \"main\" | \"nav\" | \"ol\" | \"section\" | \"ul\"",
885
+ "required": false,
886
+ "default": "div",
887
+ "description": "The element to render. Defaults to div; use a list, landmark or form element where the content is one."
888
+ },
889
+ {
890
+ "name": "ref",
891
+ "type": "Ref<HTMLElement>",
892
+ "required": false,
893
+ "description": "Receives the rendered element."
894
+ }
895
+ ],
896
+ "usage": {
897
+ "whenToUse": "- Children side by side with a fixed space between them: a toolbar, a row of tags, a label next to a value\n- Children that spread along the row (`justify`: start, center, end or between) or move to the next line when the row is full (`wrap`)\n- A gap taken from the design system's spacing scale: 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 24, 32 or 80 px\n- A row that is also a list or a landmark: `as` renders `ul`, `ol`, `li`, `nav`, `header`, `footer`, `main`, `aside`, `section`, `form` or `fieldset` instead of `div`",
898
+ "whenNotToUse": "- Children one under another: use Stack\n- Columns that have to line up from one row to the next: Inline lays out one row at a time, and wave 0 has no grid\n- Space around one child or at the edge of a container: Inline spaces siblings only, and has no padding or margin\n- Styling: Inline takes no `className` and no `style`\n- A list without markers: `as=\"ul\"` and `as=\"ol\"` leave the browser's list markers as they are",
899
+ "example": "<Inline gap={8} justify=\"between\" wrap>\n <div>Orders</div>\n <Inline gap={4}>\n <div>Export</div>\n <div>Filter</div>\n </Inline>\n</Inline>"
900
+ }
901
+ },
902
+ {
903
+ "name": "Input",
904
+ "import": "import { Input } from '@ratio-ui/core'",
905
+ "description": "A single-line text field in the design system's look: 8px radius, a 1px ring that turns blue on focus, 14px body type. It takes every attribute of an input (type, inputMode, value, onChange). For a prefix, suffix, icon or shortcut hint, put an InputGroupInput in an InputGroup.",
906
+ "props": [
907
+ {
908
+ "name": "size",
909
+ "type": "32 | 36",
910
+ "required": false,
911
+ "default": "36",
912
+ "description": "The height in px: 36 for forms and search, 32 for fields inside a table. Defaults to 36."
913
+ },
914
+ {
915
+ "name": "ref",
916
+ "type": "Ref<HTMLInputElement>",
917
+ "required": false,
918
+ "description": "Receives the input element."
919
+ }
920
+ ],
921
+ "usage": {
922
+ "whenToUse": "- A single-line text field in a form, a settings page or a table cell: `size` is 36 for forms and 32 for fields inside a table\n- Any input type the browser supports, such as text, email, number or password: Input takes every attribute of an input\n- A field with an error: set `aria-invalid` and the ring changes, and put the error message beside it in text",
923
+ "whenNotToUse": "- A field with a prefix, suffix, icon or shortcut hint, such as a rupee price, a weight in kg or a search field: use InputGroup\n- Several lines of text: use a textarea\n- A choice from a fixed list: use a select\n- Custom classes or inline styles: Input takes no `className` and no `style`",
924
+ "example": "<Input aria-label=\"Product title\" placeholder=\"Short sleeve t-shirt\" />\n<Input aria-label=\"Variant stock\" size={32} inputMode=\"numeric\" />"
925
+ }
926
+ },
927
+ {
928
+ "name": "InputGroup",
929
+ "import": "import { InputGroup } from '@ratio-ui/core'",
930
+ "description": "A field with a prefix, suffix, icon or shortcut hint beside the input, inside one ring. Put an InputGroupInput in it and an InputGroupAddon on either side, for example a rupee sign before a price or kg after a weight.",
931
+ "props": [
932
+ {
933
+ "name": "size",
934
+ "type": "32 | 36",
935
+ "required": false,
936
+ "default": "36",
937
+ "description": "The height in px: 36 for forms and search, 32 for fields inside a table. Defaults to 36."
938
+ },
939
+ {
940
+ "name": "ref",
941
+ "type": "Ref<HTMLDivElement>",
942
+ "required": false,
943
+ "description": "Receives the group element."
944
+ }
945
+ ],
946
+ "usage": {
947
+ "whenToUse": "- A field with something beside the input, all inside one ring: a rupee sign before a price, kg after a weight, a search icon, or a shortcut hint such as ⌘K\n- A table search field or the top bar search: an icon at the start and a shortcut hint at the end\n- A compact field in a table: `size` 32, as for Input",
948
+ "whenNotToUse": "- A plain field with nothing beside the input: use Input\n- A button inside the field: put it next to the group, because an addon holds text and icons only\n- Custom classes or inline styles: none of the parts take `className` or `style`",
949
+ "example": "<InputGroup>\n <InputGroupAddon>\n <InputGroupText>₹</InputGroupText>\n </InputGroupAddon>\n <InputGroupInput aria-label=\"Selling price\" inputMode=\"decimal\" />\n</InputGroup>\n<InputGroup>\n <InputGroupAddon><Icon name=\"search\" /></InputGroupAddon>\n <InputGroupInput aria-label=\"Search\" placeholder=\"Search by name, email, etc.\" />\n <InputGroupAddon align=\"end\"><InputGroupShortcut>⌘K</InputGroupShortcut></InputGroupAddon>\n</InputGroup>"
950
+ },
951
+ "parts": [
952
+ {
953
+ "name": "InputGroupAddon",
954
+ "import": "import { InputGroupAddon } from '@ratio-ui/core'",
955
+ "description": "Holds an icon, a prefix, a suffix or a shortcut hint on one side of the input. Clicking it, off a button, moves focus to the input.",
956
+ "props": [
957
+ {
958
+ "name": "align",
959
+ "type": "\"end\" | \"start\"",
960
+ "required": false,
961
+ "default": "start",
962
+ "description": "The side of the input: start before it, end after it. Defaults to start."
963
+ }
964
+ ]
965
+ },
966
+ {
967
+ "name": "InputGroupInput",
968
+ "import": "import { InputGroupInput } from '@ratio-ui/core'",
969
+ "description": "The input of an InputGroup. It draws no ring: the group does. Mark it invalid with aria-invalid.",
970
+ "props": [
971
+ {
972
+ "name": "ref",
973
+ "type": "Ref<HTMLInputElement>",
974
+ "required": false,
975
+ "description": "Receives the input element."
976
+ }
977
+ ]
978
+ },
979
+ {
980
+ "name": "InputGroupShortcut",
981
+ "import": "import { InputGroupShortcut } from '@ratio-ui/core'",
982
+ "description": "A shortcut hint such as ⌘K inside an end addon, drawn as a small keycap.",
983
+ "props": []
984
+ },
985
+ {
986
+ "name": "InputGroupText",
987
+ "import": "import { InputGroupText } from '@ratio-ui/core'",
988
+ "description": "A text prefix or suffix inside an addon, such as a currency sign or a unit, in the muted colour.",
989
+ "props": []
990
+ }
991
+ ]
992
+ },
993
+ {
994
+ "name": "Kbd",
995
+ "import": "import { Kbd } from '@ratio-ui/core'",
996
+ "description": "A key hint, 20px high with a 4px radius, such as ⌘K or esc.",
997
+ "props": [
998
+ {
999
+ "name": "ref",
1000
+ "type": "Ref<HTMLElement>",
1001
+ "required": false,
1002
+ "description": "Receives the rendered element."
1003
+ }
1004
+ ],
1005
+ "usage": {
1006
+ "whenToUse": "- A shortcut hint beside a control or in a menu: ⌘K, ↵, esc\n- Several keys of one shortcut, inside KbdGroup",
1007
+ "whenNotToUse": "- A button that triggers the action: Kbd only shows the key and is not interactive\n- Long text: a key is one symbol or a short name",
1008
+ "example": "<KbdGroup>\n <Kbd>⌘</Kbd>\n <Kbd>K</Kbd>\n</KbdGroup>"
1009
+ },
1010
+ "parts": [
1011
+ {
1012
+ "name": "KbdGroup",
1013
+ "import": "import { KbdGroup } from '@ratio-ui/core'",
1014
+ "description": "Puts the keys of one shortcut in a row with a 4px gap.",
1015
+ "props": []
1016
+ }
1017
+ ]
1018
+ },
1019
+ {
1020
+ "name": "LinkProvider",
1021
+ "import": "import { LinkProvider } from '@ratio-ui/core'",
1022
+ "description": "Draws every link of Ratio's components with the app's own router link, so a click navigates without a page reload: SideNav rows with an href, DataTable row links and the PageHeader back link. Wrap the app once, at its root. Without it those links are plain <a href> elements, which reload the page.",
1023
+ "props": [
1024
+ {
1025
+ "name": "render",
1026
+ "type": "LinkRender",
1027
+ "required": true,
1028
+ "description": "Draws every link Ratio renders. The function receives href, the styling class, the content and attributes such as aria-current and onClick, all to spread onto the element: `({ href, ...props }) => <RouterLink to={href} {...props} />`."
1029
+ },
1030
+ {
1031
+ "name": "children",
1032
+ "type": "ReactNode",
1033
+ "required": true,
1034
+ "description": "The app, or the part of it that the router serves."
1035
+ }
1036
+ ],
1037
+ "usage": {
1038
+ "whenToUse": "- An app on a client-side router (React Router, Next.js, TanStack Router): wrap it once, at its root\n- So that SideNav rows with an href, DataTable row links and the PageHeader back link navigate without a page reload",
1039
+ "whenNotToUse": "- An app whose pages load from the server: the plain links already work\n- A link in your own markup: use your router's link directly",
1040
+ "example": "import { Link as RouterLink } from 'react-router-dom'\n\n<LinkProvider render={({ href, ...props }) => <RouterLink to={href} {...props} />}>\n <App />\n</LinkProvider>"
1041
+ }
1042
+ },
1043
+ {
1044
+ "name": "MetricStrip",
1045
+ "import": "import { MetricStrip } from '@ratio-ui/core'",
1046
+ "description": "A row of metrics in one hairline container: each segment is a title, optionally with a definition, over its value.",
1047
+ "props": [
1048
+ {
1049
+ "name": "segments",
1050
+ "type": "MetricSegment[]",
1051
+ "required": true,
1052
+ "description": "The metrics, left to right."
1053
+ },
1054
+ {
1055
+ "name": "label",
1056
+ "type": "string",
1057
+ "required": false,
1058
+ "description": "The accessible name of the strip."
1059
+ }
1060
+ ],
1061
+ "usage": {
1062
+ "whenToUse": "- A few headline numbers above a listing, such as the Products metrics\n- A metric whose name needs a one-sentence definition",
1063
+ "whenNotToUse": "- A chart or a trend: this is plain values\n- Many metrics or long values: use a table",
1064
+ "example": "<MetricStrip\n label=\"Product metrics\"\n segments={[\n {\n id: 'rate',\n title: 'Average sell-through rate',\n definition: 'Share of stock sold',\n value: '9.95%',\n },\n { id: 'days', title: 'Days of inventory remaining', value: null },\n { id: 'abc', title: 'ABC product analysis', value: 0, format: 'money' },\n ]}\n/>"
1065
+ }
1066
+ },
1067
+ {
1068
+ "name": "PageHeader",
1069
+ "import": "import { PageHeader } from '@ratio-ui/core'",
1070
+ "description": "The top of a page: an optional back link, then an icon, the title and a status pill on the left, and the actions with an overflow menu on the right. It wraps on a phone.",
1071
+ "props": [
1072
+ {
1073
+ "name": "title",
1074
+ "type": "string",
12
1075
  "required": true,
13
- "description": "Which icon to draw, by name."
1076
+ "description": "The page title, shown as the level 1 heading."
14
1077
  },
15
1078
  {
16
- "name": "size",
17
- "type": "8 | 12 | 16",
1079
+ "name": "icon",
1080
+ "type": "ReactNode",
18
1081
  "required": false,
19
- "default": "12",
20
- "description": "The square box in px: 8, 12 or 16. Defaults to 12, the size inside controls."
1082
+ "description": "An icon or logo shown before the title."
21
1083
  },
22
1084
  {
23
- "name": "tone",
24
- "type": "\"current\" | \"icon\" | \"primary\" | \"subdued\"",
1085
+ "name": "status",
1086
+ "type": "{ label: string; tone?: StatusPillTone; }",
25
1087
  "required": false,
26
- "default": "current",
27
- "description": "The colour. current, the default, takes the colour of the text around the icon; the other tones set their own."
1088
+ "description": "The state of the record, one word, beside the title."
28
1089
  },
29
1090
  {
30
- "name": "label",
1091
+ "name": "backHref",
31
1092
  "type": "string",
32
1093
  "required": false,
33
- "description": "The accessible name. With a label the icon is an image that assistive technology announces under that name. Without one it is decoration, hidden from assistive technology, and the text beside it has to say what it means."
1094
+ "description": "Where the back link goes. The link shows only with this. On a router, LinkProvider draws it with the app's own link."
1095
+ },
1096
+ {
1097
+ "name": "backLabel",
1098
+ "type": "string",
1099
+ "required": false,
1100
+ "default": "Back",
1101
+ "description": "The text of the back link, such as the parent page's name. Defaults to Back."
1102
+ },
1103
+ {
1104
+ "name": "actions",
1105
+ "type": "ReactNode",
1106
+ "required": false,
1107
+ "description": "Buttons on the right, such as Export and Add product."
1108
+ },
1109
+ {
1110
+ "name": "menuItems",
1111
+ "type": "PageHeaderMenuItem[]",
1112
+ "required": false,
1113
+ "description": "Entries of the overflow menu. The More actions button shows only when there is at least one."
34
1114
  }
35
1115
  ],
36
1116
  "usage": {
37
- "whenToUse": "- A glyph beside a label inside a control: the plus on a main button, the magnifier in a search field, the export tray on a secondary button. Use size 12\n- The side navigation: the nav glyphs at size 16, and the disclosure caret (nav-chevron or nav-caret-down, one glyph under two names) at size 8\n- Pagination: chevron-left and chevron-right at size 8\n- A mark that Tabler supplies: check, x, alert-triangle and info-circle. They are drawn with a 1.5px line at every size, so they sit beside the design system's own glyphs without looking heavier\n- Meaning that lives only in the icon: pass label. The icon is then an image that assistive technology announces under that name. Without a label it is decoration and is hidden from assistive technology\n- Colour: tone current, the default, takes the colour of the text around the icon. icon is the design system's icon ink, subdued is its subdued text ink and primary is the accent\n- Chevron geometry (design question 9): the design system has no vector for its pagination chevrons, only a 1.5px CSS caret in an 8px box, so chevron-left and chevron-right stand in. Each is the outline of a line 1.5 units wide with round ends, two legs at 45 degrees, each 2.5 units across and 2.5 down, through (2.75, 1.5), (5.25, 4) and (2.75, 6.5) in an 8 unit box. They scale like every filled glyph, so draw them at size 8, and expect them to be replaced when the designer sends real vectors",
38
- "whenNotToUse": "- Decoration that adds no meaning. An icon next to text that already says the same thing is hidden from assistive technology, so leave the label off, and leave the icon out when it adds nothing\n- An icon the registry does not have. Components accept no className or style, and icons come from the registry only, so a new one is a registry entry: the design system's glyph first, Tabler for the rest\n- An icon-only control without a name. Pass label, or give the control its own accessible name\n- A size other than 8, 12 or 16, or a colour other than the four tones. There is no red, no destructive colour and no third accent: an error is marked with the alert-triangle glyph beside its message, not with a colour\n- Emoji, or unicode arrows and symbols, as icons. Inter's web subsets do not hold them and the design system bans them",
39
- "example": "import { Icon } from '@ratio-ui/core'\n\n// Beside text that names it: decoration, hidden from assistive technology\n<button type=\"button\">\n <Icon name=\"plus\" />\n Add product\n</button>\n\n// Alone: the label is the accessible name\n<Icon name=\"search\" tone=\"icon\" label=\"Search\" />\n\n// Pagination chevrons are drawn for an 8px box\n<Icon name=\"chevron-right\" size={8} />"
1117
+ "whenToUse": "- The top of a page: Products, Product detail\n- A title with an icon, a one-word status, a back link, actions and an overflow menu",
1118
+ "whenNotToUse": "- A heading inside a panel: use Text\n- More than one main action: keep one main Button, the rest secondary or in the overflow menu",
1119
+ "example": "<PageHeader\n title=\"Blue shirt\"\n status={{ label: 'Active', tone: 'success' }}\n backHref=\"/products\"\n backLabel=\"Products\"\n actions={<Button>Save</Button>}\n menuItems={[\n { label: 'Duplicate', onSelect: duplicate },\n { label: 'Delete', onSelect: remove, destructive: true },\n ]}\n/>"
40
1120
  }
41
1121
  },
42
1122
  {
43
- "name": "Inline",
44
- "import": "import { Inline } from '@ratio-ui/core'",
45
- "description": "Lays its children out in a row with a fixed space between them. It takes no className and no style: the gap is a step of the design system's spacing scale, and the layout is the only styling it has.",
1123
+ "name": "Pagination",
1124
+ "import": "import { Pagination } from '@ratio-ui/core'",
1125
+ "description": "The footer under a table: Previous and Next arrows and the range on show, written \"1 – 20 of 84 results\". An arrow is dimmed to 50% and disabled (aria-disabled, still focusable) when there is no page that way. The parent owns the page: it passes `page` and updates it in `onPageChange`.",
46
1126
  "props": [
47
1127
  {
48
- "name": "gap",
49
- "type": "0 | 1 | 2 | 4 | 6 | 7 | 8 | 11 | 12 | 14 | 24 | 32 | 80",
1128
+ "name": "page",
1129
+ "type": "number",
50
1130
  "required": true,
51
- "description": "The space between children, a step of the design system's scale in px: 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 24, 32 or 80."
1131
+ "description": "The page being shown, counting from 1."
52
1132
  },
53
1133
  {
54
- "name": "align",
55
- "type": "\"baseline\" | \"center\" | \"end\" | \"start\" | \"stretch\"",
1134
+ "name": "pageSize",
1135
+ "type": "number",
1136
+ "required": true,
1137
+ "description": "How many results one page holds."
1138
+ },
1139
+ {
1140
+ "name": "total",
1141
+ "type": "number",
1142
+ "required": true,
1143
+ "description": "How many results there are in all, across every page."
1144
+ },
1145
+ {
1146
+ "name": "onPageChange",
1147
+ "type": "(page: number) => void",
1148
+ "required": true,
1149
+ "description": "Called with the page to show when Previous or Next is pressed."
1150
+ }
1151
+ ],
1152
+ "usage": {
1153
+ "whenToUse": "- The footer under a table or list that has more than one page of results\n- When the parent knows the page, the page size and the total",
1154
+ "whenNotToUse": "- Numbered page links or jump to page: this is Previous and Next only\n- Infinite scroll or a load more button\n- Styling: Pagination takes no `className` and no `style`",
1155
+ "example": "const [page, setPage] = useState(1)\n\n<Pagination page={page} pageSize={20} total={84} onPageChange={setPage} />"
1156
+ }
1157
+ },
1158
+ {
1159
+ "name": "Popover",
1160
+ "import": "import { Popover } from '@ratio-ui/core'",
1161
+ "description": "A panel that opens from a trigger and holds more than text, such as a list of notifications. Put a PopoverTrigger and a PopoverContent inside it.",
1162
+ "props": [],
1163
+ "usage": {
1164
+ "whenToUse": "- A small panel that opens from a button and holds more than a sentence, such as the notifications list\n- Content that people read or act on, and that they close with Escape or by clicking away",
1165
+ "whenNotToUse": "- A one-line hint: use Tooltip, or Definition for a term\n- A choice of actions: use DropdownMenu\n- A task that needs the whole screen's attention: use Dialog",
1166
+ "example": "<Popover>\n <PopoverTrigger>Notifications</PopoverTrigger>\n <PopoverContent>\n <PopoverHeader>\n <PopoverTitle>Notifications</PopoverTitle>\n <PopoverDescription>No new notifications</PopoverDescription>\n </PopoverHeader>\n </PopoverContent>\n</Popover>"
1167
+ },
1168
+ "parts": [
1169
+ {
1170
+ "name": "PopoverContent",
1171
+ "import": "import { PopoverContent } from '@ratio-ui/core'",
1172
+ "description": "The popover panel: a white panel with a 1px outline and no shadow, 288px wide. It opens below the trigger by default.",
1173
+ "props": [
1174
+ {
1175
+ "name": "render",
1176
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverPopupState>",
1177
+ "required": false,
1178
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1179
+ }
1180
+ ]
1181
+ },
1182
+ {
1183
+ "name": "PopoverDescription",
1184
+ "import": "import { PopoverDescription } from '@ratio-ui/core'",
1185
+ "description": "The supporting text under the title, in the muted colour.",
1186
+ "props": [
1187
+ {
1188
+ "name": "render",
1189
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverDescriptionState>",
1190
+ "required": false,
1191
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1192
+ }
1193
+ ]
1194
+ },
1195
+ {
1196
+ "name": "PopoverHeader",
1197
+ "import": "import { PopoverHeader } from '@ratio-ui/core'",
1198
+ "description": "Groups a title and a description at the top of the panel.",
1199
+ "props": []
1200
+ },
1201
+ {
1202
+ "name": "PopoverTitle",
1203
+ "import": "import { PopoverTitle } from '@ratio-ui/core'",
1204
+ "description": "The title of the panel; it names the dialog for screen readers.",
1205
+ "props": [
1206
+ {
1207
+ "name": "render",
1208
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTitleState>",
1209
+ "required": false,
1210
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1211
+ }
1212
+ ]
1213
+ },
1214
+ {
1215
+ "name": "PopoverTrigger",
1216
+ "import": "import { PopoverTrigger } from '@ratio-ui/core'",
1217
+ "description": "The element that opens the popover on click or Enter. It renders a button unless render says otherwise.",
1218
+ "props": [
1219
+ {
1220
+ "name": "render",
1221
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, PopoverTriggerState>",
1222
+ "required": false,
1223
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1224
+ },
1225
+ {
1226
+ "name": "handle",
1227
+ "type": "PopoverHandle<unknown>",
1228
+ "required": false,
1229
+ "description": "A handle to associate the trigger with a popover."
1230
+ },
1231
+ {
1232
+ "name": "payload",
1233
+ "type": "unknown",
1234
+ "required": false,
1235
+ "description": "A payload to pass to the popover when it is opened."
1236
+ },
1237
+ {
1238
+ "name": "delay",
1239
+ "type": "number",
1240
+ "required": false,
1241
+ "default": "300",
1242
+ "description": "How long to wait before the popover may be opened on hover. Specified in milliseconds.\n\nRequires the `openOnHover` prop."
1243
+ },
1244
+ {
1245
+ "name": "closeDelay",
1246
+ "type": "number",
1247
+ "required": false,
1248
+ "default": "0",
1249
+ "description": "How long to wait before closing the popover that was opened on hover. Specified in milliseconds.\n\nRequires the `openOnHover` prop."
1250
+ },
1251
+ {
1252
+ "name": "openOnHover",
1253
+ "type": "boolean",
1254
+ "required": false,
1255
+ "default": "false",
1256
+ "description": "Whether the popover should also open when the trigger is hovered."
1257
+ }
1258
+ ]
1259
+ }
1260
+ ]
1261
+ },
1262
+ {
1263
+ "name": "SearchField",
1264
+ "import": "import { SearchField } from '@ratio-ui/core'",
1265
+ "description": "The search field above a listing: a search icon, the input and a clear button that appears once there is text. Escape clears it too. The field reports every change; the debounce stays in the app.",
1266
+ "props": [
1267
+ {
1268
+ "name": "label",
1269
+ "type": "string",
1270
+ "required": true,
1271
+ "description": "The accessible name of the field, such as \"Search products\". Required because the field has no visible label."
1272
+ },
1273
+ {
1274
+ "name": "value",
1275
+ "type": "string",
56
1276
  "required": false,
57
- "default": "center",
58
- "description": "How children line up across the row, which is the vertical axis. Defaults to center."
1277
+ "description": "The text in the field. Pass it with onValueChange to control the field."
59
1278
  },
60
1279
  {
61
- "name": "justify",
62
- "type": "\"between\" | \"center\" | \"end\" | \"start\"",
1280
+ "name": "defaultValue",
1281
+ "type": "string",
63
1282
  "required": false,
64
- "default": "start",
65
- "description": "How children spread along the row, which is the horizontal axis. Defaults to start."
1283
+ "default": "",
1284
+ "description": "The starting text of an uncontrolled field."
66
1285
  },
67
1286
  {
68
- "name": "wrap",
69
- "type": "boolean",
1287
+ "name": "onValueChange",
1288
+ "type": "((value: string) => void)",
70
1289
  "required": false,
71
- "default": "false",
72
- "description": "Whether children move to a new line when the row is full. Defaults to false, which keeps one line."
1290
+ "description": "Called with the new text on every change and when the field is cleared. It is not debounced: debounce in the app."
73
1291
  },
74
1292
  {
75
- "name": "as",
76
- "type": "\"aside\" | \"div\" | \"fieldset\" | \"footer\" | \"form\" | \"header\" | \"li\" | \"main\" | \"nav\" | \"ol\" | \"section\" | \"ul\"",
1293
+ "name": "size",
1294
+ "type": "32 | 36",
77
1295
  "required": false,
78
- "default": "div",
79
- "description": "The element to render. Defaults to div; use a list, landmark or form element where the content is one."
1296
+ "default": "36",
1297
+ "description": "The height in px: 36 above a listing, 32 inside a table. Defaults to 36."
80
1298
  },
81
1299
  {
82
1300
  "name": "ref",
83
- "type": "Ref<HTMLElement>",
1301
+ "type": "Ref<HTMLInputElement>",
84
1302
  "required": false,
85
- "description": "Receives the rendered element."
1303
+ "description": "Receives the input element."
86
1304
  }
87
1305
  ],
88
1306
  "usage": {
89
- "whenToUse": "- Children side by side with a fixed space between them: a toolbar, a row of tags, a label next to a value\n- Children that spread along the row (`justify`: start, center, end or between) or move to the next line when the row is full (`wrap`)\n- A gap taken from the design system's spacing scale: 0, 1, 2, 4, 6, 7, 8, 11, 12, 14, 24, 32 or 80 px\n- A row that is also a list or a landmark: `as` renders `ul`, `ol`, `li`, `nav`, `header`, `footer`, `main`, `aside`, `section`, `form` or `fieldset` instead of `div`",
90
- "whenNotToUse": "- Children one under another: use Stack\n- Columns that have to line up from one row to the next: Inline lays out one row at a time, and wave 0 has no grid\n- Space around one child or at the edge of a container: Inline spaces siblings only, and has no padding or margin\n- Styling: Inline takes no `className` and no `style`\n- A list without markers: `as=\"ul\"` and `as=\"ol\"` leave the browser's list markers as they are",
91
- "example": "<Inline gap={8} justify=\"between\" wrap>\n <div>Orders</div>\n <Inline gap={4}>\n <div>Export</div>\n <div>Filter</div>\n </Inline>\n</Inline>"
1307
+ "whenToUse": "- The search above a listing or inside a table, where typing narrows the rows\n- A field that needs a clear button and Escape to clear",
1308
+ "whenNotToUse": "- A search across the whole product from the top bar: use GlobalSearch\n- A field that must wait before searching: debounce in the app, the field reports every keystroke",
1309
+ "example": "<SearchField label=\"Search products\" placeholder=\"Search by name, SKU\" value={query} onValueChange={setQuery} />"
1310
+ }
1311
+ },
1312
+ {
1313
+ "name": "SideNav",
1314
+ "import": "import { SideNav } from '@ratio-ui/core'",
1315
+ "description": "The 240px side navigation of the admin: an account switcher, blocks of rows with nested groups, counts and collapsible labels, and a footer pinned at the bottom. On a phone it becomes a drawer: inside AppShell the menu button in TopBar opens it and choosing a row closes it; used alone, the caller opens it with `open` and closes it in `onOpenChange`.",
1316
+ "props": [
1317
+ {
1318
+ "name": "label",
1319
+ "type": "string",
1320
+ "required": true,
1321
+ "description": "The accessible name of the navigation landmark."
1322
+ },
1323
+ {
1324
+ "name": "sections",
1325
+ "type": "SideNavSection[]",
1326
+ "required": true,
1327
+ "description": "The blocks of rows, top to bottom."
1328
+ },
1329
+ {
1330
+ "name": "active",
1331
+ "type": "string",
1332
+ "required": false,
1333
+ "description": "The id of the current row. It is marked and its group opens."
1334
+ },
1335
+ {
1336
+ "name": "onSelect",
1337
+ "type": "((id: string) => void)",
1338
+ "required": false,
1339
+ "description": "Called with the id of the row pressed."
1340
+ },
1341
+ {
1342
+ "name": "account",
1343
+ "type": "SideNavAccount",
1344
+ "required": false,
1345
+ "description": "The account switcher shown above the rows."
1346
+ },
1347
+ {
1348
+ "name": "footer",
1349
+ "type": "ReactNode",
1350
+ "required": false,
1351
+ "description": "Content pinned at the bottom, such as Settings."
1352
+ },
1353
+ {
1354
+ "name": "open",
1355
+ "type": "boolean",
1356
+ "required": false,
1357
+ "description": "On a phone, whether the drawer is open."
1358
+ },
1359
+ {
1360
+ "name": "onOpenChange",
1361
+ "type": "((open: boolean) => void)",
1362
+ "required": false,
1363
+ "description": "On a phone, called when the drawer asks to open or close."
1364
+ }
1365
+ ],
1366
+ "usage": {
1367
+ "whenToUse": "- The admin's left navigation: grouped links with nested items, counts and a pinned footer\n- A phone layout where the same navigation slides in as a drawer",
1368
+ "whenNotToUse": "- Switching between views inside one page: use Tabs or FilterTabs\n- A menu of actions: use DropdownMenu",
1369
+ "example": "<SideNav\n label=\"Main\"\n active=\"orders\"\n onSelect={setView}\n sections={[{ id: 'main', items: [{ id: 'home', label: 'Home', icon: 'nav-home' }, { id: 'orders', label: 'Orders', count: '12' }] }]}\n footer={<a href=\"/settings\">Settings</a>}\n/>"
1370
+ }
1371
+ },
1372
+ {
1373
+ "name": "Skeleton",
1374
+ "import": "import { Skeleton } from '@ratio-ui/core'",
1375
+ "description": "A static placeholder for content that is still loading on a first load. It is hidden from assistive technology: the container that holds it says it is busy. Its fill and animation come from the loading tokens, so it does not move.",
1376
+ "props": [
1377
+ {
1378
+ "name": "shape",
1379
+ "type": "\"block\" | \"circle\" | \"line\"",
1380
+ "required": false,
1381
+ "default": "line",
1382
+ "description": "The shape of the placeholder. Defaults to line. A line and a block fill the width of their parent."
1383
+ }
1384
+ ],
1385
+ "usage": {
1386
+ "whenToUse": "- A first load, where the shape of the content is known: a line for text, a block for a tile, a circle for an avatar\n- Under real headings in a table, so the columns stay put while the rows load",
1387
+ "whenNotToUse": "- A refetch of content already on screen: keep the content and show the thin progress bar\n- A wait with no known shape: use Spinner\n- A load that has failed or returned nothing: use EmptyState",
1388
+ "example": "<Skeleton shape=\"line\" />\n<Skeleton shape=\"circle\" />"
1389
+ }
1390
+ },
1391
+ {
1392
+ "name": "Spinner",
1393
+ "import": "import { Spinner } from '@ratio-ui/core'",
1394
+ "description": "A small ring for a wait with no known shape. It is static until the designer answers question 5: its motion is the --rt-spinner-animation token, none for now, so the ring is drawn from the progress colours and the status role says what it means.",
1395
+ "props": [
1396
+ {
1397
+ "name": "label",
1398
+ "type": "string",
1399
+ "required": false,
1400
+ "default": "Loading",
1401
+ "description": "The accessible name, announced to assistive technology. Defaults to Loading."
1402
+ }
1403
+ ],
1404
+ "usage": {
1405
+ "whenToUse": "- A short wait where the shape of the result is not known, such as a button that saves or a panel that opens",
1406
+ "whenNotToUse": "- A first load of a list or table: use Skeleton rows\n- A refetch of content on screen: use the thin progress bar",
1407
+ "example": "<Spinner label=\"Saving\" />"
92
1408
  }
93
1409
  },
94
1410
  {
@@ -129,6 +1445,212 @@
129
1445
  "example": "<Stack gap={12}>\n <div>Settlement summary</div>\n <Stack gap={4} align=\"start\">\n <div>Paid out every weekday</div>\n <div>Next payout on Monday</div>\n </Stack>\n</Stack>"
130
1446
  }
131
1447
  },
1448
+ {
1449
+ "name": "StatusPill",
1450
+ "import": "import { StatusPill } from '@ratio-ui/core'",
1451
+ "description": "A 20px pill with a 4px radius and a 1px border that names the state of a row, such as a product's status in a listing.",
1452
+ "props": [
1453
+ {
1454
+ "name": "tone",
1455
+ "type": "\"neutral\" | \"success\" | \"warning\"",
1456
+ "required": false,
1457
+ "default": "neutral",
1458
+ "description": "The colour of the pill. Defaults to neutral."
1459
+ },
1460
+ {
1461
+ "name": "ref",
1462
+ "type": "Ref<HTMLSpanElement>",
1463
+ "required": false,
1464
+ "description": "Receives the rendered span."
1465
+ }
1466
+ ],
1467
+ "usage": {
1468
+ "whenToUse": "- The state of a row or record in a listing: Draft, Active, Hidden, Low, Pending\n- A single-word status, with success for a good state and warning for one that needs a look",
1469
+ "whenNotToUse": "- A count or a tag that is not a status\n- A state that needs its own colour: the design system has two tones, and neutral is plain text until question 3 is answered\n- A button or a link: the pill is not interactive",
1470
+ "example": "<StatusPill tone=\"success\">Active</StatusPill>"
1471
+ }
1472
+ },
1473
+ {
1474
+ "name": "Table",
1475
+ "import": "import { Table } from '@ratio-ui/core'",
1476
+ "description": "A data table in the design system's Listing look: a 36px heading row over 48px rows split by 1px hairlines, 12px uppercase headings and a fill on the row under the pointer. It scrolls sideways inside its own box when the columns do not fit. Compose it from TableHeader, TableBody, TableRow, TableHead and TableCell.",
1477
+ "props": [],
1478
+ "usage": {
1479
+ "whenToUse": "- Rows of records with the same columns: products, orders, customers\n- A list that needs headings, right-aligned numbers and a hover fill\n- Static content composed by hand; for sorting, selection and paging use DataTable",
1480
+ "whenNotToUse": "- Layout: a table is for data, so use Stack and Inline to arrange a page\n- A single record's fields: use a definition list or a card\n- Styling: the parts take no `className` and no `style`",
1481
+ "example": "<Table>\n <TableHeader>\n <TableRow>\n <TableHead>Product</TableHead>\n <TableHead numeric>Stock</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n <TableRow>\n <TableCell>RYZE - 2mg</TableCell>\n <TableCell numeric>1200</TableCell>\n </TableRow>\n </TableBody>\n</Table>"
1482
+ },
1483
+ "parts": [
1484
+ {
1485
+ "name": "TableBody",
1486
+ "import": "import { TableBody } from '@ratio-ui/core'",
1487
+ "description": "The data rows of a table. Its rows take the hover fill.",
1488
+ "props": []
1489
+ },
1490
+ {
1491
+ "name": "TableCaption",
1492
+ "import": "import { TableCaption } from '@ratio-ui/core'",
1493
+ "description": "The table's title for assistive technology, shown under the table in the subdued colour.",
1494
+ "props": []
1495
+ },
1496
+ {
1497
+ "name": "TableCell",
1498
+ "import": "import { TableCell } from '@ratio-ui/core'",
1499
+ "description": "One cell. Text stays on one line; a numeric cell is right aligned with tabular figures so digits line up.",
1500
+ "props": [
1501
+ {
1502
+ "name": "numeric",
1503
+ "type": "boolean",
1504
+ "required": false,
1505
+ "default": "false",
1506
+ "description": "Aligns the cell to the right with tabular figures, for a number. Defaults to false, which aligns it to the left."
1507
+ }
1508
+ ]
1509
+ },
1510
+ {
1511
+ "name": "TableFooter",
1512
+ "import": "import { TableFooter } from '@ratio-ui/core'",
1513
+ "description": "The summary rows at the foot of a table, such as totals. Its rows take no hover fill.",
1514
+ "props": []
1515
+ },
1516
+ {
1517
+ "name": "TableHead",
1518
+ "import": "import { TableHead } from '@ratio-ui/core'",
1519
+ "description": "A column heading: 12px uppercase text in the subdued colour. It is a column header unless `scope` says otherwise.",
1520
+ "props": [
1521
+ {
1522
+ "name": "numeric",
1523
+ "type": "boolean",
1524
+ "required": false,
1525
+ "default": "false",
1526
+ "description": "Aligns the heading to the right, over a column of numbers. Defaults to false, which aligns it to the left."
1527
+ }
1528
+ ]
1529
+ },
1530
+ {
1531
+ "name": "TableHeader",
1532
+ "import": "import { TableHeader } from '@ratio-ui/core'",
1533
+ "description": "The heading rows of a table. Its rows take no hover fill.",
1534
+ "props": []
1535
+ },
1536
+ {
1537
+ "name": "TableRow",
1538
+ "import": "import { TableRow } from '@ratio-ui/core'",
1539
+ "description": "One row, 48px high (36px in the heading), with a hairline under it. A body row is filled when the pointer is over it or it is selected.",
1540
+ "props": [
1541
+ {
1542
+ "name": "selected",
1543
+ "type": "boolean",
1544
+ "required": false,
1545
+ "default": "false",
1546
+ "description": "Keeps the hover fill on the row to show it is chosen, for example when its checkbox is ticked. Defaults to false."
1547
+ }
1548
+ ]
1549
+ }
1550
+ ]
1551
+ },
1552
+ {
1553
+ "name": "TableLoader",
1554
+ "import": "import { TableLoader } from '@ratio-ui/core'",
1555
+ "description": "What a table shows on its first load: the real headings over static skeleton rows, and a count line such as \"72 of 1000 products\". The table is marked busy and the fake rows are hidden from screen readers. Use it for the first load only; later loads keep the old rows.",
1556
+ "props": [
1557
+ {
1558
+ "name": "columns",
1559
+ "type": "TableLoaderColumn[]",
1560
+ "required": true,
1561
+ "description": "The columns, in order."
1562
+ },
1563
+ {
1564
+ "name": "rows",
1565
+ "type": "number",
1566
+ "required": false,
1567
+ "default": "5",
1568
+ "description": "How many placeholder rows to draw. Defaults to 5."
1569
+ },
1570
+ {
1571
+ "name": "shown",
1572
+ "type": "number",
1573
+ "required": true,
1574
+ "description": "How many results the first page will hold, for the count line."
1575
+ },
1576
+ {
1577
+ "name": "total",
1578
+ "type": "number",
1579
+ "required": true,
1580
+ "description": "How many results there are in all."
1581
+ },
1582
+ {
1583
+ "name": "noun",
1584
+ "type": "string",
1585
+ "required": true,
1586
+ "description": "What the results are called, in the plural: \"products\"."
1587
+ }
1588
+ ],
1589
+ "usage": {
1590
+ "whenToUse": "- The first load of a list: it shows the real headings over static skeleton rows, with a count such as \"72 of 1000 products\"\n- When the column headings are known before the data arrives",
1591
+ "whenNotToUse": "- Later loads such as a new page or sort: keep the old rows and show them as busy\n- Content that is not a table: use Skeleton on its own\n- Styling: it takes no `className` and no `style`",
1592
+ "example": "<TableLoader\n aria-label=\"Products\"\n columns={[{ id: 'name', header: 'Product' }, { id: 'stock', header: 'Stock', numeric: true }]}\n shown={72}\n total={1000}\n noun=\"products\"\n/>"
1593
+ }
1594
+ },
1595
+ {
1596
+ "name": "Tabs",
1597
+ "import": "import { Tabs } from '@ratio-ui/core'",
1598
+ "description": "Sections of one page, switched by an underlined row of tabs: ABC analysis, the sections of a settings page. Holds the selected value, uncontrolled with defaultValue or controlled with value and onValueChange. To filter a list use FilterTabs, which looks like the design system's chip tab.",
1599
+ "props": [
1600
+ {
1601
+ "name": "render",
1602
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsRootState>",
1603
+ "required": false,
1604
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1605
+ }
1606
+ ],
1607
+ "usage": {
1608
+ "whenToUse": "- Sections of one page that share a heading, such as the steps of an ABC analysis or the parts of a settings page\n- Content that is shown one section at a time, with the section named by an underlined tab\n- A row of tabs whose panels sit on the same page: the tab list and the panels are tied by value",
1609
+ "whenNotToUse": "- A single choice that filters a list, such as All, Active and Inactive above a table: use FilterTabs\n- Moving to another page: use a link\n- Steps that must be done in order: use a wizard",
1610
+ "example": "<Tabs defaultValue=\"rules\">\n <TabsList aria-label=\"Sections\">\n <TabsTrigger value=\"rules\">Rules</TabsTrigger>\n <TabsTrigger value=\"history\">History</TabsTrigger>\n </TabsList>\n <TabsContent value=\"rules\">Rules</TabsContent>\n <TabsContent value=\"history\">History</TabsContent>\n</Tabs>"
1611
+ },
1612
+ "parts": [
1613
+ {
1614
+ "name": "TabsContent",
1615
+ "import": "import { TabsContent } from '@ratio-ui/core'",
1616
+ "description": "The panel of the tab with the same value; only the selected one is shown.",
1617
+ "props": [
1618
+ {
1619
+ "name": "render",
1620
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsPanelState>",
1621
+ "required": false,
1622
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1623
+ }
1624
+ ]
1625
+ },
1626
+ {
1627
+ "name": "TabsList",
1628
+ "import": "import { TabsList } from '@ratio-ui/core'",
1629
+ "description": "The row of tabs, with a hairline under it. Give it an aria-label that names the sections.",
1630
+ "props": [
1631
+ {
1632
+ "name": "render",
1633
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsListState>",
1634
+ "required": false,
1635
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1636
+ }
1637
+ ]
1638
+ },
1639
+ {
1640
+ "name": "TabsTrigger",
1641
+ "import": "import { TabsTrigger } from '@ratio-ui/core'",
1642
+ "description": "One tab: 36px tall, with a 2px blue underline and a blue semibold label when selected. A disabled tab is at 50%.",
1643
+ "props": [
1644
+ {
1645
+ "name": "render",
1646
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TabsTabState>",
1647
+ "required": false,
1648
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1649
+ }
1650
+ ]
1651
+ }
1652
+ ]
1653
+ },
132
1654
  {
133
1655
  "name": "Text",
134
1656
  "import": "import { Text } from '@ratio-ui/core'",
@@ -174,6 +1696,160 @@
174
1696
  "whenNotToUse": "- Text in a button: buttons take the design system's two button styles, which are not Text variants\n- A colour that is not a tone: `color` is a type error, and so are `className` and `style`\n- A style that is not in the type scale: `variant=\"h2\"` is a type error, so pick the nearest of the 13\n- Space around text: use Stack or Inline\n- Several lines cut at a number of lines: `truncate` keeps one line",
175
1697
  "example": "<Text as=\"h1\" variant=\"h1\">Settlements</Text>\n<Text tone=\"subdued\">Paid out every weekday</Text>"
176
1698
  }
1699
+ },
1700
+ {
1701
+ "name": "Toaster",
1702
+ "import": "import { Toaster } from '@ratio-ui/core'",
1703
+ "description": "Mounts the toast viewport, bottom right, once for the app. Messages from toast() appear in it as plain panels with the ring outline, stacked, with no entrance animation.",
1704
+ "props": [
1705
+ {
1706
+ "name": "closeLabel",
1707
+ "type": "string",
1708
+ "required": false,
1709
+ "default": "Close notification",
1710
+ "description": "The accessible name of each close button. Defaults to Close notification."
1711
+ }
1712
+ ],
1713
+ "usage": {
1714
+ "whenToUse": "- Telling the user what happened after an action: a saved product, a started sync, a failed request\n- One of three kinds: `toast.success`, `toast.info` or `toast.error`; each is announced to screen readers",
1715
+ "whenNotToUse": "- A decision the user must make: use a Dialog\n- A message that has to stay on the page: use a banner\n- Field validation: show it on the field",
1716
+ "example": "<Toaster />\n\ntoast.success('Product saved')\ntoast.error('Could not save', { description: 'Try again' })"
1717
+ }
1718
+ },
1719
+ {
1720
+ "name": "Tooltip",
1721
+ "import": "import { Tooltip } from '@ratio-ui/core'",
1722
+ "description": "A short hint for a control or term, shown on hover and keyboard focus. Put a TooltipTrigger and a TooltipContent inside it.",
1723
+ "props": [],
1724
+ "usage": {
1725
+ "whenToUse": "- A short hint for an icon-only control or an \"i\" icon: it opens on hover and on keyboard focus\n- Extra detail that is not needed to finish the task, one short sentence\n- Wrap the app once in TooltipProvider so the tooltips share their open delay",
1726
+ "whenNotToUse": "- A term in running text that explains itself: use Definition, which keyboard and touch users can open\n- Anything with a link, button or form inside: use Popover\n- Information that touch users need: a tooltip does not open on touch, so show the text on the screen",
1727
+ "example": "<TooltipProvider>\n <Tooltip>\n <TooltipTrigger>Settlement</TooltipTrigger>\n <TooltipContent>Paid out every weekday</TooltipContent>\n </Tooltip>\n</TooltipProvider>"
1728
+ },
1729
+ "parts": [
1730
+ {
1731
+ "name": "TooltipContent",
1732
+ "import": "import { TooltipContent } from '@ratio-ui/core'",
1733
+ "description": "The tooltip text: a dark panel with light text, no shadow and no animation. It sits above the trigger by default.",
1734
+ "props": [
1735
+ {
1736
+ "name": "render",
1737
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TooltipPopupState>",
1738
+ "required": false,
1739
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1740
+ }
1741
+ ]
1742
+ },
1743
+ {
1744
+ "name": "TooltipProvider",
1745
+ "import": "import { TooltipProvider } from '@ratio-ui/core'",
1746
+ "description": "Shares the open delay between the tooltips inside it. Wrap the app, or a group of tooltips, once. The delay defaults to 0 ms.",
1747
+ "props": []
1748
+ },
1749
+ {
1750
+ "name": "TooltipTrigger",
1751
+ "import": "import { TooltipTrigger } from '@ratio-ui/core'",
1752
+ "description": "The element that opens the tooltip on hover and focus. It renders a button unless render says otherwise.",
1753
+ "props": [
1754
+ {
1755
+ "name": "render",
1756
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, TooltipTriggerState>",
1757
+ "required": false,
1758
+ "description": "Allows you to replace the component's HTML element with a different tag, or compose it with another component.\n\nAccepts a `ReactElement` or a function that returns the element to render."
1759
+ }
1760
+ ]
1761
+ }
1762
+ ]
1763
+ },
1764
+ {
1765
+ "name": "TopBar",
1766
+ "import": "import { TopBar } from '@ratio-ui/core'",
1767
+ "description": "The bar across the top of every page: logo, search, actions, the store switcher and the account. It is 60px high on a desktop and 52px on a phone, with a hairline under it. The parts are slots, so the bar does not depend on their components. Inside an AppShell with a side nav it shows a menu button on phones, which opens the side nav drawer.",
1768
+ "props": [
1769
+ {
1770
+ "name": "logo",
1771
+ "type": "ReactNode",
1772
+ "required": false,
1773
+ "description": "The logo, usually an image. When it is left out the bar shows the text wordmark instead (--rt-wordmark)."
1774
+ },
1775
+ {
1776
+ "name": "wordmarkLabel",
1777
+ "type": "string",
1778
+ "required": false,
1779
+ "default": "Ratio",
1780
+ "description": "The accessible name of the text wordmark, shown when there is no logo. Defaults to Ratio."
1781
+ },
1782
+ {
1783
+ "name": "menuLabel",
1784
+ "type": "string",
1785
+ "required": false,
1786
+ "default": "Open navigation menu",
1787
+ "description": "The accessible name of the phone menu button that opens the side navigation. Defaults to Open navigation menu."
1788
+ },
1789
+ {
1790
+ "name": "search",
1791
+ "type": "ReactNode",
1792
+ "required": false,
1793
+ "description": "The global search, for example GlobalSearch. It takes the free space in the middle."
1794
+ },
1795
+ {
1796
+ "name": "actions",
1797
+ "type": "ReactNode",
1798
+ "required": false,
1799
+ "description": "The buttons at the right, such as help and notifications."
1800
+ },
1801
+ {
1802
+ "name": "storeSwitcher",
1803
+ "type": "ReactNode",
1804
+ "required": false,
1805
+ "description": "The store switcher, for example StoreSwitcher. It sits at the right, after the actions."
1806
+ },
1807
+ {
1808
+ "name": "account",
1809
+ "type": "ReactNode",
1810
+ "required": false,
1811
+ "description": "The account at the far right, such as an Avatar or a menu of the signed-in person."
1812
+ }
1813
+ ],
1814
+ "usage": {
1815
+ "whenToUse": "- The bar across the top of the admin: logo, global search, actions, the store switcher and the account\n- Inside AppShell's `topBar` slot",
1816
+ "whenNotToUse": "- A heading for one page: use PageHeader\n- Navigation between sections: use SideNav",
1817
+ "example": "<TopBar\n search={<GlobalSearch />}\n actions={<Button variant=\"ghost\">Help</Button>}\n storeSwitcher={<StoreSwitcher stores={stores} storeId={storeId} onStoreChange={setStoreId} />}\n account={<Avatar name=\"Naman Gupta\" />}\n/>"
1818
+ },
1819
+ "parts": [
1820
+ {
1821
+ "name": "StoreSwitcher",
1822
+ "import": "import { StoreSwitcher } from '@ratio-ui/core'",
1823
+ "description": "The store switcher of TopBar: a square avatar, the store name and a chevron that open a menu of the stores the app passes. On a phone only the avatar shows. Pass it to TopBar's `storeSwitcher` slot.",
1824
+ "props": [
1825
+ {
1826
+ "name": "stores",
1827
+ "type": "StoreOption[]",
1828
+ "required": true,
1829
+ "description": "The stores the person can switch between, as the app passes them."
1830
+ },
1831
+ {
1832
+ "name": "storeId",
1833
+ "type": "string",
1834
+ "required": true,
1835
+ "description": "The id of the current store."
1836
+ },
1837
+ {
1838
+ "name": "onStoreChange",
1839
+ "type": "((id: string) => void)",
1840
+ "required": false,
1841
+ "description": "Called with the id of the store chosen."
1842
+ },
1843
+ {
1844
+ "name": "label",
1845
+ "type": "string",
1846
+ "required": false,
1847
+ "default": "Switch store",
1848
+ "description": "The caption of its menu. Defaults to Switch store. The button is named by the store name."
1849
+ }
1850
+ ]
1851
+ }
1852
+ ]
177
1853
  }
178
1854
  ]
179
1855
  }