artalk 2.7.1 → 2.7.2

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.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +155 -147
  4. package/dist/Artalk.es.js.map +1 -1
  5. package/dist/Artalk.iife.js +1 -1
  6. package/dist/Artalk.iife.js.map +1 -1
  7. package/dist/Artalk.js +1 -1
  8. package/dist/Artalk.js.map +1 -1
  9. package/dist/ArtalkLite.css +1 -1
  10. package/dist/ArtalkLite.es.js +155 -147
  11. package/dist/ArtalkLite.es.js.map +1 -1
  12. package/dist/ArtalkLite.iife.js +1 -1
  13. package/dist/ArtalkLite.iife.js.map +1 -1
  14. package/dist/ArtalkLite.js +1 -1
  15. package/dist/ArtalkLite.js.map +1 -1
  16. package/dist/i18n/jp.js +1 -1
  17. package/dist/i18n/zh-TW.js +1 -1
  18. package/dist/src/api/_base.d.ts +7 -0
  19. package/dist/src/api/_options.d.ts +5 -0
  20. package/dist/src/components/checker/index.d.ts +2 -0
  21. package/dist/src/i18n/en.d.ts +1 -0
  22. package/dist/src/i18n/external.d.ts +1 -0
  23. package/dist/src/i18n/index.d.ts +3 -0
  24. package/dist/src/i18n/jp.d.ts +1 -0
  25. package/dist/src/i18n/zh-TW.d.ts +1 -0
  26. package/dist/src/lib/user.d.ts +8 -6
  27. package/dist/src/plugins/editor/_kit.d.ts +7 -1
  28. package/dist/src/service.d.ts +2 -8
  29. package/dist/types/event.d.ts +1 -1
  30. package/package.json +43 -17
  31. package/dist/src/components/dark-mode.d.ts +0 -2
  32. package/src/api/_base.ts +0 -18
  33. package/src/api/_options.ts +0 -25
  34. package/src/api/_request.ts +0 -104
  35. package/src/api/admin.ts +0 -18
  36. package/src/api/captcha.ts +0 -24
  37. package/src/api/comment.ts +0 -84
  38. package/src/api/index.ts +0 -29
  39. package/src/api/page.ts +0 -83
  40. package/src/api/site.ts +0 -51
  41. package/src/api/system.ts +0 -49
  42. package/src/api/upload.ts +0 -28
  43. package/src/api/user.ts +0 -132
  44. package/src/artalk.ts +0 -115
  45. package/src/comment/actions.ts +0 -83
  46. package/src/comment/comment.html +0 -18
  47. package/src/comment/comment.ts +0 -192
  48. package/src/comment/height-limit.ts +0 -100
  49. package/src/comment/index.ts +0 -3
  50. package/src/comment/render.ts +0 -150
  51. package/src/comment/renders/actions.ts +0 -114
  52. package/src/comment/renders/avatar.ts +0 -22
  53. package/src/comment/renders/content.ts +0 -39
  54. package/src/comment/renders/header.ts +0 -84
  55. package/src/comment/renders/index.ts +0 -19
  56. package/src/comment/renders/pending.ts +0 -12
  57. package/src/comment/renders/reply-at.ts +0 -16
  58. package/src/comment/renders/reply-to.ts +0 -24
  59. package/src/components/action-btn.ts +0 -170
  60. package/src/components/checker/admin.ts +0 -37
  61. package/src/components/checker/captcha-renders.ts +0 -69
  62. package/src/components/checker/captcha.ts +0 -26
  63. package/src/components/checker/index.ts +0 -192
  64. package/src/components/dark-mode.ts +0 -30
  65. package/src/components/dialog.ts +0 -62
  66. package/src/components/error-dialog.ts +0 -35
  67. package/src/components/pagination.ts +0 -153
  68. package/src/components/read-more-btn.ts +0 -92
  69. package/src/config.ts +0 -119
  70. package/src/context.ts +0 -146
  71. package/src/data.ts +0 -110
  72. package/src/defaults.ts +0 -61
  73. package/src/editor/editor.html +0 -18
  74. package/src/editor/editor.ts +0 -114
  75. package/src/editor/state.ts +0 -52
  76. package/src/editor/ui.ts +0 -36
  77. package/src/i18n/en.ts +0 -84
  78. package/src/i18n/external.ts +0 -10
  79. package/src/i18n/index.ts +0 -67
  80. package/src/i18n/jp.ts +0 -86
  81. package/src/i18n/zh-CN.ts +0 -88
  82. package/src/i18n/zh-TW.ts +0 -86
  83. package/src/layer/index.ts +0 -4
  84. package/src/layer/layer-manager.ts +0 -32
  85. package/src/layer/layer.ts +0 -39
  86. package/src/layer/scrollbar-helper.ts +0 -24
  87. package/src/layer/sidebar-layer.html +0 -8
  88. package/src/layer/sidebar-layer.ts +0 -155
  89. package/src/layer/wrap.ts +0 -77
  90. package/src/lib/component.ts +0 -12
  91. package/src/lib/detect.ts +0 -222
  92. package/src/lib/event-manager.ts +0 -47
  93. package/src/lib/highlight.ts +0 -5
  94. package/src/lib/marked-renderer.ts +0 -32
  95. package/src/lib/marked.ts +0 -72
  96. package/src/lib/sanitizer.ts +0 -57
  97. package/src/lib/ui.ts +0 -193
  98. package/src/lib/user.ts +0 -55
  99. package/src/lib/utils.ts +0 -236
  100. package/src/list/comment.ts +0 -20
  101. package/src/list/layout.ts +0 -120
  102. package/src/list/list.html +0 -20
  103. package/src/list/list.ts +0 -79
  104. package/src/list/nest.ts +0 -66
  105. package/src/list/page.ts +0 -97
  106. package/src/list/paginator/index.ts +0 -25
  107. package/src/list/paginator/read-more.ts +0 -67
  108. package/src/list/paginator/up-down.ts +0 -51
  109. package/src/main.ts +0 -14
  110. package/src/plugins/admin-only-elem.ts +0 -40
  111. package/src/plugins/conf-remoter.ts +0 -54
  112. package/src/plugins/dark-mode.ts +0 -12
  113. package/src/plugins/editor/_kit.ts +0 -57
  114. package/src/plugins/editor/_plug.ts +0 -60
  115. package/src/plugins/editor/_sample.ts +0 -8
  116. package/src/plugins/editor/closable.ts +0 -44
  117. package/src/plugins/editor/emoticons.scss +0 -72
  118. package/src/plugins/editor/emoticons.ts +0 -293
  119. package/src/plugins/editor/header-event.ts +0 -35
  120. package/src/plugins/editor/header-link.ts +0 -31
  121. package/src/plugins/editor/header-user.ts +0 -98
  122. package/src/plugins/editor/index.ts +0 -40
  123. package/src/plugins/editor/local-storage.ts +0 -36
  124. package/src/plugins/editor/mover.ts +0 -29
  125. package/src/plugins/editor/preview.scss +0 -7
  126. package/src/plugins/editor/preview.ts +0 -41
  127. package/src/plugins/editor/state-edit.ts +0 -116
  128. package/src/plugins/editor/state-reply.ts +0 -91
  129. package/src/plugins/editor/submit-add.ts +0 -33
  130. package/src/plugins/editor/submit-btn.ts +0 -25
  131. package/src/plugins/editor/submit.ts +0 -73
  132. package/src/plugins/editor/textarea.ts +0 -55
  133. package/src/plugins/editor/upload.ts +0 -145
  134. package/src/plugins/editor-kit.ts +0 -161
  135. package/src/plugins/index.ts +0 -16
  136. package/src/plugins/list/copyright.ts +0 -16
  137. package/src/plugins/list/count.ts +0 -31
  138. package/src/plugins/list/dropdown.ts +0 -95
  139. package/src/plugins/list/error-dialog.ts +0 -22
  140. package/src/plugins/list/fetch.ts +0 -64
  141. package/src/plugins/list/goto-first.ts +0 -23
  142. package/src/plugins/list/goto.ts +0 -72
  143. package/src/plugins/list/index.ts +0 -25
  144. package/src/plugins/list/loading.ts +0 -17
  145. package/src/plugins/list/no-comment.ts +0 -25
  146. package/src/plugins/list/reach-bottom.ts +0 -46
  147. package/src/plugins/list/sidebar-btn.ts +0 -39
  148. package/src/plugins/list/time-ticking.ts +0 -22
  149. package/src/plugins/list/unread-badge.ts +0 -26
  150. package/src/plugins/list/unread.ts +0 -38
  151. package/src/plugins/list/with-editor.ts +0 -59
  152. package/src/plugins/markdown.ts +0 -10
  153. package/src/plugins/stat.ts +0 -82
  154. package/src/plugins/version-check.ts +0 -36
  155. package/src/service.ts +0 -91
  156. package/src/style/_color.scss +0 -41
  157. package/src/style/_extend.scss +0 -20
  158. package/src/style/_variables.scss +0 -3
  159. package/src/style/comment.scss +0 -360
  160. package/src/style/editor.scss +0 -194
  161. package/src/style/list.scss +0 -212
  162. package/src/style/main.scss +0 -486
  163. package/src/style/pagination.scss +0 -58
  164. package/src/style/sidebar-layer.scss +0 -92
  165. package/types/config.ts +0 -153
  166. package/types/context.ts +0 -84
  167. package/types/data.ts +0 -271
  168. package/types/editor.ts +0 -95
  169. package/types/event.ts +0 -40
  170. package/types/global.d.ts +0 -1
  171. package/types/index.ts +0 -7
  172. package/types/plugin.ts +0 -3
  173. package/types/sidebar.ts +0 -3
package/src/lib/utils.ts DELETED
@@ -1,236 +0,0 @@
1
- export function createElement<E extends HTMLElement = HTMLElement>(htmlStr: string = ''): E {
2
- const div = document.createElement('div')
3
- div.innerHTML = htmlStr.trim()
4
- return (div.firstElementChild || div) as E
5
- }
6
-
7
- export function getHeight(el: HTMLElement) {
8
- return parseFloat(getComputedStyle(el, null).height.replace('px', ''))
9
- }
10
-
11
- export function htmlEncode(str: string) {
12
- const temp = document.createElement('div')
13
- temp.innerText = str
14
- const output = temp.innerHTML
15
- return output
16
- }
17
-
18
- export function htmlDecode(str: string) {
19
- const temp = document.createElement('div')
20
- temp.innerHTML = str
21
- const output = temp.innerText
22
- return output
23
- }
24
-
25
- export function getQueryParam(name: string) {
26
- const match = RegExp(`[?&]${name}=([^&]*)`).exec(window.location.search);
27
- return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
28
- }
29
-
30
- export function getOffset(el: HTMLElement, relativeTo?: HTMLElement) {
31
- const getOffsetRecursive = (element: HTMLElement): { top: number, left: number } => {
32
- const rect = element.getBoundingClientRect()
33
- const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
34
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop
35
- return {
36
- top: rect.top + scrollTop,
37
- left: rect.left + scrollLeft
38
- }
39
- }
40
-
41
- const elOffset = getOffsetRecursive(el)
42
- if (!relativeTo) return elOffset
43
-
44
- const relativeToOffset = getOffsetRecursive(relativeTo)
45
-
46
- return {
47
- top: elOffset.top - relativeToOffset.top,
48
- left: elOffset.left - relativeToOffset.left
49
- }
50
- }
51
-
52
- export function padWithZeros(vNumber: number, width: number) {
53
- let numAsString = vNumber.toString()
54
- while (numAsString.length < width) {
55
- numAsString = `0${numAsString}`
56
- }
57
- return numAsString
58
- }
59
-
60
- export function dateFormat(date: Date) {
61
- const vDay = padWithZeros(date.getDate(), 2)
62
- const vMonth = padWithZeros(date.getMonth() + 1, 2)
63
- const vYear = padWithZeros(date.getFullYear(), 2)
64
- // var vHour = padWithZeros(date.getHours(), 2);
65
- // var vMinute = padWithZeros(date.getMinutes(), 2);
66
- // var vSecond = padWithZeros(date.getSeconds(), 2);
67
- return `${vYear}-${vMonth}-${vDay}`
68
- }
69
-
70
- export function timeAgo(date: Date, $t: Function = (n: string) => n) {
71
- try {
72
- const oldTime = date.getTime()
73
- const currTime = new Date().getTime()
74
- const diffValue = currTime - oldTime
75
-
76
- const days = Math.floor(diffValue / (24 * 3600 * 1000))
77
- if (days === 0) {
78
- // 计算相差小时数
79
- const leave1 = diffValue % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
80
- const hours = Math.floor(leave1 / (3600 * 1000))
81
- if (hours === 0) {
82
- // 计算相差分钟数
83
- const leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
84
- const minutes = Math.floor(leave2 / (60 * 1000))
85
- if (minutes === 0) {
86
- // 计算相差秒数
87
- const leave3 = leave2 % (60 * 1000) // 计算分钟数后剩余的毫秒数
88
- const seconds = Math.round(leave3 / 1000)
89
- if (seconds < 10) return $t('now')
90
- return `${seconds} ${$t('seconds')}`
91
- }
92
- return `${minutes} ${$t('minutes')}`
93
- }
94
- return `${hours} ${$t('hours')}`
95
- }
96
- if (days < 0) return $t('now')
97
-
98
- if (days < 8) {
99
- return `${days} ${$t('days')}`
100
- }
101
-
102
- return dateFormat(date)
103
- } catch (error) {
104
- console.error(error)
105
- return ' - '
106
- }
107
- }
108
-
109
- /** 所有图片加载完毕后执行 */
110
- export function onImagesLoaded($container: HTMLElement, event: Function) {
111
- if (!$container) return
112
- const images = $container.getElementsByTagName('img')
113
- if (!images.length) return
114
- let loaded = images.length
115
- for (let i = 0; i < images.length; i++) {
116
- if (images[i].complete) {
117
- loaded--
118
- } else {
119
- // eslint-disable-next-line @typescript-eslint/no-loop-func
120
- images[i].addEventListener('load', () => {
121
- loaded--
122
- if (loaded === 0) event()
123
- })
124
- }
125
-
126
- if (loaded === 0) event()
127
- }
128
- }
129
-
130
- export function getGravatarURL(opts: { params: string, mirror: string, emailMD5: string }) {
131
- return `${opts.mirror.replace(/\/$/, '')}/${opts.emailMD5}?${opts.params.replace(/^\?/, '')}`
132
- }
133
-
134
- export function sleep(ms: number) {
135
- return new Promise(resolve => { setTimeout(resolve, ms) });
136
- }
137
-
138
- /** 版本号比较(a < b :-1 | 0 | b < a :1) */
139
- export function versionCompare(a: string, b: string) {
140
- const pa = a.split('.')
141
- const pb = b.split('.')
142
- for (let i = 0; i < 3; i++) {
143
- const na = Number(pa[i])
144
- const nb = Number(pb[i])
145
- if (na > nb) return 1
146
- if (nb > na) return -1
147
- if (!Number.isNaN(na) && Number.isNaN(nb)) return 1
148
- if (Number.isNaN(na) && !Number.isNaN(nb)) return -1
149
- }
150
- return 0
151
- }
152
-
153
- /** 获取修正后的 UserAgent */
154
- export async function getCorrectUserAgent() {
155
- const uaRaw = navigator.userAgent
156
- if (!(navigator as any).userAgentData || !(navigator as any).userAgentData.getHighEntropyValues) {
157
- return uaRaw
158
- }
159
-
160
- // @link https://web.dev/migrate-to-ua-ch/
161
- // @link https://web.dev/user-agent-client-hints/
162
- const uaData = (navigator as any).userAgentData
163
- let uaGot: any = null
164
- try {
165
- uaGot = await uaData.getHighEntropyValues(["platformVersion"])
166
- } catch (err) { console.error(err); return uaRaw }
167
- const majorPlatformVersion = Number(uaGot.platformVersion.split('.')[0])
168
-
169
- if (uaData.platform === "Windows") {
170
- if (majorPlatformVersion >= 13) { // @link https://docs.microsoft.com/en-us/microsoft-edge/web-platform/how-to-detect-win11
171
- // @date 2022-4-29
172
- // Win 11 样本:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"
173
- return uaRaw.replace(/Windows NT 10.0/, 'Windows NT 11.0')
174
- }
175
- }
176
- if (uaData.platform === "macOS") {
177
- if (majorPlatformVersion >= 11) { // 11 => BigSur
178
- // @date 2022-4-29
179
- // (Intel Chip) macOS 12.3 样本:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
180
- // (Arm Chip) macOS 样本:"Mozilla/5.0 (Macintosh; ARM Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.2 Safari/605.1.15"
181
- return uaRaw.replace(/(Mac OS X \d+_\d+_\d+|Mac OS X)/, `Mac OS X ${uaGot.platformVersion.replace(/\./g, '_')}`)
182
- }
183
- }
184
-
185
- return uaRaw
186
- }
187
-
188
- /** 是否为完整的 URL */
189
- export function isValidURL(urlRaw: string) {
190
- // @link https://www.rfc-editor.org/rfc/rfc3986
191
- let url: URL
192
- try {
193
- url = new URL(urlRaw)
194
- } catch (_) { return false }
195
- return url.protocol === "http:" || url.protocol === "https:"
196
- }
197
-
198
- /** 获取基于 conf.server 的 URL */
199
- export function getURLBasedOnApi(opts: { base: string, path: string }) {
200
- return getURLBasedOn(opts.base, opts.path)
201
- }
202
-
203
- /** 获取基于某个 baseURL 的 URL */
204
- export function getURLBasedOn(baseURL: string, path: string) {
205
- return `${baseURL.replace(/\/$/, '')}/${path.replace(/^\//, '')}`
206
- }
207
-
208
- /**
209
- * Performs a deep merge of `source` into `target`.
210
- * Mutates `target` only but not its objects and arrays.
211
- *
212
- * @author inspired by [jhildenbiddle](https://stackoverflow.com/a/48218209).
213
- * @link https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6
214
- */
215
- export function mergeDeep(target: any, source: any) {
216
- const isObject = (obj: any) => obj && typeof obj === 'object'
217
-
218
- if (!isObject(target) || !isObject(source)) {
219
- return source
220
- }
221
-
222
- Object.keys(source).forEach(key => {
223
- const targetValue = target[key]
224
- const sourceValue = source[key]
225
-
226
- if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
227
- target[key] = targetValue.concat(sourceValue)
228
- } else if (isObject(targetValue) && isObject(sourceValue)) {
229
- target[key] = mergeDeep({ ...targetValue}, sourceValue)
230
- } else {
231
- target[key] = sourceValue
232
- }
233
- })
234
-
235
- return target
236
- }
@@ -1,20 +0,0 @@
1
- import type { ContextApi, CommentData } from '~/types'
2
- import Comment from '../comment/comment'
3
-
4
- export function createComment(ctx: ContextApi, comment: CommentData, ctxComments: CommentData[]): Comment {
5
- const instance = new Comment(ctx, comment, {
6
- isFlatMode: ctx.getData().getListLastFetch()?.params.flatMode!,
7
- afterRender: () => {
8
- ctx.trigger('comment-rendered', instance)
9
- },
10
- onDelete: (c: Comment) => {
11
- ctx.getData().deleteComment(c.getID())
12
- },
13
- replyTo: (comment.rid ? ctxComments.find(c => c.id === comment.rid) : undefined)
14
- })
15
-
16
- // 渲染元素
17
- instance.render()
18
-
19
- return instance
20
- }
@@ -1,120 +0,0 @@
1
- import type { CommentData } from '~/types'
2
- import * as Ui from '@/lib/ui'
3
- import Comment from '../comment/comment'
4
- import * as ListNest from './nest'
5
-
6
- export interface LayoutOptions {
7
- $commentsWrap: HTMLElement
8
- nestSortBy: ListNest.SortByType
9
- nestMax: number
10
- flatMode: boolean
11
-
12
- createCommentNode(comment: CommentData, ctxComments: CommentData[]): Comment
13
- findCommentNode(id: number): Comment|undefined
14
- getCommentDataList(): CommentData[]
15
- }
16
-
17
- export default class ListLayout {
18
- constructor(private options: LayoutOptions) {}
19
-
20
- // TODO refactor `if syntax` to strategy pattern
21
- import(comments: CommentData[]) {
22
- if (this.options.flatMode) {
23
- comments.forEach((commentData: CommentData) => {
24
- this.putCommentFlatMode(commentData, comments, 'append')
25
- })
26
- } else {
27
- this.importCommentsNestMode(comments)
28
- }
29
- }
30
-
31
- insert(comment: CommentData) {
32
- if (!this.options.flatMode) {
33
- this.insertCommentNest(comment)
34
- } else {
35
- this.insertCommentFlatMode(comment)
36
- }
37
- }
38
-
39
- // 导入评论 · 嵌套模式
40
- private importCommentsNestMode(srcData: CommentData[]) {
41
- // 遍历 root 评论
42
- const rootNodes = ListNest.makeNestCommentNodeList(srcData, this.options.nestSortBy, this.options.nestMax)
43
- rootNodes.forEach((rootNode: ListNest.CommentNode) => {
44
- const rootC = this.options.createCommentNode(rootNode.comment, srcData)
45
-
46
- // 显示并播放渐入动画
47
- this.options.$commentsWrap?.appendChild(rootC.getEl())
48
- rootC.getRender().playFadeAnim()
49
-
50
- // 加载子评论
51
- const loadChildren = (parentC: Comment, parentNode: ListNest.CommentNode) => {
52
- parentNode.children.forEach((node: ListNest.CommentNode) => {
53
- const childD = node.comment
54
- const childC = this.options.createCommentNode(childD, srcData)
55
-
56
- // 插入到父评论中
57
- parentC.putChild(childC)
58
-
59
- // 递归加载子评论
60
- loadChildren(childC, node)
61
- })
62
- }
63
- loadChildren(rootC, rootNode)
64
-
65
- // 限高检测
66
- rootC.getRender().checkHeightLimit()
67
- })
68
- }
69
-
70
- /** 导入评论 · 平铺模式 */
71
- private putCommentFlatMode(cData: CommentData, ctxData: CommentData[], insertMode: 'append'|'prepend') {
72
- if (cData.is_collapsed) cData.is_allow_reply = false
73
- const comment = this.options.createCommentNode(cData, ctxData)
74
-
75
- // 可见评论添加到界面
76
- // 注:不可见评论用于显示 “引用内容”
77
- if (cData.visible) {
78
- if (insertMode === 'append') this.options.$commentsWrap?.append(comment.getEl())
79
- if (insertMode === 'prepend') this.options.$commentsWrap?.prepend(comment.getEl())
80
- comment.getRender().playFadeAnim()
81
- }
82
-
83
- // 平铺评论插入后 · 内容限高检测
84
- comment.getRender().checkHeightLimit()
85
-
86
- return comment
87
- }
88
-
89
- private insertCommentNest(commentData: CommentData) {
90
- // 嵌套模式
91
- const comment = this.options.createCommentNode(commentData, this.options.getCommentDataList())
92
-
93
- if (commentData.rid === 0) {
94
- // root评论 新增
95
- this.options.$commentsWrap?.prepend(comment.getEl())
96
- } else {
97
- // 子评论 新增
98
- const parent = this.options.findCommentNode(commentData.rid)
99
- if (parent) {
100
- parent.putChild(comment, (this.options.nestSortBy === 'DATE_ASC' ? 'append' : 'prepend'))
101
-
102
- // 若父评论存在 “子评论部分” 限高,取消限高
103
- comment.getParents().forEach((p) => {
104
- p.getRender().heightLimitRemoveForChildren()
105
- })
106
- }
107
- }
108
-
109
- comment.getRender().checkHeightLimit()
110
-
111
- Ui.scrollIntoView(comment.getEl()) // 滚动到可以见
112
- comment.getRender().playFadeAnim() // 播放评论渐出动画
113
- }
114
-
115
- private insertCommentFlatMode(commentData: CommentData) {
116
- // 平铺模式
117
- const comment = this.putCommentFlatMode(commentData, this.options.getCommentDataList(), 'prepend')
118
- Ui.scrollIntoView(comment.getEl()) // 滚动到可见
119
- }
120
- }
@@ -1,20 +0,0 @@
1
- <div class="atk-list">
2
- <div class="atk-list-header">
3
- <div class="atk-comment-count">
4
- <div class="atk-text"></div>
5
- </div>
6
- <div class="atk-right-action">
7
- <span data-action="admin-close-comment" class="atk-hide" atk-only-admin-show></span>
8
- <span data-action="open-sidebar" class="atk-hide atk-on">
9
- <span class="atk-unread-badge" style="display: none;"></span>
10
- <div class="atk-text"></div>
11
- </span>
12
- </div>
13
- </div>
14
- <div class="atk-list-body">
15
- <div class="atk-list-comments-wrap"></div>
16
- </div>
17
- <div class="atk-list-footer">
18
- <div class="atk-copyright"></div>
19
- </div>
20
- </div>
package/src/list/list.ts DELETED
@@ -1,79 +0,0 @@
1
- import type { ContextApi } from '~/types'
2
- import Component from '../lib/component'
3
- import * as Utils from '../lib/utils'
4
- import CommentNode from '../comment/comment'
5
- import ListHTML from './list.html?raw'
6
- import ListLayout from './layout'
7
- import { createComment as createCommentNode } from './comment'
8
- import { initListPaginatorFunc } from './page'
9
-
10
- export default class List extends Component {
11
- /** 列表评论集区域元素 */
12
- $commentsWrap!: HTMLElement
13
- public getCommentsWrapEl() { return this.$commentsWrap }
14
-
15
- protected commentNodes: CommentNode[] = []
16
- getCommentNodes() { return this.commentNodes }
17
-
18
- constructor (ctx: ContextApi) {
19
- super(ctx)
20
-
21
- // Init base element
22
- this.$el = Utils.createElement(ListHTML)
23
- this.$commentsWrap = this.$el.querySelector('.atk-list-comments-wrap')!
24
-
25
- // Init paginator
26
- initListPaginatorFunc(ctx)
27
-
28
- // Bind events
29
- this.initCrudEvents()
30
- }
31
-
32
- getListLayout() {
33
- return new ListLayout({
34
- $commentsWrap: this.$commentsWrap,
35
- nestSortBy: this.ctx.conf.nestSort,
36
- nestMax: this.ctx.conf.nestMax,
37
- flatMode: this.ctx.conf.flatMode as boolean,
38
- // flatMode must be boolean because it had been handled when Artalk.init
39
- createCommentNode: (d, c) => {
40
- const node = createCommentNode(this.ctx, d, c)
41
- this.commentNodes.push(node)
42
- return node
43
- },
44
- findCommentNode: (id) => this.commentNodes.find(c => c.getID() === id),
45
- getCommentDataList: () => this.ctx.getData().getComments(),
46
- })
47
- }
48
-
49
- private initCrudEvents() {
50
- this.ctx.on('list-load', (comments) => {
51
- // 导入数据
52
- this.getListLayout().import(comments)
53
- })
54
-
55
- this.ctx.on('list-loaded', (comments) => {
56
- if (comments.length === 0) this.$commentsWrap.innerHTML = ''
57
- })
58
-
59
- // When comment insert
60
- this.ctx.on('comment-inserted', (comment) => {
61
- this.getListLayout().insert(comment)
62
- })
63
-
64
- // When comment delete
65
- this.ctx.on('comment-deleted', (comment) => {
66
- const node = this.commentNodes.find(c => c.getID() === comment.id)
67
- if (!node) { console.error(`comment node id=${comment.id} not found`);return }
68
- node.getEl().remove()
69
- this.commentNodes = this.commentNodes.filter(c => c.getID() !== comment.id)
70
- // TODO remove child nodes
71
- })
72
-
73
- // When comment update
74
- this.ctx.on('comment-updated', (comment) => {
75
- const node = this.commentNodes.find(c => c.getID() === comment.id)
76
- node && node.setData(comment)
77
- })
78
- }
79
- }
package/src/list/nest.ts DELETED
@@ -1,66 +0,0 @@
1
- import type { CommentData } from '~/types'
2
-
3
- export type CommentNode = {
4
- id: number
5
- comment: CommentData
6
- children: CommentNode[]
7
- parent?: CommentNode
8
- level: number
9
- }
10
-
11
- export type SortByType = 'DATE_DESC'|'DATE_ASC'|'SRC_INDEX'|'VOTE_UP_DESC'
12
-
13
- // 构建树状结构列表
14
- export function makeNestCommentNodeList(srcData: CommentData[], sortBy: SortByType = 'DATE_DESC', nestMax = 2) {
15
- const nodeList: CommentNode[] = []
16
-
17
- const roots = srcData.filter((o) => o.rid === 0)
18
- roots.forEach((root: CommentData) => {
19
- const rootNode: CommentNode = {
20
- id: root.id,
21
- comment: root,
22
- children: [],
23
- level: 1,
24
- }
25
-
26
- rootNode.parent = rootNode
27
- nodeList.push(rootNode)
28
-
29
- ;(function loadChildren(parentNode: CommentNode) {
30
- const children = srcData.filter((o) => o.rid === parentNode.id)
31
- if (children.length === 0) return
32
- if (parentNode.level >= nestMax) parentNode = parentNode.parent!
33
- children.forEach((child: CommentData) => {
34
- const childNode: CommentNode = {
35
- id: child.id,
36
- comment: child,
37
- children: [],
38
- parent: parentNode,
39
- level: parentNode.level + 1,
40
- }
41
-
42
- parentNode.children.push(childNode)
43
- loadChildren(childNode)
44
- })
45
- })(rootNode)
46
- })
47
-
48
- // 排序
49
- const sortFunc = (a: CommentNode, b: CommentNode): number => {
50
- let v = a.id - b.id
51
- if (sortBy === 'DATE_ASC') v = +new Date(a.comment.date) - +new Date(b.comment.date)
52
- else if (sortBy === 'DATE_DESC') v = +new Date(b.comment.date) - +new Date(a.comment.date)
53
- else if (sortBy === 'SRC_INDEX') v = srcData.indexOf(a.comment) - srcData.indexOf(b.comment)
54
- else if (sortBy === 'VOTE_UP_DESC') v = b.comment.vote_up - a.comment.vote_up
55
- return v
56
- }
57
-
58
- (function sortLevels(nodes: CommentNode[]) {
59
- nodes.forEach((node) => {
60
- node.children = node.children.sort(sortFunc)
61
- sortLevels(node.children)
62
- })
63
- })(nodeList)
64
-
65
- return nodeList
66
- }
package/src/list/page.ts DELETED
@@ -1,97 +0,0 @@
1
- import type { ArtalkConfig, ContextApi } from '~/types'
2
- import $t from '@/i18n'
3
- import { Paginator } from './paginator'
4
- import ReadMorePaginator from './paginator/read-more'
5
- import UpDownPaginator from './paginator/up-down'
6
-
7
- function createPaginatorByConf(conf: ArtalkConfig): Paginator {
8
- if (conf.pagination.readMore) return new ReadMorePaginator()
9
- return new UpDownPaginator()
10
- }
11
-
12
- function getPageDataByLastData(ctx: ContextApi): { offset: number, total: number } {
13
- const last = ctx.getData().getListLastFetch()
14
- const r = { offset: 0, total: 0 }
15
- if (!last) return r
16
-
17
- r.offset = last.params.offset
18
- if (last.data) r.total = last.params.flatMode ? last.data.total : last.data.total_roots
19
-
20
- return r
21
- }
22
-
23
- export const initListPaginatorFunc = (ctx: ContextApi) => {
24
- let paginator: Paginator|null = null
25
-
26
- // Init paginator when conf loaded
27
- ctx.on('conf-loaded', (conf) => {
28
- const list = ctx.get('list')
29
-
30
- if (paginator) paginator.dispose() // if had been init, dispose it
31
-
32
- // create paginator instance
33
- paginator = createPaginatorByConf(conf)
34
-
35
- // create paginator dom
36
- const { offset, total } = getPageDataByLastData(ctx)
37
- const $paginator = paginator.create({
38
- ctx, pageSize: conf.pagination.pageSize, total,
39
-
40
- readMoreAutoLoad: conf.pagination.autoLoad,
41
- })
42
-
43
- // mount paginator dom
44
- list.$commentsWrap.after($paginator)
45
-
46
- // update paginator info
47
- paginator?.update(offset, total)
48
- })
49
-
50
- // When list loaded
51
- ctx.on('list-loaded', (comments) => {
52
- // update paginator info
53
- const { offset, total } = getPageDataByLastData(ctx)
54
- paginator?.update(offset, total)
55
- })
56
-
57
- // When list fetch
58
- ctx.on('list-fetch', (params) => {
59
- // if clear comments when fetch new page data
60
- if (ctx.getData().getComments().length > 0 && paginator?.getIsClearComments(params)) {
61
- ctx.getData().clearComments()
62
- }
63
- })
64
-
65
- // When list error
66
- ctx.on('list-error', () => {
67
- paginator?.showErr?.($t('loadFail'))
68
- })
69
-
70
- // List goto auto next page when comment not found
71
- const autoSwitchPageForFindComment = (commentID: number) => {
72
- const comment = ctx.getData().findComment(commentID)
73
- if (!!comment || !paginator?.getHasMore()) return
74
-
75
- // wait for list loaded
76
- ctx.on('list-loaded', () => {
77
- autoSwitchPageForFindComment(commentID) // recursive, until comment found or no more page
78
- }, { once: true })
79
-
80
- // TODO 自动范围改为直接跳转到计算后的页面
81
- setTimeout(() => {
82
- paginator?.next()
83
- }, 80)
84
- }
85
-
86
- ctx.on('list-goto', (commentID) => {
87
- autoSwitchPageForFindComment(commentID)
88
- })
89
-
90
- // loading
91
- ctx.on('list-fetch', (params) => {
92
- paginator?.setLoading(true)
93
- })
94
- ctx.on('list-fetched', ({ params }) => {
95
- paginator?.setLoading(false)
96
- })
97
- }
@@ -1,25 +0,0 @@
1
- import type { ContextApi, ListFetchParams } from '~/types'
2
- import UpDownPaginator from './up-down'
3
- import ReadMorePaginator from './read-more'
4
-
5
- export interface IPgHolderOpt {
6
- ctx: ContextApi
7
- total: number
8
- pageSize: number
9
-
10
- readMoreAutoLoad?: boolean
11
- }
12
-
13
- export interface Paginator {
14
- create(opts: IPgHolderOpt): HTMLElement
15
- setLoading(val: boolean): void
16
- update(offset: number, total: number): void
17
- next(): void
18
- showErr?(msg: string): void
19
- getHasMore(): boolean
20
- /** Clear comments when fetch new page data */
21
- getIsClearComments(params: Partial<ListFetchParams>): boolean
22
- dispose(): void
23
- }
24
-
25
- export default { UpDownPaginator, ReadMorePaginator }