Files
SkinbaseNova/resources/js/components/artwork/ArtworkDescription.jsx

71 lines
2.0 KiB
JavaScript

import React, { useState } from 'react'
const COLLAPSE_AT = 560
function stripTags(value) {
return String(value || '')
.replace(/<\/?(?:html|head|body|title|meta|link|script|style)[^>]*>/gi, '')
.replace(/<[^>]*>/g, '')
.trim()
}
function sanitizeDescriptionHtml(value) {
const html = String(value || '').trim()
if (!html) {
return ''
}
if (/<\/?(?:html|head|body|title|meta|link|script|style)\b/i.test(html)) {
return ''
}
return html
}
export default function ArtworkDescription({ artwork }) {
const [expanded, setExpanded] = useState(false)
const content = (artwork?.description || '').trim()
const contentHtml = sanitizeDescriptionHtml(artwork?.description_html || '')
const collapsed = content.length > COLLAPSE_AT && !expanded
const fallbackText = contentHtml ? stripTags(contentHtml) : content
if (content.length === 0) return null
return (
<div>
<div
className={[
'max-w-[720px] overflow-hidden transition-[max-height] duration-300',
collapsed ? 'max-h-[11.5rem]' : 'max-h-[100rem]',
].join(' ')}
>
<div
className="prose prose-invert max-w-none text-sm leading-7 prose-p:my-3 prose-p:text-white/50 prose-a:text-accent prose-a:no-underline hover:prose-a:underline prose-strong:text-white/80 prose-em:text-white/70 prose-code:text-white/80"
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: contentHtml || escapeHtml(fallbackText) }}
/>
</div>
{content.length > COLLAPSE_AT && (
<button
type="button"
className="mt-3 text-sm font-medium text-accent transition-colors hover:text-accent/80"
onClick={() => setExpanded((value) => !value)}
>
{expanded ? 'Show less' : 'Show more'}
</button>
)}
</div>
)
}
function escapeHtml(value) {
return String(value || '')
.replaceAll('&', '&amp;')
.replaceAll('<', '&lt;')
.replaceAll('>', '&gt;')
.replaceAll('"', '&quot;')
.replaceAll("'", '&#39;')
}