Skip to content

Commit

Permalink
feat(fe): add Katex editor (#1752)
Browse files Browse the repository at this point in the history
* feat: add katex rendering function

* feat: change katex render function with useRef

* chore: render with useEffect

* feat(fe): save

* fix(fe): delete annotation

---------

Co-authored-by: jiho <[email protected]>
  • Loading branch information
youznn and jihorobert authored Jul 2, 2024
1 parent 56de77d commit 690235c
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 12 deletions.
42 changes: 35 additions & 7 deletions apps/frontend/app/admin/problem/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { GET_PROBLEM_DETAIL } from '@/graphql/problem/queries'
import { usePagination } from '@/lib/pagination'
import type { SubmissionItem } from '@/types/type'
import { useQuery } from '@apollo/client'
import { sanitize } from 'isomorphic-dompurify'
// import { sanitize } from 'isomorphic-dompurify'
import katex from 'katex'
import Link from 'next/link'
import { useEffect, useRef, type RefObject } from 'react'
import { FaAngleLeft, FaPencil } from 'react-icons/fa6'
import { columns } from './_components/Columns'
import DataTable from './_components/DataTable'
Expand All @@ -28,6 +30,37 @@ export default function Page({ params }: { params: { id: string } }) {
20
)

const renderKatex = (
html: string | undefined,
katexRef: RefObject<HTMLDivElement>
) => {
if (katexRef.current) {
katexRef.current.innerHTML = html ?? ''
const div = katexRef.current
div.querySelectorAll('math-component').forEach((el) => {
const content = el.getAttribute('content') || ''
const mathHtml = katex.renderToString(content, {
throwOnError: false,
strict: false,
globalGroup: true,
output: 'mathml'
})
el.outerHTML = mathHtml
})
}
}

const katexRef = useRef<HTMLDivElement>(null)!
useEffect(() => {
renderKatex(problemData?.description, katexRef)
}, [problemData?.description, katexRef])

const katexContent = (
<div
className="prose mb-12 w-full max-w-full border-y-2 border-y-gray-300 p-5 py-12"
ref={katexRef}
/>
)
return (
<ScrollArea className="shrink-0">
<main className="flex flex-col gap-6 px-20 py-16">
Expand All @@ -45,12 +78,7 @@ export default function Page({ params }: { params: { id: string } }) {
</Button>
</Link>
</div>
<div
className="prose mb-12 w-full max-w-full border-y-2 border-y-gray-300 p-5 py-12"
dangerouslySetInnerHTML={{
__html: sanitize(problemData?.description ?? '')
}}
/>
{katexContent}
<p className="text-xl font-bold">Submission</p>
<DataTable
data={items ?? []}
Expand Down
32 changes: 27 additions & 5 deletions apps/frontend/components/EditorDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import { convertToLetter } from '@/lib/utils'
import type { ContestProblem, ProblemDetail } from '@/types/type'
import { motion } from 'framer-motion'
import { sanitize } from 'isomorphic-dompurify'
import katex from 'katex'
import { CheckCircle, Lightbulb, Tag } from 'lucide-react'
import { Clipboard } from 'lucide-react'
import { useState } from 'react'
import { useState, useEffect, useRef, type RefObject } from 'react'
import useCopyToClipboard from 'react-use/lib/useCopyToClipboard'
import {
Tooltip,
Expand Down Expand Up @@ -43,6 +44,23 @@ const useCopy = () => {
return { copiedID, copy }
}

const renderKatex = (html: string, katexRef: RefObject<HTMLDivElement>) => {
if (katexRef.current) {
katexRef.current.innerHTML = html
const div = katexRef.current
div.querySelectorAll('math-component').forEach((el) => {
const content = el.getAttribute('content') || ''
const mathHtml = katex.renderToString(content, {
throwOnError: false,
strict: false,
globalGroup: true,
output: 'mathml'
})
el.outerHTML = mathHtml
})
}
}

export function EditorDescription({
problem,
contestProblems
Expand All @@ -51,15 +69,19 @@ export function EditorDescription({
contestProblems?: ContestProblem[]
}) {
const { copiedID, copy } = useCopy()
const katexRef = useRef<HTMLDivElement>(null)!
useEffect(() => {
renderKatex(problem.description, katexRef)
}, [problem.description, katexRef])

const katexContent = <div ref={katexRef} />
return (
<div className="dark flex h-full flex-col gap-8 p-6 text-lg">
<div>
<h1 className="mb-3 text-xl font-bold">{`#${contestProblems ? convertToLetter(contestProblems.find((item) => item.id === problem.id)?.order as number) : problem.id}. ${problem.title}`}</h1>
<div
className="prose prose-invert max-w-full text-sm leading-relaxed text-slate-300"
dangerouslySetInnerHTML={{ __html: sanitize(problem.description) }}
/>
<div className="prose prose-invert max-w-full text-sm leading-relaxed text-slate-300">
{katexContent}
</div>
</div>
<div>
<h2 className="mb-3 font-bold">Input</h2>
Expand Down

0 comments on commit 690235c

Please sign in to comment.