@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/README.md +52 -0
- package/dist/index.js +161 -195
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +161 -212
- package/dist/index.mjs.map +1 -1
- package/package.json +41 -3
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# @deckedout/visual-editor
|
|
2
2
|
|
|
3
|
+
[](https://github.com/EIP-DeckedOut-Orga/eip-visual-editor-package/actions/workflows/ci.yml)
|
|
4
|
+
[](https://badge.fury.io/js/@deckedout%2Fvisual-editor)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[]()
|
|
7
|
+
|
|
3
8
|
A flexible, drag-and-drop visual editor built with React and Konva for creating interactive canvases with customizable elements.
|
|
4
9
|
|
|
5
10
|
## Features
|
|
@@ -12,6 +17,7 @@ A flexible, drag-and-drop visual editor built with React and Konva for creating
|
|
|
12
17
|
- 🎭 **Multiple Modes**: Edit and preview modes
|
|
13
18
|
- 📸 **Export Support**: Export canvas to JSON or image formats
|
|
14
19
|
- 🎨 **Asset Management**: Built-in asset picker for images
|
|
20
|
+
- ✅ **Well Tested**: 95%+ code coverage with comprehensive test suite
|
|
15
21
|
|
|
16
22
|
## Installation
|
|
17
23
|
|
|
@@ -219,6 +225,52 @@ function EditorWithCustomElements() {
|
|
|
219
225
|
}
|
|
220
226
|
```
|
|
221
227
|
|
|
228
|
+
## Development
|
|
229
|
+
|
|
230
|
+
### Running Tests
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Run all tests
|
|
234
|
+
npm test
|
|
235
|
+
|
|
236
|
+
# Run tests in watch mode
|
|
237
|
+
npm run test:watch
|
|
238
|
+
|
|
239
|
+
# Generate coverage report
|
|
240
|
+
npm run test:coverage
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Building
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Build the package
|
|
247
|
+
npm run build
|
|
248
|
+
|
|
249
|
+
# Type check
|
|
250
|
+
npm run type-check
|
|
251
|
+
|
|
252
|
+
# Lint
|
|
253
|
+
npm run lint
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Testing Coverage
|
|
257
|
+
|
|
258
|
+
This package maintains high test coverage:
|
|
259
|
+
- **95.26%** statements
|
|
260
|
+
- **94.2%** branches
|
|
261
|
+
- **93.05%** functions
|
|
262
|
+
- **95.77%** lines
|
|
263
|
+
|
|
264
|
+
See [TESTING.md](./TESTING.md) for detailed testing documentation.
|
|
265
|
+
|
|
266
|
+
## Contributing
|
|
267
|
+
|
|
268
|
+
Contributions are welcome! Please ensure:
|
|
269
|
+
1. All tests pass (`npm test`)
|
|
270
|
+
2. Code coverage remains above 90%
|
|
271
|
+
3. Code is properly linted (`npm run lint`)
|
|
272
|
+
4. Types are correct (`npm run type-check`)
|
|
273
|
+
|
|
222
274
|
## License
|
|
223
275
|
|
|
224
276
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __esm = (fn, res) => function __init() {
|
|
10
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
-
};
|
|
12
9
|
var __export = (target, all) => {
|
|
13
10
|
for (var name in all)
|
|
14
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -31,178 +28,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
28
|
));
|
|
32
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
30
|
|
|
34
|
-
// src/utils/editorUtils.ts
|
|
35
|
-
var editorUtils_exports = {};
|
|
36
|
-
__export(editorUtils_exports, {
|
|
37
|
-
bringToFront: () => bringToFront,
|
|
38
|
-
checkOverlap: () => checkOverlap,
|
|
39
|
-
clamp: () => clamp,
|
|
40
|
-
cloneElement: () => cloneElement,
|
|
41
|
-
constrainToCanvas: () => constrainToCanvas,
|
|
42
|
-
createElement: () => createElement,
|
|
43
|
-
degToRad: () => degToRad,
|
|
44
|
-
distance: () => distance,
|
|
45
|
-
duplicateElement: () => duplicateElement,
|
|
46
|
-
exportToJSON: () => exportToJSON,
|
|
47
|
-
generateElementId: () => generateElementId,
|
|
48
|
-
getElementCenter: () => getElementCenter,
|
|
49
|
-
getMaxZIndex: () => getMaxZIndex,
|
|
50
|
-
getRotatedBoundingBox: () => getRotatedBoundingBox,
|
|
51
|
-
importFromJSON: () => importFromJSON,
|
|
52
|
-
isValidCanvasExport: () => isValidCanvasExport,
|
|
53
|
-
isValidElement: () => isValidElement,
|
|
54
|
-
pointInRect: () => pointInRect,
|
|
55
|
-
radToDeg: () => radToDeg,
|
|
56
|
-
sendToBack: () => sendToBack,
|
|
57
|
-
snapPositionToGrid: () => snapPositionToGrid,
|
|
58
|
-
snapToGrid: () => snapToGrid,
|
|
59
|
-
sortByZIndex: () => sortByZIndex
|
|
60
|
-
});
|
|
61
|
-
var import_uuid, generateElementId, createElement, cloneElement, duplicateElement, sortByZIndex, getMaxZIndex, bringToFront, sendToBack, checkOverlap, pointInRect, snapToGrid, snapPositionToGrid, clamp, constrainToCanvas, getRotatedBoundingBox, exportToJSON, importFromJSON, getElementCenter, distance, degToRad, radToDeg, isValidElement, isValidCanvasExport;
|
|
62
|
-
var init_editorUtils = __esm({
|
|
63
|
-
"src/utils/editorUtils.ts"() {
|
|
64
|
-
"use strict";
|
|
65
|
-
import_uuid = require("uuid");
|
|
66
|
-
generateElementId = () => {
|
|
67
|
-
return `element-${(0, import_uuid.v4)()}`;
|
|
68
|
-
};
|
|
69
|
-
createElement = (type, props, options) => {
|
|
70
|
-
return {
|
|
71
|
-
id: generateElementId(),
|
|
72
|
-
type,
|
|
73
|
-
position: options?.position || { x: 0, y: 0 },
|
|
74
|
-
size: options?.size || { width: 100, height: 100 },
|
|
75
|
-
rotation: options?.rotation || 0,
|
|
76
|
-
opacity: options?.opacity ?? 1,
|
|
77
|
-
zIndex: options?.zIndex || 0,
|
|
78
|
-
visible: options?.visible ?? true,
|
|
79
|
-
locked: options?.locked ?? false,
|
|
80
|
-
displayName: options?.displayName,
|
|
81
|
-
props
|
|
82
|
-
};
|
|
83
|
-
};
|
|
84
|
-
cloneElement = (element) => {
|
|
85
|
-
return {
|
|
86
|
-
...element,
|
|
87
|
-
id: generateElementId(),
|
|
88
|
-
// New ID for the clone
|
|
89
|
-
props: { ...element.props },
|
|
90
|
-
position: { ...element.position },
|
|
91
|
-
size: { ...element.size }
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
duplicateElement = (element, offset = { x: 20, y: 20 }) => {
|
|
95
|
-
const cloned = cloneElement(element);
|
|
96
|
-
return {
|
|
97
|
-
...cloned,
|
|
98
|
-
position: {
|
|
99
|
-
x: element.position.x + offset.x,
|
|
100
|
-
y: element.position.y + offset.y
|
|
101
|
-
},
|
|
102
|
-
zIndex: element.zIndex + 1
|
|
103
|
-
// Place on top
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
sortByZIndex = (elements) => {
|
|
107
|
-
return [...elements].sort((a, b) => a.zIndex - b.zIndex);
|
|
108
|
-
};
|
|
109
|
-
getMaxZIndex = (elements) => {
|
|
110
|
-
if (elements.length === 0) return 0;
|
|
111
|
-
return Math.max(...elements.map((el) => el.zIndex));
|
|
112
|
-
};
|
|
113
|
-
bringToFront = (elements, elementId) => {
|
|
114
|
-
const maxZ = getMaxZIndex(elements);
|
|
115
|
-
return elements.map((el) => el.id === elementId ? { ...el, zIndex: maxZ + 1 } : el);
|
|
116
|
-
};
|
|
117
|
-
sendToBack = (elements, elementId) => {
|
|
118
|
-
const minZ = Math.min(...elements.map((el) => el.zIndex));
|
|
119
|
-
return elements.map((el) => el.id === elementId ? { ...el, zIndex: minZ - 1 } : el);
|
|
120
|
-
};
|
|
121
|
-
checkOverlap = (rect1, rect2) => {
|
|
122
|
-
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);
|
|
123
|
-
};
|
|
124
|
-
pointInRect = (point, rect) => {
|
|
125
|
-
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
126
|
-
};
|
|
127
|
-
snapToGrid = (value, gridSize) => {
|
|
128
|
-
return Math.round(value / gridSize) * gridSize;
|
|
129
|
-
};
|
|
130
|
-
snapPositionToGrid = (position, gridSize) => {
|
|
131
|
-
return {
|
|
132
|
-
x: snapToGrid(position.x, gridSize),
|
|
133
|
-
y: snapToGrid(position.y, gridSize)
|
|
134
|
-
};
|
|
135
|
-
};
|
|
136
|
-
clamp = (value, min, max) => {
|
|
137
|
-
return Math.min(Math.max(value, min), max);
|
|
138
|
-
};
|
|
139
|
-
constrainToCanvas = (position, size, canvasSize) => {
|
|
140
|
-
return {
|
|
141
|
-
x: clamp(position.x, 0, canvasSize.width - size.width),
|
|
142
|
-
y: clamp(position.y, 0, canvasSize.height - size.height)
|
|
143
|
-
};
|
|
144
|
-
};
|
|
145
|
-
getRotatedBoundingBox = (x, y, width, height, rotation) => {
|
|
146
|
-
const rad = rotation * Math.PI / 180;
|
|
147
|
-
const cos = Math.abs(Math.cos(rad));
|
|
148
|
-
const sin = Math.abs(Math.sin(rad));
|
|
149
|
-
const newWidth = width * cos + height * sin;
|
|
150
|
-
const newHeight = width * sin + height * cos;
|
|
151
|
-
return {
|
|
152
|
-
x: x - (newWidth - width) / 2,
|
|
153
|
-
y: y - (newHeight - height) / 2,
|
|
154
|
-
width: newWidth,
|
|
155
|
-
height: newHeight
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
exportToJSON = (data) => {
|
|
159
|
-
return JSON.stringify(data, null, 2);
|
|
160
|
-
};
|
|
161
|
-
importFromJSON = (json) => {
|
|
162
|
-
try {
|
|
163
|
-
const data = JSON.parse(json);
|
|
164
|
-
if (!data.width || !data.height || !Array.isArray(data.elements)) {
|
|
165
|
-
throw new Error("Invalid canvas data structure");
|
|
166
|
-
}
|
|
167
|
-
const normalizedElements = data.elements.map((element) => ({
|
|
168
|
-
...element,
|
|
169
|
-
visible: element.visible ?? true,
|
|
170
|
-
locked: element.locked ?? false
|
|
171
|
-
}));
|
|
172
|
-
return {
|
|
173
|
-
...data,
|
|
174
|
-
elements: normalizedElements
|
|
175
|
-
};
|
|
176
|
-
} catch (error) {
|
|
177
|
-
throw new Error(`Failed to parse canvas data: ${error.message}`);
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
getElementCenter = (element) => {
|
|
181
|
-
return {
|
|
182
|
-
x: element.position.x + element.size.width / 2,
|
|
183
|
-
y: element.position.y + element.size.height / 2
|
|
184
|
-
};
|
|
185
|
-
};
|
|
186
|
-
distance = (p1, p2) => {
|
|
187
|
-
const dx = p2.x - p1.x;
|
|
188
|
-
const dy = p2.y - p1.y;
|
|
189
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
190
|
-
};
|
|
191
|
-
degToRad = (degrees) => {
|
|
192
|
-
return degrees * Math.PI / 180;
|
|
193
|
-
};
|
|
194
|
-
radToDeg = (radians) => {
|
|
195
|
-
return radians * 180 / Math.PI;
|
|
196
|
-
};
|
|
197
|
-
isValidElement = (element) => {
|
|
198
|
-
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;
|
|
199
|
-
};
|
|
200
|
-
isValidCanvasExport = (data) => {
|
|
201
|
-
return data && typeof data.width === "number" && typeof data.height === "number" && Array.isArray(data.elements) && data.elements.every(isValidElement);
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
31
|
// src/index.ts
|
|
207
32
|
var index_exports = {};
|
|
208
33
|
__export(index_exports, {
|
|
@@ -254,6 +79,148 @@ var import_react_konva3 = require("react-konva");
|
|
|
254
79
|
|
|
255
80
|
// src/core/useEditorState.ts
|
|
256
81
|
var import_react = require("react");
|
|
82
|
+
|
|
83
|
+
// src/utils/editorUtils.ts
|
|
84
|
+
var import_uuid = require("uuid");
|
|
85
|
+
var generateElementId = () => {
|
|
86
|
+
return `element-${(0, import_uuid.v4)()}`;
|
|
87
|
+
};
|
|
88
|
+
var createElement = (type, props, options) => {
|
|
89
|
+
return {
|
|
90
|
+
id: generateElementId(),
|
|
91
|
+
type,
|
|
92
|
+
position: options?.position || { x: 0, y: 0 },
|
|
93
|
+
size: options?.size || { width: 100, height: 100 },
|
|
94
|
+
rotation: options?.rotation || 0,
|
|
95
|
+
opacity: options?.opacity ?? 1,
|
|
96
|
+
zIndex: options?.zIndex || 0,
|
|
97
|
+
visible: options?.visible ?? true,
|
|
98
|
+
locked: options?.locked ?? false,
|
|
99
|
+
displayName: options?.displayName,
|
|
100
|
+
props
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
var cloneElement = (element) => {
|
|
104
|
+
return {
|
|
105
|
+
...element,
|
|
106
|
+
id: generateElementId(),
|
|
107
|
+
// New ID for the clone
|
|
108
|
+
props: { ...element.props },
|
|
109
|
+
position: { ...element.position },
|
|
110
|
+
size: { ...element.size }
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
var duplicateElement = (element, offset = { x: 20, y: 20 }) => {
|
|
114
|
+
const cloned = cloneElement(element);
|
|
115
|
+
return {
|
|
116
|
+
...cloned,
|
|
117
|
+
position: {
|
|
118
|
+
x: element.position.x + offset.x,
|
|
119
|
+
y: element.position.y + offset.y
|
|
120
|
+
},
|
|
121
|
+
zIndex: element.zIndex + 1
|
|
122
|
+
// Place on top
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
var sortByZIndex = (elements) => {
|
|
126
|
+
return [...elements].sort((a, b) => a.zIndex - b.zIndex);
|
|
127
|
+
};
|
|
128
|
+
var getMaxZIndex = (elements) => {
|
|
129
|
+
if (elements.length === 0) return 0;
|
|
130
|
+
return Math.max(...elements.map((el) => el.zIndex));
|
|
131
|
+
};
|
|
132
|
+
var bringToFront = (elements, elementId) => {
|
|
133
|
+
const maxZ = getMaxZIndex(elements);
|
|
134
|
+
return elements.map((el) => el.id === elementId ? { ...el, zIndex: maxZ + 1 } : el);
|
|
135
|
+
};
|
|
136
|
+
var sendToBack = (elements, elementId) => {
|
|
137
|
+
const minZ = Math.min(...elements.map((el) => el.zIndex));
|
|
138
|
+
return elements.map((el) => el.id === elementId ? { ...el, zIndex: minZ - 1 } : el);
|
|
139
|
+
};
|
|
140
|
+
var checkOverlap = (rect1, rect2) => {
|
|
141
|
+
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);
|
|
142
|
+
};
|
|
143
|
+
var pointInRect = (point, rect) => {
|
|
144
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
145
|
+
};
|
|
146
|
+
var snapToGrid = (value, gridSize) => {
|
|
147
|
+
return Math.round(value / gridSize) * gridSize;
|
|
148
|
+
};
|
|
149
|
+
var snapPositionToGrid = (position, gridSize) => {
|
|
150
|
+
return {
|
|
151
|
+
x: snapToGrid(position.x, gridSize),
|
|
152
|
+
y: snapToGrid(position.y, gridSize)
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
var clamp = (value, min, max) => {
|
|
156
|
+
return Math.min(Math.max(value, min), max);
|
|
157
|
+
};
|
|
158
|
+
var constrainToCanvas = (position, size, canvasSize) => {
|
|
159
|
+
return {
|
|
160
|
+
x: clamp(position.x, 0, canvasSize.width - size.width),
|
|
161
|
+
y: clamp(position.y, 0, canvasSize.height - size.height)
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
var getRotatedBoundingBox = (x, y, width, height, rotation) => {
|
|
165
|
+
const rad = rotation * Math.PI / 180;
|
|
166
|
+
const cos = Math.abs(Math.cos(rad));
|
|
167
|
+
const sin = Math.abs(Math.sin(rad));
|
|
168
|
+
const newWidth = width * cos + height * sin;
|
|
169
|
+
const newHeight = width * sin + height * cos;
|
|
170
|
+
return {
|
|
171
|
+
x: x - (newWidth - width) / 2,
|
|
172
|
+
y: y - (newHeight - height) / 2,
|
|
173
|
+
width: newWidth,
|
|
174
|
+
height: newHeight
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
var exportToJSON = (data) => {
|
|
178
|
+
return JSON.stringify(data, null, 2);
|
|
179
|
+
};
|
|
180
|
+
var importFromJSON = (json) => {
|
|
181
|
+
try {
|
|
182
|
+
const data = JSON.parse(json);
|
|
183
|
+
if (!data.width || !data.height || !Array.isArray(data.elements)) {
|
|
184
|
+
throw new Error("Invalid canvas data structure");
|
|
185
|
+
}
|
|
186
|
+
const normalizedElements = data.elements.map((element) => ({
|
|
187
|
+
...element,
|
|
188
|
+
visible: element.visible ?? true,
|
|
189
|
+
locked: element.locked ?? false
|
|
190
|
+
}));
|
|
191
|
+
return {
|
|
192
|
+
...data,
|
|
193
|
+
elements: normalizedElements
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error(`Failed to parse canvas data: ${error.message}`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
var getElementCenter = (element) => {
|
|
200
|
+
return {
|
|
201
|
+
x: element.position.x + element.size.width / 2,
|
|
202
|
+
y: element.position.y + element.size.height / 2
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
var distance = (p1, p2) => {
|
|
206
|
+
const dx = p2.x - p1.x;
|
|
207
|
+
const dy = p2.y - p1.y;
|
|
208
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
209
|
+
};
|
|
210
|
+
var degToRad = (degrees) => {
|
|
211
|
+
return degrees * Math.PI / 180;
|
|
212
|
+
};
|
|
213
|
+
var radToDeg = (radians) => {
|
|
214
|
+
return radians * 180 / Math.PI;
|
|
215
|
+
};
|
|
216
|
+
var isValidElement = (element) => {
|
|
217
|
+
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;
|
|
218
|
+
};
|
|
219
|
+
var isValidCanvasExport = (data) => {
|
|
220
|
+
return data && typeof data.width === "number" && typeof data.height === "number" && Array.isArray(data.elements) && data.elements.every(isValidElement);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// src/core/useEditorState.ts
|
|
257
224
|
var createInitialState = (mode) => ({
|
|
258
225
|
elements: [],
|
|
259
226
|
selectedElementId: null,
|
|
@@ -527,8 +494,7 @@ var useEditorState = (initialMode = null) => {
|
|
|
527
494
|
(id = null, offset = { x: 20, y: 20 }) => {
|
|
528
495
|
const elementToDuplicate = id ? state.elements.find((el) => el.id === id) : getSelectedElement();
|
|
529
496
|
if (!elementToDuplicate) return;
|
|
530
|
-
const
|
|
531
|
-
const duplicated = duplicateUtil(elementToDuplicate, offset);
|
|
497
|
+
const duplicated = duplicateElement(elementToDuplicate, offset);
|
|
532
498
|
addElement(duplicated);
|
|
533
499
|
},
|
|
534
500
|
[state.elements, getSelectedElement, addElement]
|
|
@@ -536,10 +502,9 @@ var useEditorState = (initialMode = null) => {
|
|
|
536
502
|
const pasteElement = (0, import_react.useCallback)(
|
|
537
503
|
(copiedElement, offset = { x: 20, y: 20 }) => {
|
|
538
504
|
if (!copiedElement) return;
|
|
539
|
-
const { generateElementId: generateElementId2 } = (init_editorUtils(), __toCommonJS(editorUtils_exports));
|
|
540
505
|
const pasted = {
|
|
541
506
|
...copiedElement,
|
|
542
|
-
id:
|
|
507
|
+
id: generateElementId(),
|
|
543
508
|
props: { ...copiedElement.props },
|
|
544
509
|
position: {
|
|
545
510
|
x: copiedElement.position.x + offset.x,
|
|
@@ -2090,7 +2055,7 @@ var Inspector = ({
|
|
|
2090
2055
|
function renderField(field, props, editingValues, onChange, onColorChange, mode) {
|
|
2091
2056
|
const value = editingValues[field.name] ?? props[field.name] ?? field.defaultValue;
|
|
2092
2057
|
switch (field.type) {
|
|
2093
|
-
case "custom":
|
|
2058
|
+
case "custom": {
|
|
2094
2059
|
if (!field.customRenderer) {
|
|
2095
2060
|
console.error(`Custom field "${field.name}" has no customRenderer defined`);
|
|
2096
2061
|
return null;
|
|
@@ -2106,7 +2071,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2106
2071
|
mode
|
|
2107
2072
|
}
|
|
2108
2073
|
) }, field.name);
|
|
2109
|
-
|
|
2074
|
+
}
|
|
2075
|
+
case "string": {
|
|
2110
2076
|
const isMultiline = field.name === "content" || field.name === "text";
|
|
2111
2077
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2112
2078
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
|
|
@@ -2130,7 +2096,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2130
2096
|
),
|
|
2131
2097
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2132
2098
|
] }, field.name);
|
|
2133
|
-
|
|
2099
|
+
}
|
|
2100
|
+
case "number": {
|
|
2134
2101
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2135
2102
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
|
|
2136
2103
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
@@ -2148,7 +2115,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2148
2115
|
),
|
|
2149
2116
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2150
2117
|
] }, field.name);
|
|
2151
|
-
|
|
2118
|
+
}
|
|
2119
|
+
case "color": {
|
|
2152
2120
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2153
2121
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
|
|
2154
2122
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
@@ -2163,7 +2131,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2163
2131
|
),
|
|
2164
2132
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2165
2133
|
] }, field.name);
|
|
2166
|
-
|
|
2134
|
+
}
|
|
2135
|
+
case "select": {
|
|
2167
2136
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2168
2137
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
|
|
2169
2138
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
@@ -2179,7 +2148,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2179
2148
|
),
|
|
2180
2149
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2181
2150
|
] }, field.name);
|
|
2182
|
-
|
|
2151
|
+
}
|
|
2152
|
+
case "boolean": {
|
|
2183
2153
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-start space-x-2", children: [
|
|
2184
2154
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2185
2155
|
Checkbox,
|
|
@@ -2195,7 +2165,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2195
2165
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground", children: field.description })
|
|
2196
2166
|
] })
|
|
2197
2167
|
] }, field.name);
|
|
2198
|
-
|
|
2168
|
+
}
|
|
2169
|
+
case "slider": {
|
|
2199
2170
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2200
2171
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between mb-2", children: [
|
|
2201
2172
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { className: "text-xs", children: field.label }),
|
|
@@ -2214,7 +2185,8 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2214
2185
|
),
|
|
2215
2186
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2216
2187
|
] }, field.name);
|
|
2217
|
-
|
|
2188
|
+
}
|
|
2189
|
+
case "image": {
|
|
2218
2190
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
2219
2191
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Label, { htmlFor: field.name, className: "text-xs", children: field.label }),
|
|
2220
2192
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
@@ -2229,6 +2201,7 @@ function renderField(field, props, editingValues, onChange, onColorChange, mode)
|
|
|
2229
2201
|
),
|
|
2230
2202
|
field.description && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-muted-foreground mt-1", children: field.description })
|
|
2231
2203
|
] }, field.name);
|
|
2204
|
+
}
|
|
2232
2205
|
default:
|
|
2233
2206
|
return null;
|
|
2234
2207
|
}
|
|
@@ -3571,12 +3544,8 @@ var Canvas = ({
|
|
|
3571
3544
|
);
|
|
3572
3545
|
};
|
|
3573
3546
|
|
|
3574
|
-
// src/components/VisualEditorWorkspace.tsx
|
|
3575
|
-
init_editorUtils();
|
|
3576
|
-
|
|
3577
3547
|
// src/components/Toolbar.tsx
|
|
3578
3548
|
var import_lucide_react8 = require("lucide-react");
|
|
3579
|
-
init_editorUtils();
|
|
3580
3549
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
3581
3550
|
var Toolbar = ({
|
|
3582
3551
|
api,
|
|
@@ -3876,7 +3845,7 @@ var VisualEditorWorkspace = ({
|
|
|
3876
3845
|
readonly,
|
|
3877
3846
|
enableSnapGuides: snapGuidesEnabled,
|
|
3878
3847
|
enablePanZoom,
|
|
3879
|
-
backgroundImageUrl: backgroundImage && mode?.context?.imageUrls ? mode.context.imageUrls.get(backgroundImage) :
|
|
3848
|
+
backgroundImageUrl: backgroundImage && mode?.context?.imageUrls ? mode.context.imageUrls.get(backgroundImage) : backgroundImageUrl,
|
|
3880
3849
|
hideElements,
|
|
3881
3850
|
onSelectElement: (id) => api.selectElement(id),
|
|
3882
3851
|
onTransformElement: (id, updates) => api.updateElement(id, updates)
|
|
@@ -3989,9 +3958,6 @@ var AssetPicker = ({
|
|
|
3989
3958
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScrollArea, { className: "flex-1", children: filteredAssets.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "grid grid-cols-3 gap-2 p-3", children: filteredAssets.map((asset) => assetRenderer(asset)) }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex items-center justify-center h-32 text-sm text-muted-foreground", children: searchQuery ? "No assets found" : "No assets available" }) })
|
|
3990
3959
|
] });
|
|
3991
3960
|
};
|
|
3992
|
-
|
|
3993
|
-
// src/index.ts
|
|
3994
|
-
init_editorUtils();
|
|
3995
3961
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3996
3962
|
0 && (module.exports = {
|
|
3997
3963
|
AssetPicker,
|