o2o-negosium-original/frontend/src/features/chat/components/menu/MDInformation.tsx

79 lines
2.3 KiB
TypeScript

import { useMemo } from 'react'
import { ChevronDown } from 'lucide-react'
import { cn } from '@/lib'
import { useChatInitStore } from '@/features/chat/stores/useChatInitStore'
interface Props {
isOpen: boolean
setIsOpen: (isOpen: boolean) => void
}
export function MDInformation({ isOpen, setIsOpen }: Props) {
const { quotation_memo } = useChatInitStore()
const memoArray = useMemo(() => {
if (!quotation_memo?.trim()) return []
return quotation_memo
.split(/\r?\n+/)
.map((s) => s.trim())
.filter(Boolean)
}, [quotation_memo])
if (memoArray.length === 0) return null
return (
<section className="w-full">
<button
type="button"
aria-expanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
className="flex w-full items-center justify-between border-b border-border pb-2"
>
<span className="text-[11px] font-bold tracking-wide text-neutral-60">MD 안내사항</span>
<ChevronDown
size={16}
className={cn('text-neutral-50 transition-transform duration-300', !isOpen && '-rotate-90')}
/>
</button>
{isOpen && (
<div className="max-h-[220px] animate-fade-in overflow-y-auto pt-3">
<div className="flex flex-col gap-2 break-keep">
{memoArray.map((item, i) => (
<div key={i} className="flex items-start gap-2 text-sm text-neutral-70">
<span className="shrink-0 text-neutral-50">•</span>
<div className="flex-1 whitespace-pre-wrap">{linkText(item)}</div>
</div>
))}
</div>
</div>
)}
</section>
)
}
function linkText(text: string) {
const urlRegex = /(https?:\/\/[^\s]+)/g
const isUrl = (s: string) => /^https?:\/\/[^\s]+$/.test(s)
const parts = text.split(urlRegex)
return (
<div className="text-sm text-neutral-70">
{parts.map((part, i) =>
isUrl(part) ? (
<a
key={i}
href={part}
target="_blank"
rel="noopener noreferrer"
className="block text-brand-600 underline transition-all hover:brightness-95"
>
상품 사이트로 이동
</a>
) : (
<span key={i}>{i > 0 && isUrl(parts[i - 1]) ? part.replace(/^(\s)/, '') : part}</span>
),
)}
</div>
)
}