@xsolla/xui-b2b-drawer 0.150.0 → 0.152.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -73
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,32 +1,42 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Drawer
|
|
2
2
|
|
|
3
|
-
A slide-in panel that opens from the right edge of the screen
|
|
3
|
+
A slide-in panel that opens from the right edge of the screen for B2B settings, multi-step flows, and auxiliary content. Supports an optional stepper sidebar for guided wizards.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @xsolla/xui-b2b-drawer
|
|
9
|
-
# or
|
|
10
|
-
yarn add @xsolla/xui-b2b-drawer
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## Imports
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
```tsx
|
|
14
|
+
import {
|
|
15
|
+
Drawer,
|
|
16
|
+
useDrawer,
|
|
17
|
+
type DrawerProps,
|
|
18
|
+
type DrawerSize,
|
|
19
|
+
type DrawerFooterAlign,
|
|
20
|
+
type UseDrawerOptions,
|
|
21
|
+
type UseDrawerReturn,
|
|
22
|
+
} from '@xsolla/xui-b2b-drawer';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
16
26
|
|
|
17
27
|
```tsx
|
|
18
|
-
import
|
|
28
|
+
import { useState } from 'react';
|
|
19
29
|
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
20
30
|
import { Button } from '@xsolla/xui-button';
|
|
21
31
|
import { Typography } from '@xsolla/xui-typography';
|
|
22
32
|
|
|
23
|
-
|
|
33
|
+
function BasicDrawer() {
|
|
24
34
|
const [open, setOpen] = useState(false);
|
|
25
35
|
|
|
26
36
|
return (
|
|
27
37
|
<>
|
|
28
38
|
<Button variant="primary" tone="brand" size="sm" onPress={() => setOpen(true)}>
|
|
29
|
-
Open
|
|
39
|
+
Open drawer
|
|
30
40
|
</Button>
|
|
31
41
|
<Drawer
|
|
32
42
|
open={open}
|
|
@@ -50,37 +60,106 @@ export default function BasicDrawer() {
|
|
|
50
60
|
}
|
|
51
61
|
```
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
## API Reference
|
|
64
|
+
|
|
65
|
+
### `<Drawer>`
|
|
66
|
+
|
|
67
|
+
| Prop | Type | Default | Description |
|
|
68
|
+
| --- | --- | --- | --- |
|
|
69
|
+
| `open` | `boolean` | `false` | Whether the drawer is visible. |
|
|
70
|
+
| `onClose` | `() => void` | — | Called when the drawer should close (Escape, overlay click, close button). |
|
|
71
|
+
| `size` | `DrawerSize` | `"md"` | Width preset: `sm` 480px, `md` 620px, `lg` 1056px. |
|
|
72
|
+
| `title` | `ReactNode` | — | Title displayed in the header. |
|
|
73
|
+
| `onBack` | `() => void` | — | When provided, renders a back arrow button in the header. |
|
|
74
|
+
| `headerAction` | `ReactNode` | — | Element rendered between the title and the close button. |
|
|
75
|
+
| `closeOnOverlayClick` | `boolean` | `true` | Whether clicking the backdrop closes the drawer. |
|
|
76
|
+
| `closeOnEscape` | `boolean` | `true` | Whether pressing Escape closes the drawer. |
|
|
77
|
+
| `footer` | `ReactNode` | — | Footer content (typically action buttons). |
|
|
78
|
+
| `footerAlign` | `DrawerFooterAlign` | `"right"` | Alignment of footer content. |
|
|
79
|
+
| `footerShadow` | `boolean` | `false` | Show a drop shadow above the footer (useful with scrollable content). |
|
|
80
|
+
| `footerFullWidth` | `boolean` | `true` | Whether footer buttons stretch to full width. |
|
|
81
|
+
| `stepper` | `ReactNode` | — | Stepper sidebar rendered to the left of the content panel. |
|
|
82
|
+
| `children` | `ReactNode` | — | **Required.** Main scrollable body content. |
|
|
83
|
+
| `initialFocusRef` | `RefObject<HTMLElement>` | — | Ref to the element that should receive focus when the drawer opens. |
|
|
84
|
+
|
|
85
|
+
Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).
|
|
86
|
+
|
|
87
|
+
#### Deprecated props
|
|
88
|
+
|
|
89
|
+
| Prop | Replacement |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `isOpen` | `open` |
|
|
92
|
+
| `header` (`{ title, onBack }`) | `title` + `onBack` |
|
|
93
|
+
| `bottom` | `footer` |
|
|
94
|
+
|
|
95
|
+
### `useDrawer(options?)`
|
|
96
|
+
|
|
97
|
+
Convenience hook that owns local open/close state for a drawer.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
function useDrawer(options?: UseDrawerOptions): UseDrawerReturn;
|
|
101
|
+
|
|
102
|
+
interface UseDrawerOptions {
|
|
103
|
+
onOpen?: () => void;
|
|
104
|
+
onClose?: () => void;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface UseDrawerReturn {
|
|
108
|
+
isOpen: boolean;
|
|
109
|
+
open: () => void;
|
|
110
|
+
close: () => void;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`onOpen` / `onClose` fire after the internal state flips — use them to coordinate analytics or external state.
|
|
115
|
+
|
|
116
|
+
### Type exports
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
type DrawerSize = 'sm' | 'md' | 'lg';
|
|
120
|
+
type DrawerFooterAlign = 'center' | 'right';
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Examples
|
|
124
|
+
|
|
125
|
+
### Size variants
|
|
54
126
|
|
|
55
127
|
```tsx
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
128
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
129
|
+
|
|
130
|
+
<>
|
|
131
|
+
{/* sm = 480px, md = 620px (default), lg = 1056px */}
|
|
132
|
+
<Drawer open={open} onClose={onClose} title="Small" size="sm">{...}</Drawer>
|
|
133
|
+
<Drawer open={open} onClose={onClose} title="Medium" size="md">{...}</Drawer>
|
|
134
|
+
<Drawer open={open} onClose={onClose} title="Large" size="lg">{...}</Drawer>
|
|
135
|
+
</>;
|
|
60
136
|
```
|
|
61
137
|
|
|
62
|
-
### With
|
|
138
|
+
### With back button
|
|
63
139
|
|
|
64
140
|
```tsx
|
|
141
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
142
|
+
|
|
65
143
|
<Drawer
|
|
66
144
|
open={open}
|
|
67
145
|
onClose={onClose}
|
|
68
|
-
title="Step
|
|
146
|
+
title="Step details"
|
|
69
147
|
onBack={() => goToPreviousStep()}
|
|
70
148
|
>
|
|
71
149
|
{/* back arrow appears in the header */}
|
|
72
|
-
</Drawer
|
|
150
|
+
</Drawer>;
|
|
73
151
|
```
|
|
74
152
|
|
|
75
|
-
### With
|
|
153
|
+
### With header action
|
|
76
154
|
|
|
77
155
|
```tsx
|
|
156
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
78
157
|
import { Button } from '@xsolla/xui-button';
|
|
79
158
|
|
|
80
159
|
<Drawer
|
|
81
160
|
open={open}
|
|
82
161
|
onClose={onClose}
|
|
83
|
-
title="User
|
|
162
|
+
title="User profile"
|
|
84
163
|
headerAction={
|
|
85
164
|
<Button variant="ghost" tone="brand" size="sm">
|
|
86
165
|
View history
|
|
@@ -88,15 +167,35 @@ import { Button } from '@xsolla/xui-button';
|
|
|
88
167
|
}
|
|
89
168
|
>
|
|
90
169
|
{/* action renders between the title and the close button */}
|
|
91
|
-
</Drawer
|
|
170
|
+
</Drawer>;
|
|
92
171
|
```
|
|
93
172
|
|
|
94
|
-
###
|
|
173
|
+
### `useDrawer` hook
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { Drawer, useDrawer } from '@xsolla/xui-b2b-drawer';
|
|
177
|
+
import { Button } from '@xsolla/xui-button';
|
|
178
|
+
import { Typography } from '@xsolla/xui-typography';
|
|
179
|
+
|
|
180
|
+
function HookExample() {
|
|
181
|
+
const drawer = useDrawer({ onOpen: () => console.log('opened') });
|
|
182
|
+
return (
|
|
183
|
+
<>
|
|
184
|
+
<Button variant="primary" tone="brand" size="sm" onPress={drawer.open}>
|
|
185
|
+
Open
|
|
186
|
+
</Button>
|
|
187
|
+
<Drawer open={drawer.isOpen} onClose={drawer.close} title="Hello">
|
|
188
|
+
<Typography variant="bodyMd">Driven by useDrawer.</Typography>
|
|
189
|
+
</Drawer>
|
|
190
|
+
</>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
95
194
|
|
|
96
|
-
|
|
195
|
+
### With stepper sidebar
|
|
97
196
|
|
|
98
197
|
```tsx
|
|
99
|
-
import
|
|
198
|
+
import { useState } from 'react';
|
|
100
199
|
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
101
200
|
import { Stepper } from '@xsolla/xui-b2b-stepper';
|
|
102
201
|
import { Button } from '@xsolla/xui-button';
|
|
@@ -108,13 +207,15 @@ const STEPS = [
|
|
|
108
207
|
{ title: 'Confirm', description: 'Review and submit' },
|
|
109
208
|
];
|
|
110
209
|
|
|
111
|
-
|
|
210
|
+
function StepperDrawer() {
|
|
112
211
|
const [open, setOpen] = useState(false);
|
|
113
212
|
const [active, setActive] = useState(0);
|
|
114
213
|
|
|
115
214
|
const steps = STEPS.map((s, i) => ({
|
|
116
215
|
...s,
|
|
117
|
-
state:
|
|
216
|
+
state:
|
|
217
|
+
i < active ? ('complete' as const) :
|
|
218
|
+
i === active ? ('current' as const) : ('incomplete' as const),
|
|
118
219
|
}));
|
|
119
220
|
|
|
120
221
|
const isLast = active === steps.length - 1;
|
|
@@ -122,13 +223,13 @@ export default function StepperDrawer() {
|
|
|
122
223
|
return (
|
|
123
224
|
<>
|
|
124
225
|
<Button variant="primary" tone="brand" size="sm" onPress={() => { setActive(0); setOpen(true); }}>
|
|
125
|
-
Open
|
|
226
|
+
Open wizard
|
|
126
227
|
</Button>
|
|
127
228
|
<Drawer
|
|
128
229
|
open={open}
|
|
129
230
|
onClose={() => setOpen(false)}
|
|
130
231
|
title={STEPS[active].title}
|
|
131
|
-
onBack={active > 0 ? () => setActive(a => a - 1) : undefined}
|
|
232
|
+
onBack={active > 0 ? () => setActive((a) => a - 1) : undefined}
|
|
132
233
|
stepper={
|
|
133
234
|
<Stepper
|
|
134
235
|
steps={steps}
|
|
@@ -146,7 +247,7 @@ export default function StepperDrawer() {
|
|
|
146
247
|
variant="primary"
|
|
147
248
|
tone="brand"
|
|
148
249
|
size="sm"
|
|
149
|
-
onPress={() => isLast ? setOpen(false) : setActive(a => a + 1)}
|
|
250
|
+
onPress={() => (isLast ? setOpen(false) : setActive((a) => a + 1))}
|
|
150
251
|
>
|
|
151
252
|
{isLast ? 'Submit' : 'Next'}
|
|
152
253
|
</Button>
|
|
@@ -160,13 +261,17 @@ export default function StepperDrawer() {
|
|
|
160
261
|
}
|
|
161
262
|
```
|
|
162
263
|
|
|
163
|
-
### Scrollable
|
|
264
|
+
### Scrollable content with footer shadow
|
|
164
265
|
|
|
165
266
|
```tsx
|
|
267
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
268
|
+
import { Button } from '@xsolla/xui-button';
|
|
269
|
+
import { Typography } from '@xsolla/xui-typography';
|
|
270
|
+
|
|
166
271
|
<Drawer
|
|
167
272
|
open={open}
|
|
168
273
|
onClose={onClose}
|
|
169
|
-
title="Long
|
|
274
|
+
title="Long content"
|
|
170
275
|
footerShadow
|
|
171
276
|
footer={<Button variant="primary" tone="brand" size="sm">Done</Button>}
|
|
172
277
|
>
|
|
@@ -175,53 +280,20 @@ export default function StepperDrawer() {
|
|
|
175
280
|
<Typography key={i} variant="bodyMd">Item {i + 1}</Typography>
|
|
176
281
|
))}
|
|
177
282
|
</div>
|
|
178
|
-
</Drawer
|
|
283
|
+
</Drawer>;
|
|
179
284
|
```
|
|
180
285
|
|
|
181
|
-
##
|
|
182
|
-
|
|
183
|
-
### Drawer
|
|
184
|
-
|
|
185
|
-
| Prop | Type | Default | Description |
|
|
186
|
-
| :--- | :--- | :------ | :---------- |
|
|
187
|
-
| `open` | `boolean` | `false` | Whether the drawer is visible. |
|
|
188
|
-
| `onClose` | `() => void` | - | Called when the drawer should close (Escape key, overlay click, close button). |
|
|
189
|
-
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Width preset: sm=480px, md=620px, lg=1056px. |
|
|
190
|
-
| `title` | `ReactNode` | - | Title displayed in the header. |
|
|
191
|
-
| `onBack` | `() => void` | - | When provided, renders a back arrow button in the header. |
|
|
192
|
-
| `headerAction` | `ReactNode` | - | Optional element rendered between the title and the close button. |
|
|
193
|
-
| `closeOnOverlayClick` | `boolean` | `true` | Whether clicking the backdrop closes the drawer. |
|
|
194
|
-
| `closeOnEscape` | `boolean` | `true` | Whether pressing Escape closes the drawer. |
|
|
195
|
-
| `footer` | `ReactNode` | - | Footer content (typically action buttons). |
|
|
196
|
-
| `footerAlign` | `"center" \| "right"` | `"right"` | Alignment of footer content. |
|
|
197
|
-
| `footerShadow` | `boolean` | `false` | Show a drop shadow above the footer (useful with scrollable content). |
|
|
198
|
-
| `footerFullWidth` | `boolean` | `true` | Whether footer buttons stretch to full width. |
|
|
199
|
-
| `stepper` | `ReactNode` | - | Optional stepper sidebar rendered to the left of the content panel. |
|
|
200
|
-
| `children` | `ReactNode` | - | Main scrollable body content. |
|
|
201
|
-
| `initialFocusRef` | `RefObject<HTMLElement>` | - | Ref to the element that should receive focus when the drawer opens. |
|
|
202
|
-
| `themeMode` | `string` | - | Override the global theme mode for this component. |
|
|
203
|
-
| `themeProductContext` | `string` | - | Override the global product context for this component. |
|
|
204
|
-
|
|
205
|
-
**Deprecated props** (still accepted for backwards compatibility):
|
|
286
|
+
## Accessibility
|
|
206
287
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
288
|
+
- Drawer panel is `role="dialog"` with `aria-modal="true"`.
|
|
289
|
+
- `aria-label` is set to the `title` when it is a string. If you pass a non-string `title` and need a label, supply your own labelling via the surrounding markup or wrap the title text in a string node.
|
|
290
|
+
- Focus is moved to `initialFocusRef` (or the close button, or the first focusable element) on open, and restored to the previously active element after the exit animation completes.
|
|
291
|
+
- Tab is trapped inside the drawer; Shift+Tab wraps to the last focusable element, Tab wraps back to the first.
|
|
292
|
+
- Escape closes the drawer unless `closeOnEscape={false}`.
|
|
293
|
+
- Close and back buttons in the header have accessible labels.
|
|
212
294
|
|
|
213
295
|
## Behaviour
|
|
214
296
|
|
|
215
297
|
- Slides in from the right with an animated transition.
|
|
216
|
-
- Renders in a portal (z-index
|
|
217
|
-
-
|
|
218
|
-
- Body scroll is locked while the drawer is open on web.
|
|
219
|
-
- When `stepper` is provided, the panel widens to accommodate the sidebar to the left of the content area.
|
|
220
|
-
|
|
221
|
-
## Accessibility
|
|
222
|
-
|
|
223
|
-
- `role="dialog"` and `aria-modal="true"` on the drawer panel.
|
|
224
|
-
- Header title is linked via `aria-labelledby`.
|
|
225
|
-
- Focus trap keeps Tab and Shift+Tab within the drawer.
|
|
226
|
-
- Escape key closes the drawer (unless `closeOnEscape={false}`).
|
|
227
|
-
- Close and back buttons have accessible labels.
|
|
298
|
+
- Renders in a portal layer (z-index from `theme.sizing.drawer().zIndex`) so it appears above page content.
|
|
299
|
+
- When `stepper` is provided, the panel widens to render the sidebar to the left of the content area inside the same surface.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xsolla/xui-b2b-drawer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.152.0",
|
|
4
4
|
"main": "./web/index.js",
|
|
5
5
|
"module": "./web/index.mjs",
|
|
6
6
|
"types": "./web/index.d.ts",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"test:coverage": "vitest run --coverage"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@xsolla/xui-b2b-stepper": "0.
|
|
17
|
-
"@xsolla/xui-button": "0.
|
|
18
|
-
"@xsolla/xui-core": "0.
|
|
19
|
-
"@xsolla/xui-icons-base": "0.
|
|
20
|
-
"@xsolla/xui-primitives-core": "0.
|
|
21
|
-
"@xsolla/xui-typography": "0.
|
|
16
|
+
"@xsolla/xui-b2b-stepper": "0.152.0",
|
|
17
|
+
"@xsolla/xui-button": "0.152.0",
|
|
18
|
+
"@xsolla/xui-core": "0.152.0",
|
|
19
|
+
"@xsolla/xui-icons-base": "0.152.0",
|
|
20
|
+
"@xsolla/xui-primitives-core": "0.152.0",
|
|
21
|
+
"@xsolla/xui-typography": "0.152.0"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": ">=16.8.0",
|