@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.
- package/.eslintrc.js +45 -0
- package/README.md +280 -0
- package/declaration.d.ts +8 -0
- package/dist/@pdfme/ui.js +3 -0
- package/dist/@pdfme/ui.js.LICENSE.txt +95 -0
- package/dist/@pdfme/ui.js.map +1 -0
- package/dist/types/common/src/barcode.d.ts +2 -0
- package/dist/types/common/src/constants.d.ts +6 -0
- package/dist/types/common/src/helper.d.ts +15 -0
- package/dist/types/common/src/index.d.ts +4 -0
- package/dist/types/common/src/schema.d.ts +3613 -0
- package/dist/types/common/src/type.d.ts +64 -0
- package/dist/types/common/src/utils.d.ts +12 -0
- package/dist/types/ui/src/Designer.d.ts +13 -0
- package/dist/types/ui/src/Form.d.ts +13 -0
- package/dist/types/ui/src/Viewer.d.ts +7 -0
- package/dist/types/ui/src/class.d.ts +72 -0
- package/dist/types/ui/src/components/Designer/Main/Guides.d.ts +9 -0
- package/dist/types/ui/src/components/Designer/Main/Mask.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Main/Moveable.d.ts +31 -0
- package/dist/types/ui/src/components/Designer/Main/Selecto.d.ts +8 -0
- package/dist/types/ui/src/components/Designer/Main/index.d.ts +24 -0
- package/dist/types/ui/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/DetailView/TextPropEditor.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/DetailView/index.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/ListView.d.ts +3 -0
- package/dist/types/ui/src/components/Designer/Sidebar/index.d.ts +26 -0
- package/dist/types/ui/src/components/Designer/index.d.ts +99 -0
- package/dist/types/ui/src/components/Divider.d.ts +2 -0
- package/dist/types/ui/src/components/Error.d.ts +7 -0
- package/dist/types/ui/src/components/Paper.d.ts +19 -0
- package/dist/types/ui/src/components/Preview/Pager/Page.d.ts +8 -0
- package/dist/types/ui/src/components/Preview/Pager/Unit.d.ts +8 -0
- package/dist/types/ui/src/components/Preview/index.d.ts +4 -0
- package/dist/types/ui/src/components/Root.d.ts +9 -0
- package/dist/types/ui/src/components/Schemas/BarcodeSchema.d.ts +15 -0
- package/dist/types/ui/src/components/Schemas/ImageSchema.d.ts +15 -0
- package/dist/types/ui/src/components/Schemas/SchemaUI.d.ts +14 -0
- package/dist/types/ui/src/components/Schemas/TextSchema.d.ts +22 -0
- package/dist/types/ui/src/components/Spinner.d.ts +2 -0
- package/dist/types/ui/src/constants.d.ts +5 -0
- package/dist/types/ui/src/contexts.d.ts +7 -0
- package/dist/types/ui/src/helper.d.ts +91 -0
- package/dist/types/ui/src/hooks.d.ts +26 -0
- package/dist/types/ui/src/i18n.d.ts +30 -0
- package/dist/types/ui/src/index.d.ts +5 -0
- package/dist/types/ui/src/libs/class.d.ts +84 -0
- package/dist/types/ui/src/libs/contexts.d.ts +7 -0
- package/dist/types/ui/src/libs/helper.d.ts +64 -0
- package/dist/types/ui/src/libs/hooks.d.ts +26 -0
- package/dist/types/ui/src/libs/i18n.d.ts +30 -0
- package/dist/types/ui/src/libs/ui.d.ts +64 -0
- package/package.json +80 -0
- package/public/Designer.html +90 -0
- package/public/Form.html +74 -0
- package/public/SauceHanSansJP.ttf +0 -0
- package/public/SauceHanSerifJP.ttf +0 -0
- package/public/Viewer.html +73 -0
- package/public/helper.js +51 -0
- package/public/index.html +54 -0
- package/src/Designer.tsx +72 -0
- package/src/Form.tsx +45 -0
- package/src/Viewer.tsx +27 -0
- package/src/assets/barcodeExamples/code128.png +0 -0
- package/src/assets/barcodeExamples/code39.png +0 -0
- package/src/assets/barcodeExamples/ean13.png +0 -0
- package/src/assets/barcodeExamples/ean8.png +0 -0
- package/src/assets/barcodeExamples/itf14.png +0 -0
- package/src/assets/barcodeExamples/japanpost.png +0 -0
- package/src/assets/barcodeExamples/nw7.png +0 -0
- package/src/assets/barcodeExamples/qrcode.png +0 -0
- package/src/assets/barcodeExamples/upca.png +0 -0
- package/src/assets/barcodeExamples/upce.png +0 -0
- package/src/assets/icons/back.svg +4 -0
- package/src/assets/icons/double-left.svg +11 -0
- package/src/assets/icons/double-right.svg +11 -0
- package/src/assets/icons/drag.svg +3 -0
- package/src/assets/icons/forward.svg +4 -0
- package/src/assets/icons/left.svg +4 -0
- package/src/assets/icons/right.svg +4 -0
- package/src/assets/icons/warning.svg +4 -0
- package/src/assets/imageExample.png +0 -0
- package/src/class.ts +147 -0
- package/src/components/Designer/Main/Guides.tsx +53 -0
- package/src/components/Designer/Main/Mask.tsx +19 -0
- package/src/components/Designer/Main/Moveable.tsx +79 -0
- package/src/components/Designer/Main/Selecto.tsx +29 -0
- package/src/components/Designer/Main/index.tsx +314 -0
- package/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.tsx +62 -0
- package/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.tsx +98 -0
- package/src/components/Designer/Sidebar/DetailView/TextPropEditor.tsx +178 -0
- package/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.tsx +79 -0
- package/src/components/Designer/Sidebar/DetailView/index.tsx +39 -0
- package/src/components/Designer/Sidebar/ListView.tsx +180 -0
- package/src/components/Designer/Sidebar/index.tsx +102 -0
- package/src/components/Designer/index.tsx +283 -0
- package/src/components/Divider.tsx +7 -0
- package/src/components/Error.tsx +31 -0
- package/src/components/Paper.tsx +77 -0
- package/src/components/Preview/Pager/Page.tsx +85 -0
- package/src/components/Preview/Pager/Unit.tsx +87 -0
- package/src/components/Preview/index.tsx +102 -0
- package/src/components/Root.tsx +52 -0
- package/src/components/Schemas/BarcodeSchema.tsx +111 -0
- package/src/components/Schemas/ImageSchema.tsx +81 -0
- package/src/components/Schemas/SchemaUI.tsx +64 -0
- package/src/components/Schemas/TextSchema.tsx +62 -0
- package/src/components/Spinner.tsx +37 -0
- package/src/constants.ts +9 -0
- package/src/contexts.ts +8 -0
- package/src/helper.ts +516 -0
- package/src/hooks.ts +107 -0
- package/src/i18n.ts +64 -0
- package/src/index.ts +77 -0
- package/tsconfig.json +21 -0
- 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,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;
|