@tempoxyz/ds 0.3.10 → 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.
- package/dist/docs/AGENTS.md +9 -0
- package/dist/docs/components/accordion.md +489 -0
- package/dist/docs/components/address-card.md +147 -0
- package/dist/docs/components/agent-logo.md +194 -0
- package/dist/docs/components/alert-dialog.md +631 -0
- package/dist/docs/components/alert.md +448 -0
- package/dist/docs/components/amount-input.md +305 -0
- package/dist/docs/components/amount.md +794 -0
- package/dist/docs/components/app-shell.md +559 -0
- package/dist/docs/components/asset-chip.md +294 -0
- package/dist/docs/components/autocomplete.md +619 -0
- package/dist/docs/components/avatar.md +321 -0
- package/dist/docs/components/badge.md +339 -0
- package/dist/docs/components/badged-logo.md +116 -0
- package/dist/docs/components/banner.md +379 -0
- package/dist/docs/components/bar-chart.md +208 -0
- package/dist/docs/components/breadcrumbs.md +380 -0
- package/dist/docs/components/button-group.md +286 -0
- package/dist/docs/components/button.md +650 -0
- package/dist/docs/components/calendar.md +375 -0
- package/dist/docs/components/callout.md +96 -0
- package/dist/docs/components/checkbox-group.md +521 -0
- package/dist/docs/components/checkbox.md +467 -0
- package/dist/docs/components/choice-group.md +516 -0
- package/dist/docs/components/choicebox.md +641 -0
- package/dist/docs/components/code.md +100 -0
- package/dist/docs/components/combobox.md +939 -0
- package/dist/docs/components/container.md +257 -0
- package/dist/docs/components/copy-button.md +642 -0
- package/dist/docs/components/date-picker.md +514 -0
- package/dist/docs/components/dialog.md +1332 -0
- package/dist/docs/components/drawer.md +966 -0
- package/dist/docs/components/empty.md +340 -0
- package/dist/docs/components/field.md +570 -0
- package/dist/docs/components/fieldset.md +398 -0
- package/dist/docs/components/frame.md +434 -0
- package/dist/docs/components/gauge.md +299 -0
- package/dist/docs/components/get-started-visual.md +117 -0
- package/dist/docs/components/grid.md +288 -0
- package/dist/docs/components/hero-visual.md +155 -0
- package/dist/docs/components/hex.md +487 -0
- package/dist/docs/components/icon-tile.md +292 -0
- package/dist/docs/components/identicon.md +371 -0
- package/dist/docs/components/inline.md +233 -0
- package/dist/docs/components/inset.md +225 -0
- package/dist/docs/components/kbd.md +423 -0
- package/dist/docs/components/list-group.md +981 -0
- package/dist/docs/components/loading-dots.md +237 -0
- package/dist/docs/components/menu.md +934 -0
- package/dist/docs/components/native-select.md +204 -0
- package/dist/docs/components/network-logo.md +248 -0
- package/dist/docs/components/number-field.md +543 -0
- package/dist/docs/components/otp-field.md +616 -0
- package/dist/docs/components/pagination.md +516 -0
- package/dist/docs/components/popover.md +544 -0
- package/dist/docs/components/progress.md +388 -0
- package/dist/docs/components/qr.md +314 -0
- package/dist/docs/components/radio-group.md +528 -0
- package/dist/docs/components/secret-value.md +406 -0
- package/dist/docs/components/select.md +751 -0
- package/dist/docs/components/separator.md +379 -0
- package/dist/docs/components/sidebar-menu.md +922 -0
- package/dist/docs/components/skeleton.md +517 -0
- package/dist/docs/components/slider.md +539 -0
- package/dist/docs/components/snippet.md +424 -0
- package/dist/docs/components/spinner.md +216 -0
- package/dist/docs/components/split-bar.md +139 -0
- package/dist/docs/components/split-button.md +464 -0
- package/dist/docs/components/stack.md +299 -0
- package/dist/docs/components/status-chip.md +226 -0
- package/dist/docs/components/status-indicator.md +119 -0
- package/dist/docs/components/status-row.md +174 -0
- package/dist/docs/components/stepper.md +451 -0
- package/dist/docs/components/success-hero.md +157 -0
- package/dist/docs/components/surface.md +639 -0
- package/dist/docs/components/switch.md +472 -0
- package/dist/docs/components/table.md +967 -0
- package/dist/docs/components/tabs.md +627 -0
- package/dist/docs/components/tempo-logo.md +185 -0
- package/dist/docs/components/text-input.md +624 -0
- package/dist/docs/components/textarea.md +352 -0
- package/dist/docs/components/toast.md +535 -0
- package/dist/docs/components/token-logo.md +371 -0
- package/dist/docs/components/token.md +247 -0
- package/dist/docs/components/tooltip.md +490 -0
- package/dist/docs/components/verification-code.md +116 -0
- package/dist/docs/components/visual.md +249 -0
- package/dist/docs/components.md +203 -0
- package/dist/docs/foundations/accessibility.md +377 -0
- package/dist/docs/foundations/agents.md +150 -0
- package/dist/docs/foundations/color.md +522 -0
- package/dist/docs/foundations/content/tone.md +194 -0
- package/dist/docs/foundations/content/voice.md +194 -0
- package/dist/docs/foundations/content/writing-style.md +406 -0
- package/dist/docs/foundations/iconography.md +282 -0
- package/dist/docs/foundations/intent.md +360 -0
- package/dist/docs/foundations/layout.md +508 -0
- package/dist/docs/foundations/linter.md +296 -0
- package/dist/docs/foundations/motion.md +151 -0
- package/dist/docs/foundations/radius-and-padding.md +86 -0
- package/dist/docs/foundations/typography.md +266 -0
- package/dist/docs/index.md +137 -0
- package/dist/docs/introduction/faq.md +132 -0
- package/dist/docs/introduction/getting-started.md +377 -0
- package/dist/docs/patterns/blockchain-values.md +709 -0
- package/dist/docs/patterns/dashboards.md +638 -0
- package/dist/docs/patterns/destructive-actions.md +626 -0
- package/dist/docs/patterns/empty-states.md +612 -0
- package/dist/docs/patterns/errors-and-validation.md +808 -0
- package/dist/docs/patterns/feedback-and-status.md +518 -0
- package/dist/docs/patterns/forms.md +679 -0
- package/dist/docs/patterns/loading.md +630 -0
- package/dist/docs/patterns/navigation.md +397 -0
- package/dist/docs/patterns/payments.md +765 -0
- package/dist/docs/patterns/responsive-layout.md +997 -0
- package/dist/docs/patterns/tables-and-lists.md +964 -0
- package/dist/docs/products/tempo-console.md +671 -0
- package/dist/docs/products/tempo-performance.md +437 -0
- package/dist/docs/products/tempo-wallet.md +438 -0
- package/package.json +1 -1
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
# Empty States
|
|
2
|
+
|
|
3
|
+
Explain why a region has nothing to show, and offer the next step that fills it.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
An empty region can mean missing data, a filter with no matches, finished work, or a failed request. Each cause takes different words and actions. [Empty](/docs/components/empty) composes the parts, and every empty state covers three points:
|
|
8
|
+
|
|
9
|
+
* **What Is This Place? (Empty.Title):** Names the content that would appear.
|
|
10
|
+
* **Why Is It Empty? (Empty.Description):** Explains the cause in one sentence.
|
|
11
|
+
* **What Can I Do? (Empty.Actions):** One action that fills the region, if one exists.
|
|
12
|
+
|
|
13
|
+
## Variants
|
|
14
|
+
|
|
15
|
+
The cause decides the text and the action. These are reference layouts built from [Surface](/docs/components/surface), [Empty](/docs/components/empty), and [Button](/docs/components/button), not shipped components.
|
|
16
|
+
|
|
17
|
+
### First Use
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Button, Empty, style, Surface } from '@tempoxyz/ds'
|
|
21
|
+
import { History } from '@tempoxyz/ds/icons'
|
|
22
|
+
|
|
23
|
+
export const fill = true
|
|
24
|
+
export function Demo() {
|
|
25
|
+
return (
|
|
26
|
+
<div {...styles.page()}>
|
|
27
|
+
<Surface {...styles.card()}>
|
|
28
|
+
<Surface.Header>
|
|
29
|
+
<Surface.Title>Activity</Surface.Title>
|
|
30
|
+
</Surface.Header>
|
|
31
|
+
<Empty.Root>
|
|
32
|
+
<Empty.Icon>
|
|
33
|
+
<History />
|
|
34
|
+
</Empty.Icon>
|
|
35
|
+
<Empty.Title>No activity yet</Empty.Title>
|
|
36
|
+
<Empty.Description>
|
|
37
|
+
Payments you send and receive appear here. Add funds to make your first payment.
|
|
38
|
+
</Empty.Description>
|
|
39
|
+
<Empty.Actions>
|
|
40
|
+
<Button size="small">Add funds</Button>
|
|
41
|
+
</Empty.Actions>
|
|
42
|
+
</Empty.Root>
|
|
43
|
+
</Surface>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
namespace styles {
|
|
49
|
+
export const card = style({
|
|
50
|
+
boxSizing: 'border-box',
|
|
51
|
+
maxWidth: '26rem !custom',
|
|
52
|
+
width: '100% !custom',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
export const page = style({
|
|
56
|
+
backgroundColor: 'background.primary',
|
|
57
|
+
display: 'grid',
|
|
58
|
+
justifyItems: 'center',
|
|
59
|
+
padding: '16',
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyFirstUse.tsx)
|
|
65
|
+
|
|
66
|
+
Nothing exists yet. State what will appear, and offer the action that creates the first item, such as Add funds or Create key.
|
|
67
|
+
|
|
68
|
+
### No Results
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { Button, Empty, Field, Identicon, ListGroup, style, Surface, TextInput } from '@tempoxyz/ds'
|
|
72
|
+
import { Search } from '@tempoxyz/ds/icons'
|
|
73
|
+
import { useRef, useState } from 'react'
|
|
74
|
+
|
|
75
|
+
export const fill = true
|
|
76
|
+
const contacts = [
|
|
77
|
+
['Ada', '0x71C7656EC7ab88b098defB751B7401B5f6d8976F'],
|
|
78
|
+
['Payroll', '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC'],
|
|
79
|
+
['Treasury', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'],
|
|
80
|
+
] as const
|
|
81
|
+
|
|
82
|
+
export function Demo() {
|
|
83
|
+
const input = useRef<HTMLInputElement>(null)
|
|
84
|
+
const [query, setQuery] = useState('zara')
|
|
85
|
+
const matches = contacts.filter(([name, address]) =>
|
|
86
|
+
`${name} ${address}`.toLowerCase().includes(query.trim().toLowerCase()),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<div {...styles.page()}>
|
|
91
|
+
<Surface {...styles.card()}>
|
|
92
|
+
<Surface.Header>
|
|
93
|
+
<Surface.Title>Contacts</Surface.Title>
|
|
94
|
+
</Surface.Header>
|
|
95
|
+
<Field.Root>
|
|
96
|
+
<TextInput.Group>
|
|
97
|
+
<TextInput.Prefix>
|
|
98
|
+
<Search />
|
|
99
|
+
</TextInput.Prefix>
|
|
100
|
+
<TextInput.Control
|
|
101
|
+
aria-label="Search contacts"
|
|
102
|
+
onValueChange={setQuery}
|
|
103
|
+
placeholder="Name or address"
|
|
104
|
+
ref={input}
|
|
105
|
+
type="search"
|
|
106
|
+
value={query}
|
|
107
|
+
/>
|
|
108
|
+
<TextInput.Clear />
|
|
109
|
+
</TextInput.Group>
|
|
110
|
+
</Field.Root>
|
|
111
|
+
{/* Stays mounted, so screen readers hear the count change as you type. */}
|
|
112
|
+
<p role="status" {...styles.status()}>
|
|
113
|
+
{matches.length === 1 ? '1 contact' : `${matches.length} contacts`}
|
|
114
|
+
</p>
|
|
115
|
+
{matches.length > 0 ? (
|
|
116
|
+
<div {...styles.rows()}>
|
|
117
|
+
<ListGroup.Root>
|
|
118
|
+
{matches.map(([name, address]) => (
|
|
119
|
+
<ListGroup.Row key={address}>
|
|
120
|
+
<Identicon address={address} aria-hidden size="24" />
|
|
121
|
+
<ListGroup.Info>
|
|
122
|
+
<ListGroup.Title>{name}</ListGroup.Title>
|
|
123
|
+
<ListGroup.Description>
|
|
124
|
+
{address.slice(0, 6)}…{address.slice(-4)}
|
|
125
|
+
</ListGroup.Description>
|
|
126
|
+
</ListGroup.Info>
|
|
127
|
+
</ListGroup.Row>
|
|
128
|
+
))}
|
|
129
|
+
</ListGroup.Root>
|
|
130
|
+
</div>
|
|
131
|
+
) : (
|
|
132
|
+
<Empty.Root>
|
|
133
|
+
<Empty.Icon>
|
|
134
|
+
<Search />
|
|
135
|
+
</Empty.Icon>
|
|
136
|
+
<Empty.Title>No contacts match “{query.trim()}”</Empty.Title>
|
|
137
|
+
<Empty.Description>
|
|
138
|
+
Check the spelling, or search by the full 0x address.
|
|
139
|
+
</Empty.Description>
|
|
140
|
+
<Empty.Actions>
|
|
141
|
+
<Button
|
|
142
|
+
onClick={() => {
|
|
143
|
+
setQuery('')
|
|
144
|
+
input.current?.focus()
|
|
145
|
+
}}
|
|
146
|
+
size="small"
|
|
147
|
+
variant="secondary"
|
|
148
|
+
>
|
|
149
|
+
Clear search
|
|
150
|
+
</Button>
|
|
151
|
+
</Empty.Actions>
|
|
152
|
+
</Empty.Root>
|
|
153
|
+
)}
|
|
154
|
+
</Surface>
|
|
155
|
+
</div>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
namespace styles {
|
|
160
|
+
export const card = style({
|
|
161
|
+
boxSizing: 'border-box',
|
|
162
|
+
maxWidth: '26rem !custom',
|
|
163
|
+
width: '100% !custom',
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
export const page = style({
|
|
167
|
+
backgroundColor: 'background.primary',
|
|
168
|
+
display: 'grid',
|
|
169
|
+
justifyItems: 'center',
|
|
170
|
+
padding: '16',
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
export const rows = style({ marginBlockStart: '8' })
|
|
174
|
+
|
|
175
|
+
export const status = style({
|
|
176
|
+
clipPath: 'inset(50%) !custom',
|
|
177
|
+
height: '1px !custom',
|
|
178
|
+
margin: '0',
|
|
179
|
+
overflow: 'hidden',
|
|
180
|
+
position: 'absolute',
|
|
181
|
+
whiteSpace: 'nowrap',
|
|
182
|
+
width: '1px !custom',
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyNoResults.tsx)
|
|
188
|
+
|
|
189
|
+
Repeat the query in the title, so the reader can spot a typo. Offer Clear search or Clear filters as a `secondary` button, and keep the search field and filters visible above the empty state.
|
|
190
|
+
|
|
191
|
+
**Do:** Repeat the query and offer a way to widen the search.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<Empty.Root>
|
|
195
|
+
<Empty.Icon>
|
|
196
|
+
<Search />
|
|
197
|
+
</Empty.Icon>
|
|
198
|
+
<Empty.Title>No results for "rent"</Empty.Title>
|
|
199
|
+
<Empty.Description>Check the spelling or search for another payment.</Empty.Description>
|
|
200
|
+
<Empty.Actions>
|
|
201
|
+
<Button size="small" variant="secondary">Clear search</Button>
|
|
202
|
+
</Empty.Actions>
|
|
203
|
+
</Empty.Root>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Don't:** If a filter hides existing items, don't show the first use state.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
<Empty.Root>
|
|
210
|
+
<Empty.Icon>
|
|
211
|
+
<Wallet />
|
|
212
|
+
</Empty.Icon>
|
|
213
|
+
<Empty.Title>No payments yet</Empty.Title>
|
|
214
|
+
<Empty.Description>Payments you send or receive appear here.</Empty.Description>
|
|
215
|
+
<Empty.Actions>
|
|
216
|
+
<Button size="small">Add funds</Button>
|
|
217
|
+
</Empty.Actions>
|
|
218
|
+
</Empty.Root>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Cleared
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
import { Button, Empty, style, Surface } from '@tempoxyz/ds'
|
|
225
|
+
import { CheckCircle } from '@tempoxyz/ds/icons'
|
|
226
|
+
|
|
227
|
+
export const fill = true
|
|
228
|
+
export function Demo() {
|
|
229
|
+
return (
|
|
230
|
+
<div {...styles.page()}>
|
|
231
|
+
<Surface {...styles.card()}>
|
|
232
|
+
<Surface.Header>
|
|
233
|
+
<Surface.Title>Payment requests</Surface.Title>
|
|
234
|
+
</Surface.Header>
|
|
235
|
+
<Empty.Root>
|
|
236
|
+
<Empty.Icon>
|
|
237
|
+
<CheckCircle />
|
|
238
|
+
</Empty.Icon>
|
|
239
|
+
<Empty.Title>No pending requests</Empty.Title>
|
|
240
|
+
<Empty.Description>
|
|
241
|
+
You've paid or declined every request. New requests appear here.
|
|
242
|
+
</Empty.Description>
|
|
243
|
+
<Empty.Actions>
|
|
244
|
+
<Button size="small" variant="secondary">
|
|
245
|
+
View history
|
|
246
|
+
</Button>
|
|
247
|
+
</Empty.Actions>
|
|
248
|
+
</Empty.Root>
|
|
249
|
+
</Surface>
|
|
250
|
+
</div>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
namespace styles {
|
|
255
|
+
export const card = style({
|
|
256
|
+
boxSizing: 'border-box',
|
|
257
|
+
maxWidth: '26rem !custom',
|
|
258
|
+
width: '100% !custom',
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
export const page = style({
|
|
262
|
+
backgroundColor: 'background.primary',
|
|
263
|
+
display: 'grid',
|
|
264
|
+
justifyItems: 'center',
|
|
265
|
+
padding: '16',
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyCleared.tsx)
|
|
271
|
+
|
|
272
|
+
If the reader processed everything, such as every payment request, confirm that nothing is left. Leave out the primary action. If a history exists, link to it.
|
|
273
|
+
|
|
274
|
+
**Do:** Confirm the reader finished, and state what will appear next.
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<Empty.Root>
|
|
278
|
+
<Empty.Icon>
|
|
279
|
+
<Check />
|
|
280
|
+
</Empty.Icon>
|
|
281
|
+
<Empty.Title>All caught up</Empty.Title>
|
|
282
|
+
<Empty.Description>New requests appear here when someone asks you to pay.</Empty.Description>
|
|
283
|
+
</Empty.Root>
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Don't:** Use playful filler in place of a clear outcome.
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
<Empty.Root>
|
|
290
|
+
<Empty.Icon>
|
|
291
|
+
<Clipboard />
|
|
292
|
+
</Empty.Icon>
|
|
293
|
+
<Empty.Title>Nothing here!</Empty.Title>
|
|
294
|
+
<Empty.Description>Looks like it is pretty quiet. Check back later!</Empty.Description>
|
|
295
|
+
</Empty.Root>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Error-Empty
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { Button, Empty, ListGroup, style, Surface } from '@tempoxyz/ds'
|
|
302
|
+
import { AlertCircle } from '@tempoxyz/ds/icons'
|
|
303
|
+
import { useState } from 'react'
|
|
304
|
+
|
|
305
|
+
export const fill = true
|
|
306
|
+
function Failed(props: { onRetry: () => void; retrying: boolean }) {
|
|
307
|
+
return (
|
|
308
|
+
<Empty.Root>
|
|
309
|
+
<Empty.Icon>
|
|
310
|
+
<AlertCircle />
|
|
311
|
+
</Empty.Icon>
|
|
312
|
+
<Empty.Title>Couldn't load activity</Empty.Title>
|
|
313
|
+
<Empty.Description>
|
|
314
|
+
Check your connection, then try again. Your balance and payments aren't affected.
|
|
315
|
+
</Empty.Description>
|
|
316
|
+
<Empty.Actions>
|
|
317
|
+
<Button loading={props.retrying} onClick={props.onRetry} size="small">
|
|
318
|
+
Try again
|
|
319
|
+
</Button>
|
|
320
|
+
</Empty.Actions>
|
|
321
|
+
</Empty.Root>
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function Demo() {
|
|
326
|
+
const [loaded, setLoaded] = useState(false)
|
|
327
|
+
const [retrying, setRetrying] = useState(false)
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<div {...styles.page()}>
|
|
331
|
+
<Surface {...styles.card()}>
|
|
332
|
+
<Surface.Header>
|
|
333
|
+
<Surface.Title>Activity</Surface.Title>
|
|
334
|
+
</Surface.Header>
|
|
335
|
+
{loaded ? (
|
|
336
|
+
<div {...styles.rows()}>
|
|
337
|
+
<ListGroup.Root>
|
|
338
|
+
<ListGroup.Row>
|
|
339
|
+
<ListGroup.Label>Received from 0x3C44…93BC</ListGroup.Label>
|
|
340
|
+
120.00 USDC
|
|
341
|
+
</ListGroup.Row>
|
|
342
|
+
<ListGroup.Row>
|
|
343
|
+
<ListGroup.Label>Sent to 0x71C7…976F</ListGroup.Label>
|
|
344
|
+
25.00 USDC
|
|
345
|
+
</ListGroup.Row>
|
|
346
|
+
</ListGroup.Root>
|
|
347
|
+
</div>
|
|
348
|
+
) : (
|
|
349
|
+
<Failed
|
|
350
|
+
onRetry={() => {
|
|
351
|
+
setRetrying(true)
|
|
352
|
+
setTimeout(() => {
|
|
353
|
+
setRetrying(false)
|
|
354
|
+
setLoaded(true)
|
|
355
|
+
}, 1000)
|
|
356
|
+
}}
|
|
357
|
+
retrying={retrying}
|
|
358
|
+
/>
|
|
359
|
+
)}
|
|
360
|
+
</Surface>
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
namespace styles {
|
|
366
|
+
export const card = style({
|
|
367
|
+
boxSizing: 'border-box',
|
|
368
|
+
maxWidth: '26rem !custom',
|
|
369
|
+
width: '100% !custom',
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
export const page = style({
|
|
373
|
+
backgroundColor: 'background.primary',
|
|
374
|
+
display: 'grid',
|
|
375
|
+
justifyItems: 'center',
|
|
376
|
+
padding: '16',
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
export const rows = style({ marginBlockStart: '8' })
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Patterns/EmptyError.tsx)
|
|
384
|
+
|
|
385
|
+
If the request fails, write Couldn't load followed by the content, and offer Try again. Set `loading` on the [Button](/docs/components/button) while the retry runs.
|
|
386
|
+
|
|
387
|
+
## Guidelines
|
|
388
|
+
|
|
389
|
+
### Explain the Cause and the Next Step
|
|
390
|
+
|
|
391
|
+
Write the title as a short statement of what is missing, such as No API keys yet. Use the description for the cause and what the action does.
|
|
392
|
+
|
|
393
|
+
**Do:** State what is missing and offer the action that adds it.
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
/**
|
|
397
|
+
* Do example for "Explain the Cause and the Next Step" on the Empty States page.
|
|
398
|
+
* State what is missing and offer the action that adds it.
|
|
399
|
+
* @module
|
|
400
|
+
*/
|
|
401
|
+
import { Button, Empty } from '@tempoxyz/ds'
|
|
402
|
+
import { Key } from '@tempoxyz/ds/icons'
|
|
403
|
+
|
|
404
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
405
|
+
export function Demo() {
|
|
406
|
+
return (
|
|
407
|
+
<Empty.Root>
|
|
408
|
+
<Empty.Icon>
|
|
409
|
+
<Key />
|
|
410
|
+
</Empty.Icon>
|
|
411
|
+
<Empty.Title>No API keys yet</Empty.Title>
|
|
412
|
+
<Empty.Description>Create a key to call the Tempo API from your server.</Empty.Description>
|
|
413
|
+
<Empty.Actions>
|
|
414
|
+
<Button size="small">Create key</Button>
|
|
415
|
+
</Empty.Actions>
|
|
416
|
+
</Empty.Root>
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Don't:** Show a bare No data message with nothing to do next.
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
/**
|
|
425
|
+
* Don't example for "Explain the Cause and the Next Step" on the Empty States page.
|
|
426
|
+
* Don't show a bare No data message with nothing to do next.
|
|
427
|
+
* @module
|
|
428
|
+
*/
|
|
429
|
+
import { Empty } from '@tempoxyz/ds'
|
|
430
|
+
|
|
431
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
432
|
+
export function Demo() {
|
|
433
|
+
return (
|
|
434
|
+
<Empty.Root>
|
|
435
|
+
<Empty.Title>No data</Empty.Title>
|
|
436
|
+
</Empty.Root>
|
|
437
|
+
)
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Never Show a Failed Request as Empty
|
|
442
|
+
|
|
443
|
+
An empty list after a failed request falsely reports no activity. Keep empty states for successful requests with no content.
|
|
444
|
+
|
|
445
|
+
**Do:** State that the content couldn't load, and offer Try again.
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
/**
|
|
449
|
+
* Do example for "Never Show a Failed Request as Empty" on the Empty States page.
|
|
450
|
+
* State that the content couldn't load, and offer Try again.
|
|
451
|
+
* @module
|
|
452
|
+
*/
|
|
453
|
+
import { Button, Empty } from '@tempoxyz/ds'
|
|
454
|
+
import { AlertCircle } from '@tempoxyz/ds/icons'
|
|
455
|
+
|
|
456
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
457
|
+
export function Demo() {
|
|
458
|
+
return (
|
|
459
|
+
<Empty.Root>
|
|
460
|
+
<Empty.Icon>
|
|
461
|
+
<AlertCircle />
|
|
462
|
+
</Empty.Icon>
|
|
463
|
+
<Empty.Title>Couldn't load activity</Empty.Title>
|
|
464
|
+
<Empty.Description>Check your connection, then try again.</Empty.Description>
|
|
465
|
+
<Empty.Actions>
|
|
466
|
+
<Button size="small">Try again</Button>
|
|
467
|
+
</Empty.Actions>
|
|
468
|
+
</Empty.Root>
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Don't:** Show No activity yet after the request failed.
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
/**
|
|
477
|
+
* Don't example for "Never Show a Failed Request as Empty" on the Empty States page.
|
|
478
|
+
* Don't show No activity yet after the request failed.
|
|
479
|
+
* @module
|
|
480
|
+
*/
|
|
481
|
+
import { Empty } from '@tempoxyz/ds'
|
|
482
|
+
import { History } from '@tempoxyz/ds/icons'
|
|
483
|
+
|
|
484
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
485
|
+
export function Demo() {
|
|
486
|
+
return (
|
|
487
|
+
<Empty.Root>
|
|
488
|
+
<Empty.Icon>
|
|
489
|
+
<History />
|
|
490
|
+
</Empty.Icon>
|
|
491
|
+
<Empty.Title>No activity yet</Empty.Title>
|
|
492
|
+
<Empty.Description>Payments you send and receive appear here.</Empty.Description>
|
|
493
|
+
</Empty.Root>
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Offer One Primary Action
|
|
499
|
+
|
|
500
|
+
Make it the task that fills the region. Add a secondary button only for a related step, such as a guide, and use `Button.Link` if the action opens another page.
|
|
501
|
+
|
|
502
|
+
**Do:** Pair one primary action with an optional secondary one.
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
/**
|
|
506
|
+
* Do example for "Offer One Primary Action" on the Empty States page.
|
|
507
|
+
* Pair one primary action with an optional secondary one.
|
|
508
|
+
* @module
|
|
509
|
+
*/
|
|
510
|
+
import { Button, Empty } from '@tempoxyz/ds'
|
|
511
|
+
import { Link } from '@tempoxyz/ds/icons'
|
|
512
|
+
|
|
513
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
514
|
+
export function Demo() {
|
|
515
|
+
return (
|
|
516
|
+
<Empty.Root>
|
|
517
|
+
<Empty.Icon>
|
|
518
|
+
<Link />
|
|
519
|
+
</Empty.Icon>
|
|
520
|
+
<Empty.Title>No webhooks</Empty.Title>
|
|
521
|
+
<Empty.Description>Add an endpoint to receive payment events.</Empty.Description>
|
|
522
|
+
<Empty.Actions>
|
|
523
|
+
<Button size="small">Add webhook</Button>
|
|
524
|
+
<Button size="small" variant="secondary">
|
|
525
|
+
Read the guide
|
|
526
|
+
</Button>
|
|
527
|
+
</Empty.Actions>
|
|
528
|
+
</Empty.Root>
|
|
529
|
+
)
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
**Don't:** Offer several primary actions that compete for attention.
|
|
534
|
+
|
|
535
|
+
```tsx
|
|
536
|
+
/**
|
|
537
|
+
* Don't example for "Offer One Primary Action" on the Empty States page.
|
|
538
|
+
* Don't offer several primary actions that compete for attention.
|
|
539
|
+
* @module
|
|
540
|
+
*/
|
|
541
|
+
import { Button, Empty } from '@tempoxyz/ds'
|
|
542
|
+
import { Link } from '@tempoxyz/ds/icons'
|
|
543
|
+
|
|
544
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
545
|
+
export function Demo() {
|
|
546
|
+
return (
|
|
547
|
+
<Empty.Root>
|
|
548
|
+
<Empty.Icon>
|
|
549
|
+
<Link />
|
|
550
|
+
</Empty.Icon>
|
|
551
|
+
<Empty.Title>No webhooks</Empty.Title>
|
|
552
|
+
<Empty.Description>Add an endpoint to receive payment events.</Empty.Description>
|
|
553
|
+
<Empty.Actions>
|
|
554
|
+
<Button size="small">Add webhook</Button>
|
|
555
|
+
<Button size="small">Import</Button>
|
|
556
|
+
<Button size="small">Test event</Button>
|
|
557
|
+
</Empty.Actions>
|
|
558
|
+
</Empty.Root>
|
|
559
|
+
)
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Fit the State to the Region
|
|
564
|
+
|
|
565
|
+
* **Pages and Cards:** Use the full empty state with an icon that hints at the content.
|
|
566
|
+
* **Small Regions:** In a small region, such as a [Popover](/docs/components/popover), use a short line of text and no icon.
|
|
567
|
+
* **Comboboxes (Combobox.Empty):** Render it in a [Combobox](/docs/components/combobox), so an empty result is announced instead of an empty popup.
|
|
568
|
+
* **Keep the Frame:** Keep the heading and filters. In a [Table](/docs/components/table), put the empty state below the header row.
|
|
569
|
+
|
|
570
|
+
### Don't Show an Empty State While Loading
|
|
571
|
+
|
|
572
|
+
* **Load First (Skeleton):** Show a [Skeleton](/docs/components/skeleton) until the request settles. An empty state that flashes first reads as a missing balance.
|
|
573
|
+
* **Then Decide:** After the request settles, choose content, an empty state, or an error (see [Loading](/docs/patterns/loading)).
|
|
574
|
+
|
|
575
|
+
## States
|
|
576
|
+
|
|
577
|
+
* **Disabled:** If the reader lacks permission for the action, leave it out and state who can act.
|
|
578
|
+
* **Success (Toast):** The new item replaces the empty state, and a [Toast](/docs/components/toast) confirms it.
|
|
579
|
+
|
|
580
|
+
## Content
|
|
581
|
+
|
|
582
|
+
* **Title (Sentence case):** Name what is missing, such as "No payments yet".
|
|
583
|
+
* **Description (1 sentence):** Address the reader as "you".
|
|
584
|
+
* **Action (Verb):** Lead with a verb, such as "Add funds".
|
|
585
|
+
|
|
586
|
+
## Accessibility
|
|
587
|
+
|
|
588
|
+
* **Heading Level (h3):** Place the state under an `h2`, such as a `Surface.Title`, so heading levels stay in order.
|
|
589
|
+
* **Announce Results (role=status):** The parts render no live region. If results change on input, announce the count in a polite region that stays mounted.
|
|
590
|
+
* **Focus After Clearing:** After Clear search, move focus to the search field.
|
|
591
|
+
|
|
592
|
+
## Related
|
|
593
|
+
|
|
594
|
+
[Empty](/docs/components/empty)
|
|
595
|
+
|
|
596
|
+
Use for the component parts.
|
|
597
|
+
|
|
598
|
+
[Combobox](/docs/components/combobox)
|
|
599
|
+
|
|
600
|
+
Use for empty popups.
|
|
601
|
+
|
|
602
|
+
[Loading](/docs/patterns/loading)
|
|
603
|
+
|
|
604
|
+
Covers what shows before content.
|
|
605
|
+
|
|
606
|
+
[Feedback and Status](/docs/patterns/feedback-and-status)
|
|
607
|
+
|
|
608
|
+
Covers the Couldn't load state.
|
|
609
|
+
|
|
610
|
+
[Tables and Lists](/docs/patterns/tables-and-lists)
|
|
611
|
+
|
|
612
|
+
Covers empty tables.
|