@blocklet/pdf 1.6.61

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.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2018-2020 ArcBlock
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @blocklet/pdf
2
+
3
+ **@blocklet/pdf** is a package that integrates the **react/pdf** service to provide universal pdf capability for blocklets. For more information about react/pdf, refer to the [official documentation](https://www.npmjs.com/package/react-pdf).
4
+
5
+ ## Package Structure
6
+
7
+ The package is composed of both frontend and backend components. The frontend code resides in the `index.js` file
8
+
9
+ ## Development
10
+
11
+ ### Install In Blocklet
12
+
13
+ ```
14
+ # You can use npm / yarn
15
+ pnpm add @blocklet/pdf
16
+ ```
17
+
18
+ ### Install Dependencies
19
+
20
+ To install the required dependencies, run the following command:
21
+
22
+ ```
23
+ pnpm i
24
+ ```
25
+
26
+ ### Build Packages
27
+
28
+ To build the packages, execute the following command:
29
+
30
+ ```
31
+ pnpm build
32
+ ```
33
+
34
+ ### Build, Watch, and Run Development Server
35
+
36
+ For building, watching changes, and running the development server, use the following command:
37
+
38
+ ```
39
+ pnpm run dev
40
+ ```
41
+
42
+ ## Frontend Example
43
+
44
+ ```jsx
45
+ import { lazy } from 'react';
46
+
47
+ const PdfComponent = React.lazy(() =>
48
+ // @ts-ignore
49
+ import('@blocklet/pdf').then((module) => {
50
+ return {
51
+ default: module.PdfComponent,
52
+ };
53
+ })
54
+ );
55
+
56
+ ---
57
+
58
+ <Box>
59
+ <PdfComponent url={pdfUrl} maxHeight="60vh" />
60
+ </Box>;
61
+ ```
62
+
63
+ ## License
64
+
65
+ This package is licensed under the MIT license.
package/lib/index.cjs ADDED
@@ -0,0 +1,333 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const react = require('react');
5
+ const reactPdf = require('react-pdf');
6
+ const ahooks = require('ahooks');
7
+ const useTheme = require('@mui/material/styles/useTheme');
8
+ const useMediaQuery = require('@mui/material/useMediaQuery');
9
+ const Box = require('@mui/material/Box');
10
+ const Skeleton = require('@mui/material/Skeleton');
11
+ const IconButton = require('@mui/material/IconButton');
12
+ const FullscreenIcon = require('@mui/icons-material/Fullscreen');
13
+ const ZoomOutIcon = require('@mui/icons-material/ZoomOut');
14
+ const ZoomInIcon = require('@mui/icons-material/ZoomIn');
15
+ const DownloadIcon = require('@mui/icons-material/Download');
16
+ const ArrowOutwardOutlinedIcon = require('@mui/icons-material/ArrowOutwardOutlined');
17
+ const material = require('@mui/material');
18
+ require('react-pdf/dist/Page/AnnotationLayer.css');
19
+ require('react-pdf/dist/Page/TextLayer.css');
20
+
21
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
22
+
23
+ const useTheme__default = /*#__PURE__*/_interopDefaultCompat(useTheme);
24
+ const useMediaQuery__default = /*#__PURE__*/_interopDefaultCompat(useMediaQuery);
25
+ const Box__default = /*#__PURE__*/_interopDefaultCompat(Box);
26
+ const Skeleton__default = /*#__PURE__*/_interopDefaultCompat(Skeleton);
27
+ const IconButton__default = /*#__PURE__*/_interopDefaultCompat(IconButton);
28
+ const FullscreenIcon__default = /*#__PURE__*/_interopDefaultCompat(FullscreenIcon);
29
+ const ZoomOutIcon__default = /*#__PURE__*/_interopDefaultCompat(ZoomOutIcon);
30
+ const ZoomInIcon__default = /*#__PURE__*/_interopDefaultCompat(ZoomInIcon);
31
+ const DownloadIcon__default = /*#__PURE__*/_interopDefaultCompat(DownloadIcon);
32
+ const ArrowOutwardOutlinedIcon__default = /*#__PURE__*/_interopDefaultCompat(ArrowOutwardOutlinedIcon);
33
+
34
+ const workerSrc = `//unpkg.com/pdfjs-dist@${reactPdf.pdfjs.version}/legacy/build/pdf.worker.min.js`;
35
+ reactPdf.pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
36
+ const preventScrollChangeThumbnailTimerMap = {};
37
+ const getBorder = (color) => `2px solid ${color}`;
38
+ const defaultLoading = (props) => {
39
+ return /* @__PURE__ */ jsxRuntime.jsx(Skeleton__default, { variant: "rectangular", width: "100%", ...props });
40
+ };
41
+ function PdfComponent({
42
+ url,
43
+ backgroundColor = "#fbfbfb",
44
+ loading = defaultLoading,
45
+ maxHeight = "60vh",
46
+ ...rest
47
+ }) {
48
+ const pdfPageWrapperRef = react.useRef(null);
49
+ const thumbnailWrapperRef = react.useRef(null);
50
+ const preventScrollChangeThumbnailRef = react.useRef(false);
51
+ const state = ahooks.useReactive({
52
+ currentPage: 1,
53
+ totalPage: 1,
54
+ scale: 1
55
+ });
56
+ const wrapperSize = ahooks.useSize(pdfPageWrapperRef);
57
+ const theme = useTheme__default();
58
+ const isMobile = useMediaQuery__default(theme.breakpoints.down("sm"));
59
+ const pdfUrl = url;
60
+ const getPdfItemHeight = () => pdfPageWrapperRef.current?.children[0]?.offsetHeight;
61
+ const onDocumentLoadSuccess = (pdf) => {
62
+ state.totalPage = pdf.numPages;
63
+ };
64
+ const onPageChange = (page) => {
65
+ state.currentPage = page;
66
+ preventScrollChangeThumbnailRef.current = true;
67
+ const top = getPdfItemHeight() * (page - 1);
68
+ pdfPageWrapperRef?.current?.scrollTo({
69
+ top,
70
+ behavior: "smooth"
71
+ });
72
+ if (preventScrollChangeThumbnailTimerMap[pdfUrl])
73
+ clearTimeout(preventScrollChangeThumbnailTimerMap[pdfUrl]);
74
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = setTimeout(() => {
75
+ preventScrollChangeThumbnailRef.current = false;
76
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = null;
77
+ }, 1e3);
78
+ };
79
+ const scrollEvent = (value) => {
80
+ if (preventScrollChangeThumbnailRef.current) {
81
+ return false;
82
+ }
83
+ const scrollPage = Math.floor(value.top / getPdfItemHeight()) + 1;
84
+ if (scrollPage > 0 && scrollPage !== state.currentPage) {
85
+ state.currentPage = scrollPage;
86
+ thumbnailWrapperRef.current?.scrollTo({
87
+ // @ts-ignore
88
+ top: document.getElementById(`thumbnail-${scrollPage}`).offsetTop,
89
+ behavior: "smooth"
90
+ });
91
+ return true;
92
+ }
93
+ };
94
+ const { run: runScrollEvent } = ahooks.useDebounceFn(scrollEvent, {
95
+ wait: 200
96
+ });
97
+ ahooks.useScroll(pdfPageWrapperRef, runScrollEvent);
98
+ const commonPdfProps = {
99
+ canvasBackground: backgroundColor
100
+ };
101
+ const [, { enterFullscreen }] = ahooks.useFullscreen(() => document.querySelector(".pdf-wrapper"));
102
+ return /* @__PURE__ */ jsxRuntime.jsx(Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
103
+ reactPdf.Document,
104
+ {
105
+ loading: () => loading({
106
+ height: maxHeight
107
+ }),
108
+ file: pdfUrl,
109
+ onLoadSuccess: onDocumentLoadSuccess,
110
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
111
+ Box__default,
112
+ {
113
+ className: "pdf-wrapper",
114
+ sx: {
115
+ // default max-height: 60vh
116
+ maxHeight
117
+ },
118
+ ...rest,
119
+ children: [
120
+ /* @__PURE__ */ jsxRuntime.jsxs(
121
+ Box__default,
122
+ {
123
+ className: "pdf-page",
124
+ ref: pdfPageWrapperRef,
125
+ sx: {
126
+ background: backgroundColor,
127
+ position: "relative"
128
+ },
129
+ children: [
130
+ new Array(state.totalPage || 0).fill(0).map((_, index) => {
131
+ const currentPage = index + 1;
132
+ return /* @__PURE__ */ jsxRuntime.jsx(
133
+ reactPdf.Page,
134
+ {
135
+ pageNumber: currentPage,
136
+ renderTextLayer: false,
137
+ width: wrapperSize?.width,
138
+ scale: state.scale,
139
+ ...commonPdfProps,
140
+ loading: () => loading({
141
+ height: maxHeight
142
+ })
143
+ },
144
+ `${pdfUrl}-${currentPage}`
145
+ );
146
+ }),
147
+ /* @__PURE__ */ jsxRuntime.jsxs(Box__default, { className: "pdf-page-toolbar", children: [
148
+ /* @__PURE__ */ jsxRuntime.jsx(
149
+ IconButton__default,
150
+ {
151
+ onClick: () => {
152
+ if (state.scale > 0.1) {
153
+ state.scale -= 0.1;
154
+ }
155
+ },
156
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomOutIcon__default, {})
157
+ }
158
+ ),
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ IconButton__default,
161
+ {
162
+ onClick: () => {
163
+ state.scale += 0.1;
164
+ },
165
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomInIcon__default, {})
166
+ }
167
+ ),
168
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton__default, { onClick: enterFullscreen, children: /* @__PURE__ */ jsxRuntime.jsx(FullscreenIcon__default, {}) }),
169
+ /* @__PURE__ */ jsxRuntime.jsx(
170
+ IconButton__default,
171
+ {
172
+ onClick: () => {
173
+ const link = document.createElement("a");
174
+ link.href = pdfUrl;
175
+ link.download = pdfUrl.split("/").pop() || "download.pdf";
176
+ link.click();
177
+ },
178
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon__default, {})
179
+ }
180
+ ),
181
+ /* @__PURE__ */ jsxRuntime.jsx(
182
+ IconButton__default,
183
+ {
184
+ onClick: () => {
185
+ window.open(pdfUrl, "_blank");
186
+ },
187
+ children: /* @__PURE__ */ jsxRuntime.jsx(ArrowOutwardOutlinedIcon__default, {})
188
+ }
189
+ )
190
+ ] })
191
+ ]
192
+ }
193
+ ),
194
+ /* @__PURE__ */ jsxRuntime.jsx(
195
+ Box__default,
196
+ {
197
+ className: "pdf-thumbnail",
198
+ ref: thumbnailWrapperRef,
199
+ sx: {
200
+ ".active": {
201
+ border: getBorder(theme?.palette?.primary?.main),
202
+ ".pdf-thumbnail-index": {
203
+ background: theme?.palette?.primary?.main
204
+ }
205
+ },
206
+ ".no-active": {
207
+ border: getBorder("transparent")
208
+ },
209
+ "*": {
210
+ transition: "all 0.2s",
211
+ cursor: "pointer"
212
+ }
213
+ },
214
+ children: new Array(state.totalPage || 0).fill(0).map((_, index) => {
215
+ const currentPage = index + 1;
216
+ const key = `thumbnail-${currentPage}`;
217
+ const width = isMobile ? 90 : 120;
218
+ return /* @__PURE__ */ jsxRuntime.jsx(
219
+ Box__default,
220
+ {
221
+ id: key,
222
+ className: `${state.currentPage === currentPage ? "active" : "no-active"}`,
223
+ component: "a",
224
+ sx: {
225
+ width: width + 4
226
+ // add border width
227
+ },
228
+ children: /* @__PURE__ */ jsxRuntime.jsx(
229
+ reactPdf.Thumbnail,
230
+ {
231
+ pageIndex: currentPage,
232
+ pageNumber: currentPage,
233
+ width,
234
+ ...commonPdfProps,
235
+ onClick: (e) => {
236
+ e.stopPropagation();
237
+ e.preventDefault();
238
+ onPageChange(currentPage);
239
+ },
240
+ loading: loading({
241
+ height: `calc(${maxHeight} / ${isMobile ? 5 : 4})`,
242
+ sx: {
243
+ border: getBorder("transparent")
244
+ }
245
+ }),
246
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
247
+ Box__default,
248
+ {
249
+ className: "pdf-thumbnail-index",
250
+ component: "span",
251
+ sx: {
252
+ position: "absolute",
253
+ textAlign: "center",
254
+ bottom: 0,
255
+ left: 0,
256
+ right: 0,
257
+ color: "#fff",
258
+ fontSize: 13,
259
+ p: 0.5,
260
+ background: "rgba(0,0,0,0.25)"
261
+ },
262
+ children: [
263
+ "# ",
264
+ currentPage
265
+ ]
266
+ },
267
+ `index-${currentPage}`
268
+ )
269
+ }
270
+ )
271
+ },
272
+ key
273
+ );
274
+ })
275
+ }
276
+ )
277
+ ]
278
+ }
279
+ )
280
+ }
281
+ ) });
282
+ }
283
+ const Root = material.styled(Box__default)`
284
+ .pdf-wrapper {
285
+ display: flex;
286
+ justify-content: center;
287
+ align-items: stretch;
288
+ }
289
+
290
+ .pdf-page {
291
+ flex: 1;
292
+ display: flex;
293
+ flex-direction: column;
294
+ justify-content: flex-start;
295
+ align-items: center;
296
+ overflow-y: auto;
297
+ }
298
+
299
+ .pdf-page-toolbar {
300
+ position: sticky;
301
+ margin-top: 16px;
302
+ bottom: 16px;
303
+ right: 0;
304
+ left: 0;
305
+ background: rgba(0, 0, 0, 0.5);
306
+ border-radius: 4px;
307
+ display: flex;
308
+ align-items: center;
309
+ padding: 2px 8px;
310
+ gap: 4px;
311
+ * {
312
+ color: white;
313
+ }
314
+ }
315
+
316
+ .pdf-thumbnail {
317
+ margin-left: 16px;
318
+ display: flex;
319
+ justify-content: flex-start;
320
+ align-items: center;
321
+ flex-direction: column;
322
+ gap: 16px;
323
+ overflow-y: auto;
324
+ }
325
+
326
+ .react-pdf__Page__annotations {
327
+ display: none !important;
328
+ height: 0px !important;
329
+ width: 0px !important;
330
+ }
331
+ `;
332
+
333
+ exports.PdfComponent = PdfComponent;
@@ -0,0 +1,13 @@
1
+ import { BoxProps } from '@mui/system';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ interface PdfProps extends BoxProps {
5
+ url: string;
6
+ backgroundColor?: string;
7
+ loading?: any;
8
+ maxHeight?: number | string;
9
+ }
10
+
11
+ declare function PdfComponent({ url, backgroundColor, loading, maxHeight, ...rest }: PdfProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { PdfComponent, type PdfProps };
@@ -0,0 +1,13 @@
1
+ import { BoxProps } from '@mui/system';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ interface PdfProps extends BoxProps {
5
+ url: string;
6
+ backgroundColor?: string;
7
+ loading?: any;
8
+ maxHeight?: number | string;
9
+ }
10
+
11
+ declare function PdfComponent({ url, backgroundColor, loading, maxHeight, ...rest }: PdfProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { PdfComponent, type PdfProps };
package/lib/index.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { BoxProps } from '@mui/system';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ interface PdfProps extends BoxProps {
5
+ url: string;
6
+ backgroundColor?: string;
7
+ loading?: any;
8
+ maxHeight?: number | string;
9
+ }
10
+
11
+ declare function PdfComponent({ url, backgroundColor, loading, maxHeight, ...rest }: PdfProps): react_jsx_runtime.JSX.Element;
12
+
13
+ export { PdfComponent, type PdfProps };
package/lib/index.js ADDED
@@ -0,0 +1,333 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const react = require('react');
5
+ const reactPdf = require('react-pdf');
6
+ const ahooks = require('ahooks');
7
+ const useTheme = require('@mui/material/styles/useTheme');
8
+ const useMediaQuery = require('@mui/material/useMediaQuery');
9
+ const Box = require('@mui/material/Box');
10
+ const Skeleton = require('@mui/material/Skeleton');
11
+ const IconButton = require('@mui/material/IconButton');
12
+ const FullscreenIcon = require('@mui/icons-material/Fullscreen');
13
+ const ZoomOutIcon = require('@mui/icons-material/ZoomOut');
14
+ const ZoomInIcon = require('@mui/icons-material/ZoomIn');
15
+ const DownloadIcon = require('@mui/icons-material/Download');
16
+ const ArrowOutwardOutlinedIcon = require('@mui/icons-material/ArrowOutwardOutlined');
17
+ const material = require('@mui/material');
18
+ require('react-pdf/dist/Page/AnnotationLayer.css');
19
+ require('react-pdf/dist/Page/TextLayer.css');
20
+
21
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
22
+
23
+ const useTheme__default = /*#__PURE__*/_interopDefaultCompat(useTheme);
24
+ const useMediaQuery__default = /*#__PURE__*/_interopDefaultCompat(useMediaQuery);
25
+ const Box__default = /*#__PURE__*/_interopDefaultCompat(Box);
26
+ const Skeleton__default = /*#__PURE__*/_interopDefaultCompat(Skeleton);
27
+ const IconButton__default = /*#__PURE__*/_interopDefaultCompat(IconButton);
28
+ const FullscreenIcon__default = /*#__PURE__*/_interopDefaultCompat(FullscreenIcon);
29
+ const ZoomOutIcon__default = /*#__PURE__*/_interopDefaultCompat(ZoomOutIcon);
30
+ const ZoomInIcon__default = /*#__PURE__*/_interopDefaultCompat(ZoomInIcon);
31
+ const DownloadIcon__default = /*#__PURE__*/_interopDefaultCompat(DownloadIcon);
32
+ const ArrowOutwardOutlinedIcon__default = /*#__PURE__*/_interopDefaultCompat(ArrowOutwardOutlinedIcon);
33
+
34
+ const workerSrc = `//unpkg.com/pdfjs-dist@${reactPdf.pdfjs.version}/legacy/build/pdf.worker.min.js`;
35
+ reactPdf.pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
36
+ const preventScrollChangeThumbnailTimerMap = {};
37
+ const getBorder = (color) => `2px solid ${color}`;
38
+ const defaultLoading = (props) => {
39
+ return /* @__PURE__ */ jsxRuntime.jsx(Skeleton__default, { variant: "rectangular", width: "100%", ...props });
40
+ };
41
+ function PdfComponent({
42
+ url,
43
+ backgroundColor = "#fbfbfb",
44
+ loading = defaultLoading,
45
+ maxHeight = "60vh",
46
+ ...rest
47
+ }) {
48
+ const pdfPageWrapperRef = react.useRef(null);
49
+ const thumbnailWrapperRef = react.useRef(null);
50
+ const preventScrollChangeThumbnailRef = react.useRef(false);
51
+ const state = ahooks.useReactive({
52
+ currentPage: 1,
53
+ totalPage: 1,
54
+ scale: 1
55
+ });
56
+ const wrapperSize = ahooks.useSize(pdfPageWrapperRef);
57
+ const theme = useTheme__default();
58
+ const isMobile = useMediaQuery__default(theme.breakpoints.down("sm"));
59
+ const pdfUrl = url;
60
+ const getPdfItemHeight = () => pdfPageWrapperRef.current?.children[0]?.offsetHeight;
61
+ const onDocumentLoadSuccess = (pdf) => {
62
+ state.totalPage = pdf.numPages;
63
+ };
64
+ const onPageChange = (page) => {
65
+ state.currentPage = page;
66
+ preventScrollChangeThumbnailRef.current = true;
67
+ const top = getPdfItemHeight() * (page - 1);
68
+ pdfPageWrapperRef?.current?.scrollTo({
69
+ top,
70
+ behavior: "smooth"
71
+ });
72
+ if (preventScrollChangeThumbnailTimerMap[pdfUrl])
73
+ clearTimeout(preventScrollChangeThumbnailTimerMap[pdfUrl]);
74
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = setTimeout(() => {
75
+ preventScrollChangeThumbnailRef.current = false;
76
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = null;
77
+ }, 1e3);
78
+ };
79
+ const scrollEvent = (value) => {
80
+ if (preventScrollChangeThumbnailRef.current) {
81
+ return false;
82
+ }
83
+ const scrollPage = Math.floor(value.top / getPdfItemHeight()) + 1;
84
+ if (scrollPage > 0 && scrollPage !== state.currentPage) {
85
+ state.currentPage = scrollPage;
86
+ thumbnailWrapperRef.current?.scrollTo({
87
+ // @ts-ignore
88
+ top: document.getElementById(`thumbnail-${scrollPage}`).offsetTop,
89
+ behavior: "smooth"
90
+ });
91
+ return true;
92
+ }
93
+ };
94
+ const { run: runScrollEvent } = ahooks.useDebounceFn(scrollEvent, {
95
+ wait: 200
96
+ });
97
+ ahooks.useScroll(pdfPageWrapperRef, runScrollEvent);
98
+ const commonPdfProps = {
99
+ canvasBackground: backgroundColor
100
+ };
101
+ const [, { enterFullscreen }] = ahooks.useFullscreen(() => document.querySelector(".pdf-wrapper"));
102
+ return /* @__PURE__ */ jsxRuntime.jsx(Root, { children: /* @__PURE__ */ jsxRuntime.jsx(
103
+ reactPdf.Document,
104
+ {
105
+ loading: () => loading({
106
+ height: maxHeight
107
+ }),
108
+ file: pdfUrl,
109
+ onLoadSuccess: onDocumentLoadSuccess,
110
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
111
+ Box__default,
112
+ {
113
+ className: "pdf-wrapper",
114
+ sx: {
115
+ // default max-height: 60vh
116
+ maxHeight
117
+ },
118
+ ...rest,
119
+ children: [
120
+ /* @__PURE__ */ jsxRuntime.jsxs(
121
+ Box__default,
122
+ {
123
+ className: "pdf-page",
124
+ ref: pdfPageWrapperRef,
125
+ sx: {
126
+ background: backgroundColor,
127
+ position: "relative"
128
+ },
129
+ children: [
130
+ new Array(state.totalPage || 0).fill(0).map((_, index) => {
131
+ const currentPage = index + 1;
132
+ return /* @__PURE__ */ jsxRuntime.jsx(
133
+ reactPdf.Page,
134
+ {
135
+ pageNumber: currentPage,
136
+ renderTextLayer: false,
137
+ width: wrapperSize?.width,
138
+ scale: state.scale,
139
+ ...commonPdfProps,
140
+ loading: () => loading({
141
+ height: maxHeight
142
+ })
143
+ },
144
+ `${pdfUrl}-${currentPage}`
145
+ );
146
+ }),
147
+ /* @__PURE__ */ jsxRuntime.jsxs(Box__default, { className: "pdf-page-toolbar", children: [
148
+ /* @__PURE__ */ jsxRuntime.jsx(
149
+ IconButton__default,
150
+ {
151
+ onClick: () => {
152
+ if (state.scale > 0.1) {
153
+ state.scale -= 0.1;
154
+ }
155
+ },
156
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomOutIcon__default, {})
157
+ }
158
+ ),
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ IconButton__default,
161
+ {
162
+ onClick: () => {
163
+ state.scale += 0.1;
164
+ },
165
+ children: /* @__PURE__ */ jsxRuntime.jsx(ZoomInIcon__default, {})
166
+ }
167
+ ),
168
+ /* @__PURE__ */ jsxRuntime.jsx(IconButton__default, { onClick: enterFullscreen, children: /* @__PURE__ */ jsxRuntime.jsx(FullscreenIcon__default, {}) }),
169
+ /* @__PURE__ */ jsxRuntime.jsx(
170
+ IconButton__default,
171
+ {
172
+ onClick: () => {
173
+ const link = document.createElement("a");
174
+ link.href = pdfUrl;
175
+ link.download = pdfUrl.split("/").pop() || "download.pdf";
176
+ link.click();
177
+ },
178
+ children: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon__default, {})
179
+ }
180
+ ),
181
+ /* @__PURE__ */ jsxRuntime.jsx(
182
+ IconButton__default,
183
+ {
184
+ onClick: () => {
185
+ window.open(pdfUrl, "_blank");
186
+ },
187
+ children: /* @__PURE__ */ jsxRuntime.jsx(ArrowOutwardOutlinedIcon__default, {})
188
+ }
189
+ )
190
+ ] })
191
+ ]
192
+ }
193
+ ),
194
+ /* @__PURE__ */ jsxRuntime.jsx(
195
+ Box__default,
196
+ {
197
+ className: "pdf-thumbnail",
198
+ ref: thumbnailWrapperRef,
199
+ sx: {
200
+ ".active": {
201
+ border: getBorder(theme?.palette?.primary?.main),
202
+ ".pdf-thumbnail-index": {
203
+ background: theme?.palette?.primary?.main
204
+ }
205
+ },
206
+ ".no-active": {
207
+ border: getBorder("transparent")
208
+ },
209
+ "*": {
210
+ transition: "all 0.2s",
211
+ cursor: "pointer"
212
+ }
213
+ },
214
+ children: new Array(state.totalPage || 0).fill(0).map((_, index) => {
215
+ const currentPage = index + 1;
216
+ const key = `thumbnail-${currentPage}`;
217
+ const width = isMobile ? 90 : 120;
218
+ return /* @__PURE__ */ jsxRuntime.jsx(
219
+ Box__default,
220
+ {
221
+ id: key,
222
+ className: `${state.currentPage === currentPage ? "active" : "no-active"}`,
223
+ component: "a",
224
+ sx: {
225
+ width: width + 4
226
+ // add border width
227
+ },
228
+ children: /* @__PURE__ */ jsxRuntime.jsx(
229
+ reactPdf.Thumbnail,
230
+ {
231
+ pageIndex: currentPage,
232
+ pageNumber: currentPage,
233
+ width,
234
+ ...commonPdfProps,
235
+ onClick: (e) => {
236
+ e.stopPropagation();
237
+ e.preventDefault();
238
+ onPageChange(currentPage);
239
+ },
240
+ loading: loading({
241
+ height: `calc(${maxHeight} / ${isMobile ? 5 : 4})`,
242
+ sx: {
243
+ border: getBorder("transparent")
244
+ }
245
+ }),
246
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
247
+ Box__default,
248
+ {
249
+ className: "pdf-thumbnail-index",
250
+ component: "span",
251
+ sx: {
252
+ position: "absolute",
253
+ textAlign: "center",
254
+ bottom: 0,
255
+ left: 0,
256
+ right: 0,
257
+ color: "#fff",
258
+ fontSize: 13,
259
+ p: 0.5,
260
+ background: "rgba(0,0,0,0.25)"
261
+ },
262
+ children: [
263
+ "# ",
264
+ currentPage
265
+ ]
266
+ },
267
+ `index-${currentPage}`
268
+ )
269
+ }
270
+ )
271
+ },
272
+ key
273
+ );
274
+ })
275
+ }
276
+ )
277
+ ]
278
+ }
279
+ )
280
+ }
281
+ ) });
282
+ }
283
+ const Root = material.styled(Box__default)`
284
+ .pdf-wrapper {
285
+ display: flex;
286
+ justify-content: center;
287
+ align-items: stretch;
288
+ }
289
+
290
+ .pdf-page {
291
+ flex: 1;
292
+ display: flex;
293
+ flex-direction: column;
294
+ justify-content: flex-start;
295
+ align-items: center;
296
+ overflow-y: auto;
297
+ }
298
+
299
+ .pdf-page-toolbar {
300
+ position: sticky;
301
+ margin-top: 16px;
302
+ bottom: 16px;
303
+ right: 0;
304
+ left: 0;
305
+ background: rgba(0, 0, 0, 0.5);
306
+ border-radius: 4px;
307
+ display: flex;
308
+ align-items: center;
309
+ padding: 2px 8px;
310
+ gap: 4px;
311
+ * {
312
+ color: white;
313
+ }
314
+ }
315
+
316
+ .pdf-thumbnail {
317
+ margin-left: 16px;
318
+ display: flex;
319
+ justify-content: flex-start;
320
+ align-items: center;
321
+ flex-direction: column;
322
+ gap: 16px;
323
+ overflow-y: auto;
324
+ }
325
+
326
+ .react-pdf__Page__annotations {
327
+ display: none !important;
328
+ height: 0px !important;
329
+ width: 0px !important;
330
+ }
331
+ `;
332
+
333
+ exports.PdfComponent = PdfComponent;
package/lib/index.mjs ADDED
@@ -0,0 +1,318 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useRef } from 'react';
3
+ import { pdfjs, Document, Page, Thumbnail } from 'react-pdf';
4
+ import { useReactive, useSize, useDebounceFn, useScroll, useFullscreen } from 'ahooks';
5
+ import useTheme from '@mui/material/styles/useTheme';
6
+ import useMediaQuery from '@mui/material/useMediaQuery';
7
+ import Box from '@mui/material/Box';
8
+ import Skeleton from '@mui/material/Skeleton';
9
+ import IconButton from '@mui/material/IconButton';
10
+ import FullscreenIcon from '@mui/icons-material/Fullscreen';
11
+ import ZoomOutIcon from '@mui/icons-material/ZoomOut';
12
+ import ZoomInIcon from '@mui/icons-material/ZoomIn';
13
+ import DownloadIcon from '@mui/icons-material/Download';
14
+ import ArrowOutwardOutlinedIcon from '@mui/icons-material/ArrowOutwardOutlined';
15
+ import { styled } from '@mui/material';
16
+ import 'react-pdf/dist/Page/AnnotationLayer.css';
17
+ import 'react-pdf/dist/Page/TextLayer.css';
18
+
19
+ const workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`;
20
+ pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;
21
+ const preventScrollChangeThumbnailTimerMap = {};
22
+ const getBorder = (color) => `2px solid ${color}`;
23
+ const defaultLoading = (props) => {
24
+ return /* @__PURE__ */ jsx(Skeleton, { variant: "rectangular", width: "100%", ...props });
25
+ };
26
+ function PdfComponent({
27
+ url,
28
+ backgroundColor = "#fbfbfb",
29
+ loading = defaultLoading,
30
+ maxHeight = "60vh",
31
+ ...rest
32
+ }) {
33
+ const pdfPageWrapperRef = useRef(null);
34
+ const thumbnailWrapperRef = useRef(null);
35
+ const preventScrollChangeThumbnailRef = useRef(false);
36
+ const state = useReactive({
37
+ currentPage: 1,
38
+ totalPage: 1,
39
+ scale: 1
40
+ });
41
+ const wrapperSize = useSize(pdfPageWrapperRef);
42
+ const theme = useTheme();
43
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
44
+ const pdfUrl = url;
45
+ const getPdfItemHeight = () => pdfPageWrapperRef.current?.children[0]?.offsetHeight;
46
+ const onDocumentLoadSuccess = (pdf) => {
47
+ state.totalPage = pdf.numPages;
48
+ };
49
+ const onPageChange = (page) => {
50
+ state.currentPage = page;
51
+ preventScrollChangeThumbnailRef.current = true;
52
+ const top = getPdfItemHeight() * (page - 1);
53
+ pdfPageWrapperRef?.current?.scrollTo({
54
+ top,
55
+ behavior: "smooth"
56
+ });
57
+ if (preventScrollChangeThumbnailTimerMap[pdfUrl])
58
+ clearTimeout(preventScrollChangeThumbnailTimerMap[pdfUrl]);
59
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = setTimeout(() => {
60
+ preventScrollChangeThumbnailRef.current = false;
61
+ preventScrollChangeThumbnailTimerMap[pdfUrl] = null;
62
+ }, 1e3);
63
+ };
64
+ const scrollEvent = (value) => {
65
+ if (preventScrollChangeThumbnailRef.current) {
66
+ return false;
67
+ }
68
+ const scrollPage = Math.floor(value.top / getPdfItemHeight()) + 1;
69
+ if (scrollPage > 0 && scrollPage !== state.currentPage) {
70
+ state.currentPage = scrollPage;
71
+ thumbnailWrapperRef.current?.scrollTo({
72
+ // @ts-ignore
73
+ top: document.getElementById(`thumbnail-${scrollPage}`).offsetTop,
74
+ behavior: "smooth"
75
+ });
76
+ return true;
77
+ }
78
+ };
79
+ const { run: runScrollEvent } = useDebounceFn(scrollEvent, {
80
+ wait: 200
81
+ });
82
+ useScroll(pdfPageWrapperRef, runScrollEvent);
83
+ const commonPdfProps = {
84
+ canvasBackground: backgroundColor
85
+ };
86
+ const [, { enterFullscreen }] = useFullscreen(() => document.querySelector(".pdf-wrapper"));
87
+ return /* @__PURE__ */ jsx(Root, { children: /* @__PURE__ */ jsx(
88
+ Document,
89
+ {
90
+ loading: () => loading({
91
+ height: maxHeight
92
+ }),
93
+ file: pdfUrl,
94
+ onLoadSuccess: onDocumentLoadSuccess,
95
+ children: /* @__PURE__ */ jsxs(
96
+ Box,
97
+ {
98
+ className: "pdf-wrapper",
99
+ sx: {
100
+ // default max-height: 60vh
101
+ maxHeight
102
+ },
103
+ ...rest,
104
+ children: [
105
+ /* @__PURE__ */ jsxs(
106
+ Box,
107
+ {
108
+ className: "pdf-page",
109
+ ref: pdfPageWrapperRef,
110
+ sx: {
111
+ background: backgroundColor,
112
+ position: "relative"
113
+ },
114
+ children: [
115
+ new Array(state.totalPage || 0).fill(0).map((_, index) => {
116
+ const currentPage = index + 1;
117
+ return /* @__PURE__ */ jsx(
118
+ Page,
119
+ {
120
+ pageNumber: currentPage,
121
+ renderTextLayer: false,
122
+ width: wrapperSize?.width,
123
+ scale: state.scale,
124
+ ...commonPdfProps,
125
+ loading: () => loading({
126
+ height: maxHeight
127
+ })
128
+ },
129
+ `${pdfUrl}-${currentPage}`
130
+ );
131
+ }),
132
+ /* @__PURE__ */ jsxs(Box, { className: "pdf-page-toolbar", children: [
133
+ /* @__PURE__ */ jsx(
134
+ IconButton,
135
+ {
136
+ onClick: () => {
137
+ if (state.scale > 0.1) {
138
+ state.scale -= 0.1;
139
+ }
140
+ },
141
+ children: /* @__PURE__ */ jsx(ZoomOutIcon, {})
142
+ }
143
+ ),
144
+ /* @__PURE__ */ jsx(
145
+ IconButton,
146
+ {
147
+ onClick: () => {
148
+ state.scale += 0.1;
149
+ },
150
+ children: /* @__PURE__ */ jsx(ZoomInIcon, {})
151
+ }
152
+ ),
153
+ /* @__PURE__ */ jsx(IconButton, { onClick: enterFullscreen, children: /* @__PURE__ */ jsx(FullscreenIcon, {}) }),
154
+ /* @__PURE__ */ jsx(
155
+ IconButton,
156
+ {
157
+ onClick: () => {
158
+ const link = document.createElement("a");
159
+ link.href = pdfUrl;
160
+ link.download = pdfUrl.split("/").pop() || "download.pdf";
161
+ link.click();
162
+ },
163
+ children: /* @__PURE__ */ jsx(DownloadIcon, {})
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsx(
167
+ IconButton,
168
+ {
169
+ onClick: () => {
170
+ window.open(pdfUrl, "_blank");
171
+ },
172
+ children: /* @__PURE__ */ jsx(ArrowOutwardOutlinedIcon, {})
173
+ }
174
+ )
175
+ ] })
176
+ ]
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(
180
+ Box,
181
+ {
182
+ className: "pdf-thumbnail",
183
+ ref: thumbnailWrapperRef,
184
+ sx: {
185
+ ".active": {
186
+ border: getBorder(theme?.palette?.primary?.main),
187
+ ".pdf-thumbnail-index": {
188
+ background: theme?.palette?.primary?.main
189
+ }
190
+ },
191
+ ".no-active": {
192
+ border: getBorder("transparent")
193
+ },
194
+ "*": {
195
+ transition: "all 0.2s",
196
+ cursor: "pointer"
197
+ }
198
+ },
199
+ children: new Array(state.totalPage || 0).fill(0).map((_, index) => {
200
+ const currentPage = index + 1;
201
+ const key = `thumbnail-${currentPage}`;
202
+ const width = isMobile ? 90 : 120;
203
+ return /* @__PURE__ */ jsx(
204
+ Box,
205
+ {
206
+ id: key,
207
+ className: `${state.currentPage === currentPage ? "active" : "no-active"}`,
208
+ component: "a",
209
+ sx: {
210
+ width: width + 4
211
+ // add border width
212
+ },
213
+ children: /* @__PURE__ */ jsx(
214
+ Thumbnail,
215
+ {
216
+ pageIndex: currentPage,
217
+ pageNumber: currentPage,
218
+ width,
219
+ ...commonPdfProps,
220
+ onClick: (e) => {
221
+ e.stopPropagation();
222
+ e.preventDefault();
223
+ onPageChange(currentPage);
224
+ },
225
+ loading: loading({
226
+ height: `calc(${maxHeight} / ${isMobile ? 5 : 4})`,
227
+ sx: {
228
+ border: getBorder("transparent")
229
+ }
230
+ }),
231
+ children: /* @__PURE__ */ jsxs(
232
+ Box,
233
+ {
234
+ className: "pdf-thumbnail-index",
235
+ component: "span",
236
+ sx: {
237
+ position: "absolute",
238
+ textAlign: "center",
239
+ bottom: 0,
240
+ left: 0,
241
+ right: 0,
242
+ color: "#fff",
243
+ fontSize: 13,
244
+ p: 0.5,
245
+ background: "rgba(0,0,0,0.25)"
246
+ },
247
+ children: [
248
+ "# ",
249
+ currentPage
250
+ ]
251
+ },
252
+ `index-${currentPage}`
253
+ )
254
+ }
255
+ )
256
+ },
257
+ key
258
+ );
259
+ })
260
+ }
261
+ )
262
+ ]
263
+ }
264
+ )
265
+ }
266
+ ) });
267
+ }
268
+ const Root = styled(Box)`
269
+ .pdf-wrapper {
270
+ display: flex;
271
+ justify-content: center;
272
+ align-items: stretch;
273
+ }
274
+
275
+ .pdf-page {
276
+ flex: 1;
277
+ display: flex;
278
+ flex-direction: column;
279
+ justify-content: flex-start;
280
+ align-items: center;
281
+ overflow-y: auto;
282
+ }
283
+
284
+ .pdf-page-toolbar {
285
+ position: sticky;
286
+ margin-top: 16px;
287
+ bottom: 16px;
288
+ right: 0;
289
+ left: 0;
290
+ background: rgba(0, 0, 0, 0.5);
291
+ border-radius: 4px;
292
+ display: flex;
293
+ align-items: center;
294
+ padding: 2px 8px;
295
+ gap: 4px;
296
+ * {
297
+ color: white;
298
+ }
299
+ }
300
+
301
+ .pdf-thumbnail {
302
+ margin-left: 16px;
303
+ display: flex;
304
+ justify-content: flex-start;
305
+ align-items: center;
306
+ flex-direction: column;
307
+ gap: 16px;
308
+ overflow-y: auto;
309
+ }
310
+
311
+ .react-pdf__Page__annotations {
312
+ display: none !important;
313
+ height: 0px !important;
314
+ width: 0px !important;
315
+ }
316
+ `;
317
+
318
+ export { PdfComponent };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@blocklet/pdf",
3
+ "version": "1.6.61",
4
+ "description": "blocklet pdf component",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "require": "./lib/index.cjs",
12
+ "import": "./lib/index.mjs",
13
+ "types": "./lib/index.d.ts"
14
+ },
15
+ "./lib/": "./lib/"
16
+ },
17
+ "main": "./lib/index.cjs",
18
+ "module": "./lib/index.mjs",
19
+ "types": "./lib/index.d.ts",
20
+ "files": [
21
+ "lib",
22
+ "*.d.ts"
23
+ ],
24
+ "scripts": {
25
+ "coverage": "yarn test -- --coverage",
26
+ "test": "vitest tests",
27
+ "build": "unbuild",
28
+ "build:watch": "npx nodemon --ext 'ts,tsx,json,js,jsx' --exec 'yarn run build' --ignore 'lib/*' ",
29
+ "dev": "yarn run build:watch",
30
+ "prepublish": "yarn run build",
31
+ "prebuild:dep": "yarn run build"
32
+ },
33
+ "keywords": [
34
+ "blocklet",
35
+ "pdf"
36
+ ],
37
+ "author": "arcblock <blocklet@arcblock.io> https://github.com/blocklet",
38
+ "license": "ISC",
39
+ "dependencies": {
40
+ "@mui/icons-material": "^5.15.4",
41
+ "@mui/material": "^5.15.4",
42
+ "@mui/system": "^5.15.4",
43
+ "ahooks": "^3.7.8",
44
+ "react": "^18.2.0",
45
+ "react-pdf": "^7.7.0"
46
+ },
47
+ "devDependencies": {
48
+ "@arcblock/eslint-config-ts": "^0.2.4",
49
+ "@types/express": "^4.17.21",
50
+ "@types/lodash": "^4.14.202",
51
+ "@types/mime-types": "^2.1.4",
52
+ "@types/node": "^20.10.7",
53
+ "@types/react": "^18.2.47",
54
+ "@types/url-join": "^4.0.3",
55
+ "@vitest/coverage-c8": "^0.33.0",
56
+ "jsdom": "^22.1.0",
57
+ "typescript": "^5.3.3",
58
+ "unbuild": "^2.0.0",
59
+ "vitest": "^1.1.3",
60
+ "vitest-fetch-mock": "^0.2.2"
61
+ },
62
+ "gitHead": "c48a7dafae8605afaca690f4b1aeb70976f965f2"
63
+ }