@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.
Files changed (2) hide show
  1. package/README.md +145 -73
  2. package/package.json +7 -7
package/README.md CHANGED
@@ -1,32 +1,42 @@
1
- # B2B Drawer
1
+ # Drawer
2
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.
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
- ## Demo
11
+ ## Imports
14
12
 
15
- ### Basic Drawer
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 React, { useState } from 'react';
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
- export default function BasicDrawer() {
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 Drawer
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
- ### Size Variants
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
- // 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>
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 Back Button
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 Details"
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 Header Action
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 Profile"
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
- ### With Stepper Sidebar
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
- Use the `stepper` prop to render a `@xsolla/xui-b2b-stepper` alongside the content panel, creating a guided multi-step flow.
195
+ ### With stepper sidebar
97
196
 
98
197
  ```tsx
99
- import React, { useState } from 'react';
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
- export default function StepperDrawer() {
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: i < active ? 'complete' as const : i === active ? 'current' as const : 'incomplete' as const,
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 Wizard
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 Content with Footer Shadow
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 Content"
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
- ## 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):
286
+ ## Accessibility
206
287
 
207
- | Prop | Replacement |
208
- | :--- | :---------- |
209
- | `isOpen` | `open` |
210
- | `header` | `title` + `onBack` |
211
- | `bottom` | `footer` |
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 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.
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.150.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.150.0",
17
- "@xsolla/xui-button": "0.150.0",
18
- "@xsolla/xui-core": "0.150.0",
19
- "@xsolla/xui-icons-base": "0.150.0",
20
- "@xsolla/xui-primitives-core": "0.150.0",
21
- "@xsolla/xui-typography": "0.150.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",