@xsolla/xui-toast 0.120.0 → 0.121.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 +273 -75
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,37 +1,66 @@
1
1
  # Toast
2
2
 
3
- A cross-platform React toast notification system for displaying brief, auto-dismissing messages. Includes a provider, hook API, and support for multiple toast variants.
3
+ A cross-platform React toast notification system for displaying brief, auto-dismissing messages. Built on a provider/context architecture with a hook-based API, the Toast package supports multiple variants, configurable positioning, custom durations, custom icons, and programmatic dismiss — on both React (web) and React Native.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @xsolla/xui-toast
8
+ npm install @xsolla/xui-toast @xsolla/xui-core
9
9
  # or
10
- yarn add @xsolla/xui-toast
10
+ yarn add @xsolla/xui-toast @xsolla/xui-core
11
11
  ```
12
12
 
13
- ## Demo
13
+ **Peer dependencies:** `react >= 16.8.0`, `styled-components >= 4`
14
+
15
+ ## Architecture
16
+
17
+ The toast system uses a **Provider → Context → Hook** pattern:
18
+
19
+ ```
20
+ ┌─────────────────────────────────────────────────┐
21
+ │ ToastProvider │
22
+ │ ┌────────────┐ ┌───────────────────────────┐ │
23
+ │ │ ToastContext│ │ ToastGroup (portal / abs) │ │
24
+ │ │ - toasts │ │ ┌───────┐ ┌───────┐ │ │
25
+ │ │ - addToast│──▶ │ Toast │ │ Toast │ ... │ │
26
+ │ │ - dismiss │ │ └───────┘ └───────┘ │ │
27
+ │ └────────────┘ └───────────────────────────┘ │
28
+ │ ▲ │
29
+ │ │ useToast() │
30
+ │ ┌─────┴──────┐ │
31
+ │ │ Your App │ │
32
+ │ └────────────┘ │
33
+ └─────────────────────────────────────────────────┘
34
+ ```
14
35
 
15
- ### Basic Setup (React Web)
36
+ 1. **`ToastProvider`** Wraps your application. Manages toast state (an array of `ToastData`), auto-dismiss timers, and renders the `ToastGroup` container.
37
+ 2. **`ToastContext`** — React context that exposes `addToast`, `dismissToast`, and `dismissAllToasts` to descendants.
38
+ 3. **`useToast`** — Consumer hook that provides convenience methods (`success`, `info`, `warning`, `error`, `dismiss`, `dismissAll`).
39
+ 4. **`ToastGroup`** — Renders the stack of visible toasts. On web, uses `ReactDOM.createPortal` to escape z-index stacking contexts. On React Native, uses absolute positioning.
40
+ 5. **`Toast`** — The individual notification component with icon, message, and optional close button.
16
41
 
17
- Wrap your application with `ToastProvider`:
42
+ ## Quick Start
43
+
44
+ ### 1. Wrap your app with `ToastProvider`
45
+
46
+ **React (Web):**
18
47
 
19
48
  ```tsx
20
- import * as React from 'react';
49
+ import { XUIProvider } from '@xsolla/xui-core';
21
50
  import { ToastProvider } from '@xsolla/xui-toast';
22
51
 
23
52
  export default function App() {
24
53
  return (
25
- <ToastProvider>
26
- <YourApp />
27
- </ToastProvider>
54
+ <XUIProvider initialMode="dark" initialProductContext="b2b">
55
+ <ToastProvider>
56
+ <YourApp />
57
+ </ToastProvider>
58
+ </XUIProvider>
28
59
  );
29
60
  }
30
61
  ```
31
62
 
32
- ### Basic Setup (React Native)
33
-
34
- For React Native, wrap your app entry point with both `XUIProvider` and `ToastProvider`:
63
+ **React Native:**
35
64
 
36
65
  ```tsx
37
66
  import React from 'react';
@@ -55,12 +84,34 @@ export default function App() {
55
84
 
56
85
  > **Important for React Native:** The `ToastProvider` renders toasts using absolute positioning. Ensure your app's root container has `flex: 1` so toasts position correctly relative to the screen.
57
86
 
58
- ### Showing Toasts (React Web)
87
+ ### 2. Show toasts with `useToast`
88
+
89
+ ```tsx
90
+ import { useToast } from '@xsolla/xui-toast';
91
+
92
+ function SaveButton() {
93
+ const toast = useToast();
94
+
95
+ const handleSave = async () => {
96
+ try {
97
+ await saveData();
98
+ toast.success('Changes saved successfully');
99
+ } catch {
100
+ toast.error('Failed to save changes');
101
+ }
102
+ };
103
+
104
+ return <button onClick={handleSave}>Save</button>;
105
+ }
106
+ ```
107
+
108
+ ## Demo
109
+
110
+ ### Showing Toast Variants
59
111
 
60
- Use the `useToast` hook to show toasts:
112
+ Each variant conveys a different semantic meaning and displays a corresponding icon:
61
113
 
62
114
  ```tsx
63
- import * as React from 'react';
64
115
  import { useToast } from '@xsolla/xui-toast';
65
116
 
66
117
  export default function ToastExample() {
@@ -117,10 +168,11 @@ export default function ToastExample() {
117
168
  }
118
169
  ```
119
170
 
120
- ### Toast Variants
171
+ ### Toast Variants (Standalone `Toast` Component)
172
+
173
+ The `Toast` component can be used standalone outside the provider for static display or custom layouts:
121
174
 
122
175
  ```tsx
123
- import * as React from 'react';
124
176
  import { Toast } from '@xsolla/xui-toast';
125
177
 
126
178
  export default function ToastVariants() {
@@ -137,8 +189,9 @@ export default function ToastVariants() {
137
189
 
138
190
  ### Custom Duration
139
191
 
192
+ Control how long toasts remain visible. The default is 5000ms. Set `duration: 0` to disable auto-dismiss entirely:
193
+
140
194
  ```tsx
141
- import * as React from 'react';
142
195
  import { useToast } from '@xsolla/xui-toast';
143
196
 
144
197
  export default function CustomDuration() {
@@ -160,7 +213,44 @@ export default function CustomDuration() {
160
213
  }
161
214
  ```
162
215
 
163
- ### Dismissing Toasts
216
+ You can also set a global default duration on the provider:
217
+
218
+ ```tsx
219
+ <ToastProvider defaultDuration={3000}>
220
+ <App />
221
+ </ToastProvider>
222
+ ```
223
+
224
+ ### Custom Icons
225
+
226
+ Override the default variant icon by passing a `ReactNode` via the `icon` option:
227
+
228
+ ```tsx
229
+ import { useToast } from '@xsolla/xui-toast';
230
+ import { Star } from '@xsolla/xui-icons';
231
+
232
+ function CustomIconExample() {
233
+ const toast = useToast();
234
+
235
+ return (
236
+ <button
237
+ onClick={() =>
238
+ toast.toast({
239
+ message: 'You earned a gold star!',
240
+ variant: 'success',
241
+ icon: <Star />,
242
+ })
243
+ }
244
+ >
245
+ Show Custom Icon Toast
246
+ </button>
247
+ );
248
+ }
249
+ ```
250
+
251
+ ### Dismissing Toasts Programmatically
252
+
253
+ Every `toast.*()` call returns a unique ID. Use it to dismiss a specific toast:
164
254
 
165
255
  ```tsx
166
256
  import * as React from 'react';
@@ -189,8 +279,9 @@ export default function DismissExample() {
189
279
 
190
280
  ### Position Configuration
191
281
 
282
+ Toasts can be anchored to the top or bottom of the viewport:
283
+
192
284
  ```tsx
193
- import * as React from 'react';
194
285
  import { ToastProvider, useToast } from '@xsolla/xui-toast';
195
286
 
196
287
  function ToastTrigger() {
@@ -207,109 +298,176 @@ export default function BottomPosition() {
207
298
  }
208
299
  ```
209
300
 
301
+ ### Multiple Toasts
302
+
303
+ Multiple toasts stack vertically in the order they are created:
304
+
305
+ ```tsx
306
+ import { useToast } from '@xsolla/xui-toast';
307
+
308
+ function MultipleToastsExample() {
309
+ const toast = useToast();
310
+
311
+ const showMultiple = () => {
312
+ toast.success('First toast - Success');
313
+ setTimeout(() => toast.info('Second toast - Info'), 200);
314
+ setTimeout(() => toast.warning('Third toast - Warning'), 400);
315
+ };
316
+
317
+ return <button onClick={showMultiple}>Show Multiple Toasts</button>;
318
+ }
319
+ ```
320
+
210
321
  ## API Reference
211
322
 
212
323
  ### ToastProvider
213
324
 
214
325
  The root provider component that manages toast state and renders the toast container.
215
326
 
216
- **ToastProvider Props:**
217
-
218
327
  | Prop | Type | Default | Description |
219
328
  | :--- | :--- | :------ | :---------- |
220
- | children | `ReactNode` | - | Application content. |
221
- | position | `"top" \| "bottom"` | `"top"` | Position of the toast container. |
222
- | defaultDuration | `number` | `5000` | Default auto-dismiss duration in milliseconds. |
329
+ | `children` | `ReactNode` | | Application content. |
330
+ | `position` | `"top" \| "bottom"` | `"top"` | Position of the toast container on screen. |
331
+ | `defaultDuration` | `number` | `5000` | Default auto-dismiss duration in milliseconds. Set to `0` to disable auto-dismiss globally. |
223
332
 
224
333
  ### useToast
225
334
 
226
- A hook that provides methods to show and dismiss toasts. Must be used within a `ToastProvider`.
335
+ A hook that returns methods to show and dismiss toasts. Must be called within a `ToastProvider` — throws an error otherwise.
227
336
 
228
- **useToast Return Value:**
337
+ **Return type: `UseToastReturn`**
229
338
 
230
339
  | Method | Signature | Description |
231
340
  | :----- | :-------- | :---------- |
232
- | toast | `(options: ToastOptions) => string` | Show a toast with custom options. Returns the toast ID. |
233
- | success | `(message: string, options?) => string` | Show a success toast. |
234
- | info | `(message: string, options?) => string` | Show an info toast. |
235
- | warning | `(message: string, options?) => string` | Show a warning toast. |
236
- | error | `(message: string, options?) => string` | Show an error toast. |
237
- | dismiss | `(id: string) => void` | Dismiss a specific toast by ID. |
238
- | dismissAll | `() => void` | Dismiss all toasts. |
341
+ | `toast` | `(options: ToastOptions) => string` | Show a toast with full control over options. Returns the toast ID. |
342
+ | `success` | `(message: string, options?) => string` | Show a success toast with a check icon. |
343
+ | `info` | `(message: string, options?) => string` | Show an info toast with an info icon. |
344
+ | `warning` | `(message: string, options?) => string` | Show a warning toast with an alert icon. |
345
+ | `error` | `(message: string, options?) => string` | Show an error toast with an alert icon. |
346
+ | `dismiss` | `(id: string) => void` | Dismiss a specific toast by its ID. |
347
+ | `dismissAll` | `() => void` | Dismiss all visible toasts at once. |
239
348
 
240
349
  ### Toast
241
350
 
242
- The individual toast component. Typically used internally by `ToastProvider`, but can be used standalone.
243
-
244
- **Toast Props:**
351
+ The individual toast notification component. Used internally by `ToastGroup`, but can be rendered standalone for static display.
245
352
 
246
353
  | Prop | Type | Default | Description |
247
354
  | :--- | :--- | :------ | :---------- |
248
- | id | `string` | - | Unique toast identifier. |
249
- | message | `string` | - | Toast message text. |
250
- | variant | `"success" \| "info" \| "warning" \| "error"` | `"info"` | Toast variant/style. |
251
- | icon | `ReactNode` | - | Custom icon (optional, uses default icons per variant). |
252
- | onClose | `() => void` | - | Callback when close button is clicked. If not provided, close button is hidden. |
355
+ | `id` | `string` | | Unique identifier for the toast (required). |
356
+ | `message` | `string` | | Toast message text (required). |
357
+ | `variant` | `"success" \| "info" \| "warning" \| "error"` | `"info"` | Visual variant that determines the icon color. |
358
+ | `icon` | `ReactNode` | | Custom icon element. When omitted, a default icon for the variant is used. |
359
+ | `onClose` | `() => void` | | Callback fired when the close button is clicked. If omitted, the close button is hidden. |
253
360
 
254
361
  ### ToastOptions
255
362
 
256
- Options passed to the `toast()` method.
363
+ Options object passed to the `toast()` method or as the second argument to shorthand methods.
257
364
 
258
365
  | Property | Type | Default | Description |
259
366
  | :------- | :--- | :------ | :---------- |
260
- | message | `string` | - | Toast message text. |
261
- | variant | `"success" \| "info" \| "warning" \| "error"` | `"info"` | Toast variant. |
262
- | icon | `ReactNode` | - | Custom icon. |
263
- | duration | `number` | Provider's `defaultDuration` | Auto-dismiss duration. Use `0` to disable auto-dismiss. |
367
+ | `message` | `string` | | Toast message text (required). |
368
+ | `variant` | `"success" \| "info" \| "warning" \| "error"` | `"info"` | Toast variant. |
369
+ | `icon` | `ReactNode` | | Custom icon element. |
370
+ | `duration` | `number` | Provider's `defaultDuration` | Auto-dismiss duration in ms. Use `0` to keep the toast until manually dismissed. |
371
+
372
+ ### ToastData
373
+
374
+ Internal data structure for a toast instance (available via `ToastContext`).
375
+
376
+ | Property | Type | Description |
377
+ | :------- | :--- | :---------- |
378
+ | `id` | `string` | Unique identifier generated by the provider. |
379
+ | `variant` | `ToastVariant` | The toast variant. |
380
+ | `message` | `string` | Toast message text. |
381
+ | `icon` | `ReactNode \| undefined` | Custom icon, if provided. |
382
+ | `duration` | `number \| undefined` | Auto-dismiss duration. |
383
+
384
+ ### ToastContext
385
+
386
+ The React context object. Typically consumed via `useToast`, but available for advanced use cases:
387
+
388
+ ```tsx
389
+ import { useContext } from 'react';
390
+ import { ToastContext } from '@xsolla/xui-toast';
264
391
 
265
- ### Variant Values
392
+ function CustomToastConsumer() {
393
+ const ctx = useContext(ToastContext);
394
+ if (!ctx) throw new Error('Must be within ToastProvider');
266
395
 
267
- | Variant | Use Case | Default Icon |
268
- | :------ | :------- | :----------- |
269
- | `success` | Positive outcomes, confirmations | Check circle |
270
- | `info` | General information, neutral messages | Info circle |
271
- | `warning` | Caution, requires attention | Alert circle |
272
- | `error` | Errors, failures, critical issues | Alert circle |
396
+ return <span>Active toasts: {ctx.toasts.length}</span>;
397
+ }
398
+ ```
399
+
400
+ ### Variant Reference
401
+
402
+ | Variant | Use Case | Default Icon | Icon Color Theme Path |
403
+ | :------ | :------- | :----------- | :-------------------- |
404
+ | `success` | Positive outcomes, confirmations | `<Check />` | `theme.colors.content.success.primary` |
405
+ | `info` | General information, neutral messages | `<AlertCircle />` | `theme.colors.content.inverse` |
406
+ | `warning` | Caution, requires user attention | `<AlertCircle />` | `theme.colors.content.warning.primary` |
407
+ | `error` | Errors, failures, critical issues | `<AlertCircle />` | `theme.colors.content.alert.primary` |
273
408
 
274
409
  ## Theming
275
410
 
276
- Toast uses the inverse color scheme (dark background with light text):
411
+ Toast uses the **inverse** color scheme (dark background with light text) to stand out from the application content:
412
+
413
+ ### Colors
277
414
 
278
415
  ```typescript
279
- // Toast styling accessed via theme
280
- theme.colors.background.inverse // Toast background (black)
281
- theme.colors.content.inverse // Toast text (white)
282
-
283
- // Variant-specific icon colors
284
- theme.colors.content.success.primary // Success icon
285
- theme.colors.content.warning.primary // Warning icon
286
- theme.colors.content.alert.primary // Error icon
416
+ theme.colors.background.inverse // Toast background
417
+ theme.colors.content.inverse // Toast text and info icon color
418
+
419
+ theme.colors.content.success.primary // Success icon color
420
+ theme.colors.content.warning.primary // Warning icon color
421
+ theme.colors.content.alert.primary // Error icon color
287
422
  ```
288
423
 
424
+ ### Sizing
425
+
426
+ Toast dimensions come from `theme.sizing.toast()`:
427
+
428
+ | Token | Value | Description |
429
+ | :---- | :---- | :---------- |
430
+ | `minHeight` | `64` | Minimum toast height |
431
+ | `paddingHorizontal` | `12` | Left/right padding |
432
+ | `paddingVertical` | `8` | Top/bottom padding |
433
+ | `borderRadius` | `4` | Corner radius |
434
+ | `gap` | `12` | Space between icon, text, and close button |
435
+ | `iconSize` | `24` | Icon dimensions |
436
+ | `closeButtonSize` | `24` | Close button tap target |
437
+ | `closeIconSize` | `20` | Close icon dimensions |
438
+ | `fontSize` | `16` | Message text size |
439
+ | `lineHeight` | `20` | Message line height |
440
+ | `maxWidth` | `400` | Maximum toast width |
441
+ | `containerPadding` | `12` | Padding between toast container and screen edges |
442
+ | `groupGap` | `4` | Vertical gap between stacked toasts |
443
+
289
444
  ## Platform Support
290
445
 
291
- This package supports both React (web) and React Native with identical API:
446
+ This package supports both React (web) and React Native with an identical API.
292
447
 
293
448
  ### React (Web)
294
- - Uses `ReactDOM.createPortal` for fixed positioning at the document body level
295
- - Toasts appear above all other content regardless of z-index stacking context
296
- - Works with any React web framework (Next.js, Vite, Create React App, etc.)
449
+
450
+ - Uses `ReactDOM.createPortal` to render toasts at the document body level
451
+ - Toasts appear above all other content regardless of z-index stacking contexts
452
+ - Works with any React web framework (Next.js, Vite, Create React App, Remix, etc.)
297
453
 
298
454
  ### React Native
455
+
299
456
  - Uses absolute positioning within the app container
300
457
  - Toasts render within the component tree (no portal equivalent in RN)
301
458
  - Works with React Navigation, Expo, and bare React Native projects
302
459
 
303
- ### Cross-Platform Usage Notes
460
+ ### Cross-Platform Comparison
304
461
 
305
462
  | Feature | Web | React Native |
306
463
  | :------ | :-- | :----------- |
307
- | Provider setup | Wrap with `ToastProvider` | Wrap with `XUIProvider` + `ToastProvider` |
464
+ | Provider setup | `ToastProvider` | `XUIProvider` + `ToastProvider` |
308
465
  | Hook API | `useToast()` | `useToast()` (identical) |
309
- | Event handlers | `onClick` (optional) | `onPress` |
466
+ | Event handlers | `onClick` | `onPress` |
310
467
  | Root container | Any element | Must have `flex: 1` |
311
468
  | Position: top | Fixed to viewport top | Absolute to container top |
312
469
  | Position: bottom | Fixed to viewport bottom | Absolute to container bottom |
470
+ | Rendering | `ReactDOM.createPortal` into `document.body` | Absolute-positioned `Box` in tree |
313
471
 
314
472
  ### React Native Example with Navigation
315
473
 
@@ -338,7 +496,47 @@ export default function App() {
338
496
 
339
497
  ## Accessibility
340
498
 
341
- - Toasts use `role="alert"` and `aria-live="polite"` for screen reader announcements
342
- - Close button includes `aria-label="Dismiss toast"`
343
- - Icons are decorative and do not affect accessibility
344
- - Keyboard users can dismiss toasts via the close button
499
+ - Each toast renders with `role="alert"` and `aria-live="polite"` for screen reader announcements
500
+ - The close button includes `aria-label="Dismiss toast"` for assistive technology
501
+ - Icons are decorative and do not interfere with accessibility
502
+ - Keyboard users can Tab to the close button and press Enter to dismiss
503
+ - Toast messages are limited to 2 lines (`numberOfLines={2}`) for readability
504
+
505
+ ## Troubleshooting
506
+
507
+ ### "useToast must be used within a ToastProvider"
508
+
509
+ The `useToast` hook was called in a component that is not a descendant of `ToastProvider`. Ensure your component tree has a `ToastProvider` ancestor:
510
+
511
+ ```tsx
512
+ // Correct — hook is inside the provider tree
513
+ <ToastProvider>
514
+ <ComponentThatCallsUseToast />
515
+ </ToastProvider>
516
+
517
+ // Wrong — hook is outside the provider
518
+ <ComponentThatCallsUseToast />
519
+ <ToastProvider>
520
+ <OtherContent />
521
+ </ToastProvider>
522
+ ```
523
+
524
+ ### Toasts not visible on React Native
525
+
526
+ Make sure the container inside `ToastProvider` has `flex: 1`. Without this, the absolute-positioned toast group has no reference height:
527
+
528
+ ```tsx
529
+ <ToastProvider>
530
+ <View style={{ flex: 1 }}> {/* Required */}
531
+ <App />
532
+ </View>
533
+ </ToastProvider>
534
+ ```
535
+
536
+ ### Toasts appear behind a modal or overlay
537
+
538
+ On web, `ToastGroup` uses `z-index: 9999`. If your modal or overlay uses a higher z-index, the toasts may be hidden. Consider placing the `ToastProvider` above your modal provider in the tree, or adjusting z-index values.
539
+
540
+ ### Auto-dismiss not working
541
+
542
+ Ensure `duration` is not set to `0`. A duration of `0` disables auto-dismiss. Check both the per-toast `duration` option and the provider's `defaultDuration` prop.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsolla/xui-toast",
3
- "version": "0.120.0",
3
+ "version": "0.121.0",
4
4
  "main": "./web/index.js",
5
5
  "module": "./web/index.mjs",
6
6
  "types": "./web/index.d.ts",
@@ -14,9 +14,9 @@
14
14
  "test:coverage": "vitest run --coverage"
15
15
  },
16
16
  "dependencies": {
17
- "@xsolla/xui-core": "0.120.0",
18
- "@xsolla/xui-icons": "0.120.0",
19
- "@xsolla/xui-primitives-core": "0.120.0"
17
+ "@xsolla/xui-core": "0.121.0",
18
+ "@xsolla/xui-icons": "0.121.0",
19
+ "@xsolla/xui-primitives-core": "0.121.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": ">=16.8.0",