react-id-card-generator 1.0.8 → 1.0.10
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/components/IDCardDesigner.js +122 -21
- package/dist/cjs/components/IDCardDesigner.js.map +1 -1
- package/dist/cjs/components/IDCardPreview.js +7 -2
- package/dist/cjs/components/IDCardPreview.js.map +1 -1
- package/dist/esm/components/IDCardDesigner.js +122 -21
- package/dist/esm/components/IDCardDesigner.js.map +1 -1
- package/dist/esm/components/IDCardPreview.js +7 -2
- package/dist/esm/components/IDCardPreview.js.map +1 -1
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
29
29
|
const { template, updateField, addField, removeField, setBackground, setCardSize, setOrientation, setSides, isValid, validationErrors, } = useIDCardTemplate(initialTemplate);
|
|
30
30
|
const [activeSide, setActiveSide] = useState('front');
|
|
31
31
|
const [selectedFieldId, setSelectedFieldId] = useState(null);
|
|
32
|
+
const [lineHeightInputFocused, setLineHeightInputFocused] = useState(false);
|
|
32
33
|
const [showGrid, setShowGrid] = useState(true);
|
|
33
34
|
const [isDragging, setIsDragging] = useState(false);
|
|
34
35
|
const [isResizing, setIsResizing] = useState(false);
|
|
@@ -61,6 +62,24 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
61
62
|
const handleFieldSelect = useCallback((field) => {
|
|
62
63
|
setSelectedFieldId(field?.id || null);
|
|
63
64
|
}, []);
|
|
65
|
+
// Duplicate selected text field
|
|
66
|
+
const handleDuplicateField = useCallback(() => {
|
|
67
|
+
if (!selectedField || selectedField.type !== 'text')
|
|
68
|
+
return;
|
|
69
|
+
const { id, fieldKey, position, ...rest } = selectedField;
|
|
70
|
+
const newField = {
|
|
71
|
+
...rest,
|
|
72
|
+
id: generateId('field'),
|
|
73
|
+
fieldKey: `field_${Date.now()}`,
|
|
74
|
+
position: {
|
|
75
|
+
...position,
|
|
76
|
+
x: Math.min(position.x + 20, template.cardSize.width - position.width),
|
|
77
|
+
y: Math.min(position.y + 20, template.cardSize.height - position.height),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
addField(newField);
|
|
81
|
+
setSelectedFieldId(newField.id);
|
|
82
|
+
}, [selectedField, addField, setSelectedFieldId, template.cardSize.width, template.cardSize.height]);
|
|
64
83
|
// Handle adding new field
|
|
65
84
|
const handleAddField = useCallback((type) => {
|
|
66
85
|
const newField = {
|
|
@@ -163,6 +182,7 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
163
182
|
if (isDragging) {
|
|
164
183
|
const deltaX = e.clientX - dragStartRef.current.x;
|
|
165
184
|
const deltaY = e.clientY - dragStartRef.current.y;
|
|
185
|
+
// Unified drag logic for all field types
|
|
166
186
|
const newX = Math.max(0, Math.min(template.cardSize.width - selectedField.position.width, dragStartRef.current.fieldX + deltaX));
|
|
167
187
|
const newY = Math.max(0, Math.min(template.cardSize.height - selectedField.position.height, dragStartRef.current.fieldY + deltaY));
|
|
168
188
|
updateField(selectedField.id, {
|
|
@@ -177,19 +197,50 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
177
197
|
let newHeight = height;
|
|
178
198
|
let newX = fieldX;
|
|
179
199
|
let newY = fieldY;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
200
|
+
// Enforce fixed aspect ratio for QR code fields
|
|
201
|
+
if (selectedField.type === 'qrcode') {
|
|
202
|
+
// Use the larger delta for both width and height
|
|
203
|
+
let delta = 0;
|
|
204
|
+
if (handle.includes('e') || handle.includes('w')) {
|
|
205
|
+
delta = deltaX;
|
|
206
|
+
}
|
|
207
|
+
else if (handle.includes('s') || handle.includes('n')) {
|
|
208
|
+
delta = deltaY;
|
|
209
|
+
}
|
|
210
|
+
if (handle.includes('e')) {
|
|
211
|
+
newWidth = Math.max(20, width + delta);
|
|
212
|
+
newHeight = newWidth;
|
|
213
|
+
}
|
|
214
|
+
if (handle.includes('w')) {
|
|
215
|
+
newWidth = Math.max(20, width - delta);
|
|
216
|
+
newHeight = newWidth;
|
|
217
|
+
newX = Math.max(0, fieldX + delta);
|
|
218
|
+
}
|
|
219
|
+
if (handle.includes('s')) {
|
|
220
|
+
newHeight = Math.max(20, height + delta);
|
|
221
|
+
newWidth = newHeight;
|
|
222
|
+
}
|
|
223
|
+
if (handle.includes('n')) {
|
|
224
|
+
newHeight = Math.max(20, height - delta);
|
|
225
|
+
newWidth = newHeight;
|
|
226
|
+
newY = Math.max(0, fieldY + delta);
|
|
227
|
+
}
|
|
189
228
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
229
|
+
else {
|
|
230
|
+
if (handle.includes('e')) {
|
|
231
|
+
newWidth = Math.max(20, width + deltaX);
|
|
232
|
+
}
|
|
233
|
+
if (handle.includes('w')) {
|
|
234
|
+
newWidth = Math.max(20, width - deltaX);
|
|
235
|
+
newX = Math.max(0, fieldX + deltaX);
|
|
236
|
+
}
|
|
237
|
+
if (handle.includes('s')) {
|
|
238
|
+
newHeight = Math.max(20, height + deltaY);
|
|
239
|
+
}
|
|
240
|
+
if (handle.includes('n')) {
|
|
241
|
+
newHeight = Math.max(20, height - deltaY);
|
|
242
|
+
newY = Math.max(0, fieldY + deltaY);
|
|
243
|
+
}
|
|
193
244
|
}
|
|
194
245
|
updateField(selectedField.id, {
|
|
195
246
|
position: { x: newX, y: newY, width: newWidth, height: newHeight },
|
|
@@ -199,14 +250,19 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
199
250
|
const handleMouseUp = () => {
|
|
200
251
|
setIsDragging(false);
|
|
201
252
|
setIsResizing(false);
|
|
253
|
+
// Remove any accidental selection or pointer capture
|
|
254
|
+
window.getSelection?.()?.removeAllRanges?.();
|
|
255
|
+
if (previewContainerRef.current) {
|
|
256
|
+
previewContainerRef.current.releasePointerCapture?.(0);
|
|
257
|
+
}
|
|
202
258
|
};
|
|
203
259
|
if (isDragging || isResizing) {
|
|
204
260
|
document.addEventListener('mousemove', handleMouseMove);
|
|
205
|
-
|
|
261
|
+
window.addEventListener('mouseup', handleMouseUp);
|
|
206
262
|
}
|
|
207
263
|
return () => {
|
|
208
264
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
209
|
-
|
|
265
|
+
window.removeEventListener('mouseup', handleMouseUp);
|
|
210
266
|
};
|
|
211
267
|
}, [isDragging, isResizing, selectedField, updateField, template.cardSize]);
|
|
212
268
|
// Handle field property update
|
|
@@ -256,17 +312,62 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
256
312
|
}, [selectedFieldId, removeField]);
|
|
257
313
|
// Keyboard shortcuts
|
|
258
314
|
useEffect(() => {
|
|
315
|
+
// Listen for focus/blur on line height input
|
|
316
|
+
const lhInput = document.getElementById('line-height-input');
|
|
317
|
+
if (lhInput) {
|
|
318
|
+
lhInput.addEventListener('focus', () => { });
|
|
319
|
+
lhInput.addEventListener('blur', () => { });
|
|
320
|
+
}
|
|
259
321
|
const handleKeyDown = (e) => {
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
322
|
+
if (selectedFieldId) {
|
|
323
|
+
const field = template.fields.find(f => f.id === selectedFieldId);
|
|
324
|
+
// Arrow key movement for position
|
|
325
|
+
const step = e.shiftKey ? 5 : 1;
|
|
326
|
+
let dx = 0, dy = 0;
|
|
327
|
+
if (e.key === 'ArrowLeft')
|
|
328
|
+
dx = -step;
|
|
329
|
+
if (e.key === 'ArrowRight')
|
|
330
|
+
dx = step;
|
|
331
|
+
if (e.key === 'ArrowUp')
|
|
332
|
+
dy = -step;
|
|
333
|
+
if (e.key === 'ArrowDown')
|
|
334
|
+
dy = step;
|
|
335
|
+
if ((dx !== 0 || dy !== 0) && field) {
|
|
336
|
+
// If text field and Ctrl is pressed, adjust line height instead
|
|
337
|
+
if (field.type === 'text' && (e.ctrlKey || e.metaKey)) {
|
|
338
|
+
let lh = field.style?.lineHeight ? Number(field.style.lineHeight) : 1;
|
|
339
|
+
if (e.key === 'ArrowUp')
|
|
340
|
+
lh = Math.round((lh + 0.1) * 10) / 10;
|
|
341
|
+
if (e.key === 'ArrowDown')
|
|
342
|
+
lh = Math.max(0.1, Math.round((lh - 0.1) * 10) / 10);
|
|
343
|
+
updateField(field.id, {
|
|
344
|
+
style: { ...field.style, lineHeight: lh.toString() },
|
|
345
|
+
});
|
|
346
|
+
e.preventDefault();
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
let newX = Math.max(0, Math.min(template.cardSize.width - field.position.width, field.position.x + dx));
|
|
351
|
+
let newY = Math.max(0, Math.min(template.cardSize.height - field.position.height, field.position.y + dy));
|
|
352
|
+
updateField(field.id, {
|
|
353
|
+
position: { ...field.position, x: newX, y: newY },
|
|
354
|
+
});
|
|
355
|
+
e.preventDefault();
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// Delete and Escape
|
|
360
|
+
if (e.key === 'Delete') {
|
|
361
|
+
handleDeleteField();
|
|
362
|
+
}
|
|
363
|
+
if (e.key === 'Escape') {
|
|
364
|
+
setSelectedFieldId(null);
|
|
365
|
+
}
|
|
265
366
|
}
|
|
266
367
|
};
|
|
267
368
|
document.addEventListener('keydown', handleKeyDown);
|
|
268
369
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
269
|
-
}, [selectedFieldId, handleDeleteField]);
|
|
370
|
+
}, [selectedFieldId, handleDeleteField, template.fields, template.cardSize, updateField]);
|
|
270
371
|
return (jsxs("div", { className: `idcard-designer ${className}`, style: style, children: [jsxs("div", { className: "designer-header", children: [jsx("input", { type: "text", value: templateName, onChange: (e) => setTemplateName(e.target.value), className: "template-name-input", placeholder: "Template Name" }), jsxs("div", { className: "header-actions", children: [onCancel && (jsx("button", { onClick: onCancel, className: "btn btn-secondary", children: "Cancel" })), jsx("button", { onClick: handleSave, className: "btn btn-primary", disabled: !isValid, children: "Save Template" })] })] }), jsxs("div", { className: "designer-body", children: [jsxs("div", { className: "designer-sidebar left-sidebar", children: [jsx("h3", { children: "Add Fields" }), jsx("div", { className: "field-types", children: defaultFieldTypes.map((fieldType) => (jsxs("button", { onClick: () => handleAddField(fieldType.type), className: "field-type-btn", children: [jsx("span", { className: "field-icon", children: fieldType.icon }), jsx("span", { className: "field-label", children: fieldType.label })] }, fieldType.type))) }), jsx("h3", { children: "Card Settings" }), jsxs("div", { className: "card-settings", children: [jsxs("div", { className: "setting-group", children: [jsx("label", { children: "Orientation" }), jsxs("div", { className: "btn-group", children: [jsx("button", { className: `btn ${template.orientation === 'landscape' ? 'active' : ''}`, onClick: () => handleOrientationChange('landscape'), children: "Landscape" }), jsx("button", { className: `btn ${template.orientation === 'portrait' ? 'active' : ''}`, onClick: () => handleOrientationChange('portrait'), children: "Portrait" })] })] }), jsxs("div", { className: "setting-group", children: [jsx("label", { children: "Card Sides" }), jsxs("div", { className: "btn-group", children: [jsx("button", { className: `btn ${template.sides === 'single' ? 'active' : ''}`, onClick: () => handleSidesChange('single'), children: "Single" }), jsx("button", { className: `btn ${template.sides === 'double' ? 'active' : ''}`, onClick: () => handleSidesChange('double'), children: "Double" })] })] }), jsxs("div", { className: "setting-group", children: [jsx("label", { children: "Card Size (px)" }), jsxs("div", { className: "size-inputs", children: [jsx("input", { type: "number", value: template.cardSize.width, onChange: (e) => setCardSize({ ...template.cardSize, width: Number(e.target.value) }), placeholder: "Width" }), jsx("span", { children: "\u00D7" }), jsx("input", { type: "number", value: template.cardSize.height, onChange: (e) => setCardSize({ ...template.cardSize, height: Number(e.target.value) }), placeholder: "Height" })] })] })] }), jsx("h3", { children: "Background" }), jsxs("div", { className: "background-upload", children: [jsx("input", { type: "file", accept: "image/*", onChange: handleBackgroundUpload, id: "bg-upload", className: "file-input" }), jsxs("label", { htmlFor: "bg-upload", className: "btn btn-outline", children: ["Upload ", activeSide, " Background"] }), template.backgrounds[activeSide] && (jsx("button", { className: "btn btn-danger btn-sm", onClick: () => setBackground(activeSide, ''), children: "Remove" }))] })] }), jsxs("div", { className: "designer-preview", children: [jsxs("div", { className: "side-tabs", children: [jsx("button", { className: `tab ${activeSide === 'front' ? 'active' : ''}`, onClick: () => setActiveSide('front'), children: "Front" }), template.sides === 'double' && (jsx("button", { className: `tab ${activeSide === 'back' ? 'active' : ''}`, onClick: () => setActiveSide('back'), children: "Back" })), jsxs("label", { className: "grid-toggle", children: [jsx("input", { type: "checkbox", checked: showGrid, onChange: (e) => setShowGrid(e.target.checked) }), "Show Grid"] })] }), jsx("div", { ref: previewContainerRef, className: "preview-wrapper", onMouseDown: handleMouseDown, children: jsx(IDCardPreview, { template: template, data: sampleData, side: activeSide, showGrid: showGrid, onFieldSelect: handleFieldSelect, selectedFieldId: selectedFieldId, editable: true }) }), jsxs("div", { className: "fields-list", children: [jsxs("h4", { children: ["Fields on ", activeSide, " side:"] }), template.fields
|
|
271
372
|
.filter((f) => f.side === activeSide)
|
|
272
373
|
.map((field) => (jsxs("div", { className: `field-item ${field.id === selectedFieldId ? 'selected' : ''}`, onClick: () => setSelectedFieldId(field.id), children: [jsx("span", { className: "field-type-icon", children: defaultFieldTypes.find((t) => t.type === field.type)?.icon }), jsx("span", { className: "field-key", children: field.fieldKey }), jsx("button", { className: "delete-btn", onClick: (e) => {
|
|
@@ -274,7 +375,7 @@ const IDCardDesigner = ({ initialTemplate, onSave, onCancel, className = '', sty
|
|
|
274
375
|
removeField(field.id);
|
|
275
376
|
if (field.id === selectedFieldId)
|
|
276
377
|
setSelectedFieldId(null);
|
|
277
|
-
}, children: "\u00D7" })] }, field.id)))] })] }), jsxs("div", { className: "designer-sidebar right-sidebar", children: [jsx("h3", { children: "Field Properties" }), selectedField ? (jsxs("div", { className: "field-properties", children: [jsxs("div", { className: "property-group", children: [jsx("label", { children: "Field Key" }), jsx("input", { type: "text", value: selectedField.fieldKey, onChange: (e) => handleFieldPropertyUpdate('fieldKey', e.target.value) })] }), selectedField.type !== 'label' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label" }), jsx("input", { type: "text", value: selectedField.label || '', onChange: (e) => handleFieldPropertyUpdate('label', e.target.value) })] })), selectedField.type === 'text' && (jsxs(Fragment, { children: [jsxs("div", { style: { border: '1px solid #eee', borderRadius: 4, marginBottom: 12, padding: 8 }, children: [jsx("strong", { style: { fontSize: '1em', display: 'block', marginBottom: 6 }, children: "Label Style" }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Position" }), jsxs("select", { value: selectedField.style?.labelPosition || 'top', onChange: (e) => handleFieldPropertyUpdate('style.labelPosition', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "left", children: "Left (Side)" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Vertical Align" }), jsxs("select", { value: selectedField.style?.labelVerticalAlign || 'top', onChange: (e) => handleFieldPropertyUpdate('style.labelVerticalAlign', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "middle", children: "Middle" }), jsx("option", { value: "bottom", children: "Bottom" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Font Size" }), jsx("input", { type: "text", value: selectedField.style?.labelFontSize || '0.75em', onChange: (e) => handleFieldPropertyUpdate('style.labelFontSize', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Font Weight" }), jsxs("select", { value: selectedField.style?.labelFontWeight || 'normal', onChange: (e) => handleFieldPropertyUpdate('style.labelFontWeight', e.target.value), children: [jsx("option", { value: "normal", children: "Normal" }), jsx("option", { value: "bold", children: "Bold" }), jsx("option", { value: "100", children: "100" }), jsx("option", { value: "200", children: "200" }), jsx("option", { value: "300", children: "300" }), jsx("option", { value: "400", children: "400" }), jsx("option", { value: "500", children: "500" }), jsx("option", { value: "600", children: "600" }), jsx("option", { value: "700", children: "700" }), jsx("option", { value: "800", children: "800" }), jsx("option", { value: "900", children: "900" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Color" }), jsx("input", { type: "color", value: selectedField.style?.labelColor || '#666666', onChange: (e) => handleFieldPropertyUpdate('style.labelColor', e.target.value) })] })] }), jsxs("div", { style: { border: '1px solid #eee', borderRadius: 4, marginBottom: 12, padding: 8 }, children: [jsx("strong", { style: { fontSize: '1em', display: 'block', marginBottom: 6 }, children: "Text Style" }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Font Size" }), jsx("input", { type: "text", value: selectedField.style?.fontSize || '12px', onChange: (e) => handleFieldPropertyUpdate('style.fontSize', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Font Weight" }), jsxs("select", { value: selectedField.style?.fontWeight || 'normal', onChange: (e) => handleFieldPropertyUpdate('style.fontWeight', e.target.value), children: [jsx("option", { value: "normal", children: "Normal" }), jsx("option", { value: "bold", children: "Bold" }), jsx("option", { value: "100", children: "100" }), jsx("option", { value: "200", children: "200" }), jsx("option", { value: "300", children: "300" }), jsx("option", { value: "400", children: "400" }), jsx("option", { value: "500", children: "500" }), jsx("option", { value: "600", children: "600" }), jsx("option", { value: "700", children: "700" }), jsx("option", { value: "800", children: "800" }), jsx("option", { value: "900", children: "900" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Color" }), jsx("input", { type: "color", value: selectedField.style?.color || '#000000', onChange: (e) => handleFieldPropertyUpdate('style.color', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Align" }), jsxs("select", { value: selectedField.style?.textAlign || 'left', onChange: (e) => handleFieldPropertyUpdate('style.textAlign', e.target.value), children: [jsx("option", { value: "left", children: "Left" }), jsx("option", { value: "center", children: "Center" }), jsx("option", { value: "right", children: "Right" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Transform" }), jsxs("select", { value: selectedField.style?.textTransform || 'none', onChange: (e) => handleFieldPropertyUpdate('style.textTransform', e.target.value), children: [jsx("option", { value: "none", children: "None" }), jsx("option", { value: "uppercase", children: "UPPERCASE" }), jsx("option", { value: "lowercase", children: "lowercase" }), jsx("option", { value: "capitalize", children: "Capitalize" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Vertical Align" }), jsxs("select", { value: selectedField.style?.verticalAlign || 'top', onChange: (e) => handleFieldPropertyUpdate('style.verticalAlign', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "middle", children: "Middle" }), jsx("option", { value: "bottom", children: "Bottom" })] })] })] })] })), selectedField.type === 'label' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "Static Text" }), jsx("input", { type: "text", value: selectedField.staticText || '', onChange: (e) => handleFieldPropertyUpdate('staticText', e.target.value) })] })), selectedField.type === 'qrcode' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "QR Code Data Fields" }), jsx("small", { className: "hint", children: "Select which fields to encode in the QR code:" }), jsx("div", { style: { marginTop: '8px', maxHeight: '150px', overflowY: 'auto', border: '1px solid #ddd', borderRadius: '4px', padding: '8px' }, children: template.fields
|
|
378
|
+
}, children: "\u00D7" })] }, field.id)))] })] }), jsxs("div", { className: "designer-sidebar right-sidebar", children: [jsx("h3", { children: "Field Properties" }), selectedField ? (jsxs("div", { className: "field-properties", children: [jsxs("div", { className: "property-group", children: [jsx("label", { children: "Field Key" }), jsx("input", { type: "text", value: selectedField.fieldKey, onChange: (e) => handleFieldPropertyUpdate('fieldKey', e.target.value) })] }), selectedField.type !== 'label' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label" }), jsx("input", { type: "text", value: selectedField.label || '', onChange: (e) => handleFieldPropertyUpdate('label', e.target.value) })] })), selectedField.type === 'text' && (jsxs(Fragment, { children: [jsx("button", { className: "btn btn-outline btn-sm", style: { marginBottom: 12 }, onClick: handleDuplicateField, children: "Duplicate Field" }), jsxs("div", { style: { border: '1px solid #eee', borderRadius: 4, marginBottom: 12, padding: 8 }, children: [jsx("strong", { style: { fontSize: '1em', display: 'block', marginBottom: 6 }, children: "Label Style" }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Position" }), jsxs("select", { value: selectedField.style?.labelPosition || 'top', onChange: (e) => handleFieldPropertyUpdate('style.labelPosition', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "left", children: "Left (Side)" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Vertical Align" }), jsxs("select", { value: selectedField.style?.labelVerticalAlign || 'top', onChange: (e) => handleFieldPropertyUpdate('style.labelVerticalAlign', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "middle", children: "Middle" }), jsx("option", { value: "bottom", children: "Bottom" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Font Size" }), jsx("input", { type: "text", value: selectedField.style?.labelFontSize || '0.75em', onChange: (e) => handleFieldPropertyUpdate('style.labelFontSize', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Font Weight" }), jsxs("select", { value: selectedField.style?.labelFontWeight || 'normal', onChange: (e) => handleFieldPropertyUpdate('style.labelFontWeight', e.target.value), children: [jsx("option", { value: "normal", children: "Normal" }), jsx("option", { value: "bold", children: "Bold" }), jsx("option", { value: "100", children: "100" }), jsx("option", { value: "200", children: "200" }), jsx("option", { value: "300", children: "300" }), jsx("option", { value: "400", children: "400" }), jsx("option", { value: "500", children: "500" }), jsx("option", { value: "600", children: "600" }), jsx("option", { value: "700", children: "700" }), jsx("option", { value: "800", children: "800" }), jsx("option", { value: "900", children: "900" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Label Color" }), jsx("input", { type: "color", value: selectedField.style?.labelColor || '#666666', onChange: (e) => handleFieldPropertyUpdate('style.labelColor', e.target.value) })] })] }), jsxs("div", { style: { border: '1px solid #eee', borderRadius: 4, marginBottom: 12, padding: 8 }, children: [jsx("strong", { style: { fontSize: '1em', display: 'block', marginBottom: 6 }, children: "Text Style" }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Font Size" }), jsx("input", { type: "text", value: selectedField.style?.fontSize || '12px', onChange: (e) => handleFieldPropertyUpdate('style.fontSize', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Font Weight" }), jsxs("select", { value: selectedField.style?.fontWeight || 'normal', onChange: (e) => handleFieldPropertyUpdate('style.fontWeight', e.target.value), children: [jsx("option", { value: "normal", children: "Normal" }), jsx("option", { value: "bold", children: "Bold" }), jsx("option", { value: "100", children: "100" }), jsx("option", { value: "200", children: "200" }), jsx("option", { value: "300", children: "300" }), jsx("option", { value: "400", children: "400" }), jsx("option", { value: "500", children: "500" }), jsx("option", { value: "600", children: "600" }), jsx("option", { value: "700", children: "700" }), jsx("option", { value: "800", children: "800" }), jsx("option", { value: "900", children: "900" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Color" }), jsx("input", { type: "color", value: selectedField.style?.color || '#000000', onChange: (e) => handleFieldPropertyUpdate('style.color', e.target.value) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Align" }), jsxs("select", { value: selectedField.style?.textAlign || 'left', onChange: (e) => handleFieldPropertyUpdate('style.textAlign', e.target.value), children: [jsx("option", { value: "left", children: "Left" }), jsx("option", { value: "center", children: "Center" }), jsx("option", { value: "right", children: "Right" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Line Height" }), jsx("input", { id: "line-height-input", type: "number", min: 0.1, step: 0.1, value: selectedField.style?.lineHeight ? Number(selectedField.style.lineHeight) : 1, onChange: (e) => handleFieldPropertyUpdate('style.lineHeight', e.target.value), style: { width: 80 }, onFocus: () => setLineHeightInputFocused(true), onBlur: () => setLineHeightInputFocused(false) })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Text Transform" }), jsxs("select", { value: selectedField.style?.textTransform || 'none', onChange: (e) => handleFieldPropertyUpdate('style.textTransform', e.target.value), children: [jsx("option", { value: "none", children: "None" }), jsx("option", { value: "uppercase", children: "UPPERCASE" }), jsx("option", { value: "lowercase", children: "lowercase" }), jsx("option", { value: "capitalize", children: "Capitalize" })] })] }), jsxs("div", { className: "property-group", children: [jsx("label", { children: "Vertical Align" }), jsxs("select", { value: selectedField.style?.verticalAlign || 'top', onChange: (e) => handleFieldPropertyUpdate('style.verticalAlign', e.target.value), children: [jsx("option", { value: "top", children: "Top" }), jsx("option", { value: "middle", children: "Middle" }), jsx("option", { value: "bottom", children: "Bottom" })] })] })] })] })), selectedField.type === 'label' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "Static Text" }), jsx("input", { type: "text", value: selectedField.staticText || '', onChange: (e) => handleFieldPropertyUpdate('staticText', e.target.value) })] })), selectedField.type === 'qrcode' && (jsxs("div", { className: "property-group", children: [jsx("label", { children: "QR Code Data Fields" }), jsx("small", { className: "hint", children: "Select which fields to encode in the QR code:" }), jsx("div", { style: { marginTop: '8px', maxHeight: '150px', overflowY: 'auto', border: '1px solid #ddd', borderRadius: '4px', padding: '8px' }, children: template.fields
|
|
278
379
|
.filter(f => f.type !== 'qrcode' && f.type !== 'label')
|
|
279
380
|
.map((field) => (jsxs("label", { style: { display: 'block', marginBottom: '6px', cursor: 'pointer' }, children: [jsx("input", { type: "checkbox", checked: selectedField.qrFields?.includes(field.fieldKey) || false, onChange: (e) => {
|
|
280
381
|
const currentFields = selectedField.qrFields || [];
|