@pdfme/ui 3.2.3 → 4.0.0-alpha.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.
@@ -11,7 +11,7 @@ import React, {
11
11
  } from 'react';
12
12
  import { theme, Button } from 'antd';
13
13
  import { OnDrag, OnResize, OnClick, OnRotate } from 'react-moveable';
14
- import { ZOOM, SchemaForUI, Size, ChangeSchemas } from '@pdfme/common';
14
+ import { ZOOM, SchemaForUI, Size, ChangeSchemas, BasePdf, isBlankPdf } from '@pdfme/common';
15
15
  import { PluginsRegistry } from '../../../contexts';
16
16
  import { CloseOutlined } from '@ant-design/icons';
17
17
  import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../../constants';
@@ -23,6 +23,9 @@ import Selecto from './Selecto';
23
23
  import Moveable from './Moveable';
24
24
  import Guides from './Guides';
25
25
  import Mask from './Mask';
26
+ import Padding from './Padding';
27
+
28
+ const mm2px = (mm: number) => mm * 3.7795275591;
26
29
 
27
30
  const DELETE_BTN_ID = uuid();
28
31
  const fmt4Num = (prop: string) => Number(prop.replace('px', ''));
@@ -70,6 +73,7 @@ interface GuidesInterface {
70
73
  }
71
74
 
72
75
  interface Props {
76
+ basePdf: BasePdf;
73
77
  height: number;
74
78
  hoveringSchemaId: string | null;
75
79
  onChangeHoveringSchemaId: (id: string | null) => void;
@@ -89,6 +93,7 @@ interface Props {
89
93
 
90
94
  const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
91
95
  const {
96
+ basePdf,
92
97
  pageCursor,
93
98
  scale,
94
99
  backgrounds,
@@ -142,7 +147,7 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
142
147
 
143
148
  useEffect(() => {
144
149
  moveable.current?.updateRect();
145
- if (prevSchemas === null) {
150
+ if (!prevSchemas) {
146
151
  return;
147
152
  }
148
153
 
@@ -154,9 +159,37 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
154
159
  }
155
160
  }, [pageCursor, schemasList, prevSchemas]);
156
161
 
157
- const onDrag = ({ target, left, top }: OnDrag) => {
158
- target.style.left = `${left < 0 ? 0 : left}px`;
159
- target.style.top = `${top < 0 ? 0 : top}px`;
162
+ const onDrag = ({ target, top, left }: OnDrag) => {
163
+ const { width: _width, height: _height } = target.style;
164
+ const targetWidth = fmt(_width);
165
+ const targetHeight = fmt(_height);
166
+ const actualTop = top / ZOOM;
167
+ const actualLeft = left / ZOOM;
168
+ const { width: pageWidth, height: pageHeight } = pageSizes[pageCursor];
169
+ let topPadding = 0;
170
+ let rightPadding = 0;
171
+ let bottomPadding = 0;
172
+ let leftPadding = 0;
173
+
174
+ if (isBlankPdf(basePdf)) {
175
+ const [t, r, b, l] = basePdf.padding;
176
+ topPadding = t * ZOOM;
177
+ rightPadding = r;
178
+ bottomPadding = b;
179
+ leftPadding = l * ZOOM;
180
+ }
181
+
182
+ if (actualTop + targetHeight > pageHeight - bottomPadding) {
183
+ target.style.top = `${(pageHeight - targetHeight - bottomPadding) * ZOOM}px`;
184
+ } else {
185
+ target.style.top = `${top < topPadding ? topPadding : top}px`;
186
+ }
187
+
188
+ if (actualLeft + targetWidth > pageWidth - rightPadding) {
189
+ target.style.left = `${(pageWidth - targetWidth - rightPadding) * ZOOM}px`;
190
+ } else {
191
+ target.style.left = `${left < leftPadding ? leftPadding : left}px`;
192
+ }
160
193
  };
161
194
 
162
195
  const onDragEnd = ({ target }: { target: HTMLElement | SVGElement }) => {
@@ -195,24 +228,24 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
195
228
  changeSchemas(flatten(arg));
196
229
  };
197
230
 
198
- const onResizeEnd = async ({ target }: { target: HTMLElement | SVGElement }) => {
231
+ const onResizeEnd = ({ target }: { target: HTMLElement | SVGElement }) => {
199
232
  const { id, style } = target;
200
233
  const { width, height, top, left } = style;
201
234
  changeSchemas([
235
+ { key: 'position.x', value: fmt(left), schemaId: id },
236
+ { key: 'position.y', value: fmt(top), schemaId: id },
202
237
  { key: 'width', value: fmt(width), schemaId: id },
203
238
  { key: 'height', value: fmt(height), schemaId: id },
204
- { key: 'position.y', value: fmt(top), schemaId: id },
205
- { key: 'position.x', value: fmt(left), schemaId: id },
206
239
  ]);
207
240
 
208
241
  const targetSchema = schemasList[pageCursor].find((schema) => schema.id === id);
209
242
 
210
243
  if (!targetSchema) return;
211
244
 
245
+ targetSchema.position.x = fmt(left);
246
+ targetSchema.position.y = fmt(top);
212
247
  targetSchema.width = fmt(width);
213
248
  targetSchema.height = fmt(height);
214
- targetSchema.position.y = fmt(top);
215
- targetSchema.position.x = fmt(left);
216
249
  };
217
250
 
218
251
  const onResizeEnds = ({ targets }: { targets: (HTMLElement | SVGElement)[] }) => {
@@ -227,13 +260,43 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
227
260
 
228
261
  const onResize = ({ target, width, height, direction }: OnResize) => {
229
262
  if (!target) return;
230
- const s = target.style;
231
- const newLeft = fmt4Num(s.left) + (fmt4Num(s.width) - width);
232
- const newTop = fmt4Num(s.top) + (fmt4Num(s.height) - height);
263
+ let topPadding = 0;
264
+ let rightPadding = 0;
265
+ let bottomPadding = 0;
266
+ let leftPadding = 0;
267
+
268
+ if (isBlankPdf(basePdf)) {
269
+ const [t, r, b, l] = basePdf.padding;
270
+ topPadding = t * ZOOM;
271
+ rightPadding = mm2px(r);
272
+ bottomPadding = mm2px(b);
273
+ leftPadding = l * ZOOM;
274
+ }
275
+
276
+ const pageWidth = mm2px(pageSizes[pageCursor].width);
277
+ const pageHeight = mm2px(pageSizes[pageCursor].height);
278
+
233
279
  const obj: { top?: string; left?: string; width: string; height: string } = {
234
280
  width: `${width}px`,
235
281
  height: `${height}px`,
236
282
  };
283
+
284
+ const s = target.style;
285
+ let newLeft = fmt4Num(s.left) + (fmt4Num(s.width) - width);
286
+ let newTop = fmt4Num(s.top) + (fmt4Num(s.height) - height);
287
+ if (newLeft < leftPadding) {
288
+ newLeft = leftPadding;
289
+ }
290
+ if (newTop < topPadding) {
291
+ newTop = topPadding;
292
+ }
293
+ if (newLeft + width > pageWidth - rightPadding) {
294
+ obj.width = `${pageWidth - rightPadding - newLeft}px`;
295
+ }
296
+ if (newTop + height > pageHeight - bottomPadding) {
297
+ obj.height = `${pageHeight - bottomPadding - newTop}px`;
298
+ }
299
+
237
300
  const d = direction.toString();
238
301
  if (isTopLeftResize(d)) {
239
302
  obj.top = `${newTop}px`;
@@ -255,7 +318,7 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
255
318
  };
256
319
 
257
320
  const rotatable = useMemo(() => {
258
- const selectedSchemas = schemasList[pageCursor].filter((s) =>
321
+ const selectedSchemas = (schemasList[pageCursor] || []).filter((s) =>
259
322
  activeElements.map((ae) => ae.id).includes(s.id)
260
323
  );
261
324
  const schemaTypes = selectedSchemas.map((s) => s.type);
@@ -329,6 +392,7 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
329
392
  {!editing && activeElements.length > 0 && pageCursor === index && (
330
393
  <DeleteButton activeElements={activeElements} />
331
394
  )}
395
+ <Padding basePdf={basePdf} />
332
396
  <Guides
333
397
  paperSize={paperSize}
334
398
  horizontalRef={(e) => {
@@ -372,19 +436,21 @@ const Canvas = (props: Props, ref: Ref<HTMLDivElement>) => {
372
436
  <Renderer
373
437
  key={schema.id}
374
438
  schema={schema}
439
+ basePdf={basePdf}
440
+ value={schema.content || ''}
375
441
  onChangeHoveringSchemaId={onChangeHoveringSchemaId}
376
442
  mode={
377
443
  editing && activeElements.map((ae) => ae.id).includes(schema.id)
378
444
  ? 'designer'
379
445
  : 'viewer'
380
446
  }
381
- onChange={(value) => {
382
- changeSchemas([{ key: 'data', value, schemaId: schema.id }]);
447
+ onChange={(arg) => {
448
+ const args = Array.isArray(arg) ? arg : [arg];
449
+ changeSchemas(args.map(({ key, value }) => ({ key, value, schemaId: schema.id })));
383
450
  }}
384
451
  stopEditing={() => setEditing(false)}
385
- outline={`1px ${hoveringSchemaId === schema.id ? 'solid' : 'dashed'} ${
386
- schema.readOnly ? 'transparent' : token.colorPrimary
387
- }`}
452
+ outline={`1px ${hoveringSchemaId === schema.id ? 'solid' : 'dashed'} ${schema.readOnly && hoveringSchemaId !== schema.id ? 'transparent' : token.colorPrimary
453
+ }`}
388
454
  scale={scale}
389
455
  />
390
456
  )}
@@ -75,7 +75,7 @@ const DetailView = (
75
75
  const handleWatch = (newSchema: any) => {
76
76
  const changes = [];
77
77
  for (let key in newSchema) {
78
- if (['id', 'data'].includes(key)) continue;
78
+ if (['id', 'content'].includes(key)) continue;
79
79
  if (newSchema[key] !== (activeSchema as any)[key]) {
80
80
  let value = newSchema[key];
81
81
  // FIXME memo: https://github.com/pdfme/pdfme/pull/367#issuecomment-1857468274
@@ -123,15 +123,15 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
123
123
  key: { title: i18n('fieldName'), type: 'string', required: true, span: 14 },
124
124
  '-': { type: 'void', widget: 'Divider' },
125
125
  align: { title: i18n('align'), type: 'void', widget: 'AlignWidget' },
126
- x: { title: 'X', type: 'number', widget: 'inputNumber', required: true, span: 8 },
127
- y: { title: 'Y', type: 'number', widget: 'inputNumber', required: true, span: 8 },
126
+ x: { title: 'X', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 },
127
+ y: { title: 'Y', type: 'number', widget: 'inputNumber', required: true, span: 8, min: 0 },
128
128
  rotate: {
129
129
  title: i18n('rotate'),
130
130
  type: 'number',
131
131
  widget: 'inputNumber',
132
132
  disabled: defaultSchema?.rotate === undefined,
133
133
  max: 360,
134
- min: 0,
134
+ props: { min: 0 },
135
135
  span: 8,
136
136
  },
137
137
  width: {
@@ -140,7 +140,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
140
140
  widget: 'inputNumber',
141
141
  required: true,
142
142
  span: 8,
143
- min: 0,
143
+ props: { min: 0 },
144
144
  },
145
145
  height: {
146
146
  title: i18n('height'),
@@ -148,18 +148,14 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
148
148
  widget: 'inputNumber',
149
149
  required: true,
150
150
  span: 8,
151
- min: 0,
151
+ props: { min: 0 },
152
152
  },
153
153
  opacity: {
154
154
  title: i18n('opacity'),
155
155
  type: 'number',
156
156
  widget: 'inputNumber',
157
157
  disabled: defaultSchema?.opacity === undefined,
158
- props: {
159
- step: 0.1,
160
- },
161
- max: 1,
162
- min: 0,
158
+ props: { step: 0.1, min: 0, max: 1 },
163
159
  span: 8,
164
160
  },
165
161
  },
@@ -8,18 +8,19 @@ import {
8
8
  DesignerProps,
9
9
  Size,
10
10
  Plugin,
11
+ isBlankPdf,
11
12
  } from '@pdfme/common';
12
13
  import Sidebar from './Sidebar/index';
13
14
  import Canvas from './Canvas/index';
14
15
  import { RULER_HEIGHT, SIDEBAR_WIDTH } from '../../constants';
15
16
  import { I18nContext, PluginsRegistry } from '../../contexts';
16
17
  import {
17
- fmtTemplate,
18
+ schemasList2template,
18
19
  uuid,
19
- set,
20
20
  cloneDeep,
21
- templateSchemas2SchemasList,
21
+ template2SchemasList,
22
22
  getPagesScrollTopByIndex,
23
+ changeSchemas as _changeSchemas,
23
24
  } from '../../helper';
24
25
  import { useUIPreProcessor, useScrollPageCursor, useInitEvents } from '../../hooks';
25
26
  import Root from '../Root';
@@ -32,9 +33,10 @@ const TemplateEditor = ({
32
33
  onSaveTemplate,
33
34
  onChangeTemplate,
34
35
  }: Omit<DesignerProps, 'domContainer'> & {
35
- onSaveTemplate: (t: Template) => void;
36
36
  size: Size;
37
- } & { onChangeTemplate: (t: Template) => void }) => {
37
+ onSaveTemplate: (t: Template) => void;
38
+ onChangeTemplate: (t: Template) => void;
39
+ }) => {
38
40
  const past = useRef<SchemaForUI[][]>([]);
39
41
  const future = useRef<SchemaForUI[][]>([]);
40
42
  const mainRef = useRef<HTMLDivElement>(null);
@@ -51,7 +53,8 @@ const TemplateEditor = ({
51
53
  const [sidebarOpen, setSidebarOpen] = useState(true);
52
54
  const [prevTemplate, setPrevTemplate] = useState<Template | null>(null);
53
55
 
54
- const { backgrounds, pageSizes, scale, error } = useUIPreProcessor({ template, size, zoomLevel });
56
+ const { backgrounds, pageSizes, scale, error, refresh } =
57
+ useUIPreProcessor({ template, size, zoomLevel });
55
58
 
56
59
  const onEdit = (targets: HTMLElement[]) => {
57
60
  setActiveElements(targets);
@@ -81,7 +84,7 @@ const TemplateEditor = ({
81
84
  const _schemasList = cloneDeep(schemasList);
82
85
  _schemasList[pageCursor] = newSchemas;
83
86
  setSchemasList(_schemasList);
84
- onChangeTemplate(fmtTemplate(template, _schemasList));
87
+ onChangeTemplate(schemasList2template(_schemasList, template.basePdf));
85
88
  },
86
89
  [template, schemasList, pageCursor, onChangeTemplate]
87
90
  );
@@ -96,32 +99,16 @@ const TemplateEditor = ({
96
99
 
97
100
  const changeSchemas: ChangeSchemas = useCallback(
98
101
  (objs) => {
99
- const newSchemas = objs.reduce((acc, { key, value, schemaId }) => {
100
- const tgt = acc.find((s) => s.id === schemaId)! as SchemaForUI;
101
- // Assign to reference
102
- set(tgt, key, value);
103
-
104
- if (key === 'type') {
105
- const keysToKeep = ['id', 'key', 'type', 'position'];
106
- Object.keys(tgt).forEach((key) => {
107
- if (!keysToKeep.includes(key)) {
108
- delete tgt[key as keyof typeof tgt];
109
- }
110
- });
111
- const propPanel = Object.values(pluginsRegistry).find(
112
- (plugin) => plugin?.propPanel.defaultSchema.type === value
113
- )?.propPanel;
114
- set(tgt, 'data', propPanel?.defaultValue || '');
115
- Object.assign(tgt, propPanel?.defaultSchema || {});
116
- } else if (key === 'data' && tgt.readOnly) {
117
- set(tgt, 'readOnlyValue', value);
118
- }
119
-
120
- return acc;
121
- }, cloneDeep(schemasList[pageCursor]));
122
- commitSchemas(newSchemas);
102
+ _changeSchemas({
103
+ objs,
104
+ schemas: schemasList[pageCursor],
105
+ basePdf: template.basePdf,
106
+ pluginsRegistry,
107
+ pageSize: pageSizes[pageCursor],
108
+ commitSchemas,
109
+ });
123
110
  },
124
- [commitSchemas, pageCursor, schemasList]
111
+ [commitSchemas, pageCursor, schemasList, pluginsRegistry, pageSizes, template.basePdf]
125
112
  );
126
113
 
127
114
  useInitEvents({
@@ -142,7 +129,7 @@ const TemplateEditor = ({
142
129
  });
143
130
 
144
131
  const updateTemplate = useCallback(async (newTemplate: Template) => {
145
- const sl = await templateSchemas2SchemasList(newTemplate);
132
+ const sl = await template2SchemasList(newTemplate);
146
133
  setSchemasList(sl);
147
134
  onEditEnd();
148
135
  setPageCursor(0);
@@ -162,13 +149,16 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
162
149
  const s = {
163
150
  id: uuid(),
164
151
  key: `${i18n('field')}${schemasList[pageCursor].length + 1}`,
165
- data: propPanel.defaultValue || '',
166
152
  ...propPanel.defaultSchema,
167
153
  } as SchemaForUI;
168
154
 
169
155
  const paper = paperRefs.current[pageCursor];
170
156
  const rectTop = paper ? paper.getBoundingClientRect().top : 0;
171
- s.position.y = rectTop > 0 ? 0 : pageSizes[pageCursor].height / 2;
157
+ const [paddingTop, , , paddingLeft] = isBlankPdf(template.basePdf)
158
+ ? template.basePdf.padding
159
+ : [0, 0, 0, 0];
160
+ s.position.y = rectTop > 0 ? paddingTop : pageSizes[pageCursor].height / 2;
161
+ s.position.x = paddingLeft;
172
162
 
173
163
  commitSchemas(schemasList[pageCursor].concat(s));
174
164
  setTimeout(() => onEdit([document.getElementById(s.id)!]));
@@ -182,6 +172,34 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
182
172
  setHoveringSchemaId(id);
183
173
  };
184
174
 
175
+ const updatePage = async (sl: SchemaForUI[][], newPageCursor: number) => {
176
+ setPageCursor(newPageCursor);
177
+ const newTemplate = schemasList2template(sl, template.basePdf);
178
+ onChangeTemplate(newTemplate);
179
+ await updateTemplate(newTemplate);
180
+ void refresh(newTemplate);
181
+ setTimeout(
182
+ () =>
183
+ mainRef.current &&
184
+ ((mainRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, newPageCursor, scale)), 0)
185
+ );
186
+ };
187
+
188
+ const handleRemovePage = () => {
189
+ if (pageCursor === 0) return;
190
+ if (!window.confirm(i18n('removePageConfirm'))) return;
191
+
192
+ const _schemasList = cloneDeep(schemasList);
193
+ _schemasList.splice(pageCursor, 1);
194
+ void updatePage(_schemasList, pageCursor - 1);
195
+ };
196
+
197
+ const handleAddPageAfter = () => {
198
+ const _schemasList = cloneDeep(schemasList);
199
+ _schemasList.splice(pageCursor + 1, 0, []);
200
+ void updatePage(_schemasList, pageCursor + 1);
201
+ };
202
+
185
203
  if (prevTemplate !== template) {
186
204
  setPrevTemplate(template);
187
205
  void updateTemplate(template);
@@ -195,6 +213,9 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
195
213
  if (error) {
196
214
  return <ErrorScreen size={size} error={error} />;
197
215
  }
216
+ const pageManipulation = isBlankPdf(template.basePdf)
217
+ ? { addPageAfter: handleAddPageAfter, removePage: handleRemovePage }
218
+ : {};
198
219
 
199
220
  return (
200
221
  <Root size={size} scale={scale}>
@@ -210,15 +231,16 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
210
231
  }}
211
232
  zoomLevel={zoomLevel}
212
233
  setZoomLevel={setZoomLevel}
234
+ {...pageManipulation}
213
235
  />
214
236
  <Sidebar
215
237
  hoveringSchemaId={hoveringSchemaId}
216
238
  onChangeHoveringSchemaId={onChangeHoveringSchemaId}
217
239
  height={mainRef.current ? mainRef.current.clientHeight : 0}
218
240
  size={size}
219
- pageSize={pageSizes[pageCursor]}
241
+ pageSize={pageSizes[pageCursor] ?? []}
220
242
  activeElements={activeElements}
221
- schemas={schemasList[pageCursor]}
243
+ schemas={schemasList[pageCursor] ?? []}
222
244
  changeSchemas={changeSchemas}
223
245
  onSortEnd={onSortEnd}
224
246
  onEdit={(id: string) => {
@@ -234,6 +256,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
234
256
  <Canvas
235
257
  ref={mainRef}
236
258
  paperRefs={paperRefs}
259
+ basePdf={template.basePdf}
237
260
  hoveringSchemaId={hoveringSchemaId}
238
261
  onChangeHoveringSchemaId={onChangeHoveringSchemaId}
239
262
  height={size.height - RULER_HEIGHT * ZOOM}
@@ -67,8 +67,7 @@ const Paper = (props: {
67
67
 
68
68
  return (
69
69
  <div
70
- id={`@pdfme/ui-paper${paperIndex}`}
71
- key={paperIndex + JSON.stringify(paperSize)}
70
+ key={String(paperIndex) + JSON.stringify(paperSize)}
72
71
  ref={(e) => {
73
72
  if (e) {
74
73
  paperRefs.current[paperIndex] = e;
@@ -1,5 +1,6 @@
1
- import React, { useCallback, useRef, useState, useEffect } from 'react';
2
- import type { SchemaForUI, PreviewProps, Size } from '@pdfme/common';
1
+ import React, { useRef, useState, useEffect, useContext } from 'react';
2
+ import { Template, SchemaForUI, PreviewProps, Size, getDynamicTemplate } from '@pdfme/common';
3
+ import { modifyTemplateForTable, getDynamicHeightForTable } from '@pdfme/schemas';
3
4
  import UnitPager from './UnitPager';
4
5
  import Root from './Root';
5
6
  import ErrorScreen from './ErrorScreen';
@@ -7,9 +8,12 @@ import CtlBar from './CtlBar';
7
8
  import Paper from './Paper';
8
9
  import Renderer from './Renderer';
9
10
  import { useUIPreProcessor, useScrollPageCursor } from '../hooks';
10
- import { templateSchemas2SchemasList, getPagesScrollTopByIndex } from '../helper';
11
+ import { FontContext } from '../contexts';
12
+ import { template2SchemasList, getPagesScrollTopByIndex } from '../helper';
11
13
  import { theme } from 'antd';
12
14
 
15
+ const _cache = new Map();
16
+
13
17
  const Preview = ({
14
18
  template,
15
19
  inputs,
@@ -21,6 +25,8 @@ const Preview = ({
21
25
  }) => {
22
26
  const { token } = theme.useToken();
23
27
 
28
+ const font = useContext(FontContext);
29
+
24
30
  const containerRef = useRef<HTMLDivElement>(null);
25
31
  const paperRefs = useRef<HTMLDivElement[]>([]);
26
32
 
@@ -29,38 +35,55 @@ const Preview = ({
29
35
  const [zoomLevel, setZoomLevel] = useState(1);
30
36
  const [schemasList, setSchemasList] = useState<SchemaForUI[][]>([[]] as SchemaForUI[][]);
31
37
 
32
- const { backgrounds, pageSizes, scale, error } = useUIPreProcessor({ template, size, zoomLevel });
38
+ const { backgrounds, pageSizes, scale, error, refresh } =
39
+ useUIPreProcessor({ template, size, zoomLevel });
40
+
41
+ const isForm = Boolean(onChangeInput);
42
+
43
+ const input = inputs[unitCursor];
33
44
 
34
- const init = useCallback(async () => {
35
- const sl = await templateSchemas2SchemasList(template);
36
- setSchemasList(sl);
37
- }, [template]);
45
+ const init = (template: Template) => {
46
+ const options = { font };
47
+ getDynamicTemplate({
48
+ template,
49
+ input,
50
+ options,
51
+ _cache,
52
+ modifyTemplate: (arg) => {
53
+ return modifyTemplateForTable(arg);
54
+ },
55
+ getDynamicHeight: (value, args) => {
56
+ if (args.schema.type !== 'table') return Promise.resolve(args.schema.height);
57
+ return getDynamicHeightForTable(value, args);
58
+ },
59
+ })
60
+ .then(async (dynamicTemplate) => {
61
+ const sl = await template2SchemasList(dynamicTemplate);
62
+ setSchemasList(sl);
63
+ await refresh(dynamicTemplate);
64
+ })
65
+ .catch((err) => console.error(`[@pdfme/ui] `, err));
66
+ };
38
67
 
39
68
  useEffect(() => {
40
69
  if (unitCursor > inputs.length - 1) {
41
70
  setUnitCursor(inputs.length - 1);
42
71
  }
43
- }, [inputs]);
44
72
 
45
- useEffect(() => {
46
- init();
47
- }, [init]);
73
+ init(template);
74
+ }, [template, inputs, size]);
48
75
 
49
76
  useScrollPageCursor({
50
77
  ref: containerRef,
51
78
  pageSizes,
52
79
  scale,
53
80
  pageCursor,
54
- onChangePageCursor: (p) => setPageCursor(p),
81
+ onChangePageCursor: setPageCursor,
55
82
  });
56
83
 
57
84
  const handleChangeInput = ({ key, value }: { key: string; value: string }) =>
58
85
  onChangeInput && onChangeInput({ index: unitCursor, key, value });
59
86
 
60
- const isForm = Boolean(onChangeInput);
61
-
62
- const input = inputs[unitCursor];
63
-
64
87
  if (error) {
65
88
  return <ErrorScreen size={size} error={error} />;
66
89
  }
@@ -94,16 +117,43 @@ const Preview = ({
94
117
  pageSizes={pageSizes}
95
118
  backgrounds={backgrounds}
96
119
  renderSchema={({ schema, index }) => {
97
- const { key } = schema;
98
- const data = (input && input[key]) || '';
120
+ const { key, readOnly } = schema;
121
+ const content = readOnly ? String(schema.content) || '' : String(input && input[key] || '');
99
122
  return (
100
123
  <Renderer
101
124
  key={schema.id}
102
- schema={Object.assign(schema, { data })}
125
+ schema={schema}
126
+ basePdf={template.basePdf}
127
+ value={content}
103
128
  mode={isForm ? 'form' : 'viewer'}
104
- placeholder={template.sampledata?.[0]?.[key] ?? ''}
129
+ placeholder={schema.content}
105
130
  tabIndex={index + 100}
106
- onChange={(value) => handleChangeInput({ key, value })}
131
+ onChange={(arg) => {
132
+ const args = Array.isArray(arg) ? arg : [arg];
133
+ let isNeedInit = false;
134
+ args.forEach(({ key: _key, value }) => {
135
+ if (_key === 'content') {
136
+ const newValue = value as string;
137
+ const oldValue = (input?.[key] as string) || '';
138
+ if (newValue === oldValue) return;
139
+ handleChangeInput({ key, value: newValue });
140
+ // TODO Set to true only if the execution of getDynamicTemplate, such as for a table, is required.
141
+ isNeedInit = true;
142
+ } else {
143
+ const targetSchema = schemasList[pageCursor].find(
144
+ (s) => s.id === schema.id
145
+ ) as SchemaForUI;
146
+ if (!targetSchema) return;
147
+
148
+ // @ts-ignore
149
+ targetSchema[_key] = value as string;
150
+ }
151
+ });
152
+ if (isNeedInit) {
153
+ init(template);
154
+ }
155
+ setSchemasList([...schemasList])
156
+ }}
107
157
  outline={
108
158
  isForm && !schema.readOnly ? `1px dashed ${token.colorPrimary}` : 'transparent'
109
159
  }
@@ -1,15 +1,16 @@
1
1
  import React, { useEffect, useContext, ReactNode, useRef } from 'react';
2
- import { Dict, ZOOM, UIRenderProps, SchemaForUI, Schema } from '@pdfme/common';
2
+ import { Dict, ZOOM, UIRenderProps, SchemaForUI, BasePdf, Schema } from '@pdfme/common';
3
3
  import { theme as antdTheme } from 'antd';
4
4
  import { SELECTABLE_CLASSNAME } from '../constants';
5
5
  import { PluginsRegistry, OptionsContext, I18nContext } from '../contexts';
6
6
 
7
7
  type RendererProps = Omit<
8
8
  UIRenderProps<Schema>,
9
- 'value' | 'schema' | 'onChange' | 'rootElement' | 'options' | 'theme' | 'i18n' | '_cache'
9
+ 'schema' | 'rootElement' | 'options' | 'theme' | 'i18n' | '_cache'
10
10
  > & {
11
+ basePdf: BasePdf;
11
12
  schema: SchemaForUI;
12
- onChange: (value: string) => void;
13
+ value: string;
13
14
  outline: string;
14
15
  onChangeHoveringSchemaId?: (id: string | null) => void;
15
16
  scale: number;
@@ -49,7 +50,8 @@ const Renderer = (props: RendererProps) => {
49
50
  const i18n = useContext(I18nContext) as (key: keyof Dict | string) => string;
50
51
  const { token: theme } = antdTheme.useToken();
51
52
 
52
- const { schema, mode, onChange, stopEditing, tabIndex, placeholder, scale } = props;
53
+ const { schema, basePdf, value, mode, onChange, stopEditing, tabIndex, placeholder, scale } =
54
+ props;
53
55
 
54
56
  const ref = useRef<HTMLDivElement>(null);
55
57
  const _cache = useRef<Map<any, any>>(new Map());
@@ -68,16 +70,15 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
68
70
 
69
71
  ref.current.innerHTML = '';
70
72
 
71
- const editable = mode === 'form' || mode === 'designer';
72
-
73
- render({
73
+ void render({
74
74
  key: schema.key,
75
- value: schema.readOnly ? schema.readOnlyValue || '' : schema.data,
75
+ value,
76
76
  schema,
77
+ basePdf,
77
78
  rootElement: ref.current,
78
79
  mode,
79
- onChange: editable ? onChange : undefined,
80
- stopEditing: editable ? stopEditing : undefined,
80
+ onChange,
81
+ stopEditing: stopEditing,
81
82
  tabIndex,
82
83
  placeholder,
83
84
  options,
@@ -91,7 +92,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
91
92
  ref.current.innerHTML = '';
92
93
  }
93
94
  };
94
- }, [JSON.stringify(schema), JSON.stringify(options), mode, scale]);
95
+ }, [value, JSON.stringify(schema), JSON.stringify(options), mode, scale]);
95
96
 
96
97
  return (
97
98
  <Wrapper {...props}>
package/src/constants.ts CHANGED
@@ -8,6 +8,6 @@ export const RULER_HEIGHT = 30;
8
8
 
9
9
  export const PAGE_GAP = 10;
10
10
 
11
- export const SIDEBAR_WIDTH = 350;
11
+ export const SIDEBAR_WIDTH = 400;
12
12
 
13
13
  export const BACKGROUND_COLOR = 'rgb(74, 74, 74)';