labellife-design-tool 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -128,7 +128,8 @@ import {
128
128
  exportToJPG,
129
129
  exportToJSON,
130
130
  canvasToBlob,
131
- importFromJSON
131
+ importFromJSON,
132
+ importFromJSONData
132
133
  } from 'labellife-design-tool';
133
134
  import type { CanvasElement } from 'labellife-design-tool';
134
135
 
@@ -143,6 +144,122 @@ function MyEditor() {
143
144
  }
144
145
  ```
145
146
 
147
+ ### Template Import
148
+
149
+ The library provides two ways to import template designs:
150
+
151
+ #### File-Based Import
152
+
153
+ ```tsx
154
+ import { importFromJSON } from 'labellife-design-tool';
155
+
156
+ // Use with file input
157
+ function handleFileImport(event) {
158
+ importFromJSON(
159
+ event,
160
+ (design) => {
161
+ console.log('Design loaded:', design);
162
+ // Use the design
163
+ },
164
+ (error) => {
165
+ alert('Import failed: ' + error);
166
+ }
167
+ );
168
+ }
169
+ ```
170
+
171
+ #### Direct JSON Data Import
172
+
173
+ ```tsx
174
+ import { importFromJSONData } from 'labellife-design-tool';
175
+
176
+ // Import JSON data directly (no file needed)
177
+ const templateData = {
178
+ width: 800,
179
+ height: 600,
180
+ pages: [{
181
+ id: "1",
182
+ name: "Page 1",
183
+ elements: [
184
+ {
185
+ type: "text",
186
+ text: "Hello World",
187
+ x: 100,
188
+ y: 100,
189
+ fontSize: 24
190
+ }
191
+ ],
192
+ background: "white"
193
+ }]
194
+ };
195
+
196
+ importFromJSONData(
197
+ templateData,
198
+ (design) => {
199
+ console.log('Design loaded:', design);
200
+ // Use the design in CanvasEditor
201
+ },
202
+ (error) => {
203
+ console.error('Import failed:', error);
204
+ }
205
+ );
206
+ ```
207
+
208
+ #### Import from API
209
+
210
+ ```tsx
211
+ // Load template from API
212
+ async function loadTemplateFromAPI(templateId: string) {
213
+ try {
214
+ const response = await fetch(`/api/templates/${templateId}`);
215
+ const templateData = await response.json();
216
+
217
+ importFromJSONData(
218
+ templateData,
219
+ (design) => {
220
+ // Template loaded successfully
221
+ setDesign(design);
222
+ },
223
+ (error) => {
224
+ alert('Failed to load template: ' + error);
225
+ }
226
+ );
227
+ } catch (error) {
228
+ console.error('API error:', error);
229
+ }
230
+ }
231
+ ```
232
+
233
+ #### Import with User Inputs
234
+
235
+ ```tsx
236
+ importFromJSONData(
237
+ templateData,
238
+ (design) => {
239
+ // Design loaded with user inputs applied
240
+ setDesign(design);
241
+ },
242
+ (error) => {
243
+ alert('Import failed: ' + error);
244
+ },
245
+ (inputs, onComplete) => {
246
+ // Show custom input modal
247
+ showInputModal(inputs, (values) => {
248
+ onComplete(values);
249
+ });
250
+ }
251
+ );
252
+ ```
253
+
254
+ **Comparison:**
255
+
256
+ | Feature | `importFromJSON` | `importFromJSONData` |
257
+ |---------|------------------|---------------------|
258
+ | **Input Source** | File upload event | JSON data object |
259
+ | **File Reading** | Built-in FileReader | Not needed |
260
+ | **Use Case** | User file uploads | API data, database, programmatic imports |
261
+ | **Processing** | Identical | Identical |
262
+
146
263
  ### Canvas to Blob Export
147
264
 
148
265
  The library provides a flexible `canvasToBlob` function that allows converting the canvas to a Blob for various use cases:
@@ -11,6 +11,8 @@
11
11
  --color-red-500: oklch(63.7% 0.237 25.331);
12
12
  --color-red-600: oklch(57.7% 0.245 27.325);
13
13
  --color-red-700: oklch(50.5% 0.213 27.518);
14
+ --color-green-600: oklch(62.7% 0.194 149.214);
15
+ --color-green-700: oklch(52.7% 0.154 150.069);
14
16
  --color-blue-400: oklch(70.7% 0.165 254.624);
15
17
  --color-blue-500: oklch(62.3% 0.214 259.815);
16
18
  --color-blue-600: oklch(54.6% 0.245 262.881);
@@ -215,6 +217,12 @@
215
217
  .inset-0 {
216
218
  inset: calc(var(--spacing) * 0);
217
219
  }
220
+ .-top-1 {
221
+ top: calc(var(--spacing) * -1);
222
+ }
223
+ .-right-1 {
224
+ right: calc(var(--spacing) * -1);
225
+ }
218
226
  .bottom-4 {
219
227
  bottom: calc(var(--spacing) * 4);
220
228
  }
@@ -257,6 +265,9 @@
257
265
  .mt-2 {
258
266
  margin-top: calc(var(--spacing) * 2);
259
267
  }
268
+ .mt-4 {
269
+ margin-top: calc(var(--spacing) * 4);
270
+ }
260
271
  .mt-6 {
261
272
  margin-top: calc(var(--spacing) * 6);
262
273
  }
@@ -320,6 +331,9 @@
320
331
  .h-12 {
321
332
  height: calc(var(--spacing) * 12);
322
333
  }
334
+ .h-64 {
335
+ height: calc(var(--spacing) * 64);
336
+ }
323
337
  .h-full {
324
338
  height: 100%;
325
339
  }
@@ -329,6 +343,9 @@
329
343
  .max-h-32 {
330
344
  max-height: calc(var(--spacing) * 32);
331
345
  }
346
+ .max-h-\[80vh\] {
347
+ max-height: 80vh;
348
+ }
332
349
  .max-h-\[90vh\] {
333
350
  max-height: 90vh;
334
351
  }
@@ -395,6 +412,9 @@
395
412
  .resize {
396
413
  resize: both;
397
414
  }
415
+ .resize-none {
416
+ resize: none;
417
+ }
398
418
  .grid-cols-2 {
399
419
  grid-template-columns: repeat(2, minmax(0, 1fr));
400
420
  }
@@ -419,6 +439,9 @@
419
439
  .justify-center {
420
440
  justify-content: center;
421
441
  }
442
+ .justify-end {
443
+ justify-content: flex-end;
444
+ }
422
445
  .gap-2 {
423
446
  gap: calc(var(--spacing) * 2);
424
447
  }
@@ -482,6 +505,9 @@
482
505
  text-overflow: ellipsis;
483
506
  white-space: nowrap;
484
507
  }
508
+ .overflow-auto {
509
+ overflow: auto;
510
+ }
485
511
  .overflow-hidden {
486
512
  overflow: hidden;
487
513
  }
@@ -570,6 +596,12 @@
570
596
  .bg-gray-900 {
571
597
  background-color: var(--color-gray-900);
572
598
  }
599
+ .bg-green-600 {
600
+ background-color: var(--color-green-600);
601
+ }
602
+ .bg-red-500 {
603
+ background-color: var(--color-red-500);
604
+ }
573
605
  .bg-red-700 {
574
606
  background-color: var(--color-red-700);
575
607
  }
@@ -636,6 +668,9 @@
636
668
  .text-right {
637
669
  text-align: right;
638
670
  }
671
+ .font-mono {
672
+ font-family: var(--font-mono);
673
+ }
639
674
  .text-2xl {
640
675
  font-size: var(--text-2xl);
641
676
  line-height: var(--tw-leading, var(--text-2xl--line-height));
@@ -828,6 +863,13 @@
828
863
  }
829
864
  }
830
865
  }
866
+ .hover\:bg-green-700 {
867
+ &:hover {
868
+ @media (hover: hover) {
869
+ background-color: var(--color-green-700);
870
+ }
871
+ }
872
+ }
831
873
  .hover\:bg-red-500 {
832
874
  &:hover {
833
875
  @media (hover: hover) {
@@ -26,7 +26,8 @@ function initJsxCompat() {
26
26
  import React18, { useState as useState3, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback4, forwardRef, useImperativeHandle } from "react";
27
27
  import { Stage, Layer, Rect as Rect2 } from "react-konva";
28
28
  import {
29
- PlusCircle
29
+ PlusCircle,
30
+ X as X2
30
31
  } from "lucide-react";
31
32
 
32
33
  // src/elements/EditableTextElement.tsx
@@ -1964,7 +1965,8 @@ var ExportPanel = ({
1964
1965
  onExportToPNG,
1965
1966
  onExportToJPG,
1966
1967
  onExportToJSON,
1967
- onImportJSON
1968
+ onImportJSON,
1969
+ onLoadJSON
1968
1970
  }) => {
1969
1971
  return /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("h3", {
1970
1972
  className: "text-white font-semibold mb-4"
@@ -1982,7 +1984,10 @@ var ExportPanel = ({
1982
1984
  }, "Export as JSON"), /* @__PURE__ */ React15.createElement("button", {
1983
1985
  onClick: onImportJSON,
1984
1986
  className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600"
1985
- }, "Import JSON")));
1987
+ }, "Import JSON"), onLoadJSON && /* @__PURE__ */ React15.createElement("button", {
1988
+ onClick: onLoadJSON,
1989
+ className: "w-full p-3 bg-green-600 text-white rounded hover:bg-green-700"
1990
+ }, "Load JSON")));
1986
1991
  };
1987
1992
  var ExportPanel_default = ExportPanel;
1988
1993
 
@@ -2161,6 +2166,12 @@ function convertTemplateToCanvasDesign(json) {
2161
2166
  if (!converted.name) {
2162
2167
  converted.name = json.name || "Imported Design";
2163
2168
  }
2169
+ if (converted.width === "auto" || !converted.width) {
2170
+ converted.width = 800;
2171
+ }
2172
+ if (converted.height === "auto" || !converted.height) {
2173
+ converted.height = 600;
2174
+ }
2164
2175
  if (!converted.fonts) {
2165
2176
  converted.fonts = [];
2166
2177
  }
@@ -2172,11 +2183,19 @@ function convertTemplateToCanvasDesign(json) {
2172
2183
  }
2173
2184
  if (converted.pages && Array.isArray(converted.pages)) {
2174
2185
  converted.pages = converted.pages.map((page, index) => {
2186
+ const pageWidth = page.width === "auto" || !page.width ? converted.width : page.width;
2187
+ const pageHeight = page.height === "auto" || !page.height ? converted.height : page.height;
2175
2188
  const elements = (page.elements || page.children || []).map((element) => {
2189
+ let elementType = element.type;
2190
+ let shapeType = element.shapeType;
2191
+ if (element.type === "figure" && element.subType) {
2192
+ elementType = "shape";
2193
+ shapeType = element.subType;
2194
+ }
2176
2195
  return {
2177
2196
  ...element,
2178
2197
  id: element.id || `${Date.now()}-${Math.random()}`,
2179
- type: element.type || "text",
2198
+ type: elementType || "text",
2180
2199
  name: element.name || "Untitled",
2181
2200
  x: element.x ?? 0,
2182
2201
  y: element.y ?? 0,
@@ -2200,7 +2219,7 @@ function convertTemplateToCanvasDesign(json) {
2200
2219
  cornerRadius: element.cornerRadius,
2201
2220
  filters: element.filters,
2202
2221
  mask: element.mask,
2203
- shapeType: element.shapeType,
2222
+ shapeType,
2204
2223
  align: element.align,
2205
2224
  lineHeight: element.lineHeight,
2206
2225
  letterSpacing: element.letterSpacing,
@@ -2435,6 +2454,9 @@ function replaceUserInputs(design, collectedInputs) {
2435
2454
  return defaultSize;
2436
2455
  };
2437
2456
  unusedInputs.forEach(([inputId, value]) => {
2457
+ if (!value || value === "" || value === "undefined" || value === "null") {
2458
+ return;
2459
+ }
2438
2460
  if ((inputId === "optionalImage" || inputId.toLowerCase().includes("image")) && typeof value === "string") {
2439
2461
  const imageProps = processImageElement(value);
2440
2462
  newElements.push({
@@ -2567,6 +2589,22 @@ var canvasToBlob = (stage, format = "png", options) => {
2567
2589
  resolve(new Blob([arrayBuffer], { type: mimeType }));
2568
2590
  });
2569
2591
  };
2592
+ var importFromJSONData = (jsonData, onLoad, onError, onInputsRequired) => {
2593
+ try {
2594
+ const convertedDesign = convertTemplateToCanvasDesign(jsonData);
2595
+ const requiredInputs = findRequiredInputs(convertedDesign);
2596
+ if (requiredInputs.length > 0 && onInputsRequired) {
2597
+ onInputsRequired(requiredInputs, (collectedValues) => {
2598
+ const finalDesign = replaceUserInputs(convertedDesign, collectedValues);
2599
+ onLoad(finalDesign);
2600
+ });
2601
+ } else {
2602
+ onLoad(convertedDesign);
2603
+ }
2604
+ } catch (error) {
2605
+ onError(`Invalid JSON data: ${error instanceof Error ? error.message : String(error)}`);
2606
+ }
2607
+ };
2570
2608
  var importFromJSON = (event, onLoad, onError, onInputsRequired) => {
2571
2609
  const file = event.target.files?.[0];
2572
2610
  if (file) {
@@ -3019,6 +3057,8 @@ var CanvasEditor = forwardRef(({
3019
3057
  const [historyIndex, setHistoryIndex] = useState3(-1);
3020
3058
  const [showInputModal, setShowInputModal] = useState3(false);
3021
3059
  const [pendingInputs, setPendingInputs] = useState3([]);
3060
+ const [showJsonModal, setShowJsonModal] = useState3(false);
3061
+ const [jsonInputText, setJsonInputText] = useState3("");
3022
3062
  const stageRef = useRef5(null);
3023
3063
  useImperativeHandle(ref, () => ({
3024
3064
  stage: stageRef.current,
@@ -3224,15 +3264,35 @@ var CanvasEditor = forwardRef(({
3224
3264
  }, [design.pages.length]);
3225
3265
  const deletePage = useCallback4((pageId) => {
3226
3266
  if (design.pages.length > 1) {
3227
- setDesign((prev) => ({
3228
- ...prev,
3229
- pages: prev.pages.filter((p) => p.id !== pageId)
3230
- }));
3267
+ setDesign((prev) => {
3268
+ const filteredPages = prev.pages.filter((p) => p.id !== pageId);
3269
+ const renamedPages = filteredPages.map((p, idx) => ({
3270
+ ...p,
3271
+ name: `Page ${idx + 1}`
3272
+ }));
3273
+ return {
3274
+ ...prev,
3275
+ pages: renamedPages
3276
+ };
3277
+ });
3231
3278
  if (currentPageId === pageId) {
3232
- setCurrentPageId(design.pages[0].id);
3279
+ const remainingPages = design.pages.filter((p) => p.id !== pageId);
3280
+ if (remainingPages.length > 0) {
3281
+ setCurrentPageId(remainingPages[0].id);
3282
+ }
3233
3283
  }
3284
+ setTimeout(() => {
3285
+ const updatedDesign = {
3286
+ ...design,
3287
+ pages: design.pages.filter((p) => p.id !== pageId).map((p, idx) => ({
3288
+ ...p,
3289
+ name: `Page ${idx + 1}`
3290
+ }))
3291
+ };
3292
+ saveToHistory(updatedDesign);
3293
+ }, 0);
3234
3294
  }
3235
- }, [design.pages, currentPageId]);
3295
+ }, [design.pages, currentPageId, design, saveToHistory]);
3236
3296
  const handleImageUpload = useCallback4((e) => {
3237
3297
  const file = e.target.files?.[0];
3238
3298
  if (file) {
@@ -3364,14 +3424,27 @@ var CanvasEditor = forwardRef(({
3364
3424
  setUnsplashResults([]);
3365
3425
  setUnsplashMode("element");
3366
3426
  }, [currentPageId, saveToHistory, unsplashMode]);
3427
+ const calculateFitToScreenZoom = useCallback4((canvasWidth, canvasHeight) => {
3428
+ const viewportWidth = window.innerWidth - 320;
3429
+ const viewportHeight = window.innerHeight - 200;
3430
+ const scaleX = viewportWidth / canvasWidth;
3431
+ const scaleY = viewportHeight / canvasHeight;
3432
+ const optimalScale = Math.min(scaleX, scaleY, 1);
3433
+ return Math.max(0.1, Math.min(3, optimalScale));
3434
+ }, []);
3367
3435
  const handleImportJSON = (event) => {
3368
3436
  importFromJSON(event, (newDesign) => {
3369
3437
  if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3370
- setDesign(newDesign);
3371
3438
  setSelectedId(null);
3372
3439
  setSelectedIds([]);
3440
+ setTool("select");
3441
+ setDesign(newDesign);
3373
3442
  const firstPageId = newDesign.pages[0]?.id;
3374
3443
  setCurrentPageId(firstPageId || "1");
3444
+ const optimalZoom = calculateFitToScreenZoom(newDesign.width, newDesign.height);
3445
+ setZoom(optimalZoom);
3446
+ setHistory([newDesign]);
3447
+ setHistoryIndex(0);
3375
3448
  saveToHistory(newDesign);
3376
3449
  } else {
3377
3450
  alert("Invalid design structure in JSON file. Could not load.");
@@ -3387,6 +3460,40 @@ var CanvasEditor = forwardRef(({
3387
3460
  jsonInputRef.current.value = "";
3388
3461
  }
3389
3462
  };
3463
+ const handleImportJSONData = () => {
3464
+ try {
3465
+ const jsonData = JSON.parse(jsonInputText);
3466
+ importFromJSONData(jsonData, (newDesign) => {
3467
+ if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3468
+ setSelectedId(null);
3469
+ setSelectedIds([]);
3470
+ setTool("select");
3471
+ setDesign(newDesign);
3472
+ const firstPageId = newDesign.pages[0]?.id;
3473
+ setCurrentPageId(firstPageId || "1");
3474
+ const optimalZoom = calculateFitToScreenZoom(newDesign.width, newDesign.height);
3475
+ setZoom(optimalZoom);
3476
+ setHistory([newDesign]);
3477
+ setHistoryIndex(0);
3478
+ saveToHistory(newDesign);
3479
+ setShowJsonModal(false);
3480
+ setJsonInputText("");
3481
+ } else {
3482
+ alert("Invalid design structure in JSON data. Could not load.");
3483
+ }
3484
+ }, (errorMessage) => {
3485
+ alert(`Error importing JSON: ${errorMessage}`);
3486
+ }, (inputs, onComplete) => {
3487
+ setPendingInputs(inputs);
3488
+ setShowInputModal(true);
3489
+ window.__pendingImportComplete = onComplete;
3490
+ setShowJsonModal(false);
3491
+ setJsonInputText("");
3492
+ });
3493
+ } catch (error) {
3494
+ alert(`Invalid JSON format: ${error instanceof Error ? error.message : String(error)}`);
3495
+ }
3496
+ };
3390
3497
  const handleInputModalComplete = (values) => {
3391
3498
  setShowInputModal(false);
3392
3499
  const onComplete = window.__pendingImportComplete;
@@ -3493,7 +3600,8 @@ var CanvasEditor = forwardRef(({
3493
3600
  exportToJPG(stageRef.current, design);
3494
3601
  } : undefined,
3495
3602
  onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
3496
- onImportJSON: () => jsonInputRef.current?.click()
3603
+ onImportJSON: () => jsonInputRef.current?.click(),
3604
+ onLoadJSON: () => setShowJsonModal(true)
3497
3605
  }
3498
3606
  }
3499
3607
  ] : []
@@ -3570,7 +3678,7 @@ var CanvasEditor = forwardRef(({
3570
3678
  onRedo: redo,
3571
3679
  onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
3572
3680
  onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
3573
- onZoomReset: () => setZoom(1),
3681
+ onZoomReset: () => setZoom(calculateFitToScreenZoom(design.width, design.height)),
3574
3682
  onSetActivePanel: setActivePanelId,
3575
3683
  navbarConfig: config?.navbar
3576
3684
  }), /* @__PURE__ */ React18.createElement("div", {
@@ -3597,6 +3705,7 @@ var CanvasEditor = forwardRef(({
3597
3705
  transformOrigin: "center center"
3598
3706
  }
3599
3707
  }, /* @__PURE__ */ React18.createElement(Stage, {
3708
+ key: `canvas-${design.id || design.width}-${design.height}`,
3600
3709
  ref: stageRef,
3601
3710
  width: design.width,
3602
3711
  height: design.height,
@@ -3650,11 +3759,19 @@ var CanvasEditor = forwardRef(({
3650
3759
  }
3651
3760
  }))))), config?.multiPage && /* @__PURE__ */ React18.createElement("div", {
3652
3761
  className: "absolute bottom-4 left-4 flex items-center space-x-2"
3653
- }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("button", {
3762
+ }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("div", {
3654
3763
  key: page.id,
3764
+ className: "relative group"
3765
+ }, /* @__PURE__ */ React18.createElement("button", {
3655
3766
  onClick: () => setCurrentPageId(page.id),
3656
3767
  className: `px-3 py-1 rounded text-sm ${page.id === currentPageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
3657
- }, "Page ", index + 1)), /* @__PURE__ */ React18.createElement("button", {
3768
+ }, "Page ", index + 1), design.pages.length > 1 && /* @__PURE__ */ React18.createElement("button", {
3769
+ onClick: () => deletePage(page.id),
3770
+ className: "absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full flex items-center justify-center transition-opacity duration-200 hover:bg-red-600",
3771
+ title: `Delete Page ${index + 1}`
3772
+ }, /* @__PURE__ */ React18.createElement(X2, {
3773
+ className: "w-3 h-3"
3774
+ })))), /* @__PURE__ */ React18.createElement("button", {
3658
3775
  onClick: addPage,
3659
3776
  className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
3660
3777
  title: "Add Page"
@@ -3733,7 +3850,31 @@ var CanvasEditor = forwardRef(({
3733
3850
  className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
3734
3851
  }))))) : /* @__PURE__ */ React18.createElement("div", {
3735
3852
  className: "text-center text-gray-400 py-12"
3736
- }, "Enter a search term to find images")))));
3853
+ }, "Enter a search term to find images")))), showJsonModal && /* @__PURE__ */ React18.createElement("div", {
3854
+ className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
3855
+ }, /* @__PURE__ */ React18.createElement("div", {
3856
+ className: "bg-gray-800 rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-auto"
3857
+ }, /* @__PURE__ */ React18.createElement("h2", {
3858
+ className: "text-white text-xl font-semibold mb-4"
3859
+ }, "Load JSON Data"), /* @__PURE__ */ React18.createElement("p", {
3860
+ className: "text-gray-300 mb-4"
3861
+ }, 'Paste your JSON template data below and click "Load" to import it into the canvas.'), /* @__PURE__ */ React18.createElement("textarea", {
3862
+ value: jsonInputText,
3863
+ onChange: (e) => setJsonInputText(e.target.value),
3864
+ className: "w-full h-64 p-3 bg-gray-700 text-white border border-gray-600 rounded font-mono text-sm resize-none",
3865
+ placeholder: '{"width": 800, "height": 600, "pages": [...]}'
3866
+ }), /* @__PURE__ */ React18.createElement("div", {
3867
+ className: "flex justify-end space-x-3 mt-4"
3868
+ }, /* @__PURE__ */ React18.createElement("button", {
3869
+ onClick: () => {
3870
+ setShowJsonModal(false);
3871
+ setJsonInputText("");
3872
+ },
3873
+ className: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"
3874
+ }, "Cancel"), /* @__PURE__ */ React18.createElement("button", {
3875
+ onClick: handleImportJSONData,
3876
+ className: "px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
3877
+ }, "Load JSON")))));
3737
3878
  });
3738
3879
  var CanvasEditor_default = CanvasEditor;
3739
3880
  // src/lib/index.ts
@@ -3741,6 +3882,7 @@ initJsxCompat();
3741
3882
  export {
3742
3883
  setUnsplashAccessKey,
3743
3884
  replaceUserInputs,
3885
+ importFromJSONData,
3744
3886
  importFromJSON,
3745
3887
  getUnsplashAccessKey,
3746
3888
  findRequiredInputs,
@@ -26,7 +26,8 @@ function initJsxCompat() {
26
26
  import React18, { useState as useState3, useRef as useRef5, useEffect as useEffect4, useCallback as useCallback4, forwardRef, useImperativeHandle } from "react";
27
27
  import { Stage, Layer, Rect as Rect2 } from "react-konva";
28
28
  import {
29
- PlusCircle
29
+ PlusCircle,
30
+ X as X2
30
31
  } from "lucide-react";
31
32
 
32
33
  // src/elements/EditableTextElement.tsx
@@ -1964,7 +1965,8 @@ var ExportPanel = ({
1964
1965
  onExportToPNG,
1965
1966
  onExportToJPG,
1966
1967
  onExportToJSON,
1967
- onImportJSON
1968
+ onImportJSON,
1969
+ onLoadJSON
1968
1970
  }) => {
1969
1971
  return /* @__PURE__ */ React15.createElement("div", null, /* @__PURE__ */ React15.createElement("h3", {
1970
1972
  className: "text-white font-semibold mb-4"
@@ -1982,7 +1984,10 @@ var ExportPanel = ({
1982
1984
  }, "Export as JSON"), /* @__PURE__ */ React15.createElement("button", {
1983
1985
  onClick: onImportJSON,
1984
1986
  className: "w-full p-3 bg-gray-700 text-white rounded hover:bg-gray-600"
1985
- }, "Import JSON")));
1987
+ }, "Import JSON"), onLoadJSON && /* @__PURE__ */ React15.createElement("button", {
1988
+ onClick: onLoadJSON,
1989
+ className: "w-full p-3 bg-green-600 text-white rounded hover:bg-green-700"
1990
+ }, "Load JSON")));
1986
1991
  };
1987
1992
  var ExportPanel_default = ExportPanel;
1988
1993
 
@@ -2161,6 +2166,12 @@ function convertTemplateToCanvasDesign(json) {
2161
2166
  if (!converted.name) {
2162
2167
  converted.name = json.name || "Imported Design";
2163
2168
  }
2169
+ if (converted.width === "auto" || !converted.width) {
2170
+ converted.width = 800;
2171
+ }
2172
+ if (converted.height === "auto" || !converted.height) {
2173
+ converted.height = 600;
2174
+ }
2164
2175
  if (!converted.fonts) {
2165
2176
  converted.fonts = [];
2166
2177
  }
@@ -2172,11 +2183,19 @@ function convertTemplateToCanvasDesign(json) {
2172
2183
  }
2173
2184
  if (converted.pages && Array.isArray(converted.pages)) {
2174
2185
  converted.pages = converted.pages.map((page, index) => {
2186
+ const pageWidth = page.width === "auto" || !page.width ? converted.width : page.width;
2187
+ const pageHeight = page.height === "auto" || !page.height ? converted.height : page.height;
2175
2188
  const elements = (page.elements || page.children || []).map((element) => {
2189
+ let elementType = element.type;
2190
+ let shapeType = element.shapeType;
2191
+ if (element.type === "figure" && element.subType) {
2192
+ elementType = "shape";
2193
+ shapeType = element.subType;
2194
+ }
2176
2195
  return {
2177
2196
  ...element,
2178
2197
  id: element.id || `${Date.now()}-${Math.random()}`,
2179
- type: element.type || "text",
2198
+ type: elementType || "text",
2180
2199
  name: element.name || "Untitled",
2181
2200
  x: element.x ?? 0,
2182
2201
  y: element.y ?? 0,
@@ -2200,7 +2219,7 @@ function convertTemplateToCanvasDesign(json) {
2200
2219
  cornerRadius: element.cornerRadius,
2201
2220
  filters: element.filters,
2202
2221
  mask: element.mask,
2203
- shapeType: element.shapeType,
2222
+ shapeType,
2204
2223
  align: element.align,
2205
2224
  lineHeight: element.lineHeight,
2206
2225
  letterSpacing: element.letterSpacing,
@@ -2435,6 +2454,9 @@ function replaceUserInputs(design, collectedInputs) {
2435
2454
  return defaultSize;
2436
2455
  };
2437
2456
  unusedInputs.forEach(([inputId, value]) => {
2457
+ if (!value || value === "" || value === "undefined" || value === "null") {
2458
+ return;
2459
+ }
2438
2460
  if ((inputId === "optionalImage" || inputId.toLowerCase().includes("image")) && typeof value === "string") {
2439
2461
  const imageProps = processImageElement(value);
2440
2462
  newElements.push({
@@ -2567,6 +2589,22 @@ var canvasToBlob = (stage, format = "png", options) => {
2567
2589
  resolve(new Blob([arrayBuffer], { type: mimeType }));
2568
2590
  });
2569
2591
  };
2592
+ var importFromJSONData = (jsonData, onLoad, onError, onInputsRequired) => {
2593
+ try {
2594
+ const convertedDesign = convertTemplateToCanvasDesign(jsonData);
2595
+ const requiredInputs = findRequiredInputs(convertedDesign);
2596
+ if (requiredInputs.length > 0 && onInputsRequired) {
2597
+ onInputsRequired(requiredInputs, (collectedValues) => {
2598
+ const finalDesign = replaceUserInputs(convertedDesign, collectedValues);
2599
+ onLoad(finalDesign);
2600
+ });
2601
+ } else {
2602
+ onLoad(convertedDesign);
2603
+ }
2604
+ } catch (error) {
2605
+ onError(`Invalid JSON data: ${error instanceof Error ? error.message : String(error)}`);
2606
+ }
2607
+ };
2570
2608
  var importFromJSON = (event, onLoad, onError, onInputsRequired) => {
2571
2609
  const file = event.target.files?.[0];
2572
2610
  if (file) {
@@ -3019,6 +3057,8 @@ var CanvasEditor = forwardRef(({
3019
3057
  const [historyIndex, setHistoryIndex] = useState3(-1);
3020
3058
  const [showInputModal, setShowInputModal] = useState3(false);
3021
3059
  const [pendingInputs, setPendingInputs] = useState3([]);
3060
+ const [showJsonModal, setShowJsonModal] = useState3(false);
3061
+ const [jsonInputText, setJsonInputText] = useState3("");
3022
3062
  const stageRef = useRef5(null);
3023
3063
  useImperativeHandle(ref, () => ({
3024
3064
  stage: stageRef.current,
@@ -3224,15 +3264,35 @@ var CanvasEditor = forwardRef(({
3224
3264
  }, [design.pages.length]);
3225
3265
  const deletePage = useCallback4((pageId) => {
3226
3266
  if (design.pages.length > 1) {
3227
- setDesign((prev) => ({
3228
- ...prev,
3229
- pages: prev.pages.filter((p) => p.id !== pageId)
3230
- }));
3267
+ setDesign((prev) => {
3268
+ const filteredPages = prev.pages.filter((p) => p.id !== pageId);
3269
+ const renamedPages = filteredPages.map((p, idx) => ({
3270
+ ...p,
3271
+ name: `Page ${idx + 1}`
3272
+ }));
3273
+ return {
3274
+ ...prev,
3275
+ pages: renamedPages
3276
+ };
3277
+ });
3231
3278
  if (currentPageId === pageId) {
3232
- setCurrentPageId(design.pages[0].id);
3279
+ const remainingPages = design.pages.filter((p) => p.id !== pageId);
3280
+ if (remainingPages.length > 0) {
3281
+ setCurrentPageId(remainingPages[0].id);
3282
+ }
3233
3283
  }
3284
+ setTimeout(() => {
3285
+ const updatedDesign = {
3286
+ ...design,
3287
+ pages: design.pages.filter((p) => p.id !== pageId).map((p, idx) => ({
3288
+ ...p,
3289
+ name: `Page ${idx + 1}`
3290
+ }))
3291
+ };
3292
+ saveToHistory(updatedDesign);
3293
+ }, 0);
3234
3294
  }
3235
- }, [design.pages, currentPageId]);
3295
+ }, [design.pages, currentPageId, design, saveToHistory]);
3236
3296
  const handleImageUpload = useCallback4((e) => {
3237
3297
  const file = e.target.files?.[0];
3238
3298
  if (file) {
@@ -3364,14 +3424,27 @@ var CanvasEditor = forwardRef(({
3364
3424
  setUnsplashResults([]);
3365
3425
  setUnsplashMode("element");
3366
3426
  }, [currentPageId, saveToHistory, unsplashMode]);
3427
+ const calculateFitToScreenZoom = useCallback4((canvasWidth, canvasHeight) => {
3428
+ const viewportWidth = window.innerWidth - 320;
3429
+ const viewportHeight = window.innerHeight - 200;
3430
+ const scaleX = viewportWidth / canvasWidth;
3431
+ const scaleY = viewportHeight / canvasHeight;
3432
+ const optimalScale = Math.min(scaleX, scaleY, 1);
3433
+ return Math.max(0.1, Math.min(3, optimalScale));
3434
+ }, []);
3367
3435
  const handleImportJSON = (event) => {
3368
3436
  importFromJSON(event, (newDesign) => {
3369
3437
  if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3370
- setDesign(newDesign);
3371
3438
  setSelectedId(null);
3372
3439
  setSelectedIds([]);
3440
+ setTool("select");
3441
+ setDesign(newDesign);
3373
3442
  const firstPageId = newDesign.pages[0]?.id;
3374
3443
  setCurrentPageId(firstPageId || "1");
3444
+ const optimalZoom = calculateFitToScreenZoom(newDesign.width, newDesign.height);
3445
+ setZoom(optimalZoom);
3446
+ setHistory([newDesign]);
3447
+ setHistoryIndex(0);
3375
3448
  saveToHistory(newDesign);
3376
3449
  } else {
3377
3450
  alert("Invalid design structure in JSON file. Could not load.");
@@ -3387,6 +3460,40 @@ var CanvasEditor = forwardRef(({
3387
3460
  jsonInputRef.current.value = "";
3388
3461
  }
3389
3462
  };
3463
+ const handleImportJSONData = () => {
3464
+ try {
3465
+ const jsonData = JSON.parse(jsonInputText);
3466
+ importFromJSONData(jsonData, (newDesign) => {
3467
+ if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
3468
+ setSelectedId(null);
3469
+ setSelectedIds([]);
3470
+ setTool("select");
3471
+ setDesign(newDesign);
3472
+ const firstPageId = newDesign.pages[0]?.id;
3473
+ setCurrentPageId(firstPageId || "1");
3474
+ const optimalZoom = calculateFitToScreenZoom(newDesign.width, newDesign.height);
3475
+ setZoom(optimalZoom);
3476
+ setHistory([newDesign]);
3477
+ setHistoryIndex(0);
3478
+ saveToHistory(newDesign);
3479
+ setShowJsonModal(false);
3480
+ setJsonInputText("");
3481
+ } else {
3482
+ alert("Invalid design structure in JSON data. Could not load.");
3483
+ }
3484
+ }, (errorMessage) => {
3485
+ alert(`Error importing JSON: ${errorMessage}`);
3486
+ }, (inputs, onComplete) => {
3487
+ setPendingInputs(inputs);
3488
+ setShowInputModal(true);
3489
+ window.__pendingImportComplete = onComplete;
3490
+ setShowJsonModal(false);
3491
+ setJsonInputText("");
3492
+ });
3493
+ } catch (error) {
3494
+ alert(`Invalid JSON format: ${error instanceof Error ? error.message : String(error)}`);
3495
+ }
3496
+ };
3390
3497
  const handleInputModalComplete = (values) => {
3391
3498
  setShowInputModal(false);
3392
3499
  const onComplete = window.__pendingImportComplete;
@@ -3493,7 +3600,8 @@ var CanvasEditor = forwardRef(({
3493
3600
  exportToJPG(stageRef.current, design);
3494
3601
  } : undefined,
3495
3602
  onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
3496
- onImportJSON: () => jsonInputRef.current?.click()
3603
+ onImportJSON: () => jsonInputRef.current?.click(),
3604
+ onLoadJSON: () => setShowJsonModal(true)
3497
3605
  }
3498
3606
  }
3499
3607
  ] : []
@@ -3570,7 +3678,7 @@ var CanvasEditor = forwardRef(({
3570
3678
  onRedo: redo,
3571
3679
  onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
3572
3680
  onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
3573
- onZoomReset: () => setZoom(1),
3681
+ onZoomReset: () => setZoom(calculateFitToScreenZoom(design.width, design.height)),
3574
3682
  onSetActivePanel: setActivePanelId,
3575
3683
  navbarConfig: config?.navbar
3576
3684
  }), /* @__PURE__ */ React18.createElement("div", {
@@ -3597,6 +3705,7 @@ var CanvasEditor = forwardRef(({
3597
3705
  transformOrigin: "center center"
3598
3706
  }
3599
3707
  }, /* @__PURE__ */ React18.createElement(Stage, {
3708
+ key: `canvas-${design.id || design.width}-${design.height}`,
3600
3709
  ref: stageRef,
3601
3710
  width: design.width,
3602
3711
  height: design.height,
@@ -3650,11 +3759,19 @@ var CanvasEditor = forwardRef(({
3650
3759
  }
3651
3760
  }))))), config?.multiPage && /* @__PURE__ */ React18.createElement("div", {
3652
3761
  className: "absolute bottom-4 left-4 flex items-center space-x-2"
3653
- }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("button", {
3762
+ }, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("div", {
3654
3763
  key: page.id,
3764
+ className: "relative group"
3765
+ }, /* @__PURE__ */ React18.createElement("button", {
3655
3766
  onClick: () => setCurrentPageId(page.id),
3656
3767
  className: `px-3 py-1 rounded text-sm ${page.id === currentPageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
3657
- }, "Page ", index + 1)), /* @__PURE__ */ React18.createElement("button", {
3768
+ }, "Page ", index + 1), design.pages.length > 1 && /* @__PURE__ */ React18.createElement("button", {
3769
+ onClick: () => deletePage(page.id),
3770
+ className: "absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white rounded-full flex items-center justify-center transition-opacity duration-200 hover:bg-red-600",
3771
+ title: `Delete Page ${index + 1}`
3772
+ }, /* @__PURE__ */ React18.createElement(X2, {
3773
+ className: "w-3 h-3"
3774
+ })))), /* @__PURE__ */ React18.createElement("button", {
3658
3775
  onClick: addPage,
3659
3776
  className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
3660
3777
  title: "Add Page"
@@ -3733,7 +3850,31 @@ var CanvasEditor = forwardRef(({
3733
3850
  className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
3734
3851
  }))))) : /* @__PURE__ */ React18.createElement("div", {
3735
3852
  className: "text-center text-gray-400 py-12"
3736
- }, "Enter a search term to find images")))));
3853
+ }, "Enter a search term to find images")))), showJsonModal && /* @__PURE__ */ React18.createElement("div", {
3854
+ className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
3855
+ }, /* @__PURE__ */ React18.createElement("div", {
3856
+ className: "bg-gray-800 rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-auto"
3857
+ }, /* @__PURE__ */ React18.createElement("h2", {
3858
+ className: "text-white text-xl font-semibold mb-4"
3859
+ }, "Load JSON Data"), /* @__PURE__ */ React18.createElement("p", {
3860
+ className: "text-gray-300 mb-4"
3861
+ }, 'Paste your JSON template data below and click "Load" to import it into the canvas.'), /* @__PURE__ */ React18.createElement("textarea", {
3862
+ value: jsonInputText,
3863
+ onChange: (e) => setJsonInputText(e.target.value),
3864
+ className: "w-full h-64 p-3 bg-gray-700 text-white border border-gray-600 rounded font-mono text-sm resize-none",
3865
+ placeholder: '{"width": 800, "height": 600, "pages": [...]}'
3866
+ }), /* @__PURE__ */ React18.createElement("div", {
3867
+ className: "flex justify-end space-x-3 mt-4"
3868
+ }, /* @__PURE__ */ React18.createElement("button", {
3869
+ onClick: () => {
3870
+ setShowJsonModal(false);
3871
+ setJsonInputText("");
3872
+ },
3873
+ className: "px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"
3874
+ }, "Cancel"), /* @__PURE__ */ React18.createElement("button", {
3875
+ onClick: handleImportJSONData,
3876
+ className: "px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700"
3877
+ }, "Load JSON")))));
3737
3878
  });
3738
3879
  var CanvasEditor_default = CanvasEditor;
3739
3880
  // src/lib/index.ts
@@ -3794,6 +3935,7 @@ export {
3794
3935
  setUnsplashAccessKey,
3795
3936
  replaceUserInputs,
3796
3937
  initWordPressCanvasEditor,
3938
+ importFromJSONData,
3797
3939
  importFromJSON,
3798
3940
  getUnsplashAccessKey,
3799
3941
  findRequiredInputs,
@@ -1 +1 @@
1
- {"version":3,"file":"CanvasEditor.d.ts","sourceRoot":"","sources":["../../src/CanvasEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoF,MAAM,OAAO,CAAC;AAGzG,OAAO,KAAK,MAAM,OAAO,CAAC;AAyC1B,OAAO,EAEL,YAAY,EAKb,MAAM,SAAS,CAAC;AAgCjB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,YAAY,CAAC;CAC/B;AAED,QAAA,MAAM,YAAY;UAAuC,MAAM;aAAW,MAAM;yCA29B9E,CAAC;AAEH,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"CanvasEditor.d.ts","sourceRoot":"","sources":["../../src/CanvasEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoF,MAAM,OAAO,CAAC;AAGzG,OAAO,KAAK,MAAM,OAAO,CAAC;AA0C1B,OAAO,EAEL,YAAY,EAKb,MAAM,SAAS,CAAC;AAiCjB,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,YAAY,CAAC;CAC/B;AAED,QAAA,MAAM,YAAY;UAAuC,MAAM;aAAW,MAAM;yCA6nC9E,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -13,7 +13,8 @@ export interface CanvasEditorRef {
13
13
  }
14
14
  export { default as CanvasEditor } from '../CanvasEditor';
15
15
  export * from '../types';
16
- export { exportToPNG, exportToJPG, exportToJSON, importFromJSON, findRequiredInputs, replaceUserInputs, convertTemplateToCanvasDesign, canvasToBlob, } from '../utils/exportImportUtils';
16
+ export { exportToPNG, exportToJPG, exportToJSON, importFromJSON, importFromJSONData, // New: Import JSON data directly (no file needed)
17
+ findRequiredInputs, replaceUserInputs, convertTemplateToCanvasDesign, canvasToBlob, } from '../utils/exportImportUtils';
17
18
  export * from '../elements';
18
19
  export { FONT_FAMILIES, DEFAULT_COLORS, CANVAS_PRESETS } from '../constants';
19
20
  export { setUnsplashAccessKey, getUnsplashAccessKey } from '../config';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AASxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,YAAY,CAAC;CAC/B;AAGD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG1D,cAAc,UAAU,CAAC;AAGzB,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,6BAA6B,EAC7B,YAAY,GACb,MAAM,4BAA4B,CAAC;AAGpC,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AASxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,YAAY,CAAC;CAC/B;AAGD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG1D,cAAc,UAAU,CAAC;AAGzB,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,kBAAkB,EAAE,kDAAkD;AACtE,kBAAkB,EAClB,iBAAiB,EACjB,6BAA6B,EAC7B,YAAY,GACb,MAAM,4BAA4B,CAAC;AAGpC,cAAc,aAAa,CAAC;AAG5B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC"}
@@ -4,6 +4,7 @@ interface ExportPanelProps {
4
4
  onExportToJPG: () => void;
5
5
  onExportToJSON: () => void;
6
6
  onImportJSON: () => void;
7
+ onLoadJSON?: () => void;
7
8
  }
8
9
  declare const ExportPanel: React.FC<ExportPanelProps>;
9
10
  export default ExportPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"ExportPanel.d.ts","sourceRoot":"","sources":["../../../src/panels/ExportPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,gBAAgB;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAqC3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ExportPanel.d.ts","sourceRoot":"","sources":["../../../src/panels/ExportPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,gBAAgB;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA8C3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -31,5 +31,101 @@ export declare const canvasToBlob: (stage: Konva.Stage, format?: "png" | "jpg",
31
31
  quality?: number;
32
32
  pixelRatio?: number;
33
33
  }) => Promise<Blob>;
34
+ /**
35
+ * Import a canvas design from JSON data directly (without file reading)
36
+ *
37
+ * This function allows programmatic import of template data from any source
38
+ * (API calls, database, hardcoded templates, etc.) and processes it through
39
+ * the same pipeline as file-based imports.
40
+ *
41
+ * @param jsonData - The raw JSON template data to import
42
+ * @param onLoad - Callback function called when the design is successfully loaded
43
+ * @param onError - Callback function called when an error occurs during import
44
+ * @param onInputsRequired - Optional callback for handling user input collection
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Basic usage - no user inputs required
49
+ * importFromJSONData(
50
+ * {
51
+ * width: 800,
52
+ * height: 600,
53
+ * pages: [{
54
+ * id: "1",
55
+ * name: "Page 1",
56
+ * elements: [
57
+ * {
58
+ * type: "text",
59
+ * text: "Hello World",
60
+ * x: 100,
61
+ * y: 100,
62
+ * fontSize: 24
63
+ * }
64
+ * ],
65
+ * background: "white"
66
+ * }]
67
+ * },
68
+ * (design) => {
69
+ * console.log('Design loaded:', design);
70
+ * // Use the design in CanvasEditor
71
+ * },
72
+ * (error) => {
73
+ * console.error('Import failed:', error);
74
+ * }
75
+ * );
76
+ * ```
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * // With user inputs collection
81
+ * importFromJSONData(
82
+ * templateData,
83
+ * (design) => {
84
+ * // Design loaded with user inputs applied
85
+ * setDesign(design);
86
+ * },
87
+ * (error) => {
88
+ * alert('Import failed: ' + error);
89
+ * },
90
+ * (inputs, onComplete) => {
91
+ * // Show custom input modal
92
+ * showInputModal(inputs, (values) => {
93
+ * onComplete(values);
94
+ * });
95
+ * }
96
+ * );
97
+ * ```
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * // Import from API response
102
+ * async function loadTemplateFromAPI(templateId: string) {
103
+ * try {
104
+ * const response = await fetch(`/api/templates/${templateId}`);
105
+ * const templateData = await response.json();
106
+ *
107
+ * importFromJSONData(
108
+ * templateData,
109
+ * (design) => {
110
+ * // Template loaded successfully
111
+ * canvasEditorRef.current?.setDesign(design);
112
+ * },
113
+ * (error) => {
114
+ * alert('Failed to load template: ' + error);
115
+ * }
116
+ * );
117
+ * } catch (error) {
118
+ * console.error('API error:', error);
119
+ * }
120
+ * }
121
+ * ```
122
+ *
123
+ * @since 1.0.0
124
+ * @see importFromJSON - File-based version of this function
125
+ * @see convertTemplateToCanvasDesign - Template conversion logic
126
+ * @see findRequiredInputs - User input detection
127
+ * @see replaceUserInputs - Input replacement logic
128
+ */
129
+ export declare const importFromJSONData: (jsonData: any, onLoad: (design: CanvasDesign) => void, onError: (message: string) => void, onInputsRequired?: (inputs: UserInputItem[], onComplete: (values: Record<string, any>) => void) => void) => void;
34
130
  export declare const importFromJSON: (event: React.ChangeEvent<HTMLInputElement>, onLoad: (design: CanvasDesign) => void, onError: (message: string) => void, onInputsRequired?: (inputs: UserInputItem[], onComplete: (values: Record<string, any>) => void) => void) => void;
35
131
  //# sourceMappingURL=exportImportUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"exportImportUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/exportImportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,GAAG,GAAG,YAAY,CAkGrE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,EAAE,CAsHxE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACrC,YAAY,CAuNd;AAGD,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,YAAY,SAanE,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,YAAY,SAiBnE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,YAAY,SAWhD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GACrB,OAAO,KAAK,CAAC,KAAK,EAClB,SAAQ,KAAK,GAAG,KAAa,EAC7B,UAAU;IACN,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,KACF,OAAO,CAAC,IAAI,CAsCd,CAAC;AAEF,eAAO,MAAM,cAAc,GACvB,OAAO,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAC1C,QAAQ,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,EACtC,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAClC,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,KAAK,IAAI,SA0C1G,CAAC"}
1
+ {"version":3,"file":"exportImportUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/exportImportUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,GAAG,GAAG,YAAY,CAuHrE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,EAAE,CAsHxE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,YAAY,EACpB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACrC,YAAY,CA6Nd;AAGD,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,YAAY,SAanE,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,CAAC,KAAK,EAAE,QAAQ,YAAY,SAiBnE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,QAAQ,YAAY,SAWhD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GACrB,OAAO,KAAK,CAAC,KAAK,EAClB,SAAQ,KAAK,GAAG,KAAa,EAC7B,UAAU;IACN,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,KACF,OAAO,CAAC,IAAI,CAsCd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AACH,eAAO,MAAM,kBAAkB,GAC3B,UAAU,GAAG,EACb,QAAQ,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,EACtC,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAClC,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,KAAK,IAAI,SAuB1G,CAAC;AAEF,eAAO,MAAM,cAAc,GACvB,OAAO,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAC1C,QAAQ,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,EACtC,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EAClC,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,KAAK,IAAI,SA0C1G,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "labellife-design-tool",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Professional canvas editor built with React, TypeScript, and Konva",
5
5
  "main": "./dist/lib/index.js",
6
6
  "module": "./dist/lib/index.js",