You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
4.0 KiB
TypeScript
121 lines
4.0 KiB
TypeScript
import { useEffect, useState } from 'preact/hooks'
|
|
import { Funnel } from '@phosphor-icons/react'
|
|
import { marked } from 'marked'
|
|
|
|
// @ts-ignore
|
|
import extendedLatex from 'marked-extended-latex'
|
|
|
|
marked.use(
|
|
extendedLatex({
|
|
lazy: false,
|
|
render: (formula: string, display: boolean) => {
|
|
return display ? '$$' + formula + '$$' : '$' + formula + '$'
|
|
},
|
|
}),
|
|
)
|
|
|
|
import type { Database } from '@/data/domande-esami.yaml'
|
|
|
|
const useRemoteValue = <T,>(url: string): T | null => {
|
|
const [value, setValue] = useState<T | null>(null)
|
|
|
|
useEffect(() => {
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(value => setValue(value))
|
|
.catch(error => console.error(error))
|
|
}, [url])
|
|
|
|
return value
|
|
}
|
|
|
|
type Props = {
|
|
course: string
|
|
}
|
|
|
|
export const DomandeEsamiCourse = ({ course }: Props) => {
|
|
const database = useRemoteValue<Database>(`/domande-esami/api/${course}.json`)
|
|
if (!database) {
|
|
return <>Loading...</>
|
|
}
|
|
|
|
if ('requestIdleCallback' in window) {
|
|
// @ts-ignore
|
|
requestIdleCallback(() => window.renderMath())
|
|
} else {
|
|
// @ts-ignore
|
|
setTimeout(() => window.renderMath(), 100)
|
|
}
|
|
|
|
const courseTags = [
|
|
...new Set(
|
|
database.questions.filter(question => question.course === course).flatMap(question => question.tags),
|
|
),
|
|
]
|
|
|
|
const [selectedTag, setSelectedTag] = useState<string | null>(null)
|
|
|
|
const filteredQuestions = database.questions
|
|
.filter(question => question.course === course)
|
|
.filter(question => (selectedTag ? question.tags.includes(selectedTag) : true))
|
|
|
|
return (
|
|
<>
|
|
<div class="grid-center">
|
|
<h3>
|
|
<a href="/domande-esami">Domande Esami</a>
|
|
</h3>
|
|
<h1>{database.names[course]}</h1>
|
|
</div>
|
|
|
|
{courseTags.length > 1 && (
|
|
<div class="card filter">
|
|
<div class="grid-h">
|
|
<Funnel />
|
|
<strong>Filtra Tag</strong>
|
|
</div>
|
|
<div class="flex-row-wrap">
|
|
{!selectedTag
|
|
? courseTags.map(tag => (
|
|
<div class="chip clickable" onClick={() => setSelectedTag(tag)}>
|
|
{tag}
|
|
</div>
|
|
))
|
|
: courseTags.map(tag => (
|
|
<div
|
|
class={tag === selectedTag ? 'chip clickable' : 'chip clickable disabled'}
|
|
onClick={() => setSelectedTag(tag === selectedTag ? null : tag)}
|
|
>
|
|
{tag}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div class="wide-card-list" id="questions">
|
|
{filteredQuestions.length === 0 ? (
|
|
<div class="grid-center">
|
|
<em>No questions found</em>
|
|
</div>
|
|
) : (
|
|
filteredQuestions.map(question => (
|
|
<div class="card">
|
|
<div
|
|
class="text"
|
|
dangerouslySetInnerHTML={{
|
|
__html: marked(question.content, { async: false }),
|
|
}}
|
|
/>
|
|
<div class="metadata">
|
|
{question.tags.map(tag => (
|
|
<div class="chip small">{tag}</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|