@trycourier/courier-ui-toast 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1080 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { CourierColors, CourierIconSVGs, CourierThemeManager, registerElement, CourierBaseElement, CourierIcon, CourierButton, injectGlobalStyle } from "@trycourier/courier-ui-core";
5
+ import { Courier, InboxMessageEvent } from "@trycourier/courier-js";
6
+ import { Courier as Courier2 } from "@trycourier/courier-js";
7
+ const defaultLightTheme = {
8
+ item: {
9
+ backgroundColor: CourierColors.white[500],
10
+ hoverBackgroundColor: CourierColors.gray[200],
11
+ activeBackgroundColor: CourierColors.gray[500],
12
+ shadow: `0px 4px 8px -2px ${CourierColors.black[50020]}`,
13
+ border: `1px solid ${CourierColors.gray[500]}`,
14
+ borderRadius: "8px",
15
+ title: {
16
+ size: "11pt",
17
+ weight: "400",
18
+ color: CourierColors.black[500]
19
+ },
20
+ body: {
21
+ size: "11pt",
22
+ weight: "400",
23
+ color: CourierColors.gray[600]
24
+ },
25
+ icon: {
26
+ color: CourierColors.black[500],
27
+ svg: CourierIconSVGs.inbox
28
+ },
29
+ dismissIcon: {
30
+ color: CourierColors.black[500],
31
+ svg: CourierIconSVGs.remove
32
+ },
33
+ autoDismissBarColor: CourierColors.blue[400],
34
+ actions: {
35
+ backgroundColor: "transparent",
36
+ hoverBackgroundColor: CourierColors.gray[200],
37
+ activeBackgroundColor: CourierColors.gray[500],
38
+ border: `1px solid ${CourierColors.gray[500]}`,
39
+ borderRadius: "4px",
40
+ shadow: "0px 1px 2px 0px rgba(0, 0, 0, 0.06)",
41
+ font: {
42
+ color: CourierColors.black[500],
43
+ size: "14px"
44
+ }
45
+ }
46
+ }
47
+ };
48
+ const defaultDarkTheme = {
49
+ item: {
50
+ backgroundColor: CourierColors.black[500],
51
+ hoverBackgroundColor: CourierColors.gray[400],
52
+ activeBackgroundColor: CourierColors.gray[600],
53
+ shadow: `0px 4px 8px -2px ${CourierColors.gray[400]}`,
54
+ border: `1px solid ${CourierColors.black[500]}`,
55
+ borderRadius: "8px",
56
+ title: {
57
+ size: "11pt",
58
+ weight: "400",
59
+ color: CourierColors.white[500]
60
+ },
61
+ body: {
62
+ size: "11pt",
63
+ weight: "400",
64
+ color: CourierColors.gray[500]
65
+ },
66
+ icon: {
67
+ color: CourierColors.white[500],
68
+ svg: CourierIconSVGs.inbox
69
+ },
70
+ dismissIcon: {
71
+ color: CourierColors.white[500],
72
+ svg: CourierIconSVGs.remove
73
+ },
74
+ autoDismissBarColor: CourierColors.blue[400],
75
+ actions: {
76
+ backgroundColor: CourierColors.black[400],
77
+ hoverBackgroundColor: CourierColors.white[50010],
78
+ activeBackgroundColor: CourierColors.white[50020],
79
+ border: `1px solid ${CourierColors.gray[400]}`,
80
+ borderRadius: "4px",
81
+ shadow: `0px 1px 2px 0px ${CourierColors.white[50010]}`,
82
+ font: {
83
+ color: CourierColors.white[500],
84
+ size: "14px"
85
+ }
86
+ }
87
+ }
88
+ };
89
+ const mergeTheme = (mode, theme) => {
90
+ var _a, _b, _c, _d, _e, _f, _g, _h;
91
+ const defaultTheme = mode === "light" ? defaultLightTheme : defaultDarkTheme;
92
+ return {
93
+ item: {
94
+ ...defaultTheme.item,
95
+ ...theme.item,
96
+ title: {
97
+ ...(_a = defaultTheme.item) == null ? void 0 : _a.title,
98
+ ...(_b = theme.item) == null ? void 0 : _b.title
99
+ },
100
+ body: {
101
+ ...(_c = defaultTheme.item) == null ? void 0 : _c.body,
102
+ ...(_d = theme.item) == null ? void 0 : _d.body
103
+ },
104
+ icon: {
105
+ ...(_e = defaultTheme.item) == null ? void 0 : _e.icon,
106
+ ...(_f = theme.item) == null ? void 0 : _f.icon
107
+ },
108
+ dismissIcon: {
109
+ ...(_g = defaultTheme.item) == null ? void 0 : _g.dismissIcon,
110
+ ...(_h = theme.item) == null ? void 0 : _h.dismissIcon
111
+ }
112
+ }
113
+ };
114
+ };
115
+ class CourierToastThemeManager extends CourierThemeManager {
116
+ constructor(initialTheme) {
117
+ super(initialTheme);
118
+ // Event ID for toast theme changes
119
+ __publicField(this, "THEME_CHANGE_EVENT", "courier_toast_theme_change");
120
+ }
121
+ /**
122
+ * Get the default light theme for toast
123
+ */
124
+ getDefaultLightTheme() {
125
+ return defaultLightTheme;
126
+ }
127
+ /**
128
+ * Get the default dark theme for toast
129
+ */
130
+ getDefaultDarkTheme() {
131
+ return defaultDarkTheme;
132
+ }
133
+ /**
134
+ * Merge the toast theme with defaults
135
+ */
136
+ mergeTheme(mode, theme) {
137
+ return mergeTheme(mode, theme);
138
+ }
139
+ /**
140
+ * Subscribe to toast theme changes
141
+ * @param callback - Function to run when the theme changes
142
+ * @returns Object with unsubscribe method to stop listening
143
+ */
144
+ subscribe(callback) {
145
+ const baseSubscription = super.subscribe(callback);
146
+ return {
147
+ ...baseSubscription,
148
+ manager: this
149
+ };
150
+ }
151
+ }
152
+ const _CourierToastItem = class _CourierToastItem extends CourierBaseElement {
153
+ constructor(props) {
154
+ super();
155
+ __publicField(this, "_themeManager");
156
+ __publicField(this, "_themeSubscription");
157
+ __publicField(this, "_message");
158
+ __publicField(this, "_customToastItemContent");
159
+ /** Whether this toast item is auto-dismissed. */
160
+ __publicField(this, "_autoDismiss");
161
+ /** The timeout before the toast item is auto-dismissed, applicable if _autoDismiss is true. */
162
+ __publicField(this, "_autoDismissTimeoutMs");
163
+ // Callbacks
164
+ __publicField(this, "_onItemDismissedCallback", null);
165
+ __publicField(this, "_onToastItemClickCallback", null);
166
+ __publicField(this, "_onToastItemActionClickCallback", null);
167
+ this._message = props.message;
168
+ this._autoDismiss = props.autoDismiss;
169
+ this._autoDismissTimeoutMs = props.autoDismissTimeoutMs;
170
+ this._themeManager = props.themeManager;
171
+ this._themeSubscription = this._themeManager.subscribe((_) => {
172
+ this.render();
173
+ });
174
+ }
175
+ /**
176
+ * Registers a handler called when the item is dismissed.
177
+ *
178
+ * @param handler - A function to be called when the item is dismissed.
179
+ */
180
+ onItemDismissed(handler) {
181
+ this._onItemDismissedCallback = handler;
182
+ }
183
+ /**
184
+ * Registers a handler for item click events.
185
+ *
186
+ * @param handler - A function to be called when the item is clicked.
187
+ */
188
+ onToastItemClick(handler) {
189
+ this._onToastItemClickCallback = handler;
190
+ this.render();
191
+ }
192
+ /**
193
+ * Registers a handler for item click events.
194
+ *
195
+ * @param handler - A function to be called when the item is clicked.
196
+ */
197
+ onToastItemActionClick(handler) {
198
+ this._onToastItemActionClickCallback = handler;
199
+ this.render();
200
+ }
201
+ /**
202
+ * Set a custom content element for the toast item, reusing the {@link CourierToastItem}
203
+ * container, which provides styling, animations, and event handling.
204
+ *
205
+ * @param factory - Function that returns an `HTMLElement` to render as the content.
206
+ */
207
+ setToastItemContent(factory) {
208
+ this._customToastItemContent = factory;
209
+ }
210
+ /**
211
+ * Dismiss the toast item.
212
+ *
213
+ * By default the toast fades out before it's removed. Set `timeoutMs` to
214
+ * `0` to remove the item immediately.
215
+ *
216
+ * @param timeoutMs - the animation duration to fade out the toast item
217
+ */
218
+ dismiss(timeoutMs = _CourierToastItem.dismissAnimationTimeoutMs) {
219
+ this.classList.add("dismissing");
220
+ setTimeout(() => {
221
+ this.remove();
222
+ if (this._onItemDismissedCallback) {
223
+ this._onItemDismissedCallback({ message: this._message });
224
+ }
225
+ }, timeoutMs);
226
+ }
227
+ /** @override */
228
+ onComponentMounted() {
229
+ this.render();
230
+ }
231
+ /** @override */
232
+ onComponentUnmounted() {
233
+ this._themeSubscription.unsubscribe();
234
+ }
235
+ get theme() {
236
+ return this._themeManager.getTheme();
237
+ }
238
+ /**
239
+ * @override
240
+ */
241
+ static get id() {
242
+ return "courier-toast-item";
243
+ }
244
+ /**
245
+ * @override
246
+ */
247
+ static get observedAttributes() {
248
+ return [];
249
+ }
250
+ /**
251
+ * Render a toast item, either with default content or custom content if
252
+ * a content factory function is provided.
253
+ *
254
+ * Note: Styles for the toast item are set in {@link CourierToast} since
255
+ * toasts are ephemeral by nature and we want to avoid adding/removing/duplicating
256
+ * styles as {@link CourierToastItem}s enter/exit.
257
+ */
258
+ render() {
259
+ var _a, _b, _c, _d;
260
+ while (this.firstChild) {
261
+ this.removeChild(this.firstChild);
262
+ }
263
+ this.removeEventListener("click", this.onClick);
264
+ const overflowHiddenContainer = document.createElement("div");
265
+ overflowHiddenContainer.classList.add("overflow-hidden-container");
266
+ this.appendChild(overflowHiddenContainer);
267
+ if (this._autoDismiss) {
268
+ const autoDismiss = document.createElement("div");
269
+ autoDismiss.classList.add("auto-dismiss");
270
+ overflowHiddenContainer.append(autoDismiss);
271
+ }
272
+ if (this._customToastItemContent) {
273
+ overflowHiddenContainer.appendChild(this._customToastItemContent({ message: this._message, autoDismiss: this._autoDismiss, autoDismissTimeoutMs: this._autoDismissTimeoutMs }));
274
+ } else {
275
+ overflowHiddenContainer.appendChild(this.createDefaultContent());
276
+ }
277
+ if (this._onToastItemClickCallback) {
278
+ this.classList.add("clickable");
279
+ }
280
+ this.addEventListener("click", this.onClick);
281
+ const dismiss = new CourierIcon(
282
+ (_b = (_a = this.theme.item) == null ? void 0 : _a.dismissIcon) == null ? void 0 : _b.color,
283
+ (_d = (_c = this.theme.item) == null ? void 0 : _c.dismissIcon) == null ? void 0 : _d.svg
284
+ );
285
+ dismiss.classList.add("dismiss");
286
+ dismiss.addEventListener("click", (event) => {
287
+ event.stopPropagation();
288
+ this.remove();
289
+ if (this._onItemDismissedCallback) {
290
+ this._onItemDismissedCallback({ message: this._message });
291
+ }
292
+ });
293
+ this.appendChild(dismiss);
294
+ }
295
+ /** Create the default content for this item. */
296
+ createDefaultContent() {
297
+ var _a, _b, _c, _d, _e;
298
+ const content = document.createElement("div");
299
+ content.classList.add("content");
300
+ this.append(content);
301
+ const icon = new CourierIcon(
302
+ (_b = (_a = this.theme.item) == null ? void 0 : _a.icon) == null ? void 0 : _b.color,
303
+ (_d = (_c = this.theme.item) == null ? void 0 : _c.icon) == null ? void 0 : _d.svg
304
+ );
305
+ icon.classList.add("icon");
306
+ content.appendChild(icon);
307
+ const textContent = document.createElement("div");
308
+ textContent.classList.add("text-content");
309
+ content.appendChild(textContent);
310
+ const title = document.createElement("p");
311
+ title.classList.add("title");
312
+ title.textContent = this._message.title ?? "";
313
+ textContent.appendChild(title);
314
+ const body = document.createElement("p");
315
+ body.classList.add("body");
316
+ body.textContent = this._message.preview ?? this._message.body ?? "";
317
+ textContent.appendChild(body);
318
+ if (this.messageHasActions) {
319
+ const actionsContainer = document.createElement("div");
320
+ actionsContainer.classList.add("actions-container");
321
+ textContent.appendChild(actionsContainer);
322
+ (_e = this._message.actions) == null ? void 0 : _e.forEach((action) => {
323
+ const button = this.createActionButton(action);
324
+ actionsContainer.appendChild(button);
325
+ });
326
+ }
327
+ return content;
328
+ }
329
+ get messageHasActions() {
330
+ return this._message.actions && this._message.actions.length > 0;
331
+ }
332
+ createActionButton(action) {
333
+ var _a, _b, _c, _d, _e;
334
+ const actionsTheme = (_a = this._themeManager.getTheme().item) == null ? void 0 : _a.actions;
335
+ return new CourierButton({
336
+ mode: this._themeManager.mode,
337
+ text: action.content,
338
+ variant: "secondary",
339
+ backgroundColor: actionsTheme == null ? void 0 : actionsTheme.backgroundColor,
340
+ hoverBackgroundColor: actionsTheme == null ? void 0 : actionsTheme.hoverBackgroundColor,
341
+ activeBackgroundColor: actionsTheme == null ? void 0 : actionsTheme.activeBackgroundColor,
342
+ border: actionsTheme == null ? void 0 : actionsTheme.border,
343
+ borderRadius: actionsTheme == null ? void 0 : actionsTheme.borderRadius,
344
+ shadow: actionsTheme == null ? void 0 : actionsTheme.shadow,
345
+ fontFamily: (_b = actionsTheme == null ? void 0 : actionsTheme.font) == null ? void 0 : _b.family,
346
+ fontSize: (_c = actionsTheme == null ? void 0 : actionsTheme.font) == null ? void 0 : _c.size,
347
+ fontWeight: (_d = actionsTheme == null ? void 0 : actionsTheme.font) == null ? void 0 : _d.weight,
348
+ textColor: (_e = actionsTheme == null ? void 0 : actionsTheme.font) == null ? void 0 : _e.color,
349
+ onClick: () => {
350
+ if (this._onToastItemActionClickCallback) {
351
+ this._onToastItemActionClickCallback({ message: this._message, action });
352
+ }
353
+ }
354
+ });
355
+ }
356
+ onClick(event) {
357
+ event.stopPropagation();
358
+ if (this._onToastItemClickCallback) {
359
+ this._onToastItemClickCallback({ message: this._message });
360
+ }
361
+ }
362
+ };
363
+ /** The animation duration to fade out a dismissed toast before its element is removed. */
364
+ __publicField(_CourierToastItem, "dismissAnimationTimeoutMs", 300);
365
+ let CourierToastItem = _CourierToastItem;
366
+ registerElement(CourierToastItem);
367
+ const _CourierToastDatastore = class _CourierToastDatastore {
368
+ /** Access the shared instance with {@link CourierToastDatastore.shared}. */
369
+ // Prevent instantiation via constructor.
370
+ constructor() {
371
+ /** FIFO stack of toast messages. The end of the array is index 0 of the stack. */
372
+ __publicField(this, "_dataset", []);
373
+ /** Set of listeners whose handlers will be called when the datastore changes. */
374
+ __publicField(this, "_datastoreListeners", []);
375
+ }
376
+ /** The shared instance of CourierToastDatastore, used to access all public methods. */
377
+ static get shared() {
378
+ if (!_CourierToastDatastore.instance) {
379
+ _CourierToastDatastore.instance = new _CourierToastDatastore();
380
+ }
381
+ return _CourierToastDatastore.instance;
382
+ }
383
+ /**
384
+ * Add a listener whose handlers are called when there are changes to the datastore.
385
+ *
386
+ * @param listener - an implementation of {@link CourierToastDatastoreListener}
387
+ */
388
+ addDatastoreListener(listener) {
389
+ this._datastoreListeners.push(listener);
390
+ }
391
+ /**
392
+ * Remove a previously added listener.
393
+ *
394
+ * Note: the `listener` param is matched by object reference, so callers must pass
395
+ * the same object previously passed to {@link CourierToastDatastore.addDatastoreListener}.
396
+ *
397
+ * See also: {@link CourierToastDatastoreListener.remove}
398
+ *
399
+ * @param listener - a previously added listener implementation
400
+ */
401
+ removeDatastoreListener(listener) {
402
+ this._datastoreListeners = this._datastoreListeners.filter((l) => l !== listener);
403
+ }
404
+ /**
405
+ * Start listening for toast messages.
406
+ *
407
+ * Calling this method will open a WebSocket connection to the Courier backend if one
408
+ * is not already open.
409
+ *
410
+ * See also: {@link @trycourier/courier-js#Courier.shared.signIn} and {@link @trycourier/courier-js#Courier.shared.signOut}
411
+ */
412
+ async listenForMessages() {
413
+ var _a, _b, _c, _d, _e, _f, _g;
414
+ try {
415
+ const socketClient = (_a = Courier.shared.client) == null ? void 0 : _a.inbox.socket;
416
+ if (!socketClient) {
417
+ (_c = (_b = Courier.shared.client) == null ? void 0 : _b.options.logger) == null ? void 0 : _c.info("CourierInbox socket not available");
418
+ return;
419
+ }
420
+ socketClient.addMessageEventListener((messageEvent) => {
421
+ if (messageEvent.event === InboxMessageEvent.NewMessage) {
422
+ const message = messageEvent.data;
423
+ this.addMessage(message);
424
+ }
425
+ });
426
+ if (socketClient.isConnecting || socketClient.isOpen) {
427
+ (_f = (_d = Courier.shared.client) == null ? void 0 : _d.options.logger) == null ? void 0 : _f.info(`Inbox socket already connecting or open for client ID: [${(_e = Courier.shared.client) == null ? void 0 : _e.options.connectionId}]`);
428
+ return;
429
+ }
430
+ socketClient.connect();
431
+ } catch (error) {
432
+ (_g = Courier.shared.client) == null ? void 0 : _g.options.logger.error("Error listening for messages:", error);
433
+ this._datastoreListeners.forEach((listener) => {
434
+ var _a2, _b2;
435
+ (_b2 = (_a2 = listener.events).onError) == null ? void 0 : _b2.call(_a2, error);
436
+ });
437
+ }
438
+ }
439
+ /**
440
+ * Find the position of an {@link @trycourier/courier-js#InboxMessage} in the toast stack.
441
+ *
442
+ * Notes:
443
+ * - Since the stack is an array, with the last item being the "top" of the stack,
444
+ * a toast's position in the underlying array is the inverse of its stack position.
445
+ * - `message` is matched by {@link @trycourier/courier-js#InboxMessage.messageId}, not by object reference.
446
+ *
447
+ * @param message - the {@link @trycourier/courier-js#InboxMessage} to find in the stack
448
+ */
449
+ toastIndexOfMessage(message) {
450
+ const position = this._dataset.findIndex((m) => m.messageId === message.messageId);
451
+ if (position < 0) {
452
+ return position;
453
+ }
454
+ return this._dataset.length - position - 1;
455
+ }
456
+ /**
457
+ * Add an {@link @trycourier/courier-js#InboxMessage} toast item to the datastore.
458
+ *
459
+ * Calling this directly is useful to send test messages while developing with the Courier SDK.</p>
460
+ *
461
+ * @example
462
+ * ```
463
+ * CourierToastDatastore.shared.addMessage({
464
+ * title: 'Lorem ipsum dolor sit',
465
+ * body: 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet',
466
+ * messageId: '1'
467
+ * });
468
+ * ```
469
+ *
470
+ * @param message - the message to add as a toast item.
471
+ */
472
+ addMessage(message) {
473
+ this._dataset.push(message);
474
+ this._datastoreListeners.forEach((listener) => {
475
+ if (listener.events.onMessageAdd) {
476
+ listener.events.onMessageAdd(message);
477
+ }
478
+ });
479
+ }
480
+ /**
481
+ * Remove an {@link @trycourier/courier-js#InboxMessage} from the datastore.
482
+ *
483
+ * Note: `message` is matched by {@link @trycourier/courier-js#InboxMessage.messageId}, not by object reference
484
+ */
485
+ removeMessage(message) {
486
+ const index = this._dataset.findIndex((m) => m.messageId === message.messageId);
487
+ if (index < 0) {
488
+ return;
489
+ }
490
+ this._dataset.splice(
491
+ index,
492
+ /* deleteCount */
493
+ 1
494
+ );
495
+ this._datastoreListeners.forEach((listener) => {
496
+ if (listener.events.onMessageRemove) {
497
+ listener.events.onMessageRemove(message);
498
+ }
499
+ });
500
+ }
501
+ };
502
+ /** Shared instance. Access via {@link CourierToastDatastore.shared}. */
503
+ __publicField(_CourierToastDatastore, "instance");
504
+ let CourierToastDatastore = _CourierToastDatastore;
505
+ class CourierToastDatastoreListener {
506
+ constructor(events) {
507
+ this.events = events;
508
+ }
509
+ /** Remove this listener. Callbacks will no longer be invoked after remove is called. */
510
+ remove() {
511
+ CourierToastDatastore.shared.removeDatastoreListener(this);
512
+ }
513
+ }
514
+ const _CourierToast = class _CourierToast extends CourierBaseElement {
515
+ constructor(props) {
516
+ super();
517
+ // Internally-maintained state
518
+ __publicField(this, "_themeManager");
519
+ __publicField(this, "_themeSubscription");
520
+ __publicField(this, "_toastStyle");
521
+ __publicField(this, "_authListener");
522
+ __publicField(this, "_datastoreListener");
523
+ // Consumer-provided options
524
+ __publicField(this, "_autoDismiss", false);
525
+ __publicField(this, "_autoDismissTimeoutMs", 5e3);
526
+ __publicField(this, "_dismissButtonOption", "auto");
527
+ __publicField(this, "_customToastItem");
528
+ __publicField(this, "_customToastItemContent");
529
+ // Consumer-provided callbacks
530
+ __publicField(this, "_onItemClick");
531
+ __publicField(this, "_onItemActionClick");
532
+ /** Default layout props. */
533
+ __publicField(this, "_defaultLayoutProps", {
534
+ position: "fixed",
535
+ width: "380px",
536
+ top: "30px",
537
+ right: "30px",
538
+ zIndex: 999
539
+ });
540
+ this._themeManager = (props == null ? void 0 : props.themeManager) ?? new CourierToastThemeManager(defaultLightTheme);
541
+ this._themeSubscription = this._themeManager.subscribe((_) => {
542
+ this.refreshStyles();
543
+ });
544
+ this._datastoreListener = new CourierToastDatastoreListener({
545
+ onMessageAdd: this.datastoreAddMessageListener.bind(this),
546
+ onMessageRemove: this.datastoreRemoveMessageListener.bind(this)
547
+ });
548
+ }
549
+ /** Set the handler invoked when a toast item is clicked. */
550
+ onToastItemClick(handler) {
551
+ this._onItemClick = handler;
552
+ }
553
+ /** Set the handler invoked when a toast item action button is clicked. */
554
+ onToastItemActionClick(handler) {
555
+ this._onItemActionClick = handler;
556
+ }
557
+ /** Enable auto-dismiss for toast items. */
558
+ enableAutoDismiss() {
559
+ this._autoDismiss = true;
560
+ }
561
+ /** Disable auto-dismiss for toast items. */
562
+ disableAutoDismiss() {
563
+ this._autoDismiss = false;
564
+ }
565
+ /**
566
+ * Set the timeout before auto-dismissing toasts.
567
+ * Only applicable if auto-dismiss is enabled.
568
+ * @param timeoutMs - The timeout in milliseconds before a toast is dismissed.
569
+ */
570
+ setAutoDismissTimeoutMs(timeoutMs) {
571
+ this._autoDismissTimeoutMs = timeoutMs;
572
+ }
573
+ /**
574
+ * Set the light theme for the toast.
575
+ * @param theme - The light theme object to set.
576
+ */
577
+ setLightTheme(theme) {
578
+ this._themeManager.setLightTheme(theme);
579
+ }
580
+ /**
581
+ * Set the dark theme for the toast.
582
+ * @param theme - The dark theme object to set.
583
+ */
584
+ setDarkTheme(theme) {
585
+ this._themeManager.setDarkTheme(theme);
586
+ }
587
+ /**
588
+ * Set the dismiss button display option.
589
+ *
590
+ * @param option - a value of {@link CourierToastDismissButtonOption}
591
+ */
592
+ setDismissButton(option) {
593
+ this._dismissButtonOption = option;
594
+ this.refreshStyles();
595
+ }
596
+ /**
597
+ * Set the theme mode.
598
+ *
599
+ * @param mode - The theme mode, one of "dark", "light", or "system".
600
+ */
601
+ setMode(mode) {
602
+ this._themeManager.setMode(mode);
603
+ }
604
+ /**
605
+ * Set a factory function that renders a toast item.
606
+ *
607
+ * See {@link CourierToast.setToastItemContent} to set the content while preserving the toast item's
608
+ * container and stack styling.
609
+ */
610
+ setToastItem(factory) {
611
+ this._customToastItem = factory;
612
+ }
613
+ /**
614
+ * Set a factory function that renders a toast item's content.
615
+ *
616
+ * The toast item's container, including the stack, auto-dismiss timer, and dismiss button
617
+ * and all events are still present when custom content is set.
618
+ *
619
+ * See {@link CourierToast.setDismissButton} to customize the dismiss button's visibility and
620
+ * {@link CourierToast.setToastItem} to customize the entire toast item, including
621
+ * its container.
622
+ */
623
+ setToastItemContent(factory) {
624
+ this._customToastItemContent = factory;
625
+ }
626
+ /**
627
+ * Dismiss the toast item(s) associated with a particular {@link @trycourier/courier-js#InboxMessage}.
628
+ *
629
+ * Toast items are matched to messages by the field {@link @trycourier/courier-js#InboxMessage.messageId}.
630
+ * If the item is an instance of {@link CourierToastItem} it will be animated out
631
+ * before removal, otherwise custom items are removed immediately.
632
+ *
633
+ * If there are multiple toast items matching the message, all items will be dismissed.
634
+ *
635
+ * @example
636
+ * Using dismissToastForMessage with setToastItem to dismiss a custom element.
637
+ * ```ts
638
+ * // Get a reference to the toast component
639
+ * const toast = document.getElementById("my-toast");
640
+ *
641
+ * toast.setToastItem((props) => {
642
+ * const el = document.createElement("div");
643
+ * el.addEventListener("click", () => toast.dismissToastForMessage(props.message));
644
+ * return el;
645
+ * });
646
+ * ```
647
+ *
648
+ * @param message - the {@link @trycourier/courier-js#InboxMessage} for which toast items should be dismissed
649
+ */
650
+ dismissToastForMessage(message) {
651
+ this.childNodes.forEach((node) => {
652
+ const nodeMessageId = node.dataset.courierMessageId;
653
+ if (nodeMessageId !== message.messageId) {
654
+ return;
655
+ }
656
+ if (node instanceof CourierToastItem) {
657
+ node.dismiss();
658
+ } else {
659
+ node.remove();
660
+ }
661
+ });
662
+ }
663
+ /**
664
+ * @override
665
+ */
666
+ onComponentMounted() {
667
+ this._toastStyle = injectGlobalStyle(_CourierToast.id, this.getStyles(this.theme));
668
+ CourierToastDatastore.shared.addDatastoreListener(this._datastoreListener);
669
+ Courier.shared.addAuthenticationListener(this.authChangedCallback.bind(this));
670
+ CourierToastDatastore.shared.listenForMessages();
671
+ }
672
+ /**
673
+ * @override
674
+ */
675
+ onComponentUnmounted() {
676
+ var _a, _b;
677
+ this._datastoreListener.remove();
678
+ (_a = this._authListener) == null ? void 0 : _a.remove();
679
+ (_b = this._toastStyle) == null ? void 0 : _b.remove();
680
+ this._themeManager.cleanup();
681
+ this._themeSubscription.unsubscribe();
682
+ }
683
+ /**
684
+ * Lifecycle callback invoked when an observed attribute changes.
685
+ *
686
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes
687
+ */
688
+ attributeChangedCallback(name, _, newValue) {
689
+ switch (name) {
690
+ case "auto-dismiss":
691
+ if (newValue === "false") {
692
+ this.disableAutoDismiss();
693
+ } else {
694
+ this.enableAutoDismiss();
695
+ }
696
+ break;
697
+ case "auto-dismiss-timeout-ms":
698
+ this.setAutoDismissTimeoutMs(parseInt(
699
+ newValue,
700
+ /* base= */
701
+ 10
702
+ ));
703
+ break;
704
+ case "dismiss-button":
705
+ if (newValue && _CourierToast.isDismissButtonOption(newValue)) {
706
+ this.setDismissButton(newValue);
707
+ } else {
708
+ this.setDismissButton("auto");
709
+ }
710
+ break;
711
+ case "light-theme":
712
+ if (newValue) {
713
+ this.setLightTheme(JSON.parse(newValue));
714
+ }
715
+ break;
716
+ case "dark-theme":
717
+ if (newValue) {
718
+ this.setDarkTheme(JSON.parse(newValue));
719
+ }
720
+ break;
721
+ case "mode":
722
+ this._themeManager.setMode(newValue);
723
+ break;
724
+ }
725
+ }
726
+ get theme() {
727
+ return this._themeManager.getTheme();
728
+ }
729
+ /** Refresh the styles tag, if it exists, with the current theme. */
730
+ refreshStyles() {
731
+ if (this._toastStyle) {
732
+ this._toastStyle.textContent = this.getStyles(this.theme);
733
+ }
734
+ }
735
+ authChangedCallback() {
736
+ this.removeAllItems();
737
+ CourierToastDatastore.shared.listenForMessages();
738
+ }
739
+ removeAllItems() {
740
+ while (this.firstChild) {
741
+ this.removeChild(this.firstChild);
742
+ }
743
+ }
744
+ addToastItem(message) {
745
+ const toastItem = this.createToastItem(message);
746
+ toastItem.dataset.courierMessageId = message.messageId;
747
+ this.appendChild(toastItem);
748
+ this.resizeContainerToHeight(this.topStackItemHeight);
749
+ return toastItem;
750
+ }
751
+ createToastItem(message) {
752
+ if (this._customToastItem) {
753
+ return this.createCustomToastItem(message);
754
+ }
755
+ return this.createDefaultToastItem(message);
756
+ }
757
+ createDefaultToastItem(message) {
758
+ const item = new CourierToastItem({
759
+ message,
760
+ autoDismiss: this._autoDismiss,
761
+ autoDismissTimeoutMs: this._autoDismissTimeoutMs,
762
+ themeManager: this._themeManager
763
+ });
764
+ item.onItemDismissed((_) => {
765
+ this.resizeContainerToHeight(this.topStackItemHeight);
766
+ });
767
+ if (this._customToastItemContent) {
768
+ item.setToastItemContent(this._customToastItemContent);
769
+ }
770
+ if (this._onItemClick) {
771
+ item.onToastItemClick(this._onItemClick);
772
+ }
773
+ if (this._onItemActionClick) {
774
+ item.onToastItemActionClick(this._onItemActionClick);
775
+ }
776
+ if (this._autoDismiss) {
777
+ setTimeout(item.dismiss.bind(item), this._autoDismissTimeoutMs);
778
+ }
779
+ return item;
780
+ }
781
+ createCustomToastItem(message) {
782
+ if (!this._customToastItem) {
783
+ throw Error("Attempted to create customToastItem, but none is set");
784
+ }
785
+ const customItem = this._customToastItem({
786
+ message,
787
+ autoDismiss: this._autoDismiss,
788
+ autoDismissTimeoutMs: this._autoDismissTimeoutMs
789
+ });
790
+ if (this._autoDismiss) {
791
+ setTimeout(() => {
792
+ this.removeChild(customItem);
793
+ }, this._autoDismissTimeoutMs);
794
+ }
795
+ customItem.addEventListener("click", () => {
796
+ if (this._onItemClick) {
797
+ this._onItemClick({ message });
798
+ }
799
+ });
800
+ return customItem;
801
+ }
802
+ datastoreAddMessageListener(message) {
803
+ this.addToastItem(message);
804
+ }
805
+ datastoreRemoveMessageListener(message) {
806
+ this.dismissToastForMessage(message);
807
+ }
808
+ getStyles(theme) {
809
+ var _a, _b, _c, _d, _e, _f;
810
+ const item = theme.item;
811
+ const toastStyles = `
812
+ ${_CourierToast.id} {
813
+ position: ${this._defaultLayoutProps.position};
814
+ z-index: ${this._defaultLayoutProps.zIndex};
815
+ top: ${this._defaultLayoutProps.top};
816
+ right: ${this._defaultLayoutProps.right};
817
+ width: ${this._defaultLayoutProps.width};
818
+ }
819
+ `;
820
+ const toastStackStyles = `
821
+ ${CourierToastItem.id}:last-child {
822
+ top: 0;
823
+ right: 0;
824
+ }
825
+
826
+ ${CourierToastItem.id}:nth-last-child(2) {
827
+ top: 12px;
828
+ bottom: -12px;
829
+ --scale: 0.95
830
+ }
831
+
832
+ ${CourierToastItem.id}:nth-last-child(3) {
833
+ top: 24px;
834
+ bottom: -24px;
835
+ --scale: 0.9;
836
+ }
837
+
838
+ ${CourierToastItem.id}:nth-last-child(n+4) {
839
+ top: 24px;
840
+ bottom: -24px;
841
+ --scale: 0.9;
842
+ visibility: hidden;
843
+ }
844
+
845
+ ${CourierToastItem.id}:nth-last-child(n+2) > .overflow-hidden-container {
846
+ opacity: 0;
847
+ }
848
+ `;
849
+ const toastItemStyles = `
850
+ ${CourierToastItem.id} {
851
+ position: absolute;
852
+ box-sizing: border-box;
853
+ width: 100%;
854
+ background-color: ${item == null ? void 0 : item.backgroundColor};
855
+ box-shadow: ${item == null ? void 0 : item.shadow};
856
+ border: ${item == null ? void 0 : item.border};
857
+ border-radius: ${item == null ? void 0 : item.borderRadius};
858
+ transition: 0.2s ease-in-out;
859
+ cursor: default;
860
+
861
+ opacity: 0;
862
+ transform: translate(0, -10px) scaleX(var(--scale, 1));
863
+ animation: show 0.3s ease-in-out forwards;
864
+ }
865
+
866
+ ${CourierToastItem.id} > .overflow-hidden-container {
867
+ height: 100%;
868
+ width: 100%;
869
+ border-radius: ${item == null ? void 0 : item.borderRadius};
870
+ overflow: hidden;
871
+ }
872
+
873
+ ${CourierToastItem.id}.dismissing {
874
+ animation: hide 0.3s ease-in-out forwards;
875
+ }
876
+
877
+ @keyframes show {
878
+ 0% {
879
+ opacity: 0;
880
+ }
881
+
882
+ 100% {
883
+ opacity: 1;
884
+ transform: scaleX(var(--scale, 1));
885
+ }
886
+ }
887
+
888
+ @keyframes hide {
889
+ 0% {
890
+ opacity: 1;
891
+ transform: scaleX(var(--scale, 1));
892
+ }
893
+
894
+ 100% {
895
+ opacity: 0;
896
+ transform: scaleX(var(--scale, 1));
897
+ }
898
+ }
899
+
900
+ ${CourierToastItem.id}.clickable:last-child {
901
+ cursor: pointer;
902
+ }
903
+
904
+ ${CourierToastItem.id}.clickable:last-child:hover {
905
+ background-color: ${item == null ? void 0 : item.hoverBackgroundColor};
906
+ }
907
+
908
+ ${CourierToastItem.id}.clickable:last-child:active {
909
+ background-color: ${item == null ? void 0 : item.activeBackgroundColor};
910
+ }
911
+
912
+ ${CourierToastItem.id}.clickable:nth-last-child(n+2) {
913
+ pointer-events: none;
914
+ }
915
+ `;
916
+ const dismissStyles = `
917
+ ${CourierToastItem.id} > .dismiss {
918
+ position: absolute;
919
+ visibility: hidden;
920
+ opacity: 0%;
921
+ top: -10px;
922
+ right: -10px;
923
+ background-color: ${item == null ? void 0 : item.backgroundColor};
924
+ border: ${item == null ? void 0 : item.border};
925
+ padding: 3px;
926
+ border-radius: 50%;
927
+ font-size: 12pt;
928
+ box-shadow: ${item == null ? void 0 : item.shadow};
929
+ cursor: pointer;
930
+ transition: 0.2s ease-in-out;
931
+ }
932
+
933
+ ${CourierToastItem.id} > .dismiss:hover {
934
+ background-color: ${item == null ? void 0 : item.hoverBackgroundColor};
935
+ }
936
+
937
+ ${CourierToastItem.id} > .dismiss:active {
938
+ background-color: ${item == null ? void 0 : item.activeBackgroundColor};
939
+ }
940
+
941
+ ${CourierToastItem.id}:last-child${this.showDismissOnHover ? ":hover" : ""} > .dismiss {
942
+ visibility: ${this.showDismiss ? "visible" : "hidden"};
943
+ opacity: 100%;
944
+ transition: 0.2s ease-in-out;
945
+ }
946
+ `;
947
+ const autoDismissStyles = `
948
+ ${CourierToastItem.id} > .overflow-hidden-container > .auto-dismiss {
949
+ width: 100%;
950
+ height: 5px;
951
+ background-color: ${item == null ? void 0 : item.autoDismissBarColor};
952
+ animation: auto-dismiss ${this._autoDismissTimeoutMs}ms ease-in-out forwards;
953
+ }
954
+
955
+ @keyframes auto-dismiss {
956
+ 100% {
957
+ width: 0px;
958
+ }
959
+ }
960
+ `;
961
+ const contentStyles = `
962
+ ${CourierToastItem.id} > .overflow-hidden-container > .content {
963
+ display: flex;
964
+ gap: 12px;
965
+ align-items: center;
966
+ align-self: stretch;
967
+ box-sizing: border-box;
968
+ padding: 12px;
969
+ }
970
+
971
+ ${CourierToastItem.id} > .overflow-hidden-container > .content > .text-content {
972
+ }
973
+
974
+ ${CourierToastItem.id} > .overflow-hidden-container > .content > .icon {
975
+ }
976
+
977
+ ${CourierToastItem.id} > .overflow-hidden-container > .content > .text-content > .title {
978
+ margin: 0;
979
+ font-weight: ${(_a = item == null ? void 0 : item.title) == null ? void 0 : _a.weight};
980
+ font-size: ${(_b = item == null ? void 0 : item.title) == null ? void 0 : _b.size};
981
+ color: ${(_c = item == null ? void 0 : item.title) == null ? void 0 : _c.color};
982
+ }
983
+
984
+ ${CourierToastItem.id} > .overflow-hidden-container > .content > .text-content > .body {
985
+ margin: 8px 0 0 0;
986
+ font-weight: ${(_d = item == null ? void 0 : item.body) == null ? void 0 : _d.weight};
987
+ font-size: ${(_e = item == null ? void 0 : item.body) == null ? void 0 : _e.size};
988
+ line-height: 150%;
989
+ color: ${(_f = item == null ? void 0 : item.body) == null ? void 0 : _f.color};
990
+ }
991
+ `;
992
+ const actionStyles = `
993
+ ${CourierToastItem.id} > .overflow-hidden-container > .content > .text-content > .actions-container {
994
+ display: flex;
995
+ gap: 8px;
996
+ margin-top: 12px;
997
+ }
998
+ `;
999
+ return [
1000
+ toastStyles,
1001
+ toastStackStyles,
1002
+ toastItemStyles,
1003
+ dismissStyles,
1004
+ autoDismissStyles,
1005
+ contentStyles,
1006
+ actionStyles
1007
+ ].join("");
1008
+ }
1009
+ /** Get the top item's (i.e. the fully visible item's) height. */
1010
+ get topStackItemHeight() {
1011
+ if (this.lastChild) {
1012
+ const height = this.lastChild.getBoundingClientRect().height;
1013
+ return `${height}px`;
1014
+ }
1015
+ return "0px";
1016
+ }
1017
+ resizeContainerToHeight(height) {
1018
+ this.style.height = height;
1019
+ }
1020
+ /** Whether the dismiss button should only be shown on hover. */
1021
+ get showDismissOnHover() {
1022
+ if (this._autoDismiss && this._dismissButtonOption === "auto") {
1023
+ return true;
1024
+ }
1025
+ if (this._dismissButtonOption === "hover") {
1026
+ return true;
1027
+ }
1028
+ return false;
1029
+ }
1030
+ /** Whether to show the dismiss button. The button is visible (either always or on hover) if not explicitly disabled. */
1031
+ get showDismiss() {
1032
+ return this._dismissButtonOption !== "hidden";
1033
+ }
1034
+ /** @override */
1035
+ static get id() {
1036
+ return "courier-toast";
1037
+ }
1038
+ static isDismissButtonOption(value) {
1039
+ const validOptions = ["visible", "hidden", "hover", "auto"];
1040
+ return validOptions.includes(value);
1041
+ }
1042
+ };
1043
+ /**
1044
+ * The names of all attributes for which the web component needs change notifications.
1045
+ *
1046
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes
1047
+ */
1048
+ __publicField(_CourierToast, "observedAttributes", [
1049
+ "auto-dismiss",
1050
+ "auto-dismiss-timeout-ms",
1051
+ "dismiss-button",
1052
+ "light-theme",
1053
+ "dark-theme",
1054
+ "mode"
1055
+ ]);
1056
+ let CourierToast = _CourierToast;
1057
+ registerElement(CourierToast);
1058
+ class CourierToastDatastoreEvents {
1059
+ onMessageAdd(_) {
1060
+ }
1061
+ onMessageRemove(_) {
1062
+ }
1063
+ onError(_) {
1064
+ }
1065
+ }
1066
+ Courier.shared.courierUserAgentName = "courier-ui-toast";
1067
+ Courier.shared.courierUserAgentVersion = "1.0.0";
1068
+ export {
1069
+ Courier2 as Courier,
1070
+ CourierToast,
1071
+ CourierToastDatastore,
1072
+ CourierToastDatastoreEvents,
1073
+ CourierToastDatastoreListener,
1074
+ CourierToastItem,
1075
+ CourierToastThemeManager,
1076
+ defaultDarkTheme,
1077
+ defaultLightTheme,
1078
+ mergeTheme
1079
+ };
1080
+ //# sourceMappingURL=index.mjs.map