@campxdev/pdfme 1.2.2 → 1.3.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/dist/cjs/chunks/{index-C8qZMUOU.js → fontSizePxWidget-Drk8HKGH.js} +138 -9
- package/dist/cjs/chunks/fontSizeTransform-CQQ_O42f.js +37 -0
- package/dist/cjs/chunks/{helper-BfoMn47R.js → helper-DGH62Z2s.js} +4 -0
- package/dist/cjs/chunks/{index-COKtXyPp.js → index-CoNR0xQU.js} +6 -2
- package/dist/cjs/chunks/{index-CVqJfcgy.js → index-CsEKt088.js} +697 -547
- package/dist/cjs/chunks/{pluginRegistry-C8bMreez.js → pluginRegistry-D2vr9MUy.js} +1 -1
- package/dist/cjs/common.js +7 -3
- package/dist/cjs/converter.js +1 -1
- package/dist/cjs/generator.js +3 -3
- package/dist/cjs/index.js +23 -16
- package/dist/cjs/print-designer-editor.js +3320 -3296
- package/dist/cjs/schemas.js +500 -38
- package/dist/cjs/ui.js +2007 -1868
- package/dist/esm/chunks/{index-C4F7EwBG.js → fontSizePxWidget-CbzQrSSM.js} +130 -5
- package/dist/esm/chunks/fontSizeTransform-CkTVJdRF.js +34 -0
- package/dist/esm/chunks/{helper-D5PPN6Bv.js → helper-DSxGxZ0j.js} +4 -1
- package/dist/esm/chunks/{index-CDhErAtE.js → index-DJkUkUo9.js} +4 -3
- package/dist/esm/chunks/{index-C7jr4GIK.js → index-D_-j4c4P.js} +691 -544
- package/dist/esm/chunks/{pluginRegistry-B-XSNgmK.js → pluginRegistry-Bgrz5qWG.js} +1 -1
- package/dist/esm/common.js +4 -3
- package/dist/esm/converter.js +1 -1
- package/dist/esm/generator.js +3 -3
- package/dist/esm/index.js +7 -6
- package/dist/esm/print-designer-editor.js +3307 -3286
- package/dist/esm/schemas.js +472 -13
- package/dist/esm/ui.js +2007 -1868
- package/dist/types/_vendors/common/fontSizeTransform.d.ts +5 -0
- package/dist/types/_vendors/common/helper.d.ts +1 -0
- package/dist/types/_vendors/common/index.d.ts +3 -2
- package/dist/types/_vendors/print-designer-editor/index.d.ts +2 -1
- package/dist/types/_vendors/print-designer-editor/types.d.ts +1 -1
- package/dist/types/_vendors/print-designer-editor/useDesigner.d.ts +1 -1
- package/dist/types/_vendors/schemas/index.d.ts +8 -2
- package/dist/types/_vendors/schemas/richText/helper.d.ts +3 -0
- package/dist/types/_vendors/schemas/richText/index.d.ts +4 -0
- package/dist/types/_vendors/schemas/richText/pdfRender.d.ts +3 -0
- package/dist/types/_vendors/schemas/richText/propPanel.d.ts +3 -0
- package/dist/types/_vendors/schemas/richText/types.d.ts +7 -0
- package/dist/types/_vendors/schemas/richText/uiRender.d.ts +3 -0
- package/dist/types/_vendors/schemas/singleVariableText/index.d.ts +4 -0
- package/dist/types/_vendors/schemas/singleVariableText/propPanel.d.ts +3 -0
- package/dist/types/_vendors/schemas/singleVariableText/types.d.ts +4 -0
- package/dist/types/_vendors/schemas/text/fontSizePxWidget.d.ts +9 -0
- package/dist/types/_vendors/ui/components/CtlBar.d.ts +1 -1
- package/dist/types/_vendors/ui/components/Paper.d.ts +1 -0
- package/package.json +1 -1
package/dist/cjs/schemas.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./chunks/index-
|
|
4
|
-
var
|
|
3
|
+
var index = require('./chunks/index-CsEKt088.js');
|
|
4
|
+
var fontSizePxWidget = require('./chunks/fontSizePxWidget-Drk8HKGH.js');
|
|
5
5
|
var lucide = require('lucide');
|
|
6
|
-
var helper = require('./chunks/helper-
|
|
6
|
+
var helper = require('./chunks/helper-DGH62Z2s.js');
|
|
7
7
|
var AirDatepicker = require('air-datepicker');
|
|
8
8
|
var localeAr = require('air-datepicker/locale/ar');
|
|
9
9
|
var localeBg = require('air-datepicker/locale/bg');
|
|
@@ -108,11 +108,470 @@ var localeUk__default = /*#__PURE__*/_interopDefault(localeUk);
|
|
|
108
108
|
var localeZh__default = /*#__PURE__*/_interopDefault(localeZh);
|
|
109
109
|
var dateFns__namespace = /*#__PURE__*/_interopNamespace(dateFns);
|
|
110
110
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
const pdfRender$2 = async (arg) => {
|
|
112
|
+
const { value, schema, pageContext, ...rest } = arg;
|
|
113
|
+
// Static mode: no template text → render value directly as plain text
|
|
114
|
+
if (!schema.text) {
|
|
115
|
+
await index.pdfRender({ value, schema, ...rest });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
// readOnly: value is already resolved by generate.ts via replacePlaceholders
|
|
119
|
+
if (schema.readOnly) {
|
|
120
|
+
await index.pdfRender({ value, schema, ...rest });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Dynamic mode (form): substitute variables in template
|
|
124
|
+
if (!fontSizePxWidget.validateVariables(value, schema)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const renderArgs = {
|
|
128
|
+
value: fontSizePxWidget.substituteVariables(schema.text, value || '{}', pageContext),
|
|
129
|
+
schema,
|
|
130
|
+
...rest,
|
|
131
|
+
};
|
|
132
|
+
await index.pdfRender(renderArgs);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const insertVariableWidget = fontSizePxWidget.createInsertVariableWidget('text');
|
|
136
|
+
const mapDynamicVariables = (props) => {
|
|
137
|
+
const { rootElement, changeSchemas, activeSchema } = props;
|
|
138
|
+
const mvtSchema = activeSchema;
|
|
139
|
+
const text = mvtSchema.text ?? '';
|
|
140
|
+
if (!text) {
|
|
141
|
+
rootElement.style.display = 'none';
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const variables = JSON.parse(mvtSchema.content || '{}');
|
|
145
|
+
const variablesChanged = updateVariablesFromText(text, variables);
|
|
146
|
+
const varNames = Object.keys(variables);
|
|
147
|
+
if (variablesChanged) {
|
|
148
|
+
changeSchemas([
|
|
149
|
+
{ key: 'content', value: JSON.stringify(variables), schemaId: activeSchema.id },
|
|
150
|
+
{ key: 'variables', value: varNames, schemaId: activeSchema.id },
|
|
151
|
+
{ key: 'readOnly', value: varNames.length === 0, schemaId: activeSchema.id },
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
// No UI needed — sample data is auto-generated from variable paths
|
|
155
|
+
rootElement.style.display = 'none';
|
|
156
|
+
};
|
|
157
|
+
const propPanel$1 = {
|
|
158
|
+
schema: (propPanelProps) => {
|
|
159
|
+
if (typeof index.propPanel.schema !== 'function') {
|
|
160
|
+
throw new Error('Oops, is text schema no longer a function?');
|
|
161
|
+
}
|
|
162
|
+
// Safely call schema function with proper type handling
|
|
163
|
+
const parentSchema = typeof index.propPanel.schema === 'function' ? index.propPanel.schema(propPanelProps) : {};
|
|
164
|
+
return {
|
|
165
|
+
insertVariablePicker: {
|
|
166
|
+
type: 'void',
|
|
167
|
+
widget: 'insertVariableWidget',
|
|
168
|
+
bind: false,
|
|
169
|
+
span: 24,
|
|
170
|
+
},
|
|
171
|
+
'----': { type: 'void', widget: 'Divider' },
|
|
172
|
+
...parentSchema,
|
|
173
|
+
dynamicVariables: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
widget: 'mapDynamicVariables',
|
|
176
|
+
bind: false,
|
|
177
|
+
span: 0,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
widgets: { ...(index.propPanel.widgets || {}), mapDynamicVariables, insertVariableWidget },
|
|
182
|
+
defaultSchema: {
|
|
183
|
+
...index.propPanel.defaultSchema,
|
|
184
|
+
readOnly: false,
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: 'Type Something...',
|
|
187
|
+
width: 50,
|
|
188
|
+
height: 15,
|
|
189
|
+
content: '{}',
|
|
190
|
+
variables: [],
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
/** Known JS globals/keywords that should NOT be treated as user-defined variables */
|
|
194
|
+
const RESERVED_NAMES$1 = new Set([
|
|
195
|
+
'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'in',
|
|
196
|
+
'void', 'delete', 'new', 'this', 'NaN', 'Infinity',
|
|
197
|
+
'Math', 'String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'JSON',
|
|
198
|
+
'isNaN', 'parseFloat', 'parseInt', 'decodeURI', 'decodeURIComponent',
|
|
199
|
+
'encodeURI', 'encodeURIComponent', 'date', 'dateTime',
|
|
200
|
+
'currentPage', 'totalPages',
|
|
201
|
+
]);
|
|
202
|
+
/**
|
|
203
|
+
* Extract full dot-notation paths from an expression string.
|
|
204
|
+
* E.g. "student.marks.sem1 > 80" → ["student.marks.sem1"]
|
|
205
|
+
* Handles method calls: "student.name.toUpperCase()" → ["student.name"]
|
|
206
|
+
* Skips string literals and reserved names.
|
|
207
|
+
*/
|
|
208
|
+
const extractDotPaths = (expr) => {
|
|
209
|
+
// Replace string literals with spaces (preserving positions for nextChar lookup)
|
|
210
|
+
const cleaned = expr.replace(/'[^']*'|"[^"]*"|`[^`]*`/g, (m) => ' '.repeat(m.length));
|
|
211
|
+
const pathRegex = /[a-zA-Z_$][a-zA-Z0-9_$]*(?:\.[a-zA-Z_$][a-zA-Z0-9_$]*)*/g;
|
|
212
|
+
const paths = new Set();
|
|
213
|
+
let m;
|
|
214
|
+
while ((m = pathRegex.exec(cleaned)) !== null) {
|
|
215
|
+
let path = m[0];
|
|
216
|
+
// If followed by '(', the last segment is a method call — trim it
|
|
217
|
+
const nextChar = cleaned[m.index + path.length];
|
|
218
|
+
if (nextChar === '(') {
|
|
219
|
+
const lastDot = path.lastIndexOf('.');
|
|
220
|
+
if (lastDot !== -1) {
|
|
221
|
+
path = path.substring(0, lastDot);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// Standalone function call like parseInt(...) — skip
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const root = path.split('.')[0];
|
|
229
|
+
if (!RESERVED_NAMES$1.has(root))
|
|
230
|
+
paths.add(path);
|
|
231
|
+
}
|
|
232
|
+
return Array.from(paths);
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Build a nested default object from dot-paths.
|
|
236
|
+
* E.g. ["student.name", "student.marks.sem1"] →
|
|
237
|
+
* { name: "NAME", marks: { sem1: "SEM1" } }
|
|
238
|
+
* Merges into an existing object, only adding missing leaves.
|
|
239
|
+
* Returns true if anything was added.
|
|
240
|
+
*/
|
|
241
|
+
const buildNestedDefault = (obj, paths) => {
|
|
242
|
+
let added = false;
|
|
243
|
+
for (const path of paths) {
|
|
244
|
+
const parts = path.split('.');
|
|
245
|
+
if (parts.length <= 1)
|
|
246
|
+
continue; // no nested parts
|
|
247
|
+
let current = obj;
|
|
248
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
249
|
+
if (!(parts[i] in current) || typeof current[parts[i]] !== 'object' || current[parts[i]] === null) {
|
|
250
|
+
current[parts[i]] = {};
|
|
251
|
+
added = true;
|
|
252
|
+
}
|
|
253
|
+
current = current[parts[i]];
|
|
254
|
+
}
|
|
255
|
+
const leaf = parts[parts.length - 1];
|
|
256
|
+
if (!(leaf in current)) {
|
|
257
|
+
current[leaf] = path.replace(/\./g, '_').toUpperCase();
|
|
258
|
+
added = true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return added;
|
|
262
|
+
};
|
|
263
|
+
const updateVariablesFromText = (text, variables) => {
|
|
264
|
+
// Find all {...} blocks and extract dot-notation paths from each
|
|
265
|
+
const blockRegex = /\{([^{}]+)\}/g;
|
|
266
|
+
const allPaths = new Set();
|
|
267
|
+
let blockMatch;
|
|
268
|
+
while ((blockMatch = blockRegex.exec(text)) !== null) {
|
|
269
|
+
for (const path of extractDotPaths(blockMatch[1])) {
|
|
270
|
+
allPaths.add(path);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Group paths by root identifier
|
|
274
|
+
const rootToPaths = new Map();
|
|
275
|
+
for (const path of allPaths) {
|
|
276
|
+
const root = path.split('.')[0];
|
|
277
|
+
if (!rootToPaths.has(root))
|
|
278
|
+
rootToPaths.set(root, []);
|
|
279
|
+
rootToPaths.get(root).push(path);
|
|
280
|
+
}
|
|
281
|
+
const allRoots = new Set(rootToPaths.keys());
|
|
282
|
+
let changed = false;
|
|
283
|
+
for (const [root, paths] of rootToPaths) {
|
|
284
|
+
const hasNested = paths.some((p) => p.includes('.'));
|
|
285
|
+
if (hasNested) {
|
|
286
|
+
// Parse existing value or start fresh
|
|
287
|
+
let obj = {};
|
|
288
|
+
if (root in variables) {
|
|
289
|
+
try {
|
|
290
|
+
const parsed = JSON.parse(variables[root]);
|
|
291
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
292
|
+
obj = parsed;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
/* not JSON, will rebuild */
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const added = buildNestedDefault(obj, paths);
|
|
300
|
+
if (!(root in variables) || added) {
|
|
301
|
+
variables[root] = JSON.stringify(obj);
|
|
302
|
+
changed = true;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
if (!(root in variables)) {
|
|
307
|
+
variables[root] = root.toUpperCase();
|
|
308
|
+
changed = true;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// Remove variables whose root is no longer referenced
|
|
313
|
+
for (const varName of Object.keys(variables)) {
|
|
314
|
+
if (!allRoots.has(varName)) {
|
|
315
|
+
delete variables[varName];
|
|
316
|
+
changed = true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return changed;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const uiRender$2 = async (arg) => {
|
|
323
|
+
const { value, schema, rootElement, mode, onChange, pageContext, ...rest } = arg;
|
|
324
|
+
// Static mode: no template text → delegate to plain text behavior
|
|
325
|
+
if (!schema.text) {
|
|
326
|
+
await index.uiRender(arg);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// Dynamic mode: template with optional variables
|
|
330
|
+
let text = schema.text;
|
|
331
|
+
let numVariables = (schema.variables || []).length;
|
|
332
|
+
if (mode === 'form' && numVariables > 0) {
|
|
333
|
+
await formUiRender(arg);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
await index.uiRender({
|
|
337
|
+
value: index.isEditable(mode, schema) ? text : fontSizePxWidget.substituteVariables(text, value, pageContext),
|
|
338
|
+
schema,
|
|
339
|
+
mode: mode === 'form' ? 'viewer' : mode, // if no variables for form it's just a viewer
|
|
340
|
+
rootElement,
|
|
341
|
+
onChange: (arg) => {
|
|
342
|
+
if (!Array.isArray(arg)) {
|
|
343
|
+
if (onChange) {
|
|
344
|
+
onChange({ key: 'text', value: arg.value });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
throw new Error('onChange is not an array, the parent text plugin has changed...');
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
...rest,
|
|
352
|
+
});
|
|
353
|
+
const textBlock = rootElement.querySelector('#text-' + String(schema.id));
|
|
354
|
+
if (!textBlock) {
|
|
355
|
+
throw new Error('Text block not found. Ensure the text block has an id of "text-" + schema.id');
|
|
356
|
+
}
|
|
357
|
+
if (mode === 'designer') {
|
|
358
|
+
textBlock.addEventListener('keyup', (event) => {
|
|
359
|
+
text = textBlock.textContent || '';
|
|
360
|
+
if (keyPressShouldBeChecked(event)) {
|
|
361
|
+
const newNumVariables = countUniqueVariableNames(text);
|
|
362
|
+
if (numVariables !== newNumVariables) {
|
|
363
|
+
if (onChange) {
|
|
364
|
+
onChange({ key: 'text', value: text });
|
|
365
|
+
}
|
|
366
|
+
numVariables = newNumVariables;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
/** Re-evaluate all expression spans inside a container using current variable values */
|
|
373
|
+
const refreshExpressionSpans = (container, variables, pageCtx) => {
|
|
374
|
+
container.querySelectorAll('[data-expr]').forEach((span) => {
|
|
375
|
+
const expr = span.getAttribute('data-expr');
|
|
376
|
+
span.textContent = fontSizePxWidget.substituteVariables(`{${expr}}`, variables, pageCtx);
|
|
377
|
+
});
|
|
378
|
+
};
|
|
379
|
+
const formUiRender = async (arg) => {
|
|
380
|
+
const { value, schema, rootElement, onChange, stopEditing, theme, _cache, options, pageContext } = arg;
|
|
381
|
+
const rawText = schema.text;
|
|
382
|
+
if (rootElement.parentElement) {
|
|
383
|
+
// remove the outline for the whole schema, we'll apply outlines on each individual variable field instead
|
|
384
|
+
rootElement.parentElement.style.outline = '';
|
|
385
|
+
}
|
|
386
|
+
const variables = value
|
|
387
|
+
? JSON.parse(value) || {}
|
|
388
|
+
: {};
|
|
389
|
+
const substitutedText = fontSizePxWidget.substituteVariables(rawText, variables, pageContext);
|
|
390
|
+
const font = options?.font || helper.getDefaultFont();
|
|
391
|
+
const fontKitFont = await index.getFontKitFont(schema.fontName, font, _cache);
|
|
392
|
+
const textBlock = index.buildStyledTextContainer(arg, fontKitFont, substitutedText);
|
|
393
|
+
// Tokenize rawText into static text, simple-identifier variables (editable), and
|
|
394
|
+
// complex expressions (evaluated, static). This supports both {name} and {price * 1.1}.
|
|
395
|
+
const tokens = tokenizeTemplate(rawText, variables, pageContext);
|
|
396
|
+
// Find variables that have inline editable spans (standalone {var} tokens)
|
|
397
|
+
const inlineVarNames = new Set(tokens.filter((t) => t.type === 'simpleVar')
|
|
398
|
+
.map(t => t.name));
|
|
399
|
+
// Variables that only appear inside expressions — need separate input fields
|
|
400
|
+
const expressionOnlyVars = (schema.variables || []).filter(v => !inlineVarNames.has(v));
|
|
401
|
+
if (expressionOnlyVars.length > 0) {
|
|
402
|
+
const varSection = document.createElement('div');
|
|
403
|
+
Object.assign(varSection.style, {
|
|
404
|
+
width: '100%',
|
|
405
|
+
padding: '2px 4px',
|
|
406
|
+
boxSizing: 'border-box',
|
|
407
|
+
display: 'flex',
|
|
408
|
+
flexWrap: 'wrap',
|
|
409
|
+
gap: '4px',
|
|
410
|
+
marginBottom: '2px',
|
|
411
|
+
});
|
|
412
|
+
for (const varName of expressionOnlyVars) {
|
|
413
|
+
const row = document.createElement('div');
|
|
414
|
+
Object.assign(row.style, { display: 'flex', alignItems: 'center', gap: '2px' });
|
|
415
|
+
const label = document.createElement('label');
|
|
416
|
+
label.textContent = varName + ':';
|
|
417
|
+
Object.assign(label.style, {
|
|
418
|
+
fontSize: '9px', whiteSpace: 'nowrap', color: theme.colorText,
|
|
419
|
+
});
|
|
420
|
+
const input = document.createElement('input');
|
|
421
|
+
input.type = 'text';
|
|
422
|
+
input.value = variables[varName] ?? '';
|
|
423
|
+
Object.assign(input.style, {
|
|
424
|
+
width: '60px',
|
|
425
|
+
fontSize: '9px',
|
|
426
|
+
border: `1px dashed ${theme.colorPrimary}`,
|
|
427
|
+
borderRadius: '2px',
|
|
428
|
+
padding: '1px 3px',
|
|
429
|
+
outline: 'none',
|
|
430
|
+
});
|
|
431
|
+
input.addEventListener('input', (e) => {
|
|
432
|
+
variables[varName] = e.target.value;
|
|
433
|
+
if (onChange)
|
|
434
|
+
onChange({ key: 'content', value: JSON.stringify(variables) });
|
|
435
|
+
refreshExpressionSpans(textBlock, variables, pageContext);
|
|
436
|
+
});
|
|
437
|
+
row.appendChild(label);
|
|
438
|
+
row.appendChild(input);
|
|
439
|
+
varSection.appendChild(row);
|
|
440
|
+
}
|
|
441
|
+
rootElement.insertBefore(varSection, textBlock);
|
|
442
|
+
}
|
|
443
|
+
for (const token of tokens) {
|
|
444
|
+
if (token.type === 'text') {
|
|
445
|
+
const span = document.createElement('span');
|
|
446
|
+
span.style.letterSpacing = 'inherit';
|
|
447
|
+
span.textContent = token.text;
|
|
448
|
+
textBlock.appendChild(span);
|
|
449
|
+
}
|
|
450
|
+
else if (token.type === 'simpleVar') {
|
|
451
|
+
const varName = token.name;
|
|
452
|
+
const span = document.createElement('span');
|
|
453
|
+
span.style.outline = `${theme.colorPrimary} dashed 1px`;
|
|
454
|
+
index.makeElementPlainTextContentEditable(span);
|
|
455
|
+
span.textContent = variables[varName] ?? '';
|
|
456
|
+
span.addEventListener('blur', (e) => {
|
|
457
|
+
const newValue = e.target.textContent || '';
|
|
458
|
+
if (newValue !== variables[varName]) {
|
|
459
|
+
variables[varName] = newValue;
|
|
460
|
+
if (onChange)
|
|
461
|
+
onChange({ key: 'content', value: JSON.stringify(variables) });
|
|
462
|
+
if (stopEditing)
|
|
463
|
+
stopEditing();
|
|
464
|
+
refreshExpressionSpans(textBlock, variables, pageContext);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
textBlock.appendChild(span);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
// Complex expression — show evaluated result as static span with data-expr for re-evaluation
|
|
471
|
+
const span = document.createElement('span');
|
|
472
|
+
span.style.letterSpacing = 'inherit';
|
|
473
|
+
span.textContent = token.evaluated;
|
|
474
|
+
span.setAttribute('data-expr', token.raw);
|
|
475
|
+
textBlock.appendChild(span);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
// Simple identifier regex — matches {name}, {orderId}, etc. but NOT {price * 1.1}
|
|
480
|
+
const SIMPLE_VAR_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
481
|
+
/** Tokenize a template string into static text, simple-identifier vars, and complex expressions. */
|
|
482
|
+
const tokenizeTemplate = (rawText, variables, pageCtx) => {
|
|
483
|
+
const tokens = [];
|
|
484
|
+
let i = 0;
|
|
485
|
+
while (i < rawText.length) {
|
|
486
|
+
if (rawText[i] !== '{') {
|
|
487
|
+
const start = i;
|
|
488
|
+
while (i < rawText.length && rawText[i] !== '{')
|
|
489
|
+
i++;
|
|
490
|
+
tokens.push({ type: 'text', text: rawText.slice(start, i) });
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
// Find matching closing brace (handle nested braces)
|
|
494
|
+
let depth = 1;
|
|
495
|
+
let j = i + 1;
|
|
496
|
+
while (j < rawText.length && depth > 0) {
|
|
497
|
+
if (rawText[j] === '{')
|
|
498
|
+
depth++;
|
|
499
|
+
else if (rawText[j] === '}')
|
|
500
|
+
depth--;
|
|
501
|
+
j++;
|
|
502
|
+
}
|
|
503
|
+
const inner = rawText.slice(i + 1, j - 1);
|
|
504
|
+
if (SIMPLE_VAR_RE.test(inner)) {
|
|
505
|
+
tokens.push({ type: 'simpleVar', name: inner });
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
// Evaluate the expression using substituteVariables on just this segment
|
|
509
|
+
const evaluated = fontSizePxWidget.substituteVariables(`{${inner}}`, variables, pageCtx);
|
|
510
|
+
tokens.push({ type: 'expression', evaluated, raw: inner });
|
|
511
|
+
}
|
|
512
|
+
i = j;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return tokens;
|
|
516
|
+
};
|
|
517
|
+
/** Reserved names — JS globals and expression builtins (must match propPanel's set) */
|
|
518
|
+
const RESERVED_NAMES = new Set([
|
|
519
|
+
'true', 'false', 'null', 'undefined', 'typeof', 'instanceof', 'in',
|
|
520
|
+
'void', 'delete', 'new', 'this', 'NaN', 'Infinity',
|
|
521
|
+
'Math', 'String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'JSON',
|
|
522
|
+
'isNaN', 'parseFloat', 'parseInt', 'decodeURI', 'decodeURIComponent',
|
|
523
|
+
'encodeURI', 'encodeURIComponent', 'date', 'dateTime',
|
|
524
|
+
'currentPage', 'totalPages',
|
|
525
|
+
]);
|
|
526
|
+
/** Count unique user-defined variable identifiers in all {...} blocks */
|
|
527
|
+
const countUniqueVariableNames = (content) => {
|
|
528
|
+
const blockRegex = /\{([^{}]+)\}/g;
|
|
529
|
+
const allIds = new Set();
|
|
530
|
+
let blockMatch;
|
|
531
|
+
while ((blockMatch = blockRegex.exec(content)) !== null) {
|
|
532
|
+
const cleaned = blockMatch[1].replace(/'[^']*'|"[^"]*"|`[^`]*`/g, '');
|
|
533
|
+
const tokenRegex = /\.?[a-zA-Z_$][a-zA-Z0-9_$]*/g;
|
|
534
|
+
let m;
|
|
535
|
+
while ((m = tokenRegex.exec(cleaned)) !== null) {
|
|
536
|
+
const token = m[0];
|
|
537
|
+
if (!token.startsWith('.') && !RESERVED_NAMES.has(token)) {
|
|
538
|
+
allIds.add(token);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return allIds.size;
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* An optimisation to try to minimise jank while typing.
|
|
546
|
+
* Only check whether variables were modified based on certain key presses.
|
|
547
|
+
* Regex would otherwise be performed on every key press (which isn't terrible, but this code helps).
|
|
548
|
+
*/
|
|
549
|
+
const keyPressShouldBeChecked = (event) => {
|
|
550
|
+
if (event.key === 'ArrowUp' ||
|
|
551
|
+
event.key === 'ArrowDown' ||
|
|
552
|
+
event.key === 'ArrowLeft' ||
|
|
553
|
+
event.key === 'ArrowRight') {
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
const selection = window.getSelection();
|
|
557
|
+
const contenteditable = event.target;
|
|
558
|
+
const isCursorAtEnd = selection?.focusOffset === contenteditable?.textContent?.length;
|
|
559
|
+
if (isCursorAtEnd) {
|
|
560
|
+
return event.key === '}' || event.key === 'Backspace' || event.key === 'Delete';
|
|
561
|
+
}
|
|
562
|
+
const isCursorAtStart = selection?.anchorOffset === 0;
|
|
563
|
+
if (isCursorAtStart) {
|
|
564
|
+
return event.key === '{' || event.key === 'Backspace' || event.key === 'Delete';
|
|
565
|
+
}
|
|
566
|
+
return true;
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const schema$3 = {
|
|
570
|
+
pdf: pdfRender$2,
|
|
571
|
+
ui: uiRender$2,
|
|
572
|
+
propPanel: propPanel$1,
|
|
573
|
+
icon: index.createSvgStr(lucide.Type),
|
|
574
|
+
uninterruptedEditMode: true,
|
|
116
575
|
};
|
|
117
576
|
|
|
118
577
|
const isValidSVG = (svgString) => {
|
|
@@ -224,12 +683,12 @@ const getBarcodeCacheKey = (schema, value) => {
|
|
|
224
683
|
};
|
|
225
684
|
const pdfRender$1 = async (arg) => {
|
|
226
685
|
const { value, schema, pdfDoc, page, _cache } = arg;
|
|
227
|
-
if (!
|
|
686
|
+
if (!fontSizePxWidget.validateBarcodeInput(schema.type, value))
|
|
228
687
|
return;
|
|
229
688
|
const inputBarcodeCacheKey = getBarcodeCacheKey(schema, value);
|
|
230
689
|
let image = _cache.get(inputBarcodeCacheKey);
|
|
231
690
|
if (!image) {
|
|
232
|
-
const imageBuf = await
|
|
691
|
+
const imageBuf = await fontSizePxWidget.createBarCode({
|
|
233
692
|
...schema,
|
|
234
693
|
type: schema.type,
|
|
235
694
|
input: value,
|
|
@@ -243,11 +702,11 @@ const pdfRender$1 = async (arg) => {
|
|
|
243
702
|
};
|
|
244
703
|
|
|
245
704
|
const defaultColors = {
|
|
246
|
-
backgroundColor:
|
|
247
|
-
barColor:
|
|
705
|
+
backgroundColor: fontSizePxWidget.DEFAULT_BARCODE_BG_COLOR,
|
|
706
|
+
barColor: fontSizePxWidget.DEFAULT_BARCODE_COLOR,
|
|
248
707
|
};
|
|
249
|
-
const defaultTextColors = { textColor:
|
|
250
|
-
const defaultIncludetext = { includetext:
|
|
708
|
+
const defaultTextColors = { textColor: fontSizePxWidget.DEFAULT_BARCODE_COLOR };
|
|
709
|
+
const defaultIncludetext = { includetext: fontSizePxWidget.DEFAULT_BARCODE_INCLUDETEXT };
|
|
251
710
|
const position = { x: 0, y: 0 };
|
|
252
711
|
const default40x20 = { width: 40, height: 20 };
|
|
253
712
|
const barcodeDefaults = [
|
|
@@ -255,7 +714,7 @@ const barcodeDefaults = [
|
|
|
255
714
|
defaultSchema: {
|
|
256
715
|
name: '',
|
|
257
716
|
type: 'qrcode',
|
|
258
|
-
content: '
|
|
717
|
+
content: '',
|
|
259
718
|
position,
|
|
260
719
|
...defaultColors,
|
|
261
720
|
width: 30,
|
|
@@ -485,7 +944,7 @@ const blobToDataURL = (blob) => new Promise((resolve, reject) => {
|
|
|
485
944
|
reader.readAsDataURL(blob);
|
|
486
945
|
});
|
|
487
946
|
const createBarcodeImage = async (schema, value) => {
|
|
488
|
-
const imageBuf = await
|
|
947
|
+
const imageBuf = await fontSizePxWidget.createBarCode({
|
|
489
948
|
...schema,
|
|
490
949
|
input: value,
|
|
491
950
|
});
|
|
@@ -551,7 +1010,7 @@ const uiRender$1 = async (arg) => {
|
|
|
551
1010
|
if (!value)
|
|
552
1011
|
return;
|
|
553
1012
|
try {
|
|
554
|
-
if (!
|
|
1013
|
+
if (!fontSizePxWidget.validateBarcodeInput(schema.type, value))
|
|
555
1014
|
throw new Error('[@campxdev/schemas/barcodes] Invalid barcode input');
|
|
556
1015
|
const imgElm = await createBarcodeImageElm(schema, value);
|
|
557
1016
|
container.appendChild(imgElm);
|
|
@@ -562,7 +1021,7 @@ const uiRender$1 = async (arg) => {
|
|
|
562
1021
|
}
|
|
563
1022
|
};
|
|
564
1023
|
|
|
565
|
-
const barcodes =
|
|
1024
|
+
const barcodes = fontSizePxWidget.BARCODE_TYPES.reduce((acc, type) => Object.assign(acc, {
|
|
566
1025
|
[type]: {
|
|
567
1026
|
pdf: pdfRender$1,
|
|
568
1027
|
ui: uiRender$1,
|
|
@@ -571,8 +1030,8 @@ const barcodes = index$1.BARCODE_TYPES.reduce((acc, type) => Object.assign(acc,
|
|
|
571
1030
|
},
|
|
572
1031
|
}), {});
|
|
573
1032
|
|
|
574
|
-
const rectanglePdfRender =
|
|
575
|
-
const cellPdfRender =
|
|
1033
|
+
const rectanglePdfRender = fontSizePxWidget.rectangle.pdf;
|
|
1034
|
+
const cellPdfRender = fontSizePxWidget.cellSchema.pdf;
|
|
576
1035
|
async function drawCell(arg, cell) {
|
|
577
1036
|
await cellPdfRender({
|
|
578
1037
|
...arg,
|
|
@@ -686,7 +1145,7 @@ function createButton(options) {
|
|
|
686
1145
|
button.onclick = options.onClick;
|
|
687
1146
|
return button;
|
|
688
1147
|
}
|
|
689
|
-
const cellUiRender =
|
|
1148
|
+
const cellUiRender = fontSizePxWidget.cellSchema.ui;
|
|
690
1149
|
const convertToCellStyle = (styles) => ({
|
|
691
1150
|
fontName: styles.fontName,
|
|
692
1151
|
alignment: styles.alignment,
|
|
@@ -1261,7 +1720,7 @@ const getPlugin = ({ type, icon }) => {
|
|
|
1261
1720
|
justifyContent: index.mapVerticalAlignToFlex(index.VERTICAL_ALIGN_MIDDLE),
|
|
1262
1721
|
};
|
|
1263
1722
|
Object.assign(textElement.style, textElementStyle);
|
|
1264
|
-
await textSchema.ui({
|
|
1723
|
+
await index.textSchema.ui({
|
|
1265
1724
|
...arg,
|
|
1266
1725
|
rootElement: textElement,
|
|
1267
1726
|
mode: 'viewer',
|
|
@@ -1372,7 +1831,7 @@ const getPlugin = ({ type, icon }) => {
|
|
|
1372
1831
|
if (!value)
|
|
1373
1832
|
return void 0;
|
|
1374
1833
|
const locale = getAirDatepickerLocale(schema.locale || options.lang || defaultLocale);
|
|
1375
|
-
return textSchema.pdf({
|
|
1834
|
+
return index.textSchema.pdf({
|
|
1376
1835
|
...arg,
|
|
1377
1836
|
value: getFmtValue(value, type, schema, locale),
|
|
1378
1837
|
schema: {
|
|
@@ -1609,7 +2068,7 @@ const addOptions = (props) => {
|
|
|
1609
2068
|
const schema$2 = {
|
|
1610
2069
|
ui: async (arg) => {
|
|
1611
2070
|
const { schema, value, onChange, rootElement, mode } = arg;
|
|
1612
|
-
await textSchema.ui(Object.assign(arg, { mode: 'viewer' }));
|
|
2071
|
+
await index.textSchema.ui(Object.assign(arg, { mode: 'viewer' }));
|
|
1613
2072
|
if (mode !== 'viewer' && !(mode === 'form' && schema.readOnly)) {
|
|
1614
2073
|
const buttonWidth = 30;
|
|
1615
2074
|
const selectButton = document.createElement('button');
|
|
@@ -1653,9 +2112,9 @@ const schema$2 = {
|
|
|
1653
2112
|
rootElement.appendChild(selectElement);
|
|
1654
2113
|
}
|
|
1655
2114
|
},
|
|
1656
|
-
pdf: textSchema.pdf,
|
|
2115
|
+
pdf: index.textSchema.pdf,
|
|
1657
2116
|
propPanel: {
|
|
1658
|
-
...textSchema.propPanel,
|
|
2117
|
+
...index.textSchema.propPanel,
|
|
1659
2118
|
widgets: { ...index.propPanel.widgets, addOptions },
|
|
1660
2119
|
schema: (propPanelProps) => {
|
|
1661
2120
|
if (typeof index.propPanel.schema !== 'function') {
|
|
@@ -1677,7 +2136,7 @@ const schema$2 = {
|
|
|
1677
2136
|
};
|
|
1678
2137
|
},
|
|
1679
2138
|
defaultSchema: {
|
|
1680
|
-
...textSchema.propPanel.defaultSchema,
|
|
2139
|
+
...index.textSchema.propPanel.defaultSchema,
|
|
1681
2140
|
type: 'select',
|
|
1682
2141
|
content: 'option1',
|
|
1683
2142
|
options: ['option1', 'option2'],
|
|
@@ -1822,23 +2281,26 @@ const schema = {
|
|
|
1822
2281
|
|
|
1823
2282
|
exports.builtInPlugins = index.builtInPlugins;
|
|
1824
2283
|
exports.getDynamicHeightsForTable = index.getDynamicHeightsForTable;
|
|
1825
|
-
exports.
|
|
1826
|
-
exports.
|
|
1827
|
-
exports.
|
|
1828
|
-
exports.
|
|
1829
|
-
exports.
|
|
1830
|
-
exports.
|
|
1831
|
-
exports.
|
|
1832
|
-
exports.
|
|
1833
|
-
exports.
|
|
1834
|
-
exports.
|
|
2284
|
+
exports.richText = index.schema;
|
|
2285
|
+
exports.singleVariableText = index.schema$1;
|
|
2286
|
+
exports.text = index.textSchema;
|
|
2287
|
+
exports.dynamicBarcode = fontSizePxWidget.dynamicBarcode;
|
|
2288
|
+
exports.dynamicQrCode = fontSizePxWidget.dynamicQrCode;
|
|
2289
|
+
exports.dynamicTable = fontSizePxWidget.dynamicTableSchema;
|
|
2290
|
+
exports.ellipse = fontSizePxWidget.ellipse;
|
|
2291
|
+
exports.fontSizePxWidget = fontSizePxWidget.fontSizePxWidget;
|
|
2292
|
+
exports.image = fontSizePxWidget.imageSchema;
|
|
2293
|
+
exports.line = fontSizePxWidget.lineSchema;
|
|
2294
|
+
exports.rectangle = fontSizePxWidget.rectangle;
|
|
2295
|
+
exports.signature = fontSizePxWidget.signature;
|
|
2296
|
+
exports.variableBarcodes = fontSizePxWidget.variableBarcodes;
|
|
1835
2297
|
exports.barcodes = barcodes;
|
|
1836
2298
|
exports.checkbox = schema;
|
|
1837
2299
|
exports.date = date;
|
|
1838
2300
|
exports.dateTime = dateTime;
|
|
2301
|
+
exports.multiVariableText = schema$3;
|
|
1839
2302
|
exports.radioGroup = schema$1;
|
|
1840
2303
|
exports.select = schema$2;
|
|
1841
2304
|
exports.svg = svgSchema;
|
|
1842
2305
|
exports.table = tableSchema;
|
|
1843
|
-
exports.text = textSchema;
|
|
1844
2306
|
exports.time = time;
|