@justeattakeaway/pie-toast 0.3.3 → 0.4.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.
@@ -60,7 +60,7 @@
60
60
  "type": {
61
61
  "text": "DefaultProps"
62
62
  },
63
- "default": "{\n isOpen: true,\n variant: 'neutral',\n isStrong: false,\n isDismissible: true,\n isMultiline: false,\n}"
63
+ "default": "{\n isOpen: true,\n variant: 'neutral',\n isStrong: false,\n isDismissible: true,\n isMultiline: false,\n duration: 5000,\n}"
64
64
  }
65
65
  ],
66
66
  "exports": [
@@ -180,6 +180,12 @@
180
180
  "privacy": "public",
181
181
  "attribute": "leadingAction"
182
182
  },
183
+ {
184
+ "kind": "field",
185
+ "name": "duration",
186
+ "privacy": "public",
187
+ "attribute": "duration"
188
+ },
183
189
  {
184
190
  "kind": "field",
185
191
  "name": "actionButton",
@@ -187,6 +193,13 @@
187
193
  "text": "HTMLElement | undefined"
188
194
  }
189
195
  },
196
+ {
197
+ "kind": "field",
198
+ "name": "closeButton",
199
+ "type": {
200
+ "text": "HTMLElement | undefined"
201
+ }
202
+ },
190
203
  {
191
204
  "kind": "field",
192
205
  "name": "_actionButtonOffset",
@@ -205,6 +218,36 @@
205
218
  "privacy": "private",
206
219
  "default": "0"
207
220
  },
221
+ {
222
+ "kind": "field",
223
+ "name": "_timeoutId",
224
+ "type": {
225
+ "text": "NodeJS.Timeout | null"
226
+ },
227
+ "privacy": "private",
228
+ "default": "null"
229
+ },
230
+ {
231
+ "kind": "field",
232
+ "name": "_abortController",
233
+ "type": {
234
+ "text": "AbortController | null"
235
+ },
236
+ "privacy": "private",
237
+ "default": "null"
238
+ },
239
+ {
240
+ "kind": "method",
241
+ "name": "setAutoDismiss",
242
+ "privacy": "private",
243
+ "description": "Create a timeout function and set its id into a private attribute."
244
+ },
245
+ {
246
+ "kind": "method",
247
+ "name": "abortAndCleanEventListeners",
248
+ "privacy": "private",
249
+ "description": "If the _abortController is set, it aborts all event\nlisteners in this controller and the controller turns into null."
250
+ },
208
251
  {
209
252
  "kind": "method",
210
253
  "name": "getMessageMaxWidth",
@@ -246,6 +289,48 @@
246
289
  ],
247
290
  "description": "Calculates and returns the width of the message based on the toast properties."
248
291
  },
292
+ {
293
+ "kind": "method",
294
+ "name": "addListenersToElement",
295
+ "privacy": "private",
296
+ "parameters": [
297
+ {
298
+ "name": "element",
299
+ "type": {
300
+ "text": "typeof this | HTMLElement | undefined"
301
+ },
302
+ "description": "The element to which the listeners will be added. It can be the current instance, an HTMLElement, or undefined."
303
+ },
304
+ {
305
+ "name": "inEvent",
306
+ "type": {
307
+ "text": "keyof HTMLElementEventMap"
308
+ },
309
+ "description": "The event type to listen for when entering the element. (e.g., 'mouseenter', 'focusin')."
310
+ },
311
+ {
312
+ "name": "outEvent",
313
+ "type": {
314
+ "text": "keyof HTMLElementEventMap"
315
+ },
316
+ "description": "The event type to listen for when leaving the element. (e.g., 'mouseleave', 'focusout')."
317
+ },
318
+ {
319
+ "name": "abortSignal",
320
+ "type": {
321
+ "text": "AddEventListenerOptions['signal']"
322
+ },
323
+ "description": "An AbortSignal that can be used to remove the event listeners."
324
+ }
325
+ ],
326
+ "description": "Adds event listeners to the specified element for handling the auto dismiss behavior."
327
+ },
328
+ {
329
+ "kind": "method",
330
+ "name": "createAutoDismissEventListeners",
331
+ "privacy": "private",
332
+ "description": "It creates all event listeners to handle the auto-dismiss capability\nas well the controller responsible to remove the events when needed."
333
+ },
249
334
  {
250
335
  "kind": "method",
251
336
  "name": "handleActionClick",
@@ -385,6 +470,10 @@
385
470
  "text": "ToastProps['leadingAction']"
386
471
  },
387
472
  "fieldName": "leadingAction"
473
+ },
474
+ {
475
+ "name": "duration",
476
+ "fieldName": "duration"
388
477
  }
389
478
  ],
390
479
  "mixins": [
package/dist/index.d.ts CHANGED
@@ -2,7 +2,6 @@ import { ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
2
  import type { CSSResult } from 'lit';
3
3
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
4
4
  import type { LitElement } from 'lit';
5
- import type { nothing } from 'lit';
6
5
  import type { PropertyValues } from 'lit';
7
6
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
8
7
  import type { TemplateResult } from 'lit';
@@ -58,10 +57,27 @@ export declare class PieToast extends PieToast_base implements ToastProps {
58
57
  isDismissible: boolean;
59
58
  isMultiline: boolean;
60
59
  leadingAction: ToastProps['leadingAction'];
60
+ duration: number | null;
61
61
  actionButton?: HTMLElement;
62
+ closeButton?: HTMLElement;
62
63
  private _actionButtonOffset;
63
64
  private _messageAreaMaxWidth;
65
+ private _timeoutId;
66
+ private _abortController;
64
67
  static styles: CSSResult;
68
+ /**
69
+ * Create a timeout function and set its id into a private attribute.
70
+ *
71
+ * @private
72
+ */
73
+ private setAutoDismiss;
74
+ /**
75
+ * If the _abortController is set, it aborts all event
76
+ * listeners in this controller and the controller turns into null.
77
+ *
78
+ * @private
79
+ */
80
+ private abortAndCleanEventListeners;
65
81
  /**
66
82
  * Calculates and returns the width of the message based on the toast properties.
67
83
  *
@@ -73,9 +89,29 @@ export declare class PieToast extends PieToast_base implements ToastProps {
73
89
  * @returns {number} - The width of the message in pixels.
74
90
  */
75
91
  private getMessageMaxWidth;
92
+ /**
93
+ * Adds event listeners to the specified element for handling the auto dismiss behavior.
94
+ *
95
+ * @param {typeof this | HTMLElement | undefined} element - The element to which the listeners will be added. It can be the current instance, an HTMLElement, or undefined.
96
+ * @param {keyof HTMLElementEventMap} inEvent - The event type to listen for when entering the element. (e.g., 'mouseenter', 'focusin').
97
+ * @param {keyof HTMLElementEventMap} outEvent - The event type to listen for when leaving the element. (e.g., 'mouseleave', 'focusout').
98
+ * @param {AddEventListenerOptions['signal']} abortSignal - An AbortSignal that can be used to remove the event listeners.
99
+ *
100
+ * @private
101
+ */
102
+ private addListenersToElement;
103
+ /**
104
+ * It creates all event listeners to handle the auto-dismiss capability
105
+ * as well the controller responsible to remove the events when needed.
106
+ *
107
+ * @private
108
+ */
109
+ private createAutoDismissEventListeners;
76
110
  /**
77
111
  * Lifecycle method executed when component is updated.
78
112
  * It dispatches an event if toast is opened.
113
+ * It adds event listeners when toast is opened and if the duration is not null
114
+ * It aborts all event listeners when toast is closed.
79
115
  * It calculates _messageAreaMaxWidth
80
116
  */
81
117
  protected updated(_changedProperties: PropertyValues<this>): Promise<void>;
@@ -146,7 +182,7 @@ export declare class PieToast extends PieToast_base implements ToastProps {
146
182
  * @private
147
183
  */
148
184
  private shouldNotUseInverseBtnVariant;
149
- render(): TemplateResult<1> | typeof nothing;
185
+ render(): TemplateResult<1>;
150
186
  }
151
187
 
152
188
  declare const PieToast_base: GenericConstructor<RTLInterface> & typeof LitElement;
@@ -180,6 +216,12 @@ export declare interface ToastProps {
180
216
  * The leading action for the toast.
181
217
  */
182
218
  leadingAction?: ActionProps;
219
+ /**
220
+ * Sets the duration of the toast in milliseconds before it auto-dismisses.
221
+ * If the value is null auto-dismiss is disabled
222
+ * If the value is not provided it auto-dismisses after 5 seconds (5000 milliseconds)
223
+ */
224
+ duration?: number | null;
183
225
  }
184
226
 
185
227
  export declare const variants: readonly ["neutral", "info", "warning", "success", "error"];
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { LitElement as y, html as c, nothing as l, unsafeCSS as w } from "lit";
2
- import { property as h, query as O } from "lit/decorators.js";
3
- import { classMap as b } from "lit/directives/class-map.js";
4
- import { RtlMixin as A, dispatchCustomEvent as v, validPropertyValues as M, defineCustomElement as B } from "@justeattakeaway/pie-webc-core";
1
+ import { LitElement as C, unsafeCSS as w, html as l, nothing as d } from "lit";
2
+ import { property as u, query as y } from "lit/decorators.js";
3
+ import { classMap as $ } from "lit/directives/class-map.js";
4
+ import { RtlMixin as O, dispatchCustomEvent as f, validPropertyValues as E, defineCustomElement as _ } from "@justeattakeaway/pie-webc-core";
5
5
  import "@justeattakeaway/pie-icon-button";
6
6
  import "@justeattakeaway/pie-icons-webc/dist/IconClose.js";
7
7
  import "@justeattakeaway/pie-icons-webc/dist/IconInfoCircle.js";
@@ -9,22 +9,41 @@ import "@justeattakeaway/pie-icons-webc/dist/IconAlertCircle.js";
9
9
  import "@justeattakeaway/pie-icons-webc/dist/IconAlertTriangle.js";
10
10
  import "@justeattakeaway/pie-icons-webc/dist/IconCheckCircle.js";
11
11
  import "@justeattakeaway/pie-button";
12
- const C = `*,*:after,*:before{box-sizing:inherit}.c-toast{--toast-border-radius: var(--dt-radius-rounded-b);--toast-background-color: var(--dt-color-container-inverse);--toast-font-color: var(--dt-color-content-inverse);--toast-font-size: calc(var(--dt-font-body-s-size) * 1px);--toast-line-height: calc(var(--dt-font-body-s-line-height) * 1px);--toast-icon-fill: var(--dt-color-content-default);--toast-message-max-width: 100%;display:flex;flex-direction:column;justify-content:center;min-height:48px;max-height:122px;min-width:300px;max-width:420px;padding:var(--dt-spacing-a) var(--dt-spacing-c) var(--dt-spacing-a) var(--dt-spacing-d);border-radius:var(--toast-border-radius);gap:var(--dt-spacing-a);background-color:var(--toast-background-color);box-shadow:var(--dt-elevation-03);color:var(--toast-font-color);font-size:var(--toast-font-size);line-height:var(--toast-line-height)}.c-toast--info{--toast-icon-fill: var(--dt-color-support-info-inverse)}.c-toast--info.c-toast--strong{--toast-background-color: var(--dt-color-support-info);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast--warning{--toast-icon-fill: var(--dt-color-support-warning-inverse)}.c-toast--warning.c-toast--strong{--toast-background-color: var(--dt-color-support-warning);--toast-icon-fill: var(--dt-color-content-dark);--toast-font-color: var(--dt-color-content-dark)}.c-toast--success{--toast-icon-fill: var(--dt-color-support-positive-inverse)}.c-toast--success.c-toast--strong{--toast-background-color: var(--dt-color-support-positive);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast--error{--toast-icon-fill: var(--dt-color-support-error-inverse)}.c-toast--error.c-toast--strong{--toast-background-color: var(--dt-color-support-error);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast-contentArea{display:flex;gap:var(--dt-spacing-b);justify-content:space-between}.c-toast-messageArea{display:flex;align-items:center;gap:var(--dt-spacing-b);padding:calc(var(--dt-spacing-03) / 2) 0}.c-toast-messageArea span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;max-width:var(--toast-message-max-width)}.c-toast-messageArea.c-toast--multiline{align-items:flex-start}.c-toast-messageArea.c-toast--multiline span{max-height:60px;white-space:inherit;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.c-toast-actionsArea{display:flex;gap:var(--dt-spacing-b)}.c-toast-footer{display:flex;justify-content:flex-end}.c-toast-icon{color:var(--toast-icon-fill)}
13
- `, _ = ["neutral", "info", "warning", "success", "error"], i = "pie-toast", a = "c-toast", N = `${i}-close`, k = `${i}-open`, I = `${i}-leading-action-click`, g = {
12
+ const k = "*,*:after,*:before{box-sizing:inherit}.c-toast{--toast-border-radius: var(--dt-radius-rounded-b);--toast-background-color: var(--dt-color-container-inverse);--toast-font-color: var(--dt-color-content-inverse);--toast-font-size: calc(var(--dt-font-body-s-size) * 1px);--toast-line-height: calc(var(--dt-font-body-s-line-height) * 1px);--toast-icon-fill: var(--dt-color-content-default);--toast-message-max-width: 100%;display:flex;flex-direction:column;justify-content:center;min-height:48px;max-height:122px;min-width:300px;max-width:420px;padding:var(--dt-spacing-a) var(--dt-spacing-c) var(--dt-spacing-a) var(--dt-spacing-d);border-radius:var(--toast-border-radius);gap:var(--dt-spacing-a);background-color:var(--toast-background-color);box-shadow:var(--dt-elevation-03);color:var(--toast-font-color);font-size:var(--toast-font-size);line-height:var(--toast-line-height);transition-property:all;transition-duration:var(--dt-motion-timing-100);transition-timing-function:var(--dt-motion-easing-in)}.c-toast--animate-in{animation-duration:var(--dt-motion-timing-200);animation-name:animate-in;animation-timing-function:var(--dt-motion-easing-in);transform:translate(0)}.c-toast--animate-out{animation-duration:var(--dt-motion-timing-100);animation-name:animate-out;animation-timing-function:var(--dt-motion-easing-out);transform:translate(100%);opacity:0}.c-toast--info{--toast-icon-fill: var(--dt-color-support-info-inverse)}.c-toast--info.c-toast--strong{--toast-background-color: var(--dt-color-support-info);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast--warning{--toast-icon-fill: var(--dt-color-support-warning-inverse)}.c-toast--warning.c-toast--strong{--toast-background-color: var(--dt-color-support-warning);--toast-icon-fill: var(--dt-color-content-dark);--toast-font-color: var(--dt-color-content-dark)}.c-toast--success{--toast-icon-fill: var(--dt-color-support-positive-inverse)}.c-toast--success.c-toast--strong{--toast-background-color: var(--dt-color-support-positive);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast--error{--toast-icon-fill: var(--dt-color-support-error-inverse)}.c-toast--error.c-toast--strong{--toast-background-color: var(--dt-color-support-error);--toast-icon-fill: var(--dt-color-content-inverse)}.c-toast-contentArea{display:flex;gap:var(--dt-spacing-b);justify-content:space-between}.c-toast-messageArea{display:flex;align-items:center;gap:var(--dt-spacing-b);padding:calc(var(--dt-spacing-03) / 2) 0}.c-toast-messageArea span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;max-width:var(--toast-message-max-width)}.c-toast-messageArea.c-toast--multiline{align-items:flex-start}.c-toast-messageArea.c-toast--multiline span{max-height:60px;white-space:inherit;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical}.c-toast-actionsArea{display:flex;gap:var(--dt-spacing-b)}.c-toast-footer{display:flex;justify-content:flex-end}.c-toast-icon{color:var(--toast-icon-fill)}@keyframes animate-in{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes animate-out{0%{transform:translate(0)}to{transform:translate(100%)}}", B = ["neutral", "info", "warning", "success", "error"], o = "pie-toast", s = "c-toast", M = `${o}-close`, I = `${o}-open`, T = `${o}-leading-action-click`, h = {
14
13
  isOpen: !0,
15
14
  variant: "neutral",
16
15
  isStrong: !1,
17
16
  isDismissible: !0,
18
- isMultiline: !1
17
+ isMultiline: !1,
18
+ duration: 5e3
19
19
  };
20
- var S = Object.defineProperty, T = Object.getOwnPropertyDescriptor, p = (f, t, s, o) => {
21
- for (var n = o > 1 ? void 0 : o ? T(t, s) : t, e = f.length - 1, d; e >= 0; e--)
22
- (d = f[e]) && (n = (o ? d(t, s, n) : d(n)) || n);
23
- return o && n && S(t, s, n), n;
20
+ var L = Object.defineProperty, c = (v, t, i, n) => {
21
+ for (var e = void 0, r = v.length - 1, p; r >= 0; r--)
22
+ (p = v[r]) && (e = p(t, i, e) || e);
23
+ return e && L(t, i, e), e;
24
24
  };
25
- class r extends A(y) {
25
+ const g = class g extends O(C) {
26
26
  constructor() {
27
- super(...arguments), this.message = "", this.isOpen = g.isOpen, this.variant = g.variant, this.isStrong = g.isStrong, this.isDismissible = g.isDismissible, this.isMultiline = g.isMultiline, this._actionButtonOffset = 0, this._messageAreaMaxWidth = 0;
27
+ super(...arguments), this.message = "", this.isOpen = h.isOpen, this.variant = h.variant, this.isStrong = h.isStrong, this.isDismissible = h.isDismissible, this.isMultiline = h.isMultiline, this.duration = h.duration, this._actionButtonOffset = 0, this._messageAreaMaxWidth = 0, this._timeoutId = null, this._abortController = null;
28
+ }
29
+ /**
30
+ * Create a timeout function and set its id into a private attribute.
31
+ *
32
+ * @private
33
+ */
34
+ setAutoDismiss() {
35
+ this.duration !== null && (this._timeoutId = setTimeout(() => {
36
+ this.closeToastComponent();
37
+ }, this.duration));
38
+ }
39
+ /**
40
+ * If the _abortController is set, it aborts all event
41
+ * listeners in this controller and the controller turns into null.
42
+ *
43
+ * @private
44
+ */
45
+ abortAndCleanEventListeners() {
46
+ this._abortController && (this._abortController.abort(), this._abortController = null);
28
47
  }
29
48
  /**
30
49
  * Calculates and returns the width of the message based on the toast properties.
@@ -36,20 +55,50 @@ class r extends A(y) {
36
55
  *
37
56
  * @returns {number} - The width of the message in pixels.
38
57
  */
39
- getMessageMaxWidth(t, s, o, n) {
40
- let u = 0;
41
- return t && (u += 20 + 8), !s && o && (u += this._actionButtonOffset + 8), n && (u += 32 + 8), 392 - u;
58
+ getMessageMaxWidth(t, i, n, e) {
59
+ let m = 0;
60
+ return t && (m += 28), !i && n && (m += this._actionButtonOffset + 8), e && (m += 40), 392 - m;
61
+ }
62
+ /**
63
+ * Adds event listeners to the specified element for handling the auto dismiss behavior.
64
+ *
65
+ * @param {typeof this | HTMLElement | undefined} element - The element to which the listeners will be added. It can be the current instance, an HTMLElement, or undefined.
66
+ * @param {keyof HTMLElementEventMap} inEvent - The event type to listen for when entering the element. (e.g., 'mouseenter', 'focusin').
67
+ * @param {keyof HTMLElementEventMap} outEvent - The event type to listen for when leaving the element. (e.g., 'mouseleave', 'focusout').
68
+ * @param {AddEventListenerOptions['signal']} abortSignal - An AbortSignal that can be used to remove the event listeners.
69
+ *
70
+ * @private
71
+ */
72
+ addListenersToElement(t, i, n, e) {
73
+ t && (t.addEventListener(i, () => {
74
+ this._timeoutId && clearTimeout(this._timeoutId);
75
+ }, { signal: e }), t.addEventListener(n, () => {
76
+ this.setAutoDismiss();
77
+ }, { signal: e }));
78
+ }
79
+ /**
80
+ * It creates all event listeners to handle the auto-dismiss capability
81
+ * as well the controller responsible to remove the events when needed.
82
+ *
83
+ * @private
84
+ */
85
+ createAutoDismissEventListeners() {
86
+ this._abortController = new AbortController(), this.setAutoDismiss();
87
+ const { signal: t } = this._abortController;
88
+ this.addListenersToElement(this.actionButton, "focus", "focusout", t), this.addListenersToElement(this.closeButton, "focus", "focusout", t), this.addListenersToElement(this, "mouseover", "mouseout", t);
42
89
  }
43
90
  /**
44
91
  * Lifecycle method executed when component is updated.
45
92
  * It dispatches an event if toast is opened.
93
+ * It adds event listeners when toast is opened and if the duration is not null
94
+ * It aborts all event listeners when toast is closed.
46
95
  * It calculates _messageAreaMaxWidth
47
96
  */
48
97
  async updated(t) {
49
- var o;
50
- t.has("isOpen") && this.isOpen && v(this, k, { targetNotification: this }), await this.updateComplete, this.actionButton && (this._actionButtonOffset = this.actionButton.offsetWidth);
51
- const s = this.variantHasIcon(this.variant);
52
- this._messageAreaMaxWidth = this.getMessageMaxWidth(s, this.isMultiline, !!((o = this.leadingAction) != null && o.text), this.isDismissible), (t.has("variant") || t.has("isStrong") || t.has("message") || t.has("isDismissible") || t.has("isMultiline") || t.has("leadingAction")) && this.requestUpdate();
98
+ var n;
99
+ t.has("isOpen") && this.isOpen && (f(this, I, { targetNotification: this }), this.duration !== null && this.createAutoDismissEventListeners()), t.has("isOpen") && !this.isOpen && this.abortAndCleanEventListeners(), await this.updateComplete, this.actionButton && (this._actionButtonOffset = this.actionButton.offsetWidth);
100
+ const i = this.variantHasIcon(this.variant);
101
+ this._messageAreaMaxWidth = this.getMessageMaxWidth(i, this.isMultiline, !!((n = this.leadingAction) != null && n.text), this.isDismissible), (t.has("variant") || t.has("isStrong") || t.has("message") || t.has("isDismissible") || t.has("isMultiline") || t.has("leadingAction") || t.has("duration")) && this.requestUpdate();
53
102
  }
54
103
  /**
55
104
  * It handle the action button action.
@@ -57,7 +106,7 @@ class r extends A(y) {
57
106
  * @private
58
107
  */
59
108
  handleActionClick() {
60
- v(this, I, { targetNotification: this });
109
+ f(this, T, { targetNotification: this });
61
110
  }
62
111
  /**
63
112
  * Render the action button depending on action type and its action.
@@ -68,16 +117,16 @@ class r extends A(y) {
68
117
  * @private
69
118
  */
70
119
  renderActionButton(t) {
71
- const { text: s, ariaLabel: o } = t;
72
- return c`
120
+ const { text: i, ariaLabel: n } = t;
121
+ return l`
73
122
  <pie-button
74
123
  variant="${this.shouldNotUseInverseBtnVariant() ? "ghost" : "ghost-inverse"}"
75
124
  size="xsmall"
76
- aria-label="${o || l}"
125
+ aria-label="${n || d}"
77
126
  @click="${() => this.handleActionClick()}"
78
- data-test-id="${i}-leading-action"
127
+ data-test-id="${o}-leading-action"
79
128
  type="button">
80
- ${s}
129
+ ${i}
81
130
  </pie-button>
82
131
  `;
83
132
  }
@@ -90,9 +139,9 @@ class r extends A(y) {
90
139
  */
91
140
  renderFooter() {
92
141
  const { leadingAction: t } = this;
93
- return c`
94
- <footer class="${a}-footer" data-test-id="${i}-footer" >
95
- ${t ? this.renderActionButton(t) : l}
142
+ return l`
143
+ <footer class="${s}-footer" data-test-id="${o}-footer" >
144
+ ${t ? this.renderActionButton(t) : d}
96
145
  </footer>
97
146
  `;
98
147
  }
@@ -103,11 +152,11 @@ class r extends A(y) {
103
152
  * @private
104
153
  */
105
154
  renderCloseButton() {
106
- return c`
155
+ return l`
107
156
  <pie-icon-button
108
157
  variant="${this.shouldNotUseInverseBtnVariant() ? "ghost-secondary" : "ghost-inverse"}"
109
158
  size="xsmall"
110
- data-test-id="${i}-close"
159
+ data-test-id="${o}-close"
111
160
  @click="${this.closeToastComponent}">
112
161
  <icon-close></icon-close>
113
162
  </pie-icon-button>`;
@@ -121,9 +170,9 @@ class r extends A(y) {
121
170
  *
122
171
  * @private
123
172
  */
124
- renderMessage(t, s) {
125
- return c`
126
- <span style="--toast-message-max-width: ${s}px" data-test-id="${i}-message">
173
+ renderMessage(t, i) {
174
+ return l`
175
+ <span style="--toast-message-max-width: ${i}px" data-test-id="${o}-message">
127
176
  ${t}
128
177
  </span>
129
178
  `;
@@ -135,7 +184,7 @@ class r extends A(y) {
135
184
  * @private
136
185
  */
137
186
  closeToastComponent() {
138
- this.isOpen = !1, v(this, N, { targetNotification: this });
187
+ this.isOpen = !1, f(this, M, { targetNotification: this }), this.abortAndCleanEventListeners();
139
188
  }
140
189
  /**
141
190
  * Util method that returns an icon from a variant that has default icon.
@@ -145,15 +194,15 @@ class r extends A(y) {
145
194
  getVariantIcon() {
146
195
  switch (this.variant) {
147
196
  case "info":
148
- return c`<icon-info-circle class="${a}-icon" size="s" data-test-id="${i}-heading-icon-info"></icon-info-circle>`;
197
+ return l`<icon-info-circle class="${s}-icon" size="s" data-test-id="${o}-heading-icon-info"></icon-info-circle>`;
149
198
  case "success":
150
- return c`<icon-check-circle class="${a}-icon" size="s" data-test-id="${i}-heading-icon-success"></icon-check-circle>`;
199
+ return l`<icon-check-circle class="${s}-icon" size="s" data-test-id="${o}-heading-icon-success"></icon-check-circle>`;
151
200
  case "warning":
152
- return c`<icon-alert-triangle class="${a}-icon" size="s" data-test-id="${i}-heading-icon-warning"></icon-alert-triangle>`;
201
+ return l`<icon-alert-triangle class="${s}-icon" size="s" data-test-id="${o}-heading-icon-warning"></icon-alert-triangle>`;
153
202
  case "error":
154
- return c`<icon-alert-circle class="${a}-icon" size="s" data-test-id="${i}-heading-icon-error"></icon-alert-circle>`;
203
+ return l`<icon-alert-circle class="${s}-icon" size="s" data-test-id="${o}-heading-icon-error"></icon-alert-circle>`;
155
204
  default:
156
- return l;
205
+ return d;
157
206
  }
158
207
  }
159
208
  /**
@@ -177,74 +226,84 @@ class r extends A(y) {
177
226
  render() {
178
227
  const {
179
228
  isOpen: t,
180
- variant: s,
181
- message: o,
182
- isDismissible: n,
183
- leadingAction: e,
184
- isMultiline: d,
185
- isStrong: m,
229
+ variant: i,
230
+ message: n,
231
+ isDismissible: e,
232
+ leadingAction: r,
233
+ isMultiline: p,
234
+ isStrong: b,
186
235
  _messageAreaMaxWidth: x
187
- } = this;
188
- if (!t)
189
- return l;
190
- const u = {
191
- [a]: !0,
192
- [`${a}--${s}`]: !0,
193
- [`${a}--strong`]: m
194
- }, $ = {
195
- [`${a}-messageArea`]: !0,
196
- [`${a}--multiline`]: d
236
+ } = this, m = {
237
+ [s]: !0,
238
+ [`${s}--${i}`]: !0,
239
+ [`${s}--strong`]: b,
240
+ [`${s}--animate-in`]: t,
241
+ [`${s}--animate-out`]: !t
242
+ }, A = {
243
+ [`${s}-messageArea`]: !0,
244
+ [`${s}--multiline`]: p
197
245
  };
198
- return c`
199
- <div data-test-id="${i}" class="${b(u)}">
200
- <div class="${a}-contentArea">
201
- <div class="${b($)}">
202
- ${this.variantHasIcon(s) ? this.getVariantIcon() : l}
203
- ${o === "" ? l : this.renderMessage(o, x)}
246
+ return l`
247
+ <div
248
+ data-test-id="${o}"
249
+ class="${$(m)}"
250
+ aria-live="${i === "error" ? "assertive" : "polite"}"
251
+ >
252
+ <div class="${s}-contentArea">
253
+ <div class="${$(A)}">
254
+ ${this.variantHasIcon(i) ? this.getVariantIcon() : d}
255
+ ${n === "" ? d : this.renderMessage(n, x)}
204
256
  </div>
205
- <div class="${a}-actionsArea">
206
- ${!d && (e != null && e.text) ? this.renderActionButton(e) : l}
207
- ${n ? this.renderCloseButton() : l}
257
+ <div class="${s}-actionsArea">
258
+ ${!p && (r != null && r.text) ? this.renderActionButton(r) : d}
259
+ ${e ? this.renderCloseButton() : d}
208
260
  </div>
209
261
  </div>
210
- ${d && (e != null && e.text) ? this.renderFooter() : l}
262
+ ${p && (r != null && r.text) ? this.renderFooter() : d}
211
263
  </div>`;
212
264
  }
213
- }
214
- r.styles = w(C);
215
- p([
216
- h()
217
- ], r.prototype, "message", 2);
218
- p([
219
- h({ type: Boolean })
220
- ], r.prototype, "isOpen", 2);
221
- p([
222
- h(),
223
- M(i, _, g.variant)
224
- ], r.prototype, "variant", 2);
225
- p([
226
- h({ type: Boolean })
227
- ], r.prototype, "isStrong", 2);
228
- p([
229
- h({ type: Boolean })
230
- ], r.prototype, "isDismissible", 2);
231
- p([
232
- h({ type: Boolean })
233
- ], r.prototype, "isMultiline", 2);
234
- p([
235
- h({ type: Object })
236
- ], r.prototype, "leadingAction", 2);
237
- p([
238
- O("pie-button")
239
- ], r.prototype, "actionButton", 2);
240
- B(i, r);
265
+ };
266
+ g.styles = w(k);
267
+ let a = g;
268
+ c([
269
+ u()
270
+ ], a.prototype, "message");
271
+ c([
272
+ u({ type: Boolean })
273
+ ], a.prototype, "isOpen");
274
+ c([
275
+ u(),
276
+ E(o, B, h.variant)
277
+ ], a.prototype, "variant");
278
+ c([
279
+ u({ type: Boolean })
280
+ ], a.prototype, "isStrong");
281
+ c([
282
+ u({ type: Boolean })
283
+ ], a.prototype, "isDismissible");
284
+ c([
285
+ u({ type: Boolean })
286
+ ], a.prototype, "isMultiline");
287
+ c([
288
+ u({ type: Object })
289
+ ], a.prototype, "leadingAction");
290
+ c([
291
+ u({ type: Number })
292
+ ], a.prototype, "duration");
293
+ c([
294
+ y("pie-button")
295
+ ], a.prototype, "actionButton");
296
+ c([
297
+ y("pie-icon-button")
298
+ ], a.prototype, "closeButton");
299
+ _(o, a);
241
300
  export {
242
- N as ON_TOAST_CLOSE_EVENT,
243
- I as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
244
- k as ON_TOAST_OPEN_EVENT,
245
- r as PieToast,
246
- a as componentClass,
247
- i as componentSelector,
248
- g as defaultProps,
249
- _ as variants
301
+ M as ON_TOAST_CLOSE_EVENT,
302
+ T as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
303
+ I as ON_TOAST_OPEN_EVENT,
304
+ a as PieToast,
305
+ s as componentClass,
306
+ o as componentSelector,
307
+ h as defaultProps,
308
+ B as variants
250
309
  };
package/dist/react.d.ts CHANGED
@@ -2,7 +2,6 @@ import { ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
2
  import type { CSSResult } from 'lit';
3
3
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
4
4
  import type { LitElement } from 'lit';
5
- import type { nothing } from 'lit';
6
5
  import type { PropertyValues } from 'lit';
7
6
  import * as React_2 from 'react';
8
7
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
@@ -61,10 +60,27 @@ declare class PieToast_2 extends PieToast_base implements ToastProps {
61
60
  isDismissible: boolean;
62
61
  isMultiline: boolean;
63
62
  leadingAction: ToastProps['leadingAction'];
63
+ duration: number | null;
64
64
  actionButton?: HTMLElement;
65
+ closeButton?: HTMLElement;
65
66
  private _actionButtonOffset;
66
67
  private _messageAreaMaxWidth;
68
+ private _timeoutId;
69
+ private _abortController;
67
70
  static styles: CSSResult;
71
+ /**
72
+ * Create a timeout function and set its id into a private attribute.
73
+ *
74
+ * @private
75
+ */
76
+ private setAutoDismiss;
77
+ /**
78
+ * If the _abortController is set, it aborts all event
79
+ * listeners in this controller and the controller turns into null.
80
+ *
81
+ * @private
82
+ */
83
+ private abortAndCleanEventListeners;
68
84
  /**
69
85
  * Calculates and returns the width of the message based on the toast properties.
70
86
  *
@@ -76,9 +92,29 @@ declare class PieToast_2 extends PieToast_base implements ToastProps {
76
92
  * @returns {number} - The width of the message in pixels.
77
93
  */
78
94
  private getMessageMaxWidth;
95
+ /**
96
+ * Adds event listeners to the specified element for handling the auto dismiss behavior.
97
+ *
98
+ * @param {typeof this | HTMLElement | undefined} element - The element to which the listeners will be added. It can be the current instance, an HTMLElement, or undefined.
99
+ * @param {keyof HTMLElementEventMap} inEvent - The event type to listen for when entering the element. (e.g., 'mouseenter', 'focusin').
100
+ * @param {keyof HTMLElementEventMap} outEvent - The event type to listen for when leaving the element. (e.g., 'mouseleave', 'focusout').
101
+ * @param {AddEventListenerOptions['signal']} abortSignal - An AbortSignal that can be used to remove the event listeners.
102
+ *
103
+ * @private
104
+ */
105
+ private addListenersToElement;
106
+ /**
107
+ * It creates all event listeners to handle the auto-dismiss capability
108
+ * as well the controller responsible to remove the events when needed.
109
+ *
110
+ * @private
111
+ */
112
+ private createAutoDismissEventListeners;
79
113
  /**
80
114
  * Lifecycle method executed when component is updated.
81
115
  * It dispatches an event if toast is opened.
116
+ * It adds event listeners when toast is opened and if the duration is not null
117
+ * It aborts all event listeners when toast is closed.
82
118
  * It calculates _messageAreaMaxWidth
83
119
  */
84
120
  protected updated(_changedProperties: PropertyValues<this>): Promise<void>;
@@ -149,7 +185,7 @@ declare class PieToast_2 extends PieToast_base implements ToastProps {
149
185
  * @private
150
186
  */
151
187
  private shouldNotUseInverseBtnVariant;
152
- render(): TemplateResult<1> | typeof nothing;
188
+ render(): TemplateResult<1>;
153
189
  }
154
190
 
155
191
  declare const PieToast_base: GenericConstructor<RTLInterface> & typeof LitElement;
@@ -185,6 +221,12 @@ export declare interface ToastProps {
185
221
  * The leading action for the toast.
186
222
  */
187
223
  leadingAction?: ActionProps;
224
+ /**
225
+ * Sets the duration of the toast in milliseconds before it auto-dismisses.
226
+ * If the value is null auto-dismiss is disabled
227
+ * If the value is not provided it auto-dismisses after 5 seconds (5000 milliseconds)
228
+ */
229
+ duration?: number | null;
188
230
  }
189
231
 
190
232
  export declare const variants: readonly ["neutral", "info", "warning", "success", "error"];
package/dist/react.js CHANGED
@@ -1,32 +1,21 @@
1
1
  import * as t from "react";
2
- import { createComponent as o } from "@lit/react";
3
- import { PieToast as e } from "./index.js";
4
- import { ON_TOAST_CLOSE_EVENT as A, ON_TOAST_LEADING_ACTION_CLICK_EVENT as S, ON_TOAST_OPEN_EVENT as I, componentClass as L, componentSelector as V, defaultProps as d, variants as v } from "./index.js";
5
- import "lit";
6
- import "lit/decorators.js";
7
- import "lit/directives/class-map.js";
8
- import "@justeattakeaway/pie-webc-core";
9
- import "@justeattakeaway/pie-icon-button";
10
- import "@justeattakeaway/pie-icons-webc/dist/IconClose.js";
11
- import "@justeattakeaway/pie-icons-webc/dist/IconInfoCircle.js";
12
- import "@justeattakeaway/pie-icons-webc/dist/IconAlertCircle.js";
13
- import "@justeattakeaway/pie-icons-webc/dist/IconAlertTriangle.js";
14
- import "@justeattakeaway/pie-icons-webc/dist/IconCheckCircle.js";
15
- import "@justeattakeaway/pie-button";
16
- const r = o({
2
+ import { createComponent as e } from "@lit/react";
3
+ import { PieToast as o } from "./index.js";
4
+ import { ON_TOAST_CLOSE_EVENT as n, ON_TOAST_LEADING_ACTION_CLICK_EVENT as p, ON_TOAST_OPEN_EVENT as N, componentClass as _, componentSelector as c, defaultProps as E, variants as O } from "./index.js";
5
+ const a = e({
17
6
  displayName: "PieToast",
18
- elementClass: e,
7
+ elementClass: o,
19
8
  react: t,
20
9
  tagName: "pie-toast",
21
10
  events: {}
22
- }), P = r;
11
+ }), r = a;
23
12
  export {
24
- A as ON_TOAST_CLOSE_EVENT,
25
- S as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
26
- I as ON_TOAST_OPEN_EVENT,
27
- P as PieToast,
28
- L as componentClass,
29
- V as componentSelector,
30
- d as defaultProps,
31
- v as variants
13
+ n as ON_TOAST_CLOSE_EVENT,
14
+ p as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
15
+ N as ON_TOAST_OPEN_EVENT,
16
+ r as PieToast,
17
+ _ as componentClass,
18
+ c as componentSelector,
19
+ E as defaultProps,
20
+ O as variants
32
21
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-toast",
3
3
  "description": "PIE Design System Toast built using Web Components",
4
- "version": "0.3.3",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -37,14 +37,14 @@
37
37
  "devDependencies": {
38
38
  "@custom-elements-manifest/analyzer": "0.9.0",
39
39
  "@justeattakeaway/pie-components-config": "0.18.0",
40
- "@justeattakeaway/pie-css": "0.12.1",
40
+ "@justeattakeaway/pie-css": "0.13.1",
41
41
  "cem-plugin-module-file-extensions": "0.0.5"
42
42
  },
43
43
  "dependencies": {
44
- "@justeattakeaway/pie-button": "0.49.1",
45
- "@justeattakeaway/pie-icon-button": "0.28.12",
46
- "@justeattakeaway/pie-icons-webc": "0.25.1",
47
- "@justeattakeaway/pie-webc-core": "0.24.0"
44
+ "@justeattakeaway/pie-button": "0.49.3",
45
+ "@justeattakeaway/pie-icon-button": "0.28.14",
46
+ "@justeattakeaway/pie-icons-webc": "0.25.3",
47
+ "@justeattakeaway/pie-webc-core": "0.24.2"
48
48
  },
49
49
  "volta": {
50
50
  "extends": "../../../package.json"
package/src/defs-react.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import type React from 'react';
2
2
  /**
3
3
  * TODO: Verify if ReactBaseType can be set as a more specific React interface
4
4
  * Use the React IntrinsicElements interface to find how to map standard HTML elements to existing React Interfaces
package/src/defs.ts CHANGED
@@ -44,6 +44,13 @@ export interface ToastProps {
44
44
  * The leading action for the toast.
45
45
  */
46
46
  leadingAction?: ActionProps;
47
+
48
+ /**
49
+ * Sets the duration of the toast in milliseconds before it auto-dismisses.
50
+ * If the value is null auto-dismiss is disabled
51
+ * If the value is not provided it auto-dismisses after 5 seconds (5000 milliseconds)
52
+ */
53
+ duration?: number | null;
47
54
  }
48
55
 
49
56
  export const componentSelector = 'pie-toast';
@@ -78,4 +85,5 @@ export const defaultProps: DefaultProps = {
78
85
  isStrong: false,
79
86
  isDismissible: true,
80
87
  isMultiline: false,
88
+ duration: 5000,
81
89
  };
package/src/index.ts CHANGED
@@ -24,10 +24,10 @@ import '@justeattakeaway/pie-button';
24
24
 
25
25
  import styles from './toast.scss?inline';
26
26
  import {
27
- ToastProps,
27
+ type ToastProps,
28
28
  componentSelector,
29
29
  componentClass,
30
- ActionProps,
30
+ type ActionProps,
31
31
  ON_TOAST_CLOSE_EVENT,
32
32
  ON_TOAST_OPEN_EVENT,
33
33
  ON_TOAST_LEADING_ACTION_CLICK_EVENT,
@@ -64,15 +64,52 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
64
64
  @property({ type: Object })
65
65
  public leadingAction: ToastProps['leadingAction'];
66
66
 
67
+ @property({ type: Number })
68
+ public duration = defaultProps.duration;
69
+
67
70
  @query('pie-button') actionButton?: HTMLElement;
68
71
 
72
+ @query('pie-icon-button') closeButton?: HTMLElement;
73
+
69
74
  private _actionButtonOffset = 0;
70
75
 
71
76
  private _messageAreaMaxWidth = 0;
72
77
 
78
+ private _timeoutId: NodeJS.Timeout | null = null;
79
+
80
+ private _abortController: AbortController | null = null;
81
+
73
82
  // Renders a `CSSResult` generated from SCSS by Vite
74
83
  static styles = unsafeCSS(styles);
75
84
 
85
+ /**
86
+ * Create a timeout function and set its id into a private attribute.
87
+ *
88
+ * @private
89
+ */
90
+ private setAutoDismiss () {
91
+ if (this.duration === null) {
92
+ return;
93
+ }
94
+
95
+ this._timeoutId = setTimeout(() => {
96
+ this.closeToastComponent();
97
+ }, this.duration);
98
+ }
99
+
100
+ /**
101
+ * If the _abortController is set, it aborts all event
102
+ * listeners in this controller and the controller turns into null.
103
+ *
104
+ * @private
105
+ */
106
+ private abortAndCleanEventListeners () {
107
+ if (this._abortController) {
108
+ this._abortController.abort();
109
+ this._abortController = null;
110
+ }
111
+ }
112
+
76
113
  /**
77
114
  * Calculates and returns the width of the message based on the toast properties.
78
115
  *
@@ -111,16 +148,76 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
111
148
  return toastMaxWidthWithoutPadding - offset;
112
149
  }
113
150
 
151
+ /**
152
+ * Adds event listeners to the specified element for handling the auto dismiss behavior.
153
+ *
154
+ * @param {typeof this | HTMLElement | undefined} element - The element to which the listeners will be added. It can be the current instance, an HTMLElement, or undefined.
155
+ * @param {keyof HTMLElementEventMap} inEvent - The event type to listen for when entering the element. (e.g., 'mouseenter', 'focusin').
156
+ * @param {keyof HTMLElementEventMap} outEvent - The event type to listen for when leaving the element. (e.g., 'mouseleave', 'focusout').
157
+ * @param {AddEventListenerOptions['signal']} abortSignal - An AbortSignal that can be used to remove the event listeners.
158
+ *
159
+ * @private
160
+ */
161
+ private addListenersToElement (
162
+ element: typeof this | HTMLElement | undefined,
163
+ inEvent: keyof HTMLElementEventMap,
164
+ outEvent: keyof HTMLElementEventMap,
165
+ abortSignal: AddEventListenerOptions['signal'],
166
+ ) {
167
+ if (element) {
168
+ element.addEventListener(inEvent, () => {
169
+ if (this._timeoutId) {
170
+ clearTimeout(this._timeoutId);
171
+ }
172
+ }, { signal: abortSignal });
173
+ element.addEventListener(outEvent, () => {
174
+ this.setAutoDismiss();
175
+ }, { signal: abortSignal });
176
+ }
177
+ }
178
+
179
+ /**
180
+ * It creates all event listeners to handle the auto-dismiss capability
181
+ * as well the controller responsible to remove the events when needed.
182
+ *
183
+ * @private
184
+ */
185
+ private createAutoDismissEventListeners () {
186
+ this._abortController = new AbortController();
187
+
188
+ this.setAutoDismiss();
189
+
190
+ const { signal } = this._abortController;
191
+
192
+ this.addListenersToElement(this.actionButton, 'focus', 'focusout', signal);
193
+ this.addListenersToElement(this.closeButton, 'focus', 'focusout', signal);
194
+ this.addListenersToElement(this, 'mouseover', 'mouseout', signal);
195
+ }
196
+
114
197
  /**
115
198
  * Lifecycle method executed when component is updated.
116
199
  * It dispatches an event if toast is opened.
200
+ * It adds event listeners when toast is opened and if the duration is not null
201
+ * It aborts all event listeners when toast is closed.
117
202
  * It calculates _messageAreaMaxWidth
118
203
  */
119
204
  protected async updated (_changedProperties: PropertyValues<this>) {
120
205
  if (_changedProperties.has('isOpen') && this.isOpen) {
121
206
  dispatchCustomEvent(this, ON_TOAST_OPEN_EVENT, { targetNotification: this });
207
+
208
+ if (this.duration !== null) {
209
+ this.createAutoDismissEventListeners();
210
+ }
211
+ }
212
+
213
+ if (_changedProperties.has('isOpen') && !this.isOpen) {
214
+ this.abortAndCleanEventListeners();
122
215
  }
123
216
 
217
+ // This lifecycle method is async on purpose because we
218
+ // need to wait for the component to complete its rendering
219
+ // so we can calculate _messageAreaMaxWidth based on
220
+ // existing components such as icons and action buttons.
124
221
  await this.updateComplete;
125
222
 
126
223
  if (this.actionButton) {
@@ -131,13 +228,19 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
131
228
 
132
229
  this._messageAreaMaxWidth = this.getMessageMaxWidth(hasIcon, this.isMultiline, !!this.leadingAction?.text, this.isDismissible);
133
230
 
231
+ // It checks if there are changes on one of the properties
232
+ // below and requests a new update in order to repeat the
233
+ // lifecycle and perform new calculations.
234
+ // This will make sure that all components will re-render
235
+ // properly on Storybook.
134
236
  if (
135
237
  _changedProperties.has('variant') ||
136
238
  _changedProperties.has('isStrong') ||
137
239
  _changedProperties.has('message') ||
138
240
  _changedProperties.has('isDismissible') ||
139
241
  _changedProperties.has('isMultiline') ||
140
- _changedProperties.has('leadingAction')) {
242
+ _changedProperties.has('leadingAction') ||
243
+ _changedProperties.has('duration')) {
141
244
  this.requestUpdate();
142
245
  }
143
246
  }
@@ -234,6 +337,7 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
234
337
  private closeToastComponent () {
235
338
  this.isOpen = false;
236
339
  dispatchCustomEvent(this, ON_TOAST_CLOSE_EVENT, { targetNotification: this });
340
+ this.abortAndCleanEventListeners();
237
341
  }
238
342
 
239
343
  /**
@@ -288,14 +392,12 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
288
392
  _messageAreaMaxWidth,
289
393
  } = this;
290
394
 
291
- if (!isOpen) {
292
- return nothing;
293
- }
294
-
295
395
  const componentWrapperClasses = {
296
396
  [componentClass]: true,
297
397
  [`${componentClass}--${variant}`]: true,
298
398
  [`${componentClass}--strong`]: isStrong,
399
+ [`${componentClass}--animate-in`]: isOpen,
400
+ [`${componentClass}--animate-out`]: !isOpen,
299
401
  };
300
402
 
301
403
  const messageAreaClasses = {
@@ -304,11 +406,15 @@ export class PieToast extends RtlMixin(LitElement) implements ToastProps {
304
406
  };
305
407
 
306
408
  return html`
307
- <div data-test-id="${componentSelector}" class="${classMap(componentWrapperClasses)}">
409
+ <div
410
+ data-test-id="${componentSelector}"
411
+ class="${classMap(componentWrapperClasses)}"
412
+ aria-live="${variant === 'error' ? 'assertive' : 'polite'}"
413
+ >
308
414
  <div class="${componentClass}-contentArea">
309
415
  <div class="${classMap(messageAreaClasses)}">
310
416
  ${this.variantHasIcon(variant) ? this.getVariantIcon() : nothing}
311
- ${message === '' ? nothing : this.renderMessage(message, _messageAreaMaxWidth)}
417
+ ${message === '' ? nothing : this.renderMessage(message, _messageAreaMaxWidth)}
312
418
  </div>
313
419
  <div class="${componentClass}-actionsArea">
314
420
  ${!isMultiline && leadingAction?.text ? this.renderActionButton(leadingAction) : nothing}
package/src/react.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { createComponent } from '@lit/react';
3
3
  import { PieToast as PieToastLit } from './index';
4
- import { ToastProps } from './defs';
4
+ import { type ToastProps } from './defs';
5
5
 
6
6
  export * from './defs';
7
7
 
package/src/toast.scss CHANGED
@@ -25,6 +25,24 @@
25
25
  color: var(--toast-font-color);
26
26
  font-size: var(--toast-font-size);
27
27
  line-height: var(--toast-line-height);
28
+ transition-property: all;
29
+ transition-duration: var(--dt-motion-timing-100);
30
+ transition-timing-function: var(--dt-motion-easing-in);
31
+ }
32
+
33
+ .c-toast--animate-in {
34
+ animation-duration: var(--dt-motion-timing-200);
35
+ animation-name: animate-in;
36
+ animation-timing-function: var(--dt-motion-easing-in);
37
+ transform: translateX(0);
38
+ }
39
+
40
+ .c-toast--animate-out {
41
+ animation-duration: var(--dt-motion-timing-100);
42
+ animation-name: animate-out;
43
+ animation-timing-function: var(--dt-motion-easing-out);
44
+ transform: translateX(100%);
45
+ opacity: 0;
28
46
  }
29
47
 
30
48
  .c-toast--info {
@@ -114,3 +132,22 @@
114
132
  color: var(--toast-icon-fill);
115
133
  }
116
134
 
135
+ @keyframes animate-in {
136
+ from {
137
+ transform: translateX(100%);
138
+ }
139
+
140
+ to {
141
+ transform: translateX(0);
142
+ }
143
+ }
144
+
145
+ @keyframes animate-out {
146
+ from {
147
+ transform: translateX(0);
148
+ }
149
+
150
+ to {
151
+ transform: translateX(100%);
152
+ }
153
+ }