UNPKG

opal-components

Version:

[Rionite](https://github.com/Riim/Rionite) component set.

181 lines (146 loc) 3.67 kB
import { Cell, IEvent } from 'cellx'; import { observable } from 'cellx-decorators'; import { Component, IDisposableListening } from 'rionite'; import './index.css'; import template from './template.nelm'; let openedDropdowns: Array<OpalDropdown> = []; @Component.Config({ elementIs: 'OpalDropdown', params: { autoHeight: true, autoClosing: false, opened: false }, template }) export class OpalDropdown extends Component { @observable isContentRendered = false; _documentClickListening: IDisposableListening | undefined; ready() { if (this.params.opened) { this._open(); } } elementAttached() { this.listenTo(this, 'param-opened-change', this._onParamOpenedChange); } _onParamOpenedChange(evt: IEvent) { if (evt.data.value) { this._open(); } else { this._close(); } } renderContent() { this.isContentRendered = true; Cell.forceRelease(); } open(): boolean { if (this.params.opened) { return false; } this.params.opened = true; Cell.forceRelease(); return true; } close(): boolean { if (!this.params.opened) { return false; } this.params.opened = false; Cell.forceRelease(); return true; } toggle(value?: boolean): boolean { if (value !== undefined) { return value ? this.open() : !this.close(); } return this.open() || !this.close(); } _open() { openedDropdowns.push(this); if (this.isContentRendered) { this._open$(); } else { this.isContentRendered = true; Cell.forceRelease(); this._open$(); } } _open$() { let el = this.element; let elStyle = el.style; elStyle.top = '100%'; elStyle.right = 'auto'; elStyle.bottom = 'auto'; elStyle.left = '0'; elStyle.maxHeight = 'none'; let docElClientWidth = document.documentElement.clientWidth; let containerClientRect = el.offsetParent.getBoundingClientRect(); let elClientRect = el.getBoundingClientRect(); if (elClientRect.right > docElClientWidth) { if (containerClientRect.right - el.offsetWidth >= 0) { elStyle.right = '0'; elStyle.left = 'auto'; } else { elStyle.left = Math.max(-containerClientRect.left, docElClientWidth - elClientRect.right) + 'px'; } } let margin = elClientRect.top - containerClientRect.bottom; let excess = elClientRect.bottom + margin - document.documentElement.clientHeight; if (excess > 0) { let diff = containerClientRect.top - (document.documentElement.clientHeight - containerClientRect.bottom); if (this.params.autoHeight) { if (diff > 0) { elStyle.top = 'auto'; elStyle.bottom = '100%'; excess -= diff; if (excess > 0) { elStyle.maxHeight = el.offsetHeight - excess + 'px'; } } else { elStyle.maxHeight = el.offsetHeight - excess + 'px'; } } else if (diff > 0 && excess - diff <= document.body.scrollTop) { elStyle.top = 'auto'; elStyle.bottom = '100%'; } } if (this.params.autoClosing) { setTimeout(() => { if (this.params.opened) { this._documentClickListening = this.listenTo( document, 'click', this._onDocumentClick ); } }, 1); } } _close() { openedDropdowns.splice(openedDropdowns.indexOf(this), 1); if (this._documentClickListening) { this._documentClickListening.stop(); } } _onDocumentClick(evt: Event) { let docEl = document.documentElement; let componentEl = this.element; for (let el: HTMLElement | null = evt.target as HTMLElement; el != componentEl; ) { if (el == docEl || el.tagName == 'A') { this.close(); this.emit('close'); break; } el = el.parentNode as HTMLElement; if (!el) { break; } } } }