Spaces:
Running
Running
Now I cannot increase the size from anywhere it's only dragging but that's only from the middle make sure that's working OK why are you doing like this make sure that's working I want see I'm clarifying once again if I wanna increase the size of the element only from the border OK any border so if I are placing on the right border I can extend to right if I'm keeping the upside border I can extend to upside if I am keeping bottom down side bottom right left side corner I can extend aside understood so if my mouse point inside that element we cannot extend the size of it only we can drag - Follow Up Deployment
Browse files- index.html +282 -86
index.html
CHANGED
@@ -47,129 +47,116 @@
|
|
47 |
</script>
|
48 |
<script>
|
49 |
$(document).ready(function() {
|
50 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
$('.element-card').draggable({
|
52 |
helper: 'clone',
|
53 |
cursor: 'move',
|
54 |
zIndex: 1000,
|
55 |
-
revert: 'invalid'
|
|
|
|
|
|
|
|
|
56 |
});
|
57 |
|
58 |
// Make canvas a drop zone
|
59 |
$('#dropZone').droppable({
|
60 |
accept: '.element-card',
|
|
|
|
|
61 |
drop: function(event, ui) {
|
62 |
const elementType = ui.draggable.data('element');
|
63 |
-
|
|
|
|
|
|
|
|
|
64 |
}
|
65 |
});
|
66 |
|
67 |
-
// Function to add element to canvas
|
68 |
-
function addElementToCanvas(type) {
|
69 |
const elementId = 'element-' + Date.now();
|
70 |
let elementHTML = '';
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
switch(type) {
|
73 |
case 'button':
|
74 |
elementHTML = `
|
75 |
<div class="canvas-element glass-panel p-2 rounded flex items-center justify-center absolute"
|
76 |
-
style="width: 120px; height: 40px; top:
|
77 |
id="${elementId}">
|
78 |
<span class="text-sm">Button</span>
|
79 |
-
<div class="resizer resizer-nw"></div>
|
80 |
-
<div class="resizer resizer-ne"></div>
|
81 |
-
<div class="resizer resizer-sw"></div>
|
82 |
-
<div class="resizer resizer-se"></div>
|
83 |
-
<div class="rotator"></div>
|
84 |
</div>
|
85 |
`;
|
86 |
break;
|
87 |
case 'input':
|
88 |
elementHTML = `
|
89 |
<div class="canvas-element glass-panel p-2 rounded absolute"
|
90 |
-
style="width: 200px; height: 40px; top:
|
91 |
id="${elementId}">
|
92 |
<input type="text" placeholder="Input field" class="w-full h-full bg-transparent border border-gray-600 rounded px-2 text-sm">
|
93 |
-
<div class="resizer resizer-nw"></div>
|
94 |
-
<div class="resizer resizer-ne"></div>
|
95 |
-
<div class="resizer resizer-sw"></div>
|
96 |
-
<div class="resizer resizer-se"></div>
|
97 |
-
<div class="rotator"></div>
|
98 |
</div>
|
99 |
`;
|
100 |
break;
|
101 |
case 'card':
|
102 |
elementHTML = `
|
103 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
104 |
-
style="width: 250px; height: 150px; top:
|
105 |
id="${elementId}">
|
106 |
<h3 class="font-semibold mb-2">Card Title</h3>
|
107 |
<p class="text-xs">Card content goes here...</p>
|
108 |
-
<div class="resizer resizer-nw"></div>
|
109 |
-
<div class="resizer resizer-ne"></div>
|
110 |
-
<div class="resizer resizer-sw"></div>
|
111 |
-
<div class="resizer resizer-se"></div>
|
112 |
-
<div class="rotator"></div>
|
113 |
</div>
|
114 |
`;
|
115 |
break;
|
116 |
case 'image':
|
117 |
elementHTML = `
|
118 |
<div class="canvas-element glass-panel rounded absolute flex items-center justify-center"
|
119 |
-
style="width: 200px; height: 150px; top:
|
120 |
id="${elementId}">
|
121 |
<i class="fas fa-image text-4xl text-gray-500"></i>
|
122 |
-
<div class="resizer resizer-nw"></div>
|
123 |
-
<div class="resizer resizer-ne"></div>
|
124 |
-
<div class="resizer resizer-sw"></div>
|
125 |
-
<div class="resizer resizer-se"></div>
|
126 |
-
<div class="rotator"></div>
|
127 |
</div>
|
128 |
`;
|
129 |
break;
|
130 |
case 'container':
|
131 |
elementHTML = `
|
132 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
133 |
-
style="width: 300px; height: 200px; top:
|
134 |
id="${elementId}">
|
135 |
<div class="w-full h-full border-2 border-dashed border-gray-600 rounded flex items-center justify-center">
|
136 |
<span class="text-gray-500">Container</span>
|
137 |
</div>
|
138 |
-
<div class="resizer resizer-nw"></div>
|
139 |
-
<div class="resizer resizer-ne"></div>
|
140 |
-
<div class="resizer resizer-sw"></div>
|
141 |
-
<div class="resizer resizer-se"></div>
|
142 |
-
<div class="rotator"></div>
|
143 |
</div>
|
144 |
`;
|
145 |
break;
|
146 |
case 'section':
|
147 |
elementHTML = `
|
148 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
149 |
-
style="width: 400px; height: 300px; top:
|
150 |
id="${elementId}">
|
151 |
<div class="w-full h-full border border-gray-600 rounded flex items-center justify-center">
|
152 |
<span class="text-gray-500">Section</span>
|
153 |
</div>
|
154 |
-
<div class="resizer resizer-nw"></div>
|
155 |
-
<div class="resizer resizer-ne"></div>
|
156 |
-
<div class="resizer resizer-sw"></div>
|
157 |
-
<div class="resizer resizer-se"></div>
|
158 |
-
<div class="rotator"></div>
|
159 |
</div>
|
160 |
`;
|
161 |
break;
|
162 |
default:
|
163 |
elementHTML = `
|
164 |
<div class="canvas-element glass-panel p-2 rounded absolute"
|
165 |
-
style="width: 150px; height: 50px; top:
|
166 |
id="${elementId}">
|
167 |
<span class="text-sm">${type.charAt(0).toUpperCase() + type.slice(1)}</span>
|
168 |
-
<div class="resizer resizer-nw"></div>
|
169 |
-
<div class="resizer resizer-ne"></div>
|
170 |
-
<div class="resizer resizer-sw"></div>
|
171 |
-
<div class="resizer resizer-se"></div>
|
172 |
-
<div class="rotator"></div>
|
173 |
</div>
|
174 |
`;
|
175 |
}
|
@@ -179,8 +166,11 @@
|
|
179 |
// Make the new element selectable
|
180 |
$(`#${elementId}`).click(function(e) {
|
181 |
e.stopPropagation();
|
182 |
-
|
183 |
-
|
|
|
|
|
|
|
184 |
});
|
185 |
|
186 |
// Make the new element draggable
|
@@ -188,17 +178,28 @@
|
|
188 |
|
189 |
// Make the new element resizable and rotatable
|
190 |
makeElementResizableRotatable(`#${elementId}`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
}
|
192 |
|
193 |
-
// Make canvas elements draggable
|
194 |
function makeElementDraggable(selector) {
|
195 |
$(selector).draggable({
|
196 |
-
containment: '
|
197 |
cursor: 'move',
|
198 |
handle: ':not(.resizer):not(.rotator)',
|
199 |
start: function() {
|
200 |
$(this).addClass('dragging');
|
201 |
},
|
|
|
|
|
|
|
|
|
202 |
stop: function() {
|
203 |
$(this).removeClass('dragging');
|
204 |
}
|
@@ -208,12 +209,28 @@
|
|
208 |
// Make elements resizable and rotatable
|
209 |
function makeElementResizableRotatable(selector) {
|
210 |
interact(selector)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
.resizable({
|
212 |
-
edges: {
|
|
|
|
|
|
|
|
|
|
|
213 |
modifiers: [
|
214 |
interact.modifiers.restrictEdges({
|
215 |
-
outer: 'parent'
|
216 |
-
endOnly: true
|
217 |
})
|
218 |
],
|
219 |
inertia: true
|
@@ -239,20 +256,89 @@
|
|
239 |
})
|
240 |
.on('resizeend', function (event) {
|
241 |
event.target.classList.remove('resizing');
|
|
|
|
|
242 |
});
|
243 |
}
|
244 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
// Select canvas elements
|
246 |
$(document).on('click', '.canvas-element', function(e) {
|
247 |
e.stopPropagation();
|
248 |
-
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
});
|
251 |
|
252 |
// Deselect when clicking on canvas
|
253 |
$('#dropZone').click(function(e) {
|
254 |
-
if (e.target.id === 'dropZone') {
|
255 |
$('.canvas-element').removeClass('selected');
|
|
|
256 |
}
|
257 |
});
|
258 |
|
@@ -261,6 +347,100 @@
|
|
261 |
makeElementDraggable(this);
|
262 |
makeElementResizableRotatable(this);
|
263 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
264 |
});
|
265 |
</script>
|
266 |
<style>
|
@@ -335,31 +515,6 @@
|
|
335 |
background-size: 20px 20px;
|
336 |
}
|
337 |
|
338 |
-
.resizer {
|
339 |
-
position: absolute;
|
340 |
-
width: 10px;
|
341 |
-
height: 10px;
|
342 |
-
background: rgba(0, 212, 255, 0.8);
|
343 |
-
border-radius: 50%;
|
344 |
-
z-index: 10;
|
345 |
-
}
|
346 |
-
|
347 |
-
.resizer-nw { top: -5px; left: -5px; cursor: nw-resize; }
|
348 |
-
.resizer-ne { top: -5px; right: -5px; cursor: ne-resize; }
|
349 |
-
.resizer-sw { bottom: -5px; left: -5px; cursor: sw-resize; }
|
350 |
-
.resizer-se { bottom: -5px; right: -5px; cursor: se-resize; }
|
351 |
-
|
352 |
-
.rotator {
|
353 |
-
position: absolute;
|
354 |
-
top: -30px;
|
355 |
-
left: 50%;
|
356 |
-
transform: translateX(-50%);
|
357 |
-
width: 20px;
|
358 |
-
height: 20px;
|
359 |
-
background: rgba(0, 212, 255, 0.8);
|
360 |
-
border-radius: 50%;
|
361 |
-
cursor: grab;
|
362 |
-
}
|
363 |
|
364 |
.canvas-element {
|
365 |
position: absolute;
|
@@ -371,6 +526,10 @@
|
|
371 |
box-shadow: 0 0 0 2px #00D4FF, 0 0 15px rgba(0, 212, 255, 0.5);
|
372 |
}
|
373 |
|
|
|
|
|
|
|
|
|
374 |
.dragging {
|
375 |
opacity: 0.7;
|
376 |
z-index: 1000;
|
@@ -380,6 +539,7 @@
|
|
380 |
position: relative;
|
381 |
width: 100%;
|
382 |
height: 100%;
|
|
|
383 |
}
|
384 |
|
385 |
.property-slider::-webkit-slider-thumb {
|
@@ -448,6 +608,14 @@
|
|
448 |
color: #00D4FF;
|
449 |
}
|
450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
451 |
.layer-item {
|
452 |
transition: all 0.2s ease;
|
453 |
}
|
@@ -611,17 +779,45 @@
|
|
611 |
|
612 |
<!-- Canvas Area -->
|
613 |
<div class="flex-1 overflow-auto relative canvas-grid" id="canvas">
|
614 |
-
<div class="drop-zone" id="dropZone">
|
615 |
<!-- Canvas is now clean - ready for elements to be added -->
|
616 |
</div>
|
617 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
618 |
</div>
|
619 |
|
620 |
<!-- Right Customization Panel -->
|
621 |
<div class="w-48 glass-panel flex flex-col border-l border-gray-800">
|
622 |
<div class="p-2 border-b border-gray-800">
|
623 |
<h2 class="text-xs font-semibold">Properties</h2>
|
624 |
-
<p class="tiny-text text-gray-400">
|
625 |
</div>
|
626 |
|
627 |
<div class="flex-1 overflow-y-auto p-2">
|
@@ -676,12 +872,12 @@
|
|
676 |
</div>
|
677 |
</div>
|
678 |
|
679 |
-
<div class="mb-2">
|
680 |
<h3 class="font-medium tiny-text mb-1">Text</h3>
|
681 |
<div class="space-y-2">
|
682 |
<div>
|
683 |
<label class="tiny-text text-gray-400">Content</label>
|
684 |
-
<input type="text" value="Primary Button" class="w-full mt-1 tiny-input rounded-md glow-input">
|
685 |
</div>
|
686 |
|
687 |
<div>
|
|
|
47 |
</script>
|
48 |
<script>
|
49 |
$(document).ready(function() {
|
50 |
+
// Current tool state
|
51 |
+
let currentTool = 'select'; // 'select', 'hand', 'text', 'rectangle', 'circle', 'line'
|
52 |
+
let isPanning = false;
|
53 |
+
let panStart = { x: 0, y: 0 };
|
54 |
+
let canvasStart = { x: 0, y: 0 };
|
55 |
+
|
56 |
+
// Make component cards draggable (clone mode)
|
57 |
$('.element-card').draggable({
|
58 |
helper: 'clone',
|
59 |
cursor: 'move',
|
60 |
zIndex: 1000,
|
61 |
+
revert: 'invalid',
|
62 |
+
start: function(event, ui) {
|
63 |
+
// Improve visual feedback during drag
|
64 |
+
ui.helper.addClass('opacity-75');
|
65 |
+
}
|
66 |
});
|
67 |
|
68 |
// Make canvas a drop zone
|
69 |
$('#dropZone').droppable({
|
70 |
accept: '.element-card',
|
71 |
+
tolerance: 'pointer',
|
72 |
+
activeClass: 'border-2 border-dashed border-accent-blue',
|
73 |
drop: function(event, ui) {
|
74 |
const elementType = ui.draggable.data('element');
|
75 |
+
const position = {
|
76 |
+
x: event.clientX - event.target.getBoundingClientRect().left,
|
77 |
+
y: event.clientY - event.target.getBoundingClientRect().top
|
78 |
+
};
|
79 |
+
addElementToCanvas(elementType, position);
|
80 |
}
|
81 |
});
|
82 |
|
83 |
+
// Function to add element to canvas with position
|
84 |
+
function addElementToCanvas(type, position) {
|
85 |
const elementId = 'element-' + Date.now();
|
86 |
let elementHTML = '';
|
87 |
|
88 |
+
// Position element at drop point
|
89 |
+
const adjustedPosition = {
|
90 |
+
x: position.x - 50,
|
91 |
+
y: position.y - 25
|
92 |
+
};
|
93 |
+
|
94 |
switch(type) {
|
95 |
case 'button':
|
96 |
elementHTML = `
|
97 |
<div class="canvas-element glass-panel p-2 rounded flex items-center justify-center absolute"
|
98 |
+
style="width: 120px; height: 40px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
99 |
id="${elementId}">
|
100 |
<span class="text-sm">Button</span>
|
|
|
|
|
|
|
|
|
|
|
101 |
</div>
|
102 |
`;
|
103 |
break;
|
104 |
case 'input':
|
105 |
elementHTML = `
|
106 |
<div class="canvas-element glass-panel p-2 rounded absolute"
|
107 |
+
style="width: 200px; height: 40px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
108 |
id="${elementId}">
|
109 |
<input type="text" placeholder="Input field" class="w-full h-full bg-transparent border border-gray-600 rounded px-2 text-sm">
|
|
|
|
|
|
|
|
|
|
|
110 |
</div>
|
111 |
`;
|
112 |
break;
|
113 |
case 'card':
|
114 |
elementHTML = `
|
115 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
116 |
+
style="width: 250px; height: 150px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
117 |
id="${elementId}">
|
118 |
<h3 class="font-semibold mb-2">Card Title</h3>
|
119 |
<p class="text-xs">Card content goes here...</p>
|
|
|
|
|
|
|
|
|
|
|
120 |
</div>
|
121 |
`;
|
122 |
break;
|
123 |
case 'image':
|
124 |
elementHTML = `
|
125 |
<div class="canvas-element glass-panel rounded absolute flex items-center justify-center"
|
126 |
+
style="width: 200px; height: 150px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
127 |
id="${elementId}">
|
128 |
<i class="fas fa-image text-4xl text-gray-500"></i>
|
|
|
|
|
|
|
|
|
|
|
129 |
</div>
|
130 |
`;
|
131 |
break;
|
132 |
case 'container':
|
133 |
elementHTML = `
|
134 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
135 |
+
style="width: 300px; height: 200px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
136 |
id="${elementId}">
|
137 |
<div class="w-full h-full border-2 border-dashed border-gray-600 rounded flex items-center justify-center">
|
138 |
<span class="text-gray-500">Container</span>
|
139 |
</div>
|
|
|
|
|
|
|
|
|
|
|
140 |
</div>
|
141 |
`;
|
142 |
break;
|
143 |
case 'section':
|
144 |
elementHTML = `
|
145 |
<div class="canvas-element glass-panel p-4 rounded absolute"
|
146 |
+
style="width: 400px; height: 300px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
147 |
id="${elementId}">
|
148 |
<div class="w-full h-full border border-gray-600 rounded flex items-center justify-center">
|
149 |
<span class="text-gray-500">Section</span>
|
150 |
</div>
|
|
|
|
|
|
|
|
|
|
|
151 |
</div>
|
152 |
`;
|
153 |
break;
|
154 |
default:
|
155 |
elementHTML = `
|
156 |
<div class="canvas-element glass-panel p-2 rounded absolute"
|
157 |
+
style="width: 150px; height: 50px; top: ${adjustedPosition.y}px; left: ${adjustedPosition.x}px;"
|
158 |
id="${elementId}">
|
159 |
<span class="text-sm">${type.charAt(0).toUpperCase() + type.slice(1)}</span>
|
|
|
|
|
|
|
|
|
|
|
160 |
</div>
|
161 |
`;
|
162 |
}
|
|
|
166 |
// Make the new element selectable
|
167 |
$(`#${elementId}`).click(function(e) {
|
168 |
e.stopPropagation();
|
169 |
+
if (currentTool === 'select') {
|
170 |
+
$('.canvas-element').removeClass('selected');
|
171 |
+
$(this).addClass('selected');
|
172 |
+
updatePropertyPanel($(this));
|
173 |
+
}
|
174 |
});
|
175 |
|
176 |
// Make the new element draggable
|
|
|
178 |
|
179 |
// Make the new element resizable and rotatable
|
180 |
makeElementResizableRotatable(`#${elementId}`);
|
181 |
+
|
182 |
+
// Select the newly added element
|
183 |
+
if (currentTool === 'select') {
|
184 |
+
$('.canvas-element').removeClass('selected');
|
185 |
+
$(`#${elementId}`).addClass('selected');
|
186 |
+
updatePropertyPanel($(`#${elementId}`));
|
187 |
+
}
|
188 |
}
|
189 |
|
190 |
+
// Make canvas elements draggable with better containment
|
191 |
function makeElementDraggable(selector) {
|
192 |
$(selector).draggable({
|
193 |
+
containment: 'parent',
|
194 |
cursor: 'move',
|
195 |
handle: ':not(.resizer):not(.rotator)',
|
196 |
start: function() {
|
197 |
$(this).addClass('dragging');
|
198 |
},
|
199 |
+
drag: function(event, ui) {
|
200 |
+
// Smooth dragging without visual artifacts
|
201 |
+
ui.helper.removeClass('bg-dark-700');
|
202 |
+
},
|
203 |
stop: function() {
|
204 |
$(this).removeClass('dragging');
|
205 |
}
|
|
|
209 |
// Make elements resizable and rotatable
|
210 |
function makeElementResizableRotatable(selector) {
|
211 |
interact(selector)
|
212 |
+
.draggable({
|
213 |
+
inertia: true,
|
214 |
+
modifiers: [
|
215 |
+
interact.modifiers.restrictRect({
|
216 |
+
restriction: 'parent'
|
217 |
+
})
|
218 |
+
],
|
219 |
+
autoScroll: true,
|
220 |
+
listeners: {
|
221 |
+
move: dragMoveListener
|
222 |
+
}
|
223 |
+
})
|
224 |
.resizable({
|
225 |
+
edges: {
|
226 |
+
left: '.resize-left',
|
227 |
+
right: '.resize-right',
|
228 |
+
top: '.resize-top',
|
229 |
+
bottom: '.resize-bottom'
|
230 |
+
},
|
231 |
modifiers: [
|
232 |
interact.modifiers.restrictEdges({
|
233 |
+
outer: 'parent'
|
|
|
234 |
})
|
235 |
],
|
236 |
inertia: true
|
|
|
256 |
})
|
257 |
.on('resizeend', function (event) {
|
258 |
event.target.classList.remove('resizing');
|
259 |
+
// Reset resize classes
|
260 |
+
event.target.classList.remove('resize-left', 'resize-right', 'resize-top', 'resize-bottom');
|
261 |
});
|
262 |
}
|
263 |
|
264 |
+
// Drag move listener for positioning elements
|
265 |
+
function dragMoveListener (event) {
|
266 |
+
var target = event.target;
|
267 |
+
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
|
268 |
+
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
269 |
+
|
270 |
+
target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
|
271 |
+
|
272 |
+
target.setAttribute('data-x', x);
|
273 |
+
target.setAttribute('data-y', y);
|
274 |
+
}
|
275 |
+
|
276 |
+
// Update cursor and resize state based on mouse position relative to element edges
|
277 |
+
$(document).on('mousemove', '.canvas-element', function(e) {
|
278 |
+
const element = $(this);
|
279 |
+
const offset = element.offset();
|
280 |
+
const width = element.outerWidth();
|
281 |
+
const height = element.outerHeight();
|
282 |
+
const x = e.pageX - offset.left;
|
283 |
+
const y = e.pageY - offset.top;
|
284 |
+
const edgeThreshold = 8;
|
285 |
+
|
286 |
+
// Check if mouse is near any edge
|
287 |
+
const nearLeft = x < edgeThreshold;
|
288 |
+
const nearRight = x > width - edgeThreshold;
|
289 |
+
const nearTop = y < edgeThreshold;
|
290 |
+
const nearBottom = y > height - edgeThreshold;
|
291 |
+
const nearAnyEdge = nearLeft || nearRight || nearTop || nearBottom;
|
292 |
+
|
293 |
+
// Set appropriate cursor
|
294 |
+
if ((nearLeft && nearTop) || (nearRight && nearBottom)) {
|
295 |
+
element.css('cursor', 'nwse-resize');
|
296 |
+
} else if ((nearRight && nearTop) || (nearLeft && nearBottom)) {
|
297 |
+
element.css('cursor', 'nesw-resize');
|
298 |
+
} else if (nearLeft || nearRight) {
|
299 |
+
element.css('cursor', 'ew-resize');
|
300 |
+
} else if (nearTop || nearBottom) {
|
301 |
+
element.css('cursor', 'ns-resize');
|
302 |
+
} else {
|
303 |
+
element.css('cursor', 'move');
|
304 |
+
}
|
305 |
+
|
306 |
+
// Add resize classes for interact.js
|
307 |
+
element.removeClass('resize-left resize-right resize-top resize-bottom');
|
308 |
+
if (nearLeft) element.addClass('resize-left');
|
309 |
+
if (nearRight) element.addClass('resize-right');
|
310 |
+
if (nearTop) element.addClass('resize-top');
|
311 |
+
if (nearBottom) element.addClass('resize-bottom');
|
312 |
+
});
|
313 |
+
|
314 |
+
// Reset cursor and resize state when mouse leaves element
|
315 |
+
$(document).on('mouseleave', '.canvas-element', function() {
|
316 |
+
$(this).css('cursor', 'default')
|
317 |
+
.removeClass('resize-left resize-right resize-top resize-bottom');
|
318 |
+
});
|
319 |
+
|
320 |
// Select canvas elements
|
321 |
$(document).on('click', '.canvas-element', function(e) {
|
322 |
e.stopPropagation();
|
323 |
+
if (currentTool === 'select') {
|
324 |
+
$('.canvas-element').removeClass('selected');
|
325 |
+
$(this).addClass('selected');
|
326 |
+
updatePropertyPanel($(this));
|
327 |
+
}
|
328 |
+
});
|
329 |
+
|
330 |
+
// Add hover effect for bounding box visualization
|
331 |
+
$(document).on('mouseenter', '.canvas-element', function() {
|
332 |
+
$(this).addClass('hovered');
|
333 |
+
}).on('mouseleave', '.canvas-element', function() {
|
334 |
+
$(this).removeClass('hovered');
|
335 |
});
|
336 |
|
337 |
// Deselect when clicking on canvas
|
338 |
$('#dropZone').click(function(e) {
|
339 |
+
if (e.target.id === 'dropZone' && currentTool === 'select') {
|
340 |
$('.canvas-element').removeClass('selected');
|
341 |
+
updatePropertyPanel(null);
|
342 |
}
|
343 |
});
|
344 |
|
|
|
347 |
makeElementDraggable(this);
|
348 |
makeElementResizableRotatable(this);
|
349 |
});
|
350 |
+
|
351 |
+
// Tool selection
|
352 |
+
$('.tool-btn').click(function() {
|
353 |
+
$('.tool-btn').removeClass('bg-dark-700');
|
354 |
+
$(this).addClass('bg-dark-700');
|
355 |
+
currentTool = $(this).data('tool');
|
356 |
+
|
357 |
+
// Update cursor based on tool
|
358 |
+
updateCanvasCursor();
|
359 |
+
});
|
360 |
+
|
361 |
+
// Update cursor based on current tool
|
362 |
+
function updateCanvasCursor() {
|
363 |
+
const canvas = $('#canvas');
|
364 |
+
canvas.removeClass('cursor-move cursor-crosshair cursor-text');
|
365 |
+
|
366 |
+
switch(currentTool) {
|
367 |
+
case 'hand':
|
368 |
+
canvas.addClass('cursor-move');
|
369 |
+
break;
|
370 |
+
case 'rectangle':
|
371 |
+
case 'circle':
|
372 |
+
case 'line':
|
373 |
+
canvas.addClass('cursor-crosshair');
|
374 |
+
break;
|
375 |
+
case 'text':
|
376 |
+
canvas.addClass('cursor-text');
|
377 |
+
break;
|
378 |
+
default:
|
379 |
+
canvas.css('cursor', 'default');
|
380 |
+
}
|
381 |
+
}
|
382 |
+
|
383 |
+
// Spacebar panning functionality
|
384 |
+
$(document).keydown(function(e) {
|
385 |
+
if (e.keyCode === 32 && !isPanning) { // Spacebar
|
386 |
+
e.preventDefault();
|
387 |
+
isPanning = true;
|
388 |
+
$('#canvas').addClass('cursor-grab');
|
389 |
+
}
|
390 |
+
});
|
391 |
+
|
392 |
+
$(document).keyup(function(e) {
|
393 |
+
if (e.keyCode === 32 && isPanning) {
|
394 |
+
isPanning = false;
|
395 |
+
$('#canvas').removeClass('cursor-grab cursor-grabbing');
|
396 |
+
}
|
397 |
+
});
|
398 |
+
|
399 |
+
// Panning implementation
|
400 |
+
$('#canvas').mousedown(function(e) {
|
401 |
+
if (isPanning) {
|
402 |
+
e.preventDefault();
|
403 |
+
panStart = { x: e.clientX, y: e.clientY };
|
404 |
+
canvasStart = {
|
405 |
+
x: parseInt($('#dropZone').css('left')) || 0,
|
406 |
+
y: parseInt($('#dropZone').css('top')) || 0
|
407 |
+
};
|
408 |
+
$('#canvas').addClass('cursor-grabbing').removeClass('cursor-grab');
|
409 |
+
$(document).on('mousemove.pan', function(e) {
|
410 |
+
const dx = e.clientX - panStart.x;
|
411 |
+
const dy = e.clientY - panStart.y;
|
412 |
+
$('#dropZone').css({
|
413 |
+
left: canvasStart.x + dx,
|
414 |
+
top: canvasStart.y + dy
|
415 |
+
});
|
416 |
+
});
|
417 |
+
}
|
418 |
+
});
|
419 |
+
|
420 |
+
$(document).mouseup(function(e) {
|
421 |
+
if (isPanning) {
|
422 |
+
$('#canvas').addClass('cursor-grab').removeClass('cursor-grabbing');
|
423 |
+
$(document).off('mousemove.pan');
|
424 |
+
}
|
425 |
+
});
|
426 |
+
|
427 |
+
// Update property panel based on selected element
|
428 |
+
function updatePropertyPanel(element) {
|
429 |
+
if (element) {
|
430 |
+
const elementType = element.data('element') || 'element';
|
431 |
+
$('#selectedElementName').text(elementType.charAt(0).toUpperCase() + elementType.slice(1) + ' selected');
|
432 |
+
|
433 |
+
// Show/hide text customization based on element content
|
434 |
+
if (element.find('span, h3, p, input').length > 0) {
|
435 |
+
$('#textCustomization').show();
|
436 |
+
} else {
|
437 |
+
$('#textCustomization').hide();
|
438 |
+
}
|
439 |
+
} else {
|
440 |
+
$('#selectedElementName').text('No element selected');
|
441 |
+
$('#textCustomization').hide();
|
442 |
+
}
|
443 |
+
}
|
444 |
});
|
445 |
</script>
|
446 |
<style>
|
|
|
515 |
background-size: 20px 20px;
|
516 |
}
|
517 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
518 |
|
519 |
.canvas-element {
|
520 |
position: absolute;
|
|
|
526 |
box-shadow: 0 0 0 2px #00D4FF, 0 0 15px rgba(0, 212, 255, 0.5);
|
527 |
}
|
528 |
|
529 |
+
.canvas-element.hovered {
|
530 |
+
box-shadow: 0 0 0 1px #00D4FF, 0 0 10px rgba(0, 212, 255, 0.3);
|
531 |
+
}
|
532 |
+
|
533 |
.dragging {
|
534 |
opacity: 0.7;
|
535 |
z-index: 1000;
|
|
|
539 |
position: relative;
|
540 |
width: 100%;
|
541 |
height: 100%;
|
542 |
+
transition: border-color 0.2s ease;
|
543 |
}
|
544 |
|
545 |
.property-slider::-webkit-slider-thumb {
|
|
|
608 |
color: #00D4FF;
|
609 |
}
|
610 |
|
611 |
+
.tool-btn {
|
612 |
+
transition: all 0.2s ease;
|
613 |
+
}
|
614 |
+
|
615 |
+
.tool-btn:hover {
|
616 |
+
background: rgba(100, 100, 150, 0.1);
|
617 |
+
}
|
618 |
+
|
619 |
.layer-item {
|
620 |
transition: all 0.2s ease;
|
621 |
}
|
|
|
779 |
|
780 |
<!-- Canvas Area -->
|
781 |
<div class="flex-1 overflow-auto relative canvas-grid" id="canvas">
|
782 |
+
<div class="drop-zone absolute" id="dropZone" style="width: 200%; height: 200%; left: -50%; top: -50%;">
|
783 |
<!-- Canvas is now clean - ready for elements to be added -->
|
784 |
</div>
|
785 |
</div>
|
786 |
+
|
787 |
+
<!-- Bottom Toolbar -->
|
788 |
+
<div class="glass h-10 flex items-center justify-center border-t border-gray-800">
|
789 |
+
<div class="flex space-x-1">
|
790 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center bg-dark-700" data-tool="select" title="Selection Tool (V)">
|
791 |
+
<i class="fas fa-mouse-pointer text-xs"></i>
|
792 |
+
</button>
|
793 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="hand" title="Hand Tool (H)">
|
794 |
+
<i class="fas fa-hand-paper text-xs"></i>
|
795 |
+
</button>
|
796 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="rectangle" title="Rectangle Tool (R)">
|
797 |
+
<i class="fas fa-square text-xs"></i>
|
798 |
+
</button>
|
799 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="circle" title="Ellipse Tool (O)">
|
800 |
+
<i class="fas fa-circle text-xs"></i>
|
801 |
+
</button>
|
802 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="line" title="Line Tool (L)">
|
803 |
+
<i class="fas fa-minus text-xs"></i>
|
804 |
+
</button>
|
805 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="text" title="Text Tool (T)">
|
806 |
+
<i class="fas fa-font text-xs"></i>
|
807 |
+
</button>
|
808 |
+
<div class="w-px h-6 bg-gray-700 mx-1"></div>
|
809 |
+
<button class="tool-btn w-8 h-8 rounded flex items-center justify-center" data-tool="eyedropper" title="Eyedropper Tool (I)">
|
810 |
+
<i class="fas fa-eye-dropper text-xs"></i>
|
811 |
+
</button>
|
812 |
+
</div>
|
813 |
+
</div>
|
814 |
</div>
|
815 |
|
816 |
<!-- Right Customization Panel -->
|
817 |
<div class="w-48 glass-panel flex flex-col border-l border-gray-800">
|
818 |
<div class="p-2 border-b border-gray-800">
|
819 |
<h2 class="text-xs font-semibold">Properties</h2>
|
820 |
+
<p class="tiny-text text-gray-400" id="selectedElementName">No element selected</p>
|
821 |
</div>
|
822 |
|
823 |
<div class="flex-1 overflow-y-auto p-2">
|
|
|
872 |
</div>
|
873 |
</div>
|
874 |
|
875 |
+
<div class="mb-2" id="textCustomization">
|
876 |
<h3 class="font-medium tiny-text mb-1">Text</h3>
|
877 |
<div class="space-y-2">
|
878 |
<div>
|
879 |
<label class="tiny-text text-gray-400">Content</label>
|
880 |
+
<input type="text" value="Primary Button" class="w-full mt-1 tiny-input rounded-md glow-input" id="textContent">
|
881 |
</div>
|
882 |
|
883 |
<div>
|