@tempoxyz/ds 0.3.10 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/docs/AGENTS.md +12 -0
- package/dist/docs/index.md +145 -0
- package/dist/docs/marketing/introduction/getting-started.md +241 -0
- package/dist/docs/product/components/accordion.md +489 -0
- package/dist/docs/product/components/address-card.md +147 -0
- package/dist/docs/product/components/agent-logo.md +194 -0
- package/dist/docs/product/components/alert-dialog.md +631 -0
- package/dist/docs/product/components/alert.md +448 -0
- package/dist/docs/product/components/amount-input.md +305 -0
- package/dist/docs/product/components/amount.md +794 -0
- package/dist/docs/product/components/app-shell.md +559 -0
- package/dist/docs/product/components/asset-chip.md +294 -0
- package/dist/docs/product/components/autocomplete.md +620 -0
- package/dist/docs/product/components/avatar.md +321 -0
- package/dist/docs/product/components/badge.md +339 -0
- package/dist/docs/product/components/badged-logo.md +116 -0
- package/dist/docs/product/components/banner.md +379 -0
- package/dist/docs/product/components/bar-chart.md +208 -0
- package/dist/docs/product/components/breadcrumbs.md +380 -0
- package/dist/docs/product/components/button-group.md +286 -0
- package/dist/docs/product/components/button.md +650 -0
- package/dist/docs/product/components/calendar.md +375 -0
- package/dist/docs/product/components/callout.md +96 -0
- package/dist/docs/product/components/checkbox-group.md +521 -0
- package/dist/docs/product/components/checkbox.md +467 -0
- package/dist/docs/product/components/choice-group.md +516 -0
- package/dist/docs/product/components/choicebox.md +641 -0
- package/dist/docs/product/components/code.md +100 -0
- package/dist/docs/product/components/combobox.md +939 -0
- package/dist/docs/product/components/container.md +257 -0
- package/dist/docs/product/components/copy-button.md +642 -0
- package/dist/docs/product/components/date-picker.md +514 -0
- package/dist/docs/product/components/dialog.md +1332 -0
- package/dist/docs/product/components/drawer.md +966 -0
- package/dist/docs/product/components/empty.md +340 -0
- package/dist/docs/product/components/field.md +570 -0
- package/dist/docs/product/components/fieldset.md +398 -0
- package/dist/docs/product/components/frame.md +434 -0
- package/dist/docs/product/components/gauge.md +299 -0
- package/dist/docs/product/components/get-started-visual.md +117 -0
- package/dist/docs/product/components/grid.md +288 -0
- package/dist/docs/product/components/hero-visual.md +155 -0
- package/dist/docs/product/components/hex.md +487 -0
- package/dist/docs/product/components/icon-tile.md +292 -0
- package/dist/docs/product/components/identicon.md +371 -0
- package/dist/docs/product/components/inline.md +233 -0
- package/dist/docs/product/components/inset.md +225 -0
- package/dist/docs/product/components/kbd.md +423 -0
- package/dist/docs/product/components/list-group.md +981 -0
- package/dist/docs/product/components/loading-dots.md +237 -0
- package/dist/docs/product/components/menu.md +934 -0
- package/dist/docs/product/components/native-select.md +204 -0
- package/dist/docs/product/components/network-logo.md +248 -0
- package/dist/docs/product/components/number-field.md +543 -0
- package/dist/docs/product/components/otp-field.md +616 -0
- package/dist/docs/product/components/pagination.md +516 -0
- package/dist/docs/product/components/popover.md +544 -0
- package/dist/docs/product/components/progress.md +388 -0
- package/dist/docs/product/components/qr.md +314 -0
- package/dist/docs/product/components/radio-group.md +528 -0
- package/dist/docs/product/components/secret-value.md +406 -0
- package/dist/docs/product/components/select.md +751 -0
- package/dist/docs/product/components/separator.md +379 -0
- package/dist/docs/product/components/sidebar-menu.md +922 -0
- package/dist/docs/product/components/skeleton.md +517 -0
- package/dist/docs/product/components/slider.md +539 -0
- package/dist/docs/product/components/snippet.md +424 -0
- package/dist/docs/product/components/spinner.md +216 -0
- package/dist/docs/product/components/split-bar.md +139 -0
- package/dist/docs/product/components/split-button.md +464 -0
- package/dist/docs/product/components/stack.md +299 -0
- package/dist/docs/product/components/status-chip.md +226 -0
- package/dist/docs/product/components/status-indicator.md +119 -0
- package/dist/docs/product/components/status-row.md +174 -0
- package/dist/docs/product/components/stepper.md +451 -0
- package/dist/docs/product/components/success-hero.md +157 -0
- package/dist/docs/product/components/surface.md +639 -0
- package/dist/docs/product/components/switch.md +472 -0
- package/dist/docs/product/components/table.md +967 -0
- package/dist/docs/product/components/tabs.md +627 -0
- package/dist/docs/product/components/tempo-logo.md +185 -0
- package/dist/docs/product/components/text-input.md +624 -0
- package/dist/docs/product/components/textarea.md +352 -0
- package/dist/docs/product/components/toast.md +535 -0
- package/dist/docs/product/components/token-logo.md +371 -0
- package/dist/docs/product/components/token.md +247 -0
- package/dist/docs/product/components/tooltip.md +490 -0
- package/dist/docs/product/components/verification-code.md +116 -0
- package/dist/docs/product/components/visual.md +249 -0
- package/dist/docs/product/components.md +203 -0
- package/dist/docs/product/foundations/accessibility.md +377 -0
- package/dist/docs/product/foundations/agents.md +150 -0
- package/dist/docs/product/foundations/color.md +522 -0
- package/dist/docs/product/foundations/content/tone.md +194 -0
- package/dist/docs/product/foundations/content/voice.md +194 -0
- package/dist/docs/product/foundations/content/writing-style.md +406 -0
- package/dist/docs/product/foundations/iconography.md +282 -0
- package/dist/docs/product/foundations/intent.md +360 -0
- package/dist/docs/product/foundations/layout.md +508 -0
- package/dist/docs/product/foundations/linter.md +496 -0
- package/dist/docs/product/foundations/motion.md +151 -0
- package/dist/docs/product/foundations/radius-and-padding.md +86 -0
- package/dist/docs/product/foundations/typography.md +266 -0
- package/dist/docs/product/introduction/faq.md +133 -0
- package/dist/docs/product/introduction/getting-started.md +377 -0
- package/dist/docs/product/patterns/blockchain-values.md +709 -0
- package/dist/docs/product/patterns/dashboards.md +638 -0
- package/dist/docs/product/patterns/destructive-actions.md +626 -0
- package/dist/docs/product/patterns/empty-states.md +612 -0
- package/dist/docs/product/patterns/errors-and-validation.md +808 -0
- package/dist/docs/product/patterns/feedback-and-status.md +518 -0
- package/dist/docs/product/patterns/forms.md +679 -0
- package/dist/docs/product/patterns/loading.md +630 -0
- package/dist/docs/product/patterns/navigation.md +397 -0
- package/dist/docs/product/patterns/payments.md +765 -0
- package/dist/docs/product/patterns/responsive-layout.md +997 -0
- package/dist/docs/product/patterns/tables-and-lists.md +964 -0
- package/dist/docs/product/products/tempo-console.md +671 -0
- package/dist/docs/product/products/tempo-performance.md +437 -0
- package/dist/docs/product/products/tempo-wallet.md +438 -0
- package/dist/marketing/index.d.ts +4 -0
- package/dist/marketing/index.d.ts.map +1 -0
- package/dist/marketing/index.js +4 -0
- package/dist/marketing/index.js.map +1 -0
- package/dist/marketing/index.js.zyzz.json +1 -0
- package/dist/marketing/vars.d.ts +907 -0
- package/dist/marketing/vars.d.ts.map +1 -0
- package/dist/marketing/vars.js +15 -0
- package/dist/marketing/vars.js.map +1 -0
- package/dist/marketing/vars.js.zyzz.json +1 -0
- package/dist/marketing/zyzz.config.d.ts +4512 -0
- package/dist/marketing/zyzz.config.d.ts.map +1 -0
- package/dist/marketing/zyzz.config.js +63 -0
- package/dist/marketing/zyzz.config.js.map +1 -0
- package/dist/marketing/zyzz.config.js.zyzz.json +1 -0
- package/dist/oxlint/index.d.ts +12 -1
- package/dist/oxlint/index.d.ts.map +1 -1
- package/dist/oxlint/index.js +39 -1
- package/dist/oxlint/index.js.map +1 -1
- package/dist/oxlint/internal/Docs.d.ts +9 -0
- package/dist/oxlint/internal/Docs.d.ts.map +1 -0
- package/dist/oxlint/internal/Docs.js +15 -0
- package/dist/oxlint/internal/Docs.js.map +1 -0
- package/dist/oxlint/internal/Imports.d.ts +1 -1
- package/dist/oxlint/internal/Imports.d.ts.map +1 -1
- package/dist/oxlint/internal/Imports.js +2 -0
- package/dist/oxlint/internal/Imports.js.map +1 -1
- package/dist/oxlint/internal/Styles.d.ts +40 -0
- package/dist/oxlint/internal/Styles.d.ts.map +1 -0
- package/dist/oxlint/internal/Styles.js +194 -0
- package/dist/oxlint/internal/Styles.js.map +1 -0
- package/dist/oxlint/internal/Tokens.d.ts +16 -0
- package/dist/oxlint/internal/Tokens.d.ts.map +1 -0
- package/dist/oxlint/internal/Tokens.js +39 -0
- package/dist/oxlint/internal/Tokens.js.map +1 -0
- package/dist/oxlint/rules/NoCrossSurface.d.ts +2 -2
- package/dist/oxlint/rules/NoCrossSurface.js +3 -3
- package/dist/oxlint/rules/NoCrossSurface.js.map +1 -1
- package/dist/oxlint/rules/NoCustomValues.d.ts +11 -0
- package/dist/oxlint/rules/NoCustomValues.d.ts.map +1 -0
- package/dist/oxlint/rules/NoCustomValues.js +150 -0
- package/dist/oxlint/rules/NoCustomValues.js.map +1 -0
- package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts +11 -0
- package/dist/oxlint/rules/NoSingleKeyShortcuts.d.ts.map +1 -0
- package/dist/oxlint/rules/NoSingleKeyShortcuts.js +158 -0
- package/dist/oxlint/rules/NoSingleKeyShortcuts.js.map +1 -0
- package/dist/oxlint/rules/PreferComponent.d.ts +3 -2
- package/dist/oxlint/rules/PreferComponent.d.ts.map +1 -1
- package/dist/oxlint/rules/PreferComponent.js +11 -4
- package/dist/oxlint/rules/PreferComponent.js.map +1 -1
- package/dist/oxlint/rules/PreferLayout.d.ts +9 -0
- package/dist/oxlint/rules/PreferLayout.d.ts.map +1 -0
- package/dist/oxlint/rules/PreferLayout.js +90 -0
- package/dist/oxlint/rules/PreferLayout.js.map +1 -0
- package/dist/oxlint/rules/PreferProgress.d.ts +9 -0
- package/dist/oxlint/rules/PreferProgress.d.ts.map +1 -0
- package/dist/oxlint/rules/PreferProgress.js +43 -0
- package/dist/oxlint/rules/PreferProgress.js.map +1 -0
- package/dist/oxlint/rules/PreferSurface.d.ts +9 -0
- package/dist/oxlint/rules/PreferSurface.d.ts.map +1 -0
- package/dist/oxlint/rules/PreferSurface.js +55 -0
- package/dist/oxlint/rules/PreferSurface.js.map +1 -0
- package/dist/oxlint/rules/RequireAccessibleName.d.ts.map +1 -1
- package/dist/oxlint/rules/RequireAccessibleName.js +3 -1
- package/dist/oxlint/rules/RequireAccessibleName.js.map +1 -1
- package/dist/oxlint/rules/RequireScrollRegion.d.ts +11 -0
- package/dist/oxlint/rules/RequireScrollRegion.d.ts.map +1 -0
- package/dist/oxlint/rules/RequireScrollRegion.js +71 -0
- package/dist/oxlint/rules/RequireScrollRegion.js.map +1 -0
- package/dist/web/icons/index.js.zyzz.json +1 -0
- package/package.json +6 -1
- package/src/marketing/README.md +21 -0
- package/src/marketing/index.ts +3 -0
- package/src/marketing/vars.ts +751 -0
- package/src/marketing/zyzz.config.ts +376 -0
- package/src/oxlint/index.ts +40 -1
- package/src/oxlint/internal/Docs.ts +18 -0
- package/src/oxlint/internal/Imports.ts +2 -1
- package/src/oxlint/internal/Styles.ts +240 -0
- package/src/oxlint/internal/Tokens.ts +43 -0
- package/src/oxlint/rules/NoCrossSurface.ts +3 -3
- package/src/oxlint/rules/NoCustomValues.ts +170 -0
- package/src/oxlint/rules/NoSingleKeyShortcuts.ts +190 -0
- package/src/oxlint/rules/PreferComponent.ts +11 -4
- package/src/oxlint/rules/PreferLayout.ts +96 -0
- package/src/oxlint/rules/PreferProgress.ts +49 -0
- package/src/oxlint/rules/PreferSurface.ts +70 -0
- package/src/oxlint/rules/RequireAccessibleName.ts +3 -1
- package/src/oxlint/rules/RequireScrollRegion.ts +92 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
Opens a list of options for choosing one value.
|
|
4
|
+
|
|
5
|
+
[View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Select.tsx)
|
|
6
|
+
|
|
7
|
+
Figma: [Inputs/Select](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-5451) (`Select.Trigger`)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Select renders a trigger that opens a listbox, for choosing one value from a short list. If the options fit on screen, prefer [ChoiceGroup](choice-group.md).
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
19
|
+
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
|
|
20
|
+
import { useState } from 'react'
|
|
21
|
+
|
|
22
|
+
const assets = [
|
|
23
|
+
{ balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
|
|
24
|
+
{ balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
|
|
25
|
+
{ balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))
|
|
29
|
+
|
|
30
|
+
function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'variant'>) {
|
|
31
|
+
const [value, setValue] = useState<string | null>('pathusd')
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Select.Root items={items} onValueChange={setValue} value={value}>
|
|
35
|
+
<Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
|
|
36
|
+
<Select.Value placeholder="Select an asset" />
|
|
37
|
+
<Select.Icon />
|
|
38
|
+
</Select.Trigger>
|
|
39
|
+
<Select.Popup>
|
|
40
|
+
{assets.map((asset) => (
|
|
41
|
+
<Select.Item key={asset.value} label={asset.label} value={asset.value}>
|
|
42
|
+
<Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
|
|
43
|
+
<Select.ItemText>{asset.label}</Select.ItemText>
|
|
44
|
+
<Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
|
|
45
|
+
</Select.Item>
|
|
46
|
+
))}
|
|
47
|
+
<Select.Separator />
|
|
48
|
+
<Select.Group>
|
|
49
|
+
<Select.GroupLabel>Coming soon</Select.GroupLabel>
|
|
50
|
+
<Select.Item disabled value="other">
|
|
51
|
+
<Select.ItemText>Other</Select.ItemText>
|
|
52
|
+
</Select.Item>
|
|
53
|
+
</Select.Group>
|
|
54
|
+
</Select.Popup>
|
|
55
|
+
</Select.Root>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function Demo() {
|
|
60
|
+
return (
|
|
61
|
+
<Field.Root>
|
|
62
|
+
<Field.Label>Asset</Field.Label>
|
|
63
|
+
<Example />
|
|
64
|
+
</Field.Root>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
namespace styles {
|
|
69
|
+
export const trigger = style({ maxWidth: '320px !custom' })
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Basic.tsx)
|
|
74
|
+
|
|
75
|
+
### Variant
|
|
76
|
+
|
|
77
|
+
Set `variant` on `Select.Trigger` to `default` (default) or `outlined`, which draws a 1px line instead of the fill.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
81
|
+
|
|
82
|
+
const items = { mainnet: 'Tempo', testnet: 'Tempo Testnet' }
|
|
83
|
+
|
|
84
|
+
export function Demo() {
|
|
85
|
+
return (
|
|
86
|
+
<div {...styles.grid()}>
|
|
87
|
+
{(['default', 'outlined'] as const).map((variant) => (
|
|
88
|
+
<Field.Root key={variant}>
|
|
89
|
+
<Field.Label>Network</Field.Label>
|
|
90
|
+
<Select.Root defaultValue="mainnet" items={items}>
|
|
91
|
+
<Select.Trigger variant={variant}>
|
|
92
|
+
<Select.Value />
|
|
93
|
+
<Select.Icon />
|
|
94
|
+
</Select.Trigger>
|
|
95
|
+
<Select.Popup>
|
|
96
|
+
{Object.entries(items).map(([value, label]) => (
|
|
97
|
+
<Select.Item key={value} label={label} value={value}>
|
|
98
|
+
<Select.ItemText>{label}</Select.ItemText>
|
|
99
|
+
</Select.Item>
|
|
100
|
+
))}
|
|
101
|
+
</Select.Popup>
|
|
102
|
+
</Select.Root>
|
|
103
|
+
</Field.Root>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
namespace styles {
|
|
110
|
+
export const grid = style({
|
|
111
|
+
alignItems: 'start',
|
|
112
|
+
display: 'grid',
|
|
113
|
+
gap: '24',
|
|
114
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !custom',
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Variant.tsx)
|
|
120
|
+
|
|
121
|
+
### Prefix
|
|
122
|
+
|
|
123
|
+
Put a `Select.Prefix` before `Select.Value` to name what a filter selects, such as By in By Anyone. Name the trigger with `aria-label` too.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
/**
|
|
127
|
+
* Prefix demo for the Select page: a dashboard filter whose prefix names what it filters.
|
|
128
|
+
* @module
|
|
129
|
+
*/
|
|
130
|
+
import { Select, style } from '@tempoxyz/ds'
|
|
131
|
+
|
|
132
|
+
const owners = { anyone: 'Anyone', me: 'Me', team: 'My team' }
|
|
133
|
+
|
|
134
|
+
/** Renders a filter that hugs its prefix, value, and chevron. */
|
|
135
|
+
export function Demo() {
|
|
136
|
+
return (
|
|
137
|
+
<Select.Root defaultValue="anyone" items={owners}>
|
|
138
|
+
<Select.Trigger aria-label="Owner" {...styles.trigger()}>
|
|
139
|
+
<Select.Prefix>By</Select.Prefix>
|
|
140
|
+
<Select.Value />
|
|
141
|
+
<Select.Icon />
|
|
142
|
+
</Select.Trigger>
|
|
143
|
+
<Select.Popup>
|
|
144
|
+
{Object.entries(owners).map(([value, label]) => (
|
|
145
|
+
<Select.Item key={value} label={label} value={value}>
|
|
146
|
+
<Select.ItemText>{label}</Select.ItemText>
|
|
147
|
+
</Select.Item>
|
|
148
|
+
))}
|
|
149
|
+
</Select.Popup>
|
|
150
|
+
</Select.Root>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
namespace styles {
|
|
155
|
+
// A filter sizes to its content instead of filling the row.
|
|
156
|
+
export const trigger = style({ width: 'max-content !custom' })
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Prefix.tsx)
|
|
161
|
+
|
|
162
|
+
### Field
|
|
163
|
+
|
|
164
|
+
Wrap the select in a `Field.Root` to label it and describe it.
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
168
|
+
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
|
|
169
|
+
import { useState } from 'react'
|
|
170
|
+
|
|
171
|
+
const assets = [
|
|
172
|
+
{ balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
|
|
173
|
+
{ balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
|
|
174
|
+
{ balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))
|
|
178
|
+
|
|
179
|
+
function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'variant'>) {
|
|
180
|
+
const [value, setValue] = useState<string | null>('pathusd')
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<Select.Root items={items} onValueChange={setValue} value={value}>
|
|
184
|
+
<Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
|
|
185
|
+
<Select.Value placeholder="Select an asset" />
|
|
186
|
+
<Select.Icon />
|
|
187
|
+
</Select.Trigger>
|
|
188
|
+
<Select.Popup>
|
|
189
|
+
{assets.map((asset) => (
|
|
190
|
+
<Select.Item key={asset.value} label={asset.label} value={asset.value}>
|
|
191
|
+
<Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
|
|
192
|
+
<Select.ItemText>{asset.label}</Select.ItemText>
|
|
193
|
+
<Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
|
|
194
|
+
</Select.Item>
|
|
195
|
+
))}
|
|
196
|
+
<Select.Separator />
|
|
197
|
+
<Select.Group>
|
|
198
|
+
<Select.GroupLabel>Coming soon</Select.GroupLabel>
|
|
199
|
+
<Select.Item disabled value="other">
|
|
200
|
+
<Select.ItemText>Other</Select.ItemText>
|
|
201
|
+
</Select.Item>
|
|
202
|
+
</Select.Group>
|
|
203
|
+
</Select.Popup>
|
|
204
|
+
</Select.Root>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function Demo() {
|
|
209
|
+
return (
|
|
210
|
+
<div {...styles.grid()}>
|
|
211
|
+
<Field.Root>
|
|
212
|
+
<Field.Label>Asset</Field.Label>
|
|
213
|
+
<Example />
|
|
214
|
+
<Field.Description>Pays the network fee.</Field.Description>
|
|
215
|
+
</Field.Root>
|
|
216
|
+
</div>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
namespace styles {
|
|
221
|
+
export const grid = style({
|
|
222
|
+
alignItems: 'start',
|
|
223
|
+
display: 'grid',
|
|
224
|
+
gap: '12',
|
|
225
|
+
gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr)) !custom',
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
export const trigger = style({ maxWidth: '320px !custom' })
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Field.tsx)
|
|
233
|
+
|
|
234
|
+
### Invalid
|
|
235
|
+
|
|
236
|
+
Set `invalid` on `Select.Trigger` to mark the value as invalid.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { Select, style } from '@tempoxyz/ds'
|
|
240
|
+
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
|
|
241
|
+
import { useState } from 'react'
|
|
242
|
+
|
|
243
|
+
const assets = [
|
|
244
|
+
{ balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
|
|
245
|
+
{ balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
|
|
246
|
+
{ balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
|
|
247
|
+
]
|
|
248
|
+
|
|
249
|
+
const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))
|
|
250
|
+
|
|
251
|
+
function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'variant'>) {
|
|
252
|
+
const [value, setValue] = useState<string | null>('pathusd')
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<Select.Root items={items} onValueChange={setValue} value={value}>
|
|
256
|
+
<Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
|
|
257
|
+
<Select.Value placeholder="Select an asset" />
|
|
258
|
+
<Select.Icon />
|
|
259
|
+
</Select.Trigger>
|
|
260
|
+
<Select.Popup>
|
|
261
|
+
{assets.map((asset) => (
|
|
262
|
+
<Select.Item key={asset.value} label={asset.label} value={asset.value}>
|
|
263
|
+
<Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
|
|
264
|
+
<Select.ItemText>{asset.label}</Select.ItemText>
|
|
265
|
+
<Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
|
|
266
|
+
</Select.Item>
|
|
267
|
+
))}
|
|
268
|
+
<Select.Separator />
|
|
269
|
+
<Select.Group>
|
|
270
|
+
<Select.GroupLabel>Coming soon</Select.GroupLabel>
|
|
271
|
+
<Select.Item disabled value="other">
|
|
272
|
+
<Select.ItemText>Other</Select.ItemText>
|
|
273
|
+
</Select.Item>
|
|
274
|
+
</Select.Group>
|
|
275
|
+
</Select.Popup>
|
|
276
|
+
</Select.Root>
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function Demo() {
|
|
281
|
+
return <Example invalid />
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
namespace styles {
|
|
285
|
+
export const trigger = style({ maxWidth: '320px !custom' })
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Invalid.tsx)
|
|
290
|
+
|
|
291
|
+
### Disabled
|
|
292
|
+
|
|
293
|
+
```tsx
|
|
294
|
+
import { Select, style } from '@tempoxyz/ds'
|
|
295
|
+
import { Coins, Ethereum, Wallet } from '@tempoxyz/ds/icons'
|
|
296
|
+
import { useState } from 'react'
|
|
297
|
+
|
|
298
|
+
const assets = [
|
|
299
|
+
{ balance: '1,240.50', icon: <Coins />, label: 'pathUSD', value: 'pathusd' },
|
|
300
|
+
{ balance: '845.00', icon: <Ethereum />, label: 'USDC.e', value: 'usdce' },
|
|
301
|
+
{ balance: '0.00', icon: <Wallet />, label: 'Tether', value: 'usdt' },
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
const items = Object.fromEntries(assets.map((asset) => [asset.value, asset.label]))
|
|
305
|
+
|
|
306
|
+
function Example(props: Pick<Select.Trigger.Props, 'disabled' | 'invalid' | 'variant'>) {
|
|
307
|
+
const [value, setValue] = useState<string | null>('pathusd')
|
|
308
|
+
|
|
309
|
+
return (
|
|
310
|
+
<Select.Root items={items} onValueChange={setValue} value={value}>
|
|
311
|
+
<Select.Trigger aria-label="Asset" {...props} {...styles.trigger()}>
|
|
312
|
+
<Select.Value placeholder="Select an asset" />
|
|
313
|
+
<Select.Icon />
|
|
314
|
+
</Select.Trigger>
|
|
315
|
+
<Select.Popup>
|
|
316
|
+
{assets.map((asset) => (
|
|
317
|
+
<Select.Item key={asset.value} label={asset.label} value={asset.value}>
|
|
318
|
+
<Select.ItemPrefix>{asset.icon}</Select.ItemPrefix>
|
|
319
|
+
<Select.ItemText>{asset.label}</Select.ItemText>
|
|
320
|
+
<Select.ItemSuffix>{asset.balance}</Select.ItemSuffix>
|
|
321
|
+
</Select.Item>
|
|
322
|
+
))}
|
|
323
|
+
<Select.Separator />
|
|
324
|
+
<Select.Group>
|
|
325
|
+
<Select.GroupLabel>Coming soon</Select.GroupLabel>
|
|
326
|
+
<Select.Item disabled value="other">
|
|
327
|
+
<Select.ItemText>Other</Select.ItemText>
|
|
328
|
+
</Select.Item>
|
|
329
|
+
</Select.Group>
|
|
330
|
+
</Select.Popup>
|
|
331
|
+
</Select.Root>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function Demo() {
|
|
336
|
+
return <Example disabled />
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
namespace styles {
|
|
340
|
+
export const trigger = style({ maxWidth: '320px !custom' })
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Select/Disabled.tsx)
|
|
345
|
+
|
|
346
|
+
## Anatomy
|
|
347
|
+
|
|
348
|
+
Import the component and assemble its parts.
|
|
349
|
+
|
|
350
|
+
```tsx
|
|
351
|
+
import { Select } from '@tempoxyz/ds'
|
|
352
|
+
|
|
353
|
+
<Select.Root>
|
|
354
|
+
<Select.Trigger>
|
|
355
|
+
<Select.Prefix />
|
|
356
|
+
<Select.Value />
|
|
357
|
+
<Select.Icon />
|
|
358
|
+
</Select.Trigger>
|
|
359
|
+
<Select.Popup>
|
|
360
|
+
<Select.Item>
|
|
361
|
+
<Select.ItemPrefix />
|
|
362
|
+
<Select.ItemText />
|
|
363
|
+
<Select.ItemSuffix />
|
|
364
|
+
</Select.Item>
|
|
365
|
+
<Select.Separator />
|
|
366
|
+
<Select.Group>
|
|
367
|
+
<Select.GroupLabel />
|
|
368
|
+
</Select.Group>
|
|
369
|
+
</Select.Popup>
|
|
370
|
+
</Select.Root>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Guidelines
|
|
374
|
+
|
|
375
|
+
### When to Use
|
|
376
|
+
|
|
377
|
+
* Choose one value from more than five scannable options, such as an asset or a network.
|
|
378
|
+
* Filter a dashboard or table where space is tight, such as a chart's time range.
|
|
379
|
+
* Set `multiple` to pick several values when a [CheckboxGroup](checkbox-group.md) takes too much room.
|
|
380
|
+
|
|
381
|
+
### When Not to Use
|
|
382
|
+
|
|
383
|
+
* Use [RadioGroup](radio-group.md) for two to five options compared side by side.
|
|
384
|
+
* Use [ChoiceGroup](choice-group.md) for two to four short options that switch a view, such as 1H, 24H, and 7D.
|
|
385
|
+
* Use [Combobox](combobox.md) for a long list filtered by typing, such as every token on Tempo.
|
|
386
|
+
* Use [Autocomplete](autocomplete.md) when any typed text is a valid value.
|
|
387
|
+
* Use [Menu](menu.md) for a list of actions. A select holds a value.
|
|
388
|
+
|
|
389
|
+
### Show Labels, Not Raw Values
|
|
390
|
+
|
|
391
|
+
Pass `items` to `Select.Root` to map each value to its label. Without it, `Select.Value` renders the raw value.
|
|
392
|
+
|
|
393
|
+
**Do:** Pass `items` so the trigger shows the asset's name.
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
/**
|
|
397
|
+
* Do example for "Show Labels, Not Raw Values" on the Select page.
|
|
398
|
+
* Pass `items` so the trigger shows the asset's name.
|
|
399
|
+
* @module
|
|
400
|
+
*/
|
|
401
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
402
|
+
|
|
403
|
+
const items = { pathusd: 'pathUSD', usdce: 'USDC.e' }
|
|
404
|
+
|
|
405
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
406
|
+
export function Demo() {
|
|
407
|
+
return (
|
|
408
|
+
<Field.Root {...styles.field()}>
|
|
409
|
+
<Field.Label>Asset</Field.Label>
|
|
410
|
+
<Select.Root defaultValue="usdce" items={items}>
|
|
411
|
+
<Select.Trigger>
|
|
412
|
+
<Select.Value placeholder="Select an asset" />
|
|
413
|
+
<Select.Icon />
|
|
414
|
+
</Select.Trigger>
|
|
415
|
+
<Select.Popup>
|
|
416
|
+
<Select.Item value="pathusd">
|
|
417
|
+
<Select.ItemText>pathUSD</Select.ItemText>
|
|
418
|
+
</Select.Item>
|
|
419
|
+
<Select.Item value="usdce">
|
|
420
|
+
<Select.ItemText>USDC.e</Select.ItemText>
|
|
421
|
+
</Select.Item>
|
|
422
|
+
</Select.Popup>
|
|
423
|
+
</Select.Root>
|
|
424
|
+
</Field.Root>
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
namespace styles {
|
|
429
|
+
export const field = style({ maxWidth: '18rem !custom', width: '100% !custom' })
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**Don't:** Leave the trigger showing an internal value such as `usdce`.
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
/**
|
|
437
|
+
* Don't example for "Show Labels, Not Raw Values" on the Select page.
|
|
438
|
+
* Don't leave the trigger showing an internal value such as `usdce`.
|
|
439
|
+
* @module
|
|
440
|
+
*/
|
|
441
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
442
|
+
|
|
443
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
444
|
+
export function Demo() {
|
|
445
|
+
return (
|
|
446
|
+
<Field.Root {...styles.field()}>
|
|
447
|
+
<Field.Label>Asset</Field.Label>
|
|
448
|
+
<Select.Root defaultValue="usdce">
|
|
449
|
+
<Select.Trigger>
|
|
450
|
+
<Select.Value placeholder="Select an asset" />
|
|
451
|
+
<Select.Icon />
|
|
452
|
+
</Select.Trigger>
|
|
453
|
+
<Select.Popup>
|
|
454
|
+
<Select.Item value="pathusd">
|
|
455
|
+
<Select.ItemText>pathUSD</Select.ItemText>
|
|
456
|
+
</Select.Item>
|
|
457
|
+
<Select.Item value="usdce">
|
|
458
|
+
<Select.ItemText>USDC.e</Select.ItemText>
|
|
459
|
+
</Select.Item>
|
|
460
|
+
</Select.Popup>
|
|
461
|
+
</Select.Root>
|
|
462
|
+
</Field.Root>
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
namespace styles {
|
|
467
|
+
export const field = style({ maxWidth: '18rem !custom', width: '100% !custom' })
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Order Options Predictably
|
|
472
|
+
|
|
473
|
+
Pick one ordering rule per list and keep it everywhere the list appears.
|
|
474
|
+
|
|
475
|
+
* **Numeric or Chronological (smallest first):** For values with a natural order, such as time ranges, sizes, and block heights.
|
|
476
|
+
* **Alphabetical (Select.Group):** For tokens, networks, and countries, with the common ones grouped first.
|
|
477
|
+
* **Most Used First:** For a few options with one clear favorite first, then the rest in a fixed order.
|
|
478
|
+
|
|
479
|
+
* Don't reorder options by recent use. People learn positions and typeahead order.
|
|
480
|
+
* Split long lists into `Select.Group` sections with a `Select.GroupLabel`, divided by `Select.Separator`.
|
|
481
|
+
* Hide options that can never be chosen. Show a `disabled` item only when its group label or text states why, such as Coming soon.
|
|
482
|
+
|
|
483
|
+
### Choose the Default Deliberately
|
|
484
|
+
|
|
485
|
+
If one choice suits most people, preselect it with `defaultValue`, such as the largest balance or the last 24 hours. Always give a filter a default.
|
|
486
|
+
|
|
487
|
+
**Do:** Give a dashboard filter a default that shows data right away.
|
|
488
|
+
|
|
489
|
+
```tsx
|
|
490
|
+
/**
|
|
491
|
+
* Do example for "Choose the Default Deliberately" on the Select page.
|
|
492
|
+
* Give a dashboard filter a default that shows data right away.
|
|
493
|
+
* @module
|
|
494
|
+
*/
|
|
495
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
496
|
+
|
|
497
|
+
const items = { '1h': 'Last hour', '24h': 'Last 24 hours', '7d': 'Last 7 days' }
|
|
498
|
+
|
|
499
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
500
|
+
export function Demo() {
|
|
501
|
+
return (
|
|
502
|
+
<Field.Root {...styles.field()}>
|
|
503
|
+
<Field.Label>Time range</Field.Label>
|
|
504
|
+
<Select.Root defaultValue="24h" items={items}>
|
|
505
|
+
<Select.Trigger>
|
|
506
|
+
<Select.Value placeholder="Select a time range" />
|
|
507
|
+
<Select.Icon />
|
|
508
|
+
</Select.Trigger>
|
|
509
|
+
<Select.Popup size="small">
|
|
510
|
+
{Object.entries(items).map(([value, label]) => (
|
|
511
|
+
<Select.Item key={value} value={value}>
|
|
512
|
+
<Select.ItemText>{label}</Select.ItemText>
|
|
513
|
+
</Select.Item>
|
|
514
|
+
))}
|
|
515
|
+
</Select.Popup>
|
|
516
|
+
</Select.Root>
|
|
517
|
+
</Field.Root>
|
|
518
|
+
)
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
namespace styles {
|
|
522
|
+
export const field = style({ width: '12rem !custom' })
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**Don't:** Leave a filter empty with a generic placeholder.
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
/**
|
|
530
|
+
* Don't example for "Choose the Default Deliberately" on the Select page.
|
|
531
|
+
* Don't leave a filter empty with a generic placeholder.
|
|
532
|
+
* @module
|
|
533
|
+
*/
|
|
534
|
+
import { Field, Select, style } from '@tempoxyz/ds'
|
|
535
|
+
|
|
536
|
+
const items = { '1h': 'Last hour', '24h': 'Last 24 hours', '7d': 'Last 7 days' }
|
|
537
|
+
|
|
538
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
539
|
+
export function Demo() {
|
|
540
|
+
return (
|
|
541
|
+
<Field.Root {...styles.field()}>
|
|
542
|
+
<Field.Label>Time range</Field.Label>
|
|
543
|
+
<Select.Root items={items}>
|
|
544
|
+
<Select.Trigger>
|
|
545
|
+
<Select.Value placeholder="Select" />
|
|
546
|
+
<Select.Icon />
|
|
547
|
+
</Select.Trigger>
|
|
548
|
+
<Select.Popup size="small">
|
|
549
|
+
{Object.entries(items).map(([value, label]) => (
|
|
550
|
+
<Select.Item key={value} value={value}>
|
|
551
|
+
<Select.ItemText>{label}</Select.ItemText>
|
|
552
|
+
</Select.Item>
|
|
553
|
+
))}
|
|
554
|
+
</Select.Popup>
|
|
555
|
+
</Select.Root>
|
|
556
|
+
</Field.Root>
|
|
557
|
+
)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
namespace styles {
|
|
561
|
+
export const field = style({ width: '12rem !custom' })
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
If a guess could cost something, such as a withdrawal network, leave the select empty. Set a `placeholder` on `Select.Value` that names the choice, such as Select a network, and validate on submit. Add a None option only if none is a valid answer.
|
|
566
|
+
|
|
567
|
+
### Keep Option Rows Scannable
|
|
568
|
+
|
|
569
|
+
* **Prefix and suffix:** Put a token icon or [Avatar](avatar.md) in `Select.ItemPrefix`, and a secondary value such as a balance in `Select.ItemSuffix`.
|
|
570
|
+
* **Typeahead label:** If a row holds a prefix or suffix, give `Select.Item` a `label`.
|
|
571
|
+
* **Text length:** Keep `Select.ItemText` short. It truncates beside a prefix or suffix.
|
|
572
|
+
* **First words:** Start options with distinct words, in a parallel format, such as Last hour and Last 24 hours. Typeahead jumps by first letters.
|
|
573
|
+
|
|
574
|
+
**Do:** Put the icon and balance in the item's prefix and suffix, and keep the text short.
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
/**
|
|
578
|
+
* Do example for "Keep Option Rows Scannable" on the Select page.
|
|
579
|
+
* Put the icon and balance in the item's prefix and suffix, and keep the text short.
|
|
580
|
+
* @module
|
|
581
|
+
*/
|
|
582
|
+
import { Check } from '@tempoxyz/ds/icons'
|
|
583
|
+
import { style } from '@tempoxyz/ds'
|
|
584
|
+
import pathusd from '../../coins/PathUSD.svg'
|
|
585
|
+
import usdce from '../../coins/USDCe.svg'
|
|
586
|
+
import usdt0 from '../../coins/USDT0.svg'
|
|
587
|
+
|
|
588
|
+
const assets = [
|
|
589
|
+
{ balance: '1,250.00', icon: pathusd, name: 'pathUSD', selected: true },
|
|
590
|
+
{ balance: '480.25', icon: usdce, name: 'USDC.e', selected: false },
|
|
591
|
+
{ balance: '12.00', icon: usdt0, name: 'USDT0', selected: false },
|
|
592
|
+
]
|
|
593
|
+
|
|
594
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
595
|
+
export function Demo() {
|
|
596
|
+
return (
|
|
597
|
+
<div {...styles.popup()}>
|
|
598
|
+
{assets.map((asset) => (
|
|
599
|
+
<div key={asset.name} {...styles.item()}>
|
|
600
|
+
<img alt="" height={20} src={asset.icon} width={20} />
|
|
601
|
+
<span {...styles.text()}>{asset.name}</span>
|
|
602
|
+
<span {...styles.suffix()}>{asset.balance}</span>
|
|
603
|
+
<span {...styles.check()}>{asset.selected && <Check />}</span>
|
|
604
|
+
</div>
|
|
605
|
+
))}
|
|
606
|
+
</div>
|
|
607
|
+
)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
namespace styles {
|
|
611
|
+
export const check = style({ display: 'flex', width: '16px !custom' })
|
|
612
|
+
|
|
613
|
+
export const item = style({
|
|
614
|
+
alignItems: 'center',
|
|
615
|
+
borderRadius: 'xs',
|
|
616
|
+
color: 'content.primary',
|
|
617
|
+
display: 'flex',
|
|
618
|
+
gap: '8',
|
|
619
|
+
minHeight: '40',
|
|
620
|
+
paddingInline: '12',
|
|
621
|
+
typography: 'body.b2',
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
export const popup = style({
|
|
625
|
+
backgroundColor: 'background.elevated',
|
|
626
|
+
borderRadius: 'sm',
|
|
627
|
+
boxShadow: 'regular',
|
|
628
|
+
boxSizing: 'border-box',
|
|
629
|
+
padding: '8',
|
|
630
|
+
width: '16rem !custom',
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
export const suffix = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
634
|
+
|
|
635
|
+
export const text = style({ flex: '1 !custom' })
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
**Don't:** Pack names, details, and balances into the option text.
|
|
640
|
+
|
|
641
|
+
```tsx
|
|
642
|
+
/**
|
|
643
|
+
* Don't example for "Keep Option Rows Scannable" on the Select page.
|
|
644
|
+
* Don't pack names, details, and balances into the option text.
|
|
645
|
+
* @module
|
|
646
|
+
*/
|
|
647
|
+
import { Check } from '@tempoxyz/ds/icons'
|
|
648
|
+
import { style } from '@tempoxyz/ds'
|
|
649
|
+
|
|
650
|
+
const assets = [
|
|
651
|
+
{ label: 'pathUSD, Tempo stablecoin, balance 1,250.00', selected: true },
|
|
652
|
+
{ label: 'USDC.e, bridged USD Coin, balance 480.25', selected: false },
|
|
653
|
+
{ label: 'USDT0, bridged Tether, balance 12.00', selected: false },
|
|
654
|
+
]
|
|
655
|
+
|
|
656
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
657
|
+
export function Demo() {
|
|
658
|
+
return (
|
|
659
|
+
<div {...styles.popup()}>
|
|
660
|
+
{assets.map((asset) => (
|
|
661
|
+
<div key={asset.label} {...styles.item()}>
|
|
662
|
+
<span {...styles.text()}>{asset.label}</span>
|
|
663
|
+
<span {...styles.check()}>{asset.selected && <Check />}</span>
|
|
664
|
+
</div>
|
|
665
|
+
))}
|
|
666
|
+
</div>
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
namespace styles {
|
|
671
|
+
export const check = style({ display: 'flex', flexShrink: '0 !custom', width: '16px !custom' })
|
|
672
|
+
|
|
673
|
+
export const item = style({
|
|
674
|
+
alignItems: 'center',
|
|
675
|
+
borderRadius: 'xs',
|
|
676
|
+
color: 'content.primary',
|
|
677
|
+
display: 'flex',
|
|
678
|
+
gap: '8',
|
|
679
|
+
minHeight: '40',
|
|
680
|
+
paddingInline: '12',
|
|
681
|
+
typography: 'body.b2',
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
export const popup = style({
|
|
685
|
+
backgroundColor: 'background.elevated',
|
|
686
|
+
borderRadius: 'sm',
|
|
687
|
+
boxShadow: 'regular',
|
|
688
|
+
boxSizing: 'border-box',
|
|
689
|
+
padding: '8',
|
|
690
|
+
width: '16rem !custom',
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
export const text = style({
|
|
694
|
+
flex: '1 !custom',
|
|
695
|
+
minWidth: '0 !custom',
|
|
696
|
+
overflow: 'hidden',
|
|
697
|
+
textOverflow: 'ellipsis',
|
|
698
|
+
whiteSpace: 'nowrap',
|
|
699
|
+
})
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Accessibility
|
|
704
|
+
|
|
705
|
+
Select handles arrow keys, typeahead, and Escape. It follows the [WAI-ARIA Authoring Practices select-only combobox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-select-only/).
|
|
706
|
+
|
|
707
|
+
### Semantics
|
|
708
|
+
|
|
709
|
+
* **Select.Trigger (combobox):** A button that announces its popup and whether the list is open.
|
|
710
|
+
* **Select.Popup and Select.Item (listbox):** The list renders in a portal. The selected option shows a check, not only a color.
|
|
711
|
+
* **Select.Group (group):** A set of related options, named by the group label inside it.
|
|
712
|
+
* **Invalid and Disabled (aria-invalid):** `invalid` marks the trigger and fills it red. `disabled` removes it from the tab order.
|
|
713
|
+
* **Select.Icon (aria-hidden):** Decorative, so it is hidden from assistive technology and not announced.
|
|
714
|
+
* **Target Size (48px):** The trigger matches the 48px field, above the 24px WCAG 2.2 minimum.
|
|
715
|
+
|
|
716
|
+
### Keyboard
|
|
717
|
+
|
|
718
|
+
* **Move to the Trigger (Tab):** Moves focus to the trigger.
|
|
719
|
+
* **Open the List (Enter, Space, or Down Arrow):** Opens the list and focuses the selected option.
|
|
720
|
+
* **Move Between Options (Down Arrow and Up Arrow):** Move focus to the next or previous option.
|
|
721
|
+
* **Jump to the Ends (Home and End):** Move focus to the first or last option.
|
|
722
|
+
* **Type to Find (Character):** Focuses the next option starting with that key.
|
|
723
|
+
* **Select an Option (Enter or Space):** On an option, selects it and closes the list.
|
|
724
|
+
* **Close the List (Escape):** Keeps the value and returns focus to the trigger.
|
|
725
|
+
|
|
726
|
+
### Your Responsibilities
|
|
727
|
+
|
|
728
|
+
* **Name the Trigger (Field.Label):** Use a visible field label. If none fits, set `aria-label`.
|
|
729
|
+
* **Explain Invalid Values (Field.Error):** Explain invalid values in text, not only the red fill.
|
|
730
|
+
|
|
731
|
+
## Related
|
|
732
|
+
|
|
733
|
+
[Combobox](combobox.md)
|
|
734
|
+
|
|
735
|
+
Use for long, filterable lists.
|
|
736
|
+
|
|
737
|
+
[RadioGroup](radio-group.md)
|
|
738
|
+
|
|
739
|
+
Use for a few options shown at once.
|
|
740
|
+
|
|
741
|
+
[ChoiceGroup](choice-group.md)
|
|
742
|
+
|
|
743
|
+
Use for switching views.
|
|
744
|
+
|
|
745
|
+
[Menu](menu.md)
|
|
746
|
+
|
|
747
|
+
Use for lists of actions.
|
|
748
|
+
|
|
749
|
+
[Dashboards](../patterns/dashboards.md)
|
|
750
|
+
|
|
751
|
+
Covers filter bars and controls.
|