@waline/client 1.6.0 → 2.0.0-alpha.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.
- package/dist/component.js +2 -0
- package/dist/component.js.map +1 -0
- package/dist/pageview.cjs.js +2 -0
- package/dist/pageview.cjs.js.map +1 -0
- package/dist/pageview.d.ts +33 -0
- package/dist/pageview.esm.js +2 -0
- package/dist/pageview.esm.js.map +1 -0
- package/dist/pageview.js +2 -0
- package/dist/pageview.js.map +1 -0
- package/dist/{Waline.min.d.ts → shim.d.ts} +192 -261
- package/dist/{Waline.noStyle.d.ts → shim.esm.d.ts} +192 -261
- package/dist/shim.esm.js +2 -0
- package/dist/shim.esm.js.map +1 -0
- package/dist/shim.js +2 -0
- package/dist/shim.js.map +1 -0
- package/dist/waline.cjs.d.ts +388 -0
- package/dist/waline.cjs.js +2 -0
- package/dist/waline.cjs.js.map +1 -0
- package/dist/waline.css +1 -0
- package/dist/waline.css.map +1 -0
- package/dist/waline.d.ts +388 -0
- package/dist/waline.esm.d.ts +388 -0
- package/dist/waline.esm.js +2 -0
- package/dist/waline.esm.js.map +1 -0
- package/dist/waline.js +2 -0
- package/dist/waline.js.map +1 -0
- package/package.json +33 -18
- package/src/comment.ts +39 -0
- package/src/components/CommentBox.vue +667 -0
- package/src/components/CommentCard.vue +125 -0
- package/src/components/Icons.ts +124 -0
- package/src/components/Waline.vue +359 -0
- package/src/composables/index.ts +3 -0
- package/src/composables/inputs.ts +29 -0
- package/src/composables/store.ts +38 -0
- package/src/composables/userInfo.ts +27 -0
- package/src/config/default.ts +21 -0
- package/src/config/i18n/en.ts +34 -0
- package/src/config/i18n/generate.ts +39 -0
- package/src/config/i18n/index.ts +30 -0
- package/src/config/i18n/jp.ts +34 -0
- package/src/config/i18n/pt-BR.ts +34 -0
- package/src/config/i18n/ru.ts +34 -0
- package/src/config/i18n/vi-VN.ts +34 -0
- package/src/config/i18n/zh-CN.ts +34 -0
- package/src/config/i18n/zh-TW.ts +34 -0
- package/src/config/index.ts +2 -0
- package/src/entrys/components.ts +2 -0
- package/src/entrys/full.ts +7 -0
- package/src/entrys/init.ts +4 -0
- package/src/entrys/pageview.ts +2 -0
- package/src/init.ts +92 -0
- package/src/pageview.ts +100 -0
- package/src/shims-hanabi.d.ts +9 -0
- package/src/shims-vue.d.ts +5 -0
- package/src/styles/base.scss +67 -0
- package/src/styles/card.scss +223 -0
- package/src/styles/config.scss +52 -0
- package/src/styles/emoji.scss +118 -0
- package/src/styles/highlight.scss +135 -0
- package/src/styles/index.scss +12 -0
- package/src/styles/layout.scss +78 -0
- package/src/styles/nomalize.scss +112 -0
- package/src/styles/panel.scss +293 -0
- package/src/styles/recent.scss +3 -0
- package/src/typings/base.ts +54 -0
- package/src/typings/comment.ts +25 -0
- package/src/typings/index.ts +5 -0
- package/src/typings/locale.ts +32 -0
- package/src/typings/options.ts +41 -0
- package/src/typings/waline.ts +208 -0
- package/src/utils/config.ts +99 -0
- package/src/utils/darkmode.ts +11 -0
- package/src/utils/data.ts +10 -0
- package/src/utils/emoji.ts +75 -0
- package/src/utils/error.ts +3 -0
- package/src/utils/fetch.ts +177 -0
- package/src/utils/getRoot.ts +8 -0
- package/src/utils/index.ts +13 -0
- package/src/utils/markdown.ts +41 -0
- package/src/utils/markedMathExtension.ts +52 -0
- package/src/utils/path.ts +15 -0
- package/src/utils/query.ts +2 -0
- package/src/utils/timeAgo.ts +75 -0
- package/src/utils/userInfo.ts +26 -0
- package/src/utils/wordCount.ts +20 -0
- package/src/version.ts +3 -0
- package/src/widgets/index.ts +1 -0
- package/src/widgets/recentComments.ts +52 -0
- package/dist/Waline.min.js +0 -2
- package/dist/Waline.min.js.map +0 -1
- package/dist/Waline.noStyle.js +0 -2
- package/dist/Waline.noStyle.js.map +0 -1
- package/dist/index.html +0 -11
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readonly, ref } from 'vue';
|
|
2
|
+
import { getUserInfo } from '../utils';
|
|
3
|
+
|
|
4
|
+
import type { Ref } from 'vue';
|
|
5
|
+
import type { UserInfo } from '../utils';
|
|
6
|
+
|
|
7
|
+
export type UserInfoRef = Ref<UserInfo | Record<string, never>>;
|
|
8
|
+
|
|
9
|
+
const userInfo: UserInfoRef = ref({});
|
|
10
|
+
|
|
11
|
+
export const useUserInfo = (): {
|
|
12
|
+
userInfo: UserInfoRef;
|
|
13
|
+
setUserInfo: (userInfo: UserInfo | Record<string, never>) => void;
|
|
14
|
+
} => {
|
|
15
|
+
if (!userInfo.value.token) {
|
|
16
|
+
const info = getUserInfo();
|
|
17
|
+
|
|
18
|
+
if (info) userInfo.value = info;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
userInfo: readonly(userInfo),
|
|
23
|
+
setUserInfo: (info: UserInfo | Record<string, never>): void => {
|
|
24
|
+
userInfo.value = info;
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { WalineMeta } from '../typings';
|
|
2
|
+
|
|
3
|
+
const availableMeta: WalineMeta[] = ['nick', 'mail', 'link'];
|
|
4
|
+
|
|
5
|
+
export const getMeta = (meta: WalineMeta[]): WalineMeta[] =>
|
|
6
|
+
meta.filter((item) => availableMeta.includes(item));
|
|
7
|
+
|
|
8
|
+
export const defaultLang = 'zh-CN';
|
|
9
|
+
|
|
10
|
+
export const defaultUploadImage = (file: File): Promise<string> =>
|
|
11
|
+
new Promise((resolve, reject) => {
|
|
12
|
+
const reader = new FileReader();
|
|
13
|
+
reader.readAsDataURL(file);
|
|
14
|
+
reader.onload = (): void => resolve(reader.result?.toString() || '');
|
|
15
|
+
reader.onerror = reject;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const defaultTexRenderer = (blockMode: boolean): string =>
|
|
19
|
+
blockMode === true
|
|
20
|
+
? '<p class="vtex">Tex is not available in preview</p>'
|
|
21
|
+
: '<span class="vtex">Tex is not available in preview</span>';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'NickName',
|
|
5
|
+
'NickName cannot be less than 3 bytes.',
|
|
6
|
+
'E-Mail',
|
|
7
|
+
'Please confirm your email address.',
|
|
8
|
+
'Website',
|
|
9
|
+
'Optional',
|
|
10
|
+
'Comment here...',
|
|
11
|
+
'No comment yet.',
|
|
12
|
+
'Submit',
|
|
13
|
+
'Reply',
|
|
14
|
+
'Cancel reply',
|
|
15
|
+
'Comments',
|
|
16
|
+
'Refresh',
|
|
17
|
+
'Load More...',
|
|
18
|
+
'Preview',
|
|
19
|
+
'Emoji',
|
|
20
|
+
'Upload Image',
|
|
21
|
+
'seconds ago',
|
|
22
|
+
'minutes ago',
|
|
23
|
+
'hours ago',
|
|
24
|
+
'days ago',
|
|
25
|
+
'just now',
|
|
26
|
+
'Uploading',
|
|
27
|
+
'Login',
|
|
28
|
+
'logout',
|
|
29
|
+
'Admin',
|
|
30
|
+
'Sticky',
|
|
31
|
+
'Words',
|
|
32
|
+
'Please input comments between $0 and $1 words!\n Current word number: $2',
|
|
33
|
+
'Anonymous',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WalineLocale } from '../../typings';
|
|
2
|
+
|
|
3
|
+
const localeKeys = [
|
|
4
|
+
'nick',
|
|
5
|
+
'nickError',
|
|
6
|
+
'mail',
|
|
7
|
+
'mailError',
|
|
8
|
+
'link',
|
|
9
|
+
'optional',
|
|
10
|
+
'placeholder',
|
|
11
|
+
'sofa',
|
|
12
|
+
'submit',
|
|
13
|
+
'reply',
|
|
14
|
+
'cancelReply',
|
|
15
|
+
'comment',
|
|
16
|
+
'refresh',
|
|
17
|
+
'more',
|
|
18
|
+
'preview',
|
|
19
|
+
'emoji',
|
|
20
|
+
'uploadImage',
|
|
21
|
+
'seconds',
|
|
22
|
+
'minutes',
|
|
23
|
+
'hours',
|
|
24
|
+
'days',
|
|
25
|
+
'now',
|
|
26
|
+
'uploading',
|
|
27
|
+
'login',
|
|
28
|
+
'logout',
|
|
29
|
+
'admin',
|
|
30
|
+
'sticky',
|
|
31
|
+
'word',
|
|
32
|
+
'wordHint',
|
|
33
|
+
'anonymous',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const generateLocale = (locale: string[]): WalineLocale =>
|
|
37
|
+
Object.fromEntries(
|
|
38
|
+
locale.map((item, index) => [localeKeys[index], item])
|
|
39
|
+
) as unknown as WalineLocale;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
|
+
import en from './en';
|
|
3
|
+
import jp from './jp';
|
|
4
|
+
import zhCN from './zh-CN';
|
|
5
|
+
import zhTW from './zh-TW';
|
|
6
|
+
import ptBR from './pt-BR';
|
|
7
|
+
import ru from './ru';
|
|
8
|
+
|
|
9
|
+
import type { WalineLocale } from '../../typings';
|
|
10
|
+
|
|
11
|
+
export type Locales = Record<string, WalineLocale>;
|
|
12
|
+
|
|
13
|
+
export const locales: Locales = {
|
|
14
|
+
zh: zhCN,
|
|
15
|
+
'zh-cn': zhCN,
|
|
16
|
+
'zh-CN': zhCN,
|
|
17
|
+
'zh-tw': zhTW,
|
|
18
|
+
'zh-TW': zhTW,
|
|
19
|
+
en: en,
|
|
20
|
+
'en-US': en,
|
|
21
|
+
'en-us': en,
|
|
22
|
+
jp: jp,
|
|
23
|
+
'jp-jp': jp,
|
|
24
|
+
'jp-JP': jp,
|
|
25
|
+
'pt-br': ptBR,
|
|
26
|
+
'pt-BR': ptBR,
|
|
27
|
+
ru: ru,
|
|
28
|
+
'ru-ru': ru,
|
|
29
|
+
'ru-RU': ru,
|
|
30
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'ニックネーム',
|
|
5
|
+
'3バイト以上のニックネームをご入力ください.',
|
|
6
|
+
'メールアドレス',
|
|
7
|
+
'メールアドレスをご確認ください.',
|
|
8
|
+
'サイト',
|
|
9
|
+
'オプション',
|
|
10
|
+
'ここにコメント',
|
|
11
|
+
'コメントしましょう~',
|
|
12
|
+
'提出する',
|
|
13
|
+
'返信する',
|
|
14
|
+
'キャンセル',
|
|
15
|
+
'コメント',
|
|
16
|
+
'更新',
|
|
17
|
+
'さらに読み込む',
|
|
18
|
+
'プレビュー',
|
|
19
|
+
'絵文字',
|
|
20
|
+
'画像をアップロード',
|
|
21
|
+
'秒前',
|
|
22
|
+
'分前',
|
|
23
|
+
'時間前',
|
|
24
|
+
'日前',
|
|
25
|
+
'たっだ今',
|
|
26
|
+
'アップロード',
|
|
27
|
+
'ログインする',
|
|
28
|
+
'ログアウト',
|
|
29
|
+
'管理者',
|
|
30
|
+
'トップに置く',
|
|
31
|
+
'ワード',
|
|
32
|
+
'コメントは $0 から $1 ワードの間でなければなりません!\n 現在の単語番号: $2',
|
|
33
|
+
'匿名',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'Apelido',
|
|
5
|
+
'Apelido não pode ser menor que 3 bytes.',
|
|
6
|
+
'E-Mail',
|
|
7
|
+
'Por favor, confirme seu endereço de e-mail.',
|
|
8
|
+
'Website',
|
|
9
|
+
'Opcional',
|
|
10
|
+
'Comente aqui...',
|
|
11
|
+
'Nenhum comentário, ainda.',
|
|
12
|
+
'Enviar',
|
|
13
|
+
'Responder',
|
|
14
|
+
'Cancelar resposta',
|
|
15
|
+
'Comentários',
|
|
16
|
+
'Refrescar',
|
|
17
|
+
'Carregar Mais...',
|
|
18
|
+
'Visualizar',
|
|
19
|
+
'Emoji',
|
|
20
|
+
'Enviar Imagem',
|
|
21
|
+
'segundos atrás',
|
|
22
|
+
'minutos atrás',
|
|
23
|
+
'horas atrás',
|
|
24
|
+
'dias atrás',
|
|
25
|
+
'agora mesmo',
|
|
26
|
+
'Enviando',
|
|
27
|
+
'Entrar',
|
|
28
|
+
'Sair',
|
|
29
|
+
'Admin',
|
|
30
|
+
'Sticky',
|
|
31
|
+
'Palavras',
|
|
32
|
+
'Favor enviar comentário com $0 a $1 palavras!\n Número de palavras atuais: $2',
|
|
33
|
+
'Anônimo',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'Псевдоним',
|
|
5
|
+
'Никнейм не может быть меньше 3 байт.',
|
|
6
|
+
'Эл. адрес',
|
|
7
|
+
'Пожалуйста, подтвердите адрес вашей электронной почты.',
|
|
8
|
+
'Веб-сайт',
|
|
9
|
+
'Необязательный',
|
|
10
|
+
'Комментарий здесь...',
|
|
11
|
+
'Пока нет комментариев.',
|
|
12
|
+
'Отправить',
|
|
13
|
+
'Отвечать',
|
|
14
|
+
'Отменить ответ',
|
|
15
|
+
'Комментарии',
|
|
16
|
+
'Обновить',
|
|
17
|
+
'Загрузи больше...',
|
|
18
|
+
'Превью',
|
|
19
|
+
'эмодзи',
|
|
20
|
+
'Загрузить изображение',
|
|
21
|
+
'секунд назад',
|
|
22
|
+
'несколько минут назад',
|
|
23
|
+
'несколько часов назад',
|
|
24
|
+
'дней назад',
|
|
25
|
+
'прямо сейчас',
|
|
26
|
+
'Загрузка',
|
|
27
|
+
'Авторизоваться',
|
|
28
|
+
'Выход из системы',
|
|
29
|
+
'Админ',
|
|
30
|
+
'Липкий',
|
|
31
|
+
'Слова',
|
|
32
|
+
'Пожалуйста, введите комментарии от $0 до $1 слов!\nНомер текущего слова: $2',
|
|
33
|
+
'Анонимный',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'Tên',
|
|
5
|
+
'Tên không được nhỏ hơn 3 ký tự.',
|
|
6
|
+
'E-Mail',
|
|
7
|
+
'Vui lòng xác nhập địa chỉ email của bạn.',
|
|
8
|
+
'Website',
|
|
9
|
+
'Tùy chọn',
|
|
10
|
+
'Hãy bình luận có văn hoá!',
|
|
11
|
+
'Chưa có bình luận',
|
|
12
|
+
'Gửi',
|
|
13
|
+
'Trả lời',
|
|
14
|
+
'Hủy bỏ',
|
|
15
|
+
'bình luận',
|
|
16
|
+
'Làm mới',
|
|
17
|
+
'Tải thêm...',
|
|
18
|
+
'Xem trước',
|
|
19
|
+
'Emoji',
|
|
20
|
+
'Tải lên hình ảnh',
|
|
21
|
+
'giây trước',
|
|
22
|
+
'phút trước',
|
|
23
|
+
'giờ trước',
|
|
24
|
+
'ngày trước',
|
|
25
|
+
'Vừa xong',
|
|
26
|
+
'Đang tải lên',
|
|
27
|
+
'Đăng nhập',
|
|
28
|
+
'đăng xuất',
|
|
29
|
+
'Admin',
|
|
30
|
+
'Sticky',
|
|
31
|
+
'từ',
|
|
32
|
+
'Please input comments between $0 and $1 words!\n Current word number: $2',
|
|
33
|
+
'Vô danh',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'昵称',
|
|
5
|
+
'昵称不能少于3个字符',
|
|
6
|
+
'邮箱',
|
|
7
|
+
'请填写正确的邮件地址',
|
|
8
|
+
'网址',
|
|
9
|
+
'可选',
|
|
10
|
+
'欢迎评论',
|
|
11
|
+
'来发评论吧~',
|
|
12
|
+
'提交',
|
|
13
|
+
'回复',
|
|
14
|
+
'取消回复',
|
|
15
|
+
'评论',
|
|
16
|
+
'刷新',
|
|
17
|
+
'加载更多...',
|
|
18
|
+
'预览',
|
|
19
|
+
'表情',
|
|
20
|
+
'上传图片',
|
|
21
|
+
'秒前',
|
|
22
|
+
'分钟前',
|
|
23
|
+
'小时前',
|
|
24
|
+
'天前',
|
|
25
|
+
'刚刚',
|
|
26
|
+
'正在上传',
|
|
27
|
+
'登录',
|
|
28
|
+
'退出',
|
|
29
|
+
'博主',
|
|
30
|
+
'置顶',
|
|
31
|
+
'字',
|
|
32
|
+
'评论字数应在 $0 到 $1 字之间!\n当前字数:$2',
|
|
33
|
+
'匿名',
|
|
34
|
+
]);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateLocale } from './generate';
|
|
2
|
+
|
|
3
|
+
export default generateLocale([
|
|
4
|
+
'暱稱',
|
|
5
|
+
'郵箱',
|
|
6
|
+
'網址',
|
|
7
|
+
'可選',
|
|
8
|
+
'暱稱不能少於3個字元',
|
|
9
|
+
'請填寫正確的郵件地址',
|
|
10
|
+
'歡迎評論',
|
|
11
|
+
'來發評論吧~',
|
|
12
|
+
'提交',
|
|
13
|
+
'回覆',
|
|
14
|
+
'取消回覆',
|
|
15
|
+
'評論',
|
|
16
|
+
'刷新',
|
|
17
|
+
'載入更多...',
|
|
18
|
+
'預覽',
|
|
19
|
+
'表情',
|
|
20
|
+
'上傳圖片',
|
|
21
|
+
'秒前',
|
|
22
|
+
'分鐘前',
|
|
23
|
+
'小時前',
|
|
24
|
+
'天前',
|
|
25
|
+
'剛剛',
|
|
26
|
+
'正在上傳',
|
|
27
|
+
'登錄',
|
|
28
|
+
'退出',
|
|
29
|
+
'博主',
|
|
30
|
+
'置頂',
|
|
31
|
+
'字',
|
|
32
|
+
'評論字數應在 $0 到 $1 字之間!\n當前字數:$2',
|
|
33
|
+
'匿名',
|
|
34
|
+
]);
|
package/src/init.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createApp, h, reactive, watchEffect } from 'vue';
|
|
2
|
+
|
|
3
|
+
import Waline from './components/Waline.vue';
|
|
4
|
+
import { commentCount } from './comment';
|
|
5
|
+
import { pageviewCount } from './pageview';
|
|
6
|
+
import { getRoot } from './utils';
|
|
7
|
+
|
|
8
|
+
import type { WalineInitOptions, WalineProps } from './typings';
|
|
9
|
+
|
|
10
|
+
export interface WalineInstance {
|
|
11
|
+
el: HTMLElement | null;
|
|
12
|
+
update: (newOptions: Partial<WalineProps>) => void;
|
|
13
|
+
destroy: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const init = ({
|
|
17
|
+
el = '#waline',
|
|
18
|
+
path = window.location.pathname,
|
|
19
|
+
comment = false,
|
|
20
|
+
pageview = false,
|
|
21
|
+
...initProps
|
|
22
|
+
}: WalineInitOptions): WalineInstance | null => {
|
|
23
|
+
// check el element
|
|
24
|
+
const root = el ? getRoot(el) : null;
|
|
25
|
+
|
|
26
|
+
// check root
|
|
27
|
+
if (el && !root) throw new Error(`Option 'el' do not match any domElement!`);
|
|
28
|
+
|
|
29
|
+
// check serverURL
|
|
30
|
+
if (!initProps.serverURL) throw new Error("Option 'serverURL' is missing!");
|
|
31
|
+
|
|
32
|
+
const props = reactive({ ...initProps });
|
|
33
|
+
const state = reactive({ comment, pageview, path });
|
|
34
|
+
|
|
35
|
+
const updateCommentCount = (): void => {
|
|
36
|
+
if (state.comment)
|
|
37
|
+
commentCount({
|
|
38
|
+
serverURL: props.serverURL,
|
|
39
|
+
path: state.path,
|
|
40
|
+
selector: typeof state.comment === 'string' ? state.comment : undefined,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const updatePageviewCount = (): void => {
|
|
45
|
+
if (state.pageview)
|
|
46
|
+
pageviewCount({
|
|
47
|
+
serverURL: props.serverURL,
|
|
48
|
+
path: state.path,
|
|
49
|
+
selector:
|
|
50
|
+
typeof state.pageview === 'string' ? state.pageview : undefined,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const app = root
|
|
55
|
+
? createApp(() => h(Waline, { path: state.path, ...props }))
|
|
56
|
+
: null;
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
59
|
+
if (app) app.mount(root!);
|
|
60
|
+
|
|
61
|
+
updateCommentCount();
|
|
62
|
+
updatePageviewCount();
|
|
63
|
+
|
|
64
|
+
const stopComment = watchEffect(updateCommentCount);
|
|
65
|
+
const stopPageview = watchEffect(updatePageviewCount);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
el: root,
|
|
69
|
+
update: ({
|
|
70
|
+
comment,
|
|
71
|
+
pageview,
|
|
72
|
+
path = window.location.pathname,
|
|
73
|
+
...newProps
|
|
74
|
+
}: Partial<Omit<WalineInitOptions, 'el'>>): void => {
|
|
75
|
+
Object.entries(newProps).map(([key, value]) => {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
// eslint-disable-next-line
|
|
79
|
+
props[key] = value;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
state.path = path;
|
|
83
|
+
if (comment !== undefined) state.comment = comment;
|
|
84
|
+
if (pageview !== undefined) state.pageview = pageview;
|
|
85
|
+
},
|
|
86
|
+
destroy: (): void => {
|
|
87
|
+
app?.unmount();
|
|
88
|
+
stopComment();
|
|
89
|
+
stopPageview();
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
package/src/pageview.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
errorHandler,
|
|
3
|
+
fetchPageviews,
|
|
4
|
+
getQuery,
|
|
5
|
+
updatePageviews,
|
|
6
|
+
} from './utils';
|
|
7
|
+
|
|
8
|
+
const renderVisitorCount = (
|
|
9
|
+
counts: number[],
|
|
10
|
+
countElements: HTMLElement[]
|
|
11
|
+
): void => {
|
|
12
|
+
countElements.forEach((element, index) => {
|
|
13
|
+
element.innerText = counts[index].toString();
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface VisitorCountOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Waline server url
|
|
20
|
+
*
|
|
21
|
+
* Waline 服务端地址
|
|
22
|
+
*/
|
|
23
|
+
serverURL: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Path to be fetched and updated
|
|
27
|
+
*
|
|
28
|
+
* 需要更新和获取的路径
|
|
29
|
+
*
|
|
30
|
+
* @default window.location.pathname
|
|
31
|
+
*/
|
|
32
|
+
path?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @default '.waline-pageview-count'
|
|
36
|
+
*/
|
|
37
|
+
selector?: string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether update pageviews when fetching path result
|
|
41
|
+
*
|
|
42
|
+
* 是否在查询时更新 path 的浏览量
|
|
43
|
+
*
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
update?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const pageviewCount = ({
|
|
50
|
+
serverURL,
|
|
51
|
+
path = window.location.pathname,
|
|
52
|
+
selector = '.waline-pageview-count',
|
|
53
|
+
update = true,
|
|
54
|
+
}: VisitorCountOptions): ((reason?: unknown) => void) => {
|
|
55
|
+
const controller = new AbortController();
|
|
56
|
+
|
|
57
|
+
const elements = Array.from(
|
|
58
|
+
// visitor selectors
|
|
59
|
+
document.querySelectorAll<HTMLElement>(selector)
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const filter = (element: HTMLElement): boolean => {
|
|
63
|
+
const query = getQuery(element);
|
|
64
|
+
|
|
65
|
+
return query !== null && path !== query;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const fetch = (elements: HTMLElement[]): Promise<void> =>
|
|
69
|
+
fetchPageviews({
|
|
70
|
+
serverURL,
|
|
71
|
+
paths: elements.map((element) => getQuery(element) || path),
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
})
|
|
74
|
+
.then((counts) => renderVisitorCount(counts, elements))
|
|
75
|
+
.catch(errorHandler);
|
|
76
|
+
|
|
77
|
+
// we should update pageviews
|
|
78
|
+
if (update) {
|
|
79
|
+
const normalElements = elements.filter((element) => !filter(element));
|
|
80
|
+
const elementsNeedstoBeFetched = elements.filter(filter);
|
|
81
|
+
|
|
82
|
+
void updatePageviews({ serverURL, path }).then((count) =>
|
|
83
|
+
renderVisitorCount(
|
|
84
|
+
new Array<number>(normalElements.length).fill(count),
|
|
85
|
+
normalElements
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// if we should fetch count of other pages
|
|
90
|
+
if (elementsNeedstoBeFetched.length) {
|
|
91
|
+
void fetch(elementsNeedstoBeFetched);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// we should not update pageviews
|
|
95
|
+
else {
|
|
96
|
+
void fetch(elements);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return controller.abort.bind(controller);
|
|
100
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
.wl-btn {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
vertical-align: middle;
|
|
4
|
+
|
|
5
|
+
min-width: 2.5em;
|
|
6
|
+
margin-bottom: 0;
|
|
7
|
+
padding: 0.5em 1em;
|
|
8
|
+
border: 1px solid var(--waline-border-color);
|
|
9
|
+
border-radius: 0.5em;
|
|
10
|
+
|
|
11
|
+
background: transparent;
|
|
12
|
+
color: var(--waline-color);
|
|
13
|
+
|
|
14
|
+
font-weight: 400;
|
|
15
|
+
font-size: 0.75em;
|
|
16
|
+
line-height: 1.5;
|
|
17
|
+
text-align: center;
|
|
18
|
+
white-space: nowrap;
|
|
19
|
+
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
user-select: none;
|
|
22
|
+
|
|
23
|
+
transition-duration: 0.4s;
|
|
24
|
+
|
|
25
|
+
touch-action: manipulation;
|
|
26
|
+
|
|
27
|
+
&:hover,
|
|
28
|
+
&:active {
|
|
29
|
+
border-color: var(--waline-theme-color);
|
|
30
|
+
color: var(--waline-theme-color);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&:disabled {
|
|
34
|
+
border-color: var(--waline-border-color);
|
|
35
|
+
background: var(--waline-disable-bgcolor);
|
|
36
|
+
color: var(--waline-disable-color);
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
&.primary {
|
|
41
|
+
border-color: var(--waline-theme-color);
|
|
42
|
+
background: var(--waline-theme-color);
|
|
43
|
+
color: var(--waline-white);
|
|
44
|
+
|
|
45
|
+
&:hover,
|
|
46
|
+
&:active {
|
|
47
|
+
border-color: var(--waline-active-color);
|
|
48
|
+
background: var(--waline-active-color);
|
|
49
|
+
color: var(--waline-white);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&:disabled {
|
|
53
|
+
border-color: var(--waline-border-color);
|
|
54
|
+
background: var(--waline-disable-bgcolor);
|
|
55
|
+
color: var(--waline-disable-color);
|
|
56
|
+
cursor: not-allowed;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.wl-loading {
|
|
62
|
+
text-align: center;
|
|
63
|
+
|
|
64
|
+
svg {
|
|
65
|
+
margin: 0 auto;
|
|
66
|
+
}
|
|
67
|
+
}
|