@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.
Files changed (97) hide show
  1. package/README.md +9 -0
  2. package/__mocks__/assetsTransformer.js +7 -0
  3. package/__mocks__/form-render.js +7 -0
  4. package/__mocks__/lucide-react.js +19 -0
  5. package/dist/index.es.js +159393 -0
  6. package/dist/index.umd.js +1041 -0
  7. package/dist/types/__tests__/assets/helper.d.ts +3 -0
  8. package/dist/types/__tests__/components/Designer.test.d.ts +1 -0
  9. package/dist/types/__tests__/components/PluginIcon.test.d.ts +1 -0
  10. package/dist/types/__tests__/components/Preview.test.d.ts +1 -0
  11. package/dist/types/__tests__/helper.test.d.ts +1 -0
  12. package/dist/types/src/Designer.d.ts +21 -0
  13. package/dist/types/src/Form.d.ts +24 -0
  14. package/dist/types/src/Viewer.d.ts +15 -0
  15. package/dist/types/src/class.d.ts +89 -0
  16. package/dist/types/src/components/AppContextProvider.d.ts +11 -0
  17. package/dist/types/src/components/CtlBar.d.ts +14 -0
  18. package/dist/types/src/components/Designer/Canvas/Guides.d.ts +9 -0
  19. package/dist/types/src/components/Designer/Canvas/Mask.d.ts +4 -0
  20. package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +37 -0
  21. package/dist/types/src/components/Designer/Canvas/Padding.d.ts +6 -0
  22. package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +10 -0
  23. package/dist/types/src/components/Designer/Canvas/index.d.ts +22 -0
  24. package/dist/types/src/components/Designer/LeftSidebar.d.ts +8 -0
  25. package/dist/types/src/components/Designer/PluginIcon.d.ts +10 -0
  26. package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +4 -0
  27. package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +4 -0
  28. package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +7 -0
  29. package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +8 -0
  30. package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +45 -0
  31. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +4 -0
  32. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +14 -0
  33. package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +4 -0
  34. package/dist/types/src/components/Designer/RightSidebar/index.d.ts +4 -0
  35. package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +15 -0
  36. package/dist/types/src/components/Designer/index.d.ts +11 -0
  37. package/dist/types/src/components/ErrorScreen.d.ts +7 -0
  38. package/dist/types/src/components/Paper.d.ts +20 -0
  39. package/dist/types/src/components/Preview.d.ts +15 -0
  40. package/dist/types/src/components/Renderer.d.ts +13 -0
  41. package/dist/types/src/components/Root.d.ts +9 -0
  42. package/dist/types/src/components/Spinner.d.ts +3 -0
  43. package/dist/types/src/components/StaticSchema.d.ts +10 -0
  44. package/dist/types/src/components/UnitPager.d.ts +10 -0
  45. package/dist/types/src/constants.d.ts +11 -0
  46. package/dist/types/src/contexts.d.ts +10 -0
  47. package/dist/types/src/helper.d.ts +73 -0
  48. package/dist/types/src/hooks.d.ts +46 -0
  49. package/dist/types/src/i18n.d.ts +3 -0
  50. package/dist/types/src/index.d.ts +4 -0
  51. package/dist/types/src/theme.d.ts +2 -0
  52. package/dist/types/src/types.d.ts +19 -0
  53. package/eslint.config.mjs +41 -0
  54. package/package.json +127 -0
  55. package/src/Designer.tsx +107 -0
  56. package/src/Form.tsx +102 -0
  57. package/src/Viewer.tsx +59 -0
  58. package/src/class.ts +188 -0
  59. package/src/components/AppContextProvider.tsx +78 -0
  60. package/src/components/CtlBar.tsx +183 -0
  61. package/src/components/Designer/Canvas/Guides.tsx +49 -0
  62. package/src/components/Designer/Canvas/Mask.tsx +20 -0
  63. package/src/components/Designer/Canvas/Moveable.tsx +91 -0
  64. package/src/components/Designer/Canvas/Padding.tsx +56 -0
  65. package/src/components/Designer/Canvas/Selecto.tsx +45 -0
  66. package/src/components/Designer/Canvas/index.tsx +536 -0
  67. package/src/components/Designer/LeftSidebar.tsx +120 -0
  68. package/src/components/Designer/PluginIcon.tsx +87 -0
  69. package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +229 -0
  70. package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +78 -0
  71. package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +28 -0
  72. package/src/components/Designer/RightSidebar/DetailView/index.tsx +469 -0
  73. package/src/components/Designer/RightSidebar/ListView/Item.tsx +158 -0
  74. package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +204 -0
  75. package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +88 -0
  76. package/src/components/Designer/RightSidebar/ListView/index.tsx +116 -0
  77. package/src/components/Designer/RightSidebar/index.tsx +72 -0
  78. package/src/components/Designer/RightSidebar/layout.tsx +75 -0
  79. package/src/components/Designer/index.tsx +389 -0
  80. package/src/components/ErrorScreen.tsx +33 -0
  81. package/src/components/Paper.tsx +117 -0
  82. package/src/components/Preview.tsx +220 -0
  83. package/src/components/Renderer.tsx +165 -0
  84. package/src/components/Root.tsx +38 -0
  85. package/src/components/Spinner.tsx +45 -0
  86. package/src/components/StaticSchema.tsx +50 -0
  87. package/src/components/UnitPager.tsx +119 -0
  88. package/src/constants.ts +21 -0
  89. package/src/contexts.ts +14 -0
  90. package/src/helper.ts +534 -0
  91. package/src/hooks.ts +308 -0
  92. package/src/i18n.ts +903 -0
  93. package/src/index.ts +5 -0
  94. package/src/theme.ts +20 -0
  95. package/src/types.ts +20 -0
  96. package/tsconfig.json +48 -0
  97. 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;