@justeattakeaway/pie-toast-provider 0.7.44 → 0.8.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/README.md CHANGED
@@ -19,6 +19,7 @@
19
19
  - [Events](#events)
20
20
  - [Usage Examples](#usage-examples)
21
21
  - [Creating Toasts with `toaster`](#creating-toasts-with-toaster)
22
+ - [Multiple Providers](#multiple-providers)
22
23
  - [Questions and Support](#questions-and-support)
23
24
  - [Contributing](#contributing)
24
25
 
@@ -56,9 +57,8 @@ This component does not have any slots. All content is controlled through proper
56
57
  ## Usage Examples
57
58
 
58
59
  The usage guideline is:
59
-
60
- - Place `pie-toast-provider` at the root level of your application or page.
61
- - Use the `toaster` utility from any where in your app to dynamically create toasts.
60
+ - Place `pie-toast-provider` wherever toasts should appear (e.g. root of your application, page or inside a modal).
61
+ - Use the `toaster` utility to dynamically create toasts from anywhere in your app.
62
62
 
63
63
  **For HTML:**
64
64
 
@@ -107,7 +107,49 @@ toaster.create({
107
107
  To clear all active and queued toasts:
108
108
 
109
109
  ```js
110
- toaster.clearToasts();
110
+ toaster.clearAll();
111
+ ```
112
+
113
+ ### Multiple Providers
114
+
115
+ You can use multiple `pie-toast-provider` instances in the same page or application (e.g. one at the root level and one inside a modal). Each provider maintains its own independent toast queue and logic.
116
+
117
+ When `providerId` is not specified, the toaster utility resolves the target provider automatically:
118
+ - If there is only one provider in the DOM, it is always used.
119
+ - If there are multiple providers, the provider in the nearest containing scope of the currently focused element is used. For example, if a button inside a modal triggers a toast, the modal's own provider is selected.
120
+
121
+ For explicit control when using multiple providers, give each one a native `id` attribute and pass the `providerId` option when creating or clearing toasts:
122
+
123
+ ```html
124
+ <pie-toast-provider id="main"></pie-toast-provider>
125
+
126
+ <pie-modal>
127
+ <pie-toast-provider id="modal" position="bottom-center"></pie-toast-provider>
128
+ </pie-modal>
129
+ ```
130
+
131
+ ```js
132
+ import { toaster } from '@justeattakeaway/pie-webc/components/toast-provider.js';
133
+
134
+ // Target the main page provider
135
+ toaster.create({
136
+ message: 'Saved successfully!',
137
+ variant: 'success',
138
+ providerId: 'main',
139
+ });
140
+
141
+ // Target the modal provider
142
+ toaster.create({
143
+ message: 'Form submitted!',
144
+ variant: 'success',
145
+ providerId: 'modal',
146
+ });
147
+ ```
148
+
149
+ To clear toasts for a specific provider:
150
+
151
+ ```js
152
+ toaster.clearAll('modal');
111
153
  ```
112
154
 
113
155
  ## Questions and Support
@@ -257,7 +257,7 @@
257
257
  "type": {
258
258
  "text": "object"
259
259
  },
260
- "default": "{ _getToastProvider (): PieToastProvider | null { const toastProviders = document.querySelectorAll('pie-toast-provider'); if (toastProviders.length === 0) { console.error('The pie-toast component requires a pie-toast-provider element present in the DOM.'); return null; } if (toastProviders.length > 1) { console.error('Multiple pie-toast-provider are found in the DOM. Only one provider is supported currently and should be registered at the root of the app.'); return null; } return toastProviders[0]; }, create (toast: ExtendedToastProps) { const toastProvider = this._getToastProvider(); if (!toastProvider) return; toastProvider.createToast(toast); }, clearAll () { const toastProvider = this._getToastProvider(); if (!toastProvider) return; toastProvider.clearToasts(); }, }",
260
+ "default": "{ _getToastProvider (providerId?: string): PieToastProvider | null { // 1. Explicit ID lookup if (providerId) { const el = document.getElementById(providerId); if (!el || el.tagName.toLowerCase() !== 'pie-toast-provider') { console.error(`No pie-toast-provider found with id \"${providerId}\".`); return null; } return el as PieToastProvider; } // 2. Find the provider in the nearest containing scope of the focused element (e.g. a button inside a modal with its own provider) let el: Element | null = document.activeElement; while (el) { const parent = el.parentElement; if (parent) { const provider = parent.querySelector(':scope > pie-toast-provider') as PieToastProvider | null; if (provider) return provider; } el = parent; } // 3. Sole provider in the DOM const providers = document.querySelectorAll('pie-toast-provider'); if (providers.length === 0) { console.error('No pie-toast-provider found in the DOM. Add a <pie-toast-provider> element to your page or at the root of your application.'); return null; } if (providers.length > 1) { console.error('Multiple pie-toast-provider elements found. Use the `providerId` option to target a specific provider.'); return null; } return providers[0] as PieToastProvider; }, create (toast: ExtendedToastProps) { const toastProvider = this._getToastProvider(toast.providerId); if (!toastProvider) return; toastProvider.createToast(toast); }, clearAll (providerId?: string) { const toastProvider = this._getToastProvider(providerId); if (!toastProvider) return; toastProvider.clearToasts(); }, }",
261
261
  "description": "Singleton toaster interface for global access."
262
262
  }
263
263
  ],
package/dist/index.d.ts CHANGED
@@ -10,6 +10,11 @@ export declare type DefaultProps = ComponentDefaultProps<ToastProviderProps>;
10
10
  export declare const defaultProps: DefaultProps;
11
11
 
12
12
  export declare interface ExtendedToastProps extends ToastProps {
13
+ /**
14
+ * The ID of the toast provider to target.
15
+ * If not specified, resolves to the provider in the nearest containing scope of the focused element, or the sole provider in the DOM.
16
+ */
17
+ providerId?: string;
13
18
  /**
14
19
  * Triggered when the user interacts with the close icon or when the toast auto dismiss.
15
20
  */
@@ -92,9 +97,9 @@ export declare const PRIORITY_ORDER: {
92
97
  * Singleton toaster interface for global access.
93
98
  */
94
99
  export declare const toaster: {
95
- _getToastProvider(): PieToastProvider | null;
100
+ _getToastProvider(providerId?: string): PieToastProvider | null;
96
101
  create(toast: ExtendedToastProps): void;
97
- clearAll(): void;
102
+ clearAll(providerId?: string): void;
98
103
  };
99
104
 
100
105
  export declare interface ToastProviderProps {
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import { LitElement as g, unsafeCSS as T, nothing as b, html as u } from "lit";
2
- import { property as h, state as _ } from "lit/decorators.js";
3
- import { ifDefined as y } from "lit/directives/if-defined.js";
4
- import { classMap as m } from "lit/directives/class-map.js";
1
+ import { LitElement as _, unsafeCSS as T, nothing as y, html as v } from "lit";
2
+ import { property as h, state as g } from "lit/decorators.js";
3
+ import { ifDefined as b } from "lit/directives/if-defined.js";
4
+ import { classMap as f } from "lit/directives/class-map.js";
5
5
  import { validPropertyValues as P, safeCustomElement as $, dispatchCustomEvent as x } from "@justeattakeaway/pie-webc-core";
6
- import { defaultProps as f } from "@justeattakeaway/pie-toast";
7
- const l = class l extends g {
6
+ import { defaultProps as m } from "@justeattakeaway/pie-toast";
7
+ const l = class l extends _ {
8
8
  willUpdate() {
9
9
  this.getAttribute("v") || this.setAttribute("v", l.v);
10
10
  }
11
11
  };
12
- l.v = "0.7.44";
12
+ l.v = "0.8.0";
13
13
  let d = l;
14
- const A = "*,*:after,*:before{box-sizing:inherit}:root{--pie-animation-slide-translate-start: -100%}.pie-animation--slide-in{animation:pie-animation-slide-in var(--dt-motion-timing-200) var(--dt-motion-easing-out)}.pie-animation--slide-out{animation:pie-animation-slide-out var(--dt-motion-timing-100) var(--dt-motion-easing-in)}@keyframes pie-animation-slide-in{0%{transform:translate(var(--pie-animation-slide-translate-start))}to{transform:translate(0)}}@keyframes pie-animation-slide-out{0%{transform:translate(0)}to{transform:translate(var(--pie-animation-slide-translate-start))}}:host{--toast-provider-z-index: var(--dt-z-index-toast);--toast-provider-offset: var(--dt-spacing-d)}@media (min-width: 769px){:host{--toast-provider-offset: var(--dt-spacing-e)}}.c-toast-provider{position:fixed;z-index:var(--toast-provider-z-index)}.c-toast-provider.c-toast-provider--default{inset-inline-start:var(--toast-provider-offset);inset-block-end:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--default:dir(rtl){--pie-animation-slide-translate-start: 100%}.c-toast-provider.c-toast-provider--bottom-left{--pie-animation-slide-translate-start: -100%;left:var(--toast-provider-offset);bottom:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--bottom-right{--pie-animation-slide-translate-start: 100%;right:var(--toast-provider-offset);bottom:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--bottom-center{--pie-animation-slide-translate-start: 0, 100%;left:50%;bottom:var(--toast-provider-offset);transform:translate(-50%)}", O = {
14
+ const A = "*,*:after,*:before{box-sizing:inherit}:root{--pie-animation-slide-translate-start: -100%}.pie-animation--slide-in{animation:pie-animation-slide-in var(--dt-motion-timing-200) var(--dt-motion-easing-out)}.pie-animation--slide-out{animation:pie-animation-slide-out var(--dt-motion-timing-100) var(--dt-motion-easing-in)}@keyframes pie-animation-slide-in{0%{transform:translate(var(--pie-animation-slide-translate-start))}to{transform:translate(0)}}@keyframes pie-animation-slide-out{0%{transform:translate(0)}to{transform:translate(var(--pie-animation-slide-translate-start))}}:host{--toast-provider-z-index: var(--dt-z-index-toast);--toast-provider-offset: var(--dt-spacing-d)}@media (min-width: 769px){:host{--toast-provider-offset: var(--dt-spacing-e)}}.c-toast-provider{position:fixed;z-index:var(--toast-provider-z-index)}.c-toast-provider.c-toast-provider--default{inset-inline-start:var(--toast-provider-offset);inset-block-end:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--default:dir(rtl){--pie-animation-slide-translate-start: 100%}.c-toast-provider.c-toast-provider--bottom-left{--pie-animation-slide-translate-start: -100%;left:var(--toast-provider-offset);bottom:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--bottom-right{--pie-animation-slide-translate-start: 100%;right:var(--toast-provider-offset);bottom:var(--toast-provider-offset)}.c-toast-provider.c-toast-provider--bottom-center{--pie-animation-slide-translate-start: 0, 100%;left:50%;bottom:var(--toast-provider-offset);transform:translate(-50%)}", w = {
15
15
  "error-actionable": 1,
16
16
  error: 2,
17
17
  "warning-actionable": 3,
@@ -22,27 +22,40 @@ const A = "*,*:after,*:before{box-sizing:inherit}:root{--pie-animation-slide-tra
22
22
  success: 8,
23
23
  info: 9,
24
24
  neutral: 10
25
- }, w = ["default", "bottom-left", "bottom-right", "bottom-center"], c = {
25
+ }, E = ["default", "bottom-left", "bottom-right", "bottom-center"], c = {
26
26
  options: {},
27
27
  position: "default"
28
- }, D = "pie-toast-provider-queue-update", j = {
29
- _getToastProvider() {
30
- const o = document.querySelectorAll("pie-toast-provider");
31
- return o.length === 0 ? (console.error("The pie-toast component requires a pie-toast-provider element present in the DOM."), null) : o.length > 1 ? (console.error("Multiple pie-toast-provider are found in the DOM. Only one provider is supported currently and should be registered at the root of the app."), null) : o[0];
28
+ }, O = "pie-toast-provider-queue-update", I = {
29
+ _getToastProvider(o) {
30
+ if (o) {
31
+ const e = document.getElementById(o);
32
+ return !e || e.tagName.toLowerCase() !== "pie-toast-provider" ? (console.error(`No pie-toast-provider found with id "${o}".`), null) : e;
33
+ }
34
+ let t = document.activeElement;
35
+ for (; t; ) {
36
+ const e = t.parentElement;
37
+ if (e) {
38
+ const s = e.querySelector(":scope > pie-toast-provider");
39
+ if (s) return s;
40
+ }
41
+ t = e;
42
+ }
43
+ const i = document.querySelectorAll("pie-toast-provider");
44
+ return i.length === 0 ? (console.error("No pie-toast-provider found in the DOM. Add a <pie-toast-provider> element to your page or at the root of your application."), null) : i.length > 1 ? (console.error("Multiple pie-toast-provider elements found. Use the `providerId` option to target a specific provider."), null) : i[0];
32
45
  },
33
46
  create(o) {
34
- const t = this._getToastProvider();
47
+ const t = this._getToastProvider(o.providerId);
35
48
  t && t.createToast(o);
36
49
  },
37
- clearAll() {
38
- const o = this._getToastProvider();
39
- o && o.clearToasts();
50
+ clearAll(o) {
51
+ const t = this._getToastProvider(o);
52
+ t && t.clearToasts();
40
53
  }
41
54
  };
42
- var E = Object.defineProperty, S = Object.getOwnPropertyDescriptor, n = (o, t, e, i) => {
43
- for (var s = i > 1 ? void 0 : i ? S(t, e) : t, p = o.length - 1, a; p >= 0; p--)
44
- (a = o[p]) && (s = (i ? a(t, e, s) : a(s)) || s);
45
- return i && s && E(t, e, s), s;
55
+ var D = Object.defineProperty, S = Object.getOwnPropertyDescriptor, n = (o, t, i, e) => {
56
+ for (var s = e > 1 ? void 0 : e ? S(t, i) : t, p = o.length - 1, a; p >= 0; p--)
57
+ (a = o[p]) && (s = (e ? a(t, i, s) : a(s)) || s);
58
+ return e && s && D(t, i, s), s;
46
59
  };
47
60
  const C = "pie-toast-provider";
48
61
  let r = class extends d {
@@ -55,7 +68,7 @@ let r = class extends d {
55
68
  _dispatchQueueUpdateEvent() {
56
69
  x(
57
70
  this,
58
- D,
71
+ O,
59
72
  this._toasts
60
73
  );
61
74
  }
@@ -65,9 +78,9 @@ let r = class extends d {
65
78
  * @param {boolean} hasAction - Whether the toast has an action.
66
79
  * @returns {number} - The priority based on the variant and action.
67
80
  */
68
- getPriority(o = f.variant, t = !1) {
69
- const e = `${o}${t ? "-actionable" : ""}`;
70
- return O[e];
81
+ getPriority(o = m.variant, t = !1) {
82
+ const i = `${o}${t ? "-actionable" : ""}`;
83
+ return w[i];
71
84
  }
72
85
  /**
73
86
  * Handles the dismissal of the current toast and displays the next one in the queue (if any).
@@ -93,11 +106,11 @@ let r = class extends d {
93
106
  * @param {ToastProps} toast - The toast props to display.
94
107
  */
95
108
  createToast(o) {
96
- const t = { ...f, ...this.options, ...o };
97
- this._toasts = [...this._toasts, t].sort((e, i) => {
98
- var a, v;
99
- const s = this.getPriority(i.variant, !!((a = i.leadingAction) != null && a.text));
100
- return this.getPriority(e.variant, !!((v = e.leadingAction) != null && v.text)) - s;
109
+ const t = { ...m, ...this.options, ...o };
110
+ this._toasts = [...this._toasts, t].sort((i, e) => {
111
+ var a, u;
112
+ const s = this.getPriority(e.variant, !!((a = e.leadingAction) != null && a.text));
113
+ return this.getPriority(i.variant, !!((u = i.leadingAction) != null && u.text)) - s;
101
114
  }), this._currentToast || this._showNextToast();
102
115
  }
103
116
  /**
@@ -111,27 +124,27 @@ let r = class extends d {
111
124
  const {
112
125
  position: o,
113
126
  _currentToast: t
114
- } = this, e = {
127
+ } = this, i = {
115
128
  "c-toast-provider": !0,
116
129
  [`c-toast-provider--${o}`]: !0
117
- }, i = {
130
+ }, e = {
118
131
  "pie-animation--slide-in": !!t,
119
132
  "pie-animation--slide-out": t === null
120
133
  };
121
- return u`
134
+ return v`
122
135
  <div
123
- class=${m(e)}
136
+ class=${f(i)}
124
137
  data-test-id="pie-toast-provider">
125
- ${t && u`
138
+ ${t && v`
126
139
  <pie-toast
127
- class=${m(i)}
140
+ class=${f(e)}
128
141
  message="${t.message}"
129
- variant="${y(t.variant)}"
142
+ variant="${b(t.variant)}"
130
143
  ?isStrong="${t.isStrong}"
131
144
  ?isDismissible="${t.isDismissible}"
132
145
  ?isMultiline="${t.isMultiline}"
133
146
  .leadingAction="${t.leadingAction}"
134
- .duration="${typeof t.duration > "u" ? b : t.duration}"
147
+ .duration="${typeof t.duration > "u" ? y : t.duration}"
135
148
  @pie-toast-close="${this._dismissToast}"
136
149
  @pie-toast-open="${t.onPieToastOpen}"
137
150
  @pie-toast-leading-action-click="${t.onPieToastLeadingActionClick}">
@@ -147,22 +160,22 @@ n([
147
160
  ], r.prototype, "options", 2);
148
161
  n([
149
162
  h({ type: String }),
150
- P(C, w, c.position)
163
+ P(C, E, c.position)
151
164
  ], r.prototype, "position", 2);
152
165
  n([
153
- _()
166
+ g()
154
167
  ], r.prototype, "_toasts", 2);
155
168
  n([
156
- _()
169
+ g()
157
170
  ], r.prototype, "_currentToast", 2);
158
171
  r = n([
159
172
  $("pie-toast-provider")
160
173
  ], r);
161
174
  export {
162
- D as ON_TOAST_PROVIDER_QUEUE_UPDATE_EVENT,
163
- O as PRIORITY_ORDER,
175
+ O as ON_TOAST_PROVIDER_QUEUE_UPDATE_EVENT,
176
+ w as PRIORITY_ORDER,
164
177
  r as PieToastProvider,
165
178
  c as defaultProps,
166
- w as positions,
167
- j as toaster
179
+ E as positions,
180
+ I as toaster
168
181
  };
package/dist/react.d.ts CHANGED
@@ -11,6 +11,11 @@ export declare type DefaultProps = ComponentDefaultProps<ToastProviderProps>;
11
11
  export declare const defaultProps: DefaultProps;
12
12
 
13
13
  export declare interface ExtendedToastProps extends ToastProps {
14
+ /**
15
+ * The ID of the toast provider to target.
16
+ * If not specified, resolves to the provider in the nearest containing scope of the focused element, or the sole provider in the DOM.
17
+ */
18
+ providerId?: string;
14
19
  /**
15
20
  * Triggered when the user interacts with the close icon or when the toast auto dismiss.
16
21
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-toast-provider",
3
3
  "description": "PIE Design System Toast Provider built using Web Components",
4
- "version": "0.7.44",
4
+ "version": "0.8.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/justeattakeaway/pie",
@@ -46,12 +46,9 @@
46
46
  "@justeattakeaway/pie-monorepo-utils": "0.9.1"
47
47
  },
48
48
  "dependencies": {
49
- "@justeattakeaway/pie-toast": "0.12.43",
49
+ "@justeattakeaway/pie-toast": "0.12.44",
50
50
  "@justeattakeaway/pie-webc-core": "14.0.1"
51
51
  },
52
- "volta": {
53
- "extends": "../../../package.json"
54
- },
55
52
  "customElements": "custom-elements.json",
56
53
  "sideEffects": [
57
54
  "dist/*.js"
package/src/defs.ts CHANGED
@@ -20,6 +20,12 @@ export type Priority = keyof typeof PRIORITY_ORDER;
20
20
  export const positions = ['default', 'bottom-left', 'bottom-right', 'bottom-center'] as const;
21
21
 
22
22
  export interface ExtendedToastProps extends ToastProps {
23
+ /**
24
+ * The ID of the toast provider to target.
25
+ * If not specified, resolves to the provider in the nearest containing scope of the focused element, or the sole provider in the DOM.
26
+ */
27
+ providerId?: string;
28
+
23
29
  /**
24
30
  * Triggered when the user interacts with the close icon or when the toast auto dismiss.
25
31
  */
package/src/toaster.ts CHANGED
@@ -5,29 +5,53 @@ import { type ExtendedToastProps } from './defs';
5
5
  * Singleton toaster interface for global access.
6
6
  */
7
7
  export const toaster = {
8
- _getToastProvider (): PieToastProvider | null {
9
- const toastProviders = document.querySelectorAll('pie-toast-provider');
8
+ _getToastProvider (providerId?: string): PieToastProvider | null {
9
+ // 1. Explicit ID lookup
10
+ if (providerId) {
11
+ const el = document.getElementById(providerId);
10
12
 
11
- if (toastProviders.length === 0) {
12
- console.error('The pie-toast component requires a pie-toast-provider element present in the DOM.');
13
+ if (!el || el.tagName.toLowerCase() !== 'pie-toast-provider') {
14
+ console.error(`No pie-toast-provider found with id "${providerId}".`);
15
+ return null;
16
+ }
17
+
18
+ return el as PieToastProvider;
19
+ }
20
+
21
+ // 2. Find the provider in the nearest containing scope of the focused element (e.g. a button inside a modal with its own provider)
22
+ let el: Element | null = document.activeElement;
23
+ while (el) {
24
+ const parent = el.parentElement;
25
+ if (parent) {
26
+ const provider = parent.querySelector(':scope > pie-toast-provider') as PieToastProvider | null;
27
+ if (provider) return provider;
28
+ }
29
+ el = parent;
30
+ }
31
+
32
+ // 3. Sole provider in the DOM
33
+ const providers = document.querySelectorAll('pie-toast-provider');
34
+
35
+ if (providers.length === 0) {
36
+ console.error('No pie-toast-provider found in the DOM. Add a <pie-toast-provider> element to your page or at the root of your application.');
13
37
  return null;
14
38
  }
15
39
 
16
- if (toastProviders.length > 1) {
17
- console.error('Multiple pie-toast-provider are found in the DOM. Only one provider is supported currently and should be registered at the root of the app.');
40
+ if (providers.length > 1) {
41
+ console.error('Multiple pie-toast-provider elements found. Use the `providerId` option to target a specific provider.');
18
42
  return null;
19
43
  }
20
44
 
21
- return toastProviders[0];
45
+ return providers[0] as PieToastProvider;
22
46
  },
23
47
  create (toast: ExtendedToastProps) {
24
- const toastProvider = this._getToastProvider();
48
+ const toastProvider = this._getToastProvider(toast.providerId);
25
49
  if (!toastProvider) return;
26
50
 
27
51
  toastProvider.createToast(toast);
28
52
  },
29
- clearAll () {
30
- const toastProvider = this._getToastProvider();
53
+ clearAll (providerId?: string) {
54
+ const toastProvider = this._getToastProvider(providerId);
31
55
  if (!toastProvider) return;
32
56
 
33
57
  toastProvider.clearToasts();