@samline/notify 0.1.0 → 0.1.6
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/.local/agent-index.md +63 -0
- package/.local/deploy-and-release-guide.md +277 -0
- package/.local/lessons.md +76 -0
- package/.local/package-replication-guide.md +501 -0
- package/.local/todo.md +49 -0
- package/AGENTS.md +55 -0
- package/README.md +9 -7
- package/docs/browser.md +4 -4
- package/package.json +9 -7
- package/CHANGELOG.md +0 -9
- package/dist/core/index.d.ts +0 -47
- package/dist/core/index.test.d.ts +0 -1
- package/dist/react/Toaster.d.ts +0 -6
- package/dist/react/index.d.ts +0 -10
- package/dist/svelte/store.d.ts +0 -3
- package/dist/vanilla/index.d.ts +0 -13
- package/dist/vanilla/toasterManager.d.ts +0 -6
- package/dist/vue/index.d.ts +0 -20
- package/examples/no-bundler/index.html +0 -25
- package/src/core/index.test.ts +0 -26
- package/src/core/index.ts +0 -122
- package/src/react/Toaster.tsx +0 -67
- package/src/react/index.ts +0 -13
- package/src/styles/sileo.css +0 -49
- package/src/svelte/Toaster.svelte +0 -69
- package/src/svelte/store.ts +0 -9
- package/src/vanilla/index.ts +0 -22
- package/src/vanilla/toasterManager.ts +0 -132
- package/src/vue/index.ts +0 -61
- package/test/smoke/no-bundler.test.ts +0 -10
- package/test/smoke/svelte.test.ts +0 -12
- package/tsconfig.json +0 -16
- /package/{rollup.config.js → rollup.config.mjs} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@samline/notify",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Notifications engine inspired by Sileo, with adapters for vanilla, React, Vue and Svelte.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -42,13 +42,15 @@
|
|
|
42
42
|
"@types/node": "^20.0.0",
|
|
43
43
|
"@types/react": "^18.2.0",
|
|
44
44
|
"@types/react-dom": "^18.2.0"
|
|
45
|
-
}
|
|
46
|
-
,
|
|
45
|
+
},
|
|
47
46
|
"devDependenciesMeta": {
|
|
48
|
-
"@types/react": {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
"@types/react": {
|
|
48
|
+
"optional": true
|
|
49
|
+
},
|
|
50
|
+
"@types/react-dom": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
52
54
|
"dependencies": {
|
|
53
55
|
"motion": "^10.0.0"
|
|
54
56
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.1.0 - Initial rename and multi-adapter release
|
|
4
|
-
|
|
5
|
-
- Renamed internal controller `sileo` -> `notify` (kept `sileo` alias for compatibility).
|
|
6
|
-
- Package renamed to `@samline/notify`.
|
|
7
|
-
- UMD build changed to `dist/notify.umd.js` and exposes `window.notify` (compat `window.notifications`).
|
|
8
|
-
- Updated docs, examples and tests to reflect the rename.
|
|
9
|
-
- Copied styles to `dist/styles.css` as part of build.
|
package/dist/core/index.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export type Position = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
2
|
-
export type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading' | 'action';
|
|
3
|
-
export interface ToastButton {
|
|
4
|
-
title: string;
|
|
5
|
-
onClick: () => void;
|
|
6
|
-
}
|
|
7
|
-
export interface ToastOptions {
|
|
8
|
-
title?: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
position?: Position;
|
|
11
|
-
duration?: number | null;
|
|
12
|
-
type?: ToastType;
|
|
13
|
-
button?: ToastButton;
|
|
14
|
-
[key: string]: any;
|
|
15
|
-
}
|
|
16
|
-
export interface ToastItem {
|
|
17
|
-
id: string;
|
|
18
|
-
options: ToastOptions;
|
|
19
|
-
createdAt: number;
|
|
20
|
-
}
|
|
21
|
-
type Listener = (items: ToastItem[]) => void;
|
|
22
|
-
declare class NotifyController {
|
|
23
|
-
private toasts;
|
|
24
|
-
private listeners;
|
|
25
|
-
private idCounter;
|
|
26
|
-
private nextId;
|
|
27
|
-
subscribe(fn: Listener): () => boolean;
|
|
28
|
-
private notify;
|
|
29
|
-
getToasts(): ToastItem[];
|
|
30
|
-
show(opts: ToastOptions): string;
|
|
31
|
-
success(opts: ToastOptions): string;
|
|
32
|
-
error(opts: ToastOptions): string;
|
|
33
|
-
info(opts: ToastOptions): string;
|
|
34
|
-
warning(opts: ToastOptions): string;
|
|
35
|
-
action(opts: ToastOptions): string;
|
|
36
|
-
dismiss(id: string): void;
|
|
37
|
-
clear(position?: Position): void;
|
|
38
|
-
promise<T = any>(p: Promise<T>, opts: {
|
|
39
|
-
loading: ToastOptions;
|
|
40
|
-
success?: ToastOptions | ((r: T) => ToastOptions);
|
|
41
|
-
error?: ToastOptions | ((e: any) => ToastOptions);
|
|
42
|
-
position?: Position;
|
|
43
|
-
}): Promise<T>;
|
|
44
|
-
}
|
|
45
|
-
export declare const notify: NotifyController;
|
|
46
|
-
export declare const sileo: NotifyController;
|
|
47
|
-
export default notify;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/react/Toaster.d.ts
DELETED
package/dist/react/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { notify, sileo } from '../core/index';
|
|
2
|
-
export { Toaster } from './Toaster';
|
|
3
|
-
import { notify as coreNotify, sileo as coreSileo } from '../core/index';
|
|
4
|
-
type ReactAPI = {
|
|
5
|
-
notify: typeof coreNotify;
|
|
6
|
-
sileo: typeof coreSileo;
|
|
7
|
-
Toaster?: any;
|
|
8
|
-
};
|
|
9
|
-
declare const defaultExport: ReactAPI;
|
|
10
|
-
export default defaultExport;
|
package/dist/svelte/store.d.ts
DELETED
package/dist/vanilla/index.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { notify as coreNotify, sileo as coreSileo } from '../core/index';
|
|
2
|
-
import { initToasters, POSITIONS } from './toasterManager';
|
|
3
|
-
export { initToasters, POSITIONS };
|
|
4
|
-
export declare const notify: (opts: import("../core/index").ToastOptions) => string;
|
|
5
|
-
type VanillaAPI = {
|
|
6
|
-
sileo: typeof coreSileo;
|
|
7
|
-
initToasters: typeof initToasters;
|
|
8
|
-
notify: typeof notify;
|
|
9
|
-
controller?: typeof coreNotify;
|
|
10
|
-
notifications?: any;
|
|
11
|
-
};
|
|
12
|
-
declare const defaultExport: VanillaAPI;
|
|
13
|
-
export default defaultExport;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
declare const POSITIONS: readonly ["top-left", "top-center", "top-right", "bottom-left", "bottom-center", "bottom-right"];
|
|
2
|
-
export type Position = typeof POSITIONS[number];
|
|
3
|
-
export declare function initToasters(root?: HTMLElement, positions?: Position[]): {
|
|
4
|
-
destroy(): void;
|
|
5
|
-
};
|
|
6
|
-
export { POSITIONS };
|
package/dist/vue/index.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { App } from 'vue';
|
|
2
|
-
export declare const Toaster: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
3
|
-
position: {
|
|
4
|
-
type: StringConstructor;
|
|
5
|
-
default: string;
|
|
6
|
-
};
|
|
7
|
-
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
8
|
-
[key: string]: any;
|
|
9
|
-
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
10
|
-
position: {
|
|
11
|
-
type: StringConstructor;
|
|
12
|
-
default: string;
|
|
13
|
-
};
|
|
14
|
-
}>> & Readonly<{}>, {
|
|
15
|
-
position: string;
|
|
16
|
-
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
17
|
-
declare const _default: {
|
|
18
|
-
install(app: App): void;
|
|
19
|
-
};
|
|
20
|
-
export default _default;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html>
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
6
|
-
<title>Samline Notifications - No Bundler Example</title>
|
|
7
|
-
<link rel="stylesheet" href="../../dist/styles.css">
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<h1>Samline Notifications - No Bundler</h1>
|
|
11
|
-
<button id="ok">Show toast</button>
|
|
12
|
-
|
|
13
|
-
<script src="../../dist/notify.umd.js"></script>
|
|
14
|
-
<script>
|
|
15
|
-
// UMD global: window.notify (preferred). For backward compatibility the API
|
|
16
|
-
// object also exposes a `notifications` key when necessary.
|
|
17
|
-
const api = window.notify || window.notifications;
|
|
18
|
-
const manager = api.initToasters(document.body, ['top-right','bottom-left']);
|
|
19
|
-
|
|
20
|
-
document.getElementById('ok').addEventListener('click', () => {
|
|
21
|
-
api.notify({ title: 'Hola', description: 'Toast desde UMD', duration: 4000 });
|
|
22
|
-
});
|
|
23
|
-
</script>
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|
package/src/core/index.test.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { sileo } from './index';
|
|
3
|
-
|
|
4
|
-
describe('sileo core', () => {
|
|
5
|
-
it('shows and dismisses a toast', () => {
|
|
6
|
-
const id = sileo.success({ title: 't1' });
|
|
7
|
-
const items = sileo.getToasts();
|
|
8
|
-
expect(items.find((i) => i.id === id)).toBeDefined();
|
|
9
|
-
sileo.dismiss(id);
|
|
10
|
-
const after = sileo.getToasts();
|
|
11
|
-
expect(after.find((i) => i.id === id)).toBeUndefined();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('promise flow shows loading then result', async () => {
|
|
15
|
-
const p = new Promise((resolve) => setTimeout(() => resolve('ok'), 50));
|
|
16
|
-
const promise = sileo.promise(p as any, {
|
|
17
|
-
loading: { title: 'loading' },
|
|
18
|
-
success: { title: 'done' },
|
|
19
|
-
error: { title: 'err' }
|
|
20
|
-
});
|
|
21
|
-
await promise;
|
|
22
|
-
const items = sileo.getToasts();
|
|
23
|
-
// success toast should be present
|
|
24
|
-
expect(items.some((t) => t.options.title === 'done')).toBe(true);
|
|
25
|
-
});
|
|
26
|
-
});
|
package/src/core/index.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
export type Position =
|
|
2
|
-
| 'top-left'
|
|
3
|
-
| 'top-center'
|
|
4
|
-
| 'top-right'
|
|
5
|
-
| 'bottom-left'
|
|
6
|
-
| 'bottom-center'
|
|
7
|
-
| 'bottom-right';
|
|
8
|
-
|
|
9
|
-
export type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading' | 'action';
|
|
10
|
-
|
|
11
|
-
export interface ToastButton {
|
|
12
|
-
title: string;
|
|
13
|
-
onClick: () => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface ToastOptions {
|
|
17
|
-
title?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
position?: Position;
|
|
20
|
-
duration?: number | null; // ms, null = sticky
|
|
21
|
-
type?: ToastType;
|
|
22
|
-
button?: ToastButton;
|
|
23
|
-
[key: string]: any;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ToastItem {
|
|
27
|
-
id: string;
|
|
28
|
-
options: ToastOptions;
|
|
29
|
-
createdAt: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
type Listener = (items: ToastItem[]) => void;
|
|
33
|
-
|
|
34
|
-
class NotifyController {
|
|
35
|
-
private toasts: ToastItem[] = [];
|
|
36
|
-
private listeners = new Set<Listener>();
|
|
37
|
-
private idCounter = 1;
|
|
38
|
-
|
|
39
|
-
private nextId() {
|
|
40
|
-
return `notify_${Date.now()}_${this.idCounter++}`;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
subscribe(fn: Listener) {
|
|
44
|
-
this.listeners.add(fn);
|
|
45
|
-
fn(this.toasts.slice());
|
|
46
|
-
return () => this.listeners.delete(fn);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private notify() {
|
|
50
|
-
const snapshot = this.toasts.slice();
|
|
51
|
-
this.listeners.forEach((l) => l(snapshot));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
getToasts() {
|
|
55
|
-
return this.toasts.slice();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
show(opts: ToastOptions): string {
|
|
59
|
-
const id = this.nextId();
|
|
60
|
-
const item: ToastItem = { id, options: { ...opts }, createdAt: Date.now() };
|
|
61
|
-
this.toasts.push(item);
|
|
62
|
-
this.notify();
|
|
63
|
-
return id;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
success(opts: ToastOptions) {
|
|
67
|
-
return this.show({ ...opts, type: 'success' });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
error(opts: ToastOptions) {
|
|
71
|
-
return this.show({ ...opts, type: 'error' });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
info(opts: ToastOptions) {
|
|
75
|
-
return this.show({ ...opts, type: 'info' });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
warning(opts: ToastOptions) {
|
|
79
|
-
return this.show({ ...opts, type: 'warning' });
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
action(opts: ToastOptions) {
|
|
83
|
-
return this.show({ ...opts, type: 'action' });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
dismiss(id: string) {
|
|
87
|
-
const idx = this.toasts.findIndex((t) => t.id === id);
|
|
88
|
-
if (idx >= 0) {
|
|
89
|
-
this.toasts.splice(idx, 1);
|
|
90
|
-
this.notify();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
clear(position?: Position) {
|
|
95
|
-
if (position) {
|
|
96
|
-
this.toasts = this.toasts.filter((t) => t.options.position !== position);
|
|
97
|
-
} else {
|
|
98
|
-
this.toasts = [];
|
|
99
|
-
}
|
|
100
|
-
this.notify();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
promise<T = any>(p: Promise<T>, opts: { loading: ToastOptions; success?: ToastOptions | ((r: T) => ToastOptions); error?: ToastOptions | ((e: any) => ToastOptions); position?: Position; }) {
|
|
104
|
-
const loadingId = this.show({ ...(opts.loading || {}), type: 'loading', position: opts.position });
|
|
105
|
-
p.then((res) => {
|
|
106
|
-
this.dismiss(loadingId);
|
|
107
|
-
const successOpt = typeof opts.success === 'function' ? (opts.success as any)(res) : opts.success;
|
|
108
|
-
if (successOpt) this.show({ ...(successOpt || {}), type: 'success', position: opts.position });
|
|
109
|
-
}).catch((err) => {
|
|
110
|
-
this.dismiss(loadingId);
|
|
111
|
-
const errorOpt = typeof opts.error === 'function' ? (opts.error as any)(err) : opts.error;
|
|
112
|
-
if (errorOpt) this.show({ ...(errorOpt || {}), type: 'error', position: opts.position });
|
|
113
|
-
});
|
|
114
|
-
return p;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export const notify = new NotifyController();
|
|
119
|
-
// backward compatibility alias
|
|
120
|
-
export const sileo = notify as unknown as NotifyController;
|
|
121
|
-
|
|
122
|
-
export default notify;
|
package/src/react/Toaster.tsx
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import type { ToastItem } from '../core/index';
|
|
3
|
-
import { sileo } from '../core/index';
|
|
4
|
-
import { animate } from 'motion';
|
|
5
|
-
|
|
6
|
-
export interface ReactToasterProps {
|
|
7
|
-
position?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const Toaster: React.FC<ReactToasterProps> = ({ position = 'top-right' }) => {
|
|
11
|
-
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
|
12
|
-
const lastSnapshot = React.useRef<string[]>([]);
|
|
13
|
-
|
|
14
|
-
useEffect(() => {
|
|
15
|
-
const unsub = sileo.subscribe((items) => setToasts(items.filter((t) => (t.options.position || 'top-right') === position)));
|
|
16
|
-
return () => { unsub(); };
|
|
17
|
-
}, [position]);
|
|
18
|
-
|
|
19
|
-
const handleDismiss = async (id: string) => {
|
|
20
|
-
const el = document.querySelector(`[data-id="${id}"]`) as HTMLElement | null;
|
|
21
|
-
if (el) {
|
|
22
|
-
try {
|
|
23
|
-
const a = animate(el, { opacity: [1, 0], transform: ['translateY(0px)', 'translateY(-8px)'] }, { duration: 0.15 });
|
|
24
|
-
await (a as any).finished;
|
|
25
|
-
} catch (e) { /* ignore */ }
|
|
26
|
-
}
|
|
27
|
-
sileo.dismiss(id);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// animate in newly added toasts
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const ids = toasts.map(t => t.id);
|
|
33
|
-
const added = ids.filter(id => !lastSnapshot.current.includes(id));
|
|
34
|
-
added.forEach(id => {
|
|
35
|
-
const el = document.querySelector(`[data-id="${id}"]`) as HTMLElement | null;
|
|
36
|
-
if (el) {
|
|
37
|
-
// set initial state then animate
|
|
38
|
-
el.style.opacity = '0';
|
|
39
|
-
el.style.transform = 'translateY(-6px)';
|
|
40
|
-
try {
|
|
41
|
-
const a = animate(el, { opacity: [0, 1], transform: ['translateY(-6px)', 'translateY(0px)'] }, { duration: 0.22 });
|
|
42
|
-
// ignore finished
|
|
43
|
-
} catch (e) {}
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
lastSnapshot.current = ids;
|
|
47
|
-
}, [toasts]);
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div className="sileo-toaster" data-position={position}>
|
|
51
|
-
{toasts.map((t) => (
|
|
52
|
-
<div key={t.id} className="sileo-toast" data-type={t.options.type} data-id={t.id}>
|
|
53
|
-
<div style={{ flex: 1 }}>
|
|
54
|
-
<div className="sileo-toast-header">{t.options.title}</div>
|
|
55
|
-
{t.options.description && <div className="sileo-toast-desc">{t.options.description}</div>}
|
|
56
|
-
</div>
|
|
57
|
-
{t.options.button && (
|
|
58
|
-
<button className="sileo-toast-btn" onClick={(e) => { e.stopPropagation(); t.options.button!.onClick(); }}>{t.options.button.title}</button>
|
|
59
|
-
)}
|
|
60
|
-
<button className="sileo-toast-close" onClick={() => handleDismiss(t.id)}>×</button>
|
|
61
|
-
</div>
|
|
62
|
-
))}
|
|
63
|
-
</div>
|
|
64
|
-
);
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default Toaster;
|
package/src/react/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export { notify, sileo } from '../core/index';
|
|
2
|
-
export { Toaster } from './Toaster';
|
|
3
|
-
|
|
4
|
-
import { notify as coreNotify, sileo as coreSileo } from '../core/index';
|
|
5
|
-
|
|
6
|
-
type ReactAPI = {
|
|
7
|
-
notify: typeof coreNotify;
|
|
8
|
-
sileo: typeof coreSileo;
|
|
9
|
-
Toaster?: any;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const defaultExport: ReactAPI = { notify: coreNotify, sileo: coreSileo };
|
|
13
|
-
export default defaultExport;
|
package/src/styles/sileo.css
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
:root{
|
|
2
|
-
--sileo-bg: #0f1724;
|
|
3
|
-
--sileo-foreground: #ffffff;
|
|
4
|
-
--sileo-success: #16a34a;
|
|
5
|
-
--sileo-error: #ef4444;
|
|
6
|
-
--sileo-info: #2563eb;
|
|
7
|
-
--sileo-warning: #f59e0b;
|
|
8
|
-
--sileo-gap: 12px;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
.sileo-toaster{
|
|
12
|
-
position: fixed;
|
|
13
|
-
z-index: 9999;
|
|
14
|
-
display: flex;
|
|
15
|
-
flex-direction: column;
|
|
16
|
-
gap: var(--sileo-gap);
|
|
17
|
-
pointer-events: none;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.sileo-toaster[data-position="top-right"]{ top: 16px; right: 16px; align-items:flex-end; }
|
|
21
|
-
.sileo-toaster[data-position="top-left"]{ top: 16px; left: 16px; align-items:flex-start; }
|
|
22
|
-
.sileo-toaster[data-position="bottom-right"]{ bottom: 16px; right: 16px; align-items:flex-end; }
|
|
23
|
-
.sileo-toaster[data-position="bottom-left"]{ bottom: 16px; left: 16px; align-items:flex-start; }
|
|
24
|
-
|
|
25
|
-
.sileo-toast{
|
|
26
|
-
pointer-events: auto;
|
|
27
|
-
min-width: 220px;
|
|
28
|
-
max-width: 360px;
|
|
29
|
-
background: var(--sileo-bg);
|
|
30
|
-
color: var(--sileo-foreground);
|
|
31
|
-
padding: 12px 14px;
|
|
32
|
-
border-radius: 10px;
|
|
33
|
-
box-shadow: 0 6px 18px rgba(2,6,23,0.6);
|
|
34
|
-
display: flex;
|
|
35
|
-
gap: 8px;
|
|
36
|
-
align-items: center;
|
|
37
|
-
position: relative;
|
|
38
|
-
overflow: hidden;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.sileo-toast[data-type="success"]{ border-left: 4px solid var(--sileo-success); }
|
|
42
|
-
.sileo-toast[data-type="error"]{ border-left: 4px solid var(--sileo-error); }
|
|
43
|
-
.sileo-toast[data-type="info"]{ border-left: 4px solid var(--sileo-info); }
|
|
44
|
-
.sileo-toast[data-type="warning"]{ border-left: 4px solid var(--sileo-warning); }
|
|
45
|
-
|
|
46
|
-
.sileo-toast-header{ font-weight: 600; font-size: 14px; }
|
|
47
|
-
.sileo-toast-desc{ font-size: 13px; opacity: 0.85; margin-top: 4px; }
|
|
48
|
-
.sileo-toast-btn{ margin-left: 8px; background: transparent; color: inherit; border: 1px solid rgba(255,255,255,0.08); padding: 6px 8px; border-radius: 6px; cursor: pointer; }
|
|
49
|
-
.sileo-toast-close{ position: absolute; right: 8px; top: 6px; background: transparent; border: none; color: inherit; font-size: 16px; cursor: pointer; }
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { onMount, onDestroy } from 'svelte';
|
|
3
|
-
import { toasts, initSileoStore } from './store';
|
|
4
|
-
import type { ToastItem } from '../core/index';
|
|
5
|
-
import { sileo } from '../core/index';
|
|
6
|
-
import { animate } from 'motion';
|
|
7
|
-
|
|
8
|
-
let unsubscribe: () => void;
|
|
9
|
-
let items: ToastItem[] = [];
|
|
10
|
-
|
|
11
|
-
onMount(() => {
|
|
12
|
-
// subscribe local items to avoid $store auto-subscription issues
|
|
13
|
-
unsubscribe = toasts.subscribe((v) => (items = v));
|
|
14
|
-
// also initialize sileo store subscription helper if present
|
|
15
|
-
try {
|
|
16
|
-
initSileoStore();
|
|
17
|
-
} catch (e) {
|
|
18
|
-
// no-op
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
onDestroy(() => { unsubscribe && unsubscribe(); });
|
|
23
|
-
|
|
24
|
-
export function animateIn(node: HTMLElement) {
|
|
25
|
-
try {
|
|
26
|
-
const a = animate(node, { opacity: [0, 1], transform: ['translateY(-8px)', 'translateY(0px)'] }, { duration: 0.2 });
|
|
27
|
-
return {
|
|
28
|
-
destroy() {
|
|
29
|
-
// no-op
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
} catch (e) {
|
|
33
|
-
return { destroy() {} };
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function handleDismiss(id: string, ev: Event) {
|
|
38
|
-
const btn = ev.currentTarget as HTMLElement | null;
|
|
39
|
-
const el = btn ? btn.closest('.sileo-toast') as HTMLElement | null : null;
|
|
40
|
-
if (el) {
|
|
41
|
-
try {
|
|
42
|
-
const a = animate(el, { opacity: [1, 0], transform: ['translateY(0px)', 'translateY(-8px)'] }, { duration: 0.15 });
|
|
43
|
-
await (a as any).finished;
|
|
44
|
-
} catch (e) { /* ignore */ }
|
|
45
|
-
}
|
|
46
|
-
sileo.dismiss(id);
|
|
47
|
-
}
|
|
48
|
-
</script>
|
|
49
|
-
|
|
50
|
-
<style>
|
|
51
|
-
/* rely on global sileo styles */
|
|
52
|
-
</style>
|
|
53
|
-
|
|
54
|
-
{#if items && items.length}
|
|
55
|
-
{#each items as t (t.id)}
|
|
56
|
-
<div class="sileo-toast" data-type={t.options.type} data-id={t.id} use:animateIn>
|
|
57
|
-
<div style="flex:1">
|
|
58
|
-
<div class="sileo-toast-header">{t.options.title}</div>
|
|
59
|
-
{#if t.options.description}
|
|
60
|
-
<div class="sileo-toast-desc">{t.options.description}</div>
|
|
61
|
-
{/if}
|
|
62
|
-
</div>
|
|
63
|
-
{#if t.options.button}
|
|
64
|
-
<button class="sileo-toast-btn" on:click|stopPropagation={(e) => { t.options.button && typeof t.options.button.onClick === 'function' ? t.options.button.onClick() : undefined; handleDismiss(t.id, e); }}>{t.options.button.title}</button>
|
|
65
|
-
{/if}
|
|
66
|
-
<button class="sileo-toast-close" on:click={(e) => handleDismiss(t.id, e)}>×</button>
|
|
67
|
-
</div>
|
|
68
|
-
{/each}
|
|
69
|
-
{/if}
|
package/src/svelte/store.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { writable } from 'svelte/store';
|
|
2
|
-
import { sileo, ToastItem } from '../core/index';
|
|
3
|
-
|
|
4
|
-
export const toasts = writable<ToastItem[]>([]);
|
|
5
|
-
|
|
6
|
-
export function initSileoStore() {
|
|
7
|
-
const unsub = sileo.subscribe((items: ToastItem[]) => toasts.set(items));
|
|
8
|
-
return unsub;
|
|
9
|
-
}
|
package/src/vanilla/index.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { notify as coreNotify, sileo as coreSileo } from '../core/index';
|
|
2
|
-
import { initToasters, POSITIONS } from './toasterManager';
|
|
3
|
-
|
|
4
|
-
export { initToasters, POSITIONS };
|
|
5
|
-
|
|
6
|
-
// convenience: keep the previous shape where `notify` was the quick-show function
|
|
7
|
-
export const notify = coreNotify.show.bind(coreNotify);
|
|
8
|
-
|
|
9
|
-
type VanillaAPI = {
|
|
10
|
-
sileo: typeof coreSileo;
|
|
11
|
-
initToasters: typeof initToasters;
|
|
12
|
-
notify: typeof notify;
|
|
13
|
-
// keep full controller available under `controller` for advanced use
|
|
14
|
-
controller?: typeof coreNotify;
|
|
15
|
-
notifications?: any;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const defaultExport: VanillaAPI = { sileo: coreSileo, initToasters, notify, controller: coreNotify };
|
|
19
|
-
// backward compatibility: expose `notifications` key pointing to the same API object
|
|
20
|
-
(defaultExport as any).notifications = defaultExport;
|
|
21
|
-
|
|
22
|
-
export default defaultExport;
|