diff --git a/README.md b/README.md index 1ab27b7..a203ec2 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# tp-element +# tp-popup diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..eea5a66 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "@tp/tp-popup", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@tp/helpers": { + "version": "1.0.1", + "resolved": "https://verdaccio.codeblob.work/@tp%2fhelpers/-/helpers-1.0.1.tgz", + "integrity": "sha512-f6pDPw4QpjWnmVkYgOHjMXQXtGB4vbA45eZV9DjCF9OoCXsa+Pz32H2rLQRKbdpsfFllywOBI+GMGPYDJyrG/Q==" + } + } +} diff --git a/package.json b/package.json index c39fdff..afd61f5 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,19 @@ { - "name": "@tp/tp-element", - "version": "0.0.1", + "name": "@tp/tp-popup", + "version": "1.0.0", "description": "", - "main": "tp-element.js", + "main": "tp-popup.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", - "url": "https://gitea.codeblob.work/tp-elements/tp-element.git" + "url": "https://gitea.codeblob.work/tp-elements/tp-popup.git" }, "author": "trading_peter", "license": "Apache-2.0", "dependencies": { + "@tp/helpers": "^1.0.1", "lit": "^2.2.0" } } diff --git a/tp-element.js b/tp-element.js deleted file mode 100644 index 6a92a2f..0000000 --- a/tp-element.js +++ /dev/null @@ -1,35 +0,0 @@ -/** -@license -Copyright (c) 2022 trading_peter -This program is available under Apache License Version 2.0 -*/ - -import { LitElement, html, css } from 'lit'; - -class TpElement extends LitElement { - static get styles() { - return [ - css` - :host { - display: block; - } - ` - ]; - } - - render() { - const { } = this; - - return html` - - `; - } - - static get properties() { - return { }; - } - - -} - -window.customElements.define('tp-element', TpElement); diff --git a/tp-popup.js b/tp-popup.js new file mode 100644 index 0000000..f65b254 --- /dev/null +++ b/tp-popup.js @@ -0,0 +1,201 @@ +/** +@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` +
+
+ +
+
+
+ +
+
+
+ `; + } + + 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);