@brightspace-ui/core 3.93.2 → 3.94.1
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/tabs/demo/tabs.html +21 -3
- package/components/tabs/tabs.js +196 -127
- package/custom-elements.json +11 -0
- package/package.json +1 -1
@@ -40,13 +40,31 @@
|
|
40
40
|
|
41
41
|
<d2l-demo-snippet>
|
42
42
|
<template>
|
43
|
-
<d2l-tabs>
|
43
|
+
<d2l-tabs text="Courses">
|
44
44
|
<d2l-tab id="all" text="All" slot="tabs"></d2l-tab>
|
45
45
|
<d2l-tab id="biology" text="Biology" slot="tabs" selected></d2l-tab>
|
46
46
|
<d2l-tab id="chemistry" text="Chemistry" slot="tabs"></d2l-tab>
|
47
|
-
<d2l-tab
|
48
|
-
<d2l-tab
|
47
|
+
<d2l-tab id="physics" text="Physics" slot="tabs"></d2l-tab>
|
48
|
+
<d2l-tab id="math" text="Math" slot="tabs"></d2l-tab>
|
49
|
+
<d2l-tab id="earth-sciences" text="Earth Sciences" slot="tabs"></d2l-tab>
|
50
|
+
<d2l-tab-panel labelled-by="all" slot="panels" id="all-panel">Tab content for All</d2l-tab-panel>
|
51
|
+
<d2l-tab-panel labelled-by="biology" slot="panels" id="biology-panel">Tab content for Biology</d2l-tab-panel>
|
49
52
|
<d2l-tab-panel labelled-by="chemistry" slot="panels">Tab content for Chemistry</d2l-tab-panel>
|
53
|
+
<d2l-tab-panel labelled-by="physics" slot="panels">Tab content for Physics</d2l-tab-panel>
|
54
|
+
<d2l-tab-panel labelled-by="math" slot="panels">Tab content for Math</d2l-tab-panel>
|
55
|
+
<d2l-tab-panel labelled-by="earth-sciences" slot="panels">Tab content for Earth Sciences</d2l-tab-panel>
|
56
|
+
<d2l-dropdown-button-subtle slot="ext" text="Explore Topics">
|
57
|
+
<d2l-dropdown-menu>
|
58
|
+
<d2l-menu label="Astronomy">
|
59
|
+
<d2l-menu-item text="Introduction"></d2l-menu-item>
|
60
|
+
<d2l-menu-item text="Searching for the Heavens "></d2l-menu-item>
|
61
|
+
<d2l-menu-item text="The Solar System"></d2l-menu-item>
|
62
|
+
<d2l-menu-item text="Stars & Galaxies"></d2l-menu-item>
|
63
|
+
<d2l-menu-item text="The Night Sky"></d2l-menu-item>
|
64
|
+
<d2l-menu-item text="The Universe"></d2l-menu-item>
|
65
|
+
</d2l-menu>
|
66
|
+
</d2l-dropdown-menu>
|
67
|
+
</d2l-dropdown-button-subtle>
|
50
68
|
</d2l-tabs>
|
51
69
|
</template>
|
52
70
|
</d2l-demo-snippet>
|
package/components/tabs/tabs.js
CHANGED
@@ -3,11 +3,12 @@ import '../icons/icon.js';
|
|
3
3
|
import '../../helpers/queueMicrotask.js';
|
4
4
|
import './tab-internal.js';
|
5
5
|
import { css, html, LitElement, unsafeCSS } from 'lit';
|
6
|
-
import { cssEscape, findComposedAncestor } from '../../helpers/dom.js';
|
6
|
+
import { cssEscape, findComposedAncestor, getOffsetParent } from '../../helpers/dom.js';
|
7
7
|
import { ArrowKeysMixin } from '../../mixins/arrow-keys/arrow-keys-mixin.js';
|
8
8
|
import { bodyCompactStyles } from '../typography/styles.js';
|
9
9
|
import { classMap } from 'lit/directives/class-map.js';
|
10
10
|
import { getFocusPseudoClass } from '../../helpers/focus.js';
|
11
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
11
12
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
12
13
|
import { repeat } from 'lit/directives/repeat.js';
|
13
14
|
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
|
@@ -18,6 +19,11 @@ const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
18
19
|
|
19
20
|
const scrollButtonWidth = 56;
|
20
21
|
|
22
|
+
function getOffsetLeft(tab, tabRect) {
|
23
|
+
const offsetParent = getOffsetParent(tab);
|
24
|
+
return Math.round(tabRect.left - offsetParent.getBoundingClientRect().left);
|
25
|
+
}
|
26
|
+
|
21
27
|
/**
|
22
28
|
* A component for tabbed content. It supports the "d2l-tab-panel" component for the content, renders tabs responsively, and provides virtual scrolling for large tab lists.
|
23
29
|
* @slot - Contains the tab panels (e.g., "d2l-tab-panel" components)
|
@@ -32,6 +38,11 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
32
38
|
* @type {number}
|
33
39
|
*/
|
34
40
|
maxToShow: { type: Number, attribute: 'max-to-show' },
|
41
|
+
/**
|
42
|
+
* REQUIRED: ACCESSIBILITY: Accessible text for the tablist
|
43
|
+
* @type {string}
|
44
|
+
*/
|
45
|
+
text: { type: String },
|
35
46
|
_allowScrollNext: { type: Boolean },
|
36
47
|
_allowScrollPrevious: { type: Boolean },
|
37
48
|
_defaultSlotBehavior: { state: true },
|
@@ -88,6 +99,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
88
99
|
}
|
89
100
|
.d2l-tabs-container-list {
|
90
101
|
display: flex;
|
102
|
+
position: relative;
|
91
103
|
-webkit-transition: transform 200ms ease-out;
|
92
104
|
transition: transform 200ms ease-out;
|
93
105
|
white-space: nowrap;
|
@@ -241,10 +253,10 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
241
253
|
await this.updateComplete;
|
242
254
|
|
243
255
|
if (!this._scrollCollapsed) {
|
244
|
-
return this.
|
256
|
+
return this._updateScrollPositionDefaultSlotBehavior(tabInfo);
|
245
257
|
} else {
|
246
258
|
const measures = this._getMeasures();
|
247
|
-
const newTranslationValue = this.
|
259
|
+
const newTranslationValue = this._calculateScrollPositionDefaultSlotBehavior(tabInfo, measures);
|
248
260
|
|
249
261
|
if (!this.#isRTL()) {
|
250
262
|
if (newTranslationValue >= 0) return;
|
@@ -256,7 +268,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
256
268
|
if (expanded) {
|
257
269
|
return;
|
258
270
|
} else {
|
259
|
-
return this.
|
271
|
+
return this._updateScrollPositionDefaultSlotBehavior(tabInfo);
|
260
272
|
}
|
261
273
|
}
|
262
274
|
};
|
@@ -305,6 +317,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
305
317
|
<div class="d2l-tabs-container-list"
|
306
318
|
@d2l-tab-selected="${this._handleTabSelected}"
|
307
319
|
@focusout="${this._handleFocusOut}"
|
320
|
+
aria-label="${ifDefined(this.text)}"
|
308
321
|
role="tablist"
|
309
322
|
style="${styleMap(tabsContainerListStyles)}">
|
310
323
|
${repeat(this._tabInfos, (tabInfo) => tabInfo.id, (tabInfo) => html`
|
@@ -333,7 +346,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
333
346
|
@d2l-tab-panel-selected="${this._handlePanelSelected}"
|
334
347
|
@d2l-tab-panel-text-changed="${this._handlePanelTextChange}">
|
335
348
|
<slot @slotchange="${this._handleDefaultSlotChange}"></slot>
|
336
|
-
<slot name="panels"></slot>
|
349
|
+
<slot name="panels" @slotchange="${this._handlePanelsSlotChange}"></slot>
|
337
350
|
</div>
|
338
351
|
`;
|
339
352
|
}
|
@@ -343,7 +356,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
343
356
|
}
|
344
357
|
|
345
358
|
async getLoadingComplete() {
|
346
|
-
return this.
|
359
|
+
return this._loadingCompletePromise;
|
347
360
|
}
|
348
361
|
|
349
362
|
getTabListRect() {
|
@@ -381,91 +394,16 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
381
394
|
});
|
382
395
|
}
|
383
396
|
|
384
|
-
_calculateScrollPosition(
|
397
|
+
_calculateScrollPosition(selectedTab, measures) {
|
398
|
+
const tabs = this._tabs;
|
399
|
+
const selectedTabIndex = tabs.indexOf(selectedTab);
|
400
|
+
return this.#calculateScrollPositionLogic(tabs, selectedTabIndex, measures);
|
401
|
+
}
|
385
402
|
|
403
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
404
|
+
_calculateScrollPositionDefaultSlotBehavior(selectedTabInfo, measures) {
|
386
405
|
const selectedTabIndex = this._tabInfos.indexOf(selectedTabInfo);
|
387
|
-
|
388
|
-
if (!measures.tabRects[selectedTabIndex]) return 0;
|
389
|
-
|
390
|
-
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
391
|
-
|
392
|
-
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
393
|
-
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
394
|
-
|
395
|
-
const isRTL = this.#isRTL();
|
396
|
-
|
397
|
-
let getNewTranslationValue;
|
398
|
-
if (!isRTL) {
|
399
|
-
getNewTranslationValue = () => {
|
400
|
-
if (selectedTabIndex === 0) {
|
401
|
-
// position selected tab at beginning
|
402
|
-
return 0;
|
403
|
-
} else if (selectedTabIndex === (this._tabInfos.length - 1)) {
|
404
|
-
// position selected tab at end
|
405
|
-
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
406
|
-
} else {
|
407
|
-
// position selected tab in middle
|
408
|
-
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
409
|
-
}
|
410
|
-
};
|
411
|
-
} else {
|
412
|
-
getNewTranslationValue = () => {
|
413
|
-
if (selectedTabIndex === 0) {
|
414
|
-
// position selected tab at beginning
|
415
|
-
return 0;
|
416
|
-
} else if (selectedTabIndex === (this._tabInfos.length - 1)) {
|
417
|
-
// position selected tab at end
|
418
|
-
return -1 * selectedTabMeasures.offsetLeft;
|
419
|
-
} else {
|
420
|
-
// position selected tab in middle
|
421
|
-
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
422
|
-
}
|
423
|
-
};
|
424
|
-
}
|
425
|
-
|
426
|
-
let newTranslationValue = this._translationValue;
|
427
|
-
if (isOverflowingLeft || isOverflowingRight) {
|
428
|
-
newTranslationValue = getNewTranslationValue();
|
429
|
-
}
|
430
|
-
|
431
|
-
let expectedPosition;
|
432
|
-
|
433
|
-
// make sure the new position will not place selected tab behind left scroll button
|
434
|
-
if (!isRTL) {
|
435
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
436
|
-
if (newTranslationValue < 0 && this._isPositionInLeftScrollArea(expectedPosition)) {
|
437
|
-
newTranslationValue = getNewTranslationValue();
|
438
|
-
}
|
439
|
-
} else {
|
440
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
441
|
-
if (newTranslationValue > 0 && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
442
|
-
newTranslationValue = getNewTranslationValue();
|
443
|
-
}
|
444
|
-
}
|
445
|
-
|
446
|
-
if (!isRTL) {
|
447
|
-
// make sure there will not be any empty space between left side of container and first tab
|
448
|
-
if (newTranslationValue > 0) newTranslationValue = 0;
|
449
|
-
} else {
|
450
|
-
// make sure there will not be any empty space between right side of container and first tab
|
451
|
-
if (newTranslationValue < 0) newTranslationValue = 0;
|
452
|
-
}
|
453
|
-
|
454
|
-
// make sure the new position will not place selected tab behind the right scroll button
|
455
|
-
if (!isRTL) {
|
456
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
457
|
-
if ((selectedTabIndex < this._tabInfos.length - 1) && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
458
|
-
newTranslationValue = getNewTranslationValue();
|
459
|
-
}
|
460
|
-
} else {
|
461
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
462
|
-
if ((selectedTabIndex < this._tabInfos.length - 1) && this._isPositionInLeftScrollArea(expectedPosition)) {
|
463
|
-
newTranslationValue = getNewTranslationValue();
|
464
|
-
}
|
465
|
-
}
|
466
|
-
|
467
|
-
return newTranslationValue;
|
468
|
-
|
406
|
+
return this.#calculateScrollPositionLogic(this._tabInfos, selectedTabIndex, measures);
|
469
407
|
}
|
470
408
|
|
471
409
|
async _focusSelected() {
|
@@ -473,7 +411,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
473
411
|
if (!selectedTab) return;
|
474
412
|
|
475
413
|
const selectedTabInfo = this._getTabInfo(selectedTab.controlsPanel);
|
476
|
-
await this.
|
414
|
+
await this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
477
415
|
|
478
416
|
selectedTab.focus();
|
479
417
|
}
|
@@ -588,7 +526,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
588
526
|
if (!this._initialized && this._tabInfos.length > 0) {
|
589
527
|
|
590
528
|
this._initialized = true;
|
591
|
-
await this.
|
529
|
+
await this._updateTabsContainerWidthDefaultSlotBehavior(selectedTabInfo);
|
592
530
|
|
593
531
|
} else {
|
594
532
|
|
@@ -606,7 +544,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
606
544
|
if (selectedTabInfo) {
|
607
545
|
Promise.all(animPromises).then(() => {
|
608
546
|
this._updateMeasures();
|
609
|
-
return this.
|
547
|
+
return this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
610
548
|
});
|
611
549
|
}
|
612
550
|
|
@@ -630,6 +568,12 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
630
568
|
this.requestUpdate();
|
631
569
|
}
|
632
570
|
|
571
|
+
_handlePanelsSlotChange() {
|
572
|
+
if (this._defaultSlotBehavior) return;
|
573
|
+
|
574
|
+
this.#setAriaControls();
|
575
|
+
}
|
576
|
+
|
633
577
|
async _handlePanelTextChange(e) {
|
634
578
|
const tabInfo = this._getTabInfo(e.target.id);
|
635
579
|
// event could be from nested tabs
|
@@ -725,7 +669,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
725
669
|
|
726
670
|
}
|
727
671
|
|
728
|
-
_handleTabSelected(e) {
|
672
|
+
async _handleTabSelected(e) {
|
729
673
|
if (this._defaultSlotBehavior) {
|
730
674
|
this._handleTabSelectedDefaultSlotBehavior(e);
|
731
675
|
return;
|
@@ -733,6 +677,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
733
677
|
|
734
678
|
const selectedTab = e.target;
|
735
679
|
this.#updateSelectedTab(selectedTab);
|
680
|
+
await this.updateComplete;
|
681
|
+
this._updateScrollPosition(selectedTab);
|
736
682
|
}
|
737
683
|
|
738
684
|
// remove after d2l-tab/d2l-tab-panel backport
|
@@ -745,7 +691,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
745
691
|
selectedTabInfo.activeFocusable = true;
|
746
692
|
|
747
693
|
await this.updateComplete;
|
748
|
-
this.
|
694
|
+
this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
749
695
|
|
750
696
|
selectedPanel.selected = true;
|
751
697
|
this._tabInfos.forEach((tabInfo) => {
|
@@ -781,17 +727,18 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
781
727
|
if (selectedTab) {
|
782
728
|
this.#updateSelectedTab(selectedTab);
|
783
729
|
}
|
730
|
+
this.#setAriaControls();
|
784
731
|
|
785
732
|
await this.updateComplete;
|
786
733
|
|
787
734
|
if (!this._initialized && this._tabs.length > 0) {
|
788
735
|
this._initialized = true;
|
736
|
+
await this._updateTabsContainerWidth(selectedTab);
|
789
737
|
}
|
790
738
|
|
791
739
|
if (selectedTab) {
|
792
|
-
|
793
|
-
|
794
|
-
if (selectedPanel) selectedPanel.selected = true;
|
740
|
+
this._updateMeasures();
|
741
|
+
this._updateScrollPosition(selectedTab);
|
795
742
|
}
|
796
743
|
}
|
797
744
|
|
@@ -884,12 +831,15 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
884
831
|
_updateMeasures() {
|
885
832
|
let totalTabsWidth = 0;
|
886
833
|
if (!this.shadowRoot) return;
|
887
|
-
const tabs = [...this.shadowRoot.querySelectorAll('d2l-tab-internal')];
|
834
|
+
const tabs = this._defaultSlotBehavior ? [...this.shadowRoot.querySelectorAll('d2l-tab-internal')] : this._tabs;
|
888
835
|
|
889
836
|
const tabRects = tabs.map((tab) => {
|
837
|
+
const tabRect = tab.getBoundingClientRect();
|
838
|
+
const offsetLeft = this._defaultSlotBehavior ? tab.offsetLeft : getOffsetLeft(tab, tabRect);
|
839
|
+
|
890
840
|
const measures = {
|
891
|
-
rect:
|
892
|
-
offsetLeft:
|
841
|
+
rect: tabRect,
|
842
|
+
offsetLeft: offsetLeft
|
893
843
|
};
|
894
844
|
totalTabsWidth += measures.rect.width;
|
895
845
|
return measures;
|
@@ -903,22 +853,17 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
903
853
|
};
|
904
854
|
}
|
905
855
|
|
906
|
-
_updateScrollPosition(
|
856
|
+
_updateScrollPosition(selectedTab) {
|
907
857
|
const measures = this._getMeasures();
|
908
|
-
const newTranslationValue = this._calculateScrollPosition(
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
this._loadingCompleteResolve();
|
918
|
-
this._loadingCompleteResolve = undefined;
|
919
|
-
}
|
920
|
-
});
|
921
|
-
return p;
|
858
|
+
const newTranslationValue = this._calculateScrollPosition(selectedTab, measures);
|
859
|
+
return this.#updateScrollPositionLogic(measures, newTranslationValue);
|
860
|
+
}
|
861
|
+
|
862
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
863
|
+
_updateScrollPositionDefaultSlotBehavior(selectedTabInfo) {
|
864
|
+
const measures = this._getMeasures();
|
865
|
+
const newTranslationValue = this._calculateScrollPositionDefaultSlotBehavior(selectedTabInfo, measures);
|
866
|
+
return this.#updateScrollPositionLogic(measures, newTranslationValue);
|
922
867
|
}
|
923
868
|
|
924
869
|
_updateScrollVisibility(measures) {
|
@@ -969,34 +914,138 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
969
914
|
}
|
970
915
|
}
|
971
916
|
|
972
|
-
_updateTabsContainerWidth(
|
917
|
+
_updateTabsContainerWidth(selectedTab) {
|
918
|
+
const tabs = this._tabs;
|
919
|
+
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= tabs.length) return;
|
920
|
+
if (tabs.indexOf(selectedTab) > this.maxToShow - 1) return;
|
921
|
+
return this.#updateTabsContainerWidthLogic();
|
922
|
+
}
|
923
|
+
|
924
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
925
|
+
_updateTabsContainerWidthDefaultSlotBehavior(selectedTabInfo) {
|
973
926
|
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= this._tabInfos.length) return;
|
974
927
|
if (this._tabInfos.indexOf(selectedTabInfo) > this.maxToShow - 1) return;
|
928
|
+
return this.#updateTabsContainerWidthLogic();
|
929
|
+
}
|
975
930
|
|
976
|
-
|
931
|
+
#calculateScrollPositionLogic(tabsDataStructure, selectedTabIndex, measures) {
|
932
|
+
if (!measures.tabRects[selectedTabIndex]) return 0;
|
977
933
|
|
978
|
-
|
979
|
-
|
980
|
-
|
934
|
+
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
935
|
+
|
936
|
+
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
937
|
+
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
938
|
+
|
939
|
+
const isRTL = this.#isRTL();
|
940
|
+
|
941
|
+
let getNewTranslationValue;
|
942
|
+
if (!isRTL) {
|
943
|
+
getNewTranslationValue = () => {
|
944
|
+
if (selectedTabIndex === 0) {
|
945
|
+
// position selected tab at beginning
|
946
|
+
return 0;
|
947
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
948
|
+
// position selected tab at end
|
949
|
+
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
950
|
+
} else {
|
951
|
+
// position selected tab in middle
|
952
|
+
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
953
|
+
}
|
954
|
+
};
|
955
|
+
} else {
|
956
|
+
getNewTranslationValue = () => {
|
957
|
+
if (selectedTabIndex === 0) {
|
958
|
+
// position selected tab at beginning
|
959
|
+
return 0;
|
960
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
961
|
+
// position selected tab at end
|
962
|
+
return -1 * selectedTabMeasures.offsetLeft;
|
963
|
+
} else {
|
964
|
+
// position selected tab in middle
|
965
|
+
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
966
|
+
}
|
967
|
+
};
|
981
968
|
}
|
982
969
|
|
983
|
-
|
984
|
-
|
970
|
+
let newTranslationValue = this._translationValue;
|
971
|
+
if (isOverflowingLeft || isOverflowingRight) {
|
972
|
+
newTranslationValue = getNewTranslationValue();
|
985
973
|
}
|
986
974
|
|
987
|
-
|
975
|
+
let expectedPosition;
|
988
976
|
|
989
|
-
|
990
|
-
|
991
|
-
|
977
|
+
// make sure the new position will not place selected tab behind left scroll button
|
978
|
+
if (!isRTL) {
|
979
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
980
|
+
if (newTranslationValue < 0 && this._isPositionInLeftScrollArea(expectedPosition)) {
|
981
|
+
newTranslationValue = getNewTranslationValue();
|
982
|
+
}
|
983
|
+
} else {
|
984
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
985
|
+
if (newTranslationValue > 0 && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
986
|
+
newTranslationValue = getNewTranslationValue();
|
987
|
+
}
|
988
|
+
}
|
992
989
|
|
993
|
-
|
990
|
+
if (!isRTL) {
|
991
|
+
// make sure there will not be any empty space between left side of container and first tab
|
992
|
+
if (newTranslationValue > 0) newTranslationValue = 0;
|
993
|
+
} else {
|
994
|
+
// make sure there will not be any empty space between right side of container and first tab
|
995
|
+
if (newTranslationValue < 0) newTranslationValue = 0;
|
996
|
+
}
|
997
|
+
|
998
|
+
// make sure the new position will not place selected tab behind the right scroll button
|
999
|
+
if (!isRTL) {
|
1000
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
1001
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
1002
|
+
newTranslationValue = getNewTranslationValue();
|
1003
|
+
}
|
1004
|
+
} else {
|
1005
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
1006
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInLeftScrollArea(expectedPosition)) {
|
1007
|
+
newTranslationValue = getNewTranslationValue();
|
1008
|
+
}
|
1009
|
+
}
|
1010
|
+
|
1011
|
+
return newTranslationValue;
|
994
1012
|
}
|
995
1013
|
|
996
1014
|
#isRTL() {
|
997
1015
|
return document.documentElement.getAttribute('dir') === 'rtl';
|
998
1016
|
}
|
999
1017
|
|
1018
|
+
#setAriaControls() {
|
1019
|
+
// debounce so only runs once when tabs/panels slots changing
|
1020
|
+
if (this._updateAriaControlsRequested) return;
|
1021
|
+
|
1022
|
+
this._updateAriaControlsRequested = true;
|
1023
|
+
setTimeout(() => {
|
1024
|
+
this._tabs?.forEach((tab) => {
|
1025
|
+
const panel = this._getPanel(tab.id);
|
1026
|
+
if (!panel) return;
|
1027
|
+
tab.setAttribute('aria-controls', `${panel.id}`);
|
1028
|
+
});
|
1029
|
+
this._updateAriaControlsRequested = false;
|
1030
|
+
}, 0);
|
1031
|
+
}
|
1032
|
+
|
1033
|
+
#updateScrollPositionLogic(measures, newTranslationValue) {
|
1034
|
+
const scrollToPromise = this._scrollToPosition(newTranslationValue);
|
1035
|
+
const scrollVisibilityPromise = this._updateScrollVisibility(measures);
|
1036
|
+
const p = Promise.all([
|
1037
|
+
scrollVisibilityPromise,
|
1038
|
+
scrollToPromise
|
1039
|
+
]);
|
1040
|
+
p.then(() => {
|
1041
|
+
if (this._loadingCompleteResolve) {
|
1042
|
+
this._loadingCompleteResolve();
|
1043
|
+
this._loadingCompleteResolve = undefined;
|
1044
|
+
}
|
1045
|
+
});
|
1046
|
+
return p;
|
1047
|
+
}
|
1048
|
+
|
1000
1049
|
async #updateSelectedTab(selectedTab) {
|
1001
1050
|
const selectedPanel = this._getPanel(selectedTab.id);
|
1002
1051
|
selectedTab.tabIndex = 0;
|
@@ -1017,6 +1066,26 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
1017
1066
|
});
|
1018
1067
|
}
|
1019
1068
|
|
1069
|
+
#updateTabsContainerWidthLogic() {
|
1070
|
+
const measures = this._getMeasures();
|
1071
|
+
|
1072
|
+
let maxWidth = 4; // initial value to allow for padding hack
|
1073
|
+
for (let i = 0; i < this.maxToShow; i++) {
|
1074
|
+
maxWidth += measures.tabRects[i].rect.width;
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
if (measures.tabsContainerListRect.width > maxWidth) {
|
1078
|
+
maxWidth += scrollButtonWidth;
|
1079
|
+
}
|
1080
|
+
|
1081
|
+
if (maxWidth >= measures.tabsContainerRect.width) return;
|
1082
|
+
|
1083
|
+
this._maxWidth = maxWidth;
|
1084
|
+
this._scrollCollapsed = true;
|
1085
|
+
this._measures = null;
|
1086
|
+
|
1087
|
+
return this.updateComplete;
|
1088
|
+
}
|
1020
1089
|
}
|
1021
1090
|
|
1022
1091
|
customElements.define('d2l-tabs', Tabs);
|
package/custom-elements.json
CHANGED
@@ -12905,6 +12905,11 @@
|
|
12905
12905
|
"path": "./components/tabs/tabs.js",
|
12906
12906
|
"description": "A component for tabbed content. It supports the \"d2l-tab-panel\" component for the content, renders tabs responsively, and provides virtual scrolling for large tab lists.",
|
12907
12907
|
"attributes": [
|
12908
|
+
{
|
12909
|
+
"name": "text",
|
12910
|
+
"description": "REQUIRED: ACCESSIBILITY: Accessible text for the tablist",
|
12911
|
+
"type": "string"
|
12912
|
+
},
|
12908
12913
|
{
|
12909
12914
|
"name": "max-to-show",
|
12910
12915
|
"description": "Limit the number of tabs to initially display",
|
@@ -12918,6 +12923,12 @@
|
|
12918
12923
|
}
|
12919
12924
|
],
|
12920
12925
|
"properties": [
|
12926
|
+
{
|
12927
|
+
"name": "text",
|
12928
|
+
"attribute": "text",
|
12929
|
+
"description": "REQUIRED: ACCESSIBILITY: Accessible text for the tablist",
|
12930
|
+
"type": "string"
|
12931
|
+
},
|
12921
12932
|
{
|
12922
12933
|
"name": "maxToShow",
|
12923
12934
|
"attribute": "max-to-show",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.94.1",
|
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",
|