@pdfme/ui 0.0.0
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/README.md +9 -0
- package/__mocks__/assetsTransformer.js +7 -0
- package/__mocks__/form-render.js +7 -0
- package/__mocks__/lucide-react.js +19 -0
- package/dist/index.es.js +159393 -0
- package/dist/index.umd.js +1041 -0
- package/dist/types/__tests__/assets/helper.d.ts +3 -0
- package/dist/types/__tests__/components/Designer.test.d.ts +1 -0
- package/dist/types/__tests__/components/PluginIcon.test.d.ts +1 -0
- package/dist/types/__tests__/components/Preview.test.d.ts +1 -0
- package/dist/types/__tests__/helper.test.d.ts +1 -0
- package/dist/types/src/Designer.d.ts +21 -0
- package/dist/types/src/Form.d.ts +24 -0
- package/dist/types/src/Viewer.d.ts +15 -0
- package/dist/types/src/class.d.ts +89 -0
- package/dist/types/src/components/AppContextProvider.d.ts +11 -0
- package/dist/types/src/components/CtlBar.d.ts +14 -0
- package/dist/types/src/components/Designer/Canvas/Guides.d.ts +9 -0
- package/dist/types/src/components/Designer/Canvas/Mask.d.ts +4 -0
- package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +37 -0
- package/dist/types/src/components/Designer/Canvas/Padding.d.ts +6 -0
- package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +10 -0
- package/dist/types/src/components/Designer/Canvas/index.d.ts +22 -0
- package/dist/types/src/components/Designer/LeftSidebar.d.ts +8 -0
- package/dist/types/src/components/Designer/PluginIcon.d.ts +10 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +7 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +8 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +45 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +14 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +15 -0
- package/dist/types/src/components/Designer/index.d.ts +11 -0
- package/dist/types/src/components/ErrorScreen.d.ts +7 -0
- package/dist/types/src/components/Paper.d.ts +20 -0
- package/dist/types/src/components/Preview.d.ts +15 -0
- package/dist/types/src/components/Renderer.d.ts +13 -0
- package/dist/types/src/components/Root.d.ts +9 -0
- package/dist/types/src/components/Spinner.d.ts +3 -0
- package/dist/types/src/components/StaticSchema.d.ts +10 -0
- package/dist/types/src/components/UnitPager.d.ts +10 -0
- package/dist/types/src/constants.d.ts +11 -0
- package/dist/types/src/contexts.d.ts +10 -0
- package/dist/types/src/helper.d.ts +73 -0
- package/dist/types/src/hooks.d.ts +46 -0
- package/dist/types/src/i18n.d.ts +3 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/theme.d.ts +2 -0
- package/dist/types/src/types.d.ts +19 -0
- package/eslint.config.mjs +41 -0
- package/package.json +127 -0
- package/src/Designer.tsx +107 -0
- package/src/Form.tsx +102 -0
- package/src/Viewer.tsx +59 -0
- package/src/class.ts +188 -0
- package/src/components/AppContextProvider.tsx +78 -0
- package/src/components/CtlBar.tsx +183 -0
- package/src/components/Designer/Canvas/Guides.tsx +49 -0
- package/src/components/Designer/Canvas/Mask.tsx +20 -0
- package/src/components/Designer/Canvas/Moveable.tsx +91 -0
- package/src/components/Designer/Canvas/Padding.tsx +56 -0
- package/src/components/Designer/Canvas/Selecto.tsx +45 -0
- package/src/components/Designer/Canvas/index.tsx +536 -0
- package/src/components/Designer/LeftSidebar.tsx +120 -0
- package/src/components/Designer/PluginIcon.tsx +87 -0
- package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +229 -0
- package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +78 -0
- package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +28 -0
- package/src/components/Designer/RightSidebar/DetailView/index.tsx +469 -0
- package/src/components/Designer/RightSidebar/ListView/Item.tsx +158 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +204 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +88 -0
- package/src/components/Designer/RightSidebar/ListView/index.tsx +116 -0
- package/src/components/Designer/RightSidebar/index.tsx +72 -0
- package/src/components/Designer/RightSidebar/layout.tsx +75 -0
- package/src/components/Designer/index.tsx +389 -0
- package/src/components/ErrorScreen.tsx +33 -0
- package/src/components/Paper.tsx +117 -0
- package/src/components/Preview.tsx +220 -0
- package/src/components/Renderer.tsx +165 -0
- package/src/components/Root.tsx +38 -0
- package/src/components/Spinner.tsx +45 -0
- package/src/components/StaticSchema.tsx +50 -0
- package/src/components/UnitPager.tsx +119 -0
- package/src/constants.ts +21 -0
- package/src/contexts.ts +14 -0
- package/src/helper.ts +534 -0
- package/src/hooks.ts +308 -0
- package/src/i18n.ts +903 -0
- package/src/index.ts +5 -0
- package/src/theme.ts +20 -0
- package/src/types.ts +20 -0
- package/tsconfig.json +48 -0
- package/vite.config.mts +22 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
import React, { useRef, useState, useContext, useCallback, useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
cloneDeep,
|
|
4
|
+
ZOOM,
|
|
5
|
+
Template,
|
|
6
|
+
Schema,
|
|
7
|
+
SchemaForUI,
|
|
8
|
+
ChangeSchemas,
|
|
9
|
+
DesignerProps,
|
|
10
|
+
Size,
|
|
11
|
+
isBlankPdf,
|
|
12
|
+
px2mm,
|
|
13
|
+
} from '@pdfme/common';
|
|
14
|
+
import { DndContext } from '@dnd-kit/core';
|
|
15
|
+
import RightSidebar from './RightSidebar/index.js';
|
|
16
|
+
import LeftSidebar from './LeftSidebar.js';
|
|
17
|
+
import Canvas from './Canvas/index.js';
|
|
18
|
+
import { RULER_HEIGHT, RIGHT_SIDEBAR_WIDTH, LEFT_SIDEBAR_WIDTH } from '../../constants.js';
|
|
19
|
+
import { I18nContext, OptionsContext, PluginsRegistry } from '../../contexts.js';
|
|
20
|
+
import {
|
|
21
|
+
schemasList2template,
|
|
22
|
+
uuid,
|
|
23
|
+
round,
|
|
24
|
+
template2SchemasList,
|
|
25
|
+
getPagesScrollTopByIndex,
|
|
26
|
+
changeSchemas as _changeSchemas,
|
|
27
|
+
useMaxZoom,
|
|
28
|
+
} from '../../helper.js';
|
|
29
|
+
import { useUIPreProcessor, useScrollPageCursor, useInitEvents } from '../../hooks.js';
|
|
30
|
+
import Root from '../Root.js';
|
|
31
|
+
import ErrorScreen from '../ErrorScreen.js';
|
|
32
|
+
import CtlBar from '../CtlBar.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* When the canvas scales there is a displacement of the starting position of the dragged schema.
|
|
36
|
+
* It moves left or right from the top-left corner of the drag icon depending on the scale.
|
|
37
|
+
* This function calculates the adjustment needed to compensate for this displacement.
|
|
38
|
+
*/
|
|
39
|
+
const scaleDragPosAdjustment = (adjustment: number, scale: number): number => {
|
|
40
|
+
if (scale > 1) return adjustment * (scale - 1);
|
|
41
|
+
if (scale < 1) return adjustment * -(1 - scale);
|
|
42
|
+
return 0;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const TemplateEditor = ({
|
|
46
|
+
template,
|
|
47
|
+
size,
|
|
48
|
+
onSaveTemplate,
|
|
49
|
+
onChangeTemplate,
|
|
50
|
+
onPageCursorChange,
|
|
51
|
+
}: Omit<DesignerProps, 'domContainer'> & {
|
|
52
|
+
size: Size;
|
|
53
|
+
onSaveTemplate: (t: Template) => void;
|
|
54
|
+
onChangeTemplate: (t: Template) => void;
|
|
55
|
+
} & {
|
|
56
|
+
onChangeTemplate: (t: Template) => void;
|
|
57
|
+
onPageCursorChange: (newPageCursor: number, totalPages: number) => void;
|
|
58
|
+
}) => {
|
|
59
|
+
const past = useRef<SchemaForUI[][]>([]);
|
|
60
|
+
const future = useRef<SchemaForUI[][]>([]);
|
|
61
|
+
const canvasRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
const paperRefs = useRef<HTMLDivElement[]>([]);
|
|
63
|
+
|
|
64
|
+
const i18n = useContext(I18nContext);
|
|
65
|
+
const pluginsRegistry = useContext(PluginsRegistry);
|
|
66
|
+
const options = useContext(OptionsContext);
|
|
67
|
+
const maxZoom = useMaxZoom();
|
|
68
|
+
|
|
69
|
+
const [hoveringSchemaId, setHoveringSchemaId] = useState<string | null>(null);
|
|
70
|
+
const [activeElements, setActiveElements] = useState<HTMLElement[]>([]);
|
|
71
|
+
const [schemasList, setSchemasList] = useState<SchemaForUI[][]>([[]] as SchemaForUI[][]);
|
|
72
|
+
const [pageCursor, setPageCursor] = useState(0);
|
|
73
|
+
const [zoomLevel, setZoomLevel] = useState(options.zoomLevel ?? 1);
|
|
74
|
+
const [sidebarOpen, setSidebarOpen] = useState(options.sidebarOpen ?? true);
|
|
75
|
+
const [prevTemplate, setPrevTemplate] = useState<Template | null>(null);
|
|
76
|
+
|
|
77
|
+
const { backgrounds, pageSizes, scale, error, refresh } = useUIPreProcessor({
|
|
78
|
+
template,
|
|
79
|
+
size,
|
|
80
|
+
zoomLevel,
|
|
81
|
+
maxZoom,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const onEdit = (targets: HTMLElement[]) => {
|
|
85
|
+
setActiveElements(targets);
|
|
86
|
+
setHoveringSchemaId(null);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const onEditEnd = () => {
|
|
90
|
+
setActiveElements([]);
|
|
91
|
+
setHoveringSchemaId(null);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Update component state only when _options_ changes
|
|
95
|
+
// Ignore exhaustive useEffect dependency warnings here
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (typeof options.zoomLevel === 'number' && options.zoomLevel !== zoomLevel) {
|
|
98
|
+
setZoomLevel(options.zoomLevel);
|
|
99
|
+
}
|
|
100
|
+
if (typeof options.sidebarOpen === 'boolean' && options.sidebarOpen !== sidebarOpen) {
|
|
101
|
+
setSidebarOpen(options.sidebarOpen);
|
|
102
|
+
}
|
|
103
|
+
// eslint-disable-next-line
|
|
104
|
+
}, [options]);
|
|
105
|
+
|
|
106
|
+
useScrollPageCursor({
|
|
107
|
+
ref: canvasRef,
|
|
108
|
+
pageSizes,
|
|
109
|
+
scale,
|
|
110
|
+
pageCursor,
|
|
111
|
+
onChangePageCursor: (p) => {
|
|
112
|
+
setPageCursor(p);
|
|
113
|
+
onPageCursorChange(p, schemasList.length);
|
|
114
|
+
onEditEnd();
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const commitSchemas = useCallback(
|
|
119
|
+
(newSchemas: SchemaForUI[]) => {
|
|
120
|
+
future.current = [];
|
|
121
|
+
past.current.push(cloneDeep(schemasList[pageCursor]));
|
|
122
|
+
const _schemasList = cloneDeep(schemasList);
|
|
123
|
+
_schemasList[pageCursor] = newSchemas;
|
|
124
|
+
setSchemasList(_schemasList);
|
|
125
|
+
onChangeTemplate(schemasList2template(_schemasList, template.basePdf));
|
|
126
|
+
},
|
|
127
|
+
[template, schemasList, pageCursor, onChangeTemplate],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const removeSchemas = useCallback(
|
|
131
|
+
(ids: string[]) => {
|
|
132
|
+
commitSchemas(schemasList[pageCursor].filter((schema) => !ids.includes(schema.id)));
|
|
133
|
+
onEditEnd();
|
|
134
|
+
},
|
|
135
|
+
[schemasList, pageCursor, commitSchemas],
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const changeSchemas: ChangeSchemas = useCallback(
|
|
139
|
+
(objs) => {
|
|
140
|
+
_changeSchemas({
|
|
141
|
+
objs,
|
|
142
|
+
schemas: schemasList[pageCursor],
|
|
143
|
+
basePdf: template.basePdf,
|
|
144
|
+
pluginsRegistry,
|
|
145
|
+
pageSize: pageSizes[pageCursor],
|
|
146
|
+
commitSchemas,
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
[commitSchemas, pageCursor, schemasList, pluginsRegistry, pageSizes, template.basePdf],
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
useInitEvents({
|
|
153
|
+
pageCursor,
|
|
154
|
+
pageSizes,
|
|
155
|
+
activeElements,
|
|
156
|
+
template,
|
|
157
|
+
schemasList,
|
|
158
|
+
changeSchemas,
|
|
159
|
+
commitSchemas,
|
|
160
|
+
removeSchemas,
|
|
161
|
+
onSaveTemplate,
|
|
162
|
+
past,
|
|
163
|
+
future,
|
|
164
|
+
setSchemasList,
|
|
165
|
+
onEdit,
|
|
166
|
+
onEditEnd,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const updateTemplate = useCallback(async (newTemplate: Template) => {
|
|
170
|
+
const sl = await template2SchemasList(newTemplate);
|
|
171
|
+
setSchemasList(sl);
|
|
172
|
+
onEditEnd();
|
|
173
|
+
setPageCursor(0);
|
|
174
|
+
if (canvasRef.current?.scroll) {
|
|
175
|
+
canvasRef.current.scroll({ top: 0, behavior: 'smooth' });
|
|
176
|
+
}
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
const addSchema = (defaultSchema: Schema) => {
|
|
180
|
+
const [paddingTop, paddingRight, paddingBottom, paddingLeft] = isBlankPdf(template.basePdf)
|
|
181
|
+
? template.basePdf.padding
|
|
182
|
+
: [0, 0, 0, 0];
|
|
183
|
+
const pageSize = pageSizes[pageCursor];
|
|
184
|
+
|
|
185
|
+
const newSchemaName = (prefix: string) => {
|
|
186
|
+
let index = schemasList.reduce((acc, page) => acc + page.length, 1);
|
|
187
|
+
let newName = prefix + index;
|
|
188
|
+
while (schemasList.some((page) => page.find((s) => s.name === newName))) {
|
|
189
|
+
index++;
|
|
190
|
+
newName = prefix + index;
|
|
191
|
+
}
|
|
192
|
+
return newName;
|
|
193
|
+
};
|
|
194
|
+
const ensureMiddleValue = (min: number, value: number, max: number) =>
|
|
195
|
+
Math.min(Math.max(min, value), max);
|
|
196
|
+
|
|
197
|
+
const s = {
|
|
198
|
+
id: uuid(),
|
|
199
|
+
...defaultSchema,
|
|
200
|
+
name: newSchemaName(i18n('field')),
|
|
201
|
+
position: {
|
|
202
|
+
x: ensureMiddleValue(
|
|
203
|
+
paddingLeft,
|
|
204
|
+
defaultSchema.position.x,
|
|
205
|
+
pageSize.width - paddingRight - defaultSchema.width,
|
|
206
|
+
),
|
|
207
|
+
y: ensureMiddleValue(
|
|
208
|
+
paddingTop,
|
|
209
|
+
defaultSchema.position.y,
|
|
210
|
+
pageSize.height - paddingBottom - defaultSchema.height,
|
|
211
|
+
),
|
|
212
|
+
},
|
|
213
|
+
required: defaultSchema.readOnly
|
|
214
|
+
? false
|
|
215
|
+
: options.requiredByDefault || defaultSchema.required || false,
|
|
216
|
+
} as SchemaForUI;
|
|
217
|
+
|
|
218
|
+
if (defaultSchema.position.y === 0) {
|
|
219
|
+
const paper = paperRefs.current[pageCursor];
|
|
220
|
+
const rectTop = paper ? paper.getBoundingClientRect().top : 0;
|
|
221
|
+
s.position.y = rectTop > 0 ? paddingTop : pageSizes[pageCursor].height / 2;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
commitSchemas(schemasList[pageCursor].concat(s));
|
|
225
|
+
setTimeout(() => onEdit([document.getElementById(s.id)!]));
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const onSortEnd = (sortedSchemas: SchemaForUI[]) => {
|
|
229
|
+
commitSchemas(sortedSchemas);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const onChangeHoveringSchemaId = (id: string | null) => {
|
|
233
|
+
setHoveringSchemaId(id);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const updatePage = async (sl: SchemaForUI[][], newPageCursor: number) => {
|
|
237
|
+
setPageCursor(newPageCursor);
|
|
238
|
+
const newTemplate = schemasList2template(sl, template.basePdf);
|
|
239
|
+
onChangeTemplate(newTemplate);
|
|
240
|
+
await updateTemplate(newTemplate);
|
|
241
|
+
void refresh(newTemplate);
|
|
242
|
+
|
|
243
|
+
// Notify page change with updated total pages
|
|
244
|
+
onPageCursorChange(newPageCursor, sl.length);
|
|
245
|
+
|
|
246
|
+
// Use setTimeout to update scroll position after render
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
if (canvasRef.current) {
|
|
249
|
+
canvasRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, newPageCursor, scale);
|
|
250
|
+
}
|
|
251
|
+
}, 0);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const handleRemovePage = () => {
|
|
255
|
+
if (pageCursor === 0) return;
|
|
256
|
+
if (!window.confirm(i18n('removePageConfirm'))) return;
|
|
257
|
+
|
|
258
|
+
const _schemasList = cloneDeep(schemasList);
|
|
259
|
+
_schemasList.splice(pageCursor, 1);
|
|
260
|
+
void updatePage(_schemasList, pageCursor - 1);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const handleAddPageAfter = () => {
|
|
264
|
+
const _schemasList = cloneDeep(schemasList);
|
|
265
|
+
_schemasList.splice(pageCursor + 1, 0, []);
|
|
266
|
+
void updatePage(_schemasList, pageCursor + 1);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
if (prevTemplate !== template) {
|
|
270
|
+
setPrevTemplate(template);
|
|
271
|
+
void updateTemplate(template);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const canvasWidth = size.width - LEFT_SIDEBAR_WIDTH;
|
|
275
|
+
const sizeExcSidebars = {
|
|
276
|
+
width: sidebarOpen ? canvasWidth - RIGHT_SIDEBAR_WIDTH : canvasWidth,
|
|
277
|
+
height: size.height,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
if (error) {
|
|
281
|
+
// Pass the error directly to ErrorScreen
|
|
282
|
+
return <ErrorScreen size={size} error={error} />;
|
|
283
|
+
}
|
|
284
|
+
const pageManipulation = isBlankPdf(template.basePdf)
|
|
285
|
+
? { addPageAfter: handleAddPageAfter, removePage: handleRemovePage }
|
|
286
|
+
: {};
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<Root size={size} scale={scale}>
|
|
290
|
+
<DndContext
|
|
291
|
+
onDragEnd={(event) => {
|
|
292
|
+
// Triggered after a schema is dragged & dropped from the left sidebar.
|
|
293
|
+
if (!event.active) return;
|
|
294
|
+
const active = event.active;
|
|
295
|
+
const pageRect = paperRefs.current[pageCursor].getBoundingClientRect();
|
|
296
|
+
|
|
297
|
+
const dragStartLeft = active.rect.current.initial?.left || 0;
|
|
298
|
+
const dragStartTop = active.rect.current.initial?.top || 0;
|
|
299
|
+
|
|
300
|
+
const canvasLeftOffsetFromPageCorner =
|
|
301
|
+
pageRect.left - dragStartLeft + scaleDragPosAdjustment(20, scale);
|
|
302
|
+
const canvasTopOffsetFromPageCorner = pageRect.top - dragStartTop;
|
|
303
|
+
|
|
304
|
+
const moveY = (event.delta.y - canvasTopOffsetFromPageCorner) / scale;
|
|
305
|
+
const moveX = (event.delta.x - canvasLeftOffsetFromPageCorner) / scale;
|
|
306
|
+
|
|
307
|
+
const position = {
|
|
308
|
+
x: round(px2mm(Math.max(0, moveX)), 2),
|
|
309
|
+
y: round(px2mm(Math.max(0, moveY)), 2),
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
addSchema({ ...(active.data.current as Schema), position });
|
|
313
|
+
}}
|
|
314
|
+
onDragStart={onEditEnd}
|
|
315
|
+
>
|
|
316
|
+
<LeftSidebar
|
|
317
|
+
height={canvasRef.current ? canvasRef.current.clientHeight : 0}
|
|
318
|
+
scale={scale}
|
|
319
|
+
basePdf={template.basePdf}
|
|
320
|
+
/>
|
|
321
|
+
|
|
322
|
+
<div style={{ position: 'absolute', width: canvasWidth, marginLeft: LEFT_SIDEBAR_WIDTH }}>
|
|
323
|
+
<CtlBar
|
|
324
|
+
size={sizeExcSidebars}
|
|
325
|
+
pageCursor={pageCursor}
|
|
326
|
+
pageNum={schemasList.length}
|
|
327
|
+
setPageCursor={(p) => {
|
|
328
|
+
if (!canvasRef.current) return;
|
|
329
|
+
// Update scroll position and state
|
|
330
|
+
canvasRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, p, scale);
|
|
331
|
+
setPageCursor(p);
|
|
332
|
+
onPageCursorChange(p, schemasList.length);
|
|
333
|
+
onEditEnd();
|
|
334
|
+
}}
|
|
335
|
+
zoomLevel={zoomLevel}
|
|
336
|
+
setZoomLevel={setZoomLevel}
|
|
337
|
+
{...pageManipulation}
|
|
338
|
+
/>
|
|
339
|
+
|
|
340
|
+
<RightSidebar
|
|
341
|
+
hoveringSchemaId={hoveringSchemaId}
|
|
342
|
+
onChangeHoveringSchemaId={onChangeHoveringSchemaId}
|
|
343
|
+
height={canvasRef.current ? canvasRef.current.clientHeight : 0}
|
|
344
|
+
size={size}
|
|
345
|
+
pageSize={pageSizes[pageCursor] ?? []}
|
|
346
|
+
basePdf={template.basePdf}
|
|
347
|
+
activeElements={activeElements}
|
|
348
|
+
schemasList={schemasList}
|
|
349
|
+
schemas={schemasList[pageCursor] ?? []}
|
|
350
|
+
changeSchemas={changeSchemas}
|
|
351
|
+
onSortEnd={onSortEnd}
|
|
352
|
+
onEdit={(id) => {
|
|
353
|
+
const editingElem = document.getElementById(id);
|
|
354
|
+
if (editingElem) {
|
|
355
|
+
onEdit([editingElem]);
|
|
356
|
+
}
|
|
357
|
+
}}
|
|
358
|
+
onEditEnd={onEditEnd}
|
|
359
|
+
deselectSchema={onEditEnd}
|
|
360
|
+
sidebarOpen={sidebarOpen}
|
|
361
|
+
setSidebarOpen={setSidebarOpen}
|
|
362
|
+
/>
|
|
363
|
+
|
|
364
|
+
<Canvas
|
|
365
|
+
ref={canvasRef}
|
|
366
|
+
paperRefs={paperRefs}
|
|
367
|
+
basePdf={template.basePdf}
|
|
368
|
+
hoveringSchemaId={hoveringSchemaId}
|
|
369
|
+
onChangeHoveringSchemaId={onChangeHoveringSchemaId}
|
|
370
|
+
height={size.height - RULER_HEIGHT * ZOOM}
|
|
371
|
+
pageCursor={pageCursor}
|
|
372
|
+
scale={scale}
|
|
373
|
+
size={sizeExcSidebars}
|
|
374
|
+
pageSizes={pageSizes}
|
|
375
|
+
backgrounds={backgrounds}
|
|
376
|
+
activeElements={activeElements}
|
|
377
|
+
schemasList={schemasList}
|
|
378
|
+
changeSchemas={changeSchemas}
|
|
379
|
+
removeSchemas={removeSchemas}
|
|
380
|
+
sidebarOpen={sidebarOpen}
|
|
381
|
+
onEdit={onEdit}
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
</DndContext>
|
|
385
|
+
</Root>
|
|
386
|
+
);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
export default TemplateEditor;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { Size } from '@pdfme/common';
|
|
3
|
+
import { I18nContext } from '../contexts.js';
|
|
4
|
+
import { BACKGROUND_COLOR } from '../constants.js';
|
|
5
|
+
import { theme, Result } from 'antd';
|
|
6
|
+
|
|
7
|
+
const ErrorScreen = ({ size, error }: { size: Size; error: Error }) => {
|
|
8
|
+
const i18n = useContext(I18nContext);
|
|
9
|
+
const { token } = theme.useToken();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
style={{
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
justifyContent: 'center',
|
|
17
|
+
background: BACKGROUND_COLOR,
|
|
18
|
+
...size,
|
|
19
|
+
}}
|
|
20
|
+
>
|
|
21
|
+
<div style={{ width: 300, margin: '0 auto', background: token.colorBgLayout }}>
|
|
22
|
+
<Result
|
|
23
|
+
icon={null}
|
|
24
|
+
title="ERROR"
|
|
25
|
+
subTitle={i18n('errorOccurred')}
|
|
26
|
+
extra={<span>{error.message}</span>}
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default ErrorScreen;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React, { MutableRefObject, ReactNode, useContext } from 'react';
|
|
2
|
+
import { ZOOM, SchemaForUI, Size, getFallbackFontName } from '@pdfme/common';
|
|
3
|
+
import { FontContext } from '../contexts.js';
|
|
4
|
+
import { RULER_HEIGHT, PAGE_GAP } from '../constants.js';
|
|
5
|
+
|
|
6
|
+
const Paper = (props: {
|
|
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
|
+
hasRulers?: boolean;
|
|
16
|
+
}) => {
|
|
17
|
+
const {
|
|
18
|
+
paperRefs,
|
|
19
|
+
scale,
|
|
20
|
+
size,
|
|
21
|
+
schemasList,
|
|
22
|
+
pageSizes,
|
|
23
|
+
backgrounds,
|
|
24
|
+
renderPaper,
|
|
25
|
+
renderSchema,
|
|
26
|
+
hasRulers,
|
|
27
|
+
} = props;
|
|
28
|
+
const font = useContext(FontContext);
|
|
29
|
+
const rulerHeight = hasRulers ? RULER_HEIGHT : 0;
|
|
30
|
+
|
|
31
|
+
if (pageSizes.length !== backgrounds.length || pageSizes.length !== schemasList.length) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div
|
|
37
|
+
style={{
|
|
38
|
+
transform: `scale(${scale})`,
|
|
39
|
+
transformOrigin: 'top left',
|
|
40
|
+
// NOTE: These values do not impact the UI unless they exceed the Paper sizes.
|
|
41
|
+
// We set them to the scale value to ensure the container is redrawn when you zoom in/out.
|
|
42
|
+
height: scale,
|
|
43
|
+
width: scale,
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{backgrounds.map((background, paperIndex) => {
|
|
47
|
+
const pageSize = pageSizes[paperIndex];
|
|
48
|
+
const paperSize = { width: pageSize.width * ZOOM, height: pageSize.height * ZOOM };
|
|
49
|
+
|
|
50
|
+
// We want to center the content within the available viewport, but transform: scale()
|
|
51
|
+
// must be done from the top-left or CSS crops off left-hand content as you zoom in.
|
|
52
|
+
// However, we want to display the content centrally, so we apply a left indent for
|
|
53
|
+
// when the content does not exceed its container
|
|
54
|
+
const leftCenteringIndent =
|
|
55
|
+
paperSize.width * scale + rulerHeight < size.width
|
|
56
|
+
? `${(size.width / scale - paperSize.width) / 2}px`
|
|
57
|
+
: `${rulerHeight}px`;
|
|
58
|
+
|
|
59
|
+
// Rulers are drawn above/before the top of each page, so each Paper div must have
|
|
60
|
+
// a top offset considering them.
|
|
61
|
+
let pageTop = paperIndex > 0 ? (rulerHeight + PAGE_GAP) * (paperIndex + 1) : rulerHeight;
|
|
62
|
+
|
|
63
|
+
if (!hasRulers) {
|
|
64
|
+
// If no rulers (i.e. Preview/Form) then we'll add an initial gap at the top of the first page
|
|
65
|
+
pageTop += PAGE_GAP * 2;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
key={String(paperIndex) + JSON.stringify(paperSize)}
|
|
71
|
+
ref={(e) => {
|
|
72
|
+
if (e) {
|
|
73
|
+
paperRefs.current[paperIndex] = e;
|
|
74
|
+
}
|
|
75
|
+
}}
|
|
76
|
+
onMouseDown={(e) => {
|
|
77
|
+
if (
|
|
78
|
+
e.currentTarget === e.target &&
|
|
79
|
+
document &&
|
|
80
|
+
document.hasFocus() &&
|
|
81
|
+
document.activeElement instanceof HTMLElement
|
|
82
|
+
) {
|
|
83
|
+
document.activeElement.blur();
|
|
84
|
+
}
|
|
85
|
+
}}
|
|
86
|
+
style={{
|
|
87
|
+
fontFamily: `'${getFallbackFontName(font)}'`,
|
|
88
|
+
top: `${pageTop}px`,
|
|
89
|
+
left: leftCenteringIndent,
|
|
90
|
+
position: 'relative',
|
|
91
|
+
backgroundImage: `url(${background})`,
|
|
92
|
+
backgroundSize: `${paperSize.width}px ${paperSize.height}px`,
|
|
93
|
+
...paperSize,
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
{renderPaper({ paperSize, index: paperIndex })}
|
|
97
|
+
{schemasList[paperIndex].map((schema, schemaIndex) => {
|
|
98
|
+
return (
|
|
99
|
+
<div key={schema.id}>
|
|
100
|
+
{renderSchema({
|
|
101
|
+
schema,
|
|
102
|
+
index:
|
|
103
|
+
paperIndex === 0
|
|
104
|
+
? schemaIndex
|
|
105
|
+
: schemaIndex + schemasList[paperIndex - 1].length,
|
|
106
|
+
})}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
})}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default Paper;
|