react-id-card-generator 1.0.18 → 1.0.20

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.
@@ -108,10 +108,10 @@ const IDCardPreview = ({ template, data, side, scale = 1, showGrid = false, onFi
108
108
  cursor: editable ? 'move' : 'default',
109
109
  outline: isSelected ? '2px solid #2196F3' : 'none',
110
110
  outlineOffset: '1px',
111
- display: field.type === 'text' ? 'flex' : undefined,
111
+ display: field.type === 'text' || field.type === 'qrcode' ? 'flex' : undefined,
112
112
  flexDirection: field.type === 'text' ? flexDirection : undefined,
113
- justifyContent: field.type === 'text' ? justifyContent : undefined,
114
- alignItems: field.type === 'text' ? alignItems : undefined,
113
+ justifyContent: field.type === 'text' ? justifyContent : field.type === 'qrcode' ? 'center' : undefined,
114
+ alignItems: field.type === 'text' ? alignItems : field.type === 'qrcode' ? 'center' : undefined,
115
115
  };
116
116
  const handleClick = (e) => {
117
117
  e.stopPropagation();
@@ -222,11 +222,16 @@ const IDCardPreview = ({ template, data, side, scale = 1, showGrid = false, onFi
222
222
  ? generateQrCodeFromFields(data, field.qrFields, qrSize)
223
223
  : '';
224
224
  return qrDataUrl ? (jsx("img", { src: qrDataUrl, alt: "QR Code", "data-qr-code-image": "true", style: {
225
- width: '100%',
226
- height: '100%',
225
+ width: `${qrSize}px`,
226
+ height: `${qrSize}px`,
227
227
  display: 'block',
228
228
  pointerEvents: 'none',
229
229
  backgroundColor: 'transparent',
230
+ objectFit: 'contain',
231
+ objectPosition: 'center',
232
+ maxWidth: '100%',
233
+ maxHeight: '100%',
234
+ flexShrink: 0,
230
235
  } })) : (jsx("div", { style: {
231
236
  width: '100%',
232
237
  height: '100%',
@@ -1 +1 @@
1
- {"version":3,"file":"IDCardPreview.js","sources":["../../../src/components/IDCardPreview.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport type { IDCardPreviewProps, FieldMapping } from '../types';\nimport { fieldStyleToCSS, positionToStyle } from '../utils/styleUtils';\nimport { generateQrCodeFromFields, getQrCodeSize } from '../core/qrUtils';\n\r\n/**\r\n * IDCardPreview - Interactive card preview component\r\n * \r\n * Renders an ID card with:\r\n * - Field selection and highlighting\r\n * - Resize handles for selected fields\r\n * - Grid overlay for alignment\r\n * - Dynamic field rendering based on type\r\n * \r\n * Used inside the designer for visual editing\r\n */\r\nexport const IDCardPreview: React.FC<IDCardPreviewProps> = ({\r\n template,\r\n data,\r\n side,\r\n scale = 1,\r\n showGrid = false,\r\n onFieldSelect,\r\n selectedFieldId,\r\n editable = true,\r\n onFieldUpdate,\r\n}: IDCardPreviewProps) => {\r\n // Get only fields that belong to the current side\r\n const fieldsToRender = useMemo(() => {\r\n return template.fields.filter((field: FieldMapping) => field.side === side);\r\n }, [template.fields, side]);\r\n\r\n // Get background for current side\r\n const backgroundImage = side === 'front' ? template.backgrounds.front : template.backgrounds.back;\r\n\r\n // Render resize handles for selected field\r\n const renderResizeHandles = (field: FieldMapping) => {\r\n if (field.id !== selectedFieldId || !editable) return null;\r\n\r\n const handles = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\r\n const handleStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n width: '8px',\r\n height: '8px',\r\n backgroundColor: '#2196F3',\r\n border: '1px solid #fff',\r\n borderRadius: '2px',\r\n zIndex: 1000,\r\n };\r\n\r\n const positions: Record<string, React.CSSProperties> = {\r\n n: { top: '-4px', left: '50%', transform: 'translateX(-50%)', cursor: 'n-resize' },\r\n s: { bottom: '-4px', left: '50%', transform: 'translateX(-50%)', cursor: 's-resize' },\r\n e: { right: '-4px', top: '50%', transform: 'translateY(-50%)', cursor: 'e-resize' },\r\n w: { left: '-4px', top: '50%', transform: 'translateY(-50%)', cursor: 'w-resize' },\r\n ne: { top: '-4px', right: '-4px', cursor: 'ne-resize' },\r\n nw: { top: '-4px', left: '-4px', cursor: 'nw-resize' },\r\n se: { bottom: '-4px', right: '-4px', cursor: 'se-resize' },\r\n sw: { bottom: '-4px', left: '-4px', cursor: 'sw-resize' },\r\n };\r\n\r\n return handles.map((handle) => (\r\n <div\r\n key={handle}\r\n className={`resize-handle resize-handle-${handle}`}\r\n style={{ ...handleStyle, ...positions[handle] }}\r\n data-handle={handle}\r\n />\r\n ));\r\n };\r\n\r\n // Render a single field\r\n const renderField = (field: FieldMapping) => {\r\n const posStyle = positionToStyle(field.position);\r\n let fieldStyle = fieldStyleToCSS(field.style);\r\n const isSelected = field.id === selectedFieldId;\r\n\r\n // Determine flex direction and alignment for text fields\r\n let flexDirection: 'column' | 'row' = 'column';\r\n if (field.type === 'text' && field.style?.labelPosition === 'left') flexDirection = 'row';\r\n\r\n // Map verticalAlign and textAlign to flex properties\r\n let justifyContent: 'flex-start' | 'center' | 'flex-end' = 'flex-start';\r\n let alignItems: 'flex-start' | 'center' | 'flex-end' = 'flex-start';\r\n if (field.type === 'text') {\r\n const textAlign = field.style?.textTextAlign || field.style?.textAlign || 'left';\r\n const verticalAlign = field.style?.verticalAlign || 'top';\r\n \r\n if (flexDirection === 'column') {\r\n // For column: verticalAlign controls justifyContent (vertical), textAlign controls alignItems (horizontal)\r\n if (verticalAlign === 'middle') justifyContent = 'center';\r\n else if (verticalAlign === 'bottom') justifyContent = 'flex-end';\r\n else justifyContent = 'flex-start';\r\n \r\n if (textAlign === 'center') alignItems = 'center';\r\n else if (textAlign === 'right') alignItems = 'flex-end';\r\n else alignItems = 'flex-start';\r\n } else {\r\n // For row: verticalAlign controls alignItems (vertical)\r\n if (verticalAlign === 'middle') alignItems = 'center';\r\n else if (verticalAlign === 'bottom') alignItems = 'flex-end';\r\n else alignItems = 'flex-start';\r\n // justifyContent can be left as default for row\r\n }\r\n }\r\n\r\n // Remove fontSize for label fields so text field font size does not affect them\r\n if (field.type === 'label' && fieldStyle.fontSize) {\r\n const { fontSize, ...rest } = fieldStyle;\r\n fieldStyle = rest;\r\n }\r\n\r\n // Remove textTransform from container for text and label fields - applied directly to spans\r\n if ((field.type === 'text' || field.type === 'label') && fieldStyle.textTransform) {\r\n const { textTransform, ...rest } = fieldStyle;\r\n fieldStyle = rest;\r\n }\r\n\r\n const combinedStyle: React.CSSProperties = {\n ...posStyle,\n ...fieldStyle,\n backgroundColor: field.type === 'qrcode' ? 'transparent' : fieldStyle.backgroundColor,\n zIndex: field.zIndex || 1,\n boxSizing: 'border-box',\n overflow: 'hidden',\n cursor: editable ? 'move' : 'default',\r\n outline: isSelected ? '2px solid #2196F3' : 'none',\r\n outlineOffset: '1px',\r\n display: field.type === 'text' ? 'flex' : undefined,\r\n flexDirection: field.type === 'text' ? flexDirection : undefined,\r\n justifyContent: field.type === 'text' ? justifyContent : undefined,\r\n alignItems: field.type === 'text' ? alignItems : undefined,\r\n };\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n onFieldSelect?.(field);\r\n };\r\n\r\n const value = data[field.fieldKey];\r\n\r\n const content = (() => {\r\n\r\n switch (field.type) {\r\n case 'text': {\r\n // Label style properties\r\n // Always use px for label font size, default 12px\r\n let labelFontSize = field.style?.labelFontSize;\r\n if (!labelFontSize || labelFontSize === '0.75em') {\r\n labelFontSize = '12px';\r\n } else if (!labelFontSize.endsWith('px')) {\r\n const num = parseFloat(labelFontSize);\r\n if (!isNaN(num)) labelFontSize = num + 'px';\r\n else labelFontSize = '12px';\r\n }\r\n const labelFontWeight = field.style?.labelFontWeight || 'normal';\r\n const labelColor = field.style?.labelColor || '#666666';\r\n const labelTextTransform = field.style?.labelTextTransform || 'none';\r\n const labelTextAlign = field.style?.labelTextAlign || 'left';\r\n \r\n // Text/value style properties (separate from label)\r\n const textFontSize = field.style?.textFontSize || field.style?.fontSize || '1em';\r\n const textFontWeight = field.style?.textFontWeight || field.style?.fontWeight || 'normal';\r\n const textColor = field.style?.textColor || field.style?.color || '#000000';\r\n const textTextTransform = field.style?.textTransform || 'none';\r\n const textTextAlign = field.style?.textTextAlign || field.style?.textAlign || 'left';\r\n \r\n // For row layout (label on left), use individual span alignment\r\n if (field.style?.labelPosition === 'left') {\r\n let labelAlignSelf: React.CSSProperties['alignSelf'] = 'flex-start';\r\n if (field.style?.labelVerticalAlign === 'middle') labelAlignSelf = 'center';\r\n else if (field.style?.labelVerticalAlign === 'bottom') labelAlignSelf = 'flex-end';\r\n \r\n let textAlignSelf: React.CSSProperties['alignSelf'] = 'flex-start';\r\n if (field.style?.textVerticalAlign === 'middle') textAlignSelf = 'center';\r\n else if (field.style?.textVerticalAlign === 'bottom') textAlignSelf = 'flex-end';\r\n \r\n const labelStyle: React.CSSProperties = { \r\n fontSize: labelFontSize, \r\n fontWeight: labelFontWeight, \r\n color: labelColor, \r\n opacity: 0.7, \r\n marginRight: 8, \r\n alignSelf: labelAlignSelf, \r\n whiteSpace: 'nowrap', \r\n textTransform: labelTextTransform \r\n };\r\n \r\n const textStyle: React.CSSProperties = { \r\n textTransform: textTextTransform, \r\n fontSize: textFontSize, \r\n fontWeight: textFontWeight, \r\n color: textColor, \r\n alignSelf: textAlignSelf\r\n };\r\n \r\n return (\r\n <>\r\n {field.label && (\r\n <span style={labelStyle}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span style={{ width: '100%', textAlign: textTextAlign }}>\r\n <span style={textStyle}>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </span>\r\n </>\r\n );\r\n } else {\r\n // For column layout (label on top), let container alignment handle positioning\r\n const labelStyle: React.CSSProperties = { \r\n fontSize: labelFontSize, \r\n fontWeight: labelFontWeight, \r\n color: labelColor, \r\n opacity: 0.7, \r\n textTransform: labelTextTransform,\r\n width: '100%',\r\n textAlign: labelTextAlign\r\n };\r\n \r\n const textStyle: React.CSSProperties = { \r\n textTransform: textTextTransform, \r\n fontSize: textFontSize, \r\n fontWeight: textFontWeight, \r\n color: textColor,\r\n width: '100%',\r\n textAlign: textTextAlign\r\n };\r\n \r\n return (\r\n <>\r\n {field.label && (\r\n <span style={labelStyle}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span style={textStyle}>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </>\r\n );\r\n }\r\n }\r\n\r\n case 'label': {\r\n const labelTextTransform = field.style?.labelTextTransform || 'none';\r\n return <span style={{ textTransform: labelTextTransform }}>{field.staticText || field.label || 'Label'}</span>;\r\n }\r\n\r\n case 'image':\r\n return value ? (\r\n <img\r\n src={String(value)}\r\n alt={field.label || 'Image'}\r\n style={{\r\n width: '100%',\r\n display: 'block',\r\n }}\r\n />\r\n ) : (\r\n <div\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n backgroundColor: '#e0e0e0',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n fontSize: '10px',\r\n color: '#666',\r\n flexDirection: 'column',\r\n }}\r\n >\r\n <span>📷</span>\r\n <span>{field.label || 'Photo'}</span>\r\n </div>\r\n );\r\n\n case 'qrcode':\n const qrSize = getQrCodeSize(field.position.width, field.position.height);\n const qrDataUrl = field.qrFields?.length\n ? generateQrCodeFromFields(data, field.qrFields, qrSize)\n : '';\n\r\n return qrDataUrl ? (\r\n <img\n src={qrDataUrl}\n alt=\"QR Code\"\n data-qr-code-image=\"true\"\n style={{\n width: '100%',\n height: '100%',\n display: 'block',\n pointerEvents: 'none',\n backgroundColor: 'transparent',\n }}\n />\n ) : (\n <div\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n backgroundColor: '#f0f0f0',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n fontSize: '10px',\r\n color: '#666',\r\n border: '1px dashed #ccc',\r\n }}\r\n >\r\n QR Code\r\n </div>\r\n );\r\n\r\n default:\r\n return null;\r\n }\r\n })();\r\n\r\n return (\r\n <div\r\n key={field.id}\r\n id={`preview-${field.id}`}\r\n style={combinedStyle}\r\n onClick={handleClick}\r\n className={`preview-field preview-field-${field.type} ${isSelected ? 'selected' : ''}`}\r\n >\r\n {content}\r\n {renderResizeHandles(field)}\r\n </div>\r\n );\r\n };\r\n\r\n // Card container style\r\n const cardStyle: React.CSSProperties = {\r\n width: Math.round(template.cardSize.width),\r\n height: Math.round(template.cardSize.height),\r\n position: 'relative',\r\n overflow: 'hidden',\r\n backgroundColor: backgroundImage ? 'transparent' : '#f5f5f5',\r\n border: '1px solid #ddd',\r\n };\r\n\r\n // Grid overlay style\r\n const gridStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n backgroundImage: showGrid\r\n ? 'linear-gradient(to right, rgba(0,0,0,0.05) 1px, transparent 1px), linear-gradient(to bottom, rgba(0,0,0,0.05) 1px, transparent 1px)'\r\n : 'none',\r\n backgroundSize: '10px 10px',\r\n pointerEvents: 'none',\r\n zIndex: 999,\r\n };\r\n\r\n const handleContainerClick = () => {\r\n onFieldSelect?.(null);\r\n };\r\n\r\n return (\r\n <div\r\n className=\"idcard-preview-container\"\r\n style={cardStyle}\r\n onClick={handleContainerClick}\r\n >\r\n {/* Background */}\r\n {backgroundImage && (\r\n <img\r\n src={backgroundImage}\r\n alt={`${side} background`}\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: `${Math.round(template.cardSize.width)}px`,\r\n height: `${Math.round(template.cardSize.height)}px`,\r\n zIndex: 0,\r\n pointerEvents: 'none',\r\n }}\r\n />\r\n )}\r\n\r\n {/* Fields */}\r\n {fieldsToRender.map(renderField)}\r\n\r\n {/* Grid overlay */}\r\n {showGrid && <div style={gridStyle} />}\r\n\r\n {/* Empty state */}\r\n {!backgroundImage && fieldsToRender.length === 0 && (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n textAlign: 'center',\r\n color: '#999',\r\n fontSize: '14px',\r\n }}\r\n >\r\n <p>Upload a background image</p>\r\n <p>and add fields to get started</p>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"IDCardPreview.js","sources":["../../../src/components/IDCardPreview.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\nimport type { IDCardPreviewProps, FieldMapping } from '../types';\nimport { fieldStyleToCSS, positionToStyle } from '../utils/styleUtils';\nimport { generateQrCodeFromFields, getQrCodeSize } from '../core/qrUtils';\n\r\n/**\r\n * IDCardPreview - Interactive card preview component\r\n * \r\n * Renders an ID card with:\r\n * - Field selection and highlighting\r\n * - Resize handles for selected fields\r\n * - Grid overlay for alignment\r\n * - Dynamic field rendering based on type\r\n * \r\n * Used inside the designer for visual editing\r\n */\r\nexport const IDCardPreview: React.FC<IDCardPreviewProps> = ({\r\n template,\r\n data,\r\n side,\r\n scale = 1,\r\n showGrid = false,\r\n onFieldSelect,\r\n selectedFieldId,\r\n editable = true,\r\n onFieldUpdate,\r\n}: IDCardPreviewProps) => {\r\n // Get only fields that belong to the current side\r\n const fieldsToRender = useMemo(() => {\r\n return template.fields.filter((field: FieldMapping) => field.side === side);\r\n }, [template.fields, side]);\r\n\r\n // Get background for current side\r\n const backgroundImage = side === 'front' ? template.backgrounds.front : template.backgrounds.back;\r\n\r\n // Render resize handles for selected field\r\n const renderResizeHandles = (field: FieldMapping) => {\r\n if (field.id !== selectedFieldId || !editable) return null;\r\n\r\n const handles = ['n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw'];\r\n const handleStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n width: '8px',\r\n height: '8px',\r\n backgroundColor: '#2196F3',\r\n border: '1px solid #fff',\r\n borderRadius: '2px',\r\n zIndex: 1000,\r\n };\r\n\r\n const positions: Record<string, React.CSSProperties> = {\r\n n: { top: '-4px', left: '50%', transform: 'translateX(-50%)', cursor: 'n-resize' },\r\n s: { bottom: '-4px', left: '50%', transform: 'translateX(-50%)', cursor: 's-resize' },\r\n e: { right: '-4px', top: '50%', transform: 'translateY(-50%)', cursor: 'e-resize' },\r\n w: { left: '-4px', top: '50%', transform: 'translateY(-50%)', cursor: 'w-resize' },\r\n ne: { top: '-4px', right: '-4px', cursor: 'ne-resize' },\r\n nw: { top: '-4px', left: '-4px', cursor: 'nw-resize' },\r\n se: { bottom: '-4px', right: '-4px', cursor: 'se-resize' },\r\n sw: { bottom: '-4px', left: '-4px', cursor: 'sw-resize' },\r\n };\r\n\r\n return handles.map((handle) => (\r\n <div\r\n key={handle}\r\n className={`resize-handle resize-handle-${handle}`}\r\n style={{ ...handleStyle, ...positions[handle] }}\r\n data-handle={handle}\r\n />\r\n ));\r\n };\r\n\r\n // Render a single field\r\n const renderField = (field: FieldMapping) => {\r\n const posStyle = positionToStyle(field.position);\r\n let fieldStyle = fieldStyleToCSS(field.style);\r\n const isSelected = field.id === selectedFieldId;\r\n\r\n // Determine flex direction and alignment for text fields\r\n let flexDirection: 'column' | 'row' = 'column';\r\n if (field.type === 'text' && field.style?.labelPosition === 'left') flexDirection = 'row';\r\n\r\n // Map verticalAlign and textAlign to flex properties\r\n let justifyContent: 'flex-start' | 'center' | 'flex-end' = 'flex-start';\r\n let alignItems: 'flex-start' | 'center' | 'flex-end' = 'flex-start';\r\n if (field.type === 'text') {\r\n const textAlign = field.style?.textTextAlign || field.style?.textAlign || 'left';\r\n const verticalAlign = field.style?.verticalAlign || 'top';\r\n \r\n if (flexDirection === 'column') {\r\n // For column: verticalAlign controls justifyContent (vertical), textAlign controls alignItems (horizontal)\r\n if (verticalAlign === 'middle') justifyContent = 'center';\r\n else if (verticalAlign === 'bottom') justifyContent = 'flex-end';\r\n else justifyContent = 'flex-start';\r\n \r\n if (textAlign === 'center') alignItems = 'center';\r\n else if (textAlign === 'right') alignItems = 'flex-end';\r\n else alignItems = 'flex-start';\r\n } else {\r\n // For row: verticalAlign controls alignItems (vertical)\r\n if (verticalAlign === 'middle') alignItems = 'center';\r\n else if (verticalAlign === 'bottom') alignItems = 'flex-end';\r\n else alignItems = 'flex-start';\r\n // justifyContent can be left as default for row\r\n }\r\n }\r\n\r\n // Remove fontSize for label fields so text field font size does not affect them\r\n if (field.type === 'label' && fieldStyle.fontSize) {\r\n const { fontSize, ...rest } = fieldStyle;\r\n fieldStyle = rest;\r\n }\r\n\r\n // Remove textTransform from container for text and label fields - applied directly to spans\r\n if ((field.type === 'text' || field.type === 'label') && fieldStyle.textTransform) {\r\n const { textTransform, ...rest } = fieldStyle;\r\n fieldStyle = rest;\r\n }\r\n\r\n const combinedStyle: React.CSSProperties = {\n ...posStyle,\n ...fieldStyle,\n backgroundColor: field.type === 'qrcode' ? 'transparent' : fieldStyle.backgroundColor,\n zIndex: field.zIndex || 1,\n boxSizing: 'border-box',\n overflow: 'hidden',\n cursor: editable ? 'move' : 'default',\n outline: isSelected ? '2px solid #2196F3' : 'none',\n outlineOffset: '1px',\n display: field.type === 'text' || field.type === 'qrcode' ? 'flex' : undefined,\n flexDirection: field.type === 'text' ? flexDirection : undefined,\n justifyContent: field.type === 'text' ? justifyContent : field.type === 'qrcode' ? 'center' : undefined,\n alignItems: field.type === 'text' ? alignItems : field.type === 'qrcode' ? 'center' : undefined,\n };\r\n\r\n const handleClick = (e: React.MouseEvent) => {\r\n e.stopPropagation();\r\n onFieldSelect?.(field);\r\n };\r\n\r\n const value = data[field.fieldKey];\r\n\r\n const content = (() => {\r\n\r\n switch (field.type) {\r\n case 'text': {\r\n // Label style properties\r\n // Always use px for label font size, default 12px\r\n let labelFontSize = field.style?.labelFontSize;\r\n if (!labelFontSize || labelFontSize === '0.75em') {\r\n labelFontSize = '12px';\r\n } else if (!labelFontSize.endsWith('px')) {\r\n const num = parseFloat(labelFontSize);\r\n if (!isNaN(num)) labelFontSize = num + 'px';\r\n else labelFontSize = '12px';\r\n }\r\n const labelFontWeight = field.style?.labelFontWeight || 'normal';\r\n const labelColor = field.style?.labelColor || '#666666';\r\n const labelTextTransform = field.style?.labelTextTransform || 'none';\r\n const labelTextAlign = field.style?.labelTextAlign || 'left';\r\n \r\n // Text/value style properties (separate from label)\r\n const textFontSize = field.style?.textFontSize || field.style?.fontSize || '1em';\r\n const textFontWeight = field.style?.textFontWeight || field.style?.fontWeight || 'normal';\r\n const textColor = field.style?.textColor || field.style?.color || '#000000';\r\n const textTextTransform = field.style?.textTransform || 'none';\r\n const textTextAlign = field.style?.textTextAlign || field.style?.textAlign || 'left';\r\n \r\n // For row layout (label on left), use individual span alignment\r\n if (field.style?.labelPosition === 'left') {\r\n let labelAlignSelf: React.CSSProperties['alignSelf'] = 'flex-start';\r\n if (field.style?.labelVerticalAlign === 'middle') labelAlignSelf = 'center';\r\n else if (field.style?.labelVerticalAlign === 'bottom') labelAlignSelf = 'flex-end';\r\n \r\n let textAlignSelf: React.CSSProperties['alignSelf'] = 'flex-start';\r\n if (field.style?.textVerticalAlign === 'middle') textAlignSelf = 'center';\r\n else if (field.style?.textVerticalAlign === 'bottom') textAlignSelf = 'flex-end';\r\n \r\n const labelStyle: React.CSSProperties = { \r\n fontSize: labelFontSize, \r\n fontWeight: labelFontWeight, \r\n color: labelColor, \r\n opacity: 0.7, \r\n marginRight: 8, \r\n alignSelf: labelAlignSelf, \r\n whiteSpace: 'nowrap', \r\n textTransform: labelTextTransform \r\n };\r\n \r\n const textStyle: React.CSSProperties = { \r\n textTransform: textTextTransform, \r\n fontSize: textFontSize, \r\n fontWeight: textFontWeight, \r\n color: textColor, \r\n alignSelf: textAlignSelf\r\n };\r\n \r\n return (\r\n <>\r\n {field.label && (\r\n <span style={labelStyle}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span style={{ width: '100%', textAlign: textTextAlign }}>\r\n <span style={textStyle}>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </span>\r\n </>\r\n );\r\n } else {\r\n // For column layout (label on top), let container alignment handle positioning\r\n const labelStyle: React.CSSProperties = { \r\n fontSize: labelFontSize, \r\n fontWeight: labelFontWeight, \r\n color: labelColor, \r\n opacity: 0.7, \r\n textTransform: labelTextTransform,\r\n width: '100%',\r\n textAlign: labelTextAlign\r\n };\r\n \r\n const textStyle: React.CSSProperties = { \r\n textTransform: textTextTransform, \r\n fontSize: textFontSize, \r\n fontWeight: textFontWeight, \r\n color: textColor,\r\n width: '100%',\r\n textAlign: textTextAlign\r\n };\r\n \r\n return (\r\n <>\r\n {field.label && (\r\n <span style={labelStyle}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span style={textStyle}>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </>\r\n );\r\n }\r\n }\r\n\r\n case 'label': {\r\n const labelTextTransform = field.style?.labelTextTransform || 'none';\r\n return <span style={{ textTransform: labelTextTransform }}>{field.staticText || field.label || 'Label'}</span>;\r\n }\r\n\r\n case 'image':\r\n return value ? (\r\n <img\r\n src={String(value)}\r\n alt={field.label || 'Image'}\r\n style={{\r\n width: '100%',\r\n display: 'block',\r\n }}\r\n />\r\n ) : (\r\n <div\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n backgroundColor: '#e0e0e0',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n fontSize: '10px',\r\n color: '#666',\r\n flexDirection: 'column',\r\n }}\r\n >\r\n <span>📷</span>\r\n <span>{field.label || 'Photo'}</span>\r\n </div>\r\n );\r\n\n case 'qrcode':\n const qrSize = getQrCodeSize(field.position.width, field.position.height);\n const qrDataUrl = field.qrFields?.length\n ? generateQrCodeFromFields(data, field.qrFields, qrSize)\n : '';\n\r\n return qrDataUrl ? (\r\n <img\n src={qrDataUrl}\n alt=\"QR Code\"\n data-qr-code-image=\"true\"\n style={{\n width: `${qrSize}px`,\n height: `${qrSize}px`,\n display: 'block',\n pointerEvents: 'none',\n backgroundColor: 'transparent',\n objectFit: 'contain',\n objectPosition: 'center',\n maxWidth: '100%',\n maxHeight: '100%',\n flexShrink: 0,\n }}\n />\n ) : (\n <div\r\n style={{\r\n width: '100%',\r\n height: '100%',\r\n backgroundColor: '#f0f0f0',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n fontSize: '10px',\r\n color: '#666',\r\n border: '1px dashed #ccc',\r\n }}\r\n >\r\n QR Code\r\n </div>\r\n );\r\n\r\n default:\r\n return null;\r\n }\r\n })();\r\n\r\n return (\r\n <div\r\n key={field.id}\r\n id={`preview-${field.id}`}\r\n style={combinedStyle}\r\n onClick={handleClick}\r\n className={`preview-field preview-field-${field.type} ${isSelected ? 'selected' : ''}`}\r\n >\r\n {content}\r\n {renderResizeHandles(field)}\r\n </div>\r\n );\r\n };\r\n\r\n // Card container style\r\n const cardStyle: React.CSSProperties = {\r\n width: Math.round(template.cardSize.width),\r\n height: Math.round(template.cardSize.height),\r\n position: 'relative',\r\n overflow: 'hidden',\r\n backgroundColor: backgroundImage ? 'transparent' : '#f5f5f5',\r\n border: '1px solid #ddd',\r\n };\r\n\r\n // Grid overlay style\r\n const gridStyle: React.CSSProperties = {\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: '100%',\r\n height: '100%',\r\n backgroundImage: showGrid\r\n ? 'linear-gradient(to right, rgba(0,0,0,0.05) 1px, transparent 1px), linear-gradient(to bottom, rgba(0,0,0,0.05) 1px, transparent 1px)'\r\n : 'none',\r\n backgroundSize: '10px 10px',\r\n pointerEvents: 'none',\r\n zIndex: 999,\r\n };\r\n\r\n const handleContainerClick = () => {\r\n onFieldSelect?.(null);\r\n };\r\n\r\n return (\r\n <div\r\n className=\"idcard-preview-container\"\r\n style={cardStyle}\r\n onClick={handleContainerClick}\r\n >\r\n {/* Background */}\r\n {backgroundImage && (\r\n <img\r\n src={backgroundImage}\r\n alt={`${side} background`}\r\n style={{\r\n position: 'absolute',\r\n top: 0,\r\n left: 0,\r\n width: `${Math.round(template.cardSize.width)}px`,\r\n height: `${Math.round(template.cardSize.height)}px`,\r\n zIndex: 0,\r\n pointerEvents: 'none',\r\n }}\r\n />\r\n )}\r\n\r\n {/* Fields */}\r\n {fieldsToRender.map(renderField)}\r\n\r\n {/* Grid overlay */}\r\n {showGrid && <div style={gridStyle} />}\r\n\r\n {/* Empty state */}\r\n {!backgroundImage && fieldsToRender.length === 0 && (\r\n <div\r\n style={{\r\n position: 'absolute',\r\n top: '50%',\r\n left: '50%',\r\n transform: 'translate(-50%, -50%)',\r\n textAlign: 'center',\r\n color: '#999',\r\n fontSize: '14px',\r\n }}\r\n >\r\n <p>Upload a background image</p>\r\n <p>and add fields to get started</p>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
@@ -36,11 +36,17 @@ function hasQrFieldValue(value) {
36
36
  return true;
37
37
  }
38
38
  function getQrCodeSize(width, height = width, scale = 1) {
39
- const scaledBaseSize = Math.max(Number.isFinite(width) ? width : 0, Number.isFinite(height) ? height : 0);
40
- if (scaledBaseSize <= 0) {
39
+ const normalizedWidth = Number.isFinite(width) ? width : 0;
40
+ const normalizedHeight = Number.isFinite(height) ? height : 0;
41
+ const hasWidth = normalizedWidth > 0;
42
+ const hasHeight = normalizedHeight > 0;
43
+ const baseSize = hasWidth && hasHeight
44
+ ? Math.min(normalizedWidth, normalizedHeight)
45
+ : Math.max(normalizedWidth, normalizedHeight);
46
+ if (baseSize <= 0) {
41
47
  return Math.max(MIN_QR_SIZE, Math.round(DEFAULT_QR_SIZE * normalizeQrScale(scale)));
42
48
  }
43
- return Math.max(MIN_QR_SIZE, Math.round(scaledBaseSize * normalizeQrScale(scale)));
49
+ return Math.max(MIN_QR_SIZE, Math.round(baseSize * normalizeQrScale(scale)));
44
50
  }
45
51
  /**
46
52
  * Generate a QR code as a base64 data URL
@@ -1 +1 @@
1
- {"version":3,"file":"qrUtils.js","sources":["../../../src/core/qrUtils.ts"],"sourcesContent":["import QRious from 'qrious';\nimport type { IDCardData } from '../types';\n\nconst DEFAULT_QR_SIZE = 200;\nconst MIN_QR_SIZE = 32;\n\nfunction normalizeQrScale(scale = 1): number {\n return Number.isFinite(scale) && scale > 0 ? scale : 1;\n}\n\nfunction resolveQrTargetElement(\n elementId: string,\n container: Document | HTMLElement\n): HTMLElement | null {\n if (container instanceof HTMLElement) {\n if (container.id === elementId) {\n return container;\n }\n\n return container.querySelector<HTMLElement>(`#${elementId}`);\n }\n\n return container.getElementById(elementId);\n}\n\nfunction resolveQrImageElement(targetElement: HTMLElement | null): HTMLImageElement | null {\n if (!targetElement) {\n return null;\n }\n\n if (targetElement instanceof HTMLImageElement) {\n return targetElement;\n }\n\n return targetElement.querySelector<HTMLImageElement>('img[data-qr-code-image=\"true\"], img');\n}\n\nfunction hasQrFieldValue(value: IDCardData[string]): boolean {\n if (value === undefined) {\n return false;\n }\n\n if (typeof value === 'string') {\n return value.trim().length > 0;\n }\n\n if (Array.isArray(value)) {\n return value.some((item) => String(item).trim().length > 0);\n }\n\n return true;\n}\n\nexport function getQrCodeSize(\n width: number,\n height = width,\n scale = 1\n): number {\n const scaledBaseSize = Math.max(\n Number.isFinite(width) ? width : 0,\n Number.isFinite(height) ? height : 0\n );\n\n if (scaledBaseSize <= 0) {\n return Math.max(MIN_QR_SIZE, Math.round(DEFAULT_QR_SIZE * normalizeQrScale(scale)));\n }\n\n return Math.max(MIN_QR_SIZE, Math.round(scaledBaseSize * normalizeQrScale(scale)));\n}\n\n/**\n * Generate a QR code as a base64 data URL\n * @param data - Data to encode in the QR code (will be JSON stringified)\n * @param size - QR code size in pixels\n * @param options - Customization options (error correction level, colors, etc.)\r\n * @returns Base64 PNG data URL\r\n */\r\nexport function generateQrCodeDataURL(\n data: Record<string, unknown>,\n size = DEFAULT_QR_SIZE,\n options?: {\n level?: 'L' | 'M' | 'Q' | 'H'; // Error correction level\n padding?: number;\n background?: string;\n foreground?: string;\n }\n): string {\n const background = options?.background ?? 'transparent';\n const isTransparentBackground = background === 'transparent';\n const qr = new (QRious as any)({\n value: JSON.stringify(data),\n size,\n level: options?.level || 'H', // High error correction by default\n padding: options?.padding ?? 0,\n background,\n backgroundAlpha: isTransparentBackground ? 0 : 1,\n foreground: options?.foreground || 'black',\n foregroundAlpha: 1,\n });\n return qr.toDataURL('image/png');\n}\n\r\n/**\r\n * Generate QR code from specific fields in the card data\r\n * Only includes fields that exist in the data object\r\n * @param data - Full card data object\r\n * @param fields - Array of field keys to include in QR code\r\n * @param size - QR code size in pixels\r\n * @returns Base64 PNG data URL\r\n */\r\nexport function generateQrCodeFromFields(\n data: IDCardData,\n fields: string[],\n size = DEFAULT_QR_SIZE\n): string {\n const qrData = getQrCodeDataFromFields(data, fields);\n if (Object.keys(qrData).length === 0) {\n return '';\n }\n\n return generateQrCodeDataURL(qrData, size);\n}\n\nexport function getQrCodeDataFromFields(\n data: IDCardData,\n fields: string[]\n): Record<string, unknown> {\n // Build object with only the specified fields that actually contain data\n const qrData: Record<string, unknown> = {};\n\n for (const field of fields) {\n const value = data[field];\n if (hasQrFieldValue(value)) {\n qrData[field] = value;\n }\n }\n\n return qrData;\n}\n\r\n/**\r\n * Set QR code image src on a DOM element\r\n */\r\nexport function setQrCodeOnElement(\n elementId: string,\n data: Record<string, unknown>,\n size = DEFAULT_QR_SIZE,\n container: Document | HTMLElement = document\n): boolean {\n const qrDataURL = generateQrCodeDataURL(data, size);\n const targetElement = resolveQrTargetElement(elementId, container);\n const imgTag = resolveQrImageElement(targetElement);\n\n if (imgTag) {\n imgTag.setAttribute('src', qrDataURL);\n imgTag.setAttribute('data-qr-code-image', 'true');\n imgTag.style.display = 'block';\n imgTag.style.opacity = '1';\n imgTag.style.visibility = 'visible';\n imgTag.style.backgroundColor = 'transparent';\n if (targetElement) {\n targetElement.style.backgroundColor = 'transparent';\n }\n return true;\n }\n return false;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"qrUtils.js","sources":["../../../src/core/qrUtils.ts"],"sourcesContent":["import QRious from 'qrious';\nimport type { IDCardData } from '../types';\n\nconst DEFAULT_QR_SIZE = 200;\nconst MIN_QR_SIZE = 32;\n\nfunction normalizeQrScale(scale = 1): number {\n return Number.isFinite(scale) && scale > 0 ? scale : 1;\n}\n\nfunction resolveQrTargetElement(\n elementId: string,\n container: Document | HTMLElement\n): HTMLElement | null {\n if (container instanceof HTMLElement) {\n if (container.id === elementId) {\n return container;\n }\n\n return container.querySelector<HTMLElement>(`#${elementId}`);\n }\n\n return container.getElementById(elementId);\n}\n\nfunction resolveQrImageElement(targetElement: HTMLElement | null): HTMLImageElement | null {\n if (!targetElement) {\n return null;\n }\n\n if (targetElement instanceof HTMLImageElement) {\n return targetElement;\n }\n\n return targetElement.querySelector<HTMLImageElement>('img[data-qr-code-image=\"true\"], img');\n}\n\nfunction hasQrFieldValue(value: IDCardData[string]): boolean {\n if (value === undefined) {\n return false;\n }\n\n if (typeof value === 'string') {\n return value.trim().length > 0;\n }\n\n if (Array.isArray(value)) {\n return value.some((item) => String(item).trim().length > 0);\n }\n\n return true;\n}\n\nexport function getQrCodeSize(\n width: number,\n height = width,\n scale = 1\n): number {\n const normalizedWidth = Number.isFinite(width) ? width : 0;\n const normalizedHeight = Number.isFinite(height) ? height : 0;\n const hasWidth = normalizedWidth > 0;\n const hasHeight = normalizedHeight > 0;\n const baseSize = hasWidth && hasHeight\n ? Math.min(normalizedWidth, normalizedHeight)\n : Math.max(normalizedWidth, normalizedHeight);\n\n if (baseSize <= 0) {\n return Math.max(MIN_QR_SIZE, Math.round(DEFAULT_QR_SIZE * normalizeQrScale(scale)));\n }\n\n return Math.max(MIN_QR_SIZE, Math.round(baseSize * normalizeQrScale(scale)));\n}\n\n/**\n * Generate a QR code as a base64 data URL\n * @param data - Data to encode in the QR code (will be JSON stringified)\n * @param size - QR code size in pixels\n * @param options - Customization options (error correction level, colors, etc.)\r\n * @returns Base64 PNG data URL\r\n */\r\nexport function generateQrCodeDataURL(\n data: Record<string, unknown>,\n size = DEFAULT_QR_SIZE,\n options?: {\n level?: 'L' | 'M' | 'Q' | 'H'; // Error correction level\n padding?: number;\n background?: string;\n foreground?: string;\n }\n): string {\n const background = options?.background ?? 'transparent';\n const isTransparentBackground = background === 'transparent';\n const qr = new (QRious as any)({\n value: JSON.stringify(data),\n size,\n level: options?.level || 'H', // High error correction by default\n padding: options?.padding ?? 0,\n background,\n backgroundAlpha: isTransparentBackground ? 0 : 1,\n foreground: options?.foreground || 'black',\n foregroundAlpha: 1,\n });\n return qr.toDataURL('image/png');\n}\n\r\n/**\r\n * Generate QR code from specific fields in the card data\r\n * Only includes fields that exist in the data object\r\n * @param data - Full card data object\r\n * @param fields - Array of field keys to include in QR code\r\n * @param size - QR code size in pixels\r\n * @returns Base64 PNG data URL\r\n */\r\nexport function generateQrCodeFromFields(\n data: IDCardData,\n fields: string[],\n size = DEFAULT_QR_SIZE\n): string {\n const qrData = getQrCodeDataFromFields(data, fields);\n if (Object.keys(qrData).length === 0) {\n return '';\n }\n\n return generateQrCodeDataURL(qrData, size);\n}\n\nexport function getQrCodeDataFromFields(\n data: IDCardData,\n fields: string[]\n): Record<string, unknown> {\n // Build object with only the specified fields that actually contain data\n const qrData: Record<string, unknown> = {};\n\n for (const field of fields) {\n const value = data[field];\n if (hasQrFieldValue(value)) {\n qrData[field] = value;\n }\n }\n\n return qrData;\n}\n\r\n/**\r\n * Set QR code image src on a DOM element\r\n */\r\nexport function setQrCodeOnElement(\n elementId: string,\n data: Record<string, unknown>,\n size = DEFAULT_QR_SIZE,\n container: Document | HTMLElement = document\n): boolean {\n const qrDataURL = generateQrCodeDataURL(data, size);\n const targetElement = resolveQrTargetElement(elementId, container);\n const imgTag = resolveQrImageElement(targetElement);\n\n if (imgTag) {\n imgTag.setAttribute('src', qrDataURL);\n imgTag.setAttribute('data-qr-code-image', 'true');\n imgTag.style.display = 'block';\n imgTag.style.opacity = '1';\n imgTag.style.visibility = 'visible';\n imgTag.style.backgroundColor = 'transparent';\n if (targetElement) {\n targetElement.style.backgroundColor = 'transparent';\n }\n return true;\n }\n return false;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-id-card-generator",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "A flexible React library for designing, configuring, and generating ID cards with drag-and-drop field positioning",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",