|
<!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"> |
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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> |
|
|
|
|
|
<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">​</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> |
|
|
|
|
|
<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">​</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> |
|
|
|
|
|
<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">​</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> |