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