diff --git a/components/backdrop/README.md b/components/backdrop/README.md index b284044a505..015c3396994 100644 --- a/components/backdrop/README.md +++ b/components/backdrop/README.md @@ -1,9 +1,11 @@ # Backdrops -Use a backdrop to de-emphasize background elements and draw the user's attention to a dialog or other modal content. +Use a backdrop to de-emphasize the content of an element or page. ## Backdrop [d2l-backdrop] +Use a backdrop to de-emphasize background elements and draw the user's attention to a dialog or other modal content. + ```html + +
Refresh Content
+
+ + + + + + + + + + + + + + + + +
CourseGradeHours Spent in Content
Math85%100
Art98%10
+ +
+``` + +### Properties: + +| Property | Type | Description | +|--|--|--| +| `shown` | Boolean | Used to control whether the loading backdrop is shown | + diff --git a/components/backdrop/loading-backdrop.js b/components/backdrop/loading-backdrop.js new file mode 100644 index 00000000000..a7374d7a092 --- /dev/null +++ b/components/backdrop/loading-backdrop.js @@ -0,0 +1,131 @@ +import '../colors/colors.js'; +import { css, html, LitElement } from 'lit'; + +const BACKDROP_DELAY_MS = 800; +const BACKDROP_FADE_IN_DURATION_MS = 500; +const BACKDROP_FADE_OUT_DURATION_MS = 200; +const SPINNER_DELAY_MS = BACKDROP_DELAY_MS + BACKDROP_FADE_IN_DURATION_MS; +const SPINNER_FADE_IN_DURATION_MS = 500; +const SPINNER_FADE_OUT_DURATION_MS = 200; + +/** + * A component for displaying a semi-transparent backdrop and a loading spinner over the containing element + */ +class LoadingBackdrop extends LitElement { + + static get properties() { + return { + /** + * Used to control whether the loading backdrop is shown + * @type {boolean} + */ + shown: { type: Boolean }, + _state: { type: String, reflect: true }, + }; + } + + static get styles() { + return css` + :host, .backdrop, d2l-loading-spinner { + width: 0%; + height: 0%; + position: absolute; + } + + .backdrop, d2l-loading-spinner { + opacity: 0; + } + + :host { + z-index: 999; + top: 0px; + } + + .backdrop { + background-color: var(--d2l-color-regolith); + } + + d2l-loading-spinner { + top: 100px; + } + + :host([_state="showing"]), + :host([_state="hiding"]), + d2l-loading-spinner[_state="showing"], + d2l-loading-spinner[_state="hiding"], + .backdrop[_state="showing"], + .backdrop[_state="hiding"] { + width: 100%; + height: 100%; + } + + d2l-loading-spinner[_state="showing"] { + opacity: 1; + transition: opacity ${SPINNER_FADE_IN_DURATION_MS}ms ease-in ${SPINNER_DELAY_MS}ms; + } + + .backdrop[_state="showing"] { + opacity: 0.7; + transition: opacity ${BACKDROP_FADE_IN_DURATION_MS}ms ease-in ${BACKDROP_DELAY_MS}ms; + } + + d2l-loading-spinner[_state="hiding"] { + transition: opacity ${SPINNER_FADE_OUT_DURATION_MS}ms ease-out; + } + + .backdrop[_state="hiding"] { + transition: opacity ${BACKDROP_FADE_OUT_DURATION_MS}ms ease-out; + } + + @media (prefers-reduced-motion: reduce) { + * { transition: none} + } + `; + } + + constructor() { + super(); + this.shown = false; + this._state = null; + } + + render() { + return html` +
+ + `; + } + + willUpdate(changedProperties) { + if (changedProperties.has('shown')) { + if (this.shown) { + this._state = 'showing'; + } else if (changedProperties.get('shown') !== undefined) { + this._state = 'hiding'; + + this._hideAfterFading(); + } + } + } + + _hideAfterFading() { + const backdrop = this.shadowRoot.querySelector('.backdrop'); + const loadingSpinner = this.shadowRoot.querySelector('d2l-loading-spinner'); + + Promise.all([ + new Promise(resolve => { + backdrop.addEventListener('transitionend', resolve, { once: true }); + backdrop.addEventListener('transitioncancel', resolve, { once: true }); + }), + new Promise(resolve => { + loadingSpinner.addEventListener('transitionend', resolve, { once: true }); + loadingSpinner.addEventListener('transitioncancel', resolve, { once: true }); + }) + ]).then(() => { + this._state = null; + }); + } + +} + +customElements.define('d2l-loading-backdrop', LoadingBackdrop); diff --git a/components/table/table-wrapper.js b/components/table/table-wrapper.js index b26422ca184..852f6315449 100644 --- a/components/table/table-wrapper.js +++ b/components/table/table-wrapper.js @@ -1,5 +1,6 @@ import '../colors/colors.js'; import '../scroll-wrapper/scroll-wrapper.js'; +import '../backdrop/loading-backdrop.js'; import { css, html, LitElement, nothing } from 'lit'; import { cssSizes } from '../inputs/input-checkbox.js'; import { getComposedParent } from '../../helpers/dom.js'; @@ -254,6 +255,7 @@ export const tableStyles = css` [data-popover-count] { z-index: 6 !important; /* if opened above, we want to stack on top of sticky table-controls */ } + `; const SELECTORS = { @@ -313,6 +315,10 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { reflect: true, type: Boolean, }, + loading: { + reflect: true, + type: Boolean + }, }; } @@ -382,6 +388,7 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { this._tableIntersectionObserver = null; this._tableMutationObserver = null; this._tableScrollers = {}; + this._loading = false; } connectedCallback() { @@ -410,7 +417,10 @@ export class TableWrapper extends PageableMixin(SelectionMixin(LitElement)) { } render() { - const slot = html``; + const slot = html` + + + `; const useScrollWrapper = this.stickyHeadersScrollWrapper || !this.stickyHeaders; return html`