@pdfme/ui 5.5.10-dev.13 → 5.5.10-dev.135

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 (77) hide show
  1. package/README.md +3 -1
  2. package/__mocks__/converter.ts +3 -0
  3. package/__mocks__/form-render.js +3 -3
  4. package/__mocks__/form-render.ts +18 -0
  5. package/__mocks__/lucide-react.ts +15 -0
  6. package/package.json +39 -86
  7. package/src/Designer.tsx +1 -3
  8. package/src/Form.tsx +1 -3
  9. package/src/Viewer.tsx +1 -3
  10. package/src/class.ts +17 -6
  11. package/src/components/CtlBar.tsx +7 -2
  12. package/src/components/Designer/PluginIcon.tsx +64 -16
  13. package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +1 -2
  14. package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +8 -7
  15. package/src/components/Designer/RightSidebar/DetailView/index.tsx +15 -17
  16. package/src/components/Designer/RightSidebar/ListView/Item.tsx +3 -5
  17. package/src/components/Designer/index.tsx +28 -17
  18. package/src/components/Preview.tsx +40 -14
  19. package/src/components/Renderer.tsx +73 -27
  20. package/src/components/Root.tsx +8 -2
  21. package/src/contexts.ts +1 -1
  22. package/src/helper.ts +1 -1
  23. package/src/hooks.ts +102 -67
  24. package/tsconfig.build.json +15 -0
  25. package/tsconfig.json +5 -40
  26. package/tsconfig.typecheck.json +19 -0
  27. package/vite.config.mts +46 -5
  28. package/vitest.setup.ts +47 -0
  29. package/dist/index.es.js +0 -159737
  30. package/dist/index.umd.js +0 -1060
  31. package/dist/types/__tests__/assets/helper.d.ts +0 -3
  32. package/dist/types/__tests__/components/Designer.test.d.ts +0 -1
  33. package/dist/types/__tests__/components/PluginIcon.test.d.ts +0 -1
  34. package/dist/types/__tests__/components/Preview.test.d.ts +0 -1
  35. package/dist/types/__tests__/helper.test.d.ts +0 -1
  36. package/dist/types/src/Designer.d.ts +0 -21
  37. package/dist/types/src/Form.d.ts +0 -24
  38. package/dist/types/src/Viewer.d.ts +0 -15
  39. package/dist/types/src/class.d.ts +0 -89
  40. package/dist/types/src/components/AppContextProvider.d.ts +0 -11
  41. package/dist/types/src/components/CtlBar.d.ts +0 -14
  42. package/dist/types/src/components/Designer/Canvas/Guides.d.ts +0 -9
  43. package/dist/types/src/components/Designer/Canvas/Mask.d.ts +0 -4
  44. package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +0 -37
  45. package/dist/types/src/components/Designer/Canvas/Padding.d.ts +0 -6
  46. package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +0 -10
  47. package/dist/types/src/components/Designer/Canvas/index.d.ts +0 -22
  48. package/dist/types/src/components/Designer/LeftSidebar.d.ts +0 -8
  49. package/dist/types/src/components/Designer/PluginIcon.d.ts +0 -10
  50. package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +0 -4
  51. package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +0 -4
  52. package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +0 -7
  53. package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +0 -8
  54. package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +0 -45
  55. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +0 -4
  56. package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +0 -14
  57. package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +0 -4
  58. package/dist/types/src/components/Designer/RightSidebar/index.d.ts +0 -4
  59. package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +0 -15
  60. package/dist/types/src/components/Designer/index.d.ts +0 -11
  61. package/dist/types/src/components/ErrorScreen.d.ts +0 -7
  62. package/dist/types/src/components/Paper.d.ts +0 -20
  63. package/dist/types/src/components/Preview.d.ts +0 -15
  64. package/dist/types/src/components/Renderer.d.ts +0 -13
  65. package/dist/types/src/components/Root.d.ts +0 -9
  66. package/dist/types/src/components/Spinner.d.ts +0 -3
  67. package/dist/types/src/components/StaticSchema.d.ts +0 -10
  68. package/dist/types/src/components/UnitPager.d.ts +0 -10
  69. package/dist/types/src/constants.d.ts +0 -11
  70. package/dist/types/src/contexts.d.ts +0 -10
  71. package/dist/types/src/helper.d.ts +0 -73
  72. package/dist/types/src/hooks.d.ts +0 -46
  73. package/dist/types/src/i18n.d.ts +0 -3
  74. package/dist/types/src/index.d.ts +0 -4
  75. package/dist/types/src/theme.d.ts +0 -2
  76. package/dist/types/src/types.d.ts +0 -19
  77. package/eslint.config.mjs +0 -41
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState, useContext, useCallback, useEffect } from 'react';
1
+ import React, { useRef, useState, useContext, useCallback, useEffect, useLayoutEffect } from 'react';
2
2
  import {
3
3
  cloneDeep,
4
4
  ZOOM,
@@ -72,6 +72,7 @@ const TemplateEditor = ({
72
72
  const [pageCursor, setPageCursor] = useState(0);
73
73
  const [zoomLevel, setZoomLevel] = useState(options.zoomLevel ?? 1);
74
74
  const [sidebarOpen, setSidebarOpen] = useState(options.sidebarOpen ?? true);
75
+ const [canvasHeight, setCanvasHeight] = useState(0);
75
76
  const [prevTemplate, setPrevTemplate] = useState<Template | null>(null);
76
77
 
77
78
  const { backgrounds, pageSizes, scale, error, refresh } = useUIPreProcessor({
@@ -81,8 +82,8 @@ const TemplateEditor = ({
81
82
  maxZoom,
82
83
  });
83
84
 
84
- const onEdit = (targets: HTMLElement[]) => {
85
- setActiveElements(targets);
85
+ const onEdit = (targets: Array<HTMLElement | null | undefined>) => {
86
+ setActiveElements(targets.filter((target): target is HTMLElement => target instanceof HTMLElement));
86
87
  setHoveringSchemaId(null);
87
88
  };
88
89
 
@@ -91,17 +92,17 @@ const TemplateEditor = ({
91
92
  setHoveringSchemaId(null);
92
93
  };
93
94
 
94
- // Update component state only when _options_ changes
95
- // Ignore exhaustive useEffect dependency warnings here
96
95
  useEffect(() => {
97
- if (typeof options.zoomLevel === 'number' && options.zoomLevel !== zoomLevel) {
96
+ if (typeof options.zoomLevel === 'number') {
98
97
  setZoomLevel(options.zoomLevel);
99
98
  }
100
- if (typeof options.sidebarOpen === 'boolean' && options.sidebarOpen !== sidebarOpen) {
99
+ }, [options.zoomLevel]);
100
+
101
+ useEffect(() => {
102
+ if (typeof options.sidebarOpen === 'boolean') {
101
103
  setSidebarOpen(options.sidebarOpen);
102
104
  }
103
- // eslint-disable-next-line
104
- }, [options]);
105
+ }, [options.sidebarOpen]);
105
106
 
106
107
  useScrollPageCursor({
107
108
  ref: canvasRef,
@@ -115,6 +116,20 @@ const TemplateEditor = ({
115
116
  },
116
117
  });
117
118
 
119
+ useLayoutEffect(() => {
120
+ const updateHeight = () => {
121
+ setCanvasHeight(canvasRef.current ? canvasRef.current.clientHeight : 0);
122
+ };
123
+ updateHeight();
124
+
125
+ if (typeof ResizeObserver === 'function' && canvasRef.current) {
126
+ const observer = new ResizeObserver(updateHeight);
127
+ observer.observe(canvasRef.current);
128
+ return () => observer.disconnect();
129
+ }
130
+ return undefined;
131
+ }, [scale]);
132
+
118
133
  const commitSchemas = useCallback(
119
134
  (newSchemas: SchemaForUI[]) => {
120
135
  future.current = [];
@@ -222,7 +237,7 @@ const TemplateEditor = ({
222
237
  }
223
238
 
224
239
  commitSchemas(schemasList[pageCursor].concat(s));
225
- setTimeout(() => onEdit([document.getElementById(s.id)!]));
240
+ setTimeout(() => onEdit([document.getElementById(s.id)]));
226
241
  };
227
242
 
228
243
  const onSortEnd = (sortedSchemas: SchemaForUI[]) => {
@@ -239,7 +254,7 @@ const TemplateEditor = ({
239
254
  onChangeTemplate(newTemplate);
240
255
  await updateTemplate(newTemplate);
241
256
  void refresh(newTemplate);
242
-
257
+
243
258
  // Notify page change with updated total pages
244
259
  onPageCursorChange(newPageCursor, sl.length);
245
260
 
@@ -313,11 +328,7 @@ const TemplateEditor = ({
313
328
  }}
314
329
  onDragStart={onEditEnd}
315
330
  >
316
- <LeftSidebar
317
- height={canvasRef.current ? canvasRef.current.clientHeight : 0}
318
- scale={scale}
319
- basePdf={template.basePdf}
320
- />
331
+ <LeftSidebar height={canvasHeight} scale={scale} basePdf={template.basePdf} />
321
332
 
322
333
  <div style={{ position: 'absolute', width: canvasWidth, marginLeft: LEFT_SIDEBAR_WIDTH }}>
323
334
  <CtlBar
@@ -340,7 +351,7 @@ const TemplateEditor = ({
340
351
  <RightSidebar
341
352
  hoveringSchemaId={hoveringSchemaId}
342
353
  onChangeHoveringSchemaId={onChangeHoveringSchemaId}
343
- height={canvasRef.current ? canvasRef.current.clientHeight : 0}
354
+ height={canvasHeight}
344
355
  size={size}
345
356
  pageSize={pageSizes[pageCursor] ?? []}
346
357
  basePdf={template.basePdf}
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState, useEffect, useContext } from 'react';
1
+ import React, { useRef, useState, useEffect, useContext, useCallback } from 'react';
2
2
  import {
3
3
  Template,
4
4
  SchemaForUI,
@@ -7,7 +7,7 @@ import {
7
7
  getDynamicTemplate,
8
8
  replacePlaceholders,
9
9
  } from '@pdfme/common';
10
- import { getDynamicHeightsForTable } from '@pdfme/schemas/utils';
10
+ import { getDynamicHeightsForTable } from '@pdfme/schemas/tables';
11
11
  import UnitPager from './UnitPager.js';
12
12
  import Root from './Root.js';
13
13
  import StaticSchema from './StaticSchema.js';
@@ -57,10 +57,36 @@ const Preview = ({
57
57
  const isForm = Boolean(onChangeInput);
58
58
 
59
59
  const input = inputs[unitCursor];
60
+ const latestFontRef = useRef(font);
61
+ const latestInputRef = useRef(input);
62
+ const latestRefreshRef = useRef(refresh);
63
+ const isMountedRef = useRef(true);
64
+ const initRequestIdRef = useRef(0);
60
65
 
61
- const init = (template: Template, inputOverride?: Record<string, string>) => {
62
- const currentInput = inputOverride ?? input;
63
- const options = { font };
66
+ useEffect(() => {
67
+ latestFontRef.current = font;
68
+ }, [font]);
69
+
70
+ useEffect(() => {
71
+ latestInputRef.current = input;
72
+ }, [input]);
73
+
74
+ useEffect(() => {
75
+ latestRefreshRef.current = refresh;
76
+ }, [refresh]);
77
+
78
+ useEffect(
79
+ () => () => {
80
+ isMountedRef.current = false;
81
+ },
82
+ [],
83
+ );
84
+
85
+ const init = useCallback((template: Template, inputOverride?: Record<string, string>) => {
86
+ const requestId = ++initRequestIdRef.current;
87
+ const currentInput = inputOverride ?? latestInputRef.current;
88
+ const options = { font: latestFontRef.current };
89
+ const currentRefresh = latestRefreshRef.current;
64
90
  getDynamicTemplate({
65
91
  template,
66
92
  input: currentInput,
@@ -77,28 +103,28 @@ const Preview = ({
77
103
  })
78
104
  .then(async (dynamicTemplate) => {
79
105
  const sl = await template2SchemasList(dynamicTemplate);
106
+ if (!isMountedRef.current || requestId !== initRequestIdRef.current) {
107
+ return;
108
+ }
80
109
  setSchemasList(sl);
81
- await refresh(dynamicTemplate);
110
+ await currentRefresh(dynamicTemplate);
82
111
  })
83
112
  .catch((err) => console.error(`[@pdfme/ui] `, err));
84
- };
113
+ }, []);
85
114
 
86
- // Update component state only when _options_ changes
87
- // Ignore exhaustive useEffect dependency warnings here
88
115
  useEffect(() => {
89
- if (typeof options.zoomLevel === 'number' && options.zoomLevel !== zoomLevel) {
116
+ if (typeof options.zoomLevel === 'number') {
90
117
  setZoomLevel(options.zoomLevel);
91
118
  }
92
- // eslint-disable-next-line
93
- }, [options]);
119
+ }, [options.zoomLevel]);
94
120
 
95
121
  useEffect(() => {
96
122
  if (unitCursor > inputs.length - 1) {
97
123
  setUnitCursor(inputs.length - 1);
124
+ return;
98
125
  }
99
-
100
126
  init(template);
101
- }, [template, inputs, size]);
127
+ }, [init, inputs, size, template, unitCursor]);
102
128
 
103
129
  useScrollPageCursor({
104
130
  ref: containerRef,
@@ -36,7 +36,7 @@ type ReRenderCheckProps = {
36
36
  options: UIOptions;
37
37
  };
38
38
 
39
- const useRerenderDependencies = (arg: ReRenderCheckProps) => {
39
+ const useRenderKey = (arg: ReRenderCheckProps) => {
40
40
  const { plugin, value, mode, scale, schema, options } = arg;
41
41
  const _options = cloneDeep(options);
42
42
  if (_options.font) {
@@ -48,9 +48,9 @@ const useRerenderDependencies = (arg: ReRenderCheckProps) => {
48
48
 
49
49
  return useMemo(() => {
50
50
  if (plugin?.uninterruptedEditMode && mode === 'designer') {
51
- return [mode];
51
+ return mode;
52
52
  } else {
53
- return [value, mode, scale, JSON.stringify(schema), optionStr];
53
+ return JSON.stringify([value, mode, scale, schema, optionStr]);
54
54
  }
55
55
  }, [value, mode, scale, schema, optionStr, plugin]);
56
56
  };
@@ -110,8 +110,41 @@ const Renderer = (props: RendererProps) => {
110
110
  const ref = useRef<HTMLDivElement>(null);
111
111
  const _cache = useContext(CacheContext);
112
112
  const plugin = pluginsRegistry.findByType(schema.type);
113
+ const renderArgsRef = useRef({
114
+ plugin,
115
+ value,
116
+ schema,
117
+ basePdf,
118
+ mode,
119
+ onChange,
120
+ stopEditing,
121
+ tabIndex,
122
+ placeholder,
123
+ options,
124
+ theme,
125
+ i18n,
126
+ scale,
127
+ _cache,
128
+ });
113
129
 
114
- const reRenderDependencies = useRerenderDependencies({
130
+ renderArgsRef.current = {
131
+ plugin,
132
+ value,
133
+ schema,
134
+ basePdf,
135
+ mode,
136
+ onChange,
137
+ stopEditing,
138
+ tabIndex,
139
+ placeholder,
140
+ options,
141
+ theme,
142
+ i18n,
143
+ scale,
144
+ _cache,
145
+ };
146
+
147
+ const renderKey = useRenderKey({
115
148
  plugin,
116
149
  value,
117
150
  mode,
@@ -121,34 +154,47 @@ const Renderer = (props: RendererProps) => {
121
154
  });
122
155
 
123
156
  useEffect(() => {
124
- if (!plugin?.ui || !ref.current || !schema.type) return;
125
-
126
- ref.current.innerHTML = '';
127
- const render = plugin.ui;
128
-
129
- void render({
130
- value,
131
- schema,
132
- basePdf,
133
- rootElement: ref.current,
134
- mode,
135
- onChange,
136
- stopEditing,
137
- tabIndex,
138
- placeholder,
139
- options,
140
- theme,
141
- i18n,
142
- scale,
143
- _cache,
157
+ const element = ref.current;
158
+ const renderArgs = renderArgsRef.current;
159
+ if (!renderArgs.plugin?.ui || !element || !schema.type) return;
160
+
161
+ let cancelled = false;
162
+ element.innerHTML = '';
163
+ element.dataset.pdfmeRenderReady = 'false';
164
+ const render = renderArgs.plugin.ui;
165
+
166
+ void Promise.resolve(
167
+ render({
168
+ value: renderArgs.value,
169
+ schema: renderArgs.schema,
170
+ basePdf: renderArgs.basePdf,
171
+ rootElement: element,
172
+ mode: renderArgs.mode,
173
+ onChange: renderArgs.onChange,
174
+ stopEditing: renderArgs.stopEditing,
175
+ tabIndex: renderArgs.tabIndex,
176
+ placeholder: renderArgs.placeholder,
177
+ options: renderArgs.options,
178
+ theme: renderArgs.theme,
179
+ i18n: renderArgs.i18n,
180
+ scale: renderArgs.scale,
181
+ _cache: renderArgs._cache,
182
+ }),
183
+ ).finally(() => {
184
+ if (!cancelled) {
185
+ element.dataset.pdfmeRenderReady = 'true';
186
+ }
144
187
  });
145
188
 
146
189
  return () => {
147
- if (ref.current) {
148
- ref.current.innerHTML = '';
190
+ cancelled = true;
191
+ if (element) {
192
+ element.dispatchEvent(new Event('beforeRemove'));
193
+ element.innerHTML = '';
194
+ delete element.dataset.pdfmeRenderReady;
149
195
  }
150
196
  };
151
- }, reRenderDependencies);
197
+ }, [renderKey, schema.type]);
152
198
 
153
199
  if (!plugin) {
154
200
  console.error(`[@pdfme/ui] Renderer for type ${schema.type} not found.
@@ -29,8 +29,14 @@ const Root = ({ size, scale, children }: Props, ref: Ref<HTMLDivElement>) => {
29
29
  }, [font]);
30
30
 
31
31
  return (
32
- <div className={DESIGNER_CLASSNAME + 'root'} ref={ref} style={{ position: 'relative', background: BACKGROUND_COLOR, ...size }}>
33
- <div className={DESIGNER_CLASSNAME + 'background'} style={{ margin: '0 auto', ...size }}>{scale === 0 ? <Spinner /> : children}</div>
32
+ <div
33
+ className={DESIGNER_CLASSNAME + 'root'}
34
+ ref={ref}
35
+ style={{ position: 'relative', background: BACKGROUND_COLOR, ...size }}
36
+ >
37
+ <div className={DESIGNER_CLASSNAME + 'background'} style={{ margin: '0 auto', ...size }}>
38
+ {scale === 0 ? <Spinner /> : children}
39
+ </div>
34
40
  </div>
35
41
  );
36
42
  };
package/src/contexts.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createContext } from 'react';
2
2
  import { i18n } from './i18n.js';
3
3
  import { getDefaultFont, PluginRegistry, pluginRegistry, UIOptions } from '@pdfme/common';
4
- import { builtInPlugins } from '@pdfme/schemas';
4
+ import { builtInPlugins } from '@pdfme/schemas/builtins';
5
5
 
6
6
  export const I18nContext = createContext(i18n);
7
7
 
package/src/helper.ts CHANGED
@@ -301,7 +301,7 @@ export const template2SchemasList = async (_template: Template) => {
301
301
 
302
302
  return (
303
303
  ssl < psl
304
- ? schemasForUI.concat(new Array(psl - ssl).fill(cloneDeep([])))
304
+ ? schemasForUI.concat(Array.from({ length: psl - ssl }, () => cloneDeep([])))
305
305
  : schemasForUI.slice(0, pageSizes.length)
306
306
  ).map((schema, i) => {
307
307
  Object.values(schema).forEach((value) => {
package/src/hooks.ts CHANGED
@@ -42,81 +42,108 @@ export const useUIPreProcessor = ({ template, size, zoomLevel, maxZoom }: UIPreP
42
42
  const [pageSizes, setPageSizes] = useState<Size[]>([]);
43
43
  const [scale, setScale] = useState(0);
44
44
  const [error, setError] = useState<Error | null>(null);
45
+ const isMountedRef = useRef(true);
46
+ const requestIdRef = useRef(0);
47
+
48
+ const init = useCallback(
49
+ async (prop: { template: Template; size: Size }) => {
50
+ const {
51
+ template: { basePdf, schemas },
52
+ size,
53
+ } = prop;
54
+
55
+ let paperWidth: number;
56
+ let paperHeight: number;
57
+ let _backgrounds: string[];
58
+ let _pageSizes: { width: number; height: number }[];
59
+
60
+ if (isBlankPdf(basePdf)) {
61
+ const { width, height } = basePdf;
62
+ paperWidth = width * ZOOM;
63
+ paperHeight = height * ZOOM;
64
+ _backgrounds = schemas.map(
65
+ () =>
66
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+P///38ACfsD/QVDRcoAAAAASUVORK5CYII=',
67
+ );
68
+ _pageSizes = schemas.map(() => ({ width, height }));
69
+ } else {
70
+ const _basePdf = await getB64BasePdf(basePdf);
71
+ const uint8Array = b64toUint8Array(_basePdf);
72
+ const createPdfArrayBuffer = () => {
73
+ const buffer = new ArrayBuffer(uint8Array.byteLength);
74
+ new Uint8Array(buffer).set(uint8Array);
75
+ return buffer;
76
+ };
77
+
78
+ const [pageSizeBuffer, imageBuffer] = [createPdfArrayBuffer(), createPdfArrayBuffer()];
79
+ const [_pages, imgBuffers] = await Promise.all([
80
+ pdf2size(pageSizeBuffer),
81
+ pdf2img(imageBuffer, { scale: maxZoom }),
82
+ ]);
83
+ _pageSizes = _pages;
84
+ paperWidth = _pageSizes[0].width * ZOOM;
85
+ paperHeight = _pageSizes[0].height * ZOOM;
86
+ _backgrounds = imgBuffers.map(arrayBufferToBase64);
87
+ }
45
88
 
46
- const init = async (prop: { template: Template; size: Size }) => {
47
- const {
48
- template: { basePdf, schemas },
49
- size,
50
- } = prop;
51
-
52
- let paperWidth: number;
53
- let paperHeight: number;
54
- let _backgrounds: string[];
55
- let _pageSizes: { width: number; height: number }[];
56
-
57
- if (isBlankPdf(basePdf)) {
58
- const { width, height } = basePdf;
59
- paperWidth = width * ZOOM;
60
- paperHeight = height * ZOOM;
61
- _backgrounds = schemas.map(
62
- () =>
63
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdj+P///38ACfsD/QVDRcoAAAAASUVORK5CYII=',
89
+ const _scale = Math.min(
90
+ getScale(size.width, paperWidth),
91
+ getScale(size.height - RULER_HEIGHT, paperHeight),
64
92
  );
65
- _pageSizes = schemas.map(() => ({ width, height }));
66
- } else {
67
- const _basePdf = await getB64BasePdf(basePdf);
68
-
69
- const uint8Array = b64toUint8Array(_basePdf);
70
- // Create a new ArrayBuffer copy to avoid detachment issues
71
- const pdfArrayBuffer = new ArrayBuffer(uint8Array.byteLength);
72
- new Uint8Array(pdfArrayBuffer).set(uint8Array);
73
-
74
- const [_pages, imgBuffers] = await Promise.all([
75
- pdf2size(pdfArrayBuffer),
76
- pdf2img(pdfArrayBuffer.slice(), { scale: maxZoom }),
77
- ]);
78
- _pageSizes = _pages;
79
- paperWidth = _pageSizes[0].width * ZOOM;
80
- paperHeight = _pageSizes[0].height * ZOOM;
81
- _backgrounds = imgBuffers.map(arrayBufferToBase64);
82
- }
83
-
84
- const _scale = Math.min(
85
- getScale(size.width, paperWidth),
86
- getScale(size.height - RULER_HEIGHT, paperHeight),
87
- );
88
93
 
89
- return {
90
- backgrounds: _backgrounds,
91
- pageSizes: _pageSizes,
92
- scale: _scale,
93
- };
94
- };
94
+ return {
95
+ backgrounds: _backgrounds,
96
+ pageSizes: _pageSizes,
97
+ scale: _scale,
98
+ };
99
+ },
100
+ [maxZoom],
101
+ );
102
+
103
+ useEffect(
104
+ () => () => {
105
+ isMountedRef.current = false;
106
+ },
107
+ [],
108
+ );
109
+
110
+ const runInit = useCallback(
111
+ async (prop: { template: Template; size: Size }) => {
112
+ const requestId = ++requestIdRef.current;
113
+
114
+ try {
115
+ const { pageSizes, scale, backgrounds } = await init(prop);
116
+ if (!isMountedRef.current || requestId !== requestIdRef.current) {
117
+ return;
118
+ }
95
119
 
96
- useEffect(() => {
97
- init({ template, size })
98
- .then(({ pageSizes, scale, backgrounds }) => {
99
120
  setPageSizes(pageSizes);
100
121
  setScale(scale);
101
122
  setBackgrounds(backgrounds);
102
- })
103
- .catch((err: Error) => {
104
- setError(err);
105
- console.error('[@pdfme/ui]', err);
106
- });
107
- }, [template, size]);
123
+ setError(null);
124
+ } catch (err: unknown) {
125
+ const error = err instanceof Error ? err : new Error(String(err));
126
+ if (isMountedRef.current && requestId === requestIdRef.current) {
127
+ setError(error);
128
+ console.error('[@pdfme/ui]', error);
129
+ }
130
+ }
131
+ },
132
+ [init],
133
+ );
134
+
135
+ useEffect(() => {
136
+ void runInit({ template, size });
137
+ }, [runInit, template, size]);
138
+
139
+ const refresh = useCallback((template: Template) => runInit({ template, size }), [runInit, size]);
108
140
 
109
141
  return {
110
142
  backgrounds,
111
143
  pageSizes,
112
144
  scale: scale * zoomLevel,
113
145
  error,
114
- refresh: (template: Template) =>
115
- init({ template, size }).then(({ pageSizes, scale, backgrounds }) => {
116
- setPageSizes(pageSizes);
117
- setScale(scale);
118
- setBackgrounds(backgrounds);
119
- }),
146
+ refresh,
120
147
  };
121
148
  };
122
149
 
@@ -164,10 +191,11 @@ export const useScrollPageCursor = ({
164
191
  }, [onChangePageCursor, pageCursor, pageSizes, ref, scale]);
165
192
 
166
193
  useEffect(() => {
167
- ref.current?.addEventListener('scroll', onScroll);
194
+ const node = ref.current;
195
+ node?.addEventListener('scroll', onScroll);
168
196
 
169
197
  return () => {
170
- ref.current?.removeEventListener('scroll', onScroll);
198
+ node?.removeEventListener('scroll', onScroll);
171
199
  };
172
200
  }, [ref, onScroll]);
173
201
  };
@@ -196,7 +224,7 @@ interface UseInitEventsParams {
196
224
  past: React.MutableRefObject<SchemaForUI[][]>;
197
225
  future: React.MutableRefObject<SchemaForUI[][]>;
198
226
  setSchemasList: React.Dispatch<React.SetStateAction<SchemaForUI[][]>>;
199
- onEdit: (targets: HTMLElement[]) => void;
227
+ onEdit: (targets: Array<HTMLElement | null | undefined>) => void;
200
228
  onEditEnd: () => void;
201
229
  }
202
230
 
@@ -219,6 +247,11 @@ export const useInitEvents = ({
219
247
  const copiedSchemas = useRef<SchemaForUI[] | null>(null);
220
248
 
221
249
  const initEvents = useCallback(() => {
250
+ const getElementsByIds = (ids: string[]) =>
251
+ ids
252
+ .map((id) => document.getElementById(id))
253
+ .filter((element): element is HTMLElement => element instanceof HTMLElement);
254
+
222
255
  const getActiveSchemas = () => {
223
256
  const ids = activeElements.map((ae) => ae.id);
224
257
 
@@ -267,7 +300,9 @@ export const useInitEvents = ({
267
300
  return Object.assign(cloneDeep(cs), { id, name, position });
268
301
  });
269
302
  commitSchemas(schemasList[pageCursor].concat(pasteSchemas));
270
- onEdit(pasteSchemas.map((s) => document.getElementById(s.id)!));
303
+ setTimeout(() => {
304
+ onEdit(getElementsByIds(pasteSchemas.map((s) => s.id)));
305
+ });
271
306
  copiedSchemas.current = pasteSchemas;
272
307
  },
273
308
  redo: () => timeTravel('redo'),
@@ -276,7 +311,7 @@ export const useInitEvents = ({
276
311
  onSaveTemplate && onSaveTemplate(schemasList2template(schemasList, template.basePdf)),
277
312
  remove: () => removeSchemas(getActiveSchemas().map((s) => s.id)),
278
313
  esc: onEditEnd,
279
- selectAll: () => onEdit(schemasList[pageCursor].map((s) => document.getElementById(s.id)!)),
314
+ selectAll: () => onEdit(getElementsByIds(schemasList[pageCursor].map((s) => s.id))),
280
315
  });
281
316
  }, [
282
317
  template,
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "../../tsconfig.build.base.json",
3
+ "compilerOptions": {
4
+ "jsx": "react",
5
+ "declaration": true,
6
+ "declarationDir": "./dist",
7
+ "emitDeclarationOnly": true,
8
+ "module": "ESNext",
9
+ "moduleResolution": "bundler",
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "skipLibCheck": true
13
+ },
14
+ "include": ["src/**/*.ts", "src/**/*.tsx"]
15
+ }
package/tsconfig.json CHANGED
@@ -1,48 +1,13 @@
1
1
  {
2
+ "extends": "../../tsconfig.base.json",
2
3
  "compilerOptions": {
3
- "noImplicitAny": true,
4
- "target": "ES2020",
5
4
  "jsx": "react",
6
- "esModuleInterop": true,
7
- "isolatedModules": true,
8
- "declaration": true,
9
- "declarationDir": "dist/types",
10
5
  "module": "ESNext",
11
- "lib": [
12
- "ES2020",
13
- "DOM",
14
- "DOM.Iterable",
15
- "ESNext"
16
- ],
17
6
  "moduleResolution": "bundler",
18
- "allowSyntheticDefaultImports": true,
19
- "strict": true,
20
- "types": ["jest"],
21
- "typeRoots": [
22
- "../node_modules/@types",
23
- "./node_modules/@types",
24
- "../../node_modules/@types"
25
- ],
26
- "sourceMap": true,
27
- "baseUrl": ".",
28
- "paths": {
29
- "@pdfme/common": [
30
- "packages/common/dist/esm/src"
31
- ],
32
- "@pdfme/schemas": [
33
- "packages/schemas/dist/esm/src"
34
- ]
35
- },
7
+ "noEmit": true,
8
+ "typeRoots": ["../node_modules/@types", "./node_modules/@types", "../../node_modules/@types"],
36
9
  "skipLibCheck": true
37
10
  },
38
- "include": [
39
- "src/**/*.ts",
40
- "src/**/*.tsx",
41
- "__tests__/**/*.ts",
42
- "__tests__/**/*.tsx"
43
- ],
44
- "exclude": [
45
- "node_modules",
46
- "dist"
47
- ]
11
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
12
+ "exclude": ["node_modules", "dist"]
48
13
  }
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "composite": true,
5
+ "jsx": "react",
6
+ "declaration": true,
7
+ "declarationDir": "dist/typecheck",
8
+ "emitDeclarationOnly": true,
9
+ "module": "ESNext",
10
+ "moduleResolution": "bundler",
11
+ "outDir": "dist/typecheck",
12
+ "tsBuildInfoFile": "dist/typecheck/tsconfig.tsbuildinfo",
13
+ "typeRoots": ["../node_modules/@types", "./node_modules/@types", "../../node_modules/@types"],
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
17
+ "exclude": ["node_modules", "dist"],
18
+ "references": [{ "path": "../common" }, { "path": "../converter" }, { "path": "../schemas" }]
19
+ }