@xsolla/xui-b2b-drawer 0.148.0 → 0.148.1
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 +227 -0
- package/package.json +7 -7
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# B2B Drawer
|
|
2
|
+
|
|
3
|
+
A slide-in panel that opens from the right edge of the screen, designed for B2B settings panels, multi-step flows, and auxiliary content that should not disrupt the main interface. Supports an optional stepper sidebar for multi-step workflows.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @xsolla/xui-b2b-drawer
|
|
9
|
+
# or
|
|
10
|
+
yarn add @xsolla/xui-b2b-drawer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Demo
|
|
14
|
+
|
|
15
|
+
### Basic Drawer
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import React, { useState } from 'react';
|
|
19
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
20
|
+
import { Button } from '@xsolla/xui-button';
|
|
21
|
+
import { Typography } from '@xsolla/xui-typography';
|
|
22
|
+
|
|
23
|
+
export default function BasicDrawer() {
|
|
24
|
+
const [open, setOpen] = useState(false);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
<Button variant="primary" tone="brand" size="sm" onPress={() => setOpen(true)}>
|
|
29
|
+
Open Drawer
|
|
30
|
+
</Button>
|
|
31
|
+
<Drawer
|
|
32
|
+
open={open}
|
|
33
|
+
onClose={() => setOpen(false)}
|
|
34
|
+
title="Settings"
|
|
35
|
+
footer={
|
|
36
|
+
<>
|
|
37
|
+
<Button variant="secondary" tone="mono" size="sm" onPress={() => setOpen(false)}>
|
|
38
|
+
Cancel
|
|
39
|
+
</Button>
|
|
40
|
+
<Button variant="primary" tone="brand" size="sm" onPress={() => setOpen(false)}>
|
|
41
|
+
Save
|
|
42
|
+
</Button>
|
|
43
|
+
</>
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
<Typography variant="bodyMd">Drawer body content goes here.</Typography>
|
|
47
|
+
</Drawer>
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Size Variants
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
// sm = 480px, md = 620px (default), lg = 1056px
|
|
57
|
+
<Drawer open={open} onClose={onClose} title="Small" size="sm">{...}</Drawer>
|
|
58
|
+
<Drawer open={open} onClose={onClose} title="Medium" size="md">{...}</Drawer>
|
|
59
|
+
<Drawer open={open} onClose={onClose} title="Large" size="lg">{...}</Drawer>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### With Back Button
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
<Drawer
|
|
66
|
+
open={open}
|
|
67
|
+
onClose={onClose}
|
|
68
|
+
title="Step Details"
|
|
69
|
+
onBack={() => goToPreviousStep()}
|
|
70
|
+
>
|
|
71
|
+
{/* back arrow appears in the header */}
|
|
72
|
+
</Drawer>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### With Header Action
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { Button } from '@xsolla/xui-button';
|
|
79
|
+
|
|
80
|
+
<Drawer
|
|
81
|
+
open={open}
|
|
82
|
+
onClose={onClose}
|
|
83
|
+
title="User Profile"
|
|
84
|
+
headerAction={
|
|
85
|
+
<Button variant="ghost" tone="brand" size="sm">
|
|
86
|
+
View history
|
|
87
|
+
</Button>
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
{/* action renders between the title and the close button */}
|
|
91
|
+
</Drawer>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### With Stepper Sidebar
|
|
95
|
+
|
|
96
|
+
Use the `stepper` prop to render a `@xsolla/xui-b2b-stepper` alongside the content panel, creating a guided multi-step flow.
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import React, { useState } from 'react';
|
|
100
|
+
import { Drawer } from '@xsolla/xui-b2b-drawer';
|
|
101
|
+
import { Stepper } from '@xsolla/xui-b2b-stepper';
|
|
102
|
+
import { Button } from '@xsolla/xui-button';
|
|
103
|
+
import { Typography } from '@xsolla/xui-typography';
|
|
104
|
+
|
|
105
|
+
const STEPS = [
|
|
106
|
+
{ title: 'Account', description: 'Name and email' },
|
|
107
|
+
{ title: 'Billing', description: 'Payment method' },
|
|
108
|
+
{ title: 'Confirm', description: 'Review and submit' },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
export default function StepperDrawer() {
|
|
112
|
+
const [open, setOpen] = useState(false);
|
|
113
|
+
const [active, setActive] = useState(0);
|
|
114
|
+
|
|
115
|
+
const steps = STEPS.map((s, i) => ({
|
|
116
|
+
...s,
|
|
117
|
+
state: i < active ? 'complete' as const : i === active ? 'current' as const : 'incomplete' as const,
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
const isLast = active === steps.length - 1;
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<>
|
|
124
|
+
<Button variant="primary" tone="brand" size="sm" onPress={() => { setActive(0); setOpen(true); }}>
|
|
125
|
+
Open Wizard
|
|
126
|
+
</Button>
|
|
127
|
+
<Drawer
|
|
128
|
+
open={open}
|
|
129
|
+
onClose={() => setOpen(false)}
|
|
130
|
+
title={STEPS[active].title}
|
|
131
|
+
onBack={active > 0 ? () => setActive(a => a - 1) : undefined}
|
|
132
|
+
stepper={
|
|
133
|
+
<Stepper
|
|
134
|
+
steps={steps}
|
|
135
|
+
direction="vertical"
|
|
136
|
+
surface
|
|
137
|
+
onClick={({ number }) => setActive(number - 1)}
|
|
138
|
+
/>
|
|
139
|
+
}
|
|
140
|
+
footer={
|
|
141
|
+
<>
|
|
142
|
+
<Button variant="secondary" tone="mono" size="sm" onPress={() => setOpen(false)}>
|
|
143
|
+
Cancel
|
|
144
|
+
</Button>
|
|
145
|
+
<Button
|
|
146
|
+
variant="primary"
|
|
147
|
+
tone="brand"
|
|
148
|
+
size="sm"
|
|
149
|
+
onPress={() => isLast ? setOpen(false) : setActive(a => a + 1)}
|
|
150
|
+
>
|
|
151
|
+
{isLast ? 'Submit' : 'Next'}
|
|
152
|
+
</Button>
|
|
153
|
+
</>
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
<Typography variant="bodyMd">Content for step {active + 1}.</Typography>
|
|
157
|
+
</Drawer>
|
|
158
|
+
</>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Scrollable Content with Footer Shadow
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<Drawer
|
|
167
|
+
open={open}
|
|
168
|
+
onClose={onClose}
|
|
169
|
+
title="Long Content"
|
|
170
|
+
footerShadow
|
|
171
|
+
footer={<Button variant="primary" tone="brand" size="sm">Done</Button>}
|
|
172
|
+
>
|
|
173
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
174
|
+
{Array.from({ length: 40 }, (_, i) => (
|
|
175
|
+
<Typography key={i} variant="bodyMd">Item {i + 1}</Typography>
|
|
176
|
+
))}
|
|
177
|
+
</div>
|
|
178
|
+
</Drawer>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## API Reference
|
|
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):
|
|
206
|
+
|
|
207
|
+
| Prop | Replacement |
|
|
208
|
+
| :--- | :---------- |
|
|
209
|
+
| `isOpen` | `open` |
|
|
210
|
+
| `header` | `title` + `onBack` |
|
|
211
|
+
| `bottom` | `footer` |
|
|
212
|
+
|
|
213
|
+
## Behaviour
|
|
214
|
+
|
|
215
|
+
- Slides in from the right with an animated transition.
|
|
216
|
+
- Renders in a portal (z-index controlled by `theme.sizing.drawer().zIndex`) so it always appears above page content.
|
|
217
|
+
- Traps keyboard focus inside the drawer while open; restores focus to the previously active element on close.
|
|
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.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xsolla/xui-b2b-drawer",
|
|
3
|
-
"version": "0.148.
|
|
3
|
+
"version": "0.148.1",
|
|
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.148.
|
|
17
|
-
"@xsolla/xui-button": "0.148.
|
|
18
|
-
"@xsolla/xui-core": "0.148.
|
|
19
|
-
"@xsolla/xui-icons-base": "0.148.
|
|
20
|
-
"@xsolla/xui-primitives-core": "0.148.
|
|
21
|
-
"@xsolla/xui-typography": "0.148.
|
|
16
|
+
"@xsolla/xui-b2b-stepper": "0.148.1",
|
|
17
|
+
"@xsolla/xui-button": "0.148.1",
|
|
18
|
+
"@xsolla/xui-core": "0.148.1",
|
|
19
|
+
"@xsolla/xui-icons-base": "0.148.1",
|
|
20
|
+
"@xsolla/xui-primitives-core": "0.148.1",
|
|
21
|
+
"@xsolla/xui-typography": "0.148.1"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": ">=16.8.0",
|