react-id-card-generator 1.0.3 → 1.0.5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"IDCardPreview.js","sources":["../../../src/components/IDCardPreview.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\r\nimport type { IDCardPreviewProps, FieldMapping } from '../types';\r\nimport { fieldStyleToCSS, positionToStyle } from '../utils/styleUtils';\r\nimport { generateQrCodeFromFields } from '../core/qrUtils';\r\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 const fieldStyle = fieldStyleToCSS(field.style);\r\n const isSelected = field.id === selectedFieldId;\r\n\r\n const combinedStyle: React.CSSProperties = {\r\n ...posStyle,\r\n ...fieldStyle,\r\n zIndex: field.zIndex || 1,\r\n boxSizing: 'border-box',\r\n overflow: 'hidden',\r\n cursor: editable ? 'move' : 'default',\r\n outline: isSelected ? '2px solid #2196F3' : 'none',\r\n outlineOffset: '1px',\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 switch (field.type) {\r\n case 'text':\r\n return (\r\n <>\r\n {field.label && (\r\n <span style={{ fontSize: '0.75em', display: 'block', opacity: 0.7 }}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </>\r\n );\r\n\r\n case 'label':\r\n return <span>{field.staticText || field.label || 'Label'}</span>;\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 height: '100%',\r\n objectFit: 'cover',\r\n objectPosition: 'center top',\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\r\n case 'qrcode':\r\n const qrDataUrl = field.qrFields\r\n ? generateQrCodeFromFields(data, field.qrFields, 200)\r\n : '';\r\n\r\n return qrDataUrl ? (\r\n <img\r\n src={qrDataUrl}\r\n alt=\"QR Code\"\r\n style={{ width: '100%', height: '100%', objectFit: 'contain' }}\r\n />\r\n ) : (\r\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: template.cardSize.width,\r\n height: 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 transform: `scale(${scale})`,\r\n transformOrigin: 'top left',\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: '100%',\r\n height: '100%',\r\n objectFit: 'cover',\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\nexport default IDCardPreview;\r\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"IDCardPreview.js","sources":["../../../src/components/IDCardPreview.tsx"],"sourcesContent":["import React, { useMemo } from 'react';\r\nimport type { IDCardPreviewProps, FieldMapping } from '../types';\r\nimport { fieldStyleToCSS, positionToStyle } from '../utils/styleUtils';\r\nimport { generateQrCodeFromFields } from '../core/qrUtils';\r\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 const fieldStyle = fieldStyleToCSS(field.style);\r\n const isSelected = field.id === selectedFieldId;\r\n\r\n const combinedStyle: React.CSSProperties = {\r\n ...posStyle,\r\n ...fieldStyle,\r\n zIndex: field.zIndex || 1,\r\n boxSizing: 'border-box',\r\n overflow: 'hidden',\r\n cursor: editable ? 'move' : 'default',\r\n outline: isSelected ? '2px solid #2196F3' : 'none',\r\n outlineOffset: '1px',\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 switch (field.type) {\r\n case 'text':\r\n return (\r\n <>\r\n {field.label && (\r\n <span style={{ fontSize: '0.75em', display: 'block', opacity: 0.7 }}>\r\n {field.label}\r\n </span>\r\n )}\r\n <span>{String(value || field.placeholder || field.fieldKey)}</span>\r\n </>\r\n );\r\n\r\n case 'label':\r\n return <span>{field.staticText || field.label || 'Label'}</span>;\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 height: '100%',\r\n objectFit: 'cover',\r\n objectPosition: 'center top',\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\r\n case 'qrcode':\r\n const qrDataUrl = field.qrFields\r\n ? generateQrCodeFromFields(data, field.qrFields, 200)\r\n : '';\r\n\r\n return qrDataUrl ? (\r\n <img\r\n src={qrDataUrl}\r\n alt=\"QR Code\"\r\n style={{ width: '100%', height: '100%', objectFit: 'contain' }}\r\n />\r\n ) : (\r\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: template.cardSize.width,\r\n height: 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 transform: `scale(${scale})`,\r\n transformOrigin: 'top left',\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: '100%',\r\n height: '100%',\r\n objectFit: 'cover',\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"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import html2canvas from '../node_modules/html2canvas/dist/html2canvas.esm.js';
1
+ import html2canvas from 'html2canvas';
2
2
  import jsPDF from 'jspdf';
3
3
  import { setQrCodeOnElement } from './qrUtils.js';
4
4
 
@@ -29,25 +29,42 @@ function useIDCardTemplate(initialTemplate) {
29
29
  }, []);
30
30
  // Update specific properties of a field
31
31
  const updateField = useCallback((fieldId, updates) => {
32
- setTemplateState((prev) => ({
33
- ...prev,
34
- updatedAt: new Date().toISOString(),
35
- fields: prev.fields.map((field) => field.id === fieldId ? { ...field, ...updates } : field),
36
- }));
32
+ if (!fieldId || !updates)
33
+ return;
34
+ setTemplateState((prev) => {
35
+ const fieldExists = prev.fields.some((field) => field?.id === fieldId);
36
+ if (!fieldExists)
37
+ return prev;
38
+ return {
39
+ ...prev,
40
+ updatedAt: new Date().toISOString(),
41
+ fields: prev.fields.map((field) => field?.id === fieldId ? { ...field, ...updates } : field).filter(Boolean),
42
+ };
43
+ });
37
44
  }, []);
38
45
  // Add a new field to the template
39
46
  const addField = useCallback((field) => {
47
+ if (!field || !field.type || !field.position) {
48
+ console.warn('Invalid field object provided to addField:', field);
49
+ return;
50
+ }
51
+ const validField = {
52
+ ...field,
53
+ id: field.id || generateId('field'),
54
+ };
40
55
  setTemplateState((prev) => ({
41
56
  ...prev,
42
57
  updatedAt: new Date().toISOString(),
43
- fields: [...prev.fields, { ...field, id: field.id || generateId('field') }],
58
+ fields: [...prev.fields.filter(Boolean), validField],
44
59
  }));
45
60
  }, []);
46
61
  const removeField = useCallback((fieldId) => {
62
+ if (!fieldId)
63
+ return;
47
64
  setTemplateState((prev) => ({
48
65
  ...prev,
49
66
  updatedAt: new Date().toISOString(),
50
- fields: prev.fields.filter((field) => field.id !== fieldId),
67
+ fields: prev.fields.filter((field) => field?.id && field.id !== fieldId),
51
68
  }));
52
69
  }, []);
53
70
  const setBackground = useCallback((side, imageData) => {
@@ -94,9 +111,11 @@ function useIDCardTemplate(initialTemplate) {
94
111
  }));
95
112
  }, []);
96
113
  const duplicateField = useCallback((fieldId) => {
114
+ if (!fieldId)
115
+ return;
97
116
  setTemplateState((prev) => {
98
- const field = prev.fields.find((f) => f.id === fieldId);
99
- if (!field)
117
+ const field = prev.fields.find((f) => f?.id === fieldId);
118
+ if (!field || !field.position)
100
119
  return prev;
101
120
  const newField = {
102
121
  ...field,
@@ -110,15 +129,17 @@ function useIDCardTemplate(initialTemplate) {
110
129
  return {
111
130
  ...prev,
112
131
  updatedAt: new Date().toISOString(),
113
- fields: [...prev.fields, newField],
132
+ fields: [...prev.fields.filter(Boolean), newField],
114
133
  };
115
134
  });
116
135
  }, []);
117
136
  const moveFieldToSide = useCallback((fieldId, side) => {
137
+ if (!fieldId || !side)
138
+ return;
118
139
  setTemplateState((prev) => ({
119
140
  ...prev,
120
141
  updatedAt: new Date().toISOString(),
121
- fields: prev.fields.map((field) => field.id === fieldId ? { ...field, side } : field),
142
+ fields: prev.fields.map((field) => field?.id === fieldId ? { ...field, side } : field).filter(Boolean),
122
143
  }));
123
144
  }, []);
124
145
  const reset = useCallback(() => {
@@ -131,8 +152,16 @@ function useIDCardTemplate(initialTemplate) {
131
152
  validationErrors: errors.map((e) => `${e.field}: ${e.message}`),
132
153
  };
133
154
  }, [template]);
155
+ // Ensure template always has valid fields array
156
+ const safeTemplate = useMemo(() => ({
157
+ ...template,
158
+ fields: template.fields.filter((field) => Boolean(field) &&
159
+ Boolean(field.id) &&
160
+ Boolean(field.type) &&
161
+ Boolean(field.position)),
162
+ }), [template]);
134
163
  return {
135
- template,
164
+ template: safeTemplate,
136
165
  setTemplate,
137
166
  updateField,
138
167
  addField,
@@ -1 +1 @@
1
- {"version":3,"file":"useIDCardTemplate.js","sources":["../../../src/hooks/useIDCardTemplate.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\r\nimport type {\r\n IDCardTemplate,\r\n FieldMapping,\r\n CardSize,\r\n CardOrientation,\r\n CardSides,\r\n UseIDCardTemplateReturn,\r\n} from '../types';\r\nimport { createDefaultTemplate, generateId } from '../storage/templateStorage';\r\nimport { validateTemplate } from '../utils/validators';\r\n\r\n/**\r\n * Hook for managing ID card template state\r\n * Provides methods for updating fields, backgrounds, and card properties\r\n * Automatically updates the template's updatedAt timestamp on changes\r\n * \r\n * @param initialTemplate - Optional existing template to start with\r\n * @returns Template state and helper methods\r\n */\r\nexport function useIDCardTemplate(\r\n initialTemplate?: Partial<IDCardTemplate>\r\n): UseIDCardTemplateReturn {\r\n const [template, setTemplateState] = useState<IDCardTemplate>(() => {\r\n if (initialTemplate) {\r\n return {\r\n ...createDefaultTemplate(),\r\n ...initialTemplate,\r\n updatedAt: new Date().toISOString(),\r\n };\r\n }\r\n return createDefaultTemplate();\r\n });\r\n\r\n const setTemplate = useCallback((newTemplate: IDCardTemplate) => {\r\n setTemplateState({\r\n ...newTemplate,\r\n updatedAt: new Date().toISOString(),\r\n });\r\n }, []);\r\n\r\n // Update specific properties of a field\r\n const updateField = useCallback((fieldId: string, updates: Partial<FieldMapping>) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.map((field: FieldMapping) =>\r\n field.id === fieldId ? { ...field, ...updates } : field\r\n ),\r\n }));\r\n }, []);\r\n\r\n // Add a new field to the template\r\n const addField = useCallback((field: FieldMapping) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: [...prev.fields, { ...field, id: field.id || generateId('field') }],\r\n }));\r\n }, []);\r\n\r\n const removeField = useCallback((fieldId: string) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.filter((field: FieldMapping) => field.id !== fieldId),\r\n }));\r\n }, []);\r\n\r\n const setBackground = useCallback((side: 'front' | 'back', imageData: string) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n backgrounds: {\r\n ...prev.backgrounds,\r\n [side]: imageData,\r\n },\r\n }));\r\n }, []);\r\n\r\n const setCardSize = useCallback((size: CardSize) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n cardSize: size,\r\n }));\r\n }, []);\r\n\r\n const setOrientation = useCallback((orientation: CardOrientation) => {\r\n setTemplateState((prev: IDCardTemplate) => {\r\n // Swap dimensions if orientation changes\r\n const shouldSwap =\r\n (orientation === 'portrait' && prev.cardSize.width > prev.cardSize.height) ||\r\n (orientation === 'landscape' && prev.cardSize.height > prev.cardSize.width);\r\n\r\n return {\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n orientation,\r\n cardSize: shouldSwap\r\n ? {\r\n ...prev.cardSize,\r\n width: prev.cardSize.height,\r\n height: prev.cardSize.width,\r\n }\r\n : prev.cardSize,\r\n };\r\n });\r\n }, []);\r\n\r\n const setSides = useCallback((sides: CardSides) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n sides,\r\n }));\r\n }, []);\r\n\r\n const duplicateField = useCallback((fieldId: string) => {\r\n setTemplateState((prev: IDCardTemplate) => {\r\n const field = prev.fields.find((f: FieldMapping) => f.id === fieldId);\r\n if (!field) return prev;\r\n\r\n const newField: FieldMapping = {\r\n ...field,\r\n id: generateId('field'),\r\n position: {\r\n ...field.position,\r\n x: field.position.x + 10,\r\n y: field.position.y + 10,\r\n },\r\n };\r\n\r\n return {\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: [...prev.fields, newField],\r\n };\r\n });\r\n }, []);\r\n\r\n const moveFieldToSide = useCallback((fieldId: string, side: 'front' | 'back') => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.map((field: FieldMapping) =>\r\n field.id === fieldId ? { ...field, side } : field\r\n ),\r\n }));\r\n }, []);\r\n\r\n const reset = useCallback(() => {\r\n setTemplateState(createDefaultTemplate());\r\n }, []);\r\n\r\n const validationResult = useMemo(() => {\r\n const errors = validateTemplate(template);\r\n return {\r\n isValid: errors.length === 0,\r\n validationErrors: errors.map((e) => `${e.field}: ${e.message}`),\r\n };\r\n }, [template]);\r\n\r\n return {\r\n template,\r\n setTemplate,\r\n updateField,\r\n addField,\r\n removeField,\r\n setBackground,\r\n setCardSize,\r\n setOrientation,\r\n setSides,\r\n duplicateField,\r\n moveFieldToSide,\r\n reset,\r\n isValid: validationResult.isValid,\r\n validationErrors: validationResult.validationErrors,\r\n };\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useIDCardTemplate.js","sources":["../../../src/hooks/useIDCardTemplate.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\r\nimport type {\r\n IDCardTemplate,\r\n FieldMapping,\r\n CardSize,\r\n CardOrientation,\r\n CardSides,\r\n UseIDCardTemplateReturn,\r\n} from '../types';\r\nimport { createDefaultTemplate, generateId } from '../storage/templateStorage';\r\nimport { validateTemplate } from '../utils/validators';\r\n\r\n/**\r\n * Hook for managing ID card template state\r\n * Provides methods for updating fields, backgrounds, and card properties\r\n * Automatically updates the template's updatedAt timestamp on changes\r\n * \r\n * @param initialTemplate - Optional existing template to start with\r\n * @returns Template state and helper methods\r\n */\r\nexport function useIDCardTemplate(\r\n initialTemplate?: Partial<IDCardTemplate>\r\n): UseIDCardTemplateReturn {\r\n const [template, setTemplateState] = useState<IDCardTemplate>(() => {\r\n if (initialTemplate) {\r\n return {\r\n ...createDefaultTemplate(),\r\n ...initialTemplate,\r\n updatedAt: new Date().toISOString(),\r\n };\r\n }\r\n return createDefaultTemplate();\r\n });\r\n\r\n const setTemplate = useCallback((newTemplate: IDCardTemplate) => {\r\n setTemplateState({\r\n ...newTemplate,\r\n updatedAt: new Date().toISOString(),\r\n });\r\n }, []);\r\n\r\n // Update specific properties of a field\r\n const updateField = useCallback((fieldId: string, updates: Partial<FieldMapping>) => {\r\n if (!fieldId || !updates) return;\r\n \r\n setTemplateState((prev: IDCardTemplate) => {\r\n const fieldExists = prev.fields.some((field: FieldMapping) => field?.id === fieldId);\r\n if (!fieldExists) return prev;\r\n \r\n return {\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.map((field: FieldMapping) =>\r\n field?.id === fieldId ? { ...field, ...updates } : field\r\n ).filter(Boolean),\r\n };\r\n });\r\n }, []);\r\n\r\n // Add a new field to the template\r\n const addField = useCallback((field: FieldMapping) => {\r\n if (!field || !field.type || !field.position) {\r\n console.warn('Invalid field object provided to addField:', field);\r\n return;\r\n }\r\n \r\n const validField = {\r\n ...field,\r\n id: field.id || generateId('field'),\r\n };\r\n \r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: [...prev.fields.filter(Boolean), validField],\r\n }));\r\n }, []);\r\n\r\n const removeField = useCallback((fieldId: string) => {\r\n if (!fieldId) return;\r\n \r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.filter((field: FieldMapping) => field?.id && field.id !== fieldId),\r\n }));\r\n }, []);\r\n\r\n const setBackground = useCallback((side: 'front' | 'back', imageData: string) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n backgrounds: {\r\n ...prev.backgrounds,\r\n [side]: imageData,\r\n },\r\n }));\r\n }, []);\r\n\r\n const setCardSize = useCallback((size: CardSize) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n cardSize: size,\r\n }));\r\n }, []);\r\n\r\n const setOrientation = useCallback((orientation: CardOrientation) => {\r\n setTemplateState((prev: IDCardTemplate) => {\r\n // Swap dimensions if orientation changes\r\n const shouldSwap =\r\n (orientation === 'portrait' && prev.cardSize.width > prev.cardSize.height) ||\r\n (orientation === 'landscape' && prev.cardSize.height > prev.cardSize.width);\r\n\r\n return {\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n orientation,\r\n cardSize: shouldSwap\r\n ? {\r\n ...prev.cardSize,\r\n width: prev.cardSize.height,\r\n height: prev.cardSize.width,\r\n }\r\n : prev.cardSize,\r\n };\r\n });\r\n }, []);\r\n\r\n const setSides = useCallback((sides: CardSides) => {\r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n sides,\r\n }));\r\n }, []);\r\n\r\n const duplicateField = useCallback((fieldId: string) => {\r\n if (!fieldId) return;\r\n \r\n setTemplateState((prev: IDCardTemplate) => {\r\n const field = prev.fields.find((f: FieldMapping) => f?.id === fieldId);\r\n if (!field || !field.position) return prev;\r\n\r\n const newField: FieldMapping = {\r\n ...field,\r\n id: generateId('field'),\r\n position: {\r\n ...field.position,\r\n x: field.position.x + 10,\r\n y: field.position.y + 10,\r\n },\r\n };\r\n\r\n return {\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: [...prev.fields.filter(Boolean), newField],\r\n };\r\n });\r\n }, []);\r\n\r\n const moveFieldToSide = useCallback((fieldId: string, side: 'front' | 'back') => {\r\n if (!fieldId || !side) return;\r\n \r\n setTemplateState((prev: IDCardTemplate) => ({\r\n ...prev,\r\n updatedAt: new Date().toISOString(),\r\n fields: prev.fields.map((field: FieldMapping) =>\r\n field?.id === fieldId ? { ...field, side } : field\r\n ).filter(Boolean),\r\n }));\r\n }, []);\r\n\r\n const reset = useCallback(() => {\r\n setTemplateState(createDefaultTemplate());\r\n }, []);\r\n\r\n const validationResult = useMemo(() => {\r\n const errors = validateTemplate(template);\r\n return {\r\n isValid: errors.length === 0,\r\n validationErrors: errors.map((e) => `${e.field}: ${e.message}`),\r\n };\r\n }, [template]);\r\n\r\n // Ensure template always has valid fields array\r\n const safeTemplate = useMemo(() => ({\r\n ...template,\r\n fields: template.fields.filter((field): field is FieldMapping => \r\n Boolean(field) && \r\n Boolean(field.id) && \r\n Boolean(field.type) && \r\n Boolean(field.position)\r\n ),\r\n }), [template]);\r\n\r\n return {\r\n template: safeTemplate,\r\n setTemplate,\r\n updateField,\r\n addField,\r\n removeField,\r\n setBackground,\r\n setCardSize,\r\n setOrientation,\r\n setSides,\r\n duplicateField,\r\n moveFieldToSide,\r\n reset,\r\n isValid: validationResult.isValid,\r\n validationErrors: validationResult.validationErrors,\r\n };\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ // Recursively removes all properties with value undefined from an object or array.
2
+ function deepCleanUndefined(obj) {
3
+ if (Array.isArray(obj)) {
4
+ return obj.map(deepCleanUndefined).filter(v => v !== undefined);
5
+ }
6
+ else if (obj && typeof obj === 'object') {
7
+ const cleaned = {};
8
+ for (const [key, value] of Object.entries(obj)) {
9
+ if (value !== undefined) {
10
+ cleaned[key] = deepCleanUndefined(value);
11
+ }
12
+ }
13
+ return cleaned;
14
+ }
15
+ return obj;
16
+ }
17
+
18
+ export { deepCleanUndefined };
19
+ //# sourceMappingURL=deepCleanUndefined.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepCleanUndefined.js","sources":["../../../src/utils/deepCleanUndefined.ts"],"sourcesContent":["// Recursively removes all properties with value undefined from an object or array.\r\nexport function deepCleanUndefined<T>(obj: T): T {\r\n if (Array.isArray(obj)) {\r\n return obj.map(deepCleanUndefined).filter(v => v !== undefined) as unknown as T;\r\n } else if (obj && typeof obj === 'object') {\r\n const cleaned: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n if (value !== undefined) {\r\n cleaned[key] = deepCleanUndefined(value);\r\n }\r\n }\r\n return cleaned;\r\n }\r\n return obj;\r\n}\r\n"],"names":[],"mappings":"AAAA;AACM,SAAU,kBAAkB,CAAI,GAAM,EAAA;AAC1C,IAAA,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;AACtB,QAAA,OAAO,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,SAAS,CAAiB;IACjF;AAAO,SAAA,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QACzC,MAAM,OAAO,GAAQ,EAAE;AACvB,QAAA,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;AAC9C,YAAA,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,OAAO,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC;YAC1C;QACF;AACA,QAAA,OAAO,OAAO;IAChB;AACA,IAAA,OAAO,GAAG;AACZ;;;;"}
@@ -15,4 +15,3 @@ import './IDCardDesigner.css';
15
15
  * IDCardDesigner - Visual drag-and-drop ID card template builder
16
16
  */
17
17
  export declare const IDCardDesigner: React.FC<IDCardDesignerProps>;
18
- export default IDCardDesigner;
@@ -16,4 +16,3 @@ import type { IDCardGeneratorProps } from '../types';
16
16
  * ```
17
17
  */
18
18
  export declare const IDCardGenerator: React.FC<IDCardGeneratorProps>;
19
- export default IDCardGenerator;
@@ -12,4 +12,3 @@ import type { IDCardPreviewProps } from '../types';
12
12
  * Used inside the designer for visual editing
13
13
  */
14
14
  export declare const IDCardPreview: React.FC<IDCardPreviewProps>;
15
- export default IDCardPreview;
@@ -0,0 +1 @@
1
+ export declare function deepCleanUndefined<T>(obj: T): T;
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "react-id-card-generator",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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
+ "type": "module",
6
7
  "module": "dist/esm/index.js",
7
8
  "types": "dist/types/index.d.ts",
8
9
  "exports": {