Skip to content

Commit 820220a

Browse files
authored
Merge pull request #44 from Regulus0811/feat/class-prompt-page
PDFビュアー機能の実装
2 parents 3dc914c + 042c6df commit 820220a

File tree

6 files changed

+435
-9
lines changed

6 files changed

+435
-9
lines changed

next.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = {
2+
webpack: config => {
3+
config.resolve.alias.canvas = false;
4+
5+
return config;
6+
},
7+
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"next": "14.0.4",
3030
"react": "^18",
3131
"react-dom": "^18",
32+
"react-pdf": "^7.7.0",
3233
"ts-node": "^10.9.2"
3334
},
3435
"devDependencies": {

src/app/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function RootLayout({children}: {children: React.ReactNode}) {
1818
<div className="w-72 fixed">
1919
<Navbar />
2020
</div>
21-
<div className="w-5/6 pl-72">{children}</div>
21+
<div className="w-full pl-72">{children}</div>
2222
</div>
2323
</body>
2424
</html>

src/app/prompts/layout.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import React, {FC, ReactNode} from 'react';
2+
3+
interface LayoutProps {
4+
children: ReactNode;
5+
}
6+
7+
const Layout: FC<LayoutProps> = ({children}: LayoutProps) => (
8+
<div className="flex flex-col items-center space-y-4">{children}</div>
9+
);
10+
11+
export default Layout;

src/app/prompts/page.tsx

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
'use client';
2+
3+
import React, {useState, useRef, useEffect} from 'react';
4+
import {Document, Page, pdfjs} from 'react-pdf';
5+
import 'react-pdf/dist/Page/TextLayer.css';
6+
import 'react-pdf/dist/Page/AnnotationLayer.css';
7+
8+
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
9+
10+
const MyPdfViewer = () => {
11+
const [file, setFile] = useState<File | null>(null);
12+
const [numPages, setNumPages] = useState<number | null>(null);
13+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
14+
const [pageNumber, setPageNumber] = useState<number>(1);
15+
const [inputValue, setInputValue] = useState<string>('1');
16+
const pageRefs = useRef<(React.RefObject<HTMLDivElement> | null)[]>([]);
17+
18+
// PDFファイルが読み込まれたときの処理
19+
function onDocumentLoadSuccess({numPages}: {numPages: number}) {
20+
setNumPages(numPages);
21+
pageRefs.current = Array(numPages)
22+
.fill(null)
23+
.map(() => React.createRef());
24+
}
25+
26+
// ファイルを選択したときの処理
27+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
28+
const file = event.target.files?.[0];
29+
if (file) {
30+
const reader = new FileReader();
31+
reader.onload = () => {
32+
setFile(file);
33+
};
34+
reader.readAsDataURL(file);
35+
}
36+
};
37+
38+
// ページが表示されたときの処理
39+
const observePage = (pageIndex: number) => {
40+
const observer = new IntersectionObserver(
41+
entries => {
42+
if (entries[0].isIntersecting) {
43+
setPageNumber(pageIndex + 1);
44+
setInputValue((pageIndex + 1).toString());
45+
}
46+
},
47+
{threshold: 0.5}
48+
);
49+
50+
// ページが表示されたときにページ番号を更新する
51+
const pageElement = pageRefs.current[pageIndex]?.current;
52+
if (pageElement) {
53+
observer.observe(pageElement);
54+
}
55+
return () => {
56+
if (pageElement) {
57+
observer.unobserve(pageElement);
58+
}
59+
};
60+
};
61+
62+
// ページ番号を指定してページに移動する
63+
const goToPage = (page: number) => {
64+
const pageElement = pageRefs.current[page - 1]?.current;
65+
if (pageElement) {
66+
pageElement.scrollIntoView();
67+
}
68+
};
69+
70+
// ページ数が変わったときにページの表示を監視する
71+
useEffect(() => {
72+
const cleanupFunctions = Array(numPages || 0)
73+
.fill(null)
74+
.map((_, index) => observePage(index));
75+
return () => {
76+
cleanupFunctions.forEach(cleanup => cleanup());
77+
};
78+
}, [numPages]);
79+
80+
return (
81+
<div>
82+
<input type="file" onChange={handleChange} />
83+
{file && (
84+
<div>
85+
<div>
86+
<label>Page: </label>
87+
<input
88+
type="text"
89+
value={inputValue}
90+
onChange={e => setInputValue(e.target.value)}
91+
onKeyDown={e => {
92+
if (e.key === 'Enter') {
93+
goToPage(Number(inputValue));
94+
}
95+
}}
96+
className="border rounded p-1 w-10 text-center"
97+
/>
98+
/ {numPages}
99+
</div>
100+
<div className="h-[80vh] overflow-auto">
101+
<Document file={file as File} onLoadSuccess={onDocumentLoadSuccess}>
102+
{Array.from(new Array(numPages || 0), (el, index) => (
103+
<Page
104+
key={`page_${index + 1}`}
105+
pageNumber={index + 1}
106+
inputRef={pageRefs.current[index]}
107+
/>
108+
))}
109+
</Document>
110+
</div>
111+
</div>
112+
)}
113+
</div>
114+
);
115+
};
116+
117+
export default MyPdfViewer;

0 commit comments

Comments
 (0)