UNPKG

web-portals

Version:

web-portals

358 lines (323 loc) 11.2 kB
'use strict' import { Animate } from '../animate/index' import { Application } from '../Application' import { TransformAnimation } from './animation' import { TransformAnimateEvent, Module } from '../types' type TransformToParam = [ id: string, param?: string, history?: number, touches?: TouchEvent ] class TransformSwitch extends TransformAnimation { private readonly windowSet: string[] = [] private readonly promiseQueue: (Promise<void> | undefined)[] = [] private readonly promiseParamQueue: [...TransformToParam][] = [] constructor (app: Application) { super(app) } public checkSingleLock (): boolean { return this.options.singleLock && this.module.config.level === 0 ? true : false } public createContainer (module: Module) { if (module.status.init) return const container = document.createElement('module-container') container.setAttribute('name', this.id) container.setAttribute('type', ['frameworks', 'system'].indexOf(this.id) !== -1 ? this.id : 'module') module.rigesterElement('container', container) this.resetContainer(module, this.switchover || !this.animation) this.getViewport(module).appendChild(container) } resetContainer (module: Module, situ: boolean = false) { const config = module.config const container = module.elements.container const systemLevel = ['frameworks', 'system'].includes(module.rel) const isDarkModel = this.app.properties.darkTheme container.style.cssText = '' if (!systemLevel) { container.style.cssText = ` position: absolute; z-index: ${(Number(module.level) || 0) + 1}; width: 100%; height: 100%; background: ${config.color || (isDarkModel ? '#000' : '#fff')}; transform: ${situ ? 'translate3d(0, 0, 0)' : 'translate3d(200%, 200%, 0)'}; contain: strict; ` } } public getViewport (module: Module = this.module): HTMLElement | ShadowRoot { return module.rel === 'system' ? this.fixedViewport : module.config.free === false ? (this.relativeViewport.shadowRoot || this.relativeViewport) : (this.absoluteViewport.shadowRoot || this.absoluteViewport) } public checkSwitchover (modulu: Module = this.modulu, module: Module = this.module): boolean { return modulu?.rel === 'module' && module.config.free !== modulu.config.free ? true : false } public async to (...args: TransformToParam): Promise<void> { return this.pushPromise(this.next() || this.promise(...args), args) } private prev (): Promise<void> | undefined { return this.promiseQueue[0] } private next (): Promise<void> | undefined { const prev = this.prev() if (prev) { return new Promise((resolve, reject) => { prev.then(() => { if (this.promiseParamQueue[0]) { this.promise(...this.promiseParamQueue[0]).then(resolve).catch(reject) } }) }) } return } private pushPromise <P extends Promise<void>> (promise: P, param: TransformToParam): P { this.promiseParamQueue.push(param) this.promiseQueue.push(promise) return promise } private shiftPromise () { this.promiseParamQueue.shift() this.promiseQueue.shift() } public limit (id: string) { const limit = Math.max(this?.options?.limit || 3, 2) const index = this.windowSet.indexOf(id) if (this.module.rel !== 'module' || this.module.config.background === true) return if (index !== -1) this.windowSet.splice(index, 1) this.windowSet.push(id) if (this.windowSet.length > limit) { this.app.modules[this.windowSet.splice(0, 1)[0]]?.destroy() } } public destroy (module: Module) { if (module.transient && this.history === -1) { this.app.del(module).then(() => { const index = this.windowSet.indexOf(module.id) this.windowSet.splice(index, 1) }) } else { module.fate().then(() => { module.destroy().then(() => { const index = this.windowSet.indexOf(module.id) this.windowSet.splice(index, 1) }) }) } } public beforehandDependencies (dependencies: string[] = []) { const allPromise: Promise<any>[] = [] for (const dep of dependencies) { allPromise.push(new Promise((resolve, reject) => { this.app.get(dep).then((module) => { module.prerender().then(resolve).catch(resolve) }).catch(reject) })) } return new Promise((resolve, reject) => { Promise.all(allPromise).then(resolve).catch(reject) }) } public checkPushState (): boolean { return this.history !== -1 && this.module.viewType !== 'portal' } private promise (id: string, param: string = location.search, history: number = 1, touches?: TouchEvent): Promise<void> { return new Promise((resolve, reject) => { const od = this.od const ids = od ? [id, od] : [id] const module = this.app.modules[id] const modulu = this.app.modules[od] const moduli = od ? [module, modulu] : [module] if (!module) { return this.app.get(id).then(() => { this.promise(id, param, history).then(() => { this.shiftPromise() resolve() }).catch(() => { reject() }) }) } this.id = id this.ids = ids this.param = param this.module = module this.modulu = modulu this.moduli = moduli this.history = history this.touches = touches this.animation = this.getAnimationGroup() this.switchover = this.checkSwitchover() this.target = this.getViewport() this.createContainer(module) this.start().then(() => { this.hintWillTrans(this.viewport) if (this.checkPushState() === true) { this.pushState(id, module.config.title, param as string) } module.create(!!this.animation).then(() => { document.title = module.config.title this.transform().then((stillness) => { this.od = id this.end(stillness) this.shiftPromise() this.limit(this.id) resolve() }) this.beforehandDependencies(module.config?.prerender).then(() => { this.app.trigger('prerenderComplete') }) }) }) }) } public pos () { let x = 0 let y = 0 let attach: string | Array<number> = 'center' let origin: string | Array<number> = 'center' const width = this.relativeViewport.offsetWidth const height = this.relativeViewport.offsetHeight const event = this.touches ? this.touches['srcEvent'] : {} if (event.changedTouches) { x = event.changedTouches[0].pageX y = event.changedTouches[0].pageY } else { x = event.x y = event.y } if (x && y) { origin = [x, y] if (x < width / 4) { x = 0 } else if (x > width * 3 / 4) { x = width } if (y < height / 4) { y = 0 } else if (y > height * 3 / 4) { y = height } attach = [x, y] } return { x, y, width, height, attach, origin } } get backsetState () { return (this.moduli.length === 1 || this.module.level === this.modulu.level) ? -1 : (this.module.level > this.modulu.level ? 0 : 1) } get viewport (): [HTMLElement, HTMLElement] { return this.switchover ? [ this.module.config.free === false ? this.relativeViewport : this.absoluteViewport, this.modulu?.config.free === false ? this.relativeViewport : this.absoluteViewport ] : [ this.module.elements.container, this.modulu?.elements.container ] } public modulation (callback: (stillness: boolean) => void): TransformAnimateEvent { const viewport = this.viewport const backset = this.backsetState const reverse = backset === 0 ? false : true const pos = this.pos() const animateEvent: TransformAnimateEvent = { x: pos.x, y: pos.y, in: new Animate(viewport[0]), out: new Animate(viewport[1]), view: viewport, width: pos.width, height: pos.height, viewport: [this.relativeViewport, this.absoluteViewport], modules: this.moduli, reverse: reverse, direction: reverse ? -1 : 1, backset: backset, origin: origin, attach: pos.attach, touches: this.touches, callback: callback } return animateEvent } public transform (): Promise<boolean> { return new Promise((resolve) => { if (!this.animation) { this.switchViewport() resolve(true) return } const backset = this.backsetState const animation = this.getAnimationOneSide(backset) const end = (stillness: boolean) => resolve(stillness) if (!animation) return end(true) const prosise = animation(this.modulation(end)) if (prosise instanceof Promise) prosise.then(end) }) } private hintWillTrans (viewport: [HTMLElement, HTMLElement]) { viewport[0].style.willChange = 'transform, opacity' if (viewport[1]) { viewport[1].style.willChange = 'transform, opacity' } } private removeTransHint (viewport: [HTMLElement, HTMLElement]) { viewport[0].style.willChange = 'auto' if (viewport[1]) { viewport[1].style.willChange = 'auto' } } public start (): Promise<void> { return new Promise((resolve, reject) => { this.app.trigger('transformStart', this.moduli) const transformStart = this.module.events.transformStart if (typeof transformStart === 'function') { if (transformStart.call(this.module) === false) return reject() } if (!this.animation || this.switchover) { this.module.elements.container.style.transform = 'translate3d(0, 0, 0)' } resolve() }) } public end (stillness: boolean = false) { const transformEnd = this.module.events.transformEnd this.removeTransHint(this.viewport) if (this.switchover) { this.switchViewport() } if (this.modulu) { if (!this.animation || !stillness) { if (this.modulu.rel === 'module') { this.modulu.elements.container.style.transform = 'translate3d(200%, 200%, 0)' } this.resetContainer(this.module, true) } this.modulu.elements.container.style.transitionDuration = '0ms' if (this.modulu.config.background === true) { this.modulu.hide() } else { this.destroy(this.modulu) } } this.module.show() if (typeof transformEnd === 'function') { transformEnd.call(this.module) } this.app.trigger('transformEnd', this.moduli) } public switchViewport () { this.resetViewport() if (this.module?.config?.free === false) { this.absoluteViewport.style.transform = 'translate3d(200%, 200%, 0)' this.relativeViewport.style.transform = 'translate3d(0, 0, 0)' } else { this.relativeViewport.style.transform = 'translate3d(200%, 200%, 0)' this.absoluteViewport.style.transform = 'translate3d(0, 0, 0)' } } } export { TransformSwitch }