@pdfme/ui 1.0.0-beta.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 (118) hide show
  1. package/.eslintrc.js +45 -0
  2. package/README.md +280 -0
  3. package/declaration.d.ts +8 -0
  4. package/dist/@pdfme/ui.js +3 -0
  5. package/dist/@pdfme/ui.js.LICENSE.txt +95 -0
  6. package/dist/@pdfme/ui.js.map +1 -0
  7. package/dist/types/common/src/barcode.d.ts +2 -0
  8. package/dist/types/common/src/constants.d.ts +6 -0
  9. package/dist/types/common/src/helper.d.ts +15 -0
  10. package/dist/types/common/src/index.d.ts +4 -0
  11. package/dist/types/common/src/schema.d.ts +3613 -0
  12. package/dist/types/common/src/type.d.ts +64 -0
  13. package/dist/types/common/src/utils.d.ts +12 -0
  14. package/dist/types/ui/src/Designer.d.ts +13 -0
  15. package/dist/types/ui/src/Form.d.ts +13 -0
  16. package/dist/types/ui/src/Viewer.d.ts +7 -0
  17. package/dist/types/ui/src/class.d.ts +72 -0
  18. package/dist/types/ui/src/components/Designer/Main/Guides.d.ts +9 -0
  19. package/dist/types/ui/src/components/Designer/Main/Mask.d.ts +3 -0
  20. package/dist/types/ui/src/components/Designer/Main/Moveable.d.ts +31 -0
  21. package/dist/types/ui/src/components/Designer/Main/Selecto.d.ts +8 -0
  22. package/dist/types/ui/src/components/Designer/Main/index.d.ts +24 -0
  23. package/dist/types/ui/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.d.ts +3 -0
  24. package/dist/types/ui/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.d.ts +3 -0
  25. package/dist/types/ui/src/components/Designer/Sidebar/DetailView/TextPropEditor.d.ts +3 -0
  26. package/dist/types/ui/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.d.ts +3 -0
  27. package/dist/types/ui/src/components/Designer/Sidebar/DetailView/index.d.ts +3 -0
  28. package/dist/types/ui/src/components/Designer/Sidebar/ListView.d.ts +3 -0
  29. package/dist/types/ui/src/components/Designer/Sidebar/index.d.ts +26 -0
  30. package/dist/types/ui/src/components/Designer/index.d.ts +99 -0
  31. package/dist/types/ui/src/components/Divider.d.ts +2 -0
  32. package/dist/types/ui/src/components/Error.d.ts +7 -0
  33. package/dist/types/ui/src/components/Paper.d.ts +19 -0
  34. package/dist/types/ui/src/components/Preview/Pager/Page.d.ts +8 -0
  35. package/dist/types/ui/src/components/Preview/Pager/Unit.d.ts +8 -0
  36. package/dist/types/ui/src/components/Preview/index.d.ts +4 -0
  37. package/dist/types/ui/src/components/Root.d.ts +9 -0
  38. package/dist/types/ui/src/components/Schemas/BarcodeSchema.d.ts +15 -0
  39. package/dist/types/ui/src/components/Schemas/ImageSchema.d.ts +15 -0
  40. package/dist/types/ui/src/components/Schemas/SchemaUI.d.ts +14 -0
  41. package/dist/types/ui/src/components/Schemas/TextSchema.d.ts +22 -0
  42. package/dist/types/ui/src/components/Spinner.d.ts +2 -0
  43. package/dist/types/ui/src/constants.d.ts +5 -0
  44. package/dist/types/ui/src/contexts.d.ts +7 -0
  45. package/dist/types/ui/src/helper.d.ts +91 -0
  46. package/dist/types/ui/src/hooks.d.ts +26 -0
  47. package/dist/types/ui/src/i18n.d.ts +30 -0
  48. package/dist/types/ui/src/index.d.ts +5 -0
  49. package/dist/types/ui/src/libs/class.d.ts +84 -0
  50. package/dist/types/ui/src/libs/contexts.d.ts +7 -0
  51. package/dist/types/ui/src/libs/helper.d.ts +64 -0
  52. package/dist/types/ui/src/libs/hooks.d.ts +26 -0
  53. package/dist/types/ui/src/libs/i18n.d.ts +30 -0
  54. package/dist/types/ui/src/libs/ui.d.ts +64 -0
  55. package/package.json +80 -0
  56. package/public/Designer.html +90 -0
  57. package/public/Form.html +74 -0
  58. package/public/SauceHanSansJP.ttf +0 -0
  59. package/public/SauceHanSerifJP.ttf +0 -0
  60. package/public/Viewer.html +73 -0
  61. package/public/helper.js +51 -0
  62. package/public/index.html +54 -0
  63. package/src/Designer.tsx +72 -0
  64. package/src/Form.tsx +45 -0
  65. package/src/Viewer.tsx +27 -0
  66. package/src/assets/barcodeExamples/code128.png +0 -0
  67. package/src/assets/barcodeExamples/code39.png +0 -0
  68. package/src/assets/barcodeExamples/ean13.png +0 -0
  69. package/src/assets/barcodeExamples/ean8.png +0 -0
  70. package/src/assets/barcodeExamples/itf14.png +0 -0
  71. package/src/assets/barcodeExamples/japanpost.png +0 -0
  72. package/src/assets/barcodeExamples/nw7.png +0 -0
  73. package/src/assets/barcodeExamples/qrcode.png +0 -0
  74. package/src/assets/barcodeExamples/upca.png +0 -0
  75. package/src/assets/barcodeExamples/upce.png +0 -0
  76. package/src/assets/icons/back.svg +4 -0
  77. package/src/assets/icons/double-left.svg +11 -0
  78. package/src/assets/icons/double-right.svg +11 -0
  79. package/src/assets/icons/drag.svg +3 -0
  80. package/src/assets/icons/forward.svg +4 -0
  81. package/src/assets/icons/left.svg +4 -0
  82. package/src/assets/icons/right.svg +4 -0
  83. package/src/assets/icons/warning.svg +4 -0
  84. package/src/assets/imageExample.png +0 -0
  85. package/src/class.ts +147 -0
  86. package/src/components/Designer/Main/Guides.tsx +53 -0
  87. package/src/components/Designer/Main/Mask.tsx +19 -0
  88. package/src/components/Designer/Main/Moveable.tsx +79 -0
  89. package/src/components/Designer/Main/Selecto.tsx +29 -0
  90. package/src/components/Designer/Main/index.tsx +314 -0
  91. package/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.tsx +62 -0
  92. package/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.tsx +98 -0
  93. package/src/components/Designer/Sidebar/DetailView/TextPropEditor.tsx +178 -0
  94. package/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.tsx +79 -0
  95. package/src/components/Designer/Sidebar/DetailView/index.tsx +39 -0
  96. package/src/components/Designer/Sidebar/ListView.tsx +180 -0
  97. package/src/components/Designer/Sidebar/index.tsx +102 -0
  98. package/src/components/Designer/index.tsx +283 -0
  99. package/src/components/Divider.tsx +7 -0
  100. package/src/components/Error.tsx +31 -0
  101. package/src/components/Paper.tsx +77 -0
  102. package/src/components/Preview/Pager/Page.tsx +85 -0
  103. package/src/components/Preview/Pager/Unit.tsx +87 -0
  104. package/src/components/Preview/index.tsx +102 -0
  105. package/src/components/Root.tsx +52 -0
  106. package/src/components/Schemas/BarcodeSchema.tsx +111 -0
  107. package/src/components/Schemas/ImageSchema.tsx +81 -0
  108. package/src/components/Schemas/SchemaUI.tsx +64 -0
  109. package/src/components/Schemas/TextSchema.tsx +62 -0
  110. package/src/components/Spinner.tsx +37 -0
  111. package/src/constants.ts +9 -0
  112. package/src/contexts.ts +8 -0
  113. package/src/helper.ts +516 -0
  114. package/src/hooks.ts +107 -0
  115. package/src/i18n.ts +64 -0
  116. package/src/index.ts +77 -0
  117. package/tsconfig.json +21 -0
  118. package/webpack.config.js +73 -0
@@ -0,0 +1,283 @@
1
+ import React, { useRef, useState, useEffect, useContext, useCallback } from 'react';
2
+ import { DesignerReactProps, Template, SchemaForUI } from '@pdfme/common';
3
+ import Sidebar from './Sidebar';
4
+ import Main from './Main';
5
+ import { ZOOM, RULER_HEIGHT } from '../../constants';
6
+ import { I18nContext } from '../../contexts';
7
+ import {
8
+ uuid,
9
+ set,
10
+ arrayMove,
11
+ cloneDeep,
12
+ initShortCuts,
13
+ destroyShortCuts,
14
+ templateSchemas2SchemasList,
15
+ fmtTemplate,
16
+ getInitialSchema,
17
+ getSampleByType,
18
+ getKeepRatioHeightByWidth,
19
+ getUniqSchemaKey,
20
+ moveCommandToChangeSchemasArg,
21
+ } from '../../helper';
22
+ import { useUIPreProcessor, useScrollPageCursor } from '../../hooks';
23
+ import Root from '../Root';
24
+ import Error from '../Error';
25
+
26
+ const TemplateEditor = ({
27
+ template,
28
+ size,
29
+ onSaveTemplate,
30
+ onChangeTemplate,
31
+ }: DesignerReactProps & { onChangeTemplate: (t: Template) => void }) => {
32
+ const copiedSchemas = useRef<SchemaForUI[] | null>(null);
33
+ const past = useRef<SchemaForUI[][]>([]);
34
+ const future = useRef<SchemaForUI[][]>([]);
35
+ const rootRef = useRef<HTMLDivElement>(null);
36
+ const mainRef = useRef<HTMLDivElement>(null);
37
+ const paperRefs = useRef<HTMLDivElement[]>([]);
38
+
39
+ const i18n = useContext(I18nContext);
40
+
41
+ const { backgrounds, pageSizes, scale, error } = useUIPreProcessor({
42
+ template,
43
+ size,
44
+ offset: RULER_HEIGHT,
45
+ });
46
+
47
+ const [hoveringSchemaId, setHoveringSchemaId] = useState<string | null>(null);
48
+ const [activeElements, setActiveElements] = useState<HTMLElement[]>([]);
49
+ const [schemasList, setSchemasList] = useState<SchemaForUI[][]>([[]] as SchemaForUI[][]);
50
+ const [pageCursor, setPageCursor] = useState(0);
51
+
52
+ const onEdit = (targets: HTMLElement[]) => {
53
+ setActiveElements(targets);
54
+ setHoveringSchemaId(null);
55
+ };
56
+
57
+ const onEditEnd = () => {
58
+ setActiveElements([]);
59
+ setHoveringSchemaId(null);
60
+ };
61
+
62
+ useScrollPageCursor({
63
+ rootRef,
64
+ pageSizes,
65
+ scale,
66
+ pageCursor,
67
+ onChangePageCursor: (p) => {
68
+ setPageCursor(p);
69
+ onEditEnd();
70
+ },
71
+ });
72
+
73
+ const modifiedTemplate = fmtTemplate(template, schemasList);
74
+
75
+ const commitSchemas = useCallback(
76
+ (newSchemas: SchemaForUI[]) => {
77
+ future.current = [];
78
+ past.current.push(cloneDeep(schemasList[pageCursor]));
79
+ const _schemasList = cloneDeep(schemasList);
80
+ _schemasList[pageCursor] = newSchemas;
81
+ setSchemasList(_schemasList);
82
+ onChangeTemplate(fmtTemplate(template, _schemasList));
83
+ },
84
+ [template, schemasList, pageCursor, onChangeTemplate]
85
+ );
86
+
87
+ const removeSchemas = useCallback(
88
+ (ids: string[]) => {
89
+ commitSchemas(schemasList[pageCursor].filter((schema) => !ids.includes(schema.id)));
90
+ onEditEnd();
91
+ },
92
+ [schemasList, pageCursor, commitSchemas]
93
+ );
94
+
95
+ const changeSchemas = useCallback(
96
+ (objs: { key: string; value: string | number; schemaId: string }[]) => {
97
+ const newSchemas = objs.reduce((acc, { key, value, schemaId }) => {
98
+ const tgt = acc.find((s) => s.id === schemaId)!;
99
+ // Assign to reference
100
+ set(tgt, key, value);
101
+ if (key === 'type') {
102
+ // set default value, text or barcode
103
+ set(tgt, 'data', value === 'text' ? 'text' : getSampleByType(String(value)));
104
+ // For barcodes, adjust the height to get the correct ratio.
105
+ if (value !== 'text' && value !== 'image') {
106
+ set(tgt, 'height', getKeepRatioHeightByWidth(String(value), tgt.width));
107
+ }
108
+ }
109
+
110
+ return acc;
111
+ }, cloneDeep(schemasList[pageCursor]));
112
+ commitSchemas(newSchemas);
113
+ },
114
+ [commitSchemas, pageCursor, schemasList]
115
+ );
116
+
117
+ const initEvents = useCallback(() => {
118
+ const getActiveSchemas = () => {
119
+ const ids = activeElements.map((ae) => ae.id);
120
+
121
+ return schemasList[pageCursor].filter((s) => ids.includes(s.id));
122
+ };
123
+ const timeTavel = (mode: 'undo' | 'redo') => {
124
+ const isUndo = mode === 'undo';
125
+ if ((isUndo ? past : future).current.length <= 0) return;
126
+ (isUndo ? future : past).current.push(cloneDeep(schemasList[pageCursor]));
127
+ const s = cloneDeep(schemasList);
128
+ s[pageCursor] = (isUndo ? past : future).current.pop()!;
129
+ setSchemasList(s);
130
+ onEditEnd();
131
+ };
132
+ initShortCuts({
133
+ move: (command, isShift) => {
134
+ const pageSize = pageSizes[pageCursor];
135
+ const activeSchemas = getActiveSchemas();
136
+ const arg = moveCommandToChangeSchemasArg({ command, activeSchemas, pageSize, isShift });
137
+ changeSchemas(arg);
138
+ },
139
+
140
+ copy: () => {
141
+ const activeSchemas = getActiveSchemas();
142
+ if (activeSchemas.length === 0) return;
143
+ copiedSchemas.current = activeSchemas;
144
+ },
145
+ paste: () => {
146
+ if (!copiedSchemas.current || copiedSchemas.current.length === 0) return;
147
+ const schema = schemasList[pageCursor];
148
+ const stackUniqSchemaKeys: string[] = [];
149
+ const pasteSchemas = copiedSchemas.current.map((cs) => {
150
+ const id = uuid();
151
+ const key = getUniqSchemaKey({ copiedSchemaKey: cs.key, schema, stackUniqSchemaKeys });
152
+ const { height, width, position: p } = cs;
153
+ const ps = pageSizes[pageCursor];
154
+ const position = {
155
+ x: p.x + 10 > ps.width - width ? ps.width - width : p.x + 10,
156
+ y: p.y + 10 > ps.height - height ? ps.height - height : p.y + 10,
157
+ };
158
+
159
+ return Object.assign(cloneDeep(cs), { id, key, position });
160
+ });
161
+ commitSchemas(schemasList[pageCursor].concat(pasteSchemas));
162
+ onEdit(pasteSchemas.map((s) => document.getElementById(s.id)!));
163
+ copiedSchemas.current = pasteSchemas;
164
+ },
165
+ redo: () => timeTavel('redo'),
166
+ undo: () => timeTavel('undo'),
167
+ save: () => onSaveTemplate && onSaveTemplate(modifiedTemplate),
168
+ remove: () => removeSchemas(getActiveSchemas().map((s) => s.id)),
169
+ esc: onEditEnd,
170
+ });
171
+ }, [
172
+ activeElements,
173
+ changeSchemas,
174
+ commitSchemas,
175
+ modifiedTemplate,
176
+ pageCursor,
177
+ pageSizes,
178
+ removeSchemas,
179
+ onSaveTemplate,
180
+ schemasList,
181
+ ]);
182
+
183
+ const destroyEvents = useCallback(() => {
184
+ destroyShortCuts();
185
+ }, []);
186
+
187
+ const updateTemplate = useCallback(async (newTemplate: Template) => {
188
+ const sl = await templateSchemas2SchemasList(newTemplate);
189
+ setSchemasList(sl);
190
+ onEditEnd();
191
+ setPageCursor(0);
192
+ if (rootRef.current?.scroll) {
193
+ rootRef.current.scroll({ top: 0, behavior: 'smooth' });
194
+ }
195
+ }, []);
196
+
197
+ useEffect(() => {
198
+ updateTemplate(template);
199
+ }, [template, updateTemplate]);
200
+
201
+ useEffect(() => {
202
+ initEvents();
203
+
204
+ return destroyEvents;
205
+ }, [initEvents, destroyEvents]);
206
+
207
+ const addSchema = () => {
208
+ const s = getInitialSchema();
209
+ const paper = paperRefs.current[pageCursor];
210
+ const rectTop = paper ? paper.getBoundingClientRect().top : 0;
211
+ s.position.y = rectTop > 0 ? 0 : pageSizes[pageCursor].height / 2;
212
+ s.data = 'text';
213
+ s.key = `${i18n('field')}${schemasList[pageCursor].length + 1}`;
214
+ commitSchemas(schemasList[pageCursor].concat(s));
215
+ setTimeout(() => onEdit([document.getElementById(s.id)!]));
216
+ };
217
+
218
+ const onSortEnd = (arg: { oldIndex: number; newIndex: number }) => {
219
+ const movedSchema = arrayMove(cloneDeep(schemasList[pageCursor]), arg.oldIndex, arg.newIndex);
220
+ commitSchemas(movedSchema);
221
+ };
222
+
223
+ const onChangeHoveringSchemaId = (id: string | null) => {
224
+ setHoveringSchemaId(id);
225
+ };
226
+
227
+ const getLastActiveSchema = () => {
228
+ if (activeElements.length === 0) return getInitialSchema();
229
+ const last = activeElements[activeElements.length - 1];
230
+
231
+ return schemasList[pageCursor].find((s) => s.id === last.id) || getInitialSchema();
232
+ };
233
+
234
+ const activeSchema = getLastActiveSchema();
235
+
236
+ if (error) {
237
+ return <Error size={size} error={error} />;
238
+ }
239
+
240
+ return (
241
+ <Root ref={rootRef} size={size} scale={scale}>
242
+ <Sidebar
243
+ hoveringSchemaId={hoveringSchemaId}
244
+ onChangeHoveringSchemaId={onChangeHoveringSchemaId}
245
+ height={mainRef.current ? mainRef.current.scrollHeight : 0}
246
+ size={size}
247
+ pageSize={pageSizes[pageCursor]}
248
+ activeElement={activeElements[activeElements.length - 1]}
249
+ schemas={schemasList[pageCursor]}
250
+ activeSchema={activeSchema}
251
+ changeSchemas={changeSchemas}
252
+ onSortEnd={onSortEnd}
253
+ onEdit={(id: string) => {
254
+ const editingElem = document.getElementById(id);
255
+ if (editingElem) {
256
+ onEdit([editingElem]);
257
+ }
258
+ }}
259
+ onEditEnd={onEditEnd}
260
+ addSchema={addSchema}
261
+ />
262
+ <Main
263
+ ref={mainRef}
264
+ paperRefs={paperRefs}
265
+ hoveringSchemaId={hoveringSchemaId}
266
+ onChangeHoveringSchemaId={onChangeHoveringSchemaId}
267
+ height={size.height - RULER_HEIGHT * ZOOM}
268
+ pageCursor={pageCursor}
269
+ scale={scale}
270
+ size={size}
271
+ pageSizes={pageSizes}
272
+ backgrounds={backgrounds}
273
+ activeElements={activeElements}
274
+ schemasList={schemasList}
275
+ changeSchemas={changeSchemas}
276
+ removeSchemas={removeSchemas}
277
+ onEdit={onEdit}
278
+ />
279
+ </Root>
280
+ );
281
+ };
282
+
283
+ export default TemplateEditor;
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ const Divider = () => (
4
+ <div style={{ marginTop: '0.5rem', marginBottom: '0.5rem', borderBottom: '1px solid #e5e5e5' }} />
5
+ );
6
+
7
+ export default Divider;
@@ -0,0 +1,31 @@
1
+ import React, { useContext } from 'react';
2
+ import { Size } from '@pdfme/common';
3
+ import { I18nContext } from '../contexts';
4
+
5
+ const Error = ({ size, error }: { size: Size; error: Error }) => {
6
+ const i18n = useContext(I18nContext);
7
+
8
+ return (
9
+ <div
10
+ style={{
11
+ position: 'relative',
12
+ background: 'rgb(74, 74, 74)',
13
+ overflowY: 'auto',
14
+ display: 'flex',
15
+ alignItems: 'center',
16
+ justifyContent: 'center',
17
+ ...size,
18
+ }}
19
+ >
20
+ <p style={{ color: '#fff', textAlign: 'center' }}>
21
+ <span style={{ fontSize: 'large', fontWeight: 'bold', borderBottom: '1px solid #fff' }}>
22
+ ERROR: {i18n('errorOccurred')}
23
+ </span>
24
+ <br />
25
+ <span style={{ fontSize: 'small' }}>*{error.message}</span>
26
+ </p>
27
+ </div>
28
+ );
29
+ };
30
+
31
+ export default Error;
@@ -0,0 +1,77 @@
1
+ import React, { MutableRefObject, ReactNode, useContext } from 'react';
2
+ import { SchemaForUI, Size, getFallbackFontName } from '@pdfme/common';
3
+ import { FontContext } from '../contexts';
4
+ import { ZOOM, RULER_HEIGHT } from '../constants';
5
+
6
+ const Paper = (porps: {
7
+ paperRefs?: MutableRefObject<HTMLDivElement[]>;
8
+ scale: number;
9
+ size: Size;
10
+ schemasList: SchemaForUI[][];
11
+ pageSizes: Size[];
12
+ backgrounds: string[];
13
+ renderPaper?: (arg: { index: number; paperSize: Size }) => ReactNode;
14
+ renderSchema: (arg: { index: number; schema: SchemaForUI }) => ReactNode;
15
+ }) => {
16
+ const { paperRefs, scale, size, schemasList, pageSizes, backgrounds, renderPaper, renderSchema } =
17
+ porps;
18
+ const font = useContext(FontContext);
19
+
20
+ if (pageSizes.length !== backgrounds.length || pageSizes.length !== schemasList.length) {
21
+ return null;
22
+ }
23
+
24
+ const topPageWidth = pageSizes[0].width;
25
+
26
+ return (
27
+ <div
28
+ style={{
29
+ transform: `scale(${scale})`,
30
+ transformOrigin: size.width <= topPageWidth * ZOOM * scale ? `left top` : `center top`,
31
+ }}
32
+ >
33
+ {backgrounds.map((background, paperIndex) => {
34
+ const pageSize = pageSizes[paperIndex];
35
+ const paperSize = { width: pageSize.width * ZOOM, height: pageSize.height * ZOOM };
36
+
37
+ return (
38
+ <div
39
+ key={paperIndex + JSON.stringify(paperSize)}
40
+ ref={(e) => {
41
+ if (e && paperRefs) {
42
+ paperRefs.current[paperIndex] = e;
43
+ }
44
+ }}
45
+ style={{
46
+ fontFamily: `'${getFallbackFontName(font)}'`,
47
+ margin: `${RULER_HEIGHT * scale}px auto`,
48
+ position: 'relative',
49
+ backgroundImage: `url(${background})`,
50
+ backgroundSize: 'cover',
51
+ backgroundPosition: 'center',
52
+ backgroundRepeat: 'no-repeat',
53
+ ...paperSize,
54
+ }}
55
+ >
56
+ {renderPaper && renderPaper({ paperSize, index: paperIndex })}
57
+ {schemasList[paperIndex].map((schema, schemaIndex) => {
58
+ return (
59
+ <div key={schema.id}>
60
+ {renderSchema({
61
+ schema,
62
+ index:
63
+ paperIndex === 0
64
+ ? schemaIndex
65
+ : schemaIndex + schemasList[paperIndex - 1].length,
66
+ })}
67
+ </div>
68
+ );
69
+ })}
70
+ </div>
71
+ );
72
+ })}
73
+ </div>
74
+ );
75
+ };
76
+
77
+ export default Paper;
@@ -0,0 +1,85 @@
1
+ import React, { useContext } from 'react';
2
+ import left from '../../../assets/icons/left.svg';
3
+ import right from '../../../assets/icons/right.svg';
4
+ import doubleLeft from '../../../assets/icons/double-left.svg';
5
+ import doubleRight from '../../../assets/icons/double-right.svg';
6
+ import { I18nContext } from '../../../contexts';
7
+
8
+ const btnStyle: React.CSSProperties = {
9
+ cursor: 'pointer',
10
+ border: 'none',
11
+ background: 'none',
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ };
15
+
16
+ type Props = {
17
+ pageCursor: number;
18
+ pageNum: number;
19
+ setPageCursor: (page: number) => void;
20
+ };
21
+
22
+ const Pager = ({ pageCursor, pageNum, setPageCursor }: Props) => {
23
+ const i18n = useContext(I18nContext);
24
+
25
+ if (pageNum <= 1) return <></>;
26
+
27
+ return (
28
+ <div
29
+ style={{
30
+ position: 'sticky',
31
+ top: '90%',
32
+ zIndex: 1,
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ width: '100%',
37
+ }}
38
+ >
39
+ <div
40
+ style={{
41
+ display: 'flex',
42
+ alignItems: 'center',
43
+ justifyContent: 'center',
44
+ background: '#777777e6',
45
+ borderRadius: 3,
46
+ padding: '0.5rem',
47
+ }}
48
+ >
49
+ <button
50
+ style={{ paddingLeft: '0.5rem', ...btnStyle }}
51
+ disabled={pageCursor <= 0}
52
+ onClick={() => setPageCursor(0)}
53
+ >
54
+ <img src={doubleLeft} alt={i18n('goToFirst')} style={{ width: 20 }} />
55
+ </button>
56
+ <button
57
+ style={{ paddingLeft: '0.5rem', ...btnStyle }}
58
+ disabled={pageCursor <= 0}
59
+ onClick={() => setPageCursor(pageCursor - 1)}
60
+ >
61
+ <img src={left} alt={i18n('goToPrevious')} style={{ width: 20 }} />
62
+ </button>
63
+ <strong style={{ color: 'white' }}>
64
+ {pageCursor + 1}/{pageNum}
65
+ </strong>
66
+ <button
67
+ style={{ paddingRight: '0.5rem', ...btnStyle }}
68
+ disabled={pageCursor + 1 >= pageNum}
69
+ onClick={() => setPageCursor(pageCursor + 1)}
70
+ >
71
+ <img src={right} alt={i18n('goToNext')} style={{ width: 20 }} />
72
+ </button>
73
+ <button
74
+ style={{ paddingRight: '0.5rem', ...btnStyle }}
75
+ disabled={pageCursor + 1 >= pageNum}
76
+ onClick={() => setPageCursor(pageNum - 1)}
77
+ >
78
+ <img src={doubleRight} alt={i18n('goToFirst')} style={{ width: 20 }} />
79
+ </button>
80
+ </div>
81
+ </div>
82
+ );
83
+ };
84
+
85
+ export default Pager;
@@ -0,0 +1,87 @@
1
+ import React, { useContext } from 'react';
2
+ import left from '../../../assets/icons/left.svg';
3
+ import right from '../../../assets/icons/right.svg';
4
+ import doubleLeft from '../../../assets/icons/double-left.svg';
5
+ import doubleRight from '../../../assets/icons/double-right.svg';
6
+ import { I18nContext } from '../../../contexts';
7
+
8
+ const buttonWrapStyle: React.CSSProperties = {
9
+ position: 'sticky',
10
+ top: '45%',
11
+ zIndex: 1,
12
+ backgroundColor: '#777777bd',
13
+ borderRadius: 3,
14
+ padding: '0.5rem',
15
+ display: 'flex',
16
+ alignItems: 'center',
17
+ width: 100,
18
+ };
19
+
20
+ const btnStyle: React.CSSProperties = {
21
+ cursor: 'pointer',
22
+ border: 'none',
23
+ background: 'none',
24
+ display: 'flex',
25
+ alignItems: 'center',
26
+ };
27
+
28
+ type Props = {
29
+ unitCursor: number;
30
+ unitNum: number;
31
+ setUnitCursor: (page: number) => void;
32
+ };
33
+
34
+ const UnitPager = ({ unitCursor, unitNum, setUnitCursor }: Props) => {
35
+ const i18n = useContext(I18nContext);
36
+
37
+ if (unitNum <= 1) return <></>;
38
+
39
+ return (
40
+ <>
41
+ {unitCursor > 0 && (
42
+ <div style={{ marginLeft: '1rem', ...buttonWrapStyle }}>
43
+ <button
44
+ style={{ paddingLeft: '0.5rem', ...btnStyle }}
45
+ disabled={unitCursor <= 0}
46
+ onClick={() => setUnitCursor(0)}
47
+ >
48
+ <img src={doubleLeft} alt={i18n('goToFirst')} style={{ width: 20 }} />
49
+ </button>
50
+ <button
51
+ style={{ paddingLeft: '0.5rem', ...btnStyle }}
52
+ disabled={unitCursor <= 0}
53
+ onClick={() => setUnitCursor(unitCursor - 1)}
54
+ >
55
+ <img src={left} alt={i18n('goToPrevious')} style={{ width: 20 }} />
56
+ </button>
57
+ <strong style={{ color: 'white' }}>
58
+ {unitCursor + 1}/{unitNum}
59
+ </strong>
60
+ </div>
61
+ )}
62
+ {unitCursor + 1 < unitNum && (
63
+ <div style={{ marginLeft: 'auto', marginRight: '1rem', ...buttonWrapStyle }}>
64
+ <strong style={{ color: 'white' }}>
65
+ {unitCursor + 1}/{unitNum}
66
+ </strong>
67
+ <button
68
+ style={{ paddingRight: '0.5rem', ...btnStyle }}
69
+ disabled={unitCursor + 1 >= unitNum}
70
+ onClick={() => setUnitCursor(unitCursor + 1)}
71
+ >
72
+ <img src={right} alt={i18n('goToNext')} style={{ width: 20 }} />
73
+ </button>
74
+ <button
75
+ style={{ paddingRight: '0.5rem', ...btnStyle }}
76
+ disabled={unitCursor + 1 >= unitNum}
77
+ onClick={() => setUnitCursor(unitNum - 1)}
78
+ >
79
+ <img src={doubleRight} alt={i18n('goToFirst')} style={{ width: 20 }} />
80
+ </button>
81
+ </div>
82
+ )}
83
+ </>
84
+ );
85
+ };
86
+
87
+ export default UnitPager;
@@ -0,0 +1,102 @@
1
+ import React, { useCallback, useRef, useState, useEffect } from 'react';
2
+ import { PreviewReactProps, SchemaForUI } from '@pdfme/common';
3
+ import { ZOOM, RULER_HEIGHT } from '../../constants';
4
+ import PagePager from './Pager/Page';
5
+ import UnitPager from './Pager/Unit';
6
+ import Root from '../Root';
7
+ import Error from '../Error';
8
+ import Paper from '../Paper';
9
+ import SchemaUI from '../Schemas/SchemaUI';
10
+ import { useUIPreProcessor, useScrollPageCursor } from '../../hooks';
11
+ import { templateSchemas2SchemasList } from '../../helper';
12
+
13
+ const Preview = ({ template, inputs, size, onChangeInput }: PreviewReactProps) => {
14
+ const { backgrounds, pageSizes, scale, error } = useUIPreProcessor({
15
+ template,
16
+ size,
17
+ offset: RULER_HEIGHT,
18
+ });
19
+
20
+ const rootRef = useRef<HTMLDivElement>(null);
21
+
22
+ const [unitCursor, setUnitCursor] = useState(0);
23
+ const [pageCursor, setPageCursor] = useState(0);
24
+ const [schemasList, setSchemasList] = useState<SchemaForUI[][]>([[]] as SchemaForUI[][]);
25
+
26
+ const init = useCallback(async () => {
27
+ const sl = await templateSchemas2SchemasList(template);
28
+ setSchemasList(sl);
29
+ }, [template]);
30
+
31
+ useEffect(() => {
32
+ init();
33
+ }, [init]);
34
+
35
+ useScrollPageCursor({
36
+ rootRef,
37
+ pageSizes,
38
+ scale,
39
+ pageCursor,
40
+ onChangePageCursor: (p) => setPageCursor(p),
41
+ });
42
+
43
+ const handleChangeInput = ({ key, value }: { key: string; value: string }) =>
44
+ onChangeInput && onChangeInput({ index: unitCursor, key, value });
45
+
46
+ const editable = Boolean(onChangeInput);
47
+ const input = inputs[unitCursor];
48
+
49
+ if (error) {
50
+ return <Error size={size} error={error} />;
51
+ }
52
+
53
+ return (
54
+ <Root ref={rootRef} size={size} scale={scale}>
55
+ <div
56
+ style={{
57
+ height: pageSizes.reduce((acc, cur) => acc + cur.height * ZOOM, 0),
58
+ width: '100%',
59
+ position: 'absolute',
60
+ }}
61
+ >
62
+ <UnitPager unitCursor={unitCursor} unitNum={inputs.length} setUnitCursor={setUnitCursor} />
63
+ <PagePager
64
+ pageCursor={pageCursor}
65
+ pageNum={schemasList.length}
66
+ setPageCursor={(p) => {
67
+ if (!rootRef.current) return;
68
+ rootRef.current.scrollTop = pageSizes
69
+ .slice(0, p)
70
+ .reduce((acc, cur) => acc + (cur.height * ZOOM + RULER_HEIGHT) * scale, 0);
71
+ setPageCursor(p);
72
+ }}
73
+ />
74
+ </div>
75
+ <Paper
76
+ scale={scale}
77
+ size={size}
78
+ schemasList={schemasList}
79
+ pageSizes={pageSizes}
80
+ backgrounds={backgrounds}
81
+ renderSchema={({ schema, index }) => {
82
+ const { key } = schema;
83
+ const data = input[key] ? input[key] : '';
84
+
85
+ return (
86
+ <SchemaUI
87
+ key={key}
88
+ schema={Object.assign(schema, { key, id: key, data })}
89
+ editable={editable}
90
+ placeholder={template.sampledata ? template.sampledata[0][key] : ''}
91
+ tabIndex={index + 100}
92
+ onChange={(value) => handleChangeInput({ key, value })}
93
+ border={editable ? '1px dashed #4af' : 'transparent'}
94
+ />
95
+ );
96
+ }}
97
+ />
98
+ </Root>
99
+ );
100
+ };
101
+
102
+ export default Preview;