@brightspace-ui/core 3.317.0 → 3.317.2
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/dialog-mixin.js +4 -0
- package/components/tabs/tabs.js +314 -308
- package/package.json +1 -1
|
@@ -116,6 +116,7 @@ export const DialogMixin = superclass => class extends superclass {
|
|
|
116
116
|
const ifrauDialogService = await tryGetIfrauBackdropService();
|
|
117
117
|
if (this.opened) {
|
|
118
118
|
if (ifrauDialogService) {
|
|
119
|
+
this.#ifrauDialogService = ifrauDialogService;
|
|
119
120
|
this._ifrauContextInfo = await ifrauDialogService.showBackdrop();
|
|
120
121
|
this._inIframe = true;
|
|
121
122
|
}
|
|
@@ -125,6 +126,7 @@ export const DialogMixin = superclass => class extends superclass {
|
|
|
125
126
|
ifrauDialogService.hideBackdrop();
|
|
126
127
|
this._ifrauContextInfo = null;
|
|
127
128
|
}
|
|
129
|
+
this.#ifrauDialogService = null;
|
|
128
130
|
this._close();
|
|
129
131
|
}
|
|
130
132
|
}
|
|
@@ -165,6 +167,7 @@ export const DialogMixin = superclass => class extends superclass {
|
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
#useNativeInitialized = false;
|
|
170
|
+
#ifrauDialogService = null;
|
|
168
171
|
|
|
169
172
|
#handleMvcDialogOpen = () => {
|
|
170
173
|
// native dialogs on top layer will be stacked on non-native dialogs regardless of z-index
|
|
@@ -182,6 +185,7 @@ export const DialogMixin = superclass => class extends superclass {
|
|
|
182
185
|
};
|
|
183
186
|
|
|
184
187
|
#updateSize = async() => {
|
|
188
|
+
if (this._inIframe && this.#ifrauDialogService) this._ifrauContextInfo = await this.#ifrauDialogService.getContextInfo();
|
|
185
189
|
if (this._autoSize) {
|
|
186
190
|
if (this._ifrauContextInfo) {
|
|
187
191
|
if (this._ifrauContextInfo.top > defaultMargin.top) {
|
package/components/tabs/tabs.js
CHANGED
|
@@ -217,13 +217,13 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
217
217
|
this.maxToShow = -1;
|
|
218
218
|
this._allowScrollNext = false;
|
|
219
219
|
this._allowScrollPrevious = false;
|
|
220
|
-
this
|
|
221
|
-
this
|
|
220
|
+
this.#loadingCompleteResolve = undefined;
|
|
221
|
+
this.#loadingCompletePromise = new Promise(resolve => this.#loadingCompleteResolve = resolve);
|
|
222
222
|
this._maxWidth = null;
|
|
223
223
|
this._scrollCollapsed = false;
|
|
224
224
|
this._state = 'shown';
|
|
225
|
-
this
|
|
226
|
-
this
|
|
225
|
+
this.#tabIds = {};
|
|
226
|
+
this.#tabs = [];
|
|
227
227
|
this._translationValue = 0;
|
|
228
228
|
}
|
|
229
229
|
|
|
@@ -231,7 +231,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
231
231
|
super.connectedCallback();
|
|
232
232
|
|
|
233
233
|
queueMicrotask(() => {
|
|
234
|
-
const bgColor = this
|
|
234
|
+
const bgColor = this.#getComputedBackgroundColor();
|
|
235
235
|
if (bgColor && bgColor !== 'rgb(255, 255, 255)' && bgColor !== 'rgba(255, 255, 255, 1)') {
|
|
236
236
|
this.style.setProperty('--d2l-tabs-background-color', bgColor);
|
|
237
237
|
}
|
|
@@ -241,27 +241,27 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
241
241
|
|
|
242
242
|
disconnectedCallback() {
|
|
243
243
|
super.disconnectedCallback();
|
|
244
|
-
if (this
|
|
244
|
+
if (this.#resizeObserver) this.#resizeObserver.disconnect();
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
firstUpdated(changedProperties) {
|
|
248
248
|
super.firstUpdated(changedProperties);
|
|
249
249
|
|
|
250
250
|
this.arrowKeysFocusablesProvider = async() => {
|
|
251
|
-
return this
|
|
251
|
+
return this.#tabs;
|
|
252
252
|
};
|
|
253
253
|
|
|
254
254
|
this.arrowKeysOnBeforeFocus = async(tab) => {
|
|
255
|
-
this
|
|
255
|
+
this.#setFocusable(tab);
|
|
256
256
|
|
|
257
257
|
this.requestUpdate();
|
|
258
258
|
await this.updateComplete;
|
|
259
259
|
|
|
260
260
|
if (!this._scrollCollapsed) {
|
|
261
|
-
return this
|
|
261
|
+
return this.#updateScrollPosition(tab);
|
|
262
262
|
} else {
|
|
263
|
-
const measures = this
|
|
264
|
-
const newTranslationValue = this
|
|
263
|
+
const measures = this.#getMeasures();
|
|
264
|
+
const newTranslationValue = this.#calculateScrollPosition(tab, measures);
|
|
265
265
|
|
|
266
266
|
if (!this.#isRTL()) {
|
|
267
267
|
if (newTranslationValue >= 0) return;
|
|
@@ -269,18 +269,17 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
269
269
|
if (newTranslationValue <= 0) return;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
-
const expanded = await this
|
|
272
|
+
const expanded = await this.#tryExpandTabsContainer(measures);
|
|
273
273
|
if (expanded) {
|
|
274
274
|
return;
|
|
275
275
|
} else {
|
|
276
|
-
return this
|
|
276
|
+
return this.#updateScrollPosition(tab);
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
};
|
|
280
280
|
|
|
281
|
-
this
|
|
282
|
-
this.
|
|
283
|
-
this._resizeObserver.observe(this.shadowRoot.querySelector('.d2l-tabs-container-list'));
|
|
281
|
+
this.#resizeObserver = new ResizeObserver(this.#handleResize);
|
|
282
|
+
this.#resizeObserver.observe(this.shadowRoot.querySelector('.d2l-tabs-container-list'));
|
|
284
283
|
|
|
285
284
|
}
|
|
286
285
|
|
|
@@ -313,27 +312,27 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
313
312
|
style="${styleMap(tabsContainerStyles)}">
|
|
314
313
|
<div class="d2l-tabs-scroll-previous-container">
|
|
315
314
|
<button class="d2l-tabs-scroll-button"
|
|
316
|
-
@click="${this
|
|
315
|
+
@click="${this.#handleScrollPrevious}"
|
|
317
316
|
title="${this.localize('components.tabs.previous')}">
|
|
318
317
|
<d2l-icon icon="tier1:chevron-left"></d2l-icon>
|
|
319
318
|
</button>
|
|
320
319
|
</div>
|
|
321
320
|
${this.arrowKeysContainer(html`
|
|
322
321
|
<div class="d2l-tabs-container-list"
|
|
323
|
-
@d2l-tab-content-change="${this
|
|
322
|
+
@d2l-tab-content-change="${this.#handleTabContentChange}"
|
|
324
323
|
@d2l-tab-hidden-change="${this.#handleTabHiddenChange}"
|
|
325
|
-
@d2l-tab-selected="${this
|
|
324
|
+
@d2l-tab-selected="${this.#handleTabSelected}"
|
|
326
325
|
@d2l-tab-deselected="${this.#handleTabDeselected}"
|
|
327
|
-
@focusout="${this
|
|
326
|
+
@focusout="${this.#handleFocusOut}"
|
|
328
327
|
aria-label="${ifDefined(this.text)}"
|
|
329
328
|
role="tablist"
|
|
330
329
|
style="${styleMap(tabsContainerListStyles)}">
|
|
331
|
-
<slot name="tabs" @slotchange="${this
|
|
330
|
+
<slot name="tabs" @slotchange="${this.#handleTabsSlotChange}"></slot>
|
|
332
331
|
</div>
|
|
333
332
|
`)}
|
|
334
333
|
<div class="d2l-tabs-scroll-next-container">
|
|
335
334
|
<button class="d2l-tabs-scroll-button"
|
|
336
|
-
@click="${this
|
|
335
|
+
@click="${this.#handleScrollNext}"
|
|
337
336
|
title="${this.localize('components.tabs.next')}">
|
|
338
337
|
<d2l-icon icon="tier1:chevron-right"></d2l-icon>
|
|
339
338
|
</button>
|
|
@@ -342,17 +341,17 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
342
341
|
<div class="d2l-tabs-container-ext"><slot name="ext"></slot></div>
|
|
343
342
|
</div>
|
|
344
343
|
<div class="${classMap(panelContainerClasses)}">
|
|
345
|
-
<slot name="panels" @slotchange="${this
|
|
344
|
+
<slot name="panels" @slotchange="${this.#handlePanelsSlotChange}"></slot>
|
|
346
345
|
</div>
|
|
347
346
|
`;
|
|
348
347
|
}
|
|
349
348
|
|
|
350
349
|
focus() {
|
|
351
|
-
return this
|
|
350
|
+
return this.#focusSelected();
|
|
352
351
|
}
|
|
353
352
|
|
|
354
353
|
async getLoadingComplete() {
|
|
355
|
-
return this
|
|
354
|
+
return this.#loadingCompletePromise;
|
|
356
355
|
}
|
|
357
356
|
|
|
358
357
|
getTabListRect() {
|
|
@@ -362,14 +361,28 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
362
361
|
|
|
363
362
|
hideTab(tab) {
|
|
364
363
|
tab.setAttribute('data-state', 'removing');
|
|
365
|
-
return (Object.keys(this
|
|
364
|
+
return (Object.keys(this.#tabIds).length > 1 && !reduceMotion) ? this.#animateTabRemoval(tab) : Promise.resolve();
|
|
366
365
|
}
|
|
367
366
|
|
|
368
367
|
#checkTabPanelMatchRequested;
|
|
368
|
+
#initialized;
|
|
369
|
+
#loadingCompletePromise;
|
|
370
|
+
#loadingCompleteResolve;
|
|
371
|
+
#measures;
|
|
369
372
|
#panels;
|
|
373
|
+
#resizeObserver;
|
|
374
|
+
#tabIds;
|
|
375
|
+
#tabs;
|
|
370
376
|
#updateAriaControlsRequested;
|
|
371
377
|
|
|
372
|
-
|
|
378
|
+
#handleResize = (entries) => {
|
|
379
|
+
const measures = this.#getMeasures();
|
|
380
|
+
if (entries.length === 1 && entries[0].contentRect.width === measures.tabsContainerListRect.width) return;
|
|
381
|
+
this.#updateMeasures();
|
|
382
|
+
this.#updateScrollVisibility(this.#getMeasures());
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
#animateTabAddition(tab) {
|
|
373
386
|
if (!tab || reduceMotion) {
|
|
374
387
|
return new Promise((resolve) => {
|
|
375
388
|
tab.setAttribute('data-state', '');
|
|
@@ -390,7 +403,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
390
403
|
});
|
|
391
404
|
}
|
|
392
405
|
|
|
393
|
-
|
|
406
|
+
#animateTabRemoval(tab) {
|
|
394
407
|
if (!tab || reduceMotion) return Promise.resolve();
|
|
395
408
|
|
|
396
409
|
return new Promise((resolve) => {
|
|
@@ -404,22 +417,120 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
404
417
|
});
|
|
405
418
|
}
|
|
406
419
|
|
|
407
|
-
|
|
408
|
-
const tabs = this
|
|
420
|
+
#calculateScrollPosition(selectedTab, measures) {
|
|
421
|
+
const tabs = this.#tabs;
|
|
409
422
|
const selectedTabIndex = tabs.indexOf(selectedTab);
|
|
410
423
|
return this.#calculateScrollPositionLogic(tabs, selectedTabIndex, measures);
|
|
411
424
|
}
|
|
412
425
|
|
|
413
|
-
|
|
414
|
-
|
|
426
|
+
#calculateScrollPositionLogic(tabsDataStructure, selectedTabIndex, measures) {
|
|
427
|
+
if (!measures.tabRects[selectedTabIndex]) return 0;
|
|
428
|
+
|
|
429
|
+
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
|
430
|
+
|
|
431
|
+
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
|
432
|
+
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
|
433
|
+
|
|
434
|
+
const isRTL = this.#isRTL();
|
|
435
|
+
|
|
436
|
+
let getNewTranslationValue;
|
|
437
|
+
if (!isRTL) {
|
|
438
|
+
getNewTranslationValue = () => {
|
|
439
|
+
if (selectedTabIndex === 0) {
|
|
440
|
+
// position selected tab at beginning
|
|
441
|
+
return 0;
|
|
442
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
|
443
|
+
// position selected tab at end
|
|
444
|
+
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
|
445
|
+
} else {
|
|
446
|
+
// position selected tab in middle
|
|
447
|
+
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
} else {
|
|
451
|
+
getNewTranslationValue = () => {
|
|
452
|
+
if (selectedTabIndex === 0) {
|
|
453
|
+
// position selected tab at beginning
|
|
454
|
+
return 0;
|
|
455
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
|
456
|
+
// position selected tab at end
|
|
457
|
+
return -1 * selectedTabMeasures.offsetLeft;
|
|
458
|
+
} else {
|
|
459
|
+
// position selected tab in middle
|
|
460
|
+
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
let newTranslationValue = this._translationValue;
|
|
466
|
+
if (isOverflowingLeft || isOverflowingRight) {
|
|
467
|
+
newTranslationValue = getNewTranslationValue();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
let expectedPosition;
|
|
471
|
+
|
|
472
|
+
// make sure the new position will not place selected tab behind left scroll button
|
|
473
|
+
if (!isRTL) {
|
|
474
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
|
475
|
+
if (newTranslationValue < 0 && this.#isPositionInLeftScrollArea(expectedPosition)) {
|
|
476
|
+
newTranslationValue = getNewTranslationValue();
|
|
477
|
+
}
|
|
478
|
+
} else {
|
|
479
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
|
480
|
+
if (newTranslationValue > 0 && this.#isPositionInRightScrollArea(expectedPosition, measures)) {
|
|
481
|
+
newTranslationValue = getNewTranslationValue();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!isRTL) {
|
|
486
|
+
// make sure there will not be any empty space between left side of container and first tab
|
|
487
|
+
if (newTranslationValue > 0) newTranslationValue = 0;
|
|
488
|
+
} else {
|
|
489
|
+
// make sure there will not be any empty space between right side of container and first tab
|
|
490
|
+
if (newTranslationValue < 0) newTranslationValue = 0;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// make sure the new position will not place selected tab behind the right scroll button
|
|
494
|
+
if (!isRTL) {
|
|
495
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
|
496
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this.#isPositionInRightScrollArea(expectedPosition, measures)) {
|
|
497
|
+
newTranslationValue = getNewTranslationValue();
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
|
501
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this.#isPositionInLeftScrollArea(expectedPosition)) {
|
|
502
|
+
newTranslationValue = getNewTranslationValue();
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return newTranslationValue;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
#checkTabPanelMatch() {
|
|
510
|
+
// debounce so only runs once when tabs/panels slots changing
|
|
511
|
+
if (this.#checkTabPanelMatchRequested) return;
|
|
512
|
+
|
|
513
|
+
this.#checkTabPanelMatchRequested = true;
|
|
514
|
+
setTimeout(() => {
|
|
515
|
+
if ((this.#tabs && !this.#panels) || (this.#panels && !this.#tabs)) {
|
|
516
|
+
console.warn('d2l-tabs: tabs and panels are not in sync');
|
|
517
|
+
} else if (this.#tabs.length !== this.#panels.length) {
|
|
518
|
+
console.warn('d2l-tabs: number of tabs and panels does not match');
|
|
519
|
+
}
|
|
520
|
+
this.#checkTabPanelMatchRequested = false;
|
|
521
|
+
}, 0);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async #focusSelected() {
|
|
525
|
+
const selectedTab = this.#tabs.find(ti => ti.selected);
|
|
415
526
|
if (!selectedTab) return;
|
|
416
527
|
|
|
417
|
-
await this
|
|
528
|
+
await this.#updateScrollPosition(selectedTab);
|
|
418
529
|
|
|
419
530
|
selectedTab.focus();
|
|
420
531
|
}
|
|
421
532
|
|
|
422
|
-
|
|
533
|
+
#getComputedBackgroundColor() {
|
|
423
534
|
let bgColor = null;
|
|
424
535
|
|
|
425
536
|
findComposedAncestor(this, (node) => {
|
|
@@ -433,40 +544,33 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
433
544
|
return bgColor;
|
|
434
545
|
}
|
|
435
546
|
|
|
436
|
-
|
|
437
|
-
if (!this
|
|
438
|
-
return this
|
|
547
|
+
#getMeasures() {
|
|
548
|
+
if (!this.#measures) this.#updateMeasures();
|
|
549
|
+
return this.#measures;
|
|
439
550
|
}
|
|
440
551
|
|
|
441
|
-
|
|
552
|
+
#getPanel(id) {
|
|
442
553
|
if (!this.#panels) return;
|
|
443
554
|
return this.#panels.find(panel => panel.labelledBy === id);
|
|
444
555
|
}
|
|
445
556
|
|
|
446
|
-
|
|
557
|
+
#handleFocusOut(e) {
|
|
447
558
|
if (e.relatedTarget && e.relatedTarget.role === 'tab') return;
|
|
448
|
-
this
|
|
559
|
+
this.#resetFocusables();
|
|
449
560
|
}
|
|
450
561
|
|
|
451
|
-
|
|
562
|
+
#handlePanelsSlotChange(e) {
|
|
452
563
|
this.#panels = e.target.assignedElements({ flatten: true }).filter((node) => node.role === 'tabpanel');
|
|
453
564
|
this.#checkTabPanelMatch();
|
|
454
565
|
this.#setAriaControls();
|
|
455
566
|
}
|
|
456
567
|
|
|
457
|
-
|
|
458
|
-
const measures = this._getMeasures();
|
|
459
|
-
if (entries.length === 1 && entries[0].contentRect.width === measures.tabsContainerListRect.width) return;
|
|
460
|
-
this._updateMeasures();
|
|
461
|
-
this._updateScrollVisibility(this._getMeasures());
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
async _handleScrollNext() {
|
|
568
|
+
async #handleScrollNext() {
|
|
465
569
|
|
|
466
|
-
const measures = this
|
|
570
|
+
const measures = this.#getMeasures();
|
|
467
571
|
|
|
468
|
-
const expanded = await this
|
|
469
|
-
const newMeasures = expanded ? this
|
|
572
|
+
const expanded = await this.#tryExpandTabsContainer(measures);
|
|
573
|
+
const newMeasures = expanded ? this.#getMeasures() : measures;
|
|
470
574
|
|
|
471
575
|
let newTranslationValue;
|
|
472
576
|
const lastTabMeasures = measures.tabRects[measures.tabRects.length - 1];
|
|
@@ -496,8 +600,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
496
600
|
|
|
497
601
|
}
|
|
498
602
|
|
|
499
|
-
await this
|
|
500
|
-
await this
|
|
603
|
+
await this.#scrollToPosition(newTranslationValue);
|
|
604
|
+
await this.#updateScrollVisibility(newMeasures);
|
|
501
605
|
|
|
502
606
|
if (!isOverflowingNext && this.shadowRoot) {
|
|
503
607
|
this.shadowRoot.querySelector('.d2l-tabs-scroll-previous-container button').focus();
|
|
@@ -505,12 +609,12 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
505
609
|
|
|
506
610
|
}
|
|
507
611
|
|
|
508
|
-
async
|
|
612
|
+
async #handleScrollPrevious() {
|
|
509
613
|
|
|
510
|
-
const measures = this
|
|
614
|
+
const measures = this.#getMeasures();
|
|
511
615
|
|
|
512
|
-
const expanded = await this
|
|
513
|
-
const newMeasures = expanded ? this
|
|
616
|
+
const expanded = await this.#tryExpandTabsContainer(measures);
|
|
617
|
+
const newMeasures = expanded ? this.#getMeasures() : measures;
|
|
514
618
|
|
|
515
619
|
let newTranslationValue;
|
|
516
620
|
let isOverflowingPrevious;
|
|
@@ -529,8 +633,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
529
633
|
|
|
530
634
|
}
|
|
531
635
|
|
|
532
|
-
await this
|
|
533
|
-
await this
|
|
636
|
+
await this.#scrollToPosition(newTranslationValue);
|
|
637
|
+
await this.#updateScrollVisibility(newMeasures);
|
|
534
638
|
|
|
535
639
|
if (!isOverflowingPrevious && this.shadowRoot) {
|
|
536
640
|
this.shadowRoot.querySelector('.d2l-tabs-scroll-next-container button').focus();
|
|
@@ -538,33 +642,50 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
538
642
|
|
|
539
643
|
}
|
|
540
644
|
|
|
541
|
-
async
|
|
542
|
-
this
|
|
543
|
-
await this
|
|
645
|
+
async #handleTabContentChange() {
|
|
646
|
+
this.#updateMeasures();
|
|
647
|
+
await this.#updateScrollVisibility(this.#getMeasures());
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
#handleTabDeselected(e) {
|
|
651
|
+
const panel = this.#getPanel(e.target.id);
|
|
652
|
+
if (panel) panel._selected = false;
|
|
544
653
|
}
|
|
545
654
|
|
|
546
|
-
|
|
655
|
+
#handleTabHiddenChange() {
|
|
656
|
+
if (!this.#tabs || this.#tabs.length <= 1) return;
|
|
657
|
+
|
|
658
|
+
let visibleTabCount = 0;
|
|
659
|
+
this.#tabs.forEach((tab) => {
|
|
660
|
+
if (!tab.hidden) visibleTabCount++;
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
if (visibleTabCount > 1 && this._state === 'hidden') this.#showTabsList();
|
|
664
|
+
else if (visibleTabCount <= 1 && this._state === 'shown') this.#hideTabsList();
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
async #handleTabSelected(e) {
|
|
547
668
|
const selectedTab = e.target;
|
|
548
669
|
this.#updateSelectedTab(selectedTab);
|
|
549
670
|
await this.updateComplete;
|
|
550
|
-
this
|
|
671
|
+
this.#updateScrollPosition(selectedTab);
|
|
551
672
|
}
|
|
552
673
|
|
|
553
|
-
async
|
|
554
|
-
this
|
|
674
|
+
async #handleTabsSlotChange(e) {
|
|
675
|
+
this.#tabs = e.target.assignedElements({ flatten: true }).filter((node) => node.role === 'tab');
|
|
555
676
|
|
|
556
677
|
// handle case where there are less than two tabs initially
|
|
557
|
-
this
|
|
678
|
+
this.#updateTabListVisibility(this.#tabs);
|
|
558
679
|
|
|
559
|
-
if (!this
|
|
680
|
+
if (!this.#initialized && this.#tabs.length === 0) return;
|
|
560
681
|
|
|
561
682
|
let selectedTab = null;
|
|
562
683
|
const newTabIds = {};
|
|
563
|
-
this
|
|
564
|
-
const isNew = this
|
|
565
|
-
if (isNew && !reduceMotion && this.
|
|
684
|
+
this.#tabs?.forEach((tab) => {
|
|
685
|
+
const isNew = this.#initialized && !this.#tabIds[tab.id];
|
|
686
|
+
if (isNew && !reduceMotion && this.#tabs.length !== Object.keys(this.#tabIds).length) {
|
|
566
687
|
// if it's a new tab, update state to animate addition
|
|
567
|
-
this
|
|
688
|
+
this.#tabIds[tab.id] = true;
|
|
568
689
|
tab.setAttribute('data-state', 'adding');
|
|
569
690
|
}
|
|
570
691
|
if (tab.selected && tab.getAttribute('data-state') !== 'removing') {
|
|
@@ -574,10 +695,10 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
574
695
|
newTabIds[tab.id] = true;
|
|
575
696
|
});
|
|
576
697
|
|
|
577
|
-
this
|
|
698
|
+
this.#tabIds = newTabIds;
|
|
578
699
|
|
|
579
700
|
if (!selectedTab) {
|
|
580
|
-
selectedTab = this.
|
|
701
|
+
selectedTab = this.#tabs.find((tab) => tab.getAttribute('data-state') !== 'removing');
|
|
581
702
|
if (selectedTab) selectedTab.selected = true;
|
|
582
703
|
}
|
|
583
704
|
if (selectedTab) {
|
|
@@ -590,42 +711,62 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
590
711
|
|
|
591
712
|
const animPromises = [];
|
|
592
713
|
|
|
593
|
-
if (!this
|
|
594
|
-
this
|
|
595
|
-
await this
|
|
714
|
+
if (!this.#initialized && this.#tabs.length > 0) {
|
|
715
|
+
this.#initialized = true;
|
|
716
|
+
await this.#updateTabsContainerWidth(selectedTab);
|
|
596
717
|
} else {
|
|
597
|
-
if (this.
|
|
598
|
-
this.
|
|
599
|
-
if (tab.getAttribute('data-state') === 'adding') animPromises.push(this
|
|
718
|
+
if (this.#tabs.length > 1) {
|
|
719
|
+
this.#tabs.forEach((tab) => {
|
|
720
|
+
if (tab.getAttribute('data-state') === 'adding') animPromises.push(this.#animateTabAddition(tab));
|
|
600
721
|
});
|
|
601
722
|
}
|
|
602
|
-
this
|
|
723
|
+
this.#updateMeasures();
|
|
603
724
|
}
|
|
604
725
|
|
|
605
726
|
if (selectedTab) {
|
|
606
727
|
Promise.all(animPromises).then(async() => {
|
|
607
728
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
608
|
-
this
|
|
609
|
-
this
|
|
729
|
+
this.#updateMeasures();
|
|
730
|
+
this.#updateScrollPosition(selectedTab);
|
|
610
731
|
});
|
|
611
732
|
}
|
|
612
733
|
}
|
|
613
734
|
|
|
614
|
-
|
|
735
|
+
#hideTabsList() {
|
|
736
|
+
// don't animate the tabs list visibility if it's the inital render
|
|
737
|
+
if (reduceMotion || !this.#initialized || !isVisible(this)) {
|
|
738
|
+
this._state = 'hidden';
|
|
739
|
+
} else if (this.shadowRoot) {
|
|
740
|
+
const layout = this.shadowRoot.querySelector('.d2l-tabs-layout');
|
|
741
|
+
const handleTransitionEnd = (e) => {
|
|
742
|
+
if (e.propertyName !== 'max-height') return;
|
|
743
|
+
layout.removeEventListener('transitionend', handleTransitionEnd);
|
|
744
|
+
this._state = 'hidden';
|
|
745
|
+
};
|
|
746
|
+
layout.addEventListener('transitionend', handleTransitionEnd);
|
|
747
|
+
this._state = 'anim';
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
#isPositionInLeftScrollArea(position) {
|
|
615
752
|
return position > 0 && position < scrollButtonWidth;
|
|
616
753
|
}
|
|
617
754
|
|
|
618
|
-
|
|
755
|
+
#isPositionInRightScrollArea(position, measures) {
|
|
619
756
|
return (position > measures.tabsContainerRect.width - scrollButtonWidth) && (position < measures.tabsContainerRect.width);
|
|
620
757
|
}
|
|
621
758
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
759
|
+
#isRTL() {
|
|
760
|
+
return document.documentElement.getAttribute('dir') === 'rtl';
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
#resetFocusables() {
|
|
764
|
+
const selectedTab = this.#tabs.find(ti => ti.selected);
|
|
765
|
+
if (selectedTab) this.#setFocusable(selectedTab);
|
|
625
766
|
this.requestUpdate();
|
|
626
767
|
}
|
|
627
768
|
|
|
628
|
-
|
|
769
|
+
#scrollToPosition(translationValue) {
|
|
629
770
|
if (translationValue === this._translationValue) {
|
|
630
771
|
return Promise.resolve();
|
|
631
772
|
}
|
|
@@ -646,14 +787,44 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
646
787
|
});
|
|
647
788
|
}
|
|
648
789
|
|
|
649
|
-
|
|
650
|
-
|
|
790
|
+
#setAriaControls() {
|
|
791
|
+
// debounce so only runs once when tabs/panels slots changing
|
|
792
|
+
if (this.#updateAriaControlsRequested) return;
|
|
793
|
+
|
|
794
|
+
this.#updateAriaControlsRequested = true;
|
|
795
|
+
setTimeout(() => {
|
|
796
|
+
this.#tabs?.forEach((tab) => {
|
|
797
|
+
const panel = this.#getPanel(tab.id);
|
|
798
|
+
if (!panel) {
|
|
799
|
+
console.warn('d2l-tabs: tab without matching panel');
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
tab.setAttribute('aria-controls', `${panel.id}`);
|
|
803
|
+
});
|
|
804
|
+
this.#updateAriaControlsRequested = false;
|
|
805
|
+
}, 0);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
#setFocusable(tab) {
|
|
809
|
+
const currentFocusable = this.#tabs.find(tab => tab.tabIndex === 0);
|
|
651
810
|
if (currentFocusable) currentFocusable.tabIndex = -1;
|
|
652
811
|
|
|
653
812
|
tab.tabIndex = 0;
|
|
654
813
|
}
|
|
655
814
|
|
|
656
|
-
|
|
815
|
+
#showTabsList() {
|
|
816
|
+
// don't animate the tabs list visibility if it's the inital render
|
|
817
|
+
if (reduceMotion || !this.#initialized) {
|
|
818
|
+
this._state = 'shown';
|
|
819
|
+
} else {
|
|
820
|
+
this._state = 'anim';
|
|
821
|
+
requestAnimationFrame(() => {
|
|
822
|
+
this._state = 'shown';
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async #tryExpandTabsContainer(measures) {
|
|
657
828
|
|
|
658
829
|
if (!this._scrollCollapsed) return false;
|
|
659
830
|
|
|
@@ -680,14 +851,14 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
680
851
|
|
|
681
852
|
await expandedPromise;
|
|
682
853
|
|
|
683
|
-
this
|
|
854
|
+
this.#measures = null;
|
|
684
855
|
|
|
685
|
-
await this
|
|
856
|
+
await this.#updateScrollVisibility(this.#getMeasures());
|
|
686
857
|
this._maxWidth = null;
|
|
687
858
|
|
|
688
859
|
if (!this._allowScrollNext) {
|
|
689
860
|
if (!this._allowScrollPrevious) {
|
|
690
|
-
this
|
|
861
|
+
this.#focusSelected();
|
|
691
862
|
} else {
|
|
692
863
|
if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-tabs-scroll-previous-container button').focus();
|
|
693
864
|
}
|
|
@@ -697,10 +868,10 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
697
868
|
return true;
|
|
698
869
|
}
|
|
699
870
|
|
|
700
|
-
|
|
871
|
+
#updateMeasures() {
|
|
701
872
|
let totalTabsWidth = 0;
|
|
702
873
|
if (!this.shadowRoot) return;
|
|
703
|
-
const tabs = this
|
|
874
|
+
const tabs = this.#tabs;
|
|
704
875
|
|
|
705
876
|
const tabRects = tabs.map((tab) => {
|
|
706
877
|
const tabRect = tab.getBoundingClientRect();
|
|
@@ -714,7 +885,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
714
885
|
return measures;
|
|
715
886
|
});
|
|
716
887
|
|
|
717
|
-
this
|
|
888
|
+
this.#measures = {
|
|
718
889
|
tabsContainerRect: this.shadowRoot.querySelector('.d2l-tabs-container').getBoundingClientRect(),
|
|
719
890
|
tabsContainerListRect: this.shadowRoot.querySelector('.d2l-tabs-container-list').getBoundingClientRect(),
|
|
720
891
|
tabRects: tabRects,
|
|
@@ -722,13 +893,29 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
722
893
|
};
|
|
723
894
|
}
|
|
724
895
|
|
|
725
|
-
|
|
726
|
-
const measures = this
|
|
727
|
-
const newTranslationValue = this
|
|
896
|
+
#updateScrollPosition(selectedTab) {
|
|
897
|
+
const measures = this.#getMeasures();
|
|
898
|
+
const newTranslationValue = this.#calculateScrollPosition(selectedTab, measures);
|
|
728
899
|
return this.#updateScrollPositionLogic(measures, newTranslationValue);
|
|
729
900
|
}
|
|
730
901
|
|
|
731
|
-
|
|
902
|
+
#updateScrollPositionLogic(measures, newTranslationValue) {
|
|
903
|
+
const scrollToPromise = this.#scrollToPosition(newTranslationValue);
|
|
904
|
+
const scrollVisibilityPromise = this.#updateScrollVisibility(measures);
|
|
905
|
+
const p = Promise.all([
|
|
906
|
+
scrollVisibilityPromise,
|
|
907
|
+
scrollToPromise
|
|
908
|
+
]);
|
|
909
|
+
p.then(() => {
|
|
910
|
+
if (this.#loadingCompleteResolve) {
|
|
911
|
+
this.#loadingCompleteResolve();
|
|
912
|
+
this.#loadingCompleteResolve = undefined;
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
return p;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
#updateScrollVisibility(measures) {
|
|
732
919
|
|
|
733
920
|
const lastTabMeasures = measures.tabRects[measures.tabRects.length - 1];
|
|
734
921
|
if (!lastTabMeasures) {
|
|
@@ -748,219 +935,19 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
748
935
|
return this.updateComplete;
|
|
749
936
|
}
|
|
750
937
|
|
|
751
|
-
_updateTabListVisibility(tabs) {
|
|
752
|
-
const visibleCount = tabs.filter(tab => !tab.hidden).length;
|
|
753
|
-
if (this._state === 'shown' && visibleCount < 2) {
|
|
754
|
-
this.#hideTabsList();
|
|
755
|
-
} else if (this._state === 'hidden' && visibleCount > 1) {
|
|
756
|
-
this.#showTabsList();
|
|
757
|
-
} else if (this._state === 'shown' && visibleCount > 1) {
|
|
758
|
-
// check if there are hidden tabs and tab list container should actually be hidden
|
|
759
|
-
this.#handleTabHiddenChange();
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
_updateTabsContainerWidth(selectedTab) {
|
|
764
|
-
const tabs = this._tabs;
|
|
765
|
-
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= tabs.length) return;
|
|
766
|
-
if (tabs.indexOf(selectedTab) > this.maxToShow - 1) return;
|
|
767
|
-
return this.#updateTabsContainerWidthLogic();
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
#calculateScrollPositionLogic(tabsDataStructure, selectedTabIndex, measures) {
|
|
771
|
-
if (!measures.tabRects[selectedTabIndex]) return 0;
|
|
772
|
-
|
|
773
|
-
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
|
774
|
-
|
|
775
|
-
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
|
776
|
-
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
|
777
|
-
|
|
778
|
-
const isRTL = this.#isRTL();
|
|
779
|
-
|
|
780
|
-
let getNewTranslationValue;
|
|
781
|
-
if (!isRTL) {
|
|
782
|
-
getNewTranslationValue = () => {
|
|
783
|
-
if (selectedTabIndex === 0) {
|
|
784
|
-
// position selected tab at beginning
|
|
785
|
-
return 0;
|
|
786
|
-
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
|
787
|
-
// position selected tab at end
|
|
788
|
-
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
|
789
|
-
} else {
|
|
790
|
-
// position selected tab in middle
|
|
791
|
-
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
} else {
|
|
795
|
-
getNewTranslationValue = () => {
|
|
796
|
-
if (selectedTabIndex === 0) {
|
|
797
|
-
// position selected tab at beginning
|
|
798
|
-
return 0;
|
|
799
|
-
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
|
800
|
-
// position selected tab at end
|
|
801
|
-
return -1 * selectedTabMeasures.offsetLeft;
|
|
802
|
-
} else {
|
|
803
|
-
// position selected tab in middle
|
|
804
|
-
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
let newTranslationValue = this._translationValue;
|
|
810
|
-
if (isOverflowingLeft || isOverflowingRight) {
|
|
811
|
-
newTranslationValue = getNewTranslationValue();
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
let expectedPosition;
|
|
815
|
-
|
|
816
|
-
// make sure the new position will not place selected tab behind left scroll button
|
|
817
|
-
if (!isRTL) {
|
|
818
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
|
819
|
-
if (newTranslationValue < 0 && this._isPositionInLeftScrollArea(expectedPosition)) {
|
|
820
|
-
newTranslationValue = getNewTranslationValue();
|
|
821
|
-
}
|
|
822
|
-
} else {
|
|
823
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
|
824
|
-
if (newTranslationValue > 0 && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
|
825
|
-
newTranslationValue = getNewTranslationValue();
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
if (!isRTL) {
|
|
830
|
-
// make sure there will not be any empty space between left side of container and first tab
|
|
831
|
-
if (newTranslationValue > 0) newTranslationValue = 0;
|
|
832
|
-
} else {
|
|
833
|
-
// make sure there will not be any empty space between right side of container and first tab
|
|
834
|
-
if (newTranslationValue < 0) newTranslationValue = 0;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// make sure the new position will not place selected tab behind the right scroll button
|
|
838
|
-
if (!isRTL) {
|
|
839
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
|
840
|
-
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
|
841
|
-
newTranslationValue = getNewTranslationValue();
|
|
842
|
-
}
|
|
843
|
-
} else {
|
|
844
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
|
845
|
-
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInLeftScrollArea(expectedPosition)) {
|
|
846
|
-
newTranslationValue = getNewTranslationValue();
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
return newTranslationValue;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
#checkTabPanelMatch() {
|
|
854
|
-
// debounce so only runs once when tabs/panels slots changing
|
|
855
|
-
if (this.#checkTabPanelMatchRequested) return;
|
|
856
|
-
|
|
857
|
-
this.#checkTabPanelMatchRequested = true;
|
|
858
|
-
setTimeout(() => {
|
|
859
|
-
if ((this._tabs && !this.#panels) || (this.#panels && !this._tabs)) {
|
|
860
|
-
console.warn('d2l-tabs: tabs and panels are not in sync');
|
|
861
|
-
} else if (this._tabs.length !== this.#panels.length) {
|
|
862
|
-
console.warn('d2l-tabs: number of tabs and panels does not match');
|
|
863
|
-
}
|
|
864
|
-
this.#checkTabPanelMatchRequested = false;
|
|
865
|
-
}, 0);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
#handleTabDeselected(e) {
|
|
869
|
-
const panel = this._getPanel(e.target.id);
|
|
870
|
-
if (panel) panel._selected = false;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
#handleTabHiddenChange() {
|
|
874
|
-
if (!this._tabs || this._tabs.length <= 1) return;
|
|
875
|
-
|
|
876
|
-
let visibleTabCount = 0;
|
|
877
|
-
this._tabs.forEach((tab) => {
|
|
878
|
-
if (!tab.hidden) visibleTabCount++;
|
|
879
|
-
});
|
|
880
|
-
|
|
881
|
-
if (visibleTabCount > 1 && this._state === 'hidden') this.#showTabsList();
|
|
882
|
-
else if (visibleTabCount <= 1 && this._state === 'shown') this.#hideTabsList();
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
#hideTabsList() {
|
|
886
|
-
// don't animate the tabs list visibility if it's the inital render
|
|
887
|
-
if (reduceMotion || !this._initialized || !isVisible(this)) {
|
|
888
|
-
this._state = 'hidden';
|
|
889
|
-
} else if (this.shadowRoot) {
|
|
890
|
-
const layout = this.shadowRoot.querySelector('.d2l-tabs-layout');
|
|
891
|
-
const handleTransitionEnd = (e) => {
|
|
892
|
-
if (e.propertyName !== 'max-height') return;
|
|
893
|
-
layout.removeEventListener('transitionend', handleTransitionEnd);
|
|
894
|
-
this._state = 'hidden';
|
|
895
|
-
};
|
|
896
|
-
layout.addEventListener('transitionend', handleTransitionEnd);
|
|
897
|
-
this._state = 'anim';
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
#isRTL() {
|
|
902
|
-
return document.documentElement.getAttribute('dir') === 'rtl';
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
#setAriaControls() {
|
|
906
|
-
// debounce so only runs once when tabs/panels slots changing
|
|
907
|
-
if (this.#updateAriaControlsRequested) return;
|
|
908
|
-
|
|
909
|
-
this.#updateAriaControlsRequested = true;
|
|
910
|
-
setTimeout(() => {
|
|
911
|
-
this._tabs?.forEach((tab) => {
|
|
912
|
-
const panel = this._getPanel(tab.id);
|
|
913
|
-
if (!panel) {
|
|
914
|
-
console.warn('d2l-tabs: tab without matching panel');
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
tab.setAttribute('aria-controls', `${panel.id}`);
|
|
918
|
-
});
|
|
919
|
-
this.#updateAriaControlsRequested = false;
|
|
920
|
-
}, 0);
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
#showTabsList() {
|
|
924
|
-
// don't animate the tabs list visibility if it's the inital render
|
|
925
|
-
if (reduceMotion || !this._initialized) {
|
|
926
|
-
this._state = 'shown';
|
|
927
|
-
} else {
|
|
928
|
-
this._state = 'anim';
|
|
929
|
-
requestAnimationFrame(() => {
|
|
930
|
-
this._state = 'shown';
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
#updateScrollPositionLogic(measures, newTranslationValue) {
|
|
936
|
-
const scrollToPromise = this._scrollToPosition(newTranslationValue);
|
|
937
|
-
const scrollVisibilityPromise = this._updateScrollVisibility(measures);
|
|
938
|
-
const p = Promise.all([
|
|
939
|
-
scrollVisibilityPromise,
|
|
940
|
-
scrollToPromise
|
|
941
|
-
]);
|
|
942
|
-
p.then(() => {
|
|
943
|
-
if (this._loadingCompleteResolve) {
|
|
944
|
-
this._loadingCompleteResolve();
|
|
945
|
-
this._loadingCompleteResolve = undefined;
|
|
946
|
-
}
|
|
947
|
-
});
|
|
948
|
-
return p;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
938
|
async #updateSelectedTab(selectedTab) {
|
|
952
939
|
await this.updateComplete;
|
|
953
940
|
|
|
954
941
|
selectedTab.selected = true;
|
|
955
942
|
selectedTab.tabIndex = 0;
|
|
956
943
|
|
|
957
|
-
const selectedPanel = this
|
|
944
|
+
const selectedPanel = this.#getPanel(selectedTab.id);
|
|
958
945
|
if (selectedPanel) selectedPanel._selected = true;
|
|
959
|
-
this.
|
|
946
|
+
this.#tabs.forEach((tab) => {
|
|
960
947
|
if (tab.id !== selectedTab.id) {
|
|
961
948
|
if (tab.selected) {
|
|
962
949
|
tab.selected = false;
|
|
963
|
-
const panel = this
|
|
950
|
+
const panel = this.#getPanel(tab.id);
|
|
964
951
|
// panel may not exist if it's being removed
|
|
965
952
|
if (panel) panel._selected = false;
|
|
966
953
|
}
|
|
@@ -969,8 +956,27 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
969
956
|
});
|
|
970
957
|
}
|
|
971
958
|
|
|
959
|
+
#updateTabListVisibility(tabs) {
|
|
960
|
+
const visibleCount = tabs.filter(tab => !tab.hidden).length;
|
|
961
|
+
if (this._state === 'shown' && visibleCount < 2) {
|
|
962
|
+
this.#hideTabsList();
|
|
963
|
+
} else if (this._state === 'hidden' && visibleCount > 1) {
|
|
964
|
+
this.#showTabsList();
|
|
965
|
+
} else if (this._state === 'shown' && visibleCount > 1) {
|
|
966
|
+
// check if there are hidden tabs and tab list container should actually be hidden
|
|
967
|
+
this.#handleTabHiddenChange();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
#updateTabsContainerWidth(selectedTab) {
|
|
972
|
+
const tabs = this.#tabs;
|
|
973
|
+
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= tabs.length) return;
|
|
974
|
+
if (tabs.indexOf(selectedTab) > this.maxToShow - 1) return;
|
|
975
|
+
return this.#updateTabsContainerWidthLogic();
|
|
976
|
+
}
|
|
977
|
+
|
|
972
978
|
#updateTabsContainerWidthLogic() {
|
|
973
|
-
const measures = this
|
|
979
|
+
const measures = this.#getMeasures();
|
|
974
980
|
|
|
975
981
|
let maxWidth = 4; // initial value to allow for padding hack
|
|
976
982
|
for (let i = 0; i < this.maxToShow; i++) {
|
|
@@ -985,7 +991,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
|
985
991
|
|
|
986
992
|
this._maxWidth = maxWidth;
|
|
987
993
|
this._scrollCollapsed = true;
|
|
988
|
-
this
|
|
994
|
+
this.#measures = null;
|
|
989
995
|
|
|
990
996
|
return this.updateComplete;
|
|
991
997
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brightspace-ui/core",
|
|
3
|
-
"version": "3.317.
|
|
3
|
+
"version": "3.317.2",
|
|
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",
|