form-builder / index.html
engmsilva's picture
Add 1 files
c971f4d verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Site Builder Pro</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>
.builder-container {
display: grid;
grid-template-columns: 250px 1fr 300px;
gap: 1rem;
height: calc(100vh - 80px);
}
.component-list {
background-color: #f8fafc;
border-right: 1px solid #e2e8f0;
overflow-y: auto;
}
.site-canvas {
background-color: #ffffff;
overflow-y: auto;
padding: 1rem;
min-height: 100%;
position: relative;
}
.component-settings {
background-color: #f8fafc;
border-left: 1px solid #e2e8f0;
overflow-y: auto;
}
.draggable-component {
cursor: grab;
transition: all 0.2s;
}
.draggable-component:active {
cursor: grabbing;
}
.site-component {
position: relative;
transition: all 0.2s;
}
.site-component:hover {
box-shadow: 0 0 0 2px #3b82f6;
}
.site-component.selected {
box-shadow: 0 0 0 2px #3b82f6;
background-color: #eff6ff;
}
.component-actions {
position: absolute;
right: 0;
top: 0;
display: none;
}
.site-component:hover .component-actions {
display: flex;
}
.site-component.selected .component-actions {
display: flex;
}
.empty-state {
border: 2px dashed #cbd5e1;
border-radius: 0.5rem;
padding: 2rem;
text-align: center;
color: #64748b;
}
#sitePreviewModal {
transition: opacity 0.3s ease;
}
.site-preview {
width: 100%;
margin: 0 auto;
background: white;
border-radius: 0.5rem;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
.layout-container {
border: 2px dashed #cbd5e1;
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 1rem;
background-color: #f8fafc;
}
.layout-row {
border: 2px dashed #a5b4fc;
border-radius: 0.5rem;
padding: 0.75rem;
margin-bottom: 0.5rem;
background-color: #eef2ff;
}
.layout-column {
border: 2px dashed #93c5fd;
border-radius: 0.5rem;
padding: 0.5rem;
margin-bottom: 0.25rem;
background-color: #eff6ff;
}
.layout-placeholder {
border: 2px dashed #d1d5db;
border-radius: 0.25rem;
padding: 1rem;
text-align: center;
color: #6b7280;
background-color: #f9fafb;
margin-bottom: 0.5rem;
}
.layout-actions {
position: absolute;
right: 0;
top: 0;
display: none;
}
.layout-container:hover .layout-actions,
.layout-row:hover .layout-actions,
.layout-column:hover .layout-actions {
display: flex;
}
.grid-resize-handle {
position: absolute;
right: 5px;
bottom: 5px;
width: 12px;
height: 12px;
cursor: nwse-resize;
opacity: 0;
transition: opacity 0.2s;
}
.layout-column:hover .grid-resize-handle {
opacity: 1;
}
.drop-zone {
min-height: 50px;
transition: background-color 0.2s;
}
.drop-zone.active {
background-color: #dbeafe;
}
.navbar-preview {
background-color: #ffffff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.hero-preview {
background: linear-gradient(135deg, #6b7280, #1e40af);
color: white;
}
.section-preview {
background-color: #f9fafb;
}
.footer-preview {
background-color: #1f2937;
color: white;
}
.template-card {
transition: all 0.2s;
}
.template-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.color-option {
width: 24px;
height: 24px;
border-radius: 50%;
cursor: pointer;
border: 2px solid transparent;
}
.color-option.selected {
border-color: #1e40af;
}
</style>
</head>
<body class="bg-gray-50">
<header class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8 flex justify-between items-center">
<h1 class="text-2xl font-bold text-gray-900">Site Builder Pro</h1>
<div class="flex space-x-3">
<button id="templatesBtn" class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<i class="fas fa-th-large mr-2"></i> Templates
</button>
<button id="previewBtn" class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<i class="fas fa-eye mr-2"></i> Preview
</button>
<button id="publishBtn" class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<i class="fas fa-cloud-upload-alt mr-2"></i> Publicar
</button>
</div>
</div>
</header>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div class="builder-container">
<!-- Component Library -->
<div class="component-list p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-gray-800">Componentes</h2>
<div class="relative">
<button id="toggleComponents" class="p-1 rounded-md hover:bg-gray-200">
<i class="fas fa-chevron-down"></i>
</button>
</div>
</div>
<div id="componentsAccordion" class="space-y-4">
<div class="space-y-2">
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Layout</h3>
<div class="space-y-2">
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="container">
<div class="flex items-center">
<i class="fas fa-square-full text-indigo-500 mr-2"></i>
<span>Container</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="row">
<div class="flex items-center">
<i class="fas fa-grip-lines text-indigo-500 mr-2"></i>
<span>Row</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="column">
<div class="flex items-center">
<i class="fas fa-columns text-indigo-500 mr-2"></i>
<span>Column</span>
</div>
</div>
</div>
</div>
<div class="space-y-2">
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Seções</h3>
<div class="space-y-2">
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="navbar">
<div class="flex items-center">
<i class="fas fa-bars text-blue-500 mr-2"></i>
<span>Navbar</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="hero">
<div class="flex items-center">
<i class="fas fa-star text-blue-500 mr-2"></i>
<span>Hero Section</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="section">
<div class="flex items-center">
<i class="fas fa-square text-blue-500 mr-2"></i>
<span>Content Section</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="footer">
<div class="flex items-center">
<i class="fas fa-window-minimize text-blue-500 mr-2"></i>
<span>Footer</span>
</div>
</div>
</div>
</div>
<div class="space-y-2">
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Elementos</h3>
<div class="space-y-2">
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="heading">
<div class="flex items-center">
<i class="fas fa-heading text-blue-500 mr-2"></i>
<span>Heading</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="paragraph">
<div class="flex items-center">
<i class="fas fa-paragraph text-blue-500 mr-2"></i>
<span>Paragraph</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="button">
<div class="flex items-center">
<i class="fas fa-square text-blue-500 mr-2"></i>
<span>Button</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="image">
<div class="flex items-center">
<i class="fas fa-image text-blue-500 mr-2"></i>
<span>Image</span>
</div>
</div>
</div>
</div>
<div class="space-y-2">
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wider mb-2">Formulários</h3>
<div class="space-y-2">
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="text">
<div class="flex items-center">
<i class="fas fa-font text-blue-500 mr-2"></i>
<span>Text Input</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="email">
<div class="flex items-center">
<i class="fas fa-envelope text-blue-500 mr-2"></i>
<span>Email Input</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="textarea">
<div class="flex items-center">
<i class="fas fa-align-left text-blue-500 mr-2"></i>
<span>Text Area</span>
</div>
</div>
<div draggable="true" class="draggable-component p-3 bg-white rounded border border-gray-200 shadow-sm" data-type="select">
<div class="flex items-center">
<i class="fas fa-list-ul text-blue-500 mr-2"></i>
<span>Dropdown</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Site Canvas -->
<div class="site-canvas" id="siteCanvas">
<div id="siteContainer" class="drop-zone">
<div class="empty-state">
<i class="fas fa-mouse-pointer text-4xl mb-3 text-gray-400"></i>
<h3 class="font-medium text-gray-500">Arraste componentes aqui</h3>
<p class="text-sm text-gray-400 mt-1">Comece construindo seu site arrastando componentes do painel esquerdo</p>
</div>
</div>
</div>
<!-- Component Settings -->
<div class="component-settings p-4">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-gray-800">Configurações</h2>
<div class="flex space-x-2">
<button id="pageSettingsBtn" class="p-1 rounded-md hover:bg-gray-200" title="Configurações da Página">
<i class="fas fa-cog"></i>
</button>
<button id="themeSettingsBtn" class="p-1 rounded-md hover:bg-gray-200" title="Configurações de Tema">
<i class="fas fa-palette"></i>
</button>
</div>
</div>
<div id="componentSettings">
<p class="text-gray-500 italic">Selecione um componente para editar suas propriedades</p>
</div>
<div id="pageSettings" class="hidden">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Título da Página</label>
<input type="text" id="pageTitle" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" value="Meu Site">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Favicon</label>
<div class="mt-1 flex items-center">
<span class="inline-block h-8 w-8 rounded-full overflow-hidden bg-gray-100">
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
</span>
<button type="button" class="ml-3 bg-white py-1 px-2 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Alterar
</button>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Meta Description</label>
<textarea class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" rows="3">Descrição do meu site para motores de busca</textarea>
</div>
</div>
</div>
<div id="themeSettings" class="hidden">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Cores do Tema</label>
<div class="flex space-x-2">
<div class="color-option bg-blue-600 selected" data-color="blue"></div>
<div class="color-option bg-indigo-600" data-color="indigo"></div>
<div class="color-option bg-purple-600" data-color="purple"></div>
<div class="color-option bg-pink-600" data-color="pink"></div>
<div class="color-option bg-red-600" data-color="red"></div>
<div class="color-option bg-green-600" data-color="green"></div>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Fonte Principal</label>
<select class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
<option>Inter (sans-serif)</option>
<option>Roboto (sans-serif)</option>
<option>Lora (serif)</option>
<option>Montserrat (sans-serif)</option>
<option>Open Sans (sans-serif)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Estilo de Botão</label>
<select class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
<option>Padrão</option>
<option>Arredondado</option>
<option>Outline</option>
<option>Minimalista</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Templates Modal -->
<div id="templatesModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Escolha um Template</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 1</span>
</div>
<h4 class="font-medium text-gray-900">Landing Page</h4>
<p class="text-sm text-gray-500">Página inicial simples</p>
</div>
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 2</span>
</div>
<h4 class="font-medium text-gray-900">Portfolio</h4>
<p class="text-sm text-gray-500">Para mostrar seu trabalho</p>
</div>
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 3</span>
</div>
<h4 class="font-medium text-gray-900">Blog</h4>
<p class="text-sm text-gray-500">Para publicar artigos</p>
</div>
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 4</span>
</div>
<h4 class="font-medium text-gray-900">E-commerce</h4>
<p class="text-sm text-gray-500">Loja virtual simples</p>
</div>
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 5</span>
</div>
<h4 class="font-medium text-gray-900">Serviços</h4>
<p class="text-sm text-gray-500">Para empresas de serviços</p>
</div>
<div class="template-card p-4 border border-gray-200 rounded-md cursor-pointer">
<div class="bg-gray-100 h-40 rounded-md mb-2 flex items-center justify-center">
<span class="text-gray-400">Template 6</span>
</div>
<h4 class="font-medium text-gray-900">Em branco</h4>
<p class="text-sm text-gray-500">Comece do zero</p>
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="applyTemplate" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Aplicar Template
</button>
<button type="button" id="closeTemplates" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancelar
</button>
</div>
</div>
</div>
</div>
<!-- Site Preview Modal -->
<div id="sitePreviewModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Visualização do Site</h3>
<div class="site-preview">
<div id="previewSiteContainer" class="w-full"></div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="closePreview" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Fechar
</button>
</div>
</div>
</div>
</div>
<!-- Publish Modal -->
<div id="publishModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<i class="fas fa-cloud-upload-alt text-blue-600"></i>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900">Publicar Site</h3>
<div class="mt-2">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Domínio</label>
<div class="mt-1 flex rounded-md shadow-sm">
<input type="text" class="focus:ring-blue-500 focus:border-blue-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300" placeholder="meusite">
<span class="inline-flex items-center px-3 rounded-r-md border border-l-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">.meudominio.com</span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Plano</label>
<select class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
<option>Básico (R$ 29/mês)</option>
<option>Profissional (R$ 59/mês)</option>
<option>Premium (R$ 99/mês)</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button type="button" id="confirmPublish" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
Publicar Agora
</button>
<button type="button" id="cancelPublish" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancelar
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Site data structure
let siteData = {
title: 'Meu Site',
description: 'Descrição do meu site',
theme: {
color: 'blue',
font: 'Inter',
buttonStyle: 'default'
},
pages: [
{
id: 'page-1',
name: 'Home',
components: []
}
],
currentPage: 'page-1'
};
let selectedComponent = null;
let nextComponentId = 1;
let isResizing = false;
let currentResizeColumn = null;
let startX, startWidth;
let draggedElement = null;
// DOM elements
const draggableComponents = document.querySelectorAll('.draggable-component');
const siteCanvas = document.getElementById('siteCanvas');
const siteContainer = document.getElementById('siteContainer');
const componentSettings = document.getElementById('componentSettings');
const pageSettings = document.getElementById('pageSettings');
const themeSettings = document.getElementById('themeSettings');
const previewSiteContainer = document.getElementById('previewSiteContainer');
const templatesBtn = document.getElementById('templatesBtn');
const previewBtn = document.getElementById('previewBtn');
const publishBtn = document.getElementById('publishBtn');
const pageSettingsBtn = document.getElementById('pageSettingsBtn');
const themeSettingsBtn = document.getElementById('themeSettingsBtn');
const templatesModal = document.getElementById('templatesModal');
const sitePreviewModal = document.getElementById('sitePreviewModal');
const publishModal = document.getElementById('publishModal');
const closeTemplates = document.getElementById('closeTemplates');
const applyTemplate = document.getElementById('applyTemplate');
const closePreview = document.getElementById('closePreview');
const confirmPublish = document.getElementById('confirmPublish');
const cancelPublish = document.getElementById('cancelPublish');
const colorOptions = document.querySelectorAll('.color-option');
const toggleComponents = document.getElementById('toggleComponents');
const componentsAccordion = document.getElementById('componentsAccordion');
// Set up drag events for all draggable components
draggableComponents.forEach(component => {
component.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', component.dataset.type);
draggedElement = component;
e.dataTransfer.effectAllowed = 'copy';
});
});
// Set up drop zones
function setupDropZones() {
// Main container drop zone
siteContainer.addEventListener('dragover', function(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
this.classList.add('active');
});
siteContainer.addEventListener('dragleave', function() {
this.classList.remove('active');
});
siteContainer.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('active');
const componentType = e.dataTransfer.getData('text/plain');
addComponentToSite(componentType, this);
});
// Container drop zones (can accept rows or components)
document.querySelectorAll('.layout-container').forEach(container => {
container.addEventListener('dragover', function(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
this.classList.add('active');
});
container.addEventListener('dragleave', function() {
this.classList.remove('active');
});
container.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('active');
const componentType = e.dataTransfer.getData('text/plain');
// Containers can accept rows or components directly
if (componentType === 'row' || componentType === 'container' ||
['navbar', 'hero', 'section', 'footer', 'heading', 'paragraph',
'button', 'image', 'text', 'email', 'textarea', 'select'].includes(componentType)) {
addComponentToSite(componentType, this);
}
});
});
// Row drop zones (can accept columns)
document.querySelectorAll('.layout-row').forEach(row => {
row.addEventListener('dragover', function(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
this.classList.add('active');
});
row.addEventListener('dragleave', function() {
this.classList.remove('active');
});
row.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('active');
const componentType = e.dataTransfer.getData('text/plain');
// Rows can only accept columns
if (componentType === 'column') {
addComponentToSite(componentType, this);
}
});
});
// Column drop zones (can accept components)
document.querySelectorAll('.layout-column').forEach(column => {
column.addEventListener('dragover', function(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
this.classList.add('active');
});
column.addEventListener('dragleave', function() {
this.classList.remove('active');
});
column.addEventListener('drop', function(e) {
e.preventDefault();
this.classList.remove('active');
const componentType = e.dataTransfer.getData('text/plain');
// Columns can accept components but not layout elements
if (['navbar', 'hero', 'section', 'footer', 'heading', 'paragraph',
'button', 'image', 'text', 'email', 'textarea', 'select'].includes(componentType)) {
addComponentToSite(componentType, this);
}
});
});
}
// Add a new component to the site at a specific drop zone
function addComponentToSite(type, dropZone) {
// Remove empty state if it exists
const emptyState = dropZone.querySelector('.empty-state');
if (emptyState) {
emptyState.remove();
}
const componentId = `component-${nextComponentId++}`;
let componentHtml = '';
let defaultSettings = {};
switch(type) {
case 'container':
componentHtml = `
<div class="site-component layout-container mb-4 relative drop-zone" data-id="${componentId}" data-type="container">
<div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="layout-placeholder">
<i class="fas fa-square-full text-indigo-300 text-xl mb-2"></i>
<p class="text-sm">Drop rows or components here</p>
</div>
</div>
`;
defaultSettings = {
padding: '1rem',
margin: '0 0 1rem 0',
background: '#f8fafc'
};
break;
case 'row':
componentHtml = `
<div class="site-component layout-row mb-2 relative drop-zone" data-id="${componentId}" data-type="row">
<div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="layout-placeholder">
<i class="fas fa-grip-lines text-indigo-300 text-xl mb-2"></i>
<p class="text-sm">Drop columns here</p>
</div>
</div>
`;
defaultSettings = {
columns: 1,
gap: '0.5rem'
};
break;
case 'column':
componentHtml = `
<div class="site-component layout-column relative drop-zone" data-id="${componentId}" data-type="column">
<div class="layout-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="grid-resize-handle">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4 text-gray-400">
<path fill-rule="evenodd" d="M12 1.586l-4 4v3.586l4-4V1.586zm4 4l-4 4h3.586l4-4V5.586zm-4 9.414l4-4v3.586l-4 4v-3.586zm-4-4l4-4H8.414l-4 4v3.586z" clip-rule="evenodd" />
</svg>
</div>
<div class="layout-placeholder">
<i class="fas fa-columns text-indigo-300 text-xl mb-2"></i>
<p class="text-sm">Drop components here</p>
</div>
</div>
`;
defaultSettings = {
width: '100%',
minWidth: '200px'
};
break;
case 'navbar':
componentHtml = `
<div class="site-component mb-4 relative" data-id="${componentId}" data-type="navbar">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="navbar-preview py-4 px-6">
<div class="flex justify-between items-center">
<div class="text-xl font-bold">Logo</div>
<div class="hidden md:flex space-x-6">
<a href="#" class="text-gray-700 hover:text-blue-600">Home</a>
<a href="#" class="text-gray-700 hover:text-blue-600">Sobre</a>
<a href="#" class="text-gray-700 hover:text-blue-600">Serviços</a>
<a href="#" class="text-gray-700 hover:text-blue-600">Contato</a>
</div>
<button class="md:hidden text-gray-700">
<i class="fas fa-bars"></i>
</button>
</div>
</div>
</div>
`;
defaultSettings = {
logoText: 'Logo',
menuItems: ['Home', 'Sobre', 'Serviços', 'Contato'],
backgroundColor: '#ffffff',
textColor: '#374151'
};
break;
case 'hero':
componentHtml = `
<div class="site-component mb-4 relative" data-id="${componentId}" data-type="hero">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-blue-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="hero-preview py-16 px-6 text-center">
<h1 class="text-4xl font-bold mb-4">Título Principal</h1>
<p class="text-xl mb-8 max-w-2xl mx-auto">Uma frase impactante que resume seu negócio ou proposta de valor.</p>
<div class="flex justify-center space-x-4">
<button class="px-6 py-3 bg-white text-blue-600 font-medium rounded-md shadow-sm hover:bg-gray-100">Botão Primário</button>
<button class="px-6 py-3 border border-white text-white font-medium rounded-md shadow-sm hover:bg-white hover:text-blue-600">Botão Secundário</button>
</div>
</div>
</div>
`;
defaultSettings = {
title: 'Título Principal',
subtitle: 'Uma frase impactante que resume seu negócio ou proposta de valor.',
primaryButtonText: 'Botão Primário',
secondaryButtonText: 'Botão Secundário',
background: 'linear-gradient(135deg, #6b7280, #1e40af)',
textColor: '#ffffff'
};
break;
case 'section':
componentHtml = `
<div class="site-component mb-4 relative" data-id="${componentId}" data-type="section">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="section-preview py-12 px-6">
<div class="max-w-4xl mx-auto text-center">
<h2 class="text-3xl font-bold mb-4">Título da Seção</h2>
<p class="text-lg text-gray-600 mb-8">Descrição da seção. Adicione conteúdo relevante aqui para informar seus visitantes.</p>
<div class="grid md:grid-cols-3 gap-8">
<div class="bg-white p-6 rounded-lg shadow-sm">
<div class="text-blue-600 text-3xl mb-4">
<i class="fas fa-star"></i>
</div>
<h3 class="text-xl font-semibold mb-2">Recurso 1</h3>
<p class="text-gray-600">Descrição breve do recurso ou benefício.</p>
</div>
<div class="bg-white p-6 rounded-lg shadow-sm">
<div class="text-blue-600 text-3xl mb-4">
<i class="fas fa-heart"></i>
</div>
<h3 class="text-xl font-semibold mb-2">Recurso 2</h3>
<p class="text-gray-600">Descrição breve do recurso ou benefício.</p>
</div>
<div class="bg-white p-6 rounded-lg shadow-sm">
<div class="text-blue-600 text-3xl mb-4">
<i class="fas fa-flag"></i>
</div>
<h3 class="text-xl font-semibold mb-2">Recurso 3</h3>
<p class="text-gray-600">Descrição breve do recurso ou benefício.</p>
</div>
</div>
</div>
</div>
</div>
`;
defaultSettings = {
title: 'Título da Seção',
description: 'Descrição da seção. Adicione conteúdo relevante aqui para informar seus visitantes.',
backgroundColor: '#f9fafb',
textColor: '#111827',
features: [
{ icon: 'star', title: 'Recurso 1', description: 'Descrição breve do recurso ou benefício.' },
{ icon: 'heart', title: 'Recurso 2', description: 'Descrição breve do recurso ou benefício.' },
{ icon: 'flag', title: 'Recurso 3', description: 'Descrição breve do recurso ou benefício.' }
]
};
break;
case 'footer':
componentHtml = `
<div class="site-component mb-4 relative" data-id="${componentId}" data-type="footer">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="footer-preview py-12 px-6">
<div class="max-w-6xl mx-auto">
<div class="grid md:grid-cols-4 gap-8">
<div>
<h3 class="text-xl font-bold mb-4">Empresa</h3>
<p class="text-gray-400 mb-4">Uma breve descrição sobre sua empresa e o que você oferece.</p>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-twitter"></i></a>
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-instagram"></i></a>
<a href="#" class="text-gray-400 hover:text-white"><i class="fab fa-linkedin-in"></i></a>
</div>
</div>
<div>
<h3 class="text-xl font-bold mb-4">Links</h3>
<ul class="space-y-2">
<li><a href="#" class="text-gray-400 hover:text-white">Home</a></li>
<li><a href="#" class="text-gray-400 hover:text-white">Sobre</a></li>
<li><a href="#" class="text-gray-400 hover:text-white">Serviços</a></li>
<li><a href="#" class="text-gray-400 hover:text-white">Contato</a></li>
</ul>
</div>
<div>
<h3 class="text-xl font-bold mb-4">Contato</h3>
<ul class="space-y-2">
<li class="text-gray-400">email@exemplo.com</li>
<li class="text-gray-400">(11) 99999-9999</li>
<li class="text-gray-400">Rua Exemplo, 123 - São Paulo, SP</li>
</ul>
</div>
<div>
<h3 class="text-xl font-bold mb-4">Newsletter</h3>
<p class="text-gray-400 mb-4">Assine nossa newsletter para receber atualizações.</p>
<div class="flex">
<input type="email" placeholder="Seu email" class="px-4 py-2 w-full rounded-l-md focus:outline-none">
<button class="bg-blue-600 px-4 py-2 rounded-r-md hover:bg-blue-700">Enviar</button>
</div>
</div>
</div>
<div class="border-t border-gray-800 mt-8 pt-8 text-center text-gray-400">
<p>© 2023 Empresa. Todos os direitos reservados.</p>
</div>
</div>
</div>
</div>
`;
defaultSettings = {
companyDescription: 'Uma breve descrição sobre sua empresa e o que você oferece.',
links: ['Home', 'Sobre', 'Serviços', 'Contato'],
contactInfo: {
email: 'email@exemplo.com',
phone: '(11) 99999-9999',
address: 'Rua Exemplo, 123 - São Paulo, SP'
},
socialLinks: ['facebook-f', 'twitter', 'instagram', 'linkedin-in'],
copyrightText: '© 2023 Empresa. Todos os direitos reservados.',
backgroundColor: '#1f2937',
textColor: '#ffffff'
};
break;
case 'heading':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="heading">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<h2 class="text-2xl font-bold">Título</h2>
</div>
`;
defaultSettings = {
text: 'Título',
level: 'h2',
alignment: 'left',
color: '#111827'
};
break;
case 'paragraph':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="paragraph">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<p class="text-gray-700">Este é um parágrafo de texto. Você pode editar o conteúdo, tamanho, cor e alinhamento nas configurações.</p>
</div>
`;
defaultSettings = {
text: 'Este é um parágrafo de texto. Você pode editar o conteúdo, tamanho, cor e alinhamento nas configurações.',
size: 'base',
alignment: 'left',
color: '#374151'
};
break;
case 'button':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="button">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<button class="px-4 py-2 bg-blue-600 text-white rounded-md shadow-sm hover:bg-blue-700">Botão</button>
</div>
`;
defaultSettings = {
text: 'Botão',
link: '#',
style: 'primary',
size: 'medium',
color: '#2563eb'
};
break;
case 'image':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="image">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<div class="bg-gray-100 h-40 rounded-md flex items-center justify-center">
<span class="text-gray-400">Imagem</span>
</div>
</div>
`;
defaultSettings = {
src: '',
alt: 'Imagem',
width: '100%',
height: 'auto',
alignment: 'center'
};
break;
case 'text':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="text">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<label class="block text-sm font-medium text-gray-700 mb-1">Text Input</label>
<input type="text" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter text">
</div>
`;
defaultSettings = {
label: 'Text Input',
placeholder: 'Enter text',
required: false
};
break;
case 'email':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="email">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<label class="block text-sm font-medium text-gray-700 mb-1">Email Input</label>
<input type="email" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter email">
</div>
`;
defaultSettings = {
label: 'Email Input',
placeholder: 'Enter email',
required: false
};
break;
case 'textarea':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="textarea">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<label class="block text-sm font-medium text-gray-700 mb-1">Text Area</label>
<textarea rows="3" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" placeholder="Enter text"></textarea>
</div>
`;
defaultSettings = {
label: 'Text Area',
placeholder: 'Enter text',
required: false,
rows: 3
};
break;
case 'select':
componentHtml = `
<div class="site-component mb-4 p-4 bg-white rounded border border-gray-200 shadow-sm relative" data-id="${componentId}" data-type="select">
<div class="component-actions absolute -top-3 -right-3 bg-white rounded-full shadow-md p-1 space-x-1">
<button class="edit-component p-1 text-blue-500 hover:text-blue-700 rounded-full hover:bg-blue-50">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-component p-1 text-red-500 hover:text-red-700 rounded-full hover:bg-red-50">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
<label class="block text-sm font-medium text-gray-700 mb-1">Dropdown</label>
<select class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
</div>
`;
defaultSettings = {
label: 'Dropdown',
options: ['Option 1', 'Option 2', 'Option 3'],
required: false
};
break;
}
// Create a temporary element to parse the HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = componentHtml.trim();
const newComponent = tempDiv.firstChild;
// Add to site
if (dropZone === siteContainer) {
// Adding to main container
dropZone.appendChild(newComponent);
} else {
// Adding to a layout element (container, row, or column)
const placeholder = dropZone.querySelector('.layout-placeholder');
if (placeholder) {
dropZone.insertBefore(newComponent, placeholder);
} else {
dropZone.appendChild(newComponent);
}
}
// Add to site data
const currentPage = siteData.pages.find(page => page.id === siteData.currentPage);
if (currentPage) {
currentPage.components.push({
id: componentId,
type: type,
settings: defaultSettings,
parentId: dropZone !== siteContainer ? dropZone.dataset.id : null
});
}
// Set up component interactions
setupComponentInteractions(newComponent);
// Set up drop zones for the new element if it's a layout element
if (['container', 'row', 'column'].includes(type)) {
setupDropZones();
}
// If it's a column, set up resize handle
if (type === 'column') {
setupColumnResize(newComponent);
}
// Select the new component
selectComponent(newComponent);
}
// Set up column resize functionality
function setupColumnResize(columnElement) {
const resizeHandle = columnElement.querySelector('.grid-resize-handle');
resizeHandle.addEventListener('mousedown', function(e) {
e.preventDefault();
isResizing = true;
currentResizeColumn = columnElement;
startX = e.clientX;
startWidth = parseInt(document.defaultView.getComputedStyle(columnElement).width, 10);
document.documentElement.style.cursor = 'nwse-resize';
document.addEventListener('mousemove', handleResize);
document.addEventListener('mouseup', stopResize);
});
function handleResize(e) {
if (!isResizing) return;
const width = startWidth + e.clientX - startX;
currentResizeColumn.style.width = `${width}px`;
currentResizeColumn.style.minWidth = `${width}px`;
// Update site data
const componentId = currentResizeColumn.dataset.id;
const currentPage = siteData.pages.find(page => page.id === siteData.currentPage);
if (currentPage) {
const componentData = currentPage.components.find(comp => comp.id === componentId);
if (componentData) {
componentData.settings.width = `${width}px`;
componentData.settings.minWidth = `${width}px`;
}
}
}
function stopResize() {
isResizing = false;
currentResizeColumn = null;
document.documentElement.style.cursor = '';
document.removeEventListener('mousemove', handleResize);
document.removeEventListener('mouseup', stopResize);
}
}
// Set up component interactions (click, delete, etc.)
function setupComponentInteractions(componentElement) {
// Select component on click
componentElement.addEventListener('click', function(e) {
if (!e.target.closest('.component-actions') && !e.target.closest('.layout-actions')) {
selectComponent(componentElement);
}
});
// Delete component
const deleteBtn = componentElement.querySelector('.delete-component');
if (deleteBtn) {
deleteBtn.addEventListener('click', function() {
const componentId = componentElement.dataset.id;
// Remove from DOM
componentElement.remove();
// Remove from site data
const currentPage = siteData.pages.find(page => page.id === siteData.currentPage);
if (currentPage) {
currentPage.components = currentPage.components.filter(comp => comp.id !== componentId);
}
// Clear selection
selectedComponent = null;
renderComponentSettings(null);
// Show empty state if no components left in container
const containers = document.querySelectorAll('.layout-container, .layout-row, .layout-column');
containers.forEach(container => {
if (container.children.length === 1 && container.querySelector('.layout-placeholder')) {
container.innerHTML = `
<div class="layout-placeholder">
<i class="fas fa-square-full text-indigo-300 text-xl mb-2"></i>
<p class="text-sm">Drop ${container.classList.contains('layout-container') ? 'rows or components' :
container.classList.contains('layout-row') ? 'columns' : 'components'} here</p>
</div>
`;
}
});
// Show empty state if no components left in main container
if (siteContainer.children.length === 0) {
siteContainer.innerHTML = `
<div class="empty-state">
<i class="fas fa-mouse-pointer text-4xl mb-3 text-gray-400"></i>
<h3 class="font-medium text-gray-500">Arraste componentes aqui</h3>
<p class="text-sm text-gray-400 mt-1">Comece construindo seu site arrastando componentes do painel esquerdo</p>
</div>
`;
}
});
}
// Edit component (same as select for now)
const editBtn = componentElement.querySelector('.edit-component');
if (editBtn) {
editBtn.addEventListener('click', function() {
selectComponent(componentElement);
});
}
}
// Select a component and show its settings
function selectComponent(componentElement) {
// Deselect previously selected component
if (selectedComponent) {
selectedComponent.classList.remove('selected');
}
// Select new component
selectedComponent = componentElement;
if (selectedComponent) {
selectedComponent.classList.add('selected');
// Scroll to show the selected component
selectedComponent.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// Find the component in site data
const componentId = selectedComponent.dataset.id;
const currentPage = siteData.pages.find(page => page.id === siteData.currentPage);
const componentData = currentPage ? currentPage.components.find(comp => comp.id === componentId) : null;
// Show component settings
renderComponentSettings(componentData);
} else {
renderComponentSettings(null);
}
}
// Render component settings panel
function renderComponentSettings(componentData) {
// Hide page and theme settings
pageSettings.classList.add('hidden');
themeSettings.classList.add('hidden');
componentSettings.classList.remove('hidden');
if (!componentData) {
componentSettings.innerHTML = '<p class="text-gray-500 italic">Selecione um componente para editar suas propriedades</p>';
return;
}
let settingsHtml = '';
switch(componentData.type) {
case 'container':
settingsHtml = `
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Padding</label>
<input type="text" class="component-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="padding" value="${componentData.settings.padding}">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Margin</label>
<input type="text" class="component-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="margin" value="${componentData.settings.margin}">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Background Color</label>
<input type="color" class="component-setting w-full h-10 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500" data-setting="background" value="${componentData.settings.background}">
</div>
</div>
`;
break;
case 'row':
settingsHtml = `
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Number of Columns</label>
<select class="component-setting w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" data-setting="columns">
<option value="1" ${componentData.settings.columns === 1 ? 'selected' : ''}>1 Column</
</html>