@brightspace-ui/core 3.216.1 → 3.218.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.
@@ -0,0 +1,142 @@
1
+ import '../colors/colors.js';
2
+ import '../loading-spinner/loading-spinner.js';
3
+ import { css, html, LitElement } from 'lit';
4
+ import { getOffsetParent } from '../../helpers/dom.js';
5
+
6
+ const BACKDROP_DELAY_MS = 800;
7
+ const FADE_IN_DURATION_MS = 500;
8
+ const FADE_OUT_DURATION_MS = 500;
9
+ const SPINNER_DELAY_MS = BACKDROP_DELAY_MS + FADE_IN_DURATION_MS;
10
+
11
+ const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
12
+
13
+ /**
14
+ * A component for displaying a semi-transparent backdrop and a loading spinner over the containing element
15
+ */
16
+ class LoadingBackdrop extends LitElement {
17
+
18
+ static get properties() {
19
+ return {
20
+ /**
21
+ * Used to control whether the loading backdrop is shown
22
+ * @type {boolean}
23
+ */
24
+ shown: { type: Boolean },
25
+ _state: { type: String, reflect: true },
26
+ };
27
+ }
28
+
29
+ static get styles() {
30
+ return css`
31
+ :host, .backdrop, d2l-loading-spinner {
32
+ height: 0%;
33
+ position: absolute;
34
+ width: 0%;
35
+ }
36
+
37
+ .backdrop, d2l-loading-spinner {
38
+ opacity: 0;
39
+ }
40
+
41
+ :host {
42
+ top: 0;
43
+ z-index: 999;
44
+ }
45
+
46
+ .backdrop {
47
+ background-color: var(--d2l-color-regolith);
48
+ }
49
+
50
+ d2l-loading-spinner {
51
+ top: 100px;
52
+ }
53
+
54
+ :host([_state="showing"]),
55
+ :host([_state="hiding"]),
56
+ d2l-loading-spinner[_state="showing"],
57
+ d2l-loading-spinner[_state="hiding"],
58
+ .backdrop[_state="showing"],
59
+ .backdrop[_state="hiding"] {
60
+ height: 100%;
61
+ width: 100%;
62
+ }
63
+
64
+ d2l-loading-spinner[_state="showing"] {
65
+ opacity: 1;
66
+ transition: opacity ${FADE_IN_DURATION_MS}ms ease-in ${SPINNER_DELAY_MS}ms;
67
+ }
68
+
69
+ .backdrop[_state="showing"] {
70
+ opacity: 0.7;
71
+ transition: opacity ${FADE_IN_DURATION_MS}ms ease-in ${BACKDROP_DELAY_MS}ms;
72
+ }
73
+
74
+ d2l-loading-spinner[_state="hiding"],
75
+ .backdrop[_state="hiding"] {
76
+ transition: opacity ${FADE_OUT_DURATION_MS}ms ease-out;
77
+ }
78
+
79
+ @media (prefers-reduced-motion: reduce) {
80
+ * { transition: none; }
81
+ }
82
+ `;
83
+ }
84
+
85
+ constructor() {
86
+ super();
87
+ this.shown = false;
88
+ this._state = null;
89
+ }
90
+
91
+ render() {
92
+ return html`
93
+ <div class="backdrop" _state=${this._state} @transitionend=${this.#handleTransitionEnd} @transitioncancel=${this.#hide}></div>
94
+ <d2l-loading-spinner _state=${this._state}></d2l-loading-spinner>
95
+ `;
96
+ }
97
+
98
+ willUpdate(changedProperties) {
99
+ if (changedProperties.has('shown')) {
100
+ if (this.shown) {
101
+ this.#show();
102
+ } else if (changedProperties.get('shown') !== undefined) {
103
+ this.#fade();
104
+ }
105
+ }
106
+ }
107
+
108
+ #fade() {
109
+ if (reduceMotion) {
110
+ this.#hide();
111
+ } else {
112
+ this._state = 'hiding';
113
+ }
114
+ }
115
+
116
+ #handleTransitionEnd() {
117
+ if (this._state === 'hiding') {
118
+ this.#hide();
119
+ }
120
+ }
121
+
122
+ #hide() {
123
+ this._state = null;
124
+
125
+ const containingBlock = getOffsetParent(this);
126
+
127
+ if (containingBlock.dataset.initiallyInert !== '1') containingBlock.removeAttribute('inert');
128
+ }
129
+
130
+ #show() {
131
+ this._state = 'showing';
132
+
133
+ const containingBlock = getOffsetParent(this);
134
+
135
+ if (containingBlock.getAttribute('inert') !== null) containingBlock.dataset.initiallyInert = '1';
136
+
137
+ containingBlock.setAttribute('inert', 'inert');
138
+ }
139
+
140
+ }
141
+
142
+ customElements.define('d2l-backdrop-loading', LoadingBackdrop);
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="UTF-8">
6
+ <link rel="stylesheet" href="../../demo/styles.css" type="text/css">
7
+ <script type="module">
8
+ import '../../demo/demo-page.js';
9
+ import '../../button/button.js';
10
+ import '../backdrop-loading.js';
11
+ </script>
12
+ <style>
13
+ th, td {
14
+ border-bottom: 1px solid #dddddd;
15
+ padding: 30px;
16
+ text-align: left;
17
+ }
18
+ #grade-container {
19
+ position: relative;
20
+ width: fit-content;
21
+ }
22
+ </style>
23
+ </head>
24
+ <body unresolved>
25
+ <d2l-demo-page page-title="d2l-backdrop">
26
+ <d2l-demo-snippet>
27
+ <template>
28
+ <div id="target"><d2l-button primary>Refresh Content</d2l-button></div>
29
+ <div id="grade-container">
30
+ <table>
31
+ <thead>
32
+ <th>Course</th>
33
+ <th>Grade</th>
34
+ <th>Hours Spent in Content</th>
35
+ </thead>
36
+ <tr>
37
+ <td>Math</td>
38
+ <td class="grade">85%</td>
39
+ <td>100</td>
40
+ </tr>
41
+ <tr>
42
+ <td>Art</td>
43
+ <td class="grade">98%</td>
44
+ <td>10</td>
45
+ </tr>
46
+ </table>
47
+ <d2l-backdrop-loading></d2l-backdrop-loading>
48
+ </div>
49
+ <script data-demo-hide>
50
+ const demo = document.currentScript.parentNode;
51
+ const loadingBackdrop = demo.querySelector('d2l-backdrop-loading');
52
+ demo.querySelector('#target > d2l-button').addEventListener('click', () => {
53
+ loadingBackdrop.shown = !loadingBackdrop.shown;
54
+ setTimeout(() => {
55
+ demo.querySelectorAll('.grade').forEach((grade) => {
56
+ grade.innerHTML = `${Math.round(Math.random() * 100).toString()}%`;
57
+ });
58
+ loadingBackdrop.shown = !loadingBackdrop.shown;
59
+ }, 5000);
60
+ });
61
+ </script>
62
+ </template>
63
+ </d2l-demo-snippet>
64
+ </d2l-demo-page>
65
+ </body>
66
+ </html>
@@ -1,5 +1,6 @@
1
1
  import '../colors/colors.js';
2
2
  import '../scroll-wrapper/scroll-wrapper.js';
3
+ import '../backdrop/backdrop-loading.js';
3
4
  import { css, html, LitElement, nothing } from 'lit';
4
5
  import { cssSizes } from '../inputs/input-checkbox.js';
5
6
  import { getComposedParent } from '../../helpers/dom.js';
@@ -259,6 +260,7 @@ export const tableStyles = css`
259
260
  [data-popover-count] {
260
261
  z-index: 6 !important; /* if opened above, we want to stack on top of sticky table-controls */
261
262
  }
263
+
262
264
  `;
263
265
 
264
266
  const SELECTORS = {
@@ -318,6 +320,14 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) {
318
320
  reflect: true,
319
321
  type: Boolean,
320
322
  },
323
+ /**
324
+ * Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed.
325
+ * @type {boolean}
326
+ */
327
+ loading: {
328
+ reflect: true,
329
+ type: Boolean
330
+ },
321
331
  };
322
332
  }
323
333
 
@@ -387,6 +397,7 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) {
387
397
  this._tableIntersectionObserver = null;
388
398
  this._tableMutationObserver = null;
389
399
  this._tableScrollers = {};
400
+ this.loading = false;
390
401
  }
391
402
 
392
403
  connectedCallback() {
@@ -415,7 +426,10 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) {
415
426
  }
416
427
 
417
428
  render() {
418
- const slot = html`<slot @slotchange="${this._handleSlotChange}"></slot>`;
429
+ const slot = html`
430
+ <slot @slotchange="${this._handleSlotChange}"></slot>
431
+ <d2l-backdrop-loading ?shown=${this.loading}></d2l-backdrop-loading>
432
+ `;
419
433
  const useScrollWrapper = this.stickyHeadersScrollWrapper || !this.stickyHeaders;
420
434
  return html`
421
435
  <slot name="controls" @slotchange="${this._handleControlsSlotChange}"></slot>
@@ -186,6 +186,28 @@
186
186
  }
187
187
  ]
188
188
  },
189
+ {
190
+ "name": "d2l-backdrop-loading",
191
+ "path": "./components/backdrop/backdrop-loading.js",
192
+ "description": "A component for displaying a semi-transparent backdrop and a loading spinner over the containing element",
193
+ "attributes": [
194
+ {
195
+ "name": "shown",
196
+ "description": "Used to control whether the loading backdrop is shown",
197
+ "type": "boolean",
198
+ "default": "false"
199
+ }
200
+ ],
201
+ "properties": [
202
+ {
203
+ "name": "shown",
204
+ "attribute": "shown",
205
+ "description": "Used to control whether the loading backdrop is shown",
206
+ "type": "boolean",
207
+ "default": "false"
208
+ }
209
+ ]
210
+ },
189
211
  {
190
212
  "name": "d2l-backdrop",
191
213
  "path": "./components/backdrop/backdrop.js",
@@ -14113,6 +14135,12 @@
14113
14135
  "type": "'default'|'light'",
14114
14136
  "default": "\"default\""
14115
14137
  },
14138
+ {
14139
+ "name": "loading",
14140
+ "description": "Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed.",
14141
+ "type": "boolean",
14142
+ "default": "false"
14143
+ },
14116
14144
  {
14117
14145
  "name": "item-count",
14118
14146
  "description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
@@ -14189,6 +14217,13 @@
14189
14217
  "type": "'default'|'light'",
14190
14218
  "default": "\"default\""
14191
14219
  },
14220
+ {
14221
+ "name": "loading",
14222
+ "attribute": "loading",
14223
+ "description": "Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed.",
14224
+ "type": "boolean",
14225
+ "default": "false"
14226
+ },
14192
14227
  {
14193
14228
  "name": "itemCount",
14194
14229
  "attribute": "item-count",
@@ -14524,6 +14559,12 @@
14524
14559
  "type": "'default'|'light'",
14525
14560
  "default": "\"default\""
14526
14561
  },
14562
+ {
14563
+ "name": "loading",
14564
+ "description": "Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed.",
14565
+ "type": "boolean",
14566
+ "default": "false"
14567
+ },
14527
14568
  {
14528
14569
  "name": "item-count",
14529
14570
  "description": "Total number of items. If not specified, features like select-all-pages will be disabled.",
@@ -14565,6 +14606,13 @@
14565
14606
  "type": "'default'|'light'",
14566
14607
  "default": "\"default\""
14567
14608
  },
14609
+ {
14610
+ "name": "loading",
14611
+ "attribute": "loading",
14612
+ "description": "Whether or not to display a loading backdrop. Set this property when the content in the table is being refreshed.",
14613
+ "type": "boolean",
14614
+ "default": "false"
14615
+ },
14568
14616
  {
14569
14617
  "name": "itemCount",
14570
14618
  "attribute": "item-count",
@@ -15603,12 +15651,12 @@
15603
15651
  ],
15604
15652
  "events": [
15605
15653
  {
15606
- "name": "d2l-template-primary-secondary-resize-start",
15607
- "description": "Dispatched when a user begins moving the divider."
15654
+ "name": "d2l-iframe-pointer-events-disable",
15655
+ "description": "Dispatched when a user begins moving the divider to instruct iframe owners to disable pointer events."
15608
15656
  },
15609
15657
  {
15610
- "name": "d2l-template-primary-secondary-resize-end",
15611
- "description": "Dispatched when a user finishes moving the divider."
15658
+ "name": "d2l-iframe-pointer-events-enable",
15659
+ "description": "Dispatched when a user finishes moving the divider to instruct iframe owners to re-enable pointer events."
15612
15660
  },
15613
15661
  {
15614
15662
  "name": "d2l-template-primary-secondary-form-invalid",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/core",
3
- "version": "3.216.1",
3
+ "version": "3.218.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",
@@ -47,7 +47,7 @@ Note: this template automatically includes `<header>`, `<main>`, `<aside>` and `
47
47
 
48
48
  ### IFrames
49
49
 
50
- If either of the panels contain an `iframe`, resizing may not work properly. This occurs because `iframe`s prevent the page template from receiving mouse events. To handle situations like these, setting `pointer-events: none` for the `iframe` is recommended during resizing. This can be done by listening for `d2l-template-primary-secondary-resize-start` and `d2l-template-primary-secondary-resize-end` events.
50
+ If either of the panels contain an `iframe`, resizing may not work properly. This occurs because `iframe`s prevent the page template from receiving mouse events. To handle situations like these, setting `pointer-events: none` for the `iframe` is recommended during resizing. This can be done by listening for `d2l-iframe-pointer-events-disable` and `d2l-iframe-pointer-events-enable` events.
51
51
 
52
52
  <!-- docs: start hidden content -->
53
53
  ### Properties
@@ -63,8 +63,8 @@ If either of the panels contain an `iframe`, resizing may not work properly. Thi
63
63
  | `width-type` | String, default: `'fullscreen'` | Whether content fills the screen or not. When set to `normal`, the width of the template is constrained to `1230px`. Can be one of `'fullscreen'`, `'normal'`. |
64
64
 
65
65
  ### Events
66
- * `d2l-template-primary-secondary-resize-start`: dispatched when a user begins moving the divider
67
- * `d2l-template-primary-secondary-resize-end`: dispatched when a user finishes moving the divider
66
+ * `d2l-iframe-pointer-events-disable`: dispatched when a user begins moving the divider to instruct iframe owners to disable pointer events
67
+ * `d2l-iframe-pointer-events-enable`: dispatched when a user finishes moving the divider to instruct iframe owners to re-enable pointer events.
68
68
 
69
69
  ### Slots
70
70
  * `header`: page header content
@@ -547,8 +547,8 @@ class MobileTouchResizer extends Resizer {
547
547
  * @slot footer - Page footer content
548
548
  * @slot primary - Main page content
549
549
  * @slot secondary - Supplementary page content
550
- * @fires d2l-template-primary-secondary-resize-start - Dispatched when a user begins moving the divider.
551
- * @fires d2l-template-primary-secondary-resize-end - Dispatched when a user finishes moving the divider.
550
+ * @fires d2l-iframe-pointer-events-disable - Dispatched when a user begins moving the divider to instruct iframe owners to disable pointer events.
551
+ * @fires d2l-iframe-pointer-events-enable - Dispatched when a user finishes moving the divider to instruct iframe owners to re-enable pointer events.
552
552
  * @fires d2l-template-primary-secondary-form-invalid Dispatched when the form fails validation. The error map can be obtained from the detail's errors property.
553
553
  * @fires d2l-template-primary-secondary-form-dirty Dispatched whenever any form element fires an input or change event. Can be used to track whether the form is dirty or not.
554
554
  * @fires d2l-template-primary-secondary-form-submit Dispatched when the form is submitted. The form data can be obtained from the detail's formData property.
@@ -1262,10 +1262,18 @@ class TemplatePrimarySecondary extends LocalizeCoreElement(LitElement) {
1262
1262
  }
1263
1263
 
1264
1264
  _onPanelResizeEnd() {
1265
+ this.dispatchEvent(new CustomEvent('d2l-iframe-pointer-events-enable', { bubbles: true, composed: true }));
1266
+ /**
1267
+ * @ignore
1268
+ */
1265
1269
  this.dispatchEvent(new CustomEvent('d2l-template-primary-secondary-resize-end', { bubbles: true, composed: true }));
1266
1270
  }
1267
1271
 
1268
1272
  _onPanelResizeStart() {
1273
+ this.dispatchEvent(new CustomEvent('d2l-iframe-pointer-events-disable', { bubbles: true, composed: true }));
1274
+ /**
1275
+ * @ignore
1276
+ */
1269
1277
  this.dispatchEvent(new CustomEvent('d2l-template-primary-secondary-resize-start', { bubbles: true, composed: true }));
1270
1278
  }
1271
1279