Prism Helper
Tokens
Token |
Example |
keyword |
import { stuff } from 'some/where.js';
|
builtin |
pi = round(float('3.14159'), 2)
|
class-name |
class Wizard extends Human { /* ... */ }
|
function |
function helloGrumpy(name) { /* ... */ }
helloGrumpy('Wizard');
|
parameter |
function helloGrumpy(name) { /* ... */ }
|
boolean |
const wizardsLikeBeer = false;
|
number |
const favouriteNumber = 3.14159;
|
string |
const hiGrumpy = 'Hi Grumpy!';
|
template-string, template-punctuation |
const hiGrumpy = `Hi Grumpy!`;
|
interpolation, interpolation-punctuation |
console.log(`Hi there ${name}.`);
|
char |
|
symbol |
|
regex |
let entity = /&#x?[\da-f]{1,8};/;
|
url |
body { background: url("wizard.png"); }
|
operator |
x += (y + 4 >> -z === w) ? b ** c : ~a;
|
variable |
DECLARE @MyCounter INT;
|
constant |
const PI = 3.14159;
|
property |
.grumpy > img { height: 100%; }
|
punctuation |
import { stuff } from 'some/where.js';
|
important |
.grumpy > img { height: 100% !important; }
|
comment |
/* grump wizards */
|
tag |
<h1>Grumpy Wizards</h1>
|
attr-name, attr-value |
<h1 class="dangerous">Grumpy Wizards</h1>
|
namespace |
throw new java.lang.UnsupportedOperationException();
|
prolog |
<?xml version="1.0" encoding="utf-8"?>
|
doctype |
<!DOCTYPE html>
|
cdata |
<![CDATA[ grumpy wizards eat donuts ]]>
|
entity |
£ £
|
atrule |
@media (prefers-reduced-motion: reduce) { /* no animations */ }
|
selector |
.grumpy > img { /* ... */ }
|
null |
{ "fritter": null }
|
color |
background-color: rgba(0, 0, 0, 0.4);
border: 1px solid #cd2026;
|
Large Samples
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8">
<link rel="stylesheet" href="../../demo/styles.css" type="text/css">
<script type="module">
import '../../demo/demo-page.js';
import '../../button/button.js';
import '../../button/button-icon.js';
import '../../dropdown/dropdown-more.js';
import '../../dropdown/dropdown-content.js';
import '../../status-indicator/status-indicator.js';
import '../../tooltip/tooltip.js';
import '../card.js';
import '../card-loading-shimmer.js';
import '../card-content-meta.js';
import '../card-content-title.js';
import '../card-footer-link.js';
</script>
<style>
d2l-card {
vertical-align: top;
}
.subtle-demo {
background-color: #f6f7f8;
padding: 20px;
}
.badge {
text-align: center;
}
.badge > img {
background-color: white;
border: 1px solid #202122; /* ferrite */
border-radius: 6px;
height: 70px;
object-fit: cover;
object-position: center;
}
.badge-status {
background-color: white;
border: 1px solid white;
border-radius: 0.6rem;
display: inline-block;
}
div[slot="footer"] {
display: inline-block;
}
#toggleLoading {
margin-top: 20px;
}
</style>
</head>
<body unresolved>
<d2l-demo-page page-title="d2l-card">
<h2>Subtle Card (badges, no-link)</h2>
<d2l-demo-snippet>
<template>
<div class="subtle-demo">
<d2l-card subtle align-center text="Image Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
<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">
<div slot="badge" class="badge">
<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Stick_Figure.svg/340px-Stick_Figure.svg.png">
</div>
<div slot="content">Image Badge</div>
</d2l-card>
<d2l-card subtle align-center text="Status Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
<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">
<div class="badge-status" slot="badge">
<d2l-status-indicator text="Success" state="success"></d2l-status-indicator>
</div>
<div slot="content">Status Badge</div>
</d2l-card>
<d2l-card subtle align-center text="No Link" style="height: 280px; width: 245px;">
<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">
<div slot="content">No Link</div>
</d2l-card>
</div>
</template>
</d2l-demo-snippet>
<h2>Subtle Card (header actions, meta-content, footer links)</h2>
<d2l-demo-snippet>
<template>
<div class="subtle-demo">
<d2l-card subtle align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
<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>
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<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>
<div slot="footer">
<d2l-card-footer-link id="googleDriveLink1" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
<d2l-tooltip slot="tooltip" for="googleDriveLink1">Go to Google Drive</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="rssFeedLink1" icon="tier1:rss" text="RSS Feed" secondary-count="1">
<d2l-tooltip slot="tooltip" for="rssFeedLink1">RSS Feed</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="outcomesLink1" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
<d2l-tooltip slot="tooltip" for="outcomesLink1">Outcomes</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="assignmentsLink1" icon="tier1:assignments" text="Assignments" secondary-count="3">
<d2l-tooltip slot="tooltip" position="top" style="width: 100%;" for="assignmentsLink1">You have 3 assignments due tomorrow.</d2l-tooltip>
</d2l-card-footer-link>
</div>
</d2l-card>
<d2l-card subtle align-center text="Grade 2" href="https://en.wikipedia.org/wiki/Second_grade" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<div slot="content">Grade 2</div>
<div slot="footer">
<d2l-card-footer-link id="googleDriveLink2" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
<d2l-tooltip slot="tooltip" for="googleDriveLink2">Go to Google Drive</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="rssFeedLink2" icon="tier1:rss" text="RSS Feed" secondary-count="1">
<d2l-tooltip slot="tooltip" for="rssFeedLink2">RSS Feed</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="outcomesLink2" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
<d2l-tooltip slot="tooltip" for="outcomesLink2">Outcomes</d2l-tooltip>
</d2l-card-footer-link>
</div>
</d2l-card>
<d2l-card subtle align-center text="Painting" href="https://en.wikipedia.org/wiki/Painting" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<div slot="content">Painting</div>
<d2l-button slot="footer" style="width: 100%;">Shiny Button</d2l-button>
</d2l-card>
</div>
</template>
</d2l-demo-snippet>
<h2>Card (badges, no-link)</h2>
<d2l-demo-snippet>
<template>
<d2l-card align-center text="Image Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
<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">
<div slot="badge" class="badge">
<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Stick_Figure.svg/340px-Stick_Figure.svg.png">
</div>
<div slot="content">Image Badge</div>
</d2l-card>
<d2l-card align-center text="Status Badge" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 280px; width: 245px;">
<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">
<div class="badge-status" slot="badge">
<d2l-status-indicator text="Success" state="success"></d2l-status-indicator>
</div>
<div slot="content">Status Badge</div>
</d2l-card>
<d2l-card align-center text="No Link" style="height: 280px; width: 245px;">
<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">
<div slot="content">No Link</div>
</d2l-card>
</template>
</d2l-demo-snippet>
<h2>Card (header actions, meta-content, footer links)</h2>
<d2l-demo-snippet>
<template>
<d2l-card align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
<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>
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<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>
<div slot="footer">
<d2l-card-footer-link id="googleDriveLink3" icon="tier1:google-drive" text="Google Drive" secondary-count="100" href="https://www.google.ca/drive/">
<d2l-tooltip slot="tooltip" for="googleDriveLink3">Go to Google Drive</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="rssFeedLink3" icon="tier1:rss" text="RSS Feed" secondary-count="1">
<d2l-tooltip slot="tooltip" for="rssFeedLink3">RSS Feed</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="outcomesLink3" icon="tier1:outcomes" text="Outcomes" secondary-count="5">
<d2l-tooltip slot="tooltip" for="outcomesLink3">Outcomes</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="assignmentsLink3" icon="tier1:assignments" text="Assignments" secondary-count="3">
<d2l-tooltip slot="tooltip" position="top" style="width: 100%;" for="assignmentsLink3">You have 3 assignments due tomorrow.</d2l-tooltip>
</d2l-card-footer-link>
</div>
</d2l-card>
<d2l-card align-center text="Painting" href="https://en.wikipedia.org/wiki/Painting" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<div slot="content">Painting</div>
<d2l-button slot="footer" style="width: 100%;">Shiny Button</d2l-button>
</d2l-card>
<d2l-card align-center text="Grade 2" href="https://en.wikipedia.org/wiki/Second_grade" style="height: 300px; width: 245px;">
<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">
<d2l-dropdown-more slot="actions" translucent visible-on-ancestor text="Open!">
<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>
</d2l-dropdown-more>
<d2l-button-icon slot="actions" translucent text="unpin" icon="tier1:pin-filled"></d2l-button-icon>
<div slot="content">Grade 2</div>
<div slot="footer">
<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/">
<d2l-tooltip slot="tooltip" for="googleDriveLink4">Go to Google Drive</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="rssFeedLink4" icon="tier1:rss" text="RSS Feed" secondary-count-type="count" secondary-count="1">
<d2l-tooltip slot="tooltip" for="rssFeedLink4">RSS Feed</d2l-tooltip>
</d2l-card-footer-link>
<d2l-card-footer-link id="outcomesLink4" icon="tier1:outcomes" text="Outcomes" secondary-count-type="count" secondary-count="5">
<d2l-tooltip slot="tooltip" for="outcomesLink4">Outcomes</d2l-tooltip>
</d2l-card-footer-link>
</div>
</d2l-card>
</template>
</d2l-demo-snippet>
<h2>Card (with header loading)</h2>
<d2l-demo-snippet>
<template>
<d2l-card align-center text="Hydrology" href="https://en.wikipedia.org/wiki/Hydrology" style="height: 300px; width: 245px;">
<d2l-card-loading-shimmer slot="header" loading style="display: block; height: 103.5px; width: 100%;">
<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">
</d2l-card-loading-shimmer>
<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>
</d2l-card>
<div>
<d2l-button id="toggleLoading">Toggle Loading State</d2l-button>
</div>
<script>
document.querySelector('#toggleLoading').addEventListener('click', () => {
const loadingContainer = document.querySelector('d2l-card-loading-shimmer');
loadingContainer.loading = !loadingContainer.loading;
});
</script>
</template>
</d2l-demo-snippet>
</d2l-demo-page>
</body>
</html>
import '../backdrop/backdrop.js';
import '../button/button.js';
import '../focus-trap/focus-trap.js';
import { clearDismissible, setDismissible } from '../../helpers/dismissible.js';
import { findComposedAncestor, getBoundingAncestor, isComposedAncestor, isVisible } from '../../helpers/dom.js';
import { getComposedActiveElement, getFirstFocusableDescendant, getPreviousFocusableAncestor } from '../../helpers/focus.js';
import { classMap } from 'lit/directives/class-map.js';
import { html } from 'lit';
import { LocalizeCoreElement } from '../../helpers/localize-core-element.js';
import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
import { styleMap } from 'lit/directives/style-map.js';
import { tryGetIfrauBackdropService } from '../../helpers/ifrauBackdropService.js';
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
const minBackdropHeightMobile = 42;
const minBackdropWidthMobile = 30;
const outerMarginTopBottom = 18;
const defaultVerticalOffset = 16;
export const DropdownContentMixin = superclass => class extends LocalizeCoreElement(RtlMixin(superclass)) {
static get properties() {
return {
/**
* Optionally align dropdown to either start or end. If not set, the dropdown will attempt be centred.
* @type {'start'|'end'}
*/
align: {
type: String,
reflect: true
},
/**
* Optionally provide boundaries to where the dropdown will appear. Valid properties are "above", "below", "left", and "right".
* @type {object}
*/
boundary: {
type: Object,
},
/**
* Override default max-width (undefined). Specify a number that would be the px value.
* @type {number}
*/
maxWidth: {
type: Number,
reflect: true,
attribute: 'max-width'
},
/**
* Override default min-width (undefined). Specify a number that would be the px value.
* @type {number}
*/
minWidth: {
type: Number,
reflect: true,
attribute: 'min-width'
},
/**
* 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.
* @type {number}
*/
maxHeight: {
type: Number,
attribute: 'max-height'
},
/**
* Override the breakpoint at which mobile styling is used. Defaults to 616px.
* @type {number}
*/
mobileBreakpointOverride: {
type: Number,
attribute: 'mobile-breakpoint'
},
/**
* 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.
* @type {number}
*/
minHeight: {
type: Number,
reflect: true,
attribute: 'min-height'
},
/**
* Opt-out of showing a close button in the footer of tray-style mobile dropdowns.
* @type {boolean}
*/
noMobileCloseButton: {
type: Boolean,
reflect: true,
attribute: 'no-mobile-close-button'
},
/**
* Mobile dropdown style.
* @type {'left'|'right'|'bottom'}
*/
mobileTray: {
type: String,
reflect: true,
attribute: 'mobile-tray'
},
/**
* Opt out of automatically closing on focus or click outside of the dropdown content
* @type {boolean}
*/
noAutoClose: {
type: Boolean,
reflect: true,
attribute: 'no-auto-close'
},
/**
* Opt out of auto-sizing
* @type {boolean}
*/
noAutoFit: {
type: Boolean,
reflect: true,
attribute: 'no-auto-fit'
},
/**
* Opt out of focus being automatically moved to the first focusable element in the dropdown when opened
* @type {boolean}
*/
noAutoFocus: {
type: Boolean,
reflect: true,
attribute: 'no-auto-focus'
},
/**
* Render with no padding
* @type {boolean}
*/
noPadding: {
type: Boolean,
reflect: true,
attribute: 'no-padding'
},
/**
* Render the footer with no padding (if it has content)
* @type {boolean}
*/
noPaddingFooter: {
type: Boolean,
reflect: true,
attribute: 'no-padding-footer'
},
/**
* Render the header with no padding (if it has content)
* @type {boolean}
*/
noPaddingHeader: {
type: Boolean,
reflect: true,
attribute: 'no-padding-header'
},
/**
* Render without a pointer
* @type {boolean}
*/
noPointer: {
type: Boolean,
reflect: true,
attribute: 'no-pointer'
},
/**
* Whether the dropdown is open or not
* @type {boolean}
*/
opened: {
type: Boolean,
reflect: true
},
/**
* Private.
* @ignore
*/
openedAbove: {
type: Boolean,
reflect: true,
attribute: 'opened-above'
},
/**
* Optionally render a d2l-focus-trap around the dropdown content
* @type {boolean}
*/
trapFocus: {
type: Boolean,
reflect: true,
attribute: 'trap-focus'
},
/**
* Provide custom offset, positive or negative
* @type {string}
*/
verticalOffset: {
type: String,
attribute: 'vertical-offset'
},
_bottomOverflow: {
type: Boolean
},
_closing: {
type: Boolean
},
_contentOverflow: {
type: Boolean
},
_dropdownContent: {
type: Boolean,
attribute: 'dropdown-content',
reflect: true
},
_useMobileStyling: {
type: Boolean,
attribute: 'data-mobile',
reflect: true
},
_hasHeader: {
type: Boolean
},
_hasFooter: {
type: Boolean
},
_contentHeight: {
type: Number
},
_position: {
type: Number
},
_showBackdrop: {
type: Boolean
},
_topOverflow: {
type: Boolean
},
_width: {
type: Number
}
};
}
constructor() {
super();
this.noAutoClose = false;
this.noAutoFit = false;
this.noAutoFocus = false;
this.noMobileCloseButton = false;
this.noPadding = false;
this.noPaddingFooter = false;
this.noPaddingHeader = false;
this.noPointer = false;
this.mobileBreakpointOverride = 616;
this.trapFocus = false;
this._useMobileStyling = false;
this.__opened = false;
this.__content = null;
this.__previousFocusableAncestor = null;
this.__applyFocus = true;
this.__dismissibleId = null;
this._dropdownContent = true;
this._bottomOverflow = false;
this._topOverflow = false;
this._closing = false;
this._contentOverflow = false;
this._hasHeader = false;
this._hasFooter = false;
this._showBackdrop = false;
this._verticalOffset = defaultVerticalOffset;
this.__onResize = this.__onResize.bind(this);
this.__onAutoCloseFocus = this.__onAutoCloseFocus.bind(this);
this.__onAutoCloseClick = this.__onAutoCloseClick.bind(this);
this.__toggleScrollStyles = this.__toggleScrollStyles.bind(this);
this._handleMobileResize = this._handleMobileResize.bind(this);
this.__disconnectResizeObserver = this.__disconnectResizeObserver.bind(this);
}
get opened() {
return this.__opened;
}
set opened(val) {
const oldVal = this.__opened;
if (oldVal !== val) {
this.__opened = val;
this.requestUpdate('opened', oldVal);
this.__openedChanged(val);
}
}
connectedCallback() {
super.connectedCallback();
window.addEventListener('resize', this.__onResize);
this.addEventListener('blur', this.__onAutoCloseFocus, true);
document.body.addEventListener('focus', this.__onAutoCloseFocus, true);
document.body.addEventListener('click', this.__onAutoCloseClick, true);
this.mediaQueryList = window.matchMedia(`(max-width: ${this.mobileBreakpointOverride - 1}px)`);
this._useMobileStyling = this.mediaQueryList.matches;
if (this.mediaQueryList.addEventListener) this.mediaQueryList.addEventListener('change', this._handleMobileResize);
}
disconnectedCallback() {
super.disconnectedCallback();
if (this.mediaQueryList.removeEventListener) this.mediaQueryList.removeEventListener('change', this._handleMobileResize);
this.removeEventListener('blur', this.__onAutoCloseFocus);
window.removeEventListener('resize', this.__onResize);
if (document.body) {
// DE41322: document.body can be null in some scenarios
document.body.removeEventListener('focus', this.__onAutoCloseFocus, true);
document.body.removeEventListener('click', this.__onAutoCloseClick, true);
}
clearDismissible(this.__dismissibleId);
this.__dismissibleId = null;
if (this.__resizeObserver) this.__resizeObserver.disconnect();
}
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
this.__content = this.getContentContainer();
this.addEventListener('d2l-dropdown-close', this.__onClose);
this.addEventListener('d2l-dropdown-position', this.__toggleScrollStyles);
}
updated(changedProperties) {
changedProperties.forEach((_, propName) => {
if (propName === 'verticalOffset') {
let newVerticalOffset = parseInt(this.verticalOffset);
if (isNaN(newVerticalOffset)) {
newVerticalOffset = defaultVerticalOffset;
}
this.style.setProperty('--d2l-dropdown-verticaloffset', `${newVerticalOffset}px`);
this._verticalOffset = newVerticalOffset;
}
});
}
close() {
const hide = () => {
this._closing = false;
this._showBackdrop = false;
this.opened = false;
};
if (!reduceMotion && this._useMobileStyling && this.mobileTray && isVisible(this)) {
if (this.shadowRoot) this.shadowRoot.querySelector('.d2l-dropdown-content-width')
.addEventListener('animationend', hide, { once: true });
this._closing = true;
this._showBackdrop = false;
} else {
hide();
}
}
/**
* forceRender is no longer necessary, this is left as a stub so that
* places calling it will not break. It will be removed once the Polymer
* dropdown is swapped over to use this and all instances of
* forceRender are removed.
*/
forceRender() {}
getContentContainer() {
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-container');
}
/**
* Private.
*/
height() {
return this.__content && this.__content.offsetHeight;
}
async open(applyFocus) {
this.__applyFocus = applyFocus !== undefined ? applyFocus : true;
this.opened = true;
await this.updateComplete;
this._showBackdrop = this._useMobileStyling && this.mobileTray;
}
/**
* Waits for the next resize when elem has a height > 0px,
* then calls the __position function.
*/
requestRepositionNextResize(elem) {
if (!elem) return;
if (this.__resizeObserver) this.__resizeObserver.disconnect();
this.__resizeObserver = new ResizeObserver(this.__disconnectResizeObserver);
this.__resizeObserver.observe(elem);
}
async resize() {
if (!this.opened) {
return;
}
this._showBackdrop = this._useMobileStyling && this.mobileTray;
await this.__position();
}
/**
* Private.
*/
scrollTo(scrollTop) {
const content = this.__content;
if (content) {
if (typeof scrollTop === 'number') {
content.scrollTop = scrollTop;
}
return content.scrollTop;
}
}
toggleOpen(applyFocus) {
if (this.opened) {
this.close();
} else {
this.open(!this.noAutoFocus && applyFocus);
}
}
__disconnectResizeObserver(entries) {
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
if (this.__resizeObserver && entry.contentRect.height !== 0) {
this.__resizeObserver.disconnect();
// wrap in rAF for Firefox
requestAnimationFrame(() => {
if (this.opened) this.__position();
});
break;
}
}
}
__getContentBottom() {
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-bottom');
}
__getContentTop() {
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-top');
}
__getOpener() {
const opener = findComposedAncestor(this, (elem) => {
if (elem.dropdownOpener) {
return true;
}
});
return opener;
}
__getPositionContainer() {
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-position');
}
__getWidthContainer() {
return this.shadowRoot && this.shadowRoot.querySelector('.d2l-dropdown-content-width');
}
__handleFooterSlotChange(e) {
this._hasFooter = e.target.assignedNodes().length !== 0;
}
__handleHeaderSlotChange(e) {
this._hasHeader = e.target.assignedNodes().length !== 0;
}
__onAutoCloseClick(e) {
if (!this.opened || this.noAutoClose) {
return;
}
const rootTarget = e.composedPath()[0];
const clickInside = isComposedAncestor(this.getContentContainer(), rootTarget) ||
isComposedAncestor(this.__getContentTop(), rootTarget) ||
isComposedAncestor(this.__getContentBottom(), rootTarget);
if (clickInside) {
return;
}
const opener = this.__getOpener();
if (isComposedAncestor(opener.getOpenerElement(), rootTarget)) {
return;
}
this.close();
}
__onAutoCloseFocus() {
/* timeout needed to work around lack of support for relatedTarget */
setTimeout(() => {
if (!this.opened
|| this.noAutoClose
|| !document.activeElement
|| document.activeElement === this.__previousFocusableAncestor
|| document.activeElement === document.body) {
return;
}
const activeElement = getComposedActiveElement();
if (isComposedAncestor(this, activeElement)
|| isComposedAncestor(this.__getOpener(), activeElement)) {
return;
}
this.close();
}, 0);
}
__onClose(e) {
if (e.target !== this || !document.activeElement) {
return;
}
const activeElement = getComposedActiveElement();
if (!isComposedAncestor(this, activeElement)) {
return;
}
const opener = this.__getOpener();
opener.getOpenerElement().focus();
}
__onResize() {
this.resize();
}
async __openedChanged(newValue) {
// DE44538: wait for dropdown content to fully render,
// otherwise this.getContentContainer() can return null.
await this.updateComplete;
this.__previousFocusableAncestor =
newValue === true
? getPreviousFocusableAncestor(this, false, false)
: null;
const doOpen = async() => {
const content = this.getContentContainer();
if (!this.noAutoFit) {
content.scrollTop = 0;
}
await this.__position();
this._showBackdrop = this._useMobileStyling && this.mobileTray;
if (!this.noAutoFocus && this.__applyFocus) {
const focusable = getFirstFocusableDescendant(this);
if (focusable) {
// Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
requestAnimationFrame(() => focusable.focus());
} else {
content.setAttribute('tabindex', '-1');
content.focus();
}
}
setTimeout(() =>
this.dispatchEvent(new CustomEvent('d2l-dropdown-open', { bubbles: true, composed: true })), 0
);
this.__dismissibleId = setDismissible(() => {
this.close();
});
};
const ifrauBackdropService = await tryGetIfrauBackdropService();
if (newValue) {
if (ifrauBackdropService && this.mobileTray && this._useMobileStyling) {
this._ifrauContextInfo = await ifrauBackdropService.showBackdrop();
}
await doOpen();
} else {
if (this.__dismissibleId) {
clearDismissible(this.__dismissibleId);
this.__dismissibleId = null;
}
if (ifrauBackdropService && this.mobileTray && this._useMobileStyling) {
ifrauBackdropService.hideBackdrop();
this._ifrauContextInfo = null;
}
this._showBackdrop = false;
await this.updateComplete;
/** Dispatched when the dropdown is closed */
this.dispatchEvent(new CustomEvent('d2l-dropdown-close', { bubbles: true, composed: true }));
}
}
async __position(ignoreVertical, contentRect) {
const opener = this.__getOpener();
if (!opener) {
return;
}
const target = opener.getOpenerElement();
if (!target) {
return;
}
const content = this.getContentContainer();
const header = this.__getContentTop();
const footer = this.__getContentBottom();
if (!this.noAutoFit) {
this._contentHeight = null;
}
/* don't let dropdown content horizontally overflow viewport */
this._width = null;
const openerPosition = window.getComputedStyle(opener, null).getPropertyValue('position');
const boundingContainer = getBoundingAncestor(target.parentNode);
const boundingContainerRect = boundingContainer.getBoundingClientRect();
const scrollHeight = boundingContainer.scrollHeight;
await this.updateComplete;
// position check in case consuming app (LMS) has overriden position to make content absolute wrt document
const bounded = (openerPosition === 'relative' && boundingContainer !== document.documentElement);
const adjustPosition = async() => {
const targetRect = target.getBoundingClientRect();
contentRect = contentRect ? contentRect : content.getBoundingClientRect();
const headerFooterHeight = header.getBoundingClientRect().height + footer.getBoundingClientRect().height;
const height = this.minHeight ? this.minHeight : Math.min(this.maxHeight ? this.maxHeight : Number.MAX_VALUE, contentRect.height + headerFooterHeight);
const spaceRequired = {
height: height + 10,
width: contentRect.width
};
let spaceAround;
let spaceAroundScroll;
if (bounded) {
spaceAround = this._constrainSpaceAround({
// allow for target offset + outer margin
above: targetRect.top - boundingContainerRect.top - this._verticalOffset - outerMarginTopBottom,
// allow for target offset + outer margin
below: boundingContainerRect.bottom - targetRect.bottom - this._verticalOffset - outerMarginTopBottom,
// allow for outer margin
left: targetRect.left - boundingContainerRect.left - 20,
// allow for outer margin
right: boundingContainerRect.right - targetRect.right - 20
}, spaceRequired, targetRect);
spaceAroundScroll = this._constrainSpaceAround({
above: targetRect.top - boundingContainerRect.top + boundingContainer.scrollTop,
below: scrollHeight - targetRect.bottom + boundingContainerRect.top - boundingContainer.scrollTop
}, spaceRequired, targetRect);
} else {
spaceAround = this._constrainSpaceAround({
// allow for target offset + outer margin
above: targetRect.top - this._verticalOffset - outerMarginTopBottom,
// allow for target offset + outer margin
below: window.innerHeight - targetRect.bottom - this._verticalOffset - outerMarginTopBottom,
// allow for outer margin
left: targetRect.left - 20,
// allow for outer margin
right: document.documentElement.clientWidth - targetRect.right - 15
}, spaceRequired, targetRect);
spaceAroundScroll = this._constrainSpaceAround({
above: targetRect.top + document.documentElement.scrollTop,
below: scrollHeight - targetRect.bottom - document.documentElement.scrollTop
}, spaceRequired, targetRect);
}
if (!ignoreVertical) {
this.openedAbove = this._getOpenedAbove(spaceAround, spaceAroundScroll, spaceRequired);
}
const centerDelta = contentRect.width - targetRect.width;
const position = this._getPosition(spaceAround, centerDelta);
if (position !== null) {
this._position = position;
}
//Calculate height available to the dropdown contents for overflow because that is the only area capable of scrolling
const availableHeight = this.openedAbove ? spaceAround.above : spaceAround.below;
if (!this.noAutoFit && availableHeight && availableHeight > 0) {
//Only apply maximum if it's less than space available and the header/footer alone won't exceed it (content must be visible)
this._contentHeight = this.maxHeight !== null
&& availableHeight > this.maxHeight
&& headerFooterHeight < this.maxHeight
? this.maxHeight - headerFooterHeight - 2
: availableHeight - headerFooterHeight;
this.__toggleOverflowY(contentRect.height + headerFooterHeight > availableHeight);
// ensure the content height has updated when the __toggleScrollStyles event handler runs
await this.updateComplete;
}
/** Dispatched when the dropdown position finishes adjusting */
this.dispatchEvent(new CustomEvent('d2l-dropdown-position', { bubbles: true, composed: true }));
};
const scrollWidth = Math.max(header.scrollWidth, content.scrollWidth, footer.scrollWidth);
const availableWidth = (bounded ? boundingContainerRect.width - 60 : window.innerWidth - 40);
this._width = (availableWidth > scrollWidth ? scrollWidth : availableWidth) ;
await this.updateComplete;
await adjustPosition();
}
__toggleOverflowY(isOverflowing) {
if (!this.__content) {
return;
}
if (!this._contentHeight) {
return;
}
this._contentOverflow = isOverflowing || this.__content.scrollHeight > this._contentHeight;
}
__toggleScrollStyles() {
/* scrollHeight incorrect in IE by 4px second time opened */
this._bottomOverflow = this.__content.scrollHeight - (this.__content.scrollTop + this.__content.clientHeight) >= 5;
this._topOverflow = this.__content.scrollTop !== 0;
}
_constrainSpaceAround(spaceAround, spaceRequired, targetRect) {
const constrained = { ...spaceAround };
if (this.boundary) {
constrained.above = this.boundary.above >= 0 ? Math.min(spaceAround.above, this.boundary.above) : spaceAround.above;
constrained.below = this.boundary.below >= 0 ? Math.min(spaceAround.below, this.boundary.below) : spaceAround.below;
constrained.left = this.boundary.left >= 0 ? Math.min(spaceAround.left, this.boundary.left) : spaceAround.left;
constrained.right = this.boundary.right >= 0 ? Math.min(spaceAround.right, this.boundary.right) : spaceAround.right;
}
const isRTL = this.getAttribute('dir') === 'rtl';
if ((this.align === 'start' && !isRTL) || (this.align === 'end' && isRTL)) {
constrained.left = Math.max(0, spaceRequired.width - (targetRect.width + spaceAround.right));
} else if ((this.align === 'start' && isRTL) || (this.align === 'end' && !isRTL)) {
constrained.right = Math.max(0, spaceRequired.width - (targetRect.width + spaceAround.left));
}
return constrained;
}
_getBottomTrayStyling() {
let maxHeightOverride;
let availableHeight = Math.min(window.innerHeight, window.screen.height);
if (this._ifrauContextInfo) availableHeight = this._ifrauContextInfo.availableHeight;
// default maximum height for bottom tray (42px margin)
const mobileTrayMaxHeightDefault = availableHeight - minBackdropHeightMobile;
if (this.maxHeight) {
// if maxWidth provided is smaller, use the maxWidth
maxHeightOverride = Math.min(mobileTrayMaxHeightDefault, this.maxHeight);
} else {
maxHeightOverride = mobileTrayMaxHeightDefault;
}
maxHeightOverride = `${maxHeightOverride}px`;
let bottomOverride;
if (this._ifrauContextInfo) {
// Bottom override is measured as
// the distance from the bottom of the screen
const screenHeight =
window.innerHeight
- this._ifrauContextInfo.availableHeight
+ Math.min(this._ifrauContextInfo.top, 0);
bottomOverride = `${screenHeight}px`;
}
const widthOverride = '100vw';
const widthStyle = {
minWidth: widthOverride,
width: widthOverride,
maxHeight: maxHeightOverride,
bottom: bottomOverride
};
const contentWidthStyle = {
/* set width of content in addition to width container so header and footer borders are full width */
width: widthOverride
};
const headerStyle = {
...contentWidthStyle,
minHeight: this._hasHeader ? 'auto' : '5px'
};
const footerStyle = {
...contentWidthStyle,
minHeight: this._hasFooter || !this.noMobileCloseButton ? 'auto' : '5px'
};
const contentStyle = {
...contentWidthStyle,
maxHeight: maxHeightOverride,
overflowY: this._contentOverflow ? 'auto' : 'hidden'
};
const closeButtonStyles = {
display: !this.noMobileCloseButton ? 'inline-block' : 'none',
width: this._getTrayFooterWidth(),
padding: this._hasFooter && !this.noPaddingFooter ? '12px 0 0 0' : '12px',
margin: this._getTrayFooterMargin()
};
return {
'width' : widthStyle,
'header' : headerStyle,
'footer' : footerStyle,
'content' : contentStyle,
'close' : closeButtonStyles
};
}
_getDropdownStyling() {
const widthStyle = {
maxWidth: this.maxWidth ? `${this.maxWidth}px` : '',
minWidth: this.minWidth ? `${this.minWidth}px` : '',
/* add 2 to content width since scrollWidth does not include border */
width: this._width ? `${this._width + 20}px` : ''
};
const contentWidthStyle = {
minWidth: this.minWidth ? `${this.minWidth}px` : '',
/* set width of content in addition to width container so header and footer borders are full width */
width: this._width ? `${this._width + 18}px` : '',
};
const contentStyle = {
...contentWidthStyle,
maxHeight: this._contentHeight ? `${this._contentHeight}px` : '',
overflowY: this._contentOverflow ? 'auto' : 'hidden'
};
const closeButtonStyle = {
display: 'none',
};
return {
'width' : widthStyle,
'content' : contentStyle,
'close' : closeButtonStyle,
'header' : contentWidthStyle,
'footer' : contentWidthStyle
};
}
_getLeftRightTrayStyling() {
let maxWidthOverride = this.maxWidth;
let availableWidth = Math.min(window.innerWidth, window.screen.width);
if (this._ifrauContextInfo) availableWidth = this._ifrauContextInfo.availableWidth;
// default maximum width for tray (30px margin)
const mobileTrayMaxWidthDefault = Math.min(availableWidth - minBackdropWidthMobile, 420);
if (maxWidthOverride) {
// if maxWidth provided is smaller, use the maxWidth
maxWidthOverride = Math.min(mobileTrayMaxWidthDefault, maxWidthOverride);
} else {
maxWidthOverride = mobileTrayMaxWidthDefault;
}
let minWidthOverride = this.minWidth;
// minimum size - 285px
const mobileTrayMinWidthDefault = 285;
if (minWidthOverride) {
// if minWidth provided is smaller, use the minumum width for tray
minWidthOverride = Math.max(mobileTrayMinWidthDefault, minWidthOverride);
} else {
minWidthOverride = mobileTrayMinWidthDefault;
}
// if no width property set, automatically size to maximum width
let widthOverride = this._width ? this._width : maxWidthOverride;
// ensure width is between minWidth and maxWidth
if (widthOverride && maxWidthOverride && widthOverride > (maxWidthOverride - 20)) widthOverride = maxWidthOverride - 20;
if (widthOverride && minWidthOverride && widthOverride < (minWidthOverride - 20)) widthOverride = minWidthOverride - 20;
maxWidthOverride = `${maxWidthOverride}px`;
minWidthOverride = `${minWidthOverride}px`;
const contentWidth = `${widthOverride + 18}px`;
/* add 2 to content width since scrollWidth does not include border */
const containerWidth = `${widthOverride + 20}px`;
let maxHeightOverride = '';
if (this._ifrauContextInfo) maxHeightOverride = `${this._ifrauContextInfo.availableHeight}px`;
let topOverride;
if (this._ifrauContextInfo) {
// if inside iframe, use ifrauContext top as top of screen
topOverride = `${this._ifrauContextInfo.top < 0 ? -this._ifrauContextInfo.top : 0}px`;
} else if (window.innerHeight > window.screen.height) {
// non-responsive page, manually override top to scroll distance
topOverride = window.pageYOffset;
}
let rightOverride;
let leftOverride;
if (this.mobileTray === 'right') {
// On non-responsive pages, the innerWidth may be wider than the screen,
// override right to stick to right of viewport
rightOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
}
if (this.mobileTray === 'left') {
// On non-responsive pages, the innerWidth may be wider than the screen,
// override left to stick to left of viewport
leftOverride = `${Math.max(window.innerWidth - window.screen.width, 0)}px`;
}
const widthStyle = {
maxWidth: maxWidthOverride,
minWidth: minWidthOverride,
width: containerWidth,
maxHeight: maxHeightOverride,
top: topOverride,
right: rightOverride,
left: leftOverride,
};
const contentWidthStyle = {
minWidth: minWidthOverride,
/* set width of content in addition to width container so header and footer borders are full width */
width: contentWidth,
};
const headerStyle = {
...contentWidthStyle,
minHeight: this._hasHeader ? 'auto' : '5px'
};
const footerStyle = {
...contentWidthStyle,
minHeight: this._hasFooter || !this.noMobileCloseButton ? 'auto' : '5px'
};
const contentStyle = {
...contentWidthStyle,
maxHeight: maxHeightOverride,
overflowY: this._contentOverflow ? 'auto' : 'hidden'
};
const closeButtonStyles = {
display: !this.noMobileCloseButton ? 'inline-block' : 'none',
width: this._getTrayFooterWidth(),
padding: this._hasFooter && !this.noPaddingFooter ? '12px 0 0 0' : '12px',
margin: this._getTrayFooterMargin()
};
return {
'width' : widthStyle,
'header' : headerStyle,
'footer' : footerStyle,
'content' : contentStyle,
'close' : closeButtonStyles
};
}
_getOpenedAbove(spaceAround, spaceAroundScroll, spaceRequired) {
if (spaceAround.below >= spaceRequired.height) {
return false;
}
if (spaceAround.above >= spaceRequired.height) {
return true;
}
if (!this.noAutoFit) {
// if auto-fit is enabled, scroll will be enabled for the
// inner content so it will always fit in the available space
// so pick the largest space it can be displayed in
return spaceAround.above > spaceAround.below;
}
if (spaceAroundScroll.below >= spaceRequired.height) {
return false;
}
if (spaceAroundScroll.above >= spaceRequired.height) {
return true;
}
// if auto-fit is disabled and it doesn't fit in the scrollable space
// above or below, always open down because it can add scrollable space
return false;
}
_getPosition(spaceAround, centerDelta) {
const contentXAdjustment = centerDelta / 2;
if (centerDelta <= 0) {
return contentXAdjustment * -1;
}
if (spaceAround.left > contentXAdjustment && spaceAround.right > contentXAdjustment) {
// center with target
return contentXAdjustment * -1;
}
const isRTL = this.getAttribute('dir') === 'rtl';
if (!isRTL) {
if (spaceAround.left < contentXAdjustment) {
// slide content right (not enough space to center)
return spaceAround.left * -1;
} else if (spaceAround.right < contentXAdjustment) {
// slide content left (not enough space to center)
return (centerDelta * -1) + spaceAround.right;
}
} else {
if (spaceAround.left < contentXAdjustment) {
// slide content right (not enough space to center)
return (centerDelta * -1) + spaceAround.left;
} else if (spaceAround.right < contentXAdjustment) {
// slide content left (not enough space to center)
return spaceAround.right * -1;
}
}
return null;
}
_getTrayFooterMargin() {
let footerMargin;
if (this._hasFooter) {
footerMargin = '0';
} else if (this.getAttribute('dir') === 'rtl') {
footerMargin = '-20px -20px -20px 0px';
} else {
footerMargin = '-20px 0 -20px -20px';
}
return footerMargin;
}
_getTrayFooterWidth() {
let footerWidth;
if (this.noPaddingFooter) {
footerWidth = 'calc(100% - 24px)';
} else if (this._hasFooter) {
footerWidth = '100%';
} else {
footerWidth = 'calc(100% + 16px)';
}
return footerWidth;
}
_handleFocusTrapEnter() {
if (this.__applyFocus && !this.noAutoFocus) {
const content = this.getContentContainer();
const focusable = getFirstFocusableDescendant(content);
if (focusable) {
// Removing the rAF call can allow infinite focus looping to happen in content using a focus trap
requestAnimationFrame(() => focusable.focus());
} else {
content.setAttribute('tabindex', '-1');
content.focus();
}
}
/** Dispatched when user focus enters the dropdown content (trap-focus option only) */
this.dispatchEvent(new CustomEvent('d2l-dropdown-focus-enter', { detail:{ applyFocus: this.__applyFocus } }));
}
async _handleMobileResize() {
this._useMobileStyling = this.mediaQueryList.matches;
if (this.opened) this._showBackdrop = this._useMobileStyling && this.mobileTray;
if (this.opened) await this.__position();
}
_renderContent() {
const positionStyle = {};
const isRTL = this.getAttribute('dir') === 'rtl';
if (this._position) {
if (!isRTL) {
positionStyle.left = `${this._position}px`;
} else {
positionStyle.right = `${this._position}px`;
}
}
const mobileTrayRightLeft = this._useMobileStyling && (this.mobileTray === 'right' || this.mobileTray === 'left');
const mobileTrayBottom = this._useMobileStyling && (this.mobileTray === 'bottom');
let stylesMap;
if (mobileTrayBottom) {
stylesMap = this._getBottomTrayStyling();
} else if (mobileTrayRightLeft) {
stylesMap = this._getLeftRightTrayStyling();
} else {
stylesMap = this._getDropdownStyling();
}
const widthStyle = stylesMap['width'];
const headerStyle = stylesMap['header'];
const footerStyle = stylesMap['footer'];
const contentStyle = stylesMap['content'];
const closeButtonStyles = stylesMap['close'];
const topClasses = {
'd2l-dropdown-content-top': true,
'd2l-dropdown-content-top-scroll': this._topOverflow,
'd2l-dropdown-content-header': this._hasHeader
};
const bottomClasses = {
'd2l-dropdown-content-bottom': true,
'd2l-dropdown-content-bottom-scroll': this._bottomOverflow,
'd2l-dropdown-content-footer': this._hasFooter || (this._useMobileStyling && this.mobileTray && !this.noMobileCloseButton)
};
let dropdownContentSlots = html`
<div
id="d2l-dropdown-wrapper"
class="d2l-dropdown-content-width"
style=${styleMap(widthStyle)}
?data-closing="${this._closing}">
<div class=${classMap(topClasses)} style=${styleMap(headerStyle)}>
<slot name="header" @slotchange="${this.__handleHeaderSlotChange}"></slot>
</div>
<div
class="d2l-dropdown-content-container"
style=${styleMap(contentStyle)}
@scroll=${this.__toggleScrollStyles}>
<slot class="d2l-dropdown-content-slot"></slot>
</div>
<div class=${classMap(bottomClasses)} style=${styleMap(footerStyle)}>
<slot name="footer" @slotchange="${this.__handleFooterSlotChange}"></slot>
<d2l-button
class="dropdown-close-btn"
style=${styleMap(closeButtonStyles)}
@click=${this.close}>
${this.localize('components.dropdown.close')}
</d2l-button>
</div>
</div>
`;
if (this.trapFocus) {
dropdownContentSlots = html`
<d2l-focus-trap
@d2l-focus-trap-enter="${this._handleFocusTrapEnter}"
?trap="${this.opened}">
${dropdownContentSlots}
</d2l-focus-trap>`;
}
const dropdown = html`
<div class="d2l-dropdown-content-position" style=${styleMap(positionStyle)}>
${dropdownContentSlots}
</div>
`;
return (this.mobileTray) ? html`
${dropdown}
<d2l-backdrop
for-target="d2l-dropdown-wrapper"
?shown="${this._showBackdrop}" >
</d2l-backdrop>`
: html`${dropdown}`;
}
};
import '../colors/colors.js';
import { css, html, LitElement } from 'lit';
import { classMap } from 'lit/directives/class-map.js';
import { FocusMixin } from '../../mixins/focus/focus-mixin.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { offscreenStyles } from '../offscreen/offscreen.js';
import ResizeObserver from 'resize-observer-polyfill/dist/ResizeObserver.es.js';
import { RtlMixin } from '../../mixins/rtl/rtl-mixin.js';
import { styleMap } from 'lit/directives/style-map.js';
/**
* A container element that provides specific layout using several slots.
* @slot content - Slot for primary content such as title and supplementary info (no actionable elements)
* @slot actions - Slot for buttons and dropdown openers to be placed in top right corner of header
* @slot badge - Slot for badge content, such as a profile image or status indicator
* @slot footer - Slot for footer content, such secondary actions
* @slot header - Slot for header content, such as course image (no actionable elements)
*/
class Card extends FocusMixin(RtlMixin(LitElement)) {
static get properties() {
return {
/**
* Style the card's content and footer as centered horizontally
* @type {boolean}
*/
alignCenter: { type: Boolean, attribute: 'align-center', reflect: true },
/**
* Download a URL instead of navigating to it
* @type {boolean}
*/
download: { type: Boolean, reflect: true },
/**
* Location for the primary action/navigation
* @type {string}
*/
href: { type: String, reflect: true },
/**
* Indicates the human language of the linked resource; purely advisory, with no built-in functionality
* @type {string}
*/
hreflang: { type: String, reflect: true },
/**
* Specifies the relationship of the target object to the link object
* @type {string}
*/
rel: { type: String, reflect: true },
/**
* Subtle aesthetic on non-white backgrounds
* @type {boolean}
*/
subtle: { type: Boolean, reflect: true },
/**
* Where to display the linked URL
* @type {string}
*/
target: { type: String, reflect: true },
/**
* Accessible text for the card (will be announced when AT user focuses)
* @type {string}
*/
text: { type: String, reflect: true },
/**
* Specifies the media type in the form of a MIME type for the linked URL; purely advisory, with no built-in functionality
* @type {string}
*/
type: { type: String, reflect: true },
_active: { type: Boolean, reflect: true },
_dropdownActionOpen: { type: Boolean, attribute: '_dropdown-action-open', reflect: true },
_hover: { type: Boolean },
_badgeMarginTop: { type: String },
_footerHidden: { type: Boolean },
_tooltipShowing: { type: Boolean, attribute: '_tooltip_showing', reflect: true }
};
}
static get styles() {
return [offscreenStyles, css`
:host {
background-color: #ffffff;
border: 1px solid var(--d2l-color-gypsum);
border-radius: 6px;
box-sizing: border-box;
display: inline-block;
position: relative;
z-index: 0;
}
.d2l-card-container {
align-items: flex-start; /* required so that footer will not stretch to 100% width */
display: flex;
flex-direction: column;
height: 100%;
position: relative;
}
.d2l-card-link-container {
flex-basis: auto;
flex-grow: 1;
flex-shrink: 1;
width: 100%; /* required for Legacy-Edge and FF when align-items: flex-start is specified */
}
.d2l-card-link-text {
display: inline-block;
}
.d2l-card-header {
border-start-end-radius: 6px;
border-start-start-radius: 6px;
overflow: hidden;
}
a {
bottom: -1px;
display: block;
left: -1px;
outline: none;
position: absolute;
right: -1px;
top: -1px;
z-index: 1;
}
:host([subtle]) a {
bottom: 0;
left: 0;
right: 0;
top: 0;
}
:host(:hover) a {
bottom: -5px;
}
:host([subtle]:hover) a {
bottom: -4px;
}
.d2l-card-content {
padding: 1.2rem 0.8rem 0 0.8rem;
}
:host([align-center]) .d2l-card-content {
text-align: center;
}
.d2l-card-footer-hidden .d2l-card-content {
padding-bottom: 1.2rem;
}
.d2l-card-actions {
inset-inline-end: 0.6rem;
position: absolute;
top: 0.6rem;
/* this must be higher than footer z-index so dropdowns will be on top */
z-index: 3;
}
.d2l-card-actions ::slotted(*) {
margin-inline-start: 0.3rem;
}
.d2l-card-badge {
line-height: 0;
padding: 0 0.8rem;
}
.d2l-card-footer {
box-sizing: border-box;
flex: none;
padding: 1.2rem 0.8rem 0.6rem 0.8rem;
pointer-events: none;
width: 100%;
z-index: 2;
}
:host([align-center]) .d2l-card-footer {
text-align: center;
}
.d2l-card-footer ::slotted([slot="footer"]) {
pointer-events: all;
}
.d2l-card-footer-hidden .d2l-card-footer {
box-sizing: content-box;
height: auto;
}
:host([subtle]) {
border: none;
}
:host([subtle][href]) {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.03);
}
:host([href]:not([_active]):hover) {
box-shadow: 0 2px 14px 1px rgba(0, 0, 0, 0.06);
}
:host([subtle][href]:not([_active]):hover) {
box-shadow: 0 4px 18px 2px rgba(0, 0, 0, 0.06);
}
${getFocusRingStyles(() => ':host([_active])', {'extraStyles': css`border-color: transparent;`})}
/* .d2l-card-link-container-hover is used to only color/underline when
hovering the anchor; these styles are not applied when hovering actions */
:host([href]) .d2l-card-link-container-hover,
:host([href][_active]) .d2l-card-content {
color: var(--d2l-color-celestine);
text-decoration: underline;
}
/* this is needed to ensure tooltip is not be clipped by adjacent cards */
:host([_tooltip_showing]) {
z-index: 1;
}
/* this is needed to ensure open menu will be ontop of adjacent cards */
:host([_dropdown-action-open]) {
z-index: 2;
}
@media (prefers-reduced-motion: no-preference) {
:host {
transition: box-shadow 0.2s;
}
}
@media (prefers-contrast: more) {
:host([subtle]) {
border: 1px solid var(--d2l-color-gypsum);;
}
}
`];
}
constructor() {
super();
this.alignCenter = false;
this.download = false;
this.subtle = false;
this._active = false;
this._dropdownActionOpen = false;
this._footerHidden = true;
this._hover = false;
this._tooltipShowing = false;
this._onBadgeResize = this._onBadgeResize.bind(this);
this._onFooterResize = this._onFooterResize.bind(this);
}
static get focusElementSelector() {
return 'a';
}
firstUpdated(changedProperties) {
super.firstUpdated(changedProperties);
const badgeObserver = new ResizeObserver(this._onBadgeResize);
badgeObserver.observe(this.shadowRoot.querySelector('.d2l-card-badge'));
const footerObserver = new ResizeObserver(this._onFooterResize);
footerObserver.observe(this.shadowRoot.querySelector('.d2l-card-footer'));
}
render() {
const containerClass = {
'd2l-card-container': true,
'd2l-visible-on-ancestor-target': true,
'd2l-card-footer-hidden': this._footerHidden
};
const linkContainerClass = {
'd2l-card-link-container': true,
'd2l-card-link-container-hover': this._hover
};
const badgeStyle = {};
if (this._badgeMarginTop) badgeStyle.marginTop = this._badgeMarginTop;
const footerClass = {
'd2l-card-footer': true,
'd2l-offscreen': this._footerHidden
};
return html`
<div class="${classMap(containerClass)}"
@d2l-dropdown-open="${this._onDropdownOpen}"
@d2l-dropdown-close="${this._onDropdownClose}"
@d2l-tooltip-show="${this._onTooltipShow}"
@d2l-tooltip-hide="${this._onTooltipHide}">
<a @blur="${this._onLinkBlur}"
?download="${this.download}"
@focus="${this._onLinkFocus}"
href="${ifDefined(this.href ? this.href : undefined)}"
hreflang="${ifDefined(this.hreflang)}"
@mouseenter="${this._onLinkMouseEnter}"
@mouseleave="${this._onLinkMouseLeave}"
rel="${ifDefined(this.rel)}"
target="${ifDefined(this.target)}"
type="${ifDefined(this.type)}">
<span class="d2l-card-link-text d2l-offscreen">${this.text}</span>
</a>
<div class="${classMap(linkContainerClass)}">
<div class="d2l-card-header"><slot name="header"></slot></div>
<div class="d2l-card-badge" style="${styleMap(badgeStyle)}"><slot name="badge"></slot></div>
<div class="d2l-card-content"><slot name="content"></slot></div>
</div>
<div class="d2l-card-actions"><slot name="actions"></slot></div>
<div class="${classMap(footerClass)}"><slot name="footer"></slot></div>
</div>
`;
}
_onBadgeResize(entries) {
if (!entries || entries.length === 0) return;
const entry = entries[0];
this._badgeMarginTop = `${-0.5 * entry.contentRect.height}px`;
}
_onDropdownClose() {
this._dropdownActionOpen = false;
}
_onDropdownOpen() {
this._dropdownActionOpen = true;
}
_onFooterResize(entries) {
if (!entries || entries.length === 0) return;
const entry = entries[0];
// firefox has a rounding error when calculating the height of the contentRect
// with `box-sizing: border-box;` so check for numbers which are close to 0 as well
this._footerHidden = (entry.contentRect.height < 1);
}
_onLinkBlur() {
this._active = false;
}
_onLinkFocus() {
this._active = true;
}
_onLinkMouseEnter() {
this._hover = true;
}
_onLinkMouseLeave() {
this._hover = false;
}
_onTooltipHide() {
this._tooltipShowing = false;
}
_onTooltipShow() {
this._tooltipShowing = true;
}
}
customElements.define('d2l-card', Card);
Language Samples
// digital pin 2 has a pushbutton attached to it. Give it a name:
int pushButton = 2;
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
// make the pushbutton's pin an input:
pinMode(pushButton, INPUT);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input pin:
int buttonState = digitalRead(pushButton);
// print out the state of the button:
Serial.println(buttonState);
delay(1); // delay in between reads for stability
}
AREA ARMex, CODE, READONLY
; Name this block of code ARMex
ENTRY ; Mark first instruction to execute
start
MOV r0, #10 ; Set up parameters
MOV r1, #3
ADD r0, r0, r1 ; r0 = r0 + r1
stop
MOV r0, #0x18 ; angel_SWIreason_ReportException
LDR r1, =0x20026 ; ADP_Stopped_ApplicationExit
SVC #0x123456 ; ARM semihosting (formerly SWI)
END ; Mark end of file
#!/bin/bash
if [ -d $directory ]; then
echo "Directory exists"
else
echo "Directory does not exists"
fi
#include <stdio.h>
int main() {
char c;
printf("Enter a character: ");
scanf("%c", &c);
// %d displays the integer value of a character
// %c displays the actual character
printf("ASCII value of %c = %d", c, c);
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int divisor, dividend, quotient, remainder;
cout << "Enter dividend: ";
cin >> dividend;
cout << "Enter divisor: ";
cin >> divisor;
quotient = dividend / divisor;
remainder = dividend % divisor;
cout << "Quotient = " << quotient << endl;
cout << "Remainder = " << remainder;
return 0;
}
using System;
namespace Method {
class Program {
// method declaration
public void display() {
Console.WriteLine("Hello World");
}
static void Main(string[] args) {
// create class object
Program p1 = new Program();
//call method
p1.display();
Console.ReadLine();
}
}
}
.grumpy > img {
background-image: url("smile.png");
height: 100%;
width: var(--some-var);
}
@font-face {
font-family: 'Lato';
font-style: normal;
src: local('Lato Regular');
}
@media (prefers-reduced-motion: reduce) {
:host([opened]), :host([opened-above]) {
animation: none !important;
}
}
{-# START_FILE main.hs #-}
import System.IO
main = do
handle <- openFile "file.txt" ReadMode
contents <- hGetContents handle
putStr contents
hClose handle
{-# START_FILE file.txt #-}
Hello, world!
import System.Random
main = (randomRIO (1, 100) :: IO Int) >>= print
public class VowelConsonant {
public static void main(String[] args) {
char ch = 'i';
if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' )
System.out.println(ch + " is vowel");
else
System.out.println(ch + " is consonant");
}
}
/* grumpy javascript */
import { stuff } from 'some/where.js';
function helloGrumpy(name) {
console.log(`Hi there ${name}.`);
}
class Wizard extends Human { }
const grumpy = new Wizard();
let wizardFoods = [...donuts, ... cakes, ...pies];
const wizardsLikeBeer = false;
const favouriteNumber = 3.14159;
const PI = 3.14159;
const regex = /[^\w\s]/g; // regex, regex-delimiter, regex-source, regex-flag
{
"jinxed": "gnomes",
"grumpy": [ "wizards" ],
"jelly": 99,
"donut": true,
"fritter": null
}
fun main(args: Array) {
val a = 2.3
val b = 4
val c = 5.6
val root1: Double
val root2: Double
val output: String
val determinant = b * b - 4.0 * a * c
// condition for real and different roots
if (determinant > 0) {
root1 = (-b + Math.sqrt(determinant)) / (2 * a)
root2 = (-b - Math.sqrt(determinant)) / (2 * a)
output = "root1 = %.2f and root2 = %.2f".format(root1, root2)
}
// Condition for real and equal roots
else if (determinant == 0.0) {
root2 = -b / (2 * a)
root1 = root2
output = "root1 = root2 = %.2f;".format(root1)
}
// If roots are not real
else {
val realPart = -b / (2 * a)
val imaginaryPart = Math.sqrt(-determinant) / (2 * a)
output = "root1 = %.2f+%.2fi and root2 = %.2f-%.2fi".format(realPart, imaginaryPart, realPart, imaginaryPart)
}
println(output)
}
\begin{aligned} \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\ \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\ \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\ \nabla \cdot \vec{\mathbf{B}} & = 0 \end{aligned}
P(E) = {n \choose k} p^k (1-p)^{ n-k}
\begin{aligned} \dot{x} & = \sigma(y-x) \\ \dot{y} & = \rho x - y - xz \\ \dot{z} & = -\beta z + xy \end{aligned}
<!DOCTYPE html> <!-- doctype, doctype-tag, name -->
<html>
<body class="typography">
<![CDATA[ some cdata section ]]>
<div style="color: blue;">
£
£
</div>
</body>
</html>
for n = 1:5
x = n*0.1;
z = myfunc2(x,2,3,7);
fprintf('x = %4.2f f(x) = %8.4f \r',x,z)
end
# Program to check if a number is prime or not
num = 29
# To take input from the user
#num = int(input("Enter a number: "))
# define a flag variable
flag = False
# prime numbers are greater than 1
if num > 1:
# check for factors
for i in range(2, num):
if (num % i) == 0:
# if factor is found, set flag to True
flag = True
# break out of loop
break
# check if flag is True
if flag:
print(num, "is not a prime number")
else:
print(num, "is a prime number")
# decimal variable
num = as.integer(readline(prompt = "Enter a number: "))
# num = 15
isPrime = 0
if (num > 1) {
isPrime = 1
for (i in 2: (num - 1)) {
if ((num %% i) == 0) {
isPrime = 0
break
}
}
}
if (num == 2) isPrime = 1
if (isPrime == 1) {
print(paste(num, "is a prime number"))
} else {
print(paste(num, "is not a prime number"))
}
(define (rgb-maker mk)
(lambda (sz)
(vc-append (colorize (mk sz) "red")
(colorize (mk sz) "green")
(colorize (mk sz) "blue"))))
(\W|^)[\w.\-]{0,25}@(yahoo|hotmail|gmail)\.com(\W|$)
(\W|^)po[#\-]{0,1}\s{0,1}\d{2}[\s-]{0,1}\d{4}(\W|$)
^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$
\b(?:word1\W+(?:\w+\W+){1,6}?word2|word2\W+(?:\w+\W+){1,6}?word1)\b
^(?=.*?\bone\b)(?=.*?\btwo\b)(?=.*?\bthree\b).*$
DECLARE @MyCounter INT; /* keyword, variable, punctuation */
SELECT AVG(Calories) AS AverageCalories FROM Desserts;
SELECT * FROM Desserts
SET ROWCOUNT 4;
SET @Hidden = FALSE;
SET @Donuts = 'Yummy';
CloudDeploy[
FormFunction[FormObject[{"ImageURL" -> "String"}],
ImageEffect[
ColorConvert[
ImageMultiply[
ColorConvert[
ImageAdd[ImageAdjust[Import[#ImageURL], .2],
RGBColor[.25, .25, -.1]], "HSB"], Hue[1, .7, 1]],
"RGB"], {"PoissonNoise", .5}] &, "JPEG"],
Permissions -> "Public"]
More Samples (Web Component Scope)