docgen-tool 6.0.3 → 6.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/app/pdf/pdf-generator/generate-pdf.tsx +9 -0
  2. package/dist/app/pdf/pdf-generator/generate-pdf.worker.tsx +56 -0
  3. package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-page/pdf-footer/pdf-footer.tsx +6 -6
  4. package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-page/pdf-html-block/custom-renderers/custom-renderers.tsx +22 -18
  5. package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-page/pdf-html-block/pdf-html-block.tsx +5 -8
  6. package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-page/pdf-page.tsx +4 -8
  7. package/dist/app/pdf/{react-pdf → pdf-generator}/react-pdf.tsx +16 -47
  8. package/dist/app/pdf/pdf-generator/use-generate.pdf.tsx +46 -0
  9. package/dist/app/pdf/pdf-viewer/pdf-controls/pdf-controls.module.css +61 -0
  10. package/dist/app/pdf/pdf-viewer/pdf-controls/pdf-controls.tsx +62 -0
  11. package/dist/app/pdf/pdf-viewer/pdf-display/pdf-display.module.css +22 -0
  12. package/dist/app/pdf/pdf-viewer/pdf-display/pdf-display.tsx +37 -0
  13. package/dist/app/pdf/pdf-viewer/pdf-loader/pdf-loader.tsx +11 -0
  14. package/dist/app/pdf/pdf-viewer/pdf-viewer.module.css +50 -0
  15. package/dist/app/pdf/pdf-viewer/pdf-viewer.tsx +67 -0
  16. package/dist/app/views/components/card/card.module.css +65 -0
  17. package/dist/app/views/components/card/card.tsx +53 -0
  18. package/dist/app/views/components/copyright/copyright.tsx +1 -3
  19. package/dist/app/views/components/footer/footer.tsx +6 -11
  20. package/dist/app/views/components/loader/loader.module.css +49 -0
  21. package/dist/app/views/components/loader/loader.tsx +52 -0
  22. package/dist/app/views/components/loader/spinner.module.css +28 -0
  23. package/dist/app/views/components/loader/spinner.tsx +6 -0
  24. package/dist/app/views/components/logo/logo.tsx +3 -11
  25. package/dist/app/views/components/page/page.tsx +3 -8
  26. package/dist/app/views/components/pdf-footer/pdf-footer.tsx +15 -16
  27. package/dist/app/views/components/pdf-header/pdf-header.tsx +2 -4
  28. package/dist/app/views/components/pdf-toggle-button/pdf-toggle-button.tsx +1 -5
  29. package/dist/app/views/components/side-bar/side-bar.tsx +7 -6
  30. package/dist/app/views/content/markdown.tsx +14 -3
  31. package/dist/app/views/pages/cover/cover.tsx +79 -85
  32. package/dist/app/views/pages/main/main.module.css +4 -0
  33. package/dist/app/views/pages/main/main.tsx +12 -13
  34. package/dist/app/views/pages/main/pdf-viewer.tsx +0 -1
  35. package/dist/app/views/router.tsx +24 -38
  36. package/dist/cli/cli.js +2 -2
  37. package/package.json +7 -5
  38. package/dist/app/views/assets/.DS_Store +0 -0
  39. package/dist/app/views/assets/styles/.DS_Store +0 -0
  40. package/dist/app/views/assets/styles/fonts/.DS_Store +0 -0
  41. /package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-styles/pdf-admonitions-styles.ts +0 -0
  42. /package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-styles/pdf-docs-styles.ts +0 -0
  43. /package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-styles/pdf-styles.ts +0 -0
  44. /package/dist/app/pdf/{react-pdf → pdf-generator}/pdf-styles/pdf-table-styles.ts +0 -0
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { pdf } from '@react-pdf/renderer';
3
+ import { Pdf } from './react-pdf.tsx';
4
+
5
+ export const generatePdf = async (loadedPages) => {
6
+ const instance = await pdf(<Pdf loadedPages={loadedPages} />);
7
+ const blob = await instance.toBlob();
8
+ return blob;
9
+ };
@@ -0,0 +1,56 @@
1
+ import { generatePdf } from './generate-pdf.tsx';
2
+ import type { TSection, TSortedPages } from '../../../docgen/types.ts';
3
+
4
+ declare const __BASE_PATH__: string;
5
+ declare const __DOCGEN_PAGES__: TSortedPages;
6
+
7
+ const loadPdfPages = async (
8
+ sortedPages: any,
9
+ ): Promise<Record<string, string>> => {
10
+ const pages: Record<string, string> = {};
11
+
12
+ const sources = Object.values(sortedPages).flatMap((columns: TSection) =>
13
+ columns.flatMap((section) => section.pages.map((p: any) => p.source)),
14
+ );
15
+
16
+ await Promise.all(
17
+ sources.map(async (filename) => {
18
+ const url = `${__BASE_PATH__}${filename}`;
19
+ try {
20
+ const res = await fetch(url);
21
+ pages[filename] = res.ok
22
+ ? await res.text()
23
+ : `Error loading ${filename}: ${res.status}`;
24
+ } catch (err) {
25
+ pages[filename] = `Error loading ${filename}: ${err}`;
26
+ }
27
+ }),
28
+ );
29
+
30
+ return pages;
31
+ };
32
+
33
+ const slowTask = async () => {
34
+ let data;
35
+ try {
36
+ const loadedPages = await loadPdfPages(__DOCGEN_PAGES__);
37
+ data = await generatePdf(loadedPages);
38
+ } catch (error) {
39
+ console.error(error);
40
+ }
41
+ self.postMessage({
42
+ type: 'complete',
43
+ payload: {
44
+ data,
45
+ },
46
+ });
47
+ };
48
+
49
+ self.onmessage = ({ data: message }) => {
50
+ switch (message.type) {
51
+ case 'start':
52
+ slowTask();
53
+ break;
54
+ default:
55
+ }
56
+ };
@@ -18,18 +18,18 @@ const styles = StyleSheet.create({
18
18
  width: '33%',
19
19
  textAlign: 'center',
20
20
  fontSize: 10,
21
- }
21
+ },
22
22
  });
23
23
 
24
- export const PdfFooter = ({parameters}) => (
24
+ export const PdfFooter = ({ parameters }) => (
25
25
  <View style={styles.footer} fixed>
26
26
  <Text style={styles.column}>{parameters.title}</Text>
27
- <Text style={styles.column}>{`© ${parameters.year} ${parameters.name}`}</Text>
28
27
  <Text
29
28
  style={styles.column}
30
- render={({ pageNumber, totalPages }) => (
31
- `${pageNumber} / ${totalPages}`
32
- )}
29
+ >{`© ${parameters.year} ${parameters.name}`}</Text>
30
+ <Text
31
+ style={styles.column}
32
+ render={({ pageNumber, totalPages }) => `${pageNumber} / ${totalPages}`}
33
33
  />
34
34
  </View>
35
35
  );
@@ -1,12 +1,9 @@
1
1
  import React from 'react';
2
- //import path from 'path';
3
- import {
4
- View,
5
- Text,
6
- Image,
7
- } from '@react-pdf/renderer';
2
+ import { View, Text, Image } from '@react-pdf/renderer';
8
3
  import * as cheerio from 'cheerio';
9
4
 
5
+ declare const __BASE_PATH__: string;
6
+
10
7
  /*
11
8
  For "default" renderers in react-pdf-html, see
12
9
 
@@ -16,17 +13,21 @@ import * as cheerio from 'cheerio';
16
13
  (and renderers.tsx for more details)
17
14
  */
18
15
 
19
- export const customRenderers = ({options}) => ({
16
+ export const customRenderers = ({ options }) => ({
20
17
  div: (payload) => {
21
- const {children, style, element} = payload;
18
+ const { children, style, element } = payload;
22
19
  const classNames = element.classList.toString();
23
- if (classNames.includes("dgPDFPageBreak")) {
24
- return <View break style={style}>{children}</View>;
20
+ if (classNames.includes('dgPDFPageBreak')) {
21
+ return (
22
+ <View break style={style}>
23
+ {children}
24
+ </View>
25
+ );
25
26
  }
26
27
  return <View style={style}>{children}</View>;
27
28
  },
28
29
  pre: (payload) => {
29
- const {children, element, style} = payload;
30
+ const { children, element, style } = payload;
30
31
  //strip and handle code blocks
31
32
  const $ = cheerio.load(element.content.join());
32
33
  const code = $('code');
@@ -35,11 +36,14 @@ export const customRenderers = ({options}) => ({
35
36
  }
36
37
  return <Text style={style}>{children}</Text>;
37
38
  },
38
- // TODO: reinstate images without path
39
- // img: (payload) => {
40
- // const {element, style} = payload;
41
- // const relativeSource = element.attributes.src;
42
- // //const source = path.join(options.input, relativeSource);
43
- // return <Image style={style} source={source} />;
44
- // },
39
+ img: (payload) => {
40
+ const { element, style } = payload;
41
+ // Load images from base URL
42
+ return (
43
+ <Image
44
+ style={style}
45
+ source={`${__BASE_PATH__}${element?.attributes?.src}`}
46
+ />
47
+ );
48
+ },
45
49
  });
@@ -1,19 +1,16 @@
1
1
  import React from 'react';
2
- import Html from 'react-pdf-html';
2
+ import Html from 'react-pdf-html-simple';
3
3
  import { fontSize, htmlStyleSheet } from '../../pdf-styles/pdf-styles.ts';
4
- import { customRenderers } from "./custom-renderers/custom-renderers.tsx";
4
+ import { customRenderers } from './custom-renderers/custom-renderers.tsx';
5
5
 
6
- export const PdfHtmlBlock = ({
7
- page,
8
- options
9
- }) => {
6
+ export const PdfHtmlBlock = ({ page, options }) => {
10
7
  return (
11
8
  <Html
12
9
  style={{ fontSize }}
13
10
  stylesheet={htmlStyleSheet}
14
- renderers={customRenderers({options})}
11
+ renderers={customRenderers({ options })}
15
12
  >
16
13
  {page}
17
14
  </Html>
18
15
  );
19
- }
16
+ };
@@ -1,22 +1,18 @@
1
1
  import React from 'react';
2
2
  import { Page, View, StyleSheet } from '@react-pdf/renderer';
3
- import { PdfHtmlBlock } from "./pdf-html-block/pdf-html-block.tsx";
3
+ import { PdfHtmlBlock } from './pdf-html-block/pdf-html-block.tsx';
4
4
  import { PdfFooter } from './pdf-footer/pdf-footer.tsx';
5
5
  import { pdfStyleSheet } from '../pdf-styles/pdf-styles.ts';
6
6
 
7
7
  export const reactPdfStyles = StyleSheet.create(pdfStyleSheet);
8
8
 
9
- export const PdfPage = ({
10
- page,
11
- parameters,
12
- options
13
- }) => {
9
+ export const PdfPage = ({ page, parameters, options }) => {
14
10
  return (
15
11
  <Page size="A4" style={reactPdfStyles.page}>
16
12
  <View>
17
13
  <PdfHtmlBlock page={page} options={options} />
18
14
  </View>
19
- <PdfFooter parameters={parameters}/>
15
+ <PdfFooter parameters={parameters} />
20
16
  </Page>
21
- )
17
+ );
22
18
  };
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React from 'react';
2
2
  import { Document, Font } from '@react-pdf/renderer';
3
3
  import { PdfPage } from './pdf-page/pdf-page.tsx';
4
4
  import { marked } from 'marked';
@@ -15,10 +15,11 @@ import spaceMonoRegular from '../../views/assets/styles/fonts/space-mono-regular
15
15
  import spaceMonoItalic from '../../views/assets/styles/fonts/space-mono-italic.ttf';
16
16
  import spaceMono700 from '../../views/assets/styles/fonts/space-mono-700.ttf';
17
17
  import spaceMono700Italic from '../../views/assets/styles/fonts/space-mono-700-italic.ttf';
18
- import type { TParameters, TSortedPages, TSection } from '../../../docgen/types.ts';
18
+ import type { TParameters, TSortedPages } from '../../../docgen/types.ts';
19
19
  import { preprocessAdmonitions } from '../../common/markdown/markdown.ts';
20
20
 
21
- declare const __BASE_PATH__: string;
21
+ declare const __DOCGEN_PARAMETERS__: TParameters;
22
+ declare const __DOCGEN_PAGES__: TSortedPages;
22
23
 
23
24
  Font.register({
24
25
  family: 'archivo',
@@ -48,58 +49,26 @@ Font.register({
48
49
  ],
49
50
  });
50
51
 
51
- type PdfProps = {
52
- parameters: TParameters;
53
- options: any;
54
- sortedPages: TSortedPages;
55
- };
56
-
57
52
  // Async loader for PDF pages
58
- const loadPdfPages = async (sortedPages: any): Promise<Record<string, string>> => {
59
- const pages: Record<string, string> = {};
60
53
 
61
- const sources = Object.values(sortedPages)
62
- .flatMap((columns: TSection) =>
63
- columns.flatMap((section) => section.pages.map((p: any) => p.source)),
64
- );
65
-
66
- await Promise.all(
67
- sources.map(async (filename) => {
68
- const url = `${__BASE_PATH__}${filename}`;
69
- try {
70
- const res = await fetch(url);
71
- pages[filename] = res.ok ? await res.text() : `Error loading ${filename}: ${res.status}`;
72
- } catch (err) {
73
- pages[filename] = `Error loading ${filename}: ${err}`;
74
- }
75
- }),
54
+ export const Pdf = ({ loadedPages }) => {
55
+ const parameters = __DOCGEN_PARAMETERS__;
56
+ const options = {};
57
+ const allSources = Object.values(__DOCGEN_PAGES__).flatMap((columns) =>
58
+ columns.flatMap((section) => section.pages.map((p: any) => p.source)),
76
59
  );
77
60
 
78
- return pages;
79
- };
80
-
81
- export const Pdf = ({ parameters, options, sortedPages }: PdfProps) => {
82
- const [pages, setPages] = useState<Record<string, string>>({});
83
-
84
- useEffect(() => {
85
- const fetchPages = async () => {
86
- const loaded = await loadPdfPages(sortedPages);
87
- setPages(loaded);
88
- };
89
- fetchPages();
90
- }, [sortedPages]);
91
-
92
- const allSources = Object.values(sortedPages)
93
- .flatMap((columns) =>
94
- columns.flatMap((section) => section.pages.map((p: any) => p.source)),
95
- );
96
-
97
61
  return (
98
62
  <Document>
99
63
  {allSources.map((source, i) => {
100
- const html = marked(preprocessAdmonitions(pages[source] || ''));
64
+ const html = marked(preprocessAdmonitions(loadedPages[source] || ''));
101
65
  return (
102
- <PdfPage key={i} page={html} parameters={parameters} options={options} />
66
+ <PdfPage
67
+ key={i}
68
+ page={html}
69
+ parameters={parameters}
70
+ options={options}
71
+ />
103
72
  );
104
73
  })}
105
74
  </Document>
@@ -0,0 +1,46 @@
1
+ import { useState, useEffect } from 'react';
2
+ import WorkerURL from './generate-pdf.worker.tsx?worker';
3
+
4
+ export type TGeneratedPdf = {
5
+ pdfBlob: Blob | null;
6
+ pdfUrl: string | null;
7
+ };
8
+
9
+ let cachedPdf: TGeneratedPdf | null = null;
10
+
11
+ export const useGeneratePdf = () => {
12
+ const [result, setResult] = useState<TGeneratedPdf>({
13
+ pdfBlob: null,
14
+ pdfUrl: null,
15
+ });
16
+
17
+ useEffect(() => {
18
+ if (cachedPdf) {
19
+ setResult(cachedPdf);
20
+ return;
21
+ }
22
+
23
+ const worker = new WorkerURL();
24
+
25
+ worker.onmessage = (e) => {
26
+ if (e.data.type === 'complete') {
27
+ const blob: Blob | null = e.data.payload.data ?? null;
28
+ const url = blob ? URL.createObjectURL(blob) : null;
29
+
30
+ cachedPdf = {
31
+ pdfBlob: blob,
32
+ pdfUrl: url,
33
+ };
34
+
35
+ setResult(cachedPdf);
36
+ worker.terminate();
37
+ }
38
+ };
39
+
40
+ worker.postMessage({ type: 'start' });
41
+
42
+ return () => worker.terminate();
43
+ }, []);
44
+
45
+ return result;
46
+ };
@@ -0,0 +1,61 @@
1
+ .pdfControlsContainer {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ /* Push items to edges */
5
+ align-items: center;
6
+ width: 100%;
7
+ gap: 16px;
8
+ }
9
+
10
+ .paginationControls {
11
+ display: flex;
12
+ gap: 4px;
13
+ align-items: center;
14
+ /* Removed flex: 1 and justify-content: center to keep it on the left */
15
+ }
16
+
17
+ .actionButtons {
18
+ display: flex;
19
+ gap: 4px;
20
+ align-items: center;
21
+ }
22
+
23
+ .controlButton {
24
+ display: inline-flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ width: 32px;
28
+ height: 32px;
29
+ padding: 0;
30
+ border: none;
31
+ background-color: transparent;
32
+ color: #f1f3f4;
33
+ /* Light grey/white text */
34
+ border-radius: 16px;
35
+ /* Rounded for hover effect */
36
+ cursor: pointer;
37
+ transition: background-color 0.1s ease;
38
+ font-size: 20px;
39
+ /* Icon size */
40
+ }
41
+
42
+ .controlButton:hover {
43
+ background-color: rgba(255, 255, 255, 0.1);
44
+ /* Subtle grey highlight */
45
+ }
46
+
47
+ .controlButton:disabled {
48
+ color: #5f6368;
49
+ /* Dimmed icon color */
50
+ cursor: default;
51
+ background-color: transparent;
52
+ }
53
+
54
+ .pageText {
55
+ font-size: 13px;
56
+ color: #f1f3f4;
57
+ margin: 0 8px;
58
+ font-family: Roboto, Arial, sans-serif;
59
+ font-weight: 500;
60
+ user-select: none;
61
+ }
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import {
3
+ TbChevronLeft,
4
+ TbChevronRight,
5
+ TbDownload,
6
+ TbExternalLink,
7
+ } from 'react-icons/tb';
8
+ import styles from './pdf-controls.module.css';
9
+
10
+ export const PdfControls = ({
11
+ pageNumber,
12
+ numPages,
13
+ goToPrevPage,
14
+ goToNextPage,
15
+ onDownload,
16
+ onOpenNewTab,
17
+ }) => {
18
+ const mobileDevice = 'ongesturechange' in window;
19
+ return (
20
+ <div className={styles.pdfControlsContainer}>
21
+ <div className={styles.paginationControls}>
22
+ <button
23
+ className={styles.controlButton}
24
+ onClick={goToPrevPage}
25
+ disabled={pageNumber <= 1}
26
+ title="Previous page"
27
+ >
28
+ <TbChevronLeft />
29
+ </button>
30
+ <span className={styles.pageText}>
31
+ {pageNumber} of {numPages}
32
+ </span>
33
+ <button
34
+ className={styles.controlButton}
35
+ onClick={goToNextPage}
36
+ disabled={pageNumber >= numPages}
37
+ title="Next page"
38
+ >
39
+ <TbChevronRight />
40
+ </button>
41
+ </div>
42
+ <div className={styles.actionButtons}>
43
+ <button
44
+ className={styles.controlButton}
45
+ onClick={onDownload}
46
+ title="Download PDF"
47
+ >
48
+ <TbDownload />
49
+ </button>
50
+ {!mobileDevice && (
51
+ <button
52
+ className={styles.controlButton}
53
+ onClick={onOpenNewTab}
54
+ title="Open in new tab"
55
+ >
56
+ <TbExternalLink />
57
+ </button>
58
+ )}
59
+ </div>
60
+ </div>
61
+ );
62
+ };
@@ -0,0 +1,22 @@
1
+ .pdfDisplayWrapper {
2
+ > div div {
3
+ /* Override global rule from page.scss that interferes with the text highlighting layer in the react-pdf viewer */
4
+ margin: 0;
5
+ }
6
+
7
+ :global(.react-pdf__Page__annotations) {
8
+ background: transparent !important;
9
+ }
10
+
11
+ :global(.react-pdf__Page__annotations) * {
12
+ background: transparent !important;
13
+ border: none !important;
14
+ box-shadow: none !important;
15
+ }
16
+
17
+ :global(.react-pdf__Page__annotations) a {
18
+ cursor: pointer;
19
+ color: var(--color-text-link) !important;
20
+ text-decoration: underline !important;
21
+ }
22
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { useResizeDetector } from 'react-resize-detector';
3
+ import { Document, Page, pdfjs } from 'react-pdf';
4
+ import styles from './pdf-display.module.css';
5
+
6
+ import 'react-pdf/dist/Page/TextLayer.css';
7
+ import 'react-pdf/dist/Page/AnnotationLayer.css';
8
+ import { PdfLoader } from '../pdf-loader/pdf-loader.tsx';
9
+
10
+ pdfjs.GlobalWorkerOptions.workerSrc = new URL(
11
+ 'pdfjs-dist/build/pdf.worker.min.mjs',
12
+ import.meta.url,
13
+ ).toString();
14
+
15
+ export const PdfDisplay = ({ pdfUrl, pageNumber, onPdfLoadSuccess }) => {
16
+ const { width, ref } = useResizeDetector<HTMLDivElement>();
17
+ if (!pdfUrl) {
18
+ return null;
19
+ }
20
+ const key = `${pdfUrl}-${pageNumber}`;
21
+ return (
22
+ <div ref={ref} className={styles.pdfDisplayWrapper}>
23
+ <Document
24
+ file={pdfUrl}
25
+ loading={<PdfLoader />}
26
+ onLoadSuccess={onPdfLoadSuccess}
27
+ >
28
+ <Page
29
+ key={key}
30
+ pageNumber={pageNumber}
31
+ width={width}
32
+ renderAnnotationLayer={true}
33
+ />
34
+ </Document>
35
+ </div>
36
+ );
37
+ };
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { Loader } from '../../../views/components/loader/loader.tsx';
3
+ import { Spinner } from '../../../views/components/loader/spinner.tsx';
4
+
5
+ export const PdfLoader = () => {
6
+ return (
7
+ <Loader cover text="Loading PDF...">
8
+ <Spinner />
9
+ </Loader>
10
+ );
11
+ };
@@ -0,0 +1,50 @@
1
+ .pdfViewerOuterWrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ flex-grow: 1;
6
+ width: auto;
7
+ margin: 30px 30px 0 30px;
8
+ }
9
+
10
+ .pdfViewerInnerWrapper {
11
+ flex-grow: 1;
12
+ display: flex;
13
+ flex-direction: column;
14
+ min-width: 300px;
15
+ max-width: 800px;
16
+ width: 100%;
17
+ aspect-ratio: 210 / 297;
18
+ overflow: hidden;
19
+ }
20
+
21
+ .card {
22
+ display: flex;
23
+ flex-direction: column;
24
+ height: 100%;
25
+ overflow: hidden;
26
+ }
27
+
28
+ .cardContent {
29
+ flex-grow: 1;
30
+ overflow-y: auto;
31
+ scrollbar-width: none;
32
+ -ms-overflow-style: none;
33
+ }
34
+
35
+ .cardContent::-webkit-scrollbar {
36
+ display: none;
37
+ }
38
+
39
+ .loaderWrapper {
40
+ position: relative;
41
+ width: 100%;
42
+ aspect-ratio: 210 / 297;
43
+ }
44
+
45
+ .pdfHeader {
46
+ background-color: #323639 !important;
47
+ color: white;
48
+ border-bottom: 1px solid #323639;
49
+ padding: 4px 12px !important;
50
+ }
@@ -0,0 +1,67 @@
1
+ import React, { useState } from 'react';
2
+ import { useGeneratePdf } from '../pdf-generator/use-generate.pdf.tsx';
3
+ import { PdfDisplay } from './pdf-display/pdf-display.tsx';
4
+ import { PdfControls } from './pdf-controls/pdf-controls.tsx';
5
+ import styles from './pdf-viewer.module.css';
6
+ import { PdfLoader } from './pdf-loader/pdf-loader.tsx';
7
+ import { Card } from '../../views/components/card/card.tsx';
8
+
9
+ export const PdfViewer = () => {
10
+ const { pdfUrl } = useGeneratePdf();
11
+ const [numPages, setNumPages] = useState(0);
12
+ const [pageNumber, setPageNumber] = useState(1);
13
+
14
+ const loading = !pdfUrl;
15
+
16
+ const onPdfLoadSuccess = ({ numPages }: { numPages: number }) =>
17
+ setNumPages(numPages);
18
+ const goToPrevPage = () => setPageNumber((p) => Math.max(p - 1, 1));
19
+ const goToNextPage = () => setPageNumber((p) => Math.min(p + 1, numPages));
20
+
21
+ const onDownload = () => {
22
+ if (!pdfUrl) return;
23
+ const link = document.createElement('a');
24
+ link.href = pdfUrl;
25
+ link.download = 'document.pdf';
26
+ document.body.appendChild(link);
27
+ link.click();
28
+ document.body.removeChild(link);
29
+ };
30
+
31
+ const onOpenNewTab = () => {
32
+ if (!pdfUrl) return;
33
+ window.open(pdfUrl, '_blank');
34
+ };
35
+
36
+ return (
37
+ <div className={styles.pdfViewerOuterWrapper}>
38
+ <div className={styles.pdfViewerInnerWrapper}>
39
+ <Card
40
+ padding={false}
41
+ headerClassName={styles.pdfHeader}
42
+ className={styles.card}
43
+ contentClassName={styles.cardContent}
44
+ heading={
45
+ <PdfControls
46
+ pageNumber={pageNumber}
47
+ numPages={numPages}
48
+ goToPrevPage={goToPrevPage}
49
+ goToNextPage={goToNextPage}
50
+ onDownload={onDownload}
51
+ onOpenNewTab={onOpenNewTab}
52
+ />
53
+ }
54
+ >
55
+ <div className={styles.loaderWrapper}>
56
+ {loading && <PdfLoader />}
57
+ <PdfDisplay
58
+ pdfUrl={pdfUrl}
59
+ pageNumber={pageNumber}
60
+ onPdfLoadSuccess={onPdfLoadSuccess}
61
+ />
62
+ </div>
63
+ </Card>
64
+ </div>
65
+ </div>
66
+ );
67
+ };