tp-popup/tp-popup.js

200 lines
4.6 KiB
JavaScript
Raw Normal View History

2022-03-16 23:45:06 +01:00
/**
@license
Copyright (c) 2022 trading_peter
This program is available under Apache License Version 2.0
*/
import { LitElement, html, css } from 'lit';
import { DomQuery } from '@tp/helpers/dom-query.js';
import { Position } from '@tp/helpers/position.js';
import { EventHelpers } from '@tp/helpers/event-helpers.js';
import { closest } from '@tp/helpers/closest.js';
class TpPopup extends EventHelpers(Position(DomQuery(LitElement))) {
static get styles() {
return [
css`
:host {
display: inline-block;
position: relative;
outline: 0;
}
.toggle {
cursor: pointer;
}
#content {
pointer-events: none;
position: fixed;
z-index: 1;
margin-top: 5px;
transition: opacity 180ms;
opacity: 0;
border-radius: 2px;
box-shadow: var(--tp-popup-shadow, none);
}
#content[open] {
pointer-events: all;
opacity: 1;
background: var(--tp-popup-background, #F5F5F5);
}
#content .content-wrap {
padding: var(--tp-popup-content-padding, 10px);
}
@media all and (min-width: 0) and (max-width: 480px) {
:host(:not([not-responsive])) #content {
bottom: 0 !important;
top: auto !important;
right: 0 !important;
left: 0 !important;
margin-top: 0;
transition: transform 0.3s, opacity 0.3s;
transform: translateY(100%);
max-height: 80%;
overflow: hidden;
overflow-y: auto;
box-shadow: var(--tp-popup-shadow-responsive, none);
}
:host(:not([not-responsive])) #content[open] {
transform: translateY(0%);
}
}
`
];
}
render() {
const { isOpen } = this;
return html`
2022-03-17 11:47:44 +01:00
<div class="toggle" part="toggle">
<slot id="toggleContent" name="toggle"></slot>
</div>
<div id="content" part="content" ?open=${isOpen}>
<div class="content-wrap" part="content-wrap">
<slot id="contentSlot" name="content"></slot>
2022-03-16 23:45:06 +01:00
</div>
</div>
`;
}
static get properties() {
return {
isOpen: { type: Boolean, reflect: true },
alwaysToggle: { type: Boolean, reflect: true },
halign: { type: String },
valign: { type: String },
scrollTarget: { type: Object }
};
}
constructor() {
super();
this.isOpen = false;
this.alwaysToggle = false;
this.halign = 'middle';
this.valign = 'bottom';
this.scrollTarget = document;
this.fit = this.fit.bind(this);
this._onDocClickHandler = () => this.close();
}
get toggleEl() {
if (!this._toggleEl) {
this._toggleEl = this.querySelector('[slot="toggle"]');
}
return this._toggleEl;
}
firstUpdated() {
super.firstUpdated();
this.listen(this, 'click', '_onClick');
}
disconnectedCallback() {
super.disconnectedCallback();
this._cleanupEvents();
}
close() {
this.isOpen = false;
this._cleanupEvents();
}
open() {
this.updateComplete.then(() => {
this.fit();
});
this.isOpen = true;
this._registerEvents();
const autofocusEl = this.querySelector('[autofocus]');
if (autofocusEl) {
autofocusEl.focus();
}
}
toggle() {
if (!this.isOpen) {
this.open();
} else {
this.isOpen = false;
this._cleanupEvents();
}
}
/**
* Set correct position on the popup to fit into the window.
*/
fit() {
this._posFixed(this.shadowRoot.querySelector('.toggle'), this.$.content, {
valign: this.valign || 'bottom',
halign: this.halign || 'middle',
spacing: 5
});
}
_docClick(e) {
const isThisPopover = e.composedPath().indexOf(this) > -1;
if (!isThisPopover && this.isOpen) {
this.close();
}
}
_onClick(e) {
const toggle = closest(e.composedPath()[0], '[slot="toggle"]', true) === this.toggleEl;
const closePopover = closest(e.composedPath()[0], '[close-popover]', true);
if (toggle && this.toggleEl.hasAttribute('disabled')) return;
if (toggle || this.alwaysToggle) {
this.toggle();
}
if (closePopover) {
this.close();
}
}
_registerEvents() {
this._cleanupEvents();
this.listen(document, 'mousedown', '_docClick');
this.scrollTarget.addEventListener('scroll', this.fit, { passive: true });
}
_cleanupEvents() {
this.unlisten(document, 'mousedown', '_docClick');
this.scrollTarget.removeEventListener('scroll', this.fit, { passive: true });
}
}
window.customElements.define('tp-popup', TpPopup);