@deckedout/visual-editor 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,196 +1,4 @@
1
1
  "use client"
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res) => function __init() {
7
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
- };
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
-
23
- // src/utils/editorUtils.ts
24
- var editorUtils_exports = {};
25
- __export(editorUtils_exports, {
26
- bringToFront: () => bringToFront,
27
- checkOverlap: () => checkOverlap,
28
- clamp: () => clamp,
29
- cloneElement: () => cloneElement,
30
- constrainToCanvas: () => constrainToCanvas,
31
- createElement: () => createElement,
32
- degToRad: () => degToRad,
33
- distance: () => distance,
34
- duplicateElement: () => duplicateElement,
35
- exportToJSON: () => exportToJSON,
36
- generateElementId: () => generateElementId,
37
- getElementCenter: () => getElementCenter,
38
- getMaxZIndex: () => getMaxZIndex,
39
- getRotatedBoundingBox: () => getRotatedBoundingBox,
40
- importFromJSON: () => importFromJSON,
41
- isValidCanvasExport: () => isValidCanvasExport,
42
- isValidElement: () => isValidElement,
43
- pointInRect: () => pointInRect,
44
- radToDeg: () => radToDeg,
45
- sendToBack: () => sendToBack,
46
- snapPositionToGrid: () => snapPositionToGrid,
47
- snapToGrid: () => snapToGrid,
48
- sortByZIndex: () => sortByZIndex
49
- });
50
- import { v4 as uuidv4 } from "uuid";
51
- var generateElementId, createElement, cloneElement, duplicateElement, sortByZIndex, getMaxZIndex, bringToFront, sendToBack, checkOverlap, pointInRect, snapToGrid, snapPositionToGrid, clamp, constrainToCanvas, getRotatedBoundingBox, exportToJSON, importFromJSON, getElementCenter, distance, degToRad, radToDeg, isValidElement, isValidCanvasExport;
52
- var init_editorUtils = __esm({
53
- "src/utils/editorUtils.ts"() {
54
- "use strict";
55
- generateElementId = () => {
56
- return `element-${uuidv4()}`;
57
- };
58
- createElement = (type, props, options) => {
59
- return {
60
- id: generateElementId(),
61
- type,
62
- position: options?.position || { x: 0, y: 0 },
63
- size: options?.size || { width: 100, height: 100 },
64
- rotation: options?.rotation || 0,
65
- opacity: options?.opacity ?? 1,
66
- zIndex: options?.zIndex || 0,
67
- visible: options?.visible ?? true,
68
- locked: options?.locked ?? false,
69
- displayName: options?.displayName,
70
- props
71
- };
72
- };
73
- cloneElement = (element) => {
74
- return {
75
- ...element,
76
- id: generateElementId(),
77
- // New ID for the clone
78
- props: { ...element.props },
79
- position: { ...element.position },
80
- size: { ...element.size }
81
- };
82
- };
83
- duplicateElement = (element, offset = { x: 20, y: 20 }) => {
84
- const cloned = cloneElement(element);
85
- return {
86
- ...cloned,
87
- position: {
88
- x: element.position.x + offset.x,
89
- y: element.position.y + offset.y
90
- },
91
- zIndex: element.zIndex + 1
92
- // Place on top
93
- };
94
- };
95
- sortByZIndex = (elements) => {
96
- return [...elements].sort((a, b) => a.zIndex - b.zIndex);
97
- };
98
- getMaxZIndex = (elements) => {
99
- if (elements.length === 0) return 0;
100
- return Math.max(...elements.map((el) => el.zIndex));
101
- };
102
- bringToFront = (elements, elementId) => {
103
- const maxZ = getMaxZIndex(elements);
104
- return elements.map((el) => el.id === elementId ? { ...el, zIndex: maxZ + 1 } : el);
105
- };
106
- sendToBack = (elements, elementId) => {
107
- const minZ = Math.min(...elements.map((el) => el.zIndex));
108
- return elements.map((el) => el.id === elementId ? { ...el, zIndex: minZ - 1 } : el);
109
- };
110
- checkOverlap = (rect1, rect2) => {
111
- return !(rect1.x + rect1.width < rect2.x || rect2.x + rect2.width < rect1.x || rect1.y + rect1.height < rect2.y || rect2.y + rect2.height < rect1.y);
112
- };
113
- pointInRect = (point, rect) => {
114
- return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
115
- };
116
- snapToGrid = (value, gridSize) => {
117
- return Math.round(value / gridSize) * gridSize;
118
- };
119
- snapPositionToGrid = (position, gridSize) => {
120
- return {
121
- x: snapToGrid(position.x, gridSize),
122
- y: snapToGrid(position.y, gridSize)
123
- };
124
- };
125
- clamp = (value, min, max) => {
126
- return Math.min(Math.max(value, min), max);
127
- };
128
- constrainToCanvas = (position, size, canvasSize) => {
129
- return {
130
- x: clamp(position.x, 0, canvasSize.width - size.width),
131
- y: clamp(position.y, 0, canvasSize.height - size.height)
132
- };
133
- };
134
- getRotatedBoundingBox = (x, y, width, height, rotation) => {
135
- const rad = rotation * Math.PI / 180;
136
- const cos = Math.abs(Math.cos(rad));
137
- const sin = Math.abs(Math.sin(rad));
138
- const newWidth = width * cos + height * sin;
139
- const newHeight = width * sin + height * cos;
140
- return {
141
- x: x - (newWidth - width) / 2,
142
- y: y - (newHeight - height) / 2,
143
- width: newWidth,
144
- height: newHeight
145
- };
146
- };
147
- exportToJSON = (data) => {
148
- return JSON.stringify(data, null, 2);
149
- };
150
- importFromJSON = (json) => {
151
- try {
152
- const data = JSON.parse(json);
153
- if (!data.width || !data.height || !Array.isArray(data.elements)) {
154
- throw new Error("Invalid canvas data structure");
155
- }
156
- const normalizedElements = data.elements.map((element) => ({
157
- ...element,
158
- visible: element.visible ?? true,
159
- locked: element.locked ?? false
160
- }));
161
- return {
162
- ...data,
163
- elements: normalizedElements
164
- };
165
- } catch (error) {
166
- throw new Error(`Failed to parse canvas data: ${error.message}`);
167
- }
168
- };
169
- getElementCenter = (element) => {
170
- return {
171
- x: element.position.x + element.size.width / 2,
172
- y: element.position.y + element.size.height / 2
173
- };
174
- };
175
- distance = (p1, p2) => {
176
- const dx = p2.x - p1.x;
177
- const dy = p2.y - p1.y;
178
- return Math.sqrt(dx * dx + dy * dy);
179
- };
180
- degToRad = (degrees) => {
181
- return degrees * Math.PI / 180;
182
- };
183
- radToDeg = (radians) => {
184
- return radians * 180 / Math.PI;
185
- };
186
- isValidElement = (element) => {
187
- return element && typeof element.id === "string" && typeof element.type === "string" && element.position && typeof element.position.x === "number" && typeof element.position.y === "number" && element.size && typeof element.size.width === "number" && typeof element.size.height === "number" && typeof element.rotation === "number" && typeof element.zIndex === "number" && element.props !== void 0;
188
- };
189
- isValidCanvasExport = (data) => {
190
- return data && typeof data.width === "number" && typeof data.height === "number" && Array.isArray(data.elements) && data.elements.every(isValidElement);
191
- };
192
- }
193
- });
194
2
 
195
3
  // src/core/VisualEditor.tsx
196
4
  import { useEffect, useCallback as useCallback2, useRef } from "react";
@@ -198,6 +6,148 @@ import { Stage, Layer } from "react-konva";
198
6
 
199
7
  // src/core/useEditorState.ts
200
8
  import { useReducer, useCallback, useMemo } from "react";
9
+
10
+ // src/utils/editorUtils.ts
11
+ import { v4 as uuidv4 } from "uuid";
12
+ var generateElementId = () => {
13
+ return `element-${uuidv4()}`;
14
+ };
15
+ var createElement = (type, props, options) => {
16
+ return {
17
+ id: generateElementId(),
18
+ type,
19
+ position: options?.position || { x: 0, y: 0 },
20
+ size: options?.size || { width: 100, height: 100 },
21
+ rotation: options?.rotation || 0,
22
+ opacity: options?.opacity ?? 1,
23
+ zIndex: options?.zIndex || 0,
24
+ visible: options?.visible ?? true,
25
+ locked: options?.locked ?? false,
26
+ displayName: options?.displayName,
27
+ props
28
+ };
29
+ };
30
+ var cloneElement = (element) => {
31
+ return {
32
+ ...element,
33
+ id: generateElementId(),
34
+ // New ID for the clone
35
+ props: { ...element.props },
36
+ position: { ...element.position },
37
+ size: { ...element.size }
38
+ };
39
+ };
40
+ var duplicateElement = (element, offset = { x: 20, y: 20 }) => {
41
+ const cloned = cloneElement(element);
42
+ return {
43
+ ...cloned,
44
+ position: {
45
+ x: element.position.x + offset.x,
46
+ y: element.position.y + offset.y
47
+ },
48
+ zIndex: element.zIndex + 1
49
+ // Place on top
50
+ };
51
+ };
52
+ var sortByZIndex = (elements) => {
53
+ return [...elements].sort((a, b) => a.zIndex - b.zIndex);
54
+ };
55
+ var getMaxZIndex = (elements) => {
56
+ if (elements.length === 0) return 0;
57
+ return Math.max(...elements.map((el) => el.zIndex));
58
+ };
59
+ var bringToFront = (elements, elementId) => {
60
+ const maxZ = getMaxZIndex(elements);
61
+ return elements.map((el) => el.id === elementId ? { ...el, zIndex: maxZ + 1 } : el);
62
+ };
63
+ var sendToBack = (elements, elementId) => {
64
+ const minZ = Math.min(...elements.map((el) => el.zIndex));
65
+ return elements.map((el) => el.id === elementId ? { ...el, zIndex: minZ - 1 } : el);
66
+ };
67
+ var checkOverlap = (rect1, rect2) => {
68
+ return !(rect1.x + rect1.width < rect2.x || rect2.x + rect2.width < rect1.x || rect1.y + rect1.height < rect2.y || rect2.y + rect2.height < rect1.y);
69
+ };
70
+ var pointInRect = (point, rect) => {
71
+ return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
72
+ };
73
+ var snapToGrid = (value, gridSize) => {
74
+ return Math.round(value / gridSize) * gridSize;
75
+ };
76
+ var snapPositionToGrid = (position, gridSize) => {
77
+ return {
78
+ x: snapToGrid(position.x, gridSize),
79
+ y: snapToGrid(position.y, gridSize)
80
+ };
81
+ };
82
+ var clamp = (value, min, max) => {
83
+ return Math.min(Math.max(value, min), max);
84
+ };
85
+ var constrainToCanvas = (position, size, canvasSize) => {
86
+ return {
87
+ x: clamp(position.x, 0, canvasSize.width - size.width),
88
+ y: clamp(position.y, 0, canvasSize.height - size.height)
89
+ };
90
+ };
91
+ var getRotatedBoundingBox = (x, y, width, height, rotation) => {
92
+ const rad = rotation * Math.PI / 180;
93
+ const cos = Math.abs(Math.cos(rad));
94
+ const sin = Math.abs(Math.sin(rad));
95
+ const newWidth = width * cos + height * sin;
96
+ const newHeight = width * sin + height * cos;
97
+ return {
98
+ x: x - (newWidth - width) / 2,
99
+ y: y - (newHeight - height) / 2,
100
+ width: newWidth,
101
+ height: newHeight
102
+ };
103
+ };
104
+ var exportToJSON = (data) => {
105
+ return JSON.stringify(data, null, 2);
106
+ };
107
+ var importFromJSON = (json) => {
108
+ try {
109
+ const data = JSON.parse(json);
110
+ if (!data.width || !data.height || !Array.isArray(data.elements)) {
111
+ throw new Error("Invalid canvas data structure");
112
+ }
113
+ const normalizedElements = data.elements.map((element) => ({
114
+ ...element,
115
+ visible: element.visible ?? true,
116
+ locked: element.locked ?? false
117
+ }));
118
+ return {
119
+ ...data,
120
+ elements: normalizedElements
121
+ };
122
+ } catch (error) {
123
+ throw new Error(`Failed to parse canvas data: ${error.message}`);
124
+ }
125
+ };
126
+ var getElementCenter = (element) => {
127
+ return {
128
+ x: element.position.x + element.size.width / 2,
129
+ y: element.position.y + element.size.height / 2
130
+ };
131
+ };
132
+ var distance = (p1, p2) => {
133
+ const dx = p2.x - p1.x;
134
+ const dy = p2.y - p1.y;
135
+ return Math.sqrt(dx * dx + dy * dy);
136
+ };
137
+ var degToRad = (degrees) => {
138
+ return degrees * Math.PI / 180;
139
+ };
140
+ var radToDeg = (radians) => {
141
+ return radians * 180 / Math.PI;
142
+ };
143
+ var isValidElement = (element) => {
144
+ return element && typeof element.id === "string" && typeof element.type === "string" && element.position && typeof element.position.x === "number" && typeof element.position.y === "number" && element.size && typeof element.size.width === "number" && typeof element.size.height === "number" && typeof element.rotation === "number" && typeof element.zIndex === "number" && element.props !== void 0;
145
+ };
146
+ var isValidCanvasExport = (data) => {
147
+ return data && typeof data.width === "number" && typeof data.height === "number" && Array.isArray(data.elements) && data.elements.every(isValidElement);
148
+ };
149
+
150
+ // src/core/useEditorState.ts
201
151
  var createInitialState = (mode) => ({
202
152
  elements: [],
203
153
  selectedElementId: null,
@@ -471,8 +421,7 @@ var useEditorState = (initialMode = null) => {
471
421
  (id = null, offset = { x: 20, y: 20 }) => {
472
422
  const elementToDuplicate = id ? state.elements.find((el) => el.id === id) : getSelectedElement();
473
423
  if (!elementToDuplicate) return;
474
- const { duplicateElement: duplicateUtil } = (init_editorUtils(), __toCommonJS(editorUtils_exports));
475
- const duplicated = duplicateUtil(elementToDuplicate, offset);
424
+ const duplicated = duplicateElement(elementToDuplicate, offset);
476
425
  addElement(duplicated);
477
426
  },
478
427
  [state.elements, getSelectedElement, addElement]
@@ -480,10 +429,9 @@ var useEditorState = (initialMode = null) => {
480
429
  const pasteElement = useCallback(
481
430
  (copiedElement, offset = { x: 20, y: 20 }) => {
482
431
  if (!copiedElement) return;
483
- const { generateElementId: generateElementId2 } = (init_editorUtils(), __toCommonJS(editorUtils_exports));
484
432
  const pasted = {
485
433
  ...copiedElement,
486
- id: generateElementId2(),
434
+ id: generateElementId(),
487
435
  props: { ...copiedElement.props },
488
436
  position: {
489
437
  x: copiedElement.position.x + offset.x,
@@ -2034,7 +1982,7 @@ var Inspector = ({
2034
1982
  function renderField(field, props, editingValues, onChange, onColorChange, mode) {
2035
1983
  const value = editingValues[field.name] ?? props[field.name] ?? field.defaultValue;
2036
1984
  switch (field.type) {
2037
- case "custom":
1985
+ case "custom": {
2038
1986
  if (!field.customRenderer) {
2039
1987
  console.error(`Custom field "${field.name}" has no customRenderer defined`);
2040
1988
  return null;
@@ -2050,7 +1998,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2050
1998
  mode
2051
1999
  }
2052
2000
  ) }, field.name);
2053
- case "string":
2001
+ }
2002
+ case "string": {
2054
2003
  const isMultiline = field.name === "content" || field.name === "text";
2055
2004
  return /* @__PURE__ */ jsxs5("div", { children: [
2056
2005
  /* @__PURE__ */ jsx11(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
@@ -2074,7 +2023,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2074
2023
  ),
2075
2024
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2076
2025
  ] }, field.name);
2077
- case "number":
2026
+ }
2027
+ case "number": {
2078
2028
  return /* @__PURE__ */ jsxs5("div", { children: [
2079
2029
  /* @__PURE__ */ jsx11(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
2080
2030
  /* @__PURE__ */ jsx11(
@@ -2092,7 +2042,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2092
2042
  ),
2093
2043
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2094
2044
  ] }, field.name);
2095
- case "color":
2045
+ }
2046
+ case "color": {
2096
2047
  return /* @__PURE__ */ jsxs5("div", { children: [
2097
2048
  /* @__PURE__ */ jsx11(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
2098
2049
  /* @__PURE__ */ jsx11(
@@ -2107,7 +2058,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2107
2058
  ),
2108
2059
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2109
2060
  ] }, field.name);
2110
- case "select":
2061
+ }
2062
+ case "select": {
2111
2063
  return /* @__PURE__ */ jsxs5("div", { children: [
2112
2064
  /* @__PURE__ */ jsx11(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
2113
2065
  /* @__PURE__ */ jsxs5(
@@ -2123,7 +2075,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2123
2075
  ),
2124
2076
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2125
2077
  ] }, field.name);
2126
- case "boolean":
2078
+ }
2079
+ case "boolean": {
2127
2080
  return /* @__PURE__ */ jsxs5("div", { className: "flex items-start space-x-2", children: [
2128
2081
  /* @__PURE__ */ jsx11(
2129
2082
  Checkbox,
@@ -2139,7 +2092,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2139
2092
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground", children: field.description })
2140
2093
  ] })
2141
2094
  ] }, field.name);
2142
- case "slider":
2095
+ }
2096
+ case "slider": {
2143
2097
  return /* @__PURE__ */ jsxs5("div", { children: [
2144
2098
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mb-2", children: [
2145
2099
  /* @__PURE__ */ jsx11(Label, { className: "text-xs", children: field.label }),
@@ -2158,7 +2112,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2158
2112
  ),
2159
2113
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2160
2114
  ] }, field.name);
2161
- case "image":
2115
+ }
2116
+ case "image": {
2162
2117
  return /* @__PURE__ */ jsxs5("div", { children: [
2163
2118
  /* @__PURE__ */ jsx11(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
2164
2119
  /* @__PURE__ */ jsx11(
@@ -2173,6 +2128,7 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
2173
2128
  ),
2174
2129
  field.description && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
2175
2130
  ] }, field.name);
2131
+ }
2176
2132
  default:
2177
2133
  return null;
2178
2134
  }
@@ -3527,11 +3483,7 @@ var Canvas = ({
3527
3483
  );
3528
3484
  };
3529
3485
 
3530
- // src/components/VisualEditorWorkspace.tsx
3531
- init_editorUtils();
3532
-
3533
3486
  // src/components/Toolbar.tsx
3534
- init_editorUtils();
3535
3487
  import { Plus as Plus2 } from "lucide-react";
3536
3488
  import { Fragment as Fragment6, jsx as jsx25, jsxs as jsxs13 } from "react/jsx-runtime";
3537
3489
  var Toolbar = ({
@@ -3832,7 +3784,7 @@ var VisualEditorWorkspace = ({
3832
3784
  readonly,
3833
3785
  enableSnapGuides: snapGuidesEnabled,
3834
3786
  enablePanZoom,
3835
- backgroundImageUrl: backgroundImage && mode?.context?.imageUrls ? mode.context.imageUrls.get(backgroundImage) : void 0,
3787
+ backgroundImageUrl: backgroundImage && mode?.context?.imageUrls ? mode.context.imageUrls.get(backgroundImage) : backgroundImageUrl,
3836
3788
  hideElements,
3837
3789
  onSelectElement: (id) => api.selectElement(id),
3838
3790
  onTransformElement: (id, updates) => api.updateElement(id, updates)
@@ -3945,9 +3897,6 @@ var AssetPicker = ({
3945
3897
  /* @__PURE__ */ jsx27(ScrollArea, { className: "flex-1", children: filteredAssets.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "grid grid-cols-3 gap-2 p-3", children: filteredAssets.map((asset) => assetRenderer(asset)) }) : /* @__PURE__ */ jsx27("div", { className: "flex items-center justify-center h-32 text-sm text-muted-foreground", children: searchQuery ? "No assets found" : "No assets available" }) })
3946
3898
  ] });
3947
3899
  };
3948
-
3949
- // src/index.ts
3950
- init_editorUtils();
3951
3900
  export {
3952
3901
  AssetPicker,
3953
3902
  ElementRegistry,