Initial commit
This commit is contained in:
80
src/components/ds/toast.js
Normal file
80
src/components/ds/toast.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { toast as hotToast } from 'react-hot-toast';
|
||||
|
||||
/**
|
||||
* Toast — transient confirmation of a user action.
|
||||
*
|
||||
* The app already depends on react-hot-toast (13 call sites), so this wraps it
|
||||
* rather than introducing a second toast system. Wrapping buys three things:
|
||||
* one place to set styling so toasts match the design system, a named API
|
||||
* (`toast.success` / `toast.error` / `toast.promise`) instead of ad-hoc option
|
||||
* objects, and a single default duration.
|
||||
*
|
||||
* Rule of thumb: toast for "that worked", `Alert` for "here is a condition you
|
||||
* need to keep seeing", `ConfirmModal` for "are you sure".
|
||||
*/
|
||||
|
||||
const BASE_STYLE = {
|
||||
borderRadius: '12px',
|
||||
background: '#FFFFFF',
|
||||
color: 'hsl(215 25% 27%)',
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
padding: '10px 14px',
|
||||
boxShadow: '0 12px 28px -6px rgb(16 24 40 / 0.12), 0 4px 10px -4px rgb(16 24 40 / 0.06)',
|
||||
border: '1px solid hsl(150 8% 87%)',
|
||||
maxWidth: '420px',
|
||||
};
|
||||
|
||||
const DURATIONS = { success: 2600, error: 4200, info: 3000, loading: Infinity };
|
||||
|
||||
const withDefaults = (options = {}) => ({
|
||||
position: 'bottom-center',
|
||||
...options,
|
||||
style: { ...BASE_STYLE, ...(options.style || {}) },
|
||||
});
|
||||
|
||||
export const toast = {
|
||||
success(message, options) {
|
||||
return hotToast.success(message, {
|
||||
duration: DURATIONS.success,
|
||||
iconTheme: { primary: 'hsl(142 71% 36%)', secondary: '#FFFFFF' },
|
||||
...withDefaults(options),
|
||||
});
|
||||
},
|
||||
|
||||
error(message, options) {
|
||||
return hotToast.error(message, {
|
||||
duration: DURATIONS.error,
|
||||
iconTheme: { primary: 'hsl(0 84% 60%)', secondary: '#FFFFFF' },
|
||||
...withDefaults(options),
|
||||
});
|
||||
},
|
||||
|
||||
info(message, options) {
|
||||
return hotToast(message, { duration: DURATIONS.info, ...withDefaults(options) });
|
||||
},
|
||||
|
||||
loading(message, options) {
|
||||
return hotToast.loading(message, { duration: DURATIONS.loading, ...withDefaults(options) });
|
||||
},
|
||||
|
||||
/**
|
||||
* Ties a toast to an async action: loading while it runs, then success or
|
||||
* error. Preferred over a manual loading/dismiss pair, which leaks a spinner
|
||||
* whenever the promise rejects.
|
||||
*/
|
||||
promise(promise, messages, options) {
|
||||
return hotToast.promise(
|
||||
promise,
|
||||
{
|
||||
loading: messages?.loading ?? 'Working…',
|
||||
success: messages?.success ?? 'Done',
|
||||
error: messages?.error ?? 'Something went wrong',
|
||||
},
|
||||
withDefaults(options)
|
||||
);
|
||||
},
|
||||
|
||||
dismiss: hotToast.dismiss,
|
||||
remove: hotToast.remove,
|
||||
};
|
||||
Reference in New Issue
Block a user