@justeattakeaway/pie-modal 0.48.1 → 0.49.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.
@@ -306,6 +306,15 @@
306
306
  },
307
307
  "privacy": "private"
308
308
  },
309
+ {
310
+ "kind": "field",
311
+ "name": "_escKeyAbortController",
312
+ "type": {
313
+ "text": "AbortController | null"
314
+ },
315
+ "privacy": "private",
316
+ "default": "null"
317
+ },
309
318
  {
310
319
  "kind": "field",
311
320
  "name": "_modalScrollContainer",
@@ -315,6 +324,19 @@
315
324
  "privacy": "private",
316
325
  "readonly": true
317
326
  },
327
+ {
328
+ "kind": "method",
329
+ "name": "_handleIsDismissibleChanged",
330
+ "privacy": "private",
331
+ "parameters": [
332
+ {
333
+ "name": "changedProperties",
334
+ "type": {
335
+ "text": "PropertyValues<this>"
336
+ }
337
+ }
338
+ ]
339
+ },
318
340
  {
319
341
  "kind": "method",
320
342
  "name": "_handleModalOpened",
@@ -353,9 +375,26 @@
353
375
  ],
354
376
  "description": "Closes the dialog element and re-enables page scrolling"
355
377
  },
378
+ {
379
+ "kind": "method",
380
+ "name": "_setupEscKeyListener",
381
+ "privacy": "private",
382
+ "return": {
383
+ "type": {
384
+ "text": "void"
385
+ }
386
+ },
387
+ "description": "Sets up an event listener on the Escape key to prevent dismissing the modal if isDismissible is false"
388
+ },
389
+ {
390
+ "kind": "method",
391
+ "name": "_removeEscKeyEventListener",
392
+ "privacy": "private",
393
+ "description": "Removes any event listeners set up that are listening to keyboard events and nulls the existing AbortController."
394
+ },
356
395
  {
357
396
  "kind": "field",
358
- "name": "_handleDialogCancelEvent",
397
+ "name": "_preventModalKeyboardDismissal",
359
398
  "privacy": "private",
360
399
  "description": "Prevents the user from dismissing the dialog via the `cancel`\nevent (ESC key) when `isDismissible` is set to false.",
361
400
  "parameters": [
@@ -532,6 +571,17 @@
532
571
  },
533
572
  "description": "Renders the modal inner content and footer of the modal."
534
573
  },
574
+ {
575
+ "kind": "method",
576
+ "name": "renderHeading",
577
+ "privacy": "private",
578
+ "return": {
579
+ "type": {
580
+ "text": "TemplateResult"
581
+ }
582
+ },
583
+ "description": "Renders the modal heading content in the correct heading tag"
584
+ },
535
585
  {
536
586
  "kind": "field",
537
587
  "name": "_handleDialogLightDismiss",
package/dist/index.d.ts CHANGED
@@ -170,12 +170,14 @@ export declare class PieModal extends PieModal_base implements ModalProps {
170
170
  private _dialog;
171
171
  private _backButtonClicked;
172
172
  private _abortController;
173
+ private _escKeyAbortController;
173
174
  private get _modalScrollContainer();
174
175
  static styles: CSSResult;
175
176
  connectedCallback(): void;
176
177
  disconnectedCallback(): void;
177
178
  firstUpdated(changedProperties: PropertyValues<this>): Promise<void>;
178
179
  updated(changedProperties: PropertyValues<this>): void;
180
+ private _handleIsDismissibleChanged;
179
181
  /**
180
182
  * Opens the dialog element and disables page scrolling
181
183
  */
@@ -184,13 +186,21 @@ export declare class PieModal extends PieModal_base implements ModalProps {
184
186
  * Closes the dialog element and re-enables page scrolling
185
187
  */
186
188
  private _handleModalClosed;
189
+ /**
190
+ * Sets up an event listener on the Escape key to prevent dismissing the modal if isDismissible is false
191
+ */
192
+ private _setupEscKeyListener;
193
+ /**
194
+ * Removes any event listeners set up that are listening to keyboard events and nulls the existing AbortController.
195
+ */
196
+ private _removeEscKeyEventListener;
187
197
  /**
188
198
  * Prevents the user from dismissing the dialog via the `cancel`
189
199
  * event (ESC key) when `isDismissible` is set to false.
190
200
  *
191
201
  * @param {Event} event - The event object.
192
202
  */
193
- private _handleDialogCancelEvent;
203
+ private _preventModalKeyboardDismissal;
194
204
  private _handleModalOpenStateOnFirstRender;
195
205
  private _handleModalOpenStateChanged;
196
206
  private _handleActionClick;
@@ -259,6 +269,11 @@ export declare class PieModal extends PieModal_base implements ModalProps {
259
269
  * @private
260
270
  */
261
271
  private renderModalContentAndFooter;
272
+ /**
273
+ * Renders the modal heading content in the correct heading tag
274
+ * @private
275
+ */
276
+ private renderHeading;
262
277
  render(): TemplateResult;
263
278
  /**
264
279
  * Dismisses the modal on backdrop click if `isDismissible` is `true`.
package/dist/index.js CHANGED
@@ -1,80 +1,80 @@
1
- import { LitElement as I, unsafeCSS as N, nothing as p } from "lit";
2
- import { html as s, unsafeStatic as W } from "lit/static-html.js";
3
- import { property as d, query as R } from "lit/decorators.js";
4
- import { classMap as S } from "lit/directives/class-map.js";
5
- import { ifDefined as k } from "lit/directives/if-defined.js";
1
+ import { LitElement as F, unsafeCSS as T, nothing as p } from "lit";
2
+ import { html as s, unsafeStatic as P } from "lit/static-html.js";
3
+ import { property as d, query as K } from "lit/decorators.js";
4
+ import { classMap as B } from "lit/directives/class-map.js";
5
+ import { ifDefined as b } from "lit/directives/if-defined.js";
6
6
  import "@justeattakeaway/pie-button";
7
7
  import "@justeattakeaway/pie-icon-button";
8
- import { RtlMixin as j, dispatchCustomEvent as h, requiredProperty as Y, validPropertyValues as _, defineCustomElement as V } from "@justeattakeaway/pie-webc-core";
8
+ import { RtlMixin as I, dispatchCustomEvent as h, requiredProperty as N, validPropertyValues as _, defineCustomElement as W } from "@justeattakeaway/pie-webc-core";
9
9
  import "@justeattakeaway/pie-icons-webc/dist/IconClose.js";
10
10
  import "@justeattakeaway/pie-icons-webc/dist/IconChevronLeft.js";
11
11
  import "@justeattakeaway/pie-icons-webc/dist/IconChevronRight.js";
12
12
  import "@justeattakeaway/pie-spinner";
13
- function q(n) {
13
+ function R(n) {
14
14
  if (Array.isArray(n)) {
15
- for (var o = 0, e = Array(n.length); o < n.length; o++)
16
- e[o] = n[o];
17
- return e;
15
+ for (var e = 0, o = Array(n.length); e < n.length; e++)
16
+ o[e] = n[e];
17
+ return o;
18
18
  } else
19
19
  return Array.from(n);
20
20
  }
21
- var C = !1;
21
+ var x = !1;
22
22
  if (typeof window < "u") {
23
- var O = {
23
+ var S = {
24
24
  get passive() {
25
- C = !0;
25
+ x = !0;
26
26
  }
27
27
  };
28
- window.addEventListener("testPassive", null, O), window.removeEventListener("testPassive", null, O);
28
+ window.addEventListener("testPassive", null, S), window.removeEventListener("testPassive", null, S);
29
29
  }
30
- var M = typeof window < "u" && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1), m = [], y = !1, $ = -1, u = void 0, f = void 0, D = function(o) {
31
- return m.some(function(e) {
32
- return !!(e.options.allowTouchMove && e.options.allowTouchMove(o));
30
+ var O = typeof window < "u" && window.navigator && window.navigator.platform && (/iP(ad|hone|od)/.test(window.navigator.platform) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1), m = [], k = !1, M = -1, u = void 0, f = void 0, D = function(e) {
31
+ return m.some(function(o) {
32
+ return !!(o.options.allowTouchMove && o.options.allowTouchMove(e));
33
33
  });
34
- }, w = function(o) {
35
- var e = o || window.event;
36
- return D(e.target) || e.touches.length > 1 ? !0 : (e.preventDefault && e.preventDefault(), !1);
37
- }, H = function(o) {
34
+ }, y = function(e) {
35
+ var o = e || window.event;
36
+ return D(o.target) || o.touches.length > 1 ? !0 : (o.preventDefault && o.preventDefault(), !1);
37
+ }, j = function(e) {
38
38
  if (f === void 0) {
39
- var e = !!o, t = window.innerWidth - document.documentElement.clientWidth;
40
- e && t > 0 && (f = document.body.style.paddingRight, document.body.style.paddingRight = t + "px");
39
+ var o = !!e, t = window.innerWidth - document.documentElement.clientWidth;
40
+ o && t > 0 && (f = document.body.style.paddingRight, document.body.style.paddingRight = t + "px");
41
41
  }
42
42
  u === void 0 && (u = document.body.style.overflow, document.body.style.overflow = "hidden");
43
- }, K = function() {
43
+ }, V = function() {
44
44
  f !== void 0 && (document.body.style.paddingRight = f, f = void 0), u !== void 0 && (document.body.style.overflow = u, u = void 0);
45
- }, U = function(o) {
46
- return o ? o.scrollHeight - o.scrollTop <= o.clientHeight : !1;
47
- }, X = function(o, e) {
48
- var t = o.targetTouches[0].clientY - $;
49
- return D(o.target) ? !1 : e && e.scrollTop === 0 && t > 0 || U(e) && t < 0 ? w(o) : (o.stopPropagation(), !0);
50
- }, G = function(o, e) {
51
- if (!o) {
45
+ }, Y = function(e) {
46
+ return e ? e.scrollHeight - e.scrollTop <= e.clientHeight : !1;
47
+ }, H = function(e, o) {
48
+ var t = e.targetTouches[0].clientY - M;
49
+ return D(e.target) ? !1 : o && o.scrollTop === 0 && t > 0 || Y(o) && t < 0 ? y(e) : (e.stopPropagation(), !0);
50
+ }, q = function(e, o) {
51
+ if (!e) {
52
52
  console.error("disableBodyScroll unsuccessful - targetElement must be provided when calling disableBodyScroll on IOS devices.");
53
53
  return;
54
54
  }
55
55
  if (!m.some(function(i) {
56
- return i.targetElement === o;
56
+ return i.targetElement === e;
57
57
  })) {
58
58
  var t = {
59
- targetElement: o,
59
+ targetElement: e,
60
60
  options: {}
61
61
  };
62
- m = [].concat(q(m), [t]), M ? (o.ontouchstart = function(i) {
63
- i.targetTouches.length === 1 && ($ = i.targetTouches[0].clientY);
64
- }, o.ontouchmove = function(i) {
65
- i.targetTouches.length === 1 && X(i, o);
66
- }, y || (document.addEventListener("touchmove", w, C ? { passive: !1 } : void 0), y = !0)) : H(e);
62
+ m = [].concat(R(m), [t]), O ? (e.ontouchstart = function(i) {
63
+ i.targetTouches.length === 1 && (M = i.targetTouches[0].clientY);
64
+ }, e.ontouchmove = function(i) {
65
+ i.targetTouches.length === 1 && H(i, e);
66
+ }, k || (document.addEventListener("touchmove", y, x ? { passive: !1 } : void 0), k = !0)) : j(o);
67
67
  }
68
- }, J = function(o) {
69
- if (!o) {
68
+ }, U = function(e) {
69
+ if (!e) {
70
70
  console.error("enableBodyScroll unsuccessful - targetElement must be provided when calling enableBodyScroll on IOS devices.");
71
71
  return;
72
72
  }
73
- m = m.filter(function(e) {
74
- return e.targetElement !== o;
75
- }), M ? (o.ontouchstart = null, o.ontouchmove = null, y && m.length === 0 && (document.removeEventListener("touchmove", w, C ? { passive: !1 } : void 0), y = !1)) : m.length || K();
73
+ m = m.filter(function(o) {
74
+ return o.targetElement !== e;
75
+ }), O ? (e.ontouchstart = null, e.ontouchmove = null, k && m.length === 0 && (document.removeEventListener("touchmove", y, x ? { passive: !1 } : void 0), k = !1)) : m.length || V();
76
76
  };
77
- const Q = '*,*:after,*:before{box-sizing:inherit}dialog{position:absolute;left:0;right:0;width:-moz-fit-content;width:-webkit-fit-content;width:fit-content;height:-moz-fit-content;height:-webkit-fit-content;height:fit-content;margin:auto;border:solid;padding:1em;background:#fff;color:#000;display:block}dialog:not([open]){display:none}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000001a}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}dialog.fixed{position:fixed;top:50%;transform:translateY(-50%)}.c-modal{--modal-size-s: 450px;--modal-size-m: 600px;--modal-size-l: 1080px;--modal-border-radius: var(--dt-radius-rounded-d);--modal-font: var(--dt-font-interactive-l-family);--modal-bg-color: var(--dt-color-container-default);--modal-elevation: var(--dt-elevation-04);border-radius:var(--modal-border-radius);border:none;box-shadow:var(--modal-elevation);font-family:var(--modal-font);background-color:var(--modal-bg-color);padding:0;--modal-margin-none: var(--dt-spacing-none);--modal-margin-small: var(--dt-spacing-g);--modal-margin-large: var(--dt-spacing-j);--modal-margin-block: var(--modal-margin-small);--modal-block-size: fit-content;--modal-inline-size: 75%;--modal-max-block-size: calc(100vh - calc(var(--modal-margin-block) * 2));--modal-max-inline-size: var(--modal-size-m);block-size:var(--modal-block-size);inline-size:var(--modal-inline-size);max-block-size:var(--modal-max-block-size);max-inline-size:var(--modal-max-inline-size)}.c-modal:focus-visible{outline:none}@media (max-width: 767px){.c-modal pie-icon-button{--btn-dimension: 40px}}.c-modal[open]{display:flex;flex-direction:column}@media (min-width: 769px){.c-modal{--modal-margin-block: var(--modal-margin-large)}}.c-modal.c-modal--small{--modal-max-inline-size: var(--modal-size-s)}@media (min-width: 769px){.c-modal.c-modal--small{--modal-margin-block: var(--modal-margin-large)}}.c-modal.c-modal--large{--modal-inline-size: 75%;--modal-max-inline-size: var(--modal-size-l);--modal-margin-block: var(--modal-margin-large)}@media (max-width: 767px){.c-modal.c-modal--large,.c-modal.c-modal--medium.c-modal--fullWidthBelowMid{--modal-margin-block: var(--modal-margin-none);--modal-border-radius: var(--dt-radius-rounded-none);--modal-block-size: 100%;--modal-inline-size: 100%;--modal-max-inline-size: 100%}.c-modal.c-modal--large>.c-modal-scrollContainer,.c-modal.c-modal--medium.c-modal--fullWidthBelowMid>.c-modal-scrollContainer{block-size:100%}}.c-modal.c-modal--top{margin-block-start:var(--dt-spacing-j);max-block-size:calc(100% - var(--dt-spacing-j) * 2)}@media (max-width: 767px){.c-modal.c-modal--top.c-modal--large,.c-modal.c-modal--top.c-modal--fullWidthBelowMid.c-modal--medium{margin-block-start:var(--dt-spacing-none);max-block-size:100%}}.c-modal::backdrop{background:var(--dt-color-overlay)}@supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none){.c-modal::backdrop{background:#0000008c}}.c-modal .c-modal-footer{--modal-button-spacing: var(--dt-spacing-d);--modal-footer-padding: var(--dt-spacing-d);display:flex;flex-flow:row-reverse;flex-wrap:wrap;gap:var(--modal-button-spacing);padding:var(--modal-footer-padding)}@media (min-width: 769px){.c-modal .c-modal-footer{--modal-footer-padding: var(--dt-spacing-e)}}@media (max-width: 767px){.c-modal .c-modal-footer.c-modal-footer--stackedActions{flex-direction:column}}.c-modal .c-modal-header{display:grid;grid-template-areas:"back heading close";grid-template-columns:minmax(0,max-content) minmax(0,1fr) minmax(0,max-content);align-items:start}.c-modal .c-modal-heading{--modal-header-font-size: calc(var(--dt-font-heading-m-size--narrow) * 1px);--modal-header-font-line-height: calc(var(--dt-font-heading-m-line-height--narrow) * 1px);--modal-header-font-weight: var(--dt-font-heading-m-weight);font-size:var(--modal-header-font-size);line-height:var(--modal-header-font-line-height);font-weight:var(--modal-header-font-weight);margin:0;grid-area:heading;margin-inline-start:var(--dt-spacing-d);margin-inline-end:var(--dt-spacing-d);margin-block:14px}@media (min-width: 769px){.c-modal .c-modal-heading{--modal-header-font-size: calc(var(--dt-font-heading-m-size--wide) * 1px);--modal-header-font-line-height: calc(var(--dt-font-heading-m-line-height--wide) * 1px);margin-inline-start:var(--dt-spacing-e);margin-inline-end:var(--dt-spacing-e);margin-block:20px}}.c-modal .c-modal-backBtn+.c-modal-heading{margin-inline-start:var(--dt-spacing-b)}@media (min-width: 769px){.c-modal .c-modal-backBtn+.c-modal-heading{margin-inline-start:var(--dt-spacing-c)}}.c-modal.c-modal--dismissible .c-modal-heading{margin-inline-end:var(--dt-spacing-d)}@media (min-width: 769px){.c-modal.c-modal--dismissible .c-modal-heading{margin-inline-end:var(--dt-spacing-e)}}.c-modal .c-modal-backBtn{grid-area:back;margin-block-start:var(--dt-spacing-b);margin-block-end:var(--dt-spacing-b);margin-inline-start:var(--dt-spacing-b);margin-inline-end:var(--dt-spacing-none)}@media (min-width: 769px){.c-modal .c-modal-backBtn{margin-block-start:var(--dt-spacing-c);margin-block-end:var(--dt-spacing-c);margin-inline-start:var(--dt-spacing-c);margin-inline-end:var(--dt-spacing-none)}}.c-modal .c-modal-closeBtn{grid-area:close;margin-block-start:var(--dt-spacing-b);margin-block-end:var(--dt-spacing-b);margin-inline-start:var(--dt-spacing-none);margin-inline-end:var(--dt-spacing-b)}@media (min-width: 769px){.c-modal .c-modal-closeBtn{margin-block-start:var(--dt-spacing-c);margin-block-end:var(--dt-spacing-c);margin-inline-start:var(--dt-spacing-none);margin-inline-end:var(--dt-spacing-c)}}.c-modal .c-modal-content{--modal-content-font-size: calc(var(--dt-font-size-16) * 1px);--modal-content-font-weight: var(--dt-font-weight-regular);--modal-content-line-height: calc(var(--dt-font-size-16-line-height) * 1px);--modal-content-padding-block: var(--dt-spacing-a);--modal-content-padding-inline: var(--dt-spacing-d);--modal-content-padding-block-end: var(--dt-spacing-e);--modal-content-min-block-size: var(--dt-spacing-j);position:relative;min-block-size:calc(var(--modal-content-min-block-size) + var(--modal-content-padding-block) + var(--modal-content-padding-block-end));font-size:var(--modal-content-font-size);line-height:var(--modal-content-line-height);font-weight:var(--modal-content-font-weight);padding-inline-start:var(--modal-content-padding-inline);padding-inline-end:var(--modal-content-padding-inline);padding-block-start:var(--modal-content-padding-block);padding-block-end:var(--modal-content-padding-block-end);flex-grow:1}@media (min-width: 769px){.c-modal .c-modal-content{--modal-content-padding-inline: var(--dt-spacing-e)}}.c-modal .c-modal-content:not(:last-child){padding-block-end:var(--modal-content-padding-block);min-block-size:var(--modal-content-min-block-size)}.c-modal .c-modal-content--scrollable{background:linear-gradient(to bottom,transparent,var(--dt-color-container-default) 75%) center bottom,linear-gradient(transparent,var(--dt-color-border-strong)) center bottom;background-repeat:no-repeat;background-size:100% 48px,100% 12px;background-attachment:local,scroll}.c-modal>.c-modal-scrollContainer{display:flex;flex-direction:column;overflow-y:auto;--bg-scroll-end: linear-gradient(rgba(255, 255, 255, 0), var(--dt-color-container-default) 70%) 0 100%;--bg-scroll-bottom: radial-gradient(farthest-corner at 50% 100%, rgba(0, 0, 0, .3), rgba(0, 0, 0, 0)) 0 100%;--bg-size-scroll-end: 100% 40px;--bg-size-scroll-bottom: 100% 8px;background:var(--bg-scroll-end),var(--bg-scroll-bottom);background-repeat:no-repeat;background-size:var(--bg-size-scroll-end),var(--bg-size-scroll-bottom);background-attachment:local,scroll}.c-modal>.c-modal-scrollContainer .c-modal-content{flex-shrink:0}.c-modal.c-modal--pinnedFooter .c-modal-content{overflow-y:auto}.c-modal.c-modal--loading .c-modal-content pie-spinner{position:absolute;left:50%;top:calc(50% - (var(--modal-content-padding-block-end) - var(--modal-content-padding-block)) / 2);transform:translate(-50%,-50%)}.c-modal.c-modal--loading .c-modal-content .c-modal-contentInner{display:none}.c-modal.c-modal--loading .c-modal-content:not(:last-child) pie-spinner{top:50%}@supports not (aspect-ratio: 1/1){.c-modal .c-modal-scrollContainer{background:none}}', Z = ["h1", "h2", "h3", "h4", "h5", "h6"], oo = ["small", "medium", "large"], eo = ["top", "center"], A = "pie-modal-close", x = "pie-modal-open", L = "pie-modal-back", to = "pie-modal-leading-action-click", io = "pie-modal-supporting-action-click", r = {
77
+ const X = '*,*:after,*:before{box-sizing:inherit}dialog{position:absolute;left:0;right:0;width:-moz-fit-content;width:-webkit-fit-content;width:fit-content;height:-moz-fit-content;height:-webkit-fit-content;height:fit-content;margin:auto;border:solid;padding:1em;background:#fff;color:#000;display:block}dialog:not([open]){display:none}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000001a}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}dialog.fixed{position:fixed;top:50%;transform:translateY(-50%)}.c-modal{--modal-size-s: 450px;--modal-size-m: 600px;--modal-size-l: 1080px;--modal-border-radius: var(--dt-radius-rounded-d);--modal-font: var(--dt-font-interactive-l-family);--modal-bg-color: var(--dt-color-container-default);--modal-elevation: var(--dt-elevation-04);border-radius:var(--modal-border-radius);border:none;box-shadow:var(--modal-elevation);font-family:var(--modal-font);background-color:var(--modal-bg-color);padding:0;--modal-margin-none: var(--dt-spacing-none);--modal-margin-small: var(--dt-spacing-g);--modal-margin-large: var(--dt-spacing-j);--modal-margin-block: var(--modal-margin-small);--modal-block-size: fit-content;--modal-inline-size: 75%;--modal-max-block-size: calc(100vh - calc(var(--modal-margin-block) * 2));--modal-max-inline-size: var(--modal-size-m);block-size:var(--modal-block-size);inline-size:var(--modal-inline-size);max-block-size:var(--modal-max-block-size);max-inline-size:var(--modal-max-inline-size)}.c-modal:focus-visible{outline:none}@media (max-width: 767px){.c-modal pie-icon-button{--btn-dimension: 40px}}.c-modal[open]{display:flex;flex-direction:column}@media (min-width: 769px){.c-modal{--modal-margin-block: var(--modal-margin-large)}}.c-modal.c-modal--small{--modal-max-inline-size: var(--modal-size-s)}@media (min-width: 769px){.c-modal.c-modal--small{--modal-margin-block: var(--modal-margin-large)}}.c-modal.c-modal--large{--modal-inline-size: 75%;--modal-max-inline-size: var(--modal-size-l);--modal-margin-block: var(--modal-margin-large)}@media (max-width: 767px){.c-modal.c-modal--large,.c-modal.c-modal--medium.c-modal--fullWidthBelowMid{--modal-margin-block: var(--modal-margin-none);--modal-border-radius: var(--dt-radius-rounded-none);--modal-block-size: 100%;--modal-inline-size: 100%;--modal-max-inline-size: 100%}.c-modal.c-modal--large>.c-modal-scrollContainer,.c-modal.c-modal--medium.c-modal--fullWidthBelowMid>.c-modal-scrollContainer{block-size:100%}}.c-modal.c-modal--top{margin-block-start:var(--dt-spacing-j);max-block-size:calc(100% - var(--dt-spacing-j) * 2)}@media (max-width: 767px){.c-modal.c-modal--top.c-modal--large,.c-modal.c-modal--top.c-modal--fullWidthBelowMid.c-modal--medium{margin-block-start:var(--dt-spacing-none);max-block-size:100%}}.c-modal::backdrop{background:var(--dt-color-overlay)}@supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none){.c-modal::backdrop{background:#0000008c}}.c-modal .c-modal-footer{--modal-button-spacing: var(--dt-spacing-d);--modal-footer-padding: var(--dt-spacing-d);display:flex;flex-flow:row-reverse;flex-wrap:wrap;gap:var(--modal-button-spacing);padding:var(--modal-footer-padding)}@media (min-width: 769px){.c-modal .c-modal-footer{--modal-footer-padding: var(--dt-spacing-e)}}@media (max-width: 767px){.c-modal .c-modal-footer.c-modal-footer--stackedActions{flex-direction:column}}.c-modal .c-modal-header{display:grid;grid-template-areas:"back heading close";grid-template-columns:minmax(0,max-content) minmax(0,1fr) minmax(0,max-content);align-items:start}.c-modal .c-modal-heading{--modal-header-font-size: calc(var(--dt-font-heading-m-size--narrow) * 1px);--modal-header-font-line-height: calc(var(--dt-font-heading-m-line-height--narrow) * 1px);--modal-header-font-weight: var(--dt-font-heading-m-weight);font-size:var(--modal-header-font-size);line-height:var(--modal-header-font-line-height);font-weight:var(--modal-header-font-weight);margin:0;grid-area:heading;margin-inline-start:var(--dt-spacing-d);margin-inline-end:var(--dt-spacing-d);margin-block:14px}@media (min-width: 769px){.c-modal .c-modal-heading{--modal-header-font-size: calc(var(--dt-font-heading-m-size--wide) * 1px);--modal-header-font-line-height: calc(var(--dt-font-heading-m-line-height--wide) * 1px);margin-inline-start:var(--dt-spacing-e);margin-inline-end:var(--dt-spacing-e);margin-block:20px}}.c-modal .c-modal-backBtn+.c-modal-heading{margin-inline-start:var(--dt-spacing-b)}@media (min-width: 769px){.c-modal .c-modal-backBtn+.c-modal-heading{margin-inline-start:var(--dt-spacing-c)}}.c-modal.c-modal--dismissible .c-modal-heading{margin-inline-end:var(--dt-spacing-d)}@media (min-width: 769px){.c-modal.c-modal--dismissible .c-modal-heading{margin-inline-end:var(--dt-spacing-e)}}.c-modal .c-modal-backBtn{grid-area:back;margin-block-start:var(--dt-spacing-b);margin-block-end:var(--dt-spacing-b);margin-inline-start:var(--dt-spacing-b);margin-inline-end:var(--dt-spacing-none)}@media (min-width: 769px){.c-modal .c-modal-backBtn{margin-block-start:var(--dt-spacing-c);margin-block-end:var(--dt-spacing-c);margin-inline-start:var(--dt-spacing-c);margin-inline-end:var(--dt-spacing-none)}}.c-modal .c-modal-closeBtn{grid-area:close;margin-block-start:var(--dt-spacing-b);margin-block-end:var(--dt-spacing-b);margin-inline-start:var(--dt-spacing-none);margin-inline-end:var(--dt-spacing-b)}@media (min-width: 769px){.c-modal .c-modal-closeBtn{margin-block-start:var(--dt-spacing-c);margin-block-end:var(--dt-spacing-c);margin-inline-start:var(--dt-spacing-none);margin-inline-end:var(--dt-spacing-c)}}.c-modal .c-modal-content{--modal-content-font-size: calc(var(--dt-font-size-16) * 1px);--modal-content-font-weight: var(--dt-font-weight-regular);--modal-content-line-height: calc(var(--dt-font-size-16-line-height) * 1px);--modal-content-padding-block: var(--dt-spacing-a);--modal-content-padding-inline: var(--dt-spacing-d);--modal-content-padding-block-end: var(--dt-spacing-e);--modal-content-min-block-size: var(--dt-spacing-j);position:relative;min-block-size:calc(var(--modal-content-min-block-size) + var(--modal-content-padding-block) + var(--modal-content-padding-block-end));font-size:var(--modal-content-font-size);line-height:var(--modal-content-line-height);font-weight:var(--modal-content-font-weight);padding-inline-start:var(--modal-content-padding-inline);padding-inline-end:var(--modal-content-padding-inline);padding-block-start:var(--modal-content-padding-block);padding-block-end:var(--modal-content-padding-block-end);flex-grow:1}@media (min-width: 769px){.c-modal .c-modal-content{--modal-content-padding-inline: var(--dt-spacing-e)}}.c-modal .c-modal-content:not(:last-child){padding-block-end:var(--modal-content-padding-block);min-block-size:var(--modal-content-min-block-size)}.c-modal .c-modal-content--scrollable{background:linear-gradient(to bottom,transparent,var(--dt-color-container-default) 75%) center bottom,linear-gradient(transparent,var(--dt-color-border-strong)) center bottom;background-repeat:no-repeat;background-size:100% 48px,100% 12px;background-attachment:local,scroll}.c-modal>.c-modal-scrollContainer{display:flex;flex-direction:column;overflow-y:auto;--bg-scroll-end: linear-gradient(rgba(255, 255, 255, 0), var(--dt-color-container-default) 70%) 0 100%;--bg-scroll-bottom: radial-gradient(farthest-corner at 50% 100%, rgba(0, 0, 0, .3), rgba(0, 0, 0, 0)) 0 100%;--bg-size-scroll-end: 100% 40px;--bg-size-scroll-bottom: 100% 8px;background:var(--bg-scroll-end),var(--bg-scroll-bottom);background-repeat:no-repeat;background-size:var(--bg-size-scroll-end),var(--bg-size-scroll-bottom);background-attachment:local,scroll}.c-modal>.c-modal-scrollContainer .c-modal-content{flex-shrink:0}.c-modal.c-modal--pinnedFooter .c-modal-content{overflow-y:auto}.c-modal.c-modal--loading .c-modal-content pie-spinner{position:absolute;left:50%;top:calc(50% - (var(--modal-content-padding-block-end) - var(--modal-content-padding-block)) / 2);transform:translate(-50%,-50%)}.c-modal.c-modal--loading .c-modal-content .c-modal-contentInner{display:none}.c-modal.c-modal--loading .c-modal-content:not(:last-child) pie-spinner{top:50%}@supports not (aspect-ratio: 1/1){.c-modal .c-modal-scrollContainer{background:none}}', G = ["h1", "h2", "h3", "h4", "h5", "h6"], J = ["small", "medium", "large"], Q = ["top", "center"], A = "pie-modal-close", w = "pie-modal-open", L = "pie-modal-back", Z = "pie-modal-leading-action-click", ee = "pie-modal-supporting-action-click", r = {
78
78
  hasBackButton: !1,
79
79
  hasStackedActions: !1,
80
80
  headingLevel: "h2",
@@ -86,27 +86,27 @@ const Q = '*,*:after,*:before{box-sizing:inherit}dialog{position:absolute;left:0
86
86
  position: "center",
87
87
  size: "medium"
88
88
  };
89
- var ao = Object.defineProperty, l = (n, o, e, t) => {
89
+ var oe = Object.defineProperty, l = (n, e, o, t) => {
90
90
  for (var i = void 0, c = n.length - 1, g; c >= 0; c--)
91
- (g = n[c]) && (i = g(o, e, i) || i);
92
- return i && ao(o, e, i), i;
91
+ (g = n[c]) && (i = g(e, o, i) || i);
92
+ return i && oe(e, o, i), i;
93
93
  };
94
- const v = "pie-modal", z = class z extends j(I) {
94
+ const v = "pie-modal", C = class C extends I(F) {
95
95
  constructor() {
96
- super(...arguments), this.headingLevel = r.headingLevel, this.hasBackButton = r.hasBackButton, this.hasStackedActions = r.hasStackedActions, this.isDismissible = r.isDismissible, this.isFooterPinned = r.isFooterPinned, this.isFullWidthBelowMid = r.isFullWidthBelowMid, this.isLoading = r.isLoading, this.isOpen = r.isOpen, this.position = r.position, this.size = r.size, this._backButtonClicked = !1, this._handleDialogCancelEvent = (o) => {
97
- this.isDismissible || o.preventDefault();
98
- }, this._handleDialogLightDismiss = (o) => {
96
+ super(...arguments), this.headingLevel = r.headingLevel, this.hasBackButton = r.hasBackButton, this.hasStackedActions = r.hasStackedActions, this.isDismissible = r.isDismissible, this.isFooterPinned = r.isFooterPinned, this.isFullWidthBelowMid = r.isFullWidthBelowMid, this.isLoading = r.isLoading, this.isOpen = r.isOpen, this.position = r.position, this.size = r.size, this._backButtonClicked = !1, this._escKeyAbortController = null, this._preventModalKeyboardDismissal = (e) => {
97
+ e.key === "Escape" && e.preventDefault();
98
+ }, this._handleDialogLightDismiss = (e) => {
99
99
  if (!this.isDismissible)
100
100
  return;
101
- const e = this._dialog.getBoundingClientRect(), {
101
+ const o = this._dialog.getBoundingClientRect(), {
102
102
  top: t = 0,
103
103
  bottom: i = 0,
104
104
  left: c = 0,
105
105
  right: g = 0
106
- } = e || {};
106
+ } = o || {};
107
107
  if (t === 0 && i === 0 && c === 0 && g === 0)
108
108
  return;
109
- (o.clientY < t || o.clientY > i || o.clientX < c || o.clientX > g) && (this.isOpen = !1);
109
+ (e.clientY < t || e.clientY > i || e.clientX < c || e.clientX > g) && (this.isOpen = !1);
110
110
  };
111
111
  }
112
112
  get _modalScrollContainer() {
@@ -114,75 +114,93 @@ const v = "pie-modal", z = class z extends j(I) {
114
114
  }
115
115
  connectedCallback() {
116
116
  super.connectedCallback(), this._abortController = new AbortController();
117
- const { signal: o } = this._abortController;
118
- this.addEventListener("click", (e) => this._handleDialogLightDismiss(e)), document.addEventListener(x, (e) => this._handleModalOpened(e), { signal: o }), document.addEventListener(A, (e) => this._handleModalClosed(e), { signal: o }), document.addEventListener(L, (e) => this._handleModalClosed(e), { signal: o });
117
+ const { signal: e } = this._abortController;
118
+ this.addEventListener("click", (o) => this._handleDialogLightDismiss(o)), this._setupEscKeyListener(), document.addEventListener(w, (o) => this._handleModalOpened(o), { signal: e }), document.addEventListener(A, (o) => this._handleModalClosed(o), { signal: e }), document.addEventListener(L, (o) => this._handleModalClosed(o), { signal: e });
119
119
  }
120
120
  disconnectedCallback() {
121
- this._abortController.abort(), this._enableBodyScroll(), super.disconnectedCallback();
121
+ super.disconnectedCallback(), this._abortController.abort(), this._enableBodyScroll(), this._removeEscKeyEventListener();
122
122
  }
123
- async firstUpdated(o) {
124
- if (super.firstUpdated(o), this._dialog) {
125
- (await import("./dialog-polyfill.esm-CbjBMXAG.js").then((i) => i.default)).registerDialog(this._dialog);
126
- const { signal: t } = this._abortController;
127
- this._dialog.addEventListener("cancel", (i) => this._handleDialogCancelEvent(i), { signal: t }), this._dialog.addEventListener("close", () => {
128
- this.isOpen = !1;
129
- }, { signal: t });
130
- }
131
- this._handleModalOpenStateOnFirstRender(o);
123
+ async firstUpdated(e) {
124
+ (await import("./dialog-polyfill.esm-CbjBMXAG.js").then((i) => i.default)).registerDialog(this._dialog);
125
+ const { signal: t } = this._abortController;
126
+ this._dialog.addEventListener("close", () => {
127
+ this.isOpen = !1;
128
+ }, { signal: t }), this._handleModalOpenStateOnFirstRender(e);
129
+ }
130
+ updated(e) {
131
+ this._handleModalOpenStateChanged(e), this._handleIsDismissibleChanged(e);
132
132
  }
133
- updated(o) {
134
- super.updated(o), this._handleModalOpenStateChanged(o);
133
+ _handleIsDismissibleChanged(e) {
134
+ const o = e.get("isDismissible"), t = this.isDismissible;
135
+ !o && t && this._removeEscKeyEventListener(), o && !t && this._setupEscKeyListener();
135
136
  }
136
137
  /**
137
138
  * Opens the dialog element and disables page scrolling
138
139
  */
139
- _handleModalOpened(o) {
140
- const { targetModal: e } = o.detail;
141
- if (e === this) {
140
+ _handleModalOpened(e) {
141
+ const { targetModal: o } = e.detail;
142
+ if (o === this) {
142
143
  if (this._disableBodyScroll(), this._dialog.hasAttribute("open") || !this._dialog.isConnected)
143
144
  return;
144
- this._dialog.showModal();
145
+ this._setupEscKeyListener(), this._dialog.showModal();
145
146
  }
146
147
  }
147
148
  /**
148
149
  * Closes the dialog element and re-enables page scrolling
149
150
  */
150
- _handleModalClosed(o) {
151
- const { targetModal: e } = o.detail;
152
- e === this && (this._enableBodyScroll(), this._dialog.close(), this._returnFocus());
151
+ _handleModalClosed(e) {
152
+ const { targetModal: o } = e.detail;
153
+ o === this && (this._enableBodyScroll(), this._dialog.close(), this._returnFocus(), this._removeEscKeyEventListener());
154
+ }
155
+ /**
156
+ * Sets up an event listener on the Escape key to prevent dismissing the modal if isDismissible is false
157
+ */
158
+ _setupEscKeyListener() {
159
+ if (!this._escKeyAbortController && !this.isDismissible) {
160
+ this._escKeyAbortController = new AbortController();
161
+ const { signal: e } = this._escKeyAbortController;
162
+ document.addEventListener("keydown", (o) => this._preventModalKeyboardDismissal(o), { signal: e });
163
+ }
164
+ }
165
+ /**
166
+ * Removes any event listeners set up that are listening to keyboard events and nulls the existing AbortController.
167
+ */
168
+ _removeEscKeyEventListener() {
169
+ var e;
170
+ (e = this._escKeyAbortController) == null || e.abort(), this._escKeyAbortController = null;
153
171
  }
154
172
  // Handles the value of the isOpen property on first render of the component
155
- _handleModalOpenStateOnFirstRender(o) {
156
- o.get("isOpen") === void 0 && this.isOpen && h(this, x, { targetModal: this });
173
+ _handleModalOpenStateOnFirstRender(e) {
174
+ e.get("isOpen") === void 0 && this.isOpen && h(this, w, { targetModal: this });
157
175
  }
158
176
  // Handles changes to the modal isOpen property by dispatching any appropriate events
159
- _handleModalOpenStateChanged(o) {
160
- const e = o.get("isOpen");
161
- e !== void 0 && (e ? this._backButtonClicked ? (this._backButtonClicked = !1, h(this, L, { targetModal: this })) : h(this, A, { targetModal: this }) : h(this, x, { targetModal: this }));
177
+ _handleModalOpenStateChanged(e) {
178
+ const o = e.get("isOpen");
179
+ o !== void 0 && (o ? this._backButtonClicked ? (this._backButtonClicked = !1, h(this, L, { targetModal: this })) : h(this, A, { targetModal: this }) : h(this, w, { targetModal: this }));
162
180
  }
163
- _handleActionClick(o) {
164
- o === "leading" ? (this._dialog.close("leading"), h(this, to, { targetModal: this })) : o === "supporting" && (this._dialog.close("supporting"), h(this, io, { targetModal: this }));
181
+ _handleActionClick(e) {
182
+ e === "leading" ? (this._dialog.close("leading"), h(this, Z, { targetModal: this })) : e === "supporting" && (this._dialog.close("supporting"), h(this, ee, { targetModal: this }));
165
183
  }
166
184
  /**
167
185
  * Return focus to the specified element, providing the selector is valid
168
186
  * and the chosen element can be found.
169
187
  */
170
188
  _returnFocus() {
171
- var e, t;
172
- const o = (e = this.returnFocusAfterCloseSelector) == null ? void 0 : e.trim();
173
- o && ((t = document.querySelector(o)) == null || t.focus());
189
+ var o, t;
190
+ const e = (o = this.returnFocusAfterCloseSelector) == null ? void 0 : o.trim();
191
+ e && ((t = document.querySelector(e)) == null || t.focus());
174
192
  }
175
193
  /**
176
194
  * Enables body scroll by unlocking the scroll container.
177
195
  */
178
196
  _enableBodyScroll() {
179
- this._modalScrollContainer && J(this._modalScrollContainer);
197
+ this._modalScrollContainer && U(this._modalScrollContainer);
180
198
  }
181
199
  /**
182
200
  * Disables body scroll by locking the scroll container.
183
201
  */
184
202
  _disableBodyScroll() {
185
- this._modalScrollContainer && ("scrollTo" in window && window.scrollTo(0, 0), G(this._modalScrollContainer));
203
+ this._modalScrollContainer && ("scrollTo" in window && window.scrollTo(0, 0), q(this._modalScrollContainer));
186
204
  }
187
205
  /**
188
206
  * Template for the close button element. Called within the
@@ -191,7 +209,7 @@ const v = "pie-modal", z = class z extends j(I) {
191
209
  * @private
192
210
  */
193
211
  renderCloseButton() {
194
- var o;
212
+ var e;
195
213
  return this.isDismissible ? s`
196
214
  <pie-icon-button
197
215
  @click="${() => {
@@ -199,7 +217,7 @@ const v = "pie-modal", z = class z extends j(I) {
199
217
  }}"
200
218
  variant="ghost-secondary"
201
219
  class="c-modal-closeBtn"
202
- aria-label="${((o = this.aria) == null ? void 0 : o.close) || p}"
220
+ aria-label="${((e = this.aria) == null ? void 0 : e.close) || p}"
203
221
  data-test-id="modal-close-button">
204
222
  <icon-close></icon-close>
205
223
  </pie-icon-button>` : p;
@@ -211,7 +229,7 @@ const v = "pie-modal", z = class z extends j(I) {
211
229
  * @private
212
230
  */
213
231
  renderBackButton() {
214
- var o;
232
+ var e;
215
233
  return this.hasBackButton ? s`
216
234
  <pie-icon-button
217
235
  @click="${() => {
@@ -219,7 +237,7 @@ const v = "pie-modal", z = class z extends j(I) {
219
237
  }}"
220
238
  variant="ghost-secondary"
221
239
  class="c-modal-backBtn"
222
- aria-label="${k((o = this.aria) == null ? void 0 : o.back)}"
240
+ aria-label="${b((e = this.aria) == null ? void 0 : e.back)}"
223
241
  data-test-id="modal-back-button">
224
242
  ${this.isRTL ? s`<icon-chevron-right></icon-chevron-right>` : s`<icon-chevron-left></icon-chevron-left>`}
225
243
  </pie-icon-button>
@@ -235,16 +253,16 @@ const v = "pie-modal", z = class z extends j(I) {
235
253
  * @private
236
254
  */
237
255
  renderLeadingAction() {
238
- const { ariaLabel: o, text: e, variant: t = "primary" } = this.leadingAction || {};
239
- return e ? s`
256
+ const { ariaLabel: e, text: o, variant: t = "primary" } = this.leadingAction || {};
257
+ return o ? s`
240
258
  <pie-button
241
259
  variant="${t}"
242
- aria-label="${k(o)}"
260
+ aria-label="${b(e)}"
243
261
  type="submit"
244
262
  ?isFullWidth="${this.hasStackedActions}"
245
263
  @click="${() => this._handleActionClick("leading")}"
246
264
  data-test-id="modal-leading-action">
247
- ${e}
265
+ ${o}
248
266
  </pie-button>
249
267
  ` : p;
250
268
  }
@@ -260,16 +278,16 @@ const v = "pie-modal", z = class z extends j(I) {
260
278
  */
261
279
  renderSupportingAction() {
262
280
  var i;
263
- const { ariaLabel: o, text: e, variant: t = "ghost" } = this.supportingAction || {};
264
- return !e || !((i = this.leadingAction) != null && i.text) ? p : s`
281
+ const { ariaLabel: e, text: o, variant: t = "ghost" } = this.supportingAction || {};
282
+ return !o || !((i = this.leadingAction) != null && i.text) ? p : s`
265
283
  <pie-button
266
284
  variant="${t}"
267
- aria-label="${k(o)}"
285
+ aria-label="${b(e)}"
268
286
  type="reset"
269
287
  ?isFullWidth="${this.hasStackedActions}"
270
288
  @click="${() => this._handleActionClick("supporting")}"
271
289
  data-test-id="modal-supporting-action">
272
- ${e}
290
+ ${o}
273
291
  </pie-button>
274
292
  `;
275
293
  }
@@ -280,15 +298,15 @@ const v = "pie-modal", z = class z extends j(I) {
280
298
  * @private
281
299
  */
282
300
  renderModalFooter() {
283
- var e, t;
284
- if (!((e = this.leadingAction) != null && e.text))
301
+ var o, t;
302
+ if (!((o = this.leadingAction) != null && o.text))
285
303
  return (t = this.supportingAction) != null && t.text && console.warn("You cannot have a supporting action without a leading action. If you only need one button then use a leading action instead."), p;
286
- const o = {
304
+ const e = {
287
305
  "c-modal-footer": !0,
288
306
  "c-modal-footer--stackedActions": this.hasStackedActions
289
307
  };
290
308
  return s`
291
- <footer class="${S(o)}" data-test-id="pie-modal-footer">
309
+ <footer class="${B(e)}" data-test-id="pie-modal-footer">
292
310
  ${this.renderLeadingAction()}
293
311
  ${this.renderSupportingAction()}
294
312
  </footer>`;
@@ -314,61 +332,68 @@ const v = "pie-modal", z = class z extends j(I) {
314
332
  </article>
315
333
  ${this.renderModalFooter()}`;
316
334
  }
335
+ /**
336
+ * Renders the modal heading content in the correct heading tag
337
+ * @private
338
+ */
339
+ renderHeading() {
340
+ const { heading: e, headingLevel: o } = this, t = P(o);
341
+ return s`
342
+ <${t} class="c-modal-heading">
343
+ ${e}
344
+ </${t}>
345
+ `;
346
+ }
317
347
  render() {
318
348
  const {
319
- aria: o,
320
- heading: e,
321
- headingLevel: t,
322
- isDismissible: i,
323
- isFooterPinned: c,
324
- isFullWidthBelowMid: g,
325
- isLoading: b,
326
- position: F,
327
- size: T
328
- } = this, B = W(t), E = b && (o == null ? void 0 : o.loading) || void 0, P = {
349
+ aria: e,
350
+ isDismissible: o,
351
+ isFooterPinned: t,
352
+ isFullWidthBelowMid: i,
353
+ isLoading: c,
354
+ position: g,
355
+ size: z
356
+ } = this, $ = c && (e == null ? void 0 : e.loading) || void 0, E = {
329
357
  "c-modal": !0,
330
- [`c-modal--${T}`]: !0,
331
- "c-modal--top": F === "top",
332
- "c-modal--dismissible": i,
333
- "c-modal--loading": b,
334
- "c-modal--pinnedFooter": c,
335
- "c-modal--fullWidthBelowMid": g
358
+ [`c-modal--${z}`]: !0,
359
+ "c-modal--top": g === "top",
360
+ "c-modal--dismissible": o,
361
+ "c-modal--loading": c,
362
+ "c-modal--pinnedFooter": t,
363
+ "c-modal--fullWidthBelowMid": i
336
364
  };
337
365
  return s`
338
366
  <dialog
339
367
  id="dialog"
340
- class="${S(P)}"
341
- aria-busy="${b ? "true" : "false"}"
342
- aria-label="${k(E)}"
368
+ class="${B(E)}"
369
+ aria-busy="${c ? "true" : "false"}"
370
+ aria-label="${b($)}"
343
371
  data-test-id="pie-modal">
344
- <header class="c-modal-header"
345
- data-test-id="modal-header">
372
+ <header class="c-modal-header" data-test-id="modal-header">
346
373
  ${this.renderBackButton()}
347
- <${B} class="c-modal-heading">
348
- ${e}
349
- </${B}>
374
+ ${this.renderHeading()}
350
375
  ${this.renderCloseButton()}
351
376
  </header>
352
377
  ${// We need to wrap the remaining content in a shared scrollable container if the footer is not pinned
353
- c ? this.renderModalContentAndFooter() : s`
378
+ t ? this.renderModalContentAndFooter() : s`
354
379
  <div class="c-modal-scrollContainer">
355
380
  ${this.renderModalContentAndFooter()}
356
381
  </div>`}
357
382
  </dialog>`;
358
383
  }
359
384
  };
360
- z.styles = N(Q);
361
- let a = z;
385
+ C.styles = T(X);
386
+ let a = C;
362
387
  l([
363
388
  d({ type: Object })
364
389
  ], a.prototype, "aria");
365
390
  l([
366
391
  d({ type: String }),
367
- Y(v)
392
+ N(v)
368
393
  ], a.prototype, "heading");
369
394
  l([
370
395
  d(),
371
- _(v, Z, r.headingLevel)
396
+ _(v, G, r.headingLevel)
372
397
  ], a.prototype, "headingLevel");
373
398
  l([
374
399
  d({ type: Boolean })
@@ -396,31 +421,31 @@ l([
396
421
  ], a.prototype, "leadingAction");
397
422
  l([
398
423
  d(),
399
- _(v, eo, r.position)
424
+ _(v, Q, r.position)
400
425
  ], a.prototype, "position");
401
426
  l([
402
427
  d()
403
428
  ], a.prototype, "returnFocusAfterCloseSelector");
404
429
  l([
405
430
  d(),
406
- _(v, oo, r.size)
431
+ _(v, J, r.size)
407
432
  ], a.prototype, "size");
408
433
  l([
409
434
  d({ type: Object })
410
435
  ], a.prototype, "supportingAction");
411
436
  l([
412
- R("dialog")
437
+ K("dialog")
413
438
  ], a.prototype, "_dialog");
414
- V(v, a);
439
+ W(v, a);
415
440
  export {
416
441
  L as ON_MODAL_BACK_EVENT,
417
442
  A as ON_MODAL_CLOSE_EVENT,
418
- to as ON_MODAL_LEADING_ACTION_CLICK,
419
- x as ON_MODAL_OPEN_EVENT,
420
- io as ON_MODAL_SUPPORTING_ACTION_CLICK,
443
+ Z as ON_MODAL_LEADING_ACTION_CLICK,
444
+ w as ON_MODAL_OPEN_EVENT,
445
+ ee as ON_MODAL_SUPPORTING_ACTION_CLICK,
421
446
  a as PieModal,
422
447
  r as defaultProps,
423
- Z as headingLevels,
424
- eo as positions,
425
- oo as sizes
448
+ G as headingLevels,
449
+ Q as positions,
450
+ J as sizes
426
451
  };
package/dist/react.d.ts CHANGED
@@ -169,12 +169,14 @@ declare class PieModal_2 extends PieModal_base implements ModalProps {
169
169
  private _dialog;
170
170
  private _backButtonClicked;
171
171
  private _abortController;
172
+ private _escKeyAbortController;
172
173
  private get _modalScrollContainer();
173
174
  static styles: CSSResult;
174
175
  connectedCallback(): void;
175
176
  disconnectedCallback(): void;
176
177
  firstUpdated(changedProperties: PropertyValues<this>): Promise<void>;
177
178
  updated(changedProperties: PropertyValues<this>): void;
179
+ private _handleIsDismissibleChanged;
178
180
  /**
179
181
  * Opens the dialog element and disables page scrolling
180
182
  */
@@ -183,13 +185,21 @@ declare class PieModal_2 extends PieModal_base implements ModalProps {
183
185
  * Closes the dialog element and re-enables page scrolling
184
186
  */
185
187
  private _handleModalClosed;
188
+ /**
189
+ * Sets up an event listener on the Escape key to prevent dismissing the modal if isDismissible is false
190
+ */
191
+ private _setupEscKeyListener;
192
+ /**
193
+ * Removes any event listeners set up that are listening to keyboard events and nulls the existing AbortController.
194
+ */
195
+ private _removeEscKeyEventListener;
186
196
  /**
187
197
  * Prevents the user from dismissing the dialog via the `cancel`
188
198
  * event (ESC key) when `isDismissible` is set to false.
189
199
  *
190
200
  * @param {Event} event - The event object.
191
201
  */
192
- private _handleDialogCancelEvent;
202
+ private _preventModalKeyboardDismissal;
193
203
  private _handleModalOpenStateOnFirstRender;
194
204
  private _handleModalOpenStateChanged;
195
205
  private _handleActionClick;
@@ -258,6 +268,11 @@ declare class PieModal_2 extends PieModal_base implements ModalProps {
258
268
  * @private
259
269
  */
260
270
  private renderModalContentAndFooter;
271
+ /**
272
+ * Renders the modal heading content in the correct heading tag
273
+ * @private
274
+ */
275
+ private renderHeading;
261
276
  render(): TemplateResult;
262
277
  /**
263
278
  * Dismisses the modal on backdrop click if `isDismissible` is `true`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-modal",
3
- "version": "0.48.1",
3
+ "version": "0.49.0",
4
4
  "description": "PIE design system modal built using web components",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -110,6 +110,10 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
110
110
 
111
111
  private _abortController!: AbortController;
112
112
 
113
+ // We are using a separate controller for this event because it needs
114
+ // to be aborted / reinstantiated on modal open/close
115
+ private _escKeyAbortController: AbortController | null = null;
116
+
113
117
  private get _modalScrollContainer (): Element | null {
114
118
  return this._dialog.querySelector('.c-modal-scrollContainer');
115
119
  }
@@ -123,39 +127,52 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
123
127
  const { signal } = this._abortController;
124
128
 
125
129
  this.addEventListener('click', (event) => this._handleDialogLightDismiss(event));
130
+
131
+ this._setupEscKeyListener();
132
+
126
133
  document.addEventListener(ON_MODAL_OPEN_EVENT, (event) => this._handleModalOpened(<CustomEvent>event), { signal });
127
134
  document.addEventListener(ON_MODAL_CLOSE_EVENT, (event) => this._handleModalClosed(<CustomEvent>event), { signal });
128
135
  document.addEventListener(ON_MODAL_BACK_EVENT, (event) => this._handleModalClosed(<CustomEvent>event), { signal });
129
136
  }
130
137
 
131
138
  disconnectedCallback (): void {
132
- // Aborts all event listeners
133
- this._abortController.abort();
139
+ super.disconnectedCallback();
134
140
 
141
+ this._abortController.abort();
135
142
  this._enableBodyScroll();
136
- super.disconnectedCallback();
143
+ this._removeEscKeyEventListener();
137
144
  }
138
145
 
139
146
  async firstUpdated (changedProperties: PropertyValues<this>): Promise<void> {
140
- super.firstUpdated(changedProperties);
141
-
142
- if (this._dialog) {
143
- const dialogPolyfill = await import('dialog-polyfill').then((module) => module.default);
144
- dialogPolyfill.registerDialog(this._dialog);
145
- const { signal } = this._abortController;
147
+ const dialogPolyfill = await import('dialog-polyfill').then((module) => module.default);
148
+ dialogPolyfill.registerDialog(this._dialog);
149
+ const { signal } = this._abortController;
146
150
 
147
- this._dialog.addEventListener('cancel', (event) => this._handleDialogCancelEvent(event), { signal });
148
- this._dialog.addEventListener('close', () => {
149
- this.isOpen = false;
150
- }, { signal });
151
- }
151
+ this._dialog.addEventListener('close', () => {
152
+ this.isOpen = false;
153
+ }, { signal });
152
154
 
153
155
  this._handleModalOpenStateOnFirstRender(changedProperties);
154
156
  }
155
157
 
156
158
  updated (changedProperties: PropertyValues<this>): void {
157
- super.updated(changedProperties);
158
159
  this._handleModalOpenStateChanged(changedProperties);
160
+ this._handleIsDismissibleChanged(changedProperties);
161
+ }
162
+
163
+ private _handleIsDismissibleChanged (changedProperties: PropertyValues<this>) {
164
+ const oldValue = changedProperties.get('isDismissible');
165
+ const newValue = this.isDismissible;
166
+
167
+ // if the modal is being set to dismissible, remove any esc key listener
168
+ if (!oldValue && newValue) {
169
+ this._removeEscKeyEventListener();
170
+ }
171
+
172
+ // if the modal is being set to NOT dismissible, add the esc key listener
173
+ if (oldValue && !newValue) {
174
+ this._setupEscKeyListener();
175
+ }
159
176
  }
160
177
 
161
178
  /**
@@ -171,6 +188,8 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
171
188
  return;
172
189
  }
173
190
 
191
+ this._setupEscKeyListener();
192
+
174
193
  // The ::backdrop pseudoelement is only shown if the modal is opened via JS
175
194
  this._dialog.showModal();
176
195
  }
@@ -186,17 +205,42 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
186
205
  this._enableBodyScroll();
187
206
  this._dialog.close();
188
207
  this._returnFocus();
208
+ this._removeEscKeyEventListener();
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Sets up an event listener on the Escape key to prevent dismissing the modal if isDismissible is false
214
+ */
215
+ private _setupEscKeyListener () : void {
216
+ // AbortControllers are single-use. Meaning if we have already aborted this controller,
217
+ // we need to create a new one for setting up subsequent event listeners.
218
+ // We only perform this if the controller does not exist to prevent multiple listeners being set up
219
+ if (!this._escKeyAbortController && !this.isDismissible) {
220
+ this._escKeyAbortController = new AbortController();
221
+ const { signal } = this._escKeyAbortController;
222
+
223
+ document.addEventListener('keydown', (event: KeyboardEvent) => this._preventModalKeyboardDismissal(event), { signal });
189
224
  }
190
225
  }
191
226
 
227
+ /**
228
+ * Removes any event listeners set up that are listening to keyboard events and nulls the existing AbortController.
229
+ */
230
+ private _removeEscKeyEventListener () {
231
+ this._escKeyAbortController?.abort();
232
+ // This is so we can create a new AbortController for any subsequent event listener setup in the future
233
+ this._escKeyAbortController = null;
234
+ }
235
+
192
236
  /**
193
237
  * Prevents the user from dismissing the dialog via the `cancel`
194
238
  * event (ESC key) when `isDismissible` is set to false.
195
239
  *
196
240
  * @param {Event} event - The event object.
197
241
  */
198
- private _handleDialogCancelEvent = (event: Event): void => {
199
- if (!this.isDismissible) {
242
+ private _preventModalKeyboardDismissal = (event: KeyboardEvent): void => {
243
+ if (event.key === 'Escape') {
200
244
  event.preventDefault();
201
245
  }
202
246
  };
@@ -429,11 +473,24 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
429
473
  ${this.renderModalFooter()}`;
430
474
  }
431
475
 
476
+ /**
477
+ * Renders the modal heading content in the correct heading tag
478
+ * @private
479
+ */
480
+ private renderHeading (): TemplateResult {
481
+ const { heading, headingLevel } = this;
482
+ const headingTag = unsafeStatic(headingLevel);
483
+
484
+ return html`
485
+ <${headingTag} class="c-modal-heading">
486
+ ${heading}
487
+ </${headingTag}>
488
+ `;
489
+ }
490
+
432
491
  render () {
433
492
  const {
434
493
  aria,
435
- heading,
436
- headingLevel,
437
494
  isDismissible,
438
495
  isFooterPinned,
439
496
  isFullWidthBelowMid,
@@ -442,7 +499,6 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
442
499
  size,
443
500
  } = this;
444
501
 
445
- const headingTag = unsafeStatic(headingLevel);
446
502
  const ariaLabel = (isLoading && aria?.loading) || undefined;
447
503
 
448
504
  const modalClasses = {
@@ -462,12 +518,9 @@ export class PieModal extends RtlMixin(LitElement) implements ModalProps {
462
518
  aria-busy="${isLoading ? 'true' : 'false'}"
463
519
  aria-label="${ifDefined(ariaLabel)}"
464
520
  data-test-id="pie-modal">
465
- <header class="c-modal-header"
466
- data-test-id="modal-header">
521
+ <header class="c-modal-header" data-test-id="modal-header">
467
522
  ${this.renderBackButton()}
468
- <${headingTag} class="c-modal-heading">
469
- ${heading}
470
- </${headingTag}>
523
+ ${this.renderHeading()}
471
524
  ${this.renderCloseButton()}
472
525
  </header>
473
526
  ${