@brightspace-ui/core 3.79.5 → 3.79.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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",