artalk 2.0.4 → 2.1.1

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 (91) hide show
  1. package/README.md +7 -60
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +5996 -1
  4. package/dist/Artalk.iife.js +266 -1
  5. package/dist/Artalk.js +266 -1
  6. package/package.json +3 -4
  7. package/src/api/index.ts +269 -0
  8. package/src/api/request.ts +125 -0
  9. package/src/artalk.ts +153 -0
  10. package/src/components/_component.ts.txt +10 -0
  11. package/src/components/action-btn.ts +169 -0
  12. package/src/components/admin/page-list.ts +194 -0
  13. package/src/components/admin/site-list-floater.ts +98 -0
  14. package/src/components/admin/site-list.ts +289 -0
  15. package/src/components/comment.ts +478 -0
  16. package/src/components/dialog.ts +63 -0
  17. package/src/components/editor-plugs/{_ExamplePlug.ts.txt → _example-plug.ts.txt} +0 -0
  18. package/src/components/editor-plugs/{EditorPlug.ts → editor-plug.ts} +3 -3
  19. package/src/components/editor-plugs/{EmoticonsPlug.less → emoticons-plug.less} +0 -0
  20. package/src/components/editor-plugs/{EmoticonsPlug.ts → emoticons-plug.ts} +9 -9
  21. package/src/components/editor-plugs/{PreviewPlug.less → preview-plug.less} +0 -0
  22. package/src/components/editor-plugs/{PreviewPlug.ts → preview-plug.ts} +10 -10
  23. package/src/components/{Editor.ts → editor.ts} +104 -102
  24. package/src/components/html/comment.html +2 -6
  25. package/src/components/html/list.html +0 -1
  26. package/src/components/html/sidebar.html +18 -4
  27. package/src/components/item-text-editor.ts +136 -0
  28. package/src/components/layer.ts +135 -0
  29. package/src/components/list-lite.ts +507 -0
  30. package/src/components/list.ts +172 -0
  31. package/src/components/pagination.ts +150 -0
  32. package/src/components/read-more-btn.ts +71 -0
  33. package/src/components/sidebar-view.ts +26 -0
  34. package/src/components/sidebar-views/comments-view.ts +66 -0
  35. package/src/components/sidebar-views/pages-view.ts +61 -0
  36. package/src/components/sidebar-views/setting-view.ts +21 -0
  37. package/src/components/sidebar-views/sites-view.ts +42 -0
  38. package/src/components/sidebar-views/transfer-view.ts +218 -0
  39. package/src/components/sidebar.ts +288 -0
  40. package/src/context.ts +42 -0
  41. package/src/default-conf.ts +42 -0
  42. package/src/lib/checker/admin-checker.ts +33 -0
  43. package/src/lib/checker/captcha-checker.ts +38 -0
  44. package/src/lib/checker/index.ts +134 -0
  45. package/src/lib/component.ts +3 -3
  46. package/src/lib/md5.js +6 -0
  47. package/src/lib/ui.ts +64 -74
  48. package/src/lib/user.ts +2 -8
  49. package/src/lib/utils.ts +30 -11
  50. package/src/main.ts +1 -1
  51. package/src/style/_color.less +8 -2
  52. package/src/style/_extend.less +81 -0
  53. package/src/style/_variables.less +3 -0
  54. package/src/style/comment.less +60 -24
  55. package/src/style/editor.less +3 -0
  56. package/src/style/item-text-editor.less +35 -0
  57. package/src/style/list.less +51 -32
  58. package/src/style/main.less +75 -1
  59. package/src/style/page-list.less +50 -0
  60. package/src/style/pagination.less +58 -0
  61. package/src/style/sidebar.less +274 -199
  62. package/src/style/site-list.less +208 -0
  63. package/tsconfig.json +27 -0
  64. package/types/artalk-config.d.ts +68 -51
  65. package/types/artalk-data.d.ts +38 -1
  66. package/types/event.d.ts +73 -0
  67. package/types/index.d.ts +1 -6
  68. package/.github/workflows/npm.yml +0 -31
  69. package/.github/workflows/pages.yml +0 -30
  70. package/dist/src/Event.d.ts +0 -59
  71. package/dist/types/artalk-config.d.ts +0 -79
  72. package/dist/types/artalk-data.d.ts +0 -153
  73. package/dist/types/global.d.ts +0 -1
  74. package/dist/types/index.d.ts +0 -8
  75. package/dist/types/vite-env.d.ts +0 -1
  76. package/example/pjax/res1.html +0 -41
  77. package/example/pjax/res2.html +0 -41
  78. package/src/Artalk.ts +0 -160
  79. package/src/Constant.ts +0 -6
  80. package/src/Context.ts +0 -34
  81. package/src/Event.d.ts +0 -59
  82. package/src/components/Comment.ts +0 -402
  83. package/src/components/Layer.ts +0 -138
  84. package/src/components/List.ts +0 -162
  85. package/src/components/ListLite.ts +0 -407
  86. package/src/components/Sidebar.ts +0 -141
  87. package/src/components/sidebar-views/AdminView.ts +0 -523
  88. package/src/components/sidebar-views/MessageView.ts +0 -48
  89. package/src/components/sidebar-views/SidebarView.ts +0 -24
  90. package/src/lib/api.ts +0 -380
  91. package/src/lib/checker.ts +0 -164
@@ -0,0 +1,478 @@
1
+ import '../style/comment.less'
2
+
3
+ import Context from '../context'
4
+ import Component from '../lib/component'
5
+ import * as Utils from '../lib/utils'
6
+ import * as Ui from '../lib/ui'
7
+ import UADetect from '../lib/detect'
8
+ import { CommentData } from '~/types/artalk-data'
9
+ import CommentHTML from './html/comment.html?raw'
10
+ import Api from '../api'
11
+ import ActionBtn from './action-btn'
12
+
13
+ export default class Comment extends Component {
14
+ public data: CommentData
15
+
16
+ public $main!: HTMLElement
17
+ public $header!: HTMLElement
18
+ public $body!: HTMLElement
19
+ public $content!: HTMLElement
20
+ public $children!: HTMLElement|null
21
+ public $actions!: HTMLElement
22
+ public voteBtnUp?: ActionBtn
23
+ public voteBtnDown?: ActionBtn
24
+
25
+ public parent: Comment|null
26
+ public nestedNum: number
27
+ private maxNestingNum: number // 最多嵌套层数
28
+ public children: Comment[] = []
29
+
30
+ public replyTo?: CommentData // 回复对象(flatMode 用)
31
+ public $replyTo?: HTMLElement
32
+
33
+ public afterRender?: () => void
34
+
35
+ private unread = false
36
+ private openable = false
37
+ private openURL?: string
38
+ public openEvt?: () => void
39
+
40
+ constructor(ctx: Context, data: CommentData) {
41
+ super(ctx)
42
+
43
+ // 最大嵌套数
44
+ this.maxNestingNum = ctx.conf.maxNesting || 3
45
+
46
+ this.data = { ...data }
47
+ this.data.date = this.data.date.replace(/-/g, '/') // 解决 Safari 日期解析 NaN 问题
48
+
49
+ this.parent = null
50
+ this.nestedNum = 1 // 现在已嵌套 n 层
51
+ }
52
+
53
+ /** 渲染 UI */
54
+ public render() {
55
+ this.$el = Utils.createElement(CommentHTML)
56
+ this.$main = this.$el.querySelector('.atk-comment-main')!
57
+ this.$header = this.$el.querySelector('.atk-header')!
58
+ this.$body = this.$el.querySelector('.atk-body')!
59
+ this.$content = this.$body.querySelector('.atk-content')!
60
+ this.$actions = this.$el.querySelector('.atk-comment-actions')!
61
+ this.$children = null
62
+
63
+ this.$el.setAttribute('data-comment-id', `${this.data.id}`)
64
+
65
+ this.renderCheckUnread()
66
+ this.renderCheckClickable()
67
+
68
+ this.renderAvatar()
69
+ this.renderHeader()
70
+ this.renderContent()
71
+ this.renderReplyTo()
72
+ this.renderPending()
73
+ this.renderActionBtn()
74
+
75
+ if (this.afterRender) this.afterRender()
76
+
77
+ return this.$el
78
+ }
79
+
80
+ //#region Renders
81
+ private renderCheckUnread() {
82
+ if (this.unread) this.$el.classList.add('atk-unread')
83
+ else this.$el.classList.remove('atk-unread')
84
+ }
85
+
86
+ private renderCheckClickable() {
87
+ if (this.openable) {
88
+ this.$el.classList.add('atk-openable')
89
+ } else {
90
+ this.$el.classList.remove('atk-openable')
91
+ }
92
+
93
+ this.$el.addEventListener('click', (evt) => {
94
+ if (this.openable && this.openURL) {
95
+ evt.preventDefault()
96
+ window.open(this.openURL)
97
+ }
98
+ if (this.openEvt)
99
+ this.openEvt()
100
+ })
101
+ }
102
+
103
+ private renderAvatar() {
104
+ const $avatar = this.$el.querySelector<HTMLElement>('.atk-avatar')!
105
+ const $avatarImg = Utils.createElement<HTMLImageElement>('<img />')
106
+ $avatarImg.src = this.getGravatarUrl()
107
+ if (this.data.link) {
108
+ const $avatarA = Utils.createElement<HTMLLinkElement>('<a target="_blank"></a>')
109
+ $avatarA.href = this.data.link
110
+ $avatarA.append($avatarImg)
111
+ $avatar.append($avatarA)
112
+ } else {
113
+ $avatar.append($avatarImg)
114
+ }
115
+ }
116
+
117
+ private renderHeader() {
118
+ const $nick = this.$el.querySelector<HTMLElement>('.atk-nick')!
119
+ if (this.data.link) {
120
+ const $nickA = Utils.createElement<HTMLLinkElement>('<a target="_blank"></a>')
121
+ $nickA.innerText = this.data.nick
122
+ $nickA.href = this.data.link
123
+ $nick.append($nickA)
124
+ } else {
125
+ $nick.innerText = this.data.nick
126
+ }
127
+
128
+ const $badge = this.$el.querySelector<HTMLElement>('.atk-badge')!
129
+ if (this.data.badge_name) {
130
+ $badge.innerText = this.data.badge_name
131
+ if (this.data.badge_color)
132
+ $badge.style.backgroundColor = this.data.badge_color
133
+ } else {
134
+ $badge.remove()
135
+ }
136
+
137
+ const $date = this.$el.querySelector<HTMLElement>('.atk-date')!
138
+ $date.innerText = this.getDateFormatted()
139
+ $date.setAttribute('data-atk-comment-date', String(+new Date(this.data.date)))
140
+
141
+ if (this.conf.uaBadge) {
142
+ const $uaWrap = Utils.createElement(`<span class="atk-ua-wrap"></span>`)
143
+ const $uaBrowser = Utils.createElement(`<span class="atk-ua ua-browser"></span>`)
144
+ const $usOS = Utils.createElement(`<span class="atk-ua ua-os"></span>`)
145
+ $uaBrowser.innerText = this.getUserUaBrowser()
146
+ $usOS.innerText = this.getUserUaOS()
147
+ $uaWrap.append($uaBrowser)
148
+ $uaWrap.append($usOS)
149
+ this.$header.append($uaWrap)
150
+ }
151
+ }
152
+
153
+ private renderContent() {
154
+ // 内容 & 折叠
155
+ if (!this.data.is_collapsed) {
156
+ this.$content.innerHTML = this.getContentMarked()
157
+ return
158
+ }
159
+
160
+ this.$content.classList.add('atk-hide', 'atk-type-collapsed')
161
+ const collapsedInfoEl = Utils.createElement(`
162
+ <div class="atk-collapsed">
163
+ <span class="atk-text">该评论已被系统或管理员折叠</span>
164
+ <span class="atk-show-btn">查看内容</span>
165
+ </div>`)
166
+ this.$body.insertAdjacentElement('beforeend', collapsedInfoEl)
167
+
168
+ const contentShowBtn = collapsedInfoEl.querySelector('.atk-show-btn')!
169
+ contentShowBtn.addEventListener('click', (e) => {
170
+ e.stopPropagation() // 防止穿透
171
+
172
+ if (this.$content.classList.contains('atk-hide')) {
173
+ this.$content.innerHTML = this.getContentMarked()
174
+ this.$content.classList.remove('atk-hide')
175
+ Ui.playFadeInAnim(this.$content)
176
+ contentShowBtn.innerHTML = '收起内容'
177
+ } else {
178
+ this.$content.innerHTML = ''
179
+ this.$content.classList.add('atk-hide')
180
+ contentShowBtn.innerHTML = '查看内容'
181
+ }
182
+ })
183
+ }
184
+
185
+ // 回复的对象
186
+ private renderReplyTo() {
187
+ if (!this.replyTo) return
188
+
189
+ this.$replyTo = Utils.createElement(`
190
+ <div class="atk-reply-to">
191
+ <div class="atk-meta">回复 <span class="atk-nick"></span>:</div>
192
+ <div class="atk-content"></div>
193
+ </div>`)
194
+ this.$replyTo.querySelector<HTMLElement>('.atk-nick')!.innerText = `@${this.replyTo.nick}`
195
+ let replyContent = Utils.marked(this.ctx, this.replyTo.content)
196
+ if (this.replyTo.is_collapsed) replyContent = '[已折叠]'
197
+ this.$replyTo.querySelector<HTMLElement>('.atk-content')!.innerHTML = replyContent
198
+ this.$body.prepend(this.$replyTo)
199
+ }
200
+
201
+ // 待审核状态
202
+ private renderPending() {
203
+ if (!this.data.is_pending) return
204
+
205
+ const pendingEl = Utils.createElement(`<div class="atk-pending">审核中,仅本人可见。</div>`)
206
+ this.$body.prepend(pendingEl)
207
+ }
208
+
209
+ /** 初始化评论操作按钮 */
210
+ private renderActionBtn() {
211
+ // 投票功能
212
+ if (this.ctx.conf.vote) {
213
+ // 赞同按钮
214
+ this.voteBtnUp = new ActionBtn(() => `赞同 (${this.data.vote_up || 0})`).appendTo(this.$actions)
215
+ this.voteBtnUp.setClick(() => {
216
+ this.vote('up')
217
+ })
218
+
219
+ // 反对按钮
220
+ if (this.ctx.conf.voteDown) {
221
+ this.voteBtnDown = new ActionBtn(() => `反对 (${this.data.vote_down || 0})`).appendTo(this.$actions)
222
+ this.voteBtnDown.setClick(() => {
223
+ this.vote('down')
224
+ })
225
+ }
226
+ }
227
+
228
+ // 绑定回复按钮事件
229
+ if (this.data.is_allow_reply) {
230
+ const replyBtn = Utils.createElement(`<span data-atk-action="comment-reply">回复</span>`)
231
+ this.$actions.append(replyBtn)
232
+ replyBtn.addEventListener('click', (e) => {
233
+ e.stopPropagation() // 防止穿透
234
+ this.ctx.trigger('editor-reply', this.data)
235
+ })
236
+ }
237
+
238
+ // 绑定折叠按钮事件
239
+ const collapseBtn = new ActionBtn({
240
+ text: () => (this.data.is_collapsed ? '取消折叠' : '折叠'),
241
+ adminOnly: true
242
+ })
243
+ collapseBtn.appendTo(this.$actions)
244
+ collapseBtn.setClick(() => {
245
+ this.adminEdit('collapsed', collapseBtn)
246
+ })
247
+
248
+ // 绑定待审核按钮事件
249
+ const pendingBtn = new ActionBtn({
250
+ text: () => (this.data.is_pending ? '待审' : '已审'),
251
+ adminOnly: true
252
+ })
253
+ pendingBtn.appendTo(this.$actions)
254
+ pendingBtn.setClick(() => {
255
+ this.adminEdit('pending', pendingBtn)
256
+ })
257
+
258
+ // 绑定删除按钮事件
259
+ const delBtn = new ActionBtn({
260
+ text: '删除',
261
+ confirm: true,
262
+ confirmText: '确认删除',
263
+ adminOnly: true,
264
+ })
265
+ delBtn.appendTo(this.$actions)
266
+ delBtn.setClick(() => {
267
+ this.adminDelete(delBtn)
268
+ })
269
+ }
270
+ //#endregion
271
+
272
+ /** 刷新评论 UI */
273
+ public refreshUI() {
274
+ const originalEl = this.$el
275
+ const newEl = this.render()
276
+ originalEl.replaceWith(newEl) // 替换 document 中的的 elem
277
+ this.playFadeInAnim()
278
+
279
+ // 重建子评论元素
280
+ this.eachComment(this.children, (child) => {
281
+ child.parent?.getChildrenEl().appendChild(child.render())
282
+ child.playFadeInAnim()
283
+ })
284
+
285
+ this.ctx.trigger('comments-loaded')
286
+ }
287
+
288
+ /** 遍历评论 */
289
+ private eachComment(commentList: Comment[], action: (comment: Comment, levelList: Comment[]) => boolean|void) {
290
+ if (commentList.length === 0) return
291
+ commentList.every((item) => {
292
+ if (action(item, commentList) === false) return false
293
+ this.eachComment(item.getChildren(), action)
294
+ return true
295
+ })
296
+ }
297
+
298
+ getIsRoot() {
299
+ return this.parent === null
300
+ }
301
+
302
+ getChildren() {
303
+ return this.children
304
+ }
305
+
306
+ putChild(childC: Comment) {
307
+ childC.parent = this
308
+ childC.nestedNum = this.nestedNum + 1 // 嵌套层数 +1
309
+ this.children.push(childC)
310
+
311
+ this.getChildrenEl().appendChild(childC.getEl())
312
+ childC.playFadeInAnim()
313
+ }
314
+
315
+ getChildrenEl() {
316
+ if (this.$children === null) {
317
+ // console.log(this.nestedNum)
318
+ if (this.nestedNum < this.maxNestingNum) {
319
+ this.$children = Utils.createElement('<div class="atk-comment-children"></div>')
320
+ this.$main.appendChild(this.$children)
321
+ } else if (this.parent) {
322
+ this.$children = this.parent.getChildrenEl()
323
+ }
324
+ }
325
+
326
+ return this.$children
327
+ }
328
+
329
+ getParent() {
330
+ return this.parent
331
+ }
332
+
333
+ getEl() {
334
+ return this.$el
335
+ }
336
+
337
+ getData() {
338
+ return this.data
339
+ }
340
+
341
+ getGravatarUrl() {
342
+ return Utils.getGravatarURL(this.ctx, this.data.email_encrypted)
343
+ }
344
+
345
+ getContentMarked() {
346
+ return Utils.marked(this.ctx, this.data.content)
347
+ }
348
+
349
+ getDateFormatted() {
350
+ return Utils.timeAgo(new Date(this.data.date))
351
+ }
352
+
353
+ getUserUaBrowser() {
354
+ const info = UADetect(this.data.ua)
355
+ return `${info.browser} ${info.version}`
356
+ }
357
+
358
+ getUserUaOS() {
359
+ const info = UADetect(this.data.ua)
360
+ return `${info.os} ${info.osVersion}`
361
+ }
362
+
363
+ /** 渐出动画 */
364
+ playFadeInAnim() {
365
+ Ui.playFadeInAnim(this.$el)
366
+ }
367
+
368
+ /** 投票操作 */
369
+ vote(type: 'up'|'down') {
370
+ const actionBtn = type === 'up' ? this.voteBtnUp : this.voteBtnDown
371
+
372
+ new Api(this.ctx).vote(this.data.id, `comment_${type}`)
373
+ .then((v) => {
374
+ this.data.vote_up = v.up
375
+ this.data.vote_down = v.down
376
+ this.voteBtnUp?.updateText()
377
+ this.voteBtnDown?.updateText()
378
+ })
379
+ .catch((err) => {
380
+ actionBtn?.setError(`投票失败`)
381
+ console.log(err)
382
+ })
383
+ }
384
+
385
+ /** 管理员 - 评论折叠 */
386
+ adminEdit(type: 'collapsed'|'pending', btnElem: ActionBtn) {
387
+ if (btnElem.isLoading) return // 若正在折叠中
388
+
389
+ btnElem.setLoading(true, '修改中...')
390
+
391
+ // 克隆并修改当前数据
392
+ const modify = { ...this.data }
393
+ if (type === 'collapsed') {
394
+ modify.is_collapsed = !modify.is_collapsed
395
+ } else if (type === 'pending') {
396
+ modify.is_pending = !modify.is_pending
397
+ }
398
+
399
+ new Api(this.ctx).commentEdit(modify).then((comment) => {
400
+ btnElem.setLoading(false)
401
+
402
+ // 刷新当前 Comment UI
403
+ this.data = comment
404
+ this.refreshUI()
405
+ Ui.playFadeInAnim(this.$body)
406
+
407
+ // 刷新 List UI
408
+ this.ctx.trigger('list-refresh-ui')
409
+ }).catch((err) => {
410
+ console.error(err)
411
+ btnElem.setError('修改失败')
412
+ })
413
+ }
414
+
415
+ public onDelete?: (comment: Comment) => void
416
+
417
+ /** 管理员 - 评论删除 */
418
+ adminDelete(btnElem: ActionBtn) {
419
+ if (btnElem.isLoading) return // 若正在删除中
420
+
421
+ btnElem.setLoading(true, '删除中...')
422
+ new Api(this.ctx).commentDel(this.data.id, this.data.site_name)
423
+ .then(() => {
424
+ btnElem.setLoading(false)
425
+ if (this.onDelete) this.onDelete(this)
426
+ })
427
+ .catch((e) => {
428
+ console.error(e)
429
+ btnElem.setError('删除失败')
430
+ })
431
+ }
432
+
433
+ public setUnread(val: boolean) {
434
+ this.unread = val
435
+ if (this.unread) this.$el.classList.add('atk-unread')
436
+ else this.$el.classList.remove('atk-unread')
437
+ }
438
+
439
+ public setOpenURL(url: string) {
440
+ if (!url) {
441
+ this.openable = false
442
+ this.$el.classList.remove('atk-openable')
443
+ }
444
+
445
+ this.openable = true
446
+ this.openURL = url
447
+ this.$el.classList.add('atk-openable')
448
+ }
449
+
450
+ /** 内容过多,折叠显示 */
451
+ public checkMoreHide($target: HTMLElement|null, allowHeight = 300) {
452
+ if (!$target) return
453
+
454
+ let $hideMoreOpenBtn = $target?.querySelector<HTMLElement>('.atk-more-hide-open-btn')
455
+
456
+ const removeHideMore = () => {
457
+ $target.classList.remove('atk-comment-more-hide')
458
+ if ($hideMoreOpenBtn) $hideMoreOpenBtn.remove()
459
+ $target.style.height = ''
460
+ $target.style.overflow = ''
461
+ }
462
+
463
+ if (Utils.getHeight($target) > allowHeight) {
464
+ //console.log('内容过多,需要折叠', $target)
465
+ $target.classList.add('atk-comment-more-hide')
466
+ $target.style.height = `${allowHeight}px`
467
+ $target.style.overflow = 'hidden'
468
+ if (!$hideMoreOpenBtn) {
469
+ $hideMoreOpenBtn = Utils.createElement(`<div class="atk-more-hide-open-btn">阅读更多</span>`)
470
+ $hideMoreOpenBtn.onclick = (e) => {
471
+ e.stopPropagation()
472
+ removeHideMore()
473
+ }
474
+ $target.append($hideMoreOpenBtn)
475
+ }
476
+ }
477
+ }
478
+ }
@@ -0,0 +1,63 @@
1
+ import * as Utils from '../lib/utils'
2
+
3
+ type BtnClickHandler = (btnEl: HTMLElement, dialog: Dialog) => boolean|void
4
+
5
+ /**
6
+ * 对话框
7
+ */
8
+ export default class Dialog {
9
+ public $el: HTMLElement
10
+ public $content: HTMLElement
11
+ public $actions: HTMLElement
12
+
13
+ constructor (contentEl: HTMLElement) {
14
+ this.$el = Utils.createElement(
15
+ `<div class="atk-layer-dialog-wrap">
16
+ <div class="atk-layer-dialog">
17
+ <div class="atk-layer-dialog-content"></div>
18
+ <div class="atk-layer-dialog-actions"></div>
19
+ </div>
20
+ </div>`
21
+ )
22
+
23
+ // 按钮
24
+ this.$actions = this.$el.querySelector<HTMLElement>('.atk-layer-dialog-actions')!
25
+
26
+ // 内容
27
+ this.$content = this.$el.querySelector('.atk-layer-dialog-content')!
28
+ this.$content.appendChild(contentEl)
29
+
30
+ return this
31
+ }
32
+
33
+ /** 按钮 · 确定 */
34
+ public setYes(handler: BtnClickHandler) {
35
+ const btn = Utils.createElement<HTMLButtonElement>(
36
+ '<button data-action="confirm">确定</button>'
37
+ )
38
+ btn.onclick = this.onBtnClick(handler)
39
+ this.$actions.appendChild(btn)
40
+
41
+ return this
42
+ }
43
+
44
+ /** 按钮 · 取消 */
45
+ public setNo(handler: BtnClickHandler) {
46
+ const btn = Utils.createElement<HTMLButtonElement>(
47
+ '<button data-action="cancel">取消</button>'
48
+ )
49
+ btn.onclick = this.onBtnClick(handler)
50
+ this.$actions.appendChild(btn)
51
+
52
+ return this
53
+ }
54
+
55
+ private onBtnClick(handler: BtnClickHandler) {
56
+ return (evt: Event) => {
57
+ const re = handler(evt.currentTarget as HTMLElement, this)
58
+ if (re === undefined || re === true) {
59
+ this.$el.remove()
60
+ }
61
+ }
62
+ }
63
+ }
@@ -1,10 +1,10 @@
1
- import Context from "~/src/Context"
2
- import Editor from "../Editor"
1
+ import Context from "~/src/context"
2
+ import Editor from "../editor"
3
3
 
4
4
  export default abstract class EditorPlug {
5
5
  protected editor: Editor
6
6
  protected ctx: Context
7
- public abstract el: HTMLElement
7
+ public abstract $el: HTMLElement
8
8
 
9
9
  constructor (editor: Editor) {
10
10
  this.editor = editor
@@ -1,7 +1,7 @@
1
- import './EmoticonsPlug.less'
1
+ import './emoticons-plug.less'
2
2
 
3
- import Editor from '../Editor'
4
- import EditorPlug from './EditorPlug'
3
+ import Editor from '../editor'
4
+ import EditorPlug from './editor-plug'
5
5
  import * as Utils from '~/src/lib/utils'
6
6
 
7
7
  type EmoticonListType = {
@@ -12,7 +12,7 @@ type EmoticonListType = {
12
12
  }
13
13
 
14
14
  export default class EmoticonsPlug extends EditorPlug {
15
- public el!: HTMLElement
15
+ public $el!: HTMLElement
16
16
  public emoticons: EmoticonListType
17
17
 
18
18
  public listWrapEl!: HTMLElement
@@ -26,11 +26,11 @@ export default class EmoticonsPlug extends EditorPlug {
26
26
  }
27
27
 
28
28
  initEl () {
29
- this.el = Utils.createElement(`<div class="atk-editor-plug-emoticons"></div>`)
29
+ this.$el = Utils.createElement(`<div class="atk-editor-plug-emoticons"></div>`)
30
30
 
31
31
  // 表情列表
32
32
  this.listWrapEl = Utils.createElement(`<div class="atk-emoticons-list-wrap"></div>`)
33
- this.el.append(this.listWrapEl)
33
+ this.$el.append(this.listWrapEl)
34
34
 
35
35
  Object.entries(this.emoticons).forEach(([key, item]) => {
36
36
  const emoticonsEl = Utils.createElement(`<div class="atk-emoticons-list" style="display: none;"></div>`)
@@ -55,7 +55,7 @@ export default class EmoticonsPlug extends EditorPlug {
55
55
 
56
56
  // 表情分类
57
57
  this.typesEl = Utils.createElement(`<div class="atk-emoticons-types"></div>`)
58
- this.el.append(this.typesEl)
58
+ this.$el.append(this.typesEl)
59
59
  Object.entries(this.emoticons).forEach(([key, item]) => {
60
60
  const itemEl = Utils.createElement('<span />')
61
61
  this.typesEl.append(itemEl)
@@ -118,7 +118,7 @@ export default class EmoticonsPlug extends EditorPlug {
118
118
  }
119
119
 
120
120
  getEl () {
121
- return this.el
121
+ return this.$el
122
122
  }
123
123
 
124
124
  changeListHeight () {
@@ -134,7 +134,7 @@ export default class EmoticonsPlug extends EditorPlug {
134
134
  }
135
135
 
136
136
  onHide () {
137
- this.el.parentElement!.style.height = ''
137
+ this.$el.parentElement!.style.height = ''
138
138
  }
139
139
 
140
140
  public transEmoticonImageText (text: string) {
@@ -1,11 +1,11 @@
1
- import './PreviewPlug.less'
1
+ import './preview-plug.less'
2
2
 
3
- import Editor from '../Editor'
4
- import EditorPlug from './EditorPlug'
3
+ import Editor from '../editor'
4
+ import EditorPlug from './editor-plug'
5
5
  import * as Utils from '~/src/lib/utils'
6
6
 
7
7
  export default class PreviewPlug extends EditorPlug {
8
- el!: HTMLElement
8
+ $el!: HTMLElement
9
9
  binded: boolean = false
10
10
 
11
11
  constructor (editor: Editor) {
@@ -15,7 +15,7 @@ export default class PreviewPlug extends EditorPlug {
15
15
  }
16
16
 
17
17
  initEl () {
18
- this.el = Utils.createElement('<div class="atk-editor-plug-preview"></div>')
18
+ this.$el = Utils.createElement('<div class="atk-editor-plug-preview"></div>')
19
19
  this.binded = false
20
20
  }
21
21
 
@@ -28,7 +28,7 @@ export default class PreviewPlug extends EditorPlug {
28
28
  }
29
29
 
30
30
  getEl () {
31
- return this.el
31
+ return this.$el
32
32
  }
33
33
 
34
34
  onShow () {
@@ -37,8 +37,8 @@ export default class PreviewPlug extends EditorPlug {
37
37
  const event = () => {
38
38
  this.updateContent()
39
39
  }
40
- this.editor.textareaEl.addEventListener('input', event)
41
- this.editor.textareaEl.addEventListener('change', event)
40
+ this.editor.$textarea.addEventListener('input', event)
41
+ this.editor.$textarea.addEventListener('change', event)
42
42
  this.binded = true
43
43
  }
44
44
  }
@@ -46,8 +46,8 @@ export default class PreviewPlug extends EditorPlug {
46
46
  onHide () {}
47
47
 
48
48
  updateContent () {
49
- if (this.el.style.display !== 'none') {
50
- this.el.innerHTML = this.editor.getContentMarked()
49
+ if (this.$el.style.display !== 'none') {
50
+ this.$el.innerHTML = this.editor.getContentMarked()
51
51
  }
52
52
  }
53
53
  }