UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

113 lines (110 loc) 9.13 kB
import { jsxs, jsx } from 'react/jsx-runtime'; import { useState, useRef, useEffect, useCallback } from 'react'; import { Button } from '../../../../components/ui/Button.js'; import { SuggestedQuestions } from './suggested-questions.js'; import { useI18n, RTLText } from './i18n-context.js'; function ChatDock({ isOpen, onClose, onMinimize, onSendMessage, messages, isTyping = false, title, placeholder, className = "", maxHeight = "600px", position = "bottom-right", showHeader = true, allowMinimize = true, disabled = false, showSuggestedQuestions = true, suggestedQuestionsContext = "welcome", }) { // Use i18n context const { t, isRTL, currentLanguage } = useI18n(); // Use default values from translations if not provided const displayTitle = title || t("chatbot"); const displayPlaceholder = placeholder || t("placeholder"); const [inputValue, setInputValue] = useState(""); const [isMinimized, setIsMinimized] = useState(false); const messagesEndRef = useRef(null); const inputRef = useRef(null); const messagesContainerRef = useRef(null); // Auto-scroll to bottom when new messages arrive useEffect(() => { if (messagesEndRef.current && !isMinimized) { messagesEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [messages, isMinimized]); // Focus input when dock opens useEffect(() => { if (isOpen && !isMinimized && inputRef.current) { const timer = setTimeout(() => { var _a; (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 100); return () => clearTimeout(timer); } }, [isOpen, isMinimized]); const handleSendMessage = useCallback(() => { const trimmedValue = inputValue.trim(); if (trimmedValue && !disabled) { onSendMessage(trimmedValue); setInputValue(""); } }, [inputValue, onSendMessage, disabled]); const handleKeyPress = useCallback((event) => { if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); handleSendMessage(); } }, [handleSendMessage]); const handleMinimize = useCallback(() => { setIsMinimized(!isMinimized); if (onMinimize) { onMinimize(); } }, [isMinimized, onMinimize]); const getPositionClasses = () => { switch (position) { case "bottom-left": return "bottom-24 left-6"; case "center": return "top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"; default: return "bottom-24 right-6"; } }; const formatTimestamp = (timestamp) => { return timestamp.toLocaleTimeString("ko-KR", { hour: "2-digit", minute: "2-digit", }); }; const TypingIndicator = () => (jsxs("div", { className: "flex items-center space-x-2 px-4 py-3", children: [jsxs("div", { className: "flex space-x-1", children: [jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.3s]" }), jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce [animation-delay:-0.15s]" }), jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce" })] }), jsx(RTLText, { className: "text-sm text-gray-500", children: t("typing") })] })); const MessageBubble = ({ message }) => { const isUser = message.sender === "user"; const isError = message.type === "error"; const isSystem = message.type === "system"; return (jsx("div", { className: `flex ${isUser ? "justify-end" : "justify-start"} mb-4 animate-in slide-in-from-bottom-1 duration-300`, children: jsxs("div", { className: ` max-w-[85%] px-4 py-3 rounded-2xl shadow-sm ${isUser ? "bg-blue-600 text-white rounded-br-md" : isError ? "bg-red-50 text-red-800 border border-red-200 rounded-bl-md" : isSystem ? "bg-gray-50 text-gray-600 border border-gray-200 rounded-bl-md" : "bg-gray-100 text-gray-800 rounded-bl-md"} ${message.isStreaming ? "animate-pulse" : ""} `, children: [jsx("div", { className: "text-sm leading-relaxed whitespace-pre-wrap break-words", children: message.content }), jsx("div", { className: `text-xs mt-1 opacity-70 ${isUser ? "text-blue-100" : "text-gray-500"}`, children: formatTimestamp(message.timestamp) })] }) })); }; if (!isOpen) return null; return (jsxs("div", { className: ` fixed z-40 ${getPositionClasses()} w-96 bg-white rounded-2xl shadow-2xl border border-gray-200 transition-all duration-300 ease-in-out ${isMinimized ? "h-16" : ""} ${className} `, style: { maxHeight: isMinimized ? "64px" : maxHeight }, role: "dialog", "aria-labelledby": "chat-title", "aria-describedby": "chat-description", children: [showHeader && (jsxs("div", { className: "flex items-center justify-between p-4 border-b border-gray-200 bg-gray-50 rounded-t-2xl", children: [jsxs("div", { className: "flex items-center space-x-3", children: [jsx("div", { className: "w-3 h-3 bg-green-500 rounded-full animate-pulse" }), jsx("h3", { id: "chat-title", className: `font-semibold text-gray-800 ${isRTL ? "text-right" : "text-left"}`, dir: isRTL ? "rtl" : "ltr", children: displayTitle })] }), jsxs("div", { className: "flex items-center space-x-2", children: [allowMinimize && (jsx(Button, { onClick: handleMinimize, variant: "outline", className: "w-8 h-8 p-0 rounded-full border-gray-300", "aria-label": isMinimized ? t("maximize") : t("minimize"), children: jsx("svg", { className: `w-4 h-4 transition-transform duration-200 ${isMinimized ? "rotate-180" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) }) })), jsx(Button, { onClick: onClose, variant: "outline", className: "w-8 h-8 p-0 rounded-full border-gray-300", "aria-label": t("close"), children: jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] })] })), !isMinimized && (jsxs("div", { ref: messagesContainerRef, className: "flex-1 overflow-y-auto p-4 space-y-4", style: { height: "calc(100% - 140px)" }, id: "chat-description", "aria-label": t("messageFromBot"), children: [messages.length === 0 ? (jsxs("div", { className: "text-center text-gray-500 py-8", children: [jsx("div", { className: "text-4xl mb-4", children: "\uD83D\uDCAC" }), jsx(RTLText, { className: "text-sm mb-6", children: t("welcomeMessage") }), showSuggestedQuestions && (jsx("div", { className: "mt-6", children: jsx(SuggestedQuestions, { onQuestionSelect: onSendMessage, context: suggestedQuestionsContext, variant: "chips", maxQuestions: 3, className: "justify-center" }) }))] })) : (messages.map((message) => (jsx(MessageBubble, { message: message }, message.id)))), isTyping && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] })), !isMinimized && (jsxs("div", { className: "p-4 border-t border-gray-200 bg-gray-50 rounded-b-2xl", children: [jsxs("div", { className: "flex items-center space-x-3", children: [jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyDown: handleKeyPress, placeholder: displayPlaceholder, disabled: disabled || isTyping, className: "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500", "aria-label": t("sendMessage"), dir: isRTL ? "rtl" : "ltr" }), jsx(Button, { onClick: handleSendMessage, disabled: !inputValue.trim() || disabled || isTyping, className: "px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors duration-200", "aria-label": t("send"), children: jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8" }) }) })] }), jsx(RTLText, { className: "mt-2 text-xs text-gray-500 text-center", children: currentLanguage === "ko" ? "Enter로 전송 • Shift+Enter로 줄바꿈" : currentLanguage === "ja" ? "Enterで送信 • Shift+Enterで改行" : currentLanguage === "ar" ? "اضغط Enter للإرسال • Shift+Enter للسطر الجديد" : "Press Enter to send • Shift+Enter for new line" })] }))] })); } export { ChatDock, ChatDock as default }; //# sourceMappingURL=chat-dock.js.map