@pdfme/ui 2.2.0 → 3.0.0-beta.2

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 (94) hide show
  1. package/README.md +33 -35
  2. package/__mocks__/form-render.js +7 -0
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.LICENSE.txt +42 -4
  5. package/dist/index.js.map +1 -1
  6. package/dist/types/Designer.d.ts +3 -0
  7. package/dist/types/builtInPropPanel.d.ts +3 -0
  8. package/dist/types/builtInRenderer.d.ts +3 -0
  9. package/dist/types/class.d.ts +18 -38
  10. package/dist/types/components/CtlBar/Pager.d.ts +3 -2
  11. package/dist/types/components/CtlBar/Zoom.d.ts +3 -2
  12. package/dist/types/components/CtlBar/index.d.ts +3 -2
  13. package/dist/types/components/Designer/{Main → Canvas}/Guides.d.ts +2 -2
  14. package/dist/types/components/Designer/Canvas/Mask.d.ts +4 -0
  15. package/dist/types/components/Designer/Canvas/Moveable.d.ts +37 -0
  16. package/dist/types/components/Designer/{Main → Canvas}/Selecto.d.ts +2 -1
  17. package/dist/types/components/Designer/{Main → Canvas}/index.d.ts +3 -6
  18. package/dist/types/components/Designer/Sidebar/DetailView/AlignWidget.d.ts +4 -0
  19. package/dist/types/components/Designer/Sidebar/DetailView/WidgetRenderer.d.ts +7 -0
  20. package/dist/types/components/Designer/Sidebar/DetailView/index.d.ts +5 -4
  21. package/dist/types/components/Designer/Sidebar/ListView/SelectableSortableContainer.d.ts +3 -2
  22. package/dist/types/components/Designer/Sidebar/ListView/SelectableSortableItem.d.ts +1 -1
  23. package/dist/types/components/Designer/Sidebar/ListView/index.d.ts +3 -2
  24. package/dist/types/components/Designer/Sidebar/index.d.ts +3 -23
  25. package/dist/types/components/Designer/index.d.ts +6 -107
  26. package/dist/types/components/Divider.d.ts +2 -1
  27. package/dist/types/components/ErrorScreen.d.ts +7 -0
  28. package/dist/types/components/Paper.d.ts +3 -2
  29. package/dist/types/components/Preview.d.ts +10 -2
  30. package/dist/types/components/Renderer.d.ts +10 -0
  31. package/dist/types/components/Root.d.ts +1 -1
  32. package/dist/types/components/Spinner.d.ts +2 -1
  33. package/dist/types/components/UnitPager.d.ts +3 -2
  34. package/dist/types/constants.d.ts +3 -3
  35. package/dist/types/contexts.d.ts +4 -1
  36. package/dist/types/helper.d.ts +4 -46
  37. package/dist/types/hooks.d.ts +2 -2
  38. package/dist/types/i18n.d.ts +4 -2
  39. package/dist/types/index.d.ts +1 -4
  40. package/dist/types/types.d.ts +25 -0
  41. package/package.json +19 -8
  42. package/src/Designer.tsx +69 -21
  43. package/src/Form.tsx +18 -14
  44. package/src/Viewer.tsx +6 -2
  45. package/src/builtInPropPanel.ts +5 -0
  46. package/src/builtInRenderer.ts +5 -0
  47. package/src/class.ts +25 -2
  48. package/src/components/CtlBar/index.tsx +4 -7
  49. package/src/components/Designer/{Main → Canvas}/Guides.tsx +2 -2
  50. package/src/components/Designer/{Main → Canvas}/Moveable.tsx +23 -19
  51. package/src/components/Designer/{Main → Canvas}/index.tsx +77 -30
  52. package/src/components/Designer/Sidebar/DetailView/AlignWidget.tsx +182 -0
  53. package/src/components/Designer/Sidebar/DetailView/WidgetRenderer.tsx +28 -0
  54. package/src/components/Designer/Sidebar/DetailView/index.tsx +153 -22
  55. package/src/components/Designer/Sidebar/ListView/Item.tsx +1 -1
  56. package/src/components/Designer/Sidebar/ListView/SelectableSortableContainer.tsx +6 -6
  57. package/src/components/Designer/Sidebar/ListView/index.tsx +1 -4
  58. package/src/components/Designer/Sidebar/index.tsx +26 -60
  59. package/src/components/Designer/index.tsx +53 -32
  60. package/src/components/{Error.tsx → ErrorScreen.tsx} +2 -2
  61. package/src/components/Paper.tsx +35 -9
  62. package/src/components/Preview.tsx +48 -50
  63. package/src/components/Renderer.tsx +90 -0
  64. package/src/components/Root.tsx +5 -1
  65. package/src/constants.ts +4 -4
  66. package/src/contexts.ts +7 -0
  67. package/src/helper.ts +19 -122
  68. package/src/hooks.ts +6 -5
  69. package/src/i18n.ts +48 -11
  70. package/src/index.ts +1 -76
  71. package/src/types.ts +36 -0
  72. package/tsconfig.json +2 -1
  73. package/webpack.config.js +6 -1
  74. package/dist/types/components/Designer/Main/Mask.d.ts +0 -3
  75. package/dist/types/components/Designer/Main/Moveable.d.ts +0 -31
  76. package/dist/types/components/Designer/Sidebar/DetailView/ExampleInputEditor.d.ts +0 -6
  77. package/dist/types/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.d.ts +0 -6
  78. package/dist/types/components/Designer/Sidebar/DetailView/TextPropEditor.d.ts +0 -6
  79. package/dist/types/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.d.ts +0 -6
  80. package/dist/types/components/Error.d.ts +0 -6
  81. package/dist/types/components/Schemas/BarcodeSchema.d.ts +0 -15
  82. package/dist/types/components/Schemas/ImageSchema.d.ts +0 -15
  83. package/dist/types/components/Schemas/SchemaUI.d.ts +0 -15
  84. package/dist/types/components/Schemas/TextSchema.d.ts +0 -28
  85. package/src/components/Designer/Sidebar/DetailView/ExampleInputEditor.tsx +0 -85
  86. package/src/components/Designer/Sidebar/DetailView/PositionAndSizeEditor.tsx +0 -275
  87. package/src/components/Designer/Sidebar/DetailView/TextPropEditor.tsx +0 -357
  88. package/src/components/Designer/Sidebar/DetailView/TypeAndKeyEditor.tsx +0 -87
  89. package/src/components/Schemas/BarcodeSchema.tsx +0 -124
  90. package/src/components/Schemas/ImageSchema.tsx +0 -87
  91. package/src/components/Schemas/SchemaUI.tsx +0 -62
  92. package/src/components/Schemas/TextSchema.tsx +0 -175
  93. /package/src/components/Designer/{Main → Canvas}/Mask.tsx +0 -0
  94. /package/src/components/Designer/{Main → Canvas}/Selecto.tsx +0 -0
@@ -1,9 +1,9 @@
1
1
  import React, { useContext, useState } from 'react';
2
+ import type { SidebarProps } from '../../../../types';
2
3
  import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../../../constants';
3
4
  import { I18nContext } from '../../../../contexts';
4
5
  import Divider from '../../../Divider';
5
6
  import SelectableSortableContainer from './SelectableSortableContainer';
6
- import { SidebarProps } from '../index';
7
7
 
8
8
  const ListView = (
9
9
  props: Pick<
@@ -38,7 +38,6 @@ const ListView = (
38
38
  </span>
39
39
  </div>
40
40
  <Divider />
41
-
42
41
  {isBulkUpdateFieldNamesMode ? (
43
42
  <div>
44
43
  <textarea
@@ -68,7 +67,6 @@ const ListView = (
68
67
  onEdit={onEdit}
69
68
  />
70
69
  )}
71
-
72
70
  <div
73
71
  style={{
74
72
  display: 'flex',
@@ -112,7 +110,6 @@ const ListView = (
112
110
  </u>
113
111
  )}
114
112
  </div>
115
- <Divider />
116
113
  </div>
117
114
  );
118
115
  };
@@ -1,55 +1,17 @@
1
- import React, { useState, useContext } from 'react';
2
- import { SchemaForUI, Size } from '@pdfme/common';
3
- import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../../constants';
4
- import { I18nContext, FontContext } from '../../../contexts';
1
+ import React, { useContext } from 'react';
2
+ import type { SidebarProps } from '../../../types';
3
+ import { SIDEBAR_WIDTH } from '../../../constants';
4
+ import { I18nContext } from '../../../contexts';
5
5
  import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
6
6
  import ListView from './ListView/index';
7
7
  import DetailView from './DetailView/index';
8
- import { getFallbackFontName } from '@pdfme/common';
9
-
10
- export type SidebarProps = {
11
- height: number;
12
- hoveringSchemaId: string | null;
13
- onChangeHoveringSchemaId: (id: string | null) => void;
14
- size: Size;
15
- pageSize: Size;
16
- activeElements: HTMLElement[];
17
- schemas: SchemaForUI[];
18
- onSortEnd: (sortedSchemas: SchemaForUI[]) => void;
19
- onEdit: (id: string) => void;
20
- onEditEnd: () => void;
21
- changeSchemas: (
22
- objs: {
23
- key: string; value: undefined | string | number | {
24
- min: number;
25
- max: number;
26
- }; schemaId: string
27
- }[]
28
- ) => void;
29
- addSchema: () => void;
30
- };
31
8
 
32
9
  const Sidebar = (props: SidebarProps) => {
33
- const { height, size, activeElements, schemas, addSchema } = props;
10
+ const { sidebarOpen, setSidebarOpen, activeElements, schemas, addSchema } = props;
34
11
 
35
12
  const i18n = useContext(I18nContext);
36
- const fonts = useContext(FontContext);
37
- const fallbackFont = getFallbackFontName(fonts);
38
- const [open, setOpen] = useState(true);
39
-
40
- const getActiveSchemas = () => {
41
- const ids = activeElements.map((ae) => ae.id);
42
- const activeSchema = schemas.find((s) => ids.includes(s.id));
43
-
44
- if (activeSchema?.type === 'text') {
45
- if (!activeSchema.fontName) {
46
- activeSchema.fontName = fallbackFont;
47
- }
48
- }
49
-
50
- return schemas.filter((s) => ids.includes(s.id));
51
- };
52
-
13
+ const getActiveSchemas = () =>
14
+ schemas.filter((s) => activeElements.map((ae) => ae.id).includes(s.id));
53
15
  const getLastActiveSchema = () => {
54
16
  const activeSchemas = getActiveSchemas();
55
17
  return activeSchemas[activeSchemas.length - 1];
@@ -61,16 +23,18 @@ const Sidebar = (props: SidebarProps) => {
61
23
  position: 'absolute',
62
24
  right: 0,
63
25
  zIndex: 1,
64
- height: height ? height : '100%',
65
- width: open ? SIDEBAR_WIDTH : 0,
26
+ height: '100%',
27
+ width: sidebarOpen ? SIDEBAR_WIDTH : 0,
28
+ fontSize: '1rem',
66
29
  }}
67
30
  >
68
- <div style={{ position: 'sticky', top: 0, zIndex: 1, fontSize: '1rem' }}>
31
+ <div>
69
32
  <button
33
+ type="button"
70
34
  style={{
71
35
  position: 'absolute',
72
- top: '1.75rem',
73
- right: '0.5rem',
36
+ top: '1.15rem',
37
+ right: '1rem',
74
38
  zIndex: 100,
75
39
  border: 'none',
76
40
  borderRadius: 2,
@@ -80,9 +44,9 @@ const Sidebar = (props: SidebarProps) => {
80
44
  width: 30,
81
45
  height: 30,
82
46
  }}
83
- onClick={() => setOpen(!open)}
47
+ onClick={() => setSidebarOpen(!sidebarOpen)}
84
48
  >
85
- {open ? (
49
+ {sidebarOpen ? (
86
50
  <ArrowRightIcon width={15} height={15} />
87
51
  ) : (
88
52
  <ArrowLeftIcon width={15} height={15} />
@@ -91,20 +55,20 @@ const Sidebar = (props: SidebarProps) => {
91
55
  <div
92
56
  style={{
93
57
  width: SIDEBAR_WIDTH,
94
- height: size.height - RULER_HEIGHT - RULER_HEIGHT / 2,
95
- display: open ? 'block' : 'none',
96
- top: RULER_HEIGHT / 2,
58
+ height: '100%',
59
+ display: sidebarOpen ? 'block' : 'none',
60
+ top: 0,
97
61
  right: 0,
98
62
  position: 'absolute',
99
- background: '#ffffffed',
63
+ background: '#fffffffa',
100
64
  color: '#333',
101
65
  border: '1px solid #eee',
102
- padding: '0.5rem',
66
+ padding: '0.7rem 1rem',
103
67
  overflowY: 'auto',
104
68
  fontFamily: "'Open Sans', sans-serif",
105
69
  fontWeight: 400,
106
70
  textAlign: 'left',
107
- boxSizing: 'content-box',
71
+ boxSizing: 'border-box',
108
72
  }}
109
73
  >
110
74
  {getActiveSchemas().length === 0 ? (
@@ -114,8 +78,6 @@ const Sidebar = (props: SidebarProps) => {
114
78
  )}
115
79
  <div
116
80
  style={{
117
- display: 'flex',
118
- justifyContent: 'space-around',
119
81
  position: 'absolute',
120
82
  width: '100%',
121
83
  left: 0,
@@ -123,13 +85,17 @@ const Sidebar = (props: SidebarProps) => {
123
85
  paddingTop: '1rem',
124
86
  }}
125
87
  >
88
+ <div style={{ marginBottom: '1rem', borderBottom: '1px solid #e5e5e5' }} />
126
89
  <button
90
+ type="button"
127
91
  style={{
128
92
  padding: '0.5rem',
129
93
  background: '#18a0fb',
130
94
  border: 'none',
131
95
  borderRadius: 2,
132
96
  cursor: 'pointer',
97
+ margin: '0 auto',
98
+ display: 'block',
133
99
  }}
134
100
  onClick={addSchema}
135
101
  >
@@ -1,9 +1,9 @@
1
1
  import React, { useRef, useState, useEffect, useContext, useCallback } from 'react';
2
- import { DesignerReactProps, Template, SchemaForUI, SchemaType } from '@pdfme/common';
2
+ import { ZOOM, Template, SchemaForUI, ChangeSchemas, DesignerProps, Size } from '@pdfme/common';
3
3
  import Sidebar from './Sidebar/index';
4
- import Main from './Main/index';
5
- import { ZOOM, RULER_HEIGHT } from '../../constants';
6
- import { I18nContext } from '../../contexts';
4
+ import Canvas from './Canvas/index';
5
+ import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../constants';
6
+ import { I18nContext, PropPanelRegistry } from '../../contexts';
7
7
  import {
8
8
  uuid,
9
9
  set,
@@ -12,16 +12,13 @@ import {
12
12
  destroyShortCuts,
13
13
  templateSchemas2SchemasList,
14
14
  fmtTemplate,
15
- getInitialSchema,
16
- getSampleByType,
17
- getKeepRatioHeightByWidth,
18
15
  getUniqSchemaKey,
19
16
  moveCommandToChangeSchemasArg,
20
17
  getPagesScrollTopByIndex,
21
18
  } from '../../helper';
22
19
  import { useUIPreProcessor, useScrollPageCursor } from '../../hooks';
23
20
  import Root from '../Root';
24
- import Error from '../Error';
21
+ import ErrorScreen from '../ErrorScreen';
25
22
  import CtlBar from '../CtlBar/index';
26
23
 
27
24
  const TemplateEditor = ({
@@ -29,7 +26,10 @@ const TemplateEditor = ({
29
26
  size,
30
27
  onSaveTemplate,
31
28
  onChangeTemplate,
32
- }: DesignerReactProps & { onChangeTemplate: (t: Template) => void }) => {
29
+ }: Omit<DesignerProps, 'domContainer'> & {
30
+ onSaveTemplate: (t: Template) => void;
31
+ size: Size;
32
+ } & { onChangeTemplate: (t: Template) => void }) => {
33
33
  const copiedSchemas = useRef<SchemaForUI[] | null>(null);
34
34
  const past = useRef<SchemaForUI[][]>([]);
35
35
  const future = useRef<SchemaForUI[][]>([]);
@@ -37,12 +37,14 @@ const TemplateEditor = ({
37
37
  const paperRefs = useRef<HTMLDivElement[]>([]);
38
38
 
39
39
  const i18n = useContext(I18nContext);
40
+ const propPanelRegistry = useContext(PropPanelRegistry);
40
41
 
41
42
  const [hoveringSchemaId, setHoveringSchemaId] = useState<string | null>(null);
42
43
  const [activeElements, setActiveElements] = useState<HTMLElement[]>([]);
43
44
  const [schemasList, setSchemasList] = useState<SchemaForUI[][]>([[]] as SchemaForUI[][]);
44
45
  const [pageCursor, setPageCursor] = useState(0);
45
46
  const [zoomLevel, setZoomLevel] = useState(1);
47
+ const [sidebarOpen, setSidebarOpen] = useState(true);
46
48
 
47
49
  const { backgrounds, pageSizes, scale, error } = useUIPreProcessor({ template, size, zoomLevel });
48
50
 
@@ -89,20 +91,23 @@ const TemplateEditor = ({
89
91
  [schemasList, pageCursor, commitSchemas]
90
92
  );
91
93
 
92
- const changeSchemas = useCallback(
93
- (objs: { key: string; value: undefined | string | number | { min: number, max: number }; schemaId: string }[]) => {
94
+ const changeSchemas: ChangeSchemas = useCallback(
95
+ (objs) => {
94
96
  const newSchemas = objs.reduce((acc, { key, value, schemaId }) => {
95
- const tgt = acc.find((s) => s.id === schemaId)!;
97
+ const tgt = acc.find((s) => s.id === schemaId)! as SchemaForUI;
96
98
  // Assign to reference
97
99
  set(tgt, key, value);
100
+
98
101
  if (key === 'type') {
99
- const type = String(value) as SchemaType;
100
- // set default value, text or barcode
101
- set(tgt, 'data', getSampleByType(type));
102
- // For barcodes, adjust the height to get the correct ratio.
103
- if (type !== 'text' && type !== 'image') {
104
- set(tgt, 'height', getKeepRatioHeightByWidth(type, tgt.width));
105
- }
102
+ const keysToKeep = ['id', 'key', 'type', 'position'];
103
+ Object.keys(tgt).forEach((key) => {
104
+ if (!keysToKeep.includes(key)) {
105
+ delete tgt[key as keyof typeof tgt];
106
+ }
107
+ });
108
+ const propPanel = propPanelRegistry[value as string];
109
+ set(tgt, 'data', propPanel?.defaultValue || '');
110
+ Object.assign(tgt, propPanel?.defaultSchema || {});
106
111
  }
107
112
 
108
113
  return acc;
@@ -204,12 +209,24 @@ const TemplateEditor = ({
204
209
  }, [initEvents, destroyEvents]);
205
210
 
206
211
  const addSchema = () => {
207
- const s = getInitialSchema();
212
+ const propPanel = Object.values(propPanelRegistry)[0];
213
+
214
+ if (!propPanel) {
215
+ throw new Error(`[@pdfme/ui] addSchema failed: propPanel is empty.
216
+ Check this document: https://pdfme.com/docs/custom-schemas`);
217
+ }
218
+
219
+ const s = {
220
+ id: uuid(),
221
+ key: `${i18n('field')}${schemasList[pageCursor].length + 1}`,
222
+ data: propPanel.defaultValue || '',
223
+ ...propPanel.defaultSchema,
224
+ } as SchemaForUI;
225
+
208
226
  const paper = paperRefs.current[pageCursor];
209
227
  const rectTop = paper ? paper.getBoundingClientRect().top : 0;
210
228
  s.position.y = rectTop > 0 ? 0 : pageSizes[pageCursor].height / 2;
211
- s.data = 'text';
212
- s.key = `${i18n('field')}${schemasList[pageCursor].length + 1}`;
229
+
213
230
  commitSchemas(schemasList[pageCursor].concat(s));
214
231
  setTimeout(() => onEdit([document.getElementById(s.id)!]));
215
232
  };
@@ -222,14 +239,19 @@ const TemplateEditor = ({
222
239
  setHoveringSchemaId(id);
223
240
  };
224
241
 
242
+ const sizeExcSidebar = {
243
+ width: sidebarOpen ? size.width - SIDEBAR_WIDTH : size.width,
244
+ height: size.height,
245
+ };
246
+
225
247
  if (error) {
226
- return <Error size={size} error={error} />;
248
+ return <ErrorScreen size={size} error={error} />;
227
249
  }
228
250
 
229
251
  return (
230
252
  <Root size={size} scale={scale}>
231
253
  <CtlBar
232
- size={size}
254
+ size={sizeExcSidebar}
233
255
  pageCursor={pageCursor}
234
256
  pageNum={schemasList.length}
235
257
  setPageCursor={(p) => {
@@ -239,12 +261,7 @@ const TemplateEditor = ({
239
261
  onEditEnd();
240
262
  }}
241
263
  zoomLevel={zoomLevel}
242
- setZoomLevel={(zoom) => {
243
- if (mainRef.current) {
244
- mainRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, pageCursor, scale);
245
- }
246
- setZoomLevel(zoom);
247
- }}
264
+ setZoomLevel={setZoomLevel}
248
265
  />
249
266
  <Sidebar
250
267
  hoveringSchemaId={hoveringSchemaId}
@@ -262,8 +279,11 @@ const TemplateEditor = ({
262
279
  }}
263
280
  onEditEnd={onEditEnd}
264
281
  addSchema={addSchema}
282
+ deselectSchema={onEditEnd}
283
+ sidebarOpen={sidebarOpen}
284
+ setSidebarOpen={setSidebarOpen}
265
285
  />
266
- <Main
286
+ <Canvas
267
287
  ref={mainRef}
268
288
  paperRefs={paperRefs}
269
289
  hoveringSchemaId={hoveringSchemaId}
@@ -271,13 +291,14 @@ const TemplateEditor = ({
271
291
  height={size.height - RULER_HEIGHT * ZOOM}
272
292
  pageCursor={pageCursor}
273
293
  scale={scale}
274
- size={size}
294
+ size={sizeExcSidebar}
275
295
  pageSizes={pageSizes}
276
296
  backgrounds={backgrounds}
277
297
  activeElements={activeElements}
278
298
  schemasList={schemasList}
279
299
  changeSchemas={changeSchemas}
280
300
  removeSchemas={removeSchemas}
301
+ sidebarOpen={sidebarOpen}
281
302
  onEdit={onEdit}
282
303
  />
283
304
  </Root>
@@ -2,7 +2,7 @@ import React, { useContext } from 'react';
2
2
  import { Size } from '@pdfme/common';
3
3
  import { I18nContext } from '../contexts';
4
4
 
5
- const Error = ({ size, error }: { size: Size; error: Error }) => {
5
+ const ErrorScreen = ({ size, error }: { size: Size; error: Error }) => {
6
6
  const i18n = useContext(I18nContext);
7
7
 
8
8
  return (
@@ -28,4 +28,4 @@ const Error = ({ size, error }: { size: Size; error: Error }) => {
28
28
  );
29
29
  };
30
30
 
31
- export default Error;
31
+ export default ErrorScreen;
@@ -1,7 +1,7 @@
1
1
  import React, { MutableRefObject, ReactNode, useContext } from 'react';
2
- import { SchemaForUI, Size, getFallbackFontName } from '@pdfme/common';
2
+ import { ZOOM, SchemaForUI, Size, getFallbackFontName } from '@pdfme/common';
3
3
  import { FontContext } from '../contexts';
4
- import { ZOOM, RULER_HEIGHT } from '../constants';
4
+ import { RULER_HEIGHT, PAGE_GAP } from '../constants';
5
5
 
6
6
  const Paper = (props: {
7
7
  paperRefs: MutableRefObject<HTMLDivElement[]>;
@@ -12,10 +12,21 @@ const Paper = (props: {
12
12
  backgrounds: string[];
13
13
  renderPaper?: (arg: { index: number; paperSize: Size }) => ReactNode;
14
14
  renderSchema: (arg: { index: number; schema: SchemaForUI }) => ReactNode;
15
+ hasRulers?: boolean;
15
16
  }) => {
16
- const { paperRefs, scale, size, schemasList, pageSizes, backgrounds, renderPaper, renderSchema } =
17
- props;
17
+ const {
18
+ paperRefs,
19
+ scale,
20
+ size,
21
+ schemasList,
22
+ pageSizes,
23
+ backgrounds,
24
+ renderPaper,
25
+ renderSchema,
26
+ hasRulers,
27
+ } = props;
18
28
  const font = useContext(FontContext);
29
+ const rulerHeight = hasRulers ? RULER_HEIGHT : 0;
19
30
 
20
31
  if (pageSizes.length !== backgrounds.length || pageSizes.length !== schemasList.length) {
21
32
  return null;
@@ -25,14 +36,30 @@ const Paper = (props: {
25
36
  <div
26
37
  style={{
27
38
  transform: `scale(${scale})`,
28
- transformOrigin: 'center top',
29
- ...size,
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,
30
44
  }}
31
45
  >
32
46
  {backgrounds.map((background, paperIndex) => {
33
47
  const pageSize = pageSizes[paperIndex];
34
48
  const paperSize = { width: pageSize.width * ZOOM, height: pageSize.height * ZOOM };
35
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 we place the start of the page below them
60
+ const pageTop =
61
+ paperIndex > 0 ? `${(rulerHeight + PAGE_GAP) * (paperIndex + 1)}px` : `${rulerHeight}px`;
62
+
36
63
  return (
37
64
  <div
38
65
  id={`@pdfme/ui-paper${paperIndex}`}
@@ -54,9 +81,8 @@ const Paper = (props: {
54
81
  }}
55
82
  style={{
56
83
  fontFamily: `'${getFallbackFontName(font)}'`,
57
- top: `${RULER_HEIGHT}px`,
58
- left: paperSize.width > size.width ? `${(size.width - paperSize.width) / 2}px` : 0,
59
- margin: '0 auto',
84
+ top: pageTop,
85
+ left: leftCenteringIndent,
60
86
  position: 'relative',
61
87
  backgroundImage: `url(${background})`,
62
88
  backgroundSize: `${paperSize.width}px ${paperSize.height}px`,
@@ -1,17 +1,24 @@
1
1
  import React, { useCallback, useRef, useState, useEffect } from 'react';
2
- import { PreviewReactProps, SchemaForUI } from '@pdfme/common';
3
- import { ZOOM, RULER_HEIGHT } from '../constants';
2
+ import type { SchemaForUI, PreviewProps, Size } from '@pdfme/common';
4
3
  import UnitPager from './UnitPager';
5
4
  import Root from './Root';
6
- import Error from './Error';
5
+ import ErrorScreen from './ErrorScreen';
7
6
  import CtlBar from './CtlBar/index';
8
7
  import Paper from './Paper';
9
- import SchemaUI from './Schemas/SchemaUI';
8
+ import Renderer from './Renderer';
10
9
  import { useUIPreProcessor, useScrollPageCursor } from '../hooks';
11
10
  import { templateSchemas2SchemasList, getPagesScrollTopByIndex } from '../helper';
12
11
 
13
- const Preview = ({ template, inputs, size, onChangeInput }: PreviewReactProps) => {
14
- const rootRef = useRef<HTMLDivElement>(null);
12
+ const Preview = ({
13
+ template,
14
+ inputs,
15
+ size,
16
+ onChangeInput,
17
+ }: Omit<PreviewProps, 'domContainer'> & {
18
+ onChangeInput?: (args: { index: number; value: string; key: string }) => void;
19
+ size: Size;
20
+ }) => {
21
+ const containerRef = useRef<HTMLDivElement>(null);
15
22
  const paperRefs = useRef<HTMLDivElement[]>([]);
16
23
 
17
24
  const [unitCursor, setUnitCursor] = useState(0);
@@ -37,7 +44,7 @@ const Preview = ({ template, inputs, size, onChangeInput }: PreviewReactProps) =
37
44
  }, [init]);
38
45
 
39
46
  useScrollPageCursor({
40
- ref: rootRef,
47
+ ref: containerRef,
41
48
  pageSizes,
42
49
  scale,
43
50
  pageCursor,
@@ -47,68 +54,59 @@ const Preview = ({ template, inputs, size, onChangeInput }: PreviewReactProps) =
47
54
  const handleChangeInput = ({ key, value }: { key: string; value: string }) =>
48
55
  onChangeInput && onChangeInput({ index: unitCursor, key, value });
49
56
 
50
- const editable = Boolean(onChangeInput);
57
+ const isForm = Boolean(onChangeInput);
58
+
51
59
  const input = inputs[unitCursor];
52
60
 
53
61
  if (error) {
54
- return <Error size={size} error={error} />;
62
+ return <ErrorScreen size={size} error={error} />;
55
63
  }
56
64
 
57
- const pageSizesHeightSum = pageSizes.reduce(
58
- (acc, cur) => acc + (cur.height * ZOOM + RULER_HEIGHT * scale) * scale,
59
- 0
60
- );
61
-
62
65
  return (
63
- <Root ref={rootRef} size={size} scale={scale}>
66
+ <Root size={size} scale={scale}>
64
67
  <CtlBar
65
- size={{ height: Math.max(size.height, pageSizesHeightSum), width: size.width }}
68
+ size={size}
66
69
  pageCursor={pageCursor}
67
70
  pageNum={schemasList.length}
68
71
  setPageCursor={(p) => {
69
- if (!rootRef.current) return;
70
- rootRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, p, scale);
72
+ if (!containerRef.current) return;
73
+ containerRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, p, scale);
71
74
  setPageCursor(p);
72
75
  }}
73
76
  zoomLevel={zoomLevel}
74
- setZoomLevel={(zoom) => {
75
- if (rootRef.current) {
76
- rootRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, pageCursor, scale);
77
- }
78
- setZoomLevel(zoom);
79
- }}
77
+ setZoomLevel={setZoomLevel}
80
78
  />
81
79
  <UnitPager
82
- size={{ height: Math.max(size.height, pageSizesHeightSum), width: size.width }}
80
+ size={size}
83
81
  unitCursor={unitCursor}
84
82
  unitNum={inputs.length}
85
83
  setUnitCursor={setUnitCursor}
86
84
  />
87
-
88
- <Paper
89
- paperRefs={paperRefs}
90
- scale={scale}
91
- size={size}
92
- schemasList={schemasList}
93
- pageSizes={pageSizes}
94
- backgrounds={backgrounds}
95
- renderSchema={({ schema, index }) => {
96
- const { key } = schema;
97
- const data = (input && input[key]) || '';
98
- return (
99
- <SchemaUI
100
- key={schema.id}
101
- schema={Object.assign(schema, { data })}
102
- editable={editable}
103
- placeholder={template.sampledata?.[0]?.[key] ?? ''}
104
- tabIndex={index + 100}
105
- onStopEditing={() => { }}
106
- onChange={(value) => handleChangeInput({ key, value })}
107
- outline={editable ? '1px dashed #4af' : 'transparent'}
108
- />
109
- );
110
- }}
111
- />
85
+ <div ref={containerRef} style={{ ...size, position: 'relative', overflow: 'auto' }}>
86
+ <Paper
87
+ paperRefs={paperRefs}
88
+ scale={scale}
89
+ size={size}
90
+ schemasList={schemasList}
91
+ pageSizes={pageSizes}
92
+ backgrounds={backgrounds}
93
+ renderSchema={({ schema, index }) => {
94
+ const { key } = schema;
95
+ const data = (input && input[key]) || '';
96
+ return (
97
+ <Renderer
98
+ key={schema.id}
99
+ schema={Object.assign(schema, { data })}
100
+ mode={isForm ? 'form' : 'viewer'}
101
+ placeholder={template.sampledata?.[0]?.[key] ?? ''}
102
+ tabIndex={index + 100}
103
+ onChange={(value) => handleChangeInput({ key, value })}
104
+ outline={isForm ? '1px dashed #4af' : 'transparent'}
105
+ />
106
+ );
107
+ }}
108
+ />
109
+ </div>
112
110
  </Root>
113
111
  );
114
112
  };