Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ui/height-limit): modify height limit func to standalone module #435

Merged
merged 1 commit into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
'@typescript-eslint/lines-between-class-members': 0,
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-use-before-define': 0,
'@typescript-eslint/naming-convention': 0,
},
settings: {
'import/resolver': {
Expand Down
76 changes: 76 additions & 0 deletions ui/packages/artalk/src/comment/height-limit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as Utils from '../lib/utils'
import $t from '../i18n'

export interface IHeightLimitConf {
/** Post expand btn click */
postExpandBtnClick?: (e: MouseEvent) => void
}

export interface IHeightLimitRule {
/** Target element need to check */
el: HTMLElement|null|undefined

/** Max height (unit: px) */
max: number

/** Whether or not the element contains `<img />` */
imgContains?: boolean
}

export type THeightLimitRuleSet = IHeightLimitRule[]

/** Check all elements below the max height limit */
export function check(conf: IHeightLimitConf, rules: THeightLimitRuleSet) {
rules.forEach(rule => {
const _check = () => {
// 是否超过高度
if (!rule.el) return
if (Utils.getHeight(rule.el) > rule.max)
applyHeightLimit({
el: rule.el, maxHeight: rule.max,
postBtnClick: conf.postExpandBtnClick
})
}

_check() // check now
if (rule.imgContains && rule.el) // check again if img contains
Utils.onImagesLoaded(rule.el, () => _check())
})
}

/** Apply height limit on an element and add expand btn */
export function applyHeightLimit(obj: {
el: HTMLElement,
maxHeight: number,
postBtnClick?: (e: MouseEvent) => void
}) {
if (!obj.el) return
if (obj.el.classList.contains('atk-height-limit')) return

obj.el.classList.add('atk-height-limit')
obj.el.style.height = `${obj.maxHeight}px`
obj.el.style.overflow = 'hidden'

/* Expand button */
const $expandBtn = Utils.createElement(`<div class="atk-height-limit-btn">${$t('readMore')}</span>`)
$expandBtn.onclick = (e) => {
e.stopPropagation()
disposeHeightLimit(obj.el)

if (obj.postBtnClick) obj.postBtnClick(e)
}
obj.el.append($expandBtn)
}

/** Dispose height limit on an element and remove expand btn */
export function disposeHeightLimit($el: HTMLElement) {
if (!$el) return
if (!$el.classList.contains('atk-height-limit')) return

$el.classList.remove('atk-height-limit')
Array.from($el.children).forEach((e) => {
if (e.classList.contains('atk-height-limit-btn')) e.remove()
})
$el.style.height = ''
$el.style.overflow = ''
}
91 changes: 15 additions & 76 deletions ui/packages/artalk/src/comment/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import CommentHTML from './comment.html?raw'
import Comment from './comment'
import RenderCtx from './render-ctx'
import loadRenders from './renders'
import * as HeightLimit from './height-limit'

export default class CommentRender extends RenderCtx {
public constructor(comment: Comment) {
Expand Down Expand Up @@ -33,90 +34,28 @@ export default class CommentRender extends RenderCtx {

/** 内容限高检测 */
public checkHeightLimit() {
this.checkHeightLimitArea('content') // 评论内容限高
this.checkHeightLimitArea('children') // 子评论部分限高(嵌套模式)
}

/** 目标内容限高检测 */
public checkHeightLimitArea(area: 'children'|'content') {
// 参数准备
const childrenMaxH = this.ctx.conf.heightLimit.children
const contentMaxH = this.ctx.conf.heightLimit.content
const childrenMaxH = this.ctx.conf.heightLimit.children

if (area === 'children' && !childrenMaxH) return
if (area === 'content' && !contentMaxH) return

// 限高
let maxHeight: number
if (area === 'children') maxHeight = childrenMaxH!
if (area === 'content') maxHeight = contentMaxH!

// 检测指定元素
const checkEl = ($el?: HTMLElement|null) => {
if (!$el) return

// 是否超过高度
if (Utils.getHeight($el) > maxHeight) {
this.heightLimitAdd($el, maxHeight)
}
}

// 执行限高检测
if (area === 'children') {
checkEl(this.$childrenWrap)
} else if (area === 'content') {
checkEl(this.$content)
checkEl(this.$replyTo)

// 若有图片 · 图片加载完后再检测一次
Utils.onImagesLoaded(this.$content, () => {
checkEl(this.$content)
})
if (this.$replyTo) {
Utils.onImagesLoaded(this.$replyTo, () => {
checkEl(this.$replyTo)
})
HeightLimit.check({
postExpandBtnClick: () => {
// 子评论数仅有 1,直接取消限高
const children = this.comment.getChildren()
if (children.length === 1) HeightLimit.disposeHeightLimit(children[0].getRender().$content)
}
}
}

/** 移除限高 */
private heightLimitRemove($el: HTMLElement) {
if (!$el) return
if (!$el.classList.contains('atk-height-limit')) return

$el.classList.remove('atk-height-limit')
Array.from($el.children).forEach((e) => {
if (e.classList.contains('atk-height-limit-btn')) e.remove()
})
$el.style.height = ''
$el.style.overflow = ''
}, [
// 评论内容限高
{ el: this.$content, max: contentMaxH, imgContains: true },
{ el: this.$replyTo, max: contentMaxH, imgContains: true },
// 子评论区域限高(仅嵌套模式)
{ el: this.$childrenWrap, max: childrenMaxH, imgContains: false }
])
}

/** 子评论区域移除限高 */
public heightLimitRemoveForChildren() {
if (!this.$childrenWrap) return
this.heightLimitRemove(this.$childrenWrap)
}

/** 内容限高区域新增 */
private heightLimitAdd($el: HTMLElement, maxHeight: number) {
if (!$el) return
if ($el.classList.contains('atk-height-limit')) return

$el.classList.add('atk-height-limit')
$el.style.height = `${maxHeight}px`
$el.style.overflow = 'hidden'
const $hideMoreOpenBtn = Utils.createElement(`<div class="atk-height-limit-btn">${this.ctx.$t('readMore')}</span>`)
$hideMoreOpenBtn.onclick = (e) => {
e.stopPropagation()
this.heightLimitRemove($el)

// 子评论数等于 1,直接取消限高
const children = this.comment.getChildren()
if (children.length === 1) children[0].getRender().heightLimitRemove(children[0].getRender().$content)
}
$el.append($hideMoreOpenBtn)
HeightLimit.disposeHeightLimit(this.$childrenWrap)
}

/** 渐出动画 */
Expand Down