Mercurial
annotate design_system/src/components/notifications.js @ 253:fdf3816959cb
[ui] Add Sonner-like notification stack
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 04 Aug 2026 12:21:19 -0700 |
| parents | 7a7581f040e8 |
| children | 2b6e732087ff |
| rev | line source |
|---|---|
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
1 const MAX_VISIBLE = 3; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
2 const MAX_QUEUED = 20; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
3 const MAX_RECORDS = MAX_VISIBLE + MAX_QUEUED; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
4 const DEFAULT_DURATION_MS = 5000; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
5 const MIN_DURATION_MS = 250; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
6 const MAX_DURATION_MS = 120000; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
7 const TONES = new Set(["info", "success", "warning", "error"]); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
8 const ANNOUNCEMENTS = new Set(["none", "polite", "assertive"]); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
9 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
10 function isShortString(value, maximum, allowEmpty = false) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
11 return typeof value === "string" && |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
12 value.length <= maximum && |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
13 (allowEmpty || value.trim().length > 0); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
14 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
15 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
16 export function validateNotification(value) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
17 if (!value || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
18 value.version !== 1 || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
19 !isShortString(value.id, 128) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
20 !TONES.has(value.tone) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
21 !isShortString(value.message, 500) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
22 !ANNOUNCEMENTS.has(value.announcement)) return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
23 if (value.description !== undefined && |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
24 !isShortString(value.description, 1000, true)) return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
25 if (value.persistent !== undefined && |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
26 typeof value.persistent !== "boolean") return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
27 if (value.durationMs !== undefined && |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
28 (!Number.isFinite(value.durationMs) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
29 value.durationMs < MIN_DURATION_MS || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
30 value.durationMs > MAX_DURATION_MS)) return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
31 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
32 let action; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
33 if (value.action !== undefined) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
34 if (!value.action || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
35 !isShortString(value.action.token, 256) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
36 !isShortString(value.action.label, 120)) return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
37 action = Object.freeze({ |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
38 token: value.action.token, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
39 label: value.action.label, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
40 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
41 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
42 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
43 return Object.freeze({ |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
44 version: 1, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
45 id: value.id, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
46 tone: value.tone, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
47 message: value.message, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
48 description: value.description || "", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
49 announcement: value.announcement, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
50 durationMs: value.durationMs ?? DEFAULT_DURATION_MS, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
51 persistent: value.persistent === true, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
52 action, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
53 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
54 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
55 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
56 export function validateNotificationDismiss(value) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
57 if (!value || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
58 value.version !== 1 || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
59 !isShortString(value.id, 128)) return null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
60 return value.id; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
61 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
62 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
63 function makeRecord(notification, announced = false) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
64 return { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
65 ...notification, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
66 announced, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
67 pauseReasons: new Set(), |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
68 remainingMs: notification.durationMs, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
69 startedAt: 0, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
70 timer: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
71 view: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
72 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
73 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
74 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
75 export class ZenNotifications extends HTMLElement { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
76 static version = 1; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
77 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
78 #records = new Map(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
79 #announcementQueues = { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
80 polite: [], |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
81 assertive: [], |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
82 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
83 #announcementTimers = { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
84 polite: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
85 assertive: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
86 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
87 #announcementRunning = { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
88 polite: false, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
89 assertive: false, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
90 }; |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
91 #exitTimers = new Map(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
92 #exitingElements = new Set(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
93 #layoutFrame = null; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
94 #resizeObserver = null; |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
95 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
96 constructor() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
97 super(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
98 this.handleNotify = event => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
99 if (!(event instanceof CustomEvent) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
100 !event.bubbles || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
101 event.composed) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
102 event.stopPropagation(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
103 this.notify(event.detail); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
104 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
105 this.handleDismiss = event => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
106 if (!(event instanceof CustomEvent) || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
107 !event.bubbles || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
108 event.composed) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
109 event.stopPropagation(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
110 const id = validateNotificationDismiss(event.detail); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
111 if (id) this.dismiss(id, "programmatic"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
112 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
113 this.handleVisibility = () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
114 for (const record of this.#records.values()) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
115 if (document.hidden) this.pause(record, "hidden"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
116 else this.resume(record, "hidden"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
117 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
118 }; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
119 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
120 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
121 connectedCallback() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
122 if (!this.stack) this.createInternalElements(); |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
123 if (!this.#resizeObserver && |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
124 typeof ResizeObserver === "function") { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
125 this.#resizeObserver = new ResizeObserver(() => { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
126 this.requestLayout(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
127 }); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
128 } |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
129 this.addEventListener("zen-notify", this.handleNotify); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
130 this.addEventListener( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
131 "zen-dismiss-notification", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
132 this.handleDismiss, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
133 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
134 document.addEventListener( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
135 "visibilitychange", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
136 this.handleVisibility, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
137 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
138 this.render(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
139 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
140 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
141 disconnectedCallback() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
142 this.removeEventListener("zen-notify", this.handleNotify); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
143 this.removeEventListener( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
144 "zen-dismiss-notification", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
145 this.handleDismiss, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
146 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
147 document.removeEventListener( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
148 "visibilitychange", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
149 this.handleVisibility, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
150 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
151 for (const record of this.#records.values()) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
152 this.clearTimer(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
153 } |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
154 cancelAnimationFrame(this.#layoutFrame); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
155 this.#layoutFrame = null; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
156 this.#resizeObserver?.disconnect(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
157 for (const timer of this.#exitTimers.values()) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
158 clearTimeout(timer); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
159 } |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
160 this.#exitTimers.clear(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
161 this.#exitingElements.clear(); |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
162 this.#records.clear(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
163 for (const priority of ["polite", "assertive"]) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
164 clearTimeout(this.#announcementTimers[priority]); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
165 this.#announcementTimers[priority] = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
166 this.#announcementQueues[priority].length = 0; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
167 this.#announcementRunning[priority] = false; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
168 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
169 for (const element of this.querySelectorAll( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
170 ":scope > [data-zen-notifications-internal]", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
171 )) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
172 element.remove(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
173 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
174 this.stack = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
175 this.politeRegion = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
176 this.assertiveRegion = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
177 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
178 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
179 createInternalElements() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
180 const label = this.getAttribute("aria-label") || "Notifications"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
181 this.stack = document.createElement("section"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
182 this.stack.dataset.zenNotificationStack = ""; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
183 this.stack.dataset.zenNotificationsInternal = ""; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
184 this.stack.setAttribute("role", "region"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
185 this.stack.setAttribute("aria-label", label); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
186 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
187 this.politeRegion = document.createElement("span"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
188 this.politeRegion.dataset.zenLive = "polite"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
189 this.politeRegion.dataset.zenNotificationsInternal = ""; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
190 this.politeRegion.className = "zen-visually-hidden"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
191 this.politeRegion.setAttribute("aria-live", "polite"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
192 this.politeRegion.setAttribute("aria-atomic", "true"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
193 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
194 this.assertiveRegion = document.createElement("span"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
195 this.assertiveRegion.dataset.zenLive = "assertive"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
196 this.assertiveRegion.dataset.zenNotificationsInternal = ""; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
197 this.assertiveRegion.className = "zen-visually-hidden"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
198 this.assertiveRegion.setAttribute("aria-live", "assertive"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
199 this.assertiveRegion.setAttribute("aria-atomic", "true"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
200 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
201 this.append( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
202 this.stack, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
203 this.politeRegion, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
204 this.assertiveRegion, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
205 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
206 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
207 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
208 get size() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
209 return this.#records.size; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
210 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
211 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
212 get visibleCount() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
213 return Math.min(this.#records.size, MAX_VISIBLE); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
214 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
215 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
216 notify(value) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
217 const notification = validateNotification(value); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
218 if (!notification) return false; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
219 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
220 const previous = this.#records.get(notification.id); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
221 if (!previous && this.#records.size >= MAX_RECORDS) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
222 const oldestFinite = [...this.#records.values()].find( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
223 record => !record.persistent, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
224 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
225 if (!oldestFinite) return false; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
226 this.removeRecord(oldestFinite.id, "overflow", false); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
227 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
228 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
229 let record; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
230 if (previous) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
231 this.clearTimer(previous); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
232 Object.assign(previous, notification); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
233 previous.remainingMs = notification.durationMs; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
234 this.#records.delete(notification.id); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
235 record = previous; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
236 } else { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
237 record = makeRecord(notification); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
238 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
239 this.#records.set(notification.id, record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
240 this.render(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
241 return true; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
242 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
243 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
244 dismiss(id, reason = "dismissed") { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
245 if (!isShortString(id, 128) || !this.#records.has(id)) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
246 return false; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
247 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
248 this.removeRecord(id, reason, true); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
249 return true; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
250 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
251 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
252 removeRecord(id, reason, render) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
253 const record = this.#records.get(id); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
254 if (!record) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
255 this.clearTimer(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
256 this.#records.delete(id); |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
257 const article = record.view?.article; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
258 if (article && render && article.isConnected) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
259 article.dataset.removing = "true"; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
260 this.#exitingElements.add(article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
261 const exitTimer = setTimeout(() => { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
262 this.#resizeObserver?.unobserve(article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
263 article.remove(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
264 this.#exitingElements.delete(article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
265 this.#exitTimers.delete(article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
266 this.requestLayout(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
267 }, 180); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
268 this.#exitTimers.set(article, exitTimer); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
269 } else if (article) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
270 this.#resizeObserver?.unobserve(article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
271 article.remove(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
272 } |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
273 record.view = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
274 this.dispatchEvent(new CustomEvent("zen-notification-removed", { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
275 bubbles: true, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
276 composed: false, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
277 detail: Object.freeze({ |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
278 version: 1, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
279 id, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
280 reason, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
281 }), |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
282 })); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
283 if (render) this.render(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
284 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
285 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
286 clearTimer(record) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
287 if (!record.timer) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
288 clearTimeout(record.timer); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
289 record.remainingMs = Math.max( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
290 0, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
291 record.remainingMs - (performance.now() - record.startedAt), |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
292 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
293 record.timer = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
294 record.startedAt = 0; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
295 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
296 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
297 pause(record, reason) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
298 if (record.persistent || record.pauseReasons.has(reason)) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
299 record.pauseReasons.add(reason); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
300 this.clearTimer(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
301 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
302 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
303 resume(record, reason) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
304 if (record.persistent) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
305 record.pauseReasons.delete(reason); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
306 this.schedule(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
307 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
308 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
309 schedule(record) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
310 if (record.persistent || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
311 record.timer || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
312 record.pauseReasons.size > 0 || |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
313 !this.#records.has(record.id)) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
314 if (record.remainingMs <= 0) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
315 this.dismiss(record.id, "timeout"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
316 return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
317 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
318 record.startedAt = performance.now(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
319 record.timer = setTimeout(() => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
320 record.timer = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
321 record.remainingMs = 0; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
322 this.dismiss(record.id, "timeout"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
323 }, record.remainingMs); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
324 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
325 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
326 announce(record) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
327 if (record.announced) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
328 record.announced = true; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
329 if (record.announcement === "none") return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
330 this.#announcementQueues[record.announcement].push({ |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
331 id: record.id, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
332 message: record.message, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
333 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
334 this.runAnnouncementQueue(record.announcement); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
335 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
336 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
337 runAnnouncementQueue(priority) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
338 if (this.#announcementRunning[priority]) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
339 const next = this.#announcementQueues[priority].shift(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
340 if (!next) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
341 this.#announcementRunning[priority] = true; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
342 const region = priority === "assertive" |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
343 ? this.assertiveRegion |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
344 : this.politeRegion; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
345 region.textContent = ""; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
346 this.#announcementTimers[priority] = setTimeout(() => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
347 if (this.isConnected && this.#records.has(next.id)) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
348 region.textContent = next.message; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
349 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
350 this.#announcementTimers[priority] = setTimeout(() => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
351 this.#announcementTimers[priority] = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
352 this.#announcementRunning[priority] = false; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
353 this.runAnnouncementQueue(priority); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
354 }, 120); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
355 }, 16); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
356 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
357 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
358 createNotificationElement(record) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
359 if (!record.view) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
360 const article = document.createElement("article"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
361 article.dataset.zenNotificationId = record.id; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
362 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
363 const tone = document.createElement("span"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
364 tone.className = "zen-notification-tone"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
365 tone.setAttribute("aria-hidden", "true"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
366 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
367 const content = document.createElement("div"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
368 content.className = "zen-notification-content"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
369 const message = document.createElement("p"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
370 message.className = "zen-notification-message"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
371 content.append(message); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
372 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
373 const dismiss = document.createElement("button"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
374 dismiss.type = "button"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
375 dismiss.className = "zen-notification-dismiss"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
376 dismiss.textContent = "×"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
377 dismiss.addEventListener("click", () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
378 this.dismiss(record.id, "dismissed"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
379 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
380 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
381 article.addEventListener("pointerenter", () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
382 this.pause(record, "hover"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
383 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
384 article.addEventListener("pointerleave", () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
385 this.resume(record, "hover"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
386 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
387 article.addEventListener("focusin", () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
388 this.pause(record, "focus"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
389 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
390 article.addEventListener("focusout", event => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
391 if (!article.contains(event.relatedTarget)) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
392 this.resume(record, "focus"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
393 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
394 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
395 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
396 article.append(tone, content, dismiss); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
397 record.view = { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
398 action: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
399 article, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
400 content, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
401 description: null, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
402 dismiss, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
403 message, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
404 tone, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
405 }; |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
406 this.#resizeObserver?.observe(article); |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
407 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
408 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
409 const view = record.view; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
410 view.article.dataset.tone = record.tone; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
411 view.tone.textContent = { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
412 info: "i", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
413 success: "✓", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
414 warning: "!", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
415 error: "×", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
416 }[record.tone]; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
417 view.message.textContent = record.message; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
418 view.dismiss.setAttribute( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
419 "aria-label", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
420 this.getAttribute("dismiss-label") || "Dismiss notification", |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
421 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
422 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
423 if (record.description) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
424 if (!view.description) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
425 view.description = document.createElement("p"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
426 view.description.className = "zen-notification-description"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
427 view.content.insertBefore( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
428 view.description, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
429 view.action, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
430 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
431 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
432 view.description.textContent = record.description; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
433 } else if (view.description) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
434 view.description.remove(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
435 view.description = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
436 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
437 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
438 if (record.action) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
439 if (!view.action) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
440 view.action = document.createElement("button"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
441 view.action.type = "button"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
442 view.action.className = "zen-notification-action"; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
443 view.action.addEventListener("click", () => { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
444 this.dispatchEvent(new CustomEvent("zen-notification-action", { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
445 bubbles: true, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
446 composed: false, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
447 detail: Object.freeze({ |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
448 version: 1, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
449 id: record.id, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
450 token: record.action.token, |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
451 }), |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
452 })); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
453 }); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
454 view.content.append(view.action); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
455 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
456 view.action.textContent = record.action.label; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
457 } else if (view.action) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
458 view.action.remove(); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
459 view.action = null; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
460 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
461 return view.article; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
462 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
463 |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
464 requestLayout() { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
465 cancelAnimationFrame(this.#layoutFrame); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
466 this.#layoutFrame = requestAnimationFrame(() => { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
467 this.#layoutFrame = null; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
468 this.layoutStack(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
469 }); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
470 } |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
471 |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
472 layoutStack() { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
473 if (!this.stack) return; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
474 const visible = [...this.#records.values()] |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
475 .slice(-MAX_VISIBLE) |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
476 .filter(record => record.view?.article.isConnected); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
477 const styles = getComputedStyle(this.stack); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
478 const configuredGap = Number.parseFloat( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
479 styles.getPropertyValue("--zen-notification-gap"), |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
480 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
481 const gap = Number.isFinite(configuredGap) |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
482 ? configuredGap |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
483 : 12; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
484 let expandedHeight = 0; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
485 let newestHeight = 0; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
486 |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
487 for (let index = visible.length - 1; index >= 0; index--) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
488 const record = visible[index]; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
489 const article = record.view.article; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
490 const depth = visible.length - 1 - index; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
491 const height = article.offsetHeight; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
492 if (depth === 0) newestHeight = height; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
493 article.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
494 "--zen-notification-depth", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
495 String(depth), |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
496 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
497 article.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
498 "--zen-notification-collapsed-offset", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
499 `${-depth * 10}px`, |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
500 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
501 article.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
502 "--zen-notification-collapsed-opacity", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
503 String(Math.max(0.55, 1 - depth * 0.18)), |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
504 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
505 article.dataset.depth = String(depth); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
506 article.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
507 "--zen-notification-offset", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
508 `${-expandedHeight}px`, |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
509 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
510 article.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
511 "--zen-notification-scale", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
512 String(Math.max(0.88, 1 - depth * 0.055)), |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
513 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
514 article.style.zIndex = String(visible.length - depth); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
515 expandedHeight += height + gap; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
516 } |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
517 |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
518 this.stack.dataset.count = String(visible.length); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
519 this.stack.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
520 "--zen-notification-collapsed-height", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
521 `${newestHeight + Math.max(0, visible.length - 1) * 10}px`, |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
522 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
523 this.stack.style.setProperty( |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
524 "--zen-notification-expanded-height", |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
525 `${Math.max(0, expandedHeight - gap)}px`, |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
526 ); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
527 } |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
528 |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
529 render() { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
530 if (!this.stack) return; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
531 const records = [...this.#records.values()]; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
532 const visible = records.slice(-MAX_VISIBLE); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
533 const visibleIds = new Set(visible.map(record => record.id)); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
534 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
535 for (const record of records) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
536 if (visibleIds.has(record.id)) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
537 this.resume(record, "queued"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
538 } else { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
539 this.pause(record, "queued"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
540 this.resume(record, "hover"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
541 this.resume(record, "focus"); |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
542 if (record.view?.article) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
543 this.#resizeObserver?.unobserve(record.view.article); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
544 record.view.article.remove(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
545 } |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
546 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
547 if (document.hidden) this.pause(record, "hidden"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
548 else this.resume(record, "hidden"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
549 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
550 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
551 const elements = visible.map(record => |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
552 this.createNotificationElement(record) |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
553 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
554 for (let index = 0; index < elements.length; index++) { |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
555 const current = [...this.stack.children] |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
556 .filter(element => !this.#exitingElements.has(element))[index]; |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
557 if (current !== elements[index]) { |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
558 this.stack.insertBefore( |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
559 elements[index], |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
560 current || null, |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
561 ); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
562 } |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
563 this.#resizeObserver?.observe(elements[index]); |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
564 } |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
565 const desired = new Set(elements); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
566 for (const element of [...this.stack.children]) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
567 if (!desired.has(element) && |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
568 !this.#exitingElements.has(element)) { |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
569 element.remove(); |
|
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
570 } |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
571 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
572 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
573 for (const record of visible) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
574 const element = record.view.article; |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
575 if (element.matches(":hover")) this.pause(record, "hover"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
576 else this.resume(record, "hover"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
577 if (element.contains(document.activeElement)) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
578 this.pause(record, "focus"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
579 } else this.resume(record, "focus"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
580 this.announce(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
581 this.schedule(record); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
582 } |
|
253
fdf3816959cb
[ui] Add Sonner-like notification stack
MrJuneJune <me@mrjunejune.com>
parents:
252
diff
changeset
|
583 this.requestLayout(); |
|
252
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
584 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
585 } |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
586 |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
587 const existing = customElements.get("zen-notifications"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
588 if (!existing) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
589 customElements.define("zen-notifications", ZenNotifications); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
590 } else if (existing.version !== ZenNotifications.version) { |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
591 throw new Error("Incompatible zen-notifications component"); |
|
7a7581f040e8
[ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff
changeset
|
592 } |