pukaad-ui-lib 1.193.0 → 1.195.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/module.json +1 -1
- package/dist/runtime/components/card/card-reaction.vue +1 -1
- package/dist/runtime/components/comment.vue +105 -47
- package/dist/runtime/components/input/input-file.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-file.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-password.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-password.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-slider.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-slider.vue.d.ts +1 -1
- package/dist/runtime/components/input/input-textarea.d.vue.ts +1 -1
- package/dist/runtime/components/input/input-textarea.vue.d.ts +1 -1
- package/dist/runtime/components/modal/modal-password-confirmed.d.vue.ts +1 -1
- package/dist/runtime/components/modal/modal-password-confirmed.vue.d.ts +1 -1
- package/dist/runtime/components/modal/modal-password-verify.d.vue.ts +1 -1
- package/dist/runtime/components/modal/modal-password-verify.vue.d.ts +1 -1
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
<script setup>
|
|
34
34
|
import { computed, reactive, ref, watch } from "vue";
|
|
35
35
|
import { useApi } from "../../composables/useApi";
|
|
36
|
+
const api = useApi();
|
|
36
37
|
const isOpenModalShare = ref(false);
|
|
37
38
|
const props = defineProps({
|
|
38
39
|
itemCount: { type: Object, required: false, default: () => ({
|
|
@@ -119,7 +120,6 @@ async function toggleLike() {
|
|
|
119
120
|
counts.liked += reaction.liked ? 1 : -1;
|
|
120
121
|
if (props.state === "blog" && props.id) {
|
|
121
122
|
try {
|
|
122
|
-
const api = useApi();
|
|
123
123
|
await api(`/blogs/${props.id}/like`, {
|
|
124
124
|
method: reaction.liked ? "POST" : "DELETE"
|
|
125
125
|
});
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
class="w-full min-h-[36px] px-[12px] py-[5px] rounded-md border border-mercury font-body-large focus:outline-none focus:ring-2 focus:ring-primary break-words bg-white empty:before:content-[attr(data-placeholder)] empty:before:text-gray"
|
|
13
13
|
data-placeholder="แสดงความคิดเห็น"
|
|
14
14
|
@focus="isShowMainActions = true"
|
|
15
|
-
@input="
|
|
16
|
-
mainCommentInput = $event.target.innerText.trim()
|
|
17
|
-
"
|
|
15
|
+
@input="onMainCommentInput"
|
|
18
16
|
@keydown.enter.exact.prevent="onSendComment"
|
|
17
|
+
@keydown.shift.enter.prevent="onInsertLineBreak"
|
|
19
18
|
/>
|
|
20
19
|
</div>
|
|
21
20
|
<div v-if="isShowMainActions" class="flex justify-end gap-[8px]">
|
|
@@ -55,13 +54,13 @@
|
|
|
55
54
|
:ref="
|
|
56
55
|
(el) => registerContentRef(el, `comment-${cmt.id}`)
|
|
57
56
|
"
|
|
58
|
-
class="font-body-large"
|
|
57
|
+
class="font-body-large whitespace-pre-wrap"
|
|
59
58
|
>
|
|
60
59
|
<template
|
|
61
60
|
v-if="
|
|
62
61
|
isContentExpanded(`comment-${cmt.id}`) || !isContentOverflowing(`comment-${cmt.id}`)
|
|
63
62
|
"
|
|
64
|
-
>{{ cmt.content }}</template
|
|
63
|
+
>{{ normalizeContent(cmt.content) }}</template
|
|
65
64
|
>
|
|
66
65
|
<template v-else
|
|
67
66
|
>{{ getTruncatedText(`comment-${cmt.id}`) }}...<span
|
|
@@ -135,6 +134,7 @@
|
|
|
135
134
|
data-placeholder="เขียนการตอบกลับ..."
|
|
136
135
|
@input="onReplyInput($event, cmt.id)"
|
|
137
136
|
@keydown.enter.exact.prevent="onSendReplyComment(cmt)"
|
|
137
|
+
@keydown.shift.enter.prevent="onInsertLineBreak"
|
|
138
138
|
/>
|
|
139
139
|
</div>
|
|
140
140
|
</div>
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
:ref="
|
|
190
190
|
(el) => registerContentRef(el, `reply-${reply.id}`)
|
|
191
191
|
"
|
|
192
|
-
class="font-body-large"
|
|
192
|
+
class="font-body-large whitespace-pre-wrap"
|
|
193
193
|
>
|
|
194
194
|
<template
|
|
195
195
|
v-if="
|
|
@@ -200,9 +200,14 @@
|
|
|
200
200
|
@click="onViewProfileComment(reply.reply_to.path_name)"
|
|
201
201
|
class="text-primary cursor-pointer mr-1"
|
|
202
202
|
>{{ reply.reply_to.name }}</span
|
|
203
|
-
>{{ reply.content }}</template
|
|
203
|
+
>{{ normalizeContent(reply.content) }}</template
|
|
204
204
|
>
|
|
205
205
|
<template v-else
|
|
206
|
+
><span
|
|
207
|
+
v-if="reply.reply_to"
|
|
208
|
+
@click="onViewProfileComment(reply.reply_to.path_name)"
|
|
209
|
+
class="text-primary cursor-pointer mr-1"
|
|
210
|
+
>{{ reply.reply_to.name }}</span
|
|
206
211
|
>{{ getTruncatedText(`reply-${reply.id}`) }}...<span
|
|
207
212
|
class="text-primary cursor-pointer"
|
|
208
213
|
@click="toggleContentExpand(`reply-${reply.id}`)"
|
|
@@ -252,6 +257,7 @@
|
|
|
252
257
|
data-placeholder="เขียนการตอบกลับ..."
|
|
253
258
|
@input="onReplyInput($event, reply.id)"
|
|
254
259
|
@keydown.enter.exact.prevent="onSendReplyComment(cmt)"
|
|
260
|
+
@keydown.shift.enter.prevent="onInsertLineBreak"
|
|
255
261
|
/>
|
|
256
262
|
</div>
|
|
257
263
|
</div>
|
|
@@ -395,46 +401,62 @@ const onToggleReplyComment = (id) => {
|
|
|
395
401
|
const isContentExpanded = (id) => expandedContentIds.value.includes(id);
|
|
396
402
|
const isContentOverflowing = (id) => typeof truncatedTexts.value[id] === "string";
|
|
397
403
|
const getTruncatedText = (id) => truncatedTexts.value[id];
|
|
404
|
+
const normalizeContent = (text) => text.replace(/\n{3,}/g, "\n\n");
|
|
405
|
+
const measureContent = (el, id) => {
|
|
406
|
+
const rect = el.getBoundingClientRect();
|
|
407
|
+
const width = rect.width;
|
|
408
|
+
if (!width) return;
|
|
409
|
+
const fullText = el.textContent ?? "";
|
|
410
|
+
const style = getComputedStyle(el);
|
|
411
|
+
const lineHeight = parseFloat(style.lineHeight) || parseFloat(style.fontSize) * 1.5;
|
|
412
|
+
const maxH = lineHeight * 3;
|
|
413
|
+
const clone = document.createElement("div");
|
|
414
|
+
clone.style.cssText = [
|
|
415
|
+
`visibility:hidden`,
|
|
416
|
+
`position:fixed`,
|
|
417
|
+
`top:-9999px`,
|
|
418
|
+
`width:${width}px`,
|
|
419
|
+
`font-size:${style.fontSize}`,
|
|
420
|
+
`font-family:${style.fontFamily}`,
|
|
421
|
+
`font-weight:${style.fontWeight}`,
|
|
422
|
+
`line-height:${style.lineHeight}`,
|
|
423
|
+
`letter-spacing:${style.letterSpacing}`,
|
|
424
|
+
`word-break:break-word`,
|
|
425
|
+
`white-space:pre-wrap`
|
|
426
|
+
].join(";");
|
|
427
|
+
clone.textContent = fullText;
|
|
428
|
+
document.body.appendChild(clone);
|
|
429
|
+
if (clone.scrollHeight <= maxH + 2) {
|
|
430
|
+
clone.remove();
|
|
431
|
+
truncatedTexts.value[id] = null;
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
let lo = 0, hi = fullText.length;
|
|
435
|
+
while (lo < hi) {
|
|
436
|
+
const mid = Math.floor((lo + hi + 1) / 2);
|
|
437
|
+
clone.textContent = fullText.slice(0, mid) + "...\u0E14\u0E39\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21";
|
|
438
|
+
if (clone.scrollHeight <= maxH + 2) lo = mid;
|
|
439
|
+
else hi = mid - 1;
|
|
440
|
+
}
|
|
441
|
+
clone.remove();
|
|
442
|
+
truncatedTexts.value[id] = fullText.slice(0, lo);
|
|
443
|
+
};
|
|
398
444
|
const registerContentRef = (el, id) => {
|
|
399
445
|
if (!el || id in truncatedTexts.value) return;
|
|
400
446
|
nextTick(() => {
|
|
401
447
|
const rect = el.getBoundingClientRect();
|
|
402
448
|
const width = rect.width;
|
|
403
|
-
if (!width)
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
`position:fixed`,
|
|
412
|
-
`top:-9999px`,
|
|
413
|
-
`width:${width}px`,
|
|
414
|
-
`font-size:${style.fontSize}`,
|
|
415
|
-
`font-family:${style.fontFamily}`,
|
|
416
|
-
`font-weight:${style.fontWeight}`,
|
|
417
|
-
`line-height:${style.lineHeight}`,
|
|
418
|
-
`letter-spacing:${style.letterSpacing}`,
|
|
419
|
-
`word-break:break-word`,
|
|
420
|
-
`white-space:normal`
|
|
421
|
-
].join(";");
|
|
422
|
-
clone.textContent = fullText;
|
|
423
|
-
document.body.appendChild(clone);
|
|
424
|
-
if (clone.scrollHeight <= maxH + 2) {
|
|
425
|
-
clone.remove();
|
|
426
|
-
truncatedTexts.value[id] = null;
|
|
449
|
+
if (!width) {
|
|
450
|
+
const ro = new ResizeObserver((_, obs) => {
|
|
451
|
+
if (el.getBoundingClientRect().width > 0) {
|
|
452
|
+
obs.disconnect();
|
|
453
|
+
measureContent(el, id);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
ro.observe(el);
|
|
427
457
|
return;
|
|
428
458
|
}
|
|
429
|
-
|
|
430
|
-
while (lo < hi) {
|
|
431
|
-
const mid = Math.floor((lo + hi + 1) / 2);
|
|
432
|
-
clone.textContent = fullText.slice(0, mid) + "...\u0E14\u0E39\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21";
|
|
433
|
-
if (clone.scrollHeight <= maxH + 2) lo = mid;
|
|
434
|
-
else hi = mid - 1;
|
|
435
|
-
}
|
|
436
|
-
clone.remove();
|
|
437
|
-
truncatedTexts.value[id] = fullText.slice(0, lo);
|
|
459
|
+
measureContent(el, id);
|
|
438
460
|
});
|
|
439
461
|
};
|
|
440
462
|
const toggleContentExpand = (id) => {
|
|
@@ -473,7 +495,7 @@ const setReplyInputRef = (el, id) => {
|
|
|
473
495
|
};
|
|
474
496
|
const onReplyComment = (id, user) => {
|
|
475
497
|
replyingToId.value = id;
|
|
476
|
-
replyToUser.value = user;
|
|
498
|
+
replyToUser.value = user.id !== currentUser.id ? user : null;
|
|
477
499
|
replyTextMap.value[id] = "";
|
|
478
500
|
nextTick(() => {
|
|
479
501
|
const el = replyInputRefs.value[id];
|
|
@@ -498,16 +520,52 @@ const onReplyComment = (id, user) => {
|
|
|
498
520
|
el.focus();
|
|
499
521
|
});
|
|
500
522
|
};
|
|
523
|
+
const readTextWithBreaks = (el, skipMention = false) => {
|
|
524
|
+
const nodes = Array.from(el.childNodes);
|
|
525
|
+
while (nodes.length && nodes.at(-1)?.nodeName === "BR") nodes.pop();
|
|
526
|
+
let text = "";
|
|
527
|
+
nodes.forEach((node) => {
|
|
528
|
+
if (skipMention && node.dataset?.type === "mention")
|
|
529
|
+
return;
|
|
530
|
+
if (node.nodeName === "BR") {
|
|
531
|
+
text += "\n";
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
if (node.nodeName === "DIV") {
|
|
535
|
+
text += "\n" + node.innerText;
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
text += node.textContent ?? "";
|
|
539
|
+
});
|
|
540
|
+
return text.replace(/\n{3,}/g, "\n\n").replace(/^\u00A0/, "").trim();
|
|
541
|
+
};
|
|
542
|
+
const onInsertLineBreak = (event) => {
|
|
543
|
+
const el = event.target;
|
|
544
|
+
const sel = window.getSelection();
|
|
545
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
546
|
+
const range = sel.getRangeAt(0);
|
|
547
|
+
range.deleteContents();
|
|
548
|
+
const br = document.createElement("br");
|
|
549
|
+
range.insertNode(br);
|
|
550
|
+
if (!br.nextSibling || br.nextSibling.nodeName !== "BR" && br.nextSibling.textContent === "") {
|
|
551
|
+
const phantom = document.createElement("br");
|
|
552
|
+
br.after(phantom);
|
|
553
|
+
}
|
|
554
|
+
range.setStartAfter(br);
|
|
555
|
+
range.collapse(true);
|
|
556
|
+
sel.removeAllRanges();
|
|
557
|
+
sel.addRange(range);
|
|
558
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
559
|
+
};
|
|
560
|
+
const onMainCommentInput = (event) => {
|
|
561
|
+
const el = event.target;
|
|
562
|
+
mainCommentInput.value = readTextWithBreaks(el);
|
|
563
|
+
};
|
|
501
564
|
const onReplyInput = (event, id) => {
|
|
502
565
|
const el = event.target;
|
|
503
566
|
const chipExists = !!el.querySelector('[data-type="mention"]');
|
|
504
567
|
if (!chipExists) replyToUser.value = null;
|
|
505
|
-
|
|
506
|
-
el.childNodes.forEach((node) => {
|
|
507
|
-
if (node.dataset?.type === "mention") return;
|
|
508
|
-
text += node.textContent;
|
|
509
|
-
});
|
|
510
|
-
replyTextMap.value[id] = text.replace(/^\u00A0/, "").trim();
|
|
568
|
+
replyTextMap.value[id] = readTextWithBreaks(el, true);
|
|
511
569
|
};
|
|
512
570
|
const onCancelReplyComment = () => {
|
|
513
571
|
const id = replyingToId.value;
|
|
@@ -31,8 +31,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
31
31
|
fullHeight: boolean;
|
|
32
32
|
name: string;
|
|
33
33
|
limit: number;
|
|
34
|
-
disabledErrorMessage: boolean;
|
|
35
34
|
accept: string;
|
|
35
|
+
disabledErrorMessage: boolean;
|
|
36
36
|
labelIcon: string;
|
|
37
37
|
disabledDrop: boolean;
|
|
38
38
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -31,8 +31,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {
|
|
|
31
31
|
fullHeight: boolean;
|
|
32
32
|
name: string;
|
|
33
33
|
limit: number;
|
|
34
|
-
disabledErrorMessage: boolean;
|
|
35
34
|
accept: string;
|
|
35
|
+
disabledErrorMessage: boolean;
|
|
36
36
|
labelIcon: string;
|
|
37
37
|
disabledDrop: boolean;
|
|
38
38
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -22,8 +22,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
22
22
|
}>, {
|
|
23
23
|
id: string;
|
|
24
24
|
name: string;
|
|
25
|
-
new: boolean;
|
|
26
25
|
disabledForgotPassword: boolean;
|
|
26
|
+
new: boolean;
|
|
27
27
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
28
|
declare const _default: typeof __VLS_export;
|
|
29
29
|
export default _default;
|
|
@@ -22,8 +22,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
22
22
|
}>, {
|
|
23
23
|
id: string;
|
|
24
24
|
name: string;
|
|
25
|
-
new: boolean;
|
|
26
25
|
disabledForgotPassword: boolean;
|
|
26
|
+
new: boolean;
|
|
27
27
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
28
28
|
declare const _default: typeof __VLS_export;
|
|
29
29
|
export default _default;
|
|
@@ -12,9 +12,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
12
12
|
label: string;
|
|
13
13
|
color: InputSliderColor;
|
|
14
14
|
fullWidth: boolean;
|
|
15
|
+
step: number;
|
|
15
16
|
max: number;
|
|
16
17
|
min: number;
|
|
17
|
-
step: number;
|
|
18
18
|
lineHeight: number | string;
|
|
19
19
|
appearance: boolean;
|
|
20
20
|
thumbSize: number | string;
|
|
@@ -12,9 +12,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
12
12
|
label: string;
|
|
13
13
|
color: InputSliderColor;
|
|
14
14
|
fullWidth: boolean;
|
|
15
|
+
step: number;
|
|
15
16
|
max: number;
|
|
16
17
|
min: number;
|
|
17
|
-
step: number;
|
|
18
18
|
lineHeight: number | string;
|
|
19
19
|
appearance: boolean;
|
|
20
20
|
thumbSize: number | string;
|
|
@@ -45,10 +45,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
45
45
|
id: string;
|
|
46
46
|
name: string;
|
|
47
47
|
limit: number;
|
|
48
|
+
resize: "none" | "both" | "horizontal" | "vertical";
|
|
48
49
|
disabledErrorMessage: boolean;
|
|
49
50
|
disabledBorder: boolean;
|
|
50
51
|
showCounter: boolean;
|
|
51
|
-
resize: "none" | "both" | "horizontal" | "vertical";
|
|
52
52
|
readonly: boolean;
|
|
53
53
|
rows: number;
|
|
54
54
|
heightScroll: boolean;
|
|
@@ -45,10 +45,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
|
|
|
45
45
|
id: string;
|
|
46
46
|
name: string;
|
|
47
47
|
limit: number;
|
|
48
|
+
resize: "none" | "both" | "horizontal" | "vertical";
|
|
48
49
|
disabledErrorMessage: boolean;
|
|
49
50
|
disabledBorder: boolean;
|
|
50
51
|
showCounter: boolean;
|
|
51
|
-
resize: "none" | "both" | "horizontal" | "vertical";
|
|
52
52
|
readonly: boolean;
|
|
53
53
|
rows: number;
|
|
54
54
|
heightScroll: boolean;
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
24
24
|
onClose?: (() => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
-
disabledForgotPassword: boolean;
|
|
28
27
|
confirmText: string;
|
|
28
|
+
disabledForgotPassword: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const _default: typeof __VLS_export;
|
|
31
31
|
export default _default;
|
|
@@ -24,8 +24,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
24
24
|
onClose?: (() => any) | undefined;
|
|
25
25
|
}>, {
|
|
26
26
|
title: string;
|
|
27
|
-
disabledForgotPassword: boolean;
|
|
28
27
|
confirmText: string;
|
|
28
|
+
disabledForgotPassword: boolean;
|
|
29
29
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
30
30
|
declare const _default: typeof __VLS_export;
|
|
31
31
|
export default _default;
|
|
@@ -28,8 +28,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
28
28
|
}>, {
|
|
29
29
|
title: string;
|
|
30
30
|
mode: "login" | "secure";
|
|
31
|
-
disabledForgotPassword: boolean;
|
|
32
31
|
confirmText: string;
|
|
32
|
+
disabledForgotPassword: boolean;
|
|
33
33
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
34
|
declare const _default: typeof __VLS_export;
|
|
35
35
|
export default _default;
|
|
@@ -28,8 +28,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
28
28
|
}>, {
|
|
29
29
|
title: string;
|
|
30
30
|
mode: "login" | "secure";
|
|
31
|
-
disabledForgotPassword: boolean;
|
|
32
31
|
confirmText: string;
|
|
32
|
+
disabledForgotPassword: boolean;
|
|
33
33
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
34
|
declare const _default: typeof __VLS_export;
|
|
35
35
|
export default _default;
|