@tempoxyz/ds 0.3.9 → 0.3.11

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 (128) hide show
  1. package/dist/docs/AGENTS.md +9 -0
  2. package/dist/docs/components/accordion.md +489 -0
  3. package/dist/docs/components/address-card.md +147 -0
  4. package/dist/docs/components/agent-logo.md +194 -0
  5. package/dist/docs/components/alert-dialog.md +631 -0
  6. package/dist/docs/components/alert.md +448 -0
  7. package/dist/docs/components/amount-input.md +305 -0
  8. package/dist/docs/components/amount.md +794 -0
  9. package/dist/docs/components/app-shell.md +559 -0
  10. package/dist/docs/components/asset-chip.md +294 -0
  11. package/dist/docs/components/autocomplete.md +619 -0
  12. package/dist/docs/components/avatar.md +321 -0
  13. package/dist/docs/components/badge.md +339 -0
  14. package/dist/docs/components/badged-logo.md +116 -0
  15. package/dist/docs/components/banner.md +379 -0
  16. package/dist/docs/components/bar-chart.md +208 -0
  17. package/dist/docs/components/breadcrumbs.md +380 -0
  18. package/dist/docs/components/button-group.md +286 -0
  19. package/dist/docs/components/button.md +650 -0
  20. package/dist/docs/components/calendar.md +375 -0
  21. package/dist/docs/components/callout.md +96 -0
  22. package/dist/docs/components/checkbox-group.md +521 -0
  23. package/dist/docs/components/checkbox.md +467 -0
  24. package/dist/docs/components/choice-group.md +516 -0
  25. package/dist/docs/components/choicebox.md +641 -0
  26. package/dist/docs/components/code.md +100 -0
  27. package/dist/docs/components/combobox.md +939 -0
  28. package/dist/docs/components/container.md +257 -0
  29. package/dist/docs/components/copy-button.md +642 -0
  30. package/dist/docs/components/date-picker.md +514 -0
  31. package/dist/docs/components/dialog.md +1332 -0
  32. package/dist/docs/components/drawer.md +966 -0
  33. package/dist/docs/components/empty.md +340 -0
  34. package/dist/docs/components/field.md +570 -0
  35. package/dist/docs/components/fieldset.md +398 -0
  36. package/dist/docs/components/frame.md +434 -0
  37. package/dist/docs/components/gauge.md +299 -0
  38. package/dist/docs/components/get-started-visual.md +117 -0
  39. package/dist/docs/components/grid.md +288 -0
  40. package/dist/docs/components/hero-visual.md +155 -0
  41. package/dist/docs/components/hex.md +487 -0
  42. package/dist/docs/components/icon-tile.md +292 -0
  43. package/dist/docs/components/identicon.md +371 -0
  44. package/dist/docs/components/inline.md +233 -0
  45. package/dist/docs/components/inset.md +225 -0
  46. package/dist/docs/components/kbd.md +423 -0
  47. package/dist/docs/components/list-group.md +981 -0
  48. package/dist/docs/components/loading-dots.md +237 -0
  49. package/dist/docs/components/menu.md +934 -0
  50. package/dist/docs/components/native-select.md +204 -0
  51. package/dist/docs/components/network-logo.md +248 -0
  52. package/dist/docs/components/number-field.md +543 -0
  53. package/dist/docs/components/otp-field.md +616 -0
  54. package/dist/docs/components/pagination.md +516 -0
  55. package/dist/docs/components/popover.md +544 -0
  56. package/dist/docs/components/progress.md +388 -0
  57. package/dist/docs/components/qr.md +314 -0
  58. package/dist/docs/components/radio-group.md +528 -0
  59. package/dist/docs/components/secret-value.md +406 -0
  60. package/dist/docs/components/select.md +751 -0
  61. package/dist/docs/components/separator.md +379 -0
  62. package/dist/docs/components/sidebar-menu.md +922 -0
  63. package/dist/docs/components/skeleton.md +517 -0
  64. package/dist/docs/components/slider.md +539 -0
  65. package/dist/docs/components/snippet.md +424 -0
  66. package/dist/docs/components/spinner.md +216 -0
  67. package/dist/docs/components/split-bar.md +139 -0
  68. package/dist/docs/components/split-button.md +464 -0
  69. package/dist/docs/components/stack.md +299 -0
  70. package/dist/docs/components/status-chip.md +226 -0
  71. package/dist/docs/components/status-indicator.md +119 -0
  72. package/dist/docs/components/status-row.md +174 -0
  73. package/dist/docs/components/stepper.md +451 -0
  74. package/dist/docs/components/success-hero.md +157 -0
  75. package/dist/docs/components/surface.md +639 -0
  76. package/dist/docs/components/switch.md +472 -0
  77. package/dist/docs/components/table.md +967 -0
  78. package/dist/docs/components/tabs.md +627 -0
  79. package/dist/docs/components/tempo-logo.md +185 -0
  80. package/dist/docs/components/text-input.md +624 -0
  81. package/dist/docs/components/textarea.md +352 -0
  82. package/dist/docs/components/toast.md +535 -0
  83. package/dist/docs/components/token-logo.md +371 -0
  84. package/dist/docs/components/token.md +247 -0
  85. package/dist/docs/components/tooltip.md +490 -0
  86. package/dist/docs/components/verification-code.md +116 -0
  87. package/dist/docs/components/visual.md +249 -0
  88. package/dist/docs/components.md +203 -0
  89. package/dist/docs/foundations/accessibility.md +377 -0
  90. package/dist/docs/foundations/agents.md +150 -0
  91. package/dist/docs/foundations/color.md +522 -0
  92. package/dist/docs/foundations/content/tone.md +194 -0
  93. package/dist/docs/foundations/content/voice.md +194 -0
  94. package/dist/docs/foundations/content/writing-style.md +406 -0
  95. package/dist/docs/foundations/iconography.md +282 -0
  96. package/dist/docs/foundations/intent.md +360 -0
  97. package/dist/docs/foundations/layout.md +508 -0
  98. package/dist/docs/foundations/linter.md +296 -0
  99. package/dist/docs/foundations/motion.md +151 -0
  100. package/dist/docs/foundations/radius-and-padding.md +86 -0
  101. package/dist/docs/foundations/typography.md +266 -0
  102. package/dist/docs/index.md +137 -0
  103. package/dist/docs/introduction/faq.md +132 -0
  104. package/dist/docs/introduction/getting-started.md +377 -0
  105. package/dist/docs/patterns/blockchain-values.md +709 -0
  106. package/dist/docs/patterns/dashboards.md +638 -0
  107. package/dist/docs/patterns/destructive-actions.md +626 -0
  108. package/dist/docs/patterns/empty-states.md +612 -0
  109. package/dist/docs/patterns/errors-and-validation.md +808 -0
  110. package/dist/docs/patterns/feedback-and-status.md +518 -0
  111. package/dist/docs/patterns/forms.md +679 -0
  112. package/dist/docs/patterns/loading.md +630 -0
  113. package/dist/docs/patterns/navigation.md +397 -0
  114. package/dist/docs/patterns/payments.md +765 -0
  115. package/dist/docs/patterns/responsive-layout.md +997 -0
  116. package/dist/docs/patterns/tables-and-lists.md +964 -0
  117. package/dist/docs/products/tempo-console.md +671 -0
  118. package/dist/docs/products/tempo-performance.md +437 -0
  119. package/dist/docs/products/tempo-wallet.md +438 -0
  120. package/dist/web/components/OtpField.css +1 -1
  121. package/dist/web/components/OtpField.d.ts +1 -1
  122. package/dist/web/components/OtpField.d.ts.map +1 -1
  123. package/dist/web/components/OtpField.js +3 -4
  124. package/dist/web/components/OtpField.js.map +1 -1
  125. package/dist/web/components/OtpField.js.zyzz.json +1 -1
  126. package/dist/web/index.js.zyzz.json +1 -1
  127. package/package.json +1 -1
  128. package/src/web/components/OtpField.tsx +2 -24
@@ -0,0 +1,939 @@
1
+ # Combobox
2
+
3
+ A text input that filters a list of fixed options while people type.
4
+
5
+ [View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Combobox.tsx)
6
+
7
+ ## Overview
8
+
9
+ A text input with a listbox popup. The value must be one of the options.
10
+
11
+ ## Examples
12
+
13
+ ### Basic
14
+
15
+ ```tsx
16
+ import { Combobox } from '@tempoxyz/ds'
17
+ import { Search } from '@tempoxyz/ds/icons'
18
+ import { useState } from 'react'
19
+ import cbBtc from '../../coins/CbBTC.svg'
20
+ import ethereumEth from '../../coins/EthereumETH.svg'
21
+ import pathUsd from '../../coins/PathUSD.svg'
22
+ import usdce from '../../coins/USDCe.svg'
23
+ import usdt0 from '../../coins/USDT0.svg'
24
+
25
+ const coins: Record<string, string> = {
26
+ 'Bridged ETH': ethereumEth,
27
+ pathUSD: pathUsd,
28
+ Tether: usdt0,
29
+ 'USDC.e': usdce,
30
+ USDT0: usdt0,
31
+ 'Wrapped BTC': cbBtc,
32
+ }
33
+
34
+ const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']
35
+
36
+ function Coin(props: { asset: string }) {
37
+ return <img alt="" height={20} src={coins[props.asset]} width={20} />
38
+ }
39
+
40
+ function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'variant'>) {
41
+ const [value, setValue] = useState<string | null>('pathUSD')
42
+
43
+ return (
44
+ <Combobox.Root items={assets} onValueChange={setValue} value={value}>
45
+ <Combobox.InputGroup {...props}>
46
+ <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
47
+ <Combobox.Input aria-label="Asset" placeholder="Search assets" />
48
+ <Combobox.Clear />
49
+ <Combobox.Trigger />
50
+ </Combobox.InputGroup>
51
+ <Combobox.Popup>
52
+ <Combobox.Empty>No results</Combobox.Empty>
53
+ <Combobox.List>
54
+ {(asset: string) => (
55
+ <Combobox.Item key={asset} value={asset}>
56
+ <Combobox.ItemPrefix>
57
+ <Coin asset={asset} />
58
+ </Combobox.ItemPrefix>
59
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
60
+ </Combobox.Item>
61
+ )}
62
+ </Combobox.List>
63
+ </Combobox.Popup>
64
+ </Combobox.Root>
65
+ )
66
+ }
67
+
68
+ export function Demo() {
69
+ return <Single />
70
+ }
71
+ ```
72
+
73
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Basic.tsx)
74
+
75
+ ### Variant
76
+
77
+ Set `variant` on `Combobox.InputGroup` to `filled` (default) or `outlined`.
78
+
79
+ ```tsx
80
+ import { Combobox, style } from '@tempoxyz/ds'
81
+ import { Search } from '@tempoxyz/ds/icons'
82
+ import { useState } from 'react'
83
+ import cbBtc from '../../coins/CbBTC.svg'
84
+ import ethereumEth from '../../coins/EthereumETH.svg'
85
+ import pathUsd from '../../coins/PathUSD.svg'
86
+ import usdce from '../../coins/USDCe.svg'
87
+ import usdt0 from '../../coins/USDT0.svg'
88
+
89
+ const coins: Record<string, string> = {
90
+ 'Bridged ETH': ethereumEth,
91
+ pathUSD: pathUsd,
92
+ Tether: usdt0,
93
+ 'USDC.e': usdce,
94
+ USDT0: usdt0,
95
+ 'Wrapped BTC': cbBtc,
96
+ }
97
+
98
+ const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']
99
+
100
+ function Coin(props: { asset: string }) {
101
+ return <img alt="" height={20} src={coins[props.asset]} width={20} />
102
+ }
103
+
104
+ function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'variant'>) {
105
+ const [value, setValue] = useState<string | null>('pathUSD')
106
+
107
+ return (
108
+ <Combobox.Root items={assets} onValueChange={setValue} value={value}>
109
+ <Combobox.InputGroup {...props}>
110
+ <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
111
+ <Combobox.Input aria-label="Asset" placeholder="Search assets" />
112
+ <Combobox.Clear />
113
+ <Combobox.Trigger />
114
+ </Combobox.InputGroup>
115
+ <Combobox.Popup>
116
+ <Combobox.Empty>No results</Combobox.Empty>
117
+ <Combobox.List>
118
+ {(asset: string) => (
119
+ <Combobox.Item key={asset} value={asset}>
120
+ <Combobox.ItemPrefix>
121
+ <Coin asset={asset} />
122
+ </Combobox.ItemPrefix>
123
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
124
+ </Combobox.Item>
125
+ )}
126
+ </Combobox.List>
127
+ </Combobox.Popup>
128
+ </Combobox.Root>
129
+ )
130
+ }
131
+
132
+ export function Demo() {
133
+ return (
134
+ <div {...styles.grid()}>
135
+ <Single />
136
+ <Single variant="outlined" />
137
+ </div>
138
+ )
139
+ }
140
+
141
+ namespace styles {
142
+ export const grid = style({
143
+ alignItems: 'start',
144
+ display: 'grid',
145
+ gap: '12',
146
+ gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr)) !custom',
147
+ })
148
+ }
149
+ ```
150
+
151
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Variant.tsx)
152
+
153
+ ### Invalid
154
+
155
+ Set `invalid` on `Combobox.InputGroup` to style the frame, and `aria-invalid` on the input to expose the state.
156
+
157
+ ```tsx
158
+ import { Combobox } from '@tempoxyz/ds'
159
+ import { Search } from '@tempoxyz/ds/icons'
160
+ import { useState } from 'react'
161
+ import cbBtc from '../../coins/CbBTC.svg'
162
+ import ethereumEth from '../../coins/EthereumETH.svg'
163
+ import pathUsd from '../../coins/PathUSD.svg'
164
+ import usdce from '../../coins/USDCe.svg'
165
+ import usdt0 from '../../coins/USDT0.svg'
166
+
167
+ const coins: Record<string, string> = {
168
+ 'Bridged ETH': ethereumEth,
169
+ pathUSD: pathUsd,
170
+ Tether: usdt0,
171
+ 'USDC.e': usdce,
172
+ USDT0: usdt0,
173
+ 'Wrapped BTC': cbBtc,
174
+ }
175
+
176
+ const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']
177
+
178
+ function Coin(props: { asset: string }) {
179
+ return <img alt="" height={20} src={coins[props.asset]} width={20} />
180
+ }
181
+
182
+ function Single(props: Pick<Combobox.InputGroup.Props, 'invalid' | 'variant'>) {
183
+ const [value, setValue] = useState<string | null>('pathUSD')
184
+
185
+ return (
186
+ <Combobox.Root items={assets} onValueChange={setValue} value={value}>
187
+ <Combobox.InputGroup {...props}>
188
+ <Combobox.Prefix>{value ? <Coin asset={value} /> : <Search />}</Combobox.Prefix>
189
+ <Combobox.Input aria-label="Asset" placeholder="Search assets" />
190
+ <Combobox.Clear />
191
+ <Combobox.Trigger />
192
+ </Combobox.InputGroup>
193
+ <Combobox.Popup>
194
+ <Combobox.Empty>No results</Combobox.Empty>
195
+ <Combobox.List>
196
+ {(asset: string) => (
197
+ <Combobox.Item key={asset} value={asset}>
198
+ <Combobox.ItemPrefix>
199
+ <Coin asset={asset} />
200
+ </Combobox.ItemPrefix>
201
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
202
+ </Combobox.Item>
203
+ )}
204
+ </Combobox.List>
205
+ </Combobox.Popup>
206
+ </Combobox.Root>
207
+ )
208
+ }
209
+
210
+ export function Demo() {
211
+ return <Single invalid />
212
+ }
213
+ ```
214
+
215
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Invalid.tsx)
216
+
217
+ ### Multiple
218
+
219
+ Set `multiple` on `Combobox.Root` to select several values, shown as chips.
220
+
221
+ ```tsx
222
+ import { Combobox } from '@tempoxyz/ds'
223
+ import { useState } from 'react'
224
+
225
+ const assets = ['pathUSD', 'USDC.e', 'Tether', 'USDT0', 'Bridged ETH', 'Wrapped BTC']
226
+
227
+ function Multiple() {
228
+ const [value, setValue] = useState(['pathUSD', 'USDC.e'])
229
+
230
+ return (
231
+ <Combobox.Root items={assets} multiple onValueChange={setValue} value={value}>
232
+ <Combobox.InputGroup>
233
+ <Combobox.Value>
234
+ {(selected: string[]) => (
235
+ <Combobox.Chips aria-label="Selected assets">
236
+ {selected.map((asset) => (
237
+ <Combobox.Chip key={asset}>
238
+ {asset}
239
+ <Combobox.ChipRemove aria-label={`Remove ${asset}`} />
240
+ </Combobox.Chip>
241
+ ))}
242
+ <Combobox.Input
243
+ aria-label="Assets"
244
+ placeholder={selected.length ? '' : 'Add assets'}
245
+ />
246
+ </Combobox.Chips>
247
+ )}
248
+ </Combobox.Value>
249
+ </Combobox.InputGroup>
250
+ <Combobox.Popup>
251
+ <Combobox.Empty>No results</Combobox.Empty>
252
+ <Combobox.List>
253
+ {(asset: string) => (
254
+ <Combobox.Item key={asset} value={asset}>
255
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
256
+ </Combobox.Item>
257
+ )}
258
+ </Combobox.List>
259
+ </Combobox.Popup>
260
+ </Combobox.Root>
261
+ )
262
+ }
263
+
264
+ export function Demo() {
265
+ return <Multiple />
266
+ }
267
+ ```
268
+
269
+ [View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Combobox/Multiple.tsx)
270
+
271
+ ## Anatomy
272
+
273
+ Import the component and assemble its parts.
274
+
275
+ ```tsx
276
+ import { Combobox } from '@tempoxyz/ds'
277
+
278
+ <Combobox.Root>
279
+ <Combobox.InputGroup>
280
+ <Combobox.Prefix />
281
+ <Combobox.Value>
282
+ <Combobox.Chips>
283
+ <Combobox.Chip>
284
+ <Combobox.ChipRemove />
285
+ </Combobox.Chip>
286
+ <Combobox.Input />
287
+ </Combobox.Chips>
288
+ </Combobox.Value>
289
+ <Combobox.Clear />
290
+ <Combobox.Trigger />
291
+ </Combobox.InputGroup>
292
+ <Combobox.Popup>
293
+ <Combobox.Empty />
294
+ <Combobox.List>
295
+ <Combobox.Group>
296
+ <Combobox.GroupLabel />
297
+ <Combobox.Collection>
298
+ <Combobox.Item>
299
+ <Combobox.ItemPrefix />
300
+ <Combobox.ItemText />
301
+ <Combobox.ItemSuffix />
302
+ </Combobox.Item>
303
+ </Combobox.Collection>
304
+ </Combobox.Group>
305
+ <Combobox.Separator />
306
+ </Combobox.List>
307
+ </Combobox.Popup>
308
+ </Combobox.Root>
309
+ ```
310
+
311
+ ## Guidelines
312
+
313
+ ### When to Use
314
+
315
+ * Choose one item from a long, fixed list filtered by typing, such as the asset to send or the country on an account.
316
+ * Choose several items from that list with `multiple`, such as the assets a report covers.
317
+
318
+ ### When Not to Use
319
+
320
+ * If people can scan the whole list without typing, use [Select](/docs/components/select).
321
+ * If any typed text is a valid value, such as a recipient address, use [Autocomplete](/docs/components/autocomplete).
322
+ * Use [RadioGroup](/docs/components/radio-group) or [ChoiceGroup](/docs/components/choice-group) for a few options that fit on screen.
323
+ * Use [CheckboxGroup](/docs/components/checkbox-group) for several options from a short list.
324
+
325
+ ### Keep a Visible Label
326
+
327
+ The placeholder disappears on input, so it can't name the field.
328
+
329
+ **Do:** Label the field and show the selected asset's icon in `Combobox.Prefix`.
330
+
331
+ ```tsx
332
+ /**
333
+ * Do example for "Keep a Visible Label" on the Combobox page.
334
+ * Label the field and show the selected asset's icon in `Combobox.Prefix`.
335
+ * @module
336
+ */
337
+ import { Combobox, Field, style } from '@tempoxyz/ds'
338
+ import usdce from '../../coins/USDCe.svg'
339
+
340
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
341
+
342
+ /** Renders the static preview, which the guideline card shows inert. */
343
+ export function Demo() {
344
+ return (
345
+ <Field.Root {...styles.field()}>
346
+ <Field.Label>Asset</Field.Label>
347
+ <Combobox.Root defaultValue="USDC.e" items={assets}>
348
+ <Combobox.InputGroup>
349
+ <Combobox.Prefix>
350
+ <img alt="" height={20} src={usdce} width={20} />
351
+ </Combobox.Prefix>
352
+ <Combobox.Input placeholder="Search assets" />
353
+ <Combobox.Trigger />
354
+ </Combobox.InputGroup>
355
+ <Combobox.Popup>
356
+ <Combobox.Empty>No assets found</Combobox.Empty>
357
+ <Combobox.List>
358
+ {(asset: string) => (
359
+ <Combobox.Item key={asset} value={asset}>
360
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
361
+ </Combobox.Item>
362
+ )}
363
+ </Combobox.List>
364
+ </Combobox.Popup>
365
+ </Combobox.Root>
366
+ </Field.Root>
367
+ )
368
+ }
369
+
370
+ namespace styles {
371
+ export const field = style({ width: '100% !custom' })
372
+ }
373
+ ```
374
+
375
+ **Don't:** Rely on the placeholder to say what the field is for.
376
+
377
+ ```tsx
378
+ /**
379
+ * Don't example for "Keep a Visible Label" on the Combobox page.
380
+ * Don't rely on the placeholder to say what the field is for.
381
+ * @module
382
+ */
383
+ import { Combobox, style } from '@tempoxyz/ds'
384
+ import { Search } from '@tempoxyz/ds/icons'
385
+
386
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
387
+
388
+ /** Renders the static preview, which the guideline card shows inert. */
389
+ export function Demo() {
390
+ return (
391
+ <Combobox.Root items={assets}>
392
+ <Combobox.InputGroup {...styles.group()}>
393
+ <Combobox.Prefix>
394
+ <Search />
395
+ </Combobox.Prefix>
396
+ <Combobox.Input aria-label="Asset" placeholder="Search assets" />
397
+ <Combobox.Trigger />
398
+ </Combobox.InputGroup>
399
+ <Combobox.Popup>
400
+ <Combobox.Empty>No assets found</Combobox.Empty>
401
+ <Combobox.List>
402
+ {(asset: string) => (
403
+ <Combobox.Item key={asset} value={asset}>
404
+ <Combobox.ItemText>{asset}</Combobox.ItemText>
405
+ </Combobox.Item>
406
+ )}
407
+ </Combobox.List>
408
+ </Combobox.Popup>
409
+ </Combobox.Root>
410
+ )
411
+ }
412
+
413
+ namespace styles {
414
+ export const group = style({ width: '100% !custom' })
415
+ }
416
+ ```
417
+
418
+ ### Always Render Combobox.Empty
419
+
420
+ `Combobox.Empty` shows its children when nothing matches and announces them in a polite status region.
421
+
422
+ **Do:** Render a short, specific message, such as No assets found.
423
+
424
+ ```tsx
425
+ /**
426
+ * Do example for "Always Render Combobox.Empty" on the Combobox page.
427
+ * Render a short, specific message, such as No assets found.
428
+ * @module
429
+ */
430
+ import { Combobox, style } from '@tempoxyz/ds'
431
+
432
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
433
+
434
+ // The popup portals and opens on input, so a static copy shows it open.
435
+ /** Renders the static preview, which the guideline card shows inert. */
436
+ export function Demo() {
437
+ return (
438
+ <div {...styles.column()}>
439
+ <Combobox.Root defaultInputValue="EURC" items={assets}>
440
+ <Combobox.InputGroup>
441
+ <Combobox.Input aria-label="Asset" />
442
+ <Combobox.Trigger />
443
+ </Combobox.InputGroup>
444
+ </Combobox.Root>
445
+ <div {...styles.popup()}>
446
+ <div {...styles.empty()}>No assets found</div>
447
+ </div>
448
+ </div>
449
+ )
450
+ }
451
+
452
+ namespace styles {
453
+ export const column = style({
454
+ display: 'flex',
455
+ flexDirection: 'column',
456
+ gap: '8',
457
+ maxWidth: '16rem !custom',
458
+ width: '100% !custom',
459
+ })
460
+
461
+ export const empty = style({
462
+ color: 'content.secondary',
463
+ paddingBlock: '12',
464
+ paddingInline: '16',
465
+ typography: 'body.b2',
466
+ })
467
+
468
+ export const popup = style({
469
+ backgroundColor: 'background.elevated',
470
+ borderRadius: 'sm',
471
+ boxShadow: 'regular',
472
+ padding: '6px !custom',
473
+ })
474
+ }
475
+ ```
476
+
477
+ **Don't:** Leave it out. The popup opens blank and announces nothing.
478
+
479
+ ```tsx
480
+ /**
481
+ * Don't example for "Always Render Combobox.Empty" on the Combobox page.
482
+ * Don't leave it out. The popup opens blank and announces nothing.
483
+ * @module
484
+ */
485
+ import { Combobox, style } from '@tempoxyz/ds'
486
+
487
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
488
+
489
+ // The popup portals and opens on input, so a static copy shows it open.
490
+ /** Renders the static preview, which the guideline card shows inert. */
491
+ export function Demo() {
492
+ return (
493
+ <div {...styles.column()}>
494
+ <Combobox.Root defaultInputValue="EURC" items={assets}>
495
+ <Combobox.InputGroup>
496
+ <Combobox.Input aria-label="Asset" />
497
+ <Combobox.Trigger />
498
+ </Combobox.InputGroup>
499
+ </Combobox.Root>
500
+ <div {...styles.popup()} />
501
+ </div>
502
+ )
503
+ }
504
+
505
+ namespace styles {
506
+ export const column = style({
507
+ display: 'flex',
508
+ flexDirection: 'column',
509
+ gap: '8',
510
+ maxWidth: '16rem !custom',
511
+ width: '100% !custom',
512
+ })
513
+
514
+ export const popup = style({
515
+ backgroundColor: 'background.elevated',
516
+ borderRadius: 'sm',
517
+ boxShadow: 'regular',
518
+ padding: '6px !custom',
519
+ })
520
+ }
521
+ ```
522
+
523
+ ### Show What Identifies Each Item
524
+
525
+ * **`Combobox.ItemText`:** The searchable name, such as the symbol USDC.e.
526
+ * **`Combobox.ItemPrefix`:** A token icon.
527
+ * **`Combobox.ItemSuffix`:** Detail that informs the choice, such as a balance.
528
+
529
+ **Do:** Show the symbol, its icon, and the balance.
530
+
531
+ ```tsx
532
+ /**
533
+ * Do example for "Show What Identifies Each Item" on the Combobox page.
534
+ * Show the symbol, its icon, and the balance.
535
+ * @module
536
+ */
537
+ import pathusd from '../../coins/PathUSD.svg'
538
+ import usdce from '../../coins/USDCe.svg'
539
+ import usdt0 from '../../coins/USDT0.svg'
540
+ import { style } from '@tempoxyz/ds'
541
+
542
+ const assets = [
543
+ { balance: '1,250.00', icon: usdce, symbol: 'USDC.e' },
544
+ { balance: '80.50', icon: pathusd, symbol: 'pathUSD' },
545
+ { balance: '0.00', icon: usdt0, symbol: 'USDT0' },
546
+ ]
547
+
548
+ // A static copy of the open popup, since the real one portals and opens on input.
549
+ /** Renders the static preview, which the guideline card shows inert. */
550
+ export function Demo() {
551
+ return (
552
+ <div {...styles.popup()}>
553
+ {assets.map((asset) => (
554
+ <div key={asset.symbol} {...styles.item()}>
555
+ <img alt="" height={20} src={asset.icon} width={20} />
556
+ {asset.symbol}
557
+ <span {...styles.suffix()}>{asset.balance}</span>
558
+ </div>
559
+ ))}
560
+ </div>
561
+ )
562
+ }
563
+
564
+ namespace styles {
565
+ export const item = style({
566
+ alignItems: 'center',
567
+ display: 'flex',
568
+ gap: '8',
569
+ minHeight: '40',
570
+ paddingInline: '16',
571
+ typography: 'body.b2Strong',
572
+ })
573
+
574
+ export const popup = style({
575
+ backgroundColor: 'background.elevated',
576
+ borderRadius: 'sm',
577
+ boxShadow: 'regular',
578
+ padding: '6px !custom',
579
+ width: '16rem !custom',
580
+ })
581
+
582
+ export const suffix = style({
583
+ color: 'content.secondary',
584
+ marginInlineStart: 'auto !custom',
585
+ typography: 'body.b3',
586
+ })
587
+ }
588
+ ```
589
+
590
+ **Don't:** List contract addresses as item text.
591
+
592
+ ```tsx
593
+ /**
594
+ * Don't example for "Show What Identifies Each Item" on the Combobox page.
595
+ * Don't list contract addresses as item text.
596
+ * @module
597
+ */
598
+ import { style } from '@tempoxyz/ds'
599
+
600
+ const assets = ['0x20c0…0001', '0x20c0…0002', '0x20c0…0003']
601
+
602
+ // A static copy of the open popup, since the real one portals and opens on input.
603
+ /** Renders the static preview, which the guideline card shows inert. */
604
+ export function Demo() {
605
+ return (
606
+ <div {...styles.popup()}>
607
+ {assets.map((address) => (
608
+ <div key={address} {...styles.item()}>
609
+ {address}
610
+ </div>
611
+ ))}
612
+ </div>
613
+ )
614
+ }
615
+
616
+ namespace styles {
617
+ export const item = style({
618
+ alignItems: 'center',
619
+ display: 'flex',
620
+ gap: '8',
621
+ minHeight: '40',
622
+ paddingInline: '16',
623
+ typography: 'body.b2Strong',
624
+ })
625
+
626
+ export const popup = style({
627
+ backgroundColor: 'background.elevated',
628
+ borderRadius: 'sm',
629
+ boxShadow: 'regular',
630
+ padding: '6px !custom',
631
+ width: '16rem !custom',
632
+ })
633
+ }
634
+ ```
635
+
636
+ ### Offer a Way to Browse
637
+
638
+ `Combobox.Clear` mounts only while there is a value.
639
+
640
+ **Do:** Add `Combobox.Trigger` to open the full list, and `Combobox.Clear` if the value can be emptied.
641
+
642
+ ```tsx
643
+ /**
644
+ * Do example for "Offer a Way to Browse" on the Combobox page.
645
+ * Add `Combobox.Trigger` to open the full list, and `Combobox.Clear` if the value can be emptied.
646
+ * @module
647
+ */
648
+ import { Combobox, Field, style } from '@tempoxyz/ds'
649
+
650
+ const countries = ['Germany', 'Singapore', 'United States']
651
+
652
+ /** Renders the static preview, which the guideline card shows inert. */
653
+ export function Demo() {
654
+ return (
655
+ <Field.Root {...styles.field()}>
656
+ <Field.Label>Country</Field.Label>
657
+ <Combobox.Root defaultValue="Singapore" items={countries}>
658
+ <Combobox.InputGroup>
659
+ <Combobox.Input placeholder="Search countries" />
660
+ <Combobox.Clear />
661
+ <Combobox.Trigger />
662
+ </Combobox.InputGroup>
663
+ </Combobox.Root>
664
+ </Field.Root>
665
+ )
666
+ }
667
+
668
+ namespace styles {
669
+ export const field = style({ maxWidth: '16rem !custom', width: '100% !custom' })
670
+ }
671
+ ```
672
+
673
+ **Don't:** Make typing the only way to see the options.
674
+
675
+ ```tsx
676
+ /**
677
+ * Don't example for "Offer a Way to Browse" on the Combobox page.
678
+ * Don't make typing the only way to see the options.
679
+ * @module
680
+ */
681
+ import { Combobox, Field, style } from '@tempoxyz/ds'
682
+
683
+ const countries = ['Germany', 'Singapore', 'United States']
684
+
685
+ /** Renders the static preview, which the guideline card shows inert. */
686
+ export function Demo() {
687
+ return (
688
+ <Field.Root {...styles.field()}>
689
+ <Field.Label>Country</Field.Label>
690
+ <Combobox.Root defaultValue="Singapore" items={countries}>
691
+ <Combobox.InputGroup>
692
+ <Combobox.Input placeholder="Search countries" />
693
+ </Combobox.InputGroup>
694
+ </Combobox.Root>
695
+ </Field.Root>
696
+ )
697
+ }
698
+
699
+ namespace styles {
700
+ export const field = style({ maxWidth: '16rem !custom', width: '100% !custom' })
701
+ }
702
+ ```
703
+
704
+ ### Group Long Lists
705
+
706
+ Name each `Combobox.Group` with a `Combobox.GroupLabel`, and divide groups with `Combobox.Separator`. Put the most-chosen items first.
707
+
708
+ **Do:** Split a long list into named sections, such as Stablecoins.
709
+
710
+ ```tsx
711
+ /**
712
+ * Do example for "Group Long Lists" on the Combobox page.
713
+ * Split a long list into named sections, such as Stablecoins.
714
+ * @module
715
+ */
716
+ import { style } from '@tempoxyz/ds'
717
+
718
+ // A static copy of the open popup, since the real one portals and opens on input.
719
+ /** Renders the static preview, which the guideline card shows inert. */
720
+ export function Demo() {
721
+ return (
722
+ <div {...styles.popup()}>
723
+ <div {...styles.label()}>Stablecoins</div>
724
+ <div {...styles.item()}>pathUSD</div>
725
+ <div {...styles.item()}>USDC.e</div>
726
+ <div {...styles.separator()} />
727
+ <div {...styles.label()}>Other assets</div>
728
+ <div {...styles.item()}>cbBTC</div>
729
+ <div {...styles.item()}>ETH</div>
730
+ </div>
731
+ )
732
+ }
733
+
734
+ namespace styles {
735
+ export const item = style({
736
+ alignItems: 'center',
737
+ display: 'flex',
738
+ minHeight: '40',
739
+ paddingInline: '16',
740
+ typography: 'body.b2Strong',
741
+ })
742
+
743
+ export const label = style({
744
+ color: 'content.secondary',
745
+ paddingBlock: '8',
746
+ paddingInline: '16',
747
+ typography: 'body.b3',
748
+ })
749
+
750
+ export const popup = style({
751
+ backgroundColor: 'background.elevated',
752
+ borderRadius: 'sm',
753
+ boxShadow: 'regular',
754
+ padding: '6px !custom',
755
+ width: '16rem !custom',
756
+ })
757
+
758
+ export const separator = style({
759
+ backgroundColor: 'container.strong',
760
+ height: '1',
761
+ marginBlock: '8',
762
+ marginInline: '16',
763
+ })
764
+ }
765
+ ```
766
+
767
+ **Don't:** Mix unrelated kinds of items in one flat list.
768
+
769
+ ```tsx
770
+ /**
771
+ * Don't example for "Group Long Lists" on the Combobox page.
772
+ * Don't mix unrelated kinds of items in one flat list.
773
+ * @module
774
+ */
775
+ import { style } from '@tempoxyz/ds'
776
+
777
+ // A static copy of the open popup, since the real one portals and opens on input.
778
+ /** Renders the static preview, which the guideline card shows inert. */
779
+ export function Demo() {
780
+ return (
781
+ <div {...styles.popup()}>
782
+ <div {...styles.item()}>cbBTC</div>
783
+ <div {...styles.item()}>pathUSD</div>
784
+ <div {...styles.item()}>ETH</div>
785
+ <div {...styles.item()}>USDC.e</div>
786
+ </div>
787
+ )
788
+ }
789
+
790
+ namespace styles {
791
+ export const item = style({
792
+ alignItems: 'center',
793
+ display: 'flex',
794
+ minHeight: '40',
795
+ paddingInline: '16',
796
+ typography: 'body.b2Strong',
797
+ })
798
+
799
+ export const popup = style({
800
+ backgroundColor: 'background.elevated',
801
+ borderRadius: 'sm',
802
+ boxShadow: 'regular',
803
+ padding: '6px !custom',
804
+ width: '16rem !custom',
805
+ })
806
+ }
807
+ ```
808
+
809
+ ### Expose Errors on the Input
810
+
811
+ **Do:** Set `aria-invalid` on `Combobox.Input`, and explain the problem in a `Field.Error`.
812
+
813
+ ```tsx
814
+ /**
815
+ * Do example for "Expose Errors on the TextInput" on the Combobox page.
816
+ * Set `aria-invalid` on `Combobox.Input`, and explain the problem in a `Field.Error`.
817
+ * @module
818
+ */
819
+ import { Combobox, Field, style } from '@tempoxyz/ds'
820
+
821
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
822
+
823
+ /** Renders the static preview, which the guideline card shows inert. */
824
+ export function Demo() {
825
+ return (
826
+ <Field.Root invalid {...styles.field()}>
827
+ <Field.Label>Asset</Field.Label>
828
+ <Combobox.Root items={assets}>
829
+ <Combobox.InputGroup invalid>
830
+ <Combobox.Input aria-invalid placeholder="Search assets" />
831
+ <Combobox.Trigger />
832
+ </Combobox.InputGroup>
833
+ </Combobox.Root>
834
+ <Field.Error match>Select an asset to send</Field.Error>
835
+ </Field.Root>
836
+ )
837
+ }
838
+
839
+ namespace styles {
840
+ export const field = style({ maxWidth: '16rem !custom', width: '100% !custom' })
841
+ }
842
+ ```
843
+
844
+ **Don't:** Rely on `invalid` on `Combobox.InputGroup`. It only styles the frame.
845
+
846
+ ```tsx
847
+ /**
848
+ * Don't example for "Expose Errors on the TextInput" on the Combobox page.
849
+ * Don't rely on `invalid` on `Combobox.InputGroup`. It only styles the frame.
850
+ * @module
851
+ */
852
+ import { Combobox, Field, style } from '@tempoxyz/ds'
853
+
854
+ const assets = ['pathUSD', 'USDC.e', 'USDT0']
855
+
856
+ /** Renders the static preview, which the guideline card shows inert. */
857
+ export function Demo() {
858
+ return (
859
+ <Field.Root {...styles.field()}>
860
+ <Field.Label>Asset</Field.Label>
861
+ <Combobox.Root items={assets}>
862
+ <Combobox.InputGroup invalid>
863
+ <Combobox.Input placeholder="Search assets" />
864
+ <Combobox.Trigger />
865
+ </Combobox.InputGroup>
866
+ </Combobox.Root>
867
+ </Field.Root>
868
+ )
869
+ }
870
+
871
+ namespace styles {
872
+ export const field = style({ maxWidth: '16rem !custom', width: '100% !custom' })
873
+ }
874
+ ```
875
+
876
+ ## Content
877
+
878
+ * **Label:** The noun the value names, such as Asset, Network, or Country.
879
+ * **Placeholder:** What people can search by, such as Search by name or symbol.
880
+ * **Items:** The names and symbols from the token list, with the same casing everywhere.
881
+
882
+ ## Accessibility
883
+
884
+ Follows the [WAI-ARIA combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) with list autocomplete.
885
+
886
+ ### Semantics
887
+
888
+ * **Combobox.Input (combobox):** A text input that announces an option list and whether it is open.
889
+ * **Highlighted Option (aria-activedescendant):** Points to the highlighted option while focus stays in the input.
890
+ * **Combobox.List and Combobox.Item (listbox):** Each item is an option with a check mark while selected.
891
+ * **Combobox.Trigger and Combobox.Clear (button):** Named "Show options" and "Clear", both out of the tab order. Clear renders only with a value.
892
+ * **Combobox.Chips (toolbar):** With `multiple`, arrow keys move between chips. Each remove button is named "Remove".
893
+
894
+ ### Keyboard
895
+
896
+ * **Move Focus to the TextInput (Tab):** Moves focus to the input.
897
+ * **Filter the Options (Typing):** Filters the options and opens the list.
898
+ * **Open or Move Down (Down Arrow):** Opens the list, or highlights the next option.
899
+ * **Move Up (Up Arrow):** Highlights the previous option.
900
+ * **Select an Option (Enter):** Selects the highlighted option and closes the list. With `multiple`, toggles it instead.
901
+ * **Close the List (Escape):** Closes the popup.
902
+ * **Move to the Chips (Left Arrow):** With `multiple`, from the start of the input, focuses the last chip. Arrows move between chips.
903
+ * **Remove a Chip (Backspace):** With `multiple`, removes the focused chip, or the last chip from an empty input.
904
+
905
+ ### Focus
906
+
907
+ DOM focus stays on the input while the arrow keys move the highlight. Chips take focus only through the arrow keys, not Tab.
908
+
909
+ ### Announcements
910
+
911
+ * **Highlighted Option (aria-activedescendant):** Screen readers announce each option on highlight.
912
+ * **Empty Results (status):** Announces a query without matches.
913
+
914
+ ### Your Responsibilities
915
+
916
+ * **Name Chips in Multiple Mode (aria-label):** Name the chips, such as "Selected assets", and each remove button by its value, such as "Remove USDC".
917
+ * **Hide Item Icons (alt):** Leave item icon alt text empty. The item text names the item.
918
+
919
+ ## Related
920
+
921
+ [Autocomplete](/docs/components/autocomplete)
922
+
923
+ Use for free text with suggestions.
924
+
925
+ [Select](/docs/components/select)
926
+
927
+ Use for short lists.
928
+
929
+ [Field](/docs/components/field)
930
+
931
+ Use for the label and error.
932
+
933
+ [TokenLogo](/docs/components/token-logo)
934
+
935
+ Use for token icons in items.
936
+
937
+ [Blockchain Values](/docs/patterns/blockchain-values)
938
+
939
+ Covers showing assets and addresses.