@brightspace-ui/core 3.219.6 → 3.219.8
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/README.md +0 -1
- package/components/dropdown/dropdown-content.js +10 -38
- package/components/dropdown/dropdown-menu.js +126 -316
- package/components/dropdown/dropdown-opener-mixin.js +3 -28
- package/components/dropdown/dropdown-popover-mixin.js +2 -7
- package/components/dropdown/dropdown-tabs.js +45 -113
- package/components/expand-collapse/expand-collapse-content.js +4 -3
- package/components/filter/filter.js +1 -20
- package/components/table/table-col-sort-button.js +1 -2
- package/components/table/table-wrapper.js +1 -2
- package/custom-elements.json +99 -144
- package/helpers/README.md +0 -14
- package/helpers/demo/prism.html +0 -1784
- package/helpers/visualReady.js +0 -2
- package/package.json +1 -1
- package/components/dropdown/dropdown-content-mixin.js +0 -1299
- package/components/dropdown/dropdown-content-styles.js +0 -327
package/helpers/demo/prism.html
CHANGED
|
@@ -325,1790 +325,6 @@ border: 1px solid #cd2026;
|
|
|
325
325
|
</tbody>
|
|
326
326
|
</table>
|
|
327
327
|
|
|
328
|
-
<h2 class="d2l-heading-3">Large Samples</h2>
|
|
329
|
-
|
|
330
|
-
<pre class="d2l-code d2l-code-dark line-numbers"><code class="language-markup"><!DOCTYPE html>
|
|
331
|
-
<html lang="en">
|
|
332
|
-
<head>
|
|
333
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
334
|
-
<meta charset="UTF-8">
|
|
335
|
-
<link rel="stylesheet" href="../../demo/styles.css" type="text/css">
|
|
336
|
-
<script type="module">
|
|
337
|
-
import '../../demo/demo-page.js';
|
|
338
|
-
import '../../button/button.js';
|
|
339
|
-
import '../../button/button-icon.js';
|
|
340
|
-
import '../../dropdown/dropdown-more.js';
|
|
341
|
-
import '../../dropdown/dropdown-content.js';
|
|
342
|
-
import '../../status-indicator/status-indicator.js';
|
|
343
|
-
import '../../tooltip/tooltip.js';
|
|
344
|
-
import '../card.js';
|
|
345
|
-
import '../card-loading-shimmer.js';
|
|
346
|
-
import '../card-content-meta.js';
|
|
347
|
-
import '../card-content-title.js';
|
|
348
|
-
import '../card-footer-link.js';
|
|
349
|
-
</script>
|
|
350
|
-
<style>
|
|
351
|
-
d2l-card {
|
|
352
|
-
vertical-align: top;
|
|
353
|
-
}
|
|
354
|
-
.subtle-demo {
|
|
355
|
-
background-color: #f6f7f8;
|
|
356
|
-
padding: 20px;
|
|
357
|
-
}
|
|
358
|
-
.badge {
|
|
359
|
-
text-align: center;
|
|
360
|
-
}
|
|
361
|
-
.badge > img {
|
|
362
|
-
background-color: white;
|
|
363
|
-
border: 1px solid #202122; /* ferrite */
|
|
364
|
-
border-radius: 6px;
|
|
365
|
-
height: 70px;
|
|
366
|
-
object-fit: cover;
|
|
367
|
-
object-position: center;
|
|
368
|
-
}
|
|
369
|
-
.badge-status {
|
|
370
|
-
background-color: white;
|
|
371
|
-
border: 1px solid white;
|
|
372
|
-
border-radius: 0.6rem;
|
|
373
|
-
display: inline-block;
|
|
374
|
-
}
|
|
375
|
-
div[slot="footer"] {
|
|
376
|
-
display: inline-block;
|
|
377
|
-
}
|
|
378
|
-
#toggleLoading {
|
|
379
|
-
margin-top: 20px;
|
|
380
|
-
}
|
|
381
|
-
</style>
|
|
382
|
-
</head>
|
|
383
|
-
<body unresolved>
|
|
384
|
-
|
|
385
|
-
<d2l-demo-page page-title="d2l-card">
|
|
386
|
-
|
|
387
|
-
<h2>Subtle Card (badges, no-link)</h2>
|
|
388
|
-
|
|
389
|
-
<d2l-demo-snippet>
|
|
390
|
-
<template>
|
|
391
|
-
<div class="subtle-demo">
|
|
392
|
-
|
|
393
|
-
<d2l-card subtle align-center text="Image Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
|
|
394
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
395
|
-
<div slot="badge" class="badge">
|
|
396
|
-
<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Stick_Figure.svg/340px-Stick_Figure.svg.png">
|
|
397
|
-
</div>
|
|
398
|
-
<div slot="content">Image Badge</div>
|
|
399
|
-
</d2l-card>
|
|
400
|
-
|
|
401
|
-
<d2l-card subtle align-center text="Status Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
|
|
402
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
403
|
-
<div class="badge-status" slot="badge">
|
|
404
|
-
<d2l-status-indicator text="Success" state="success"></d2l-status-indicator>
|
|
405
|
-
</div>
|
|
406
|
-
<div slot="content">Status Badge</div>
|
|
407
|
-
</d2l-card>
|
|
408
|
-
|
|
409
|
-
<d2l-card subtle align-center text="No Link" style="height: 280px; width: 245px;">
|
|
410
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
411
|
-
<div slot="content">No Link</div>
|
|
412
|
-
</d2l-card>
|
|
413
|
-
|
|
414
|
-
</div>
|
|
415
|
-
</template>
|
|
416
|
-
</d2l-demo-snippet>
|
|
417
|
-
|
|
418
|
-
<h2>Subtle Card (header actions, meta-content, footer links)</h2>
|
|
419
|
-
|
|
420
|
-
<d2l-demo-snippet>
|
|
421
|
-
<template>
|
|
422
|
-
<div class="subtle-demo">
|
|
423
|
-
|
|
424
|
-
<d2l-card subtle align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
|
|
425
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
426
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
427
|
-
<d2l-dropdown-content>
|
|
428
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
429
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
430
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
431
|
-
</d2l-dropdown-content>
|
|
432
|
-
</d2l-dropdown-more>
|
|
433
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
434
|
-
<div slot="content"><div>Hydrology</div><d2l-card-content-meta>This is some extra meta data that will fill the content slot of the card.</d2l-card-content-meta></div>
|
|
435
|
-
<div slot="footer">
|
|
436
|
-
<d2l-card-footer-link id="googleDriveLink1" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
|
|
437
|
-
<d2l-tooltip slot="tooltip" for="googleDriveLink1">Go to Google Drive</d2l-tooltip>
|
|
438
|
-
</d2l-card-footer-link>
|
|
439
|
-
<d2l-card-footer-link id="rssFeedLink1" icon="tier1:rss" text="RSS Feed" secondary-count="1">
|
|
440
|
-
<d2l-tooltip slot="tooltip" for="rssFeedLink1">RSS Feed</d2l-tooltip>
|
|
441
|
-
</d2l-card-footer-link>
|
|
442
|
-
<d2l-card-footer-link id="outcomesLink1" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
|
|
443
|
-
<d2l-tooltip slot="tooltip" for="outcomesLink1">Outcomes</d2l-tooltip>
|
|
444
|
-
</d2l-card-footer-link>
|
|
445
|
-
<d2l-card-footer-link id="assignmentsLink1" icon="tier1:assignments" text="Assignments" secondary-count="3">
|
|
446
|
-
<d2l-tooltip slot="tooltip" position="top" style="width: 100%;" for="assignmentsLink1">You have 3 assignments due tomorrow.</d2l-tooltip>
|
|
447
|
-
</d2l-card-footer-link>
|
|
448
|
-
</div>
|
|
449
|
-
</d2l-card>
|
|
450
|
-
|
|
451
|
-
<d2l-card subtle align-center text="Grade 2" href="https://en.wikipedia.org/wiki/Second_grade" style="height: 300px; width: 245px;">
|
|
452
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/e5fd575a-bc14-4a80-89e1-46f349a76178/tile-low-density-max-size.jpg">
|
|
453
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
454
|
-
<d2l-dropdown-content><div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div></d2l-dropdown-content>
|
|
455
|
-
</d2l-dropdown-more>
|
|
456
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
457
|
-
<div slot="content">Grade 2</div>
|
|
458
|
-
<div slot="footer">
|
|
459
|
-
<d2l-card-footer-link id="googleDriveLink2" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
|
|
460
|
-
<d2l-tooltip slot="tooltip" for="googleDriveLink2">Go to Google Drive</d2l-tooltip>
|
|
461
|
-
</d2l-card-footer-link>
|
|
462
|
-
<d2l-card-footer-link id="rssFeedLink2" icon="tier1:rss" text="RSS Feed" secondary-count="1">
|
|
463
|
-
<d2l-tooltip slot="tooltip" for="rssFeedLink2">RSS Feed</d2l-tooltip>
|
|
464
|
-
</d2l-card-footer-link>
|
|
465
|
-
<d2l-card-footer-link id="outcomesLink2" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
|
|
466
|
-
<d2l-tooltip slot="tooltip" for="outcomesLink2">Outcomes</d2l-tooltip>
|
|
467
|
-
</d2l-card-footer-link>
|
|
468
|
-
</div>
|
|
469
|
-
</d2l-card>
|
|
470
|
-
|
|
471
|
-
<d2l-card subtle align-center text="Painting" href="https://en.wikipedia.org/wiki/Painting" style="height: 300px; width: 245px;">
|
|
472
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-low-density-max-size.jpg">
|
|
473
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
474
|
-
<d2l-dropdown-content><div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div></d2l-dropdown-content>
|
|
475
|
-
</d2l-dropdown-more>
|
|
476
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
477
|
-
<div slot="content">Painting</div>
|
|
478
|
-
<d2l-button slot="footer" style="width: 100%;">Shiny Button</d2l-button>
|
|
479
|
-
</d2l-card>
|
|
480
|
-
|
|
481
|
-
</div>
|
|
482
|
-
</template>
|
|
483
|
-
</d2l-demo-snippet>
|
|
484
|
-
|
|
485
|
-
<h2>Card (badges, no-link)</h2>
|
|
486
|
-
|
|
487
|
-
<d2l-demo-snippet>
|
|
488
|
-
<template>
|
|
489
|
-
|
|
490
|
-
<d2l-card align-center text="Image Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
|
|
491
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
492
|
-
<div slot="badge" class="badge">
|
|
493
|
-
<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Stick_Figure.svg/340px-Stick_Figure.svg.png">
|
|
494
|
-
</div>
|
|
495
|
-
<div slot="content">Image Badge</div>
|
|
496
|
-
</d2l-card>
|
|
497
|
-
|
|
498
|
-
<d2l-card align-center text="Status Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
|
|
499
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
500
|
-
<div class="badge-status" slot="badge">
|
|
501
|
-
<d2l-status-indicator text="Success" state="success"></d2l-status-indicator>
|
|
502
|
-
</div>
|
|
503
|
-
<div slot="content">Status Badge</div>
|
|
504
|
-
</d2l-card>
|
|
505
|
-
|
|
506
|
-
<d2l-card align-center text="No Link" style="height: 280px; width: 245px;">
|
|
507
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
508
|
-
<div slot="content">No Link</div>
|
|
509
|
-
</d2l-card>
|
|
510
|
-
|
|
511
|
-
</template>
|
|
512
|
-
</d2l-demo-snippet>
|
|
513
|
-
|
|
514
|
-
<h2>Card (header actions, meta-content, footer links)</h2>
|
|
515
|
-
|
|
516
|
-
<d2l-demo-snippet>
|
|
517
|
-
<template>
|
|
518
|
-
|
|
519
|
-
<d2l-card align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
|
|
520
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
521
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
522
|
-
<d2l-dropdown-content>
|
|
523
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
524
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
525
|
-
<div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div>
|
|
526
|
-
</d2l-dropdown-content>
|
|
527
|
-
</d2l-dropdown-more>
|
|
528
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
529
|
-
<div slot="content"><div>Hydrology</div><d2l-card-content-meta>This is some extra meta data that will fill the content slot of the card.</d2l-card-content-meta></div>
|
|
530
|
-
<div slot="footer">
|
|
531
|
-
<d2l-card-footer-link id="googleDriveLink3" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
|
|
532
|
-
<d2l-tooltip slot="tooltip" for="googleDriveLink3">Go to Google Drive</d2l-tooltip>
|
|
533
|
-
</d2l-card-footer-link>
|
|
534
|
-
<d2l-card-footer-link id="rssFeedLink3" icon="tier1:rss" text="RSS Feed" secondary-count="1">
|
|
535
|
-
<d2l-tooltip slot="tooltip" for="rssFeedLink3">RSS Feed</d2l-tooltip>
|
|
536
|
-
</d2l-card-footer-link>
|
|
537
|
-
<d2l-card-footer-link id="outcomesLink3" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
|
|
538
|
-
<d2l-tooltip slot="tooltip" for="outcomesLink3">Outcomes</d2l-tooltip>
|
|
539
|
-
</d2l-card-footer-link>
|
|
540
|
-
<d2l-card-footer-link id="assignmentsLink3" icon="tier1:assignments" text="Assignments" secondary-count="3">
|
|
541
|
-
<d2l-tooltip slot="tooltip" position="top" style="width: 100%;" for="assignmentsLink3">You have 3 assignments due tomorrow.</d2l-tooltip>
|
|
542
|
-
</d2l-card-footer-link>
|
|
543
|
-
</div>
|
|
544
|
-
</d2l-card>
|
|
545
|
-
|
|
546
|
-
<d2l-card align-center text="Painting" href="https://en.wikipedia.org/wiki/Painting" style="height: 300px; width: 245px;">
|
|
547
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/63b162ab-b582-4bf9-8c1d-1dad04714121/tile-low-density-max-size.jpg">
|
|
548
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
549
|
-
<d2l-dropdown-content><div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div></d2l-dropdown-content>
|
|
550
|
-
</d2l-dropdown-more>
|
|
551
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
552
|
-
<div slot="content">Painting</div>
|
|
553
|
-
<d2l-button slot="footer" style="width: 100%;">Shiny Button</d2l-button>
|
|
554
|
-
</d2l-card>
|
|
555
|
-
|
|
556
|
-
<d2l-card align-center text="Grade 2" href="https://en.wikipedia.org/wiki/Second_grade" style="height: 300px; width: 245px;">
|
|
557
|
-
<img slot="header" alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/e5fd575a-bc14-4a80-89e1-46f349a76178/tile-low-density-max-size.jpg">
|
|
558
|
-
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
|
|
559
|
-
<d2l-dropdown-content><div>This is where you could put the super cool features for your card!</div><br><div>As with all dropdowns, you can choose between a generic dropdown container, or a menu specific one.</div></d2l-dropdown-content>
|
|
560
|
-
</d2l-dropdown-more>
|
|
561
|
-
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
|
|
562
|
-
<div slot="content">Grade 2</div>
|
|
563
|
-
<div slot="footer">
|
|
564
|
-
<d2l-card-footer-link id="googleDriveLink4" icon="tier1:google-drive" text="Google Drive" secondary-count-type="count" secondary-count="100" href="https://www.google.ca/drive/">
|
|
565
|
-
<d2l-tooltip slot="tooltip" for="googleDriveLink4">Go to Google Drive</d2l-tooltip>
|
|
566
|
-
</d2l-card-footer-link>
|
|
567
|
-
<d2l-card-footer-link id="rssFeedLink4" icon="tier1:rss" text="RSS Feed" secondary-count-type="count" secondary-count="1">
|
|
568
|
-
<d2l-tooltip slot="tooltip" for="rssFeedLink4">RSS Feed</d2l-tooltip>
|
|
569
|
-
</d2l-card-footer-link>
|
|
570
|
-
<d2l-card-footer-link id="outcomesLink4" icon="tier1:outcomes" text="Outcomes" secondary-count-type="count" secondary-count="5">
|
|
571
|
-
<d2l-tooltip slot="tooltip" for="outcomesLink4">Outcomes</d2l-tooltip>
|
|
572
|
-
</d2l-card-footer-link>
|
|
573
|
-
</div>
|
|
574
|
-
</d2l-card>
|
|
575
|
-
|
|
576
|
-
</template>
|
|
577
|
-
</d2l-demo-snippet>
|
|
578
|
-
|
|
579
|
-
<h2>Card (with header loading)</h2>
|
|
580
|
-
|
|
581
|
-
<d2l-demo-snippet>
|
|
582
|
-
<template>
|
|
583
|
-
|
|
584
|
-
<d2l-card align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
|
|
585
|
-
<d2l-card-loading-shimmer slot="header" loading style="display: block; height: 103.5px; width: 100%;">
|
|
586
|
-
<img alt="" style="display: block; width: 100%;" src="https://s.brightspace.com/course-images/images/38e839b1-37fa-470c-8830-b189ce4ae134/tile-high-density-max-size.jpg">
|
|
587
|
-
</d2l-card-loading-shimmer>
|
|
588
|
-
<div slot="content"><div>Hydrology</div><d2l-card-content-meta>This is some extra meta data that will fill the content slot of the card.</d2l-card-content-meta></div>
|
|
589
|
-
</d2l-card>
|
|
590
|
-
|
|
591
|
-
<div>
|
|
592
|
-
<d2l-button id="toggleLoading">Toggle Loading State</d2l-button>
|
|
593
|
-
</div>
|
|
594
|
-
|
|
595
|
-
<script>
|
|
596
|
-
document.querySelector('#toggleLoading').addEventListener('click', () => {
|
|
597
|
-
const loadingContainer = document.querySelector('d2l-card-loading-shimmer');
|
|
598
|
-
loadingContainer.loading = !loadingContainer.loading;
|
|
599
|
-
});
|
|
600
|
-
</script>
|
|
601
|
-
</template>
|
|
602
|
-
</d2l-demo-snippet>
|
|
603
|
-
|
|
604
|
-
</d2l-demo-page>
|
|
605
|
-
</body>
|
|
606
|
-
</html>
|
|
607
|
-
</code></pre>
|
|
608
|
-
|
|
609
|
-
<pre class="d2l-code d2l-code-dark line-numbers"><code class="language-javascript">import '../backdrop/backdrop.js';
|
|
610
|
-
import '../button/button.js';
|
|
611
|
-
import '../focus-trap/focus-trap.js';
|
|
612
|
-
import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
|
|
613
|
-
import { findComposedAncestor, getBoundingAncestor, isComposedAncestor, isVisible } from '../../helpers/dom.js';
|
|
614
|
-
import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocusableAncestor } from '../../helpers/focus.js';
|
|
615
|
-
import { classMap } from 'lit/directives/class-map.js';
|
|
616
|
-
import { html } from 'lit';
|
|
617
|
-
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
|
|
618
|
-
import { styleMap } from 'lit/directives/style-map.js';
|
|
619
|
-
import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
|
|
620
|
-
|
|
621
|
-
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
622
|
-
const minBackdropHeightMobile = 42;
|
|
623
|
-
const minBackdropWidthMobile = 30;
|
|
624
|
-
const outerMarginTopBottom = 18;
|
|
625
|
-
const defaultVerticalOffset = 16;
|
|
626
|
-
|
|
627
|
-
export const DropdownContentMixin = superclass => class extends LocalizeCoreElement(superclass) {
|
|
628
|
-
|
|
629
|
-
static get properties() {
|
|
630
|
-
return {
|
|
631
|
-
/**
|
|
632
|
-
* Optionally align dropdown to either start or end. If not set, the dropdown will attempt be centred.
|
|
633
|
-
* @type {'start'|'end'}
|
|
634
|
-
*/
|
|
635
|
-
align: {
|
|
636
|
-
type: String,
|
|
637
|
-
reflect: true
|
|
638
|
-
},
|
|
639
|
-
/**
|
|
640
|
-
* Optionally provide boundaries to where the dropdown will appear. Valid properties are "above", "below", "left", and "right".
|
|
641
|
-
* @type {object}
|
|
642
|
-
*/
|
|
643
|
-
boundary: {
|
|
644
|
-
type: Object,
|
|
645
|
-
},
|
|
646
|
-
/**
|
|
647
|
-
* Override default max-width (undefined). Specify a number that would be the px value.
|
|
648
|
-
* @type {number}
|
|
649
|
-
*/
|
|
650
|
-
maxWidth: {
|
|
651
|
-
type: Number,
|
|
652
|
-
reflect: true,
|
|
653
|
-
attribute: 'max-width'
|
|
654
|
-
},
|
|
655
|
-
/**
|
|
656
|
-
* Override default min-width (undefined). Specify a number that would be the px value.
|
|
657
|
-
* @type {number}
|
|
658
|
-
*/
|
|
659
|
-
minWidth: {
|
|
660
|
-
type: Number,
|
|
661
|
-
reflect: true,
|
|
662
|
-
attribute: 'min-width'
|
|
663
|
-
},
|
|
664
|
-
/**
|
|
665
|
-
* Override max-height. Note that the default behaviour is to be as tall as necessary within the viewport, so this property is usually not needed.
|
|
666
|
-
* @type {number}
|
|
667
|
-
*/
|
|
668
|
-
maxHeight: {
|
|
669
|
-
type: Number,
|
|
670
|
-
attribute: 'max-height'
|
|
671
|
-
},
|
|
672
|
-
/**
|
|
673
|
-
* Override the breakpoint at which mobile styling is used. Defaults to 616px.
|
|
674
|
-
* @type {number}
|
|
675
|
-
*/
|
|
676
|
-
mobileBreakpointOverride: {
|
|
677
|
-
type: Number,
|
|
678
|
-
attribute: 'mobile-breakpoint'
|
|
679
|
-
},
|
|
680
|
-
/**
|
|
681
|
-
* Override default height used for required space when `no-auto-fit` is true. Specify a number that would be the px value. Note that the default behaviour is to be as tall as necessary within the viewport, so this property is usually not needed.
|
|
682
|
-
* @type {number}
|
|
683
|
-
*/
|
|
684
|
-
minHeight: {
|
|
685
|
-
type: Number,
|
|
686
|
-
reflect: true,
|
|
687
|
-
attribute: 'min-height'
|
|
688
|
-
},
|
|
689
|
-
/**
|
|
690
|
-
* Opt-out of showing a close button in the footer of tray-style mobile dropdowns.
|
|
691
|
-
* @type {boolean}
|
|
692
|
-
*/
|
|
693
|
-
noMobileCloseButton: {
|
|
694
|
-
type: Boolean,
|
|
695
|
-
reflect: true,
|
|
696
|
-
attribute: 'no-mobile-close-button'
|
|
697
|
-
},
|
|
698
|
-
/**
|
|
699
|
-
* Mobile dropdown style.
|
|
700
|
-
* @type {'left'|'right'|'bottom'}
|
|
701
|
-
*/
|
|
702
|
-
mobileTray: {
|
|
703
|
-
type: String,
|
|
704
|
-
reflect: true,
|
|
705
|
-
attribute: 'mobile-tray'
|
|
706
|
-
},
|
|
707
|
-
/**
|
|
708
|
-
* Opt out of automatically closing on focus or click outside of the dropdown content
|
|
709
|
-
* @type {boolean}
|
|
710
|
-
*/
|
|
711
|
-
noAutoClose: {
|
|
712
|
-
type: Boolean,
|
|
713
|
-
reflect: true,
|
|
714
|
-
attribute: 'no-auto-close'
|
|
715
|
-
},
|
|
716
|
-
/**
|
|
717
|
-
* Opt out of auto-sizing
|
|
718
|
-
* @type {boolean}
|
|
719
|
-
*/
|
|
720
|
-
noAutoFit: {
|
|
721
|
-
type: Boolean,
|
|
722
|
-
reflect: true,
|
|
723
|
-
attribute: 'no-auto-fit'
|
|
724
|
-
},
|
|
725
|
-
/**
|
|
726
|
-
* Opt out of focus being automatically moved to the first focusable element in the dropdown when opened
|
|
727
|
-
* @type {boolean}
|
|
728
|
-
*/
|
|
729
|
-
noAutoFocus: {
|
|
730
|
-
type: Boolean,
|
|
731
|
-
reflect: true,
|
|
732
|
-
attribute: 'no-auto-focus'
|
|
733
|
-
},
|
|
734
|
-
/**
|
|
735
|
-
* Render with no padding
|
|
736
|
-
* @type {boolean}
|
|
737
|
-
*/
|
|
738
|
-
noPadding: {
|
|
739
|
-
type: Boolean,
|
|
740
|
-
reflect: true,
|
|
741
|
-
attribute: 'no-padding'
|
|
742
|
-
},
|
|
743
|
-
/**
|
|
744
|
-
* Render the footer with no padding (if it has content)
|
|
745
|
-
* @type {boolean}
|
|
746
|
-
*/
|
|
747
|
-
noPaddingFooter: {
|
|
748
|
-
type: Boolean,
|
|
749
|
-
reflect: true,
|
|
750
|
-
attribute: 'no-padding-footer'
|
|
751
|
-
},
|
|
752
|
-
/**
|
|
753
|
-
* Render the header with no padding (if it has content)
|
|
754
|
-
* @type {boolean}
|
|
755
|
-
*/
|
|
756
|
-
noPaddingHeader: {
|
|
757
|
-
type: Boolean,
|
|
758
|
-
reflect: true,
|
|
759
|
-
attribute: 'no-padding-header'
|
|
760
|
-
},
|
|
761
|
-
/**
|
|
762
|
-
* Render without a pointer
|
|
763
|
-
* @type {boolean}
|
|
764
|
-
*/
|
|
765
|
-
noPointer: {
|
|
766
|
-
type: Boolean,
|
|
767
|
-
reflect: true,
|
|
768
|
-
attribute: 'no-pointer'
|
|
769
|
-
},
|
|
770
|
-
/**
|
|
771
|
-
* Whether the dropdown is open or not
|
|
772
|
-
* @type {boolean}
|
|
773
|
-
*/
|
|
774
|
-
opened: {
|
|
775
|
-
type: Boolean,
|
|
776
|
-
reflect: true
|
|
777
|
-
},
|
|
778
|
-
/**
|
|
779
|
-
* Private.
|
|
780
|
-
* @ignore
|
|
781
|
-
*/
|
|
782
|
-
openedAbove: {
|
|
783
|
-
type: Boolean,
|
|
784
|
-
reflect: true,
|
|
785
|
-
attribute: 'opened-above'
|
|
786
|
-
},
|
|
787
|
-
/**
|
|
788
|
-
* Optionally render a d2l-focus-trap around the dropdown content
|
|
789
|
-
* @type {boolean}
|
|
790
|
-
*/
|
|
791
|
-
trapFocus: {
|
|
792
|
-
type: Boolean,
|
|
793
|
-
reflect: true,
|
|
794
|
-
attribute: 'trap-focus'
|
|
795
|
-
},
|
|
796
|
-
/**
|
|
797
|
-
* Provide custom offset, positive or negative
|
|
798
|
-
* @type {string}
|
|
799
|
-
*/
|
|
800
|
-
verticalOffset: {
|
|
801
|
-
type: String,
|
|
802
|
-
attribute: 'vertical-offset'
|
|
803
|
-
},
|
|
804
|
-
_bottomOverflow: {
|
|
805
|
-
type: Boolean
|
|
806
|
-
},
|
|
807
|
-
_closing: {
|
|
808
|
-
type: Boolean
|
|
809
|
-
},
|
|
810
|
-
_contentOverflow: {
|
|
811
|
-
type: Boolean
|
|
812
|
-
},
|
|
813
|
-
_dropdownContent: {
|
|
814
|
-
type: Boolean,
|
|
815
|
-
attribute: 'dropdown-content',
|
|
816
|
-
reflect: true
|
|
817
|
-
},
|
|
818
|
-
_useMobileStyling: {
|
|
819
|
-
type: Boolean,
|
|
820
|
-
attribute: 'data-mobile',
|
|
821
|
-
reflect: true
|
|
822
|
-
},
|
|
823
|
-
_hasHeader: {
|
|
824
|
-
type: Boolean
|
|
825
|
-
},
|
|
826
|
-
_hasFooter: {
|
|
827
|
-
type: Boolean
|
|
828
|
-
},
|
|
829
|
-
_contentHeight: {
|
|
830
|
-
type: Number
|
|
831
|
-
},
|
|
832
|
-
_position: {
|
|
833
|
-
type: Number
|
|
834
|
-
},
|
|
835
|
-
_showBackdrop: {
|
|
836
|
-
type: Boolean
|
|
837
|
-
},
|
|
838
|
-
_topOverflow: {
|
|
839
|
-
type: Boolean
|
|
840
|
-
},
|
|
841
|
-
_width: {
|
|
842
|
-
type: Number
|
|
843
|
-
}
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
constructor() {
|
|
848
|
-
super();
|
|
849
|
-
|
|
850
|
-
this.noAutoClose = false;
|
|
851
|
-
this.noAutoFit = false;
|
|
852
|
-
this.noAutoFocus = false;
|
|
853
|
-
this.noMobileCloseButton = false;
|
|
854
|
-
this.noPadding = false;
|
|
855
|
-
this.noPaddingFooter = false;
|
|
856
|
-
this.noPaddingHeader = false;
|
|
857
|
-
this.noPointer = false;
|
|
858
|
-
this.mobileBreakpointOverride = 616;
|
|
859
|
-
this.trapFocus = false;
|
|
860
|
-
this._useMobileStyling = false;
|
|
861
|
-
|
|
862
|
-
this.__opened = false;
|
|
863
|
-
this.__content = null;
|
|
864
|
-
this.__previousFocusableAncestor = null;
|
|
865
|
-
this.__applyFocus = true;
|
|
866
|
-
this.__dismissibleId = null;
|
|
867
|
-
|
|
868
|
-
this._dropdownContent = true;
|
|
869
|
-
this._bottomOverflow = false;
|
|
870
|
-
this._topOverflow = false;
|
|
871
|
-
this._closing = false;
|
|
872
|
-
this._contentOverflow = false;
|
|
873
|
-
this._hasHeader = false;
|
|
874
|
-
this._hasFooter = false;
|
|
875
|
-
this._showBackdrop = false;
|
|
876
|
-
this._verticalOffset = defaultVerticalOffset;
|
|
877
|
-
|
|
878
|
-
this.__onResize = this.__onResize.bind(this);
|
|
879
|
-
this.__onAutoCloseFocus = this.__onAutoCloseFocus.bind(this);
|
|
880
|
-
this.__onAutoCloseClick = this.__onAutoCloseClick.bind(this);
|
|
881
|
-
this.__toggleScrollStyles = this.__toggleScrollStyles.bind(this);
|
|
882
|
-
this._handleMobileResize = this._handleMobileResize.bind(this);
|
|
883
|
-
this.__disconnectResizeObserver = this.__disconnectResizeObserver.bind(this);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
get opened() {
|
|
887
|
-
return this.__opened;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
set opened(val) {
|
|
891
|
-
const oldVal = this.__opened;
|
|
892
|
-
if (oldVal !== val) {
|
|
893
|
-
this.__opened = val;
|
|
894
|
-
this.requestUpdate('opened', oldVal);
|
|
895
|
-
this.__openedChanged(val);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
connectedCallback() {
|
|
900
|
-
super.connectedCallback();
|
|
901
|
-
|
|
902
|
-
window.addEventListener('resize', this.__onResize);
|
|
903
|
-
this.addEventListener('blur', this.__onAutoCloseFocus, true);
|
|
904
|
-
document.body.addEventListener('focus', this.__onAutoCloseFocus, true);
|
|
905
|
-
document.body.addEventListener('click', this.__onAutoCloseClick, true);
|
|
906
|
-
this.mediaQueryList = window.matchMedia(`(max-width: ${this.mobileBreakpointOverride - 1}px)`);
|
|
907
|
-
this._useMobileStyling = this.mediaQueryList.matches;
|
|
908
|
-
if (this.mediaQueryList.addEventListener) this.mediaQueryList.addEventListener('change', this._handleMobileResize);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
disconnectedCallback() {
|
|
912
|
-
super.disconnectedCallback();
|
|
913
|
-
if (this.mediaQueryList.removeEventListener) this.mediaQueryList.removeEventListener('change', this._handleMobileResize);
|
|
914
|
-
this.removeEventListener('blur', this.__onAutoCloseFocus);
|
|
915
|
-
window.removeEventListener('resize', this.__onResize);
|
|
916
|
-
if (document.body) {
|
|
917
|
-
// DE41322: document.body can be null in some scenarios
|
|
918
|
-
document.body.removeEventListener('focus', this.__onAutoCloseFocus, true);
|
|
919
|
-
document.body.removeEventListener('click', this.__onAutoCloseClick, true);
|
|
920
|
-
}
|
|
921
|
-
clearDismissible(this.__dismissibleId);
|
|
922
|
-
this.__dismissibleId = null;
|
|
923
|
-
|
|
924
|
-
if (this.__resizeObserver) this.__resizeObserver.disconnect();
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
firstUpdated(changedProperties) {
|
|
928
|
-
super.firstUpdated(changedProperties);
|
|
929
|
-
|
|
930
|
-
this.__content = this.getContentContainer();
|
|
931
|
-
this.addEventListener('d2l-dropdown-close', this.__onClose);
|
|
932
|
-
this.addEventListener('d2l-dropdown-position', this.__toggleScrollStyles);
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
updated(changedProperties) {
|
|
936
|
-
changedProperties.forEach((_, propName) => {
|
|
937
|
-
if (propName === 'verticalOffset') {
|
|
938
|
-
let newVerticalOffset = parseInt(this.verticalOffset);
|
|
939
|
-
if (isNaN(newVerticalOffset)) {
|
|
940
|
-
newVerticalOffset = defaultVerticalOffset;
|
|
941
|
-
}
|
|
942
|
-
this.style.setProperty('--d2l-dropdown-verticaloffset', `${newVerticalOffset}px`);
|
|
943
|
-
this._verticalOffset = newVerticalOffset;
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
close() {
|
|
949
|
-
const hide = () => {
|
|
950
|
-
this._closing = false;
|
|
951
|
-
this._showBackdrop = false;
|
|
952
|
-
this.opened = false;
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
if (!reduceMotion && this._useMobileStyling && this.mobileTray && isVisible(this)) {
|
|
956
|
-
if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dropdown-content-width')
|
|
957
|
-
.addEventListener('animationend', hide, { once: true });
|
|
958
|
-
this._closing = true;
|
|
959
|
-
this._showBackdrop = false;
|
|
960
|
-
} else {
|
|
961
|
-
hide();
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* forceRender is no longer necessary, this is left as a stub so that
|
|
967
|
-
* places calling it will not break. It will be removed once the Polymer
|
|
968
|
-
* dropdown is swapped over to use this and all instances of
|
|
969
|
-
* forceRender are removed.
|
|
970
|
-
*/
|
|
971
|
-
forceRender() {}
|
|
972
|
-
|
|
973
|
-
getContentContainer() {
|
|
974
|
-
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-container');
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
/**
|
|
978
|
-
* Private.
|
|
979
|
-
*/
|
|
980
|
-
height() {
|
|
981
|
-
return this.__content && this.__content.offsetHeight;
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
async open(applyFocus) {
|
|
985
|
-
this.__applyFocus = applyFocus !== undefined ? applyFocus : true;
|
|
986
|
-
this.opened = true;
|
|
987
|
-
await this.updateComplete;
|
|
988
|
-
this._showBackdrop = this._useMobileStyling && this.mobileTray;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* Waits for the next resize when elem has a height > 0px,
|
|
993
|
-
* then calls the __position function.
|
|
994
|
-
*/
|
|
995
|
-
requestRepositionNextResize(elem) {
|
|
996
|
-
if (!elem) return;
|
|
997
|
-
if (this.__resizeObserver) this.__resizeObserver.disconnect();
|
|
998
|
-
this.__resizeObserver = new ResizeObserver(this.__disconnectResizeObserver);
|
|
999
|
-
this.__resizeObserver.observe(elem);
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
async resize() {
|
|
1003
|
-
if (!this.opened) {
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
this._showBackdrop = this._useMobileStyling && this.mobileTray;
|
|
1007
|
-
await this.__position();
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
/**
|
|
1011
|
-
* Private.
|
|
1012
|
-
*/
|
|
1013
|
-
scrollTo(scrollTop) {
|
|
1014
|
-
const content = this.__content;
|
|
1015
|
-
if (content) {
|
|
1016
|
-
if (typeof scrollTop === 'number') {
|
|
1017
|
-
content.scrollTop = scrollTop;
|
|
1018
|
-
}
|
|
1019
|
-
return content.scrollTop;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
toggleOpen(applyFocus) {
|
|
1024
|
-
if (this.opened) {
|
|
1025
|
-
this.close();
|
|
1026
|
-
} else {
|
|
1027
|
-
this.open(!this.noAutoFocus && applyFocus);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
__disconnectResizeObserver(entries) {
|
|
1032
|
-
for (let i = 0; i < entries.length; i++) {
|
|
1033
|
-
const entry = entries[i];
|
|
1034
|
-
if (this.__resizeObserver && entry.contentRect.height !== 0) {
|
|
1035
|
-
this.__resizeObserver.disconnect();
|
|
1036
|
-
// wrap in rAF for Firefox
|
|
1037
|
-
requestAnimationFrame(() => {
|
|
1038
|
-
if (this.opened) this.__position();
|
|
1039
|
-
});
|
|
1040
|
-
break;
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
__getContentBottom() {
|
|
1046
|
-
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-bottom');
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
__getContentTop() {
|
|
1050
|
-
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-top');
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
__getOpener() {
|
|
1054
|
-
const opener = findComposedAncestor(this, (elem) => {
|
|
1055
|
-
if (elem.dropdownOpener) {
|
|
1056
|
-
return true;
|
|
1057
|
-
}
|
|
1058
|
-
});
|
|
1059
|
-
return opener;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
__getPositionContainer() {
|
|
1063
|
-
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-position');
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
__getWidthContainer() {
|
|
1067
|
-
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-width');
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
__handleFooterSlotChange(e) {
|
|
1071
|
-
this._hasFooter = e.target.assignedNodes().length !== 0;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
__handleHeaderSlotChange(e) {
|
|
1075
|
-
this._hasHeader = e.target.assignedNodes().length !== 0;
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
__onAutoCloseClick(e) {
|
|
1079
|
-
if (!this.opened || this.noAutoClose) {
|
|
1080
|
-
return;
|
|
1081
|
-
}
|
|
1082
|
-
const rootTarget = e.composedPath()[0];
|
|
1083
|
-
const clickInside = isComposedAncestor(this.getContentContainer(), rootTarget) ||
|
|
1084
|
-
isComposedAncestor(this.__getContentTop(), rootTarget) ||
|
|
1085
|
-
isComposedAncestor(this.__getContentBottom(), rootTarget);
|
|
1086
|
-
if (clickInside) {
|
|
1087
|
-
return;
|
|
1088
|
-
}
|
|
1089
|
-
const opener = this.__getOpener();
|
|
1090
|
-
if (isComposedAncestor(opener.getOpenerElement(), rootTarget)) {
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
this.close();
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
__onAutoCloseFocus() {
|
|
1098
|
-
|
|
1099
|
-
/* timeout needed to work around lack of support for relatedTarget */
|
|
1100
|
-
setTimeout(() => {
|
|
1101
|
-
if (!this.opened
|
|
1102
|
-
|| this.noAutoClose
|
|
1103
|
-
|| !document.activeElement
|
|
1104
|
-
|| document.activeElement === this.__previousFocusableAncestor
|
|
1105
|
-
|| document.activeElement === document.body) {
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
const activeElement = getComposedActiveElement();
|
|
1110
|
-
|
|
1111
|
-
if (isComposedAncestor(this, activeElement)
|
|
1112
|
-
|| isComposedAncestor(this.__getOpener(), activeElement)) {
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
this.close();
|
|
1116
|
-
}, 0);
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
__onClose(e) {
|
|
1120
|
-
|
|
1121
|
-
if (e.target !== this || !document.activeElement) {
|
|
1122
|
-
return;
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
const activeElement = getComposedActiveElement();
|
|
1126
|
-
|
|
1127
|
-
if (!isComposedAncestor(this, activeElement)) {
|
|
1128
|
-
return;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
const opener = this.__getOpener();
|
|
1132
|
-
opener.getOpenerElement().focus();
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
__onResize() {
|
|
1137
|
-
this.resize();
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
async __openedChanged(newValue) {
|
|
1141
|
-
|
|
1142
|
-
// DE44538: wait for dropdown content to fully render,
|
|
1143
|
-
// otherwise this.getContentContainer() can return null.
|
|
1144
|
-
await this.updateComplete;
|
|
1145
|
-
|
|
1146
|
-
this.__previousFocusableAncestor =
|
|
1147
|
-
newValue === true
|
|
1148
|
-
? getPreviousFocusableAncestor(this, false, false)
|
|
1149
|
-
: null;
|
|
1150
|
-
|
|
1151
|
-
const doOpen = async() => {
|
|
1152
|
-
|
|
1153
|
-
const content = this.getContentContainer();
|
|
1154
|
-
|
|
1155
|
-
if (!this.noAutoFit) {
|
|
1156
|
-
content.scrollTop = 0;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
await this.__position();
|
|
1160
|
-
this._showBackdrop = this._useMobileStyling && this.mobileTray;
|
|
1161
|
-
if (!this.noAutoFocus && this.__applyFocus) {
|
|
1162
|
-
const focusable = getFirstFocusableDescendant(this);
|
|
1163
|
-
if (focusable) {
|
|
1164
|
-
// Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
|
|
1165
|
-
requestAnimationFrame(() => focusable.focus());
|
|
1166
|
-
} else {
|
|
1167
|
-
content.setAttribute('tabindex', '-1');
|
|
1168
|
-
content.focus();
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
setTimeout(() =>
|
|
1173
|
-
this.dispatchEvent(new CustomEvent('d2l-dropdown-open', { bubbles: true, composed: true })), 0
|
|
1174
|
-
);
|
|
1175
|
-
|
|
1176
|
-
this.__dismissibleId = setDismissible(() => {
|
|
1177
|
-
this.close();
|
|
1178
|
-
});
|
|
1179
|
-
};
|
|
1180
|
-
|
|
1181
|
-
const ifrauBackdropService = await tryGetIfrauBackdropService();
|
|
1182
|
-
|
|
1183
|
-
if (newValue) {
|
|
1184
|
-
|
|
1185
|
-
if (ifrauBackdropService && this.mobileTray && this._useMobileStyling) {
|
|
1186
|
-
this._ifrauContextInfo = await ifrauBackdropService.showBackdrop();
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
await doOpen();
|
|
1190
|
-
|
|
1191
|
-
} else {
|
|
1192
|
-
|
|
1193
|
-
if (this.__dismissibleId) {
|
|
1194
|
-
clearDismissible(this.__dismissibleId);
|
|
1195
|
-
this.__dismissibleId = null;
|
|
1196
|
-
}
|
|
1197
|
-
if (ifrauBackdropService && this.mobileTray && this._useMobileStyling) {
|
|
1198
|
-
ifrauBackdropService.hideBackdrop();
|
|
1199
|
-
this._ifrauContextInfo = null;
|
|
1200
|
-
}
|
|
1201
|
-
this._showBackdrop = false;
|
|
1202
|
-
await this.updateComplete;
|
|
1203
|
-
|
|
1204
|
-
/** Dispatched when the dropdown is closed */
|
|
1205
|
-
this.dispatchEvent(new CustomEvent('d2l-dropdown-close', { bubbles: true, composed: true }));
|
|
1206
|
-
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
async __position(ignoreVertical, contentRect) {
|
|
1211
|
-
|
|
1212
|
-
const opener = this.__getOpener();
|
|
1213
|
-
if (!opener) {
|
|
1214
|
-
return;
|
|
1215
|
-
}
|
|
1216
|
-
const target = opener.getOpenerElement();
|
|
1217
|
-
if (!target) {
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
const content = this.getContentContainer();
|
|
1222
|
-
const header = this.__getContentTop();
|
|
1223
|
-
const footer = this.__getContentBottom();
|
|
1224
|
-
|
|
1225
|
-
if (!this.noAutoFit) {
|
|
1226
|
-
this._contentHeight = null;
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
/* don't let dropdown content horizontally overflow viewport */
|
|
1230
|
-
this._width = null;
|
|
1231
|
-
|
|
1232
|
-
const openerPosition = window.getComputedStyle(opener, null).getPropertyValue('position');
|
|
1233
|
-
const boundingContainer = getBoundingAncestor(target.parentNode);
|
|
1234
|
-
const boundingContainerRect = boundingContainer.getBoundingClientRect();
|
|
1235
|
-
const scrollHeight = boundingContainer.scrollHeight;
|
|
1236
|
-
|
|
1237
|
-
await this.updateComplete;
|
|
1238
|
-
|
|
1239
|
-
// position check in case consuming app (LMS) has overriden position to make content absolute wrt document
|
|
1240
|
-
const bounded = (openerPosition === 'relative' && boundingContainer !== document.documentElement);
|
|
1241
|
-
|
|
1242
|
-
const adjustPosition = async() => {
|
|
1243
|
-
|
|
1244
|
-
const targetRect = target.getBoundingClientRect();
|
|
1245
|
-
contentRect = contentRect ? contentRect : content.getBoundingClientRect();
|
|
1246
|
-
const headerFooterHeight = header.getBoundingClientRect().height + footer.getBoundingClientRect().height;
|
|
1247
|
-
|
|
1248
|
-
const height = this.minHeight ? this.minHeight : Math.min(this.maxHeight ? this.maxHeight : Number.MAX_VALUE, contentRect.height + headerFooterHeight);
|
|
1249
|
-
const spaceRequired = {
|
|
1250
|
-
height: height + 10,
|
|
1251
|
-
width: contentRect.width
|
|
1252
|
-
};
|
|
1253
|
-
let spaceAround;
|
|
1254
|
-
let spaceAroundScroll;
|
|
1255
|
-
if (bounded) {
|
|
1256
|
-
spaceAround = this._constrainSpaceAround({
|
|
1257
|
-
// allow for target offset + outer margin
|
|
1258
|
-
above: targetRect.top - boundingContainerRect.top - this._verticalOffset - outerMarginTopBottom,
|
|
1259
|
-
// allow for target offset + outer margin
|
|
1260
|
-
below: boundingContainerRect.bottom - targetRect.bottom - this._verticalOffset - outerMarginTopBottom,
|
|
1261
|
-
// allow for outer margin
|
|
1262
|
-
left: targetRect.left - boundingContainerRect.left - 20,
|
|
1263
|
-
// allow for outer margin
|
|
1264
|
-
right: boundingContainerRect.right - targetRect.right - 20
|
|
1265
|
-
}, spaceRequired, targetRect);
|
|
1266
|
-
spaceAroundScroll = this._constrainSpaceAround({
|
|
1267
|
-
above: targetRect.top - boundingContainerRect.top + boundingContainer.scrollTop,
|
|
1268
|
-
below: scrollHeight - targetRect.bottom + boundingContainerRect.top - boundingContainer.scrollTop
|
|
1269
|
-
}, spaceRequired, targetRect);
|
|
1270
|
-
} else {
|
|
1271
|
-
spaceAround = this._constrainSpaceAround({
|
|
1272
|
-
// allow for target offset + outer margin
|
|
1273
|
-
above: targetRect.top - this._verticalOffset - outerMarginTopBottom,
|
|
1274
|
-
// allow for target offset + outer margin
|
|
1275
|
-
below: window.innerHeight - targetRect.bottom - this._verticalOffset - outerMarginTopBottom,
|
|
1276
|
-
// allow for outer margin
|
|
1277
|
-
left: targetRect.left - 20,
|
|
1278
|
-
// allow for outer margin
|
|
1279
|
-
right: document.documentElement.clientWidth - targetRect.right - 15
|
|
1280
|
-
}, spaceRequired, targetRect);
|
|
1281
|
-
spaceAroundScroll = this._constrainSpaceAround({
|
|
1282
|
-
above: targetRect.top + document.documentElement.scrollTop,
|
|
1283
|
-
below: scrollHeight - targetRect.bottom - document.documentElement.scrollTop
|
|
1284
|
-
}, spaceRequired, targetRect);
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
if (!ignoreVertical) {
|
|
1288
|
-
this.openedAbove = this._getOpenedAbove(spaceAround, spaceAroundScroll, spaceRequired);
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
const centerDelta = contentRect.width - targetRect.width;
|
|
1292
|
-
const position = this._getPosition(spaceAround, centerDelta);
|
|
1293
|
-
if (position !== null) {
|
|
1294
|
-
this._position = position;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
//Calculate height available to the dropdown contents for overflow because that is the only area capable of scrolling
|
|
1298
|
-
const availableHeight = this.openedAbove ? spaceAround.above : spaceAround.below;
|
|
1299
|
-
if (!this.noAutoFit && availableHeight && availableHeight > 0) {
|
|
1300
|
-
//Only apply maximum if it's less than space available and the header/footer alone won't exceed it (content must be visible)
|
|
1301
|
-
this._contentHeight = this.maxHeight !== null
|
|
1302
|
-
&& availableHeight > this.maxHeight
|
|
1303
|
-
&& headerFooterHeight < this.maxHeight
|
|
1304
|
-
? this.maxHeight - headerFooterHeight - 2
|
|
1305
|
-
: availableHeight - headerFooterHeight;
|
|
1306
|
-
this.__toggleOverflowY(contentRect.height + headerFooterHeight > availableHeight);
|
|
1307
|
-
|
|
1308
|
-
// ensure the content height has updated when the __toggleScrollStyles event handler runs
|
|
1309
|
-
await this.updateComplete;
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
/** Dispatched when the dropdown position finishes adjusting */
|
|
1313
|
-
this.dispatchEvent(new CustomEvent('d2l-dropdown-position', { bubbles: true, composed: true }));
|
|
1314
|
-
};
|
|
1315
|
-
|
|
1316
|
-
const scrollWidth = Math.max(header.scrollWidth, content.scrollWidth, footer.scrollWidth);
|
|
1317
|
-
const availableWidth = (bounded ? boundingContainerRect.width - 60 : window.innerWidth - 40);
|
|
1318
|
-
this._width = (availableWidth > scrollWidth ? scrollWidth : availableWidth) ;
|
|
1319
|
-
|
|
1320
|
-
await this.updateComplete;
|
|
1321
|
-
|
|
1322
|
-
await adjustPosition();
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
__toggleOverflowY(isOverflowing) {
|
|
1326
|
-
if (!this.__content) {
|
|
1327
|
-
return;
|
|
1328
|
-
}
|
|
1329
|
-
if (!this._contentHeight) {
|
|
1330
|
-
return;
|
|
1331
|
-
}
|
|
1332
|
-
this._contentOverflow = isOverflowing || this.__content.scrollHeight > this._contentHeight;
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
__toggleScrollStyles() {
|
|
1336
|
-
/* scrollHeight incorrect in IE by 4px second time opened */
|
|
1337
|
-
this._bottomOverflow = this.__content.scrollHeight - (this.__content.scrollTop + this.__content.clientHeight) >= 5;
|
|
1338
|
-
this._topOverflow = this.__content.scrollTop !== 0;
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
_constrainSpaceAround(spaceAround, spaceRequired, targetRect) {
|
|
1342
|
-
const constrained = { ...spaceAround };
|
|
1343
|
-
if (this.boundary) {
|
|
1344
|
-
constrained.above = this.boundary.above >= 0 ? Math.min(spaceAround.above, this.boundary.above) : spaceAround.above;
|
|
1345
|
-
constrained.below = this.boundary.below >= 0 ? Math.min(spaceAround.below, this.boundary.below) : spaceAround.below;
|
|
1346
|
-
constrained.left = this.boundary.left >= 0 ? Math.min(spaceAround.left, this.boundary.left) : spaceAround.left;
|
|
1347
|
-
constrained.right = this.boundary.right >= 0 ? Math.min(spaceAround.right, this.boundary.right) : spaceAround.right;
|
|
1348
|
-
}
|
|
1349
|
-
const isRTL = this.getAttribute('dir') === 'rtl';
|
|
1350
|
-
if ((this.align === 'start' && !isRTL) || (this.align === 'end' && isRTL)) {
|
|
1351
|
-
constrained.left = Math.max(0, spaceRequired.width - (targetRect.width + spaceAround.right));
|
|
1352
|
-
} else if ((this.align === 'start' && isRTL) || (this.align === 'end' && !isRTL)) {
|
|
1353
|
-
constrained.right = Math.max(0, spaceRequired.width - (targetRect.width + spaceAround.left));
|
|
1354
|
-
}
|
|
1355
|
-
return constrained;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
_getBottomTrayStyling() {
|
|
1359
|
-
|
|
1360
|
-
let maxHeightOverride;
|
|
1361
|
-
let availableHeight = Math.min(window.innerHeight, window.screen.height);
|
|
1362
|
-
if (this._ifrauContextInfo) availableHeight = this._ifrauContextInfo.availableHeight;
|
|
1363
|
-
// default maximum height for bottom tray (42px margin)
|
|
1364
|
-
const mobileTrayMaxHeightDefault = availableHeight - minBackdropHeightMobile;
|
|
1365
|
-
if (this.maxHeight) {
|
|
1366
|
-
// if maxWidth provided is smaller, use the maxWidth
|
|
1367
|
-
maxHeightOverride = Math.min(mobileTrayMaxHeightDefault, this.maxHeight);
|
|
1368
|
-
} else {
|
|
1369
|
-
maxHeightOverride = mobileTrayMaxHeightDefault;
|
|
1370
|
-
}
|
|
1371
|
-
maxHeightOverride = `${maxHeightOverride}px`;
|
|
1372
|
-
|
|
1373
|
-
let bottomOverride;
|
|
1374
|
-
if (this._ifrauContextInfo) {
|
|
1375
|
-
// Bottom override is measured as
|
|
1376
|
-
// the distance from the bottom of the screen
|
|
1377
|
-
const screenHeight =
|
|
1378
|
-
window.innerHeight
|
|
1379
|
-
- this._ifrauContextInfo.availableHeight
|
|
1380
|
-
+ Math.min(this._ifrauContextInfo.top, 0);
|
|
1381
|
-
bottomOverride = `${screenHeight}px`;
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
const widthOverride = '100vw';
|
|
1385
|
-
|
|
1386
|
-
const widthStyle = {
|
|
1387
|
-
minWidth: widthOverride,
|
|
1388
|
-
width: widthOverride,
|
|
1389
|
-
maxHeight: maxHeightOverride,
|
|
1390
|
-
bottom: bottomOverride
|
|
1391
|
-
};
|
|
1392
|
-
|
|
1393
|
-
const contentWidthStyle = {
|
|
1394
|
-
/* set width of content in addition to width container so header and footer borders are full width */
|
|
1395
|
-
width: widthOverride
|
|
1396
|
-
};
|
|
1397
|
-
|
|
1398
|
-
const headerStyle = {
|
|
1399
|
-
...contentWidthStyle,
|
|
1400
|
-
minHeight: this._hasHeader ? 'auto' : '5px'
|
|
1401
|
-
};
|
|
1402
|
-
|
|
1403
|
-
const footerStyle = {
|
|
1404
|
-
...contentWidthStyle,
|
|
1405
|
-
minHeight: this._hasFooter || !this.noMobileCloseButton ? 'auto' : '5px'
|
|
1406
|
-
};
|
|
1407
|
-
|
|
1408
|
-
const contentStyle = {
|
|
1409
|
-
...contentWidthStyle,
|
|
1410
|
-
maxHeight: maxHeightOverride,
|
|
1411
|
-
overflowY: this._contentOverflow ? 'auto' : 'hidden'
|
|
1412
|
-
};
|
|
1413
|
-
|
|
1414
|
-
const closeButtonStyles = {
|
|
1415
|
-
display: !this.noMobileCloseButton ? 'inline-block' : 'none',
|
|
1416
|
-
width: this._getTrayFooterWidth(),
|
|
1417
|
-
padding: this._hasFooter && !this.noPaddingFooter ? '12px 0 0 0' : '12px',
|
|
1418
|
-
margin: this._getTrayFooterMargin()
|
|
1419
|
-
};
|
|
1420
|
-
|
|
1421
|
-
return {
|
|
1422
|
-
'width' : widthStyle,
|
|
1423
|
-
'header' : headerStyle,
|
|
1424
|
-
'footer' : footerStyle,
|
|
1425
|
-
'content' : contentStyle,
|
|
1426
|
-
'close' : closeButtonStyles
|
|
1427
|
-
};
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
_getDropdownStyling() {
|
|
1431
|
-
const widthStyle = {
|
|
1432
|
-
maxWidth: this.maxWidth ? `${this.maxWidth}px` : '',
|
|
1433
|
-
minWidth: this.minWidth ? `${this.minWidth}px` : '',
|
|
1434
|
-
/* add 2 to content width since scrollWidth does not include border */
|
|
1435
|
-
width: this._width ? `${this._width + 20}px` : ''
|
|
1436
|
-
};
|
|
1437
|
-
|
|
1438
|
-
const contentWidthStyle = {
|
|
1439
|
-
minWidth: this.minWidth ? `${this.minWidth}px` : '',
|
|
1440
|
-
/* set width of content in addition to width container so header and footer borders are full width */
|
|
1441
|
-
width: this._width ? `${this._width + 18}px` : '',
|
|
1442
|
-
};
|
|
1443
|
-
|
|
1444
|
-
const contentStyle = {
|
|
1445
|
-
...contentWidthStyle,
|
|
1446
|
-
maxHeight: this._contentHeight ? `${this._contentHeight}px` : '',
|
|
1447
|
-
overflowY: this._contentOverflow ? 'auto' : 'hidden'
|
|
1448
|
-
};
|
|
1449
|
-
|
|
1450
|
-
const closeButtonStyle = {
|
|
1451
|
-
display: 'none',
|
|
1452
|
-
};
|
|
1453
|
-
|
|
1454
|
-
return {
|
|
1455
|
-
'width' : widthStyle,
|
|
1456
|
-
'content' : contentStyle,
|
|
1457
|
-
'close' : closeButtonStyle,
|
|
1458
|
-
'header' : contentWidthStyle,
|
|
1459
|
-
'footer' : contentWidthStyle
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
_getLeftRightTrayStyling() {
|
|
1464
|
-
|
|
1465
|
-
let maxWidthOverride = this.maxWidth;
|
|
1466
|
-
let availableWidth = Math.min(window.innerWidth, window.screen.width);
|
|
1467
|
-
if (this._ifrauContextInfo) availableWidth = this._ifrauContextInfo.availableWidth;
|
|
1468
|
-
// default maximum width for tray (30px margin)
|
|
1469
|
-
const mobileTrayMaxWidthDefault = Math.min(availableWidth - minBackdropWidthMobile, 420);
|
|
1470
|
-
if (maxWidthOverride) {
|
|
1471
|
-
// if maxWidth provided is smaller, use the maxWidth
|
|
1472
|
-
maxWidthOverride = Math.min(mobileTrayMaxWidthDefault, maxWidthOverride);
|
|
1473
|
-
} else {
|
|
1474
|
-
maxWidthOverride = mobileTrayMaxWidthDefault;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
let minWidthOverride = this.minWidth;
|
|
1478
|
-
// minimum size - 285px
|
|
1479
|
-
const mobileTrayMinWidthDefault = 285;
|
|
1480
|
-
if (minWidthOverride) {
|
|
1481
|
-
// if minWidth provided is smaller, use the minumum width for tray
|
|
1482
|
-
minWidthOverride = Math.max(mobileTrayMinWidthDefault, minWidthOverride);
|
|
1483
|
-
} else {
|
|
1484
|
-
minWidthOverride = mobileTrayMinWidthDefault;
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
// if no width property set, automatically size to maximum width
|
|
1488
|
-
let widthOverride = this._width ? this._width : maxWidthOverride;
|
|
1489
|
-
// ensure width is between minWidth and maxWidth
|
|
1490
|
-
if (widthOverride && maxWidthOverride && widthOverride > (maxWidthOverride - 20)) widthOverride = maxWidthOverride - 20;
|
|
1491
|
-
if (widthOverride && minWidthOverride && widthOverride < (minWidthOverride - 20)) widthOverride = minWidthOverride - 20;
|
|
1492
|
-
|
|
1493
|
-
maxWidthOverride = `${maxWidthOverride}px`;
|
|
1494
|
-
minWidthOverride = `${minWidthOverride}px`;
|
|
1495
|
-
const contentWidth = `${widthOverride + 18}px`;
|
|
1496
|
-
/* add 2 to content width since scrollWidth does not include border */
|
|
1497
|
-
const containerWidth = `${widthOverride + 20}px`;
|
|
1498
|
-
|
|
1499
|
-
let maxHeightOverride = '';
|
|
1500
|
-
if (this._ifrauContextInfo) maxHeightOverride = `${this._ifrauContextInfo.availableHeight}px`;
|
|
1501
|
-
|
|
1502
|
-
let topOverride;
|
|
1503
|
-
if (this._ifrauContextInfo) {
|
|
1504
|
-
// if inside iframe, use ifrauContext top as top of screen
|
|
1505
|
-
topOverride = `${this._ifrauContextInfo.top < 0 ? -this._ifrauContextInfo.top : 0}px`;
|
|
1506
|
-
} else if (window.innerHeight > window.screen.height) {
|
|
1507
|
-
// non-responsive page, manually override top to scroll distance
|
|
1508
|
-
topOverride = window.pageYOffset;
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
let rightOverride;
|
|
1512
|
-
let leftOverride;
|
|
1513
|
-
if (this.mobileTray === 'right') {
|
|
1514
|
-
// On non-responsive pages, the innerWidth may be wider than the screen,
|
|
1515
|
-
// override right to stick to right of viewport
|
|
1516
|
-
rightOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
|
|
1517
|
-
}
|
|
1518
|
-
if (this.mobileTray === 'left') {
|
|
1519
|
-
// On non-responsive pages, the innerWidth may be wider than the screen,
|
|
1520
|
-
// override left to stick to left of viewport
|
|
1521
|
-
leftOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
const widthStyle = {
|
|
1525
|
-
maxWidth: maxWidthOverride,
|
|
1526
|
-
minWidth: minWidthOverride,
|
|
1527
|
-
width: containerWidth,
|
|
1528
|
-
maxHeight: maxHeightOverride,
|
|
1529
|
-
top: topOverride,
|
|
1530
|
-
right: rightOverride,
|
|
1531
|
-
left: leftOverride,
|
|
1532
|
-
};
|
|
1533
|
-
|
|
1534
|
-
const contentWidthStyle = {
|
|
1535
|
-
minWidth: minWidthOverride,
|
|
1536
|
-
/* set width of content in addition to width container so header and footer borders are full width */
|
|
1537
|
-
width: contentWidth,
|
|
1538
|
-
};
|
|
1539
|
-
|
|
1540
|
-
const headerStyle = {
|
|
1541
|
-
...contentWidthStyle,
|
|
1542
|
-
minHeight: this._hasHeader ? 'auto' : '5px'
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
|
-
const footerStyle = {
|
|
1546
|
-
...contentWidthStyle,
|
|
1547
|
-
minHeight: this._hasFooter || !this.noMobileCloseButton ? 'auto' : '5px'
|
|
1548
|
-
};
|
|
1549
|
-
|
|
1550
|
-
const contentStyle = {
|
|
1551
|
-
...contentWidthStyle,
|
|
1552
|
-
maxHeight: maxHeightOverride,
|
|
1553
|
-
overflowY: this._contentOverflow ? 'auto' : 'hidden'
|
|
1554
|
-
};
|
|
1555
|
-
|
|
1556
|
-
const closeButtonStyles = {
|
|
1557
|
-
display: !this.noMobileCloseButton ? 'inline-block' : 'none',
|
|
1558
|
-
width: this._getTrayFooterWidth(),
|
|
1559
|
-
padding: this._hasFooter && !this.noPaddingFooter ? '12px 0 0 0' : '12px',
|
|
1560
|
-
margin: this._getTrayFooterMargin()
|
|
1561
|
-
};
|
|
1562
|
-
|
|
1563
|
-
return {
|
|
1564
|
-
'width' : widthStyle,
|
|
1565
|
-
'header' : headerStyle,
|
|
1566
|
-
'footer' : footerStyle,
|
|
1567
|
-
'content' : contentStyle,
|
|
1568
|
-
'close' : closeButtonStyles
|
|
1569
|
-
};
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
|
-
_getOpenedAbove(spaceAround, spaceAroundScroll, spaceRequired) {
|
|
1573
|
-
if (spaceAround.below >= spaceRequired.height) {
|
|
1574
|
-
return false;
|
|
1575
|
-
}
|
|
1576
|
-
if (spaceAround.above >= spaceRequired.height) {
|
|
1577
|
-
return true;
|
|
1578
|
-
}
|
|
1579
|
-
if (!this.noAutoFit) {
|
|
1580
|
-
// if auto-fit is enabled, scroll will be enabled for the
|
|
1581
|
-
// inner content so it will always fit in the available space
|
|
1582
|
-
// so pick the largest space it can be displayed in
|
|
1583
|
-
return spaceAround.above > spaceAround.below;
|
|
1584
|
-
}
|
|
1585
|
-
if (spaceAroundScroll.below >= spaceRequired.height) {
|
|
1586
|
-
return false;
|
|
1587
|
-
}
|
|
1588
|
-
if (spaceAroundScroll.above >= spaceRequired.height) {
|
|
1589
|
-
return true;
|
|
1590
|
-
}
|
|
1591
|
-
// if auto-fit is disabled and it doesn't fit in the scrollable space
|
|
1592
|
-
// above or below, always open down because it can add scrollable space
|
|
1593
|
-
return false;
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
_getPosition(spaceAround, centerDelta) {
|
|
1597
|
-
|
|
1598
|
-
const contentXAdjustment = centerDelta / 2;
|
|
1599
|
-
if (centerDelta <= 0) {
|
|
1600
|
-
return contentXAdjustment * -1;
|
|
1601
|
-
}
|
|
1602
|
-
if (spaceAround.left > contentXAdjustment && spaceAround.right > contentXAdjustment) {
|
|
1603
|
-
// center with target
|
|
1604
|
-
return contentXAdjustment * -1;
|
|
1605
|
-
}
|
|
1606
|
-
const isRTL = this.getAttribute('dir') === 'rtl';
|
|
1607
|
-
if (!isRTL) {
|
|
1608
|
-
if (spaceAround.left < contentXAdjustment) {
|
|
1609
|
-
// slide content right (not enough space to center)
|
|
1610
|
-
return spaceAround.left * -1;
|
|
1611
|
-
} else if (spaceAround.right < contentXAdjustment) {
|
|
1612
|
-
// slide content left (not enough space to center)
|
|
1613
|
-
return (centerDelta * -1) + spaceAround.right;
|
|
1614
|
-
}
|
|
1615
|
-
} else {
|
|
1616
|
-
if (spaceAround.left < contentXAdjustment) {
|
|
1617
|
-
// slide content right (not enough space to center)
|
|
1618
|
-
return (centerDelta * -1) + spaceAround.left;
|
|
1619
|
-
} else if (spaceAround.right < contentXAdjustment) {
|
|
1620
|
-
// slide content left (not enough space to center)
|
|
1621
|
-
return spaceAround.right * -1;
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
return null;
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
_getTrayFooterMargin() {
|
|
1628
|
-
let footerMargin;
|
|
1629
|
-
if (this._hasFooter) {
|
|
1630
|
-
footerMargin = '0';
|
|
1631
|
-
} else if (this.getAttribute('dir') === 'rtl') {
|
|
1632
|
-
footerMargin = '-20px -20px -20px 0px';
|
|
1633
|
-
} else {
|
|
1634
|
-
footerMargin = '-20px 0 -20px -20px';
|
|
1635
|
-
}
|
|
1636
|
-
return footerMargin;
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
_getTrayFooterWidth() {
|
|
1640
|
-
let footerWidth;
|
|
1641
|
-
if (this.noPaddingFooter) {
|
|
1642
|
-
footerWidth = 'calc(100% - 24px)';
|
|
1643
|
-
} else if (this._hasFooter) {
|
|
1644
|
-
footerWidth = '100%';
|
|
1645
|
-
} else {
|
|
1646
|
-
footerWidth = 'calc(100% + 16px)';
|
|
1647
|
-
}
|
|
1648
|
-
return footerWidth;
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
_handleFocusTrapEnter() {
|
|
1652
|
-
if (this.__applyFocus && !this.noAutoFocus) {
|
|
1653
|
-
const content = this.getContentContainer();
|
|
1654
|
-
const focusable = getFirstFocusableDescendant(content);
|
|
1655
|
-
if (focusable) {
|
|
1656
|
-
// Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
|
|
1657
|
-
requestAnimationFrame(() => focusable.focus());
|
|
1658
|
-
} else {
|
|
1659
|
-
content.setAttribute('tabindex', '-1');
|
|
1660
|
-
content.focus();
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
/** Dispatched when user focus enters the dropdown content (trap-focus option only) */
|
|
1664
|
-
this.dispatchEvent(new CustomEvent('d2l-dropdown-focus-enter', { detail:{ applyFocus: this.__applyFocus } }));
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
async _handleMobileResize() {
|
|
1668
|
-
this._useMobileStyling = this.mediaQueryList.matches;
|
|
1669
|
-
if (this.opened) this._showBackdrop = this._useMobileStyling && this.mobileTray;
|
|
1670
|
-
if (this.opened) await this.__position();
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
_renderContent() {
|
|
1674
|
-
const positionStyle = {};
|
|
1675
|
-
const isRTL = this.getAttribute('dir') === 'rtl';
|
|
1676
|
-
if (this._position) {
|
|
1677
|
-
if (!isRTL) {
|
|
1678
|
-
positionStyle.left = `${this._position}px`;
|
|
1679
|
-
} else {
|
|
1680
|
-
positionStyle.right = `${this._position}px`;
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
const mobileTrayRightLeft = this._useMobileStyling && (this.mobileTray === 'right' || this.mobileTray === 'left');
|
|
1685
|
-
const mobileTrayBottom = this._useMobileStyling && (this.mobileTray === 'bottom');
|
|
1686
|
-
|
|
1687
|
-
let stylesMap;
|
|
1688
|
-
if (mobileTrayBottom) {
|
|
1689
|
-
stylesMap = this._getBottomTrayStyling();
|
|
1690
|
-
} else if (mobileTrayRightLeft) {
|
|
1691
|
-
stylesMap = this._getLeftRightTrayStyling();
|
|
1692
|
-
} else {
|
|
1693
|
-
stylesMap = this._getDropdownStyling();
|
|
1694
|
-
}
|
|
1695
|
-
const widthStyle = stylesMap['width'];
|
|
1696
|
-
const headerStyle = stylesMap['header'];
|
|
1697
|
-
const footerStyle = stylesMap['footer'];
|
|
1698
|
-
const contentStyle = stylesMap['content'];
|
|
1699
|
-
const closeButtonStyles = stylesMap['close'];
|
|
1700
|
-
|
|
1701
|
-
const topClasses = {
|
|
1702
|
-
'd2l-dropdown-content-top': true,
|
|
1703
|
-
'd2l-dropdown-content-top-scroll': this._topOverflow,
|
|
1704
|
-
'd2l-dropdown-content-header': this._hasHeader
|
|
1705
|
-
};
|
|
1706
|
-
const bottomClasses = {
|
|
1707
|
-
'd2l-dropdown-content-bottom': true,
|
|
1708
|
-
'd2l-dropdown-content-bottom-scroll': this._bottomOverflow,
|
|
1709
|
-
'd2l-dropdown-content-footer': this._hasFooter || (this._useMobileStyling && this.mobileTray && !this.noMobileCloseButton)
|
|
1710
|
-
};
|
|
1711
|
-
|
|
1712
|
-
let dropdownContentSlots = html`
|
|
1713
|
-
<div
|
|
1714
|
-
id="d2l-dropdown-wrapper"
|
|
1715
|
-
class="d2l-dropdown-content-width"
|
|
1716
|
-
style=${styleMap(widthStyle)}
|
|
1717
|
-
?data-closing="${this._closing}">
|
|
1718
|
-
<div class=${classMap(topClasses)} style=${styleMap(headerStyle)}>
|
|
1719
|
-
<slot name="header" @slotchange="${this.__handleHeaderSlotChange}"></slot>
|
|
1720
|
-
</div>
|
|
1721
|
-
<div
|
|
1722
|
-
class="d2l-dropdown-content-container"
|
|
1723
|
-
style=${styleMap(contentStyle)}
|
|
1724
|
-
@scroll=${this.__toggleScrollStyles}>
|
|
1725
|
-
<slot class="d2l-dropdown-content-slot"></slot>
|
|
1726
|
-
</div>
|
|
1727
|
-
<div class=${classMap(bottomClasses)} style=${styleMap(footerStyle)}>
|
|
1728
|
-
<slot name="footer" @slotchange="${this.__handleFooterSlotChange}"></slot>
|
|
1729
|
-
<d2l-button
|
|
1730
|
-
class="dropdown-close-btn"
|
|
1731
|
-
style=${styleMap(closeButtonStyles)}
|
|
1732
|
-
@click=${this.close}>
|
|
1733
|
-
${this.localize('components.dropdown.close')}
|
|
1734
|
-
</d2l-button>
|
|
1735
|
-
</div>
|
|
1736
|
-
</div>
|
|
1737
|
-
`;
|
|
1738
|
-
|
|
1739
|
-
if (this.trapFocus) {
|
|
1740
|
-
dropdownContentSlots = html`
|
|
1741
|
-
<d2l-focus-trap
|
|
1742
|
-
@d2l-focus-trap-enter="${this._handleFocusTrapEnter}"
|
|
1743
|
-
?trap="${this.opened}">
|
|
1744
|
-
${dropdownContentSlots}
|
|
1745
|
-
</d2l-focus-trap>`;
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
const dropdown = html`
|
|
1749
|
-
<div class="d2l-dropdown-content-position" style=${styleMap(positionStyle)}>
|
|
1750
|
-
${dropdownContentSlots}
|
|
1751
|
-
</div>
|
|
1752
|
-
`;
|
|
1753
|
-
|
|
1754
|
-
return (this.mobileTray) ? html`
|
|
1755
|
-
${dropdown}
|
|
1756
|
-
<d2l-backdrop
|
|
1757
|
-
for-target="d2l-dropdown-wrapper"
|
|
1758
|
-
?shown="${this._showBackdrop}" >
|
|
1759
|
-
</d2l-backdrop>`
|
|
1760
|
-
: html`${dropdown}`;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
};
|
|
1764
|
-
</code></pre>
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
<pre class="d2l-code d2l-code-dark line-numbers"><code class="language-javascript">import '../colors/colors.js';
|
|
1768
|
-
import { css, html, LitElement } from 'lit';
|
|
1769
|
-
import { classMap } from 'lit/directives/class-map.js';
|
|
1770
|
-
import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
|
|
1771
|
-
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
1772
|
-
import { offscreenStyles } from '../offscreen/offscreen.js';
|
|
1773
|
-
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
|
|
1774
|
-
import { styleMap } from 'lit/directives/style-map.js';
|
|
1775
|
-
|
|
1776
|
-
/**
|
|
1777
|
-
* A container element that provides specific layout using several slots.
|
|
1778
|
-
* @slot content - Slot for primary content such as title and supplementary info (no actionable elements)
|
|
1779
|
-
* @slot actions - Slot for buttons and dropdown openers to be placed in top right corner of header
|
|
1780
|
-
* @slot badge - Slot for badge content, such as a profile image or status indicator
|
|
1781
|
-
* @slot footer - Slot for footer content, such secondary actions
|
|
1782
|
-
* @slot header - Slot for header content, such as course image (no actionable elements)
|
|
1783
|
-
*/
|
|
1784
|
-
class Card extends FocusMixin(LitElement) {
|
|
1785
|
-
|
|
1786
|
-
static get properties() {
|
|
1787
|
-
return {
|
|
1788
|
-
/**
|
|
1789
|
-
* Style the card's content and footer as centered horizontally
|
|
1790
|
-
* @type {boolean}
|
|
1791
|
-
*/
|
|
1792
|
-
alignCenter: { type: Boolean, attribute: 'align-center', reflect: true },
|
|
1793
|
-
/**
|
|
1794
|
-
* Download a URL instead of navigating to it
|
|
1795
|
-
* @type {boolean}
|
|
1796
|
-
*/
|
|
1797
|
-
download: { type: Boolean, reflect: true },
|
|
1798
|
-
/**
|
|
1799
|
-
* Location for the primary action/navigation
|
|
1800
|
-
* @type {string}
|
|
1801
|
-
*/
|
|
1802
|
-
href: { type: String, reflect: true },
|
|
1803
|
-
/**
|
|
1804
|
-
* Indicates the human language of the linked resource; purely advisory, with no built-in functionality
|
|
1805
|
-
* @type {string}
|
|
1806
|
-
*/
|
|
1807
|
-
hreflang: { type: String, reflect: true },
|
|
1808
|
-
/**
|
|
1809
|
-
* Specifies the relationship of the target object to the link object
|
|
1810
|
-
* @type {string}
|
|
1811
|
-
*/
|
|
1812
|
-
rel: { type: String, reflect: true },
|
|
1813
|
-
/**
|
|
1814
|
-
* Subtle aesthetic on non-white backgrounds
|
|
1815
|
-
* @type {boolean}
|
|
1816
|
-
*/
|
|
1817
|
-
subtle: { type: Boolean, reflect: true },
|
|
1818
|
-
/**
|
|
1819
|
-
* Where to display the linked URL
|
|
1820
|
-
* @type {string}
|
|
1821
|
-
*/
|
|
1822
|
-
target: { type: String, reflect: true },
|
|
1823
|
-
/**
|
|
1824
|
-
* Accessible text for the card (will be announced when AT user focuses)
|
|
1825
|
-
* @type {string}
|
|
1826
|
-
*/
|
|
1827
|
-
text: { type: String, reflect: true },
|
|
1828
|
-
/**
|
|
1829
|
-
* Specifies the media type in the form of a MIME type for the linked URL; purely advisory, with no built-in functionality
|
|
1830
|
-
* @type {string}
|
|
1831
|
-
*/
|
|
1832
|
-
type: { type: String, reflect: true },
|
|
1833
|
-
_active: { type: Boolean, reflect: true },
|
|
1834
|
-
_dropdownActionOpen: { type: Boolean, attribute: '_dropdown-action-open', reflect: true },
|
|
1835
|
-
_hover: { type: Boolean },
|
|
1836
|
-
_badgeMarginTop: { type: String },
|
|
1837
|
-
_footerHidden: { type: Boolean },
|
|
1838
|
-
_tooltipShowing: { type: Boolean, attribute: '_tooltip_showing', reflect: true }
|
|
1839
|
-
};
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
static get styles() {
|
|
1843
|
-
return [offscreenStyles, css`
|
|
1844
|
-
:host {
|
|
1845
|
-
background-color: #ffffff;
|
|
1846
|
-
border: 1px solid var(--d2l-color-gypsum);
|
|
1847
|
-
border-radius: 6px;
|
|
1848
|
-
box-sizing: border-box;
|
|
1849
|
-
display: inline-block;
|
|
1850
|
-
position: relative;
|
|
1851
|
-
z-index: 0;
|
|
1852
|
-
}
|
|
1853
|
-
.d2l-card-container {
|
|
1854
|
-
align-items: flex-start; /* required so that footer will not stretch to 100% width */
|
|
1855
|
-
display: flex;
|
|
1856
|
-
flex-direction: column;
|
|
1857
|
-
height: 100%;
|
|
1858
|
-
position: relative;
|
|
1859
|
-
}
|
|
1860
|
-
.d2l-card-link-container {
|
|
1861
|
-
flex-basis: auto;
|
|
1862
|
-
flex-grow: 1;
|
|
1863
|
-
flex-shrink: 1;
|
|
1864
|
-
width: 100%; /* required for Legacy-Edge and FF when align-items: flex-start is specified */
|
|
1865
|
-
}
|
|
1866
|
-
.d2l-card-link-text {
|
|
1867
|
-
display: inline-block;
|
|
1868
|
-
}
|
|
1869
|
-
.d2l-card-header {
|
|
1870
|
-
border-start-end-radius: 6px;
|
|
1871
|
-
border-start-start-radius: 6px;
|
|
1872
|
-
overflow: hidden;
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
a {
|
|
1876
|
-
bottom: -1px;
|
|
1877
|
-
display: block;
|
|
1878
|
-
left: -1px;
|
|
1879
|
-
outline: none;
|
|
1880
|
-
position: absolute;
|
|
1881
|
-
right: -1px;
|
|
1882
|
-
top: -1px;
|
|
1883
|
-
z-index: 1;
|
|
1884
|
-
}
|
|
1885
|
-
:host([subtle]) a {
|
|
1886
|
-
bottom: 0;
|
|
1887
|
-
left: 0;
|
|
1888
|
-
right: 0;
|
|
1889
|
-
top: 0;
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
:host(:hover) a {
|
|
1893
|
-
bottom: -5px;
|
|
1894
|
-
}
|
|
1895
|
-
:host([subtle]:hover) a {
|
|
1896
|
-
bottom: -4px;
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
.d2l-card-content {
|
|
1900
|
-
padding: 1.2rem 0.8rem 0 0.8rem;
|
|
1901
|
-
}
|
|
1902
|
-
:host([align-center]) .d2l-card-content {
|
|
1903
|
-
text-align: center;
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
.d2l-card-footer-hidden .d2l-card-content {
|
|
1907
|
-
padding-bottom: 1.2rem;
|
|
1908
|
-
}
|
|
1909
|
-
.d2l-card-actions {
|
|
1910
|
-
inset-inline-end: 0.6rem;
|
|
1911
|
-
position: absolute;
|
|
1912
|
-
top: 0.6rem;
|
|
1913
|
-
/* this must be higher than footer z-index so dropdowns will be on top */
|
|
1914
|
-
z-index: 3;
|
|
1915
|
-
}
|
|
1916
|
-
.d2l-card-actions ::slotted(*) {
|
|
1917
|
-
margin-inline-start: 0.3rem;
|
|
1918
|
-
}
|
|
1919
|
-
.d2l-card-badge {
|
|
1920
|
-
line-height: 0;
|
|
1921
|
-
padding: 0 0.8rem;
|
|
1922
|
-
}
|
|
1923
|
-
.d2l-card-footer {
|
|
1924
|
-
box-sizing: border-box;
|
|
1925
|
-
flex: none;
|
|
1926
|
-
padding: 1.2rem 0.8rem 0.6rem 0.8rem;
|
|
1927
|
-
pointer-events: none;
|
|
1928
|
-
width: 100%;
|
|
1929
|
-
z-index: 2;
|
|
1930
|
-
}
|
|
1931
|
-
:host([align-center]) .d2l-card-footer {
|
|
1932
|
-
text-align: center;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
.d2l-card-footer ::slotted([slot="footer"]) {
|
|
1936
|
-
pointer-events: all;
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
.d2l-card-footer-hidden .d2l-card-footer {
|
|
1940
|
-
box-sizing: content-box;
|
|
1941
|
-
height: auto;
|
|
1942
|
-
}
|
|
1943
|
-
|
|
1944
|
-
:host([subtle]) {
|
|
1945
|
-
border: none;
|
|
1946
|
-
}
|
|
1947
|
-
:host([subtle][href]) {
|
|
1948
|
-
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.03);
|
|
1949
|
-
}
|
|
1950
|
-
:host([href]:not([_active]):hover) {
|
|
1951
|
-
box-shadow: 0 2px 14px 1px rgba(0, 0, 0, 0.06);
|
|
1952
|
-
}
|
|
1953
|
-
:host([subtle][href]:not([_active]):hover) {
|
|
1954
|
-
box-shadow: 0 4px 18px 2px rgba(0, 0, 0, 0.06);
|
|
1955
|
-
}
|
|
1956
|
-
${getFocusRingStyles(() => ':host([_active])', {'extraStyles': css`border-color: transparent;`})}
|
|
1957
|
-
/* .d2l-card-link-container-hover is used to only color/underline when
|
|
1958
|
-
hovering the anchor; these styles are not applied when hovering actions */
|
|
1959
|
-
:host([href]) .d2l-card-link-container-hover,
|
|
1960
|
-
:host([href][_active]) .d2l-card-content {
|
|
1961
|
-
color: var(--d2l-color-celestine);
|
|
1962
|
-
text-decoration: underline;
|
|
1963
|
-
}
|
|
1964
|
-
/* this is needed to ensure tooltip is not be clipped by adjacent cards */
|
|
1965
|
-
:host([_tooltip_showing]) {
|
|
1966
|
-
z-index: 1;
|
|
1967
|
-
}
|
|
1968
|
-
/* this is needed to ensure open menu will be ontop of adjacent cards */
|
|
1969
|
-
:host([_dropdown-action-open]) {
|
|
1970
|
-
z-index: 2;
|
|
1971
|
-
}
|
|
1972
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
1973
|
-
:host {
|
|
1974
|
-
transition: box-shadow 0.2s;
|
|
1975
|
-
}
|
|
1976
|
-
}
|
|
1977
|
-
@media (prefers-contrast: more) {
|
|
1978
|
-
:host([subtle]) {
|
|
1979
|
-
border: 1px solid var(--d2l-color-gypsum);;
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
`];
|
|
1983
|
-
}
|
|
1984
|
-
|
|
1985
|
-
constructor() {
|
|
1986
|
-
super();
|
|
1987
|
-
this.alignCenter = false;
|
|
1988
|
-
this.download = false;
|
|
1989
|
-
this.subtle = false;
|
|
1990
|
-
this._active = false;
|
|
1991
|
-
this._dropdownActionOpen = false;
|
|
1992
|
-
this._footerHidden = true;
|
|
1993
|
-
this._hover = false;
|
|
1994
|
-
this._tooltipShowing = false;
|
|
1995
|
-
this._onBadgeResize = this._onBadgeResize.bind(this);
|
|
1996
|
-
this._onFooterResize = this._onFooterResize.bind(this);
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
static get focusElementSelector() {
|
|
2000
|
-
return 'a';
|
|
2001
|
-
}
|
|
2002
|
-
|
|
2003
|
-
firstUpdated(changedProperties) {
|
|
2004
|
-
super.firstUpdated(changedProperties);
|
|
2005
|
-
const badgeObserver = new ResizeObserver(this._onBadgeResize);
|
|
2006
|
-
badgeObserver.observe(this.shadowRoot.querySelector('.d2l-card-badge'));
|
|
2007
|
-
const footerObserver = new ResizeObserver(this._onFooterResize);
|
|
2008
|
-
footerObserver.observe(this.shadowRoot.querySelector('.d2l-card-footer'));
|
|
2009
|
-
}
|
|
2010
|
-
|
|
2011
|
-
render() {
|
|
2012
|
-
|
|
2013
|
-
const containerClass = {
|
|
2014
|
-
'd2l-card-container': true,
|
|
2015
|
-
'd2l-visible-on-ancestor-target': true,
|
|
2016
|
-
'd2l-card-footer-hidden': this._footerHidden
|
|
2017
|
-
};
|
|
2018
|
-
|
|
2019
|
-
const linkContainerClass = {
|
|
2020
|
-
'd2l-card-link-container': true,
|
|
2021
|
-
'd2l-card-link-container-hover': this._hover
|
|
2022
|
-
};
|
|
2023
|
-
|
|
2024
|
-
const badgeStyle = {};
|
|
2025
|
-
if (this._badgeMarginTop) badgeStyle.marginTop = this._badgeMarginTop;
|
|
2026
|
-
|
|
2027
|
-
const footerClass = {
|
|
2028
|
-
'd2l-card-footer': true,
|
|
2029
|
-
'd2l-offscreen': this._footerHidden
|
|
2030
|
-
};
|
|
2031
|
-
|
|
2032
|
-
return html`
|
|
2033
|
-
<div class="${classMap(containerClass)}"
|
|
2034
|
-
@d2l-dropdown-open="${this._onDropdownOpen}"
|
|
2035
|
-
@d2l-dropdown-close="${this._onDropdownClose}"
|
|
2036
|
-
@d2l-tooltip-show="${this._onTooltipShow}"
|
|
2037
|
-
@d2l-tooltip-hide="${this._onTooltipHide}">
|
|
2038
|
-
<a @blur="${this._onLinkBlur}"
|
|
2039
|
-
?download="${this.download}"
|
|
2040
|
-
@focus="${this._onLinkFocus}"
|
|
2041
|
-
href="${ifDefined(this.href ? this.href : undefined)}"
|
|
2042
|
-
hreflang="${ifDefined(this.hreflang)}"
|
|
2043
|
-
@mouseenter="${this._onLinkMouseEnter}"
|
|
2044
|
-
@mouseleave="${this._onLinkMouseLeave}"
|
|
2045
|
-
rel="${ifDefined(this.rel)}"
|
|
2046
|
-
target="${ifDefined(this.target)}"
|
|
2047
|
-
type="${ifDefined(this.type)}">
|
|
2048
|
-
<span class="d2l-card-link-text d2l-offscreen">${this.text}</span>
|
|
2049
|
-
</a>
|
|
2050
|
-
<div class="${classMap(linkContainerClass)}">
|
|
2051
|
-
<div class="d2l-card-header"><slot name="header"></slot></div>
|
|
2052
|
-
<div class="d2l-card-badge" style="${styleMap(badgeStyle)}"><slot name="badge"></slot></div>
|
|
2053
|
-
<div class="d2l-card-content"><slot name="content"></slot></div>
|
|
2054
|
-
</div>
|
|
2055
|
-
<div class="d2l-card-actions"><slot name="actions"></slot></div>
|
|
2056
|
-
<div class="${classMap(footerClass)}"><slot name="footer"></slot></div>
|
|
2057
|
-
</div>
|
|
2058
|
-
`;
|
|
2059
|
-
}
|
|
2060
|
-
|
|
2061
|
-
_onBadgeResize(entries) {
|
|
2062
|
-
if (!entries || entries.length === 0) return;
|
|
2063
|
-
const entry = entries[0];
|
|
2064
|
-
this._badgeMarginTop = `${-0.5 * entry.contentRect.height}px`;
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
_onDropdownClose() {
|
|
2068
|
-
this._dropdownActionOpen = false;
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
_onDropdownOpen() {
|
|
2072
|
-
this._dropdownActionOpen = true;
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
_onFooterResize(entries) {
|
|
2076
|
-
if (!entries || entries.length === 0) return;
|
|
2077
|
-
const entry = entries[0];
|
|
2078
|
-
// firefox has a rounding error when calculating the height of the contentRect
|
|
2079
|
-
// with `box-sizing: border-box;` so check for numbers which are close to 0 as well
|
|
2080
|
-
this._footerHidden = (entry.contentRect.height < 1);
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
_onLinkBlur() {
|
|
2084
|
-
this._active = false;
|
|
2085
|
-
}
|
|
2086
|
-
|
|
2087
|
-
_onLinkFocus() {
|
|
2088
|
-
this._active = true;
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
_onLinkMouseEnter() {
|
|
2092
|
-
this._hover = true;
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
_onLinkMouseLeave() {
|
|
2096
|
-
this._hover = false;
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
_onTooltipHide() {
|
|
2100
|
-
this._tooltipShowing = false;
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
|
-
_onTooltipShow() {
|
|
2104
|
-
this._tooltipShowing = true;
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
customElements.define('d2l-card', Card);
|
|
2110
|
-
</code></pre>
|
|
2111
|
-
|
|
2112
328
|
<h2 class="d2l-heading-3">Language Samples</h2>
|
|
2113
329
|
|
|
2114
330
|
<pre class="d2l-code d2l-code-dark line-numbers"><code class="language-arduino">// digital pin 2 has a pushbutton attached to it. Give it a name:
|