@brightspace-ui/core 3.79.5 → 3.79.7

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.
@@ -3,8 +3,9 @@ import '../../list/list-item.js';
3
3
  import '../../list/list-item-content.js';
4
4
  import { html, LitElement } from 'lit';
5
5
  import { InitialStateError, runAsync } from '../../../directives/run-async/run-async.js';
6
+ import { LoadingCompleteMixin } from '../../../mixins/loading-complete/loading-complete-mixin.js';
6
7
 
7
- class DialogAsyncContent extends LitElement {
8
+ class DialogAsyncContent extends LoadingCompleteMixin(LitElement) {
8
9
 
9
10
  static get properties() {
10
11
  return {
@@ -32,21 +33,21 @@ class DialogAsyncContent extends LitElement {
32
33
  resolve(html`
33
34
  <d2l-list>
34
35
  <d2l-list-item>
35
- <img slot="illustration" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg"></img>
36
+ <img slot="illustration" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg" @load="${this.#handleImageLoad}">
36
37
  <d2l-list-item-content>
37
38
  <div>Introductory Earth Sciences</div>
38
39
  <div slot="supporting-info">This course explores the geological process of the Earth's interior and surface. These include volcanism, earthquakes, mountains...</div>
39
40
  </d2l-list-item-content>
40
41
  </d2l-list-item>
41
42
  <d2l-list-item>
42
- <img slot="illustration" src="https://s.brightspace.com/course-images/images/e5fd575a-bc14-4a80-89e1-46f349a76178/tile-high-density-max-size.jpg"></img>
43
+ <img slot="illustration" src="https://s.brightspace.com/course-images/images/e5fd575a-bc14-4a80-89e1-46f349a76178/tile-high-density-max-size.jpg" @load="${this.#handleImageLoad}">
43
44
  <d2l-list-item-content>
44
45
  <div>Engineering Materials for Energy Systems</div>
45
46
  <div slot="supporting-info">This course explores the geological processes of the Earth's interior and surface. These include volcanism, earthquakes, mountain...</div>
46
47
  </d2l-list-item-content>
47
48
  </d2l-list-item>
48
49
  <d2l-list-item>
49
- <img slot="illustration" src="https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-high-density-max-size.jpg"></img>
50
+ <img slot="illustration" src="https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-high-density-max-size.jpg" @load="${this.#handleImageLoad}">
50
51
  <d2l-list-item-content>
51
52
  <div>Geomorphology and GIS </div>
52
53
  <div slot="supporting-info">This course explores the geological processes of the Earth's interior and surface. These include volcanism, earthquakes, mountain...</div>
@@ -58,6 +59,14 @@ class DialogAsyncContent extends LitElement {
58
59
  });
59
60
  }
60
61
 
62
+ #handleImageLoad() {
63
+ const images = this.shadowRoot.querySelectorAll('img');
64
+ for (const image of images) {
65
+ if (!image.complete) return;
66
+ }
67
+ this.resolveLoadingComplete();
68
+ }
69
+
61
70
  }
62
71
 
63
72
  customElements.define('d2l-dialog-demo-async-content', DialogAsyncContent);
@@ -2,7 +2,7 @@ import '../focus-trap/focus-trap.js';
2
2
  import '../../helpers/viewport-size.js';
3
3
  import { allowBodyScroll, preventBodyScroll } from '../backdrop/backdrop.js';
4
4
  import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
5
- import { findComposedAncestor, isComposedAncestor } from '../../helpers/dom.js';
5
+ import { findComposedAncestor, getComposedChildren, isComposedAncestor } from '../../helpers/dom.js';
6
6
  import { getComposedActiveElement, getFirstFocusableDescendant, getNextFocusable, isFocusable } from '../../helpers/focus.js';
7
7
  import { classMap } from 'lit/directives/class-map.js';
8
8
  import { getUniqueId } from '../../helpers/uniqueId.js';
@@ -11,6 +11,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
11
11
  import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
12
12
  import { styleMap } from 'lit/directives/style-map.js';
13
13
  import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
14
+ import { waitForElem } from '../../helpers/internal/waitForElem.js';
14
15
 
15
16
  window.D2L = window.D2L || {};
16
17
  window.D2L.DialogMixin = window.D2L.DialogMixin || {};
@@ -452,6 +453,11 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
452
453
  if (reduceMotion) await new Promise(resolve => requestAnimationFrame(resolve));
453
454
  else await animPromise;
454
455
 
456
+ const flag = window.D2L?.LP?.Web?.UI?.Flags.Flag('GAUD-7397-dialog-resize-updateComplete', true) ?? true;
457
+ if (flag) {
458
+ await this.#waitForUpdateComplete();
459
+ }
460
+ await this._updateSize();
455
461
  /** Dispatched when the dialog is opened */
456
462
  this.dispatchEvent(new CustomEvent(
457
463
  'd2l-dialog-open', { bubbles: true, composed: true }
@@ -580,4 +586,10 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
580
586
  });
581
587
  });
582
588
  }
589
+
590
+ async #waitForUpdateComplete() {
591
+ const predicate = () => true;
592
+ const composedChildren = getComposedChildren(this, predicate);
593
+ await Promise.all(composedChildren.map(child => waitForElem(child, predicate)));
594
+ }
583
595
  };
@@ -1,3 +1,4 @@
1
+ import '../backdrop/backdrop.js';
1
2
  import '../colors/colors.js';
2
3
  import '../focus-trap/focus-trap.js';
3
4
  import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
@@ -6,6 +7,7 @@ import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocus
6
7
  import { getComposedParent, isComposedAncestor } from '../../helpers/dom.js';
7
8
  import { _offscreenStyleDeclarations } from '../offscreen/offscreen.js';
8
9
  import { styleMap } from 'lit/directives/style-map.js';
10
+ import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
9
11
 
10
12
  const defaultPreferredPosition = {
11
13
  location: 'block-end', // block-start, block-end
@@ -46,6 +48,7 @@ export const PopoverMixin = superclass => class extends superclass {
46
48
  _position: { state: true },
47
49
  _preferredPosition: { state: true },
48
50
  _rtl: { state: true },
51
+ _showBackdrop: { state: true },
49
52
  _trapFocus: { state: true },
50
53
  _useNativePopover: { type: String, reflect: true, attribute: 'popover' },
51
54
  _width: { state: true }
@@ -207,6 +210,7 @@ export const PopoverMixin = superclass => class extends superclass {
207
210
  super();
208
211
  this.configure();
209
212
  this._mobile = false;
213
+ this._showBackdrop = false;
210
214
  this._useNativePopover = isSupported ? 'manual' : undefined;
211
215
  this.#handleAncestorMutationBound = this.#handleAncestorMutation.bind(this);
212
216
  this.#handleAutoCloseClickBound = this.#handleAutoCloseClick.bind(this);
@@ -236,8 +240,9 @@ export const PopoverMixin = superclass => class extends superclass {
236
240
  async close() {
237
241
  if (!this._opened) return;
238
242
 
239
- this._opened = false;
243
+ const ifrauBackdropService = await tryGetIfrauBackdropService();
240
244
 
245
+ this._opened = false;
241
246
  if (this._useNativePopover) this.hidePopover();
242
247
 
243
248
  this._previousFocusableAncestor = null;
@@ -245,6 +250,13 @@ export const PopoverMixin = superclass => class extends superclass {
245
250
  this.#removeMediaQueryHandlers();
246
251
  this.#removeRepositionHandlers();
247
252
  this.#clearDismissible();
253
+
254
+ if (ifrauBackdropService && this._showBackdrop) {
255
+ ifrauBackdropService.hideBackdrop();
256
+ this.#ifrauContextInfo = null;
257
+ }
258
+ this._showBackdrop = false;
259
+
248
260
  await this.updateComplete; // wait before applying focus to opener
249
261
  this.#focusOpener();
250
262
  this.dispatchEvent(new CustomEvent('d2l-popover-close', { bubbles: true, composed: true }));
@@ -281,6 +293,8 @@ export const PopoverMixin = superclass => class extends superclass {
281
293
  async open(applyFocus = true) {
282
294
  if (this._opened) return;
283
295
 
296
+ const ifrauBackdropService = await tryGetIfrauBackdropService();
297
+
284
298
  this.#addMediaQueryHandlers();
285
299
 
286
300
  this._rtl = document.documentElement.getAttribute('dir') === 'rtl';
@@ -297,6 +311,11 @@ export const PopoverMixin = superclass => class extends superclass {
297
311
 
298
312
  await this.#position();
299
313
 
314
+ this._showBackdrop = this._mobile && this._mobileTrayLocation;
315
+ if (ifrauBackdropService && this._showBackdrop) {
316
+ this.#ifrauContextInfo = await ifrauBackdropService.showBackdrop();
317
+ }
318
+
300
319
  this._dismissibleId = setDismissible(() => this.close());
301
320
 
302
321
  this.#focusContent(this);
@@ -323,7 +342,7 @@ export const PopoverMixin = superclass => class extends superclass {
323
342
  const contentStyle = stylesMap['content'];
324
343
 
325
344
  content = html`
326
- <div class="content-width vdiff-target" style=${styleMap(widthStyle)}>
345
+ <div id="content-wrapper" class="content-width vdiff-target" style=${styleMap(widthStyle)}>
327
346
  <div class="content-container" style=${styleMap(contentStyle)}>${content}</div>
328
347
  </div>
329
348
  `;
@@ -362,12 +381,16 @@ export const PopoverMixin = superclass => class extends superclass {
362
381
  </div>
363
382
  ` : nothing;
364
383
 
365
- return html`${content}${pointer}`;
384
+ const backdrop = this._mobileTrayLocation ?
385
+ html`<d2l-backdrop for-target="content-wrapper" ?shown="${this._showBackdrop}"></d2l-backdrop>` :
386
+ nothing;
387
+ return html`${content}${backdrop}${pointer}`;
366
388
 
367
389
  }
368
390
 
369
391
  async resize() {
370
392
  if (!this._opened) return;
393
+ this._showBackdrop = this._mobile && this._mobileTrayLocation;
371
394
  await this.#position();
372
395
  }
373
396
 
@@ -376,6 +399,7 @@ export const PopoverMixin = superclass => class extends superclass {
376
399
  else return this.open(!this._noAutoFocus && applyFocus);
377
400
  }
378
401
 
402
+ #ifrauContextInfo;
379
403
  #mediaQueryList;
380
404
  #handleAncestorMutationBound;
381
405
  #handleAutoCloseClickBound;
@@ -520,7 +544,8 @@ export const PopoverMixin = superclass => class extends superclass {
520
544
  #getMobileTrayBlockStyleMaps() {
521
545
 
522
546
  let maxHeightOverride;
523
- const availableHeight = Math.min(window.innerHeight, window.screen.height);
547
+ let availableHeight = Math.min(window.innerHeight, window.screen.height);
548
+ if (this.#ifrauContextInfo) availableHeight = this.#ifrauContextInfo.availableHeight;
524
549
 
525
550
  // default maximum height for bottom tray (42px margin)
526
551
  const mobileTrayMaxHeightDefault = availableHeight - minBackdropHeightMobile;
@@ -532,12 +557,20 @@ export const PopoverMixin = superclass => class extends superclass {
532
557
  }
533
558
  maxHeightOverride = `${maxHeightOverride}px`;
534
559
 
560
+ let bottomOverride;
561
+ if (this.#ifrauContextInfo) {
562
+ // the bottom override is measured as the distance from the bottom of the screen
563
+ const screenHeight = window.innerHeight - this.#ifrauContextInfo.availableHeight + Math.min(this.#ifrauContextInfo.top, 0);
564
+ bottomOverride = `${screenHeight}px`;
565
+ }
566
+
535
567
  const widthOverride = '100vw';
536
568
 
537
569
  const widthStyle = {
538
570
  minWidth: widthOverride,
539
571
  width: widthOverride,
540
572
  maxHeight: maxHeightOverride,
573
+ bottom: bottomOverride
541
574
  };
542
575
 
543
576
  const contentWidthStyle = {
@@ -552,14 +585,15 @@ export const PopoverMixin = superclass => class extends superclass {
552
585
 
553
586
  return {
554
587
  width: widthStyle,
555
- content: contentStyle,
588
+ content: contentStyle
556
589
  };
557
590
  }
558
591
 
559
592
  #getMobileTrayInlineStyleMaps() {
560
593
 
561
594
  let maxWidthOverride = this._maxWidth;
562
- const availableWidth = Math.min(window.innerWidth, window.screen.width);
595
+ let availableWidth = Math.min(window.innerWidth, window.screen.width);
596
+ if (this.#ifrauContextInfo) availableWidth = this.#ifrauContextInfo.availableWidth;
563
597
 
564
598
  // default maximum width for tray (30px margin)
565
599
  const mobileTrayMaxWidthDefault = Math.min(availableWidth - minBackdropWidthMobile, 420);
@@ -592,7 +626,16 @@ export const PopoverMixin = superclass => class extends superclass {
592
626
  // add 2 to content width since scrollWidth does not include border
593
627
  const containerWidth = `${widthOverride + 20}px`;
594
628
 
595
- const topOverride = (window.innerHeight > window.screen.height) ? window.pageYOffset : undefined;
629
+ const maxHeightOverride = this.#ifrauContextInfo ? `${this.#ifrauContextInfo.availableHeight}px` : '';
630
+
631
+ let topOverride;
632
+ if (this.#ifrauContextInfo) {
633
+ // if inside iframe, use ifrauContext top as top of screen
634
+ topOverride = `${this.#ifrauContextInfo.top < 0 ? -this.#ifrauContextInfo.top : 0}px`;
635
+ } else if (window.innerHeight > window.screen.height) {
636
+ // non-responsive page, manually override top to scroll distance
637
+ topOverride = window.pageYOffset;
638
+ }
596
639
 
597
640
  let inlineEndOverride;
598
641
  let inlineStartOverride;
@@ -614,6 +657,7 @@ export const PopoverMixin = superclass => class extends superclass {
614
657
  minWidth: minWidthOverride,
615
658
  width: containerWidth,
616
659
  top: topOverride,
660
+ maxHeight: maxHeightOverride,
617
661
  insetInlineStart: inlineStartOverride,
618
662
  insetInlineEnd: inlineEndOverride
619
663
  };
@@ -626,11 +670,12 @@ export const PopoverMixin = superclass => class extends superclass {
626
670
 
627
671
  const contentStyle = {
628
672
  ...contentWidthStyle,
673
+ maxHeight: maxHeightOverride,
629
674
  };
630
675
 
631
676
  return {
632
- width : widthStyle,
633
- content : contentStyle,
677
+ width: widthStyle,
678
+ content: contentStyle
634
679
  };
635
680
  }
636
681
 
@@ -767,8 +812,8 @@ export const PopoverMixin = superclass => class extends superclass {
767
812
  };
768
813
 
769
814
  return {
770
- width : widthStyle,
771
- content : contentStyle
815
+ width: widthStyle,
816
+ content: contentStyle
772
817
  };
773
818
  }
774
819
 
@@ -824,7 +869,10 @@ export const PopoverMixin = superclass => class extends superclass {
824
869
 
825
870
  async #handleMobileResize() {
826
871
  this._mobile = this.#mediaQueryList.matches;
827
- if (this._opened) await this.#position();
872
+ if (this._opened) {
873
+ this._showBackdrop = this._mobile && this._mobileTrayLocation;
874
+ await this.#position();
875
+ }
828
876
  }
829
877
 
830
878
  #handleResize() {
@@ -1996,6 +1996,14 @@
1996
1996
  "name": "href",
1997
1997
  "attribute": "href",
1998
1998
  "type": "string"
1999
+ },
2000
+ {
2001
+ "name": "loadingComplete",
2002
+ "type": "Promise<any>"
2003
+ },
2004
+ {
2005
+ "name": "resolveLoadingComplete",
2006
+ "type": "() => void"
1999
2007
  }
2000
2008
  ]
2001
2009
  },
package/helpers/README.md CHANGED
@@ -86,7 +86,8 @@ elemIdListRemoves(node, attrName, value);
86
86
  findComposedAncestor(node, predicate);
87
87
 
88
88
  // gets the composed children (including shadow children & distributed children)
89
- getComposedChildren(element);
89
+ // includes a predicate which will add children nodes when predicate(node) is true
90
+ getComposedChildren(element, predicate = () => true);
90
91
 
91
92
  // gets the composed parent (including shadow host & insertion points)
92
93
  getComposedParent(node);
package/helpers/dom.js CHANGED
@@ -79,7 +79,7 @@ export function getBoundingAncestor(node) {
79
79
  });
80
80
  }
81
81
 
82
- export function getComposedChildren(node) {
82
+ export function getComposedChildren(node, predicate = () => true) {
83
83
 
84
84
  if (!node) {
85
85
  return null;
@@ -104,7 +104,9 @@ export function getComposedChildren(node) {
104
104
 
105
105
  for (let i = 0; i < nodes.length; i++) {
106
106
  if (nodes[i].nodeType === 1) {
107
- children.push(nodes[i]);
107
+ if (predicate(nodes[i])) {
108
+ children.push(nodes[i]);
109
+ }
108
110
  }
109
111
  }
110
112
 
@@ -0,0 +1,25 @@
1
+ import { getComposedChildren } from '../dom.js';
2
+
3
+ export async function waitForElem(elem, predicate = () => true) {
4
+
5
+ if (!elem) return;
6
+
7
+ const update = elem.updateComplete;
8
+ if (typeof update === 'object' && Promise.resolve(update) === update) {
9
+ await update;
10
+ await new Promise(resolve => {
11
+ requestAnimationFrame(() => resolve());
12
+ });
13
+ }
14
+
15
+ if (typeof elem.getLoadingComplete === 'function') {
16
+ await elem.getLoadingComplete();
17
+ await new Promise(resolve => {
18
+ requestAnimationFrame(() => resolve());
19
+ });
20
+ }
21
+
22
+ const children = getComposedChildren(elem, predicate);
23
+ await Promise.all(children.map(e => waitForElem(e, predicate)));
24
+
25
+ }
@@ -196,6 +196,7 @@ static get localizeConfig() {
196
196
  };
197
197
  }
198
198
  ```
199
+ See [Creating a new collection](https://desire2learn.atlassian.net/wiki/spaces/DEVCENTRAL/pages/3105063520/OSLO#Creating-a-new-collection) to determine your collection name. Backslash (`\`) characters in your collection name must be escaped.
199
200
 
200
201
  > **Important:** When defining language resource keys, avoid using the Full Stop (`.`) character for grouping. OSLO does not support it.
201
202
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.79.5",
3
+ "version": "3.79.7",
4
4
  "description": "A collection of accessible, free, open-source web components for building Brightspace applications",
5
5
  "type": "module",
6
6
  "repository": "https://github.com/BrightspaceUI/core.git",