UNPKG

@suyouwanggang/p-ui

Version:

`p-ui`是一套使用原生`Web Components`规范开发的跨框架UI组件库,基于`lit-elment`库开发。 [github项目地址](https://github.com/suyouwanggang/p-ui)

482 lines (471 loc) 20.6 kB
import { css, customElement, html, LitElement, property } from 'lit-element'; import './p-button'; import './p-tips'; @customElement('p-pop') class Ppop extends LitElement { static get styles() { return css` :host { display:inline-block; position:relative; overflow:visible; --distanceValue: var(--distance,10px); } :host([dir="top"]) ::slotted(p-pop-content){ bottom:100%; left:50%; transform:translate(-50%,calc( -1 * var(--distanceValue) ) ) scale(0); transform-origin: center bottom; } :host([dir="top"]) ::slotted(p-pop-content[open]), :host([dir="top"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="top"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(-50%,calc( -1 * var(--distanceValue) ) ) scale(1); } :host([dir="right"]) ::slotted(p-pop-content){ left:100%; top:50%; transform:translate(calc( 1 * var(--distanceValue) ),-50%) scale(0); transform-origin: left; } :host([dir="right"]) ::slotted(p-pop-content[open]), :host([dir="right"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="right"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( 1 * var(--distanceValue) ),-50%) scale(1); } :host([dir="bottom"]) ::slotted(p-pop-content){ top:100%; left:50%; transform:translate(-50%,calc( 1 * var(--distanceValue) )) scale(0); transform-origin: center top; } :host([dir="bottom"]) ::slotted(p-pop-content[open]), :host([dir="bottom"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="bottom"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(-50%,calc( 1 * var(--distanceValue) )) scale(1); } :host([dir="left"]) ::slotted(p-pop-content){ right:100%; top:50%; transform:translate(calc( -1 * var(--distanceValue) ),-50%) scale(0); transform-origin: right; } :host([dir="left"]) ::slotted(p-pop-content[open]), :host([dir="left"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="left"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( -1 * var(--distanceValue) ),-50%) scale(1); } :host([dir="lefttop"]) ::slotted(p-pop-content){ right:100%; top:0; transform:translate(calc( -1 * var(--distanceValue) )) scale(0); transform-origin: right top; } :host([dir="lefttop"]) ::slotted(p-pop-content[open]), :host([dir="lefttop"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="lefttop"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( -1 * var(--distanceValue) )) scale(1); } :host([dir="leftbottom"]) ::slotted(p-pop-content){ right:100%; bottom:0; transform:translate(calc( -1 * var(--distanceValue) )) scale(0); transform-origin: right bottom; } :host([dir="leftbottom"]) ::slotted(p-pop-content[open]), :host([dir="leftbottom"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="leftbottom"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( -1 * var(--distanceValue) )) scale(1); } :host([dir="topleft"]) ::slotted(p-pop-content){ bottom:100%; left:0; transform:translate(0,calc( -1 * var(--distanceValue) )) scale(0); transform-origin: left bottom; } :host([dir="topleft"]) ::slotted(p-pop-content[open]), :host([dir="topleft"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="topleft"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(0,calc( -1 * var(--distanceValue) )) scale(1); } :host([dir="topright"]) ::slotted(p-pop-content){ bottom:100%; right:0; transform:translate(0,calc( -1 * var(--distanceValue) )) scale(0); transform-origin: right bottom; } :host([dir="topright"]) ::slotted(p-pop-content[open]), :host([dir="topright"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="topright"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(0,calc( -1 * var(--distanceValue) )) scale(1); } :host([dir="righttop"]) ::slotted(p-pop-content){ left:100%; top:0; transform:translate(calc( 1 * var(--distanceValue) )) scale(0); transform-origin: left top; } :host([dir="righttop"]) ::slotted(p-pop-content[open]), :host([dir="righttop"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="righttop"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( 1 * var(--distanceValue) )) scale(1); } :host([dir="rightbottom"]) ::slotted(p-pop-content){ left:100%; bottom:0; transform:translate(10px) scale(0); transform-origin: left bottom; } :host([dir="rightbottom"]) ::slotted(p-pop-content[open]), :host([dir="rightbottom"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="rightbottom"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(calc( 1 * var(--distanceValue) )) scale(1); } :host([dir="bottomleft"]) ::slotted(p-pop-content), :host(:not([dir])) ::slotted(p-pop-content){ left:0; top:100%; transform:translate(0,calc( 1 * var(--distanceValue) )) scale(0); transform-origin: left top; } :host(:not([dir])) ::slotted(p-pop-content[open]), :host(:not([dir])[trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host(:not([dir])[trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content), :host([dir="bottomleft"]) ::slotted(p-pop-content[open]), :host([dir="bottomleft"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="bottomleft"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(0,calc( 1 * var(--distanceValue) )) scale(1); } :host([dir="bottomright"]) ::slotted(p-pop-content){ right:0; top:100%; transform:translate(0,calc( 1 * var(--distanceValue) )) scale(0); transform-origin: right top; } :host([dir="bottomright"]) ::slotted(p-pop-content[open]), :host([dir="bottomright"][trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([dir="bottomright"][trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ transform:translate(0,calc( 1 * var(--distanceValue) )) scale(1); } :host([trigger="contextmenu"]) ::slotted(p-pop-content){ right:auto; bottom:auto; left:var(--x,0); top:var(--y,100%); transform-origin: left top; transform:translate(5px,5px) scale(0); transition: .15s; } :host([trigger="contextmenu"]) ::slotted(p-pop-content[open]){ transform:translate(5px,5px) scale(1); } :host ::slotted(p-pop-content[open]), :host([trigger="hover"]:not([disabled]):hover) ::slotted(p-pop-content), :host([trigger="focus"]:not([disabled]):focus-within) ::slotted(p-pop-content){ opacity:1; visibility:visible; } :host([dir='center']) ::slotted(p-pop-content){ position:fixed; right:auto; bottom:auto; top:50%; left:50%; transition: .15s; transform:translate(-50%, -50%) scale(0); } :host([dir='center']) ::slotted(p-pop-content[open]){ transform:translate(-50%, -50%) scale(1); } `; } @property({ type: Boolean, reflect: true }) disabled: boolean = false; @property({ type: String, reflect: true }) type: string = undefined; @property({ type: String, reflect: true }) tipContent: string = undefined; @property({ type: String, reflect: true }) tipTitle: string = undefined; @property({ type: String, reflect: true }) okText: string = '确定'; @property({ type: String, reflect: true }) cancelText: string = '取消'; @property({ type: String, reflect: true }) trigger: string = 'click'; @property({ type: Boolean, reflect: true }) accomplish: boolean = false; render() { return html`<slot></slot>`; } _show(ev: MouseEvent) { const popContent = this.popContent; const event = ev as any; if (!this.disabled) { if (this.trigger === 'contextmenu') { const { left, top } = this.getBoundingClientRect(); popContent!.style.setProperty('--x', ev.clientX - left + 'px'); popContent!.style.setProperty('--y', ev.clientY - top + 'px'); popContent!.open = true; } else { const path = event.path || (event.composedPath && ev.composedPath()); if (!path.includes(popContent)) { if (this.accomplish) { popContent!.open = true; } else { popContent!.open = !popContent.open; } } } } } get popContent(): PPopContent { let popContent: PPopContent = this.querySelector('p-pop-content'); if (popContent === null) { popContent = new PPopContent(); popContent.type = this.type; if (this.tipContent) { const div = document.createElement('div'); div.textContent = this.tipContent; popContent.appendChild(div); } (popContent as any).isAutoCreate = true; popContent.tipTitle = this.tipTitle; popContent.okText = this.okText; popContent.cancelText = this.cancelText; this.appendChild(popContent); } return popContent; } private _bindShowHanlder = (ev: MouseEvent) => { if (this.trigger !== 'contextmenu') { this._show(ev); } else { const event = ev as any; ev.preventDefault(); const path = event.path || (ev.composedPath && ev.composedPath()); if (!path.includes(this.popContent)) { this._show(ev); } } } private _bindHiddenHanlder = (ev: MouseEvent) => { const popContent = this.popContent; popContent.open = false; } private _bindDocumentAutoHidde = (ev: any) => { const popContent = this.popContent; const path = ev.path || (ev.composedPath && ev.composedPath()); if (popContent && !path.includes(popContent) && !popContent.loading && !path.includes(this.children[0]) || (this.trigger === 'contextmenu') && !path.includes(popContent) && ev.which === '1') { popContent.open = false; } } private _bindTriggerEvent(oldTirgger?: string) { if (oldTirgger !== undefined) { if (oldTirgger === 'hover') { this.removeEventListener('mouseenter', this._bindShowHanlder); this.removeEventListener('mouseleave', this._bindHiddenHanlder); } else { this.removeEventListener(oldTirgger, this._bindShowHanlder); } } if (this.trigger !== undefined && this.trigger !== 'hover') { this.addEventListener(this.trigger, this._bindShowHanlder); } else if ( 'hover' === this.trigger ) { this.addEventListener('mouseenter', this._bindShowHanlder); this.addEventListener('mouseleave', this._bindHiddenHanlder); } } private firstUpdateFlag = false; firstUpdated() { this._bindTriggerEvent(); document.addEventListener('mousedown', this._bindDocumentAutoHidde); this.firstUpdateFlag = true; } update(changedProperties: Map<string | number | symbol, unknown>) { super.update(changedProperties); if (this.firstUpdateFlag) { const trigger = changedProperties.get('trigger') as string; if (trigger !== undefined) { this._bindTriggerEvent(trigger); } const pop = this.popContent as any; if (pop && pop.isAutoCreate) { if (changedProperties.has('tipContent')) { let firstChild = pop.firstChild; if (!firstChild) { firstChild = document.createElement('div'); pop.appendChild(firstChild); } firstChild.textContent = this.tipContent; } if (changedProperties.has('tipTitle')) { pop.tipTitle = this.tipTitle; } } } } disconnectedCallback() { document.removeEventListener('mousedown', this._bindDocumentAutoHidde); } } @customElement('p-pop-content') class PPopContent extends LitElement { static get styles() { return css` :host{ position:absolute; display:flex; flex-direction:column; box-shadow: 2px 2px 15px rgba(0,0,0,0.15); box-sizing: border-box; transform:scale(0); opacity:0.5; border-radius: 3px; z-index:10; transition:.3s cubic-bezier(.645, .045, .355, 1); transform-origin:inherit; background:#fff; visibility:hidden; } div[part=popTitle] { display:flex; } div[part=popTitle] >div[part=popTitleInner]{ flex:1; margin-left:0.5em; line-height: 30px; font-size: 1.2em; color: #4c5161; user-select: none; cursor: default; } div[part=popBody]{ width:max-content; } p-button[part="popClose"] { margin-right:5px; border:0; } :host([thinBar]) div[part=popBody] { flex: 1; overflow:auto; width:max-content; scrollbar-color: #DBDBDB #FFF; scrollbar-width: thin; } :host([thinBar]) div[part=popBody] :hover{ scrollbar-color: rgb(189,189,189) #FFF ; } :host([thinBar]) div[part=popBody]::-webkit-scrollbar { width:7px; height: 7px; } /* 滚动槽 */ :host([thinBar]) div[part=popBody]::-webkit-scrollbar-track { background-color:#FFF; } /* 滚动条滑块 */ :host([thinBar]) div[part=popBody]::-webkit-scrollbar-thumb { border-radius:3px; background:#DBDBDB; } :host([thinBar]) div[part=popBody]::-webkit-scrollbar-thumb:hover { background-color: #BDBDBD; } p-icon[part=popIcon]{ flex:auto; font-size:1.2em; color:var(--waringColor,#faad14); margin: 1em 0px 0px 0.8em; align-self:flex-start; } div[part=popFooter]{ margin-top:8px; text-align: right; white-space: nowrap; } #btn-cancel,#btn-submit { margin-left: 0.6em; margin-right:0.6em; margin-bottom:8px; cursor:pointer; } #btn-submit { margin-right:1.5em; } :host([type="confirm"]){ min-width:250px; } :host([type="confirm"]) div[part=popBody] { margin-left:0.8em; margin-right:0.8em; }` ; } @property({ type: Boolean, reflect: true }) open: boolean = false; @property({ type: Boolean, reflect: true }) loading: boolean = false; @property({ type: Boolean, reflect: true }) thinBar: boolean = false; @property({ type: Boolean, reflect: true }) hiddenClose: boolean = false; @property({ type: String, reflect: true }) type: string = undefined; @property({ type: String, reflect: true }) tipTitle: string = undefined; @property({ type: String, reflect: true }) okText: string = undefined; @property({ type: String, reflect: true }) cancelText: string = undefined; render() { return html` <div part="popTitle" id="title"> <div class='title' part="popTitleInner">${this.tipTitle} <slot name="title"></slot></div> ${this.hiddenClose ? '' : html`<p-button type="flat" shape='circle' id="btn-close" part="popClose" icon="close" @click='${this._toCloseEvent}'></p-button>`} </div> <div part="popBody" > <slot></slot> </div> <div part="popFooter"> <slot name="footer"> ${this.type === 'confirm' ? html`<p-button id="btn-cancel" @click="${this._cancleClick}">${this.cancelText === undefined ? '取消' : this.cancelText}</p-button> <p-button id="btn-submit" type="primary" @click="${this._submitClick}">${this.okText === undefined ? '确定' : this.okText}</p-button>` : ''} </slot> </div> `; } private _toCloseEvent(ev: Event) { this.open = false; this.dispatchEvent(new CustomEvent('close')); // await this.updateComplete; } private _cancleClick(ev: Event) { this.open = false; this.dispatchEvent(new CustomEvent('cancel')); // await this.updateComplete; } private _submitClick(ev: Event) { this.dispatchEvent(new CustomEvent('submit')); if (!this.loading) { this.open = false; } } firstUpdated() { const btnClose = this.shadowRoot.getElementById('btn-close'); const btnSubmit = this.shadowRoot.getElementById('btn-submit'); this.addEventListener('transitionend', (ev) => { //console.log('transitionend==='); if (ev.propertyName === 'transform' && this.open) { // console.log('transform==='); if (this.type === 'confirm') { if (btnSubmit) { btnSubmit.focus(); } } else { if (btnClose) { btnClose.focus(); } } this.dispatchEvent(new CustomEvent('open')); } if (ev.propertyName === 'transform' && !this.open) { this.dispatchEvent(new CustomEvent('close')); } }); this.addEventListener('click', (ev: Event) => { const el = ev.target as HTMLElement; if (el && el.closest('[autoclose]')) { this.open = false; } }); } } export { Ppop, PPopContent };