@simplybusiness/mobius 7.0.0 → 7.1.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/CHANGELOG.md +10 -0
- package/dist/cjs/index.js +178 -35
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs/meta.json +141 -6
- package/dist/esm/index.js +176 -27
- package/dist/esm/index.js.map +4 -4
- package/dist/esm/meta.json +142 -5
- package/dist/esm/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/components/Toast/Toast.d.ts +13 -0
- package/dist/types/src/components/Toast/ToastOptionsDoc.d.ts +9 -0
- package/dist/types/src/components/Toast/Toaster.d.ts +19 -0
- package/dist/types/src/components/Toast/index.d.ts +4 -0
- package/dist/types/src/components/Toast/state.d.ts +3 -0
- package/dist/types/src/components/Toast/types.d.ts +27 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/package.json +3 -2
- package/src/components/Combobox/Combobox.tsx +1 -0
- package/src/components/Toast/Toast.css +233 -0
- package/src/components/Toast/Toast.mdx +131 -0
- package/src/components/Toast/Toast.stories.tsx +285 -0
- package/src/components/Toast/Toast.test.tsx +188 -0
- package/src/components/Toast/Toast.tsx +154 -0
- package/src/components/Toast/ToastOptionsDoc.tsx +8 -0
- package/src/components/Toast/Toaster.tsx +46 -0
- package/src/components/Toast/index.tsx +4 -0
- package/src/components/Toast/state.ts +6 -0
- package/src/components/Toast/types.ts +36 -0
- package/src/components/index.tsx +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ToastOptions } from "./types";
|
|
2
|
+
export declare const toast: {
|
|
3
|
+
/** Show an info toast */
|
|
4
|
+
info: (message: string, options?: ToastOptions) => string | number;
|
|
5
|
+
/** Show a success toast */
|
|
6
|
+
success: (message: string, options?: ToastOptions) => string | number;
|
|
7
|
+
/** Show a warning toast */
|
|
8
|
+
warning: (message: string, options?: ToastOptions) => string | number;
|
|
9
|
+
/** Show an error toast */
|
|
10
|
+
error: (message: string, options?: ToastOptions) => string | number;
|
|
11
|
+
/** Dismiss a specific toast by ID or all toasts */
|
|
12
|
+
dismiss: (toastId?: string | number) => string | number;
|
|
13
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToastOptions } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Documentation-only component for ToastOptions.
|
|
4
|
+
* Exists solely to provide ArgTypes for the toast() function options.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ToastOptionsDoc: {
|
|
7
|
+
(_props: ToastOptions): null;
|
|
8
|
+
displayName: string;
|
|
9
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ToastPosition } from "./types";
|
|
2
|
+
export interface ToasterProps {
|
|
3
|
+
/** Position of the toast container */
|
|
4
|
+
position?: ToastPosition;
|
|
5
|
+
/** Whether to show the close (X) button on toasts */
|
|
6
|
+
closeButton?: boolean;
|
|
7
|
+
/** Whether toasts expand on hover */
|
|
8
|
+
expand?: boolean;
|
|
9
|
+
/** Duration in milliseconds before auto-dismiss (default: 4000) */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Maximum number of visible toasts */
|
|
12
|
+
visibleToasts?: number;
|
|
13
|
+
/** Gap between toasts in pixels */
|
|
14
|
+
gap?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const Toaster: {
|
|
17
|
+
({ position, closeButton, expand, duration, visibleToasts, gap, }: ToasterProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
displayName: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type ToastVariant = "info" | "success" | "warning" | "error";
|
|
3
|
+
export type ToastPosition = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
4
|
+
export type ToastOptions = {
|
|
5
|
+
/** Title of the toast */
|
|
6
|
+
title?: string;
|
|
7
|
+
/** Description/body text of the toast */
|
|
8
|
+
description?: ReactNode;
|
|
9
|
+
/** Duration in milliseconds before auto-dismiss */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Action button configuration */
|
|
12
|
+
action?: {
|
|
13
|
+
label: string;
|
|
14
|
+
onClick: () => void;
|
|
15
|
+
};
|
|
16
|
+
/** Cancel button configuration */
|
|
17
|
+
cancel?: {
|
|
18
|
+
label: string;
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
};
|
|
21
|
+
/** Callback when toast is dismissed */
|
|
22
|
+
onDismiss?: () => void;
|
|
23
|
+
/** Callback when toast auto-closes */
|
|
24
|
+
onAutoClose?: () => void;
|
|
25
|
+
/** Whether to show the close button (defaults to Toaster's closeButton prop) */
|
|
26
|
+
showCloseButton?: boolean;
|
|
27
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplybusiness/mobius",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.1.0",
|
|
5
5
|
"description": "Core library of Mobius react components",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -89,7 +89,8 @@
|
|
|
89
89
|
"dialog-polyfill": "^0.5.6",
|
|
90
90
|
"lodash.debounce": "^4.0.8",
|
|
91
91
|
"react-accessible-dropdown-menu-hook": "^4.0.1",
|
|
92
|
-
"react-imask": "^7.6.1"
|
|
92
|
+
"react-imask": "^7.6.1",
|
|
93
|
+
"sonner": "^2.0.7"
|
|
93
94
|
},
|
|
94
95
|
"lint-staged": {
|
|
95
96
|
"*.{js,ts,jsx,tsx}": "eslint --fix"
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/* Toaster container positioning (keep Sonner's positioning logic) */
|
|
2
|
+
[data-sonner-toaster] {
|
|
3
|
+
font-family: var(--font-family);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/* Base toast styles */
|
|
7
|
+
.mobius-toast {
|
|
8
|
+
position: relative;
|
|
9
|
+
font-family: var(--font-family);
|
|
10
|
+
font-size: var(--font-size-regular);
|
|
11
|
+
line-height: var(--line-height-normal);
|
|
12
|
+
color: var(--color-text);
|
|
13
|
+
background-color: var(--color-background);
|
|
14
|
+
border: 2px solid var(--color-border);
|
|
15
|
+
border-radius: var(--radius-1);
|
|
16
|
+
box-shadow: var(--shadow-md);
|
|
17
|
+
padding: var(--size-md);
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: flex-start;
|
|
20
|
+
gap: var(--size-sm);
|
|
21
|
+
width: var(--toast-width);
|
|
22
|
+
max-width: calc(100vw - var(--size-lg) * 2);
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Variant styles */
|
|
27
|
+
.mobius-toast.--info {
|
|
28
|
+
border-color: var(--color-info);
|
|
29
|
+
background-color: var(--color-info-background);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.mobius-toast.--success {
|
|
33
|
+
border-color: var(--color-valid);
|
|
34
|
+
background-color: var(--color-valid-background);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.mobius-toast.--warning {
|
|
38
|
+
border-color: var(--color-warning);
|
|
39
|
+
background-color: var(--color-warning-background);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.mobius-toast.--error {
|
|
43
|
+
border-color: var(--color-error);
|
|
44
|
+
background-color: var(--color-error-background);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Icon */
|
|
48
|
+
.mobius-toast__icon {
|
|
49
|
+
flex-shrink: 0;
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
padding-top: 2px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.mobius-toast__icon .mobius-icon {
|
|
56
|
+
width: 1.25em;
|
|
57
|
+
height: 1.25em;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Body wrapper for content + actions */
|
|
61
|
+
.mobius-toast__body {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: var(--size-sm);
|
|
65
|
+
flex: 1;
|
|
66
|
+
min-width: 0;
|
|
67
|
+
padding-right: var(--size-lg);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Content wrapper (title + description) */
|
|
71
|
+
.mobius-toast__content {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
gap: var(--size-xxs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Title */
|
|
78
|
+
.mobius-toast__title {
|
|
79
|
+
font-size: var(--font-size-regular);
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
line-height: var(--line-height-normal);
|
|
82
|
+
color: var(--color-text);
|
|
83
|
+
margin: 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Description */
|
|
87
|
+
.mobius-toast__description {
|
|
88
|
+
font-size: var(--font-size-regular);
|
|
89
|
+
line-height: var(--line-height-normal);
|
|
90
|
+
color: var(--color-text);
|
|
91
|
+
margin: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Actions wrapper - separate row for buttons */
|
|
95
|
+
.mobius-toast__actions {
|
|
96
|
+
display: flex;
|
|
97
|
+
justify-content: flex-end;
|
|
98
|
+
gap: var(--size-xs);
|
|
99
|
+
flex-wrap: wrap;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Shared button styles */
|
|
103
|
+
.mobius-toast__action,
|
|
104
|
+
.mobius-toast__cancel {
|
|
105
|
+
font-family: var(--font-family);
|
|
106
|
+
font-size: var(--font-size-small);
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
padding: var(--size-xs) var(--size-sm);
|
|
109
|
+
border-radius: var(--radius-1);
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
transition: background-color 0.15s ease;
|
|
112
|
+
flex-shrink: 0;
|
|
113
|
+
display: inline-flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Action button (primary) */
|
|
119
|
+
.mobius-toast__action {
|
|
120
|
+
background-color: var(--button-primary-color);
|
|
121
|
+
color: var(--button-primary-content-color);
|
|
122
|
+
border: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.mobius-toast__action:hover {
|
|
126
|
+
filter: brightness(0.9);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Action button variant colors */
|
|
130
|
+
.mobius-toast.--info .mobius-toast__action {
|
|
131
|
+
background-color: var(--color-info);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.mobius-toast.--success .mobius-toast__action {
|
|
135
|
+
background-color: var(--color-valid);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.mobius-toast.--warning .mobius-toast__action {
|
|
139
|
+
background-color: var(--color-warning);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.mobius-toast.--error .mobius-toast__action {
|
|
143
|
+
background-color: var(--color-error);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* Cancel button (secondary) */
|
|
147
|
+
.mobius-toast__cancel {
|
|
148
|
+
background-color: transparent;
|
|
149
|
+
color: var(--color-text);
|
|
150
|
+
border: 1px solid var(--color-border);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.mobius-toast__cancel:hover {
|
|
154
|
+
background-color: var(--color-background-hover);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Cancel button variant border colors */
|
|
158
|
+
.mobius-toast.--info .mobius-toast__cancel {
|
|
159
|
+
border-color: var(--color-info);
|
|
160
|
+
color: var(--color-info);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.mobius-toast.--success .mobius-toast__cancel {
|
|
164
|
+
border-color: var(--color-valid);
|
|
165
|
+
color: var(--color-valid);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.mobius-toast.--warning .mobius-toast__cancel {
|
|
169
|
+
border-color: var(--color-warning);
|
|
170
|
+
color: var(--color-warning);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.mobius-toast.--error .mobius-toast__cancel {
|
|
174
|
+
border-color: var(--color-error);
|
|
175
|
+
color: var(--color-error);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Close button */
|
|
179
|
+
.mobius-toast__close {
|
|
180
|
+
position: absolute;
|
|
181
|
+
top: var(--size-md);
|
|
182
|
+
right: var(--size-sm);
|
|
183
|
+
background: transparent;
|
|
184
|
+
border: none;
|
|
185
|
+
border-radius: var(--radius-1);
|
|
186
|
+
cursor: pointer;
|
|
187
|
+
padding: var(--size-xxs);
|
|
188
|
+
width: var(--size-md);
|
|
189
|
+
height: var(--size-md);
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: center;
|
|
193
|
+
color: var(--color-text-muted);
|
|
194
|
+
transition: color 0.15s ease;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.mobius-toast__close:hover {
|
|
198
|
+
color: var(--color-text);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* Close icon */
|
|
202
|
+
.mobius-toast__close-icon {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.mobius-toast__close-icon .mobius-icon {
|
|
209
|
+
width: 1.25em;
|
|
210
|
+
height: 1.25em;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Close button variant colors */
|
|
214
|
+
.mobius-toast.--info .mobius-toast__close {
|
|
215
|
+
color: var(--color-info);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.mobius-toast.--success .mobius-toast__close {
|
|
219
|
+
color: var(--color-valid);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.mobius-toast.--warning .mobius-toast__close {
|
|
223
|
+
color: var(--color-warning);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.mobius-toast.--error .mobius-toast__close {
|
|
227
|
+
color: var(--color-error);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Close button hover - darken slightly */
|
|
231
|
+
.mobius-toast .mobius-toast__close:hover {
|
|
232
|
+
filter: brightness(0.8);
|
|
233
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
{/* prettier-ignore-start */}
|
|
2
|
+
|
|
3
|
+
import { Meta, Canvas, ArgTypes } from "@storybook/addon-docs/blocks";
|
|
4
|
+
import { Toaster } from "./Toaster";
|
|
5
|
+
import { ToastOptionsDoc } from "./ToastOptionsDoc";
|
|
6
|
+
import * as ToastStories from "./Toast.stories";
|
|
7
|
+
|
|
8
|
+
<Meta of={ToastStories} />
|
|
9
|
+
|
|
10
|
+
# Toast
|
|
11
|
+
|
|
12
|
+
Toast notifications provide brief, non-blocking feedback messages to users. Built on [Sonner](https://sonner.emilkowal.ski/), toasts automatically dismiss after a configurable duration and support keyboard dismissal.
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import { Toaster, toast } from "@simplybusiness/mobius";
|
|
18
|
+
|
|
19
|
+
// 1. Add the Toaster component once at your app root
|
|
20
|
+
function App() {
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<Toaster />
|
|
24
|
+
<YourApp />
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 2. Call toast functions from anywhere in your app
|
|
30
|
+
function SaveButton() {
|
|
31
|
+
const handleSave = async () => {
|
|
32
|
+
await saveData();
|
|
33
|
+
toast.success("Your changes have been saved");
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return <Button onClick={handleSave}>Save</Button>;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Variants
|
|
41
|
+
|
|
42
|
+
Toasts come in four variants that match the Alert component styling:
|
|
43
|
+
|
|
44
|
+
<Canvas of={ToastStories.AllVariants} />
|
|
45
|
+
|
|
46
|
+
### Info
|
|
47
|
+
|
|
48
|
+
Use for neutral informational messages.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
toast.info("Your session will expire in 5 minutes");
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Success
|
|
55
|
+
|
|
56
|
+
Use to confirm successful operations.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
toast.success("Your changes have been saved");
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Warning
|
|
63
|
+
|
|
64
|
+
Use to alert users to potential issues.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
toast.warning("Please review your input before continuing");
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Error
|
|
71
|
+
|
|
72
|
+
Use to inform users of errors or failures.
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
toast.error("Something went wrong. Please try again.");
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## With Title
|
|
79
|
+
|
|
80
|
+
Add a title for more structured messages:
|
|
81
|
+
|
|
82
|
+
<Canvas of={ToastStories.WithTitle} />
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
toast.success("Your quote has been saved and sent to your email.", {
|
|
86
|
+
title: "Quote saved",
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Toaster Props
|
|
91
|
+
|
|
92
|
+
<ArgTypes of={Toaster} />
|
|
93
|
+
|
|
94
|
+
## Toast Options
|
|
95
|
+
|
|
96
|
+
All toast functions accept an optional second parameter with these options:
|
|
97
|
+
|
|
98
|
+
<ArgTypes of={ToastOptionsDoc} />
|
|
99
|
+
|
|
100
|
+
## Dismissing Toasts
|
|
101
|
+
|
|
102
|
+
Toasts can be dismissed in several ways:
|
|
103
|
+
|
|
104
|
+
1. **Auto-dismiss**: After the configured duration (default 4 seconds)
|
|
105
|
+
2. **Close button**: Clicking the X button
|
|
106
|
+
3. **Keyboard**: Pressing Escape
|
|
107
|
+
4. **Programmatically**: Using `toast.dismiss()`
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
// Dismiss all toasts
|
|
111
|
+
toast.dismiss();
|
|
112
|
+
|
|
113
|
+
// Dismiss a specific toast by ID
|
|
114
|
+
const toastId = toast.info("Loading...");
|
|
115
|
+
toast.dismiss(toastId);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Accessibility
|
|
119
|
+
|
|
120
|
+
- Toasts use appropriate ARIA attributes for announcements
|
|
121
|
+
- Keyboard dismissal with Escape key
|
|
122
|
+
- Respects `prefers-reduced-motion` for animations
|
|
123
|
+
- Focus management for action buttons
|
|
124
|
+
|
|
125
|
+
## Interactive Demo
|
|
126
|
+
|
|
127
|
+
Press 'R' to show a random toast in the interactive demo below:
|
|
128
|
+
|
|
129
|
+
<Canvas of={ToastStories.InteractiveDemo} />
|
|
130
|
+
|
|
131
|
+
{/* prettier-ignore-end */}
|