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>
|