import "./index.css"; import { StrictMode, useLayoutEffect, useRef, useState, useEffect, useMemo } from "react"; import { createRoot } from "react-dom/client"; import { useParams, Navigate, BrowserRouter, Link, Outlet, Route, Routes } from "react-router"; import { useShallow } from "zustand/shallow"; import { marked } from "marked"; import { apiInstance, resourcesInstance } from "./api.js"; import { useStore } from "./store.js"; import { ArrowBackIcon, ArrowForwardIcon, MenuBookIcon, MyLocationIcon, TitleIcon, WidthIcon, EllipsisIcon, CloseIcon, JustifyTextIcon, TextIncreaseIcon, TextDecreaseIcon, VRuleIcon, } from "./icons/Icons"; createRoot(document.getElementById("root")).render( }> } /> } /> , ); function Layout() { const params = useParams(); const topic = useStore((state) => params.topicId && state.topics.byId[params.topicId]); const config = useStore((state) => state.config); return (
{config.displayTitle && (
{topic ? `${topic.index + 1}: ${topic.title}` : "Конспект за Държавен Изпит"}
)}
); } const SUBJECTS = [ { id: 0, name: "ВЪТРЕШНИ БОЛЕСТИ" }, { id: 1, name: "ФАРМАКОЛОГИЯ" }, ]; export function TopicListView() { const itemRefs = useRef({}); const topics = useStore( useShallow((state) => state.topics.allIds.map((id) => state.topics.byId[id])), ); const selectedTopic = useStore((state) => state.selectedTopic); const selectTopic = useStore((state) => state.selectTopic); const selectedSubjectIndex = useStore((state) => state.subject); const setSelectedSubjectIndex = useStore((state) => state.selectSubject); const config = useStore((state) => state.config); const changeConfig = useStore((state) => state.changeConfig); const [isSelectingSubject, setIsSelectingSubject] = useState(false); useLayoutEffect(() => { if (selectedTopic && selectedTopic.id !== null) { itemRefs.current?.[Math.max(selectedTopic.index - 3, 0)].scrollIntoView(); } }, [selectedTopic]); return ( <>
{topics.map((topic, i) => ( { itemRefs.current[i] = node; }} to={`/${topic.id}`} onClick={() => selectTopic(topic.id)} className={`flex px-2 py-1 rounded-md cursor-pointer border-l-4 ${topic.id === selectedTopic?.id ? "bg-blue-100 border-blue-500" : "border-transparent hover:bg-gray-100"}`} >
{i + 1}
{topic.title} ))}
{selectedTopic !== null && ( )}
{isSelectingSubject && ( <>
{SUBJECTS.map((subject) => ( ))}
)}
{selectedTopic !== null && ( Продължи четенето:
{selectedTopic.index + 1}. {selectedTopic.title} )}
); } export function FileReader() { const params = useParams(); const topicsById = useStore((state) => state.topics.byId); const selectTopic = useStore((state) => state.selectTopic); const topic = topicsById[params.topicId]; if (!topic) { return ; } selectTopic(topic.id); return ; } export function Reader({ topic }) { const config = useStore((state) => state.config); const changeConfig = useStore((state) => state.changeConfig); const selectedVersion = useStore((state) => state.topicVersions[topic.id] ?? 0); const selectVersion = useStore((state) => state.selectTopicVersion); const getTopicAtIndex = useStore((state) => state.getTopicAtIndex); const prevTopic = getTopicAtIndex(topic.index - 1); const nextTopic = getTopicAtIndex(topic.index + 1); const [isSelectingVersion, setIsSelectingVersion] = useState(false); return ( <>
{config.contentZoomLevel}%
{window.innerWidth > 576 && ( )} {topic.files.length > 1 && (
{isSelectingVersion && ( )}
)}
{isSelectingVersion && (
{topic.files.map((file, vIndex) => ( ))}
)}
{topic.isFirst ? (
) : ( {prevTopic.index + 1}: {prevTopic.title} )} {topic.isLast ? (
) : ( {nextTopic.index + 1}: {nextTopic.title} )}
); } export function PDFViewer({ file, compact, zoomFactor, justifyText }) { const iframeRef = useRef(null); const [content, setContent] = useState(null); const htmlContent = useMemo(() => { const fileContent = ` ${content} `; return fileContent; }, [content, compact, justifyText, zoomFactor]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const contentZoomLevel = useStore((state) => state.config.contentZoomLevel); useEffect(() => { if (iframeRef.current && iframeRef.current.contentDocument) { iframeRef.current.contentDocument.body.style.zoom = `${contentZoomLevel}%`; } }, [contentZoomLevel]); useEffect(() => { const fetchFile = async () => { try { setIsLoading(true); const mdPath = `/files_md/${file.filename}`; const response = await fetch(mdPath); if (!response.ok) { throw new Error(`Failed to load file: ${response.status}`); } let fileContent = await response.text(); if (fileContent === "") { fileContent = "**No Data!**"; } fileContent = marked.parse(fileContent); setContent(fileContent); setError(null); } catch (err) { console.error("Error loading file:", err); setError(err.message); } finally { setIsLoading(false); } }; fetchFile(); }, [file]); if (error) { return
Error: {error}
; } if (isLoading) { return ; } return (