@brightspace-ui/core 3.77.0 → 3.78.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.
@@ -273,6 +273,22 @@
|
|
273
273
|
</template>
|
274
274
|
</d2l-demo-snippet>
|
275
275
|
|
276
|
+
<h2>Popover (mobile tray)</h2>
|
277
|
+
<d2l-demo-snippet>
|
278
|
+
<template>
|
279
|
+
<d2l-button-subtle text="Open"></d2l-button-subtle>
|
280
|
+
<d2l-test-popover mobile-tray-location="inline-start" style="max-width: 400px;">
|
281
|
+
<d2l-link href="https://pirateipsum.me/" target="_blank">Pirate Ipsum</d2l-link>
|
282
|
+
<div>Sink me piracy Gold Road quarterdeck wherry long boat line pillage walk the plank Plate Fleet. Haul wind black spot strike colors deadlights lee Barbary Coast yo-ho-ho ballast gally Shiver me timbers. Sea Legs quarterdeck yard scourge of the seven seas coffer plunder lanyard holystone code of conduct belay.</div>
|
283
|
+
<d2l-button-subtle text="Close"></d2l-button-subtle>
|
284
|
+
</d2l-test-popover>
|
285
|
+
<script>
|
286
|
+
window.wireUpPopover(document.currentScript.parentNode);
|
287
|
+
</script>
|
288
|
+
</template>
|
289
|
+
</template>
|
290
|
+
</d2l-demo-snippet>
|
291
|
+
|
276
292
|
</d2l-demo-page>
|
277
293
|
|
278
294
|
<script>
|
@@ -12,6 +12,8 @@ const defaultPreferredPosition = {
|
|
12
12
|
span: 'all', // start, end, all
|
13
13
|
allowFlip: true
|
14
14
|
};
|
15
|
+
const minBackdropHeightMobile = 42;
|
16
|
+
const minBackdropWidthMobile = 30;
|
15
17
|
const pointerLength = 16;
|
16
18
|
const pointerRotatedLength = Math.SQRT2 * parseFloat(pointerLength);
|
17
19
|
const isSupported = ('popover' in HTMLElement.prototype);
|
@@ -30,6 +32,9 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
30
32
|
_maxWidth: { state: true },
|
31
33
|
_minHeight: { state: true },
|
32
34
|
_minWidth: { state: true },
|
35
|
+
_mobile: { type: Boolean, reflect: true, attribute: '_mobile' },
|
36
|
+
_mobileBreakpoint: { state: true },
|
37
|
+
_mobileTrayLocation: { type: String, reflect: true, attribute: '_mobile-tray-location' },
|
33
38
|
_noAutoClose: { state: true },
|
34
39
|
_noAutoFit: { state: true },
|
35
40
|
_noAutoFocus: { state: true },
|
@@ -140,6 +145,58 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
140
145
|
}
|
141
146
|
}
|
142
147
|
|
148
|
+
:host([_mobile][_mobile-tray-location]) .content-width {
|
149
|
+
position: fixed;
|
150
|
+
z-index: 1000;
|
151
|
+
}
|
152
|
+
|
153
|
+
:host([_mobile][_mobile-tray-location="inline-start"]) .content-width,
|
154
|
+
:host([_mobile][_mobile-tray-location="inline-end"]) .content-width {
|
155
|
+
inset-block-end: 0;
|
156
|
+
inset-block-start: 0;
|
157
|
+
}
|
158
|
+
|
159
|
+
:host([_mobile][_mobile-tray-location="inline-start"]) .content-width {
|
160
|
+
border-end-start-radius: 0;
|
161
|
+
border-start-start-radius: 0;
|
162
|
+
}
|
163
|
+
|
164
|
+
:host([_mobile][_mobile-tray-location="inline-end"]) .content-width {
|
165
|
+
border-end-end-radius: 0;
|
166
|
+
border-start-end-radius: 0;
|
167
|
+
}
|
168
|
+
|
169
|
+
:host([_mobile][_mobile-tray-location="block-end"]) .content-width {
|
170
|
+
border-end-end-radius: 0;
|
171
|
+
border-end-start-radius: 0;
|
172
|
+
inset-inline-start: 0;
|
173
|
+
}
|
174
|
+
|
175
|
+
:host([_mobile][_mobile-tray-location="inline-end"][opened]) .content-width {
|
176
|
+
inset-inline-end: 0;
|
177
|
+
}
|
178
|
+
|
179
|
+
:host([_mobile][_mobile-tray-location="inline-start"][opened]) .content-width {
|
180
|
+
inset-inline-start: 0;
|
181
|
+
}
|
182
|
+
|
183
|
+
:host([_mobile][_mobile-tray-location="block-end"][opened]) .content-width {
|
184
|
+
inset-block-end: 0;
|
185
|
+
}
|
186
|
+
|
187
|
+
:host([_mobile][_mobile-tray-location="inline-start"][opened]) .content-container,
|
188
|
+
:host([_mobile][_mobile-tray-location="inline-end"][opened]) .content-container {
|
189
|
+
height: 100vh;
|
190
|
+
}
|
191
|
+
|
192
|
+
:host([_mobile][_mobile-tray-location]) > .pointer {
|
193
|
+
display: none;
|
194
|
+
}
|
195
|
+
|
196
|
+
:host([_mobile][_mobile-tray-location][opened]) {
|
197
|
+
animation: none;
|
198
|
+
}
|
199
|
+
|
143
200
|
:host([_offscreen]) {
|
144
201
|
${_offscreenStyleDeclarations}
|
145
202
|
}
|
@@ -149,10 +206,12 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
149
206
|
constructor() {
|
150
207
|
super();
|
151
208
|
this.configure();
|
209
|
+
this._mobile = false;
|
152
210
|
this._useNativePopover = isSupported ? 'manual' : undefined;
|
153
211
|
this.#handleAncestorMutationBound = this.#handleAncestorMutation.bind(this);
|
154
212
|
this.#handleAutoCloseClickBound = this.#handleAutoCloseClick.bind(this);
|
155
213
|
this.#handleAutoCloseFocusBound = this.#handleAutoCloseFocus.bind(this);
|
214
|
+
this.#handleMobileResizeBound = this.#handleMobileResize.bind(this);
|
156
215
|
this.#handleResizeBound = this.#handleResize.bind(this);
|
157
216
|
this.#repositionBound = this.#reposition.bind(this);
|
158
217
|
}
|
@@ -161,6 +220,7 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
161
220
|
super.connectedCallback();
|
162
221
|
if (this._opened) {
|
163
222
|
this.#addAutoCloseHandlers();
|
223
|
+
this.#addMediaQueryHandlers();
|
164
224
|
this.#addRepositionHandlers();
|
165
225
|
}
|
166
226
|
}
|
@@ -168,6 +228,7 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
168
228
|
disconnectedCallback() {
|
169
229
|
super.disconnectedCallback();
|
170
230
|
this.#removeAutoCloseHandlers();
|
231
|
+
this.#removeMediaQueryHandlers();
|
171
232
|
this.#removeRepositionHandlers();
|
172
233
|
this.#clearDismissible();
|
173
234
|
}
|
@@ -181,6 +242,7 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
181
242
|
|
182
243
|
this._previousFocusableAncestor = null;
|
183
244
|
this.#removeAutoCloseHandlers();
|
245
|
+
this.#removeMediaQueryHandlers();
|
184
246
|
this.#removeRepositionHandlers();
|
185
247
|
this.#clearDismissible();
|
186
248
|
await this.updateComplete; // wait before applying focus to opener
|
@@ -195,6 +257,8 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
195
257
|
this._maxWidth = properties?.maxWidth;
|
196
258
|
this._minHeight = properties?.minHeight;
|
197
259
|
this._minWidth = properties?.minWidth;
|
260
|
+
this._mobileBreakpoint = properties?.mobileBreakpoint ?? 616;
|
261
|
+
this._mobileTrayLocation = properties?.mobileTrayLocation;
|
198
262
|
this._noAutoClose = properties?.noAutoClose ?? false;
|
199
263
|
this._noAutoFit = properties?.noAutoFit ?? false;
|
200
264
|
this._noAutoFocus = properties?.noAutoFocus ?? false;
|
@@ -217,6 +281,8 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
217
281
|
async open(applyFocus = true) {
|
218
282
|
if (this._opened) return;
|
219
283
|
|
284
|
+
this.#addMediaQueryHandlers();
|
285
|
+
|
220
286
|
this._rtl = document.documentElement.getAttribute('dir') === 'rtl';
|
221
287
|
this._applyFocus = applyFocus !== undefined ? applyFocus : true;
|
222
288
|
this._opened = true;
|
@@ -243,7 +309,16 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
243
309
|
|
244
310
|
renderPopover(content) {
|
245
311
|
|
246
|
-
const
|
312
|
+
const mobileTrayLocation = this._mobile ? this._mobileTrayLocation : null;
|
313
|
+
|
314
|
+
let stylesMap;
|
315
|
+
if (mobileTrayLocation === 'block-end') {
|
316
|
+
stylesMap = this.#getMobileTrayBlockStyleMaps();
|
317
|
+
} else if (mobileTrayLocation === 'inline-start' || mobileTrayLocation === 'inline-end') {
|
318
|
+
stylesMap = this.#getMobileTrayInlineStyleMaps();
|
319
|
+
} else {
|
320
|
+
stylesMap = this.#getStyleMaps();
|
321
|
+
}
|
247
322
|
const widthStyle = stylesMap['width'];
|
248
323
|
const contentStyle = stylesMap['content'];
|
249
324
|
|
@@ -301,9 +376,11 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
301
376
|
else return this.open(!this._noAutoFocus && applyFocus);
|
302
377
|
}
|
303
378
|
|
379
|
+
#mediaQueryList;
|
304
380
|
#handleAncestorMutationBound;
|
305
381
|
#handleAutoCloseClickBound;
|
306
382
|
#handleAutoCloseFocusBound;
|
383
|
+
#handleMobileResizeBound;
|
307
384
|
#handleResizeBound;
|
308
385
|
#repositionBound;
|
309
386
|
|
@@ -313,6 +390,12 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
313
390
|
document.addEventListener('click', this.#handleAutoCloseClickBound, { capture: true });
|
314
391
|
}
|
315
392
|
|
393
|
+
#addMediaQueryHandlers() {
|
394
|
+
this.#mediaQueryList = window.matchMedia(`(max-width: ${this._mobileBreakpoint - 1}px)`);
|
395
|
+
this._mobile = this.#mediaQueryList.matches;
|
396
|
+
this.#mediaQueryList.addEventListener?.('change', this.#handleMobileResizeBound);
|
397
|
+
}
|
398
|
+
|
316
399
|
#addRepositionHandlers() {
|
317
400
|
|
318
401
|
const isScrollable = (node, prop) => {
|
@@ -434,6 +517,123 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
434
517
|
return 'block-end';
|
435
518
|
}
|
436
519
|
|
520
|
+
#getMobileTrayBlockStyleMaps() {
|
521
|
+
|
522
|
+
let maxHeightOverride;
|
523
|
+
const availableHeight = Math.min(window.innerHeight, window.screen.height);
|
524
|
+
|
525
|
+
// default maximum height for bottom tray (42px margin)
|
526
|
+
const mobileTrayMaxHeightDefault = availableHeight - minBackdropHeightMobile;
|
527
|
+
if (this._maxHeight) {
|
528
|
+
// if maxHeight provided is smaller, use the maxHeight
|
529
|
+
maxHeightOverride = Math.min(mobileTrayMaxHeightDefault, this._maxHeight);
|
530
|
+
} else {
|
531
|
+
maxHeightOverride = mobileTrayMaxHeightDefault;
|
532
|
+
}
|
533
|
+
maxHeightOverride = `${maxHeightOverride}px`;
|
534
|
+
|
535
|
+
const widthOverride = '100vw';
|
536
|
+
|
537
|
+
const widthStyle = {
|
538
|
+
minWidth: widthOverride,
|
539
|
+
width: widthOverride,
|
540
|
+
maxHeight: maxHeightOverride,
|
541
|
+
};
|
542
|
+
|
543
|
+
const contentWidthStyle = {
|
544
|
+
// set width of content in addition to width container so header and footer borders are full width
|
545
|
+
width: widthOverride
|
546
|
+
};
|
547
|
+
|
548
|
+
const contentStyle = {
|
549
|
+
...contentWidthStyle,
|
550
|
+
maxHeight: maxHeightOverride,
|
551
|
+
};
|
552
|
+
|
553
|
+
return {
|
554
|
+
width: widthStyle,
|
555
|
+
content: contentStyle,
|
556
|
+
};
|
557
|
+
}
|
558
|
+
|
559
|
+
#getMobileTrayInlineStyleMaps() {
|
560
|
+
|
561
|
+
let maxWidthOverride = this._maxWidth;
|
562
|
+
const availableWidth = Math.min(window.innerWidth, window.screen.width);
|
563
|
+
|
564
|
+
// default maximum width for tray (30px margin)
|
565
|
+
const mobileTrayMaxWidthDefault = Math.min(availableWidth - minBackdropWidthMobile, 420);
|
566
|
+
if (maxWidthOverride) {
|
567
|
+
// if maxWidth provided is smaller, use the maxWidth
|
568
|
+
maxWidthOverride = Math.min(mobileTrayMaxWidthDefault, maxWidthOverride);
|
569
|
+
} else {
|
570
|
+
maxWidthOverride = mobileTrayMaxWidthDefault;
|
571
|
+
}
|
572
|
+
|
573
|
+
let minWidthOverride = this.minWidth;
|
574
|
+
// minimum size - 285px
|
575
|
+
const mobileTrayMinWidthDefault = 285;
|
576
|
+
if (minWidthOverride) {
|
577
|
+
// if minWidth provided is smaller, use the minumum width for tray
|
578
|
+
minWidthOverride = Math.max(mobileTrayMinWidthDefault, minWidthOverride);
|
579
|
+
} else {
|
580
|
+
minWidthOverride = mobileTrayMinWidthDefault;
|
581
|
+
}
|
582
|
+
|
583
|
+
// if no width property set, automatically size to maximum width
|
584
|
+
let widthOverride = this._width ? this._width : maxWidthOverride;
|
585
|
+
// ensure width is between minWidth and maxWidth
|
586
|
+
if (widthOverride && maxWidthOverride && widthOverride > (maxWidthOverride - 20)) widthOverride = maxWidthOverride - 20;
|
587
|
+
if (widthOverride && minWidthOverride && widthOverride < (minWidthOverride - 20)) widthOverride = minWidthOverride - 20;
|
588
|
+
maxWidthOverride = `${maxWidthOverride}px`;
|
589
|
+
minWidthOverride = `${minWidthOverride}px`;
|
590
|
+
|
591
|
+
const contentWidth = `${widthOverride + 18}px`;
|
592
|
+
// add 2 to content width since scrollWidth does not include border
|
593
|
+
const containerWidth = `${widthOverride + 20}px`;
|
594
|
+
|
595
|
+
const topOverride = (window.innerHeight > window.screen.height) ? window.pageYOffset : undefined;
|
596
|
+
|
597
|
+
let inlineEndOverride;
|
598
|
+
let inlineStartOverride;
|
599
|
+
if (this._mobileTrayLocation === 'inline-end') {
|
600
|
+
// On non-responsive pages, the innerWidth may be wider than the screen,
|
601
|
+
// override right to stick to right of viewport
|
602
|
+
inlineEndOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
|
603
|
+
} else if (this._mobileTrayLocation === 'inline-start') {
|
604
|
+
// On non-responsive pages, the innerWidth may be wider than the screen,
|
605
|
+
// override left to stick to left of viewport
|
606
|
+
inlineStartOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
|
607
|
+
}
|
608
|
+
|
609
|
+
if (minWidthOverride > maxWidthOverride) {
|
610
|
+
minWidthOverride = maxWidthOverride;
|
611
|
+
}
|
612
|
+
const widthStyle = {
|
613
|
+
maxWidth: maxWidthOverride,
|
614
|
+
minWidth: minWidthOverride,
|
615
|
+
width: containerWidth,
|
616
|
+
top: topOverride,
|
617
|
+
insetInlineStart: inlineStartOverride,
|
618
|
+
insetInlineEnd: inlineEndOverride
|
619
|
+
};
|
620
|
+
|
621
|
+
const contentWidthStyle = {
|
622
|
+
minWidth: minWidthOverride,
|
623
|
+
// set width of content in addition to width container so header and footer borders are full width
|
624
|
+
width: contentWidth,
|
625
|
+
};
|
626
|
+
|
627
|
+
const contentStyle = {
|
628
|
+
...contentWidthStyle,
|
629
|
+
};
|
630
|
+
|
631
|
+
return {
|
632
|
+
width : widthStyle,
|
633
|
+
content : contentStyle,
|
634
|
+
};
|
635
|
+
}
|
636
|
+
|
437
637
|
#getPointer() {
|
438
638
|
return this.shadowRoot.querySelector('.pointer');
|
439
639
|
}
|
@@ -567,8 +767,8 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
567
767
|
};
|
568
768
|
|
569
769
|
return {
|
570
|
-
|
571
|
-
|
770
|
+
width : widthStyle,
|
771
|
+
content : contentStyle
|
572
772
|
};
|
573
773
|
}
|
574
774
|
|
@@ -622,6 +822,11 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
622
822
|
this.dispatchEvent(new CustomEvent('d2l-popover-focus-enter', { detail: { applyFocus: this._applyFocus } }));
|
623
823
|
}
|
624
824
|
|
825
|
+
async #handleMobileResize() {
|
826
|
+
this._mobile = this.#mediaQueryList.matches;
|
827
|
+
if (this._opened) await this.#position();
|
828
|
+
}
|
829
|
+
|
625
830
|
#handleResize() {
|
626
831
|
this.resize();
|
627
832
|
}
|
@@ -720,6 +925,10 @@ export const PopoverMixin = superclass => class extends superclass {
|
|
720
925
|
document.removeEventListener('click', this.#handleAutoCloseClickBound, { capture: true });
|
721
926
|
}
|
722
927
|
|
928
|
+
#removeMediaQueryHandlers() {
|
929
|
+
this.#mediaQueryList?.removeEventListener?.('change', this.#handleMobileResizeBound);
|
930
|
+
}
|
931
|
+
|
723
932
|
#removeRepositionHandlers() {
|
724
933
|
this._openerIntersectionObserver?.unobserve(this._opener);
|
725
934
|
this._scrollablesObserved?.forEach(node => {
|
package/custom-elements.json
CHANGED
@@ -10619,6 +10619,11 @@
|
|
10619
10619
|
"description": "Min-width (undefined). Specify a number that would be the px value.",
|
10620
10620
|
"type": "number"
|
10621
10621
|
},
|
10622
|
+
{
|
10623
|
+
"name": "mobile-tray-location",
|
10624
|
+
"description": "Mobile tray location.",
|
10625
|
+
"type": "'inline-start'|'inline-end'|'block-end'"
|
10626
|
+
},
|
10622
10627
|
{
|
10623
10628
|
"name": "position-location",
|
10624
10629
|
"description": "Position the popover before or after the opener. Default is \"block-end\" (after).",
|
@@ -10685,6 +10690,12 @@
|
|
10685
10690
|
"description": "Min-width (undefined). Specify a number that would be the px value.",
|
10686
10691
|
"type": "number"
|
10687
10692
|
},
|
10693
|
+
{
|
10694
|
+
"name": "mobileTrayLocation",
|
10695
|
+
"attribute": "mobile-tray-location",
|
10696
|
+
"description": "Mobile tray location.",
|
10697
|
+
"type": "'inline-start'|'inline-end'|'block-end'"
|
10698
|
+
},
|
10688
10699
|
{
|
10689
10700
|
"name": "positionLocation",
|
10690
10701
|
"attribute": "position-location",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@brightspace-ui/core",
|
3
|
-
"version": "3.
|
3
|
+
"version": "3.78.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",
|