@khmyznikov/pwa-install
Version:
PWA install dialog provide more convenience user experience and fix lack of native dialogs in some browsers.
190 lines (150 loc) • 5.53 kB
text/typescript
import { LitElement, html } from 'lit';
import { property, customElement } from 'lit/decorators.js';
// import TouchDragListener from "./touch-listener";
type IProps = {
name: string,
description: string,
icon: string
}
import template from './template-bottom-sheet';
import Utils from '../../../utils';
export default class PWABottomSheetElement extends LitElement {
props: IProps = {
name: '',
description: '',
icon: ''
};
install = {handleEvent: () => {}};
hideDialog = () => {};
disableClose = false;
private _callInstall = () => {
this.install.handleEvent();
}
private _callHide = () => {
this.hideDialog();
this.setupAppearence();
}
private bindedElement: {
touchElement: HTMLElement,
listener: any }| null = null;
private readonly _saveBodyStyle = document.body.style.overscrollBehaviorY;
private dragMobileSheet = (element: HTMLElement | null | undefined, touchTargetElement: HTMLElement | undefined, infoElement: HTMLElement | undefined) => {
if (!element || !touchTargetElement || !infoElement)
return null;
let dragOffset = 0;
const bounceOffset = 35;
const bottomSize = touchTargetElement.clientHeight + infoElement.clientHeight;
const getYCoord = (e: MouseEvent | TouchEvent): number => {
return (e as MouseEvent).clientY || ((e as TouchEvent).changedTouches && (e as TouchEvent).changedTouches.length? (e as TouchEvent).changedTouches[0].clientY : 0);
}
const dragMouseDown = (e: MouseEvent | TouchEvent) => {
window.addEventListener('mouseup', dragMouseUp);
window.addEventListener('mousemove', dragMouseMove);
window.addEventListener('touchend', dragMouseUp);
window.addEventListener('touchmove', dragMouseMove);
dragOffset = getYCoord(e) - touchTargetElement.getBoundingClientRect().top;
document.body.style.overscrollBehaviorY = 'none';
}
const dragMouseUp = (e: MouseEvent | TouchEvent) => {
document.body.style.overscrollBehaviorY = this._saveBodyStyle;
if (!this.disableClose && getYCoord(e) >= window.innerHeight - 25) {
closeDragElement(e, window.innerHeight + 50, true);
return
}
if (window.innerHeight - getYCoord(e) > element.clientHeight / 2.5) {
closeDragElement(e, window.innerHeight - element.clientHeight);
try {
Utils.eventGallery((this.getRootNode() as ShadowRoot).host);
} catch (e) {}
return
}
else {
closeDragElement(e, window.innerHeight - bottomSize - bounceOffset);
return
}
}
const dragMouseMove = (e: MouseEvent | TouchEvent) => {
const currentY = getYCoord(e);
if (this.disableClose && window.innerHeight - currentY < 70) {
return
}
if (currentY <= (window.innerHeight - element.clientHeight + dragOffset)) {
return
}
element.style.setProperty(
"transition",
`none`
)
element.style.setProperty(
"--translateY",
`translateY(${currentY - dragOffset}px)`
);
}
const closeDragElement = (e: MouseEvent | TouchEvent, toPoint?: number, hideDialog?: boolean) => {
window.removeEventListener('mouseup', dragMouseUp);
window.removeEventListener('mousemove', dragMouseMove);
window.removeEventListener('touchend', dragMouseUp);
window.removeEventListener('touchmove', dragMouseMove);
if (window.innerWidth >= 768)
return
if (!toPoint && getYCoord(e) >= window.innerHeight - bottomSize)
element.style.setProperty(
"--translateY",
`translateY(calc(100vh - ${bottomSize}px))`
);
else
element.style.setProperty(
"--translateY",
`translateY(${(toPoint || getYCoord(e)) + bounceOffset}px)`
);
element.style.setProperty(
"transition",
`transform 500ms cubic-bezier(0.4, 0, 0, 1) 0s`
);
if (hideDialog){
touchTargetElement.removeEventListener('mousedown', dragMouseDown);
touchTargetElement.removeEventListener('touchstart', dragMouseDown);
setTimeout(
this._callHide,
250
);
}
}
touchTargetElement.addEventListener('mousedown', dragMouseDown);
touchTargetElement.addEventListener('touchstart', dragMouseDown, {passive: true});
closeDragElement(new MouseEvent('mouseup'), window.innerHeight - bottomSize - bounceOffset);
return {
touchElement: touchTargetElement,
listener: dragMouseDown
}
}
private setupAppearence = () => {
if (this.bindedElement) {
this.bindedElement.touchElement.removeEventListener('mousedown', this.bindedElement.listener);
this.bindedElement.touchElement.removeEventListener('touchstart', this.bindedElement.listener);
}
this.bindedElement = this.dragMobileSheet(
this.parentElement?.parentElement,
this.parentElement?.getElementsByClassName('touch-header')[0] as HTMLElement,
this.parentElement?.getElementsByClassName('body-header')[0] as HTMLElement);
}
private _init = () => {
this.setupAppearence();
window.addEventListener('resize', this.setupAppearence);
return;
}
firstUpdated () {
this._init();
return;
}
createRenderRoot() {
return this;
}
connectedCallback() {
super.connectedCallback()
}
render() {
return html`${template(this.props.name, this.props.description, this.props.icon, this._callInstall)}`;
}
}