update
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
import { useLayoutEffect, useState, useRef, useEffect } from "react";
|
||||
import { pdfjs, Document, Page } from "react-pdf";
|
||||
import { ArrowBackIcon, ArrowForwardIcon, MenuBookIcon, MyLocationIcon } from "./icons/Icons";
|
||||
|
||||
import structureOriginal from "./structure.json";
|
||||
|
||||
const structure = structureOriginal.map((topic) => ({ ...topic, version: 0 }));
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
||||
|
||||
export function TopicListView({ selectedIndex, onChange }) {
|
||||
const itemRefs = useRef([]);
|
||||
|
||||
@@ -72,9 +69,9 @@ export function TopicListView({ selectedIndex, onChange }) {
|
||||
|
||||
function Layout({ children, title }) {
|
||||
return (
|
||||
<div className="max-w-xl mx-auto h-full relative flex flex-col">
|
||||
<div className="w-full p-4 font-medium text-large text-white bg-blue-600">
|
||||
<span className="line-clamp-2">{title}</span>
|
||||
<div className="max-w-7xl mx-auto h-full relative flex flex-col">
|
||||
<div className="w-full px-4 py-2 font-medium text-large text-white bg-blue-600">
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
@@ -101,12 +98,9 @@ export default function App() {
|
||||
|
||||
return (
|
||||
<Layout title={`${selectedIndex + 1}: ${structure[selectedIndex].title}`}>
|
||||
<div className="flex-1 overflow-y-scroll">
|
||||
<Reader
|
||||
key={structure[selectedIndex].files[versions[selectedIndex]]}
|
||||
file={structure[selectedIndex].files[versions[selectedIndex]]}
|
||||
/>
|
||||
<div className="absolute bottom-14 flex justify-between p-2 w-full z-999">
|
||||
<div className="flex-1">
|
||||
<Reader file={structure[selectedIndex].files[versions[selectedIndex]]} />
|
||||
<div className="absolute bottom-10 flex justify-between px-4 py-2 w-full z-999">
|
||||
<button
|
||||
className="cursor-pointer p-2 rounded-full bg-blue-600 text-white"
|
||||
onClick={() => {
|
||||
@@ -115,27 +109,29 @@ export default function App() {
|
||||
>
|
||||
<MenuBookIcon className="fill-gray-100" />
|
||||
</button>
|
||||
<div className="flex space-x-1">
|
||||
{structure[selectedIndex].files.map((file, vIndex) => (
|
||||
<button
|
||||
key={file}
|
||||
className={`flex-1 cursor-pointer px-2 py-1 rounded-md text-xs whitespace-nowrap ${
|
||||
versions[selectedIndex] === vIndex
|
||||
? "bg-blue-100 text-blue-800 font-medium"
|
||||
: "bg-gray-100 hover:bg-gray-200"
|
||||
}`}
|
||||
onClick={() => {
|
||||
setVersions((prev) => {
|
||||
const newVersions = [...prev];
|
||||
newVersions[selectedIndex] = vIndex;
|
||||
return newVersions;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Version {vIndex + 1}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{structure[selectedIndex].files.length > 1 && (
|
||||
<div className="flex space-x-1">
|
||||
{structure[selectedIndex].files.map((file, vIndex) => (
|
||||
<button
|
||||
key={file}
|
||||
className={`flex-1 cursor-pointer px-2 py-1 rounded-md text-xs whitespace-nowrap ${
|
||||
versions[selectedIndex] === vIndex
|
||||
? "bg-blue-100 text-blue-800 font-medium"
|
||||
: "bg-gray-100 hover:bg-gray-200"
|
||||
}`}
|
||||
onClick={() => {
|
||||
setVersions((prev) => {
|
||||
const newVersions = [...prev];
|
||||
newVersions[selectedIndex] = vIndex;
|
||||
return newVersions;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Version {vIndex + 1}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex flex-col space-y-2">
|
||||
@@ -145,7 +141,7 @@ export default function App() {
|
||||
) : (
|
||||
<div
|
||||
onClick={() => setSelectedIndex((prev) => prev - 1)}
|
||||
className="border-r border-blue-200 w-1/2 flex-1 p-4 hover:bg-blue-200 cursor-pointer flex align-center justify-start"
|
||||
className="border-r border-blue-200 w-1/2 flex-1 px-4 py-2 hover:bg-blue-200 cursor-pointer flex align-center justify-start"
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
<span className="ml-2 truncate w-full ">
|
||||
@@ -158,7 +154,7 @@ export default function App() {
|
||||
) : (
|
||||
<div
|
||||
onClick={() => setSelectedIndex((prev) => prev + 1)}
|
||||
className="flex-1 p-4 hover:bg-blue-200 w-1/2 cursor-pointer flex align-center justify-end"
|
||||
className="flex-1 px-4 py-2 hover:bg-blue-200 w-1/2 cursor-pointer flex align-center justify-end"
|
||||
>
|
||||
<span className="mr-2 w-full truncate">
|
||||
{selectedIndex + 2}: {structure[selectedIndex + 1].title}
|
||||
@@ -202,10 +198,9 @@ export function Reader({ file }) {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
padding-bottom: 56px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
pre, code {
|
||||
font-family: 'SF Mono', Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
@@ -236,15 +231,11 @@ export function Reader({ file }) {
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="p-4 flex justify-center items-center h-40">
|
||||
<div className="animate-pulse">Loading...</div>
|
||||
</div>
|
||||
);
|
||||
return <DelayedLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full border border-gray-300 rounded overflow-hidden">
|
||||
<div className="w-full h-full overflow-hidden">
|
||||
<iframe
|
||||
srcDoc={content}
|
||||
title={`File: ${file}`}
|
||||
@@ -255,3 +246,33 @@ export function Reader({ file }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const DelayedLoader = ({
|
||||
delayMs = 2000,
|
||||
className = "p-4 flex justify-center items-center h-40",
|
||||
text = "Loading...",
|
||||
}) => {
|
||||
const [showLoader, setShowLoader] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Set a timeout to show the loader after the specified delay
|
||||
const timer = setTimeout(() => {
|
||||
setShowLoader(true);
|
||||
}, delayMs);
|
||||
|
||||
// Clear the timeout on unmount
|
||||
return () => clearTimeout(timer);
|
||||
}, []); // Empty dependency array - only run on mount
|
||||
|
||||
// Only render if the delay has passed
|
||||
if (showLoader) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className="animate-pulse">{text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Return null before delay threshold
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user