sonner-wc 0.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/LICENSE +28 -0
- package/README.md +144 -0
- package/dist/constants.d.ts +10 -0
- package/dist/icons.d.ts +8 -0
- package/dist/index.d.ts +12 -0
- package/dist/registry.d.ts +11 -0
- package/dist/sonner-toast.d.ts +29 -0
- package/dist/sonner-toaster.d.ts +20 -0
- package/dist/sonner-wc.bundle.js +742 -0
- package/dist/sonner-wc.bundle.js.map +17 -0
- package/dist/sonner-wc.js +1962 -0
- package/dist/sonner-wc.js.map +17 -0
- package/dist/styles.d.ts +16 -0
- package/dist/toast.d.ts +21 -0
- package/dist/types.d.ts +85 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ben Nyberg
|
|
4
|
+
|
|
5
|
+
This project, `sonner-wc`, is a port of Sonner — a React toast library by
|
|
6
|
+
Emil Kowalski (https://github.com/emilkowalski/sonner) — to a framework-agnostic
|
|
7
|
+
web component. Substantial portions of the CSS, the icon SVGs, the constants,
|
|
8
|
+
and the swipe and stacking algorithms are derived directly from Sonner.
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2023 Emil Kowalski
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# sonner-wc
|
|
2
|
+
|
|
3
|
+
[Sonner](https://github.com/emilkowalski/sonner)'s toast UX as a framework-agnostic web component.
|
|
4
|
+
Drop it into anything — vanilla HTML, Vue, Svelte, htmx — same API, same look.
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<script type="module" src="https://unpkg.com/sonner-wc/dist/sonner-wc.bundle.js"></script>
|
|
8
|
+
|
|
9
|
+
<sonner-toaster position="bottom-right" theme="system" rich-colors></sonner-toaster>
|
|
10
|
+
|
|
11
|
+
<script type="module">
|
|
12
|
+
import { toast } from 'sonner-wc';
|
|
13
|
+
toast.success('Saved!', { description: 'Your changes are live.' });
|
|
14
|
+
</script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
bun add sonner-wc # or npm / pnpm / yarn
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
ESM only. Modern browsers (last two versions of Chrome, Firefox, Safari, Edge).
|
|
24
|
+
|
|
25
|
+
## API
|
|
26
|
+
|
|
27
|
+
### The `<sonner-toaster>` host
|
|
28
|
+
|
|
29
|
+
Place once, anywhere in the page. Configure with attributes.
|
|
30
|
+
|
|
31
|
+
| Attribute | Values | Default |
|
|
32
|
+
| ---------------- | -------------------------------------------------------------------------------- | -------------- |
|
|
33
|
+
| `position` | `top-left` `top-center` `top-right` `bottom-left` `bottom-center` `bottom-right` | `bottom-right` |
|
|
34
|
+
| `theme` | `light` `dark` `system` | `light` |
|
|
35
|
+
| `rich-colors` | presence flag | off |
|
|
36
|
+
| `close-button` | presence flag (applies to all toasts) | off |
|
|
37
|
+
| `invert` | presence flag | off |
|
|
38
|
+
| `dir` | `ltr` `rtl` `auto` | `auto` |
|
|
39
|
+
| `gap` | number (px) between stacked toasts | `14` |
|
|
40
|
+
| `duration` | default lifetime in ms (per-toast override wins) | `4000` |
|
|
41
|
+
| `visible-toasts` | how many toasts fan out vs. stack behind | `3` |
|
|
42
|
+
| `offset` | viewport gutter; px number or CSS length | `24px` |
|
|
43
|
+
| `mobile-offset` | gutter under 600px | `16px` |
|
|
44
|
+
| `hotkey` | `Alt+KeyT`-style chord that expands the stack | `altKey+KeyT` |
|
|
45
|
+
|
|
46
|
+
### The `toast()` helper
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { toast } from 'sonner-wc';
|
|
50
|
+
|
|
51
|
+
toast('Default message');
|
|
52
|
+
toast.success('Saved', { description: 'Looks good.' });
|
|
53
|
+
toast.error('Oops', { action: { label: 'Retry', onClick: () => retry() } });
|
|
54
|
+
toast.warning('Storage almost full');
|
|
55
|
+
toast.info('You have a new message');
|
|
56
|
+
toast.loading('Uploading…', { id: 'job-1' });
|
|
57
|
+
|
|
58
|
+
// Update in place by reusing an id:
|
|
59
|
+
toast.success('Done', { id: 'job-1' });
|
|
60
|
+
|
|
61
|
+
// Async with automatic loading → success/error:
|
|
62
|
+
toast.promise(api.save(), {
|
|
63
|
+
loading: 'Saving…',
|
|
64
|
+
success: (data) => `Saved as ${data.name}`,
|
|
65
|
+
error: (err) => `Couldn't save: ${err.message}`,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Anything custom:
|
|
69
|
+
toast.custom((id) => {
|
|
70
|
+
const el = document.createElement('div');
|
|
71
|
+
el.innerHTML = `<strong>Custom</strong> content #${id}`;
|
|
72
|
+
return el;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Dismiss programmatically:
|
|
76
|
+
toast.dismiss('job-1'); // by id
|
|
77
|
+
toast.dismiss(); // all
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The returned element is the `<sonner-toast>` itself. You can mutate it directly — set
|
|
81
|
+
attributes, add children, call `.dismiss()`, etc.
|
|
82
|
+
|
|
83
|
+
### Declarative form
|
|
84
|
+
|
|
85
|
+
You can also build toasts as plain HTML and append them to a toaster:
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<sonner-toaster id="t" position="bottom-right"></sonner-toaster>
|
|
89
|
+
|
|
90
|
+
<script>
|
|
91
|
+
const el = document.createElement('sonner-toast');
|
|
92
|
+
el.setAttribute('type', 'success');
|
|
93
|
+
el.setAttribute('duration', '4000');
|
|
94
|
+
el.innerHTML = `
|
|
95
|
+
<span slot="title">Saved!</span>
|
|
96
|
+
<span slot="description">Your changes are live.</span>
|
|
97
|
+
<button slot="action">View</button>
|
|
98
|
+
`;
|
|
99
|
+
document.getElementById('t').appendChild(el);
|
|
100
|
+
</script>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Slots on `<sonner-toast>`
|
|
104
|
+
|
|
105
|
+
| Slot | Purpose |
|
|
106
|
+
| ------------- | -------------------------------------------- |
|
|
107
|
+
| `title` | Primary text |
|
|
108
|
+
| `description` | Secondary text |
|
|
109
|
+
| `icon` | Icon override (otherwise picked from `type`) |
|
|
110
|
+
| `action` | Right-aligned action button |
|
|
111
|
+
| `cancel` | Left-aligned cancel button |
|
|
112
|
+
|
|
113
|
+
### Theming
|
|
114
|
+
|
|
115
|
+
All colors are CSS custom properties on the toaster. Override them in your own
|
|
116
|
+
CSS to restyle:
|
|
117
|
+
|
|
118
|
+
```css
|
|
119
|
+
sonner-toaster {
|
|
120
|
+
--normal-bg: #1a1a1a;
|
|
121
|
+
--normal-border: #2a2a2a;
|
|
122
|
+
--normal-text: #fafafa;
|
|
123
|
+
--success-bg: #042f2e;
|
|
124
|
+
--success-text: #5eead4;
|
|
125
|
+
--border-radius: 12px;
|
|
126
|
+
--width: 400px;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Development
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
bun install
|
|
134
|
+
bun run dev # builds with watch + serves demo at http://localhost:3000
|
|
135
|
+
bun test # runs unit tests
|
|
136
|
+
bun run build # produces dist/
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Credits
|
|
140
|
+
|
|
141
|
+
UI behavior and visual design are direct ports of [Sonner](https://github.com/emilkowalski/sonner) by Emil Kowalski.
|
|
142
|
+
This package adapts that work into a framework-agnostic web component.
|
|
143
|
+
|
|
144
|
+
MIT.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const VISIBLE_TOASTS_AMOUNT = 3;
|
|
2
|
+
export declare const VIEWPORT_OFFSET = "24px";
|
|
3
|
+
export declare const MOBILE_VIEWPORT_OFFSET = "16px";
|
|
4
|
+
export declare const TOAST_LIFETIME = 4000;
|
|
5
|
+
export declare const TOAST_WIDTH = 356;
|
|
6
|
+
export declare const GAP = 14;
|
|
7
|
+
export declare const SWIPE_THRESHOLD = 45;
|
|
8
|
+
export declare const SWIPE_VELOCITY_THRESHOLD = 0.11;
|
|
9
|
+
export declare const TIME_BEFORE_UNMOUNT = 200;
|
|
10
|
+
export declare const DEFAULT_HOTKEY: readonly string[];
|
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToastType } from './types.js';
|
|
2
|
+
export declare const SUCCESS_ICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" height=\"20\" width=\"20\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z\" clip-rule=\"evenodd\"/></svg>";
|
|
3
|
+
export declare const WARNING_ICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\" height=\"20\" width=\"20\"><path fill-rule=\"evenodd\" d=\"M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z\" clip-rule=\"evenodd\"/></svg>";
|
|
4
|
+
export declare const INFO_ICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" height=\"20\" width=\"20\"><path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z\" clip-rule=\"evenodd\"/></svg>";
|
|
5
|
+
export declare const ERROR_ICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" fill=\"currentColor\" height=\"20\" width=\"20\"><path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z\" clip-rule=\"evenodd\"/></svg>";
|
|
6
|
+
export declare const CLOSE_ICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line></svg>";
|
|
7
|
+
export declare const LOADING_SPINNER: string;
|
|
8
|
+
export declare function getTypeIcon(type: ToastType): string | null;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { SonnerToast } from './sonner-toast.js';
|
|
2
|
+
export { SonnerToaster } from './sonner-toaster.js';
|
|
3
|
+
export { toast } from './toast.js';
|
|
4
|
+
import type { SonnerToast as _SonnerToast } from './sonner-toast.js';
|
|
5
|
+
import type { SonnerToaster as _SonnerToaster } from './sonner-toaster.js';
|
|
6
|
+
export type { Position, PromiseOptions, SonnerToastElement, SonnerToasterElement, SwipeDirection, Theme, ToastAction, ToastContent, ToastOptions, ToastType, ToasterOptions, } from './types.js';
|
|
7
|
+
declare global {
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'sonner-toast': _SonnerToast;
|
|
10
|
+
'sonner-toaster': _SonnerToaster;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SonnerToastElement, SonnerToasterElement } from './types.js';
|
|
2
|
+
export declare function nextToastId(): number;
|
|
3
|
+
export declare function registerToast(el: SonnerToastElement): void;
|
|
4
|
+
export declare function unregisterToast(el: SonnerToastElement): void;
|
|
5
|
+
export declare function getToast(id: string | number): SonnerToastElement | undefined;
|
|
6
|
+
export declare function registerToaster(el: SonnerToasterElement): void;
|
|
7
|
+
export declare function unregisterToaster(el: SonnerToasterElement): void;
|
|
8
|
+
/** Find a toaster element to host a new toast. Prefer the most-recently-connected one. */
|
|
9
|
+
export declare function defaultToaster(): SonnerToasterElement | undefined;
|
|
10
|
+
export declare function allToasters(): readonly SonnerToasterElement[];
|
|
11
|
+
export declare function dismissAllToasts(): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SonnerToastElement, ToastContent, ToastOptions, ToastType } from './types.js';
|
|
2
|
+
declare const HTMLElementCtor: typeof HTMLElement;
|
|
3
|
+
export declare class SonnerToast extends HTMLElementCtor implements SonnerToastElement {
|
|
4
|
+
#private;
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
toastId: string | number;
|
|
7
|
+
constructor();
|
|
8
|
+
connectedCallback(): void;
|
|
9
|
+
disconnectedCallback(): void;
|
|
10
|
+
attributeChangedCallback(name: string, _old: string | null, _val: string | null): void;
|
|
11
|
+
get toastType(): ToastType;
|
|
12
|
+
/** Imperative dismiss — triggers exit animation, then removes from DOM. */
|
|
13
|
+
dismiss(): void;
|
|
14
|
+
/** Apply new content/options. Used by toast.promise() for loading→success/error transitions. */
|
|
15
|
+
update(options: ToastOptions & {
|
|
16
|
+
title?: ToastContent;
|
|
17
|
+
type?: ToastType;
|
|
18
|
+
}): void;
|
|
19
|
+
setTitle(value: ToastContent): void;
|
|
20
|
+
setDescription(value: ToastContent | undefined): void;
|
|
21
|
+
setIcon(value: Node | string | null | undefined): void;
|
|
22
|
+
setHandlers(handlers: {
|
|
23
|
+
onDismiss?: ToastOptions['onDismiss'];
|
|
24
|
+
onAutoClose?: ToastOptions['onAutoClose'];
|
|
25
|
+
}): void;
|
|
26
|
+
/** Called by the toaster to pause/resume this toast's auto-dismiss timer. */
|
|
27
|
+
setPaused(paused: boolean): void;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SonnerToastElement, SonnerToasterElement } from './types.js';
|
|
2
|
+
declare const HTMLElementCtor: typeof HTMLElement;
|
|
3
|
+
export declare class SonnerToaster extends HTMLElementCtor implements SonnerToasterElement {
|
|
4
|
+
#private;
|
|
5
|
+
static get observedAttributes(): string[];
|
|
6
|
+
constructor();
|
|
7
|
+
connectedCallback(): void;
|
|
8
|
+
disconnectedCallback(): void;
|
|
9
|
+
attributeChangedCallback(name: string, _old: string | null, _val: string | null): void;
|
|
10
|
+
addToast(el: SonnerToastElement): SonnerToastElement;
|
|
11
|
+
/** Re-announce `text` through the dedicated assertive live region. Used when
|
|
12
|
+
* a toast transitions to an urgent type (error/warning) after mount — the
|
|
13
|
+
* toast's own aria-live change is unreliable across screen readers, so we
|
|
14
|
+
* route the alert through a fresh region whose announcement behavior is
|
|
15
|
+
* consistent. The clear-then-set pattern forces SRs that diff content to
|
|
16
|
+
* treat the new text as a new announcement. */
|
|
17
|
+
announceUrgent(text: string): void;
|
|
18
|
+
dismissAll(): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|