@pdfme/ui 5.3.11-dev.9 → 5.3.12

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.
@@ -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';
@@ -45,6 +46,12 @@ const DetailView = (props: DetailViewProps) => {
45
46
  const pluginsRegistry = useContext(PluginsRegistry);
46
47
  const options = useContext(OptionsContext);
47
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
+ };
54
+
48
55
  const [widgets, setWidgets] = useState<{
49
56
  [key: string]: (props: PropPanelWidgetProps) => React.JSX.Element;
50
57
  }>({});
@@ -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
  );
@@ -102,22 +109,17 @@ const DetailView = (props: DetailViewProps) => {
102
109
  // Reference to a function that validates schema name uniqueness
103
110
  const uniqueSchemaName = useRef(
104
111
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
105
- (_unused: string): boolean => true
112
+ (_unused: string): boolean => true,
106
113
  );
107
114
 
108
115
  // Use proper type for validator function parameter
109
- const validateUniqueSchemaName = (
110
- _: unknown,
111
- value: string
112
- ): boolean => uniqueSchemaName.current(value);
116
+ const validateUniqueSchemaName = (_: unknown, value: string): boolean =>
117
+ uniqueSchemaName.current(value);
113
118
 
114
119
  // Use explicit type for debounce function that matches the expected signature
115
- const handleWatch = debounce(function(...args: unknown[]) {
120
+ const handleWatch = debounce(function (...args: unknown[]) {
116
121
  const formSchema = args[0] as Record<string, unknown>;
117
- const formAndSchemaValuesDiffer = (
118
- formValue: unknown,
119
- schemaValue: unknown
120
- ): boolean => {
122
+ const formAndSchemaValuesDiffer = (formValue: unknown, schemaValue: unknown): boolean => {
121
123
  if (typeof formValue === 'object' && formValue !== null) {
122
124
  return JSON.stringify(formValue) !== JSON.stringify(schemaValue);
123
125
  }
@@ -170,18 +172,19 @@ const DetailView = (props: DetailViewProps) => {
170
172
  }, 100);
171
173
 
172
174
  // Find the active plugin with proper type safety
173
- const activePlugin = Object.values(pluginsRegistry).find(
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
- );
175
+ const activePlugin = Object.values(pluginsRegistry).find((plugin) => {
176
+ if (!plugin || typeof plugin !== 'object') return false;
177
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
178
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object')
179
+ return false;
180
+
181
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
182
+ return (
183
+ 'type' in defaultSchema &&
184
+ typeof defaultSchema.type === 'string' &&
185
+ defaultSchema.type === activeSchema.type
186
+ );
187
+ });
185
188
 
186
189
  // Safely access the propPanel schema
187
190
  const activePropPanelSchema = activePlugin?.propPanel?.schema;
@@ -191,32 +194,77 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
191
194
  }
192
195
 
193
196
  // 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
-
197
+ // Create a type-safe options array for the dropdown
198
+ const typeOptions: Array<{ label: string; value: string | undefined }> = [];
199
+
200
+ // Safely populate the options array
201
+ Object.entries(pluginsRegistry).forEach(([label, value]) => {
198
202
  // 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
+ if (!value || typeof value !== 'object') {
204
+ typeOptions.push({ label, value: undefined });
205
+ return;
206
+ }
207
+
208
+ if (!('propPanel' in value) || !value.propPanel || typeof value.propPanel !== 'object') {
209
+ typeOptions.push({ label, value: undefined });
210
+ return;
211
+ }
212
+
213
+ if (
214
+ !('defaultSchema' in value.propPanel) ||
215
+ !value.propPanel.defaultSchema ||
216
+ typeof value.propPanel.defaultSchema !== 'object'
217
+ ) {
218
+ typeOptions.push({ label, value: undefined });
219
+ return;
220
+ }
221
+
203
222
  // Safely extract the type
204
223
  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 };
224
+ let schemaType: string | undefined = undefined;
225
+
226
+ if ('type' in defaultSchema && typeof defaultSchema.type === 'string') {
227
+ schemaType = defaultSchema.type;
228
+ }
229
+
230
+ typeOptions.push({ label, value: schemaType });
210
231
  });
232
+ // Create a safe empty schema as fallback
233
+ const emptySchema: Record<string, unknown> = {};
234
+
211
235
  // Safely access the default schema with proper null checking
212
- const defaultSchema = activePlugin?.propPanel?.defaultSchema || {};
236
+ const defaultSchema: Record<string, unknown> = activePlugin?.propPanel?.defaultSchema
237
+ ? // Create a safe copy of the schema
238
+ (() => {
239
+ // First check if the defaultSchema is an object
240
+ if (
241
+ typeof activePlugin.propPanel.defaultSchema !== 'object' ||
242
+ activePlugin.propPanel.defaultSchema === null
243
+ ) {
244
+ return emptySchema;
245
+ }
213
246
 
247
+ // Create a safe copy
248
+ const result: Record<string, unknown> = {};
249
+
250
+ // Only copy properties that exist on the object
251
+ for (const key in activePlugin.propPanel.defaultSchema) {
252
+ if (Object.prototype.hasOwnProperty.call(activePlugin.propPanel.defaultSchema, key)) {
253
+ result[key] = (activePlugin.propPanel.defaultSchema as Record<string, unknown>)[key];
254
+ }
255
+ }
256
+
257
+ return result;
258
+ })()
259
+ : emptySchema;
260
+
261
+ // Create a type-safe schema object
214
262
  const propPanelSchema: PropPanelSchema = {
215
263
  type: 'object',
216
264
  column: 2,
217
265
  properties: {
218
266
  type: {
219
- title: i18n('type'),
267
+ title: typedI18n('type'),
220
268
  type: 'string',
221
269
  widget: 'select',
222
270
  props: { options: typeOptions },
@@ -224,32 +272,32 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
224
272
  span: 12,
225
273
  },
226
274
  name: {
227
- title: i18n('fieldName'),
275
+ title: typedI18n('fieldName'),
228
276
  type: 'string',
229
277
  required: true,
230
278
  span: 12,
231
279
  rules: [
232
280
  {
233
281
  validator: validateUniqueSchemaName,
234
- message: i18n('validation.uniqueName'),
282
+ message: typedI18n('validation.uniqueName'),
235
283
  },
236
284
  ],
237
285
  props: { autoComplete: 'off' },
238
286
  },
239
287
  editable: {
240
- title: i18n('editable'),
288
+ title: typedI18n('editable'),
241
289
  type: 'boolean',
242
290
  span: 8,
243
- hidden: typeof (defaultSchema as Record<string, unknown>).readOnly !== 'undefined',
291
+ hidden: typeof defaultSchema.readOnly !== 'undefined',
244
292
  },
245
293
  required: {
246
- title: i18n('required'),
294
+ title: typedI18n('required'),
247
295
  type: 'boolean',
248
296
  span: 16,
249
297
  hidden: '{{!formData.editable}}',
250
298
  },
251
299
  '-': { type: 'void', widget: 'Divider' },
252
- align: { title: i18n('align'), type: 'void', widget: 'AlignWidget' },
300
+ align: { title: typedI18n('align'), type: 'void', widget: 'AlignWidget' },
253
301
  position: {
254
302
  type: 'object',
255
303
  widget: 'card',
@@ -259,7 +307,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
259
307
  },
260
308
  },
261
309
  width: {
262
- title: i18n('width'),
310
+ title: typedI18n('width'),
263
311
  type: 'number',
264
312
  widget: 'inputNumber',
265
313
  required: true,
@@ -267,7 +315,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
267
315
  props: { min: 0 },
268
316
  },
269
317
  height: {
270
- title: i18n('height'),
318
+ title: typedI18n('height'),
271
319
  type: 'number',
272
320
  widget: 'inputNumber',
273
321
  required: true,
@@ -275,48 +323,79 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
275
323
  props: { min: 0 },
276
324
  },
277
325
  rotate: {
278
- title: i18n('rotate'),
326
+ title: typedI18n('rotate'),
279
327
  type: 'number',
280
328
  widget: 'inputNumber',
281
- disabled: typeof (defaultSchema as Record<string, unknown>).rotate === 'undefined',
329
+ disabled: typeof defaultSchema.rotate === 'undefined',
282
330
  max: 360,
283
331
  props: { min: 0 },
284
332
  span: 6,
285
333
  },
286
334
  opacity: {
287
- title: i18n('opacity'),
335
+ title: typedI18n('opacity'),
288
336
  type: 'number',
289
337
  widget: 'inputNumber',
290
- disabled: typeof (defaultSchema as Record<string, unknown>).opacity === 'undefined',
338
+ disabled: typeof defaultSchema.opacity === 'undefined',
291
339
  props: { step: 0.1, min: 0, max: 1 },
292
340
  span: 6,
293
341
  },
294
342
  },
295
343
  };
296
344
 
345
+ // Create a safe copy of the properties
346
+ const safeProperties = { ...propPanelSchema.properties };
347
+
297
348
  if (typeof activePropPanelSchema === 'function') {
298
349
  // 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 };
350
+ const { size, schemas, pageSize, changeSchemas, activeElements, deselectSchema, activeSchema } =
351
+ props;
352
+ const propPanelProps = {
353
+ size,
354
+ schemas,
355
+ pageSize,
356
+ changeSchemas,
357
+ activeElements,
358
+ deselectSchema,
359
+ activeSchema,
360
+ };
301
361
 
302
- const apps =
303
- activePropPanelSchema({
304
- ...propPanelProps,
305
- options,
306
- theme: token,
307
- i18n: i18n as (key: keyof Dict | string) => string,
308
- }) || {};
362
+ // Use the typedI18n function to avoid type issues
363
+ const functionResult = activePropPanelSchema({
364
+ ...propPanelProps,
365
+ options,
366
+ theme: token,
367
+ i18n: typedI18n,
368
+ });
369
+
370
+ // Safely handle the result
371
+ const apps = functionResult && typeof functionResult === 'object' ? functionResult : {};
372
+
373
+ // Create a divider if needed
374
+ const dividerObj =
375
+ Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } };
376
+
377
+ // Assign properties safely - use type assertion to satisfy TypeScript
309
378
  propPanelSchema.properties = {
310
- ...propPanelSchema.properties,
311
- ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }),
312
- ...apps,
379
+ ...safeProperties,
380
+ ...(dividerObj as Record<string, Partial<Schema>>),
381
+ ...(apps as Record<string, Partial<Schema>>),
313
382
  };
314
383
  } else {
315
- const apps = activePropPanelSchema || {};
384
+ // Handle non-function case
385
+ const apps =
386
+ activePropPanelSchema && typeof activePropPanelSchema === 'object'
387
+ ? activePropPanelSchema
388
+ : {};
389
+
390
+ // Create a divider if needed
391
+ const dividerObj =
392
+ Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } };
393
+
394
+ // Assign properties safely - use type assertion to satisfy TypeScript
316
395
  propPanelSchema.properties = {
317
- ...propPanelSchema.properties,
318
- ...(Object.keys(apps).length === 0 ? {} : { '--': { type: 'void', widget: 'Divider' } }),
319
- ...apps,
396
+ ...safeProperties,
397
+ ...(dividerObj as Record<string, Partial<Schema>>),
398
+ ...(apps as Record<string, Partial<Schema>>),
320
399
  };
321
400
  }
322
401
 
@@ -335,7 +414,7 @@ Check this document: https://pdfme.com/docs/custom-schemas`);
335
414
  icon={<Menu strokeWidth={1.5} size={20} />}
336
415
  />
337
416
  <Text strong style={{ textAlign: 'center', width: '100%' }}>
338
- {i18n('editField')}
417
+ {typedI18n('editField')}
339
418
  </Text>
340
419
  </div>
341
420
  <Divider style={{ marginTop: token.marginXS, marginBottom: token.marginXS }} />
@@ -47,111 +47,109 @@ interface Props {
47
47
  // Using TypeScript interface for prop validation instead of PropTypes
48
48
  const Item = React.memo(
49
49
  /* eslint-disable react/prop-types */
50
- React.forwardRef<HTMLLIElement, Props>(
51
- function Item(
52
- {
53
- icon,
54
- value,
55
- status,
56
- title,
57
- required,
58
- readOnly,
59
- style,
60
- dragOverlay,
61
- onClick,
62
- onMouseEnter,
63
- onMouseLeave,
64
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
- dragging,
66
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
- fadeIn,
68
- listeners,
69
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
- sorting,
71
- transition,
72
- transform,
73
- ...props
74
- },
75
- ref,
76
- ) {
50
+ React.forwardRef<HTMLLIElement, Props>(function Item(
51
+ {
52
+ icon,
53
+ value,
54
+ status,
55
+ title,
56
+ required,
57
+ readOnly,
58
+ style,
59
+ dragOverlay,
60
+ onClick,
61
+ onMouseEnter,
62
+ onMouseLeave,
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ dragging,
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ fadeIn,
67
+ listeners,
68
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
69
+ sorting,
70
+ transition,
71
+ transform,
72
+ ...props
73
+ },
74
+ ref,
75
+ ) {
77
76
  /* eslint-enable react/prop-types */
78
- const i18n = useContext(I18nContext);
77
+ const i18n = useContext(I18nContext);
79
78
 
80
- useEffect(() => {
81
- if (!dragOverlay) {
82
- return;
83
- }
79
+ useEffect(() => {
80
+ if (!dragOverlay) {
81
+ return;
82
+ }
84
83
 
85
- document.body.style.cursor = 'grabbing';
84
+ document.body.style.cursor = 'grabbing';
86
85
 
87
- return () => {
88
- document.body.style.cursor = '';
89
- };
90
- }, [dragOverlay]);
86
+ return () => {
87
+ document.body.style.cursor = '';
88
+ };
89
+ }, [dragOverlay]);
91
90
 
92
- const { x, y, scaleX, scaleY } = transform || { x: 0, y: 0, scaleX: 1, scaleY: 1 };
91
+ const { x, y, scaleX, scaleY } = transform || { x: 0, y: 0, scaleX: 1, scaleY: 1 };
93
92
 
94
- return (
95
- <li
93
+ return (
94
+ <li
95
+ style={{
96
+ marginTop: 10,
97
+ transition,
98
+ transform: `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`,
99
+ }}
100
+ onMouseEnter={onMouseEnter}
101
+ onMouseLeave={onMouseLeave}
102
+ ref={ref}
103
+ >
104
+ <div
96
105
  style={{
97
- marginTop: 10,
98
- transition,
99
- transform: `translate(${x}px, ${y}px) scale(${scaleX}, ${scaleY})`,
106
+ display: 'flex',
107
+ alignItems: 'center',
108
+ cursor: 'pointer',
109
+ gap: '0.5rem',
110
+ ...style,
100
111
  }}
101
- onMouseEnter={onMouseEnter}
102
- onMouseLeave={onMouseLeave}
103
- ref={ref}
112
+ {...props}
113
+ onClick={() => onClick && onClick()}
104
114
  >
105
- <div
115
+ <Button
116
+ {...listeners}
106
117
  style={{
107
118
  display: 'flex',
108
119
  alignItems: 'center',
109
- cursor: 'pointer',
110
- gap: '0.5rem',
111
- ...style,
120
+ background: 'none',
121
+ boxShadow: 'none',
122
+ border: 'none',
123
+ paddingLeft: '0.25rem',
124
+ }}
125
+ icon={<GripVertical size={15} style={{ cursor: 'grab' }} />}
126
+ />
127
+ {icon}
128
+ <Text
129
+ style={{
130
+ overflow: 'hidden',
131
+ whiteSpace: 'nowrap',
132
+ textOverflow: 'ellipsis',
133
+ width: '100%',
112
134
  }}
113
- {...props}
114
- onClick={() => onClick && onClick()}
135
+ title={title || ''}
115
136
  >
116
- <Button
117
- {...listeners}
118
- style={{
119
- display: 'flex',
120
- alignItems: 'center',
121
- background: 'none',
122
- boxShadow: 'none',
123
- border: 'none',
124
- paddingLeft: '0.25rem',
125
- }}
126
- icon={<GripVertical size={15} style={{ cursor: 'grab' }} />}
127
- />
128
- {icon}
129
- <Text
130
- style={{
131
- overflow: 'hidden',
132
- whiteSpace: 'nowrap',
133
- textOverflow: 'ellipsis',
134
- width: '100%',
135
- }}
136
- title={title || ''}
137
- >
138
- {status === undefined ? (
139
- value
140
- ) : (
141
- <span style={{ display: 'flex', alignItems: 'center' }}>
142
- <CircleAlert size={15} style={{ marginRight: '0.25rem' }} />
143
- {status === 'is-warning' ? i18n('noKeyName') : value}
144
- {status === 'is-danger' ? i18n('notUniq') : ''}
145
- </span>
146
- )}
147
- </Text>
148
- {readOnly && <Lock size={15} style={{ marginRight: '0.5rem' }} />}
149
- {required && <span style={{ color: 'red', marginRight: '0.5rem' }}>*</span>}
150
- </div>
151
- </li>
152
- );
153
- },
154
- ),
137
+ {status === undefined ? (
138
+ value
139
+ ) : (
140
+ <span style={{ display: 'flex', alignItems: 'center' }}>
141
+ <CircleAlert size={15} style={{ marginRight: '0.25rem' }} />
142
+ {status === 'is-warning' ? i18n('noKeyName') : value}
143
+ {status === 'is-danger' ? i18n('notUniq') : ''}
144
+ </span>
145
+ )}
146
+ </Text>
147
+ {readOnly && <Lock size={15} style={{ marginRight: '0.5rem' }} />}
148
+ {required && <span style={{ color: 'red', marginRight: '0.5rem' }}>*</span>}
149
+ </div>
150
+ </li>
151
+ );
152
+ }),
155
153
  );
156
154
 
157
155
  // Set display name for debugging
@@ -62,27 +62,28 @@ const SelectableSortableContainer = (
62
62
  // Get schema by ID or use directly
63
63
  const thisSchema =
64
64
  typeof inSchema === 'string' ? schemas.find((schema) => schema.id === inSchema) : inSchema;
65
-
65
+
66
66
  if (!thisSchema) return <></>;
67
-
67
+
68
68
  // Safely extract schema type
69
69
  const schemaType = typeof thisSchema.type === 'string' ? thisSchema.type : '';
70
-
70
+
71
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
-
72
+ const pluginEntry = Object.entries(pluginsRegistry).find(([, plugin]) => {
73
+ if (!plugin || typeof plugin !== 'object') return false;
74
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
75
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object')
76
+ return false;
77
+
78
+ // Use Record<string, unknown> to safely access properties
79
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
80
+ return (
81
+ 'type' in defaultSchema &&
82
+ typeof defaultSchema.type === 'string' &&
83
+ defaultSchema.type === schemaType
84
+ );
85
+ });
86
+
86
87
  const [pluginLabel, activePlugin] = pluginEntry || ['', undefined];
87
88
 
88
89
  if (!activePlugin) {
@@ -44,22 +44,23 @@ const SelectableSortableItem = ({
44
44
 
45
45
  // Safely extract schema type
46
46
  const schemaType = typeof schema.type === 'string' ? schema.type : '';
47
-
47
+
48
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
-
49
+ const pluginEntry = Object.entries(pluginsRegistry).find(([, plugin]) => {
50
+ if (!plugin || typeof plugin !== 'object') return false;
51
+ if (!plugin.propPanel || typeof plugin.propPanel !== 'object') return false;
52
+ if (!plugin.propPanel.defaultSchema || typeof plugin.propPanel.defaultSchema !== 'object')
53
+ return false;
54
+
55
+ // Use Record<string, unknown> to safely access properties
56
+ const defaultSchema = plugin.propPanel.defaultSchema as Record<string, unknown>;
57
+ return (
58
+ 'type' in defaultSchema &&
59
+ typeof defaultSchema.type === 'string' &&
60
+ defaultSchema.type === schemaType
61
+ );
62
+ });
63
+
63
64
  const [pluginLabel, thisPlugin] = pluginEntry || ['', undefined];
64
65
 
65
66
  let status: undefined | 'is-warning' | 'is-danger';
@@ -227,7 +227,7 @@ const TemplateEditor = ({
227
227
  onChangeTemplate(newTemplate);
228
228
  await updateTemplate(newTemplate);
229
229
  void refresh(newTemplate);
230
-
230
+
231
231
  // Use setTimeout to update scroll position after render
232
232
  setTimeout(() => {
233
233
  if (canvasRef.current) {
@@ -20,7 +20,7 @@ import { FontContext } from '../contexts.js';
20
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,