labellife-design-tool 1.2.9 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/lib/index.js
CHANGED
|
@@ -547,11 +547,8 @@ function initJsxCompat() {
|
|
|
547
547
|
}
|
|
548
548
|
|
|
549
549
|
// src/CanvasEditor.tsx
|
|
550
|
-
import
|
|
550
|
+
import React17, { useRef as useRef4, useEffect as useEffect4, useMemo, useCallback as useCallback4 } from "react";
|
|
551
551
|
import { Stage, Layer, Rect as Rect2 } from "react-konva";
|
|
552
|
-
import {
|
|
553
|
-
PlusCircle
|
|
554
|
-
} from "lucide-react";
|
|
555
552
|
|
|
556
553
|
// src/elements/EditableTextElement.tsx
|
|
557
554
|
import React, { useRef, useEffect } from "react";
|
|
@@ -1361,61 +1358,6 @@ var UrlImageElement = ({ element, isSelected, onSelect, onChange }) => {
|
|
|
1361
1358
|
}
|
|
1362
1359
|
}));
|
|
1363
1360
|
};
|
|
1364
|
-
// src/constants/CanvasPresets.ts
|
|
1365
|
-
var CANVAS_PRESETS = [
|
|
1366
|
-
{ name: "Instagram Post", width: 1080, height: 1080 },
|
|
1367
|
-
{ name: "Instagram Story", width: 1080, height: 1920 },
|
|
1368
|
-
{ name: "Facebook Post", width: 1200, height: 630 },
|
|
1369
|
-
{ name: "Twitter Post", width: 1024, height: 512 },
|
|
1370
|
-
{ name: "A4", width: 794, height: 1123 },
|
|
1371
|
-
{ name: "Letter", width: 816, height: 1056 },
|
|
1372
|
-
{ name: "YouTube Thumbnail", width: 1280, height: 720 },
|
|
1373
|
-
{ name: "Custom", width: 800, height: 600 }
|
|
1374
|
-
];
|
|
1375
|
-
// src/constants/DefaultColors.ts
|
|
1376
|
-
var DEFAULT_COLORS = [
|
|
1377
|
-
"#000000",
|
|
1378
|
-
"#FFFFFF",
|
|
1379
|
-
"#FF0000",
|
|
1380
|
-
"#00FF00",
|
|
1381
|
-
"#0000FF",
|
|
1382
|
-
"#FFFF00",
|
|
1383
|
-
"#FF00FF",
|
|
1384
|
-
"#00FFFF",
|
|
1385
|
-
"#FFA500",
|
|
1386
|
-
"#800080",
|
|
1387
|
-
"#FFC0CB",
|
|
1388
|
-
"#A52A2A",
|
|
1389
|
-
"#808080",
|
|
1390
|
-
"#C0C0C0",
|
|
1391
|
-
"#FFD700"
|
|
1392
|
-
];
|
|
1393
|
-
// src/constants/FontFamilies.ts
|
|
1394
|
-
var FONT_FAMILIES = [
|
|
1395
|
-
"Arial",
|
|
1396
|
-
"Helvetica",
|
|
1397
|
-
"Times New Roman",
|
|
1398
|
-
"Georgia",
|
|
1399
|
-
"Verdana",
|
|
1400
|
-
"Courier New",
|
|
1401
|
-
"Comic Sans MS",
|
|
1402
|
-
"Impact",
|
|
1403
|
-
"Trebuchet MS",
|
|
1404
|
-
"Palatino",
|
|
1405
|
-
"Garamond",
|
|
1406
|
-
"Bookman",
|
|
1407
|
-
"Tahoma"
|
|
1408
|
-
];
|
|
1409
|
-
// src/config.ts
|
|
1410
|
-
var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
|
|
1411
|
-
var UNSPLASH_ACCESS_KEY = _unsplashAccessKey || "YOUR_UNSPLASH_ACCESS_KEY_HERE";
|
|
1412
|
-
function setUnsplashAccessKey(key) {
|
|
1413
|
-
_unsplashAccessKey = key;
|
|
1414
|
-
}
|
|
1415
|
-
function getUnsplashAccessKey() {
|
|
1416
|
-
return _unsplashAccessKey;
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
1361
|
// src/components/Header.tsx
|
|
1420
1362
|
import React7 from "react";
|
|
1421
1363
|
|
|
@@ -1879,6 +1821,53 @@ var ElementPanel_default = ElementPanel;
|
|
|
1879
1821
|
|
|
1880
1822
|
// src/panels/TextPanel.tsx
|
|
1881
1823
|
import React10 from "react";
|
|
1824
|
+
|
|
1825
|
+
// src/constants/CanvasPresets.ts
|
|
1826
|
+
var CANVAS_PRESETS = [
|
|
1827
|
+
{ name: "Instagram Post", width: 1080, height: 1080 },
|
|
1828
|
+
{ name: "Instagram Story", width: 1080, height: 1920 },
|
|
1829
|
+
{ name: "Facebook Post", width: 1200, height: 630 },
|
|
1830
|
+
{ name: "Twitter Post", width: 1024, height: 512 },
|
|
1831
|
+
{ name: "A4", width: 794, height: 1123 },
|
|
1832
|
+
{ name: "Letter", width: 816, height: 1056 },
|
|
1833
|
+
{ name: "YouTube Thumbnail", width: 1280, height: 720 },
|
|
1834
|
+
{ name: "Custom", width: 800, height: 600 }
|
|
1835
|
+
];
|
|
1836
|
+
// src/constants/DefaultColors.ts
|
|
1837
|
+
var DEFAULT_COLORS = [
|
|
1838
|
+
"#000000",
|
|
1839
|
+
"#FFFFFF",
|
|
1840
|
+
"#FF0000",
|
|
1841
|
+
"#00FF00",
|
|
1842
|
+
"#0000FF",
|
|
1843
|
+
"#FFFF00",
|
|
1844
|
+
"#FF00FF",
|
|
1845
|
+
"#00FFFF",
|
|
1846
|
+
"#FFA500",
|
|
1847
|
+
"#800080",
|
|
1848
|
+
"#FFC0CB",
|
|
1849
|
+
"#A52A2A",
|
|
1850
|
+
"#808080",
|
|
1851
|
+
"#C0C0C0",
|
|
1852
|
+
"#FFD700"
|
|
1853
|
+
];
|
|
1854
|
+
// src/constants/FontFamilies.ts
|
|
1855
|
+
var FONT_FAMILIES = [
|
|
1856
|
+
"Arial",
|
|
1857
|
+
"Helvetica",
|
|
1858
|
+
"Times New Roman",
|
|
1859
|
+
"Georgia",
|
|
1860
|
+
"Verdana",
|
|
1861
|
+
"Courier New",
|
|
1862
|
+
"Comic Sans MS",
|
|
1863
|
+
"Impact",
|
|
1864
|
+
"Trebuchet MS",
|
|
1865
|
+
"Palatino",
|
|
1866
|
+
"Garamond",
|
|
1867
|
+
"Bookman",
|
|
1868
|
+
"Tahoma"
|
|
1869
|
+
];
|
|
1870
|
+
// src/panels/TextPanel.tsx
|
|
1882
1871
|
import {
|
|
1883
1872
|
Bold,
|
|
1884
1873
|
Italic,
|
|
@@ -2725,399 +2714,350 @@ var RightSidebar = ({
|
|
|
2725
2714
|
}), /* @__PURE__ */ React16.createElement("span", null, "Delete"))));
|
|
2726
2715
|
};
|
|
2727
2716
|
var RightSidebar_default = RightSidebar;
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
onCancel
|
|
2717
|
+
|
|
2718
|
+
// src/CanvasEditor.tsx
|
|
2719
|
+
var CanvasEditor = ({
|
|
2720
|
+
name,
|
|
2721
|
+
config,
|
|
2722
|
+
store
|
|
2735
2723
|
}) => {
|
|
2736
|
-
const
|
|
2737
|
-
const
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
const [errors, setErrors] = useState2({});
|
|
2776
|
-
const [currentStep, setCurrentStep] = useState2(0);
|
|
2777
|
-
const fileInputRefs = useRef4({});
|
|
2778
|
-
const sortedInputs = [...inputs].sort((a, b) => (a.config.order || 0) - (b.config.order || 0));
|
|
2779
|
-
const currentInput = sortedInputs[currentStep];
|
|
2780
|
-
const isFirstStep = currentStep === 0;
|
|
2781
|
-
const isLastStep = currentStep === sortedInputs.length - 1;
|
|
2782
|
-
const handleTextChange = (id, value) => {
|
|
2783
|
-
setValues((prev) => ({ ...prev, [id]: value }));
|
|
2784
|
-
if (errors[id]) {
|
|
2785
|
-
setErrors((prev) => {
|
|
2786
|
-
const newErrors = { ...prev };
|
|
2787
|
-
delete newErrors[id];
|
|
2788
|
-
return newErrors;
|
|
2789
|
-
});
|
|
2790
|
-
}
|
|
2791
|
-
};
|
|
2792
|
-
const handleFileChange = (id, file) => {
|
|
2793
|
-
if (!file) {
|
|
2794
|
-
setValues((prev) => {
|
|
2795
|
-
const newValues = { ...prev };
|
|
2796
|
-
delete newValues[id];
|
|
2797
|
-
return newValues;
|
|
2798
|
-
});
|
|
2799
|
-
return;
|
|
2724
|
+
const stageRef = useRef4(null);
|
|
2725
|
+
const fileInputRef = useRef4(null);
|
|
2726
|
+
const jsonInputRef = useRef4(null);
|
|
2727
|
+
const containerRef = useRef4(null);
|
|
2728
|
+
const currentPage = store.design.pages.find((p) => p.id === store.activePageId) || store.design.pages[0] || null;
|
|
2729
|
+
const selectedElement = currentPage?.elements.find((el) => el.id === store.selectedId) || null;
|
|
2730
|
+
useEffect4(() => {
|
|
2731
|
+
const handleDesignChange = (newDesign) => {};
|
|
2732
|
+
const handleActivePageChange = (pageId) => {};
|
|
2733
|
+
const handleActivePanelChange = (panelId) => {};
|
|
2734
|
+
const handleSelectedIdChange = (selectedId) => {};
|
|
2735
|
+
const handleSelectedIdsChange = (selectedIds) => {};
|
|
2736
|
+
const handleToolChange = (tool) => {};
|
|
2737
|
+
const handleZoomChange = (zoom) => {};
|
|
2738
|
+
const handleSelectedShapeChange = (shape) => {};
|
|
2739
|
+
store.on("designChanged", handleDesignChange);
|
|
2740
|
+
store.on("activePageChanged", handleActivePageChange);
|
|
2741
|
+
store.on("activePanelChanged", handleActivePanelChange);
|
|
2742
|
+
store.on("selectedIdChanged", handleSelectedIdChange);
|
|
2743
|
+
store.on("selectedIdsChanged", handleSelectedIdsChange);
|
|
2744
|
+
store.on("toolChanged", handleToolChange);
|
|
2745
|
+
store.on("zoomChanged", handleZoomChange);
|
|
2746
|
+
store.on("selectedShapeChanged", handleSelectedShapeChange);
|
|
2747
|
+
return () => {
|
|
2748
|
+
store.off("designChanged");
|
|
2749
|
+
store.off("activePageChanged");
|
|
2750
|
+
store.off("activePanelChanged");
|
|
2751
|
+
store.off("selectedIdChanged");
|
|
2752
|
+
store.off("selectedIdsChanged");
|
|
2753
|
+
store.off("toolChanged");
|
|
2754
|
+
store.off("zoomChanged");
|
|
2755
|
+
store.off("selectedShapeChanged");
|
|
2756
|
+
};
|
|
2757
|
+
}, [store]);
|
|
2758
|
+
const handleStageClick = useCallback4((e) => {
|
|
2759
|
+
const clickedOnEmpty = e.target === e.target.getStage();
|
|
2760
|
+
if (clickedOnEmpty) {
|
|
2761
|
+
store.setSelectedId(null);
|
|
2762
|
+
store.setSelectedIds([]);
|
|
2800
2763
|
}
|
|
2801
|
-
|
|
2802
|
-
|
|
2764
|
+
}, [store]);
|
|
2765
|
+
const addText = useCallback4(() => {
|
|
2766
|
+
if (!store.activePage)
|
|
2803
2767
|
return;
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2768
|
+
store.activePage.addElement({
|
|
2769
|
+
type: "text",
|
|
2770
|
+
text: "Edit this text",
|
|
2771
|
+
x: store.design.width / 2 - 50,
|
|
2772
|
+
y: store.design.height / 2 - 15,
|
|
2773
|
+
width: 100,
|
|
2774
|
+
height: 30
|
|
2775
|
+
});
|
|
2776
|
+
}, [store]);
|
|
2777
|
+
const addShape = useCallback4((shapeType) => {
|
|
2778
|
+
if (!store.activePage)
|
|
2809
2779
|
return;
|
|
2780
|
+
store.activePage.addElement({
|
|
2781
|
+
type: "shape",
|
|
2782
|
+
shapeType,
|
|
2783
|
+
x: store.design.width / 2 - 50,
|
|
2784
|
+
y: store.design.height / 2 - 50,
|
|
2785
|
+
width: 100,
|
|
2786
|
+
height: 100
|
|
2787
|
+
});
|
|
2788
|
+
}, [store]);
|
|
2789
|
+
const updateElement = useCallback4((id, attrs) => {
|
|
2790
|
+
store.updateElement(id, attrs);
|
|
2791
|
+
}, [store]);
|
|
2792
|
+
const deleteSelected = useCallback4(() => {
|
|
2793
|
+
if (store.selectedId || store.selectedIds.length > 0) {
|
|
2794
|
+
const idsToDelete = store.selectedIds.length > 0 ? store.selectedIds : [store.selectedId];
|
|
2795
|
+
idsToDelete.forEach((id) => store.deleteElement(id));
|
|
2796
|
+
store.setSelectedId(null);
|
|
2797
|
+
store.setSelectedIds([]);
|
|
2810
2798
|
}
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2799
|
+
}, [store]);
|
|
2800
|
+
const duplicateSelected = useCallback4(() => {
|
|
2801
|
+
if (selectedElement) {
|
|
2802
|
+
const newElement = {
|
|
2803
|
+
...selectedElement,
|
|
2804
|
+
id: Date.now().toString(),
|
|
2805
|
+
name: selectedElement.name + " copy",
|
|
2806
|
+
x: selectedElement.x + 20,
|
|
2807
|
+
y: selectedElement.y + 20
|
|
2808
|
+
};
|
|
2809
|
+
store.activePage?.addElement(newElement);
|
|
2810
|
+
store.setSelectedId(newElement.id);
|
|
2818
2811
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
}
|
|
2830
|
-
|
|
2831
|
-
reader.onerror = () => {
|
|
2832
|
-
setErrors((prev) => ({
|
|
2833
|
-
...prev,
|
|
2834
|
-
[id]: "Failed to read file"
|
|
2835
|
-
}));
|
|
2812
|
+
}, [selectedElement, store]);
|
|
2813
|
+
const moveElementUp = useCallback4((id) => {}, [store]);
|
|
2814
|
+
const moveElementDown = useCallback4((id) => {}, [store]);
|
|
2815
|
+
const panelConfigs = useMemo(() => {
|
|
2816
|
+
const builtInConfigs = {
|
|
2817
|
+
elements: { id: "elements", title: "Elements", component: ElementPanel_default, props: { onAddShape: addShape, store } },
|
|
2818
|
+
text: { id: "text", title: "Text", component: TextPanel_default, props: { selectedElement, updateElement, onAddText: addText, store } },
|
|
2819
|
+
image: { id: "image", title: "Image", component: ImagePanel_default, props: { selectedElement, updateElement, store } },
|
|
2820
|
+
background: { id: "background", title: "Background", component: BackgroundPanel_default, props: { store } },
|
|
2821
|
+
design: { id: "design", title: "Design", component: DesignPanel_default, props: { store } },
|
|
2822
|
+
variables: { id: "variables", title: "Variables", component: VariablesPanel_default, props: { store } },
|
|
2823
|
+
export: { id: "export", title: "Export", component: ExportPanel_default, props: { store } }
|
|
2836
2824
|
};
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
if (config.type === "text" && value) {
|
|
2852
|
-
if (config.minLength && value.length < config.minLength) {
|
|
2853
|
-
newErrors[config.id] = `Minimum length is ${config.minLength} characters`;
|
|
2854
|
-
setErrors(newErrors);
|
|
2855
|
-
return false;
|
|
2856
|
-
}
|
|
2857
|
-
if (config.maxLength && value.length > config.maxLength) {
|
|
2858
|
-
newErrors[config.id] = `Maximum length is ${config.maxLength} characters`;
|
|
2859
|
-
setErrors(newErrors);
|
|
2860
|
-
return false;
|
|
2861
|
-
}
|
|
2862
|
-
}
|
|
2863
|
-
setErrors(newErrors);
|
|
2864
|
-
return true;
|
|
2865
|
-
};
|
|
2866
|
-
const handleNext = () => {
|
|
2867
|
-
if (!validateCurrentStep()) {
|
|
2868
|
-
return;
|
|
2869
|
-
}
|
|
2870
|
-
if (isLastStep) {
|
|
2871
|
-
const allErrors = {};
|
|
2872
|
-
sortedInputs.forEach((input) => {
|
|
2873
|
-
if (input.config.required && !values[input.config.id]) {
|
|
2874
|
-
allErrors[input.config.id] = "This field is required";
|
|
2825
|
+
const mergedConfigs = { ...builtInConfigs };
|
|
2826
|
+
if (config?.panels) {
|
|
2827
|
+
config.panels.forEach((panelConfig) => {
|
|
2828
|
+
if (typeof panelConfig === "string") {} else {
|
|
2829
|
+
const customPanelConfig = {
|
|
2830
|
+
id: panelConfig.id,
|
|
2831
|
+
title: panelConfig.title,
|
|
2832
|
+
component: panelConfig.component,
|
|
2833
|
+
props: panelConfig.props || {},
|
|
2834
|
+
...panelConfig.icon && typeof panelConfig.icon.type === "string" ? {
|
|
2835
|
+
icon: panelConfig.icon.type
|
|
2836
|
+
} : {}
|
|
2837
|
+
};
|
|
2838
|
+
mergedConfigs[panelConfig.id] = customPanelConfig;
|
|
2875
2839
|
}
|
|
2876
2840
|
});
|
|
2877
|
-
if (Object.keys(allErrors).length > 0) {
|
|
2878
|
-
setErrors(allErrors);
|
|
2879
|
-
const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
|
|
2880
|
-
if (firstErrorIndex !== -1) {
|
|
2881
|
-
setCurrentStep(firstErrorIndex);
|
|
2882
|
-
}
|
|
2883
|
-
return;
|
|
2884
|
-
}
|
|
2885
|
-
onComplete(values);
|
|
2886
|
-
} else {
|
|
2887
|
-
setCurrentStep((prev) => prev + 1);
|
|
2888
2841
|
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
const handleSkip = () => {
|
|
2896
|
-
if (!currentInput.config.required) {
|
|
2897
|
-
const defaultValue = currentInput.config.defaultValue || "";
|
|
2898
|
-
const valueToSet = defaultValue;
|
|
2899
|
-
setValues((prev) => ({ ...prev, [currentInput.config.id]: valueToSet }));
|
|
2900
|
-
setErrors((prev) => {
|
|
2901
|
-
const newErrors = { ...prev };
|
|
2902
|
-
delete newErrors[currentInput.config.id];
|
|
2903
|
-
return newErrors;
|
|
2904
|
-
});
|
|
2905
|
-
const updatedValues = { ...values, [currentInput.config.id]: valueToSet };
|
|
2906
|
-
if (isLastStep) {
|
|
2907
|
-
const allErrors = {};
|
|
2908
|
-
sortedInputs.forEach((input) => {
|
|
2909
|
-
if (input.config.required && !updatedValues[input.config.id]) {
|
|
2910
|
-
allErrors[input.config.id] = "This field is required";
|
|
2842
|
+
const orderedConfigs = [];
|
|
2843
|
+
if (config?.panels) {
|
|
2844
|
+
config.panels.forEach((panelConfig) => {
|
|
2845
|
+
if (typeof panelConfig === "string") {
|
|
2846
|
+
if (mergedConfigs[panelConfig]) {
|
|
2847
|
+
orderedConfigs.push(mergedConfigs[panelConfig]);
|
|
2911
2848
|
}
|
|
2912
|
-
}
|
|
2913
|
-
|
|
2914
|
-
setErrors(allErrors);
|
|
2915
|
-
const firstErrorIndex = sortedInputs.findIndex((input) => allErrors[input.config.id]);
|
|
2916
|
-
if (firstErrorIndex !== -1) {
|
|
2917
|
-
setCurrentStep(firstErrorIndex);
|
|
2918
|
-
}
|
|
2919
|
-
return;
|
|
2849
|
+
} else {
|
|
2850
|
+
orderedConfigs.push(mergedConfigs[panelConfig.id]);
|
|
2920
2851
|
}
|
|
2921
|
-
|
|
2922
|
-
} else {
|
|
2923
|
-
setCurrentStep((prev) => prev + 1);
|
|
2924
|
-
}
|
|
2852
|
+
});
|
|
2925
2853
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
const
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
const newErrors = { ...prev };
|
|
2984
|
-
delete newErrors[config.id];
|
|
2985
|
-
return newErrors;
|
|
2854
|
+
return orderedConfigs.length > 0 ? orderedConfigs : Object.values(builtInConfigs);
|
|
2855
|
+
}, [addShape, addText, config, selectedElement, updateElement, store]);
|
|
2856
|
+
const DynamicPanelRenderer = useMemo(() => {
|
|
2857
|
+
const activePanel = panelConfigs.find((panel) => panel.id === store.activePanelId);
|
|
2858
|
+
if (!activePanel)
|
|
2859
|
+
return null;
|
|
2860
|
+
const PanelComponent = activePanel.component;
|
|
2861
|
+
return /* @__PURE__ */ React17.createElement(PanelComponent, {
|
|
2862
|
+
key: activePanel.id,
|
|
2863
|
+
...activePanel.props
|
|
2864
|
+
});
|
|
2865
|
+
}, [panelConfigs, store.activePanelId]);
|
|
2866
|
+
const handleExportPNG = useCallback4(() => {
|
|
2867
|
+
exportToPNG(stageRef.current, store.design);
|
|
2868
|
+
}, [store.design]);
|
|
2869
|
+
const handleExportJPG = useCallback4(() => {
|
|
2870
|
+
exportToJPG(stageRef.current, store.design);
|
|
2871
|
+
}, [store.design]);
|
|
2872
|
+
const handleExportJSON = useCallback4(() => {
|
|
2873
|
+
exportToJSON(store.design);
|
|
2874
|
+
}, [store.design]);
|
|
2875
|
+
const handleImportJSON = useCallback4((e) => {
|
|
2876
|
+
const file = e.target.files?.[0];
|
|
2877
|
+
if (file) {
|
|
2878
|
+
const reader = new FileReader;
|
|
2879
|
+
reader.onload = (event) => {
|
|
2880
|
+
try {
|
|
2881
|
+
const jsonData = JSON.parse(event.target?.result);
|
|
2882
|
+
importFromJSON(jsonData, (loadedDesign) => {
|
|
2883
|
+
store.setDesign(loadedDesign);
|
|
2884
|
+
}, (errorMessage) => {
|
|
2885
|
+
console.error("Import error:", errorMessage);
|
|
2886
|
+
}, () => {
|
|
2887
|
+
console.log("Import requires user inputs - not implemented");
|
|
2888
|
+
});
|
|
2889
|
+
} catch (error) {
|
|
2890
|
+
console.error("Invalid JSON file:", error);
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
reader.readAsText(file);
|
|
2894
|
+
}
|
|
2895
|
+
}, [store]);
|
|
2896
|
+
const handleImageUpload = useCallback4((e) => {
|
|
2897
|
+
const file = e.target.files?.[0];
|
|
2898
|
+
if (file) {
|
|
2899
|
+
const reader = new FileReader;
|
|
2900
|
+
reader.onload = (event) => {
|
|
2901
|
+
const img = new window.Image;
|
|
2902
|
+
img.onload = () => {
|
|
2903
|
+
if (store.activePage) {
|
|
2904
|
+
store.activePage.addElement({
|
|
2905
|
+
type: "image",
|
|
2906
|
+
src: event.target?.result,
|
|
2907
|
+
x: store.design.width / 2 - img.width / 2,
|
|
2908
|
+
y: store.design.height / 2 - img.height / 2,
|
|
2909
|
+
width: img.width,
|
|
2910
|
+
height: img.height
|
|
2986
2911
|
});
|
|
2987
|
-
},
|
|
2988
|
-
className: "text-sm",
|
|
2989
|
-
style: { color: "#ef4444" }
|
|
2990
|
-
}, isDefaultValue ? "Clear" : "Remove"))), value && isUrl && /* @__PURE__ */ React17.createElement("div", {
|
|
2991
|
-
className: "mt-2"
|
|
2992
|
-
}, /* @__PURE__ */ React17.createElement("img", {
|
|
2993
|
-
src: value,
|
|
2994
|
-
alt: "Preview",
|
|
2995
|
-
className: "max-w-xs max-h-32 object-contain rounded border",
|
|
2996
|
-
style: { borderColor: isLightTheme ? "#cbd5e1" : "#4b5563" },
|
|
2997
|
-
onError: (e) => {
|
|
2998
|
-
setErrors((prev) => ({
|
|
2999
|
-
...prev,
|
|
3000
|
-
[config.id]: "Failed to load image"
|
|
3001
|
-
}));
|
|
3002
|
-
}
|
|
3003
|
-
})), error && /* @__PURE__ */ React17.createElement("p", {
|
|
3004
|
-
className: "text-sm",
|
|
3005
|
-
style: { color: "#ef4444" }
|
|
3006
|
-
}, error));
|
|
3007
|
-
}
|
|
3008
|
-
case "text":
|
|
3009
|
-
return /* @__PURE__ */ React17.createElement("div", {
|
|
3010
|
-
className: "space-y-2"
|
|
3011
|
-
}, /* @__PURE__ */ React17.createElement("label", {
|
|
3012
|
-
className: "block text-sm font-medium",
|
|
3013
|
-
style: labelStyle
|
|
3014
|
-
}, config.label, config.required && /* @__PURE__ */ React17.createElement("span", {
|
|
3015
|
-
className: "text-red-400 ml-1"
|
|
3016
|
-
}, "*")), config.helpText && /* @__PURE__ */ React17.createElement("p", {
|
|
3017
|
-
className: "text-xs",
|
|
3018
|
-
style: helpTextStyle
|
|
3019
|
-
}, config.helpText), /* @__PURE__ */ React17.createElement("input", {
|
|
3020
|
-
type: "text",
|
|
3021
|
-
value,
|
|
3022
|
-
onChange: (e) => handleTextChange(config.id, e.target.value),
|
|
3023
|
-
placeholder: config.placeholder || "Enter text here...",
|
|
3024
|
-
maxLength: config.maxLength,
|
|
3025
|
-
className: "w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500",
|
|
3026
|
-
style: {
|
|
3027
|
-
backgroundColor: "#ffffff",
|
|
3028
|
-
color: "#0f172a",
|
|
3029
|
-
borderColor: error ? "#ef4444" : "#cbd5e1"
|
|
3030
2912
|
}
|
|
3031
|
-
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
className: "text-sm",
|
|
3036
|
-
style: { color: "#ef4444" }
|
|
3037
|
-
}, error));
|
|
3038
|
-
default:
|
|
3039
|
-
return null;
|
|
2913
|
+
};
|
|
2914
|
+
img.src = event.target?.result;
|
|
2915
|
+
};
|
|
2916
|
+
reader.readAsDataURL(file);
|
|
3040
2917
|
}
|
|
3041
|
-
};
|
|
3042
|
-
if (!currentInput) {
|
|
3043
|
-
return null;
|
|
3044
|
-
}
|
|
2918
|
+
}, [store]);
|
|
3045
2919
|
return /* @__PURE__ */ React17.createElement("div", {
|
|
3046
|
-
className: "
|
|
3047
|
-
}, /* @__PURE__ */ React17.createElement(
|
|
3048
|
-
|
|
3049
|
-
|
|
2920
|
+
className: "w-full h-full flex flex-col bg-gray-900"
|
|
2921
|
+
}, /* @__PURE__ */ React17.createElement(Header_default, {
|
|
2922
|
+
name,
|
|
2923
|
+
zoom: store.zoom,
|
|
2924
|
+
historyIndex: 0,
|
|
2925
|
+
historyLength: 0,
|
|
2926
|
+
onUndo: () => {},
|
|
2927
|
+
onRedo: () => {},
|
|
2928
|
+
onZoomIn: () => store.setZoom(Math.min(3, store.zoom + 0.1)),
|
|
2929
|
+
onZoomOut: () => store.setZoom(Math.max(0.1, store.zoom - 0.1)),
|
|
2930
|
+
onZoomReset: () => store.setZoom(1),
|
|
2931
|
+
onSetActivePanel: store.openSidePanel
|
|
2932
|
+
}), /* @__PURE__ */ React17.createElement("div", {
|
|
2933
|
+
className: "flex-1 flex overflow-hidden"
|
|
3050
2934
|
}, /* @__PURE__ */ React17.createElement("div", {
|
|
3051
|
-
className: "
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
}, /* @__PURE__ */ React17.createElement(X, {
|
|
3066
|
-
className: "w-5 h-5"
|
|
3067
|
-
}))), /* @__PURE__ */ React17.createElement("div", {
|
|
3068
|
-
className: "px-6 pt-4"
|
|
2935
|
+
className: "w-80 bg-gray-800 border-r border-gray-700 flex"
|
|
2936
|
+
}, /* @__PURE__ */ React17.createElement(LeftMenu_default, {
|
|
2937
|
+
tool: store.tool,
|
|
2938
|
+
activePanel: store.activePanelId || "elements",
|
|
2939
|
+
onSetTool: store.setTool,
|
|
2940
|
+
onSetActivePanel: store.openSidePanel,
|
|
2941
|
+
config
|
|
2942
|
+
}), /* @__PURE__ */ React17.createElement("div", {
|
|
2943
|
+
className: "flex-1 p-4 overflow-y-auto"
|
|
2944
|
+
}, /* @__PURE__ */ React17.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React17.createElement("div", {
|
|
2945
|
+
className: "flex-1 bg-gray-700 overflow-hidden relative",
|
|
2946
|
+
ref: containerRef
|
|
2947
|
+
}, !currentPage ? /* @__PURE__ */ React17.createElement("div", {
|
|
2948
|
+
className: "absolute inset-0 flex items-center justify-center text-gray-400"
|
|
3069
2949
|
}, /* @__PURE__ */ React17.createElement("div", {
|
|
3070
|
-
className: "
|
|
3071
|
-
|
|
2950
|
+
className: "text-center"
|
|
2951
|
+
}, /* @__PURE__ */ React17.createElement("p", {
|
|
2952
|
+
className: "mb-4"
|
|
2953
|
+
}, "No pages available"), /* @__PURE__ */ React17.createElement("p", {
|
|
2954
|
+
className: "text-sm"
|
|
2955
|
+
}, "Please add pages through your store management"))) : /* @__PURE__ */ React17.createElement("div", {
|
|
2956
|
+
className: "absolute inset-0 flex items-center justify-center"
|
|
3072
2957
|
}, /* @__PURE__ */ React17.createElement("div", {
|
|
3073
|
-
className: "
|
|
2958
|
+
className: "bg-white shadow-2xl",
|
|
3074
2959
|
style: {
|
|
3075
|
-
|
|
3076
|
-
|
|
2960
|
+
transform: `scale(${store.zoom})`,
|
|
2961
|
+
transformOrigin: "center center"
|
|
3077
2962
|
}
|
|
3078
|
-
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
2963
|
+
}, /* @__PURE__ */ React17.createElement(Stage, {
|
|
2964
|
+
ref: stageRef,
|
|
2965
|
+
width: store.design.width,
|
|
2966
|
+
height: store.design.height,
|
|
2967
|
+
onClick: handleStageClick,
|
|
2968
|
+
onTap: handleStageClick
|
|
2969
|
+
}, /* @__PURE__ */ React17.createElement(Layer, null, /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(Rect2, {
|
|
2970
|
+
x: 0,
|
|
2971
|
+
y: 0,
|
|
2972
|
+
width: store.design.width,
|
|
2973
|
+
height: store.design.height,
|
|
2974
|
+
fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
|
|
2975
|
+
fillPatternImage: currentPage.backgroundImageObject,
|
|
2976
|
+
fillPatternRepeat: "no-repeat"
|
|
2977
|
+
}), currentPage.elements.map((element) => {
|
|
2978
|
+
switch (element.type) {
|
|
2979
|
+
case "text":
|
|
2980
|
+
return /* @__PURE__ */ React17.createElement(EditableTextElement, {
|
|
2981
|
+
key: element.id,
|
|
2982
|
+
element,
|
|
2983
|
+
isSelected: element.id === store.selectedId,
|
|
2984
|
+
onSelect: () => {
|
|
2985
|
+
store.setSelectedId(element.id);
|
|
2986
|
+
store.setSelectedIds([]);
|
|
2987
|
+
},
|
|
2988
|
+
onChange: (attrs) => updateElement(element.id, attrs)
|
|
2989
|
+
});
|
|
2990
|
+
case "image":
|
|
2991
|
+
return /* @__PURE__ */ React17.createElement(UrlImageElement, {
|
|
2992
|
+
key: element.id,
|
|
2993
|
+
element,
|
|
2994
|
+
isSelected: element.id === store.selectedId,
|
|
2995
|
+
onSelect: () => {
|
|
2996
|
+
store.setSelectedId(element.id);
|
|
2997
|
+
store.setSelectedIds([]);
|
|
2998
|
+
},
|
|
2999
|
+
onChange: (attrs) => updateElement(element.id, attrs)
|
|
3000
|
+
});
|
|
3001
|
+
case "shape":
|
|
3002
|
+
return /* @__PURE__ */ React17.createElement(ShapeElement, {
|
|
3003
|
+
key: element.id,
|
|
3004
|
+
element,
|
|
3005
|
+
isSelected: element.id === store.selectedId,
|
|
3006
|
+
onSelect: () => {
|
|
3007
|
+
store.setSelectedId(element.id);
|
|
3008
|
+
store.setSelectedIds([]);
|
|
3009
|
+
},
|
|
3010
|
+
onChange: (attrs) => updateElement(element.id, attrs)
|
|
3011
|
+
});
|
|
3012
|
+
default:
|
|
3013
|
+
return null;
|
|
3014
|
+
}
|
|
3015
|
+
})))))), config?.multiPage && /* @__PURE__ */ React17.createElement("div", {
|
|
3016
|
+
className: "absolute bottom-4 left-4 flex items-center space-x-2"
|
|
3017
|
+
}, store.design.pages.map((page, index) => /* @__PURE__ */ React17.createElement("button", {
|
|
3018
|
+
key: page.id,
|
|
3019
|
+
onClick: () => store.setActivePage(page.id),
|
|
3020
|
+
className: `px-3 py-1 rounded text-sm ${page.id === store.activePageId ? "bg-blue-600 text-white" : "bg-gray-800 text-gray-300 hover:bg-gray-700"}`
|
|
3021
|
+
}, "Page ", index + 1)))), /* @__PURE__ */ React17.createElement(RightSidebar_default, {
|
|
3022
|
+
currentPageElements: currentPage?.elements || [],
|
|
3023
|
+
selectedElement,
|
|
3024
|
+
selectedId: store.selectedId,
|
|
3025
|
+
onSelectElement: store.setSelectedId,
|
|
3026
|
+
onUpdateElement: updateElement,
|
|
3027
|
+
onDuplicateSelected: duplicateSelected,
|
|
3028
|
+
onMoveElementUp: moveElementUp,
|
|
3029
|
+
onMoveElementDown: moveElementDown,
|
|
3030
|
+
onDeleteSelected: deleteSelected
|
|
3031
|
+
})), /* @__PURE__ */ React17.createElement("input", {
|
|
3032
|
+
ref: fileInputRef,
|
|
3033
|
+
type: "file",
|
|
3034
|
+
accept: "image/*",
|
|
3035
|
+
onChange: handleImageUpload,
|
|
3036
|
+
style: { display: "none" }
|
|
3037
|
+
}), /* @__PURE__ */ React17.createElement("input", {
|
|
3038
|
+
ref: jsonInputRef,
|
|
3039
|
+
type: "file",
|
|
3040
|
+
accept: ".json",
|
|
3041
|
+
onChange: handleImportJSON,
|
|
3042
|
+
style: { display: "none" }
|
|
3043
|
+
}));
|
|
3044
|
+
};
|
|
3045
|
+
var CanvasEditor_default = CanvasEditor;
|
|
3046
|
+
// src/store/simpleStore.ts
|
|
3047
|
+
class SimpleCanvasStore {
|
|
3048
|
+
_design;
|
|
3049
|
+
_activePageId;
|
|
3050
|
+
_activePanelId;
|
|
3051
|
+
_selectedId;
|
|
3052
|
+
_selectedIds;
|
|
3053
|
+
_tool;
|
|
3054
|
+
_zoom;
|
|
3055
|
+
_selectedShape;
|
|
3056
|
+
_listeners = {};
|
|
3057
|
+
constructor(initialDesign) {
|
|
3058
|
+
this._design = {
|
|
3059
|
+
id: initialDesign?.id || `design-${Date.now()}`,
|
|
3060
|
+
name: initialDesign?.name || "Untitled Design",
|
|
3121
3061
|
width: initialDesign?.width || 800,
|
|
3122
3062
|
height: initialDesign?.height || 600,
|
|
3123
3063
|
pages: initialDesign?.pages || [],
|
|
@@ -3127,6 +3067,11 @@ class SimpleCanvasStore {
|
|
|
3127
3067
|
};
|
|
3128
3068
|
this._activePageId = initialDesign?.pages?.[0]?.id || null;
|
|
3129
3069
|
this._activePanelId = null;
|
|
3070
|
+
this._selectedId = null;
|
|
3071
|
+
this._selectedIds = [];
|
|
3072
|
+
this._tool = "select";
|
|
3073
|
+
this._zoom = 1;
|
|
3074
|
+
this._selectedShape = "rect";
|
|
3130
3075
|
}
|
|
3131
3076
|
on(event, callback) {
|
|
3132
3077
|
this._listeners[event] = callback;
|
|
@@ -3166,12 +3111,27 @@ class SimpleCanvasStore {
|
|
|
3166
3111
|
get activePanelId() {
|
|
3167
3112
|
return this._activePanelId;
|
|
3168
3113
|
}
|
|
3114
|
+
get selectedId() {
|
|
3115
|
+
return this._selectedId;
|
|
3116
|
+
}
|
|
3117
|
+
get selectedIds() {
|
|
3118
|
+
return this._selectedIds;
|
|
3119
|
+
}
|
|
3120
|
+
get tool() {
|
|
3121
|
+
return this._tool;
|
|
3122
|
+
}
|
|
3123
|
+
get zoom() {
|
|
3124
|
+
return this._zoom;
|
|
3125
|
+
}
|
|
3126
|
+
get selectedShape() {
|
|
3127
|
+
return this._selectedShape;
|
|
3128
|
+
}
|
|
3169
3129
|
setDesign(design) {
|
|
3170
3130
|
this._design = design;
|
|
3171
3131
|
if (design.pages.find((p) => p.id === this._activePageId)) {} else if (design.pages.length > 0) {
|
|
3172
3132
|
this._activePageId = design.pages[0].id;
|
|
3173
3133
|
}
|
|
3174
|
-
this.emit("designChanged",
|
|
3134
|
+
this.emit("designChanged", this._design);
|
|
3175
3135
|
this.emit("activePageChanged", this._activePageId);
|
|
3176
3136
|
}
|
|
3177
3137
|
updateDesign(updates) {
|
|
@@ -3213,14 +3173,6 @@ class SimpleCanvasStore {
|
|
|
3213
3173
|
this._design.pages = remainingPages;
|
|
3214
3174
|
this.emit("designChanged", this._design);
|
|
3215
3175
|
}
|
|
3216
|
-
setActivePage(pageId) {
|
|
3217
|
-
const page = this._design.pages.find((p) => p.id === pageId);
|
|
3218
|
-
if (page) {
|
|
3219
|
-
this._activePageId = pageId;
|
|
3220
|
-
this.emit("activePageChanged", pageId);
|
|
3221
|
-
this.emit("designChanged", this._design);
|
|
3222
|
-
}
|
|
3223
|
-
}
|
|
3224
3176
|
addElement(element) {
|
|
3225
3177
|
const page = this.activePage;
|
|
3226
3178
|
if (!page) {
|
|
@@ -3242,34 +3194,32 @@ class SimpleCanvasStore {
|
|
|
3242
3194
|
...element
|
|
3243
3195
|
};
|
|
3244
3196
|
page.elements.push(newElement);
|
|
3245
|
-
console.log(`addElement: Added element ${newElement.id} to page. Total elements: ${page.elements.length}`);
|
|
3246
3197
|
this.emit("designChanged", this._design);
|
|
3247
|
-
console.log("addElement: Emitted designChanged event");
|
|
3248
|
-
}
|
|
3249
|
-
updateElement(elementId, updates) {
|
|
3250
|
-
for (const page of this._design.pages) {
|
|
3251
|
-
const elementIndex = page.elements.findIndex((e) => e.id === elementId);
|
|
3252
|
-
if (elementIndex !== -1) {
|
|
3253
|
-
page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
|
|
3254
|
-
this.emit("designChanged", this._design);
|
|
3255
|
-
return;
|
|
3256
|
-
}
|
|
3257
|
-
}
|
|
3258
|
-
}
|
|
3259
|
-
deleteElement(elementId) {
|
|
3260
|
-
for (const page of this._design.pages) {
|
|
3261
|
-
const elementIndex = page.elements.findIndex((e) => e.id === elementId);
|
|
3262
|
-
if (elementIndex !== -1) {
|
|
3263
|
-
page.elements.splice(elementIndex, 1);
|
|
3264
|
-
this.emit("designChanged", this._design);
|
|
3265
|
-
return;
|
|
3266
|
-
}
|
|
3267
|
-
}
|
|
3268
3198
|
}
|
|
3269
3199
|
openSidePanel(panelId) {
|
|
3270
3200
|
this._activePanelId = panelId;
|
|
3271
3201
|
this.emit("activePanelChanged", panelId);
|
|
3272
3202
|
}
|
|
3203
|
+
setSelectedId(selectedId) {
|
|
3204
|
+
this._selectedId = selectedId;
|
|
3205
|
+
this.emit("selectedIdChanged", selectedId);
|
|
3206
|
+
}
|
|
3207
|
+
setSelectedIds(selectedIds) {
|
|
3208
|
+
this._selectedIds = selectedIds;
|
|
3209
|
+
this.emit("selectedIdsChanged", selectedIds);
|
|
3210
|
+
}
|
|
3211
|
+
setTool(tool) {
|
|
3212
|
+
this._tool = tool;
|
|
3213
|
+
this.emit("toolChanged", tool);
|
|
3214
|
+
}
|
|
3215
|
+
setZoom(zoom) {
|
|
3216
|
+
this._zoom = zoom;
|
|
3217
|
+
this.emit("zoomChanged", zoom);
|
|
3218
|
+
}
|
|
3219
|
+
setSelectedShape(shape) {
|
|
3220
|
+
this._selectedShape = shape;
|
|
3221
|
+
this.emit("selectedShapeChanged", shape);
|
|
3222
|
+
}
|
|
3273
3223
|
async toBlob(type = "png") {
|
|
3274
3224
|
return new Promise((resolve, reject) => {
|
|
3275
3225
|
try {
|
|
@@ -3299,6 +3249,34 @@ class SimpleCanvasStore {
|
|
|
3299
3249
|
}
|
|
3300
3250
|
});
|
|
3301
3251
|
}
|
|
3252
|
+
setActivePage(pageId) {
|
|
3253
|
+
const page = this._design.pages.find((p) => p.id === pageId);
|
|
3254
|
+
if (page) {
|
|
3255
|
+
this._activePageId = pageId;
|
|
3256
|
+
this.emit("activePageChanged", pageId);
|
|
3257
|
+
this.emit("designChanged", this._design);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
updateElement(elementId, updates) {
|
|
3261
|
+
for (const page of this._design.pages) {
|
|
3262
|
+
const elementIndex = page.elements.findIndex((e) => e.id === elementId);
|
|
3263
|
+
if (elementIndex !== -1) {
|
|
3264
|
+
page.elements[elementIndex] = { ...page.elements[elementIndex], ...updates };
|
|
3265
|
+
this.emit("designChanged", this._design);
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
deleteElement(elementId) {
|
|
3271
|
+
for (const page of this._design.pages) {
|
|
3272
|
+
const elementIndex = page.elements.findIndex((e) => e.id === elementId);
|
|
3273
|
+
if (elementIndex !== -1) {
|
|
3274
|
+
page.elements.splice(elementIndex, 1);
|
|
3275
|
+
this.emit("designChanged", this._design);
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3302
3280
|
async loadJSON(jsonData) {
|
|
3303
3281
|
return new Promise((resolve, reject) => {
|
|
3304
3282
|
try {
|
|
@@ -3321,843 +3299,6 @@ class SimpleCanvasStore {
|
|
|
3321
3299
|
var createSimpleStore = (initialDesign) => {
|
|
3322
3300
|
return new SimpleCanvasStore(initialDesign);
|
|
3323
3301
|
};
|
|
3324
|
-
|
|
3325
|
-
// src/CanvasEditor.tsx
|
|
3326
|
-
var CanvasEditor = ({
|
|
3327
|
-
name,
|
|
3328
|
-
config,
|
|
3329
|
-
store: externalStore
|
|
3330
|
-
}) => {
|
|
3331
|
-
console.log("=== CanvasEditor initializing ===", { hasExternalStore: !!externalStore });
|
|
3332
|
-
const store = externalStore || createSimpleStore({
|
|
3333
|
-
name,
|
|
3334
|
-
width: 800,
|
|
3335
|
-
height: 600
|
|
3336
|
-
});
|
|
3337
|
-
const [design, setDesign] = useState3(() => {
|
|
3338
|
-
if (externalStore) {
|
|
3339
|
-
return externalStore.design;
|
|
3340
|
-
}
|
|
3341
|
-
return {
|
|
3342
|
-
id: Date.now().toString(),
|
|
3343
|
-
name: "Untitled Design",
|
|
3344
|
-
width: 800,
|
|
3345
|
-
height: 600,
|
|
3346
|
-
pages: [
|
|
3347
|
-
{
|
|
3348
|
-
id: "1",
|
|
3349
|
-
name: "Page 1",
|
|
3350
|
-
elements: [],
|
|
3351
|
-
background: "#ffffff"
|
|
3352
|
-
}
|
|
3353
|
-
],
|
|
3354
|
-
fonts: FONT_FAMILIES,
|
|
3355
|
-
colors: DEFAULT_COLORS
|
|
3356
|
-
};
|
|
3357
|
-
});
|
|
3358
|
-
const [currentPageId, setCurrentPageId] = useState3(() => {
|
|
3359
|
-
if (externalStore?.design.pages && externalStore.design.pages.length > 0) {
|
|
3360
|
-
return externalStore.design.pages[0].id;
|
|
3361
|
-
}
|
|
3362
|
-
if (externalStore?.activePageId) {
|
|
3363
|
-
return externalStore.activePageId;
|
|
3364
|
-
}
|
|
3365
|
-
return "1";
|
|
3366
|
-
});
|
|
3367
|
-
const [selectedId, setSelectedId] = useState3(null);
|
|
3368
|
-
const [selectedIds, setSelectedIds] = useState3([]);
|
|
3369
|
-
const [tool, setTool] = useState3("select");
|
|
3370
|
-
const [activePanelId, setActivePanelId] = useState3("elements");
|
|
3371
|
-
useEffect4(() => {
|
|
3372
|
-
if (externalStore) {
|
|
3373
|
-
const handleDesignChange = (newDesign) => {
|
|
3374
|
-
console.log("CanvasEditor received designChanged:", newDesign.pages[0]?.elements.length, "elements on page 1");
|
|
3375
|
-
setDesign(newDesign);
|
|
3376
|
-
};
|
|
3377
|
-
const handleActivePageChange = (pageId) => {
|
|
3378
|
-
setCurrentPageId(pageId);
|
|
3379
|
-
};
|
|
3380
|
-
const handleActivePanelChange = (panelId) => {
|
|
3381
|
-
setActivePanelId(panelId || "elements");
|
|
3382
|
-
};
|
|
3383
|
-
externalStore.on("designChanged", handleDesignChange);
|
|
3384
|
-
externalStore.on("activePageChanged", handleActivePageChange);
|
|
3385
|
-
externalStore.on("activePanelChanged", handleActivePanelChange);
|
|
3386
|
-
return () => {
|
|
3387
|
-
externalStore.off("designChanged");
|
|
3388
|
-
externalStore.off("activePageChanged");
|
|
3389
|
-
externalStore.off("activePanelChanged");
|
|
3390
|
-
};
|
|
3391
|
-
}
|
|
3392
|
-
}, [externalStore]);
|
|
3393
|
-
const [showUnsplash, setShowUnsplash] = useState3(false);
|
|
3394
|
-
const [unsplashQuery, setUnsplashQuery] = useState3("");
|
|
3395
|
-
const [unsplashResults, setUnsplashResults] = useState3([]);
|
|
3396
|
-
const [unsplashMode, setUnsplashMode] = useState3("element");
|
|
3397
|
-
const [zoom, setZoom] = useState3(1);
|
|
3398
|
-
const [selectedShape, setSelectedShape] = useState3("rect");
|
|
3399
|
-
const [history, setHistory] = useState3([]);
|
|
3400
|
-
const [historyIndex, setHistoryIndex] = useState3(-1);
|
|
3401
|
-
const [showInputModal, setShowInputModal] = useState3(false);
|
|
3402
|
-
const [pendingInputs, setPendingInputs] = useState3([]);
|
|
3403
|
-
const stageRef = useRef5(null);
|
|
3404
|
-
const fileInputRef = useRef5(null);
|
|
3405
|
-
const jsonInputRef = useRef5(null);
|
|
3406
|
-
const containerRef = useRef5(null);
|
|
3407
|
-
const currentPage = design.pages.find((p) => p.id === currentPageId) || design.pages[0] || null;
|
|
3408
|
-
const selectedElement = currentPage?.elements.find((el) => el.id === selectedId) || null;
|
|
3409
|
-
const saveToHistory = useCallback4((newDesign) => {
|
|
3410
|
-
const newHistory = history.slice(0, historyIndex + 1);
|
|
3411
|
-
newHistory.push(JSON.parse(JSON.stringify(newDesign)));
|
|
3412
|
-
setHistory(newHistory);
|
|
3413
|
-
setHistoryIndex(newHistory.length - 1);
|
|
3414
|
-
}, [history, historyIndex]);
|
|
3415
|
-
const undo = useCallback4(() => {
|
|
3416
|
-
if (historyIndex > 0) {
|
|
3417
|
-
setHistoryIndex(historyIndex - 1);
|
|
3418
|
-
setDesign(JSON.parse(JSON.stringify(history[historyIndex - 1])));
|
|
3419
|
-
}
|
|
3420
|
-
}, [history, historyIndex]);
|
|
3421
|
-
const redo = useCallback4(() => {
|
|
3422
|
-
if (historyIndex < history.length - 1) {
|
|
3423
|
-
setHistoryIndex(historyIndex + 1);
|
|
3424
|
-
setDesign(JSON.parse(JSON.stringify(history[historyIndex + 1])));
|
|
3425
|
-
}
|
|
3426
|
-
}, [history, historyIndex]);
|
|
3427
|
-
const addText = useCallback4(() => {
|
|
3428
|
-
const newElement = {
|
|
3429
|
-
id: Date.now().toString(),
|
|
3430
|
-
type: "text",
|
|
3431
|
-
name: "Text",
|
|
3432
|
-
x: design.width / 2 - 50,
|
|
3433
|
-
y: design.height / 2 - 20,
|
|
3434
|
-
rotation: 0,
|
|
3435
|
-
visible: true,
|
|
3436
|
-
locked: false,
|
|
3437
|
-
opacity: 1,
|
|
3438
|
-
text: "Click to edit text",
|
|
3439
|
-
fontSize: 24,
|
|
3440
|
-
fontFamily: "Arial",
|
|
3441
|
-
fill: "#000000",
|
|
3442
|
-
align: "left",
|
|
3443
|
-
lineHeight: 1.2,
|
|
3444
|
-
letterSpacing: 0,
|
|
3445
|
-
textCase: "none",
|
|
3446
|
-
strikethrough: false
|
|
3447
|
-
};
|
|
3448
|
-
const newDesign = {
|
|
3449
|
-
...design,
|
|
3450
|
-
pages: design.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
|
|
3451
|
-
};
|
|
3452
|
-
setDesign(newDesign);
|
|
3453
|
-
saveToHistory(newDesign);
|
|
3454
|
-
if (externalStore) {
|
|
3455
|
-
externalStore.setDesign(newDesign);
|
|
3456
|
-
}
|
|
3457
|
-
setSelectedId(newElement.id);
|
|
3458
|
-
setTool("select");
|
|
3459
|
-
}, [currentPageId, saveToHistory, design.width, design.height, externalStore]);
|
|
3460
|
-
const addShape = useCallback4((shapeType) => {
|
|
3461
|
-
const newElement = {
|
|
3462
|
-
id: Date.now().toString(),
|
|
3463
|
-
type: "shape",
|
|
3464
|
-
name: shapeType.charAt(0).toUpperCase() + shapeType.slice(1),
|
|
3465
|
-
x: design.width / 2 - 50,
|
|
3466
|
-
y: design.height / 2 - 50,
|
|
3467
|
-
width: 100,
|
|
3468
|
-
height: 100,
|
|
3469
|
-
rotation: 0,
|
|
3470
|
-
visible: true,
|
|
3471
|
-
locked: false,
|
|
3472
|
-
opacity: 1,
|
|
3473
|
-
fill: "#4299e1",
|
|
3474
|
-
stroke: "#000000",
|
|
3475
|
-
strokeWidth: 0,
|
|
3476
|
-
shapeType,
|
|
3477
|
-
sides: shapeType === "polygon" ? 6 : undefined,
|
|
3478
|
-
innerRadius: shapeType === "star" ? 40 : undefined,
|
|
3479
|
-
outerRadius: shapeType === "star" ? 70 : undefined
|
|
3480
|
-
};
|
|
3481
|
-
setDesign((prev) => {
|
|
3482
|
-
const newDesign = {
|
|
3483
|
-
...prev,
|
|
3484
|
-
pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
|
|
3485
|
-
};
|
|
3486
|
-
saveToHistory(newDesign);
|
|
3487
|
-
return newDesign;
|
|
3488
|
-
});
|
|
3489
|
-
setSelectedId(newElement.id);
|
|
3490
|
-
}, [currentPageId, saveToHistory, design.width, design.height]);
|
|
3491
|
-
const handleStageClick = useCallback4((e) => {
|
|
3492
|
-
const clickedOnEmpty = e.target === e.target.getStage();
|
|
3493
|
-
if (clickedOnEmpty) {
|
|
3494
|
-
setSelectedId(null);
|
|
3495
|
-
setSelectedIds([]);
|
|
3496
|
-
}
|
|
3497
|
-
}, []);
|
|
3498
|
-
const updateElement = useCallback4((id, attrs) => {
|
|
3499
|
-
setDesign((prev) => ({
|
|
3500
|
-
...prev,
|
|
3501
|
-
pages: prev.pages.map((page) => ({
|
|
3502
|
-
...page,
|
|
3503
|
-
elements: page.elements.map((el) => el.id === id ? { ...el, ...attrs } : el)
|
|
3504
|
-
}))
|
|
3505
|
-
}));
|
|
3506
|
-
}, []);
|
|
3507
|
-
const deleteSelected = useCallback4(() => {
|
|
3508
|
-
if (selectedId || selectedIds.length > 0) {
|
|
3509
|
-
const idsToDelete = selectedIds.length > 0 ? selectedIds : [selectedId];
|
|
3510
|
-
setDesign((prev) => ({
|
|
3511
|
-
...prev,
|
|
3512
|
-
pages: prev.pages.map((page) => ({
|
|
3513
|
-
...page,
|
|
3514
|
-
elements: page.elements.filter((el) => !idsToDelete.includes(el.id))
|
|
3515
|
-
}))
|
|
3516
|
-
}));
|
|
3517
|
-
setSelectedId(null);
|
|
3518
|
-
setSelectedIds([]);
|
|
3519
|
-
saveToHistory(design);
|
|
3520
|
-
}
|
|
3521
|
-
}, [selectedId, selectedIds, saveToHistory, design]);
|
|
3522
|
-
const duplicateSelected = useCallback4(() => {
|
|
3523
|
-
if (selectedElement) {
|
|
3524
|
-
const newElement = {
|
|
3525
|
-
...selectedElement,
|
|
3526
|
-
id: Date.now().toString(),
|
|
3527
|
-
name: selectedElement.name + " copy",
|
|
3528
|
-
x: selectedElement.x + 20,
|
|
3529
|
-
y: selectedElement.y + 20
|
|
3530
|
-
};
|
|
3531
|
-
setDesign((prev) => {
|
|
3532
|
-
const newDesign = {
|
|
3533
|
-
...prev,
|
|
3534
|
-
pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
|
|
3535
|
-
};
|
|
3536
|
-
saveToHistory(newDesign);
|
|
3537
|
-
return newDesign;
|
|
3538
|
-
});
|
|
3539
|
-
setSelectedId(newElement.id);
|
|
3540
|
-
}
|
|
3541
|
-
}, [selectedElement, currentPageId, saveToHistory]);
|
|
3542
|
-
const moveElementUp = useCallback4((id) => {
|
|
3543
|
-
setDesign((prev) => ({
|
|
3544
|
-
...prev,
|
|
3545
|
-
pages: prev.pages.map((page) => {
|
|
3546
|
-
if (page.id === currentPageId) {
|
|
3547
|
-
const index = page.elements.findIndex((el) => el.id === id);
|
|
3548
|
-
if (index < page.elements.length - 1) {
|
|
3549
|
-
const newElements = [...page.elements];
|
|
3550
|
-
[newElements[index], newElements[index + 1]] = [
|
|
3551
|
-
newElements[index + 1],
|
|
3552
|
-
newElements[index]
|
|
3553
|
-
];
|
|
3554
|
-
return { ...page, elements: newElements };
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
return page;
|
|
3558
|
-
})
|
|
3559
|
-
}));
|
|
3560
|
-
}, [currentPageId]);
|
|
3561
|
-
const moveElementDown = useCallback4((id) => {
|
|
3562
|
-
setDesign((prev) => ({
|
|
3563
|
-
...prev,
|
|
3564
|
-
pages: prev.pages.map((page) => {
|
|
3565
|
-
if (page.id === currentPageId) {
|
|
3566
|
-
const index = page.elements.findIndex((el) => el.id === id);
|
|
3567
|
-
if (index > 0) {
|
|
3568
|
-
const newElements = [...page.elements];
|
|
3569
|
-
[newElements[index], newElements[index - 1]] = [
|
|
3570
|
-
newElements[index - 1],
|
|
3571
|
-
newElements[index]
|
|
3572
|
-
];
|
|
3573
|
-
return { ...page, elements: newElements };
|
|
3574
|
-
}
|
|
3575
|
-
}
|
|
3576
|
-
return page;
|
|
3577
|
-
})
|
|
3578
|
-
}));
|
|
3579
|
-
}, [currentPageId]);
|
|
3580
|
-
const addPage = useCallback4(() => {
|
|
3581
|
-
const newPage = {
|
|
3582
|
-
id: Date.now().toString(),
|
|
3583
|
-
name: `Page ${design.pages.length + 1}`,
|
|
3584
|
-
elements: [],
|
|
3585
|
-
background: "#ffffff",
|
|
3586
|
-
backgroundImageObject: undefined
|
|
3587
|
-
};
|
|
3588
|
-
setDesign((prev) => ({
|
|
3589
|
-
...prev,
|
|
3590
|
-
pages: [...prev.pages, newPage]
|
|
3591
|
-
}));
|
|
3592
|
-
setCurrentPageId(newPage.id);
|
|
3593
|
-
}, [design.pages.length]);
|
|
3594
|
-
const deletePage = useCallback4((pageId) => {
|
|
3595
|
-
if (design.pages.length > 1) {
|
|
3596
|
-
setDesign((prev) => ({
|
|
3597
|
-
...prev,
|
|
3598
|
-
pages: prev.pages.filter((p) => p.id !== pageId)
|
|
3599
|
-
}));
|
|
3600
|
-
if (currentPageId === pageId) {
|
|
3601
|
-
setCurrentPageId(design.pages[0].id);
|
|
3602
|
-
}
|
|
3603
|
-
}
|
|
3604
|
-
}, [design.pages, currentPageId]);
|
|
3605
|
-
const handleImageUpload = useCallback4((e) => {
|
|
3606
|
-
const file = e.target.files?.[0];
|
|
3607
|
-
if (file) {
|
|
3608
|
-
const reader = new FileReader;
|
|
3609
|
-
reader.onload = (event) => {
|
|
3610
|
-
const img = new window.Image;
|
|
3611
|
-
img.src = event.target?.result;
|
|
3612
|
-
img.onload = () => {
|
|
3613
|
-
const newElement = {
|
|
3614
|
-
id: Date.now().toString(),
|
|
3615
|
-
type: "image",
|
|
3616
|
-
name: file.name,
|
|
3617
|
-
x: 100,
|
|
3618
|
-
y: 100,
|
|
3619
|
-
width: img.naturalWidth,
|
|
3620
|
-
height: img.naturalHeight,
|
|
3621
|
-
rotation: 0,
|
|
3622
|
-
visible: true,
|
|
3623
|
-
locked: false,
|
|
3624
|
-
opacity: 1,
|
|
3625
|
-
src: event.target?.result,
|
|
3626
|
-
cropX: 0,
|
|
3627
|
-
cropY: 0,
|
|
3628
|
-
cropWidth: img.naturalWidth,
|
|
3629
|
-
cropHeight: img.naturalHeight,
|
|
3630
|
-
mask: undefined
|
|
3631
|
-
};
|
|
3632
|
-
setDesign((prev) => ({
|
|
3633
|
-
...prev,
|
|
3634
|
-
pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
|
|
3635
|
-
}));
|
|
3636
|
-
saveToHistory(design);
|
|
3637
|
-
};
|
|
3638
|
-
};
|
|
3639
|
-
reader.readAsDataURL(file);
|
|
3640
|
-
}
|
|
3641
|
-
if (fileInputRef.current) {
|
|
3642
|
-
fileInputRef.current.value = "";
|
|
3643
|
-
}
|
|
3644
|
-
}, [currentPageId, saveToHistory]);
|
|
3645
|
-
const searchUnsplash = useCallback4(async () => {
|
|
3646
|
-
if (!unsplashQuery)
|
|
3647
|
-
return;
|
|
3648
|
-
if (UNSPLASH_ACCESS_KEY === "YOUR_UNSPLASH_ACCESS_KEY_HERE" || !UNSPLASH_ACCESS_KEY) {
|
|
3649
|
-
console.warn("Unsplash API key is not configured. Please set UNSPLASH_ACCESS_KEY in src/config.ts");
|
|
3650
|
-
const mockResults = Array(9).fill(null).map((_, i) => ({
|
|
3651
|
-
id: `unsplash-mock-${i}`,
|
|
3652
|
-
urls: {
|
|
3653
|
-
regular: `https://source.unsplash.com/400x300/?${unsplashQuery}&sig=${i}`,
|
|
3654
|
-
thumb: `https://source.unsplash.com/200x150/?${unsplashQuery}&sig=${i}`
|
|
3655
|
-
},
|
|
3656
|
-
user: { name: "Mock User" }
|
|
3657
|
-
}));
|
|
3658
|
-
setUnsplashResults(mockResults);
|
|
3659
|
-
return;
|
|
3660
|
-
}
|
|
3661
|
-
try {
|
|
3662
|
-
const response = await fetch(`https://api.unsplash.com/search/photos?query=${encodeURIComponent(unsplashQuery)}&per_page=9`, {
|
|
3663
|
-
headers: {
|
|
3664
|
-
Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}`
|
|
3665
|
-
}
|
|
3666
|
-
});
|
|
3667
|
-
if (!response.ok) {
|
|
3668
|
-
console.error("Failed to fetch from Unsplash:", response.statusText);
|
|
3669
|
-
setUnsplashResults([]);
|
|
3670
|
-
return;
|
|
3671
|
-
}
|
|
3672
|
-
const data = await response.json();
|
|
3673
|
-
setUnsplashResults(data.results.map((img) => ({
|
|
3674
|
-
id: img.id,
|
|
3675
|
-
urls: { regular: img.urls.regular, thumb: img.urls.thumb },
|
|
3676
|
-
user: { name: img.user.name }
|
|
3677
|
-
})));
|
|
3678
|
-
} catch (error) {
|
|
3679
|
-
console.error("Error searching Unsplash:", error);
|
|
3680
|
-
setUnsplashResults([]);
|
|
3681
|
-
}
|
|
3682
|
-
}, [unsplashQuery]);
|
|
3683
|
-
const addUnsplashImage = useCallback4((imageUrl) => {
|
|
3684
|
-
if (unsplashMode === "element") {
|
|
3685
|
-
const img = new window.Image;
|
|
3686
|
-
img.crossOrigin = "anonymous";
|
|
3687
|
-
img.src = imageUrl;
|
|
3688
|
-
img.onload = () => {
|
|
3689
|
-
const newElement = {
|
|
3690
|
-
id: Date.now().toString(),
|
|
3691
|
-
type: "image",
|
|
3692
|
-
name: "Unsplash Image",
|
|
3693
|
-
x: 100,
|
|
3694
|
-
y: 100,
|
|
3695
|
-
width: img.naturalWidth,
|
|
3696
|
-
height: img.naturalHeight,
|
|
3697
|
-
rotation: 0,
|
|
3698
|
-
visible: true,
|
|
3699
|
-
locked: false,
|
|
3700
|
-
opacity: 1,
|
|
3701
|
-
src: imageUrl,
|
|
3702
|
-
cropX: 0,
|
|
3703
|
-
cropY: 0,
|
|
3704
|
-
cropWidth: img.naturalWidth,
|
|
3705
|
-
cropHeight: img.naturalHeight,
|
|
3706
|
-
mask: undefined
|
|
3707
|
-
};
|
|
3708
|
-
setDesign((prev) => ({
|
|
3709
|
-
...prev,
|
|
3710
|
-
pages: prev.pages.map((page) => page.id === currentPageId ? { ...page, elements: [...page.elements, newElement] } : page)
|
|
3711
|
-
}));
|
|
3712
|
-
saveToHistory(design);
|
|
3713
|
-
};
|
|
3714
|
-
} else if (unsplashMode === "background") {
|
|
3715
|
-
const bgImg = new window.Image;
|
|
3716
|
-
bgImg.crossOrigin = "anonymous";
|
|
3717
|
-
bgImg.src = imageUrl;
|
|
3718
|
-
bgImg.onload = () => {
|
|
3719
|
-
setDesign((prev) => ({
|
|
3720
|
-
...prev,
|
|
3721
|
-
pages: prev.pages.map((p) => p.id === currentPageId ? {
|
|
3722
|
-
...p,
|
|
3723
|
-
background: `url(${imageUrl})`,
|
|
3724
|
-
backgroundImageObject: bgImg
|
|
3725
|
-
} : p)
|
|
3726
|
-
}));
|
|
3727
|
-
saveToHistory(design);
|
|
3728
|
-
};
|
|
3729
|
-
}
|
|
3730
|
-
setShowUnsplash(false);
|
|
3731
|
-
setUnsplashQuery("");
|
|
3732
|
-
setUnsplashResults([]);
|
|
3733
|
-
setUnsplashMode("element");
|
|
3734
|
-
}, [currentPageId, saveToHistory, unsplashMode]);
|
|
3735
|
-
const handleImportJSON = (event) => {
|
|
3736
|
-
importFromJSON(event, (newDesign) => {
|
|
3737
|
-
if (newDesign && newDesign.pages && newDesign.pages.length > 0) {
|
|
3738
|
-
setDesign(newDesign);
|
|
3739
|
-
setSelectedId(null);
|
|
3740
|
-
setSelectedIds([]);
|
|
3741
|
-
const firstPageId = newDesign.pages[0]?.id;
|
|
3742
|
-
setCurrentPageId(firstPageId || "1");
|
|
3743
|
-
saveToHistory(newDesign);
|
|
3744
|
-
} else {
|
|
3745
|
-
alert("Invalid design structure in JSON file. Could not load.");
|
|
3746
|
-
}
|
|
3747
|
-
}, (errorMessage) => {
|
|
3748
|
-
alert(`Error importing JSON: ${errorMessage}`);
|
|
3749
|
-
}, (inputs, onComplete) => {
|
|
3750
|
-
setPendingInputs(inputs);
|
|
3751
|
-
setShowInputModal(true);
|
|
3752
|
-
window.__pendingImportComplete = onComplete;
|
|
3753
|
-
});
|
|
3754
|
-
if (jsonInputRef.current) {
|
|
3755
|
-
jsonInputRef.current.value = "";
|
|
3756
|
-
}
|
|
3757
|
-
};
|
|
3758
|
-
const handleInputModalComplete = (values) => {
|
|
3759
|
-
setShowInputModal(false);
|
|
3760
|
-
const onComplete = window.__pendingImportComplete;
|
|
3761
|
-
if (onComplete) {
|
|
3762
|
-
onComplete(values);
|
|
3763
|
-
delete window.__pendingImportComplete;
|
|
3764
|
-
}
|
|
3765
|
-
setPendingInputs([]);
|
|
3766
|
-
};
|
|
3767
|
-
const handleInputModalCancel = () => {
|
|
3768
|
-
setShowInputModal(false);
|
|
3769
|
-
setPendingInputs([]);
|
|
3770
|
-
delete window.__pendingImportComplete;
|
|
3771
|
-
};
|
|
3772
|
-
const panelConfigs = useMemo(() => {
|
|
3773
|
-
const builtInConfigs = {
|
|
3774
|
-
elements: {
|
|
3775
|
-
id: "elements",
|
|
3776
|
-
title: "Elements",
|
|
3777
|
-
component: ElementPanel_default,
|
|
3778
|
-
props: { onAddShape: addShape, store }
|
|
3779
|
-
},
|
|
3780
|
-
text: {
|
|
3781
|
-
id: "text",
|
|
3782
|
-
title: "Text",
|
|
3783
|
-
component: TextPanel_default,
|
|
3784
|
-
props: {
|
|
3785
|
-
selectedElement,
|
|
3786
|
-
updateElement,
|
|
3787
|
-
setTool,
|
|
3788
|
-
onAddText: addText,
|
|
3789
|
-
store
|
|
3790
|
-
}
|
|
3791
|
-
},
|
|
3792
|
-
image: {
|
|
3793
|
-
id: "image",
|
|
3794
|
-
title: "Images",
|
|
3795
|
-
component: ImagePanel_default,
|
|
3796
|
-
props: {
|
|
3797
|
-
selectedElement,
|
|
3798
|
-
updateElement,
|
|
3799
|
-
onUploadClick: () => fileInputRef.current?.click(),
|
|
3800
|
-
onUnsplashClick: () => {
|
|
3801
|
-
setShowUnsplash(true);
|
|
3802
|
-
setUnsplashMode("element");
|
|
3803
|
-
},
|
|
3804
|
-
canvasWidth: design.width,
|
|
3805
|
-
canvasHeight: design.height,
|
|
3806
|
-
store
|
|
3807
|
-
}
|
|
3808
|
-
},
|
|
3809
|
-
design: {
|
|
3810
|
-
id: "design",
|
|
3811
|
-
title: "Design",
|
|
3812
|
-
component: DesignPanel_default,
|
|
3813
|
-
props: {
|
|
3814
|
-
design,
|
|
3815
|
-
currentPage,
|
|
3816
|
-
selectedElement,
|
|
3817
|
-
setDesign,
|
|
3818
|
-
updateElement,
|
|
3819
|
-
onSetUnsplashBackground: () => {
|
|
3820
|
-
setShowUnsplash(true);
|
|
3821
|
-
setUnsplashMode("background");
|
|
3822
|
-
},
|
|
3823
|
-
config,
|
|
3824
|
-
store
|
|
3825
|
-
}
|
|
3826
|
-
},
|
|
3827
|
-
background: {
|
|
3828
|
-
id: "background",
|
|
3829
|
-
title: "Background",
|
|
3830
|
-
component: BackgroundPanel_default,
|
|
3831
|
-
props: {
|
|
3832
|
-
design,
|
|
3833
|
-
currentPage,
|
|
3834
|
-
setDesign,
|
|
3835
|
-
onSetUnsplashBackground: () => {
|
|
3836
|
-
setShowUnsplash(true);
|
|
3837
|
-
setUnsplashMode("background");
|
|
3838
|
-
},
|
|
3839
|
-
store
|
|
3840
|
-
}
|
|
3841
|
-
},
|
|
3842
|
-
...config?.variables ? {
|
|
3843
|
-
variables: {
|
|
3844
|
-
id: "variables",
|
|
3845
|
-
title: "Variables",
|
|
3846
|
-
component: VariablesPanel_default,
|
|
3847
|
-
props: {
|
|
3848
|
-
config,
|
|
3849
|
-
design,
|
|
3850
|
-
setDesign,
|
|
3851
|
-
store
|
|
3852
|
-
}
|
|
3853
|
-
}
|
|
3854
|
-
} : {},
|
|
3855
|
-
...config?.export ? {
|
|
3856
|
-
export: {
|
|
3857
|
-
id: "export",
|
|
3858
|
-
title: "Export",
|
|
3859
|
-
component: ExportPanel_default,
|
|
3860
|
-
props: {
|
|
3861
|
-
onExportToPNG: config.export?.png ? () => {
|
|
3862
|
-
if (stageRef.current)
|
|
3863
|
-
exportToPNG(stageRef.current, design);
|
|
3864
|
-
} : undefined,
|
|
3865
|
-
onExportToJPG: config.export?.jpg ? () => {
|
|
3866
|
-
if (stageRef.current)
|
|
3867
|
-
exportToJPG(stageRef.current, design);
|
|
3868
|
-
} : undefined,
|
|
3869
|
-
onExportToJSON: config.export?.json ? () => exportToJSON(design) : undefined,
|
|
3870
|
-
onImportJSON: () => jsonInputRef.current?.click(),
|
|
3871
|
-
store
|
|
3872
|
-
}
|
|
3873
|
-
}
|
|
3874
|
-
} : {}
|
|
3875
|
-
};
|
|
3876
|
-
if (!config?.panels || config.panels.length === 0) {
|
|
3877
|
-
return [
|
|
3878
|
-
"text",
|
|
3879
|
-
"elements",
|
|
3880
|
-
"image",
|
|
3881
|
-
"design",
|
|
3882
|
-
"background",
|
|
3883
|
-
...config?.variables ? ["variables"] : [],
|
|
3884
|
-
...config?.export ? ["export"] : []
|
|
3885
|
-
].filter((id) => builtInConfigs[id]).map((id) => builtInConfigs[id]);
|
|
3886
|
-
}
|
|
3887
|
-
const ordered = [];
|
|
3888
|
-
for (const panel of config.panels) {
|
|
3889
|
-
if (typeof panel === "string") {
|
|
3890
|
-
const config2 = builtInConfigs[panel];
|
|
3891
|
-
if (config2) {
|
|
3892
|
-
ordered.push(config2);
|
|
3893
|
-
}
|
|
3894
|
-
} else {
|
|
3895
|
-
ordered.push({
|
|
3896
|
-
id: panel.id,
|
|
3897
|
-
title: panel.title,
|
|
3898
|
-
component: panel.component,
|
|
3899
|
-
props: { ...panel.props, store }
|
|
3900
|
-
});
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
return ordered;
|
|
3904
|
-
}, [
|
|
3905
|
-
addShape,
|
|
3906
|
-
addText,
|
|
3907
|
-
config,
|
|
3908
|
-
currentPage,
|
|
3909
|
-
design,
|
|
3910
|
-
selectedElement,
|
|
3911
|
-
setTool,
|
|
3912
|
-
updateElement,
|
|
3913
|
-
store
|
|
3914
|
-
]);
|
|
3915
|
-
const DynamicPanelRenderer = () => {
|
|
3916
|
-
const activePanelConfig = panelConfigs.find((p) => p.id === activePanelId);
|
|
3917
|
-
if (!activePanelConfig) {
|
|
3918
|
-
return /* @__PURE__ */ React18.createElement("div", {
|
|
3919
|
-
className: "text-white"
|
|
3920
|
-
}, "Panel not found");
|
|
3921
|
-
}
|
|
3922
|
-
const PanelComponent = activePanelConfig.component;
|
|
3923
|
-
return /* @__PURE__ */ React18.createElement(PanelComponent, {
|
|
3924
|
-
...activePanelConfig.props
|
|
3925
|
-
});
|
|
3926
|
-
};
|
|
3927
|
-
useEffect4(() => {
|
|
3928
|
-
const handleKeyDown = (e) => {
|
|
3929
|
-
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)
|
|
3930
|
-
return;
|
|
3931
|
-
if (e.metaKey || e.ctrlKey) {
|
|
3932
|
-
switch (e.key) {
|
|
3933
|
-
case "z":
|
|
3934
|
-
if (e.shiftKey) {
|
|
3935
|
-
redo();
|
|
3936
|
-
} else {
|
|
3937
|
-
undo();
|
|
3938
|
-
}
|
|
3939
|
-
e.preventDefault();
|
|
3940
|
-
break;
|
|
3941
|
-
case "c":
|
|
3942
|
-
break;
|
|
3943
|
-
case "v":
|
|
3944
|
-
break;
|
|
3945
|
-
case "d":
|
|
3946
|
-
duplicateSelected();
|
|
3947
|
-
e.preventDefault();
|
|
3948
|
-
break;
|
|
3949
|
-
case "s":
|
|
3950
|
-
exportToJSON(design);
|
|
3951
|
-
e.preventDefault();
|
|
3952
|
-
break;
|
|
3953
|
-
}
|
|
3954
|
-
} else {
|
|
3955
|
-
switch (e.key) {
|
|
3956
|
-
case "Delete":
|
|
3957
|
-
case "Backspace":
|
|
3958
|
-
deleteSelected();
|
|
3959
|
-
break;
|
|
3960
|
-
case "Escape":
|
|
3961
|
-
setSelectedId(null);
|
|
3962
|
-
setSelectedIds([]);
|
|
3963
|
-
break;
|
|
3964
|
-
}
|
|
3965
|
-
}
|
|
3966
|
-
};
|
|
3967
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
3968
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
3969
|
-
}, [deleteSelected, duplicateSelected, exportToJSON, undo, redo]);
|
|
3970
|
-
useEffect4(() => {
|
|
3971
|
-
if (history.length === 0) {
|
|
3972
|
-
setHistory([JSON.parse(JSON.stringify(design))]);
|
|
3973
|
-
setHistoryIndex(0);
|
|
3974
|
-
}
|
|
3975
|
-
}, []);
|
|
3976
|
-
return /* @__PURE__ */ React18.createElement("div", {
|
|
3977
|
-
className: "h-screen flex flex-col bg-gray-900"
|
|
3978
|
-
}, /* @__PURE__ */ React18.createElement(Header_default, {
|
|
3979
|
-
name,
|
|
3980
|
-
zoom,
|
|
3981
|
-
historyIndex,
|
|
3982
|
-
historyLength: history.length,
|
|
3983
|
-
onUndo: undo,
|
|
3984
|
-
onRedo: redo,
|
|
3985
|
-
onZoomIn: () => setZoom(Math.min(3, zoom + 0.1)),
|
|
3986
|
-
onZoomOut: () => setZoom(Math.max(0.1, zoom - 0.1)),
|
|
3987
|
-
onZoomReset: () => setZoom(1),
|
|
3988
|
-
onSetActivePanel: setActivePanelId
|
|
3989
|
-
}), /* @__PURE__ */ React18.createElement("div", {
|
|
3990
|
-
className: "flex-1 flex overflow-hidden"
|
|
3991
|
-
}, /* @__PURE__ */ React18.createElement("div", {
|
|
3992
|
-
className: "w-80 bg-gray-800 border-r border-gray-700 flex"
|
|
3993
|
-
}, /* @__PURE__ */ React18.createElement(LeftMenu_default, {
|
|
3994
|
-
tool,
|
|
3995
|
-
activePanel: activePanelId,
|
|
3996
|
-
onSetTool: setTool,
|
|
3997
|
-
onSetActivePanel: setActivePanelId,
|
|
3998
|
-
config
|
|
3999
|
-
}), /* @__PURE__ */ React18.createElement("div", {
|
|
4000
|
-
className: "flex-1 p-4 overflow-y-auto"
|
|
4001
|
-
}, /* @__PURE__ */ React18.createElement(DynamicPanelRenderer, null))), /* @__PURE__ */ React18.createElement("div", {
|
|
4002
|
-
className: "flex-1 bg-gray-700 overflow-hidden relative",
|
|
4003
|
-
ref: containerRef
|
|
4004
|
-
}, !currentPage ? /* @__PURE__ */ React18.createElement("div", {
|
|
4005
|
-
className: "absolute inset-0 flex items-center justify-center text-gray-400"
|
|
4006
|
-
}, /* @__PURE__ */ React18.createElement("div", {
|
|
4007
|
-
className: "text-center"
|
|
4008
|
-
}, /* @__PURE__ */ React18.createElement("p", {
|
|
4009
|
-
className: "mb-4"
|
|
4010
|
-
}, "No pages available"), /* @__PURE__ */ React18.createElement("button", {
|
|
4011
|
-
onClick: () => store.addPage(),
|
|
4012
|
-
className: "px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
|
4013
|
-
}, "Add Page"))) : /* @__PURE__ */ React18.createElement("div", {
|
|
4014
|
-
className: "absolute inset-0 flex items-center justify-center"
|
|
4015
|
-
}, /* @__PURE__ */ React18.createElement("div", {
|
|
4016
|
-
className: "bg-white shadow-2xl",
|
|
4017
|
-
style: {
|
|
4018
|
-
transform: `scale(${zoom})`,
|
|
4019
|
-
transformOrigin: "center center"
|
|
4020
|
-
}
|
|
4021
|
-
}, /* @__PURE__ */ React18.createElement(Stage, {
|
|
4022
|
-
ref: stageRef,
|
|
4023
|
-
width: design.width,
|
|
4024
|
-
height: design.height,
|
|
4025
|
-
onClick: handleStageClick,
|
|
4026
|
-
onTap: handleStageClick
|
|
4027
|
-
}, /* @__PURE__ */ React18.createElement(Layer, null, /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(Rect2, {
|
|
4028
|
-
x: 0,
|
|
4029
|
-
y: 0,
|
|
4030
|
-
width: design.width,
|
|
4031
|
-
height: design.height,
|
|
4032
|
-
fill: currentPage.backgroundImageObject ? undefined : currentPage.background,
|
|
4033
|
-
fillPatternImage: currentPage.backgroundImageObject,
|
|
4034
|
-
fillPatternRepeat: "no-repeat"
|
|
4035
|
-
}), currentPage.elements.map((element) => {
|
|
4036
|
-
switch (element.type) {
|
|
4037
|
-
case "text":
|
|
4038
|
-
return /* @__PURE__ */ React18.createElement(EditableTextElement, {
|
|
4039
|
-
key: element.id,
|
|
4040
|
-
element,
|
|
4041
|
-
isSelected: element.id === selectedId,
|
|
4042
|
-
onSelect: () => {
|
|
4043
|
-
setSelectedId(element.id);
|
|
4044
|
-
setSelectedIds([]);
|
|
4045
|
-
},
|
|
4046
|
-
onChange: (attrs) => updateElement(element.id, attrs)
|
|
4047
|
-
});
|
|
4048
|
-
case "image":
|
|
4049
|
-
return /* @__PURE__ */ React18.createElement(UrlImageElement, {
|
|
4050
|
-
key: element.id,
|
|
4051
|
-
element,
|
|
4052
|
-
isSelected: element.id === selectedId,
|
|
4053
|
-
onSelect: () => {
|
|
4054
|
-
setSelectedId(element.id);
|
|
4055
|
-
setSelectedIds([]);
|
|
4056
|
-
},
|
|
4057
|
-
onChange: (attrs) => updateElement(element.id, attrs)
|
|
4058
|
-
});
|
|
4059
|
-
case "shape":
|
|
4060
|
-
return /* @__PURE__ */ React18.createElement(ShapeElement, {
|
|
4061
|
-
key: element.id,
|
|
4062
|
-
element,
|
|
4063
|
-
isSelected: element.id === selectedId,
|
|
4064
|
-
onSelect: () => {
|
|
4065
|
-
setSelectedId(element.id);
|
|
4066
|
-
setSelectedIds([]);
|
|
4067
|
-
},
|
|
4068
|
-
onChange: (attrs) => updateElement(element.id, attrs)
|
|
4069
|
-
});
|
|
4070
|
-
default:
|
|
4071
|
-
return null;
|
|
4072
|
-
}
|
|
4073
|
-
})))))), " ", config?.multiPage && /* @__PURE__ */ React18.createElement("div", {
|
|
4074
|
-
className: "absolute bottom-4 left-4 flex items-center space-x-2"
|
|
4075
|
-
}, design.pages.map((page, index) => /* @__PURE__ */ React18.createElement("button", {
|
|
4076
|
-
key: page.id,
|
|
4077
|
-
onClick: () => setCurrentPageId(page.id),
|
|
4078
|
-
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"}`
|
|
4079
|
-
}, "Page ", index + 1)), /* @__PURE__ */ React18.createElement("button", {
|
|
4080
|
-
onClick: addPage,
|
|
4081
|
-
className: "p-1 bg-gray-800 text-gray-300 rounded hover:bg-gray-700",
|
|
4082
|
-
title: "Add Page"
|
|
4083
|
-
}, /* @__PURE__ */ React18.createElement(PlusCircle, {
|
|
4084
|
-
className: "w-4 h-4"
|
|
4085
|
-
})))), /* @__PURE__ */ React18.createElement(RightSidebar_default, {
|
|
4086
|
-
currentPageElements: currentPage?.elements || [],
|
|
4087
|
-
selectedElement,
|
|
4088
|
-
selectedId,
|
|
4089
|
-
onSelectElement: setSelectedId,
|
|
4090
|
-
onUpdateElement: updateElement,
|
|
4091
|
-
onDuplicateSelected: duplicateSelected,
|
|
4092
|
-
onMoveElementUp: moveElementUp,
|
|
4093
|
-
onMoveElementDown: moveElementDown,
|
|
4094
|
-
onDeleteSelected: deleteSelected
|
|
4095
|
-
})), /* @__PURE__ */ React18.createElement("input", {
|
|
4096
|
-
ref: fileInputRef,
|
|
4097
|
-
type: "file",
|
|
4098
|
-
accept: "image/*",
|
|
4099
|
-
onChange: handleImageUpload,
|
|
4100
|
-
className: "hidden",
|
|
4101
|
-
multiple: true
|
|
4102
|
-
}), /* @__PURE__ */ React18.createElement("input", {
|
|
4103
|
-
ref: jsonInputRef,
|
|
4104
|
-
type: "file",
|
|
4105
|
-
accept: ".json",
|
|
4106
|
-
onChange: handleImportJSON,
|
|
4107
|
-
className: "hidden"
|
|
4108
|
-
}), showInputModal && /* @__PURE__ */ React18.createElement(TemplateInputModal_default, {
|
|
4109
|
-
inputs: pendingInputs,
|
|
4110
|
-
onComplete: handleInputModalComplete,
|
|
4111
|
-
onCancel: handleInputModalCancel
|
|
4112
|
-
}), showUnsplash && /* @__PURE__ */ React18.createElement("div", {
|
|
4113
|
-
className: "fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 p-8"
|
|
4114
|
-
}, /* @__PURE__ */ React18.createElement("div", {
|
|
4115
|
-
className: "bg-gray-800 rounded-lg w-full max-w-4xl max-h-full overflow-hidden flex flex-col"
|
|
4116
|
-
}, /* @__PURE__ */ React18.createElement("div", {
|
|
4117
|
-
className: "p-6 border-b border-gray-700"
|
|
4118
|
-
}, /* @__PURE__ */ React18.createElement("h3", {
|
|
4119
|
-
className: "text-xl font-semibold text-white mb-4"
|
|
4120
|
-
}, "Search Unsplash"), /* @__PURE__ */ React18.createElement("div", {
|
|
4121
|
-
className: "flex space-x-4"
|
|
4122
|
-
}, /* @__PURE__ */ React18.createElement("input", {
|
|
4123
|
-
type: "text",
|
|
4124
|
-
value: unsplashQuery,
|
|
4125
|
-
onChange: (e) => setUnsplashQuery(e.target.value),
|
|
4126
|
-
onKeyDown: (e) => e.key === "Enter" && searchUnsplash(),
|
|
4127
|
-
placeholder: "Search for images...",
|
|
4128
|
-
className: "flex-1 bg-gray-700 text-white border border-gray-600 rounded px-4 py-2 focus:border-blue-500 focus:outline-none",
|
|
4129
|
-
autoFocus: true
|
|
4130
|
-
}), /* @__PURE__ */ React18.createElement("button", {
|
|
4131
|
-
onClick: searchUnsplash,
|
|
4132
|
-
className: "px-6 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
|
4133
|
-
}, "Search"), /* @__PURE__ */ React18.createElement("button", {
|
|
4134
|
-
onClick: () => {
|
|
4135
|
-
setShowUnsplash(false);
|
|
4136
|
-
setUnsplashQuery("");
|
|
4137
|
-
setUnsplashResults([]);
|
|
4138
|
-
},
|
|
4139
|
-
className: "px-6 py-2 bg-gray-700 text-white rounded hover:bg-gray-600"
|
|
4140
|
-
}, "Cancel"))), /* @__PURE__ */ React18.createElement("div", {
|
|
4141
|
-
className: "flex-1 overflow-y-auto p-6"
|
|
4142
|
-
}, unsplashResults.length > 0 ? /* @__PURE__ */ React18.createElement("div", {
|
|
4143
|
-
className: "grid grid-cols-3 gap-4"
|
|
4144
|
-
}, unsplashResults.map((result) => /* @__PURE__ */ React18.createElement("button", {
|
|
4145
|
-
key: result.id,
|
|
4146
|
-
onClick: () => addUnsplashImage(result.urls.regular),
|
|
4147
|
-
className: "relative group overflow-hidden rounded-lg aspect-square"
|
|
4148
|
-
}, /* @__PURE__ */ React18.createElement("img", {
|
|
4149
|
-
src: result.urls.thumb,
|
|
4150
|
-
alt: "",
|
|
4151
|
-
className: "w-full h-full object-cover group-hover:scale-110 transition-transform duration-200"
|
|
4152
|
-
}), /* @__PURE__ */ React18.createElement("div", {
|
|
4153
|
-
className: "absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-50 transition-opacity duration-200 flex items-center justify-center"
|
|
4154
|
-
}, /* @__PURE__ */ React18.createElement(PlusCircle, {
|
|
4155
|
-
className: "w-8 h-8 text-white opacity-0 group-hover:opacity-100 transition-opacity duration-200"
|
|
4156
|
-
}))))) : /* @__PURE__ */ React18.createElement("div", {
|
|
4157
|
-
className: "text-center text-gray-400 py-12"
|
|
4158
|
-
}, "Enter a search term to find images")))));
|
|
4159
|
-
};
|
|
4160
|
-
var CanvasEditor_default = CanvasEditor;
|
|
4161
3302
|
// src/context/CanvasStoreContext.tsx
|
|
4162
3303
|
import { createContext, useContext } from "react";
|
|
4163
3304
|
var CanvasStoreContext = createContext(null);
|
|
@@ -4168,6 +3309,14 @@ var useCanvasStore = () => {
|
|
|
4168
3309
|
}
|
|
4169
3310
|
return store;
|
|
4170
3311
|
};
|
|
3312
|
+
// src/config.ts
|
|
3313
|
+
var _unsplashAccessKey = typeof process !== "undefined" && process.env?.UNSPLASH_ACCESS_KEY ? process.env.UNSPLASH_ACCESS_KEY : undefined;
|
|
3314
|
+
function setUnsplashAccessKey(key) {
|
|
3315
|
+
_unsplashAccessKey = key;
|
|
3316
|
+
}
|
|
3317
|
+
function getUnsplashAccessKey() {
|
|
3318
|
+
return _unsplashAccessKey;
|
|
3319
|
+
}
|
|
4171
3320
|
|
|
4172
3321
|
// src/lib/index.ts
|
|
4173
3322
|
initJsxCompat();
|