@justeattakeaway/pie-toast 0.1.0 → 0.2.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.
@@ -11,8 +11,100 @@
11
11
  {
12
12
  "kind": "javascript-module",
13
13
  "path": "src/defs.js",
14
- "declarations": [],
15
- "exports": []
14
+ "declarations": [
15
+ {
16
+ "kind": "variable",
17
+ "name": "componentSelector",
18
+ "type": {
19
+ "text": "string"
20
+ },
21
+ "default": "'pie-toast'"
22
+ },
23
+ {
24
+ "kind": "variable",
25
+ "name": "componentClass",
26
+ "type": {
27
+ "text": "string"
28
+ },
29
+ "default": "'c-toast'"
30
+ },
31
+ {
32
+ "kind": "variable",
33
+ "name": "ON_TOAST_CLOSE_EVENT",
34
+ "default": "`${componentSelector}-close`",
35
+ "description": "Event name for when the toast is closed."
36
+ },
37
+ {
38
+ "kind": "variable",
39
+ "name": "ON_TOAST_OPEN_EVENT",
40
+ "default": "`${componentSelector}-open`",
41
+ "description": "Event name for when the toast is opened."
42
+ },
43
+ {
44
+ "kind": "variable",
45
+ "name": "ON_TOAST_LEADING_ACTION_CLICK_EVENT",
46
+ "default": "`${componentSelector}-leading-action-click`",
47
+ "description": "Event name for when the toast leading action is clicked."
48
+ },
49
+ {
50
+ "kind": "variable",
51
+ "name": "defaultProps",
52
+ "type": {
53
+ "text": "DefaultProps"
54
+ },
55
+ "default": "{\n isOpen: true,\n isDismissible: true,\n isMultiline: false,\n}"
56
+ }
57
+ ],
58
+ "exports": [
59
+ {
60
+ "kind": "js",
61
+ "name": "componentSelector",
62
+ "declaration": {
63
+ "name": "componentSelector",
64
+ "module": "src/defs.js"
65
+ }
66
+ },
67
+ {
68
+ "kind": "js",
69
+ "name": "componentClass",
70
+ "declaration": {
71
+ "name": "componentClass",
72
+ "module": "src/defs.js"
73
+ }
74
+ },
75
+ {
76
+ "kind": "js",
77
+ "name": "ON_TOAST_CLOSE_EVENT",
78
+ "declaration": {
79
+ "name": "ON_TOAST_CLOSE_EVENT",
80
+ "module": "src/defs.js"
81
+ }
82
+ },
83
+ {
84
+ "kind": "js",
85
+ "name": "ON_TOAST_OPEN_EVENT",
86
+ "declaration": {
87
+ "name": "ON_TOAST_OPEN_EVENT",
88
+ "module": "src/defs.js"
89
+ }
90
+ },
91
+ {
92
+ "kind": "js",
93
+ "name": "ON_TOAST_LEADING_ACTION_CLICK_EVENT",
94
+ "declaration": {
95
+ "name": "ON_TOAST_LEADING_ACTION_CLICK_EVENT",
96
+ "module": "src/defs.js"
97
+ }
98
+ },
99
+ {
100
+ "kind": "js",
101
+ "name": "defaultProps",
102
+ "declaration": {
103
+ "name": "defaultProps",
104
+ "module": "src/defs.js"
105
+ }
106
+ }
107
+ ]
16
108
  },
17
109
  {
18
110
  "kind": "javascript-module",
@@ -22,7 +114,216 @@
22
114
  "kind": "class",
23
115
  "description": "",
24
116
  "name": "PieToast",
25
- "members": [],
117
+ "members": [
118
+ {
119
+ "kind": "field",
120
+ "name": "message",
121
+ "type": {
122
+ "text": "ToastProps['message']"
123
+ },
124
+ "privacy": "public",
125
+ "default": "''",
126
+ "attribute": "message"
127
+ },
128
+ {
129
+ "kind": "field",
130
+ "name": "isOpen",
131
+ "privacy": "public",
132
+ "attribute": "isOpen"
133
+ },
134
+ {
135
+ "kind": "field",
136
+ "name": "isDismissible",
137
+ "privacy": "public",
138
+ "attribute": "isDismissible"
139
+ },
140
+ {
141
+ "kind": "field",
142
+ "name": "isMultiline",
143
+ "privacy": "public",
144
+ "attribute": "isMultiline"
145
+ },
146
+ {
147
+ "kind": "field",
148
+ "name": "leadingAction",
149
+ "type": {
150
+ "text": "ToastProps['leadingAction']"
151
+ },
152
+ "privacy": "public",
153
+ "attribute": "leadingAction"
154
+ },
155
+ {
156
+ "kind": "field",
157
+ "name": "actionButton",
158
+ "type": {
159
+ "text": "HTMLElement | undefined"
160
+ }
161
+ },
162
+ {
163
+ "kind": "field",
164
+ "name": "_actionButtonOffset",
165
+ "type": {
166
+ "text": "number"
167
+ },
168
+ "privacy": "private",
169
+ "default": "0"
170
+ },
171
+ {
172
+ "kind": "field",
173
+ "name": "_messageAreaMaxWidth",
174
+ "type": {
175
+ "text": "number"
176
+ },
177
+ "privacy": "private",
178
+ "default": "0"
179
+ },
180
+ {
181
+ "kind": "method",
182
+ "name": "getMessageMaxWidth",
183
+ "privacy": "private",
184
+ "return": {
185
+ "type": {
186
+ "text": "number"
187
+ }
188
+ },
189
+ "parameters": [
190
+ {
191
+ "name": "hasIcon",
192
+ "type": {
193
+ "text": "boolean"
194
+ },
195
+ "description": "Indicates if the toast has an icon."
196
+ },
197
+ {
198
+ "name": "isMultiline",
199
+ "type": {
200
+ "text": "boolean"
201
+ },
202
+ "description": "Indicates if the message is multiline."
203
+ },
204
+ {
205
+ "name": "hasActionButton",
206
+ "type": {
207
+ "text": "boolean"
208
+ },
209
+ "description": "Indicates if the toast has an action button."
210
+ },
211
+ {
212
+ "name": "hasCloseIcon",
213
+ "type": {
214
+ "text": "boolean"
215
+ },
216
+ "description": "Indicates if the toast has a close icon."
217
+ }
218
+ ],
219
+ "description": "Calculates and returns the width of the message based on the toast properties."
220
+ },
221
+ {
222
+ "kind": "method",
223
+ "name": "handleActionClick",
224
+ "privacy": "private",
225
+ "description": "It handle the action button action."
226
+ },
227
+ {
228
+ "kind": "method",
229
+ "name": "renderActionButton",
230
+ "privacy": "private",
231
+ "return": {
232
+ "type": {
233
+ "text": "TemplateResult | typeof nothing"
234
+ }
235
+ },
236
+ "parameters": [
237
+ {
238
+ "name": "action",
239
+ "type": {
240
+ "text": "ActionProps"
241
+ },
242
+ "description": "The action properties."
243
+ }
244
+ ],
245
+ "description": "Render the action button depending on action type and its action."
246
+ },
247
+ {
248
+ "kind": "method",
249
+ "name": "renderFooter",
250
+ "privacy": "private",
251
+ "description": "Template for the footer area.\nIt should display only when isMultiline is true as well if has action button.\nCalled within the main render function."
252
+ },
253
+ {
254
+ "kind": "method",
255
+ "name": "renderCloseButton",
256
+ "privacy": "private",
257
+ "return": {
258
+ "type": {
259
+ "text": "TemplateResult"
260
+ }
261
+ },
262
+ "description": "Template for the close button element. Called within the\nmain render function."
263
+ },
264
+ {
265
+ "kind": "method",
266
+ "name": "renderMessage",
267
+ "privacy": "private",
268
+ "return": {
269
+ "type": {
270
+ "text": "TemplateResult"
271
+ }
272
+ },
273
+ "parameters": [
274
+ {
275
+ "name": "message",
276
+ "type": {
277
+ "text": "string"
278
+ },
279
+ "description": "The message to be displayed."
280
+ },
281
+ {
282
+ "name": "messageAreaMaxWidth",
283
+ "type": {
284
+ "text": "number"
285
+ },
286
+ "description": "The maximum width of the message area calculated in the lifecycle method."
287
+ }
288
+ ],
289
+ "description": "Template for the toast message. Called within the\nmain render function."
290
+ },
291
+ {
292
+ "kind": "method",
293
+ "name": "closeToastComponent",
294
+ "privacy": "private",
295
+ "description": "Util method responsible to close the component.\nIt handles the action when user clicks in the close button and triggers an event when is executed."
296
+ }
297
+ ],
298
+ "attributes": [
299
+ {
300
+ "name": "message",
301
+ "type": {
302
+ "text": "ToastProps['message']"
303
+ },
304
+ "default": "''",
305
+ "fieldName": "message"
306
+ },
307
+ {
308
+ "name": "isOpen",
309
+ "fieldName": "isOpen"
310
+ },
311
+ {
312
+ "name": "isDismissible",
313
+ "fieldName": "isDismissible"
314
+ },
315
+ {
316
+ "name": "isMultiline",
317
+ "fieldName": "isMultiline"
318
+ },
319
+ {
320
+ "name": "leadingAction",
321
+ "type": {
322
+ "text": "ToastProps['leadingAction']"
323
+ },
324
+ "fieldName": "leadingAction"
325
+ }
326
+ ],
26
327
  "mixins": [
27
328
  {
28
329
  "name": "RtlMixin",
package/dist/index.d.ts CHANGED
@@ -1,20 +1,155 @@
1
+ import { ComponentDefaultPropsGeneric } from '@justeattakeaway/pie-webc-core';
1
2
  import type { CSSResult } from 'lit';
2
3
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
3
4
  import type { LitElement } from 'lit';
5
+ import type { nothing } from 'lit';
6
+ import type { PropertyValues } from 'lit';
4
7
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
5
- import type { TemplateResult } from 'lit-html';
8
+ import type { TemplateResult } from 'lit';
9
+
10
+ export declare type ActionProps = {
11
+ /**
12
+ * The text to display inside the button.
13
+ */
14
+ text: string;
15
+ /**
16
+ * The ARIA label for the button.
17
+ */
18
+ ariaLabel?: string;
19
+ };
20
+
21
+ export declare const componentClass = "c-toast";
22
+
23
+ export declare const componentSelector = "pie-toast";
24
+
25
+ export declare type DefaultProps = ComponentDefaultPropsGeneric<ToastProps, 'isOpen' | 'isDismissible' | 'isMultiline'>;
26
+
27
+ export declare const defaultProps: DefaultProps;
28
+
29
+ /**
30
+ * Event name for when the toast is closed.
31
+ *
32
+ * @constant
33
+ */
34
+ export declare const ON_TOAST_CLOSE_EVENT: string;
35
+
36
+ /**
37
+ * Event name for when the toast leading action is clicked.
38
+ *
39
+ * @constant
40
+ */
41
+ export declare const ON_TOAST_LEADING_ACTION_CLICK_EVENT: string;
42
+
43
+ /**
44
+ * Event name for when the toast is opened.
45
+ *
46
+ * @constant
47
+ */
48
+ export declare const ON_TOAST_OPEN_EVENT: string;
6
49
 
7
50
  /**
8
51
  * @tagname pie-toast
9
52
  */
10
53
  export declare class PieToast extends PieToast_base implements ToastProps {
11
- render(): TemplateResult<1>;
54
+ message: ToastProps['message'];
55
+ isOpen: any;
56
+ isDismissible: any;
57
+ isMultiline: any;
58
+ leadingAction: ToastProps['leadingAction'];
59
+ actionButton?: HTMLElement;
60
+ private _actionButtonOffset;
61
+ private _messageAreaMaxWidth;
12
62
  static styles: CSSResult;
63
+ /**
64
+ * Calculates and returns the width of the message based on the toast properties.
65
+ *
66
+ * @param {boolean} hasIcon - Indicates if the toast has an icon.
67
+ * @param {boolean} isMultiline - Indicates if the message is multiline.
68
+ * @param {boolean} hasActionButton - Indicates if the toast has an action button.
69
+ * @param {boolean} hasCloseIcon - Indicates if the toast has a close icon.
70
+ *
71
+ * @returns {number} - The width of the message in pixels.
72
+ */
73
+ private getMessageMaxWidth;
74
+ /**
75
+ * Lifecycle method executed when component is updated.
76
+ * It dispatches an event if toast is opened.
77
+ * It calculates _messageAreaMaxWidth
78
+ */
79
+ protected updated(_changedProperties: PropertyValues<this>): Promise<void>;
80
+ /**
81
+ * It handle the action button action.
82
+ *
83
+ * @private
84
+ */
85
+ private handleActionClick;
86
+ /**
87
+ * Render the action button depending on action type and its action.
88
+ *
89
+ * @param {ActionProps} action - The action properties.
90
+ *
91
+ * @returns {TemplateResult | typeof nothing} - The rendered action button or nothing if the action text is not defined.
92
+ * @private
93
+ */
94
+ private renderActionButton;
95
+ /**
96
+ * Template for the footer area.
97
+ * It should display only when isMultiline is true as well if has action button.
98
+ * Called within the main render function.
99
+ *
100
+ * @private
101
+ */
102
+ private renderFooter;
103
+ /**
104
+ * Template for the close button element. Called within the
105
+ * main render function.
106
+ *
107
+ * @private
108
+ */
109
+ private renderCloseButton;
110
+ /**
111
+ * Template for the toast message. Called within the
112
+ * main render function.
113
+ *
114
+ * @param {string} message - The message to be displayed.
115
+ * @param {number} messageAreaMaxWidth - The maximum width of the message area calculated in the lifecycle method.
116
+ *
117
+ * @private
118
+ */
119
+ private renderMessage;
120
+ /**
121
+ * Util method responsible to close the component.
122
+ * It handles the action when user clicks in the close button and triggers an event when is executed.
123
+ *
124
+ * @private
125
+ */
126
+ private closeToastComponent;
127
+ render(): TemplateResult<1> | typeof nothing;
13
128
  }
14
129
 
15
130
  declare const PieToast_base: GenericConstructor<RTLInterface> & typeof LitElement;
16
131
 
17
132
  export declare interface ToastProps {
133
+ /**
134
+ * When true, the toast is set to be open and visible.
135
+ */
136
+ isOpen?: boolean;
137
+ /**
138
+ * When true, allows dismissing the toast by clicking on the close button.
139
+ */
140
+ isDismissible?: boolean;
141
+ /**
142
+ * The message content of the toast.
143
+ */
144
+ message: string;
145
+ /**
146
+ * It allows the message content being displayed as multiline limited to three rows.
147
+ */
148
+ isMultiline?: boolean;
149
+ /**
150
+ * The leading action for the toast.
151
+ */
152
+ leadingAction?: ActionProps;
18
153
  }
19
154
 
20
155
  export { }
package/dist/index.js CHANGED
@@ -1,14 +1,194 @@
1
- import { LitElement as t, html as o, unsafeCSS as s } from "lit";
2
- import { RtlMixin as n, defineCustomElement as i } from "@justeattakeaway/pie-webc-core";
3
- const r = `*,*:after,*:before{box-sizing:inherit}
4
- `, l = "pie-toast";
5
- class e extends n(t) {
1
+ import { LitElement as x, html as p, nothing as r, unsafeCSS as b } from "lit";
2
+ import { property as h, query as O } from "lit/decorators.js";
3
+ import { classMap as y } from "lit/directives/class-map.js";
4
+ import { RtlMixin as $, dispatchCustomEvent as g, defineCustomElement as A } from "@justeattakeaway/pie-webc-core";
5
+ import "@justeattakeaway/pie-icon-button";
6
+ import "@justeattakeaway/pie-icons-webc/dist/IconClose.js";
7
+ import "@justeattakeaway/pie-icons-webc/dist/IconPlaceholder.js";
8
+ import "@justeattakeaway/pie-button";
9
+ const M = `*,*: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);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-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}.c-toast-messageArea.is-multiline{align-items:flex-start}.c-toast-messageArea.is-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}
10
+ `, o = "pie-toast", d = "c-toast", _ = `${o}-close`, C = `${o}-open`, w = `${o}-leading-action-click`, m = {
11
+ isOpen: !0,
12
+ isDismissible: !0,
13
+ isMultiline: !1
14
+ };
15
+ var B = Object.defineProperty, T = Object.getOwnPropertyDescriptor, l = (u, t, s, i) => {
16
+ for (var e = i > 1 ? void 0 : i ? T(t, s) : t, a = u.length - 1, c; a >= 0; a--)
17
+ (c = u[a]) && (e = (i ? c(t, s, e) : c(e)) || e);
18
+ return i && e && B(t, s, e), e;
19
+ };
20
+ class n extends $(x) {
21
+ constructor() {
22
+ super(...arguments), this.message = "", this.isOpen = m.isOpen, this.isDismissible = m.isDismissible, this.isMultiline = m.isMultiline, this._actionButtonOffset = 0, this._messageAreaMaxWidth = 0;
23
+ }
24
+ /**
25
+ * Calculates and returns the width of the message based on the toast properties.
26
+ *
27
+ * @param {boolean} hasIcon - Indicates if the toast has an icon.
28
+ * @param {boolean} isMultiline - Indicates if the message is multiline.
29
+ * @param {boolean} hasActionButton - Indicates if the toast has an action button.
30
+ * @param {boolean} hasCloseIcon - Indicates if the toast has a close icon.
31
+ *
32
+ * @returns {number} - The width of the message in pixels.
33
+ */
34
+ getMessageMaxWidth(t, s, i, e) {
35
+ let f = 0;
36
+ return t && (f += 20 + 8), !s && i && (f += this._actionButtonOffset + 8), e && (f += 32 + 8), 392 - f;
37
+ }
38
+ /**
39
+ * Lifecycle method executed when component is updated.
40
+ * It dispatches an event if toast is opened.
41
+ * It calculates _messageAreaMaxWidth
42
+ */
43
+ async updated(t) {
44
+ var i;
45
+ t.has("isOpen") && this.isOpen && g(this, C, { targetNotification: this }), await this.updateComplete, this.actionButton && (this._actionButtonOffset = this.actionButton.offsetWidth);
46
+ const s = !0;
47
+ this._messageAreaMaxWidth = this.getMessageMaxWidth(s, this.isMultiline, !!((i = this.leadingAction) != null && i.text), this.isDismissible), (t.has("message") || t.has("isDismissible") || t.has("isMultiline") || t.has("leadingAction")) && this.requestUpdate();
48
+ }
49
+ /**
50
+ * It handle the action button action.
51
+ *
52
+ * @private
53
+ */
54
+ handleActionClick() {
55
+ g(this, w, { targetNotification: this });
56
+ }
57
+ /**
58
+ * Render the action button depending on action type and its action.
59
+ *
60
+ * @param {ActionProps} action - The action properties.
61
+ *
62
+ * @returns {TemplateResult | typeof nothing} - The rendered action button or nothing if the action text is not defined.
63
+ * @private
64
+ */
65
+ renderActionButton(t) {
66
+ const { text: s, ariaLabel: i } = t;
67
+ return p`
68
+ <pie-button
69
+ variant="ghost-inverse"
70
+ size="xsmall"
71
+ aria-label="${i || r}"
72
+ @click="${() => this.handleActionClick()}"
73
+ data-test-id="${o}-leading-action"
74
+ type="button">
75
+ ${s}
76
+ </pie-button>
77
+ `;
78
+ }
79
+ /**
80
+ * Template for the footer area.
81
+ * It should display only when isMultiline is true as well if has action button.
82
+ * Called within the main render function.
83
+ *
84
+ * @private
85
+ */
86
+ renderFooter() {
87
+ const { leadingAction: t } = this;
88
+ return p`
89
+ <footer class="${d}-footer" data-test-id="${o}-footer" >
90
+ ${t ? this.renderActionButton(t) : r}
91
+ </footer>
92
+ `;
93
+ }
94
+ /**
95
+ * Template for the close button element. Called within the
96
+ * main render function.
97
+ *
98
+ * @private
99
+ */
100
+ renderCloseButton() {
101
+ return p`
102
+ <pie-icon-button
103
+ variant="ghost-inverse"
104
+ size="xsmall"
105
+ data-test-id="${o}-close"
106
+ @click="${this.closeToastComponent}">
107
+ <icon-close></icon-close>
108
+ </pie-icon-button>`;
109
+ }
110
+ /**
111
+ * Template for the toast message. Called within the
112
+ * main render function.
113
+ *
114
+ * @param {string} message - The message to be displayed.
115
+ * @param {number} messageAreaMaxWidth - The maximum width of the message area calculated in the lifecycle method.
116
+ *
117
+ * @private
118
+ */
119
+ renderMessage(t, s) {
120
+ return p`
121
+ <span style="max-width: ${s}px" data-test-id="${o}-message">
122
+ ${t}
123
+ </span>
124
+ `;
125
+ }
126
+ /**
127
+ * Util method responsible to close the component.
128
+ * It handles the action when user clicks in the close button and triggers an event when is executed.
129
+ *
130
+ * @private
131
+ */
132
+ closeToastComponent() {
133
+ this.isOpen = !1, g(this, _, { targetNotification: this });
134
+ }
6
135
  render() {
7
- return o`<h1 data-test-id="pie-toast">Hello world!</h1>`;
136
+ const {
137
+ isOpen: t,
138
+ message: s,
139
+ isDismissible: i,
140
+ leadingAction: e,
141
+ isMultiline: a,
142
+ _messageAreaMaxWidth: c
143
+ } = this;
144
+ if (!t)
145
+ return r;
146
+ const v = {
147
+ [`${d}-messageArea`]: !0,
148
+ "is-multiline": a
149
+ };
150
+ return p`
151
+ <div data-test-id="${o}" class="${d}">
152
+ <div class="${d}-contentArea">
153
+ <div class="${y(v)}">
154
+ <icon-placeholder size="s"></icon-placeholder>
155
+ ${s === "" ? r : this.renderMessage(s, c)}
156
+ </div>
157
+ <div class="${d}-actionsArea">
158
+ ${!a && (e != null && e.text) ? this.renderActionButton(e) : r}
159
+ ${i ? this.renderCloseButton() : r}
160
+ </div>
161
+ </div>
162
+ ${a && (e != null && e.text) ? this.renderFooter() : r}
163
+ </div>`;
8
164
  }
9
165
  }
10
- e.styles = s(r);
11
- i(l, e);
166
+ n.styles = b(M);
167
+ l([
168
+ h()
169
+ ], n.prototype, "message", 2);
170
+ l([
171
+ h({ type: Boolean })
172
+ ], n.prototype, "isOpen", 2);
173
+ l([
174
+ h({ type: Boolean })
175
+ ], n.prototype, "isDismissible", 2);
176
+ l([
177
+ h({ type: Boolean })
178
+ ], n.prototype, "isMultiline", 2);
179
+ l([
180
+ h({ type: Object })
181
+ ], n.prototype, "leadingAction", 2);
182
+ l([
183
+ O("pie-button")
184
+ ], n.prototype, "actionButton", 2);
185
+ A(o, n);
12
186
  export {
13
- e as PieToast
187
+ _ as ON_TOAST_CLOSE_EVENT,
188
+ w as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
189
+ C as ON_TOAST_OPEN_EVENT,
190
+ n as PieToast,
191
+ d as componentClass,
192
+ o as componentSelector,
193
+ m as defaultProps
14
194
  };
package/dist/react.d.ts CHANGED
@@ -1,9 +1,52 @@
1
+ import { ComponentDefaultPropsGeneric } from '@justeattakeaway/pie-webc-core';
1
2
  import type { CSSResult } from 'lit';
2
3
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
3
4
  import type { LitElement } from 'lit';
5
+ import type { nothing } from 'lit';
6
+ import type { PropertyValues } from 'lit';
4
7
  import * as React_2 from 'react';
5
8
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
6
- import type { TemplateResult } from 'lit-html';
9
+ import type { TemplateResult } from 'lit';
10
+
11
+ export declare type ActionProps = {
12
+ /**
13
+ * The text to display inside the button.
14
+ */
15
+ text: string;
16
+ /**
17
+ * The ARIA label for the button.
18
+ */
19
+ ariaLabel?: string;
20
+ };
21
+
22
+ export declare const componentClass = "c-toast";
23
+
24
+ export declare const componentSelector = "pie-toast";
25
+
26
+ export declare type DefaultProps = ComponentDefaultPropsGeneric<ToastProps, 'isOpen' | 'isDismissible' | 'isMultiline'>;
27
+
28
+ export declare const defaultProps: DefaultProps;
29
+
30
+ /**
31
+ * Event name for when the toast is closed.
32
+ *
33
+ * @constant
34
+ */
35
+ export declare const ON_TOAST_CLOSE_EVENT: string;
36
+
37
+ /**
38
+ * Event name for when the toast leading action is clicked.
39
+ *
40
+ * @constant
41
+ */
42
+ export declare const ON_TOAST_LEADING_ACTION_CLICK_EVENT: string;
43
+
44
+ /**
45
+ * Event name for when the toast is opened.
46
+ *
47
+ * @constant
48
+ */
49
+ export declare const ON_TOAST_OPEN_EVENT: string;
7
50
 
8
51
  export declare const PieToast: React_2.ForwardRefExoticComponent<ToastProps & React_2.RefAttributes<PieToast_2> & ReactBaseType>;
9
52
 
@@ -11,8 +54,80 @@ export declare const PieToast: React_2.ForwardRefExoticComponent<ToastProps & Re
11
54
  * @tagname pie-toast
12
55
  */
13
56
  declare class PieToast_2 extends PieToast_base implements ToastProps {
14
- render(): TemplateResult<1>;
57
+ message: ToastProps['message'];
58
+ isOpen: any;
59
+ isDismissible: any;
60
+ isMultiline: any;
61
+ leadingAction: ToastProps['leadingAction'];
62
+ actionButton?: HTMLElement;
63
+ private _actionButtonOffset;
64
+ private _messageAreaMaxWidth;
15
65
  static styles: CSSResult;
66
+ /**
67
+ * Calculates and returns the width of the message based on the toast properties.
68
+ *
69
+ * @param {boolean} hasIcon - Indicates if the toast has an icon.
70
+ * @param {boolean} isMultiline - Indicates if the message is multiline.
71
+ * @param {boolean} hasActionButton - Indicates if the toast has an action button.
72
+ * @param {boolean} hasCloseIcon - Indicates if the toast has a close icon.
73
+ *
74
+ * @returns {number} - The width of the message in pixels.
75
+ */
76
+ private getMessageMaxWidth;
77
+ /**
78
+ * Lifecycle method executed when component is updated.
79
+ * It dispatches an event if toast is opened.
80
+ * It calculates _messageAreaMaxWidth
81
+ */
82
+ protected updated(_changedProperties: PropertyValues<this>): Promise<void>;
83
+ /**
84
+ * It handle the action button action.
85
+ *
86
+ * @private
87
+ */
88
+ private handleActionClick;
89
+ /**
90
+ * Render the action button depending on action type and its action.
91
+ *
92
+ * @param {ActionProps} action - The action properties.
93
+ *
94
+ * @returns {TemplateResult | typeof nothing} - The rendered action button or nothing if the action text is not defined.
95
+ * @private
96
+ */
97
+ private renderActionButton;
98
+ /**
99
+ * Template for the footer area.
100
+ * It should display only when isMultiline is true as well if has action button.
101
+ * Called within the main render function.
102
+ *
103
+ * @private
104
+ */
105
+ private renderFooter;
106
+ /**
107
+ * Template for the close button element. Called within the
108
+ * main render function.
109
+ *
110
+ * @private
111
+ */
112
+ private renderCloseButton;
113
+ /**
114
+ * Template for the toast message. Called within the
115
+ * main render function.
116
+ *
117
+ * @param {string} message - The message to be displayed.
118
+ * @param {number} messageAreaMaxWidth - The maximum width of the message area calculated in the lifecycle method.
119
+ *
120
+ * @private
121
+ */
122
+ private renderMessage;
123
+ /**
124
+ * Util method responsible to close the component.
125
+ * It handles the action when user clicks in the close button and triggers an event when is executed.
126
+ *
127
+ * @private
128
+ */
129
+ private closeToastComponent;
130
+ render(): TemplateResult<1> | typeof nothing;
16
131
  }
17
132
 
18
133
  declare const PieToast_base: GenericConstructor<RTLInterface> & typeof LitElement;
@@ -20,6 +135,26 @@ declare const PieToast_base: GenericConstructor<RTLInterface> & typeof LitElemen
20
135
  declare type ReactBaseType = React_2.HTMLAttributes<HTMLElement>;
21
136
 
22
137
  export declare interface ToastProps {
138
+ /**
139
+ * When true, the toast is set to be open and visible.
140
+ */
141
+ isOpen?: boolean;
142
+ /**
143
+ * When true, allows dismissing the toast by clicking on the close button.
144
+ */
145
+ isDismissible?: boolean;
146
+ /**
147
+ * The message content of the toast.
148
+ */
149
+ message: string;
150
+ /**
151
+ * It allows the message content being displayed as multiline limited to three rows.
152
+ */
153
+ isMultiline?: boolean;
154
+ /**
155
+ * The leading action for the toast.
156
+ */
157
+ leadingAction?: ActionProps;
23
158
  }
24
159
 
25
160
  export { }
package/dist/react.js CHANGED
@@ -1,15 +1,28 @@
1
1
  import * as t from "react";
2
- import { createComponent as e } from "@lit/react";
3
- import { PieToast as o } from "./index.js";
2
+ import { createComponent as o } from "@lit/react";
3
+ import { PieToast as e } from "./index.js";
4
+ import { ON_TOAST_CLOSE_EVENT as P, ON_TOAST_LEADING_ACTION_CLICK_EVENT as l, ON_TOAST_OPEN_EVENT as f, componentClass as A, componentSelector as S, defaultProps as I } from "./index.js";
4
5
  import "lit";
6
+ import "lit/decorators.js";
7
+ import "lit/directives/class-map.js";
5
8
  import "@justeattakeaway/pie-webc-core";
6
- const a = e({
9
+ import "@justeattakeaway/pie-icon-button";
10
+ import "@justeattakeaway/pie-icons-webc/dist/IconClose.js";
11
+ import "@justeattakeaway/pie-icons-webc/dist/IconPlaceholder.js";
12
+ import "@justeattakeaway/pie-button";
13
+ const m = o({
7
14
  displayName: "PieToast",
8
- elementClass: o,
15
+ elementClass: e,
9
16
  react: t,
10
17
  tagName: "pie-toast",
11
18
  events: {}
12
- }), p = a;
19
+ }), E = m;
13
20
  export {
14
- p as PieToast
21
+ P as ON_TOAST_CLOSE_EVENT,
22
+ l as ON_TOAST_LEADING_ACTION_CLICK_EVENT,
23
+ f as ON_TOAST_OPEN_EVENT,
24
+ E as PieToast,
25
+ A as componentClass,
26
+ S as componentSelector,
27
+ I as defaultProps
15
28
  };
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.1.0",
4
+ "version": "0.2.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "cem-plugin-module-file-extensions": "0.0.5"
41
41
  },
42
42
  "dependencies": {
43
- "@justeattakeaway/pie-webc-core": "0.23.0"
43
+ "@justeattakeaway/pie-webc-core": "0.24.0"
44
44
  },
45
45
  "volta": {
46
46
  "extends": "../../../package.json"
package/src/defs.ts CHANGED
@@ -1,3 +1,70 @@
1
+ import { type ComponentDefaultPropsGeneric } from '@justeattakeaway/pie-webc-core';
2
+
1
3
  // TODO - please remove the eslint disable comment below when you add props to this interface
2
4
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
3
- export interface ToastProps {}
5
+ export type ActionProps = {
6
+ /**
7
+ * The text to display inside the button.
8
+ */
9
+ text: string;
10
+
11
+ /**
12
+ * The ARIA label for the button.
13
+ */
14
+ ariaLabel?: string;
15
+ };
16
+
17
+ export interface ToastProps {
18
+ /**
19
+ * When true, the toast is set to be open and visible.
20
+ */
21
+ isOpen?: boolean;
22
+ /**
23
+ * When true, allows dismissing the toast by clicking on the close button.
24
+ */
25
+ isDismissible?: boolean;
26
+ /**
27
+ * The message content of the toast.
28
+ */
29
+ message: string;
30
+ /**
31
+ * It allows the message content being displayed as multiline limited to three rows.
32
+ */
33
+ isMultiline?: boolean;
34
+ /**
35
+ * The leading action for the toast.
36
+ */
37
+ leadingAction?: ActionProps;
38
+ }
39
+
40
+ export const componentSelector = 'pie-toast';
41
+ export const componentClass = 'c-toast';
42
+
43
+ /**
44
+ * Event name for when the toast is closed.
45
+ *
46
+ * @constant
47
+ */
48
+ export const ON_TOAST_CLOSE_EVENT = `${componentSelector}-close`;
49
+
50
+ /**
51
+ * Event name for when the toast is opened.
52
+ *
53
+ * @constant
54
+ */
55
+ export const ON_TOAST_OPEN_EVENT = `${componentSelector}-open`;
56
+
57
+ /**
58
+ * Event name for when the toast leading action is clicked.
59
+ *
60
+ * @constant
61
+ */
62
+ export const ON_TOAST_LEADING_ACTION_CLICK_EVENT = `${componentSelector}-leading-action-click`;
63
+
64
+ export type DefaultProps = ComponentDefaultPropsGeneric<ToastProps, 'isOpen' | 'isDismissible' | 'isMultiline'>;
65
+
66
+ export const defaultProps: DefaultProps = {
67
+ isOpen: true,
68
+ isDismissible: true,
69
+ isMultiline: false,
70
+ };
package/src/index.ts CHANGED
@@ -1,24 +1,258 @@
1
- import { LitElement, html, unsafeCSS } from 'lit';
2
- import { RtlMixin, defineCustomElement } from '@justeattakeaway/pie-webc-core';
1
+ import {
2
+ LitElement,
3
+ html,
4
+ unsafeCSS,
5
+ nothing,
6
+ type TemplateResult,
7
+ type PropertyValues,
8
+ } from 'lit';
9
+ import { property, query } from 'lit/decorators.js';
10
+ import { classMap } from 'lit/directives/class-map.js';
11
+ import { RtlMixin, defineCustomElement, dispatchCustomEvent } from '@justeattakeaway/pie-webc-core';
12
+ import '@justeattakeaway/pie-icon-button';
13
+ import '@justeattakeaway/pie-icons-webc/dist/IconClose.js';
14
+ import '@justeattakeaway/pie-icons-webc/dist/IconPlaceholder.js';
15
+ import '@justeattakeaway/pie-button';
3
16
 
4
17
  import styles from './toast.scss?inline';
5
- import { ToastProps } from './defs';
18
+ import {
19
+ ToastProps,
20
+ componentSelector,
21
+ componentClass,
22
+ ActionProps,
23
+ ON_TOAST_CLOSE_EVENT,
24
+ ON_TOAST_OPEN_EVENT,
25
+ ON_TOAST_LEADING_ACTION_CLICK_EVENT,
26
+ defaultProps,
27
+ } from './defs';
6
28
 
7
29
  // Valid values available to consumers
8
30
  export * from './defs';
9
31
 
10
- const componentSelector = 'pie-toast';
11
-
12
32
  /**
13
33
  * @tagname pie-toast
14
34
  */
15
35
  export class PieToast extends RtlMixin(LitElement) implements ToastProps {
16
- render () {
17
- return html`<h1 data-test-id="pie-toast">Hello world!</h1>`;
18
- }
36
+ @property()
37
+ public message: ToastProps['message'] = '';
38
+
39
+ @property({ type: Boolean })
40
+ public isOpen = defaultProps.isOpen;
41
+
42
+ @property({ type: Boolean })
43
+ public isDismissible = defaultProps.isDismissible;
44
+
45
+ @property({ type: Boolean })
46
+ public isMultiline = defaultProps.isMultiline;
47
+
48
+ @property({ type: Object })
49
+ public leadingAction: ToastProps['leadingAction'];
50
+
51
+ @query('pie-button') actionButton?: HTMLElement;
52
+
53
+ private _actionButtonOffset = 0;
54
+
55
+ private _messageAreaMaxWidth = 0;
19
56
 
20
57
  // Renders a `CSSResult` generated from SCSS by Vite
21
58
  static styles = unsafeCSS(styles);
59
+
60
+ /**
61
+ * Calculates and returns the width of the message based on the toast properties.
62
+ *
63
+ * @param {boolean} hasIcon - Indicates if the toast has an icon.
64
+ * @param {boolean} isMultiline - Indicates if the message is multiline.
65
+ * @param {boolean} hasActionButton - Indicates if the toast has an action button.
66
+ * @param {boolean} hasCloseIcon - Indicates if the toast has a close icon.
67
+ *
68
+ * @returns {number} - The width of the message in pixels.
69
+ */
70
+ private getMessageMaxWidth (
71
+ hasIcon: boolean,
72
+ isMultiline: boolean,
73
+ hasActionButton: boolean,
74
+ hasCloseIcon: boolean,
75
+ ): number {
76
+ const iconOffset = 20;
77
+ const closeIconOffset = 32;
78
+ const gap = 8;
79
+ const toastMaxWidthWithoutPadding = 392;
80
+
81
+ let offset = 0;
82
+
83
+ if (hasIcon) {
84
+ offset += iconOffset + gap;
85
+ }
86
+
87
+ if (!isMultiline && hasActionButton) {
88
+ offset += this._actionButtonOffset + gap;
89
+ }
90
+
91
+ if (hasCloseIcon) {
92
+ offset += closeIconOffset + gap;
93
+ }
94
+
95
+ return toastMaxWidthWithoutPadding - offset;
96
+ }
97
+
98
+ /**
99
+ * Lifecycle method executed when component is updated.
100
+ * It dispatches an event if toast is opened.
101
+ * It calculates _messageAreaMaxWidth
102
+ */
103
+ protected async updated (_changedProperties: PropertyValues<this>) {
104
+ if (_changedProperties.has('isOpen') && this.isOpen) {
105
+ dispatchCustomEvent(this, ON_TOAST_OPEN_EVENT, { targetNotification: this });
106
+ }
107
+
108
+ await this.updateComplete;
109
+
110
+ if (this.actionButton) {
111
+ this._actionButtonOffset = this.actionButton.offsetWidth;
112
+ }
113
+
114
+ // Temporary const. This will be removed when we implement variants.
115
+ const hasIcon = true;
116
+
117
+ this._messageAreaMaxWidth = this.getMessageMaxWidth(hasIcon, this.isMultiline, !!this.leadingAction?.text, this.isDismissible);
118
+
119
+ if (
120
+ _changedProperties.has('message') ||
121
+ _changedProperties.has('isDismissible') ||
122
+ _changedProperties.has('isMultiline') ||
123
+ _changedProperties.has('leadingAction')) {
124
+ this.requestUpdate();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * It handle the action button action.
130
+ *
131
+ * @private
132
+ */
133
+ private handleActionClick () {
134
+ dispatchCustomEvent(this, ON_TOAST_LEADING_ACTION_CLICK_EVENT, { targetNotification: this });
135
+ }
136
+
137
+ /**
138
+ * Render the action button depending on action type and its action.
139
+ *
140
+ * @param {ActionProps} action - The action properties.
141
+ *
142
+ * @returns {TemplateResult | typeof nothing} - The rendered action button or nothing if the action text is not defined.
143
+ * @private
144
+ */
145
+ private renderActionButton (action: ActionProps) : TemplateResult | typeof nothing {
146
+ const { text, ariaLabel } = action;
147
+
148
+ return html`
149
+ <pie-button
150
+ variant="ghost-inverse"
151
+ size="xsmall"
152
+ aria-label="${ariaLabel || nothing}"
153
+ @click="${() => this.handleActionClick()}"
154
+ data-test-id="${componentSelector}-leading-action"
155
+ type="button">
156
+ ${text}
157
+ </pie-button>
158
+ `;
159
+ }
160
+
161
+ /**
162
+ * Template for the footer area.
163
+ * It should display only when isMultiline is true as well if has action button.
164
+ * Called within the main render function.
165
+ *
166
+ * @private
167
+ */
168
+ private renderFooter () {
169
+ const { leadingAction } = this;
170
+ return html`
171
+ <footer class="${componentClass}-footer" data-test-id="${componentSelector}-footer" >
172
+ ${leadingAction ? this.renderActionButton(leadingAction) : nothing}
173
+ </footer>
174
+ `;
175
+ }
176
+
177
+ /**
178
+ * Template for the close button element. Called within the
179
+ * main render function.
180
+ *
181
+ * @private
182
+ */
183
+ private renderCloseButton (): TemplateResult {
184
+ return html`
185
+ <pie-icon-button
186
+ variant="ghost-inverse"
187
+ size="xsmall"
188
+ data-test-id="${componentSelector}-close"
189
+ @click="${this.closeToastComponent}">
190
+ <icon-close></icon-close>
191
+ </pie-icon-button>`;
192
+ }
193
+
194
+ /**
195
+ * Template for the toast message. Called within the
196
+ * main render function.
197
+ *
198
+ * @param {string} message - The message to be displayed.
199
+ * @param {number} messageAreaMaxWidth - The maximum width of the message area calculated in the lifecycle method.
200
+ *
201
+ * @private
202
+ */
203
+ private renderMessage (message: string, messageAreaMaxWidth: number): TemplateResult {
204
+ return html`
205
+ <span style="max-width: ${messageAreaMaxWidth}px" data-test-id="${componentSelector}-message">
206
+ ${message}
207
+ </span>
208
+ `;
209
+ }
210
+
211
+ /**
212
+ * Util method responsible to close the component.
213
+ * It handles the action when user clicks in the close button and triggers an event when is executed.
214
+ *
215
+ * @private
216
+ */
217
+ private closeToastComponent () {
218
+ this.isOpen = false;
219
+ dispatchCustomEvent(this, ON_TOAST_CLOSE_EVENT, { targetNotification: this });
220
+ }
221
+
222
+ render () {
223
+ const {
224
+ isOpen,
225
+ message,
226
+ isDismissible,
227
+ leadingAction,
228
+ isMultiline,
229
+ _messageAreaMaxWidth,
230
+ } = this;
231
+
232
+ if (!isOpen) {
233
+ return nothing;
234
+ }
235
+
236
+ const messageAreaClasses = {
237
+ [`${componentClass}-messageArea`]: true,
238
+ 'is-multiline': isMultiline,
239
+ };
240
+
241
+ return html`
242
+ <div data-test-id="${componentSelector}" class="${componentClass}">
243
+ <div class="${componentClass}-contentArea">
244
+ <div class="${classMap(messageAreaClasses)}">
245
+ <icon-placeholder size="s"></icon-placeholder>
246
+ ${message === '' ? nothing : this.renderMessage(message, _messageAreaMaxWidth)}
247
+ </div>
248
+ <div class="${componentClass}-actionsArea">
249
+ ${!isMultiline && leadingAction?.text ? this.renderActionButton(leadingAction) : nothing}
250
+ ${isDismissible ? this.renderCloseButton() : nothing}
251
+ </div>
252
+ </div>
253
+ ${isMultiline && leadingAction?.text ? this.renderFooter() : nothing}
254
+ </div>`;
255
+ }
22
256
  }
23
257
 
24
258
  defineCustomElement(componentSelector, PieToast);
package/src/toast.scss CHANGED
@@ -1 +1,71 @@
1
1
  @use '@justeattakeaway/pie-css/scss' as p;
2
+
3
+ .c-toast {
4
+ --toast-border-radius: var(--dt-radius-rounded-b);
5
+ --toast-background-color: var(--dt-color-container-inverse);
6
+ --toast-font-color: var(--dt-color-content-inverse);
7
+ --toast-font-size: #{p.font-size(--dt-font-body-s-size)};
8
+ --toast-line-height: #{p.line-height(--dt-font-body-s-line-height)};
9
+
10
+ display: flex;
11
+ flex-direction: column;
12
+ justify-content: center;
13
+ min-height: 48px;
14
+ max-height: 122px;
15
+ min-width: 300px;
16
+ max-width: 420px;
17
+
18
+ padding: var(--dt-spacing-a) var(--dt-spacing-c) var(--dt-spacing-a) var(--dt-spacing-d);
19
+ border-radius: var(--toast-border-radius);
20
+ gap: var(--dt-spacing-a);
21
+ background-color: var(--toast-background-color);
22
+ box-shadow: var(--dt-elevation-03);
23
+ color: var(--toast-font-color);
24
+ font-size: var(--toast-font-size);
25
+ line-height: var(--toast-line-height);
26
+ }
27
+
28
+ .c-toast-contentArea {
29
+ display: flex;
30
+ gap: var(--dt-spacing-b);
31
+ justify-content: space-between;
32
+ }
33
+
34
+ .c-toast-messageArea {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: var(--dt-spacing-b);
38
+ padding: calc(var(--dt-spacing-03) / 2) 0;
39
+
40
+ span {
41
+ text-overflow: ellipsis;
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ }
45
+
46
+ &.is-multiline {
47
+ align-items: flex-start;
48
+
49
+ span {
50
+ max-height: 60px;
51
+ white-space: inherit;
52
+
53
+ // text-overflow: ellipsis; for multiline lines only works using webkit prefix
54
+ /* stylelint-disable */
55
+ display: -webkit-box;
56
+ /* stylelint-enable */
57
+ -webkit-line-clamp: 3;
58
+ -webkit-box-orient: vertical;
59
+ }
60
+ }
61
+ }
62
+
63
+ .c-toast-actionsArea {
64
+ display: flex;
65
+ gap: var(--dt-spacing-b);
66
+ }
67
+
68
+ .c-toast-footer {
69
+ display: flex;
70
+ justify-content: flex-end;
71
+ }