@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,966 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
Slides a modal panel in from an edge of the viewport.
|
|
4
|
+
|
|
5
|
+
[View source and API](https://github.com/tempoxyz/ds/blob/main/src/web/components/Drawer.tsx)
|
|
6
|
+
|
|
7
|
+
Figma: [Modal/Drawer](https://www.figma.com/design/yE96kcymnNR88PJp6G1sPs?node-id=5227-3740) (`Drawer.Popup`)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
A drawer is a modal panel that slides in from a viewport edge. A swipe closes it.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### Basic
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Button, Drawer, style } from '@tempoxyz/ds'
|
|
19
|
+
|
|
20
|
+
export function Demo() {
|
|
21
|
+
return (
|
|
22
|
+
<Drawer.Root>
|
|
23
|
+
<Drawer.Trigger render={<Button variant="secondary" />}>Open drawer</Drawer.Trigger>
|
|
24
|
+
<Drawer.Popup closeButton>
|
|
25
|
+
<Drawer.Content>
|
|
26
|
+
<Drawer.Title>Filters</Drawer.Title>
|
|
27
|
+
<Drawer.Description>Swipe down or press Escape to close.</Drawer.Description>
|
|
28
|
+
<div {...styles.actions()}>
|
|
29
|
+
<Drawer.Close render={<Button variant="secondary" />}>Close</Drawer.Close>
|
|
30
|
+
</div>
|
|
31
|
+
</Drawer.Content>
|
|
32
|
+
</Drawer.Popup>
|
|
33
|
+
</Drawer.Root>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
namespace styles {
|
|
38
|
+
export const actions = style({ display: 'flex', justifyContent: 'flex-end', marginTop: '32' })
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Drawer/Basic.tsx)
|
|
43
|
+
|
|
44
|
+
### SwipeDirection
|
|
45
|
+
|
|
46
|
+
Set `swipeDirection` on `Drawer.Root` to `down` (default, opens from the bottom), `up`, `left`, or `right`.
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { Button, Drawer, style } from '@tempoxyz/ds'
|
|
50
|
+
|
|
51
|
+
const edges = { down: 'bottom', left: 'left', right: 'right', up: 'top' } as const
|
|
52
|
+
|
|
53
|
+
export function Demo() {
|
|
54
|
+
return (
|
|
55
|
+
<div {...styles.row()}>
|
|
56
|
+
{(['down', 'right', 'left', 'up'] as const).map((direction) => (
|
|
57
|
+
<Drawer.Root key={direction} swipeDirection={direction}>
|
|
58
|
+
<Drawer.Trigger render={<Button variant="secondary" />}>
|
|
59
|
+
Open from the {edges[direction]}
|
|
60
|
+
</Drawer.Trigger>
|
|
61
|
+
<Drawer.Popup closeButton>
|
|
62
|
+
<Drawer.Content>
|
|
63
|
+
<Drawer.Title>Filters</Drawer.Title>
|
|
64
|
+
<Drawer.Description>Swipe {direction} or press Escape to close.</Drawer.Description>
|
|
65
|
+
<div {...styles.actions()}>
|
|
66
|
+
<Drawer.Close render={<Button variant="secondary" />}>Close</Drawer.Close>
|
|
67
|
+
</div>
|
|
68
|
+
</Drawer.Content>
|
|
69
|
+
</Drawer.Popup>
|
|
70
|
+
</Drawer.Root>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
namespace styles {
|
|
77
|
+
export const actions = style({ display: 'flex', justifyContent: 'flex-end', marginTop: '32' })
|
|
78
|
+
|
|
79
|
+
export const row = style({ alignItems: 'center', display: 'flex', flexWrap: 'wrap', gap: '12' })
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Drawer/SwipeDirection.tsx)
|
|
84
|
+
|
|
85
|
+
## Anatomy
|
|
86
|
+
|
|
87
|
+
Import the component and assemble its parts.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
import { Drawer } from '@tempoxyz/ds'
|
|
91
|
+
|
|
92
|
+
<Drawer.Root>
|
|
93
|
+
<Drawer.Trigger />
|
|
94
|
+
<Drawer.Popup>
|
|
95
|
+
<Drawer.Content>
|
|
96
|
+
<Drawer.Title />
|
|
97
|
+
<Drawer.Description />
|
|
98
|
+
<Drawer.Close />
|
|
99
|
+
</Drawer.Content>
|
|
100
|
+
</Drawer.Popup>
|
|
101
|
+
</Drawer.Root>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Guidelines
|
|
105
|
+
|
|
106
|
+
### When to Use
|
|
107
|
+
|
|
108
|
+
* Show a record's details beside its list, such as a payment from Activity.
|
|
109
|
+
* Hold filters for a table or dashboard.
|
|
110
|
+
* Show choices, a short form, or details as a bottom sheet on narrow screens.
|
|
111
|
+
|
|
112
|
+
### When Not to Use
|
|
113
|
+
|
|
114
|
+
* Use a [Dialog](/docs/components/dialog) for a short, focused task, such as Rename wallet.
|
|
115
|
+
* Use an [AlertDialog](/docs/components/alert-dialog) to confirm a destructive action.
|
|
116
|
+
* Use a [Popover](/docs/components/popover) for content anchored to its trigger on wide screens.
|
|
117
|
+
* Use [AppShell](/docs/components/app-shell) for navigation. Below 1024px, its sidebar already opens in a drawer.
|
|
118
|
+
* Use a page for content you'd link to, such as a full transaction record.
|
|
119
|
+
|
|
120
|
+
### Pick an Edge
|
|
121
|
+
|
|
122
|
+
`swipeDirection` on `Drawer.Root` sets the edge the drawer opens from and the swipe that closes it.
|
|
123
|
+
|
|
124
|
+
* **Bottom (down):** The default, for bottom sheets on narrow screens.
|
|
125
|
+
* **Right Edge (right):** Details and filters beside a list on wide screens.
|
|
126
|
+
* **Left Edge (left):** Rarely. Use the right edge across a product.
|
|
127
|
+
* **Top (up):** Avoid. It covers the header and sits far from the thumb.
|
|
128
|
+
|
|
129
|
+
Below 640px, change a `right` drawer to `swipeDirection="down"`, to match a [Dialog](/docs/components/dialog) at that width.
|
|
130
|
+
|
|
131
|
+
### Give Drawers Content That Needs Room
|
|
132
|
+
|
|
133
|
+
Use a drawer for content taller than a dialog, such as a payment's full details. A two-button confirmation leaves the panel empty.
|
|
134
|
+
|
|
135
|
+
**Do:** Open record details in a `right` drawer beside the list they came from.
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
/**
|
|
139
|
+
* Do example for "Give Drawers Content That Needs Room" on the Drawer page.
|
|
140
|
+
* Open record details in a `right` drawer beside the list they came from.
|
|
141
|
+
* @module
|
|
142
|
+
*/
|
|
143
|
+
import { style } from '@tempoxyz/ds'
|
|
144
|
+
|
|
145
|
+
const activity = [
|
|
146
|
+
{ amount: '−250.00', name: 'Alex' },
|
|
147
|
+
{ amount: '+1,200.00', name: 'Payroll' },
|
|
148
|
+
{ amount: '−18.40', name: 'Ramen Bar' },
|
|
149
|
+
{ amount: '−75.00', name: 'Sam' },
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
const details = [
|
|
153
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
154
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
155
|
+
{ label: 'Status', value: 'Confirmed' },
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
159
|
+
export function Demo() {
|
|
160
|
+
return (
|
|
161
|
+
<div {...styles.frame()}>
|
|
162
|
+
<div {...styles.list()}>
|
|
163
|
+
{activity.map((entry) => (
|
|
164
|
+
<div key={entry.name} {...styles.row()}>
|
|
165
|
+
<span>{entry.name}</span>
|
|
166
|
+
<span>{entry.amount}</span>
|
|
167
|
+
</div>
|
|
168
|
+
))}
|
|
169
|
+
</div>
|
|
170
|
+
<div {...styles.scrim()} />
|
|
171
|
+
<div {...styles.panel()}>
|
|
172
|
+
<span {...styles.title()}>Payment to Alex</span>
|
|
173
|
+
{details.map((entry) => (
|
|
174
|
+
<div key={entry.label} {...styles.detail()}>
|
|
175
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
176
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
177
|
+
</div>
|
|
178
|
+
))}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
namespace styles {
|
|
185
|
+
export const detail = style({ display: 'flex', flexDirection: 'column', rowGap: '2px !custom' })
|
|
186
|
+
|
|
187
|
+
export const frame = style({
|
|
188
|
+
backgroundColor: 'background.primary',
|
|
189
|
+
borderColor: 'line.secondary',
|
|
190
|
+
borderRadius: 'xs',
|
|
191
|
+
borderStyle: 'solid',
|
|
192
|
+
borderWidth: 'regular',
|
|
193
|
+
boxSizing: 'border-box',
|
|
194
|
+
height: '12rem !custom',
|
|
195
|
+
overflow: 'hidden',
|
|
196
|
+
position: 'relative',
|
|
197
|
+
width: '18rem !custom',
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
201
|
+
|
|
202
|
+
export const list = style({
|
|
203
|
+
display: 'flex',
|
|
204
|
+
flexDirection: 'column',
|
|
205
|
+
padding: '12',
|
|
206
|
+
rowGap: '12',
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
export const panel = style({
|
|
210
|
+
backgroundColor: 'background.elevated',
|
|
211
|
+
borderBottomLeftRadius: 'sm',
|
|
212
|
+
borderTopLeftRadius: 'sm',
|
|
213
|
+
boxSizing: 'border-box',
|
|
214
|
+
display: 'flex',
|
|
215
|
+
flexDirection: 'column',
|
|
216
|
+
insetBlock: '0',
|
|
217
|
+
insetInlineEnd: '0',
|
|
218
|
+
padding: '16',
|
|
219
|
+
position: 'absolute',
|
|
220
|
+
rowGap: '12',
|
|
221
|
+
width: '10.5rem !custom',
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
export const row = style({
|
|
225
|
+
color: 'content.primary',
|
|
226
|
+
display: 'flex',
|
|
227
|
+
justifyContent: 'space-between',
|
|
228
|
+
typography: 'body.b3',
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
export const scrim = style({
|
|
232
|
+
backgroundColor: 'neutral.100',
|
|
233
|
+
inset: '0',
|
|
234
|
+
opacity: 'disabled',
|
|
235
|
+
position: 'absolute',
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
239
|
+
|
|
240
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Don't:** Put a short confirmation in a drawer. Use an [AlertDialog](/docs/components/alert-dialog).
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
/**
|
|
248
|
+
* Don't example for "Give Drawers Content That Needs Room" on the Drawer page.
|
|
249
|
+
* Don't put a short confirmation in a drawer. Use an AlertDialog.
|
|
250
|
+
* @module
|
|
251
|
+
*/
|
|
252
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
253
|
+
|
|
254
|
+
const keys = ['Production server', 'Staging server', 'Local testing', 'CI pipeline']
|
|
255
|
+
|
|
256
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
257
|
+
export function Demo() {
|
|
258
|
+
return (
|
|
259
|
+
<div {...styles.frame()}>
|
|
260
|
+
<div {...styles.list()}>
|
|
261
|
+
{keys.map((key) => (
|
|
262
|
+
<span key={key} {...styles.row()}>
|
|
263
|
+
{key}
|
|
264
|
+
</span>
|
|
265
|
+
))}
|
|
266
|
+
</div>
|
|
267
|
+
<div {...styles.scrim()} />
|
|
268
|
+
<div {...styles.panel()}>
|
|
269
|
+
<span {...styles.title()}>Revoke this API key?</span>
|
|
270
|
+
<Button size="small">Revoke key</Button>
|
|
271
|
+
<Button size="small" variant="secondary">
|
|
272
|
+
Cancel
|
|
273
|
+
</Button>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
namespace styles {
|
|
280
|
+
export const frame = style({
|
|
281
|
+
backgroundColor: 'background.primary',
|
|
282
|
+
borderColor: 'line.secondary',
|
|
283
|
+
borderRadius: 'xs',
|
|
284
|
+
borderStyle: 'solid',
|
|
285
|
+
borderWidth: 'regular',
|
|
286
|
+
boxSizing: 'border-box',
|
|
287
|
+
height: '12rem !custom',
|
|
288
|
+
overflow: 'hidden',
|
|
289
|
+
position: 'relative',
|
|
290
|
+
width: '18rem !custom',
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
export const list = style({
|
|
294
|
+
display: 'flex',
|
|
295
|
+
flexDirection: 'column',
|
|
296
|
+
padding: '12',
|
|
297
|
+
rowGap: '12',
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
export const panel = style({
|
|
301
|
+
backgroundColor: 'background.elevated',
|
|
302
|
+
borderBottomLeftRadius: 'sm',
|
|
303
|
+
borderTopLeftRadius: 'sm',
|
|
304
|
+
boxSizing: 'border-box',
|
|
305
|
+
display: 'flex',
|
|
306
|
+
flexDirection: 'column',
|
|
307
|
+
insetBlock: '0',
|
|
308
|
+
insetInlineEnd: '0',
|
|
309
|
+
padding: '16',
|
|
310
|
+
position: 'absolute',
|
|
311
|
+
rowGap: '8',
|
|
312
|
+
width: '10.5rem !custom',
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
export const row = style({ color: 'content.primary', typography: 'body.b3' })
|
|
316
|
+
|
|
317
|
+
export const scrim = style({
|
|
318
|
+
backgroundColor: 'neutral.100',
|
|
319
|
+
inset: '0',
|
|
320
|
+
opacity: 'disabled',
|
|
321
|
+
position: 'absolute',
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
export const title = style({
|
|
325
|
+
color: 'content.primary',
|
|
326
|
+
marginBottom: '8',
|
|
327
|
+
typography: 'body.b2Strong',
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Always Render a Close Control
|
|
333
|
+
|
|
334
|
+
Swipes aren't discoverable, and some people can't drag. Set `closeButton` on `Drawer.Popup`, or add a `Drawer.Close` action. A read-only details drawer needs no other action.
|
|
335
|
+
|
|
336
|
+
**Do:** Render a close button, even when a swipe closes the drawer.
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
/**
|
|
340
|
+
* Do example for "Always Render a Close Control" on the Drawer page.
|
|
341
|
+
* Render a close button, even when a swipe closes the drawer.
|
|
342
|
+
* @module
|
|
343
|
+
*/
|
|
344
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
345
|
+
import { Close } from '@tempoxyz/ds/icons'
|
|
346
|
+
|
|
347
|
+
const activity = ['Alex', 'Payroll', 'Ramen Bar', 'Sam']
|
|
348
|
+
|
|
349
|
+
const details = [
|
|
350
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
351
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
355
|
+
export function Demo() {
|
|
356
|
+
return (
|
|
357
|
+
<div {...styles.frame()}>
|
|
358
|
+
<div {...styles.list()}>
|
|
359
|
+
{activity.map((name) => (
|
|
360
|
+
<span key={name} {...styles.row()}>
|
|
361
|
+
{name}
|
|
362
|
+
</span>
|
|
363
|
+
))}
|
|
364
|
+
</div>
|
|
365
|
+
<div {...styles.scrim()} />
|
|
366
|
+
<div {...styles.panel()}>
|
|
367
|
+
<div {...styles.header()}>
|
|
368
|
+
<span {...styles.title()}>Payment to Alex</span>
|
|
369
|
+
<Button aria-label="Close" shape="icon" size="small" variant="tertiary">
|
|
370
|
+
<Close />
|
|
371
|
+
</Button>
|
|
372
|
+
</div>
|
|
373
|
+
{details.map((entry) => (
|
|
374
|
+
<div key={entry.label} {...styles.detail()}>
|
|
375
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
376
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
377
|
+
</div>
|
|
378
|
+
))}
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
namespace styles {
|
|
385
|
+
export const detail = style({ display: 'flex', flexDirection: 'column', rowGap: '2px !custom' })
|
|
386
|
+
|
|
387
|
+
export const frame = style({
|
|
388
|
+
backgroundColor: 'background.primary',
|
|
389
|
+
borderColor: 'line.secondary',
|
|
390
|
+
borderRadius: 'xs',
|
|
391
|
+
borderStyle: 'solid',
|
|
392
|
+
borderWidth: 'regular',
|
|
393
|
+
boxSizing: 'border-box',
|
|
394
|
+
height: '12rem !custom',
|
|
395
|
+
overflow: 'hidden',
|
|
396
|
+
position: 'relative',
|
|
397
|
+
width: '18rem !custom',
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
export const header = style({
|
|
401
|
+
alignItems: 'center',
|
|
402
|
+
columnGap: '4',
|
|
403
|
+
display: 'flex',
|
|
404
|
+
justifyContent: 'space-between',
|
|
405
|
+
minHeight: '32',
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
409
|
+
|
|
410
|
+
export const list = style({
|
|
411
|
+
display: 'flex',
|
|
412
|
+
flexDirection: 'column',
|
|
413
|
+
padding: '12',
|
|
414
|
+
rowGap: '12',
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
export const panel = style({
|
|
418
|
+
backgroundColor: 'background.elevated',
|
|
419
|
+
borderBottomLeftRadius: 'sm',
|
|
420
|
+
borderTopLeftRadius: 'sm',
|
|
421
|
+
boxSizing: 'border-box',
|
|
422
|
+
display: 'flex',
|
|
423
|
+
flexDirection: 'column',
|
|
424
|
+
insetBlock: '0',
|
|
425
|
+
insetInlineEnd: '0',
|
|
426
|
+
padding: '16',
|
|
427
|
+
position: 'absolute',
|
|
428
|
+
rowGap: '12',
|
|
429
|
+
width: '11rem !custom',
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
export const row = style({ color: 'content.primary', typography: 'body.b3' })
|
|
433
|
+
|
|
434
|
+
export const scrim = style({
|
|
435
|
+
backgroundColor: 'neutral.100',
|
|
436
|
+
inset: '0',
|
|
437
|
+
opacity: 'disabled',
|
|
438
|
+
position: 'absolute',
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
442
|
+
|
|
443
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Don't:** Make a swipe or a backdrop press the only way out.
|
|
448
|
+
|
|
449
|
+
```tsx
|
|
450
|
+
/**
|
|
451
|
+
* Don't example for "Always Render a Close Control" on the Drawer page.
|
|
452
|
+
* Don't make a swipe or a backdrop press the only way out.
|
|
453
|
+
* @module
|
|
454
|
+
*/
|
|
455
|
+
import { style } from '@tempoxyz/ds'
|
|
456
|
+
|
|
457
|
+
const activity = ['Alex', 'Payroll', 'Ramen Bar', 'Sam']
|
|
458
|
+
|
|
459
|
+
const details = [
|
|
460
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
461
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
462
|
+
]
|
|
463
|
+
|
|
464
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
465
|
+
export function Demo() {
|
|
466
|
+
return (
|
|
467
|
+
<div {...styles.frame()}>
|
|
468
|
+
<div {...styles.list()}>
|
|
469
|
+
{activity.map((name) => (
|
|
470
|
+
<span key={name} {...styles.row()}>
|
|
471
|
+
{name}
|
|
472
|
+
</span>
|
|
473
|
+
))}
|
|
474
|
+
</div>
|
|
475
|
+
<div {...styles.scrim()} />
|
|
476
|
+
<div {...styles.panel()}>
|
|
477
|
+
<div {...styles.header()}>
|
|
478
|
+
<span {...styles.title()}>Payment to Alex</span>
|
|
479
|
+
</div>
|
|
480
|
+
{details.map((entry) => (
|
|
481
|
+
<div key={entry.label} {...styles.detail()}>
|
|
482
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
483
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
484
|
+
</div>
|
|
485
|
+
))}
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
namespace styles {
|
|
492
|
+
export const detail = style({ display: 'flex', flexDirection: 'column', rowGap: '2px !custom' })
|
|
493
|
+
|
|
494
|
+
export const frame = style({
|
|
495
|
+
backgroundColor: 'background.primary',
|
|
496
|
+
borderColor: 'line.secondary',
|
|
497
|
+
borderRadius: 'xs',
|
|
498
|
+
borderStyle: 'solid',
|
|
499
|
+
borderWidth: 'regular',
|
|
500
|
+
boxSizing: 'border-box',
|
|
501
|
+
height: '12rem !custom',
|
|
502
|
+
overflow: 'hidden',
|
|
503
|
+
position: 'relative',
|
|
504
|
+
width: '18rem !custom',
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
export const header = style({
|
|
508
|
+
alignItems: 'center',
|
|
509
|
+
columnGap: '4',
|
|
510
|
+
display: 'flex',
|
|
511
|
+
justifyContent: 'space-between',
|
|
512
|
+
minHeight: '32',
|
|
513
|
+
})
|
|
514
|
+
|
|
515
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
516
|
+
|
|
517
|
+
export const list = style({
|
|
518
|
+
display: 'flex',
|
|
519
|
+
flexDirection: 'column',
|
|
520
|
+
padding: '12',
|
|
521
|
+
rowGap: '12',
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
export const panel = style({
|
|
525
|
+
backgroundColor: 'background.elevated',
|
|
526
|
+
borderBottomLeftRadius: 'sm',
|
|
527
|
+
borderTopLeftRadius: 'sm',
|
|
528
|
+
boxSizing: 'border-box',
|
|
529
|
+
display: 'flex',
|
|
530
|
+
flexDirection: 'column',
|
|
531
|
+
insetBlock: '0',
|
|
532
|
+
insetInlineEnd: '0',
|
|
533
|
+
padding: '16',
|
|
534
|
+
position: 'absolute',
|
|
535
|
+
rowGap: '12',
|
|
536
|
+
width: '11rem !custom',
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
export const row = style({ color: 'content.primary', typography: 'body.b3' })
|
|
540
|
+
|
|
541
|
+
export const scrim = style({
|
|
542
|
+
backgroundColor: 'neutral.100',
|
|
543
|
+
inset: '0',
|
|
544
|
+
opacity: 'disabled',
|
|
545
|
+
position: 'absolute',
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
549
|
+
|
|
550
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Center the Body of a Bottom Sheet
|
|
555
|
+
|
|
556
|
+
A `down` drawer spans the viewport width. Wrap its body in `Drawer.Content` to center it at up to 32rem. Mouse and pen drags inside it select text and don't move the drawer.
|
|
557
|
+
|
|
558
|
+
**Do:** Center the body of a wide bottom sheet with `Drawer.Content`.
|
|
559
|
+
|
|
560
|
+
```tsx
|
|
561
|
+
/**
|
|
562
|
+
* Do example for "Center the Body of a Bottom Sheet" on the Drawer page.
|
|
563
|
+
* Center the body of a wide bottom sheet with `Drawer.Content`.
|
|
564
|
+
* @module
|
|
565
|
+
*/
|
|
566
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
567
|
+
|
|
568
|
+
const details = [
|
|
569
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
570
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
574
|
+
export function Demo() {
|
|
575
|
+
return (
|
|
576
|
+
<div {...styles.frame()}>
|
|
577
|
+
<div {...styles.scrim()} />
|
|
578
|
+
<div {...styles.sheet()}>
|
|
579
|
+
<div {...styles.content()}>
|
|
580
|
+
<span {...styles.title()}>Payment to Alex</span>
|
|
581
|
+
{details.map((entry) => (
|
|
582
|
+
<div key={entry.label} {...styles.detail()}>
|
|
583
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
584
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
585
|
+
</div>
|
|
586
|
+
))}
|
|
587
|
+
<Button size="small" variant="secondary">
|
|
588
|
+
Share receipt
|
|
589
|
+
</Button>
|
|
590
|
+
</div>
|
|
591
|
+
</div>
|
|
592
|
+
</div>
|
|
593
|
+
)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
namespace styles {
|
|
597
|
+
// Drawer.Content centers the body at up to 32rem, drawn here at the preview's scale.
|
|
598
|
+
export const content = style({
|
|
599
|
+
display: 'flex',
|
|
600
|
+
flexDirection: 'column',
|
|
601
|
+
marginInline: 'auto !custom',
|
|
602
|
+
maxWidth: '11rem !custom',
|
|
603
|
+
rowGap: '8',
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
export const detail = style({
|
|
607
|
+
display: 'flex',
|
|
608
|
+
justifyContent: 'space-between',
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
export const frame = style({
|
|
612
|
+
backgroundColor: 'background.primary',
|
|
613
|
+
borderColor: 'line.secondary',
|
|
614
|
+
borderRadius: 'xs',
|
|
615
|
+
borderStyle: 'solid',
|
|
616
|
+
borderWidth: 'regular',
|
|
617
|
+
boxSizing: 'border-box',
|
|
618
|
+
height: '12rem !custom',
|
|
619
|
+
overflow: 'hidden',
|
|
620
|
+
position: 'relative',
|
|
621
|
+
width: '20rem !custom',
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
625
|
+
|
|
626
|
+
export const scrim = style({
|
|
627
|
+
backgroundColor: 'neutral.100',
|
|
628
|
+
inset: '0',
|
|
629
|
+
opacity: 'disabled',
|
|
630
|
+
position: 'absolute',
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
export const sheet = style({
|
|
634
|
+
backgroundColor: 'background.elevated',
|
|
635
|
+
borderTopLeftRadius: 'sm',
|
|
636
|
+
borderTopRightRadius: 'sm',
|
|
637
|
+
bottom: '0',
|
|
638
|
+
boxSizing: 'border-box',
|
|
639
|
+
insetInline: '0',
|
|
640
|
+
padding: '16',
|
|
641
|
+
position: 'absolute',
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
645
|
+
|
|
646
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
**Don't:** Stretch the body across the full width of the viewport.
|
|
651
|
+
|
|
652
|
+
```tsx
|
|
653
|
+
/**
|
|
654
|
+
* Don't example for "Center the Body of a Bottom Sheet" on the Drawer page.
|
|
655
|
+
* Don't stretch the body across the full width of the viewport.
|
|
656
|
+
* @module
|
|
657
|
+
*/
|
|
658
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
659
|
+
|
|
660
|
+
const details = [
|
|
661
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
662
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
663
|
+
]
|
|
664
|
+
|
|
665
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
666
|
+
export function Demo() {
|
|
667
|
+
return (
|
|
668
|
+
<div {...styles.frame()}>
|
|
669
|
+
<div {...styles.scrim()} />
|
|
670
|
+
<div {...styles.sheet()}>
|
|
671
|
+
<div {...styles.content()}>
|
|
672
|
+
<span {...styles.title()}>Payment to Alex</span>
|
|
673
|
+
{details.map((entry) => (
|
|
674
|
+
<div key={entry.label} {...styles.detail()}>
|
|
675
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
676
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
677
|
+
</div>
|
|
678
|
+
))}
|
|
679
|
+
<Button size="small" variant="secondary">
|
|
680
|
+
Share receipt
|
|
681
|
+
</Button>
|
|
682
|
+
</div>
|
|
683
|
+
</div>
|
|
684
|
+
</div>
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
namespace styles {
|
|
689
|
+
export const content = style({ display: 'flex', flexDirection: 'column', rowGap: '8' })
|
|
690
|
+
|
|
691
|
+
export const detail = style({
|
|
692
|
+
display: 'flex',
|
|
693
|
+
justifyContent: 'space-between',
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
export const frame = style({
|
|
697
|
+
backgroundColor: 'background.primary',
|
|
698
|
+
borderColor: 'line.secondary',
|
|
699
|
+
borderRadius: 'xs',
|
|
700
|
+
borderStyle: 'solid',
|
|
701
|
+
borderWidth: 'regular',
|
|
702
|
+
boxSizing: 'border-box',
|
|
703
|
+
height: '12rem !custom',
|
|
704
|
+
overflow: 'hidden',
|
|
705
|
+
position: 'relative',
|
|
706
|
+
width: '20rem !custom',
|
|
707
|
+
})
|
|
708
|
+
|
|
709
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
710
|
+
|
|
711
|
+
export const scrim = style({
|
|
712
|
+
backgroundColor: 'neutral.100',
|
|
713
|
+
inset: '0',
|
|
714
|
+
opacity: 'disabled',
|
|
715
|
+
position: 'absolute',
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
export const sheet = style({
|
|
719
|
+
backgroundColor: 'background.elevated',
|
|
720
|
+
borderTopLeftRadius: 'sm',
|
|
721
|
+
borderTopRightRadius: 'sm',
|
|
722
|
+
bottom: '0',
|
|
723
|
+
boxSizing: 'border-box',
|
|
724
|
+
insetInline: '0',
|
|
725
|
+
padding: '16',
|
|
726
|
+
position: 'absolute',
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
730
|
+
|
|
731
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
732
|
+
}
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
### Keep the Main Action in Reach
|
|
736
|
+
|
|
737
|
+
The popup scrolls on overflow. Put the main action at the end, and keep a bottom sheet short enough to show it without scrolling.
|
|
738
|
+
|
|
739
|
+
**Do:** Keep the bottom sheet short enough to show its main action.
|
|
740
|
+
|
|
741
|
+
```tsx
|
|
742
|
+
/**
|
|
743
|
+
* Do example for "Keep the Main Action in Reach" on the Drawer page.
|
|
744
|
+
* Keep the bottom sheet short enough to show its main action.
|
|
745
|
+
* @module
|
|
746
|
+
*/
|
|
747
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
748
|
+
|
|
749
|
+
const details = [
|
|
750
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
751
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
752
|
+
{ label: 'Fee', value: '0.01 USDC' },
|
|
753
|
+
]
|
|
754
|
+
|
|
755
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
756
|
+
export function Demo() {
|
|
757
|
+
return (
|
|
758
|
+
<div {...styles.frame()}>
|
|
759
|
+
<div {...styles.scrim()} />
|
|
760
|
+
<div {...styles.sheet()}>
|
|
761
|
+
<span {...styles.title()}>Review payment</span>
|
|
762
|
+
{details.map((entry) => (
|
|
763
|
+
<div key={entry.label} {...styles.detail()}>
|
|
764
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
765
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
766
|
+
</div>
|
|
767
|
+
))}
|
|
768
|
+
<Button size="small">Send payment</Button>
|
|
769
|
+
</div>
|
|
770
|
+
</div>
|
|
771
|
+
)
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
namespace styles {
|
|
775
|
+
export const detail = style({ display: 'flex', justifyContent: 'space-between' })
|
|
776
|
+
|
|
777
|
+
export const frame = style({
|
|
778
|
+
backgroundColor: 'background.primary',
|
|
779
|
+
borderColor: 'line.secondary',
|
|
780
|
+
borderRadius: 'xs',
|
|
781
|
+
borderStyle: 'solid',
|
|
782
|
+
borderWidth: 'regular',
|
|
783
|
+
boxSizing: 'border-box',
|
|
784
|
+
height: '14rem !custom',
|
|
785
|
+
overflow: 'hidden',
|
|
786
|
+
position: 'relative',
|
|
787
|
+
width: '12rem !custom',
|
|
788
|
+
})
|
|
789
|
+
|
|
790
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
791
|
+
|
|
792
|
+
export const scrim = style({
|
|
793
|
+
backgroundColor: 'neutral.100',
|
|
794
|
+
inset: '0',
|
|
795
|
+
opacity: 'disabled',
|
|
796
|
+
position: 'absolute',
|
|
797
|
+
})
|
|
798
|
+
|
|
799
|
+
export const sheet = style({
|
|
800
|
+
backgroundColor: 'background.elevated',
|
|
801
|
+
borderTopLeftRadius: 'sm',
|
|
802
|
+
borderTopRightRadius: 'sm',
|
|
803
|
+
bottom: '0',
|
|
804
|
+
boxSizing: 'border-box',
|
|
805
|
+
display: 'flex',
|
|
806
|
+
flexDirection: 'column',
|
|
807
|
+
insetInline: '0',
|
|
808
|
+
padding: '16',
|
|
809
|
+
position: 'absolute',
|
|
810
|
+
rowGap: '8',
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
814
|
+
|
|
815
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
816
|
+
}
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**Don't:** Push the main action below the fold of a tall sheet.
|
|
820
|
+
|
|
821
|
+
```tsx
|
|
822
|
+
/**
|
|
823
|
+
* Don't example for "Keep the Main Action in Reach" on the Drawer page.
|
|
824
|
+
* Don't push the main action below the fold of a tall sheet.
|
|
825
|
+
* @module
|
|
826
|
+
*/
|
|
827
|
+
import { Button, style } from '@tempoxyz/ds'
|
|
828
|
+
|
|
829
|
+
const details = [
|
|
830
|
+
{ label: 'Amount', value: '250.00 USDC' },
|
|
831
|
+
{ label: 'To', value: '0x3c44…93bc' },
|
|
832
|
+
{ label: 'Fee', value: '0.01 USDC' },
|
|
833
|
+
{ label: 'From', value: 'Main wallet' },
|
|
834
|
+
{ label: 'Network', value: 'Tempo' },
|
|
835
|
+
{ label: 'Memo', value: 'Invoice 1042' },
|
|
836
|
+
{ label: 'Arrives', value: 'Instantly' },
|
|
837
|
+
]
|
|
838
|
+
|
|
839
|
+
/** Renders the static preview, which the guideline card shows inert. */
|
|
840
|
+
export function Demo() {
|
|
841
|
+
return (
|
|
842
|
+
<div {...styles.frame()}>
|
|
843
|
+
<div {...styles.scrim()} />
|
|
844
|
+
<div {...styles.sheet()}>
|
|
845
|
+
<span {...styles.title()}>Review payment</span>
|
|
846
|
+
{details.map((entry) => (
|
|
847
|
+
<div key={entry.label} {...styles.detail()}>
|
|
848
|
+
<span {...styles.label()}>{entry.label}</span>
|
|
849
|
+
<span {...styles.value()}>{entry.value}</span>
|
|
850
|
+
</div>
|
|
851
|
+
))}
|
|
852
|
+
<Button size="small">Send payment</Button>
|
|
853
|
+
</div>
|
|
854
|
+
</div>
|
|
855
|
+
)
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
namespace styles {
|
|
859
|
+
export const detail = style({ display: 'flex', justifyContent: 'space-between' })
|
|
860
|
+
|
|
861
|
+
export const frame = style({
|
|
862
|
+
backgroundColor: 'background.primary',
|
|
863
|
+
borderColor: 'line.secondary',
|
|
864
|
+
borderRadius: 'xs',
|
|
865
|
+
borderStyle: 'solid',
|
|
866
|
+
borderWidth: 'regular',
|
|
867
|
+
boxSizing: 'border-box',
|
|
868
|
+
height: '14rem !custom',
|
|
869
|
+
overflow: 'hidden',
|
|
870
|
+
position: 'relative',
|
|
871
|
+
width: '12rem !custom',
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
export const label = style({ color: 'content.secondary', typography: 'body.b3' })
|
|
875
|
+
|
|
876
|
+
export const scrim = style({
|
|
877
|
+
backgroundColor: 'neutral.100',
|
|
878
|
+
inset: '0',
|
|
879
|
+
opacity: 'disabled',
|
|
880
|
+
position: 'absolute',
|
|
881
|
+
})
|
|
882
|
+
|
|
883
|
+
export const sheet = style({
|
|
884
|
+
backgroundColor: 'background.elevated',
|
|
885
|
+
borderTopLeftRadius: 'sm',
|
|
886
|
+
borderTopRightRadius: 'sm',
|
|
887
|
+
boxSizing: 'border-box',
|
|
888
|
+
display: 'flex',
|
|
889
|
+
flexDirection: 'column',
|
|
890
|
+
insetInline: '0',
|
|
891
|
+
padding: '16',
|
|
892
|
+
position: 'absolute',
|
|
893
|
+
rowGap: '8',
|
|
894
|
+
top: '24',
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
export const title = style({ color: 'content.primary', typography: 'body.b2Strong' })
|
|
898
|
+
|
|
899
|
+
export const value = style({ color: 'content.primary', typography: 'body.b3Strong' })
|
|
900
|
+
}
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
## Content
|
|
904
|
+
|
|
905
|
+
### Name the Record or Task
|
|
906
|
+
|
|
907
|
+
Render a `Drawer.Title` that names the record or task, such as Payment to Alex or Filters.
|
|
908
|
+
|
|
909
|
+
## Accessibility
|
|
910
|
+
|
|
911
|
+
A drawer is a modal dialog. It follows the [WAI-ARIA Authoring Practices modal dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
|
|
912
|
+
|
|
913
|
+
### Semantics
|
|
914
|
+
|
|
915
|
+
* **Drawer.Trigger (button):** Announces the dialog and whether it is open.
|
|
916
|
+
* **Drawer.Popup (dialog):** Modal by default.
|
|
917
|
+
* **Drawer.Title (h2):** Names the drawer.
|
|
918
|
+
* **Drawer.Description (p):** Describes the drawer.
|
|
919
|
+
* **Close Button (closeButton):** Adds an icon button named "Close".
|
|
920
|
+
* **Page Behind (inert):** Ignores input and never scrolls with the drawer.
|
|
921
|
+
|
|
922
|
+
### Keyboard
|
|
923
|
+
|
|
924
|
+
* **Open or Activate (Enter or Space):** Opens the drawer, or activates the focused control.
|
|
925
|
+
* **Move to the Next Element (Tab):** Moves to the next element, and wraps.
|
|
926
|
+
* **Move to the Previous Element (Shift+Tab):** Moves to the previous element, and wraps.
|
|
927
|
+
* **Close the Drawer (Escape):** Closes the drawer.
|
|
928
|
+
|
|
929
|
+
### Focus
|
|
930
|
+
|
|
931
|
+
* **Opening (initialFocus):** Moves to the panel, so a screen reader starts at its title.
|
|
932
|
+
* **Trapping:** Focus stays inside while open.
|
|
933
|
+
* **Backdrop and Swipe:** An outside press or a swipe to its edge closes the drawer.
|
|
934
|
+
* **Closing (finalFocus):** Returns to the trigger, or to `finalFocus`.
|
|
935
|
+
|
|
936
|
+
### Motion
|
|
937
|
+
|
|
938
|
+
* **Slide and Swipe (swipeDirection):** Slides in from its edge and follows a swipe.
|
|
939
|
+
* **Reduced Motion (reduce):** Fades in and out instead of sliding.
|
|
940
|
+
|
|
941
|
+
### Your Responsibilities
|
|
942
|
+
|
|
943
|
+
* **Render a Title (Drawer.Title):** Without it, the drawer has no accessible name.
|
|
944
|
+
* **Name an Alternative to Gestures:** If instructions name a gesture, name a button too.
|
|
945
|
+
|
|
946
|
+
## Related
|
|
947
|
+
|
|
948
|
+
[Dialog](/docs/components/dialog)
|
|
949
|
+
|
|
950
|
+
Use for short, focused tasks.
|
|
951
|
+
|
|
952
|
+
[Popover](/docs/components/popover)
|
|
953
|
+
|
|
954
|
+
Use for anchored content on wide screens.
|
|
955
|
+
|
|
956
|
+
[AppShell](/docs/components/app-shell)
|
|
957
|
+
|
|
958
|
+
Use for navigation that collapses into a drawer.
|
|
959
|
+
|
|
960
|
+
[Responsive Layout](/docs/patterns/responsive-layout)
|
|
961
|
+
|
|
962
|
+
Covers switching layouts at narrow widths.
|
|
963
|
+
|
|
964
|
+
[Tables and Lists](/docs/patterns/tables-and-lists)
|
|
965
|
+
|
|
966
|
+
Covers opening record details.
|