UNPKG

@limetech/lime-elements

Version:
182 lines (181 loc) 5.52 kB
import { Corner, MDCMenuSurface } from '@material/menu-surface'; import { h, } from '@stencil/core'; import { isDescendant } from '../../util/dom'; import { ESCAPE, TAB } from '../../util/keycodes'; /** * @slot - Content to put inside the surface * @private */ export class MenuSurface { constructor() { this.setup = () => { const menuElement = this.host.shadowRoot.querySelector('.mdc-menu-surface'); if (!menuElement) { return; } this.menuSurface = new MDCMenuSurface(menuElement); this.menuSurface.setAnchorCorner(Corner.TOP_START); document.addEventListener('mousedown', this.handleDocumentClick, { capture: true, }); this.host.addEventListener('keydown', this.handleKeyDown); }; this.teardown = () => { var _a; (_a = this.menuSurface) === null || _a === void 0 ? void 0 : _a.destroy(); document.removeEventListener('mousedown', this.handleDocumentClick, { capture: true, }); this.host.removeEventListener('keydown', this.handleKeyDown); }; this.handleDocumentClick = (event) => { const elementPath = event.composedPath ? event.composedPath() : []; if (!this.open) { return; } if (isDescendant(event.target, this.host)) { return; } if (this.allowClicksElement) { const clickedInAllowedElement = elementPath.includes(this.allowClicksElement); if (clickedInAllowedElement) { return; } } this.dismiss.emit(); this.preventClickEventPropagation(); }; this.preventClickEventPropagation = () => { // When the menu surface is open, we want to stop the `click` event from propagating // when clicking outside the surface itself. This is to prevent any dialog that might // be open from closing, etc. However, when dragging a scrollbar no `click` event is emitted, // only mousedown and mouseup. So we listen for `mousedown` and attach a one-time listener // for `click`, so we can capture and "kill" it. document.addEventListener('click', this.stopEvent, { capture: true, once: true, }); // We also capture and "kill" the next `mouseup` event. document.addEventListener('mouseup', this.stopEvent, { capture: true, once: true, }); // If the user dragged the scrollbar, no `click` event happens. So when we get the // `mouseup` event, remove the handler for `click` if it's still there. // Otherwise, we would catch the next click even though the menu is no longer open. document.addEventListener('mouseup', () => { document.removeEventListener('click', this.stopEvent, { capture: true, }); }, { once: true, }); }; this.stopEvent = (event) => { event.stopPropagation(); event.preventDefault(); }; this.handleKeyDown = (event) => { const isEscape = event.key === ESCAPE; const isTab = event.key === TAB; if (this.open && (isEscape || isTab)) { event.stopPropagation(); this.dismiss.emit(); } }; this.open = false; this.allowClicksElement = undefined; } connectedCallback() { this.setup(); } disconnectedCallback() { this.teardown(); } componentDidLoad() { this.setup(); } render() { const classList = { 'mdc-menu': true, 'mdc-menu-surface': true, 'mdc-menu-surface--open': this.open, 'mdc-elevation-transition': true, 'mdc-elevation--z4': true, }; return (h("div", { class: classList, tabindex: "-1" }, h("slot", null))); } static get is() { return "limel-menu-surface"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["menu-surface.scss"] }; } static get styleUrls() { return { "$": ["menu-surface.css"] }; } static get properties() { return { "open": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "True if the menu surface is open, false otherwise" }, "attribute": "open", "reflect": false, "defaultValue": "false" }, "allowClicksElement": { "type": "unknown", "mutable": false, "complexType": { "original": "HTMLElement", "resolved": "HTMLElement", "references": { "HTMLElement": { "location": "global" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Clicks in this element should not be prevented when the menu surface is open" } } }; } static get events() { return [{ "method": "dismiss", "name": "dismiss", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when the menu surface is dismissed and should be closed" }, "complexType": { "original": "void", "resolved": "void", "references": {} } }]; } static get elementRef() { return "host"; } } //# sourceMappingURL=menu-surface.js.map