@pdfme/ui 0.0.0
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/README.md +9 -0
- package/__mocks__/assetsTransformer.js +7 -0
- package/__mocks__/form-render.js +7 -0
- package/__mocks__/lucide-react.js +19 -0
- package/dist/index.es.js +159393 -0
- package/dist/index.umd.js +1041 -0
- package/dist/types/__tests__/assets/helper.d.ts +3 -0
- package/dist/types/__tests__/components/Designer.test.d.ts +1 -0
- package/dist/types/__tests__/components/PluginIcon.test.d.ts +1 -0
- package/dist/types/__tests__/components/Preview.test.d.ts +1 -0
- package/dist/types/__tests__/helper.test.d.ts +1 -0
- package/dist/types/src/Designer.d.ts +21 -0
- package/dist/types/src/Form.d.ts +24 -0
- package/dist/types/src/Viewer.d.ts +15 -0
- package/dist/types/src/class.d.ts +89 -0
- package/dist/types/src/components/AppContextProvider.d.ts +11 -0
- package/dist/types/src/components/CtlBar.d.ts +14 -0
- package/dist/types/src/components/Designer/Canvas/Guides.d.ts +9 -0
- package/dist/types/src/components/Designer/Canvas/Mask.d.ts +4 -0
- package/dist/types/src/components/Designer/Canvas/Moveable.d.ts +37 -0
- package/dist/types/src/components/Designer/Canvas/Padding.d.ts +6 -0
- package/dist/types/src/components/Designer/Canvas/Selecto.d.ts +10 -0
- package/dist/types/src/components/Designer/Canvas/index.d.ts +22 -0
- package/dist/types/src/components/Designer/LeftSidebar.d.ts +8 -0
- package/dist/types/src/components/Designer/PluginIcon.d.ts +10 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/AlignWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.d.ts +7 -0
- package/dist/types/src/components/Designer/RightSidebar/DetailView/index.d.ts +8 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/Item.d.ts +45 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.d.ts +14 -0
- package/dist/types/src/components/Designer/RightSidebar/ListView/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/index.d.ts +4 -0
- package/dist/types/src/components/Designer/RightSidebar/layout.d.ts +15 -0
- package/dist/types/src/components/Designer/index.d.ts +11 -0
- package/dist/types/src/components/ErrorScreen.d.ts +7 -0
- package/dist/types/src/components/Paper.d.ts +20 -0
- package/dist/types/src/components/Preview.d.ts +15 -0
- package/dist/types/src/components/Renderer.d.ts +13 -0
- package/dist/types/src/components/Root.d.ts +9 -0
- package/dist/types/src/components/Spinner.d.ts +3 -0
- package/dist/types/src/components/StaticSchema.d.ts +10 -0
- package/dist/types/src/components/UnitPager.d.ts +10 -0
- package/dist/types/src/constants.d.ts +11 -0
- package/dist/types/src/contexts.d.ts +10 -0
- package/dist/types/src/helper.d.ts +73 -0
- package/dist/types/src/hooks.d.ts +46 -0
- package/dist/types/src/i18n.d.ts +3 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/theme.d.ts +2 -0
- package/dist/types/src/types.d.ts +19 -0
- package/eslint.config.mjs +41 -0
- package/package.json +127 -0
- package/src/Designer.tsx +107 -0
- package/src/Form.tsx +102 -0
- package/src/Viewer.tsx +59 -0
- package/src/class.ts +188 -0
- package/src/components/AppContextProvider.tsx +78 -0
- package/src/components/CtlBar.tsx +183 -0
- package/src/components/Designer/Canvas/Guides.tsx +49 -0
- package/src/components/Designer/Canvas/Mask.tsx +20 -0
- package/src/components/Designer/Canvas/Moveable.tsx +91 -0
- package/src/components/Designer/Canvas/Padding.tsx +56 -0
- package/src/components/Designer/Canvas/Selecto.tsx +45 -0
- package/src/components/Designer/Canvas/index.tsx +536 -0
- package/src/components/Designer/LeftSidebar.tsx +120 -0
- package/src/components/Designer/PluginIcon.tsx +87 -0
- package/src/components/Designer/RightSidebar/DetailView/AlignWidget.tsx +229 -0
- package/src/components/Designer/RightSidebar/DetailView/ButtonGroupWidget.tsx +78 -0
- package/src/components/Designer/RightSidebar/DetailView/WidgetRenderer.tsx +28 -0
- package/src/components/Designer/RightSidebar/DetailView/index.tsx +469 -0
- package/src/components/Designer/RightSidebar/ListView/Item.tsx +158 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableContainer.tsx +204 -0
- package/src/components/Designer/RightSidebar/ListView/SelectableSortableItem.tsx +88 -0
- package/src/components/Designer/RightSidebar/ListView/index.tsx +116 -0
- package/src/components/Designer/RightSidebar/index.tsx +72 -0
- package/src/components/Designer/RightSidebar/layout.tsx +75 -0
- package/src/components/Designer/index.tsx +389 -0
- package/src/components/ErrorScreen.tsx +33 -0
- package/src/components/Paper.tsx +117 -0
- package/src/components/Preview.tsx +220 -0
- package/src/components/Renderer.tsx +165 -0
- package/src/components/Root.tsx +38 -0
- package/src/components/Spinner.tsx +45 -0
- package/src/components/StaticSchema.tsx +50 -0
- package/src/components/UnitPager.tsx +119 -0
- package/src/constants.ts +21 -0
- package/src/contexts.ts +14 -0
- package/src/helper.ts +534 -0
- package/src/hooks.ts +308 -0
- package/src/i18n.ts +903 -0
- package/src/index.ts +5 -0
- package/src/theme.ts +20 -0
- package/src/types.ts +20 -0
- package/tsconfig.json +48 -0
- package/vite.config.mts +22 -0
package/src/helper.ts
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import hotkeysJs from 'hotkeys-js';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
cloneDeep,
|
|
5
|
+
ZOOM,
|
|
6
|
+
getB64BasePdf,
|
|
7
|
+
b64toUint8Array,
|
|
8
|
+
Template,
|
|
9
|
+
BasePdf,
|
|
10
|
+
SchemaForUI,
|
|
11
|
+
Size,
|
|
12
|
+
isBlankPdf,
|
|
13
|
+
PluginRegistry,
|
|
14
|
+
} from '@pdfme/common';
|
|
15
|
+
import { pdf2size } from '@pdfme/converter';
|
|
16
|
+
import { DEFAULT_MAX_ZOOM, RULER_HEIGHT } from './constants.js';
|
|
17
|
+
import { OptionsContext } from './contexts.js';
|
|
18
|
+
|
|
19
|
+
// Define a type for the hotkeys function with additional properties
|
|
20
|
+
type HotkeysFunction = {
|
|
21
|
+
(keys: string, callback: (e: KeyboardEvent, handler: { shortcut: string }) => void): unknown;
|
|
22
|
+
shift: boolean;
|
|
23
|
+
unbind: (keys: string) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Create a simple mock for hotkeys to avoid TypeScript errors
|
|
27
|
+
const hotkeys = function (
|
|
28
|
+
keys: string,
|
|
29
|
+
callback: (e: KeyboardEvent, handler: { shortcut: string }) => void,
|
|
30
|
+
) {
|
|
31
|
+
return hotkeysJs(keys, callback);
|
|
32
|
+
} as HotkeysFunction;
|
|
33
|
+
|
|
34
|
+
// Add properties to the hotkeys function
|
|
35
|
+
hotkeys.shift = false;
|
|
36
|
+
hotkeys.unbind = function (keys: string) {
|
|
37
|
+
// Do nothing if hotkeysJs doesn't have unbind
|
|
38
|
+
const hotkeysFn = hotkeysJs as unknown as { unbind?: (keys: string) => void };
|
|
39
|
+
if (typeof hotkeysFn.unbind === 'function') {
|
|
40
|
+
hotkeysFn.unbind(keys);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const uuid = () =>
|
|
45
|
+
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
46
|
+
const r = (Math.random() * 16) | 0;
|
|
47
|
+
const v = c == 'x' ? r : (r & 0x3) | 0x8;
|
|
48
|
+
return v.toString(16);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const set = <T extends object>(obj: T, path: string | string[], value: unknown) => {
|
|
52
|
+
path = Array.isArray(path) ? path : path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
|
|
53
|
+
let src: Record<string, unknown> = obj as Record<string, unknown>;
|
|
54
|
+
path.forEach((key, index, array) => {
|
|
55
|
+
if (index == path.length - 1) {
|
|
56
|
+
src[key] = value;
|
|
57
|
+
} else {
|
|
58
|
+
if (!Object.prototype.hasOwnProperty.call(src, key)) {
|
|
59
|
+
const next = array[index + 1];
|
|
60
|
+
src[key] = String(Number(next)) === next ? [] : {};
|
|
61
|
+
}
|
|
62
|
+
src = src[key] as Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const debounce = <T extends (...args: unknown[]) => unknown>(cb: T, wait = 20) => {
|
|
68
|
+
let h: null | ReturnType<typeof setTimeout> = null;
|
|
69
|
+
const callable = (...args: Parameters<T>) => {
|
|
70
|
+
if (h) clearTimeout(h);
|
|
71
|
+
h = setTimeout(() => cb(...args), wait);
|
|
72
|
+
};
|
|
73
|
+
return callable as T;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const shift = (number: number, precision: number, reverseShift: boolean) => {
|
|
77
|
+
if (reverseShift) {
|
|
78
|
+
precision = -precision;
|
|
79
|
+
}
|
|
80
|
+
const numArray = `${number}`.split('e');
|
|
81
|
+
|
|
82
|
+
return Number(`${numArray[0]}e${numArray[1] ? Number(numArray[1]) + precision : precision}`);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const round = (number: number, precision: number) => {
|
|
86
|
+
return shift(Math.round(shift(number, precision, false)), precision, true);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const flatten = <T>(arr: T[][]): T[] => ([] as T[]).concat(...arr);
|
|
90
|
+
|
|
91
|
+
const up = 'up';
|
|
92
|
+
const shiftUp = 'shift+up';
|
|
93
|
+
const down = 'down';
|
|
94
|
+
const shiftDown = 'shift+down';
|
|
95
|
+
const left = 'left';
|
|
96
|
+
const shiftLeft = 'shift+left';
|
|
97
|
+
const right = 'right';
|
|
98
|
+
const shiftRight = 'shift+right';
|
|
99
|
+
|
|
100
|
+
const rmWin = 'backspace';
|
|
101
|
+
const rmMac = 'delete';
|
|
102
|
+
const esc = 'esc';
|
|
103
|
+
const copyWin = 'ctrl+c';
|
|
104
|
+
const copyMac = 'command+c';
|
|
105
|
+
const pasteWin = 'ctrl+v';
|
|
106
|
+
const pasteMac = 'command+v';
|
|
107
|
+
const redoWin = 'ctrl+y';
|
|
108
|
+
const redoMac = 'shift+command+z';
|
|
109
|
+
const undoWin = 'ctrl+z';
|
|
110
|
+
const undoMac = 'command+z';
|
|
111
|
+
const saveWin = 'ctrl+s';
|
|
112
|
+
const saveMac = 'command+s';
|
|
113
|
+
const selectAllWin = 'ctrl+a';
|
|
114
|
+
const selectAllMac = 'command+a';
|
|
115
|
+
|
|
116
|
+
const keys = [
|
|
117
|
+
up,
|
|
118
|
+
shiftUp,
|
|
119
|
+
down,
|
|
120
|
+
shiftDown,
|
|
121
|
+
left,
|
|
122
|
+
shiftLeft,
|
|
123
|
+
right,
|
|
124
|
+
shiftRight,
|
|
125
|
+
rmMac,
|
|
126
|
+
rmWin,
|
|
127
|
+
esc,
|
|
128
|
+
copyWin,
|
|
129
|
+
copyMac,
|
|
130
|
+
pasteWin,
|
|
131
|
+
pasteMac,
|
|
132
|
+
redoWin,
|
|
133
|
+
redoMac,
|
|
134
|
+
undoWin,
|
|
135
|
+
undoMac,
|
|
136
|
+
saveWin,
|
|
137
|
+
saveMac,
|
|
138
|
+
selectAllWin,
|
|
139
|
+
selectAllMac,
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
export const initShortCuts = (arg: {
|
|
143
|
+
move: (command: 'up' | 'down' | 'left' | 'right', isShift: boolean) => void;
|
|
144
|
+
remove: () => void;
|
|
145
|
+
esc: () => void;
|
|
146
|
+
copy: () => void;
|
|
147
|
+
paste: () => void;
|
|
148
|
+
redo: () => void;
|
|
149
|
+
undo: () => void;
|
|
150
|
+
save: () => void;
|
|
151
|
+
selectAll: () => void;
|
|
152
|
+
}) => {
|
|
153
|
+
hotkeys(keys.join(), (e: KeyboardEvent, handler: { shortcut: string }) => {
|
|
154
|
+
switch (handler.shortcut) {
|
|
155
|
+
case up:
|
|
156
|
+
case shiftUp:
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
arg.move('up', hotkeys.shift);
|
|
159
|
+
break;
|
|
160
|
+
case down:
|
|
161
|
+
case shiftDown:
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
arg.move('down', hotkeys.shift);
|
|
164
|
+
break;
|
|
165
|
+
case left:
|
|
166
|
+
case shiftLeft:
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
arg.move('left', hotkeys.shift);
|
|
169
|
+
break;
|
|
170
|
+
case right:
|
|
171
|
+
case shiftRight:
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
arg.move('right', hotkeys.shift);
|
|
174
|
+
break;
|
|
175
|
+
case rmWin:
|
|
176
|
+
case rmMac:
|
|
177
|
+
arg.remove();
|
|
178
|
+
break;
|
|
179
|
+
case esc:
|
|
180
|
+
arg.esc();
|
|
181
|
+
break;
|
|
182
|
+
case copyWin:
|
|
183
|
+
case copyMac:
|
|
184
|
+
arg.copy();
|
|
185
|
+
break;
|
|
186
|
+
case pasteWin:
|
|
187
|
+
case pasteMac:
|
|
188
|
+
arg.paste();
|
|
189
|
+
break;
|
|
190
|
+
case redoWin:
|
|
191
|
+
case redoMac:
|
|
192
|
+
arg.redo();
|
|
193
|
+
break;
|
|
194
|
+
case undoWin:
|
|
195
|
+
case undoMac:
|
|
196
|
+
arg.undo();
|
|
197
|
+
break;
|
|
198
|
+
case saveWin:
|
|
199
|
+
case saveMac:
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
arg.save();
|
|
202
|
+
break;
|
|
203
|
+
case selectAllWin:
|
|
204
|
+
case selectAllMac:
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
arg.selectAll();
|
|
207
|
+
break;
|
|
208
|
+
default:
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const destroyShortCuts = () => {
|
|
215
|
+
hotkeys.unbind(keys.join());
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Guess the MIME type by checking the first few bytes of the ArrayBuffer.
|
|
220
|
+
* Currently checks for PNG, JPEG, and GIF signatures.
|
|
221
|
+
*/
|
|
222
|
+
function detectMimeType(arrayBuffer: ArrayBuffer): string {
|
|
223
|
+
const dataView = new DataView(arrayBuffer);
|
|
224
|
+
|
|
225
|
+
// Check for PNG signature: 0x89 0x50 0x4E 0x47
|
|
226
|
+
if (
|
|
227
|
+
dataView.getUint8(0) === 0x89 &&
|
|
228
|
+
dataView.getUint8(1) === 0x50 &&
|
|
229
|
+
dataView.getUint8(2) === 0x4e &&
|
|
230
|
+
dataView.getUint8(3) === 0x47
|
|
231
|
+
) {
|
|
232
|
+
return 'image/png';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Check for JPEG signature: 0xFF 0xD8 0xFF
|
|
236
|
+
if (
|
|
237
|
+
dataView.getUint8(0) === 0xff &&
|
|
238
|
+
dataView.getUint8(1) === 0xd8 &&
|
|
239
|
+
dataView.getUint8(2) === 0xff
|
|
240
|
+
) {
|
|
241
|
+
return 'image/jpeg';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return ''; // Unknown type
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const arrayBufferToBase64 = (arrayBuffer: ArrayBuffer): string => {
|
|
248
|
+
// Detect the MIME type
|
|
249
|
+
const mimeType = detectMimeType(arrayBuffer);
|
|
250
|
+
|
|
251
|
+
// Convert ArrayBuffer to raw Base64
|
|
252
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
253
|
+
let binary = '';
|
|
254
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
255
|
+
binary += String.fromCharCode(bytes[i]);
|
|
256
|
+
}
|
|
257
|
+
const base64String = btoa(binary);
|
|
258
|
+
|
|
259
|
+
// Optionally prepend a data: URL if a known MIME type is found;
|
|
260
|
+
// otherwise just return the raw Base64.
|
|
261
|
+
if (mimeType) {
|
|
262
|
+
return `data:${mimeType};base64,${base64String}`;
|
|
263
|
+
} else {
|
|
264
|
+
// or you can default to `application/octet-stream` if unknown
|
|
265
|
+
return `data:application/octet-stream;base64,${base64String}`;
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const convertSchemasForUI = (template: Template): SchemaForUI[][] => {
|
|
270
|
+
template.schemas.forEach((page) => {
|
|
271
|
+
page.forEach((schema) => {
|
|
272
|
+
(schema as SchemaForUI).id = uuid();
|
|
273
|
+
(schema as SchemaForUI).content = schema.content || '';
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
return template.schemas as SchemaForUI[][];
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export const template2SchemasList = async (_template: Template) => {
|
|
281
|
+
const template = cloneDeep(_template);
|
|
282
|
+
const { basePdf, schemas } = template;
|
|
283
|
+
const schemasForUI = convertSchemasForUI(template);
|
|
284
|
+
|
|
285
|
+
let pageSizes: Size[] = [];
|
|
286
|
+
if (isBlankPdf(basePdf)) {
|
|
287
|
+
pageSizes = schemas.map(() => ({
|
|
288
|
+
width: basePdf.width,
|
|
289
|
+
height: basePdf.height,
|
|
290
|
+
}));
|
|
291
|
+
} else {
|
|
292
|
+
const b64BasePdf = await getB64BasePdf(basePdf);
|
|
293
|
+
// pdf2size accepts both ArrayBuffer and Uint8Array
|
|
294
|
+
const pdfArrayBuffer = b64toUint8Array(b64BasePdf);
|
|
295
|
+
|
|
296
|
+
pageSizes = await pdf2size(pdfArrayBuffer);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const ssl = schemasForUI.length;
|
|
300
|
+
const psl = pageSizes.length;
|
|
301
|
+
|
|
302
|
+
return (
|
|
303
|
+
ssl < psl
|
|
304
|
+
? schemasForUI.concat(new Array(psl - ssl).fill(cloneDeep([])))
|
|
305
|
+
: schemasForUI.slice(0, pageSizes.length)
|
|
306
|
+
).map((schema, i) => {
|
|
307
|
+
Object.values(schema).forEach((value) => {
|
|
308
|
+
const { width, height } = pageSizes[i];
|
|
309
|
+
const xEdge = value.position.x + value.width;
|
|
310
|
+
const yEdge = value.position.y + value.height;
|
|
311
|
+
if (xEdge > width) {
|
|
312
|
+
value.position.x = Math.max(0, width - value.width);
|
|
313
|
+
}
|
|
314
|
+
if (yEdge > height) {
|
|
315
|
+
value.position.y = Math.max(0, height - value.height);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
return schema;
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export const schemasList2template = (schemasList: SchemaForUI[][], basePdf: BasePdf): Template => ({
|
|
324
|
+
schemas: cloneDeep(schemasList).map((page) =>
|
|
325
|
+
page.map((schema) => {
|
|
326
|
+
// @ts-expect-error Property 'id' is used only in UI
|
|
327
|
+
delete schema.id;
|
|
328
|
+
return schema;
|
|
329
|
+
}),
|
|
330
|
+
),
|
|
331
|
+
basePdf,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
export const getUniqueSchemaName = (arg: {
|
|
335
|
+
copiedSchemaName: string;
|
|
336
|
+
schema: SchemaForUI[];
|
|
337
|
+
stackUniqueSchemaNames: string[];
|
|
338
|
+
}) => {
|
|
339
|
+
const { copiedSchemaName, schema, stackUniqueSchemaNames } = arg;
|
|
340
|
+
const schemaNames = schema.map((s) => s.name).concat(stackUniqueSchemaNames);
|
|
341
|
+
const tmp: { [originalName: string]: number } = schemaNames.reduce(
|
|
342
|
+
(acc, cur) => Object.assign(acc, { originalName: cur, copiedNum: 0 }),
|
|
343
|
+
{},
|
|
344
|
+
);
|
|
345
|
+
const extractOriginalName = (name: string) => name.replace(/ copy$| copy [0-9]*$/, '');
|
|
346
|
+
schemaNames
|
|
347
|
+
.filter((name) => / copy$| copy [0-9]*$/.test(name))
|
|
348
|
+
.forEach((name) => {
|
|
349
|
+
const originalName = extractOriginalName(name);
|
|
350
|
+
const match = name.match(/[0-9]*$/);
|
|
351
|
+
const copiedNum = match && match[0] ? Number(match[0]) : 1;
|
|
352
|
+
if ((tmp[originalName] ?? 0) < copiedNum) {
|
|
353
|
+
tmp[originalName] = copiedNum;
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const originalName = extractOriginalName(copiedSchemaName);
|
|
358
|
+
if (tmp[originalName]) {
|
|
359
|
+
const copiedNum = tmp[originalName];
|
|
360
|
+
const uniqueName = `${originalName} copy ${copiedNum + 1}`;
|
|
361
|
+
stackUniqueSchemaNames.push(uniqueName);
|
|
362
|
+
|
|
363
|
+
return uniqueName;
|
|
364
|
+
}
|
|
365
|
+
const uniqueName = `${copiedSchemaName} copy`;
|
|
366
|
+
stackUniqueSchemaNames.push(uniqueName);
|
|
367
|
+
|
|
368
|
+
return uniqueName;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
export const moveCommandToChangeSchemasArg = (props: {
|
|
372
|
+
command: 'up' | 'down' | 'left' | 'right';
|
|
373
|
+
activeSchemas: SchemaForUI[];
|
|
374
|
+
isShift: boolean;
|
|
375
|
+
pageSize: Size;
|
|
376
|
+
}) => {
|
|
377
|
+
const { command, activeSchemas, isShift, pageSize } = props;
|
|
378
|
+
const key = command === 'up' || command === 'down' ? 'y' : 'x';
|
|
379
|
+
const num = isShift ? 0.1 : 1;
|
|
380
|
+
|
|
381
|
+
const getValue = (as: SchemaForUI) => {
|
|
382
|
+
let value = 0;
|
|
383
|
+
const { position } = as;
|
|
384
|
+
switch (command) {
|
|
385
|
+
case 'up':
|
|
386
|
+
value = round(position.y - num, 2);
|
|
387
|
+
break;
|
|
388
|
+
case 'down':
|
|
389
|
+
value = round(position.y + num, 2);
|
|
390
|
+
break;
|
|
391
|
+
case 'left':
|
|
392
|
+
value = round(position.x - num, 2);
|
|
393
|
+
break;
|
|
394
|
+
case 'right':
|
|
395
|
+
value = round(position.x + num, 2);
|
|
396
|
+
break;
|
|
397
|
+
default:
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return value > 0 ? value : 0;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
return activeSchemas.map((as) => {
|
|
405
|
+
let value = getValue(as);
|
|
406
|
+
const { width, height } = as;
|
|
407
|
+
if (key === 'x') {
|
|
408
|
+
value = value > pageSize.width - width ? round(pageSize.width - width, 2) : value;
|
|
409
|
+
} else {
|
|
410
|
+
value = value > pageSize.height - height ? round(pageSize.height - height, 2) : value;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return { key: `position.${key}`, value, schemaId: as.id };
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
export const getPagesScrollTopByIndex = (pageSizes: Size[], index: number, scale: number) => {
|
|
418
|
+
return pageSizes
|
|
419
|
+
.slice(0, index)
|
|
420
|
+
.reduce((acc, cur) => acc + (cur.height * ZOOM + RULER_HEIGHT * scale) * scale, 0);
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const handlePositionSizeChange = (
|
|
424
|
+
schema: SchemaForUI,
|
|
425
|
+
key: string,
|
|
426
|
+
value: unknown,
|
|
427
|
+
basePdf: BasePdf,
|
|
428
|
+
pageSize: Size,
|
|
429
|
+
) => {
|
|
430
|
+
const padding = isBlankPdf(basePdf) ? basePdf.padding : [0, 0, 0, 0];
|
|
431
|
+
const [pt, pr, pb, pl] = padding;
|
|
432
|
+
const { width: pw, height: ph } = pageSize;
|
|
433
|
+
const calcBounds = (v: unknown, min: number, max: number) =>
|
|
434
|
+
Math.min(Math.max(Number(v), min), max);
|
|
435
|
+
if (key === 'position.x') {
|
|
436
|
+
schema.position.x = calcBounds(value, pl, pw - schema.width - pr);
|
|
437
|
+
} else if (key === 'position.y') {
|
|
438
|
+
schema.position.y = calcBounds(value, pt, ph - schema.height - pb);
|
|
439
|
+
} else if (key === 'width') {
|
|
440
|
+
schema.width = calcBounds(value, 0, pw - schema.position.x - pr);
|
|
441
|
+
} else if (key === 'height') {
|
|
442
|
+
schema.height = calcBounds(value, 0, ph - schema.position.y - pb);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const handleTypeChange = (
|
|
447
|
+
schema: SchemaForUI,
|
|
448
|
+
key: string,
|
|
449
|
+
value: unknown,
|
|
450
|
+
pluginsRegistry: PluginRegistry,
|
|
451
|
+
) => {
|
|
452
|
+
if (key !== 'type') return;
|
|
453
|
+
const keysToKeep = ['id', 'name', 'type', 'position', 'required'];
|
|
454
|
+
Object.keys(schema).forEach((key) => {
|
|
455
|
+
if (!keysToKeep.includes(key)) {
|
|
456
|
+
delete schema[key as keyof typeof schema];
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const plugin = pluginsRegistry.findByType(value as string);
|
|
461
|
+
|
|
462
|
+
// Apply default schema properties if available
|
|
463
|
+
if (plugin?.propPanel.defaultSchema) {
|
|
464
|
+
const defaultSchema = plugin.propPanel.defaultSchema;
|
|
465
|
+
const schemaRecord = schema as Record<string, unknown>;
|
|
466
|
+
|
|
467
|
+
// Use a type-safe approach to copy properties
|
|
468
|
+
for (const key of Object.keys(defaultSchema)) {
|
|
469
|
+
// Only add properties that don't already exist in the schema
|
|
470
|
+
if (!Object.prototype.hasOwnProperty.call(schema, key)) {
|
|
471
|
+
const propertyValue = defaultSchema[key];
|
|
472
|
+
if (propertyValue !== undefined) {
|
|
473
|
+
schemaRecord[key] = propertyValue;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (schema.readOnly) {
|
|
479
|
+
schema.required = false;
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
export const changeSchemas = (args: {
|
|
484
|
+
objs: { key: string; value: unknown; schemaId: string }[];
|
|
485
|
+
schemas: SchemaForUI[];
|
|
486
|
+
basePdf: BasePdf;
|
|
487
|
+
pluginsRegistry: PluginRegistry;
|
|
488
|
+
pageSize: { width: number; height: number };
|
|
489
|
+
commitSchemas: (newSchemas: SchemaForUI[]) => void;
|
|
490
|
+
}) => {
|
|
491
|
+
const { objs, schemas, basePdf, pluginsRegistry, pageSize, commitSchemas } = args;
|
|
492
|
+
const newSchemas = objs.reduce((acc, { key, value, schemaId }) => {
|
|
493
|
+
const tgt = acc.find((s) => s.id === schemaId);
|
|
494
|
+
if (!tgt) return acc;
|
|
495
|
+
// Assign to reference
|
|
496
|
+
set(tgt, key, value);
|
|
497
|
+
|
|
498
|
+
if (key === 'type') {
|
|
499
|
+
handleTypeChange(tgt, key, value, pluginsRegistry);
|
|
500
|
+
} else if (['position.x', 'position.y', 'width', 'height'].includes(key)) {
|
|
501
|
+
handlePositionSizeChange(tgt, key, value, basePdf, pageSize);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return acc;
|
|
505
|
+
}, cloneDeep(schemas));
|
|
506
|
+
commitSchemas(newSchemas);
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
export const useMaxZoom = () => {
|
|
510
|
+
const options = useContext(OptionsContext);
|
|
511
|
+
|
|
512
|
+
return options.maxZoom ? options.maxZoom / 100 : DEFAULT_MAX_ZOOM;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
export const setFontNameRecursively = (
|
|
516
|
+
obj: Record<string, unknown>,
|
|
517
|
+
fontName: string,
|
|
518
|
+
seen = new WeakSet(),
|
|
519
|
+
): void => {
|
|
520
|
+
if (!obj || typeof obj !== 'object' || seen.has(obj)) return;
|
|
521
|
+
seen.add(obj);
|
|
522
|
+
|
|
523
|
+
for (const key in obj) {
|
|
524
|
+
if (
|
|
525
|
+
key === 'fontName' &&
|
|
526
|
+
Object.prototype.hasOwnProperty.call(obj, key) &&
|
|
527
|
+
obj[key] === undefined
|
|
528
|
+
) {
|
|
529
|
+
obj[key] = fontName;
|
|
530
|
+
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
531
|
+
setFontNameRecursively(obj[key] as Record<string, unknown>, fontName, seen);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
};
|