annotate design_system/src/components/notifications.js @ 252:7a7581f040e8

[ui] Add scoped notification component Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Tue, 04 Aug 2026 11:57:16 -0700
parents
children fdf3816959cb
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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 };
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
91
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
92 constructor() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
93 super();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
94 this.handleNotify = event => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
95 if (!(event instanceof CustomEvent) ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
96 !event.bubbles ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
97 event.composed) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
98 event.stopPropagation();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
99 this.notify(event.detail);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
100 };
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
101 this.handleDismiss = event => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
102 if (!(event instanceof CustomEvent) ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
103 !event.bubbles ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
104 event.composed) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
105 event.stopPropagation();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
106 const id = validateNotificationDismiss(event.detail);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
107 if (id) this.dismiss(id, "programmatic");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
108 };
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
109 this.handleVisibility = () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
110 for (const record of this.#records.values()) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
111 if (document.hidden) this.pause(record, "hidden");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
112 else this.resume(record, "hidden");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
113 }
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 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
116
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
117 connectedCallback() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
118 if (!this.stack) this.createInternalElements();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
119 this.addEventListener("zen-notify", this.handleNotify);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
120 this.addEventListener(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
121 "zen-dismiss-notification",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
122 this.handleDismiss,
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 document.addEventListener(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
125 "visibilitychange",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
126 this.handleVisibility,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
127 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
128 this.render();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
129 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
130
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
131 disconnectedCallback() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
132 this.removeEventListener("zen-notify", this.handleNotify);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
133 this.removeEventListener(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
134 "zen-dismiss-notification",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
135 this.handleDismiss,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
136 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
137 document.removeEventListener(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
138 "visibilitychange",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
139 this.handleVisibility,
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 for (const record of this.#records.values()) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
142 this.clearTimer(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
143 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
144 this.#records.clear();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
145 for (const priority of ["polite", "assertive"]) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
146 clearTimeout(this.#announcementTimers[priority]);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
147 this.#announcementTimers[priority] = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
148 this.#announcementQueues[priority].length = 0;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
149 this.#announcementRunning[priority] = false;
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 element of this.querySelectorAll(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
152 ":scope > [data-zen-notifications-internal]",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
153 )) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
154 element.remove();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
155 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
156 this.stack = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
157 this.politeRegion = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
158 this.assertiveRegion = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
159 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
160
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
161 createInternalElements() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
162 const label = this.getAttribute("aria-label") || "Notifications";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
163 this.stack = document.createElement("section");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
164 this.stack.dataset.zenNotificationStack = "";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
165 this.stack.dataset.zenNotificationsInternal = "";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
166 this.stack.setAttribute("role", "region");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
167 this.stack.setAttribute("aria-label", label);
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 this.politeRegion = document.createElement("span");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
170 this.politeRegion.dataset.zenLive = "polite";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
171 this.politeRegion.dataset.zenNotificationsInternal = "";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
172 this.politeRegion.className = "zen-visually-hidden";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
173 this.politeRegion.setAttribute("aria-live", "polite");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
174 this.politeRegion.setAttribute("aria-atomic", "true");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
175
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
176 this.assertiveRegion = document.createElement("span");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
177 this.assertiveRegion.dataset.zenLive = "assertive";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
178 this.assertiveRegion.dataset.zenNotificationsInternal = "";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
179 this.assertiveRegion.className = "zen-visually-hidden";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
180 this.assertiveRegion.setAttribute("aria-live", "assertive");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
181 this.assertiveRegion.setAttribute("aria-atomic", "true");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
182
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
183 this.append(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
184 this.stack,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
185 this.politeRegion,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
186 this.assertiveRegion,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
187 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
188 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
189
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
190 get size() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
191 return this.#records.size;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
192 }
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 get visibleCount() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
195 return Math.min(this.#records.size, MAX_VISIBLE);
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
198 notify(value) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
199 const notification = validateNotification(value);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
200 if (!notification) return false;
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 const previous = this.#records.get(notification.id);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
203 if (!previous && this.#records.size >= MAX_RECORDS) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
204 const oldestFinite = [...this.#records.values()].find(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
205 record => !record.persistent,
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 if (!oldestFinite) return false;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
208 this.removeRecord(oldestFinite.id, "overflow", false);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
209 }
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 let record;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
212 if (previous) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
213 this.clearTimer(previous);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
214 Object.assign(previous, notification);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
215 previous.remainingMs = notification.durationMs;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
216 this.#records.delete(notification.id);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
217 record = previous;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
218 } else {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
219 record = makeRecord(notification);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
220 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
221 this.#records.set(notification.id, record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
222 this.render();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
223 return true;
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
226 dismiss(id, reason = "dismissed") {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
227 if (!isShortString(id, 128) || !this.#records.has(id)) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
228 return false;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
229 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
230 this.removeRecord(id, reason, true);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
231 return true;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
232 }
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 removeRecord(id, reason, render) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
235 const record = this.#records.get(id);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
236 if (!record) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
237 this.clearTimer(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
238 this.#records.delete(id);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
239 record.view?.article.remove();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
240 record.view = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
241 this.dispatchEvent(new CustomEvent("zen-notification-removed", {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
242 bubbles: true,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
243 composed: false,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
244 detail: Object.freeze({
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
245 version: 1,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
246 id,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
247 reason,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
248 }),
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
249 }));
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
250 if (render) this.render();
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
253 clearTimer(record) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
254 if (!record.timer) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
255 clearTimeout(record.timer);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
256 record.remainingMs = Math.max(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
257 0,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
258 record.remainingMs - (performance.now() - record.startedAt),
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
259 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
260 record.timer = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
261 record.startedAt = 0;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
262 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
263
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
264 pause(record, reason) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
265 if (record.persistent || record.pauseReasons.has(reason)) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
266 record.pauseReasons.add(reason);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
267 this.clearTimer(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
268 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
269
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
270 resume(record, reason) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
271 if (record.persistent) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
272 record.pauseReasons.delete(reason);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
273 this.schedule(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
274 }
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 schedule(record) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
277 if (record.persistent ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
278 record.timer ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
279 record.pauseReasons.size > 0 ||
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
280 !this.#records.has(record.id)) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
281 if (record.remainingMs <= 0) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
282 this.dismiss(record.id, "timeout");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
283 return;
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 record.startedAt = performance.now();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
286 record.timer = setTimeout(() => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
287 record.timer = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
288 record.remainingMs = 0;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
289 this.dismiss(record.id, "timeout");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
290 }, record.remainingMs);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
291 }
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 announce(record) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
294 if (record.announced) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
295 record.announced = true;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
296 if (record.announcement === "none") return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
297 this.#announcementQueues[record.announcement].push({
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
298 id: record.id,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
299 message: record.message,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
300 });
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
301 this.runAnnouncementQueue(record.announcement);
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
304 runAnnouncementQueue(priority) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
305 if (this.#announcementRunning[priority]) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
306 const next = this.#announcementQueues[priority].shift();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
307 if (!next) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
308 this.#announcementRunning[priority] = true;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
309 const region = priority === "assertive"
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
310 ? this.assertiveRegion
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
311 : this.politeRegion;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
312 region.textContent = "";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
313 this.#announcementTimers[priority] = setTimeout(() => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
314 if (this.isConnected && this.#records.has(next.id)) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
315 region.textContent = next.message;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
316 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
317 this.#announcementTimers[priority] = setTimeout(() => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
318 this.#announcementTimers[priority] = null;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
319 this.#announcementRunning[priority] = false;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
320 this.runAnnouncementQueue(priority);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
321 }, 120);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
322 }, 16);
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 createNotificationElement(record) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
326 if (!record.view) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
327 const article = document.createElement("article");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
328 article.dataset.zenNotificationId = record.id;
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 const tone = document.createElement("span");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
331 tone.className = "zen-notification-tone";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
332 tone.setAttribute("aria-hidden", "true");
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 const content = document.createElement("div");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
335 content.className = "zen-notification-content";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
336 const message = document.createElement("p");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
337 message.className = "zen-notification-message";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
338 content.append(message);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
339
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
340 const dismiss = document.createElement("button");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
341 dismiss.type = "button";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
342 dismiss.className = "zen-notification-dismiss";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
343 dismiss.textContent = "×";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
344 dismiss.addEventListener("click", () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
345 this.dismiss(record.id, "dismissed");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
346 });
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
347
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
348 article.addEventListener("pointerenter", () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
349 this.pause(record, "hover");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
350 });
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
351 article.addEventListener("pointerleave", () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
352 this.resume(record, "hover");
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 article.addEventListener("focusin", () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
355 this.pause(record, "focus");
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 article.addEventListener("focusout", event => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
358 if (!article.contains(event.relatedTarget)) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
359 this.resume(record, "focus");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
360 }
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
363 article.append(tone, content, dismiss);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
364 record.view = {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
365 action: null,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
366 article,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
367 content,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
368 description: null,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
369 dismiss,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
370 message,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
371 tone,
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 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
374
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
375 const view = record.view;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
376 view.article.dataset.tone = record.tone;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
377 view.tone.textContent = {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
378 info: "i",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
379 success: "✓",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
380 warning: "!",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
381 error: "×",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
382 }[record.tone];
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
383 view.message.textContent = record.message;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
384 view.dismiss.setAttribute(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
385 "aria-label",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
386 this.getAttribute("dismiss-label") || "Dismiss notification",
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
387 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
388
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
389 if (record.description) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
390 if (!view.description) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
391 view.description = document.createElement("p");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
392 view.description.className = "zen-notification-description";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
393 view.content.insertBefore(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
394 view.description,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
395 view.action,
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 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
398 view.description.textContent = record.description;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
399 } else if (view.description) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
400 view.description.remove();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
401 view.description = null;
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
404 if (record.action) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
405 if (!view.action) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
406 view.action = document.createElement("button");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
407 view.action.type = "button";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
408 view.action.className = "zen-notification-action";
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
409 view.action.addEventListener("click", () => {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
410 this.dispatchEvent(new CustomEvent("zen-notification-action", {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
411 bubbles: true,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
412 composed: false,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
413 detail: Object.freeze({
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
414 version: 1,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
415 id: record.id,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
416 token: record.action.token,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
417 }),
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 });
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
420 view.content.append(view.action);
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 view.action.textContent = record.action.label;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
423 } else if (view.action) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
424 view.action.remove();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
425 view.action = null;
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 return view.article;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
428 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
429
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
430 render() {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
431 if (!this.stack) return;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
432 const records = [...this.#records.values()];
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
433 const visible = records.slice(-MAX_VISIBLE);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
434 const visibleIds = new Set(visible.map(record => record.id));
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
435
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
436 for (const record of records) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
437 if (visibleIds.has(record.id)) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
438 this.resume(record, "queued");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
439 } else {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
440 this.pause(record, "queued");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
441 this.resume(record, "hover");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
442 this.resume(record, "focus");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
443 record.view?.article.remove();
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
444 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
445 if (document.hidden) this.pause(record, "hidden");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
446 else this.resume(record, "hidden");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
447 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
448
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
449 const elements = visible.map(record =>
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
450 this.createNotificationElement(record)
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 for (let index = 0; index < elements.length; index++) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
453 if (this.stack.children[index] !== elements[index]) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
454 this.stack.insertBefore(
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
455 elements[index],
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
456 this.stack.children[index] || null,
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
457 );
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
458 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
459 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
460 while (this.stack.children.length > elements.length) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
461 this.stack.lastElementChild.remove();
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
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
464 for (const record of visible) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
465 const element = record.view.article;
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
466 if (element.matches(":hover")) this.pause(record, "hover");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
467 else this.resume(record, "hover");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
468 if (element.contains(document.activeElement)) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
469 this.pause(record, "focus");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
470 } else this.resume(record, "focus");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
471 this.announce(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
472 this.schedule(record);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
473 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
474 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
475 }
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
476
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
477 const existing = customElements.get("zen-notifications");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
478 if (!existing) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
479 customElements.define("zen-notifications", ZenNotifications);
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
480 } else if (existing.version !== ZenNotifications.version) {
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
481 throw new Error("Incompatible zen-notifications component");
7a7581f040e8 [ui] Add scoped notification component
MrJuneJune <me@mrjunejune.com>
parents:
diff changeset
482 }