@brightspace-ui/core 3.269.3 → 3.269.5

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.
@@ -113,8 +113,6 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
113
113
 
114
114
  constructor() {
115
115
  super();
116
- this._validationCustomConnected = this._validationCustomConnected.bind(this);
117
- this._onFormElementErrorsChange = this._onFormElementErrorsChange.bind(this);
118
116
 
119
117
  this._firstUpdateResolve = null;
120
118
  this._firstUpdatePromise = new Promise((resolve) => {
@@ -160,14 +158,14 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
160
158
 
161
159
  connectedCallback() {
162
160
  super.connectedCallback();
163
- this.shadowRoot.addEventListener('d2l-validation-custom-connected', this._validationCustomConnected);
164
- this.shadowRoot.addEventListener('d2l-form-element-errors-change', this._onFormElementErrorsChange);
161
+ this.shadowRoot.addEventListener('d2l-validation-custom-connected', this.#validationCustomConnected);
162
+ this.shadowRoot.addEventListener('d2l-form-element-errors-change', this.#onFormElementErrorsChange);
165
163
  }
166
164
 
167
165
  disconnectedCallback() {
168
166
  super.disconnectedCallback();
169
- this.shadowRoot.removeEventListener('d2l-validation-custom-connected', this._validationCustomConnected);
170
- this.shadowRoot.removeEventListener('d2l-form-element-errors-change', this._onFormElementErrorsChange);
167
+ this.shadowRoot.removeEventListener('d2l-validation-custom-connected', this.#validationCustomConnected);
168
+ this.shadowRoot.removeEventListener('d2l-form-element-errors-change', this.#onFormElementErrorsChange);
171
169
  }
172
170
 
173
171
  firstUpdated(changedProperties) {
@@ -261,7 +259,7 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
261
259
  this._validationCustoms.delete(custom);
262
260
  }
263
261
 
264
- _onFormElementErrorsChange(e) {
262
+ #onFormElementErrorsChange = (e) => {
265
263
  e.stopPropagation();
266
264
  const errors = e.detail.errors;
267
265
  if (errors.length === 0) {
@@ -273,9 +271,9 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
273
271
  this.childErrors.set(e.target, errors);
274
272
  this.requestUpdate('childErrors');
275
273
  }
276
- }
274
+ };
277
275
 
278
- _validationCustomConnected(e) {
276
+ #validationCustomConnected = (e) => {
279
277
  e.stopPropagation();
280
278
  const custom = e.composedPath()[0];
281
279
  this.validationCustomConnected(custom);
@@ -285,6 +283,6 @@ export const FormElementMixin = superclass => class extends LocalizeCoreElement(
285
283
  this.validationCustomDisconnected(custom);
286
284
  };
287
285
  custom.addEventListener('d2l-validation-custom-disconnected', onDisconnect);
288
- }
286
+ };
289
287
 
290
288
  };
@@ -4,7 +4,6 @@ import '../link/link.js';
4
4
  import { css, html, LitElement } from 'lit';
5
5
  import { findFormElements, flattenMap, getFormElementData, isCustomFormElement, isNativeFormElement } from './form-helper.js';
6
6
  import { findComposedAncestor } from '../../helpers/dom.js';
7
- import { getComposedActiveElement } from '../../helpers/focus.js';
8
7
  import { getUniqueId } from '../../helpers/uniqueId.js';
9
8
  import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
10
9
  import { localizeFormElement } from './form-element-localize-helper.js';
@@ -66,9 +65,6 @@ class Form extends LocalizeCoreElement(LitElement) {
66
65
  this._tooltips = new Map();
67
66
  this._validationCustoms = new Set();
68
67
 
69
- this._onUnload = this._onUnload.bind(this);
70
- this._onNativeSubmit = this._onNativeSubmit.bind(this);
71
-
72
68
  /** @ignore */
73
69
  this.addEventListener('d2l-form-connect', this._onFormConnect);
74
70
  this.addEventListener('d2l-form-errors-change', this._onErrorsChange);
@@ -84,14 +80,14 @@ class Form extends LocalizeCoreElement(LitElement) {
84
80
 
85
81
  connectedCallback() {
86
82
  super.connectedCallback();
87
- window.addEventListener('beforeunload', this._onUnload);
83
+ window.addEventListener('beforeunload', this.#onUnload);
88
84
  /** @ignore */
89
85
  this._isSubForm = !this.dispatchEvent(new CustomEvent('d2l-form-connect', { bubbles: true, composed: true, cancelable: true }));
90
86
  }
91
87
 
92
88
  disconnectedCallback() {
93
89
  super.disconnectedCallback();
94
- window.removeEventListener('beforeunload', this._onUnload);
90
+ window.removeEventListener('beforeunload', this.#onUnload);
95
91
  /** @ignore */
96
92
  this.dispatchEvent(new CustomEvent('d2l-form-disconnect'));
97
93
  this._isSubForm = false;
@@ -193,6 +189,13 @@ class Form extends LocalizeCoreElement(LitElement) {
193
189
  return flattenedErrorMap;
194
190
  }
195
191
 
192
+ #onUnload = (e) => {
193
+ if (this.trackChanges && this._dirty) {
194
+ e.preventDefault();
195
+ e.returnValue = false;
196
+ }
197
+ };
198
+
196
199
  _displayInvalid(ele, message) {
197
200
  let tooltip = this._tooltips.get(ele);
198
201
  if (!tooltip) {
@@ -296,20 +299,6 @@ class Form extends LocalizeCoreElement(LitElement) {
296
299
  this._updateErrors(ele, errors);
297
300
  }
298
301
 
299
- _onNativeSubmit(e) {
300
- e.preventDefault();
301
- e.stopPropagation();
302
- const submitter = e.submitter || getComposedActiveElement();
303
- this.requestSubmit(submitter);
304
- }
305
-
306
- _onUnload(e) {
307
- if (this.trackChanges && this._dirty) {
308
- e.preventDefault();
309
- e.returnValue = false;
310
- }
311
- }
312
-
313
302
  _setupDialogValidationReset() {
314
303
  const dialogAncestor = findComposedAncestor(
315
304
  this,
@@ -302,8 +302,6 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
302
302
  this._lastSlotWidth = 0;
303
303
  this._prevValue = '';
304
304
 
305
- this._handleBlur = this._handleBlur.bind(this);
306
- this._handleFocus = this._handleFocus.bind(this);
307
305
  this._perfMonitor = new PerfMonitor(this);
308
306
  }
309
307
 
@@ -369,8 +367,8 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
369
367
  if (this._intersectionObserver) this._intersectionObserver.disconnect();
370
368
  const container = this.shadowRoot && this.shadowRoot.querySelector('.d2l-input-text-container');
371
369
  if (!container) return;
372
- container.removeEventListener('blur', this._handleBlur, true);
373
- container.removeEventListener('focus', this._handleFocus, true);
370
+ container.removeEventListener('blur', this.#handleBlur, true);
371
+ container.removeEventListener('focus', this.#handleFocus, true);
374
372
  }
375
373
 
376
374
  firstUpdated(changedProperties) {
@@ -380,8 +378,8 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
380
378
 
381
379
  const container = this.shadowRoot && this.shadowRoot.querySelector('.d2l-input-text-container');
382
380
  if (!container) return;
383
- container.addEventListener('blur', this._handleBlur, true);
384
- container.addEventListener('focus', this._handleFocus, true);
381
+ container.addEventListener('blur', this.#handleBlur, true);
382
+ container.addEventListener('focus', this.#handleFocus, true);
385
383
 
386
384
  // if initially hidden then update layout when it becomes visible
387
385
  if (typeof(IntersectionObserver) === 'function') {
@@ -534,6 +532,24 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
534
532
  });
535
533
  }
536
534
 
535
+ #handleBlur = async(e) => {
536
+ this._focused = false;
537
+ this.requestValidate(true);
538
+
539
+ /**
540
+ * This is needed only for Legacy-Edge
541
+ * the _handleChange function is NOT triggered, therefore we have to detect the blur and handle it ourselves.
542
+ */
543
+ const browserType = window.navigator.userAgent;
544
+ if (this._prevValue !== e.target.value && (browserType.indexOf('Edge') > -1)) {
545
+ this._handleChange();
546
+ }
547
+ };
548
+
549
+ #handleFocus = () => {
550
+ this._focused = true;
551
+ };
552
+
537
553
  _getAriaLabel() {
538
554
  let label;
539
555
  if (this.label && (this.labelHidden || this.labelledBy)) {
@@ -565,20 +581,6 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
565
581
  this._hasAfterContent = (afterContent && afterContent.length > 0);
566
582
  }
567
583
 
568
- async _handleBlur(e) {
569
- this._focused = false;
570
- this.requestValidate(true);
571
-
572
- /**
573
- * This is needed only for Legacy-Edge
574
- * the _handleChange function is NOT triggered, therefore we have to detect the blur and handle it ourselves.
575
- */
576
- const browserType = window.navigator.userAgent;
577
- if (this._prevValue !== e.target.value && (browserType.indexOf('Edge') > -1)) {
578
- this._handleChange();
579
- }
580
- }
581
-
582
584
  _handleChange() {
583
585
  // Change events aren't composed, so we need to re-dispatch
584
586
  this.dispatchEvent(new CustomEvent(
@@ -587,10 +589,6 @@ class InputText extends InputInlineHelpMixin(PropertyRequiredMixin(FocusMixin(La
587
589
  ));
588
590
  }
589
591
 
590
- _handleFocus() {
591
- this._focused = true;
592
- }
593
-
594
592
  _handleInput(e) {
595
593
  this._setValue(e.target.value, false);
596
594
  return true;
@@ -83,10 +83,8 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
83
83
  constructor() {
84
84
  super();
85
85
 
86
- this._handleItemMutation = this._handleItemMutation.bind(this);
87
- this._handleResize = this._handleResize.bind(this);
88
- this._itemObserver = new MutationObserver(this._handleItemMutation);
89
- this._resizeObserver = new ResizeObserver((entries) => requestAnimationFrame(() => this._handleResize(entries)));
86
+ this._itemObserver = new MutationObserver(this.#handleItemMutation);
87
+ this._resizeObserver = new ResizeObserver((entries) => requestAnimationFrame(() => this.#handleResize(entries)));
90
88
 
91
89
  this._hasResized = false;
92
90
  this._isObservingResize = false;
@@ -183,6 +181,46 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
183
181
  throw new Error('OverflowGroupMixin.getOverflowContainer must be overridden');
184
182
  }
185
183
 
184
+ #handleItemMutation = (mutations) => {
185
+ if (!mutations || mutations.length === 0) return;
186
+ if (this._updateOverflowItemsRequested) return;
187
+
188
+ let isWidthModifyingMutation = false;
189
+ for (const mutation of mutations) {
190
+ if (mutation.attributeName
191
+ && (mutation.attributeName === 'selected' || mutation.attributeName === 'text')
192
+ ) {
193
+ isWidthModifyingMutation = true;
194
+ break;
195
+ }
196
+ }
197
+
198
+ this._updateOverflowItemsRequested = true;
199
+ setTimeout(() => {
200
+ this._overflowItems = this._slotItems.map((node) => this.convertToOverflowItem(node));
201
+
202
+ // when certain attributes change the corresponding item width can also change and so we need to re-get the layouts and chomp
203
+ if (isWidthModifyingMutation) {
204
+ this._itemLayouts = this._getItemLayouts(this._slotItems);
205
+ this._chomp();
206
+ }
207
+ this._updateOverflowItemsRequested = false;
208
+ this.requestUpdate();
209
+ }, 0);
210
+ };
211
+
212
+ #handleResize = async(entries) => {
213
+ await (document.fonts ? document.fonts.ready : Promise.resolve()); // computed widths can be incorrect if we don't wait for fonts to load
214
+ this._availableWidth = Math.ceil(entries[0].contentRect.width);
215
+
216
+ if (!this._hasResized) {
217
+ this._hasResized = true;
218
+ this._handleSlotChange();
219
+ } else {
220
+ this._chomp();
221
+ }
222
+ };
223
+
186
224
  _autoDetectBoundaries(items) {
187
225
  if (!items) return;
188
226
 
@@ -335,46 +373,6 @@ export const OverflowGroupMixin = superclass => class extends LocalizeCoreElemen
335
373
  return filteredNodes;
336
374
  }
337
375
 
338
- _handleItemMutation(mutations) {
339
- if (!mutations || mutations.length === 0) return;
340
- if (this._updateOverflowItemsRequested) return;
341
-
342
- let isWidthModifyingMutation = false;
343
- for (const mutation of mutations) {
344
- if (mutation.attributeName
345
- && (mutation.attributeName === 'selected' || mutation.attributeName === 'text')
346
- ) {
347
- isWidthModifyingMutation = true;
348
- break;
349
- }
350
- }
351
-
352
- this._updateOverflowItemsRequested = true;
353
- setTimeout(() => {
354
- this._overflowItems = this._slotItems.map((node) => this.convertToOverflowItem(node));
355
-
356
- // when certain attributes change the corresponding item width can also change and so we need to re-get the layouts and chomp
357
- if (isWidthModifyingMutation) {
358
- this._itemLayouts = this._getItemLayouts(this._slotItems);
359
- this._chomp();
360
- }
361
- this._updateOverflowItemsRequested = false;
362
- this.requestUpdate();
363
- }, 0);
364
- }
365
-
366
- async _handleResize(entries) {
367
- await (document.fonts ? document.fonts.ready : Promise.resolve()); // computed widths can be incorrect if we don't wait for fonts to load
368
- this._availableWidth = Math.ceil(entries[0].contentRect.width);
369
-
370
- if (!this._hasResized) {
371
- this._hasResized = true;
372
- this._handleSlotChange();
373
- } else {
374
- this._chomp();
375
- }
376
- }
377
-
378
376
  _handleSlotChange() {
379
377
  if (!this._hasResized) return;
380
378
  requestAnimationFrame(async() => {
@@ -111,11 +111,15 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
111
111
  }
112
112
 
113
113
  .content {
114
+ box-sizing: border-box;
114
115
  display: flex;
115
116
  margin-inline: var(--d2l-page-margin-inline, 0);
116
117
  max-width: var(--d2l-page-content-max-width, 100%);
117
118
  padding-bottom: var(--d2l-page-footer-height, 0); /* Reserve space for fixed footer */
118
119
  }
120
+ .content.has-panels {
121
+ min-height: calc(100vh - var(--d2l-page-header-height-measured, 0px));
122
+ }
119
123
 
120
124
  main {
121
125
  flex: 1;
@@ -124,7 +128,7 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
124
128
 
125
129
  .side-nav-panel,
126
130
  .supporting-panel {
127
- height: calc(100vh - var(--d2l-page-header-height, 0) - var(--d2l-page-footer-height, 0));
131
+ max-height: calc(100vh - var(--d2l-page-header-height, 0) - var(--d2l-page-footer-height, 0));
128
132
  overflow: clip auto;
129
133
  position: sticky;
130
134
  top: var(--d2l-page-header-height, 0);
@@ -162,11 +166,11 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
162
166
  'supporting-mobile': { minSize: DRAWER_MIN_HEIGHT }
163
167
  });
164
168
  this._slotVisibility = {};
165
- this.#handleWindowResizeBound = this.#handleWindowResize.bind(this);
166
169
  this.#resizeObserver = new ResizeObserver(entries => {
167
170
  for (const entry of entries) {
168
171
  if (entry.target.classList.contains('header')) {
169
172
  this._headerHeight = entry.target.offsetHeight;
173
+ this.style.setProperty('--d2l-page-header-height-measured', `${this._headerHeight}px`);
170
174
 
171
175
  const height = this._headerIsSticky ? this._headerHeight : 0;
172
176
  this.style.setProperty('--d2l-page-header-height', `${height}px`);
@@ -194,13 +198,13 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
194
198
 
195
199
  connectedCallback() {
196
200
  super.connectedCallback();
197
- window.addEventListener('resize', this.#handleWindowResizeBound);
201
+ window.addEventListener('resize', this.#handleWindowResize);
198
202
  }
199
203
 
200
204
  disconnectedCallback() {
201
205
  super.disconnectedCallback();
202
206
  this.#resizeObserver.disconnect();
203
- window.removeEventListener('resize', this.#handleWindowResizeBound);
207
+ window.removeEventListener('resize', this.#handleWindowResize);
204
208
  }
205
209
 
206
210
  firstUpdated() {
@@ -217,11 +221,15 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
217
221
  'page': true,
218
222
  'header-sticky': this._headerIsSticky
219
223
  };
224
+ const contentClasses = {
225
+ 'content': true,
226
+ 'has-panels': this._slotVisibility['side-nav'] || this._slotVisibility['supporting']
227
+ };
220
228
 
221
229
  return html`
222
230
  <div class="${classMap(pageClasses)}">
223
231
  ${this.#renderHeader()}
224
- <div class="content">
232
+ <div class="${classMap(contentClasses)}">
225
233
  ${this.#renderSideNavPanel()}
226
234
  <main><slot></slot></main>
227
235
  ${this.#renderSupportingPanel()}
@@ -238,9 +246,12 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
238
246
  }
239
247
  }
240
248
 
241
- #handleWindowResizeBound;
242
249
  #resizeObserver;
243
250
 
251
+ #handleWindowResize = () => {
252
+ this._panelState.updateMaxSize('supporting-mobile', this.#getMaxDrawerHeight());
253
+ };
254
+
244
255
  #getMaxDrawerHeight() {
245
256
  const reservedSpace = this._headerHeight + this._footerHeight + DIVIDER_WIDTH;
246
257
  return Math.max(DRAWER_MIN_HEIGHT, window.innerHeight - reservedSpace);
@@ -266,10 +277,6 @@ class Page extends ProviderMixin(LocalizeCoreElement(LitElement)) {
266
277
  this._slotVisibility = { ...this._slotVisibility, [key]: nodes.length !== 0 };
267
278
  }
268
279
 
269
- #handleWindowResize() {
270
- this._panelState.updateMaxSize('supporting-mobile', this.#getMaxDrawerHeight());
271
- };
272
-
273
280
  #initializePanelSizes() {
274
281
  const defaultWidth = Math.floor(this._contentWidth / 3);
275
282
  const defaultHeight = Math.floor(window.innerHeight / 2);
@@ -168,9 +168,7 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
168
168
  this._scrollbarRight = false;
169
169
  this._syncDriver = null;
170
170
  this._syncDriverTimeout = null;
171
- this._checkScrollThresholds = this._checkScrollThresholds.bind(this);
172
171
 
173
- this._synchronizeScroll = this._synchronizeScroll.bind(this);
174
172
  }
175
173
 
176
174
  disconnectedCallback() {
@@ -218,7 +216,7 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
218
216
  checkScrollbar() {
219
217
  if (!this._container) return;
220
218
  this._hScrollbar = this._container.offsetWidth !== this._container.scrollWidth;
221
- this._checkScrollThresholds();
219
+ this.#checkScrollThresholds();
222
220
  }
223
221
 
224
222
  notifyResize() {
@@ -237,13 +235,24 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
237
235
  }
238
236
  }
239
237
 
240
- _checkScrollThresholds() {
238
+ #checkScrollThresholds = () => {
241
239
  if (!this._container) return;
242
240
  const lowerScrollValue = this._container.scrollWidth - this._baseContainer.offsetWidth - Math.abs(this._container.scrollLeft);
243
241
  this._scrollbarLeft = (this._container.scrollLeft === 0);
244
242
  this._scrollbarRight = (lowerScrollValue <= 0);
245
243
 
246
- }
244
+ };
245
+
246
+ #synchronizeScroll = (e) => {
247
+ if (this._syncDriver && e.target !== this._syncDriver) return;
248
+ if (this._syncDriverTimeout) clearTimeout(this._syncDriverTimeout);
249
+
250
+ this._syncDriver = e.target;
251
+ this._allScrollers.forEach(element => {
252
+ if (element && element !== e.target) element.scrollLeft = e.target.scrollLeft;
253
+ });
254
+ this._syncDriverTimeout = setTimeout(() => this._syncDriver = null, 100);
255
+ };
247
256
 
248
257
  _disconnectAll() {
249
258
  this._resizeObserver?.disconnect();
@@ -252,11 +261,11 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
252
261
  this._container.style.removeProperty('overflow-x');
253
262
  this._container.classList.remove('d2l-scroll-wrapper-focus');
254
263
  this._container.removeAttribute('tabindex');
255
- this._container.removeEventListener('scroll', this._synchronizeScroll);
256
- this._container.removeEventListener('scroll', this._checkScrollThresholds);
264
+ this._container.removeEventListener('scroll', this.#synchronizeScroll);
265
+ this._container.removeEventListener('scroll', this.#checkScrollThresholds);
257
266
  this._secondaryScrollers.forEach(element => {
258
267
  element.style.removeProperty('overflow-x');
259
- element.removeEventListener('scroll', this._synchronizeScroll);
268
+ element.removeEventListener('scroll', this.#synchronizeScroll);
260
269
  });
261
270
  }
262
271
  }
@@ -276,17 +285,6 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
276
285
  this.scrollDistance(scrollDistance, true);
277
286
  }
278
287
 
279
- _synchronizeScroll(e) {
280
- if (this._syncDriver && e.target !== this._syncDriver) return;
281
- if (this._syncDriverTimeout) clearTimeout(this._syncDriverTimeout);
282
-
283
- this._syncDriver = e.target;
284
- this._allScrollers.forEach(element => {
285
- if (element && element !== e.target) element.scrollLeft = e.target.scrollLeft;
286
- });
287
- this._syncDriverTimeout = setTimeout(() => this._syncDriver = null, 100);
288
- }
289
-
290
288
  _updateScrollTargets() {
291
289
  this._disconnectAll();
292
290
 
@@ -307,17 +305,17 @@ class ScrollWrapper extends LocalizeCoreElement(LitElement) {
307
305
  }
308
306
  this._container.style.overflowX = 'auto';
309
307
  this._resizeObserver.observe(this._container);
310
- this._container.addEventListener('scroll', this._checkScrollThresholds);
308
+ this._container.addEventListener('scroll', this.#checkScrollThresholds);
311
309
  this._updateTabIndex();
312
310
  }
313
311
 
314
312
  if (this._secondaryScrollers.length) {
315
313
  this._secondaryScrollers.forEach(element => {
316
314
  element.style.overflowX = 'hidden';
317
- element.addEventListener('scroll', this._synchronizeScroll);
315
+ element.addEventListener('scroll', this.#synchronizeScroll);
318
316
  });
319
- this._container.addEventListener('scroll', this._synchronizeScroll);
320
- this._synchronizeScroll({ target: this._container });
317
+ this._container.addEventListener('scroll', this.#synchronizeScroll);
318
+ this.#synchronizeScroll({ target: this._container });
321
319
  }
322
320
  }
323
321