lightning-base-components 1.14.3-alpha → 1.14.7-alpha

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.
Files changed (144) hide show
  1. package/metadata/raptor.json +37 -4
  2. package/package.json +11 -4
  3. package/scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js +1 -0
  4. package/scopedImports/@salesforce-label-LightningProgressBar.progressBar.js +1 -0
  5. package/src/lightning/alert/__docs__/alert.md +101 -0
  6. package/src/lightning/alert/__examples__disabled/basic/basic.css +7 -0
  7. package/src/lightning/alert/__examples__disabled/basic/basic.html +8 -0
  8. package/src/lightning/alert/__examples__disabled/basic/basic.js +14 -0
  9. package/src/lightning/alert/alert.html +3 -0
  10. package/src/lightning/alert/alert.js +78 -0
  11. package/src/lightning/alert/alert.js-meta.xml +6 -0
  12. package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +9 -0
  13. package/src/lightning/ariaObserver/ariaObserver.js +24 -35
  14. package/src/lightning/baseFormattedText/baseFormattedText.html +6 -1
  15. package/src/lightning/baseFormattedText/baseFormattedText.js +5 -0
  16. package/src/lightning/buttonMenu/buttonMenu.js +12 -0
  17. package/src/lightning/confirm/__docs__/confirm.md +100 -0
  18. package/src/lightning/confirm/__examples__disabled/basic/basic.css +7 -0
  19. package/src/lightning/confirm/__examples__disabled/basic/basic.html +8 -0
  20. package/src/lightning/confirm/__examples__disabled/basic/basic.js +14 -0
  21. package/src/lightning/confirm/confirm.html +3 -0
  22. package/src/lightning/confirm/confirm.js +80 -0
  23. package/src/lightning/confirm/confirm.js-meta.xml +6 -0
  24. package/src/lightning/datatable/__examples__/withInfiniteLoading/fetchDataHelper.js +21 -0
  25. package/src/lightning/datatable/__examples__/withInfiniteLoading/withInfiniteLoading.html +13 -0
  26. package/src/lightning/datatable/__examples__/withInfiniteLoading/withInfiniteLoading.js +42 -0
  27. package/src/lightning/datatable/autoWidthStrategy.js +170 -61
  28. package/src/lightning/datatable/{resizer.js → columnResizer.js} +0 -0
  29. package/src/lightning/datatable/columnWidthManager.js +226 -44
  30. package/src/lightning/datatable/columns.js +166 -71
  31. package/src/lightning/datatable/datatable.js +132 -60
  32. package/src/lightning/datatable/fixedWidthStrategy.js +43 -8
  33. package/src/lightning/datatable/headerActions.js +2 -2
  34. package/src/lightning/datatable/infiniteLoading.js +100 -28
  35. package/src/lightning/datatable/inlineEdit.js +21 -30
  36. package/src/lightning/datatable/keyboard.js +166 -131
  37. package/src/lightning/datatable/renderManager.js +117 -122
  38. package/src/lightning/datatable/{datatableResizeObserver.js → resizeObserver.js} +46 -29
  39. package/src/lightning/datatable/resizeSensor.js +19 -3
  40. package/src/lightning/datatable/rowSelection.js +1 -1
  41. package/src/lightning/datatable/rowSelectionShared.js +33 -20
  42. package/src/lightning/datatable/rows.js +7 -8
  43. package/src/lightning/datatable/sort.js +8 -8
  44. package/src/lightning/datatable/state.js +14 -2
  45. package/src/lightning/datatable/templates/div/div.html +127 -117
  46. package/src/lightning/datatable/templates/table/table.html +5 -0
  47. package/src/lightning/datatable/tree.js +25 -0
  48. package/src/lightning/datatable/types.js +77 -9
  49. package/src/lightning/datatable/utils.js +51 -24
  50. package/src/lightning/datatable/virtualization.js +319 -0
  51. package/src/lightning/datatable/widthManagerShared.js +27 -3
  52. package/src/lightning/datatable/wrapText.js +115 -48
  53. package/src/lightning/formattedDateTime/__docs__/formattedDateTime.md +36 -3
  54. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.html +2 -2
  55. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.js +3 -1
  56. package/src/lightning/formattedDateTime/__examples__/time/time.html +1 -1
  57. package/src/lightning/formattedDateTime/__examples__/time/time.js +3 -1
  58. package/src/lightning/formattedDateTime/formattedDateTime.js +1 -0
  59. package/src/lightning/iconSvgTemplates/buildTemplates/standard/dashboard_component.html +7 -0
  60. package/src/lightning/iconSvgTemplates/buildTemplates/standard/slack.html +7 -0
  61. package/src/lightning/iconSvgTemplates/buildTemplates/standard/tableau.html +7 -0
  62. package/src/lightning/iconSvgTemplates/buildTemplates/standard/travel_mode.html +2 -2
  63. package/src/lightning/iconSvgTemplates/buildTemplates/templates.js +8 -1
  64. package/src/lightning/iconSvgTemplates/buildTemplates/utility/data_model.html +7 -0
  65. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product.html +1 -1
  66. package/src/lightning/iconSvgTemplates/buildTemplates/utility/serialized_product_transaction.html +2 -1
  67. package/src/lightning/iconSvgTemplates/buildTemplates/utility/slack.html +7 -0
  68. package/src/lightning/iconSvgTemplates/buildTemplates/utility/tableau.html +7 -0
  69. package/src/lightning/iconSvgTemplates/buildTemplates/utility/video_off.html +7 -0
  70. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/dashboard_component.html +7 -0
  71. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/slack.html +7 -0
  72. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/tableau.html +7 -0
  73. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/standard/travel_mode.html +2 -2
  74. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/templates.js +8 -1
  75. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/data_model.html +7 -0
  76. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product.html +1 -1
  77. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  78. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/slack.html +7 -0
  79. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/tableau.html +7 -0
  80. package/src/lightning/iconSvgTemplatesRtl/buildTemplates/utility/video_off.html +7 -0
  81. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/dashboard_component.html +7 -0
  82. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/slack.html +7 -0
  83. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/tableau.html +7 -0
  84. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/standard/travel_mode.html +2 -2
  85. package/src/lightning/iconSvgTemplatesStandard/buildTemplates/templates.js +4 -1
  86. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/dashboard_component.html +7 -0
  87. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/slack.html +7 -0
  88. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/tableau.html +7 -0
  89. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/standard/travel_mode.html +2 -2
  90. package/src/lightning/iconSvgTemplatesStandardRtl/buildTemplates/templates.js +4 -1
  91. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/templates.js +5 -1
  92. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/data_model.html +7 -0
  93. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product.html +1 -1
  94. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/serialized_product_transaction.html +2 -1
  95. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/slack.html +7 -0
  96. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/tableau.html +7 -0
  97. package/src/lightning/iconSvgTemplatesUtility/buildTemplates/utility/video_off.html +7 -0
  98. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/templates.js +5 -1
  99. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/data_model.html +7 -0
  100. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product.html +1 -1
  101. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/serialized_product_transaction.html +2 -1
  102. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/slack.html +7 -0
  103. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/tableau.html +7 -0
  104. package/src/lightning/iconSvgTemplatesUtilityRtl/buildTemplates/utility/video_off.html +7 -0
  105. package/src/lightning/input/__docs__/input.md +2 -0
  106. package/src/lightning/input/input.html +2 -5
  107. package/src/lightning/interactiveDialogBase/interactiveDialogBase.css +494 -0
  108. package/src/lightning/interactiveDialogBase/interactiveDialogBase.html +63 -0
  109. package/src/lightning/interactiveDialogBase/interactiveDialogBase.js +200 -0
  110. package/src/lightning/menuItem/menuItem.js +4 -1
  111. package/src/lightning/modalBase/modalBase.css +20 -0
  112. package/src/lightning/modalBase/modalBase.html +54 -0
  113. package/src/lightning/modalBase/modalBase.js +1039 -0
  114. package/src/lightning/overlay/__docs__/overlay.md +90 -0
  115. package/src/lightning/overlay/__examples__/alert/alert.html +27 -0
  116. package/src/lightning/overlay/__examples__/alert/alert.js +33 -0
  117. package/src/lightning/overlay/__examples__/basic/basic.css +7 -0
  118. package/src/lightning/overlay/__examples__/basic/basic.html +18 -0
  119. package/src/lightning/overlay/__examples__/basic/basic.js +61 -0
  120. package/src/lightning/overlay/__examples__/demo/demo.html +29 -0
  121. package/src/lightning/overlay/__examples__/demo/demo.js +40 -0
  122. package/src/lightning/overlay/__examples__/panel/panel.html +17 -0
  123. package/src/lightning/overlay/__examples__/panel/panel.js +21 -0
  124. package/src/lightning/overlay/overlay.html +3 -0
  125. package/src/lightning/overlay/overlay.js +45 -0
  126. package/src/lightning/overlayContainer/__docs__/overlayContainer.md +0 -0
  127. package/src/lightning/overlayContainer/overlayContainer.html +3 -0
  128. package/src/lightning/overlayContainer/overlayContainer.js +138 -0
  129. package/src/lightning/overlayManager/overlayManager.js +54 -0
  130. package/src/lightning/overlayUtils/overlayUtils.js +17 -0
  131. package/src/lightning/progressBar/progressBar.html +2 -1
  132. package/src/lightning/progressBar/progressBar.js +18 -1
  133. package/src/lightning/prompt/__docs__/prompt.md +102 -0
  134. package/src/lightning/prompt/__examples__disabled/basic/basic.css +7 -0
  135. package/src/lightning/prompt/__examples__disabled/basic/basic.html +8 -0
  136. package/src/lightning/prompt/__examples__disabled/basic/basic.js +15 -0
  137. package/src/lightning/prompt/prompt.css +81 -0
  138. package/src/lightning/prompt/prompt.html +8 -0
  139. package/src/lightning/prompt/prompt.js +92 -0
  140. package/src/lightning/prompt/prompt.js-meta.xml +6 -0
  141. package/src/lightning/spinner/spinner.html +1 -1
  142. package/src/lightning/spinner/spinner.js +12 -0
  143. package/src/lightning/utilsPrivate/phonify.js +1 -1
  144. package/scopedImports/@salesforce-label-LightningModalBase.close.js +0 -1
@@ -0,0 +1,1039 @@
1
+ import { api, LightningElement } from 'lwc';
2
+ import { classSet } from 'lightning/utils';
3
+ import {
4
+ makeEverythingExceptElementInert,
5
+ normalizeString,
6
+ synchronizeAttrs,
7
+ restoreInertness,
8
+ hasAnimation,
9
+ ARIA,
10
+ isAriaDescriptionSupported,
11
+ } from 'lightning/utilsPrivate';
12
+ import { instanceName, secure } from 'lightning/overlayUtils';
13
+ import { getElementWithFocus } from 'lightning/focusUtils';
14
+ import closeButtonAltText from '@salesforce/label/LightningModalBase.cancelandclose';
15
+ import disableCloseBtnMessage from '@salesforce/label/LightningModalBase.waitstate';
16
+
17
+ const DEBOUNCE_RESIZE = 300;
18
+
19
+ export default class LightningModalBase extends LightningElement {
20
+ // this is visible in DOM, changed from 'lightning-modal-base'
21
+ static [instanceName] = 'lightning-modal-base';
22
+
23
+ // private tracked state
24
+ initialRender = true;
25
+ autoFocusCompletedOnce = false;
26
+ windowResizeEventsBound = false;
27
+ timeoutId = 0;
28
+ disableCloseButton = false;
29
+ sectionAriaBusy = null;
30
+ closeButtonAltText = closeButtonAltText;
31
+ disableCloseBtnMessage = disableCloseBtnMessage;
32
+ // modalHeader, child
33
+ headerRegistered = false;
34
+ headerHeight = 0;
35
+ headerDefaultSlotIsPopulated = false;
36
+ headerSlotWrapperId = null;
37
+ headerSlotHasRendered = false;
38
+ headerLabelId = null;
39
+ headerLabelIsPopulated = null;
40
+ headerTitleRef = null;
41
+ headerTabElemRef = null;
42
+
43
+ // modalBody, child
44
+ bodyRegistered = false;
45
+ bodyDefaultSlotIsPopulated = false;
46
+ bodySlotHasRendered = false;
47
+ bodyId = null;
48
+ baseUpdateBodyCallback = null;
49
+ bodyResizeScheduled = false;
50
+ bodyTabElemRef = null;
51
+
52
+ // modalFooter, child
53
+ footerRegistered = false;
54
+ footerHeight = 0;
55
+ footerSlotHasRendered = false;
56
+ footerDefaultSlotIsPopulated = false;
57
+ footerTabElemRef = null;
58
+
59
+ // aria attributes
60
+ modalLabel = null;
61
+ modalLabelledBy = null;
62
+ modalDescribedBy = null;
63
+ // currently used for disableClose
64
+ showAriaLiveMessage = false;
65
+ ariaLiveMessage = '';
66
+
67
+ // modal features
68
+ isModalOpen = false;
69
+ isModalTransitioningIn = false;
70
+ _size = 'medium';
71
+ // modal background elements
72
+ savedInertElements = [];
73
+ // before modal opened, element previously focused
74
+ savedActiveElement;
75
+
76
+ /**
77
+ * <lightning-modal> label value
78
+ * Text to display as the heading at the top of the modal
79
+ */
80
+ get label() {
81
+ const modal = this.modal;
82
+ if (!modal) {
83
+ return '';
84
+ }
85
+ return modal.label;
86
+ }
87
+
88
+ /**
89
+ * <lightning-modal> description value
90
+ * Text used for the accessible description of the modal. see updateAriaDescription()
91
+ * Note: this value is not visible in the UI, and only to screen readers
92
+ */
93
+ get description() {
94
+ const modal = this.modal;
95
+ if (!modal) {
96
+ return '';
97
+ }
98
+ return modal.description;
99
+ }
100
+
101
+ /**
102
+ * Get the current modal size, calculated as a percentage of the viewport.
103
+ * Valid values are small, medium, and large. The default is medium.
104
+ * @type {string}
105
+ * @default medium
106
+ */
107
+ get size() {
108
+ const sizeDefault = 'medium';
109
+ const modal = this.modal;
110
+ if (!modal) {
111
+ return sizeDefault;
112
+ }
113
+
114
+ // get the size value from <lightning-modal>
115
+ return normalizeString(modal.size, {
116
+ fallbackValue: sizeDefault,
117
+ validValues: ['small', 'medium', 'large'],
118
+ });
119
+ }
120
+
121
+ /**
122
+ * <lightning-modal> disableClose value
123
+ * Get attribute to trigger disabling ability to dismiss modal temporarily
124
+ */
125
+ get disableClose() {
126
+ const modal = this.modal;
127
+ if (!modal) {
128
+ return false;
129
+ }
130
+ return modal.disableClose;
131
+ }
132
+
133
+ /**
134
+ * Toggle on and off disable close button feature
135
+ * typically used very briefly when devs want to save form data to backend
136
+ * and do not want the form to be closed before the save has
137
+ * completed successfully
138
+ * toggleDisableCloseButton sets local state to
139
+ * (a) toggle display an aria-live message
140
+ * (b) toggle set disabled on the <lightning-button-icon>
141
+ * (c) toggle set aria-busy value on
142
+ * elsewhere in modalBase and modal, ESC key is also disabled, and
143
+ * calls to this.close() are prevented
144
+ */
145
+ toggleDisableCloseButton() {
146
+ // this.disableCloseButton is local modalBase state
147
+ // this.disableClose is modal.js @api state
148
+ // we track both in order to handle transition correctly
149
+ const isSwitchingToDisabled =
150
+ !this.disableCloseButton && this.disableClose;
151
+ /* Future enhancement possibility - trigger setInterval to remove and
152
+ again add back 'Processing' text, as this will indicate to the screen
153
+ reader user that the interface continues to be busy
154
+ */
155
+ const disableCloseButtonMessage = isSwitchingToDisabled
156
+ ? this.disableCloseBtnMessage
157
+ : '';
158
+ if (isSwitchingToDisabled) {
159
+ // Should disable close button
160
+ this.ariaLiveMessage = disableCloseButtonMessage;
161
+ this.showAriaLiveMessage = true;
162
+ synchronizeAttrs(this.modalWrapper, { [`${ARIA.BUSY}`]: true });
163
+ synchronizeAttrs(this.modalCloseButton, { disabled: 'disabled' });
164
+ this.disableCloseButton = true;
165
+ } else {
166
+ // Should enable close button
167
+ this.ariaLiveMessage = disableCloseBtnMessage;
168
+ this.showAriaLiveMessage = false;
169
+ synchronizeAttrs(this.modalWrapper, { [`${ARIA.BUSY}`]: null });
170
+ synchronizeAttrs(this.modalCloseButton, { disabled: null });
171
+ this.disableCloseButton = false;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Public method to get the modal slot element
177
+ * @type {(HTMLElement|null)} The modal slot, currently a div elem
178
+ */
179
+ @api
180
+ get defaultSlot() {
181
+ return this.template.querySelector('[data-slot]');
182
+ }
183
+
184
+ /**
185
+ * Private method to get the modal section element, the outer wrapper for modal elements
186
+ * @returns {(HTMLElement|null)} The section element, currently the section[role="dialog"]
187
+ * @private
188
+ */
189
+ get modalWrapper() {
190
+ return this.template.querySelector('[data-modal]');
191
+ }
192
+
193
+ /**
194
+ * Private method to get the lightning-modal element inside the div slot
195
+ * This element has the api for .close, size, label
196
+ * @returns {(HTMLElement|null)} The modal inside <div data-slot>: <lightning-modal>
197
+ * @private
198
+ */
199
+ get modal() {
200
+ return (this.defaultSlot && this.defaultSlot.childNodes[0]) || null;
201
+ }
202
+
203
+ /**
204
+ * Private method to get the modal span description element
205
+ * This span is present when the accessible 'description' api text is present,
206
+ * and aria-description isn't supported. Instead, aria-describedby is utilized
207
+ * @returns {(HTMLElement|null)} The modal span element for IDRef linkage
208
+ * @private
209
+ */
210
+ get modalDescSpan() {
211
+ return this.template.querySelector('[data-aria-description]');
212
+ }
213
+
214
+ /**
215
+ * Get the lightning-button-icon (close button element)
216
+ * @returns {(HTMLElement|null)}
217
+ * @private
218
+ */
219
+ get modalCloseButton() {
220
+ return this.template.querySelector('[data-close-button]');
221
+ }
222
+
223
+ /**
224
+ * Get the lightning-modal element within the modal slot
225
+ * currently modalBase's slot is a div element, not a slot element
226
+ * @returns {(HTMLElement|null)}
227
+ * @private
228
+ */
229
+ get modalElement() {
230
+ const modalSlot = this.defaultSlot;
231
+ if (!modalSlot) {
232
+ return null;
233
+ }
234
+ return modalSlot.querySelector('lightning-modal');
235
+ }
236
+
237
+ /**
238
+ * Get the lightning-modal element's backdrop
239
+ * @returns {(HTMLElement|null)}
240
+ * @private
241
+ */
242
+ get modalBackdrop() {
243
+ return this.template.querySelector('[data-backdrop]');
244
+ }
245
+
246
+ /**
247
+ * Get the background element height
248
+ * used to calculate max height on the modalBody using
249
+ * modal -> modalBody callback
250
+ * @returns {number}
251
+ * @private
252
+ */
253
+ get backdropHeight() {
254
+ const backdropElem = this.modalBackdrop;
255
+ const backdropRect = backdropElem
256
+ ? backdropElem.getBoundingClientRect()
257
+ : {};
258
+ const { height } = backdropRect;
259
+ return height || 0;
260
+ }
261
+
262
+ /**
263
+ * Gets the CSS classes applicable to the outer modal wrapper element
264
+ * Modal foreground triggers on this.isModalTransitioningIn to
265
+ * fade in/out or animate up/down
266
+ * @returns {string} CSS class string
267
+ * @private
268
+ */
269
+ get modalCssClasses() {
270
+ let classes = classSet('slds-modal fix-slds-modal');
271
+ const sizeClass = this.size;
272
+ if (hasAnimation()) {
273
+ // .slds-fade-in-open not present to trigger opacity animation
274
+ // when later this.isModalTransitioningIn is set to TRUE
275
+ // animation then occurs
276
+ classes.add({
277
+ 'slds-fade-in-open': this.isModalTransitioningIn,
278
+ });
279
+ } else {
280
+ // no animation occurs if the .slds-fade-in-open class
281
+ // is immediately present in DOM
282
+ classes.add({
283
+ 'slds-fade-in-open': true,
284
+ });
285
+ }
286
+ // don't add animation related css classes in this group
287
+ classes.add({
288
+ 'slds-modal_medium': sizeClass === 'medium',
289
+ 'slds-modal_large': sizeClass === 'large',
290
+ 'slds-modal_small': sizeClass === 'small',
291
+ });
292
+ return classes.toString();
293
+ }
294
+
295
+ /**
296
+ * Gets the CSS classes applicable to the modal background element
297
+ * Backdrop triggers on this.isModalOpen to fade/animate in first
298
+ * @returns {string} CSS class string
299
+ * @private
300
+ */
301
+ get modalBackdropCssClasses() {
302
+ let classes = classSet('slds-backdrop fix-slds-backdrop');
303
+ if (hasAnimation()) {
304
+ classes.add({
305
+ 'slds-backdrop_open': this.isModalOpen,
306
+ });
307
+ } else {
308
+ // no fading animation occurs when .slds-backdrop_open
309
+ // is immediately present in the DOM
310
+ classes = classes.add({
311
+ 'slds-backdrop_open': true,
312
+ });
313
+ }
314
+ return classes.toString();
315
+ }
316
+
317
+ /**
318
+ * Gets the CSS classes for the modal close button
319
+ * @returns {string} CSS class string
320
+ * @private
321
+ */
322
+ get modalCloseButtonCssClasses() {
323
+ let classes = classSet('slds-modal__close');
324
+ classes.add({
325
+ 'fix-slds-modal-close-disabled': this.disableCloseButton,
326
+ });
327
+ return classes;
328
+ }
329
+
330
+ /**
331
+ * Saves the current active focused element on modal creation
332
+ * in order to be able to set focus back to that previous element
333
+ * @private
334
+ */
335
+ saveActiveElement() {
336
+ this.savedActiveElement = getElementWithFocus();
337
+ }
338
+
339
+ /**
340
+ * Renders most background elements inert, while modal active
341
+ * and saves them for later setting background elements active
342
+ * @private
343
+ */
344
+ renderBackgroundInert() {
345
+ this.savedInertElements = makeEverythingExceptElementInert(
346
+ this.template.host
347
+ );
348
+ }
349
+
350
+ /**
351
+ * Renders most background elements active, as modal closes
352
+ * @private
353
+ */
354
+ renderBackgroundActive() {
355
+ restoreInertness(this.savedInertElements);
356
+ this.savedInertElements = [];
357
+ }
358
+
359
+ /**
360
+ * Returns focus to background element previously focused,
361
+ * before modal existed
362
+ * @private
363
+ */
364
+ returnFocusToBackground() {
365
+ const { savedActiveElement } = this;
366
+ const isSavedElemInDOM = document.body.contains(savedActiveElement);
367
+ if (savedActiveElement && isSavedElemInDOM) {
368
+ savedActiveElement.focus();
369
+ } else {
370
+ // eslint-disable-next-line no-console
371
+ console.warn('Modal :: Nothing to return focus to');
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Queue the showing of the modal
377
+ * utilized for triggering fade in modal CSS class additions
378
+ * @private
379
+ */
380
+ queueShowModal() {
381
+ if (this.isModalOpen && !this.isModalTransitioningIn) {
382
+ this.isModalTransitioningIn = true;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Opening the modal involves first performing necessary steps to
388
+ * prepare for when the modal closes
389
+ * isModalOpen triggers fade in CSS class on modal background
390
+ * @private
391
+ */
392
+ openModal() {
393
+ this.saveActiveElement();
394
+ this.renderBackgroundInert();
395
+ if (!this.isModalOpen) {
396
+ this.isModalOpen = true;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Closing the modal wraps up the modal lifecycle
402
+ * before it is fully removed
403
+ * @private
404
+ */
405
+ closeModal() {
406
+ this.returnFocusToBackground();
407
+ this.renderBackgroundActive(this.savedInertElements);
408
+ }
409
+
410
+ /**
411
+ * Unsets the aria-labelledby or aria-label values
412
+ * when no label value is provided
413
+ * modal requires 'label' value either at modalHeader or at modal
414
+ * @private
415
+ */
416
+ unsetAriaLabelAndError = () => {
417
+ // unset any previously set aria values
418
+ synchronizeAttrs(this.modalWrapper, {
419
+ [ARIA.LABELLEDBY]: null,
420
+ [ARIA.LABEL]: null,
421
+ });
422
+ // console.error when label empty
423
+ this.errorLabelRequired();
424
+ };
425
+
426
+ get isDescriptionSet() {
427
+ const { description } = this;
428
+ // check for being set, as well as not just a description with spaces
429
+ // avoiding setting aria-describedby on section pointing to
430
+ // an empty SPAN element
431
+ return description && description.trim().length > 0;
432
+ }
433
+
434
+ /**
435
+ * Determines if aria-describedby should be set, and the span tag shown
436
+ * occurs only when aria-description is not supported.
437
+ * ex: when description api is set to '' or ' ',
438
+ * we don't want to show the span or set aria-describedby
439
+ * @private
440
+ */
441
+ get showAriaDescribedBy() {
442
+ return !isAriaDescriptionSupported() && this.isDescriptionSet;
443
+ }
444
+
445
+ /**
446
+ * Set either 'aria-describedby' or 'aria-description' value for accessibility
447
+ * based on the presence of 'description' api value
448
+ * and support of the newer ARIA 'aria-description'.
449
+ * At launch, modern browsers Firefox and Safari DO NOT support it. IE11 won't.
450
+ * @private
451
+ */
452
+ updateAriaDescription() {
453
+ const { description } = this;
454
+ // if aria-description is supported && description set, set aria-description
455
+ if (isAriaDescriptionSupported()) {
456
+ const descriptionToSet = this.isDescriptionSet ? description : null;
457
+ // set aria-description if set, otherwise unset with null
458
+ return synchronizeAttrs(this.modalWrapper, {
459
+ [ARIA.DESCRIPTION]: descriptionToSet,
460
+ });
461
+ }
462
+ // if aria-description not supported, and description is set,
463
+ // and span id exists, or unset aria-describedby with null
464
+ const spanId = (this.isDescriptionSet && this.modalDescSpan.id) || null;
465
+ return synchronizeAttrs(this.modalWrapper, {
466
+ [ARIA.DESCRIBEDBY]: spanId,
467
+ });
468
+ }
469
+
470
+ /**
471
+ * Sets the aria-labelledby or aria-label values for accessibility
472
+ * based on presence of modalHeader child
473
+ * modal requires 'label' value either at modalHeader or at modal
474
+ * @private
475
+ */
476
+ updateAriaLabel() {
477
+ const {
478
+ label,
479
+ headerRegistered,
480
+ headerLabelId,
481
+ headerLabelIsPopulated,
482
+ } = this;
483
+ const labelIsEmpty = label === '' || label.trim().length === 0;
484
+ // when header is present,
485
+ // headerLabelIsPopulated is equivalent labelIsEmpty, but from modalHeader level
486
+ if (headerRegistered) {
487
+ if (headerLabelId && headerLabelIsPopulated) {
488
+ synchronizeAttrs(this.modalWrapper, {
489
+ [ARIA.LABELLEDBY]: headerLabelId,
490
+ [ARIA.LABEL]: null,
491
+ });
492
+ // if labelId not set OR header label value not set,
493
+ // must console.error
494
+ } else {
495
+ this.unsetAriaLabelAndError();
496
+ }
497
+ // unset if no header (gets removed dynamically, or never present)
498
+ } else {
499
+ // fallback to headless variant
500
+ // check label is actually set,
501
+ // and use aria-label instead of aria-labelledby
502
+ if (!labelIsEmpty) {
503
+ synchronizeAttrs(this.modalWrapper, {
504
+ [ARIA.LABELLEDBY]: null,
505
+ [ARIA.LABEL]: label,
506
+ });
507
+ } else {
508
+ // in headless variant, must have label value set
509
+ this.unsetAriaLabelAndError();
510
+ }
511
+ }
512
+ }
513
+
514
+ /**
515
+ * Construct and show console.error for missing label value
516
+ * Modal component requires the label attribute, either via
517
+ * modalHeader or modal to have accessibility set correctly
518
+ * @private
519
+ */
520
+ errorLabelRequired() {
521
+ let errorMsg =
522
+ 'LightningModal - Templates with <lightning-modal-header> should define the label attribute as an attribute on <lightning-modal-header label="Modal Heading"> .';
523
+ errorMsg +=
524
+ ' Templates without <lightning-modal-header> should define the label attribute in the Modal.open({ label: "Modal Heading" })';
525
+ console.error(errorMsg);
526
+ }
527
+
528
+ /**
529
+ * Handle the close button click, or via ESC key
530
+ * @private
531
+ */
532
+ handleCloseClick() {
533
+ // calls handlePrivateClose
534
+ if (!this.disableCloseButton) {
535
+ this.modal.close();
536
+ }
537
+ }
538
+
539
+ // Handle privateclose event firing is prevented from occurring
540
+ // when this.disableClose set true in modal.js
541
+ handlePrivateClose(e) {
542
+ if (!(e.detail && e.detail[secure])) {
543
+ console.error('Invalid access to privateclose event');
544
+ return;
545
+ }
546
+ if (hasAnimation()) {
547
+ /// triggers the disappearance of the foreground modal elements
548
+ this.modalWrapper.classList.remove('slds-fade-in-open');
549
+ this.modalBackdrop.classList.remove('slds-backdrop_open');
550
+ // wait until modalWrappers animation completes, then proceed
551
+ this.modalBackdrop.addEventListener('transitionend', () => {
552
+ e.detail.resolve();
553
+ });
554
+ } else {
555
+ // skip animation, resolve immediately
556
+ e.detail.resolve();
557
+ }
558
+ this.closeModal();
559
+ }
560
+
561
+ handlePrivateDisableCloseButton(e) {
562
+ if (!(e.detail && e.detail[secure])) {
563
+ console.error('Invalid access to privatedisableclose event');
564
+ return;
565
+ }
566
+ this.toggleDisableCloseButton();
567
+ }
568
+
569
+ /**
570
+ * Handle Esc key down events on the modal
571
+ * @param {Event} e The keyboard event
572
+ * @private
573
+ */
574
+ handleModalKeyDown(e) {
575
+ const { ctrlKey, metaKey, shiftKey, key } = e;
576
+ const hasModifier = ctrlKey || metaKey || shiftKey;
577
+ // 'Esc' is IE11 specific, remove when support is dropped
578
+ // when disableCloseButton set true, ESC key to close modal is deactivated
579
+ if (
580
+ !hasModifier &&
581
+ !this.disableCloseButton &&
582
+ (key === 'Esc' || key === 'Escape')
583
+ ) {
584
+ e.stopPropagation();
585
+ e.preventDefault();
586
+ this.handleCloseClick();
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Utilized to auto set (autofocus) the browser's focus to
592
+ * the first interactive element within the modal children
593
+ * Fires custom cancelable event 'autofocus'
594
+ * autoFocus should only be done during the modal's creation
595
+ * Needs to wait until all elements have rendered in the DOM
596
+ * Details on autofocus decision tree under 'Opening Dialogs' section:
597
+ * https://www.lightningdesignsystem.com/accessibility/guidelines/global-focus/#dialogs
598
+ * For modal v1, ignore multi-step modal, as not part of scope
599
+ * @private
600
+ */
601
+ focusFirstElement() {
602
+ const { autoFocusCompletedOnce, modalElement } = this;
603
+ // If any of these is TRUE, exit before proceeding
604
+ // 1. if modal has already been autofocused once, exit immediately
605
+ // 2. need to wait for modalElement to be rendered to the DOM
606
+ if (autoFocusCompletedOnce || !modalElement) {
607
+ return;
608
+ }
609
+
610
+ const {
611
+ bodyRegistered,
612
+ footerRegistered,
613
+ headerRegistered,
614
+ headerTitleRef,
615
+ headerTabElemRef,
616
+ headerDefaultSlotIsPopulated,
617
+ headerSlotHasRendered,
618
+ bodyDefaultSlotIsPopulated,
619
+ bodySlotHasRendered,
620
+ bodyTabElemRef,
621
+ footerDefaultSlotIsPopulated,
622
+ footerSlotHasRendered,
623
+ footerTabElemRef,
624
+ } = this;
625
+
626
+ const waitForHeaderSlotRender =
627
+ headerRegistered &&
628
+ headerDefaultSlotIsPopulated &&
629
+ !headerSlotHasRendered;
630
+ const waitForBodySlotRender =
631
+ bodyRegistered &&
632
+ bodyDefaultSlotIsPopulated &&
633
+ !bodySlotHasRendered;
634
+ const waitForFooterSlotRender =
635
+ footerRegistered &&
636
+ footerDefaultSlotIsPopulated &&
637
+ !footerSlotHasRendered;
638
+
639
+ // 3. Need to make sure registered child components
640
+ // slots have fully rendered before proceeding
641
+ if (
642
+ waitForHeaderSlotRender ||
643
+ waitForBodySlotRender ||
644
+ waitForFooterSlotRender
645
+ ) {
646
+ return;
647
+ }
648
+
649
+ // preferred autofocus-able elements (links:NOT(tooltip), inputs, buttons)
650
+ // located in order of preference:
651
+ // (1) lightning-modal-header,
652
+ // (2) lightning-modal-body,
653
+ // (3)) lightning-modal-footer
654
+ const preferredFocusElem =
655
+ headerTabElemRef || bodyTabElemRef || footerTabElemRef || null;
656
+
657
+ // fallback autofocus elements, in order of preference
658
+ // (1) modal heading (headless modal, not available),
659
+ // (2) close button (always present, current version)
660
+ // (3) outer modal element (always present, here ONLY as a backup,
661
+ // for when hide or disable close button becomes available
662
+ const modalHeadingElem =
663
+ headerRegistered && headerTitleRef ? headerTitleRef : null;
664
+ const closeButtonElem = this.modalCloseButton;
665
+ const outerModalElem = this.modalWrapper;
666
+
667
+ const fallbackFocusElem =
668
+ modalHeadingElem || closeButtonElem || outerModalElem;
669
+
670
+ const focusElem = preferredFocusElem
671
+ ? preferredFocusElem
672
+ : fallbackFocusElem;
673
+
674
+ if (focusElem !== null) {
675
+ focusElem.focus();
676
+ this.autoFocusCompletedOnce = true;
677
+ } else {
678
+ // Should never happen since outerModalElem always present
679
+ console.error(
680
+ 'LightningModal - at least one focusable element is required, none found.'
681
+ );
682
+ }
683
+ }
684
+
685
+ /**
686
+ * When child component modalBody is removed,
687
+ * sets private tracked state, detaches window.onresize event listeners
688
+ * @private
689
+ */
690
+ unregisterBody() {
691
+ // FUTURE TODO mechanism to support aria-describedby
692
+ // aria-describedby is optional, without a good reproducible pattern
693
+ this.initBodyState();
694
+ this.detachBodyResizeEvents();
695
+ }
696
+
697
+ /**
698
+ * Registers modalBody with its parent modal, when present
699
+ * Sets private tracked state about modalBody
700
+ * Called when 'onprivatemodalbodyregister' event gets fired
701
+ * @param {Object} detail Passed details object from modalBody
702
+ * @private
703
+ */
704
+ registerBody({
705
+ bodyId,
706
+ bodyIsPopulated,
707
+ updateBodyCallback,
708
+ defaultSlotHasRendered,
709
+ unRegisterCallback,
710
+ firstTabbableElemRef,
711
+ }) {
712
+ this.bodyRegistered = true;
713
+ this.bodyDefaultSlotIsPopulated = bodyIsPopulated;
714
+ this.bodySlotHasRendered = defaultSlotHasRendered;
715
+ this.bodyId = bodyId;
716
+ this.baseUpdateBodyCallback = updateBodyCallback;
717
+ this.bodyTabElemRef = firstTabbableElemRef || null;
718
+ unRegisterCallback(() => {
719
+ this.unregisterBody();
720
+ });
721
+ // cover case if modalBody is removed, then added back
722
+ // required to correctly set the CSS classes on modalBody
723
+ this.updateBodyHeight();
724
+
725
+ // ModalBody can register 2+ times when initially rendering
726
+ if (!this.windowResizeEventsBound) {
727
+ this.bindBodyResizeEvents();
728
+ }
729
+ }
730
+
731
+ /**
732
+ * When modalBody present, update private tracked state
733
+ * @param {Event} e The private custom registration event of modalBody
734
+ * @private
735
+ */
736
+ handleBodyRegister(e) {
737
+ const { detail } = e;
738
+ this.registerBody(detail);
739
+ e.stopPropagation();
740
+ }
741
+
742
+ /**
743
+ * When modalBody removed or at startup, initialize private tracked modalBody state
744
+ * @private
745
+ */
746
+ initBodyState() {
747
+ this.bodyRegistered = false;
748
+ this.bodyDefaultSlotIsPopulated = false;
749
+ this.bodySlotHasRendered = false;
750
+ this.bodyId = null;
751
+ this.baseUpdateBodyCallback = null;
752
+ this.bodyResizeScheduled = false;
753
+ this.bodyTabElemRef = null;
754
+ }
755
+
756
+ /**
757
+ * Sets private tracked state related to modalHeader, when removed
758
+ * @private
759
+ */
760
+ unregisterHeader() {
761
+ this.initHeaderState();
762
+ this.updateAriaLabel();
763
+ }
764
+
765
+ /**
766
+ * Registers modalHeader with its parent modal, when present
767
+ * Sets private tracked state about modalHeader
768
+ * Called when 'onprivatemodalheaderregister' event gets fired
769
+ * @param {Object} detail Passed details object from modalHeader
770
+ * @private
771
+ */
772
+ registerHeader({
773
+ defaultSlotIsPopulated,
774
+ firstTabbableElemRef,
775
+ defaultSlotWrapperId,
776
+ defaultSlotHasRendered,
777
+ unRegisterCallback,
778
+ labelIsPopulated,
779
+ headerHeight,
780
+ headerRef,
781
+ labelId,
782
+ }) {
783
+ this.headerRegistered = true;
784
+ this.headerHeight = headerHeight || 0;
785
+ this.headerDefaultSlotIsPopulated = defaultSlotIsPopulated;
786
+ this.headerSlotHasRendered = defaultSlotHasRendered;
787
+ this.headerSlotWrapperId = defaultSlotWrapperId;
788
+ this.headerLabelId = labelId;
789
+ this.headerLabelIsPopulated = labelIsPopulated;
790
+ this.headerTitleRef = headerRef;
791
+ this.headerTabElemRef = firstTabbableElemRef;
792
+ unRegisterCallback(() => {
793
+ this.unregisterHeader();
794
+ });
795
+ // update modalBody max-height values
796
+ if (this.bodyRegistered) {
797
+ this.updateBodyHeight();
798
+ }
799
+ }
800
+
801
+ /**
802
+ * Event handler for private modalHeader registration
803
+ * When modalHeader present, update private tracked state
804
+ * @param {Event} e Private custom registration event fired
805
+ * @private
806
+ */
807
+ handleHeaderRegister(e) {
808
+ const { detail } = e;
809
+ this.registerHeader(detail);
810
+ this.updateAriaLabel();
811
+ e.stopPropagation();
812
+ }
813
+
814
+ /**
815
+ * When modalHeader removed or at startup, initialize
816
+ * private tracked modalHeader state
817
+ * @private
818
+ */
819
+ initHeaderState() {
820
+ this.headerRegistered = false;
821
+ this.headerHeight = 0;
822
+ this.headerDefaultSlotIsPopulated = false;
823
+ this.headerSlotWrapperId = null;
824
+ this.headerSlotHasRendered = false;
825
+ this.headerLabelId = null;
826
+ this.headerLabelIsPopulated = null;
827
+ this.headerTitleRef = null;
828
+ this.headerTabElemRef = null;
829
+ }
830
+
831
+ /**
832
+ * Sets private tracked state related to modalFooter, when removed
833
+ * @private
834
+ */
835
+ unregisterFooter() {
836
+ this.initFooterState();
837
+ }
838
+
839
+ /**
840
+ * Registers modalFooter with its parent modal, when present
841
+ * Sets private tracked state about modalFooter
842
+ * Called when 'onprivatemodalfooterregister' event gets fired
843
+ * @param {Object} detail Passed details object from modalFooter
844
+ * @private
845
+ */
846
+ registerFooter({
847
+ defaultSlotIsPopulated,
848
+ defaultSlotHasRendered,
849
+ footerHeight,
850
+ unRegisterCallback,
851
+ firstTabbableElemRef,
852
+ }) {
853
+ this.footerRegistered = true;
854
+ this.footerDefaultSlotIsPopulated = defaultSlotIsPopulated;
855
+ this.footerSlotHasRendered = defaultSlotHasRendered;
856
+ this.footerHeight = footerHeight || 0;
857
+ this.footerTabElemRef = firstTabbableElemRef || null;
858
+ unRegisterCallback(() => {
859
+ this.unregisterFooter();
860
+ });
861
+ // update modalBody max-height values
862
+ if (this.bodyRegistered) {
863
+ this.updateBodyHeight();
864
+ }
865
+ }
866
+
867
+ /**
868
+ * Event handler for private modalFooter registration
869
+ * When modalFooter present, update private tracked state
870
+ * @param {Event} e Private custom registration event fired
871
+ * @private
872
+ */
873
+ handleFooterRegister(e) {
874
+ const { detail } = e;
875
+ this.registerFooter(detail);
876
+ e.stopPropagation();
877
+ }
878
+
879
+ /**
880
+ * When modalFooter removed or at startup, initialize
881
+ * private tracked modalFooter state
882
+ * @private
883
+ */
884
+ initFooterState() {
885
+ this.footerRegistered = false;
886
+ this.footerHeight = 0;
887
+ this.footerSlotHasRendered = false;
888
+ this.footerDefaultSlotIsPopulated = false;
889
+ this.footerTabElemRef = null;
890
+ }
891
+
892
+ /**
893
+ * When window is resizing, need to debounce callback
894
+ * Track internal variable _resizing
895
+ * @returns {Boolean}
896
+ * @private
897
+ */
898
+ get modalResizing() {
899
+ if (!this._resizing) {
900
+ this._resizing = this.scheduleResize.bind(this);
901
+ }
902
+ return this._resizing;
903
+ }
904
+
905
+ /**
906
+ * When the modalBody content height is tall, it requires max-height
907
+ * to be set in order to prevent overflow of the modal offscreen
908
+ * Throttling occurs to prevent calling this method every time
909
+ * the window.onresize event fires
910
+ * @private
911
+ */
912
+ updateBodyHeight() {
913
+ clearTimeout(this.timeoutId);
914
+ this.timeoutId = 0;
915
+ const { bodyResizeScheduled, bodyRegistered, baseUpdateBodyCallback } =
916
+ this;
917
+
918
+ if (bodyRegistered && !bodyResizeScheduled) {
919
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
920
+ requestAnimationFrame(() => {
921
+ this.bodyResizeScheduled = false;
922
+ if (bodyRegistered && baseUpdateBodyCallback) {
923
+ const values = {
924
+ footerHeight: this.footerHeight || 0,
925
+ headerHeight: this.headerHeight || 0,
926
+ backdropHeight: this.backdropHeight,
927
+ };
928
+ baseUpdateBodyCallback(values);
929
+ }
930
+ });
931
+ this.bodyResizeScheduled = true;
932
+ }
933
+ }
934
+
935
+ /**
936
+ * Provide debounce / throttling to prevent modalBody callback
937
+ * from being fired every time window.onresize event fires
938
+ * @private
939
+ */
940
+ scheduleResize() {
941
+ if (this.timeoutId === 0) {
942
+ // eslint-disable-next-line @lwc/lwc/no-async-operation
943
+ this.timeoutId = setTimeout(() => {
944
+ this.updateBodyHeight();
945
+ }, DEBOUNCE_RESIZE);
946
+ }
947
+ }
948
+
949
+ /**
950
+ * Event handler for window.onresize event
951
+ * @private
952
+ */
953
+ handleWindowResize = () => {
954
+ this.scheduleResize();
955
+ };
956
+
957
+ /**
958
+ * Bind window.onresize event listener when modalBody is present
959
+ * @private
960
+ */
961
+ bindBodyResizeEvents() {
962
+ if (window && !this.windowResizeEventsBound) {
963
+ window.addEventListener('resize', this.handleWindowResize);
964
+ this.windowResizeEventsBound = true;
965
+ }
966
+ }
967
+
968
+ /**
969
+ * Detach window.onresize event listener when modalBody is removed
970
+ * @private
971
+ */
972
+ detachBodyResizeEvents() {
973
+ if (window) {
974
+ window.removeEventListener('resize', this.handleWindowResize);
975
+ clearTimeout(this.timeoutId);
976
+ this.timeoutId = 0;
977
+ this.windowResizeEventsBound = false;
978
+ }
979
+ }
980
+
981
+ /**
982
+ * When modal is being created, initialize
983
+ * private tracked modal state
984
+ * @private
985
+ */
986
+ initState() {
987
+ // setting initialRender true handles the case where modal
988
+ // is added / removed/added back to DOM
989
+ this.initialRender = true;
990
+ this.autoFocusCompletedOnce = false;
991
+ this.windowResizeEventsBound = false;
992
+ this.timeoutId = 0;
993
+ this.disableCloseButton = false;
994
+ this.modalLabel = null;
995
+ this.modalLabelledBy = null;
996
+ this.modalDescribedBy = null;
997
+ this.showAriaLiveMessage = false;
998
+ this.ariaLiveMessage = '';
999
+ this._size = 'medium';
1000
+ this.savedInertElements = [];
1001
+ this.savedActiveElement = null;
1002
+ this.isModalOpen = false;
1003
+ this.isModalTransitioningIn = false;
1004
+ }
1005
+
1006
+ connectedCallback() {
1007
+ this.initState();
1008
+ this.initHeaderState();
1009
+ this.initBodyState();
1010
+ this.initFooterState();
1011
+ }
1012
+
1013
+ disconnectedCallback() {
1014
+ if (this.windowResizeEventsBound) {
1015
+ this.detachBodyResizeEvents();
1016
+ }
1017
+ this.closeModal();
1018
+ }
1019
+
1020
+ renderedCallback() {
1021
+ if (this.initialRender) {
1022
+ this.openModal();
1023
+ this.initialRender = false;
1024
+ } else {
1025
+ // wait until lightning-modal is defined
1026
+ this.updateAriaLabel();
1027
+ this.updateAriaDescription();
1028
+ // queue show only once
1029
+ if (!this.isModalTransitioningIn) {
1030
+ this.queueShowModal();
1031
+ }
1032
+ // autoFocus once only
1033
+ if (!this.autoFocusCompletedOnce) {
1034
+ this.focusFirstElement();
1035
+ }
1036
+ }
1037
+ this.updateBodyHeight();
1038
+ }
1039
+ }