@waline/client 2.4.2 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/component.esm.js +1 -1
- package/dist/component.esm.js.map +1 -1
- package/dist/component.js +1 -1
- package/dist/component.js.map +1 -1
- package/dist/legacy.d.ts +42 -0
- package/dist/legacy.js +1 -1
- package/dist/legacy.js.map +1 -1
- package/dist/pageview.cjs.js +1 -1
- package/dist/pageview.cjs.js.map +1 -1
- package/dist/pageview.esm.js +1 -1
- package/dist/pageview.esm.js.map +1 -1
- package/dist/pageview.js +1 -1
- package/dist/pageview.js.map +1 -1
- package/dist/shim.d.ts +43 -1
- package/dist/shim.esm.d.ts +43 -1
- package/dist/shim.esm.js +1 -1
- package/dist/shim.esm.js.map +1 -1
- package/dist/shim.js +1 -1
- package/dist/shim.js.map +1 -1
- package/dist/waline.cjs.d.ts +43 -1
- package/dist/waline.cjs.js +1 -1
- package/dist/waline.cjs.js.map +1 -1
- package/dist/waline.css +1 -1
- package/dist/waline.css.map +1 -1
- package/dist/waline.d.ts +43 -1
- package/dist/waline.esm.d.ts +43 -1
- package/dist/waline.esm.js +1 -1
- package/dist/waline.esm.js.map +1 -1
- package/dist/waline.js +1 -1
- package/dist/waline.js.map +1 -1
- package/package.json +21 -22
- package/src/comment.ts +7 -2
- package/src/components/CommentBox.vue +121 -4
- package/src/components/CommentCard.vue +1 -1
- package/src/components/Icons.ts +20 -0
- package/src/components/ImageWall.vue +166 -0
- package/src/composables/like.ts +1 -1
- package/src/config/default.ts +91 -1
- package/src/config/i18n/en.ts +2 -0
- package/src/config/i18n/generate.ts +2 -0
- package/src/config/i18n/jp.ts +8 -0
- package/src/config/i18n/pt-BR.ts +8 -0
- package/src/config/i18n/ru.ts +8 -0
- package/src/config/i18n/vi-VN.ts +8 -0
- package/src/config/i18n/zh-CN.ts +2 -0
- package/src/config/i18n/zh-TW.ts +2 -0
- package/src/init.ts +0 -3
- package/src/pageview.ts +2 -1
- package/src/styles/card.scss +1 -0
- package/src/styles/emoji.scss +112 -94
- package/src/styles/gif.scss +70 -0
- package/src/styles/index.scss +3 -0
- package/src/styles/panel.scss +0 -4
- package/src/typings/base.ts +40 -0
- package/src/typings/locale.ts +2 -0
- package/src/typings/waline.ts +8 -0
- package/src/utils/config.ts +5 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "client for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"valine",
|
|
@@ -71,30 +71,29 @@
|
|
|
71
71
|
]
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@vueuse/core": "^8.
|
|
74
|
+
"@vueuse/core": "^8.6.0",
|
|
75
75
|
"autosize": "^5.0.1",
|
|
76
|
-
"marked": "^4.0.
|
|
77
|
-
"vue": "^3.2.
|
|
76
|
+
"marked": "^4.0.16",
|
|
77
|
+
"vue": "^3.2.37"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@babel/core": "
|
|
81
|
-
"@babel/preset-env": "
|
|
82
|
-
"@rollup/plugin-babel": "
|
|
83
|
-
"@rollup/plugin-commonjs": "
|
|
84
|
-
"@rollup/plugin-node-resolve": "
|
|
85
|
-
"@rollup/plugin-replace": "
|
|
86
|
-
"@types/autosize": "
|
|
87
|
-
"@types/marked": "
|
|
88
|
-
"@types/node": "
|
|
89
|
-
"@vitejs/plugin-vue": "
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"rollup": "
|
|
93
|
-
"rollup-plugin-
|
|
94
|
-
"rollup-plugin-
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"vite": "^2.9.8"
|
|
80
|
+
"@babel/core": "7.18.2",
|
|
81
|
+
"@babel/preset-env": "7.18.2",
|
|
82
|
+
"@rollup/plugin-babel": "5.3.1",
|
|
83
|
+
"@rollup/plugin-commonjs": "22.0.0",
|
|
84
|
+
"@rollup/plugin-node-resolve": "13.3.0",
|
|
85
|
+
"@rollup/plugin-replace": "4.0.0",
|
|
86
|
+
"@types/autosize": "4.0.1",
|
|
87
|
+
"@types/marked": "4.0.3",
|
|
88
|
+
"@types/node": "17.0.41",
|
|
89
|
+
"@vitejs/plugin-vue": "2.3.3",
|
|
90
|
+
"rimraf": "3.0.2",
|
|
91
|
+
"rollup": "2.75.6",
|
|
92
|
+
"rollup-plugin-dts": "4.2.2",
|
|
93
|
+
"rollup-plugin-terser": "7.0.2",
|
|
94
|
+
"rollup-plugin-ts": "3.0.2",
|
|
95
|
+
"typescript": "4.7.3",
|
|
96
|
+
"vite": "2.9.10"
|
|
98
97
|
},
|
|
99
98
|
"engines": {
|
|
100
99
|
"node": ">=14"
|
package/src/comment.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useUserInfo } from './composables';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
decodePath,
|
|
4
|
+
errorHandler,
|
|
5
|
+
fetchCommentCount,
|
|
6
|
+
getServerURL,
|
|
7
|
+
} from './utils';
|
|
3
8
|
import type { WalineAbort } from './typings';
|
|
4
9
|
|
|
5
10
|
export interface WalineCommentCountOptions {
|
|
@@ -54,7 +59,7 @@ WalineCommentCountOptions): WalineAbort => {
|
|
|
54
59
|
|
|
55
60
|
if (elements.length)
|
|
56
61
|
void fetchCommentCount({
|
|
57
|
-
serverURL,
|
|
62
|
+
serverURL: getServerURL(serverURL),
|
|
58
63
|
paths: Array.from(elements).map((element) =>
|
|
59
64
|
decodePath(element.dataset.path || element.getAttribute('id') || path)
|
|
60
65
|
),
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<input
|
|
36
36
|
:ref="
|
|
37
37
|
(element) => {
|
|
38
|
-
if (element) inputRefs[kind] = element;
|
|
38
|
+
if (element) inputRefs[kind] = element as HTMLInputElement;
|
|
39
39
|
}
|
|
40
40
|
"
|
|
41
41
|
:id="`wl-${kind}`"
|
|
@@ -88,6 +88,17 @@
|
|
|
88
88
|
<EmojiIcon />
|
|
89
89
|
</button>
|
|
90
90
|
|
|
91
|
+
<button
|
|
92
|
+
v-if="config.search"
|
|
93
|
+
ref="gifButtonRef"
|
|
94
|
+
class="wl-action"
|
|
95
|
+
:class="{ actived: showGif }"
|
|
96
|
+
:title="locale.gif"
|
|
97
|
+
@click="showGif = !showGif"
|
|
98
|
+
>
|
|
99
|
+
<GifIcon />
|
|
100
|
+
</button>
|
|
101
|
+
|
|
91
102
|
<input
|
|
92
103
|
ref="imageUploadRef"
|
|
93
104
|
class="upload"
|
|
@@ -152,6 +163,31 @@
|
|
|
152
163
|
</button>
|
|
153
164
|
</div>
|
|
154
165
|
|
|
166
|
+
<div
|
|
167
|
+
ref="gifPopupRef"
|
|
168
|
+
class="wl-gif-popup"
|
|
169
|
+
:class="{ display: showGif }"
|
|
170
|
+
>
|
|
171
|
+
<input
|
|
172
|
+
type="text"
|
|
173
|
+
:placeholder="locale.gifSearchPlaceholder"
|
|
174
|
+
ref="gifSearchInputRef"
|
|
175
|
+
@input="onGifSearch"
|
|
176
|
+
/>
|
|
177
|
+
|
|
178
|
+
<ImageWall
|
|
179
|
+
:items="gifData.list"
|
|
180
|
+
:column-width="200"
|
|
181
|
+
:gap="6"
|
|
182
|
+
@insert="insert($event)"
|
|
183
|
+
@scroll="onImageWallScroll"
|
|
184
|
+
>
|
|
185
|
+
</ImageWall>
|
|
186
|
+
|
|
187
|
+
<div v-if="gifData.loading" class="wl-loading">
|
|
188
|
+
<LoadingIcon :size="30" />
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
155
191
|
<div
|
|
156
192
|
ref="emojiPopupRef"
|
|
157
193
|
class="wl-emoji-popup"
|
|
@@ -210,6 +246,7 @@
|
|
|
210
246
|
</template>
|
|
211
247
|
|
|
212
248
|
<script lang="ts">
|
|
249
|
+
import { useDebounceFn } from '@vueuse/core';
|
|
213
250
|
import autosize from 'autosize';
|
|
214
251
|
import {
|
|
215
252
|
computed,
|
|
@@ -217,6 +254,7 @@ import {
|
|
|
217
254
|
inject,
|
|
218
255
|
onMounted,
|
|
219
256
|
onUnmounted,
|
|
257
|
+
reactive,
|
|
220
258
|
ref,
|
|
221
259
|
watch,
|
|
222
260
|
} from 'vue';
|
|
@@ -228,19 +266,26 @@ import {
|
|
|
228
266
|
MarkdownIcon,
|
|
229
267
|
PreviewIcon,
|
|
230
268
|
LoadingIcon,
|
|
269
|
+
GifIcon,
|
|
231
270
|
} from './Icons';
|
|
271
|
+
import ImageWall from './ImageWall.vue';
|
|
232
272
|
import { useEditor, useUserMeta, useUserInfo } from '../composables';
|
|
233
273
|
import {
|
|
274
|
+
getEmojis,
|
|
234
275
|
getImagefromDataTransfer,
|
|
235
|
-
parseMarkdown,
|
|
236
276
|
getWordNumber,
|
|
237
277
|
parseEmoji,
|
|
278
|
+
parseMarkdown,
|
|
238
279
|
postComment,
|
|
239
|
-
getEmojis,
|
|
240
280
|
} from '../utils';
|
|
241
281
|
|
|
242
282
|
import type { ComputedRef, DeepReadonly } from 'vue';
|
|
243
|
-
import type {
|
|
283
|
+
import type {
|
|
284
|
+
WalineCommentData,
|
|
285
|
+
WalineImageUploader,
|
|
286
|
+
WalineSearchOptions,
|
|
287
|
+
WalineSearchResult,
|
|
288
|
+
} from '../typings';
|
|
244
289
|
import type { WalineConfig, WalineEmojiConfig } from '../utils';
|
|
245
290
|
|
|
246
291
|
export default defineComponent({
|
|
@@ -250,9 +295,11 @@ export default defineComponent({
|
|
|
250
295
|
CloseIcon,
|
|
251
296
|
EmojiIcon,
|
|
252
297
|
ImageIcon,
|
|
298
|
+
ImageWall,
|
|
253
299
|
MarkdownIcon,
|
|
254
300
|
PreviewIcon,
|
|
255
301
|
LoadingIcon,
|
|
302
|
+
GifIcon,
|
|
256
303
|
},
|
|
257
304
|
|
|
258
305
|
props: {
|
|
@@ -286,14 +333,23 @@ export default defineComponent({
|
|
|
286
333
|
const imageUploadRef = ref<HTMLInputElement | null>(null);
|
|
287
334
|
const emojiButtonRef = ref<HTMLDivElement | null>(null);
|
|
288
335
|
const emojiPopupRef = ref<HTMLDivElement | null>(null);
|
|
336
|
+
const gifButtonRef = ref<HTMLDivElement | null>(null);
|
|
337
|
+
const gifPopupRef = ref<HTMLDivElement | null>(null);
|
|
338
|
+
const gifSearchInputRef = ref<HTMLInputElement | null>(null);
|
|
289
339
|
|
|
290
340
|
const emoji = ref<DeepReadonly<WalineEmojiConfig>>({ tabs: [], map: {} });
|
|
291
341
|
const emojiTabIndex = ref(0);
|
|
292
342
|
const showEmoji = ref(false);
|
|
343
|
+
const showGif = ref(false);
|
|
293
344
|
const showPreview = ref(false);
|
|
294
345
|
const previewText = ref('');
|
|
295
346
|
const wordNumber = ref(0);
|
|
296
347
|
|
|
348
|
+
const searchResults = reactive({
|
|
349
|
+
loading: true,
|
|
350
|
+
list: [] as WalineSearchResult[],
|
|
351
|
+
});
|
|
352
|
+
|
|
297
353
|
const wordLimit = ref(0);
|
|
298
354
|
const isWordNumberLegal = ref(false);
|
|
299
355
|
|
|
@@ -548,8 +604,43 @@ export default defineComponent({
|
|
|
548
604
|
!(emojiPopupRef.value as HTMLElement).contains(event.target as Node)
|
|
549
605
|
)
|
|
550
606
|
showEmoji.value = false;
|
|
607
|
+
|
|
608
|
+
if (
|
|
609
|
+
!(gifButtonRef.value as HTMLElement).contains(event.target as Node) &&
|
|
610
|
+
!(gifPopupRef.value as HTMLElement).contains(event.target as Node)
|
|
611
|
+
)
|
|
612
|
+
showGif.value = false;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
const onImageWallScroll = async (event: Event): Promise<void> => {
|
|
616
|
+
const { scrollTop, clientHeight, scrollHeight } =
|
|
617
|
+
event.target as HTMLDivElement;
|
|
618
|
+
const percent = (clientHeight + scrollTop) / scrollHeight;
|
|
619
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
620
|
+
const keyword = gifSearchInputRef.value?.value || '';
|
|
621
|
+
|
|
622
|
+
if (percent < 0.9 || searchResults.loading) return;
|
|
623
|
+
|
|
624
|
+
searchResults.loading = true;
|
|
625
|
+
|
|
626
|
+
searchResults.list.push(
|
|
627
|
+
...(searchOptions.more
|
|
628
|
+
? await searchOptions.more(keyword, searchResults.list.length)
|
|
629
|
+
: await searchOptions.search(keyword))
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
searchResults.loading = false;
|
|
633
|
+
|
|
634
|
+
setTimeout(() => {
|
|
635
|
+
(event.target as HTMLDivElement).scrollTop = scrollTop;
|
|
636
|
+
}, 50);
|
|
551
637
|
};
|
|
552
638
|
|
|
639
|
+
const onGifSearch = useDebounceFn((event: Event) => {
|
|
640
|
+
searchResults.list = [];
|
|
641
|
+
onImageWallScroll(event);
|
|
642
|
+
}, 300);
|
|
643
|
+
|
|
553
644
|
// update wordNumber
|
|
554
645
|
watch(
|
|
555
646
|
[config, wordNumber],
|
|
@@ -575,6 +666,23 @@ export default defineComponent({
|
|
|
575
666
|
{ immediate: true }
|
|
576
667
|
);
|
|
577
668
|
|
|
669
|
+
watch(showGif, async (showGif) => {
|
|
670
|
+
if (!showGif) return;
|
|
671
|
+
|
|
672
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
673
|
+
|
|
674
|
+
// clear input
|
|
675
|
+
if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
|
|
676
|
+
|
|
677
|
+
searchResults.loading = true;
|
|
678
|
+
|
|
679
|
+
searchResults.list = searchOptions.default
|
|
680
|
+
? await searchOptions.default()
|
|
681
|
+
: await searchOptions.search('');
|
|
682
|
+
|
|
683
|
+
searchResults.loading = false;
|
|
684
|
+
});
|
|
685
|
+
|
|
578
686
|
onMounted(() => {
|
|
579
687
|
document.body.addEventListener('click', popupHandler);
|
|
580
688
|
|
|
@@ -632,6 +740,8 @@ export default defineComponent({
|
|
|
632
740
|
onLogout,
|
|
633
741
|
onProfile,
|
|
634
742
|
submitComment,
|
|
743
|
+
onImageWallScroll,
|
|
744
|
+
onGifSearch,
|
|
635
745
|
|
|
636
746
|
isLogin,
|
|
637
747
|
userInfo,
|
|
@@ -651,6 +761,10 @@ export default defineComponent({
|
|
|
651
761
|
emojiTabIndex,
|
|
652
762
|
showEmoji,
|
|
653
763
|
|
|
764
|
+
// gif
|
|
765
|
+
gifData: searchResults,
|
|
766
|
+
showGif,
|
|
767
|
+
|
|
654
768
|
// image
|
|
655
769
|
canUploadImage,
|
|
656
770
|
|
|
@@ -663,7 +777,10 @@ export default defineComponent({
|
|
|
663
777
|
editorRef,
|
|
664
778
|
emojiButtonRef,
|
|
665
779
|
emojiPopupRef,
|
|
780
|
+
gifButtonRef,
|
|
781
|
+
gifPopupRef,
|
|
666
782
|
imageUploadRef,
|
|
783
|
+
gifSearchInputRef,
|
|
667
784
|
};
|
|
668
785
|
},
|
|
669
786
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<span v-if="comment.label" class="wl-badge" v-text="comment.label" />
|
|
26
26
|
<span v-if="comment.sticky" class="wl-badge" v-text="locale.sticky" />
|
|
27
27
|
<span
|
|
28
|
-
v-if="comment.level && comment.level >= 0"
|
|
28
|
+
v-if="comment.level !== undefined && comment.level >= 0"
|
|
29
29
|
:class="`wl-badge level${comment.level}`"
|
|
30
30
|
v-text="locale[`level${comment.level}`] || `Level ${comment.level}`"
|
|
31
31
|
/>
|
package/src/components/Icons.ts
CHANGED
|
@@ -148,3 +148,23 @@ export const LoadingIcon: FunctionalComponent<{ size: number }> = ({ size }) =>
|
|
|
148
148
|
})
|
|
149
149
|
)
|
|
150
150
|
);
|
|
151
|
+
|
|
152
|
+
export const GifIcon: FunctionalComponent = () =>
|
|
153
|
+
h(
|
|
154
|
+
'svg',
|
|
155
|
+
{
|
|
156
|
+
width: 24,
|
|
157
|
+
height: 24,
|
|
158
|
+
fill: 'currentcolor',
|
|
159
|
+
viewBox: '0 0 24 24',
|
|
160
|
+
},
|
|
161
|
+
[
|
|
162
|
+
h('path', {
|
|
163
|
+
style: 'transform: translateY(0.5px)',
|
|
164
|
+
d: 'M18.968 10.5H15.968V11.484H17.984V12.984H15.968V15H14.468V9H18.968V10.5V10.5ZM8.984 9C9.26533 9 9.49967 9.09367 9.687 9.281C9.87433 9.46833 9.968 9.70267 9.968 9.984V10.5H6.499V13.5H8.468V12H9.968V14.016C9.968 14.2973 9.87433 14.5317 9.687 14.719C9.49967 14.9063 9.26533 15 8.984 15H5.984C5.70267 15 5.46833 14.9063 5.281 14.719C5.09367 14.5317 5 14.2973 5 14.016V9.985C5 9.70367 5.09367 9.46933 5.281 9.282C5.46833 9.09467 5.70267 9.001 5.984 9.001H8.984V9ZM11.468 9H12.968V15H11.468V9V9Z',
|
|
165
|
+
}),
|
|
166
|
+
h('path', {
|
|
167
|
+
d: 'M18.5 3H5.75C3.6875 3 2 4.6875 2 6.75V18C2 20.0625 3.6875 21.75 5.75 21.75H18.5C20.5625 21.75 22.25 20.0625 22.25 18V6.75C22.25 4.6875 20.5625 3 18.5 3ZM20.75 18C20.75 19.2375 19.7375 20.25 18.5 20.25H5.75C4.5125 20.25 3.5 19.2375 3.5 18V6.75C3.5 5.5125 4.5125 4.5 5.75 4.5H18.5C19.7375 4.5 20.75 5.5125 20.75 6.75V18Z',
|
|
168
|
+
}),
|
|
169
|
+
]
|
|
170
|
+
);
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<!-- forked from https://github.com/DerYeger/vue-masonry-wall/blob/master/src/masonry-wall.vue -->
|
|
2
|
+
|
|
3
|
+
<!-- MIT License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2021 Fuxing Loh, Jan Müller
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE. -->
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div ref="wall" class="wl-gallery" :style="{ gap: `${gap}px` }">
|
|
27
|
+
<div
|
|
28
|
+
v-for="(column, columnIndex) in columns"
|
|
29
|
+
:key="columnIndex"
|
|
30
|
+
class="wl-gallery-column"
|
|
31
|
+
:data-index="columnIndex"
|
|
32
|
+
:style="{ gap: `${gap}px` }"
|
|
33
|
+
>
|
|
34
|
+
<template v-for="itemIndex in column" :key="itemIndex">
|
|
35
|
+
<LoadingIcon
|
|
36
|
+
v-if="!state[items[itemIndex].src]"
|
|
37
|
+
:size="36"
|
|
38
|
+
style="margin: 20px auto"
|
|
39
|
+
/>
|
|
40
|
+
<img
|
|
41
|
+
class="wl-gallery-item"
|
|
42
|
+
:src="items[itemIndex].src"
|
|
43
|
+
:title="items[itemIndex].title"
|
|
44
|
+
loading="lazy"
|
|
45
|
+
@load="state[items[itemIndex].src] = true"
|
|
46
|
+
@click="$emit('insert', ``)"
|
|
47
|
+
/>
|
|
48
|
+
</template>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import {
|
|
55
|
+
defineComponent,
|
|
56
|
+
nextTick,
|
|
57
|
+
onBeforeUnmount,
|
|
58
|
+
onMounted,
|
|
59
|
+
ref,
|
|
60
|
+
watch,
|
|
61
|
+
} from 'vue';
|
|
62
|
+
|
|
63
|
+
import { LoadingIcon } from './Icons';
|
|
64
|
+
|
|
65
|
+
import type { PropType } from 'vue';
|
|
66
|
+
import type { WalineSearchResult } from '../typings';
|
|
67
|
+
|
|
68
|
+
type Column = number[];
|
|
69
|
+
|
|
70
|
+
export default defineComponent({
|
|
71
|
+
name: 'ImageWall',
|
|
72
|
+
|
|
73
|
+
components: {
|
|
74
|
+
LoadingIcon,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
props: {
|
|
78
|
+
items: { type: Array as PropType<WalineSearchResult[]>, default: () => [] },
|
|
79
|
+
columnWidth: { type: Number, default: 300 },
|
|
80
|
+
gap: { type: Number, default: 0 },
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
emit: ['insert'],
|
|
84
|
+
|
|
85
|
+
setup(props) {
|
|
86
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
87
|
+
const wall = ref<HTMLDivElement | null>(null);
|
|
88
|
+
const state = ref<Record<string, boolean>>({});
|
|
89
|
+
const columns = ref<Column[]>([]);
|
|
90
|
+
|
|
91
|
+
const getColumnCount = (): number => {
|
|
92
|
+
const count = Math.floor(
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
94
|
+
(wall.value!.getBoundingClientRect().width + props.gap) /
|
|
95
|
+
(props.columnWidth + props.gap)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return count > 0 ? count : 1;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createColumns = (count: number): Column[] =>
|
|
102
|
+
new Array(count).fill(null).map(() => []);
|
|
103
|
+
|
|
104
|
+
const fillColumns = async (itemIndex: number): Promise<void> => {
|
|
105
|
+
if (itemIndex >= props.items.length) return;
|
|
106
|
+
|
|
107
|
+
await nextTick();
|
|
108
|
+
|
|
109
|
+
const columnDivs = Array.from(
|
|
110
|
+
wall.value?.children || []
|
|
111
|
+
) as HTMLDivElement[];
|
|
112
|
+
|
|
113
|
+
const target = columnDivs.reduce((prev, curr) =>
|
|
114
|
+
curr.getBoundingClientRect().height <
|
|
115
|
+
prev.getBoundingClientRect().height
|
|
116
|
+
? curr
|
|
117
|
+
: prev
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
columns.value[Number(target.dataset.index)].push(itemIndex);
|
|
121
|
+
|
|
122
|
+
await fillColumns(itemIndex + 1);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const redraw = async (force = false): Promise<void> => {
|
|
126
|
+
if (columns.value.length === getColumnCount() && !force) return;
|
|
127
|
+
|
|
128
|
+
columns.value = createColumns(getColumnCount());
|
|
129
|
+
|
|
130
|
+
const scrollY = window.scrollY;
|
|
131
|
+
|
|
132
|
+
await fillColumns(0);
|
|
133
|
+
|
|
134
|
+
window.scrollTo({ top: scrollY });
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
watch(
|
|
138
|
+
() => [props.items],
|
|
139
|
+
() => {
|
|
140
|
+
state.value = {};
|
|
141
|
+
redraw(true);
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
watch(
|
|
145
|
+
() => [props.columnWidth, props.gap],
|
|
146
|
+
() => redraw()
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
onMounted(() => {
|
|
150
|
+
redraw(true);
|
|
151
|
+
resizeObserver = new ResizeObserver(() => redraw());
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
153
|
+
resizeObserver.observe(wall.value!);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
|
+
onBeforeUnmount(() => resizeObserver!.unobserve(wall.value!));
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
columns,
|
|
161
|
+
state,
|
|
162
|
+
wall,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
</script>
|
package/src/composables/like.ts
CHANGED
package/src/config/default.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WalineMeta } from '../typings';
|
|
1
|
+
import type { WalineMeta, WalineSearchOptions } from '../typings';
|
|
2
2
|
|
|
3
3
|
const availableMeta: WalineMeta[] = ['nick', 'mail', 'link'];
|
|
4
4
|
|
|
@@ -19,3 +19,93 @@ export const defaultTexRenderer = (blockMode: boolean): string =>
|
|
|
19
19
|
blockMode === true
|
|
20
20
|
? '<p class="wl-tex">Tex is not available in preview</p>'
|
|
21
21
|
: '<span class="wl-tex">Tex is not available in preview</span>';
|
|
22
|
+
|
|
23
|
+
export const getDefaultSearchOptions = (): WalineSearchOptions => {
|
|
24
|
+
interface FetchGifRequest {
|
|
25
|
+
keyword: string;
|
|
26
|
+
pos?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type GifFormat =
|
|
30
|
+
| 'gif'
|
|
31
|
+
| 'mediumgif'
|
|
32
|
+
| 'tinygif'
|
|
33
|
+
| 'nanogif'
|
|
34
|
+
| 'mp4'
|
|
35
|
+
| 'loopedmp4'
|
|
36
|
+
| 'tinymp4'
|
|
37
|
+
| 'nanomp4'
|
|
38
|
+
| 'webm'
|
|
39
|
+
| 'tinywebm'
|
|
40
|
+
| 'nanowebm';
|
|
41
|
+
|
|
42
|
+
interface MediaObject {
|
|
43
|
+
preview: string;
|
|
44
|
+
url: string;
|
|
45
|
+
dims: number[];
|
|
46
|
+
size: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface GifObject {
|
|
50
|
+
created: number;
|
|
51
|
+
hasaudio: boolean;
|
|
52
|
+
id: string;
|
|
53
|
+
media: Record<GifFormat, MediaObject>[];
|
|
54
|
+
tags: string[];
|
|
55
|
+
title: string;
|
|
56
|
+
itemurl: string;
|
|
57
|
+
hascaption: boolean;
|
|
58
|
+
url: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface FetchGifResponse {
|
|
62
|
+
next: string;
|
|
63
|
+
results: GifObject[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const state = {
|
|
67
|
+
next: '',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const fetchGif = ({
|
|
71
|
+
keyword,
|
|
72
|
+
pos,
|
|
73
|
+
}: FetchGifRequest): Promise<FetchGifResponse> => {
|
|
74
|
+
const baseUrl = `https://g.tenor.com/v1/search`;
|
|
75
|
+
const query = new URLSearchParams('media_filter=minimal');
|
|
76
|
+
query.set('key', 'PAY5JLFIH6V6');
|
|
77
|
+
query.set('limit', '20');
|
|
78
|
+
query.set('pos', pos || '');
|
|
79
|
+
query.set('q', keyword);
|
|
80
|
+
|
|
81
|
+
return fetch(`${baseUrl}?${query.toString()}`, {
|
|
82
|
+
headers: {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
.then((resp) => resp.json() as Promise<FetchGifResponse>)
|
|
88
|
+
.catch(() => ({ next: pos || '', results: [] }));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
search: (word = '') =>
|
|
93
|
+
fetchGif({ keyword: word }).then((resp) => {
|
|
94
|
+
state.next = resp.next;
|
|
95
|
+
|
|
96
|
+
return resp.results.map((item) => ({
|
|
97
|
+
title: item.title,
|
|
98
|
+
src: item.media[0].tinygif.url,
|
|
99
|
+
}));
|
|
100
|
+
}),
|
|
101
|
+
more: (word) =>
|
|
102
|
+
fetchGif({ keyword: word, pos: state.next }).then((resp) => {
|
|
103
|
+
state.next = resp.next;
|
|
104
|
+
|
|
105
|
+
return resp.results.map((item) => ({
|
|
106
|
+
title: item.title,
|
|
107
|
+
src: item.media[0].tinygif.url,
|
|
108
|
+
}));
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
};
|
package/src/config/i18n/en.ts
CHANGED
package/src/config/i18n/jp.ts
CHANGED
package/src/config/i18n/pt-BR.ts
CHANGED
|
@@ -33,4 +33,12 @@ export default generateLocale([
|
|
|
33
33
|
'Palavras',
|
|
34
34
|
'Favor enviar comentário com $0 a $1 palavras!\n Número de palavras atuais: $2',
|
|
35
35
|
'Anônimo',
|
|
36
|
+
'Dwarves',
|
|
37
|
+
'Hobbits',
|
|
38
|
+
'Ents',
|
|
39
|
+
'Wizards',
|
|
40
|
+
'Elves',
|
|
41
|
+
'Maiar',
|
|
42
|
+
'GIF',
|
|
43
|
+
'Pesquisar GIF',
|
|
36
44
|
]);
|
package/src/config/i18n/ru.ts
CHANGED