dotnotify 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,453 @@
1
+ # πŸ”” DotNotify
2
+
3
+ > **iOS-glass particle notifications** β€” dots fly in from random positions, merge into a frosted-glass card, and explode back into dots on dismiss.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/dotnotify.svg)](https://www.npmjs.com/package/dotnotify)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-green.svg)]()
8
+ [![Gzipped](https://img.shields.io/badge/gzipped-~9kb-orange.svg)]()
9
+
10
+ ---
11
+
12
+ ## ✨ Features
13
+
14
+ | Feature | Details |
15
+ |---|---|
16
+ | 🌊 **Particle entry** | 65 dots fly in from random positions with comet trails and glow halos |
17
+ | πŸ’₯ **Particle exit** | 52 dots explode outward on dismiss |
18
+ | 🧊 **iOS glass card** | `backdrop-filter: blur(40px) saturate(220%)` β€” real frosted glass |
19
+ | ⏸️ **Hover pause** | Timer freezes on card hover, dots pulse up |
20
+ | πŸ–ΌοΈ **Developer images** | Pass a URL, File object, or `<img>` element as the notification icon |
21
+ | 🎡 **Web Audio sound** | Zero-file sound via Web Audio API |
22
+ | πŸ“¦ **Stack & queue** | Up to 4 stacked, extras queue automatically |
23
+ | πŸ‘† **Swipe dismiss** | Drag left/right to fling away |
24
+ | πŸ”˜ **Action buttons** | Retry, Dismiss β€” any labels you want |
25
+ | πŸ’ **Ring progress** | Circular countdown ring around the icon as alternative to dots |
26
+ | 🎨 **4 themes** | `ios-glass` · `dark` · `light` · `minimal` |
27
+ | πŸ”· **TypeScript** | Full `.d.ts` included |
28
+ | ⚑ **Promise API** | `await notify.fire(...)` β€” knows how it was dismissed |
29
+ | 0️⃣ **Zero deps** | Pure vanilla JS, no external libraries |
30
+
31
+ ---
32
+
33
+ ## πŸ“¦ Installation
34
+
35
+ ```bash
36
+ npm install dotnotify
37
+ ```
38
+
39
+ Or via CDN (no build step):
40
+
41
+ ```html
42
+ <script src="https://cdn.jsdelivr.net/npm/dotnotify/dist/dotnotify.min.js"></script>
43
+ ```
44
+
45
+ ---
46
+
47
+ ## πŸš€ Quick Start
48
+
49
+ ```js
50
+ import DotNotify from 'dotnotify';
51
+
52
+ const notify = DotNotify({ position: 'top-right' });
53
+
54
+ notify.fire({
55
+ type: 'success',
56
+ title: 'Logged in!',
57
+ message: 'Welcome back, Rahul.',
58
+ });
59
+ ```
60
+
61
+ That's it. Dots fly in, card appears, dots explode when it disappears.
62
+
63
+ ---
64
+
65
+ ## πŸ“ Configuration
66
+
67
+ ### `DotNotify(config)` β€” Global Config
68
+
69
+ ```js
70
+ const notify = DotNotify({
71
+ position: 'top-right', // 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
72
+ theme: 'ios-glass', // 'ios-glass' | 'dark' | 'light' | 'minimal'
73
+ sound: false, // Web Audio ping on appear
74
+ maxStack: 4, // Max simultaneous notifications
75
+ duration: 4200, // Auto-dismiss after N milliseconds
76
+ zIndex: 99999, // CSS z-index
77
+ });
78
+ ```
79
+
80
+ ---
81
+
82
+ ## πŸ”₯ `notify.fire(options)` β€” Full API
83
+
84
+ ```js
85
+ const result = await notify.fire({
86
+ // ── Required ──────────────────────────────
87
+ type: 'error', // 'error' | 'success' | 'warning' | 'info'
88
+ title: 'Login Failed',
89
+ message: 'Invalid credentials. Please try again.',
90
+
91
+ // ── Icon / Image ──────────────────────────
92
+ image: 'https://example.com/avatar.png', // URL ← developer picture
93
+ // image: fileInput.files[0], // File ← from <input type="file">
94
+ // image: document.querySelector('#myImg'), // HTMLImageElement
95
+ // icon: 'πŸ”₯', // Emoji (when no image)
96
+ // icon: '<svg>...</svg>', // SVG string
97
+
98
+ thumbnail: 'https://example.com/preview.jpg', // Small image beside message
99
+
100
+ // ── Override per-notification ─────────────
101
+ theme: 'ios-glass', // Override global theme
102
+ duration: 6000, // Override global duration (ms)
103
+ app: 'My App', // Override app label above title
104
+ sound: true, // Override global sound setting
105
+ progressStyle: 'dots', // 'dots' | 'ring'
106
+
107
+ // ── Action Buttons ────────────────────────
108
+ actions: [
109
+ { label: 'Retry', primary: true, onClick: () => retryLogin() },
110
+ { label: 'Dismiss', primary: false, onClick: () => {} },
111
+ ],
112
+ });
113
+
114
+ // result = { dismissed: 'action' | 'timeout' | 'swipe-left' | 'swipe-right' | 'close', action?: 'Retry' | 'Dismiss' }
115
+ console.log(result.dismissed); // 'action'
116
+ console.log(result.action); // 'Retry'
117
+ ```
118
+
119
+ ---
120
+
121
+ ## πŸ–ΌοΈ Developer Images β€” Full Guide
122
+
123
+ DotNotify lets developers put **any image** β€” user avatar, app logo, product thumbnail β€” into the notification icon area. Three ways to do it:
124
+
125
+ ### 1. URL string (most common)
126
+
127
+ ```js
128
+ notify.fire({
129
+ type: 'info',
130
+ title: 'New message from Priya',
131
+ message: 'Hey, can we jump on a call?',
132
+ image: 'https://i.pravatar.cc/150?img=47', // any image URL
133
+ });
134
+ ```
135
+
136
+ ### 2. File object β€” from `<input type="file">`
137
+
138
+ ```html
139
+ <input type="file" id="avatar" accept="image/*">
140
+ ```
141
+
142
+ ```js
143
+ document.getElementById('avatar').addEventListener('change', (e) => {
144
+ const file = e.target.files[0];
145
+
146
+ notify.fire({
147
+ type: 'success',
148
+ title: 'Profile picture updated',
149
+ message: 'Your new avatar looks great!',
150
+ image: file, // ← File object directly, no FileReader needed
151
+ });
152
+ });
153
+ ```
154
+
155
+ ### 3. Drag and drop
156
+
157
+ ```js
158
+ document.addEventListener('drop', (e) => {
159
+ e.preventDefault();
160
+ const file = e.dataTransfer.files[0];
161
+ if (!file?.type.startsWith('image/')) return;
162
+
163
+ notify.fire({
164
+ type: 'info',
165
+ title: 'File received',
166
+ message: file.name,
167
+ image: file,
168
+ });
169
+ });
170
+ ```
171
+
172
+ ### 4. Existing `<img>` element
173
+
174
+ ```js
175
+ const img = document.querySelector('#user-avatar');
176
+
177
+ notify.fire({
178
+ type: 'success',
179
+ title: 'Logged in',
180
+ message: 'Welcome back!',
181
+ image: img, // ← pass the element directly
182
+ });
183
+ ```
184
+
185
+ ### 5. Thumbnail (secondary image beside message)
186
+
187
+ ```js
188
+ notify.fire({
189
+ type: 'info',
190
+ title: 'New photo shared',
191
+ message: 'Suresh shared a photo with you.',
192
+ image: 'https://example.com/suresh-avatar.jpg', // icon area
193
+ thumbnail: 'https://example.com/photo-preview.jpg', // beside message
194
+ });
195
+ ```
196
+
197
+ ---
198
+
199
+ ## 🎨 Themes
200
+
201
+ ```js
202
+ // Per-notification theme override:
203
+ notify.fire({ type: 'error', title: 'Oops', message: '...', theme: 'light' });
204
+ ```
205
+
206
+ | Theme | Look |
207
+ |---|---|
208
+ | `ios-glass` | Deep dark frosted glass β€” blur 40px, barely-visible tint |
209
+ | `dark` | Opaque near-black, strong shadow |
210
+ | `light` | White frosted glass, for light-background sites |
211
+ | `minimal` | Ultra-thin dark card, no sheen |
212
+
213
+ ---
214
+
215
+ ## πŸ’ Ring vs Dots Progress
216
+
217
+ ```js
218
+ // Circular ring around the icon
219
+ notify.fire({
220
+ type: 'warning',
221
+ title: 'Session expiring',
222
+ message: 'You will be logged out in 60 seconds.',
223
+ progressStyle: 'ring', // ← ring traces around the icon
224
+ });
225
+
226
+ // Default dot progress bar (14 dots at bottom)
227
+ notify.fire({
228
+ type: 'info',
229
+ title: 'Syncing...',
230
+ message: 'Your data is being uploaded.',
231
+ progressStyle: 'dots', // default
232
+ });
233
+ ```
234
+
235
+ ---
236
+
237
+ ## ⏸️ Hover Behaviour
238
+
239
+ When a user hovers the card:
240
+ - The **timer freezes** (remembers exact progress, resumes on mouse-leave)
241
+ - All **pending dots pulse up** and glow in the accent color
242
+ - Done dots revive slightly
243
+ - Individual dot hover **expands that dot to 12px** with full glow bloom
244
+
245
+ ---
246
+
247
+ ## πŸ‘† Swipe to Dismiss
248
+
249
+ Works on both desktop (click-drag) and mobile (touch). Drag left or right past 80px threshold β€” the card flings away and dots explode.
250
+
251
+ ```js
252
+ const result = await notify.fire({ ... });
253
+ if (result.dismissed === 'swipe-left') {
254
+ console.log('User swiped it away');
255
+ }
256
+ ```
257
+
258
+ ---
259
+
260
+ ## πŸ“š Framework Examples
261
+
262
+ ### React
263
+
264
+ ```jsx
265
+ import DotNotify from 'dotnotify';
266
+ import { useRef, useCallback } from 'react';
267
+
268
+ export function useNotify() {
269
+ const notifyRef = useRef(null);
270
+
271
+ function getNotify() {
272
+ if (!notifyRef.current) {
273
+ notifyRef.current = DotNotify({ position: 'top-right', theme: 'ios-glass' });
274
+ }
275
+ return notifyRef.current;
276
+ }
277
+
278
+ const fire = useCallback((opts) => getNotify().fire(opts), []);
279
+ return { fire };
280
+ }
281
+
282
+ // In any component:
283
+ function LoginForm() {
284
+ const { fire } = useNotify();
285
+
286
+ async function handleSubmit() {
287
+ try {
288
+ await loginAPI();
289
+ fire({ type: 'success', title: 'Welcome!', message: 'Logged in successfully.' });
290
+ } catch (err) {
291
+ const result = await fire({
292
+ type: 'error',
293
+ title: 'Login Failed',
294
+ message: err.message,
295
+ actions: [{ label: 'Retry', primary: true }],
296
+ });
297
+ if (result.action === 'Retry') handleSubmit();
298
+ }
299
+ }
300
+
301
+ return <button onClick={handleSubmit}>Login</button>;
302
+ }
303
+ ```
304
+
305
+ ### Vue 3
306
+
307
+ ```js
308
+ // plugins/notify.js
309
+ import DotNotify from 'dotnotify';
310
+ export const notify = DotNotify({ position: 'top-right', theme: 'ios-glass' });
311
+
312
+ // main.js
313
+ import { notify } from './plugins/notify';
314
+ app.config.globalProperties.$notify = notify;
315
+
316
+ // In component:
317
+ this.$notify.fire({ type: 'success', title: 'Done!', message: 'Saved.' });
318
+ ```
319
+
320
+ ### Vanilla JS (CDN)
321
+
322
+ ```html
323
+ <script src="https://cdn.jsdelivr.net/npm/dotnotify/dist/dotnotify.min.js"></script>
324
+ <script>
325
+ const notify = DotNotify({ position: 'top-right' });
326
+
327
+ document.querySelector('#login-btn').addEventListener('click', async () => {
328
+ const result = await notify.fire({
329
+ type: 'error',
330
+ title: 'Login Failed',
331
+ message: 'Wrong password. Try again or reset.',
332
+ actions: [
333
+ { label: 'Retry', primary: true },
334
+ { label: 'Reset pass', onClick: () => location.href = '/reset' },
335
+ ],
336
+ });
337
+ console.log(result); // { dismissed: 'action', action: 'Retry' }
338
+ });
339
+ </script>
340
+ ```
341
+
342
+ ### Next.js / Nuxt
343
+
344
+ ```js
345
+ // Works only in browser β€” wrap in useEffect / onMounted
346
+ useEffect(() => {
347
+ const notify = DotNotify({ position: 'top-right' });
348
+ notify.fire({ type: 'info', title: 'Hello', message: 'Page loaded.' });
349
+ }, []);
350
+ ```
351
+
352
+ ---
353
+
354
+ ## πŸ”§ Instance Methods
355
+
356
+ ```js
357
+ const notify = DotNotify({ ... });
358
+
359
+ // Fire a notification (returns Promise)
360
+ await notify.fire({ ... });
361
+
362
+ // Dismiss all visible notifications immediately
363
+ notify.dismissAll();
364
+
365
+ // Update global config after init
366
+ notify.configure({ theme: 'light', sound: true });
367
+ ```
368
+
369
+ ---
370
+
371
+ ## 🎡 Sound
372
+
373
+ DotNotify generates a subtle two-tone ping using the Web Audio API β€” no audio files, no network requests.
374
+
375
+ ```js
376
+ const notify = DotNotify({ sound: true }); // enable globally
377
+ notify.fire({ ..., sound: false }); // override per-notification
378
+ notify.fire({ ..., sound: true }); // override per-notification
379
+ ```
380
+
381
+ Sound frequencies by type:
382
+ - `error` β€” 440Hz β†’ 330Hz (descending, unsettling)
383
+ - `success` β€” 523Hz β†’ 659Hz (ascending, happy)
384
+ - `warning` β€” 466Hz β†’ 440Hz (slight drop, cautionary)
385
+ - `info` β€” 587Hz β†’ 659Hz (gentle rise, neutral)
386
+
387
+ ---
388
+
389
+ ## 🌐 Browser Support
390
+
391
+ | Browser | Support |
392
+ |---|---|
393
+ | Chrome 76+ | βœ… Full |
394
+ | Safari 14+ | βœ… Full |
395
+ | Firefox 70+ | βœ… Full |
396
+ | Edge 79+ | βœ… Full |
397
+ | iOS Safari 14+ | βœ… Full (touch swipe works) |
398
+ | Android Chrome | βœ… Full |
399
+
400
+ > `backdrop-filter` requires Chrome 76+, Safari 9+, Firefox 70+.
401
+ > On older browsers the card still renders β€” just without the blur effect.
402
+
403
+ ---
404
+
405
+ ## πŸ“ Package Structure
406
+
407
+ ```
408
+ dotnotify/
409
+ β”œβ”€β”€ dist/
410
+ β”‚ β”œβ”€β”€ dotnotify.js ← UMD build (CommonJS + browser global)
411
+ β”‚ β”œβ”€β”€ dotnotify.esm.js ← ES Module build (import/export)
412
+ β”‚ β”œβ”€β”€ dotnotify.min.js ← Minified browser build (~9kb gzipped)
413
+ β”‚ └── dotnotify.d.ts ← TypeScript definitions
414
+ β”œβ”€β”€ src/
415
+ β”‚ └── dotnotify.js ← Source
416
+ β”œβ”€β”€ examples/
417
+ β”‚ β”œβ”€β”€ vanilla.html ← Vanilla JS demo
418
+ β”‚ └── react-example.jsx ← React demo
419
+ β”œβ”€β”€ package.json
420
+ └── README.md
421
+ ```
422
+
423
+ ---
424
+
425
+ ## πŸ—ΊοΈ Roadmap
426
+
427
+ - [ ] `dotnotify/react` β€” official React component wrapper
428
+ - [ ] `dotnotify/vue` β€” official Vue plugin
429
+ - [ ] Custom dot count and dot size
430
+ - [ ] Notification center (history panel)
431
+ - [ ] CSS custom properties for full theme control
432
+ - [ ] RTL support
433
+
434
+ ---
435
+
436
+ ## πŸ“„ License
437
+
438
+ MIT Β© 2024 Your Name
439
+
440
+ ---
441
+
442
+ ## 🀝 Contributing
443
+
444
+ 1. Fork the repo
445
+ 2. `npm install`
446
+ 3. Edit `src/dotnotify.js`
447
+ 4. `npm run build`
448
+ 5. Open `examples/vanilla.html` to test
449
+ 6. Submit a PR
450
+
451
+ ---
452
+
453
+ <p align="center">Made with ❀️ · <a href="https://github.com/yourusername/dotnotify">GitHub</a> · <a href="https://www.npmjs.com/package/dotnotify">npm</a></p>
@@ -0,0 +1,149 @@
1
+ // DotNotify β€” TypeScript Definitions
2
+ // Version 1.0.0
3
+
4
+ export interface DotNotifyConfig {
5
+ /** Where notifications appear on screen. Default: 'top-right' */
6
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
7
+
8
+ /** Visual theme. Default: 'ios-glass' */
9
+ theme?: 'ios-glass' | 'dark' | 'light' | 'minimal';
10
+
11
+ /** Play a subtle sound on notification appear. Default: false */
12
+ sound?: boolean;
13
+
14
+ /** Max notifications visible at once before queuing. Default: 4 */
15
+ maxStack?: number;
16
+
17
+ /** How long each notification lives in ms. Default: 4200 */
18
+ duration?: number;
19
+
20
+ /** CSS z-index for notifications. Default: 99999 */
21
+ zIndex?: number;
22
+ }
23
+
24
+ export interface ActionButton {
25
+ /** Button label text */
26
+ label: string;
27
+
28
+ /** Style as primary accent button. Default: false */
29
+ primary?: boolean;
30
+
31
+ /** Called when this action button is clicked */
32
+ onClick?: () => void;
33
+ }
34
+
35
+ export interface FireOptions {
36
+ /** Notification severity type */
37
+ type: 'error' | 'success' | 'warning' | 'info';
38
+
39
+ /** Bold heading text */
40
+ title: string;
41
+
42
+ /** Body message text */
43
+ message: string;
44
+
45
+ /**
46
+ * Custom icon. Three options:
47
+ * - Emoji string: 'πŸ”₯'
48
+ * - SVG string: '<svg>...</svg>'
49
+ * - Image URL string: 'https://example.com/avatar.png'
50
+ * - File object: from <input type="file"> or drag-drop
51
+ * - HTMLImageElement: existing <img> element on the page
52
+ */
53
+ icon?: string | File | HTMLImageElement;
54
+
55
+ /**
56
+ * Developer image displayed in the notification icon area.
57
+ * Accepts URL string, File object, or HTMLImageElement.
58
+ * When provided, it replaces the icon.
59
+ *
60
+ * @example
61
+ * // URL
62
+ * image: 'https://example.com/avatar.png'
63
+ *
64
+ * // File from input
65
+ * image: fileInput.files[0]
66
+ *
67
+ * // HTMLImageElement
68
+ * image: document.querySelector('#my-img')
69
+ */
70
+ image?: string | File | HTMLImageElement;
71
+
72
+ /**
73
+ * Small thumbnail image shown to the right of the message.
74
+ * Accepts URL string.
75
+ * @example
76
+ * thumbnail: 'https://example.com/preview.jpg'
77
+ */
78
+ thumbnail?: string;
79
+
80
+ /** Override theme for this notification only */
81
+ theme?: 'ios-glass' | 'dark' | 'light' | 'minimal';
82
+
83
+ /** Override duration for this notification only (ms) */
84
+ duration?: number;
85
+
86
+ /** Progress style. Default: 'dots' */
87
+ progressStyle?: 'dots' | 'ring';
88
+
89
+ /** Override the app label above the title */
90
+ app?: string;
91
+
92
+ /** Play sound for this notification (overrides global setting) */
93
+ sound?: boolean;
94
+
95
+ /** Action buttons shown at bottom of card */
96
+ actions?: ActionButton[];
97
+ }
98
+
99
+ export interface DismissResult {
100
+ /** How the notification was dismissed */
101
+ dismissed: 'timeout' | 'close' | 'action' | 'swipe-left' | 'swipe-right' | 'external';
102
+
103
+ /** Which action button was clicked (if dismissed === 'action') */
104
+ action?: string;
105
+ }
106
+
107
+ export interface DotNotifyInstance {
108
+ /**
109
+ * Fire a notification. Returns a Promise that resolves when dismissed.
110
+ *
111
+ * @example
112
+ * const result = await notify.fire({
113
+ * type: 'error',
114
+ * title: 'Login Failed',
115
+ * message: 'Invalid credentials.',
116
+ * });
117
+ * console.log(result.dismissed); // 'timeout' | 'close' | 'action' | ...
118
+ */
119
+ fire(opts: FireOptions): Promise<DismissResult>;
120
+
121
+ /**
122
+ * Instantly dismiss all visible notifications.
123
+ */
124
+ dismissAll(): void;
125
+
126
+ /**
127
+ * Update global config after initialization.
128
+ */
129
+ configure(config: Partial<DotNotifyConfig>): void;
130
+ }
131
+
132
+ /**
133
+ * Create a DotNotify instance.
134
+ *
135
+ * @example
136
+ * import DotNotify from 'dotnotify';
137
+ *
138
+ * const notify = DotNotify({ position: 'top-right', theme: 'ios-glass' });
139
+ *
140
+ * notify.fire({
141
+ * type: 'success',
142
+ * title: 'Saved!',
143
+ * message: 'Your profile has been updated.',
144
+ * });
145
+ */
146
+ declare function DotNotify(config?: DotNotifyConfig): DotNotifyInstance;
147
+
148
+ export default DotNotify;
149
+ export = DotNotify;