Skip to content

Commit

Permalink
refactor(ui/layer): better layer implements and independence (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
qwqcode authored Dec 16, 2023
1 parent 8b6bab8 commit 87102cd
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 229 deletions.
21 changes: 7 additions & 14 deletions ui/packages/artalk/src/components/checker/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type Api from '@/api'
import Dialog from '@/components/dialog'
import Layer from '@/layer'
import $t from '@/i18n'
import type { ContextApi } from '~/types'
import type { Layer } from '@/layer'
import * as Utils from '@/lib/utils'
import CaptchaChecker from './captcha'
import AdminChecker from './admin'
Expand All @@ -18,6 +19,7 @@ export interface CheckerPayload {
}

export interface CheckerLauncherOptions {
getCtx: () => ContextApi
getApi: () => Api
getIframeURLBase: () => string
onReload: () => void
Expand All @@ -27,8 +29,6 @@ export interface CheckerLauncherOptions {
* Checker 发射台
*/
export default class CheckerLauncher {
public launched: Checker[] = []

constructor(private opts: CheckerLauncherOptions) { }

public checkCaptcha(payload: CheckerCaptchaPayload) {
Expand All @@ -43,12 +43,8 @@ export default class CheckerLauncher {
}

public fire(checker: Checker, payload: CheckerPayload, postFire?: (c: CheckerCtx) => void) {
if (this.launched.includes(checker)) return // 阻止同时 fire 相同的 checker
this.launched.push(checker)

// 创建层
const layer = new Layer(`checker-${new Date().getTime()}`)
layer.setMaskClickHide(false)
// 显示层
const layer = this.opts.getCtx().get('layerManager').create(`checker-${new Date().getTime()}`)
layer.show()

// 构建 Checker 的上下文
Expand All @@ -58,8 +54,7 @@ export default class CheckerLauncher {
set: (key, val) => { checkerStore[key] = val },
get: (key) => (checkerStore[key]),
getOpts: () => (this.opts),
getApi: () => (this.opts.getApi()),
getLayer: () => layer,
getApi: () => this.opts.getApi(),
hideInteractInput: () => {
hideInteractInput = true
},
Expand Down Expand Up @@ -167,8 +162,7 @@ export default class CheckerLauncher {

// 关闭 checker 对话框
private close(checker: Checker, layer: Layer) {
layer.disposeNow()
this.launched = this.launched.filter(c => c !== checker)
layer.destroy()
}
}

Expand All @@ -192,7 +186,6 @@ export interface CheckerCtx {
set<K extends keyof CheckerStore>(key: K, val: CheckerStore[K]): void
getOpts(): CheckerLauncherOptions
getApi(): Api
getLayer(): Layer
hideInteractInput(): void
triggerSuccess(): void
cancel(): void
Expand Down
8 changes: 4 additions & 4 deletions ui/packages/artalk/src/layer/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Layer, { getLayerWrap } from './layer'

export default Layer
export { getLayerWrap }
export * from './layer'
export * from './wrap'
export * from './scrollbar-helper'
export * from './layer-manager'
32 changes: 32 additions & 0 deletions ui/packages/artalk/src/layer/layer-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { ContextApi } from '~/types'
import { getScrollbarHelper } from './scrollbar-helper'
import { LayerWrap } from './wrap'
import { Layer } from './layer'

export class LayerManager {
private wrap: LayerWrap
private ctx: ContextApi

constructor(ctx: ContextApi) {
this.ctx = ctx

this.wrap = new LayerWrap()
document.body.appendChild(this.wrap.getWrap())

ctx.on('destroy', () => {
this.wrap.getWrap().remove()
})

// 记录页面原始 CSS 属性
getScrollbarHelper().init()
}

getEl() {
return this.wrap.getWrap()
}

create(name: string, el?: HTMLElement | undefined) {
const layer = new Layer(this.wrap, name, el)
return layer
}
}
158 changes: 18 additions & 140 deletions ui/packages/artalk/src/layer/layer.ts
Original file line number Diff line number Diff line change
@@ -1,161 +1,39 @@
import * as Utils from '../lib/utils'
import * as Ui from '../lib/ui'
import type { LayerWrap } from './wrap'

export default class Layer {
export class Layer {
private $el: HTMLElement
private wrap: LayerWrap
private onAfterHide?: () => void

private name: string
private $wrap: HTMLElement
private $mask: HTMLElement

private maskClickHideEnable: boolean = true

public static BodyOrgOverflow: string
public static BodyOrgPaddingRight: string

public afterHide?: Function

constructor(name: string, el?: HTMLElement) {
this.name = name
const { $wrap, $mask } = getLayerWrap()
this.$wrap = $wrap
this.$mask = $mask

this.$el = this.$wrap.querySelector(`[data-layer-name="${name}"].atk-layer-item`)!
if (this.$el === null) {
// 若传递 layer 元素为空
if (!el) {
this.$el = Utils.createElement()
this.$el.classList.add('atk-layer-item')
} else {
this.$el = el
}
}
this.$el.setAttribute('data-layer-name', name)
this.$el.style.display = 'none'

// 添加到 layers wrap 中
this.$wrap.append(this.$el)
}

getName() {
return this.name
constructor(wrap: LayerWrap, name: string, el?: HTMLElement) {
this.wrap = wrap
this.$el = this.wrap.createItem(name, el)
}

getWrapEl() {
return this.$wrap
setOnAfterHide(func: () => void) {
this.onAfterHide = func
}

getEl() {
return this.$el
}

show() {
this.fireAllActionTimer()

this.$wrap.style.display = 'block'
this.$mask.style.display = 'block'
this.$mask.classList.add('atk-fade-in')
this.$el.style.display = ''

this.$mask.onclick = () => {
if (this.maskClickHideEnable) this.hide()
}

// body style 禁止滚动 + 防抖
this.pageBodyScrollBarHide()
this.wrap.show()
}

hide() {
if (this.afterHide) this.afterHide()
this.$wrap.classList.add('atk-fade-out')
this.$el.style.display = 'none'

// body style 禁止滚动解除
this.pageBodyScrollBarShow()

this.newActionTimer(() => {
this.$wrap.style.display = 'none'
this.checkCleanLayer()
}, 450)
this.newActionTimer(() => {
this.$wrap.style.display = 'none'
this.$wrap.classList.remove('atk-fade-out')
}, 200)
}

setMaskClickHide(enable: boolean) {
this.maskClickHideEnable = enable
}

// 页面滚动条隐藏
pageBodyScrollBarHide() {
document.body.style.overflow = 'hidden'

const bpr = parseInt(window.getComputedStyle(document.body, null).getPropertyValue('padding-right'), 10)
document.body.style.paddingRight = `${Ui.getScrollBarWidth() + bpr || 0}px`
}

// 页面滚动条显示
pageBodyScrollBarShow() {
document.body.style.overflow = Layer.BodyOrgOverflow
document.body.style.paddingRight = Layer.BodyOrgPaddingRight
}

// Timers
private static actionTimers: {act: Function, tid: number}[] = []

private newActionTimer(func: Function, delay: number) {
const act = () => {
func() // 执行
Layer.actionTimers = Layer.actionTimers.filter(o => o.act !== act) // 删除
}

const tid = window.setTimeout(() => act(), delay)

Layer.actionTimers.push({ act, tid })
}

private fireAllActionTimer() {
Layer.actionTimers.forEach(item => {
clearTimeout(item.tid)
item.act() // 立即执行
this.wrap.hide(() => {
this.$el.style.display = 'none'
this.onAfterHide && this.onAfterHide()
})
}

/** 销毁 - 无动画 */
disposeNow() {
this.$el.remove()
this.pageBodyScrollBarShow()
// this.$el dispose
this.checkCleanLayer()
}

/** 销毁 */
dispose() {
this.hide()
this.$el.remove()
// this.$el dispose
this.checkCleanLayer()
}

checkCleanLayer() {
if (this.getWrapEl().querySelectorAll('.atk-layer-item').length === 0) {
this.$wrap.style.display = 'none'
}
}
}

export function getLayerWrap(): { $wrap: HTMLElement, $mask: HTMLElement } {
let $wrap = document.querySelector<HTMLElement>(`.atk-layer-wrap`)
if (!$wrap) {
$wrap = Utils.createElement(
`<div class="atk-layer-wrap" style="display: none;"><div class="atk-layer-mask"></div></div>`
)
document.body.appendChild($wrap)
destroy() {
this.wrap.hide(() => {
this.$el.remove()
this.onAfterHide && this.onAfterHide()
})
}

const $mask = $wrap.querySelector<HTMLElement>('.atk-layer-mask')!

return { $wrap, $mask }
}
24 changes: 24 additions & 0 deletions ui/packages/artalk/src/layer/scrollbar-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as Ui from '@/lib/ui'

let bodyOrgOverflow: string
let bodyOrgPaddingRight: string

export function getScrollbarHelper() {
return {
init() {
bodyOrgOverflow = document.body.style.overflow
bodyOrgPaddingRight = document.body.style.paddingRight
},

unlock() {
document.body.style.overflow = bodyOrgOverflow
document.body.style.paddingRight = bodyOrgPaddingRight
},

lock() {
document.body.style.overflow = 'hidden'
const barPaddingRight = parseInt(window.getComputedStyle(document.body, null).getPropertyValue('padding-right'), 10)
document.body.style.paddingRight = `${Ui.getScrollBarWidth() + barPaddingRight || 0}px`
}
}
}
Loading

0 comments on commit 87102cd

Please sign in to comment.