From 690ee343db6724ececa86f569e8eda7fe903e73e Mon Sep 17 00:00:00 2001 From: gunshiz Date: Thu, 8 Oct 2026 18:11:09 +0700 Subject: [PATCH] feat : add emoji picker --- components/comments/comment-card.tsx | 5 +++- components/comments/comment-composer.tsx | 38 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/components/comments/comment-card.tsx b/components/comments/comment-card.tsx index 4101137..4b39cd4 100644 --- a/components/comments/comment-card.tsx +++ b/components/comments/comment-card.tsx @@ -237,6 +237,8 @@ export function CommentCard({ item, viewer, onChange, revision = 0, inbox = fals
{item.replyToName &&

ตอบกลับ {item.replyToName}

} + {mode === "edit" ? setMode(null)} onSaved={() => { setMode(null); onChange(); }} /> : <> {(item.deleted || item.hidden) && !viewer?.admin ?

{item.deleted ? "ความคิดเห็นถูกลบแล้ว" : "ความคิดเห็นถูกซ่อนโดยผู้ดูแล"}

: }
@@ -250,7 +252,8 @@ export function CommentCard({ item, viewer, onChange, revision = 0, inbox = fals : optimistic.hearted && !item.deleted && !item.hidden && ผู้ดูแลถูกใจ}
- {mode && } + {mode === "reply" && setMode(null)} onSaved={() => { setMode(null); expandReplies(true); onChange(); }} />} {replies && !item.rootId && (item.replyCount > 0 || expanded) && expandReplies(value.includes("replies"))}> {`การตอบกลับ ${item.replyCount.toLocaleString("th-TH")} รายการ`} diff --git a/components/comments/comment-composer.tsx b/components/comments/comment-composer.tsx index f60adf7..2a9eeb6 100644 --- a/components/comments/comment-composer.tsx +++ b/components/comments/comment-composer.tsx @@ -1,7 +1,8 @@ "use client"; import { useEffect, useId, useRef, useState, type ClipboardEvent, type FormEvent } from "react"; -import { ImagePlusIcon, XIcon } from "lucide-react"; +import dynamic from "next/dynamic"; +import { ImagePlusIcon, SmilePlusIcon, XIcon } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field"; @@ -9,10 +10,13 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Progress, ProgressLabel, ProgressValue } from "@/components/ui/progress"; import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from "@/components/ui/popover"; import type { CommentImage, CommentItem } from "@/lib/comments/types"; import { CommentImageView } from "./comment-image"; import { COMMENT_ERRORS } from "./client"; +const EmojiPicker = dynamic(() => import("emoji-picker-react"), { ssr: false }); + function FilePreview({ file }: { file: File }) { const imageRef = useRef(null); useEffect(() => { @@ -36,9 +40,28 @@ export function CommentComposer({ target, replyTo, editing, onSaved, onCancel }: const [pending, setPending] = useState(false); const [expanded, setExpanded] = useState(Boolean(editing || replyTo)); const [progress, setProgress] = useState(0); + const [emojiOpen, setEmojiOpen] = useState(false); const uploadRef = useRef(null); const fileRef = useRef(null); + const textRef = useRef(null); + const isEditing = Boolean(editing); useEffect(() => () => uploadRef.current?.abort(), []); + useEffect(() => { if (isEditing) textRef.current?.focus(); }, [isEditing]); + + function insertEmoji(emoji: string) { + if (pending) return; + const start = textRef.current?.selectionStart ?? text.length; + const end = textRef.current?.selectionEnd ?? start; + const next = text.slice(0, start) + emoji + text.slice(end); + if (next.length > 4000) return setError("ความคิดเห็นต้องไม่เกิน 4,000 ตัวอักษร"); + setText(next); + setExpanded(true); + setEmojiOpen(false); + requestAnimationFrame(() => { + textRef.current?.focus(); + textRef.current?.setSelectionRange(start + emoji.length, start + emoji.length); + }); + } function addImages(incoming: File[]) { if (incoming.length + files.length + kept.length > 5) return setError("แนบรูปภาพได้สูงสุด 5 รูปต่อความคิดเห็น"); @@ -105,7 +128,7 @@ export function CommentComposer({ target, replyTo, editing, onSaved, onCancel }: {editing ? "แก้ไขความคิดเห็น" : replyTo ? `ตอบกลับ ${replyTo.authorName}` : "เพิ่มความคิดเห็น"} -