@pdfme/ui 5.3.11-dev.6 → 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.
- package/dist/index.es.js +5704 -5614
- package/dist/index.umd.js +122 -122
- package/dist/types/src/components/Designer/Canvas/Guides.d.ts +3 -9
- package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +2 -49
- package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +5 -16
- package/dist/types/src/components/Designer/PluginIcon.d.ts +2 -2
- package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +17 -0
- package/dist/types/src/constants.d.ts +1 -1
- package/dist/types/src/contexts.d.ts +1 -1
- package/dist/types/src/helper.d.ts +3 -3
- package/package.json +1 -1
- package/src/components/AppContextProvider.tsx +17 -10
- package/src/components/CtlBar.tsx +2 -2
- package/src/components/Designer/Canvas/Guides.tsx +4 -13
- package/src/components/Designer/Canvas/Moveable.tsx +15 -74
- package/src/components/Designer/Canvas/Selecto.tsx +9 -24
- package/src/components/Designer/Canvas/index.tsx +3 -56
- package/src/components/Designer/LeftSidebar.tsx +2 -2
- package/src/components/Designer/PluginIcon.tsx +14 -3
- package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +91 -16
- package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +12 -8
- package/src/components/Designer/RightSidebar/DetailView/index.tsx +64 -21
- package/src/components/Designer/RightSidebar/ListView/Item.tsx +30 -2
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +23 -4
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +20 -4
- package/src/components/Designer/index.tsx +14 -9
- package/src/components/Preview.tsx +4 -4
- package/src/components/Renderer.tsx +41 -35
- package/src/components/Root.tsx +1 -1
- package/src/constants.ts +1 -1
- package/src/contexts.ts +1 -1
- package/src/helper.ts +131 -38
- package/src/types/react-guides.d.ts +0 -22
- 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
|
-
|
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
|
-
|
29
|
-
const
|
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
|
-
|
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 = (
|
63
|
+
adjust = (size: number): number => size / 2;
|
37
64
|
} else if (['right', 'bottom'].includes(type)) {
|
38
65
|
basePos = max;
|
39
|
-
adjust = (
|
66
|
+
adjust = (size: number): number => size;
|
40
67
|
}
|
41
68
|
|
42
69
|
changeSchemas(
|
43
|
-
ass.map((as) =>
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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))
|
19
|
+
const ass = schemas.filter((s) => ids.includes(s.id));
|
20
20
|
changeSchemas(
|
21
|
-
ass.map((s:
|
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))
|
35
|
-
ass.forEach((s:
|
36
|
-
|
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
|
-
|
80
|
-
values
|
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
|
-
|
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
|
-
|
102
|
-
|
108
|
+
// Use proper type for validator function parameter
|
109
|
+
const validateUniqueSchemaName = (
|
110
|
+
_: unknown,
|
111
|
+
value: string
|
112
|
+
): boolean => uniqueSchemaName.current(value);
|
103
113
|
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
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) =>
|
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
|
-
|
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
|
-
|
168
|
-
|
169
|
-
value
|
170
|
-
|
171
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
66
|
-
|
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:
|
42
|
+
onClick: (event: React.MouseEvent) => onSelect(schema.id, event.shiftKey),
|
43
43
|
};
|
44
44
|
|
45
|
-
|
46
|
-
|
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
|
-
|
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 =
|
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
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
-
|
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,
|
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 =
|
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:
|
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-
|
115
|
+
// @ts-expect-error Dynamic property assignment
|
116
116
|
targetSchema[_key] = value as string;
|
117
117
|
}
|
118
118
|
});
|