@brightspace-ui/core 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/dropdown/demo/dropdown-fixed.html +171 -0
- package/components/dropdown/dropdown-content-mixin.js +136 -30
- package/components/dropdown/dropdown-content-styles.js +18 -2
- package/components/dropdown/dropdown-content.js +2 -7
- package/components/dropdown/dropdown-menu.js +2 -7
- package/components/dropdown/dropdown-opener-mixin.js +19 -0
- package/components/dropdown/dropdown-opener-styles.js +3 -0
- package/components/dropdown/dropdown-tabs.js +2 -7
- package/package.json +1 -1
@@ -0,0 +1,171 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<link rel="stylesheet" href="../../demo/styles.css" type="text/css">
|
7
|
+
<script>
|
8
|
+
window.D2L = { LP: { Web: { UI: { Flags: { Flag: () => true } } } } };
|
9
|
+
</script>
|
10
|
+
<script type="module">
|
11
|
+
import '../../demo/demo-page.js';
|
12
|
+
import '../../button/button.js';
|
13
|
+
import '../../button/button-subtle.js';
|
14
|
+
import '../../dialog/dialog.js';
|
15
|
+
import '../dropdown.js';
|
16
|
+
import '../dropdown-content.js';
|
17
|
+
import { css, html, LitElement } from 'lit';
|
18
|
+
|
19
|
+
class MutationDemo extends LitElement {
|
20
|
+
|
21
|
+
static get properties() {
|
22
|
+
return {
|
23
|
+
count: { type: Number }
|
24
|
+
};
|
25
|
+
}
|
26
|
+
|
27
|
+
static get styles() {
|
28
|
+
return css`
|
29
|
+
:host {
|
30
|
+
border: 1px solid var(--d2l-color-tungsten);
|
31
|
+
border-radius: 6px;
|
32
|
+
display: block;
|
33
|
+
margin: 1rem;
|
34
|
+
position: relative;
|
35
|
+
}
|
36
|
+
div::before {
|
37
|
+
border-bottom: 1px solid black;
|
38
|
+
border-left: 1px solid black;
|
39
|
+
content: 'WC';
|
40
|
+
font-size: 0.7rem;
|
41
|
+
padding: 0 0.2rem;
|
42
|
+
position: absolute;
|
43
|
+
right: 0;
|
44
|
+
top: 0;
|
45
|
+
}
|
46
|
+
p {
|
47
|
+
margin: 0.5rem;
|
48
|
+
}
|
49
|
+
`;
|
50
|
+
}
|
51
|
+
|
52
|
+
constructor() {
|
53
|
+
super();
|
54
|
+
this.count = 1;
|
55
|
+
}
|
56
|
+
|
57
|
+
render() {
|
58
|
+
const elems = [];
|
59
|
+
for (let i = 0; i < this.count; i++) {
|
60
|
+
const newContent = document.createElement('p');
|
61
|
+
newContent.innerText = 'Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway.';
|
62
|
+
elems.push(newContent);
|
63
|
+
}
|
64
|
+
return html`<div>${elems}</div>`;
|
65
|
+
}
|
66
|
+
|
67
|
+
}
|
68
|
+
customElements.define('d2l-demo-mutation', MutationDemo);
|
69
|
+
|
70
|
+
</script>
|
71
|
+
</head>
|
72
|
+
|
73
|
+
<body unresolved>
|
74
|
+
|
75
|
+
<d2l-demo-page page-title="d2l-dropdown (fixed position)">
|
76
|
+
|
77
|
+
<h2>Dropdown (mutation testing)</h2>
|
78
|
+
|
79
|
+
<d2l-demo-snippet>
|
80
|
+
<template>
|
81
|
+
<div style="border: 1px solid var(--d2l-color-tungsten); border-radius: 6px; height: 600px; overflow: scroll;">
|
82
|
+
<d2l-demo-mutation count="1"></d2l-demo-mutation>
|
83
|
+
<div style="padding: 25px; position: relative;">
|
84
|
+
<d2l-dropdown prefer-fixed-positioning>
|
85
|
+
<d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
|
86
|
+
<d2l-dropdown-content max-width="400" prefer-fixed-positioning>
|
87
|
+
<d2l-button-subtle id="add-content1">Add to Light</d2l-button-subtle>
|
88
|
+
<d2l-button-subtle id="add-content2">Add to Shadow</d2l-button-subtle>
|
89
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
90
|
+
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
91
|
+
commodo consequat. </p>
|
92
|
+
<a href="http://www.desire2learn.com">D2L</a>
|
93
|
+
</d2l-dropdown-content>
|
94
|
+
</d2l-dropdown>
|
95
|
+
<br><br><br><br><br><br>
|
96
|
+
</div>
|
97
|
+
</div>
|
98
|
+
<script>
|
99
|
+
document.querySelector('#add-content1').addEventListener('click', e => {
|
100
|
+
const dropdown = e.target.parentNode.parentNode;
|
101
|
+
const newContent = document.createElement('p');
|
102
|
+
newContent.innerText = 'Trysail Sail ho Corsair red ensign hulk smartly boom jib rum gangway. Case shot Shiver me timbers gangplank crack Jennys tea cup ballast Blimey lee snow crow\'s nest rutters. Fluke jib scourge of the seven seas boatswain schooner gaff booty Jack Tar transom spirits.';
|
103
|
+
dropdown.parentNode.insertBefore(newContent, dropdown);
|
104
|
+
});
|
105
|
+
document.querySelector('#add-content2').addEventListener('click', () => {
|
106
|
+
const mutationDemo = document.querySelector('d2l-demo-mutation');
|
107
|
+
mutationDemo.count += 1;
|
108
|
+
});
|
109
|
+
</script>
|
110
|
+
</template>
|
111
|
+
</d2l-demo-snippet>
|
112
|
+
|
113
|
+
<h2>Dropdown (transform container)</h2>
|
114
|
+
|
115
|
+
<d2l-demo-snippet>
|
116
|
+
<template>
|
117
|
+
|
118
|
+
<div style="padding: 25px; transform: translate(0);">
|
119
|
+
<d2l-dropdown prefer-fixed-positioning>
|
120
|
+
<d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
|
121
|
+
<d2l-dropdown-content max-width="400" prefer-fixed-positioning>
|
122
|
+
<a href="https://youtu.be/9ze87zQFSak">Google</a>
|
123
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
124
|
+
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
125
|
+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
|
126
|
+
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
127
|
+
anim id est laborum.</p>
|
128
|
+
<a href="http://www.desire2learn.com">D2L</a>
|
129
|
+
</d2l-dropdown-content>
|
130
|
+
</d2l-dropdown>
|
131
|
+
</div>
|
132
|
+
|
133
|
+
</template>
|
134
|
+
</d2l-demo-snippet>
|
135
|
+
|
136
|
+
<h2>Dropdown (in a dialog)</h2>
|
137
|
+
|
138
|
+
<d2l-demo-snippet>
|
139
|
+
<template>
|
140
|
+
<d2l-button id="openDialog1">Show Dialog</d2l-button>
|
141
|
+
<d2l-dialog id="dialog1" title-text="Dialog Title">
|
142
|
+
<div>
|
143
|
+
<p>Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker</p>
|
144
|
+
<d2l-dropdown prefer-fixed-positioning>
|
145
|
+
<d2l-button class="d2l-dropdown-opener">Open it!</d2l-button>
|
146
|
+
<d2l-dropdown-content max-width="400" prefer-fixed-positioning>
|
147
|
+
<a href="https://youtu.be/9ze87zQFSak">Google</a>
|
148
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
149
|
+
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
150
|
+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
|
151
|
+
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
152
|
+
anim id est laborum.</p>
|
153
|
+
<a href="http://www.desire2learn.com">D2L</a>
|
154
|
+
</d2l-dropdown-content>
|
155
|
+
</d2l-dropdown>
|
156
|
+
</div>
|
157
|
+
<d2l-button slot="footer" primary data-dialog-action="ok">Click Me!</d2l-button>
|
158
|
+
<d2l-button slot="footer" data-dialog-action>Cancel</d2l-button>
|
159
|
+
</d2l-dialog>
|
160
|
+
<script>
|
161
|
+
document.querySelector('#openDialog1').addEventListener('click', () => {
|
162
|
+
document.querySelector('#dialog1').opened = true;
|
163
|
+
});
|
164
|
+
</script>
|
165
|
+
</template>
|
166
|
+
</d2l-demo-snippet>
|
167
|
+
|
168
|
+
</d2l-demo-page>
|
169
|
+
|
170
|
+
</body>
|
171
|
+
</html>
|
@@ -7,6 +7,7 @@ import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocus
|
|
7
7
|
import { classMap } from 'lit/directives/class-map.js';
|
8
8
|
import { html } from 'lit';
|
9
9
|
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
10
|
+
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
|
10
11
|
import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
|
11
12
|
import { styleMap } from 'lit/directives/style-map.js';
|
12
13
|
import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
|
@@ -17,6 +18,8 @@ const minBackdropHeightMobile = 42;
|
|
17
18
|
const minBackdropWidthMobile = 30;
|
18
19
|
const outerMarginTopBottom = 18;
|
19
20
|
const defaultVerticalOffset = 16;
|
21
|
+
const pointerLength = 16;
|
22
|
+
const pointerRotatedLength = Math.SQRT2 * parseFloat(pointerLength);
|
20
23
|
|
21
24
|
export const DropdownContentMixin = superclass => class extends LocalizeCoreElement(RtlMixin(superclass)) {
|
22
25
|
|
@@ -178,6 +181,14 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
178
181
|
reflect: true,
|
179
182
|
attribute: 'opened-above'
|
180
183
|
},
|
184
|
+
/**
|
185
|
+
* Temporary.
|
186
|
+
* @ignore
|
187
|
+
*/
|
188
|
+
preferFixedPositioning: {
|
189
|
+
type: Boolean,
|
190
|
+
attribute: 'prefer-fixed-positioning'
|
191
|
+
},
|
181
192
|
/**
|
182
193
|
* Optionally render a d2l-focus-trap around the dropdown content
|
183
194
|
* @type {boolean}
|
@@ -206,6 +217,11 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
206
217
|
attribute: 'dropdown-content',
|
207
218
|
reflect: true
|
208
219
|
},
|
220
|
+
_fixedPositioning: {
|
221
|
+
type: Boolean,
|
222
|
+
attribute: '_fixed-positioning',
|
223
|
+
reflect: true
|
224
|
+
},
|
209
225
|
_useMobileStyling: {
|
210
226
|
type: Boolean,
|
211
227
|
attribute: 'data-mobile',
|
@@ -220,8 +236,11 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
220
236
|
_contentHeight: {
|
221
237
|
type: Number
|
222
238
|
},
|
239
|
+
_pointerPosition: {
|
240
|
+
state: true
|
241
|
+
},
|
223
242
|
_position: {
|
224
|
-
|
243
|
+
state: true
|
225
244
|
},
|
226
245
|
_showBackdrop: {
|
227
246
|
type: Boolean
|
@@ -339,6 +358,12 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
339
358
|
});
|
340
359
|
}
|
341
360
|
|
361
|
+
willUpdate(changedProperties) {
|
362
|
+
if (this._fixedPositioning === undefined || changedProperties.has('preferFixedPositioning')) {
|
363
|
+
this._fixedPositioning = (window.D2L?.LP?.Web?.UI?.Flags.Flag('GAUD-131-dropdown-fixed-positioning', false) && this.preferFixedPositioning);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
342
367
|
close() {
|
343
368
|
const hide = () => {
|
344
369
|
this._closing = false;
|
@@ -453,6 +478,10 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
453
478
|
return opener;
|
454
479
|
}
|
455
480
|
|
481
|
+
__getPointer() {
|
482
|
+
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-pointer');
|
483
|
+
}
|
484
|
+
|
456
485
|
__getPositionContainer() {
|
457
486
|
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-position');
|
458
487
|
}
|
@@ -619,8 +648,8 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
619
648
|
}
|
620
649
|
|
621
650
|
const content = this.getContentContainer();
|
622
|
-
const header = this.__getContentTop();
|
623
|
-
const footer = this.__getContentBottom();
|
651
|
+
const header = this.__getContentTop(); // todo: rename
|
652
|
+
const footer = this.__getContentBottom(); // todo: rename
|
624
653
|
|
625
654
|
if (!this.noAutoFit) {
|
626
655
|
this._contentHeight = null;
|
@@ -629,15 +658,15 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
629
658
|
/* don't let dropdown content horizontally overflow viewport */
|
630
659
|
this._width = null;
|
631
660
|
|
632
|
-
const openerPosition = window.getComputedStyle(opener, null).getPropertyValue('position');
|
661
|
+
const openerPosition = window.getComputedStyle(opener, null).getPropertyValue('position'); // todo: cleanup when switched to fixed positioning
|
633
662
|
const boundingContainer = getBoundingAncestor(target.parentNode);
|
634
|
-
const boundingContainerRect = boundingContainer.getBoundingClientRect();
|
663
|
+
const boundingContainerRect = boundingContainer.getBoundingClientRect(); // todo: cleanup when switched to fixed positioning
|
635
664
|
const scrollHeight = boundingContainer.scrollHeight;
|
636
665
|
|
637
666
|
await this.updateComplete;
|
638
667
|
|
639
668
|
// position check in case consuming app (LMS) has overriden position to make content absolute wrt document
|
640
|
-
const bounded = (openerPosition === 'relative' && boundingContainer !== document.documentElement);
|
669
|
+
const bounded = (!this._fixedPositioning && openerPosition === 'relative' && boundingContainer !== document.documentElement);
|
641
670
|
|
642
671
|
const adjustPosition = async() => {
|
643
672
|
|
@@ -646,13 +675,16 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
646
675
|
const headerFooterHeight = header.getBoundingClientRect().height + footer.getBoundingClientRect().height;
|
647
676
|
|
648
677
|
const height = this.minHeight ? this.minHeight : Math.min(this.maxHeight ? this.maxHeight : Number.MAX_VALUE, contentRect.height + headerFooterHeight);
|
678
|
+
|
649
679
|
const spaceRequired = {
|
650
680
|
height: height + 10,
|
651
681
|
width: contentRect.width
|
652
682
|
};
|
683
|
+
|
653
684
|
let spaceAround;
|
654
685
|
let spaceAroundScroll;
|
655
686
|
if (bounded) {
|
687
|
+
|
656
688
|
spaceAround = this._constrainSpaceAround({
|
657
689
|
// allow for target offset + outer margin
|
658
690
|
above: targetRect.top - boundingContainerRect.top - this._verticalOffset - outerMarginTopBottom,
|
@@ -663,11 +695,14 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
663
695
|
// allow for outer margin
|
664
696
|
right: boundingContainerRect.right - targetRect.right - 20
|
665
697
|
}, spaceRequired, targetRect);
|
698
|
+
|
666
699
|
spaceAroundScroll = this._constrainSpaceAround({
|
667
700
|
above: targetRect.top - boundingContainerRect.top + boundingContainer.scrollTop,
|
668
701
|
below: scrollHeight - targetRect.bottom + boundingContainerRect.top - boundingContainer.scrollTop
|
669
702
|
}, spaceRequired, targetRect);
|
703
|
+
|
670
704
|
} else {
|
705
|
+
|
671
706
|
spaceAround = this._constrainSpaceAround({
|
672
707
|
// allow for target offset + outer margin
|
673
708
|
above: targetRect.top - this._verticalOffset - outerMarginTopBottom,
|
@@ -678,20 +713,20 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
678
713
|
// allow for outer margin
|
679
714
|
right: document.documentElement.clientWidth - targetRect.right - 15
|
680
715
|
}, spaceRequired, targetRect);
|
716
|
+
|
681
717
|
spaceAroundScroll = this._constrainSpaceAround({
|
682
718
|
above: targetRect.top + document.documentElement.scrollTop,
|
683
719
|
below: scrollHeight - targetRect.bottom - document.documentElement.scrollTop
|
684
720
|
}, spaceRequired, targetRect);
|
721
|
+
|
685
722
|
}
|
686
723
|
|
687
724
|
if (!ignoreVertical) {
|
688
725
|
this.openedAbove = this._getOpenedAbove(spaceAround, spaceAroundScroll, spaceRequired);
|
689
726
|
}
|
690
727
|
|
691
|
-
|
692
|
-
|
693
|
-
this._position = position;
|
694
|
-
}
|
728
|
+
this._position = this._getPosition(spaceAround, targetRect, contentRect);
|
729
|
+
this._pointerPosition = this._getPointerPosition(targetRect);
|
695
730
|
|
696
731
|
//Calculate height available to the dropdown contents for overflow because that is the only area capable of scrolling
|
697
732
|
const availableHeight = this.openedAbove ? spaceAround.above : spaceAround.below;
|
@@ -978,8 +1013,69 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
978
1013
|
return false;
|
979
1014
|
}
|
980
1015
|
|
981
|
-
|
982
|
-
const
|
1016
|
+
_getPointerPosition(targetRect) {
|
1017
|
+
const position = {};
|
1018
|
+
if (!this._fixedPositioning) return position;
|
1019
|
+
|
1020
|
+
const pointer = this.__getPointer();
|
1021
|
+
if (!pointer) return position;
|
1022
|
+
|
1023
|
+
const pointerRect = pointer.getBoundingClientRect();
|
1024
|
+
const isRTL = this.getAttribute('dir') === 'rtl';
|
1025
|
+
if (this.align === 'start' || this.align === 'end') {
|
1026
|
+
const pointerXAdjustment = Math.min(20 + ((pointerRotatedLength - pointerLength) / 2), (targetRect.width - pointerLength) / 2);
|
1027
|
+
if ((this.align === 'start' && !isRTL) || (this.align === 'end' && isRTL)) {
|
1028
|
+
position.left = targetRect.left + pointerXAdjustment;
|
1029
|
+
} else {
|
1030
|
+
position.right = window.innerWidth - targetRect.right + pointerXAdjustment;
|
1031
|
+
}
|
1032
|
+
} else {
|
1033
|
+
if (!isRTL) {
|
1034
|
+
position.left = targetRect.left + ((targetRect.width - pointerRect.width) / 2);
|
1035
|
+
} else {
|
1036
|
+
position.right = window.innerWidth - targetRect.left - ((targetRect.width + pointerRect.width) / 2);
|
1037
|
+
}
|
1038
|
+
}
|
1039
|
+
if (this.openedAbove) {
|
1040
|
+
position.bottom = window.innerHeight - targetRect.top + 8;
|
1041
|
+
} else {
|
1042
|
+
position.top = targetRect.top + targetRect.height + this._verticalOffset - 7;
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
return position;
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
_getPosition(spaceAround, targetRect, contentRect) {
|
1049
|
+
const position = {};
|
1050
|
+
const isRTL = this.getAttribute('dir') === 'rtl';
|
1051
|
+
const positionXAdjustment = this._getPositionXAdjustment(spaceAround, targetRect, contentRect);
|
1052
|
+
if (this._fixedPositioning) {
|
1053
|
+
if (positionXAdjustment !== null) {
|
1054
|
+
if (!isRTL) {
|
1055
|
+
position.left = targetRect.left + positionXAdjustment;
|
1056
|
+
} else {
|
1057
|
+
position.right = window.innerWidth - targetRect.left - targetRect.width + positionXAdjustment;
|
1058
|
+
}
|
1059
|
+
}
|
1060
|
+
if (this.openedAbove) {
|
1061
|
+
position.bottom = window.innerHeight - targetRect.top + this._verticalOffset;
|
1062
|
+
} else {
|
1063
|
+
position.top = targetRect.top + targetRect.height + this._verticalOffset;
|
1064
|
+
}
|
1065
|
+
} else {
|
1066
|
+
if (positionXAdjustment !== null) {
|
1067
|
+
if (!isRTL) {
|
1068
|
+
position.left = positionXAdjustment;
|
1069
|
+
} else {
|
1070
|
+
position.right = positionXAdjustment;
|
1071
|
+
}
|
1072
|
+
}
|
1073
|
+
}
|
1074
|
+
return position;
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
_getPositionXAdjustment(spaceAround, targetRect, contentRect) {
|
1078
|
+
const centerDelta = contentRect.width - targetRect.width;
|
983
1079
|
const contentXAdjustment = centerDelta / 2;
|
984
1080
|
if (!this.align && centerDelta <= 0) {
|
985
1081
|
return contentXAdjustment * -1;
|
@@ -1007,11 +1103,11 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
1007
1103
|
}
|
1008
1104
|
}
|
1009
1105
|
if (this.align === 'start' || this.align === 'end') {
|
1010
|
-
const shift = Math.min((
|
1106
|
+
const shift = Math.min((targetRect.width / 2) - (20 + pointerLength / 2), 0); // 20 ~= 1rem
|
1011
1107
|
if (this.align === 'start') {
|
1012
1108
|
return shift;
|
1013
1109
|
} else {
|
1014
|
-
return
|
1110
|
+
return targetRect.width - contentRect.width - shift;
|
1015
1111
|
}
|
1016
1112
|
}
|
1017
1113
|
return null;
|
@@ -1064,15 +1160,6 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
1064
1160
|
}
|
1065
1161
|
|
1066
1162
|
_renderContent() {
|
1067
|
-
const positionStyle = {};
|
1068
|
-
const isRTL = this.getAttribute('dir') === 'rtl';
|
1069
|
-
if (this._position) {
|
1070
|
-
if (!isRTL) {
|
1071
|
-
positionStyle.left = `${this._position}px`;
|
1072
|
-
} else {
|
1073
|
-
positionStyle.right = `${this._position}px`;
|
1074
|
-
}
|
1075
|
-
}
|
1076
1163
|
|
1077
1164
|
const mobileTrayRightLeft = this._useMobileStyling && (this.mobileTray === 'right' || this.mobileTray === 'left');
|
1078
1165
|
const mobileTrayBottom = this._useMobileStyling && (this.mobileTray === 'bottom');
|
@@ -1131,16 +1218,34 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
1131
1218
|
|
1132
1219
|
if (this.trapFocus) {
|
1133
1220
|
dropdownContentSlots = html`
|
1134
|
-
<d2l-focus-trap
|
1135
|
-
|
1136
|
-
?trap="${this.opened}">
|
1137
|
-
${dropdownContentSlots}
|
1221
|
+
<d2l-focus-trap @d2l-focus-trap-enter="${this._handleFocusTrapEnter}" ?trap="${this.opened}">
|
1222
|
+
${dropdownContentSlots}
|
1138
1223
|
</d2l-focus-trap>`;
|
1139
1224
|
}
|
1140
1225
|
|
1226
|
+
const positionStyle = {};
|
1227
|
+
if (this._position) {
|
1228
|
+
for (const prop in this._position) {
|
1229
|
+
positionStyle[prop] = `${this._position[prop]}px`;
|
1230
|
+
}
|
1231
|
+
}
|
1232
|
+
|
1141
1233
|
const dropdown = html`
|
1142
1234
|
<div class="d2l-dropdown-content-position" style=${styleMap(positionStyle)}>
|
1143
|
-
|
1235
|
+
${dropdownContentSlots}
|
1236
|
+
</div>
|
1237
|
+
`;
|
1238
|
+
|
1239
|
+
const pointerPositionStyle = {};
|
1240
|
+
if (this._pointerPosition) {
|
1241
|
+
for (const prop in this._pointerPosition) {
|
1242
|
+
pointerPositionStyle[prop] = `${this._pointerPosition[prop]}px`;
|
1243
|
+
}
|
1244
|
+
}
|
1245
|
+
|
1246
|
+
const pointer = html`
|
1247
|
+
<div class="d2l-dropdown-content-pointer" style="${styleMap(pointerPositionStyle)}">
|
1248
|
+
<div></div>
|
1144
1249
|
</div>
|
1145
1250
|
`;
|
1146
1251
|
|
@@ -1149,8 +1254,9 @@ export const DropdownContentMixin = superclass => class extends LocalizeCoreElem
|
|
1149
1254
|
<d2l-backdrop
|
1150
1255
|
for-target="d2l-dropdown-wrapper"
|
1151
1256
|
?shown="${this._showBackdrop}" >
|
1152
|
-
</d2l-backdrop
|
1153
|
-
|
1257
|
+
</d2l-backdrop>
|
1258
|
+
${pointer}`
|
1259
|
+
: html`${dropdown}${pointer}`;
|
1154
1260
|
}
|
1155
1261
|
|
1156
1262
|
};
|
@@ -23,6 +23,10 @@ export const dropdownContentStyles = css`
|
|
23
23
|
width: 100%;
|
24
24
|
z-index: 998; /* position on top of floating buttons */
|
25
25
|
}
|
26
|
+
:host([_fixed-positioning]) {
|
27
|
+
position: fixed;
|
28
|
+
top: 0;
|
29
|
+
}
|
26
30
|
|
27
31
|
:host([theme="dark"]) {
|
28
32
|
--d2l-dropdown-above-animation-name: d2l-dropdown-above-animation-dark;
|
@@ -44,6 +48,9 @@ export const dropdownContentStyles = css`
|
|
44
48
|
bottom: calc(100% + var(--d2l-dropdown-verticaloffset, 16px));
|
45
49
|
top: auto;
|
46
50
|
}
|
51
|
+
:host([_fixed-positioning][opened-above]) {
|
52
|
+
bottom: 0;
|
53
|
+
}
|
47
54
|
|
48
55
|
:host([data-mobile][opened]:not([mobile-tray])) {
|
49
56
|
animation: var(--d2l-dropdown-animation-name) 300ms ease;
|
@@ -59,18 +66,24 @@ export const dropdownContentStyles = css`
|
|
59
66
|
.d2l-dropdown-content-pointer {
|
60
67
|
clip: rect(-5px, 21px, 8px, -7px);
|
61
68
|
display: inline-block;
|
62
|
-
left: calc(50% - 7px);
|
69
|
+
left: calc(50% - 7px); /* todo: cleanup when switched to fixed positioning */
|
63
70
|
position: absolute;
|
64
|
-
top: -7px;
|
71
|
+
top: -7px; /* todo: cleanup when switched to fixed positioning */
|
65
72
|
z-index: 1;
|
66
73
|
}
|
74
|
+
:host([_fixed-positioning][dir="rtl"]) .d2l-dropdown-content-pointer {
|
75
|
+
left: auto;
|
76
|
+
}
|
77
|
+
|
67
78
|
:host([align="start"]) .d2l-dropdown-content-pointer,
|
68
79
|
:host([align="end"][dir="rtl"]) .d2l-dropdown-content-pointer {
|
80
|
+
/* todo: cleanup when switched to fixed positioning */
|
69
81
|
left: min(calc(1rem + ${(pointerRotatedLength - pointerLength) / 2}px), calc(50% - ${pointerLength / 2}px)); /* 1rem corresponds to .d2l-dropdown-content-container padding */
|
70
82
|
right: auto;
|
71
83
|
}
|
72
84
|
:host([align="end"]) .d2l-dropdown-content-pointer,
|
73
85
|
:host([align="start"][dir="rtl"]) .d2l-dropdown-content-pointer {
|
86
|
+
/* todo: cleanup when switched to fixed positioning */
|
74
87
|
left: auto;
|
75
88
|
right: min(calc(1rem + ${(pointerRotatedLength - pointerLength) / 2}px), calc(50% - ${pointerLength / 2}px)); /* 1rem corresponds to .d2l-dropdown-content-container padding */
|
76
89
|
}
|
@@ -91,6 +104,9 @@ export const dropdownContentStyles = css`
|
|
91
104
|
clip: rect(9px, 21px, 22px, -3px);
|
92
105
|
top: auto;
|
93
106
|
}
|
107
|
+
:host([_fixed-positioning][opened-above]) .d2l-dropdown-content-pointer {
|
108
|
+
bottom: auto;
|
109
|
+
}
|
94
110
|
|
95
111
|
:host([opened-above]) .d2l-dropdown-content-pointer > div {
|
96
112
|
box-shadow: 4px 4px 12px -5px rgba(32, 33, 34, 0.2); /* ferrite */
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import { html, LitElement } from 'lit';
|
2
1
|
import { DropdownContentMixin } from './dropdown-content-mixin.js';
|
3
2
|
import { dropdownContentStyles } from './dropdown-content-styles.js';
|
3
|
+
import { LitElement } from 'lit';
|
4
4
|
|
5
5
|
/**
|
6
6
|
* A generic container for dropdown content. It provides behavior such as sizing, positioning, and managing focus gain/loss.
|
@@ -16,12 +16,7 @@ class DropdownContent extends DropdownContentMixin(LitElement) {
|
|
16
16
|
}
|
17
17
|
|
18
18
|
render() {
|
19
|
-
return
|
20
|
-
${this._renderContent()}
|
21
|
-
<div class="d2l-dropdown-content-pointer">
|
22
|
-
<div></div>
|
23
|
-
</div>
|
24
|
-
`;
|
19
|
+
return this._renderContent();
|
25
20
|
}
|
26
21
|
|
27
22
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { css,
|
1
|
+
import { css, LitElement } from 'lit';
|
2
2
|
import { DropdownContentMixin } from './dropdown-content-mixin.js';
|
3
3
|
import { dropdownContentStyles } from './dropdown-content-styles.js';
|
4
4
|
import { ThemeMixin } from '../../mixins/theme/theme-mixin.js';
|
@@ -91,12 +91,7 @@ class DropdownMenu extends ThemeMixin(DropdownContentMixin(LitElement)) {
|
|
91
91
|
}
|
92
92
|
|
93
93
|
render() {
|
94
|
-
return
|
95
|
-
${this._renderContent()}
|
96
|
-
<div class="d2l-dropdown-content-pointer">
|
97
|
-
<div></div>
|
98
|
-
</div>
|
99
|
-
`;
|
94
|
+
return this._renderContent();
|
100
95
|
}
|
101
96
|
|
102
97
|
__getMenuElement() {
|
@@ -44,6 +44,19 @@ export const DropdownOpenerMixin = superclass => class extends superclass {
|
|
44
44
|
type: Boolean,
|
45
45
|
attribute: 'open-on-hover'
|
46
46
|
},
|
47
|
+
/**
|
48
|
+
* Temporary.
|
49
|
+
* @ignore
|
50
|
+
*/
|
51
|
+
preferFixedPositioning: {
|
52
|
+
type: Boolean,
|
53
|
+
attribute: 'prefer-fixed-positioning'
|
54
|
+
},
|
55
|
+
_fixedPositioning: {
|
56
|
+
type: Boolean,
|
57
|
+
attribute: '_fixed-positioning',
|
58
|
+
reflect: true,
|
59
|
+
},
|
47
60
|
_isHovering: { type: Boolean },
|
48
61
|
_isOpenedViaClick: { type: Boolean },
|
49
62
|
_isFading: { type: Boolean }
|
@@ -123,6 +136,12 @@ export const DropdownOpenerMixin = superclass => class extends superclass {
|
|
123
136
|
}
|
124
137
|
}
|
125
138
|
|
139
|
+
willUpdate(changedProperties) {
|
140
|
+
if (changedProperties.has('preferFixedPositioning')) {
|
141
|
+
this._fixedPositioning = (window.D2L?.LP?.Web?.UI?.Flags.Flag('GAUD-131-dropdown-fixed-positioning', false) && this.preferFixedPositioning);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
126
145
|
/* used by open-on-hover option */
|
127
146
|
async closeDropdown(fadeOut) {
|
128
147
|
this.dropdownOpened = false;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { css,
|
1
|
+
import { css, LitElement } from 'lit';
|
2
2
|
import { DropdownContentMixin } from './dropdown-content-mixin.js';
|
3
3
|
import { dropdownContentStyles } from './dropdown-content-styles.js';
|
4
4
|
|
@@ -28,12 +28,7 @@ class DropdownTabs extends DropdownContentMixin(LitElement) {
|
|
28
28
|
}
|
29
29
|
|
30
30
|
render() {
|
31
|
-
return
|
32
|
-
${this._renderContent()}
|
33
|
-
<div class="d2l-dropdown-content-pointer">
|
34
|
-
<div></div>
|
35
|
-
</div>
|
36
|
-
`;
|
31
|
+
return this._renderContent();
|
37
32
|
}
|
38
33
|
|
39
34
|
_getTabsElement() {
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.7.0",
|
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",
|