@pdfme/ui 5.3.10 → 5.3.11-dev.10

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 (35) hide show
  1. package/dist/index.es.js +9028 -8913
  2. package/dist/index.umd.js +125 -125
  3. package/dist/types/src/components/Designer/Canvas/Guides.d.ts +3 -9
  4. package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +2 -49
  5. package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +5 -16
  6. package/dist/types/src/components/Designer/PluginIcon.d.ts +2 -2
  7. package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +17 -0
  8. package/dist/types/src/components/Renderer.d.ts +1 -10
  9. package/dist/types/src/constants.d.ts +1 -1
  10. package/dist/types/src/contexts.d.ts +1 -1
  11. package/dist/types/src/helper.d.ts +3 -4
  12. package/package.json +2 -2
  13. package/src/components/AppContextProvider.tsx +17 -10
  14. package/src/components/CtlBar.tsx +2 -2
  15. package/src/components/Designer/Canvas/Guides.tsx +4 -13
  16. package/src/components/Designer/Canvas/Moveable.tsx +15 -74
  17. package/src/components/Designer/Canvas/Selecto.tsx +9 -24
  18. package/src/components/Designer/Canvas/index.tsx +51 -72
  19. package/src/components/Designer/LeftSidebar.tsx +2 -2
  20. package/src/components/Designer/PluginIcon.tsx +14 -3
  21. package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +91 -16
  22. package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +12 -8
  23. package/src/components/Designer/RightSidebar/DetailView/index.tsx +159 -48
  24. package/src/components/Designer/RightSidebar/ListView/Item.tsx +30 -2
  25. package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +23 -4
  26. package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +20 -4
  27. package/src/components/Designer/index.tsx +14 -9
  28. package/src/components/Preview.tsx +5 -5
  29. package/src/components/Renderer.tsx +42 -36
  30. package/src/components/Root.tsx +1 -1
  31. package/src/constants.ts +1 -1
  32. package/src/contexts.ts +1 -1
  33. package/src/helper.ts +131 -38
  34. package/src/types/react-guides.d.ts +0 -22
  35. package/src/types/react-selecto.d.ts +0 -35
@@ -1,11 +1,12 @@
1
1
  import { useForm } from 'form-render';
2
2
  import React, { useRef, useContext, useState, useEffect } from 'react';
3
3
  import type {
4
- ChangeSchemaItem,
5
4
  Dict,
5
+ ChangeSchemaItem,
6
6
  SchemaForUI,
7
7
  PropPanelWidgetProps,
8
8
  PropPanelSchema,
9
+ Schema,
9
10
  } from '@pdfme/common';
10
11
  import type { SidebarProps } from '../../../../types.js';
11
12
  import { Menu } from 'lucide-react';
@@ -44,6 +45,12 @@ const DetailView = (props: DetailViewProps) => {
44
45
  const i18n = useContext(I18nContext);
45
46
  const pluginsRegistry = useContext(PluginsRegistry);
46
47
  const options = useContext(OptionsContext);
48
+
49
+ // Define a type-safe i18n function that accepts string keys
50
+ const typedI18n = (key: string): string => {
51
+ // Use a type assertion to handle the union type constraint
52
+ return typeof i18n === 'function' ? i18n(key as keyof Dict) : key;
53
+ };
47
54
 
48
55
  const [widgets, setWidgets] = useState<{
49
56
  [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element;
@@ -66,7 +73,7 @@ const DetailView = (props: DetailViewProps) => {
66
73
  {...props}
67
74
  options={options}
68
75
  theme={token}
69
- i18n={i18n as (key: keyof Dict | string) => string}
76
+ i18n={typedI18n}
70
77
  widget={widgetValue}
71
78
  />
72
79
  );
@@ -76,8 +83,11 @@ const DetailView = (props: DetailViewProps) => {
76
83
  }, [activeSchema, pluginsRegistry, JSON.stringify(options)]);
77
84
 
78
85
  useEffect(() => {
79
- const values: any = { ...activeSchema };
80
- values.editable = !values.readOnly;
86
+ // Create a type-safe copy of the schema with editable property
87
+ const values: Record<string, unknown> = { ...activeSchema };
88
+ // Safely access and set properties
89
+ const readOnly = typeof values.readOnly === 'boolean' ? values.readOnly : false;
90
+ values.editable = !readOnly;
81
91
  form.setValues(values);
82
92
  }, [activeSchema, form]);
83
93
 
@@ -96,14 +106,26 @@ const DetailView = (props: DetailViewProps) => {
96
106
  };
97
107
  }, [schemasList, activeSchema]);
98
108
 
99
- const uniqueSchemaName = useRef((value: string): boolean => true);
109
+ // Reference to a function that validates schema name uniqueness
110
+ const uniqueSchemaName = useRef(
111
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
112
+ (_unused: string): boolean => true
113
+ );
100
114
 
101
- const validateUniqueSchemaName = (_: any, value: string): boolean =>
102
- uniqueSchemaName.current(value);
115
+ // Use proper type for validator function parameter
116
+ const validateUniqueSchemaName = (
117
+ _: unknown,
118
+ value: string
119
+ ): boolean => uniqueSchemaName.current(value);
103
120
 
104
- const handleWatch = debounce((formSchema: any) => {
105
- const formAndSchemaValuesDiffer = (formValue: any, schemaValue: any): boolean => {
106
- if (typeof formValue === 'object') {
121
+ // Use explicit type for debounce function that matches the expected signature
122
+ const handleWatch = debounce(function(...args: unknown[]) {
123
+ const formSchema = args[0] as Record<string, unknown>;
124
+ const formAndSchemaValuesDiffer = (
125
+ formValue: unknown,
126
+ schemaValue: unknown
127
+ ): boolean => {
128
+ if (typeof formValue === 'object' && formValue !== null) {
107
129
  return JSON.stringify(formValue) !== JSON.stringify(schemaValue);
108
130
  }
109
131
  return formValue !== schemaValue;
@@ -114,7 +136,7 @@ const DetailView = (props: DetailViewProps) => {
114
136
  if (['id', 'content'].includes(key)) continue;
115
137
 
116
138
  let value = formSchema[key];
117
- if (formAndSchemaValuesDiffer(value, (activeSchema as any)[key])) {
139
+ if (formAndSchemaValuesDiffer(value, (activeSchema as Record<string, unknown>)[key])) {
118
140
  // FIXME memo: https://github.com/pdfme/pdfme/pull/367#issuecomment-1857468274
119
141
  if (value === null && ['rotate', 'opacity'].includes(key)) {
120
142
  value = undefined;
@@ -154,28 +176,98 @@ const DetailView = (props: DetailViewProps) => {
154
176
  }
155
177
  }, 100);
156
178
 
179
+ // Find the active plugin with proper type safety
157
180
  const activePlugin = Object.values(pluginsRegistry).find(
158
- (plugin) => plugin?.propPanel.defaultSchema.type === activeSchema.type,
159
- )!;
181
+ (plugin) => {
182
+ if (!plugin || typeof plugin !== 'object') return false;
183
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
184
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object') return false;
185
+
186
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
187
+ return 'type' in defaultSchema &&
188
+ typeof defaultSchema.type === 'string' &&
189
+ defaultSchema.type === activeSchema.type;
190
+ }
191
+ );
160
192
 
161
- const activePropPanelSchema = activePlugin?.propPanel.schema;
193
+ // Safely access the propPanel schema
194
+ const activePropPanelSchema = activePlugin?.propPanel?.schema;
162
195
  if (!activePropPanelSchema) {
163
196
  console.error(`[@pdfme/ui] No propPanel.schema for ${activeSchema.type}.
164
197
  Check this document: https://pdfme.com/docs/custom-schemas`);
165
198
  }
166
199
 
167
- const typeOptions = Object.entries(pluginsRegistry).map(([label, value]) => ({
168
- label,
169
- value: value?.propPanel.defaultSchema.type,
170
- }));
171
- const defaultSchema = activePlugin.propPanel.defaultSchema;
200
+ // Create type-safe options for the type dropdown
201
+ // Create a type-safe options array for the dropdown
202
+ const typeOptions: Array<{ label: string; value: string | undefined }> = [];
203
+
204
+ // Safely populate the options array
205
+ Object.entries(pluginsRegistry).forEach(([label, value]) => {
206
+ // Skip invalid plugins
207
+ if (!value || typeof value !== 'object') {
208
+ typeOptions.push({ label, value: undefined });
209
+ return;
210
+ }
211
+
212
+ if (!('propPanel' in value) ||
213
+ !value.propPanel ||
214
+ typeof value.propPanel !== 'object') {
215
+ typeOptions.push({ label, value: undefined });
216
+ return;
217
+ }
218
+
219
+ if (!('defaultSchema' in value.propPanel) ||
220
+ !value.propPanel.defaultSchema ||
221
+ typeof value.propPanel.defaultSchema !== 'object') {
222
+ typeOptions.push({ label, value: undefined });
223
+ return;
224
+ }
225
+
226
+ // Safely extract the type
227
+ const defaultSchema = value.propPanel.defaultSchema as Record<string, unknown>;
228
+ let schemaType: string | undefined = undefined;
229
+
230
+ if ('type' in defaultSchema && typeof defaultSchema.type === 'string') {
231
+ schemaType = defaultSchema.type;
232
+ }
233
+
234
+ typeOptions.push({ label, value: schemaType });
235
+ });
236
+ // Create a safe empty schema as fallback
237
+ const emptySchema: Record<string, unknown> = {};
238
+
239
+ // Safely access the default schema with proper null checking
240
+ const defaultSchema: Record<string, unknown> =
241
+ activePlugin?.propPanel?.defaultSchema ?
242
+ // Create a safe copy of the schema
243
+ (() => {
244
+ // First check if the defaultSchema is an object
245
+ if (typeof activePlugin.propPanel.defaultSchema !== 'object' ||
246
+ activePlugin.propPanel.defaultSchema === null) {
247
+ return emptySchema;
248
+ }
249
+
250
+ // Create a safe copy
251
+ const result: Record<string, unknown> = {};
252
+
253
+ // Only copy properties that exist on the object
254
+ for (const key in activePlugin.propPanel.defaultSchema) {
255
+ if (Object.prototype.hasOwnProperty.call(activePlugin.propPanel.defaultSchema, key)) {
256
+ result[key] = (activePlugin.propPanel.defaultSchema as Record<string, unknown>)[key];
257
+ }
258
+ }
259
+
260
+ return result;
261
+ })() :
262
+ emptySchema;
172
263
 
264
+ // Create a type-safe schema object
173
265
  const propPanelSchema: PropPanelSchema = {
174
266
  type: 'object',
175
267
  column: 2,
176
268
  properties: {
177
269
  type: {
178
- title: i18n('type'),
270
+ title: typedI18n('type'),
179
271
  type: 'string',
180
272
  widget: 'select',
181
273
  props: { options: typeOptions },
@@ -183,32 +275,32 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
183
275
  span: 12,
184
276
  },
185
277
  name: {
186
- title: i18n('fieldName'),
278
+ title: typedI18n('fieldName'),
187
279
  type: 'string',
188
280
  required: true,
189
281
  span: 12,
190
282
  rules: [
191
283
  {
192
284
  validator: validateUniqueSchemaName,
193
- message: i18n('validation.uniqueName'),
285
+ message: typedI18n('validation.uniqueName'),
194
286
  },
195
287
  ],
196
288
  props: { autoComplete: 'off' },
197
289
  },
198
290
  editable: {
199
- title: i18n('editable'),
291
+ title: typedI18n('editable'),
200
292
  type: 'boolean',
201
293
  span: 8,
202
- hidden: defaultSchema?.readOnly !== undefined,
294
+ hidden: typeof defaultSchema.readOnly !== 'undefined',
203
295
  },
204
296
  required: {
205
- title: i18n('required'),
297
+ title: typedI18n('required'),
206
298
  type: 'boolean',
207
299
  span: 16,
208
300
  hidden: '{{!formData.editable}}',
209
301
  },
210
302
  '-': { type: 'void', widget: 'Divider' },
211
- align: { title: i18n('align'), type: 'void', widget: 'AlignWidget' },
303
+ align: { title: typedI18n('align'), type: 'void', widget: 'AlignWidget' },
212
304
  position: {
213
305
  type: 'object',
214
306
  widget: 'card',
@@ -218,7 +310,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
218
310
  },
219
311
  },
220
312
  width: {
221
- title: i18n('width'),
313
+ title: typedI18n('width'),
222
314
  type: 'number',
223
315
  widget: 'inputNumber',
224
316
  required: true,
@@ -226,7 +318,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
226
318
  props: { min: 0 },
227
319
  },
228
320
  height: {
229
- title: i18n('height'),
321
+ title: typedI18n('height'),
230
322
  type: 'number',
231
323
  widget: 'inputNumber',
232
324
  required: true,
@@ -234,46 +326,65 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
234
326
  props: { min: 0 },
235
327
  },
236
328
  rotate: {
237
- title: i18n('rotate'),
329
+ title: typedI18n('rotate'),
238
330
  type: 'number',
239
331
  widget: 'inputNumber',
240
- disabled: defaultSchema?.rotate === undefined,
332
+ disabled: typeof defaultSchema.rotate === 'undefined',
241
333
  max: 360,
242
334
  props: { min: 0 },
243
335
  span: 6,
244
336
  },
245
337
  opacity: {
246
- title: i18n('opacity'),
338
+ title: typedI18n('opacity'),
247
339
  type: 'number',
248
340
  widget: 'inputNumber',
249
- disabled: defaultSchema?.opacity === undefined,
341
+ disabled: typeof defaultSchema.opacity === 'undefined',
250
342
  props: { step: 0.1, min: 0, max: 1 },
251
343
  span: 6,
252
344
  },
253
345
  },
254
346
  };
255
347
 
348
+ // Create a safe copy of the properties
349
+ const safeProperties = { ...propPanelSchema.properties };
350
+
256
351
  if (typeof activePropPanelSchema === 'function') {
257
- const { schemasList: _, ...propPanelProps } = props;
352
+ // Create a new object without the schemasList property
353
+ const { size, schemas, pageSize, changeSchemas, activeElements, deselectSchema, activeSchema } = props;
354
+ const propPanelProps = { size, schemas, pageSize, changeSchemas, activeElements, deselectSchema, activeSchema };
258
355
 
259
- const apps =
260
- activePropPanelSchema({
261
- ...propPanelProps,
262
- options,
263
- theme: token,
264
- i18n: i18n as (key: keyof Dict | string) => string,
265
- }) || {};
356
+ // Use the typedI18n function to avoid type issues
357
+ const functionResult = activePropPanelSchema({
358
+ ...propPanelProps,
359
+ options,
360
+ theme: token,
361
+ i18n: typedI18n,
362
+ });
363
+
364
+ // Safely handle the result
365
+ const apps = functionResult && typeof functionResult === 'object' ? functionResult : {};
366
+
367
+ // Create a divider if needed
368
+ const dividerObj = Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } };
369
+
370
+ // Assign properties safely - use type assertion to satisfy TypeScript
266
371
  propPanelSchema.properties = {
267
- ...propPanelSchema.properties,
268
- ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }),
269
- ...apps,
372
+ ...safeProperties,
373
+ ...(dividerObj as Record<string, Partial<Schema>>),
374
+ ...(apps as Record<string, Partial<Schema>>),
270
375
  };
271
376
  } else {
272
- const apps = activePropPanelSchema || {};
377
+ // Handle non-function case
378
+ const apps = activePropPanelSchema && typeof activePropPanelSchema === 'object' ? activePropPanelSchema : {};
379
+
380
+ // Create a divider if needed
381
+ const dividerObj = Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } };
382
+
383
+ // Assign properties safely - use type assertion to satisfy TypeScript
273
384
  propPanelSchema.properties = {
274
- ...propPanelSchema.properties,
275
- ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }),
276
- ...apps,
385
+ ...safeProperties,
386
+ ...(dividerObj as Record<string, Partial<Schema>>),
387
+ ...(apps as Record<string, Partial<Schema>>),
277
388
  };
278
389
  }
279
390
 
@@ -292,7 +403,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
292
403
  icon={<Menu strokeWidth={1.5} size={20} />}
293
404
  />
294
405
  <Text strong style={{ textAlign: 'center', width: '100%' }}>
295
- {i18n('editField')}
406
+ {typedI18n('editField')}
296
407
  </Text>
297
408
  </div>
298
409
  <Divider style={{ marginTop: token.marginXS, marginBottom: token.marginXS }} />
@@ -6,28 +6,49 @@ import { Button, Typography } from 'antd';
6
6
 
7
7
  const { Text } = Typography;
8
8
 
9
+ // Define prop types for Item component
9
10
  interface Props {
11
+ /** Content to display in the item */
10
12
  value: React.ReactNode;
13
+ /** Optional icon to display */
11
14
  icon?: React.ReactNode;
15
+ /** Custom styles for the item */
12
16
  style?: React.CSSProperties;
17
+ /** Status indicator for the item */
13
18
  status?: 'is-warning' | 'is-danger';
19
+ /** Title attribute for the item */
14
20
  title?: string;
21
+ /** Whether the item is required */
15
22
  required?: boolean;
23
+ /** Whether the item is read-only */
16
24
  readOnly?: boolean;
25
+ /** Whether the item is being dragged as an overlay */
17
26
  dragOverlay?: boolean;
27
+ /** Click handler for the item */
18
28
  onClick?: () => void;
29
+ /** Mouse enter handler */
19
30
  onMouseEnter?: () => void;
31
+ /** Mouse leave handler */
20
32
  onMouseLeave?: () => void;
33
+ /** Whether the item is currently being dragged */
21
34
  dragging?: boolean;
35
+ /** Whether items are being sorted */
22
36
  sorting?: boolean;
37
+ /** CSS transition value */
23
38
  transition?: string;
39
+ /** Transform data for the item */
24
40
  transform?: { x: number; y: number; scaleX: number; scaleY: number } | null;
41
+ /** Whether to fade the item in */
25
42
  fadeIn?: boolean;
43
+ /** Drag listeners from dnd-kit */
26
44
  listeners?: DraggableSyntheticListeners;
27
45
  }
46
+ // Using React.memo and forwardRef for optimized rendering
47
+ // Using TypeScript interface for prop validation instead of PropTypes
28
48
  const Item = React.memo(
49
+ /* eslint-disable react/prop-types */
29
50
  React.forwardRef<HTMLLIElement, Props>(
30
- (
51
+ function Item(
31
52
  {
32
53
  icon,
33
54
  value,
@@ -40,16 +61,20 @@ const Item = React.memo(
40
61
  onClick,
41
62
  onMouseEnter,
42
63
  onMouseLeave,
64
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
65
  dragging,
66
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44
67
  fadeIn,
45
68
  listeners,
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
70
  sorting,
47
71
  transition,
48
72
  transform,
49
73
  ...props
50
74
  },
51
75
  ref,
52
- ) => {
76
+ ) {
77
+ /* eslint-enable react/prop-types */
53
78
  const i18n = useContext(I18nContext);
54
79
 
55
80
  useEffect(() => {
@@ -129,4 +154,7 @@ const Item = React.memo(
129
154
  ),
130
155
  );
131
156
 
157
+ // Set display name for debugging
158
+ Item.displayName = 'Item';
159
+
132
160
  export default Item;
@@ -59,12 +59,31 @@ const SelectableSortableContainer = (
59
59
  };
60
60
 
61
61
  const getPluginIcon = (inSchema: string | SchemaForUI): ReactNode => {
62
+ // Get schema by ID or use directly
62
63
  const thisSchema =
63
64
  typeof inSchema === 'string' ? schemas.find((schema) => schema.id === inSchema) : inSchema;
64
-
65
- const [pluginLabel, activePlugin] = Object.entries(pluginsRegistry).find(
66
- ([label, plugin]) => plugin?.propPanel.defaultSchema.type === thisSchema?.type,
67
- )!;
65
+
66
+ if (!thisSchema) return <></>;
67
+
68
+ // Safely extract schema type
69
+ const schemaType = typeof thisSchema.type === 'string' ? thisSchema.type : '';
70
+
71
+ // Find matching plugin with type-safe approach
72
+ const pluginEntry = Object.entries(pluginsRegistry).find(
73
+ ([, plugin]) => {
74
+ if (!plugin || typeof plugin !== 'object') return false;
75
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
76
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object') return false;
77
+
78
+ // Use Record<string, unknown> to safely access properties
79
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
80
+ return 'type' in defaultSchema &&
81
+ typeof defaultSchema.type === 'string' &&
82
+ defaultSchema.type === schemaType;
83
+ }
84
+ );
85
+
86
+ const [pluginLabel, activePlugin] = pluginEntry || ['', undefined];
68
87
 
69
88
  if (!activePlugin) {
70
89
  return <></>;
@@ -39,12 +39,28 @@ const SelectableSortableItem = ({
39
39
 
40
40
  const newListeners = {
41
41
  ...listeners,
42
- onClick: (event: any) => onSelect(schema.id, event.shiftKey),
42
+ onClick: (event: React.MouseEvent) => onSelect(schema.id, event.shiftKey),
43
43
  };
44
44
 
45
- const [pluginLabel, thisPlugin] = Object.entries(pluginsRegistry).find(
46
- ([label, plugin]) => plugin?.propPanel.defaultSchema.type === schema.type,
47
- )!;
45
+ // Safely extract schema type
46
+ const schemaType = typeof schema.type === 'string' ? schema.type : '';
47
+
48
+ // Find matching plugin with type-safe approach
49
+ const pluginEntry = Object.entries(pluginsRegistry).find(
50
+ ([, plugin]) => {
51
+ if (!plugin || typeof plugin !== 'object') return false;
52
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
53
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object') return false;
54
+
55
+ // Use Record<string, unknown> to safely access properties
56
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
57
+ return 'type' in defaultSchema &&
58
+ typeof defaultSchema.type === 'string' &&
59
+ defaultSchema.type === schemaType;
60
+ }
61
+ );
62
+
63
+ const [pluginLabel, thisPlugin] = pluginEntry || ['', undefined];
48
64
 
49
65
  let status: undefined | 'is-warning' | 'is-danger';
50
66
  if (!schema.name) {
@@ -24,7 +24,7 @@ import {
24
24
  template2SchemasList,
25
25
  getPagesScrollTopByIndex,
26
26
  changeSchemas as _changeSchemas,
27
- getMaxZoom,
27
+ useMaxZoom,
28
28
  } from '../../helper.js';
29
29
  import { useUIPreProcessor, useScrollPageCursor, useInitEvents } from '../../hooks.js';
30
30
  import Root from '../Root.js';
@@ -64,7 +64,7 @@ const TemplateEditor = ({
64
64
  const i18n = useContext(I18nContext);
65
65
  const pluginsRegistry = useContext(PluginsRegistry);
66
66
  const options = useContext(OptionsContext);
67
- const maxZoom = getMaxZoom();
67
+ const maxZoom = useMaxZoom();
68
68
 
69
69
  const [hoveringSchemaId, setHoveringSchemaId] = useState<string | null>(null);
70
70
  const [activeElements, setActiveElements] = useState<HTMLElement[]>([]);
@@ -227,12 +227,13 @@ const TemplateEditor = ({
227
227
  onChangeTemplate(newTemplate);
228
228
  await updateTemplate(newTemplate);
229
229
  void refresh(newTemplate);
230
- setTimeout(
231
- () =>
232
- canvasRef.current &&
233
- ((canvasRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, newPageCursor, scale)),
234
- 0),
235
- );
230
+
231
+ // Use setTimeout to update scroll position after render
232
+ setTimeout(() => {
233
+ if (canvasRef.current) {
234
+ canvasRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, newPageCursor, scale);
235
+ }
236
+ }, 0);
236
237
  };
237
238
 
238
239
  const handleRemovePage = () => {
@@ -262,6 +263,7 @@ const TemplateEditor = ({
262
263
  };
263
264
 
264
265
  if (error) {
266
+ // Pass the error directly to ErrorScreen
265
267
  return <ErrorScreen size={size} error={error} />;
266
268
  }
267
269
  const pageManipulation = isBlankPdf(template.basePdf)
@@ -309,6 +311,7 @@ const TemplateEditor = ({
309
311
  pageNum={schemasList.length}
310
312
  setPageCursor={(p) => {
311
313
  if (!canvasRef.current) return;
314
+ // Update scroll position and state
312
315
  canvasRef.current.scrollTop = getPagesScrollTopByIndex(pageSizes, p, scale);
313
316
  setPageCursor(p);
314
317
  onEditEnd();
@@ -331,7 +334,9 @@ const TemplateEditor = ({
331
334
  onSortEnd={onSortEnd}
332
335
  onEdit={(id) => {
333
336
  const editingElem = document.getElementById(id);
334
- editingElem && onEdit([editingElem]);
337
+ if (editingElem) {
338
+ onEdit([editingElem]);
339
+ }
335
340
  }}
336
341
  onEditEnd={onEditEnd}
337
342
  deselectSchema={onEditEnd}
@@ -17,10 +17,10 @@ import Paper from './Paper.js';
17
17
  import Renderer from './Renderer.js';
18
18
  import { useUIPreProcessor, useScrollPageCursor } from '../hooks.js';
19
19
  import { FontContext } from '../contexts.js';
20
- import { template2SchemasList, getPagesScrollTopByIndex, getMaxZoom } from '../helper.js';
20
+ import { template2SchemasList, getPagesScrollTopByIndex, useMaxZoom } from '../helper.js';
21
21
  import { theme } from 'antd';
22
22
 
23
- const _cache = new Map();
23
+ const _cache = new Map<string | number, unknown>();
24
24
 
25
25
  const Preview = ({
26
26
  template,
@@ -34,7 +34,7 @@ const Preview = ({
34
34
  const { token } = theme.useToken();
35
35
 
36
36
  const font = useContext(FontContext);
37
- const maxZoom = getMaxZoom();
37
+ const maxZoom = useMaxZoom();
38
38
 
39
39
  const containerRef = useRef<HTMLDivElement>(null);
40
40
  const paperRefs = useRef<HTMLDivElement[]>([]);
@@ -98,7 +98,7 @@ const Preview = ({
98
98
  const handleChangeInput = ({ name, value }: { name: string; value: string }) =>
99
99
  onChangeInput && onChangeInput({ index: unitCursor, name, value });
100
100
 
101
- const handleOnChangeRenderer = (args: { key: string; value: any }[], schema: SchemaForUI) => {
101
+ const handleOnChangeRenderer = (args: { key: string; value: unknown }[], schema: SchemaForUI) => {
102
102
  let isNeedInit = false;
103
103
  args.forEach(({ key: _key, value }) => {
104
104
  if (_key === 'content') {
@@ -112,7 +112,7 @@ const Preview = ({
112
112
  const targetSchema = schemasList[pageCursor].find((s) => s.id === schema.id) as SchemaForUI;
113
113
  if (!targetSchema) return;
114
114
 
115
- // @ts-ignore
115
+ // @ts-expect-error Dynamic property assignment
116
116
  targetSchema[_key] = value as string;
117
117
  }
118
118
  });