Public Access
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import DOMPurify from 'dompurify'
|
|
import { highlightCode } from '../utils/latex'
|
|
import type { Question } from '../types'
|
|
|
|
interface Props {
|
|
q: Question
|
|
}
|
|
|
|
export default function QuestionMedia({ q }: Props) {
|
|
return (
|
|
<>
|
|
{q.image && (
|
|
<figure className="q-media">
|
|
<img src={q.image.src} alt={q.image.alt ?? ''} />
|
|
{q.image.caption && (
|
|
<figcaption className="q-media-caption">{q.image.caption}</figcaption>
|
|
)}
|
|
</figure>
|
|
)}
|
|
{q.audio && (
|
|
<div className="q-audio">
|
|
<audio controls src={q.audio.src}>Your browser does not support audio.</audio>
|
|
</div>
|
|
)}
|
|
{q.code && (
|
|
// highlightCode runs escapeHtml on the source first — output is safe
|
|
<pre
|
|
className="q-code"
|
|
dangerouslySetInnerHTML={{ __html: highlightCode(q.code.content, q.code.language) }}
|
|
/>
|
|
)}
|
|
{q.table && (
|
|
<table className="q-table">
|
|
<thead>
|
|
<tr>{q.table.headers.map((h, i) => <th key={i}>{h}</th>)}</tr>
|
|
</thead>
|
|
<tbody>
|
|
{q.table.rows.map((row, i) => (
|
|
<tr key={i}>{row.map((cell, j) => <td key={j}>{cell}</td>)}</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
{q.svg && (
|
|
// SVG is user-provided — sanitize with DOMPurify before rendering
|
|
<div
|
|
className="q-media"
|
|
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(q.svg) }}
|
|
/>
|
|
)}
|
|
</>
|
|
)
|
|
}
|