@brightspace-ui/core 3.94.0 → 3.94.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/tabs/demo/tabs.html +18 -0
- package/components/tabs/tabs.js +208 -138
- package/package.json +1 -1
@@ -44,9 +44,27 @@
|
|
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 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>
|
47
50
|
<d2l-tab-panel labelled-by="all" slot="panels" id="all-panel">Tab content for All</d2l-tab-panel>
|
48
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,7 +3,7 @@ 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';
|
@@ -19,6 +19,11 @@ const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
19
19
|
|
20
20
|
const scrollButtonWidth = 56;
|
21
21
|
|
22
|
+
function getOffsetLeft(tab, tabRect) {
|
23
|
+
const offsetParent = getOffsetParent(tab);
|
24
|
+
return Math.round(tabRect.left - offsetParent.getBoundingClientRect().left);
|
25
|
+
}
|
26
|
+
|
22
27
|
/**
|
23
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.
|
24
29
|
* @slot - Contains the tab panels (e.g., "d2l-tab-panel" components)
|
@@ -94,6 +99,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
94
99
|
}
|
95
100
|
.d2l-tabs-container-list {
|
96
101
|
display: flex;
|
102
|
+
position: relative;
|
97
103
|
-webkit-transition: transform 200ms ease-out;
|
98
104
|
transition: transform 200ms ease-out;
|
99
105
|
white-space: nowrap;
|
@@ -247,10 +253,10 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
247
253
|
await this.updateComplete;
|
248
254
|
|
249
255
|
if (!this._scrollCollapsed) {
|
250
|
-
return this.
|
256
|
+
return this._updateScrollPositionDefaultSlotBehavior(tabInfo);
|
251
257
|
} else {
|
252
258
|
const measures = this._getMeasures();
|
253
|
-
const newTranslationValue = this.
|
259
|
+
const newTranslationValue = this._calculateScrollPositionDefaultSlotBehavior(tabInfo, measures);
|
254
260
|
|
255
261
|
if (!this.#isRTL()) {
|
256
262
|
if (newTranslationValue >= 0) return;
|
@@ -262,7 +268,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
262
268
|
if (expanded) {
|
263
269
|
return;
|
264
270
|
} else {
|
265
|
-
return this.
|
271
|
+
return this._updateScrollPositionDefaultSlotBehavior(tabInfo);
|
266
272
|
}
|
267
273
|
}
|
268
274
|
};
|
@@ -350,7 +356,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
350
356
|
}
|
351
357
|
|
352
358
|
async getLoadingComplete() {
|
353
|
-
return this.
|
359
|
+
return this._loadingCompletePromise;
|
354
360
|
}
|
355
361
|
|
356
362
|
getTabListRect() {
|
@@ -358,6 +364,10 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
358
364
|
return this.shadowRoot.querySelector('.d2l-tabs-container-list').getBoundingClientRect();
|
359
365
|
}
|
360
366
|
|
367
|
+
#checkTabPanelMatchRequested;
|
368
|
+
#panels;
|
369
|
+
#updateAriaControlsRequested;
|
370
|
+
|
361
371
|
_animateTabAddition(tabInfo) {
|
362
372
|
const tab = this.shadowRoot
|
363
373
|
&& this.shadowRoot.querySelector(`d2l-tab-internal[controls-panel="${cssEscape(tabInfo.id)}"]`);
|
@@ -388,91 +398,16 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
388
398
|
});
|
389
399
|
}
|
390
400
|
|
391
|
-
_calculateScrollPosition(
|
401
|
+
_calculateScrollPosition(selectedTab, measures) {
|
402
|
+
const tabs = this._tabs;
|
403
|
+
const selectedTabIndex = tabs.indexOf(selectedTab);
|
404
|
+
return this.#calculateScrollPositionLogic(tabs, selectedTabIndex, measures);
|
405
|
+
}
|
392
406
|
|
407
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
408
|
+
_calculateScrollPositionDefaultSlotBehavior(selectedTabInfo, measures) {
|
393
409
|
const selectedTabIndex = this._tabInfos.indexOf(selectedTabInfo);
|
394
|
-
|
395
|
-
if (!measures.tabRects[selectedTabIndex]) return 0;
|
396
|
-
|
397
|
-
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
398
|
-
|
399
|
-
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
400
|
-
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
401
|
-
|
402
|
-
const isRTL = this.#isRTL();
|
403
|
-
|
404
|
-
let getNewTranslationValue;
|
405
|
-
if (!isRTL) {
|
406
|
-
getNewTranslationValue = () => {
|
407
|
-
if (selectedTabIndex === 0) {
|
408
|
-
// position selected tab at beginning
|
409
|
-
return 0;
|
410
|
-
} else if (selectedTabIndex === (this._tabInfos.length - 1)) {
|
411
|
-
// position selected tab at end
|
412
|
-
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
413
|
-
} else {
|
414
|
-
// position selected tab in middle
|
415
|
-
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
416
|
-
}
|
417
|
-
};
|
418
|
-
} else {
|
419
|
-
getNewTranslationValue = () => {
|
420
|
-
if (selectedTabIndex === 0) {
|
421
|
-
// position selected tab at beginning
|
422
|
-
return 0;
|
423
|
-
} else if (selectedTabIndex === (this._tabInfos.length - 1)) {
|
424
|
-
// position selected tab at end
|
425
|
-
return -1 * selectedTabMeasures.offsetLeft;
|
426
|
-
} else {
|
427
|
-
// position selected tab in middle
|
428
|
-
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
429
|
-
}
|
430
|
-
};
|
431
|
-
}
|
432
|
-
|
433
|
-
let newTranslationValue = this._translationValue;
|
434
|
-
if (isOverflowingLeft || isOverflowingRight) {
|
435
|
-
newTranslationValue = getNewTranslationValue();
|
436
|
-
}
|
437
|
-
|
438
|
-
let expectedPosition;
|
439
|
-
|
440
|
-
// make sure the new position will not place selected tab behind left scroll button
|
441
|
-
if (!isRTL) {
|
442
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
443
|
-
if (newTranslationValue < 0 && this._isPositionInLeftScrollArea(expectedPosition)) {
|
444
|
-
newTranslationValue = getNewTranslationValue();
|
445
|
-
}
|
446
|
-
} else {
|
447
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
448
|
-
if (newTranslationValue > 0 && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
449
|
-
newTranslationValue = getNewTranslationValue();
|
450
|
-
}
|
451
|
-
}
|
452
|
-
|
453
|
-
if (!isRTL) {
|
454
|
-
// make sure there will not be any empty space between left side of container and first tab
|
455
|
-
if (newTranslationValue > 0) newTranslationValue = 0;
|
456
|
-
} else {
|
457
|
-
// make sure there will not be any empty space between right side of container and first tab
|
458
|
-
if (newTranslationValue < 0) newTranslationValue = 0;
|
459
|
-
}
|
460
|
-
|
461
|
-
// make sure the new position will not place selected tab behind the right scroll button
|
462
|
-
if (!isRTL) {
|
463
|
-
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
464
|
-
if ((selectedTabIndex < this._tabInfos.length - 1) && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
465
|
-
newTranslationValue = getNewTranslationValue();
|
466
|
-
}
|
467
|
-
} else {
|
468
|
-
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
469
|
-
if ((selectedTabIndex < this._tabInfos.length - 1) && this._isPositionInLeftScrollArea(expectedPosition)) {
|
470
|
-
newTranslationValue = getNewTranslationValue();
|
471
|
-
}
|
472
|
-
}
|
473
|
-
|
474
|
-
return newTranslationValue;
|
475
|
-
|
410
|
+
return this.#calculateScrollPositionLogic(this._tabInfos, selectedTabIndex, measures);
|
476
411
|
}
|
477
412
|
|
478
413
|
async _focusSelected() {
|
@@ -480,7 +415,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
480
415
|
if (!selectedTab) return;
|
481
416
|
|
482
417
|
const selectedTabInfo = this._getTabInfo(selectedTab.controlsPanel);
|
483
|
-
await this.
|
418
|
+
await this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
484
419
|
|
485
420
|
selectedTab.focus();
|
486
421
|
}
|
@@ -507,10 +442,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
507
442
|
_getPanel(id) {
|
508
443
|
if (this._defaultSlotBehavior) return this._getPanelDefaultSlotBehavior(id);
|
509
444
|
|
510
|
-
if (!this
|
511
|
-
|
512
|
-
const panels = this._getPanels(slot);
|
513
|
-
return panels.find(panel => panel.labelledBy === id);
|
445
|
+
if (!this.#panels) return;
|
446
|
+
return this.#panels.find(panel => panel.labelledBy === id);
|
514
447
|
}
|
515
448
|
|
516
449
|
// remove after d2l-tab/d2l-tab-panel backport
|
@@ -518,7 +451,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
518
451
|
if (!this.shadowRoot) return;
|
519
452
|
// use simple selector for slot (Edge)
|
520
453
|
const slot = this.shadowRoot.querySelector('.d2l-panels-container').querySelector('slot');
|
521
|
-
const panels = this.
|
454
|
+
const panels = this._getPanelsDefaultSlotBehavior(slot);
|
522
455
|
for (let i = 0; i < panels.length; i++) {
|
523
456
|
if (panels[i].nodeType === Node.ELEMENT_NODE && panels[i].role === 'tabpanel' && panels[i].id === id) {
|
524
457
|
return panels[i];
|
@@ -526,7 +459,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
526
459
|
}
|
527
460
|
}
|
528
461
|
|
529
|
-
|
462
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
463
|
+
_getPanelsDefaultSlotBehavior(slot) {
|
530
464
|
if (!slot) return;
|
531
465
|
return slot.assignedElements({ flatten: true }).filter((node) => node.role === 'tabpanel');
|
532
466
|
}
|
@@ -539,7 +473,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
539
473
|
async _handleDefaultSlotChange(e) {
|
540
474
|
if (!this._defaultSlotBehavior) return;
|
541
475
|
|
542
|
-
const panels = this.
|
476
|
+
const panels = this._getPanelsDefaultSlotBehavior(e.target);
|
543
477
|
|
544
478
|
// handle case where there are less than two tabs initially
|
545
479
|
this._updateTabListVisibility(panels);
|
@@ -595,7 +529,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
595
529
|
if (!this._initialized && this._tabInfos.length > 0) {
|
596
530
|
|
597
531
|
this._initialized = true;
|
598
|
-
await this.
|
532
|
+
await this._updateTabsContainerWidthDefaultSlotBehavior(selectedTabInfo);
|
599
533
|
|
600
534
|
} else {
|
601
535
|
|
@@ -613,7 +547,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
613
547
|
if (selectedTabInfo) {
|
614
548
|
Promise.all(animPromises).then(() => {
|
615
549
|
this._updateMeasures();
|
616
|
-
return this.
|
550
|
+
return this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
617
551
|
});
|
618
552
|
}
|
619
553
|
|
@@ -637,9 +571,11 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
637
571
|
this.requestUpdate();
|
638
572
|
}
|
639
573
|
|
640
|
-
_handlePanelsSlotChange() {
|
574
|
+
_handlePanelsSlotChange(e) {
|
641
575
|
if (this._defaultSlotBehavior) return;
|
642
576
|
|
577
|
+
this.#panels = e.target.assignedElements({ flatten: true }).filter((node) => node.role === 'tabpanel');
|
578
|
+
this.#checkTabPanelMatch();
|
643
579
|
this.#setAriaControls();
|
644
580
|
}
|
645
581
|
|
@@ -738,7 +674,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
738
674
|
|
739
675
|
}
|
740
676
|
|
741
|
-
_handleTabSelected(e) {
|
677
|
+
async _handleTabSelected(e) {
|
742
678
|
if (this._defaultSlotBehavior) {
|
743
679
|
this._handleTabSelectedDefaultSlotBehavior(e);
|
744
680
|
return;
|
@@ -746,6 +682,8 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
746
682
|
|
747
683
|
const selectedTab = e.target;
|
748
684
|
this.#updateSelectedTab(selectedTab);
|
685
|
+
await this.updateComplete;
|
686
|
+
this._updateScrollPosition(selectedTab);
|
749
687
|
}
|
750
688
|
|
751
689
|
// remove after d2l-tab/d2l-tab-panel backport
|
@@ -758,7 +696,7 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
758
696
|
selectedTabInfo.activeFocusable = true;
|
759
697
|
|
760
698
|
await this.updateComplete;
|
761
|
-
this.
|
699
|
+
this._updateScrollPositionDefaultSlotBehavior(selectedTabInfo);
|
762
700
|
|
763
701
|
selectedPanel.selected = true;
|
764
702
|
this._tabInfos.forEach((tabInfo) => {
|
@@ -794,12 +732,19 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
794
732
|
if (selectedTab) {
|
795
733
|
this.#updateSelectedTab(selectedTab);
|
796
734
|
}
|
797
|
-
this.#setAriaControls();
|
798
735
|
|
799
736
|
await this.updateComplete;
|
737
|
+
this.#checkTabPanelMatch();
|
738
|
+
this.#setAriaControls();
|
800
739
|
|
801
740
|
if (!this._initialized && this._tabs.length > 0) {
|
802
741
|
this._initialized = true;
|
742
|
+
await this._updateTabsContainerWidth(selectedTab);
|
743
|
+
}
|
744
|
+
|
745
|
+
if (selectedTab) {
|
746
|
+
this._updateMeasures();
|
747
|
+
this._updateScrollPosition(selectedTab);
|
803
748
|
}
|
804
749
|
}
|
805
750
|
|
@@ -892,12 +837,15 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
892
837
|
_updateMeasures() {
|
893
838
|
let totalTabsWidth = 0;
|
894
839
|
if (!this.shadowRoot) return;
|
895
|
-
const tabs = [...this.shadowRoot.querySelectorAll('d2l-tab-internal')];
|
840
|
+
const tabs = this._defaultSlotBehavior ? [...this.shadowRoot.querySelectorAll('d2l-tab-internal')] : this._tabs;
|
896
841
|
|
897
842
|
const tabRects = tabs.map((tab) => {
|
843
|
+
const tabRect = tab.getBoundingClientRect();
|
844
|
+
const offsetLeft = this._defaultSlotBehavior ? tab.offsetLeft : getOffsetLeft(tab, tabRect);
|
845
|
+
|
898
846
|
const measures = {
|
899
|
-
rect:
|
900
|
-
offsetLeft:
|
847
|
+
rect: tabRect,
|
848
|
+
offsetLeft: offsetLeft
|
901
849
|
};
|
902
850
|
totalTabsWidth += measures.rect.width;
|
903
851
|
return measures;
|
@@ -911,22 +859,17 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
911
859
|
};
|
912
860
|
}
|
913
861
|
|
914
|
-
_updateScrollPosition(
|
862
|
+
_updateScrollPosition(selectedTab) {
|
915
863
|
const measures = this._getMeasures();
|
916
|
-
const newTranslationValue = this._calculateScrollPosition(
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
this._loadingCompleteResolve();
|
926
|
-
this._loadingCompleteResolve = undefined;
|
927
|
-
}
|
928
|
-
});
|
929
|
-
return p;
|
864
|
+
const newTranslationValue = this._calculateScrollPosition(selectedTab, measures);
|
865
|
+
return this.#updateScrollPositionLogic(measures, newTranslationValue);
|
866
|
+
}
|
867
|
+
|
868
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
869
|
+
_updateScrollPositionDefaultSlotBehavior(selectedTabInfo) {
|
870
|
+
const measures = this._getMeasures();
|
871
|
+
const newTranslationValue = this._calculateScrollPositionDefaultSlotBehavior(selectedTabInfo, measures);
|
872
|
+
return this.#updateScrollPositionLogic(measures, newTranslationValue);
|
930
873
|
}
|
931
874
|
|
932
875
|
_updateScrollVisibility(measures) {
|
@@ -977,28 +920,116 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
977
920
|
}
|
978
921
|
}
|
979
922
|
|
980
|
-
_updateTabsContainerWidth(
|
923
|
+
_updateTabsContainerWidth(selectedTab) {
|
924
|
+
const tabs = this._tabs;
|
925
|
+
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= tabs.length) return;
|
926
|
+
if (tabs.indexOf(selectedTab) > this.maxToShow - 1) return;
|
927
|
+
return this.#updateTabsContainerWidthLogic();
|
928
|
+
}
|
929
|
+
|
930
|
+
// remove after d2l-tab/d2l-tab-panel backport
|
931
|
+
_updateTabsContainerWidthDefaultSlotBehavior(selectedTabInfo) {
|
981
932
|
if (!this.maxToShow || this.maxToShow <= 0 || this.maxToShow >= this._tabInfos.length) return;
|
982
933
|
if (this._tabInfos.indexOf(selectedTabInfo) > this.maxToShow - 1) return;
|
934
|
+
return this.#updateTabsContainerWidthLogic();
|
935
|
+
}
|
983
936
|
|
984
|
-
|
937
|
+
#calculateScrollPositionLogic(tabsDataStructure, selectedTabIndex, measures) {
|
938
|
+
if (!measures.tabRects[selectedTabIndex]) return 0;
|
985
939
|
|
986
|
-
|
987
|
-
|
988
|
-
|
940
|
+
const selectedTabMeasures = measures.tabRects[selectedTabIndex];
|
941
|
+
|
942
|
+
const isOverflowingLeft = (selectedTabMeasures.offsetLeft + this._translationValue < 0);
|
943
|
+
const isOverflowingRight = (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + this._translationValue > measures.tabsContainerRect.width);
|
944
|
+
|
945
|
+
const isRTL = this.#isRTL();
|
946
|
+
|
947
|
+
let getNewTranslationValue;
|
948
|
+
if (!isRTL) {
|
949
|
+
getNewTranslationValue = () => {
|
950
|
+
if (selectedTabIndex === 0) {
|
951
|
+
// position selected tab at beginning
|
952
|
+
return 0;
|
953
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
954
|
+
// position selected tab at end
|
955
|
+
return -1 * (selectedTabMeasures.offsetLeft - measures.tabsContainerRect.width + selectedTabMeasures.rect.width);
|
956
|
+
} else {
|
957
|
+
// position selected tab in middle
|
958
|
+
return -1 * (selectedTabMeasures.offsetLeft - (measures.tabsContainerRect.width / 2) + (selectedTabMeasures.rect.width / 2));
|
959
|
+
}
|
960
|
+
};
|
961
|
+
} else {
|
962
|
+
getNewTranslationValue = () => {
|
963
|
+
if (selectedTabIndex === 0) {
|
964
|
+
// position selected tab at beginning
|
965
|
+
return 0;
|
966
|
+
} else if (selectedTabIndex === (tabsDataStructure.length - 1)) {
|
967
|
+
// position selected tab at end
|
968
|
+
return -1 * selectedTabMeasures.offsetLeft;
|
969
|
+
} else {
|
970
|
+
// position selected tab in middle
|
971
|
+
return (measures.tabsContainerRect.width / 2) - (selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width / 2) + (selectedTabMeasures.rect.width / 2);
|
972
|
+
}
|
973
|
+
};
|
989
974
|
}
|
990
975
|
|
991
|
-
|
992
|
-
|
976
|
+
let newTranslationValue = this._translationValue;
|
977
|
+
if (isOverflowingLeft || isOverflowingRight) {
|
978
|
+
newTranslationValue = getNewTranslationValue();
|
993
979
|
}
|
994
980
|
|
995
|
-
|
981
|
+
let expectedPosition;
|
996
982
|
|
997
|
-
|
998
|
-
|
999
|
-
|
983
|
+
// make sure the new position will not place selected tab behind left scroll button
|
984
|
+
if (!isRTL) {
|
985
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
986
|
+
if (newTranslationValue < 0 && this._isPositionInLeftScrollArea(expectedPosition)) {
|
987
|
+
newTranslationValue = getNewTranslationValue();
|
988
|
+
}
|
989
|
+
} else {
|
990
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
991
|
+
if (newTranslationValue > 0 && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
992
|
+
newTranslationValue = getNewTranslationValue();
|
993
|
+
}
|
994
|
+
}
|
1000
995
|
|
1001
|
-
|
996
|
+
if (!isRTL) {
|
997
|
+
// make sure there will not be any empty space between left side of container and first tab
|
998
|
+
if (newTranslationValue > 0) newTranslationValue = 0;
|
999
|
+
} else {
|
1000
|
+
// make sure there will not be any empty space between right side of container and first tab
|
1001
|
+
if (newTranslationValue < 0) newTranslationValue = 0;
|
1002
|
+
}
|
1003
|
+
|
1004
|
+
// make sure the new position will not place selected tab behind the right scroll button
|
1005
|
+
if (!isRTL) {
|
1006
|
+
expectedPosition = selectedTabMeasures.offsetLeft + selectedTabMeasures.rect.width + newTranslationValue;
|
1007
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInRightScrollArea(expectedPosition, measures)) {
|
1008
|
+
newTranslationValue = getNewTranslationValue();
|
1009
|
+
}
|
1010
|
+
} else {
|
1011
|
+
expectedPosition = selectedTabMeasures.offsetLeft + newTranslationValue;
|
1012
|
+
if ((selectedTabIndex < tabsDataStructure.length - 1) && this._isPositionInLeftScrollArea(expectedPosition)) {
|
1013
|
+
newTranslationValue = getNewTranslationValue();
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
|
1017
|
+
return newTranslationValue;
|
1018
|
+
}
|
1019
|
+
|
1020
|
+
#checkTabPanelMatch() {
|
1021
|
+
// debounce so only runs once when tabs/panels slots changing
|
1022
|
+
if (this.#checkTabPanelMatchRequested) return;
|
1023
|
+
|
1024
|
+
this.#checkTabPanelMatchRequested = true;
|
1025
|
+
setTimeout(() => {
|
1026
|
+
if ((this._tabs && !this.#panels) || (this.#panels && !this._tabs)) {
|
1027
|
+
console.warn('d2l-tabs: tabs and panels are not in sync');
|
1028
|
+
} else if (this._tabs.length !== this.#panels.length) {
|
1029
|
+
console.warn('d2l-tabs: number of tabs and panels does not match');
|
1030
|
+
}
|
1031
|
+
this.#checkTabPanelMatchRequested = false;
|
1032
|
+
}, 0);
|
1002
1033
|
}
|
1003
1034
|
|
1004
1035
|
#isRTL() {
|
@@ -1007,26 +1038,45 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
1007
1038
|
|
1008
1039
|
#setAriaControls() {
|
1009
1040
|
// debounce so only runs once when tabs/panels slots changing
|
1010
|
-
if (this
|
1041
|
+
if (this.#updateAriaControlsRequested) return;
|
1011
1042
|
|
1012
|
-
this
|
1043
|
+
this.#updateAriaControlsRequested = true;
|
1013
1044
|
setTimeout(() => {
|
1014
1045
|
this._tabs?.forEach((tab) => {
|
1015
1046
|
const panel = this._getPanel(tab.id);
|
1016
|
-
if (!panel)
|
1047
|
+
if (!panel) {
|
1048
|
+
console.warn('d2l-tabs: tab without matching panel');
|
1049
|
+
return;
|
1050
|
+
}
|
1017
1051
|
tab.setAttribute('aria-controls', `${panel.id}`);
|
1018
1052
|
});
|
1019
|
-
this
|
1053
|
+
this.#updateAriaControlsRequested = false;
|
1020
1054
|
}, 0);
|
1021
1055
|
}
|
1022
1056
|
|
1057
|
+
#updateScrollPositionLogic(measures, newTranslationValue) {
|
1058
|
+
const scrollToPromise = this._scrollToPosition(newTranslationValue);
|
1059
|
+
const scrollVisibilityPromise = this._updateScrollVisibility(measures);
|
1060
|
+
const p = Promise.all([
|
1061
|
+
scrollVisibilityPromise,
|
1062
|
+
scrollToPromise
|
1063
|
+
]);
|
1064
|
+
p.then(() => {
|
1065
|
+
if (this._loadingCompleteResolve) {
|
1066
|
+
this._loadingCompleteResolve();
|
1067
|
+
this._loadingCompleteResolve = undefined;
|
1068
|
+
}
|
1069
|
+
});
|
1070
|
+
return p;
|
1071
|
+
}
|
1072
|
+
|
1023
1073
|
async #updateSelectedTab(selectedTab) {
|
1024
|
-
const selectedPanel = this._getPanel(selectedTab.id);
|
1025
1074
|
selectedTab.tabIndex = 0;
|
1026
1075
|
|
1027
1076
|
await this.updateComplete;
|
1028
1077
|
|
1029
|
-
selectedPanel
|
1078
|
+
const selectedPanel = this._getPanel(selectedTab.id);
|
1079
|
+
if (selectedPanel) selectedPanel.selected = true;
|
1030
1080
|
this._tabs.forEach((tab) => {
|
1031
1081
|
if (tab.id !== selectedTab.id) {
|
1032
1082
|
if (tab.selected) {
|
@@ -1040,6 +1090,26 @@ class Tabs extends LocalizeCoreElement(ArrowKeysMixin(SkeletonMixin(LitElement))
|
|
1040
1090
|
});
|
1041
1091
|
}
|
1042
1092
|
|
1093
|
+
#updateTabsContainerWidthLogic() {
|
1094
|
+
const measures = this._getMeasures();
|
1095
|
+
|
1096
|
+
let maxWidth = 4; // initial value to allow for padding hack
|
1097
|
+
for (let i = 0; i < this.maxToShow; i++) {
|
1098
|
+
maxWidth += measures.tabRects[i].rect.width;
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
if (measures.tabsContainerListRect.width > maxWidth) {
|
1102
|
+
maxWidth += scrollButtonWidth;
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
if (maxWidth >= measures.tabsContainerRect.width) return;
|
1106
|
+
|
1107
|
+
this._maxWidth = maxWidth;
|
1108
|
+
this._scrollCollapsed = true;
|
1109
|
+
this._measures = null;
|
1110
|
+
|
1111
|
+
return this.updateComplete;
|
1112
|
+
}
|
1043
1113
|
}
|
1044
1114
|
|
1045
1115
|
customElements.define('d2l-tabs', Tabs);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.94.
|
3
|
+
"version": "3.94.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",
|