UNPKG

@microsoft/mgt

Version:
167 lines • 6.92 kB
/** * ------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. * See License in the project root for license information. * ------------------------------------------------------------------------------------------- */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { customElement, html, LitElement, property } from 'lit-element'; import { classMap } from 'lit-html/directives/class-map'; import { styles } from './mgt-flyout-css'; /** * A component to create flyout anchored to an element * * @export * @class MgtFlyout * @extends {LitElement} */ let MgtFlyout = class MgtFlyout extends LitElement { constructor() { super(...arguments); /** * Gets or sets whether the flyout is visible * * @type {string} * @memberof MgtComponent */ this.isOpen = false; this.renderedOnce = false; } /** * Array of styles to apply to the element. The styles should be defined * using the `css` tag function. */ static get styles() { return styles; } /** * Invoked when the element is first updated. Implement to perform one time * work on the element after update. * * Setting properties inside this method will trigger the element to update * again after this update cycle completes. * * * @param _changedProperties Map of changed properties with old values */ firstUpdated() { this.addEventListener('updated', e => { this.updateFlyout(); }); } /** * Invoked whenever the element is updated. Implement to perform * post-updating tasks via DOM APIs, for example, focusing an element. * * Setting properties inside this method will trigger the element to update * again after this update cycle completes. * * * @param changedProperties Map of changed properties with old values */ updated(changedProps) { super.updated(changedProps); this.updateFlyout(); } /** * Invoked on each update to perform rendering tasks. This method must return * a lit-html TemplateResult. Setting properties inside this method will *not* * trigger the element to update. */ render() { return html ` <div class="root"> <div class="anchor"> <slot></slot> </div> ${this.renderFlyout()} </div> `; } renderFlyout() { if (!this.isOpen && !this.renderedOnce) { return; } this.renderedOnce = true; const classes = { flyout: true, visible: this.isOpen }; return html ` <div class=${classMap(classes)}> <slot name="flyout"></slot> </div> `; } updateFlyout() { const anchor = this.renderRoot.querySelector('.anchor'); const flyout = this.renderRoot.querySelector('.flyout'); if (flyout && anchor) { let left; let bottom; let top; if (this.isOpen) { const flyoutRect = flyout.getBoundingClientRect(); const anchorRect = anchor.getBoundingClientRect(); // normalize flyoutrect since we could have moved it before // need to know where would it render, not where it renders const flyoutTop = anchorRect.bottom; const flyoutLeft = anchorRect.left; const flyoutRight = flyoutLeft + flyoutRect.width; const flyoutBottom = flyoutTop + flyoutRect.height; const windowWidth = window.innerWidth && document.documentElement.clientWidth ? Math.min(window.innerWidth, document.documentElement.clientWidth) : window.innerWidth || document.documentElement.clientWidth; const windowHeight = window.innerHeight && document.documentElement.clientHeight ? Math.min(window.innerHeight, document.documentElement.clientHeight) : window.innerHeight || document.documentElement.clientHeight; if (flyoutRect.width > windowWidth) { // page width is smaller than flyout, render all the way to the left left = -flyoutLeft; } else if (anchorRect.width >= flyoutRect.width) { // anchor is large than flyout, render aligned to anchor left = 0; } else { const centerOffset = flyoutRect.width / 2 - anchorRect.width / 2; if (flyoutLeft - centerOffset < 0) { // centered flyout is off screen to the left, render on the left edge left = -flyoutLeft; } else if (flyoutRight - centerOffset > windowWidth) { // centered flyout is off screen to the right, render on the right edge left = -(flyoutRight - windowWidth); } else { // render centered left = -centerOffset; } } if (flyoutRect.height > windowHeight || (windowHeight < flyoutBottom && anchorRect.top < flyoutRect.height)) { top = -flyoutTop + anchorRect.height; } else if (windowHeight < flyoutBottom) { bottom = anchorRect.height; } } flyout.style.left = typeof left !== 'undefined' ? `${left}px` : ''; flyout.style.bottom = typeof bottom !== 'undefined' ? `${bottom}px` : ''; flyout.style.top = typeof top !== 'undefined' ? `${top}px` : ''; } } }; __decorate([ property({ attribute: 'isOpen', type: Boolean }) ], MgtFlyout.prototype, "isOpen", void 0); MgtFlyout = __decorate([ customElement('mgt-flyout') ], MgtFlyout); export { MgtFlyout }; //# sourceMappingURL=mgt-flyout.js.map