artalk 2.4.4 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/Artalk.css +1 -1
- package/dist/Artalk.es.js +4872 -4072
- package/dist/Artalk.iife.js +1 -1
- package/dist/Artalk.js +1 -1
- package/dist/ArtalkLite.css +1 -1
- package/dist/ArtalkLite.es.js +2468 -1779
- package/dist/ArtalkLite.iife.js +1 -1
- package/dist/ArtalkLite.js +1 -1
- package/dist/i18n/jp.js +1 -1
- package/dist/i18n/zh-TW.js +1 -1
- package/dist/src/api/index.d.ts +2 -2
- package/dist/src/api/system.d.ts +5 -2
- package/dist/src/artalk.d.ts +42 -34
- package/dist/src/comment/comment.d.ts +2 -2
- package/dist/src/comment/height-limit.d.ts +30 -0
- package/dist/src/comment/render-ctx.d.ts +23 -0
- package/dist/src/comment/render.d.ts +2 -50
- package/dist/src/comment/renders/actions.d.ts +5 -0
- package/dist/src/comment/renders/avatar.d.ts +5 -0
- package/dist/src/comment/renders/content.d.ts +5 -0
- package/dist/src/comment/renders/header.d.ts +5 -0
- package/dist/src/comment/renders/index.d.ts +2 -0
- package/dist/src/comment/renders/pending.d.ts +5 -0
- package/dist/src/comment/renders/reply-at.d.ts +5 -0
- package/dist/src/comment/renders/reply-to.d.ts +5 -0
- package/dist/src/components/dialog.d.ts +1 -1
- package/dist/src/config.d.ts +8 -0
- package/dist/src/context.d.ts +15 -29
- package/dist/src/editor/edit-mode.d.ts +9 -0
- package/dist/src/editor/editor-ui.d.ts +25 -0
- package/dist/src/editor/editor.d.ts +37 -45
- package/dist/src/editor/funcs.d.ts +2 -0
- package/dist/src/editor/mover.d.ts +8 -0
- package/dist/src/editor/plug-manager.d.ts +17 -0
- package/dist/src/editor/plugs/editor-plug.d.ts +13 -1
- package/dist/src/editor/reply.d.ts +9 -0
- package/dist/src/editor/submit.d.ts +16 -0
- package/dist/src/editor/ui.d.ts +25 -0
- package/dist/src/i18n/index.d.ts +11 -7
- package/dist/src/layer/index.d.ts +2 -2
- package/dist/src/layer/layer.d.ts +1 -1
- package/dist/src/lib/checker/captcha-renders.d.ts +5 -0
- package/dist/src/lib/checker/index.d.ts +18 -10
- package/dist/src/lib/dark-mode.d.ts +4 -0
- package/dist/src/lib/marked.d.ts +9 -0
- package/dist/src/lib/utils.d.ts +1 -5
- package/dist/src/list/list-lite.d.ts +15 -11
- package/dist/src/list/list-nest.d.ts +2 -2
- package/dist/src/list/list-ui.d.ts +7 -0
- package/dist/src/list/paginator/adaptors/index.d.ts +12 -0
- package/dist/src/list/paginator/adaptors/pagination.d.ts +9 -0
- package/dist/src/list/paginator/adaptors/read-more.d.ts +10 -0
- package/dist/src/list/paginator/holder.d.ts +16 -0
- package/dist/src/list/paginator/index.d.ts +10 -0
- package/dist/src/service.d.ts +40 -0
- package/dist/types/artalk-config.d.ts +5 -2
- package/dist/types/artalk-data.d.ts +3 -3
- package/dist/types/context.d.ts +11 -17
- package/dist/types/global.d.ts +1 -0
- package/package.json +6 -7
- package/src/api/system.ts +3 -12
- package/src/artalk.ts +101 -158
- package/src/comment/comment.ts +7 -6
- package/src/comment/height-limit.ts +100 -0
- package/src/comment/render-ctx.ts +30 -0
- package/src/comment/render.ts +31 -375
- package/src/comment/renders/actions.ts +114 -0
- package/src/comment/renders/avatar.ts +22 -0
- package/src/comment/renders/content.ts +39 -0
- package/src/comment/renders/header.ts +84 -0
- package/src/comment/renders/index.ts +19 -0
- package/src/comment/renders/pending.ts +12 -0
- package/src/comment/renders/reply-at.ts +16 -0
- package/src/comment/renders/reply-to.ts +24 -0
- package/src/config.ts +65 -0
- package/src/context.ts +31 -86
- package/src/defaults.ts +1 -0
- package/src/editor/edit-mode.ts +102 -0
- package/src/editor/editor.ts +100 -395
- package/src/editor/funcs.ts +96 -0
- package/src/editor/mover.ts +43 -0
- package/src/editor/plug-manager.ts +172 -0
- package/src/editor/plugs/editor-plug.ts +20 -2
- package/src/editor/plugs/{emoticons-plug.less → emoticons-plug.scss} +5 -4
- package/src/editor/plugs/emoticons-plug.ts +2 -1
- package/src/editor/plugs/header-input-plug.ts +5 -5
- package/src/editor/plugs/{preview-plug.less → preview-plug.scss} +1 -1
- package/src/editor/plugs/preview-plug.ts +8 -4
- package/src/editor/plugs/upload-plug.ts +10 -13
- package/src/editor/reply.ts +62 -0
- package/src/editor/submit.ts +98 -0
- package/src/editor/ui.ts +36 -0
- package/src/i18n/index.ts +21 -10
- package/src/layer/index.ts +2 -2
- package/src/layer/layer.ts +4 -4
- package/src/layer/sidebar-layer.ts +2 -1
- package/src/lib/checker/admin.ts +36 -0
- package/src/lib/checker/captcha-renders.ts +68 -0
- package/src/lib/checker/captcha.ts +26 -0
- package/src/lib/checker/index.ts +38 -20
- package/src/lib/dark-mode.ts +48 -0
- package/src/lib/marked.ts +133 -0
- package/src/lib/utils.ts +1 -123
- package/src/list/list-lite.ts +59 -162
- package/src/list/list-ui.ts +80 -0
- package/src/list/list.ts +16 -8
- package/src/list/paginator/adaptors/index.ts +21 -0
- package/src/list/paginator/adaptors/pagination.ts +42 -0
- package/src/list/paginator/adaptors/read-more.ts +56 -0
- package/src/list/paginator/holder.ts +55 -0
- package/src/list/paginator/index.ts +13 -0
- package/src/service.ts +118 -0
- package/src/style/{_color.less → _color.scss} +3 -3
- package/src/style/{_extend.less → _extend.scss} +1 -1
- package/src/style/_variables.scss +3 -0
- package/src/style/{comment.less → comment.scss} +36 -12
- package/src/style/{editor.less → editor.scss} +11 -11
- package/src/style/{list.less → list.scss} +8 -6
- package/src/style/{main.less → main.scss} +24 -23
- package/src/style/{sidebar-layer.less → sidebar-layer.scss} +8 -8
- package/types/artalk-config.d.ts +5 -2
- package/types/artalk-data.d.ts +3 -3
- package/types/context.d.ts +11 -17
- package/types/global.d.ts +1 -0
- package/CNAME +0 -1
- package/LICENSE +0 -165
- package/index.html +0 -157
- package/postcss.config.js +0 -5
- package/scripts/build-i18n.mjs +0 -46
- package/src/lib/checker/admin-checker.ts +0 -36
- package/src/lib/checker/captcha-checker.ts +0 -84
- package/src/style/_variables.less +0 -3
- package/test/ArtalkCount.html +0 -16
- package/test/ArtalkI18n.html +0 -16
- package/test/ArtalkLite.html +0 -15
- package/test/CSRF.html +0 -26
- package/tsconfig.json +0 -12
- package/vite-base.config.ts +0 -21
- package/vite-demo.config.ts +0 -12
- package/vite-lite.config.ts +0 -22
- package/vite.config.ts +0 -27
- /package/dist/src/lib/checker/{admin-checker.d.ts → admin.d.ts} +0 -0
- /package/dist/src/lib/checker/{captcha-checker.d.ts → captcha.d.ts} +0 -0
- /package/src/style/{pagination.less → pagination.scss} +0 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import ListLite from './list-lite'
|
|
2
|
+
import * as Utils from '../lib/utils'
|
|
3
|
+
import * as Ui from '../lib/ui'
|
|
4
|
+
import $t from '../i18n'
|
|
5
|
+
|
|
6
|
+
/** 界面刷新 */
|
|
7
|
+
export function refreshUI(list: ListLite) {
|
|
8
|
+
// 无评论
|
|
9
|
+
const isNoComment = list.ctx.getCommentList().length <= 0
|
|
10
|
+
let $noComment = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-list-no-comment')
|
|
11
|
+
|
|
12
|
+
if (isNoComment) {
|
|
13
|
+
if (!$noComment) {
|
|
14
|
+
$noComment = Utils.createElement('<div class="atk-list-no-comment"></div>')
|
|
15
|
+
$noComment.innerHTML = list.noCommentText || list.ctx.conf.noComment || list.ctx.$t('noComment')
|
|
16
|
+
list.getCommentsWrapEl().appendChild($noComment)
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
$noComment?.remove()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 仅管理员显示控制
|
|
23
|
+
list.ctx.checkAdminShowEl()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 构建错误提示界面 */
|
|
27
|
+
export function renderErrorDialog(list: ListLite, errMsg: string, errData?: any): HTMLElement {
|
|
28
|
+
const errEl = Utils.createElement(`<span>${errMsg},${$t('listLoadFailMsg')}<br/></span>`)
|
|
29
|
+
|
|
30
|
+
const $retryBtn = Utils.createElement(`<span style="cursor:pointer;">${$t('listRetry')}</span>`)
|
|
31
|
+
$retryBtn.onclick = () => (list.fetchComments(0))
|
|
32
|
+
errEl.appendChild($retryBtn)
|
|
33
|
+
|
|
34
|
+
const adminBtn = Utils.createElement('<span atk-only-admin-show> | <span style="cursor:pointer;">打开控制台</span></span>')
|
|
35
|
+
errEl.appendChild(adminBtn)
|
|
36
|
+
if (!list.ctx.user.data.isAdmin) adminBtn.classList.add('atk-hide')
|
|
37
|
+
|
|
38
|
+
let sidebarView = ''
|
|
39
|
+
|
|
40
|
+
// 找不到站点错误,打开侧边栏并填入创建站点表单
|
|
41
|
+
if (errData?.err_no_site) {
|
|
42
|
+
const viewLoadParam = {
|
|
43
|
+
create_name: list.ctx.conf.site,
|
|
44
|
+
create_urls: `${window.location.protocol}//${window.location.host}`
|
|
45
|
+
}
|
|
46
|
+
// TODO 真的是飞鸽传书啊
|
|
47
|
+
sidebarView = `sites|${JSON.stringify(viewLoadParam)}`
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
adminBtn.onclick = () => list.ctx.showSidebar({
|
|
51
|
+
view: sidebarView as any
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return errEl
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 版本检测弹窗 */
|
|
58
|
+
export function versionCheckDialog(list: ListLite, feVer: string, beVer: string): boolean {
|
|
59
|
+
const comp = Utils.versionCompare(feVer, beVer)
|
|
60
|
+
const notSameVer = (comp !== 0)
|
|
61
|
+
if (notSameVer) {
|
|
62
|
+
const errEl = Utils.createElement(
|
|
63
|
+
`<div>请更新 Artalk ${comp < 0 ? $t('frontend') : $t('backend')}以获得完整体验 ` +
|
|
64
|
+
`(<a href="https://artalk.js.org/" target="_blank">帮助文档</a>)` +
|
|
65
|
+
`<br/><br/><span style="color: var(--at-color-meta);">` +
|
|
66
|
+
`当前版本:${$t('frontend')} ${feVer} / ${$t('backend')} ${beVer}` +
|
|
67
|
+
`</span><br/><br/></div>`)
|
|
68
|
+
const ignoreBtn = Utils.createElement('<span style="cursor:pointer">忽略</span>')
|
|
69
|
+
ignoreBtn.onclick = () => {
|
|
70
|
+
Ui.setError(list.$el.parentElement!, null)
|
|
71
|
+
list.ctx.conf.versionCheck = false
|
|
72
|
+
list.fetchComments(0)
|
|
73
|
+
}
|
|
74
|
+
errEl.append(ignoreBtn)
|
|
75
|
+
Ui.setError(list.$el.parentElement!, errEl, '<span class="atk-warn-title">Artalk Warn</span>')
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false
|
|
80
|
+
}
|
package/src/list/list.ts
CHANGED
|
@@ -29,16 +29,26 @@ export default class List extends ListLite {
|
|
|
29
29
|
// 操作按钮
|
|
30
30
|
this.initListActionBtn()
|
|
31
31
|
|
|
32
|
+
const countNumHTML = '<span class="atk-comment-count-num">0</span>'
|
|
32
33
|
this.$commentCount = this.$el.querySelector('.atk-comment-count')!
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
|
|
35
|
+
const refreshCountNumEl = () => {
|
|
36
|
+
this.$commentCount.innerHTML = this.$t('counter', {
|
|
37
|
+
count: countNumHTML,
|
|
38
|
+
})
|
|
39
|
+
this.$commentCountNum = this.$commentCount.querySelector('.atk-comment-count-num')!
|
|
40
|
+
}
|
|
41
|
+
refreshCountNumEl()
|
|
37
42
|
|
|
38
43
|
// copyright
|
|
39
44
|
this.$el.querySelector<HTMLElement>('.atk-copyright')!.innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${ARTALK_VERSION}">Artalk</a>`
|
|
40
45
|
|
|
41
46
|
// event listen
|
|
47
|
+
this.ctx.on('conf-loaded', () => {
|
|
48
|
+
// i18n support when locale changed
|
|
49
|
+
refreshCountNumEl()
|
|
50
|
+
this.refreshUI()
|
|
51
|
+
})
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
private initListActionBtn() {
|
|
@@ -73,8 +83,6 @@ export default class List extends ListLite {
|
|
|
73
83
|
this.$openSidebarBtn.classList.add('atk-hide')
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
// 仅管理员显示控制
|
|
77
|
-
this.ctx.checkAdminShowEl()
|
|
78
86
|
this.$openSidebarBtn.querySelector<HTMLElement>('.atk-text')!
|
|
79
87
|
.innerText = (!this.ctx.user.data.isAdmin) ? this.$t('msgCenter') : this.$t('ctrlCenter')
|
|
80
88
|
|
|
@@ -121,8 +129,7 @@ export default class List extends ListLite {
|
|
|
121
129
|
const comment = this.ctx.findComment(commentId)
|
|
122
130
|
if (!comment) { // 若找不到评论
|
|
123
131
|
// 自动翻页
|
|
124
|
-
|
|
125
|
-
else if (this.pagination) this.pagination.next()
|
|
132
|
+
this.pgHolder?.next()
|
|
126
133
|
return
|
|
127
134
|
}
|
|
128
135
|
|
|
@@ -154,6 +161,7 @@ export default class List extends ListLite {
|
|
|
154
161
|
else window.setTimeout(() => goTo(), 350)
|
|
155
162
|
|
|
156
163
|
this.goToCommentFounded = true
|
|
164
|
+
this.goToCommentDelay = true // reset
|
|
157
165
|
}
|
|
158
166
|
|
|
159
167
|
/** 管理员设置页面信息 */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IPgHolderConf, TPgMode } from '../index'
|
|
2
|
+
import PaginationAdaptor from './pagination'
|
|
3
|
+
import ReadMoreAdaptor from './read-more'
|
|
4
|
+
|
|
5
|
+
// 分页方式适配器
|
|
6
|
+
const Adaptors: Record<TPgMode, IPgAdaptor<any>> = {
|
|
7
|
+
'pagination': PaginationAdaptor,
|
|
8
|
+
'read-more': ReadMoreAdaptor
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default Adaptors
|
|
12
|
+
|
|
13
|
+
export interface IPgAdaptor<T> {
|
|
14
|
+
instance: T
|
|
15
|
+
el: HTMLElement
|
|
16
|
+
createInstance(conf: IPgHolderConf): [T, HTMLElement]
|
|
17
|
+
setLoading(val: boolean): void
|
|
18
|
+
update(offset: number, total: number): void
|
|
19
|
+
next(): void
|
|
20
|
+
showErr?(msg: string): void
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import Pagination from '@/components/pagination'
|
|
2
|
+
import * as Utils from '@/lib/utils'
|
|
3
|
+
import { IPgAdaptor } from '.'
|
|
4
|
+
|
|
5
|
+
interface IPaginationAdaptor extends IPgAdaptor<Pagination> {}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 翻页形式的分页
|
|
9
|
+
*/
|
|
10
|
+
export default <IPaginationAdaptor>{
|
|
11
|
+
createInstance(conf) {
|
|
12
|
+
const instance = new Pagination(conf.total, {
|
|
13
|
+
pageSize: conf.pageSize,
|
|
14
|
+
onChange: async (o) => {
|
|
15
|
+
if (conf.list.conf.editorTravel === true)
|
|
16
|
+
conf.list.ctx.editorTravelBack() // 防止评论框被吞
|
|
17
|
+
|
|
18
|
+
await conf.list.fetchComments(o)
|
|
19
|
+
|
|
20
|
+
// 滚动到第一个评论的位置
|
|
21
|
+
if (conf.list.repositionAt) {
|
|
22
|
+
const at = conf.list.scrollListenerAt || window
|
|
23
|
+
at.scroll({
|
|
24
|
+
top: conf.list.repositionAt ? Utils.getOffset(conf.list.repositionAt).top : 0,
|
|
25
|
+
left: 0,
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return [instance, instance.$el]
|
|
32
|
+
},
|
|
33
|
+
setLoading(val) {
|
|
34
|
+
this.instance.setLoading(val)
|
|
35
|
+
},
|
|
36
|
+
update(offset, total) {
|
|
37
|
+
this.instance.update(offset, total)
|
|
38
|
+
},
|
|
39
|
+
next() {
|
|
40
|
+
this.instance.next()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ReadMoreBtn from '@/components/read-more-btn'
|
|
2
|
+
import $t from '@/i18n'
|
|
3
|
+
import * as Ui from '@/lib/ui'
|
|
4
|
+
import { IPgAdaptor } from '.'
|
|
5
|
+
|
|
6
|
+
interface IReadMoreAdaptor extends IPgAdaptor<ReadMoreBtn> {
|
|
7
|
+
autoLoadScrollEvent?: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 阅读更多形式的分页
|
|
12
|
+
*/
|
|
13
|
+
export default <IReadMoreAdaptor>{
|
|
14
|
+
createInstance(conf) {
|
|
15
|
+
const readMoreBtn = new ReadMoreBtn({
|
|
16
|
+
pageSize: conf.pageSize,
|
|
17
|
+
onClick: async (o) => {
|
|
18
|
+
await conf.list.fetchComments(o)
|
|
19
|
+
},
|
|
20
|
+
text: $t('loadMore'),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// 滚动到底部自动加载
|
|
24
|
+
if (conf.list.conf.pagination.autoLoad) {
|
|
25
|
+
// 添加滚动事件监听
|
|
26
|
+
const at = conf.list.scrollListenerAt || document
|
|
27
|
+
if (this.autoLoadScrollEvent) at.removeEventListener('scroll', this.autoLoadScrollEvent) // 解除原有
|
|
28
|
+
this.autoLoadScrollEvent = () => {
|
|
29
|
+
if (conf.mode !== 'read-more'
|
|
30
|
+
|| !readMoreBtn?.hasMore
|
|
31
|
+
|| conf.list.getLoading()
|
|
32
|
+
) return
|
|
33
|
+
|
|
34
|
+
const $target = conf.list.$el.querySelector<HTMLElement>('.atk-list-comments-wrap > .atk-comment-wrap:nth-last-child(3)') // 获取倒数第3个评论元素
|
|
35
|
+
if ($target && Ui.isVisible($target, conf.list.scrollListenerAt)) {
|
|
36
|
+
readMoreBtn.click() // 自动点击加载更多按钮
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
at.addEventListener('scroll', this.autoLoadScrollEvent)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return [readMoreBtn, readMoreBtn.$el]
|
|
43
|
+
},
|
|
44
|
+
setLoading(val) {
|
|
45
|
+
this.instance.setLoading(val)
|
|
46
|
+
},
|
|
47
|
+
update(offset, total) {
|
|
48
|
+
this.instance.update(offset, total)
|
|
49
|
+
},
|
|
50
|
+
showErr(msg) {
|
|
51
|
+
this.instance.showErr(msg)
|
|
52
|
+
},
|
|
53
|
+
next() {
|
|
54
|
+
this.instance.click()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { IPgHolderConf, TPgMode } from '.'
|
|
2
|
+
import Adaptors from './adaptors'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 分页方式持有者(调度器)
|
|
6
|
+
*/
|
|
7
|
+
export default class PgHolder {
|
|
8
|
+
private conf: IPgHolderConf
|
|
9
|
+
|
|
10
|
+
constructor(conf: IPgHolderConf) {
|
|
11
|
+
this.conf = conf
|
|
12
|
+
this.init()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public getAdaptor() {
|
|
16
|
+
return Adaptors[this.conf.mode]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public init() {
|
|
20
|
+
const adaptor = this.getAdaptor()
|
|
21
|
+
const [instance, el] = adaptor.createInstance(this.conf)
|
|
22
|
+
adaptor.instance = instance
|
|
23
|
+
adaptor.el = el
|
|
24
|
+
this.conf.list.$el.append(adaptor.el)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public setLoading(val: boolean) {
|
|
28
|
+
this.getAdaptor().setLoading(val)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public update(offset: number, total: number) {
|
|
32
|
+
this.getAdaptor().update(offset, total)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public getEl() {
|
|
36
|
+
return this.getAdaptor().el
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public showErr(msg: string) {
|
|
40
|
+
const func = this.getAdaptor().showErr
|
|
41
|
+
if (func) func(msg)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public setMode(val: TPgMode) {
|
|
45
|
+
if (val !== this.conf.mode) {
|
|
46
|
+
this.getEl().remove()
|
|
47
|
+
this.conf.mode = val
|
|
48
|
+
this.init()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public next() {
|
|
53
|
+
this.getAdaptor().next()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ListLite from '../list-lite'
|
|
2
|
+
import PgHolder from './holder'
|
|
3
|
+
|
|
4
|
+
export type TPgMode = 'pagination'|'read-more'
|
|
5
|
+
|
|
6
|
+
export interface IPgHolderConf {
|
|
7
|
+
list: ListLite
|
|
8
|
+
mode: TPgMode
|
|
9
|
+
total: number
|
|
10
|
+
pageSize: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default PgHolder
|
package/src/service.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import Context from '~/types/context'
|
|
2
|
+
import CheckerLauncher from './lib/checker'
|
|
3
|
+
import Api from './api'
|
|
4
|
+
import Editor from './editor'
|
|
5
|
+
import List from './list'
|
|
6
|
+
import Layer from './layer'
|
|
7
|
+
import SidebarLayer from './layer/sidebar-layer'
|
|
8
|
+
import { initMarked } from './lib/marked'
|
|
9
|
+
import User from './lib/user'
|
|
10
|
+
import ListLite from './list/list-lite'
|
|
11
|
+
import * as DarkMode from './lib/dark-mode'
|
|
12
|
+
import * as I18n from './i18n'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Services
|
|
16
|
+
*
|
|
17
|
+
* @description
|
|
18
|
+
* 当函数有返回值时,该值将自动添加到 Context 类作为一个 private 成员的值,
|
|
19
|
+
* 这个成员的名称与函数名对应。可在 Context 类中访问该对象(同事类)。
|
|
20
|
+
*/
|
|
21
|
+
const services = {
|
|
22
|
+
// I18n
|
|
23
|
+
i18n(ctx: Context) {
|
|
24
|
+
I18n.setLocale(ctx.conf.locale)
|
|
25
|
+
|
|
26
|
+
ctx.on('conf-loaded', () => {
|
|
27
|
+
I18n.setLocale(ctx.conf.locale)
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// Markdown 组件
|
|
32
|
+
markdown() {
|
|
33
|
+
initMarked()
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// User Store
|
|
37
|
+
user(ctx: Context) {
|
|
38
|
+
const user = new User(ctx)
|
|
39
|
+
return user
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// HTTP API client
|
|
43
|
+
api(ctx: Context) {
|
|
44
|
+
const api = new Api(ctx)
|
|
45
|
+
return api
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// CheckerLauncher
|
|
49
|
+
checkerLauncher(ctx: Context) {
|
|
50
|
+
const checkerLauncher = new CheckerLauncher(ctx)
|
|
51
|
+
return checkerLauncher
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// 编辑器
|
|
55
|
+
editor(ctx: Context) {
|
|
56
|
+
const editor = new Editor(ctx)
|
|
57
|
+
ctx.$root.appendChild(editor.$el)
|
|
58
|
+
return editor
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// 评论列表
|
|
62
|
+
list(ctx: Context): ListLite|undefined {
|
|
63
|
+
// 评论列表
|
|
64
|
+
const list = new List(ctx)
|
|
65
|
+
ctx.$root.appendChild(list.$el)
|
|
66
|
+
|
|
67
|
+
// 评论获取
|
|
68
|
+
list.fetchComments(0)
|
|
69
|
+
|
|
70
|
+
return list
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// 弹出层
|
|
74
|
+
layer(ctx: Context) {
|
|
75
|
+
// 记录页面原始 CSS 属性
|
|
76
|
+
Layer.BodyOrgOverflow = document.body.style.overflow
|
|
77
|
+
Layer.BodyOrgPaddingRight = document.body.style.paddingRight
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// 侧边栏 Layer
|
|
81
|
+
sidebarLayer(ctx: Context) {
|
|
82
|
+
const sidebarLayer = new SidebarLayer(ctx)
|
|
83
|
+
return sidebarLayer
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// 默认事件绑定
|
|
87
|
+
eventsDefault(ctx: Context) {
|
|
88
|
+
// 锚点快速跳转评论
|
|
89
|
+
window.addEventListener('hashchange', () => {
|
|
90
|
+
ctx.listHashGotoCheck()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// 本地用户数据变更
|
|
94
|
+
ctx.on('user-changed', () => {
|
|
95
|
+
ctx.checkAdminShowEl()
|
|
96
|
+
ctx.listRefreshUI()
|
|
97
|
+
})
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// 夜间模式
|
|
101
|
+
darkMode(ctx: Context) {
|
|
102
|
+
DarkMode.syncDarkModeConf(ctx)
|
|
103
|
+
|
|
104
|
+
ctx.on('conf-loaded', () => {
|
|
105
|
+
DarkMode.syncDarkModeConf(ctx)
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export default services
|
|
111
|
+
|
|
112
|
+
// type tricks for dependency injection
|
|
113
|
+
type TServiceImps = typeof services
|
|
114
|
+
type TObjectWithFuncs = {[k: string]: (...args: any) => any}
|
|
115
|
+
type TKeysOnlyReturn<T extends TObjectWithFuncs, V> = {[K in keyof T]: ReturnType<T[K]> extends V ? K : never}[keyof T]
|
|
116
|
+
type TOmitConditions = TKeysOnlyReturn<TServiceImps, void>
|
|
117
|
+
type TServiceInjectors = Omit<TServiceImps, TOmitConditions>
|
|
118
|
+
export type TInjectedServices = {[K in keyof TServiceInjectors]: ReturnType<TServiceInjectors[K]>}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
--at-color-light: #4285f4;
|
|
9
9
|
--at-color-bg: #FFF;
|
|
10
10
|
--at-color-bg-transl: rgba(255, 255, 255, 0.94);
|
|
11
|
-
--at-color-bg-grey: #
|
|
11
|
+
--at-color-bg-grey: #f3f4f5;
|
|
12
12
|
--at-color-bg-grey-transl: rgba(244, 244, 244, 0.75);
|
|
13
13
|
--at-color-bg-light: rgba(29, 161, 242, 0.1);
|
|
14
14
|
--at-color-main: #0083ff;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
--at-color-pink: #fa5a57;
|
|
17
17
|
--at-color-yellow: #ff7c37;
|
|
18
18
|
--at-color-green: #4caf50;
|
|
19
|
-
--at-color-gradient: linear-gradient(180deg,
|
|
19
|
+
--at-color-gradient: linear-gradient(180deg, transparent, rgba(255, 255, 255));
|
|
20
20
|
|
|
21
21
|
&.atk-dark-mode {
|
|
22
22
|
--at-color-font: #fff;
|
|
@@ -36,6 +36,6 @@
|
|
|
36
36
|
--at-color-pink: #fa5a57;
|
|
37
37
|
--at-color-yellow: #ff7c37;
|
|
38
38
|
--at-color-green: #4caf50;
|
|
39
|
-
--at-color-gradient: linear-gradient(180deg,
|
|
39
|
+
--at-color-gradient: linear-gradient(180deg, transparent, rgba(30, 34, 36, 1));
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
img {
|
|
52
52
|
width: 50px;
|
|
53
53
|
height: 50px;
|
|
54
|
-
border-radius: (
|
|
54
|
+
border-radius: calc($block-radius / 2);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
margin-left: 63px;
|
|
61
61
|
|
|
62
62
|
& > .atk-header {
|
|
63
|
-
line-height: 1;
|
|
63
|
+
line-height: 1.5;
|
|
64
64
|
font-size: 13px;
|
|
65
65
|
margin-bottom: .5em;
|
|
66
66
|
overflow: hidden;
|
|
@@ -108,13 +108,13 @@
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
.badge
|
|
111
|
+
.badge {
|
|
112
112
|
display: inline-block;
|
|
113
113
|
color: var(--at-color-meta);
|
|
114
114
|
background: var(--at-color-bg-grey);
|
|
115
115
|
padding: 0px 6px;
|
|
116
116
|
line-height: 17px;
|
|
117
|
-
border-radius: (
|
|
117
|
+
border-radius: calc($block-radius / 2);
|
|
118
118
|
|
|
119
119
|
&:not(:last-child) {
|
|
120
120
|
margin-right: 6px;
|
|
@@ -126,24 +126,27 @@
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
.atk-badge {
|
|
129
|
-
.badge
|
|
129
|
+
@extend .badge;
|
|
130
130
|
color: #fff;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
.atk-region-badge {
|
|
134
|
+
@extend .badge;
|
|
135
|
+
}
|
|
136
|
+
|
|
133
137
|
.atk-pinned-badge {
|
|
134
|
-
.badge
|
|
138
|
+
@extend .badge;
|
|
135
139
|
color: #fff;
|
|
136
140
|
background: #f44336;
|
|
137
141
|
}
|
|
138
142
|
|
|
139
143
|
.atk-ua {
|
|
140
|
-
.badge
|
|
144
|
+
@extend .badge;
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
.atk-ua-wrap {
|
|
144
148
|
@media only screen and (max-width: 768px) {
|
|
145
149
|
display: block;
|
|
146
|
-
margin-top: 5px;
|
|
147
150
|
}
|
|
148
151
|
}
|
|
149
152
|
}
|
|
@@ -164,7 +167,7 @@
|
|
|
164
167
|
border: 3px solid var(--at-color-bg-grey);
|
|
165
168
|
border-bottom: 0;
|
|
166
169
|
padding: 5px 10px;
|
|
167
|
-
border-radius:
|
|
170
|
+
border-radius: $block-radius $block-radius 0 0;
|
|
168
171
|
margin-bottom: -5px;
|
|
169
172
|
}
|
|
170
173
|
|
|
@@ -193,7 +196,7 @@
|
|
|
193
196
|
|
|
194
197
|
& > .atk-reply-to {
|
|
195
198
|
padding: 5px 15px;
|
|
196
|
-
border-left: 3px solid
|
|
199
|
+
border-left: 3px solid var(--at-color-border);
|
|
197
200
|
margin-bottom: 10px;
|
|
198
201
|
position: relative;
|
|
199
202
|
margin-top: 10px;
|
|
@@ -217,7 +220,7 @@
|
|
|
217
220
|
padding: 10px 18px;
|
|
218
221
|
display: block;
|
|
219
222
|
background: var(--at-color-bg-grey);
|
|
220
|
-
border-radius:
|
|
223
|
+
border-radius: $block-radius;
|
|
221
224
|
|
|
222
225
|
.atk-text {
|
|
223
226
|
color: var(--at-color-meta);
|
|
@@ -291,7 +294,7 @@
|
|
|
291
294
|
transform: translateX(-50%);
|
|
292
295
|
cursor: pointer;
|
|
293
296
|
border: 1px solid var(--at-color-border);
|
|
294
|
-
border-radius:
|
|
297
|
+
border-radius: $block-radius;
|
|
295
298
|
background: var(--at-color-bg);
|
|
296
299
|
padding: 1px 20px;
|
|
297
300
|
font-size: 15px;
|
|
@@ -307,6 +310,27 @@
|
|
|
307
310
|
.atk-height-limit .atk-height-limit .atk-height-limit-btn {
|
|
308
311
|
display: none;
|
|
309
312
|
}
|
|
313
|
+
|
|
314
|
+
// 限高可滚动
|
|
315
|
+
.atk-height-limit-scroll {
|
|
316
|
+
// @extend .atk-slim-scrollbar;
|
|
317
|
+
margin-top: 10px;
|
|
318
|
+
overflow-y: auto;
|
|
319
|
+
$cBorder: var(--at-color-border);
|
|
320
|
+
$cOverwrite: var(--at-color-bg);
|
|
321
|
+
background:
|
|
322
|
+
/* Shadow covers */
|
|
323
|
+
linear-gradient($cOverwrite 1px, transparent 1px calc(100% - 1px)) center top,
|
|
324
|
+
linear-gradient(transparent calc(100% - 1px), $cOverwrite calc(100% - 1px) 1px) center bottom,
|
|
325
|
+
/* Shadows */
|
|
326
|
+
linear-gradient($cBorder 1px, transparent 1px calc(100% - 1px)) center top,
|
|
327
|
+
linear-gradient(transparent calc(100% - 1px), $cBorder calc(100% - 1px) 1px) center bottom;
|
|
328
|
+
background-repeat: no-repeat;
|
|
329
|
+
background-color: transparent;
|
|
330
|
+
background-size: 100% 1px, 100% 1px, 100% 1px, 100% 1px;
|
|
331
|
+
/* Opera doesn't support this in the shorthand */
|
|
332
|
+
background-attachment: local, local, scroll, scroll;
|
|
333
|
+
}
|
|
310
334
|
}
|
|
311
335
|
|
|
312
336
|
/** 子评论区域 **/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
.atk-main-editor {
|
|
2
|
-
|
|
2
|
+
$editor-padding: 10px;
|
|
3
3
|
z-index: 5;
|
|
4
4
|
position: relative;
|
|
5
5
|
overflow: hidden;
|
|
6
6
|
background: var(--at-color-bg);
|
|
7
7
|
border: 1px solid var(--at-color-border);
|
|
8
|
-
border-radius:
|
|
8
|
+
border-radius: $block-radius;
|
|
9
9
|
margin-bottom: 10px;
|
|
10
10
|
|
|
11
11
|
@media only screen and (max-width: 768px) {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
font-size: 14px;
|
|
24
24
|
background: transparent;
|
|
25
25
|
border: 2px solid transparent;
|
|
26
|
-
border-radius: (
|
|
26
|
+
border-radius: calc($block-radius / 2);
|
|
27
27
|
padding: 6px 5px;
|
|
28
28
|
resize: none;
|
|
29
29
|
outline: none;
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
font-size: 15px;
|
|
45
45
|
background-color: var(--at-color-bg);
|
|
46
46
|
border: 2px solid transparent;
|
|
47
|
-
border-radius: (
|
|
47
|
+
border-radius: calc($block-radius / 2);
|
|
48
48
|
width: 100%;
|
|
49
49
|
min-height: 120px;
|
|
50
50
|
margin-top: 2px;
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
position: absolute;
|
|
71
71
|
right: 7px;
|
|
72
72
|
bottom: 2px;
|
|
73
|
-
max-width: (100% - 20px);
|
|
73
|
+
max-width: calc(100% - 20px);
|
|
74
74
|
height: 30px;
|
|
75
75
|
line-height: 30px;
|
|
76
76
|
font-size: 14px;
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
text-overflow: ellipsis;
|
|
81
81
|
white-space: nowrap;
|
|
82
82
|
cursor: pointer;
|
|
83
|
-
border-radius: (
|
|
83
|
+
border-radius: calc($block-radius / 2);
|
|
84
84
|
|
|
85
85
|
.atk-text {
|
|
86
86
|
}
|
|
@@ -129,9 +129,9 @@
|
|
|
129
129
|
line-height: 24px;
|
|
130
130
|
cursor: pointer;
|
|
131
131
|
color: var(--at-color-grey);
|
|
132
|
-
font-size:
|
|
132
|
+
font-size: $font-size;
|
|
133
133
|
user-select: none;
|
|
134
|
-
border-radius: (
|
|
134
|
+
border-radius: calc($block-radius / 2);
|
|
135
135
|
|
|
136
136
|
&:not(:last-child) {
|
|
137
137
|
margin-right: 2px;
|
|
@@ -163,15 +163,15 @@
|
|
|
163
163
|
.atk-send-btn {
|
|
164
164
|
background: var(--at-color-main);
|
|
165
165
|
color: #fff;
|
|
166
|
-
font-size:
|
|
166
|
+
font-size: $font-size;
|
|
167
167
|
border: none;
|
|
168
|
-
padding: 0 30px;
|
|
169
168
|
margin: 0;
|
|
170
169
|
height: 30px;
|
|
170
|
+
width: 8em;
|
|
171
171
|
cursor: pointer;
|
|
172
172
|
transition: opacity 0.3s ease-in-out;
|
|
173
173
|
outline: none;
|
|
174
|
-
border-radius: (
|
|
174
|
+
border-radius: calc($block-radius / 2);
|
|
175
175
|
|
|
176
176
|
&:active {
|
|
177
177
|
opacity: 0.9;
|