@pdfme/ui 5.3.11-dev.7 → 5.3.11-dev.8

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 (34) hide show
  1. package/dist/index.es.js +5670 -5589
  2. package/dist/index.umd.js +121 -121
  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/constants.d.ts +1 -1
  9. package/dist/types/src/contexts.d.ts +1 -1
  10. package/dist/types/src/helper.d.ts +3 -3
  11. package/package.json +1 -1
  12. package/src/components/AppContextProvider.tsx +17 -10
  13. package/src/components/CtlBar.tsx +2 -2
  14. package/src/components/Designer/Canvas/Guides.tsx +4 -13
  15. package/src/components/Designer/Canvas/Moveable.tsx +15 -74
  16. package/src/components/Designer/Canvas/Selecto.tsx +9 -24
  17. package/src/components/Designer/Canvas/index.tsx +3 -56
  18. package/src/components/Designer/LeftSidebar.tsx +2 -2
  19. package/src/components/Designer/PluginIcon.tsx +14 -3
  20. package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +91 -16
  21. package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +12 -8
  22. package/src/components/Designer/RightSidebar/DetailView/index.tsx +64 -21
  23. package/src/components/Designer/RightSidebar/ListView/Item.tsx +30 -2
  24. package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +23 -4
  25. package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +20 -4
  26. package/src/components/Designer/index.tsx +14 -9
  27. package/src/components/Preview.tsx +4 -4
  28. package/src/components/Renderer.tsx +41 -35
  29. package/src/components/Root.tsx +1 -1
  30. package/src/constants.ts +1 -1
  31. package/src/contexts.ts +1 -1
  32. package/src/helper.ts +131 -38
  33. package/src/types/react-guides.d.ts +0 -22
  34. package/src/types/react-selecto.d.ts +0 -35
@@ -23,28 +23,63 @@ const AlignWidget = (props: PropPanelWidgetProps) => {
23
23
  const tgtPos = isVertical ? 'x' : 'y';
24
24
  const tgtSize = isVertical ? 'width' : 'height';
25
25
  const isSingle = ass.length === 1;
26
- const root = pageSize[tgtSize];
26
+ // Access pageSize property safely with proper type assertion
27
+ const root = pageSize && typeof pageSize === 'object' ?
28
+ (tgtSize === 'width' ?
29
+ (pageSize as unknown as { width: number }).width :
30
+ (pageSize as unknown as { height: number }).height) : 0;
27
31
 
28
- const min = isSingle ? 0 : Math.min(...ass.map((as) => as.position[tgtPos]));
29
- const max = isSingle ? root : Math.max(...ass.map((as) => as.position[tgtPos] + as[tgtSize]));
32
+ // Access position properties safely with proper type assertion
33
+ const min = isSingle ? 0 : Math.min(...ass.map((as) => {
34
+ // Safely access position property with proper type assertion
35
+ const position = as.position && typeof as.position === 'object' ?
36
+ (as.position as unknown as { x: number; y: number }) :
37
+ { x: 0, y: 0 };
38
+ return tgtPos === 'x' ? position.x : position.y;
39
+ }));
40
+ const max = isSingle ? root : Math.max(...ass.map((as) => {
41
+ // Safely access position and size properties with proper type assertion
42
+ const position = as.position && typeof as.position === 'object' ?
43
+ (as.position as unknown as { x: number; y: number }) :
44
+ { x: 0, y: 0 };
45
+ const posValue = tgtPos === 'x' ? position.x : position.y;
46
+
47
+ // Safely access width/height with proper type assertion
48
+ const asWithSize = as as unknown as { width?: number; height?: number };
49
+ const sizeValue = tgtSize === 'width' ?
50
+ (asWithSize.width || 0) :
51
+ (asWithSize.height || 0);
52
+
53
+ return posValue + sizeValue;
54
+ }));
30
55
 
31
56
  let basePos = min;
32
- let adjust = (_: number) => 0;
57
+ // Define adjust function with consistent parameter usage
58
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
+ let adjust = (_size: number): number => 0;
33
60
 
34
61
  if (['center', 'middle'].includes(type)) {
35
62
  basePos = (min + max) / 2;
36
- adjust = (num: number) => num / 2;
63
+ adjust = (size: number): number => size / 2;
37
64
  } else if (['right', 'bottom'].includes(type)) {
38
65
  basePos = max;
39
- adjust = (num: number) => num;
66
+ adjust = (size: number): number => size;
40
67
  }
41
68
 
42
69
  changeSchemas(
43
- ass.map((as) => ({
44
- key: `position.${tgtPos}`,
45
- value: round(basePos - adjust(as[tgtSize]), 2),
46
- schemaId: as.id,
47
- })),
70
+ ass.map((as) => {
71
+ // Safely access size property with proper type assertion
72
+ const asWithSize = as as unknown as { width?: number; height?: number; id: string };
73
+ const sizeValue = tgtSize === 'width' ?
74
+ (asWithSize.width || 0) :
75
+ (asWithSize.height || 0);
76
+
77
+ return {
78
+ key: `position.${tgtPos}`,
79
+ value: round(basePos - adjust(sizeValue), 2),
80
+ schemaId: asWithSize.id,
81
+ };
82
+ }),
48
83
  );
49
84
  };
50
85
 
@@ -55,23 +90,63 @@ const AlignWidget = (props: PropPanelWidgetProps) => {
55
90
  const isVertical = type === 'vertical';
56
91
  const tgtPos = isVertical ? 'y' : 'x';
57
92
  const tgtSize = isVertical ? 'height' : 'width';
58
- const min = Math.min(...ass.map((as) => as.position[tgtPos]));
59
- const max = Math.max(...ass.map((as) => as.position[tgtPos] + as[tgtSize]));
93
+
94
+ // Safely access position property with proper type assertion
95
+ const min = Math.min(...ass.map((as) => {
96
+ const position = as.position && typeof as.position === 'object' ?
97
+ (as.position as unknown as { x: number; y: number }) :
98
+ { x: 0, y: 0 };
99
+ return tgtPos === 'x' ? position.x : position.y;
100
+ }));
101
+
102
+ // Safely access position and size properties with proper type assertion
103
+ const max = Math.max(...ass.map((as) => {
104
+ const position = as.position && typeof as.position === 'object' ?
105
+ (as.position as unknown as { x: number; y: number }) :
106
+ { x: 0, y: 0 };
107
+ const posValue = tgtPos === 'x' ? position.x : position.y;
108
+
109
+ // Safely access width/height with proper type assertion
110
+ const asWithSize = as as unknown as { width?: number; height?: number };
111
+ const sizeValue = tgtSize === 'width' ?
112
+ (asWithSize.width || 0) :
113
+ (asWithSize.height || 0);
114
+
115
+ return posValue + sizeValue;
116
+ }));
60
117
 
61
118
  if (ass.length < 3) return;
62
119
 
63
120
  const boxPos = min;
64
121
  const boxSize = max - min;
65
- const sum = ass.reduce((acc, cur) => acc + cur[tgtSize], 0);
122
+ // Safely access size property with proper type assertion
123
+ const sum = ass.reduce((acc, cur) => {
124
+ const curWithSize = cur as unknown as { width?: number; height?: number };
125
+ const sizeValue = tgtSize === 'width' ?
126
+ (curWithSize.width || 0) :
127
+ (curWithSize.height || 0);
128
+ return acc + sizeValue;
129
+ }, 0);
66
130
  const remain = boxSize - sum;
67
131
  const unit = remain / (ass.length - 1);
68
132
 
69
133
  let prev = 0;
70
134
  changeSchemas(
71
135
  ass.map((as, index) => {
72
- prev += index === 0 ? 0 : ass[index - 1][tgtSize] + unit;
136
+ // Safely access size property of previous element with proper type assertion
137
+ const prevSize = index === 0 ? 0 : (() => {
138
+ const prevAs = ass[index - 1] as unknown as { width?: number; height?: number };
139
+ return tgtSize === 'width' ?
140
+ (prevAs.width || 0) :
141
+ (prevAs.height || 0);
142
+ })();
143
+
144
+ prev += index === 0 ? 0 : prevSize + unit;
73
145
  const value = round(boxPos + prev, 2);
74
- return { key: `position.${tgtPos}`, value, schemaId: as.id };
146
+
147
+ // Safely access id with proper type assertion
148
+ const asWithId = as as unknown as { id: string };
149
+ return { key: `position.${tgtPos}`, value, schemaId: asWithId.id };
75
150
  }),
76
151
  );
77
152
  };
@@ -16,10 +16,10 @@ const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
16
16
  const key = btn.key;
17
17
  const type = btn.type;
18
18
  const ids = activeElements.map((ae) => ae.id);
19
- const ass = schemas.filter((s) => ids.includes(s.id)) as SchemaForUI[];
19
+ const ass = schemas.filter((s) => ids.includes(s.id));
20
20
  changeSchemas(
21
- ass.map((s: Record<string, any>) => {
22
- const oldValue = s[key] ?? false;
21
+ ass.map((s: SchemaForUI) => {
22
+ const oldValue = Boolean((s as Record<string, unknown>)[key] ?? false);
23
23
  const newValue = type === 'boolean' ? !oldValue : btn.value;
24
24
  return { key, value: newValue, schemaId: s.id };
25
25
  }),
@@ -31,9 +31,13 @@ const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
31
31
  const type = btn.type;
32
32
  let active = false;
33
33
  const ids = activeElements.map((ae) => ae.id);
34
- const ass = schemas.filter((s) => ids.includes(s.id)) as SchemaForUI[];
35
- ass.forEach((s: Record<string, any>) => {
36
- active = type === 'boolean' ? (s[key] ?? false) : s[key] === btn.value;
34
+ const ass = schemas.filter((s) => ids.includes(s.id));
35
+ ass.forEach((s: SchemaForUI) => {
36
+ // Cast schema to Record to safely access dynamic properties
37
+ const schemaRecord = s as Record<string, unknown>;
38
+ active = type === 'boolean'
39
+ ? Boolean(schemaRecord[key] ?? false)
40
+ : schemaRecord[key] === btn.value;
37
41
  });
38
42
  return active;
39
43
  };
@@ -45,13 +49,13 @@ const ButtonGroupWidget = (props: PropPanelWidgetProps) => {
45
49
  const svgDataUrl = `data:image/svg+xml;utf8,${encodeURIComponent(
46
50
  replaceCurrentColor(svgString, token.colorText),
47
51
  )}`;
48
- return <img width={17} height={17} src={svgDataUrl} />;
52
+ return <img width={17} height={17} src={svgDataUrl} alt="" />;
49
53
  };
50
54
 
51
55
  return (
52
56
  <Form.Item>
53
57
  <Button.Group>
54
- {schema.buttons.map((btn: ButtonConfig, index: number) => {
58
+ {(schema.buttons as ButtonConfig[]).map((btn: ButtonConfig, index: number) => {
55
59
  const active = isActive(btn);
56
60
  return (
57
61
  <Button
@@ -76,8 +76,11 @@ const DetailView = (props: DetailViewProps) => {
76
76
  }, [activeSchema, pluginsRegistry, JSON.stringify(options)]);
77
77
 
78
78
  useEffect(() => {
79
- const values: any = { ...activeSchema };
80
- values.editable = !values.readOnly;
79
+ // Create a type-safe copy of the schema with editable property
80
+ const values: Record<string, unknown> = { ...activeSchema };
81
+ // Safely access and set properties
82
+ const readOnly = typeof values.readOnly === 'boolean' ? values.readOnly : false;
83
+ values.editable = !readOnly;
81
84
  form.setValues(values);
82
85
  }, [activeSchema, form]);
83
86
 
@@ -96,14 +99,26 @@ const DetailView = (props: DetailViewProps) => {
96
99
  };
97
100
  }, [schemasList, activeSchema]);
98
101
 
99
- const uniqueSchemaName = useRef((value: string): boolean => true);
102
+ // Reference to a function that validates schema name uniqueness
103
+ const uniqueSchemaName = useRef(
104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
105
+ (_unused: string): boolean => true
106
+ );
100
107
 
101
- const validateUniqueSchemaName = (_: any, value: string): boolean =>
102
- uniqueSchemaName.current(value);
108
+ // Use proper type for validator function parameter
109
+ const validateUniqueSchemaName = (
110
+ _: unknown,
111
+ value: string
112
+ ): boolean => uniqueSchemaName.current(value);
103
113
 
104
- const handleWatch = debounce((formSchema: any) => {
105
- const formAndSchemaValuesDiffer = (formValue: any, schemaValue: any): boolean => {
106
- if (typeof formValue === 'object') {
114
+ // Use explicit type for debounce function that matches the expected signature
115
+ const handleWatch = debounce(function(...args: unknown[]) {
116
+ const formSchema = args[0] as Record<string, unknown>;
117
+ const formAndSchemaValuesDiffer = (
118
+ formValue: unknown,
119
+ schemaValue: unknown
120
+ ): boolean => {
121
+ if (typeof formValue === 'object' && formValue !== null) {
107
122
  return JSON.stringify(formValue) !== JSON.stringify(schemaValue);
108
123
  }
109
124
  return formValue !== schemaValue;
@@ -114,7 +129,7 @@ const DetailView = (props: DetailViewProps) => {
114
129
  if (['id', 'content'].includes(key)) continue;
115
130
 
116
131
  let value = formSchema[key];
117
- if (formAndSchemaValuesDiffer(value, (activeSchema as any)[key])) {
132
+ if (formAndSchemaValuesDiffer(value, (activeSchema as Record<string, unknown>)[key])) {
118
133
  // FIXME memo: https://github.com/pdfme/pdfme/pull/367#issuecomment-1857468274
119
134
  if (value === null && ['rotate', 'opacity'].includes(key)) {
120
135
  value = undefined;
@@ -154,21 +169,47 @@ const DetailView = (props: DetailViewProps) => {
154
169
  }
155
170
  }, 100);
156
171
 
172
+ // Find the active plugin with proper type safety
157
173
  const activePlugin = Object.values(pluginsRegistry).find(
158
- (plugin) => plugin?.propPanel.defaultSchema.type === activeSchema.type,
159
- )!;
174
+ (plugin) => {
175
+ if (!plugin || typeof plugin !== 'object') return false;
176
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
177
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object') return false;
178
+
179
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
180
+ return 'type' in defaultSchema &&
181
+ typeof defaultSchema.type === 'string' &&
182
+ defaultSchema.type === activeSchema.type;
183
+ }
184
+ );
160
185
 
161
- const activePropPanelSchema = activePlugin?.propPanel.schema;
186
+ // Safely access the propPanel schema
187
+ const activePropPanelSchema = activePlugin?.propPanel?.schema;
162
188
  if (!activePropPanelSchema) {
163
189
  console.error(`[@pdfme/ui] No propPanel.schema for ${activeSchema.type}.
164
190
  Check this document: https://pdfme.com/docs/custom-schemas`);
165
191
  }
166
192
 
167
- const typeOptions = Object.entries(pluginsRegistry).map(([label, value]) => ({
168
- label,
169
- value: value?.propPanel.defaultSchema.type,
170
- }));
171
- const defaultSchema = activePlugin.propPanel.defaultSchema;
193
+ // Create type-safe options for the type dropdown
194
+ const typeOptions = Object.entries(pluginsRegistry).map(([label, value]) => {
195
+ // Default value if plugin is invalid
196
+ const defaultValue = { label, value: undefined };
197
+
198
+ // Skip invalid plugins
199
+ if (!value || typeof value !== 'object') return defaultValue;
200
+ if (!value.propPanel || typeof value.propPanel !== 'object') return defaultValue;
201
+ if (!value.propPanel.defaultSchema || typeof value.propPanel.defaultSchema !== 'object') return defaultValue;
202
+
203
+ // Safely extract the type
204
+ const defaultSchema = value.propPanel.defaultSchema as Record<string, unknown>;
205
+ const schemaType = 'type' in defaultSchema && typeof defaultSchema.type === 'string'
206
+ ? defaultSchema.type
207
+ : undefined;
208
+
209
+ return { label, value: schemaType };
210
+ });
211
+ // Safely access the default schema with proper null checking
212
+ const defaultSchema = activePlugin?.propPanel?.defaultSchema || {};
172
213
 
173
214
  const propPanelSchema: PropPanelSchema = {
174
215
  type: 'object',
@@ -199,7 +240,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
199
240
  title: i18n('editable'),
200
241
  type: 'boolean',
201
242
  span: 8,
202
- hidden: defaultSchema?.readOnly !== undefined,
243
+ hidden: typeof (defaultSchema as Record<string, unknown>).readOnly !== 'undefined',
203
244
  },
204
245
  required: {
205
246
  title: i18n('required'),
@@ -237,7 +278,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
237
278
  title: i18n('rotate'),
238
279
  type: 'number',
239
280
  widget: 'inputNumber',
240
- disabled: defaultSchema?.rotate === undefined,
281
+ disabled: typeof (defaultSchema as Record<string, unknown>).rotate === 'undefined',
241
282
  max: 360,
242
283
  props: { min: 0 },
243
284
  span: 6,
@@ -246,7 +287,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
246
287
  title: i18n('opacity'),
247
288
  type: 'number',
248
289
  widget: 'inputNumber',
249
- disabled: defaultSchema?.opacity === undefined,
290
+ disabled: typeof (defaultSchema as Record<string, unknown>).opacity === 'undefined',
250
291
  props: { step: 0.1, min: 0, max: 1 },
251
292
  span: 6,
252
293
  },
@@ -254,7 +295,9 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
254
295
  };
255
296
 
256
297
  if (typeof activePropPanelSchema === 'function') {
257
- const { schemasList: _, ...propPanelProps } = props;
298
+ // Create a new object without the schemasList property
299
+ const { size, schemas, pageSize, changeSchemas, activeElements, deselectSchema, activeSchema } = props;
300
+ const propPanelProps = { size, schemas, pageSize, changeSchemas, activeElements, deselectSchema, activeSchema };
258
301
 
259
302
  const apps =
260
303
  activePropPanelSchema({
@@ -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,7 +17,7 @@ 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
23
  const _cache = new Map();
@@ -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
  });