@waline/client 2.5.0 → 2.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/client",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "client for waline comment system",
5
5
  "keywords": [
6
6
  "valine",
@@ -56,15 +56,6 @@
56
56
  "dist",
57
57
  "src"
58
58
  ],
59
- "scripts": {
60
- "build": "pnpm rollup && pnpm style",
61
- "clean": "rimraf ./dist",
62
- "dev": "vite -c config/vite.config.js",
63
- "lint": "eslint --ext .ts,.vue .",
64
- "prepublish": "pnpm clean && pnpm build",
65
- "rollup": "rollup -c ./config/rollup.config.js",
66
- "style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
67
- },
68
59
  "browserslist": {
69
60
  "production": [
70
61
  ">0.5%",
@@ -80,26 +71,25 @@
80
71
  ]
81
72
  },
82
73
  "dependencies": {
83
- "@vueuse/core": "^8.4.2",
74
+ "@vueuse/core": "^8.5.0",
84
75
  "autosize": "^5.0.1",
85
- "marked": "^4.0.15",
86
- "vue": "^3.2.33"
76
+ "marked": "^4.0.16",
77
+ "vue": "^3.2.35"
87
78
  },
88
79
  "devDependencies": {
89
- "@babel/core": "^7.17.10",
90
- "@babel/preset-env": "^7.17.10",
80
+ "@babel/core": "^7.18.0",
81
+ "@babel/preset-env": "^7.18.0",
91
82
  "@rollup/plugin-babel": "^5.3.1",
92
83
  "@rollup/plugin-commonjs": "^22.0.0",
93
84
  "@rollup/plugin-node-resolve": "^13.3.0",
94
85
  "@rollup/plugin-replace": "^4.0.0",
95
86
  "@types/autosize": "^4.0.1",
96
87
  "@types/marked": "^4.0.3",
97
- "@types/node": "^17.0.33",
88
+ "@types/node": "^17.0.35",
98
89
  "@vitejs/plugin-vue": "^2.3.3",
99
- "@vue/compiler-sfc": "^3.2.33",
100
- "@yeger/vue-masonry-wall": "^3.0.31",
90
+ "@vue/compiler-sfc": "^3.2.35",
101
91
  "rimraf": "^3.0.2",
102
- "rollup": "^2.73.0",
92
+ "rollup": "^2.74.1",
103
93
  "rollup-plugin-dts": "^4.2.1",
104
94
  "rollup-plugin-terser": "^7.0.2",
105
95
  "rollup-plugin-ts": "^2.0.7",
@@ -108,5 +98,14 @@
108
98
  },
109
99
  "engines": {
110
100
  "node": ">=14"
101
+ },
102
+ "scripts": {
103
+ "build": "pnpm rollup && pnpm style",
104
+ "clean": "rimraf ./dist",
105
+ "dev": "vite -c config/vite.config.js",
106
+ "lint": "eslint --ext .ts,.vue .",
107
+ "prepublish": "pnpm clean && pnpm build",
108
+ "rollup": "rollup -c ./config/rollup.config.js",
109
+ "style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
111
110
  }
112
- }
111
+ }
@@ -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}`"
@@ -174,30 +174,14 @@
174
174
  @input="onGifSearch"
175
175
  />
176
176
 
177
- <masonry-wall
178
- class="wl-gif-waterfall"
177
+ <ImageWall
179
178
  :items="gifData.list"
180
- :ssr-columns="2"
181
179
  :column-width="200"
182
180
  :gap="6"
183
- @scroll="onGifMasonryScroll"
181
+ @insert="insert($event)"
182
+ @scroll="onImageWallScroll"
184
183
  >
185
- <template #default="{ item }">
186
- <img
187
- @click="insert(`![](${item.media[0].tinygif.url})`)"
188
- :src="item.media[0].tinygif.url"
189
- :title="item.title"
190
- loading="lazy"
191
- :style="{
192
- width: '200px',
193
- height:
194
- (200 * item.media[0].tinygif.dims[1]) /
195
- item.media[0].tinygif.dims[0] +
196
- 'px',
197
- }"
198
- />
199
- </template>
200
- </masonry-wall>
184
+ </ImageWall>
201
185
 
202
186
  <div v-if="gifData.loading" class="wl-loading">
203
187
  <LoadingIcon :size="30" />
@@ -272,6 +256,7 @@ import {
272
256
  watch,
273
257
  } from 'vue';
274
258
 
259
+ import ImageWall from './ImageWall.vue';
275
260
  import {
276
261
  CloseIcon,
277
262
  EmojiIcon,
@@ -283,20 +268,23 @@ import {
283
268
  } from './Icons';
284
269
  import { useEditor, useUserMeta, useUserInfo } from '../composables';
285
270
  import {
271
+ fetchGif,
272
+ getEmojis,
286
273
  getImagefromDataTransfer,
287
- parseMarkdown,
288
274
  getWordNumber,
289
275
  parseEmoji,
276
+ parseMarkdown,
290
277
  postComment,
291
- getEmojis,
292
- fetchGif,
293
- FetchGifResponse,
294
278
  throttle,
295
279
  } from '../utils';
296
280
 
297
281
  import type { ComputedRef, DeepReadonly } from 'vue';
298
282
  import type { WalineCommentData, WalineImageUploader } from '../typings';
299
- import type { WalineConfig, WalineEmojiConfig } from '../utils';
283
+ import type {
284
+ FetchGifResponse,
285
+ WalineConfig,
286
+ WalineEmojiConfig,
287
+ } from '../utils';
300
288
 
301
289
  export default defineComponent({
302
290
  name: 'CommentBox',
@@ -305,6 +293,7 @@ export default defineComponent({
305
293
  CloseIcon,
306
294
  EmojiIcon,
307
295
  ImageIcon,
296
+ ImageWall,
308
297
  MarkdownIcon,
309
298
  PreviewIcon,
310
299
  LoadingIcon,
@@ -621,21 +610,15 @@ export default defineComponent({
621
610
  showGif.value = false;
622
611
  };
623
612
 
624
- const onGifSearch = throttle(async (event: Event) => {
625
- gifData.value.cursor = '';
626
- gifData.value.list = [];
627
- onGifMasonryScroll(event);
628
- });
629
-
630
- const onGifMasonryScroll = async (event: Event): Promise<void> => {
613
+ const onImageWallScroll = async (event: Event): Promise<void> => {
631
614
  const { scrollTop, clientHeight, scrollHeight } =
632
615
  event.target as HTMLDivElement;
633
616
  const percent = (clientHeight + scrollTop) / scrollHeight;
634
- if (percent < 0.9 || gifData.value.loading) {
635
- return;
636
- }
617
+
618
+ if (percent < 0.9 || gifData.value.loading) return;
637
619
 
638
620
  gifData.value.loading = true;
621
+
639
622
  const data = await fetchGif({
640
623
  keyword: gifSearchInputRef.value?.value || '',
641
624
  pos: gifData.value.cursor,
@@ -645,11 +628,18 @@ export default defineComponent({
645
628
 
646
629
  gifData.value.cursor = data.next;
647
630
  gifData.value.list = gifData.value.list.concat(data.results);
631
+
648
632
  setTimeout(() => {
649
633
  (event.target as HTMLDivElement).scrollTop = scrollTop;
650
634
  }, 50);
651
635
  };
652
636
 
637
+ const onGifSearch = throttle(async (event: Event) => {
638
+ gifData.value.cursor = '';
639
+ gifData.value.list = [];
640
+ onImageWallScroll(event);
641
+ });
642
+
653
643
  // update wordNumber
654
644
  watch(
655
645
  [config, wordNumber],
@@ -675,10 +665,8 @@ export default defineComponent({
675
665
  { immediate: true }
676
666
  );
677
667
 
678
- watch([showGif], async ([showGif]) => {
679
- if (!showGif) {
680
- return;
681
- }
668
+ watch(showGif, async (showGif) => {
669
+ if (!showGif) return;
682
670
 
683
671
  gifData.value.loading = true;
684
672
  const data = await fetchGif({ keyword: '' }).finally(() => {
@@ -746,7 +734,7 @@ export default defineComponent({
746
734
  onLogout,
747
735
  onProfile,
748
736
  submitComment,
749
- onGifMasonryScroll,
737
+ onImageWallScroll,
750
738
  onGifSearch,
751
739
 
752
740
  isLogin,
@@ -768,6 +756,7 @@ export default defineComponent({
768
756
  showEmoji,
769
757
 
770
758
  // gif
759
+ gifData,
771
760
  showGif,
772
761
 
773
762
  // image
@@ -786,7 +775,6 @@ export default defineComponent({
786
775
  gifPopupRef,
787
776
  imageUploadRef,
788
777
  gifSearchInputRef,
789
- gifData,
790
778
  };
791
779
  },
792
780
  });
@@ -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
  />
@@ -0,0 +1,152 @@
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
+ <img
35
+ v-for="itemIndex in column"
36
+ :key="itemIndex"
37
+ class="wl-gallery-item"
38
+ :src="items[itemIndex].media[0].tinygif.url"
39
+ :title="items[itemIndex].title"
40
+ loading="lazy"
41
+ @click="
42
+ $emit('insert', `![](${items[itemIndex].media[0].tinygif.url})`)
43
+ "
44
+ />
45
+ </div>
46
+ </div>
47
+ </template>
48
+
49
+ <script lang="ts">
50
+ import {
51
+ defineComponent,
52
+ nextTick,
53
+ onBeforeUnmount,
54
+ onMounted,
55
+ ref,
56
+ watch,
57
+ } from 'vue';
58
+
59
+ import type { PropType } from 'vue';
60
+
61
+ type Column = number[];
62
+
63
+ export default defineComponent({
64
+ props: {
65
+ columnWidth: { type: Number, default: 300 },
66
+ items: { type: Array as PropType<unknown[]>, default: () => [] },
67
+ gap: { type: Number, default: 0 },
68
+ rtl: { type: Boolean, default: false },
69
+ },
70
+
71
+ emit: ['insert'],
72
+
73
+ setup(props) {
74
+ let resizeObserver: ResizeObserver | null = null;
75
+ const wall = ref<HTMLDivElement | null>(null);
76
+
77
+ const columns = ref<Column[]>([]);
78
+
79
+ const getColumnCount = (): number => {
80
+ const count = Math.floor(
81
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82
+ (wall.value!.getBoundingClientRect().width + props.gap) /
83
+ (props.columnWidth + props.gap)
84
+ );
85
+
86
+ return count > 0 ? count : 1;
87
+ };
88
+
89
+ const createColumns = (count: number): Column[] =>
90
+ new Array(count).fill(null).map(() => []);
91
+
92
+ const fillColumns = async (itemIndex: number): Promise<void> => {
93
+ if (itemIndex >= props.items.length) return;
94
+
95
+ await nextTick();
96
+
97
+ const columnDivs = Array.from(
98
+ wall.value?.children || []
99
+ ) as HTMLDivElement[];
100
+
101
+ if (props.rtl) columnDivs.reverse();
102
+
103
+ const target = columnDivs.reduce((prev, curr) =>
104
+ curr.getBoundingClientRect().height <
105
+ prev.getBoundingClientRect().height
106
+ ? curr
107
+ : prev
108
+ );
109
+
110
+ columns.value[Number(target.dataset.index)].push(itemIndex);
111
+
112
+ await fillColumns(itemIndex + 1);
113
+ };
114
+
115
+ const redraw = async (force = false): Promise<void> => {
116
+ if (columns.value.length === getColumnCount() && !force) return;
117
+
118
+ columns.value = createColumns(getColumnCount());
119
+
120
+ const scrollY = window.scrollY;
121
+
122
+ await fillColumns(0);
123
+
124
+ window.scrollTo({ top: scrollY });
125
+ };
126
+
127
+ watch(
128
+ () => [props.items, props.rtl],
129
+ () => redraw(true)
130
+ );
131
+ watch(
132
+ () => [props.columnWidth, props.gap],
133
+ () => redraw()
134
+ );
135
+
136
+ onMounted(() => {
137
+ redraw(true);
138
+ resizeObserver = new ResizeObserver(() => redraw());
139
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
140
+ resizeObserver.observe(wall.value!);
141
+ });
142
+
143
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
144
+ onBeforeUnmount(() => resizeObserver!.unobserve(wall.value!));
145
+
146
+ return {
147
+ columns,
148
+ wall,
149
+ };
150
+ },
151
+ });
152
+ </script>
@@ -2,7 +2,7 @@ import { useStorage } from '@vueuse/core';
2
2
 
3
3
  import type { Ref } from 'vue';
4
4
 
5
- const LIKE_KEY = 'WALIKE_LIKE';
5
+ const LIKE_KEY = 'WALINE_LIKE';
6
6
 
7
7
  export type LikeID = number | string;
8
8
 
package/src/init.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { createApp, h, reactive, watchEffect } from 'vue';
2
- import MasonryWall from '@yeger/vue-masonry-wall';
3
2
 
4
3
  import Waline from './components/Waline.vue';
5
4
  import { commentCount } from './comment';
@@ -81,12 +80,8 @@ export const init = ({
81
80
  ? createApp(() => h(Waline, { path: state.path, ...props }))
82
81
  : null;
83
82
 
84
- if (app) {
85
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
86
- app.use(MasonryWall);
87
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
88
- app.mount(root!);
89
- }
83
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84
+ if (app) app.mount(root!);
90
85
 
91
86
  updateCommentCount();
92
87
  updatePageviewCount();
@@ -1,118 +1,116 @@
1
- @mixin emoji() {
2
- .wl-emoji-popup {
3
- position: absolute;
4
- top: 100%;
5
- left: 1.25em;
6
- z-index: 10;
7
-
8
- max-width: 526px;
9
- border: var(--waline-border);
10
- border-radius: 6px;
11
-
12
- background: var(--waline-bgcolor);
13
- box-shadow: var(--waline-box-shadow);
14
-
15
- opacity: 0;
16
- visibility: hidden;
17
-
18
- transition: transform 0.2s ease-out, opacity 0.2s ease-out;
19
- transform: scale(0.9, 0.9);
20
- transform-origin: 0 0;
21
-
22
- &.display {
23
- opacity: 1;
24
- visibility: visible;
25
- transform: none;
26
- }
1
+ .wl-emoji-popup {
2
+ position: absolute;
3
+ top: 100%;
4
+ left: 1.25em;
5
+ z-index: 10;
6
+
7
+ max-width: 526px;
8
+ border: var(--waline-border);
9
+ border-radius: 6px;
10
+
11
+ background: var(--waline-bgcolor);
12
+ box-shadow: var(--waline-box-shadow);
13
+
14
+ opacity: 0;
15
+ visibility: hidden;
16
+
17
+ transition: transform 0.2s ease-out, opacity 0.2s ease-out;
18
+ transform: scale(0.9, 0.9);
19
+ transform-origin: 0 0;
20
+
21
+ &.display {
22
+ opacity: 1;
23
+ visibility: visible;
24
+ transform: none;
25
+ }
26
+
27
+ button {
28
+ display: inline-block;
29
+ vertical-align: middle;
30
+
31
+ width: 2em;
32
+ margin: 0.125em;
33
+ padding: 0;
34
+ border-width: 0;
35
+
36
+ background: transparent;
27
37
 
28
- button {
29
- display: inline-block;
30
- vertical-align: middle;
38
+ font-size: inherit;
39
+ line-height: 2;
40
+ text-align: center;
31
41
 
32
- width: 2em;
33
- margin: 0.125em;
34
- padding: 0;
35
- border-width: 0;
42
+ cursor: pointer;
36
43
 
37
- background: transparent;
44
+ &:hover {
45
+ background: var(--waline-bgcolor-hover);
46
+ }
47
+ }
38
48
 
39
- font-size: inherit;
40
- line-height: 2;
41
- text-align: center;
49
+ .wl-emoji {
50
+ display: inline-block;
51
+ vertical-align: middle;
52
+ max-width: 1.5em;
53
+ max-height: 1.5em;
54
+ }
42
55
 
43
- cursor: pointer;
56
+ .wl-tab-wrapper {
57
+ overflow-y: auto;
58
+ max-height: 145px;
59
+ padding: 0.5em;
44
60
 
45
- &:hover {
46
- background: var(--waline-bgcolor-hover);
47
- }
61
+ &::-webkit-scrollbar {
62
+ width: 6px;
63
+ height: 6px;
48
64
  }
49
65
 
50
- .wl-emoji {
51
- display: inline-block;
52
- vertical-align: middle;
53
- max-width: 1.5em;
54
- max-height: 1.5em;
66
+ &::-webkit-scrollbar-track-piece:vertical {
67
+ -webkit-border-radius: 6px;
68
+ border-radius: 6px;
69
+ background: rgb(0 0 0 / 10%);
55
70
  }
56
71
 
57
- .wl-tab-wrapper {
58
- overflow-y: auto;
59
- max-height: 145px;
60
- padding: 0.5em;
61
-
62
- &::-webkit-scrollbar {
63
- width: 6px;
64
- height: 6px;
65
- }
66
-
67
- &::-webkit-scrollbar-track-piece:vertical {
68
- -webkit-border-radius: 6px;
69
- border-radius: 6px;
70
- background: rgb(0 0 0 / 10%);
71
- }
72
-
73
- &::-webkit-scrollbar-thumb:vertical {
74
- width: 6px;
75
- -webkit-border-radius: 6px;
76
- border-radius: 6px;
77
- background: var(--waline-theme-color);
78
- }
72
+ &::-webkit-scrollbar-thumb:vertical {
73
+ width: 6px;
74
+ -webkit-border-radius: 6px;
75
+ border-radius: 6px;
76
+ background: var(--waline-theme-color);
79
77
  }
78
+ }
80
79
 
81
- .wl-tabs {
82
- position: relative;
83
- height: 2em;
84
- padding: 0 6px 1px;
80
+ .wl-tabs {
81
+ position: relative;
82
+ height: 2em;
83
+ padding: 0 6px 1px;
85
84
 
86
- &::before {
87
- content: ' ';
85
+ &::before {
86
+ content: ' ';
88
87
 
89
- position: absolute;
90
- top: 0;
91
- right: 0;
92
- left: 0;
93
- z-index: 2;
88
+ position: absolute;
89
+ top: 0;
90
+ right: 0;
91
+ left: 0;
92
+ z-index: 2;
94
93
 
95
- height: 1px;
94
+ height: 1px;
96
95
 
97
- background: var(--waline-border-color);
98
- }
96
+ background: var(--waline-border-color);
99
97
  }
98
+ }
100
99
 
101
- .wl-tab {
102
- position: relative;
103
- margin: 0;
104
- padding: 0 0.5em;
100
+ .wl-tab {
101
+ position: relative;
102
+ margin: 0;
103
+ padding: 0 0.5em;
105
104
 
106
- &.active {
107
- z-index: 3;
105
+ &.active {
106
+ z-index: 3;
108
107
 
109
- border: 1px solid var(--waline-border-color);
110
- border-top-width: 0;
111
- border-bottom-right-radius: 6px;
112
- border-bottom-left-radius: 6px;
108
+ border: 1px solid var(--waline-border-color);
109
+ border-top-width: 0;
110
+ border-bottom-right-radius: 6px;
111
+ border-bottom-left-radius: 6px;
113
112
 
114
- background: var(--waline-bgcolor);
115
- }
113
+ background: var(--waline-bgcolor);
116
114
  }
117
115
  }
118
116
  }