@brightspace-ui/core 3.277.0 → 3.279.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/backdrop/backdrop-loading.js +100 -133
- package/components/backdrop/backdrop-stale-overlay.js +58 -0
- package/components/page/page.js +57 -6
- package/components/table/demo/table-test.js +9 -9
- package/components/table/table-wrapper.js +41 -46
- package/custom-elements.json +105 -105
- package/lang/ar.js +8 -7
- package/lang/ca.js +8 -7
- package/lang/cy.js +8 -7
- package/lang/da.js +8 -7
- package/lang/de.js +8 -7
- package/lang/en-gb.js +1 -0
- package/lang/en.js +1 -0
- package/lang/es-es.js +8 -7
- package/lang/es.js +8 -7
- package/lang/fr-fr.js +8 -7
- package/lang/fr.js +8 -7
- package/lang/haw.js +8 -7
- package/lang/hi.js +8 -7
- package/lang/ja.js +8 -7
- package/lang/ko.js +8 -7
- package/lang/mi.js +8 -7
- package/lang/nl.js +8 -7
- package/lang/pl.js +256 -0
- package/lang/pt.js +8 -7
- package/lang/ru.js +256 -0
- package/lang/sv.js +8 -7
- package/lang/th.js +8 -7
- package/lang/tr.js +8 -7
- package/lang/ur.js +256 -0
- package/lang/vi.js +8 -7
- package/lang/zh-cn.js +8 -7
- package/lang/zh-tw.js +8 -7
- package/mixins/freshness/freshness-mixin.js +35 -0
- package/package.json +1 -1
- package/components/backdrop/backdrop-dirty-overlay.js +0 -73
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import './backdrop-stale-overlay.js';
|
|
1
2
|
import '../colors/colors.js';
|
|
2
3
|
import '../loading-spinner/loading-spinner.js';
|
|
3
|
-
import '
|
|
4
|
-
import {
|
|
4
|
+
import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
|
|
5
|
+
import { freshness, FreshnessMixin } from '../../mixins/freshness/freshness-mixin.js';
|
|
5
6
|
import { getComposedChildren, getComposedParent } from '../../helpers/dom.js';
|
|
6
7
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
7
|
-
|
|
8
8
|
import { PropertyRequiredMixin } from '../../mixins/property-required/property-required-mixin.js';
|
|
9
9
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
10
10
|
|
|
@@ -12,7 +12,6 @@ const BACKDROP_DELAY_MS = 800;
|
|
|
12
12
|
const FADE_DURATION_MS = 500;
|
|
13
13
|
const SPINNER_DELAY_MS = FADE_DURATION_MS;
|
|
14
14
|
const LOADING_ANNOUNCEMENT_DELAY = 1000;
|
|
15
|
-
|
|
16
15
|
const LOADING_SPINNER_SIZE = 50;
|
|
17
16
|
|
|
18
17
|
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
@@ -20,56 +19,31 @@ const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
20
19
|
/**
|
|
21
20
|
* A component for displaying a semi-transparent backdrop and a loading spinner over the containing element
|
|
22
21
|
*/
|
|
23
|
-
class
|
|
22
|
+
class BackdropLoading extends PropertyRequiredMixin(FreshnessMixin(LocalizeCoreElement(LitElement))) {
|
|
24
23
|
|
|
25
24
|
static properties = {
|
|
26
|
-
/**
|
|
27
|
-
* The state of data in the element being overlaid. Set to 'clean' when the data represents the user's latest selections, 'dirty' when the data does not represent the user's latest selections, and 'loading' if the data is being actively refreshed
|
|
28
|
-
* @type {'clean'|'dirty'|'loading'}
|
|
29
|
-
*/
|
|
30
|
-
dataState: {
|
|
31
|
-
reflect: true,
|
|
32
|
-
type: String
|
|
33
|
-
},
|
|
34
25
|
/**
|
|
35
26
|
* Used to identify content that the backdrop should make inert
|
|
36
27
|
* @type {string}
|
|
37
28
|
*/
|
|
38
29
|
for: { type: String, required: true },
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
dirtyText: {
|
|
44
|
-
reflect: true,
|
|
45
|
-
attribute: 'dirty-text',
|
|
46
|
-
type: String
|
|
47
|
-
},
|
|
48
|
-
/**
|
|
49
|
-
* The text displayed on the button of the dirty state overlay when the 'dirty' dataState is set.
|
|
50
|
-
* @type {string}
|
|
51
|
-
*/
|
|
52
|
-
dirtyButtonText: {
|
|
53
|
-
reflect: true,
|
|
54
|
-
attribute: 'dirty-button-text',
|
|
55
|
-
type: String
|
|
56
|
-
},
|
|
57
|
-
_state: { type: String, reflect: true },
|
|
58
|
-
_spinnerTop: { state: true },
|
|
59
|
-
_ariaContent: { state: true }
|
|
30
|
+
_ariaContent: { state: true },
|
|
31
|
+
_loadingSpinnerTop: { state: true },
|
|
32
|
+
_staleOverlayTop: { state: true },
|
|
33
|
+
_state: { type: String, reflect: true }
|
|
60
34
|
};
|
|
61
35
|
|
|
62
36
|
static styles = [css`
|
|
63
|
-
|
|
37
|
+
.backdrop-container {
|
|
64
38
|
display: none;
|
|
65
39
|
}
|
|
66
40
|
|
|
67
41
|
:host([_state="showing"]),
|
|
68
42
|
:host([_state="shown"]),
|
|
69
43
|
:host([_state="hiding"]),
|
|
70
|
-
:host([_state="showing"])
|
|
71
|
-
:host([_state="shown"])
|
|
72
|
-
:host([_state="hiding"])
|
|
44
|
+
:host([_state="showing"]) .backdrop-container,
|
|
45
|
+
:host([_state="shown"]) .backdrop-container,
|
|
46
|
+
:host([_state="hiding"]) .backdrop-container {
|
|
73
47
|
display: flex;
|
|
74
48
|
inset: 0;
|
|
75
49
|
justify-content: center;
|
|
@@ -99,13 +73,13 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
99
73
|
opacity: 1;
|
|
100
74
|
transition: opacity ${FADE_DURATION_MS}ms ease-in ${SPINNER_DELAY_MS}ms;
|
|
101
75
|
}
|
|
102
|
-
:host([_state="shown"][
|
|
76
|
+
:host([_state="shown"][freshness="${unsafeCSS(freshness.stale)}"]) d2l-loading-spinner,
|
|
103
77
|
:host([_state="hiding"]) d2l-loading-spinner {
|
|
104
78
|
opacity: 0;
|
|
105
79
|
transition: opacity ${FADE_DURATION_MS}ms ease-out;
|
|
106
80
|
}
|
|
107
81
|
|
|
108
|
-
d2l-backdrop-
|
|
82
|
+
d2l-backdrop-stale-overlay {
|
|
109
83
|
background-color: var(--d2l-theme-backdrop-dialog-color);
|
|
110
84
|
height: fit-content;
|
|
111
85
|
justify-content: center;
|
|
@@ -114,12 +88,12 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
114
88
|
top: 0;
|
|
115
89
|
z-index: 1000;
|
|
116
90
|
}
|
|
117
|
-
:host([_state="shown"]) d2l-backdrop-
|
|
91
|
+
:host([_state="shown"]) d2l-backdrop-stale-overlay {
|
|
118
92
|
opacity: 1;
|
|
119
93
|
transition: opacity ${FADE_DURATION_MS}ms ease-in;
|
|
120
94
|
}
|
|
121
|
-
:host([_state="shown"][
|
|
122
|
-
:host([_state="hiding"]) d2l-backdrop-
|
|
95
|
+
:host([_state="shown"][freshness="${unsafeCSS(freshness.loading)}"]) d2l-backdrop-stale-overlay,
|
|
96
|
+
:host([_state="hiding"]) d2l-backdrop-stale-overlay {
|
|
123
97
|
opacity: 0;
|
|
124
98
|
transition: opacity ${FADE_DURATION_MS}ms ease-out;
|
|
125
99
|
}
|
|
@@ -131,46 +105,46 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
131
105
|
|
|
132
106
|
constructor() {
|
|
133
107
|
super();
|
|
134
|
-
this.dataState = 'clean';
|
|
135
|
-
this._state = 'hidden';
|
|
136
|
-
this._spinnerTop = 0;
|
|
137
|
-
this._dirtyDialogTop = 0;
|
|
138
108
|
this._ariaContent = '';
|
|
109
|
+
this._loadingSpinnerTop = 0;
|
|
110
|
+
this._staleOverlayTop = 0;
|
|
111
|
+
this._state = 'hidden';
|
|
139
112
|
}
|
|
140
113
|
|
|
141
114
|
render() {
|
|
142
115
|
const backdropVisible = this._state !== 'hidden';
|
|
143
116
|
|
|
117
|
+
const backdropContainer = backdropVisible ? html`<div class="backdrop-container">
|
|
118
|
+
<div class="backdrop" @transitionend="${this.#handleTransitionEnd}" @transitioncancel="${this.#handleTransitionEnd}"></div>
|
|
119
|
+
<d2l-loading-spinner style="${styleMap({ top: `${this._loadingSpinnerTop}px` })}" size="${LOADING_SPINNER_SIZE}"></d2l-loading-spinner>
|
|
120
|
+
</div>` : nothing;
|
|
121
|
+
|
|
122
|
+
const backdropStaleOverlay = backdropVisible ? html`<d2l-backdrop-stale-overlay
|
|
123
|
+
button-text="${this.freshnessStaleButtonText}"
|
|
124
|
+
text="${this.freshnessStaleText}"
|
|
125
|
+
?inert="${this.freshness !== freshness.stale}"
|
|
126
|
+
style="${styleMap({ top: `${this._staleOverlayTop}px` })}">
|
|
127
|
+
</d2l-backdrop-stale-overlay>` : nothing;
|
|
128
|
+
|
|
144
129
|
return html`
|
|
145
|
-
${
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
</div>` : nothing
|
|
150
|
-
}
|
|
151
|
-
<div aria-live="polite" id="d2l-live-region">
|
|
152
|
-
${backdropVisible ?
|
|
153
|
-
html`<d2l-backdrop-dirty-overlay
|
|
154
|
-
style=${styleMap({ top: `${this._dirtyDialogTop}px` })}
|
|
155
|
-
description="${this.dirtyText}"
|
|
156
|
-
action="${this.dirtyButtonText}"
|
|
157
|
-
?inert=${this.dataState !== 'dirty'}
|
|
158
|
-
></d2l-backdrop-dirty-overlay>` : nothing }
|
|
159
|
-
<d2l-offscreen>
|
|
160
|
-
${this._ariaContent}
|
|
161
|
-
</d2l-offscreen>
|
|
130
|
+
${backdropContainer}
|
|
131
|
+
<div aria-live="polite" class="backdrop-stale-container">
|
|
132
|
+
${backdropStaleOverlay}
|
|
133
|
+
<d2l-offscreen>${this._ariaContent}</d2l-offscreen>
|
|
162
134
|
</div>
|
|
163
135
|
`;
|
|
164
136
|
}
|
|
137
|
+
|
|
165
138
|
updated(changedProperties) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
139
|
+
super.updated(changedProperties);
|
|
140
|
+
|
|
141
|
+
if (changedProperties.get('_state') && changedProperties.get('_state') === 'hidden') {
|
|
142
|
+
this.#updatePosition();
|
|
169
143
|
}
|
|
170
144
|
|
|
171
145
|
if (changedProperties.has('_state')) {
|
|
172
146
|
if (this._state === 'showing') {
|
|
173
|
-
if (this.
|
|
147
|
+
if (this.freshness === freshness.loading) {
|
|
174
148
|
setTimeout(() => {
|
|
175
149
|
if (this._state === 'showing') this._state = 'shown';
|
|
176
150
|
}, BACKDROP_DELAY_MS);
|
|
@@ -180,26 +154,27 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
180
154
|
}
|
|
181
155
|
}
|
|
182
156
|
}
|
|
157
|
+
|
|
183
158
|
willUpdate(changedProperties) {
|
|
184
|
-
|
|
185
|
-
this.#clearLiveArea();
|
|
159
|
+
super.willUpdate(changedProperties);
|
|
186
160
|
|
|
187
|
-
|
|
188
|
-
|
|
161
|
+
if (changedProperties.has('freshness') && changedProperties.get('freshness') !== undefined) {
|
|
162
|
+
this.#clearLiveRegionContent();
|
|
189
163
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.#
|
|
164
|
+
const oldState = changedProperties.get('freshness');
|
|
165
|
+
const newState = this.freshness;
|
|
166
|
+
|
|
167
|
+
if (newState === freshness.loading) {
|
|
168
|
+
this.#setLiveRegionContent(this.localize('components.backdrop-loading.loadingAnnouncement'), { delay: LOADING_ANNOUNCEMENT_DELAY });
|
|
169
|
+
} else if (oldState === freshness.loading && newState === freshness.fresh) {
|
|
170
|
+
this.#setLiveRegionContent(this.localize('components.backdrop-loading.loadingCompleteAnnouncement'));
|
|
195
171
|
}
|
|
196
172
|
|
|
197
|
-
|
|
198
|
-
if (oldState === 'clean') {
|
|
173
|
+
if (oldState === freshness.fresh) {
|
|
199
174
|
this.#show();
|
|
200
|
-
} else if (newState ===
|
|
175
|
+
} else if (newState === freshness.fresh) {
|
|
201
176
|
this.#fade();
|
|
202
|
-
} else if (oldState ===
|
|
177
|
+
} else if (oldState === freshness.loading && newState === freshness.stale) {
|
|
203
178
|
setTimeout(() => {
|
|
204
179
|
if (this._state === 'showing') this._state = 'shown';
|
|
205
180
|
}, BACKDROP_DELAY_MS);
|
|
@@ -207,52 +182,19 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
207
182
|
}
|
|
208
183
|
}
|
|
209
184
|
|
|
210
|
-
|
|
211
|
-
if (this._state === 'hidden') { return; }
|
|
212
|
-
|
|
213
|
-
const loadingSpinner = this.shadowRoot.querySelector('d2l-loading-spinner');
|
|
214
|
-
if (!loadingSpinner) { return; }
|
|
215
|
-
|
|
216
|
-
const boundingRect = this.shadowRoot.querySelector('#visible').getBoundingClientRect();
|
|
217
|
-
|
|
218
|
-
// Calculate the centerpoint of the visible portion of the element
|
|
219
|
-
const upperVisibleBound = Math.max(0, boundingRect.top);
|
|
220
|
-
const lowerVisibleBound = Math.min(window.innerHeight, boundingRect.bottom);
|
|
221
|
-
const visibleHeight = lowerVisibleBound - upperVisibleBound;
|
|
222
|
-
const centeringOffset = (visibleHeight / 4);
|
|
223
|
-
|
|
224
|
-
// Calculate if an offset is required to move to the top of the viewport before centering
|
|
225
|
-
const topOffset = Math.max(0, -boundingRect.top); // measures the distance below the top of the viewport, which is negative if the element starts above the viewport
|
|
226
|
-
|
|
227
|
-
// Adjust for the size of the spinner
|
|
228
|
-
const spinnerSizeOffset = LOADING_SPINNER_SIZE / 2;
|
|
229
|
-
|
|
230
|
-
this._spinnerTop = centeringOffset + topOffset - spinnerSizeOffset;
|
|
185
|
+
#announcementTimeoutId;
|
|
231
186
|
|
|
232
|
-
|
|
233
|
-
const dirtyOverlay = this.shadowRoot.querySelector('d2l-backdrop-dirty-overlay');
|
|
234
|
-
if (dirtyOverlay) {
|
|
235
|
-
await this.shadowRoot.querySelector('d2l-empty-state-action-button')?.getUpdateComplete();
|
|
236
|
-
const dirtyDialogSizeOffset = dirtyOverlay.getBoundingClientRect().height / 2;
|
|
237
|
-
|
|
238
|
-
this._dirtyDialogTop = centeringOffset + topOffset - dirtyDialogSizeOffset;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
#clearLiveArea() {
|
|
187
|
+
#clearLiveRegionContent() {
|
|
243
188
|
this._ariaContent = '';
|
|
244
189
|
|
|
245
|
-
if (this
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
this.announcementTimeout = null;
|
|
190
|
+
if (this.#announcementTimeoutId) clearTimeout(this.#announcementTimeoutId);
|
|
191
|
+
this.#announcementTimeoutId = null;
|
|
250
192
|
}
|
|
251
193
|
|
|
252
194
|
#fade() {
|
|
253
195
|
let hideImmediately = reduceMotion || this._state === 'showing';
|
|
254
196
|
if (this._state === 'shown') {
|
|
255
|
-
const currentOpacity = getComputedStyle(this.shadowRoot.querySelector('
|
|
197
|
+
const currentOpacity = getComputedStyle(this.shadowRoot.querySelector('.backdrop-stale-container')).opacity;
|
|
256
198
|
hideImmediately ||= (currentOpacity === '0');
|
|
257
199
|
}
|
|
258
200
|
|
|
@@ -266,14 +208,10 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
266
208
|
#getBackdropTarget() {
|
|
267
209
|
const parent = getComposedParent(this);
|
|
268
210
|
|
|
269
|
-
const targetedChildren = getComposedChildren(
|
|
270
|
-
|
|
271
|
-
(
|
|
272
|
-
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
if (targetedChildren.length === 0) { throw new Error(`Backdrop cannot find sibling identified by 'for' property with value ${this.for}`);}
|
|
276
|
-
|
|
211
|
+
const targetedChildren = getComposedChildren(parent, (elem) => elem.id === this.for, false);
|
|
212
|
+
if (targetedChildren.length === 0) {
|
|
213
|
+
throw new Error(`Backdrop cannot find sibling identified by 'for' property with value ${this.for}`);
|
|
214
|
+
}
|
|
277
215
|
return targetedChildren[0];
|
|
278
216
|
}
|
|
279
217
|
|
|
@@ -287,24 +225,53 @@ class LoadingBackdrop extends PropertyRequiredMixin(LocalizeCoreElement(LitEleme
|
|
|
287
225
|
this._state = 'hidden';
|
|
288
226
|
|
|
289
227
|
const containingBlock = this.#getBackdropTarget();
|
|
290
|
-
|
|
291
228
|
if (containingBlock.dataset.initiallyInert !== '1') containingBlock.removeAttribute('inert');
|
|
292
229
|
}
|
|
293
230
|
|
|
294
|
-
#
|
|
295
|
-
this
|
|
231
|
+
#setLiveRegionContent(content, { delay } = {}) {
|
|
232
|
+
this.#announcementTimeoutId = setTimeout(() => this._ariaContent = content, delay || 0);
|
|
296
233
|
}
|
|
297
234
|
|
|
298
235
|
#show() {
|
|
299
236
|
this._state = reduceMotion ? 'shown' : 'showing';
|
|
300
237
|
|
|
301
238
|
const containingBlock = this.#getBackdropTarget();
|
|
302
|
-
|
|
303
239
|
if (containingBlock.getAttribute('inert') !== null) containingBlock.dataset.initiallyInert = '1';
|
|
304
|
-
|
|
305
240
|
containingBlock.setAttribute('inert', 'inert');
|
|
306
241
|
}
|
|
307
242
|
|
|
243
|
+
async #updatePosition() {
|
|
244
|
+
if (this._state === 'hidden') return;
|
|
245
|
+
|
|
246
|
+
const loadingSpinner = this.shadowRoot.querySelector('d2l-loading-spinner');
|
|
247
|
+
if (!loadingSpinner) return;
|
|
248
|
+
|
|
249
|
+
const backdropContainerRect = this.shadowRoot.querySelector('.backdrop-container').getBoundingClientRect();
|
|
250
|
+
|
|
251
|
+
// Calculate the centerpoint of the visible portion of the element
|
|
252
|
+
const upperVisibleBound = Math.max(0, backdropContainerRect.top);
|
|
253
|
+
const lowerVisibleBound = Math.min(window.innerHeight, backdropContainerRect.bottom);
|
|
254
|
+
const visibleHeight = lowerVisibleBound - upperVisibleBound;
|
|
255
|
+
const centeringOffset = (visibleHeight / 4);
|
|
256
|
+
|
|
257
|
+
// Calculate if an offset is required to move to the top of the viewport before centering
|
|
258
|
+
const topOffset = Math.max(0, -backdropContainerRect.top); // measures the distance below the top of the viewport, which is negative if the element starts above the viewport
|
|
259
|
+
|
|
260
|
+
// Adjust for the size of the spinner
|
|
261
|
+
const spinnerSizeOffset = LOADING_SPINNER_SIZE / 2;
|
|
262
|
+
|
|
263
|
+
this._loadingSpinnerTop = centeringOffset + topOffset - spinnerSizeOffset;
|
|
264
|
+
|
|
265
|
+
// Adjust for the size of the stale overlay
|
|
266
|
+
const staleOverlay = this.shadowRoot.querySelector('d2l-backdrop-stale-overlay');
|
|
267
|
+
if (staleOverlay) {
|
|
268
|
+
await this.shadowRoot.querySelector('d2l-empty-state-action-button')?.getUpdateComplete();
|
|
269
|
+
const staleOverlaySizeOffset = staleOverlay.getBoundingClientRect().height / 2;
|
|
270
|
+
|
|
271
|
+
this._staleOverlayTop = centeringOffset + topOffset - staleOverlaySizeOffset;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
308
275
|
}
|
|
309
276
|
|
|
310
|
-
customElements.define('d2l-backdrop-loading',
|
|
277
|
+
customElements.define('d2l-backdrop-loading', BackdropLoading);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import '../button/button-subtle.js';
|
|
2
|
+
import { css, html, LitElement } from 'lit';
|
|
3
|
+
import { bodyCompactStyles } from '../typography/styles.js';
|
|
4
|
+
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A component to render as an overlay over another element with stale data.
|
|
8
|
+
*/
|
|
9
|
+
class BackdropStaleOverlay extends LocalizeCoreElement(LitElement) {
|
|
10
|
+
|
|
11
|
+
static properties = {
|
|
12
|
+
/**
|
|
13
|
+
* The action button text
|
|
14
|
+
* @type {string}
|
|
15
|
+
*/
|
|
16
|
+
buttonText: { type: String, attribute: 'button-text' },
|
|
17
|
+
/**
|
|
18
|
+
* The text displayed on the overlay
|
|
19
|
+
* @type {string}
|
|
20
|
+
*/
|
|
21
|
+
text: { type: String }
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
static styles = [bodyCompactStyles, css`
|
|
25
|
+
:host {
|
|
26
|
+
align-items: center;
|
|
27
|
+
border: 1px solid var(--d2l-color-mica);
|
|
28
|
+
border-radius: 0.3rem;
|
|
29
|
+
column-gap: 0.75rem;
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-wrap: wrap;
|
|
32
|
+
padding: 1.2rem 1.5rem;
|
|
33
|
+
}
|
|
34
|
+
`];
|
|
35
|
+
|
|
36
|
+
render() {
|
|
37
|
+
const message = this.text || this.localize('components.backdrop-stale-overlay.message');
|
|
38
|
+
const buttonText = this.buttonText || this.localize('intl-common:actions:reload');;
|
|
39
|
+
|
|
40
|
+
return html`
|
|
41
|
+
<p class="d2l-body-compact">${message}</p>
|
|
42
|
+
<d2l-button-subtle
|
|
43
|
+
@click="${this.#handleActionClick}"
|
|
44
|
+
text="${buttonText}">
|
|
45
|
+
</d2l-button-subtle>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#handleActionClick(e) {
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
|
|
52
|
+
/** Dispatched when the action button on the overlay is clicked */
|
|
53
|
+
this.dispatchEvent(new CustomEvent('d2l-backdrop-stale-overlay-action', { bubbles: true, composed: true }));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
customElements.define('d2l-backdrop-stale-overlay', BackdropStaleOverlay);
|
package/components/page/page.js
CHANGED
|
@@ -16,26 +16,46 @@ class PanelStateController {
|
|
|
16
16
|
this.#host = host;
|
|
17
17
|
this.#panels = {};
|
|
18
18
|
for (const [key, config] of Object.entries(panelConfigs)) {
|
|
19
|
-
this.#panels[key] = {
|
|
19
|
+
this.#panels[key] = {
|
|
20
|
+
animate: false,
|
|
21
|
+
collapsed: false,
|
|
22
|
+
size: 0,
|
|
23
|
+
minSize: config.minSize,
|
|
24
|
+
maxSize: config.minSize
|
|
25
|
+
};
|
|
20
26
|
}
|
|
21
27
|
host.addController(this);
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
getAnimate(key) { return this.#panels[key].animate; }
|
|
24
31
|
getCollapsed(key) { return this.#panels[key].collapsed; }
|
|
25
32
|
getMaxSize(key) { return this.#panels[key].maxSize; }
|
|
26
33
|
getMinSize(key) { return this.#panels[key].minSize; }
|
|
27
|
-
getSize(key) {
|
|
34
|
+
getSize(key) {
|
|
35
|
+
const panel = this.#panels[key];
|
|
36
|
+
// TO DO: Factor in dragging
|
|
37
|
+
return panel.collapsed ? 0 : panel.size;
|
|
38
|
+
}
|
|
28
39
|
|
|
29
|
-
resize(key, requestedSize) {
|
|
40
|
+
resize(key, requestedSize, animate = false) {
|
|
30
41
|
const panel = this.#panels[key];
|
|
31
42
|
// Clamp requested size to min and max bounds
|
|
32
43
|
panel.size = Math.max(panel.minSize, Math.min(requestedSize, panel.maxSize));
|
|
44
|
+
panel.animate = animate;
|
|
33
45
|
this.#host.requestUpdate();
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
setCollapsed(key, collapsed) {
|
|
37
49
|
const panel = this.#panels[key];
|
|
38
50
|
panel.collapsed = collapsed;
|
|
51
|
+
panel.animate = true;
|
|
52
|
+
this.#host.requestUpdate();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setDragSize(key) {
|
|
56
|
+
const panel = this.#panels[key];
|
|
57
|
+
// TO DO: Handle Dragging
|
|
58
|
+
panel.animate = false;
|
|
39
59
|
this.#host.requestUpdate();
|
|
40
60
|
}
|
|
41
61
|
|
|
@@ -155,6 +175,27 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
|
|
|
155
175
|
z-index: 15; /* To be over d2l-page-* panel headers */
|
|
156
176
|
}
|
|
157
177
|
|
|
178
|
+
.side-nav .divider[collapsed] {
|
|
179
|
+
margin-inline-start: 18px;
|
|
180
|
+
}
|
|
181
|
+
.supporting .divider[collapsed] {
|
|
182
|
+
margin-inline-end: 18px;
|
|
183
|
+
}
|
|
184
|
+
.side-nav-panel.collapsed,
|
|
185
|
+
.supporting-panel.collapsed {
|
|
186
|
+
visibility: hidden;
|
|
187
|
+
}
|
|
188
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
189
|
+
.side-nav-panel.animate,
|
|
190
|
+
.supporting-panel.animate {
|
|
191
|
+
transition: width 400ms cubic-bezier(0, 0.7, 0.5, 1);
|
|
192
|
+
}
|
|
193
|
+
.side-nav-panel.animate.collapsed,
|
|
194
|
+
.supporting-panel.animate.collapsed {
|
|
195
|
+
transition: width 400ms cubic-bezier(0, 0.7, 0.5, 1), visibility 0s 400ms;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
158
199
|
.footer:not([hidden]),
|
|
159
200
|
.floating-buttons-container {
|
|
160
201
|
display: inline;
|
|
@@ -285,7 +326,7 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
|
|
|
285
326
|
|
|
286
327
|
#handleDividerResize(e) {
|
|
287
328
|
const panelKey = e.target.dataset.panelKey;
|
|
288
|
-
this._panelState.resize(panelKey, e.detail.requestedSize);
|
|
329
|
+
this._panelState.resize(panelKey, e.detail.requestedSize, true);
|
|
289
330
|
};
|
|
290
331
|
|
|
291
332
|
#handleDividerToggle(e) {
|
|
@@ -363,10 +404,15 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
|
|
|
363
404
|
}
|
|
364
405
|
|
|
365
406
|
#renderSideNavPanel() {
|
|
407
|
+
const classes = {
|
|
408
|
+
'side-nav-panel': true,
|
|
409
|
+
'animate': this._panelState.getAnimate('side-nav'),
|
|
410
|
+
'collapsed': this._panelState.getCollapsed('side-nav')
|
|
411
|
+
};
|
|
366
412
|
return html`
|
|
367
413
|
<nav class="side-nav" aria-label="${this.localize('components.page.side-nav-label')}">
|
|
368
414
|
<div
|
|
369
|
-
class="
|
|
415
|
+
class="${classMap(classes)}"
|
|
370
416
|
style=${styleMap({ width: `${this._panelState.getSize('side-nav')}px` })}
|
|
371
417
|
?hidden="${!this._slotVisibility['side-nav']}">
|
|
372
418
|
<slot name="side-nav" @slotchange="${this.#handleSlotVisibilityChange}"></slot>
|
|
@@ -378,12 +424,17 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
|
|
|
378
424
|
}
|
|
379
425
|
|
|
380
426
|
#renderSupportingPanel() {
|
|
427
|
+
const classes = {
|
|
428
|
+
'supporting-panel': true,
|
|
429
|
+
'animate': this._panelState.getAnimate('supporting'),
|
|
430
|
+
'collapsed': this._panelState.getCollapsed('supporting')
|
|
431
|
+
};
|
|
381
432
|
return html`
|
|
382
433
|
<aside class="supporting" aria-label="${this.localize('components.page.supporting-label')}">
|
|
383
434
|
${!this._slotVisibility['supporting'] ? nothing :
|
|
384
435
|
this.#renderDivider('supporting', this.localize('components.page.supporting-divider-label'), 'end')}
|
|
385
436
|
<div
|
|
386
|
-
class="
|
|
437
|
+
class="${classMap(classes)}"
|
|
387
438
|
style=${styleMap({ width: `${this._panelState.getSize('supporting')}px` })}
|
|
388
439
|
?hidden="${!this._slotVisibility['supporting']}">
|
|
389
440
|
<slot name="supporting" @slotchange="${this.#handleSlotVisibilityChange}"></slot>
|
|
@@ -18,7 +18,7 @@ import '../../inputs/input-radio.js';
|
|
|
18
18
|
import '../../inputs/input-radio-group.js';
|
|
19
19
|
|
|
20
20
|
import { css, html, nothing } from 'lit';
|
|
21
|
-
import { tableStyles, TableWrapper } from '../table-wrapper.js';
|
|
21
|
+
import { tableFreshness, tableStyles, TableWrapper } from '../table-wrapper.js';
|
|
22
22
|
import { DemoPassthroughMixin } from '../../demo/demo-passthrough-mixin.js';
|
|
23
23
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
24
24
|
|
|
@@ -83,12 +83,12 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper')
|
|
|
83
83
|
this._data = data();
|
|
84
84
|
this._sortField = undefined;
|
|
85
85
|
this._sortDesc = false;
|
|
86
|
-
this.
|
|
86
|
+
this.freshness = tableFreshness.fresh;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
render() {
|
|
90
90
|
return html`
|
|
91
|
-
<d2l-table-wrapper item-count="${ifDefined(this.paging ? 500 : undefined)}"
|
|
91
|
+
<d2l-table-wrapper item-count="${ifDefined(this.paging ? 500 : undefined)}" freshness-stale-text="This table data is out of date." freshness-stale-button-text="Reload Me!">
|
|
92
92
|
<d2l-table-controls slot="controls" ?no-sticky="${!this.stickyControls}" select-all-pages-allowed>
|
|
93
93
|
<d2l-selection-action
|
|
94
94
|
text="Sticky controls"
|
|
@@ -100,10 +100,10 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper')
|
|
|
100
100
|
icon="tier1:${this.stickyHeaders ? 'check' : 'close-default'}"
|
|
101
101
|
@d2l-selection-action-click="${this._toggleStickyHeaders}">
|
|
102
102
|
</d2l-selection-action>
|
|
103
|
-
<d2l-input-radio-group label="
|
|
104
|
-
<d2l-input-radio label="
|
|
105
|
-
<d2l-input-radio label="
|
|
106
|
-
<d2l-input-radio label="Loading" value="loading" ?checked=${this.
|
|
103
|
+
<d2l-input-radio-group label="Freshness" horizontal label-hidden name="freshness" @change=${this._handleFreshnessChange}>
|
|
104
|
+
<d2l-input-radio label="Fresh" value="${tableFreshness.fresh}" ?checked=${this.freshness === tableFreshness.fresh}></d2l-input-radio>
|
|
105
|
+
<d2l-input-radio label="Stale" value="${tableFreshness.stale}" ?checked=${this.freshness === tableFreshness.stale}></d2l-input-radio>
|
|
106
|
+
<d2l-input-radio label="Loading" value="${tableFreshness.loading}" ?checked=${this.freshness === tableFreshness.loading}></d2l-input-radio>
|
|
107
107
|
</d2l-input-radio-group>
|
|
108
108
|
</d2l-table-controls>
|
|
109
109
|
|
|
@@ -170,8 +170,8 @@ class TestTable extends DemoPassthroughMixin(TableWrapper, 'd2l-table-wrapper')
|
|
|
170
170
|
`;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
this.
|
|
173
|
+
_handleFreshnessChange(e) {
|
|
174
|
+
this.freshness = e.detail.value;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
async _handlePagerLoadMore(e) {
|