artalk 2.2.12 → 2.3.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 (78) hide show
  1. package/dist/Artalk.css +1 -1
  2. package/dist/Artalk.es.js +1535 -1175
  3. package/dist/Artalk.iife.js +1 -1
  4. package/dist/Artalk.js +1 -1
  5. package/dist/ArtalkLite.css +1 -0
  6. package/dist/ArtalkLite.es.js +5123 -0
  7. package/dist/ArtalkLite.iife.js +1 -0
  8. package/dist/ArtalkLite.js +1 -0
  9. package/index.html +2 -2
  10. package/package.json +6 -5
  11. package/src/api/index.ts +20 -1
  12. package/src/api/request.ts +7 -7
  13. package/src/artalk.ts +117 -121
  14. package/src/comment/actions.ts +86 -0
  15. package/src/{components/html → comment}/comment.html +1 -1
  16. package/src/comment/comment.ts +191 -0
  17. package/src/comment/index.ts +3 -0
  18. package/src/comment/render.ts +425 -0
  19. package/src/components/action-btn.ts +6 -3
  20. package/src/components/dialog.ts +7 -3
  21. package/src/components/read-more-btn.ts +6 -2
  22. package/src/context.ts +204 -13
  23. package/src/defaults.ts +16 -5
  24. package/src/editor/editor.html +18 -0
  25. package/src/editor/editor.ts +419 -0
  26. package/src/editor/index.ts +3 -0
  27. package/src/editor/plugs/editor-plug.ts +51 -0
  28. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.less +2 -1
  29. package/src/{components/editor-plugs → editor/plugs}/emoticons-plug.ts +65 -63
  30. package/src/editor/plugs/header-input-plug.ts +78 -0
  31. package/src/{components/editor-plugs → editor/plugs}/preview-plug.less +0 -0
  32. package/src/editor/plugs/preview-plug.ts +40 -0
  33. package/src/editor/plugs/upload-plug.ts +124 -0
  34. package/src/i18n/en.ts +78 -0
  35. package/src/i18n/index.ts +21 -0
  36. package/src/i18n/zh-CN.ts +82 -0
  37. package/src/layer/index.ts +4 -0
  38. package/src/{components → layer}/layer.ts +9 -9
  39. package/src/{components/html → layer}/sidebar-layer.html +0 -0
  40. package/src/{components → layer}/sidebar-layer.ts +17 -47
  41. package/src/lib/checker/admin-checker.ts +7 -7
  42. package/src/lib/checker/captcha-checker.ts +5 -6
  43. package/src/lib/checker/index.ts +22 -17
  44. package/src/lib/component.ts +7 -2
  45. package/src/lib/stat.ts +63 -0
  46. package/src/lib/ui.ts +6 -12
  47. package/src/lib/user.ts +20 -3
  48. package/src/lib/utils.ts +23 -9
  49. package/src/list/index.ts +3 -0
  50. package/src/{components → list}/list-lite.ts +80 -100
  51. package/src/{components → list}/list-nest.ts +0 -0
  52. package/src/{components/html → list}/list.html +3 -6
  53. package/src/{components → list}/list.ts +44 -42
  54. package/src/style/_variables.less +1 -2
  55. package/src/style/comment.less +26 -13
  56. package/src/style/editor.less +22 -14
  57. package/src/style/list.less +6 -9
  58. package/src/style/main.less +9 -8
  59. package/src/style/pagination.less +1 -1
  60. package/test/ArtalkCount.html +16 -0
  61. package/test/ArtalkI18n.html +15 -0
  62. package/test/ArtalkLite.html +15 -0
  63. package/types/artalk-config.d.ts +50 -32
  64. package/types/context.d.ts +128 -0
  65. package/types/event.d.ts +6 -66
  66. package/types/global.d.ts +1 -0
  67. package/types/plug.d.ts +4 -0
  68. package/{vite-common.config.ts → vite-base.config.ts} +6 -4
  69. package/vite-demo.config.ts +2 -4
  70. package/vite-lite.config.ts +22 -0
  71. package/vite.config.ts +9 -12
  72. package/src/components/_component.ts.txt +0 -10
  73. package/src/components/comment.ts +0 -605
  74. package/src/components/editor-plugs/_example-plug.ts.txt +0 -29
  75. package/src/components/editor-plugs/editor-plug.ts +0 -20
  76. package/src/components/editor-plugs/preview-plug.ts +0 -48
  77. package/src/components/editor.ts +0 -607
  78. package/src/components/html/editor.html +0 -18
@@ -5,6 +5,8 @@ export interface ReadMoreBtnConf {
5
5
  onClick: (offset: number) => void
6
6
 
7
7
  pageSize: number
8
+
9
+ text?: string
8
10
  }
9
11
 
10
12
  /**
@@ -17,6 +19,7 @@ export default class ReadMoreBtn {
17
19
  private $text: HTMLElement
18
20
  private offset: number = 0
19
21
  private total: number = 0
22
+ private origText: string = 'Load More'
20
23
 
21
24
  /** 是否有更多内容 */
22
25
  get hasMore() {
@@ -25,12 +28,13 @@ export default class ReadMoreBtn {
25
28
 
26
29
  public constructor(conf: ReadMoreBtnConf) {
27
30
  this.conf = conf
31
+ this.origText = this.conf.text || this.origText
28
32
 
29
33
  this.$el = Utils.createElement(
30
34
  `<div class="atk-list-read-more" style="display: none;">
31
35
  <div class="atk-list-read-more-inner">
32
36
  <div class="atk-loading-icon" style="display: none;"></div>
33
- <span class="atk-text">加载更多</span>
37
+ <span class="atk-text">${this.origText}</span>
34
38
  </div>
35
39
  </div>`)
36
40
 
@@ -68,7 +72,7 @@ export default class ReadMoreBtn {
68
72
  this.$text.innerText = errMsg
69
73
  this.$el.classList.add('atk-err')
70
74
  window.setTimeout(() => {
71
- this.$text.innerText = '查看更多'
75
+ this.$text.innerText = this.origText
72
76
  this.$el.classList.remove('atk-err')
73
77
  }, 2000) // 2s后错误提示复原
74
78
  }
package/src/context.ts CHANGED
@@ -1,46 +1,237 @@
1
1
  import { marked as libMarked } from 'marked'
2
2
  import ArtalkConfig from '~/types/artalk-config'
3
- import { EventPayloadMap, Event, EventScopeType, Handler } from '~/types/event'
3
+ import { CommentData, NotifyData } from '~/types/artalk-data'
4
+ import { Event } from '~/types/event'
5
+ import { internal as internalLocales, I18n } from './i18n'
4
6
  import User from './lib/user'
7
+ import ContextApi from '../types/context'
8
+ import Editor from './editor'
9
+ import Comment from './comment'
10
+ import ListLite from './list/list-lite'
11
+ import SidebarLayer, { SidebarShowPayload } from './layer/sidebar-layer'
12
+ import CheckerLauncher, { CheckerCaptchaPayload, CheckerPayload } from './lib/checker'
13
+ import Layer, { GetLayerWrap } from './layer'
14
+ import Api from './api'
5
15
 
6
16
  /**
7
17
  * Artalk Context
8
18
  */
9
- export default class Context {
19
+ export default class Context implements ContextApi {
20
+ /* 持有同事类 */
21
+ private api!: Api
22
+ private editor!: Editor
23
+ private list!: ListLite
24
+ private sidebarLayer!: SidebarLayer
25
+ private checkerLauncher!: CheckerLauncher
26
+
27
+ /* 运行参数 */
10
28
  public cid: number // Context 唯一标识
11
- public $root: HTMLElement
12
29
  public conf: ArtalkConfig
13
30
  public user: User
31
+ public $root: HTMLElement
32
+ public markedInstance?: typeof libMarked
33
+ public markedReplacers: ((raw: string) => string)[] = []
34
+
35
+ private commentList: Comment[] = [] // Note: 无层级结构 + 无须排列
14
36
 
37
+ /* 订阅者模式 */
15
38
  private eventList: Event[] = []
16
39
 
17
- public constructor (rootEl: HTMLElement, conf: ArtalkConfig) {
40
+ public constructor(conf: ArtalkConfig, $root?: HTMLElement) {
18
41
  this.cid = +new Date()
19
- this.$root = rootEl
20
42
  this.conf = conf
21
- this.user = new User(this.conf)
43
+ this.user = new User(this)
22
44
 
45
+ this.$root = $root || document.createElement('div')
23
46
  this.$root.setAttribute('atk-run-id', this.cid.toString())
47
+ this.$root.classList.add('artalk')
48
+ this.$root.innerHTML = ''
49
+
50
+ this.api = new Api(this)
51
+ }
52
+
53
+ /* 设置持有的同事类 */
54
+ public setApi(api: Api): void {
55
+ this.api = api
56
+ }
57
+
58
+ public setEditor(editor: Editor): void {
59
+ this.editor = editor
60
+ }
61
+
62
+ public setList(list: ListLite): void {
63
+ this.list = list
64
+ }
65
+
66
+ public setSidebarLayer(sidebarLayer: SidebarLayer): void {
67
+ this.sidebarLayer = sidebarLayer
68
+ }
69
+
70
+ public setCheckerLauncher(checkerLauncher: CheckerLauncher): void {
71
+ this.checkerLauncher = checkerLauncher
72
+ }
73
+
74
+ public getApi() {
75
+ return this.api
76
+ }
77
+
78
+ /* 评论操作 */
79
+ public getCommentList() {
80
+ return this.commentList
81
+ }
82
+
83
+ public getCommentDataList() {
84
+ return this.commentList.map(c => c.getData())
85
+ }
86
+
87
+ public findComment(id: number): Comment|undefined {
88
+ return this.commentList.find(c => c.getData().id === id)
89
+ }
90
+
91
+ public deleteComment(_comment: number|Comment) {
92
+ let comment: Comment
93
+ if (typeof _comment === 'number') {
94
+ const findComment = this.findComment(_comment)
95
+ if (!findComment) throw Error(`Comment ${_comment} cannot be found`)
96
+ comment = findComment
97
+ } else comment = _comment
98
+
99
+ comment.getEl().remove()
100
+ this.commentList.splice(this.commentList.indexOf(comment), 1)
101
+
102
+ const listData = this.list.getData()
103
+ if (listData) listData.total -= 1 // 评论数减 1
104
+
105
+ this.list.refreshUI()
106
+ }
107
+
108
+ public clearAllComments() {
109
+ this.list.getCommentsWrapEl().innerHTML = ''
110
+ this.list.clearData()
111
+ this.commentList = []
112
+ }
113
+
114
+ public insertComment(commentData: CommentData) {
115
+ this.list.insertComment(commentData)
116
+ }
117
+
118
+ public replyComment(commentData: CommentData, $comment: HTMLElement, scroll?: boolean): void {
119
+ this.editor.setReply(commentData, $comment, scroll)
120
+ }
121
+
122
+ public cancelReplyComment(): void {
123
+ this.editor.cancelReply()
124
+ }
125
+
126
+ public updateNotifies(notifies: NotifyData[]): void {
127
+ this.list.updateUnread(notifies)
128
+ }
129
+
130
+ /* 评论列表 */
131
+ public listReload(): void {
132
+ this.list.reload()
133
+ }
134
+
135
+ public listRefreshUI(): void {
136
+ this.list.refreshUI()
137
+ }
138
+
139
+ /* 编辑器 */
140
+ public editorOpen(): void {
141
+ this.editor.open()
142
+ }
143
+
144
+ public editorClose(): void {
145
+ this.editor.close()
146
+ }
147
+
148
+ public editorShowLoading(): void {
149
+ this.editor.showLoading()
150
+ }
151
+
152
+ public editorHideLoading(): void {
153
+ this.editor.hideLoading()
154
+ }
155
+
156
+ public editorShowNotify(msg, type): void {
157
+ this.editor.showNotify(msg, type)
158
+ }
159
+
160
+ public editorTravel($el: HTMLElement): void {
161
+ this.editor.travel($el)
162
+ }
163
+
164
+ public editorTravelBack(): void {
165
+ this.editor.travelBack()
24
166
  }
25
167
 
26
- public on<K extends keyof EventPayloadMap>(name: K, handler: Handler<EventPayloadMap[K]>, scope: EventScopeType = 'internal') {
27
- this.eventList.push({ name, handler: handler as any, scope })
168
+ /* 侧边栏 */
169
+ public showSidebar(payload?: SidebarShowPayload): void {
170
+ this.sidebarLayer.show(payload)
28
171
  }
29
172
 
30
- public off<K extends keyof EventPayloadMap>(name: K, handler?: Handler<EventPayloadMap[K]>, scope: EventScopeType = 'internal') {
173
+ public hideSidebar(): void {
174
+ this.sidebarLayer.hide()
175
+ }
176
+
177
+ /* 权限检测 */
178
+ public checkAdmin(payload: CheckerPayload): void {
179
+ this.checkerLauncher.checkAdmin(payload)
180
+ }
181
+
182
+ public checkCaptcha(payload: CheckerCaptchaPayload): void {
183
+ this.checkerLauncher.checkCaptcha(payload)
184
+ }
185
+
186
+ public checkAdminShowEl() {
187
+ const items: HTMLElement[] = []
188
+
189
+ this.$root.querySelectorAll<HTMLElement>(`[atk-only-admin-show]`).forEach(item => items.push(item))
190
+
191
+ // for layer
192
+ const { $wrap: $layerWrap } = GetLayerWrap(this)
193
+ if ($layerWrap) $layerWrap.querySelectorAll<HTMLElement>(`[atk-only-admin-show]`).forEach(item => items.push(item))
194
+
195
+ // for sidebar
196
+ // TODO: 这个其实应该写在 packages/artalk-sidebar 里面的
197
+ const $sidebarEl = document.querySelector<HTMLElement>('.atk-sidebar')
198
+ if ($sidebarEl) $sidebarEl.querySelectorAll<HTMLElement>(`[atk-only-admin-show]`).forEach(item => items.push(item))
199
+
200
+ items.forEach(($item: HTMLElement) => {
201
+ if (this.user.data.isAdmin) $item.classList.remove('atk-hide')
202
+ else $item.classList.add('atk-hide')
203
+ })
204
+ }
205
+
206
+ /* 订阅模式 */
207
+ public on(name: any, handler: any, scope: any = 'internal') {
208
+ this.eventList.push({ name, handler, scope })
209
+ }
210
+
211
+ public off(name: any, handler: any, scope: any = 'internal') {
31
212
  this.eventList = this.eventList.filter((evt) => {
32
213
  if (handler) return !(evt.name === name && evt.handler === handler && evt.scope === scope)
33
214
  return !(evt.name === name && evt.scope === scope) // 删除全部相同 name event
34
215
  })
35
216
  }
36
217
 
37
- public trigger<K extends keyof EventPayloadMap>(name: K, payload?: EventPayloadMap[K], scope?: EventScopeType) {
218
+ public trigger(name: any, payload?: any, scope?: any) {
38
219
  this.eventList
39
220
  .filter((evt) => evt.name === name && (scope ? (evt.scope === scope) : true))
40
221
  .map((evt) => evt.handler)
41
- .forEach((handler) => handler(payload as any))
222
+ .forEach((handler) => handler(payload))
42
223
  }
43
224
 
44
- public markedInstance!: typeof libMarked
45
- public markedReplacers: ((raw: string) => string)[] = []
225
+ /* i18n */
226
+ public $t(key: keyof I18n, args: {[key: string]: string} = {}): string {
227
+ let locales = this.conf.locale
228
+ if (typeof locales === 'string') {
229
+ locales = internalLocales[locales]
230
+ }
231
+
232
+ let str = locales?.[key] || key
233
+ str = str.replace(/\{\s*(\w+?)\s*\}/g, (_, token) => args[token] || '')
234
+
235
+ return str
236
+ }
46
237
  }
package/src/defaults.ts CHANGED
@@ -1,15 +1,16 @@
1
- import ArtalkConfig from "~/types/artalk-config";
1
+ import ArtalkConfig from "~/types/artalk-config"
2
2
 
3
3
  const defaults: ArtalkConfig = {
4
4
  el: '',
5
5
  pageKey: '',
6
+ pageTitle: '',
6
7
  server: '',
7
8
  site: '',
8
9
 
9
- placeholder: '键入内容...',
10
- noComment: '「此时无声胜有声」',
11
- sendBtn: '发送评论',
12
- darkMode: 'auto',
10
+ placeholder: '',
11
+ noComment: '',
12
+ sendBtn: '',
13
+ darkMode: false,
13
14
  editorTravel: true,
14
15
 
15
16
  flatMode: 'auto',
@@ -22,6 +23,8 @@ const defaults: ArtalkConfig = {
22
23
  voteDown: false,
23
24
  uaBadge: true,
24
25
  listSort: true,
26
+ preview: true,
27
+ countEl: '#ArtalkCount',
25
28
  pvEl: '#ArtalkPV',
26
29
 
27
30
  gravatar: {
@@ -44,6 +47,14 @@ const defaults: ArtalkConfig = {
44
47
  reqTimeout: 15000,
45
48
  versionCheck: true,
46
49
  useBackendConf: false,
50
+
51
+ locale: 'zh-CN',
52
+ }
53
+
54
+ if (ARTALK_LITE) {
55
+ defaults.vote = false
56
+ defaults.uaBadge = false
57
+ defaults.emoticons = false
47
58
  }
48
59
 
49
60
  export default defaults
@@ -0,0 +1,18 @@
1
+ <div class="atk-main-editor">
2
+ <div class="atk-header">
3
+ <input name="nick" class="atk-nick" type="text" required="required">
4
+ <input name="email" class="atk-email" type="email" required="required">
5
+ <input name="link" class="atk-link" type="url">
6
+ </div>
7
+ <div class="atk-textarea-wrap">
8
+ <textarea class="atk-textarea"></textarea>
9
+ </div>
10
+ <div class="atk-plug-panel-wrap" style="display: none;"></div>
11
+ <div class="atk-bottom">
12
+ <div class="atk-item atk-plug-btn-wrap"></div>
13
+ <div class="atk-item">
14
+ <button type="button" class="atk-send-btn"></button>
15
+ </div>
16
+ </div>
17
+ <div class="atk-notify-wrap"></div>
18
+ </div>