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