@brightspace-ui/core 3.100.2 → 3.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/dialog/README.md +80 -0
- package/components/dialog/demo/dialog-async-content-until.js +89 -0
- package/components/dialog/demo/dialog.html +23 -0
- package/components/dialog/dialog-mixin.js +1 -13
- package/components/dialog/dialog-styles.js +0 -4
- package/components/dropdown/dropdown-popover-mixin.js +2 -2
- package/components/filter/filter.js +2 -6
- package/components/list/demo/demo-list.js +0 -1
- package/custom-elements.json +25 -0
- package/package.json +1 -1
@@ -258,6 +258,86 @@ document.querySelector('#open').addEventListener('click', () => {
|
|
258
258
|
});
|
259
259
|
```
|
260
260
|
|
261
|
+
## Loading Asynchronous Content
|
262
|
+
|
263
|
+
The `until` directive can be used for manging the rendering of dialog content when it is loaded asynchronously. The example below loads some dialog content asynchronously, displaying a [Loading Spinner](../loading-spinner/) until the content is ready.
|
264
|
+
|
265
|
+
Notes on this example:
|
266
|
+
- Dialog sizing: `this.resolveLoadingComplete` (part of `LoadingCompleteMixin`) on image load triggers the dialog resize
|
267
|
+
- Focus management: focus will go to the first footer button by default. If focus should instead go to a focusable element in the content then that would need to be specified, often by using `.focus()` on the other focusable element.
|
268
|
+
|
269
|
+
<!-- docs: demo code autoSize:false size:xlarge -->
|
270
|
+
```html
|
271
|
+
<script type="module">
|
272
|
+
import '@brightspace-ui/core/components/button/button.js';
|
273
|
+
import '@brightspace-ui/core/components/loading-spinner/loading-spinner.js';
|
274
|
+
import { html, LitElement, noChange } from 'lit';
|
275
|
+
import { guard } from 'lit/directives/guard.js';
|
276
|
+
import { LoadingCompleteMixin } from '@brightspace-ui/core/mixins/loading-complete/loading-complete-mixin.js';
|
277
|
+
import { until } from 'lit/directives/until.js';
|
278
|
+
|
279
|
+
class DialogAsyncContentUntil extends LoadingCompleteMixin(LitElement) {
|
280
|
+
|
281
|
+
static get properties() {
|
282
|
+
return {
|
283
|
+
key: { type: String }
|
284
|
+
};
|
285
|
+
}
|
286
|
+
|
287
|
+
constructor() {
|
288
|
+
super();
|
289
|
+
this.key = null;
|
290
|
+
}
|
291
|
+
|
292
|
+
render() {
|
293
|
+
const loadingSpinner = html`<d2l-loading-spinner size="100" style="width: 100%;"></d2l-loading-spinner>`;
|
294
|
+
return html`${guard([this.key], () => until(this.#getContent(this.key), loadingSpinner, noChange))}`;
|
295
|
+
}
|
296
|
+
|
297
|
+
#getContent(key) {
|
298
|
+
return new Promise((resolve) => {
|
299
|
+
if (!key) return;
|
300
|
+
setTimeout(() => {
|
301
|
+
resolve(html`
|
302
|
+
<d2l-button>Focus on me!</d2l-button>
|
303
|
+
<img
|
304
|
+
src="https://us.v-cdn.net/cdn-cgi/image/fit=scale-down,width=1600/https://us.v-cdn.net/6036482/uploads/Y6MNPRWX5OVH/moose-poses-guided-tour.png"
|
305
|
+
style="height: 300px; display: block;"
|
306
|
+
@load="${this.#handleImageLoad}"
|
307
|
+
>
|
308
|
+
`);
|
309
|
+
}, 1000);
|
310
|
+
});
|
311
|
+
}
|
312
|
+
|
313
|
+
#handleImageLoad() {
|
314
|
+
this.shadowRoot.querySelector('d2l-button').focus();
|
315
|
+
this.resolveLoadingComplete();
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
customElements.define('d2l-dialog-demo-async-content-until', DialogAsyncContentUntil);
|
320
|
+
</script>
|
321
|
+
<script type="module">
|
322
|
+
import '@brightspace-ui/core/components/button/button.js';
|
323
|
+
import '@brightspace-ui/core/components/dialog/dialog.js';
|
324
|
+
|
325
|
+
document.querySelector('#open').addEventListener('click', () => {
|
326
|
+
document.querySelector('#dialog').opened = true;
|
327
|
+
document.querySelector('d2l-dialog-demo-async-content-until').key = 'my-dialog';
|
328
|
+
});
|
329
|
+
document.querySelector('#dialog').addEventListener('d2l-dialog-close', (e) => {
|
330
|
+
console.log('dialog action:', e.detail.action);
|
331
|
+
});
|
332
|
+
</script>
|
333
|
+
<d2l-dialog id="dialog" title-text="Async Dialog">
|
334
|
+
<d2l-dialog-demo-async-content-until></d2l-dialog-demo-async-content-until>
|
335
|
+
<d2l-button slot="footer" primary data-dialog-action="done">Done</d2l-button>
|
336
|
+
<d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
|
337
|
+
</d2l-dialog>
|
338
|
+
<d2l-button id="open">Show Dialog</d2l-button>
|
339
|
+
```
|
340
|
+
|
261
341
|
## Accessibility
|
262
342
|
|
263
343
|
### Focus Management
|
@@ -0,0 +1,89 @@
|
|
1
|
+
import '../../button/button.js';
|
2
|
+
import '../../list/list.js';
|
3
|
+
import '../../list/list-item.js';
|
4
|
+
import '../../list/list-item-content.js';
|
5
|
+
import '../../loading-spinner/loading-spinner.js';
|
6
|
+
import { css, html, LitElement, noChange } from 'lit';
|
7
|
+
import { guard } from 'lit/directives/guard.js';
|
8
|
+
import { LoadingCompleteMixin } from '../../../mixins/loading-complete/loading-complete-mixin.js';
|
9
|
+
import { until } from 'lit/directives/until.js';
|
10
|
+
|
11
|
+
class DialogAsyncContentUntil extends LoadingCompleteMixin(LitElement) {
|
12
|
+
|
13
|
+
static get properties() {
|
14
|
+
return {
|
15
|
+
href: { type: String }
|
16
|
+
};
|
17
|
+
}
|
18
|
+
|
19
|
+
static get styles() {
|
20
|
+
return css`
|
21
|
+
.content-button {
|
22
|
+
padding-block: 1rem;
|
23
|
+
}
|
24
|
+
.d2l-dialog-content-loading {
|
25
|
+
text-align: center;
|
26
|
+
}
|
27
|
+
`;
|
28
|
+
}
|
29
|
+
|
30
|
+
constructor() {
|
31
|
+
super();
|
32
|
+
this.href = null;
|
33
|
+
}
|
34
|
+
|
35
|
+
render() {
|
36
|
+
const loadingSpinner = html`
|
37
|
+
<div class="d2l-dialog-content-loading">
|
38
|
+
<d2l-loading-spinner size="100"></d2l-loading-spinner>
|
39
|
+
</div>
|
40
|
+
`;
|
41
|
+
return html`${guard([this.href], () => until(this._getContent(this.href), loadingSpinner, noChange))}`;
|
42
|
+
}
|
43
|
+
|
44
|
+
_getContent(href) {
|
45
|
+
return new Promise((resolve) => {
|
46
|
+
if (!href) return;
|
47
|
+
setTimeout(() => {
|
48
|
+
resolve(html`
|
49
|
+
<d2l-button class="content-button">Focus on me!</d2l-button>
|
50
|
+
<d2l-list>
|
51
|
+
<d2l-list-item>
|
52
|
+
<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}">
|
53
|
+
<d2l-list-item-content>
|
54
|
+
<div>Introductory Earth Sciences</div>
|
55
|
+
<div slot="supporting-info">This course explores the geological process of the Earth's interior and surface. These include volcanism, earthquakes, mountains...</div>
|
56
|
+
</d2l-list-item-content>
|
57
|
+
</d2l-list-item>
|
58
|
+
<d2l-list-item>
|
59
|
+
<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}">
|
60
|
+
<d2l-list-item-content>
|
61
|
+
<div>Engineering Materials for Energy Systems</div>
|
62
|
+
<div slot="supporting-info">This course explores the geological processes of the Earth's interior and surface. These include volcanism, earthquakes, mountain...</div>
|
63
|
+
</d2l-list-item-content>
|
64
|
+
</d2l-list-item>
|
65
|
+
<d2l-list-item>
|
66
|
+
<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}">
|
67
|
+
<d2l-list-item-content>
|
68
|
+
<div>Geomorphology and GIS </div>
|
69
|
+
<div slot="supporting-info">This course explores the geological processes of the Earth's interior and surface. These include volcanism, earthquakes, mountain...</div>
|
70
|
+
</d2l-list-item-content>
|
71
|
+
</d2l-list-item>
|
72
|
+
</d2l-list>
|
73
|
+
`);
|
74
|
+
}, 1000);
|
75
|
+
});
|
76
|
+
}
|
77
|
+
|
78
|
+
#handleImageLoad() {
|
79
|
+
const images = this.shadowRoot.querySelectorAll('img');
|
80
|
+
for (const image of images) {
|
81
|
+
if (!image.complete) return;
|
82
|
+
}
|
83
|
+
this.shadowRoot.querySelector('.content-button').focus();
|
84
|
+
this.resolveLoadingComplete();
|
85
|
+
}
|
86
|
+
|
87
|
+
}
|
88
|
+
|
89
|
+
customElements.define('d2l-dialog-demo-async-content-until', DialogAsyncContentUntil);
|
@@ -18,6 +18,7 @@
|
|
18
18
|
import '../../list/demo/demo-list.js';
|
19
19
|
import '../dialog.js';
|
20
20
|
import './dialog-async-content.js';
|
21
|
+
import './dialog-async-content-until.js';
|
21
22
|
import './dialog-container.js';
|
22
23
|
</script>
|
23
24
|
<style>
|
@@ -133,6 +134,28 @@
|
|
133
134
|
</template>
|
134
135
|
</d2l-demo-snippet>
|
135
136
|
|
137
|
+
<h2>Dialog (async using until)</h2>
|
138
|
+
|
139
|
+
<d2l-demo-snippet>
|
140
|
+
<template>
|
141
|
+
<d2l-button id="openAsyncUntil">Show Dialog</d2l-button>
|
142
|
+
<d2l-dialog id="dialogAsyncUntil" title-text="Dialog Title">
|
143
|
+
<d2l-dialog-demo-async-content-until></d2l-dialog-demo-async-content-until>
|
144
|
+
<d2l-button slot="footer" primary data-dialog-action="ok">Click Me!</d2l-button>
|
145
|
+
<d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
|
146
|
+
</d2l-dialog>
|
147
|
+
<script>
|
148
|
+
document.querySelector('#openAsyncUntil').addEventListener('click', () => {
|
149
|
+
document.querySelector('#dialogAsyncUntil').opened = true;
|
150
|
+
document.querySelector('d2l-dialog-demo-async-content-until').href = 'some-href';
|
151
|
+
});
|
152
|
+
document.querySelector('#dialogAsyncUntil').addEventListener('d2l-dialog-close', (e) => {
|
153
|
+
console.log('dialog action:', e.detail.action);
|
154
|
+
});
|
155
|
+
</script>
|
156
|
+
</template>
|
157
|
+
</d2l-demo-snippet>
|
158
|
+
|
136
159
|
<h2>Dialog (intercept closing)</h2>
|
137
160
|
|
138
161
|
<d2l-demo-snippet>
|
@@ -53,7 +53,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
53
53
|
_isFullHeight: { state: true },
|
54
54
|
_left: { state: true },
|
55
55
|
_margin: { state: true },
|
56
|
-
_mobileDropdownShowing: { state: true },
|
57
56
|
_nestedShowing: { state: true },
|
58
57
|
_overflowBottom: { state: true },
|
59
58
|
_overflowTop: { state: true },
|
@@ -73,7 +72,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
73
72
|
this._autoSize = true;
|
74
73
|
this._dialogId = getUniqueId();
|
75
74
|
this._fullscreenWithin = 0;
|
76
|
-
this._handleDropdownOpenClose = this._handleDropdownOpenClose.bind(this);
|
77
75
|
this._handleMvcDialogOpen = this._handleMvcDialogOpen.bind(this);
|
78
76
|
this._inIframe = false;
|
79
77
|
this._isDialogMixin = true;
|
@@ -81,7 +79,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
81
79
|
this._height = 0;
|
82
80
|
this._left = 0;
|
83
81
|
this._margin = { top: defaultMargin.top, right: defaultMargin.right, bottom: defaultMargin.bottom, left: defaultMargin.left };
|
84
|
-
this._mobileDropdownShowing = false;
|
85
82
|
this._nestedShowing = false;
|
86
83
|
this._overflowBottom = false;
|
87
84
|
this._overflowTop = false;
|
@@ -158,8 +155,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
158
155
|
_addHandlers() {
|
159
156
|
window.addEventListener('resize', this._updateSize);
|
160
157
|
this.addEventListener('touchstart', this._handleTouchStart);
|
161
|
-
this.addEventListener('d2l-dropdown-open', this._handleDropdownOpenClose, { capture: true });
|
162
|
-
this.addEventListener('d2l-dropdown-close', this._handleDropdownOpenClose, { capture: true });
|
163
158
|
if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dialog-content').addEventListener('scroll', this._updateOverflow);
|
164
159
|
}
|
165
160
|
|
@@ -350,10 +345,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
350
345
|
e.stopPropagation();
|
351
346
|
}
|
352
347
|
|
353
|
-
_handleDropdownOpenClose(e) {
|
354
|
-
this._mobileDropdownShowing = e.composedPath()[0]._useMobileStyling;
|
355
|
-
}
|
356
|
-
|
357
348
|
_handleFocusTrapEnter(e) {
|
358
349
|
// ignore focus trap events when the target is another element
|
359
350
|
// to prevent infinite focus loops
|
@@ -476,8 +467,6 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
476
467
|
_removeHandlers() {
|
477
468
|
window.removeEventListener('resize', this._updateSize);
|
478
469
|
this.removeEventListener('touchstart', this._handleTouchStart);
|
479
|
-
this.removeEventListener('d2l-dropdown-open', this._handleDropdownOpenClose, { capture: true });
|
480
|
-
this.removeEventListener('d2l-dropdown-close', this._handleDropdownOpenClose, { capture: true });
|
481
470
|
if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dialog-content').removeEventListener('scroll', this._updateOverflow);
|
482
471
|
}
|
483
472
|
|
@@ -506,8 +495,7 @@ export const DialogMixin = superclass => class extends RtlMixin(superclass) {
|
|
506
495
|
'd2l-dialog-outer-nested-showing': !this._useNative && this._nestedShowing,
|
507
496
|
'd2l-dialog-outer-scroll': this._scroll,
|
508
497
|
'd2l-dialog-fullscreen-mobile': info.fullscreenMobile,
|
509
|
-
'd2l-dialog-fullscreen-within': this._fullscreenWithin !== 0
|
510
|
-
'd2l-dialog-dropdown-mobile': this._mobileDropdownShowing
|
498
|
+
'd2l-dialog-fullscreen-within': this._fullscreenWithin !== 0
|
511
499
|
};
|
512
500
|
|
513
501
|
return html`${this._useNative ?
|
@@ -151,10 +151,6 @@ export const dialogStyles = css`
|
|
151
151
|
overflow: auto;
|
152
152
|
}
|
153
153
|
|
154
|
-
.d2l-dialog-dropdown-mobile .d2l-dialog-content {
|
155
|
-
overflow: hidden; /* workaround to fix clipping of nested fixed position elements with overlowing content in Safari bug: https://bugs.webkit.org/show_bug.cgi?id=160953 */
|
156
|
-
}
|
157
|
-
|
158
154
|
.d2l-dialog-footer {
|
159
155
|
box-sizing: border-box;
|
160
156
|
flex: none;
|
@@ -285,7 +285,7 @@ export const DropdownPopoverMixin = superclass => class extends LocalizeCoreElem
|
|
285
285
|
case 'left': return 'inline-start';
|
286
286
|
case 'right': return 'inline-end';
|
287
287
|
default: return undefined;
|
288
|
-
|
288
|
+
}
|
289
289
|
}
|
290
290
|
|
291
291
|
#adaptPositionSpan(val) {
|
@@ -293,7 +293,7 @@ export const DropdownPopoverMixin = superclass => class extends LocalizeCoreElem
|
|
293
293
|
case 'start': return 'end';
|
294
294
|
case 'end': return 'start';
|
295
295
|
default: return 'all';
|
296
|
-
|
296
|
+
}
|
297
297
|
}
|
298
298
|
|
299
299
|
#getMobileCloseButtonStyles() {
|
@@ -644,9 +644,7 @@ class Filter extends FocusMixin(LocalizeCoreElement(RtlMixin(LitElement))) {
|
|
644
644
|
<div class="d2l-filter-dimension-set-value d2l-body-compact">
|
645
645
|
<div class="d2l-filter-dimension-set-value-text">${item.text}</div>
|
646
646
|
${item.count !== undefined ? html`<div class="d2l-body-small">(${formatNumber(item.count)})</div>` : nothing}
|
647
|
-
${item.additionalContent
|
648
|
-
? html`<d2l-icon icon="${item.selected ? 'tier1:arrow-collapse-small' : 'tier1:arrow-expand-small'}" aria-hidden="true"></d2l-icon>`
|
649
|
-
: nothing}
|
647
|
+
${item.additionalContent ? html`<d2l-icon icon="${item.selected ? 'tier1:arrow-collapse-small' : 'tier1:arrow-expand-small'}" aria-hidden="true"></d2l-icon>` : nothing}
|
650
648
|
</div>
|
651
649
|
${item.additionalContent ? html`
|
652
650
|
<d2l-expand-collapse-content
|
@@ -658,9 +656,7 @@ class Filter extends FocusMixin(LocalizeCoreElement(RtlMixin(LitElement))) {
|
|
658
656
|
` : nothing}
|
659
657
|
</div>
|
660
658
|
</d2l-list-item>
|
661
|
-
${item.additionalContent && item.selected && this._displayKeyboardTooltip
|
662
|
-
? html`<d2l-tooltip align="start" announced for="${itemId}" for-type="descriptor" @d2l-tooltip-hide="${this._handleTooltipHide}">${this.localizeHTML('components.filter.additionalContentTooltip')}</d2l-tooltip>`
|
663
|
-
: nothing}
|
659
|
+
${item.additionalContent && item.selected && this._displayKeyboardTooltip ? html`<d2l-tooltip align="start" announced for="${itemId}" for-type="descriptor" @d2l-tooltip-hide="${this._handleTooltipHide}">${this.localizeHTML('components.filter.additionalContentTooltip')}</d2l-tooltip>` : nothing}
|
664
660
|
`;
|
665
661
|
}
|
666
662
|
|
package/custom-elements.json
CHANGED
@@ -1982,6 +1982,31 @@
|
|
1982
1982
|
}
|
1983
1983
|
]
|
1984
1984
|
},
|
1985
|
+
{
|
1986
|
+
"name": "d2l-dialog-demo-async-content-until",
|
1987
|
+
"path": "./components/dialog/demo/dialog-async-content-until.js",
|
1988
|
+
"attributes": [
|
1989
|
+
{
|
1990
|
+
"name": "href",
|
1991
|
+
"type": "string"
|
1992
|
+
}
|
1993
|
+
],
|
1994
|
+
"properties": [
|
1995
|
+
{
|
1996
|
+
"name": "href",
|
1997
|
+
"attribute": "href",
|
1998
|
+
"type": "string"
|
1999
|
+
},
|
2000
|
+
{
|
2001
|
+
"name": "loadingComplete",
|
2002
|
+
"type": "Promise<any>"
|
2003
|
+
},
|
2004
|
+
{
|
2005
|
+
"name": "resolveLoadingComplete",
|
2006
|
+
"type": "() => void"
|
2007
|
+
}
|
2008
|
+
]
|
2009
|
+
},
|
1985
2010
|
{
|
1986
2011
|
"name": "d2l-dialog-demo-async-content",
|
1987
2012
|
"path": "./components/dialog/demo/dialog-async-content.js",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.101.0",
|
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",
|