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