| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Drag & Drop Website Builder</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .element-item { |
| transition: all 0.2s ease; |
| cursor: grab; |
| user-select: none; |
| } |
| .draggable-element { |
| position: absolute; |
| z-index: 1; |
| user-select: none; |
| } |
| .element-item:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| } |
| .element-item:active { |
| cursor: grabbing; |
| } |
| .canvas-placeholder { |
| transition: all 0.3s ease; |
| } |
| .canvas-placeholder.highlight { |
| background-color: rgba(59, 130, 246, 0.2); |
| box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.8); |
| } |
| .resize-handle { |
| position: absolute; |
| width: 10px; |
| height: 10px; |
| background-color: #3b82f6; |
| border-radius: 50%; |
| z-index: 10; |
| opacity: 0; |
| transition: opacity 0.2s; |
| } |
| .resize-handle.top-left { |
| top: -5px; |
| left: -5px; |
| cursor: nwse-resize; |
| } |
| .resize-handle.top-right { |
| top: -5px; |
| right: -5px; |
| cursor: nesw-resize; |
| } |
| .resize-handle.bottom-left { |
| bottom: -5px; |
| left: -5px; |
| cursor: nesw-resize; |
| } |
| .resize-handle.bottom-right { |
| bottom: -5px; |
| right: -5px; |
| cursor: nwse-resize; |
| } |
| .draggable-element:hover .resize-handle { |
| opacity: 1; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 font-sans"> |
| <div class="flex flex-col h-screen"> |
| |
| <header class="bg-blue-600 text-white p-4 shadow-md"> |
| <div class="container mx-auto"> |
| <div class="flex justify-between items-center mb-2"> |
| <div class="flex items-center space-x-2"> |
| <i class="fas fa-globe text-2xl"></i> |
| <h1 class="text-xl font-bold">Drag & Drop Website Builder</h1> |
| </div> |
| <button id="export-btn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center space-x-2 transition-colors"> |
| <i class="fas fa-share-alt"></i> |
| <span>Export & Share</span> |
| </button> |
| </div> |
| <div class="flex space-x-3"> |
| <select id="theme-selector" class="px-3 py-1 rounded bg-blue-500 text-white border-white border"> |
| <option value="business">Business</option> |
| <option value="portfolio">Portfolio</option> |
| <option value="blog">Blog</option> |
| <option value="education">Education</option> |
| <option value="personal">Personal</option> |
| </select> |
| <button id="apply-theme" class="bg-blue-700 hover:bg-blue-800 text-white px-3 py-1 rounded text-sm"> |
| Apply Theme |
| </button> |
| </div> |
| </div> |
| <button id="export-btn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center space-x-2 transition-colors"> |
| <i class="fas fa-share-alt"></i> |
| <span>Export & Share</span> |
| </button> |
| </div> |
| </header> |
|
|
| |
| <div class="flex flex-1 overflow-hidden"> |
| |
| <div class="w-64 bg-white shadow-md p-4 overflow-y-auto"> |
| <h2 class="text-lg font-semibold mb-4 text-gray-700 border-b pb-2">Elements</h2> |
| <div class="grid grid-cols-3 gap-3"> |
| |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="text"> |
| <i class="fas fa-text-height text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Text</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="image"> |
| <i class="fas fa-image text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Image</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="button"> |
| <i class="fas fa-mouse-pointer text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Button</span> |
| </div> |
| |
| |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="gallery"> |
| <i class="fas fa-images text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Gallery</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="slideshow"> |
| <i class="fas fa-photo-video text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Slideshow</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="video"> |
| <i class="fas fa-video text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">HD Video</span> |
| </div> |
| |
| |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="divider"> |
| <i class="fas fa-minus text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Divider</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="spacer"> |
| <i class="fas fa-arrows-alt-v text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Spacer</span> |
| </div> |
| |
| |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="contact_form"> |
| <i class="fas fa-envelope text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Contact Form</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="newsletter"> |
| <i class="fas fa-newspaper text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Newsletter</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="file_upload"> |
| <i class="fas fa-file-upload text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">File Upload</span> |
| </div> |
| |
| |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="map"> |
| <i class="fas fa-map-marked-alt text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Map</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="social"> |
| <i class="fas fa-share-alt text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Social Icons</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="quote"> |
| <i class="fas fa-quote-right text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Block Quote</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="search"> |
| <i class="fas fa-search text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Search Box</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="embed"> |
| <i class="fas fa-code text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Embed Code</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="pdf"> |
| <i class="fas fa-file-pdf text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">PDF Document</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="products"> |
| <i class="fas fa-shopping-bag text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Products</span> |
| </div> |
| <div class="element-item bg-gray-50 p-3 rounded border border-gray-200 flex flex-col items-center justify-center text-center" draggable="true" data-type="categories"> |
| <i class="fas fa-tags text-blue-500 text-xl mb-2"></i> |
| <span class="text-sm text-gray-700">Categories</span> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="flex-1 bg-gray-100 p-6 overflow-auto flex items-start justify-center"> |
| <div id="canvas" class="bg-white shadow-lg w-full max-w-4xl min-h-[80vh] relative canvas-placeholder"> |
| <div class="absolute inset-0 flex items-center justify-center text-gray-400" id="empty-canvas-msg"> |
| <div class="text-center"> |
| <i class="fas fa-arrow-left text-3xl mb-2"></i> |
| <p>Drag elements here to build your page</p> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="w-64 bg-white shadow-md p-4 overflow-y-auto"> |
| <h2 class="text-lg font-semibold mb-4 text-gray-700 border-b pb-2">Properties</h2> |
| <div id="properties-panel" class="text-gray-600"> |
| <div class="text-center py-12 text-gray-400" id="no-selection-msg"> |
| <i class="fas fa-hand-pointer text-3xl mb-2"></i> |
| <p>Select an element to edit properties</p> |
| </div> |
| <div id="element-properties" class="hidden space-y-4"> |
| <div class="space-y-2"> |
| <label class="block text-sm font-medium">Element Type</label> |
| <input type="text" id="prop-type" class="w-full px-3 py-2 border rounded bg-gray-50" readonly> |
| </div> |
| |
| |
| <div class="space-y-2"> |
| <label class="block text-sm font-medium">Position & Size</label> |
| <div class="grid grid-cols-2 gap-2"> |
| <div> |
| <label class="text-xs block">X</label> |
| <input type="number" id="prop-x" class="w-full px-2 py-1 border rounded"> |
| </div> |
| <div> |
| <label class="text-xs block">Y</label> |
| <input type="number" id="prop-y" class="w-full px-2 py-1 border rounded"> |
| </div> |
| <div> |
| <label class="text-xs block">Width</label> |
| <input type="number" id="prop-width" class="w-full px-2 py-1 border rounded"> |
| </div> |
| <div> |
| <label class="text-xs block">Height</label> |
| <input type="number" id="prop-height" class="w-full px-2 py-1 border rounded"> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div id="content-properties" class="space-y-2 hidden"> |
| <label class="block text-sm font-medium">Content</label> |
| <textarea id="prop-content" class="w-full px-3 py-2 border rounded" rows="3"></textarea> |
| </div> |
| |
| |
| <div class="space-y-2"> |
| <label class="block text-sm font-medium">Style</label> |
| <div> |
| <label class="text-xs block">Background Color</label> |
| <input type="color" id="prop-bgcolor" class="w-full h-8"> |
| </div> |
| <div> |
| <label class="text-xs block">Text Color</label> |
| <input type="color" id="prop-color" class="w-full h-8"> |
| </div> |
| <div> |
| <label class="text-xs block">Font Size</label> |
| <select id="prop-fontsize" class="w-full px-2 py-1 border rounded"> |
| <option value="12px">Small</option> |
| <option value="16px">Medium</option> |
| <option value="20px">Large</option> |
| <option value="24px">Extra Large</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="pt-4"> |
| <button id="delete-element" class="w-full bg-red-500 text-white py-2 rounded hover:bg-red-600 transition-colors"> |
| <i class="fas fa-trash mr-2"></i>Delete Element |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script> |
| |
| <div id="export-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg shadow-xl w-full max-w-xl"> |
| <div class="p-6"> |
| <div class="flex justify-between items-center mb-4"> |
| <h3 class="text-xl font-semibold">Export Your Website</h3> |
| <button id="close-export-modal" class="text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| <div class="mb-4"> |
| <p class="text-gray-600 mb-4">Here's your website design. You can save it as an image and share it.</p> |
| <div id="export-preview" class="border border-gray-300 p-4 bg-white"></div> |
| </div> |
| <div class="flex justify-end space-x-3"> |
| <button id="download-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded flex items-center space-x-2"> |
| <i class="fas fa-download"></i> |
| <span>Download</span> |
| </button> |
| <button id="whatsapp-share" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded flex items-center space-x-2"> |
| <i class="fab fa-whatsapp"></i> |
| <span>Share via WhatsApp</span> |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| // Element templates |
| const elementTemplates = { |
| text: { |
| html: `<div class="draggable-element bg-white p-4 rounded border border-blue-300 cursor-move relative" data-type="text"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <p class="text-gray-700">Double click to edit this text</p> |
| </div>`, |
| width: 200, |
| height: 100 |
| }, |
| image: { |
| html: `<div class="draggable-element bg-gray-100 rounded border border-blue-300 cursor-move relative flex items-center justify-center overflow-hidden" data-type="image"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <img src="https://via.placeholder.com/200x150" alt="Placeholder" class="w-full h-full object-cover"> |
| </div>`, |
| width: 200, |
| height: 150 |
| }, |
| button: { |
| html: `<div class="draggable-element bg-white rounded cursor-move relative" data-type="button"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded transition-colors" style="min-width: 120px;">Click Me</button> |
| </div>`, |
| width: 120, |
| height: 40 |
| }, |
| video: { |
| html: `<div class="draggable-element bg-gray-900 rounded border border-blue-300 cursor-move relative flex items-center justify-center" data-type="video"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <i class="fas fa-play text-white text-3xl"></i> |
| </div>`, |
| width: 300, |
| height: 200 |
| }, |
| divider: { |
| html: `<div class="draggable-element cursor-move relative" data-type="divider"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <hr class="border-t-2 border-gray-300"> |
| </div>`, |
| width: 300, |
| height: 10 |
| }, |
| contact_form: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-4 space-y-3" data-type="contact_form" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <h3 class="font-medium text-lg">Contact Us</h3> |
| <input type="text" placeholder="Name" class="w-full px-3 py-2 border rounded"> |
| <input type="email" placeholder="Email" class="w-full px-3 py-2 border rounded"> |
| <textarea placeholder="Message" class="w-full px-3 py-2 border rounded"></textarea> |
| <button class="bg-blue-500 hover:bg-blue-600 text-white w-full py-2 rounded transition-colors"> |
| Send Message |
| </button> |
| </div>`, |
| width: 300, |
| height: 250 |
| }, |
| newsletter: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-4" data-type="newsletter" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <h3 class="font-medium text-lg mb-2">Subscribe</h3> |
| <p class="text-gray-600 text-sm mb-3">Get the latest updates</p> |
| <input type="email" placeholder="Your email" class="w-full px-3 py-2 border rounded mb-2"> |
| <button class="bg-blue-500 hover:bg-blue-600 text-white w-full py-2 rounded transition-colors"> |
| Subscribe |
| </button> |
| </div>`, |
| width: 300, |
| height: 180 |
| }, |
| gallery: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-2" data-type="gallery" style="width: 350px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="grid grid-cols-2 gap-2"> |
| <img src="https://via.placeholder.com/160x160" class="w-full h-auto rounded"> |
| <img src="https://via.placeholder.com/160x160" class="w-full h-auto rounded"> |
| <img src="https://via.placeholder.com/160x160" class="w-full h-auto rounded"> |
| <img src="https://via.placeholder.com/160x160" class="w-full h-auto rounded"> |
| </div> |
| </div>`, |
| width: 350, |
| height: 350 |
| }, |
| slideshow: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative overflow-hidden" data-type="slideshow" style="width: 400px; height: 250px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="relative w-full h-full"> |
| <img src="https://via.placeholder.com/400x250" class="absolute inset-0 w-full h-full object-cover"> |
| <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4"> |
| <h3 class="text-white font-medium text-lg">Slide Title</h3> |
| </div> |
| <div class="absolute bottom-0 right-0 p-2"> |
| <button class="rounded-full bg-black/50 text-white p-1 mx-1"><i class="fas fa-circle text-xs"></i></button> |
| <button class="rounded-full bg-black/50 text-white p-1 mx-1"><i class="far fa-circle text-xs"></i></button> |
| <button class="rounded-full bg-black/50 text-white p-1 mx-1"><i class="far fa-circle text-xs"></i></button> |
| </div> |
| </div> |
| </div>`, |
| width: 400, |
| height: 250 |
| }, |
| map: { |
| html: `<div class="draggable-element bg-gray-200 rounded border border-blue-300 cursor-move relative" data-type="map" style="width: 350px; height: 250px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="absolute inset-0 flex items-center justify-center text-blue-500"> |
| <i class="fas fa-map-marked-alt text-4xl"></i> |
| </div> |
| <p class="absolute bottom-0 left-0 right-0 bg-white/80 text-center py-1 text-sm">Google Maps will appear here</p> |
| </div>`, |
| width: 350, |
| height: 250 |
| }, |
| social: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-4" data-type="social" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="flex justify-center space-x-4"> |
| <a href="#" class="text-xl text-blue-500 hover:text-blue-700"><i class="fab fa-facebook"></i></a> |
| <a href="#" class="text-xl text-blue-400 hover:text-blue-600"><i class="fab fa-twitter"></i></a> |
| <a href="#" class="text-xl text-pink-500 hover:text-pink-700"><i class="fab fa-instagram"></i></a> |
| <a href="#" class="text-xl text-red-500 hover:text-red-700"><i class="fab fa-youtube"></i></a> |
| </div> |
| </div>`, |
| width: 300, |
| height: 80 |
| }, |
| quote: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-4" data-type="quote" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <blockquote class="text-gray-700 italic"> |
| <p class="mb-2">"The only way to do great work is to love what you do."</p> |
| <footer class="text-right text-sm">— Steve Jobs</footer> |
| </blockquote> |
| </div>`, |
| width: 300, |
| height: 120 |
| }, |
| search: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-3" data-type="search" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="relative"> |
| <input type="text" placeholder="Search..." class="w-full px-4 py-2 border rounded-full"> |
| <button class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500"> |
| <i class="fas fa-search"></i> |
| </button> |
| </div> |
| </div>`, |
| width: 300, |
| height: 80 |
| }, |
| embed: { |
| html: `<div class="draggable-element bg-gray-100 rounded border border-blue-300 cursor-move relative p-3" data-type="embed" style="width: 350px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="bg-white p-2 rounded border"> |
| <p class="text-sm text-center text-gray-500">Paste your embed code here</p> |
| </div> |
| </div>`, |
| width: 350, |
| height: 100 |
| }, |
| spacer: { |
| html: `<div class="draggable-element cursor-move relative" data-type="spacer" style="height: 30px; width: 100%;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <hr class="border-t border-dashed border-gray-400"> |
| </div>`, |
| width: 300, |
| height: 30 |
| }, |
| pdf: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-4" data-type="pdf" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <div class="flex items-center"> |
| <div class="bg-red-100 p-3 rounded-full mr-3"> |
| <i class="fas fa-file-pdf text-red-500 text-xl"></i> |
| </div> |
| <div> |
| <p class="font-medium">Document.pdf</p> |
| <p class="text-sm text-gray-500">2.4 MB</p> |
| <button class="mt-1 text-sm text-blue-500 hover:text-blue-700">Download</button> |
| </div> |
| </div> |
| </div>`, |
| width: 300, |
| height: 120 |
| }, |
| products: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-3" data-type="products" style="width: 350px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <h3 class="font-medium mb-3">Featured Products</h3> |
| <div class="grid grid-cols-2 gap-3"> |
| <div class="border rounded p-2"> |
| <img src="https://via.placeholder.com/120x80" class="w-full h-auto mb-1"> |
| <p class="text-sm font-medium">Product 1</p> |
| <p class="text-sm text-green-600">$19.99</p> |
| </div> |
| <div class="border rounded p-2"> |
| <img src="https://via.placeholder.com/120x80" class="w-full h-auto mb-1"> |
| <p class="text-sm font-medium">Product 2</p> |
| <p class="text-sm text-green-600">$24.99</p> |
| </div> |
| </div> |
| </div>`, |
| width: 350, |
| height: 200 |
| }, |
| categories: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-3" data-type="categories" style="width: 200px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <h3 class="font-medium mb-2">Categories</h3> |
| <ul class="space-y-1"> |
| <li class="hover:bg-gray-100 px-2 py-1 rounded"><a href="#" class="text-blue-600">All Items</a></li> |
| <li class="hover:bg-gray-100 px-2 py-1 rounded"><a href="#" class="text-blue-600">Featured</a></li> |
| <li class="hover:bg-gray-100 px-2 py-1 rounded"><a href="#" class="text-blue-600">Popular</a></li> |
| <li class="hover:bg-gray-100 px-2 py-1 rounded"><a href="#" class="text-blue-600">New Arrivals</a></li> |
| </ul> |
| </div>`, |
| width: 200, |
| height: 200 |
| }, |
| file_upload: { |
| html: `<div class="draggable-element bg-white rounded border border-blue-300 cursor-move relative p-3" data-type="file_upload" style="width: 300px;"> |
| <div class="resize-handle top-left"></div> |
| <div class="resize-handle top-right"></div> |
| <div class="resize-handle bottom-left"></div> |
| <div class="resize-handle bottom-right"></div> |
| <label class="block text-sm font-medium mb-2">Upload File</label> |
| <div class="border-2 border-dashed rounded p-4 text-center"> |
| <i class="fas fa-cloud-upload-alt text-gray-400 text-2xl mb-2"></i> |
| <p class="text-sm text-gray-500 mb-2">Drag & drop files here</p> |
| <input type="file" class="hidden"> |
| <button class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm">Select Files</button> |
| </div> |
| </div>`, |
| width: 300, |
| height: 180 |
| } |
| }; |
| |
| const canvas = document.getElementById('canvas'); |
| const emptyCanvasMsg = document.getElementById('empty-canvas-msg'); |
| const noSelectionMsg = document.getElementById('no-selection-msg'); |
| const elementProperties = document.getElementById('element-properties'); |
| const contentProperties = document.getElementById('content-properties'); |
| let selectedElement = null; |
| let isDragging = false; |
| let isResizing = false; |
| let resizeDirection = ''; |
| let offsetX, offsetY; |
| let startX, startY; |
| let currentElement = null; |
| |
| // Drag and drop functionality for element items |
| document.querySelectorAll('.element-item').forEach(item => { |
| item.addEventListener('dragstart', function(e) { |
| e.dataTransfer.setData('text/plain', this.dataset.type); |
| }); |
| }); |
| |
| // Canvas drop zone |
| canvas.addEventListener('dragover', function(e) { |
| e.preventDefault(); |
| this.classList.add('highlight'); |
| }); |
| |
| canvas.addEventListener('dragleave', function() { |
| this.classList.remove('highlight'); |
| }); |
| |
| canvas.addEventListener('drop', function(e) { |
| e.preventDefault(); |
| this.classList.remove('highlight'); |
| |
| const type = e.dataTransfer.getData('text/plain'); |
| if (!type) return; |
| |
| addElementToCanvas(type, e.offsetX, e.offsetY); |
| }); |
| |
| function addElementToCanvas(type, x, y) { |
| emptyCanvasMsg.style.display = 'none'; |
| |
| const template = elementTemplates[type]; |
| const tempDiv = document.createElement('div'); |
| tempDiv.innerHTML = template.html; |
| const element = tempDiv.firstChild; |
| |
| element.style.position = 'absolute'; |
| element.style.left = `${x - (template.width / 2)}px`; |
| element.style.top = `${y - (template.height / 2)}px`; |
| element.style.width = `${template.width}px`; |
| element.style.height = `${template.height}px`; |
| |
| // Make element draggable |
| element.addEventListener('mousedown', startDrag); |
| |
| // Make resize handles functional |
| const handles = element.querySelectorAll('.resize-handle'); |
| handles.forEach(handle => { |
| handle.addEventListener('mousedown', function(e) { |
| e.stopPropagation(); |
| startResize(this); |
| }); |
| }); |
| |
| // Double click to edit content |
| if (type === 'text') { |
| element.addEventListener('dblclick', function(e) { |
| const textElement = this.querySelector('p'); |
| if (textElement.isContentEditable) { |
| textElement.contentEditable = false; |
| textElement.blur(); |
| } else { |
| textElement.contentEditable = true; |
| textElement.focus(); |
| } |
| }); |
| } |
| |
| // Select element on click |
| element.addEventListener('click', function(e) { |
| e.stopPropagation(); |
| selectElement(this); |
| }); |
| |
| canvas.appendChild(element); |
| |
| // Select the new element |
| selectElement(element); |
| } |
| |
| // Element selection and properties panel |
| function selectElement(element) { |
| // Deselect previous element |
| if (selectedElement) { |
| selectedElement.style.boxShadow = 'none'; |
| selectedElement.style.borderColor = '#93c5fd'; |
| } |
| |
| // Select new element |
| selectedElement = element; |
| selectedElement.style.boxShadow = '0 0 0 2px #3b82f6'; |
| selectedElement.style.borderColor = '#3b82f6'; |
| |
| // Update properties panel |
| noSelectionMsg.classList.add('hidden'); |
| elementProperties.classList.remove('hidden'); |
| |
| // Set type |
| document.getElementById('prop-type').value = element.dataset.type; |
| |
| // Set position and size |
| document.getElementById('prop-x').value = parseInt(element.style.left); |
| document.getElementById('prop-y').value = parseInt(element.style.top); |
| document.getElementById('prop-width').value = parseInt(element.style.width); |
| document.getElementById('prop-height').value = parseInt(element.style.height); |
| |
| // Toggle content properties based on element type |
| if (element.dataset.type === 'text' || element.dataset.type === 'button') { |
| contentProperties.classList.remove('hidden'); |
| if (element.dataset.type === 'text') { |
| document.getElementById('prop-content').value = element.querySelector('p').textContent; |
| } else { |
| document.getElementById('prop-content').value = element.querySelector('button').textContent; |
| } |
| } else { |
| contentProperties.classList.add('hidden'); |
| } |
| |
| // Set style properties |
| if (element.dataset.type === 'text') { |
| const textElement = element.querySelector('p'); |
| document.getElementById('prop-color').value = rgb2hex(textElement.style.color || '#374151'); |
| document.getElementById('prop-fontsize').value = textElement.style.fontSize || '16px'; |
| document.getElementById('prop-bgcolor').value = rgb2hex(element.style.backgroundColor || '#ffffff'); |
| } else if (element.dataset.type === 'button') { |
| const buttonElement = element.querySelector('button'); |
| document.getElementById('prop-color').value = rgb2hex(buttonElement.style.color || '#ffffff'); |
| document.getElementById('prop-fontsize').value = buttonElement.style.fontSize || '16px'; |
| document.getElementById('prop-bgcolor').value = rgb2hex(buttonElement.style.backgroundColor || '#3b82f6'); |
| } else { |
| document.getElementById('prop-bgcolor').value = rgb2hex(element.style.backgroundColor || (element.dataset.type === 'video' ? '#111827' : '#ffffff')); |
| } |
| } |
| |
| // Deselect element when clicking on empty canvas |
| canvas.addEventListener('click', function(e) { |
| if (e.target === canvas) { |
| deselectElement(); |
| } |
| }); |
| |
| function deselectElement() { |
| if (selectedElement) { |
| selectedElement.style.boxShadow = 'none'; |
| selectedElement.style.borderColor = '#93c5fd'; |
| selectedElement = null; |
| } |
| noSelectionMsg.classList.remove('hidden'); |
| elementProperties.classList.add('hidden'); |
| } |
| |
| // Drag functionality |
| function startDrag(e) { |
| if (e.target.classList.contains('resize-handle')) return; |
| |
| isDragging = true; |
| currentElement = this; |
| |
| // Calculate offset from element's top-left corner |
| const rect = this.getBoundingClientRect(); |
| offsetX = e.clientX - rect.left; |
| offsetY = e.clientY - rect.top; |
| |
| // Store element's initial position |
| startX = parseFloat(this.style.left) || 0; |
| startY = parseFloat(this.style.top) || 0; |
| |
| // Apply higher z-index while dragging |
| this.style.zIndex = '1000'; |
| |
| document.addEventListener('mousemove', drag); |
| document.addEventListener('mouseup', stopDrag); |
| |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| function drag(e) { |
| if (!isDragging) return; |
| |
| // Calculate new position |
| const x = e.clientX - canvas.getBoundingClientRect().left - offsetX; |
| const y = e.clientY - canvas.getBoundingClientRect().top - offsetY; |
| |
| // Update element position |
| currentElement.style.left = `${x}px`; |
| currentElement.style.top = `${y}px`; |
| |
| // Update properties panel if this element is selected |
| if (selectedElement === currentElement) { |
| document.getElementById('prop-x').value = x; |
| document.getElementById('prop-y').value = y; |
| } |
| } |
| |
| function stopDrag() { |
| isDragging = false; |
| if (currentElement) { |
| currentElement.style.zIndex = ''; |
| } |
| document.removeEventListener('mousemove', drag); |
| document.removeEventListener('mouseup', stopDrag); |
| } |
| |
| // Resize functionality |
| function startResize(handle) { |
| isResizing = true; |
| currentElement = handle.parentElement; |
| |
| // Store element's initial dimensions and position |
| startX = parseFloat(currentElement.style.left) || 0; |
| startY = parseFloat(currentElement.style.top) || 0; |
| const startWidth = parseFloat(currentElement.style.width) || currentElement.offsetWidth; |
| const startHeight = parseFloat(currentElement.style.height) || currentElement.offsetHeight; |
| |
| // Determine resize direction based on which handle was clicked |
| if (handle.classList.contains('top-left')) { |
| resizeDirection = 'top-left'; |
| } else if (handle.classList.contains('top-right')) { |
| resizeDirection = 'top-right'; |
| } else if (handle.classList.contains('bottom-left')) { |
| resizeDirection = 'bottom-left'; |
| } else if (handle.classList.contains('bottom-right')) { |
| resizeDirection = 'bottom-right'; |
| } |
| |
| document.addEventListener('mousemove', resize); |
| document.addEventListener('mouseup', stopResize); |
| } |
| |
| function resize(e) { |
| if (!isResizing) return; |
| |
| const rect = canvas.getBoundingClientRect(); |
| const mouseX = e.clientX - rect.left; |
| const mouseY = e.clientY - rect.top; |
| |
| const startWidth = parseFloat(currentElement.style.width) || currentElement.offsetWidth; |
| const startHeight = parseFloat(currentElement.style.height) || currentElement.offsetHeight; |
| const startLeft = parseFloat(currentElement.style.left) || 0; |
| const startTop = parseFloat(currentElement.style.top) || 0; |
| |
| let newWidth = startWidth; |
| let newHeight = startHeight; |
| let newLeft = startLeft; |
| let newTop = startTop; |
| |
| switch (resizeDirection) { |
| case 'top-left': |
| newWidth = startWidth - (mouseX - startLeft); |
| newHeight = startHeight - (mouseY - startTop); |
| newLeft = mouseX; |
| newTop = mouseY; |
| break; |
| case 'top-right': |
| newWidth = mouseX - startLeft; |
| newHeight = startHeight - (mouseY - startTop); |
| newTop = mouseY; |
| break; |
| case 'bottom-left': |
| newWidth = startWidth - (mouseX - startLeft); |
| newHeight = mouseY - startTop; |
| newLeft = mouseX; |
| break; |
| case 'bottom-right': |
| newWidth = mouseX - startLeft; |
| newHeight = mouseY - startTop; |
| break; |
| } |
| |
| // Apply minimum dimensions |
| const minSize = 30; |
| if (newWidth < minSize) newWidth = minSize; |
| if (newHeight < minSize) newHeight = minSize; |
| |
| // Update element dimensions and position |
| currentElement.style.width = `${newWidth}px`; |
| currentElement.style.height = `${newHeight}px`; |
| currentElement.style.left = `${newLeft}px`; |
| currentElement.style.top = `${newTop}px`; |
| |
| // Update properties panel if this element is selected |
| if (selectedElement === currentElement) { |
| document.getElementById('prop-x').value = newLeft; |
| document.getElementById('prop-y').value = newTop; |
| document.getElementById('prop-width').value = newWidth; |
| document.getElementById('prop-height').value = newHeight; |
| } |
| } |
| |
| function stopResize() { |
| isResizing = false; |
| document.removeEventListener('mousemove', resize); |
| document.removeEventListener('mouseup', stopResize); |
| } |
| |
| // Property panel updates |
| document.getElementById('prop-x').addEventListener('input', function() { |
| if (selectedElement) { |
| selectedElement.style.left = `${this.value}px`; |
| } |
| }); |
| |
| document.getElementById('prop-y').addEventListener('input', function() { |
| if (selectedElement) { |
| selectedElement.style.top = `${this.value}px`; |
| } |
| }); |
| |
| document.getElementById('prop-width').addEventListener('input', function() { |
| if (selectedElement) { |
| selectedElement.style.width = `${this.value}px`; |
| } |
| }); |
| |
| document.getElementById('prop-height').addEventListener('input', function() { |
| if (selectedElement) { |
| selectedElement.style.height = `${this.value}px`; |
| } |
| }); |
| |
| document.getElementById('prop-content').addEventListener('input', function() { |
| if (selectedElement) { |
| if (selectedElement.dataset.type === 'text') { |
| selectedElement.querySelector('p').textContent = this.value; |
| } else if (selectedElement.dataset.type === 'button') { |
| selectedElement.querySelector('button').textContent = this.value; |
| } |
| } |
| }); |
| |
| document.getElementById('prop-bgcolor').addEventListener('input', function() { |
| if (selectedElement) { |
| if (selectedElement.dataset.type === 'text') { |
| selectedElement.style.backgroundColor = this.value; |
| } else if (selectedElement.dataset.type === 'button') { |
| selectedElement.querySelector('button').style.backgroundColor = this.value; |
| } else { |
| selectedElement.style.backgroundColor = this.value; |
| } |
| } |
| }); |
| |
| document.getElementById('prop-color').addEventListener('input', function() { |
| if (selectedElement) { |
| if (selectedElement.dataset.type === 'text') { |
| selectedElement.querySelector('p').style.color = this.value; |
| } else if (selectedElement.dataset.type === 'button') { |
| selectedElement.querySelector('button').style.color = this.value; |
| } |
| } |
| }); |
| |
| document.getElementById('prop-fontsize').addEventListener('change', function() { |
| if (selectedElement) { |
| if (selectedElement.dataset.type === 'text') { |
| selectedElement.querySelector('p').style.fontSize = this.value; |
| } else if (selectedElement.dataset.type === 'button') { |
| selectedElement.querySelector('button').style.fontSize = this.value; |
| } |
| } |
| }); |
| |
| document.getElementById('delete-element').addEventListener('click', function() { |
| if (selectedElement) { |
| selectedElement.remove(); |
| deselectElement(); |
| |
| // Show empty canvas message if no elements left |
| if (canvas.children.length === 1) { // Only the empty message div remains |
| emptyCanvasMsg.style.display = 'flex'; |
| } |
| } |
| }); |
| |
| // Export functionality |
| const exportBtn = document.getElementById('export-btn'); |
| const exportModal = document.getElementById('export-modal'); |
| const closeExportModal = document.getElementById('close-export-modal'); |
| const downloadBtn = document.getElementById('download-btn'); |
| const whatsappShare = document.getElementById('whatsapp-share'); |
| const exportPreview = document.getElementById('export-preview'); |
| |
| exportBtn.addEventListener('click', function() { |
| // Clone the canvas without the empty message |
| const canvasClone = canvas.cloneNode(true); |
| const emptyMsg = canvasClone.querySelector('#empty-canvas-msg'); |
| if (emptyMsg) emptyMsg.remove(); |
| |
| // Reset any active selections |
| const selectedElements = canvasClone.querySelectorAll('.draggable-element'); |
| selectedElements.forEach(el => { |
| el.style.boxShadow = 'none'; |
| el.style.borderColor = '#93c5fd'; |
| }); |
| |
| exportPreview.innerHTML = ''; |
| exportPreview.appendChild(canvasClone); |
| |
| exportModal.classList.remove('hidden'); |
| }); |
| |
| closeExportModal.addEventListener('click', function() { |
| exportModal.classList.add('hidden'); |
| }); |
| |
| downloadBtn.addEventListener('click', function() { |
| // Use html2canvas to convert the preview to an image |
| html2canvas(exportPreview.firstChild).then(canvas => { |
| const link = document.createElement('a'); |
| link.download = 'website-design.png'; |
| link.href = canvas.toDataURL('image/png'); |
| link.click(); |
| }); |
| }); |
| |
| whatsappShare.addEventListener('click', function() { |
| html2canvas(exportPreview.firstChild).then(canvas => { |
| const image = canvas.toDataURL('image/png').replace(/^data:image\/(png|jpg);base64,/, ""); |
| const message = encodeURIComponent('Check out the website I designed!'); |
| const whatsappUrl = `https://api.whatsapp.com/send?text=${message}&image=${image}`; |
| window.open(whatsappUrl, '_blank'); |
| }); |
| }); |
| |
| // Helper functions |
| function rgb2hex(rgb) { |
| if (rgb.startsWith('#')) return rgb; |
| |
| const rgbValues = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*\d+\.?\d*)?\)$/); |
| if (!rgbValues) return '#000000'; |
| |
| function hex(x) { |
| return ("0" + parseInt(x).toString(16)).slice(-2); |
| } |
| |
| return "#" + hex(rgbValues[1]) + hex(rgbValues[2]) + hex(rgbValues[3]); |
| } |
| }); |
| |
| // Theme functionality |
| document.getElementById('apply-theme').addEventListener('click', function() { |
| const theme = document.getElementById('theme-selector').value; |
| let bgColor = ''; |
| let textColor = ''; |
| let primaryColor = ''; |
| |
| switch(theme) { |
| case 'business': |
| bgColor = 'gray-50'; |
| textColor = 'gray-800'; |
| primaryColor = 'blue'; |
| break; |
| case 'portfolio': |
| bgColor = 'gray-100'; |
| textColor = 'gray-900'; |
| primaryColor = 'indigo'; |
| break; |
| case 'blog': |
| bgColor = 'white'; |
| textColor = 'gray-700'; |
| primaryColor = 'green'; |
| break; |
| case 'education': |
| bgColor = 'blue-50'; |
| textColor = 'blue-900'; |
| primaryColor = 'blue'; |
| break; |
| case 'personal': |
| bgColor = 'pink-50'; |
| textColor = 'gray-800'; |
| primaryColor = 'pink'; |
| break; |
| default: |
| bgColor = 'white'; |
| textColor = 'gray-800'; |
| primaryColor = 'blue'; |
| } |
| |
| const canvas = document.getElementById('canvas'); |
| canvas.className = `bg-${bgColor} text-${textColor} shadow-lg w-full max-w-4xl min-h-[80vh] relative canvas-placeholder`; |
| canvas.style.backgroundColor = ''; // Clear inline style |
| |
| // Update all existing elements with the new color scheme |
| document.querySelectorAll('.draggable-element').forEach(el => { |
| if(el.querySelector('button')) { |
| const btn = el.querySelector('button'); |
| btn.className = btn.className.replace(/bg-\w+-500/g, `bg-${primaryColor}-500`) |
| .replace(/hover:bg-\w+-600/g, `hover:bg-${primaryColor}-600`); |
| } else if (el.dataset.type === 'text' || el.dataset.type === 'social' || el.dataset.type === 'quote') { |
| el.style.backgroundColor = ``; |
| el.classList.add(`bg-${bgColor}`, `text-${textColor}`); |
| } |
| }); |
| }); |
| |
| // For the export functionality, we need html2canvas |
| document.write('<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"><\/script>'); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Vishau/webbuilder1-0" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |