fix(builder): stabilize restaurante free-drag, reset-to-template, and memory sync
This commit is contained in:
@@ -70,6 +70,7 @@
|
||||
.block.style-dark-glow{--site-block-bg:linear-gradient(165deg,rgba(7,12,22,.92),rgba(3,8,16,.88));--site-block-border:#1f3657;--site-block-shadow:0 16px 36px rgba(2,8,20,.46);--site-block-shadow-hover:0 24px 56px rgba(8,24,56,.58);--site-block-ring-hover:0 0 0 1px rgba(37,99,235,.42);--site-block-sheen-opacity:.08;--site-block-sheen-opacity-hover:.16;--site-block-accent-opacity:.24;--site-block-accent-opacity-hover:.38;color:#e6edf8}
|
||||
.block.style-glass{--site-block-bg:linear-gradient(155deg,rgba(255,255,255,.52),rgba(255,255,255,.28));--site-block-border:rgba(255,255,255,.48);--site-block-shadow:0 16px 34px rgba(15,23,42,.13);--site-block-shadow-hover:0 24px 44px rgba(15,23,42,.2);--site-block-sheen-opacity:.46;--site-block-sheen-opacity-hover:.58;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
|
||||
.block.style-soft-gradient{--site-block-bg:linear-gradient(150deg,#f8fafc 0%,#eef2ff 55%,#e2e8f0 100%);--site-block-border:#d5deef;--site-block-shadow:0 12px 28px rgba(15,23,42,.1);--site-block-sheen-opacity:.36;--site-block-sheen-opacity-hover:.48;--site-block-accent-opacity:.12;--site-block-accent-opacity-hover:.22}
|
||||
.block.style-liquid-glass{--site-block-bg:linear-gradient(145deg,rgba(255,255,255,.72) 0%,rgba(226,237,255,.48) 45%,rgba(203,226,255,.38) 100%);--site-block-border:rgba(255,255,255,.7);--site-block-shadow:0 20px 38px rgba(15,23,42,.14);--site-block-shadow-hover:0 30px 52px rgba(15,23,42,.2);--site-block-sheen-opacity:.56;--site-block-sheen-opacity-hover:.72;--site-block-accent-opacity:.24;--site-block-accent-opacity-hover:.34;backdrop-filter:blur(14px) saturate(130%);-webkit-backdrop-filter:blur(14px) saturate(130%)}
|
||||
.block.bg-motion-flow::before{background:linear-gradient(120deg,rgba(255,255,255,.3) 0%,rgba(255,255,255,.06) 34%,rgba(59,130,246,.2) 52%,rgba(255,255,255,.06) 68%,rgba(255,255,255,.25) 100%);background-size:220% 220%;animation:bgFlow var(--block-motion-duration,18s) linear infinite}
|
||||
.block.bg-motion-aurora::after{opacity:var(--site-block-accent-opacity-hover,.26);animation:bgAurora var(--block-motion-duration,22s) var(--ease-emphasis) infinite}
|
||||
.block.bg-motion-parallax::before{animation:bgParallax var(--block-motion-duration,20s) var(--ease-standard) infinite}
|
||||
@@ -281,7 +282,8 @@
|
||||
.restaurant-site .block.style-clean-landing,
|
||||
.restaurant-site .block.style-dark-glow,
|
||||
.restaurant-site .block.style-glass,
|
||||
.restaurant-site .block.style-soft-gradient{
|
||||
.restaurant-site .block.style-soft-gradient,
|
||||
.restaurant-site .block.style-liquid-glass{
|
||||
background:var(--site-block-bg,var(--restaurant-surface,#fff));
|
||||
border-color:var(--site-block-border,var(--restaurant-border,#dbe3ee));
|
||||
box-shadow:var(--site-block-shadow,0 8px 20px rgba(15,23,42,.06));
|
||||
@@ -306,7 +308,7 @@
|
||||
.preview-shell.size-phone .hero-media,
|
||||
.preview-shell.size-tablet .hero-media{min-height:220px}
|
||||
@keyframes slowGradient{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
|
||||
.free-drag a{pointer-events:none}
|
||||
body:not(.preview-mode) .free-drag a{pointer-events:none}
|
||||
.block-actions{position:absolute;top:8px;right:8px;display:flex;gap:6px}
|
||||
.block-actions button{border:0;background:#0f172a;color:#e2e8f0;border-radius:8px;padding:4px 6px;cursor:pointer;font-size:12px}
|
||||
.block-actions button:hover{background:#1f2937}
|
||||
@@ -516,7 +518,7 @@
|
||||
<details class="acc">
|
||||
<summary>Layout</summary>
|
||||
<div class="acc-body">
|
||||
<div class="row"><label>Preset global bloques</label><select id="globalBlockPresetSelect"><option value="clean-landing">Clean Landing</option><option value="dark-glow">Dark Glow</option><option value="glass">Glass</option><option value="soft-gradient">Soft Gradient</option></select></div>
|
||||
<div class="row"><label>Preset global bloques</label><select id="globalBlockPresetSelect"><option value="clean-landing">Clean Landing</option><option value="dark-glow">Dark Glow</option><option value="glass">Glass</option><option value="soft-gradient">Soft Gradient</option><option value="liquid-glass">Liquid Glass</option></select></div>
|
||||
<div class="row"><label>Movimiento fondo global</label><select id="globalBlockMotionSelect"><option value="none">Sin movimiento</option><option value="flow">Gradient Flow</option><option value="aurora">Aurora</option><option value="parallax">Parallax Soft</option></select></div>
|
||||
<div class="row"><label>Velocidad movimiento global</label><select id="globalBlockMotionSpeedSelect"><option value="slow">Lenta</option><option value="normal">Normal</option><option value="fast">Rapida</option></select></div>
|
||||
<div class="row"><label>Animaciones</label><input id="animToggle" type="checkbox"></div>
|
||||
@@ -682,6 +684,8 @@ const state = {
|
||||
};
|
||||
let selectedBlockId = null;
|
||||
let currentPage = "home";
|
||||
let autosaveTimer = null;
|
||||
let autosaveReady = false;
|
||||
let dropIndicator = null;
|
||||
let isDraggingFree = false;
|
||||
let dragStart = { x: 0, y: 0, left: 0, top: 0, id: null };
|
||||
@@ -726,7 +730,20 @@ const state = {
|
||||
}
|
||||
function normalizeBlockPreset(value){
|
||||
const raw = String(value || "").trim().toLowerCase();
|
||||
return ["clean-landing","dark-glow","glass","soft-gradient","inherit"].includes(raw) ? raw : "inherit";
|
||||
return ["clean-landing","dark-glow","glass","liquid-glass","soft-gradient","inherit"].includes(raw) ? raw : "inherit";
|
||||
}
|
||||
function scheduleDraftAutosave(){
|
||||
if (PREVIEW_ONLY || !autosaveReady || isSaving) return;
|
||||
if (autosaveTimer){ clearTimeout(autosaveTimer); }
|
||||
setSaveStatus("Borrador pendiente...", "busy");
|
||||
autosaveTimer = setTimeout(async ()=>{
|
||||
try {
|
||||
await saveDraftSilently();
|
||||
setSaveStatus("Borrador guardado", "ok");
|
||||
} catch (_e){
|
||||
setSaveStatus("Error guardando borrador", "error");
|
||||
}
|
||||
}, 900);
|
||||
}
|
||||
function normalizeBlockMotion(value){
|
||||
const raw = String(value || "").trim().toLowerCase();
|
||||
@@ -1120,7 +1137,7 @@ const state = {
|
||||
.filter((b)=>b.type!=="menu")
|
||||
.map((b, i)=>{
|
||||
const title = (b.data && (b.data.title || b.data.text || b.type)) || b.type;
|
||||
return { id: b.id, label: String(title).slice(0, 24), index: i+1 };
|
||||
return { id: b.id, label: String(title).slice(0, 24), type: String(b.type || "").toLowerCase(), index: i+1 };
|
||||
});
|
||||
const norm = (v)=>String(v || "")
|
||||
.toLowerCase()
|
||||
@@ -1128,21 +1145,31 @@ const state = {
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/[^a-z0-9]+/g, " ")
|
||||
.trim();
|
||||
const resolveMenuTarget = (label, index)=>{
|
||||
const key = norm(label);
|
||||
const has = (...words)=>words.some((w)=>key.includes(w));
|
||||
const findType = (...types)=>contentBlocks.find((b)=>types.includes(b.type));
|
||||
// Prioridad semantica real por tipo de bloque
|
||||
if (has("inicio","home","portada")) return findType("hero") || contentBlocks[0] || null;
|
||||
if (has("carta","menu","platos","especialidades")) return findType("cards","gallery","hero") || contentBlocks[index] || null;
|
||||
if (has("resena","resenas","review","testimonio","testimonios")) return findType("review") || contentBlocks[index] || null;
|
||||
if (has("redes","social","sociales","instagram","facebook","tiktok","youtube")) return findType("social") || contentBlocks[index] || null;
|
||||
if (has("mapa","ubicacion","llegar","direccion")) return findType("map") || contentBlocks[index] || null;
|
||||
if (has("contacto","reserva","reservas","telefono","email","correo","whatsapp")) return findType("contact") || contentBlocks[index] || null;
|
||||
const exact = contentBlocks.find((b)=>norm(b.label) === key);
|
||||
if (exact) return exact;
|
||||
return contentBlocks[index] || null;
|
||||
};
|
||||
const items = manualItems.length
|
||||
? (manualItems.length >= 3
|
||||
? manualItems.map((label, i)=>{
|
||||
const key = norm(label);
|
||||
const exact = contentBlocks.find((b)=>norm(b.label) === key);
|
||||
const byIndex = contentBlocks[i];
|
||||
const target = exact || byIndex || null;
|
||||
? manualItems.map((label, i)=>{
|
||||
const target = resolveMenuTarget(label, i);
|
||||
return { id: target ? target.id : "", label: String(label).slice(0, 24), index: i+1 };
|
||||
})
|
||||
: [])
|
||||
: contentBlocks;
|
||||
const safeItems = items.length ? items : [
|
||||
{ id: "menu_item_1", label: "Inicio", index: 1 },
|
||||
{ id: "menu_item_2", label: "Productos", index: 2 },
|
||||
{ id: "menu_item_3", label: "Contacto", index: 3 }
|
||||
{ id: "", label: "Inicio", index: 1 },
|
||||
{ id: "", label: "Productos", index: 2 },
|
||||
{ id: "", label: "Contacto", index: 3 }
|
||||
];
|
||||
const links = safeItems.map(it=>`<a class="site-nav-link" href="${it.id ? `#${it.id}` : "#"}">${escapeHtml(it.label)}</a>`).join("");
|
||||
const mobileLinks = safeItems.map(it=>`<a class="site-nav-link" href="${it.id ? `#${it.id}` : "#"}" data-drawer-link="1">${escapeHtml(it.label)}</a>`).join("");
|
||||
@@ -1777,6 +1804,7 @@ const state = {
|
||||
spacer.style.width = "100%";
|
||||
spacer.style.pointerEvents = "none";
|
||||
inner.appendChild(spacer);
|
||||
// No auto-restack here: free-drag must preserve user placement.
|
||||
}
|
||||
const footer = document.createElement("footer");
|
||||
footer.className = "site-global-footer";
|
||||
@@ -1956,6 +1984,7 @@ const state = {
|
||||
if (BUILDER_MODE === "ub24"){ b.page = currentPage; }
|
||||
if (state.settings.free_drag){ b.pos = getDefaultPos(); }
|
||||
state.blocks.splice(index,0,b); selectedBlockId=b.id; renderInspector(); renderPreview();
|
||||
scheduleDraftAutosave();
|
||||
}
|
||||
function moveBlock(id,toIndex){
|
||||
const from=state.blocks.findIndex(b=>b.id===id);
|
||||
@@ -1967,6 +1996,9 @@ const state = {
|
||||
const target = Math.max(0, Math.min(state.blocks.length, clamped));
|
||||
state.blocks.splice(target,0,b);
|
||||
selectedBlockId = b.id;
|
||||
if (state.settings.free_drag){
|
||||
applyStackedFreeDragLayout(state.blocks);
|
||||
}
|
||||
renderPreview();
|
||||
}
|
||||
function moveBlockByDelta(id, delta){
|
||||
@@ -1981,6 +2013,7 @@ const state = {
|
||||
const current = Math.max(30, Math.min(100, Number(block.data.width || 100)));
|
||||
block.data.width = current >= 95 ? 50 : 100;
|
||||
renderPreview();
|
||||
scheduleDraftAutosave();
|
||||
}
|
||||
|
||||
function renderInspector(){
|
||||
@@ -2101,7 +2134,7 @@ const state = {
|
||||
html+=`<div class="row"><label>Ancho bloque (%)</label><input id="blockWidth" type="range" min="30" max="100" step="1" value="${w}"></div>`;
|
||||
html+=`<div class="row"><input id="blockWidthNumber" type="number" min="30" max="100" step="1" value="${w}"></div>`;
|
||||
const visualPreset = escapeHtml(normalizeBlockPreset(data.visual_preset || "inherit"));
|
||||
html+=`<div class="row"><label>Preset visual bloque</label><select id="blockVisualPreset"><option value="inherit" ${visualPreset==="inherit"?"selected":""}>Heredar global</option><option value="clean-landing" ${visualPreset==="clean-landing"?"selected":""}>Clean Landing</option><option value="dark-glow" ${visualPreset==="dark-glow"?"selected":""}>Dark Glow</option><option value="glass" ${visualPreset==="glass"?"selected":""}>Glass</option><option value="soft-gradient" ${visualPreset==="soft-gradient"?"selected":""}>Soft Gradient</option></select></div>`;
|
||||
html+=`<div class="row"><label>Preset visual bloque</label><select id="blockVisualPreset"><option value="inherit" ${visualPreset==="inherit"?"selected":""}>Heredar global</option><option value="clean-landing" ${visualPreset==="clean-landing"?"selected":""}>Clean Landing</option><option value="dark-glow" ${visualPreset==="dark-glow"?"selected":""}>Dark Glow</option><option value="glass" ${visualPreset==="glass"?"selected":""}>Glass</option><option value="liquid-glass" ${visualPreset==="liquid-glass"?"selected":""}>Liquid Glass</option><option value="soft-gradient" ${visualPreset==="soft-gradient"?"selected":""}>Soft Gradient</option></select></div>`;
|
||||
const blockMotion = escapeHtml(normalizeBlockMotion(data.bg_motion_style || "inherit"));
|
||||
html+=`<div class="row"><label>Movimiento fondo bloque</label><select id="blockMotionStyle"><option value="inherit" ${blockMotion==="inherit"?"selected":""}>Heredar global</option><option value="none" ${blockMotion==="none"?"selected":""}>Sin movimiento</option><option value="flow" ${blockMotion==="flow"?"selected":""}>Gradient Flow</option><option value="aurora" ${blockMotion==="aurora"?"selected":""}>Aurora</option><option value="parallax" ${blockMotion==="parallax"?"selected":""}>Parallax Soft</option></select></div>`;
|
||||
const blockMotionSpeed = escapeHtml(normalizeMotionSpeed(data.bg_motion_speed || "inherit"));
|
||||
@@ -2307,6 +2340,7 @@ const state = {
|
||||
if (animDurationEl){ block.data.anim_duration = Math.max(120, Math.min(900, Number(animDurationEl.value || 250))); }
|
||||
}
|
||||
renderPreview();
|
||||
scheduleDraftAutosave();
|
||||
}
|
||||
|
||||
function wireSidebar(){
|
||||
@@ -2337,6 +2371,7 @@ const state = {
|
||||
selectedBlockId = b.id;
|
||||
renderInspector();
|
||||
renderPreview();
|
||||
scheduleDraftAutosave();
|
||||
}
|
||||
function reorderVisibleBlocks(orderedIds){
|
||||
if (!Array.isArray(orderedIds) || !orderedIds.length) return;
|
||||
@@ -2747,27 +2782,28 @@ const state = {
|
||||
registeredBrand.value = s.registered_brand || s.site_name || "";
|
||||
developerBrand.value = s.developer_brand || "GKACHELE™";
|
||||
siteAuthor.value = s.site_author || "";
|
||||
siteName.addEventListener("input",()=>{ s.site_name=siteName.value; renderPreview(); });
|
||||
primary.addEventListener("input",()=>{ s.primary_color=primary.value; renderPreview(); });
|
||||
bgColor.addEventListener("input",()=>{ s.bg_color=bgColor.value; renderPreview(); });
|
||||
bgColor2.addEventListener("input",()=>{ s.bg_color2=bgColor2.value; renderPreview(); });
|
||||
bgGradient.addEventListener("change",()=>{ s.bg_gradient=bgGradient.checked; renderPreview(); });
|
||||
bgMotion.addEventListener("change",()=>{ s.bg_motion=bgMotion.value; renderPreview(); });
|
||||
textColor.addEventListener("input",()=>{ s.text_color=textColor.value; renderPreview(); });
|
||||
mutedColor.addEventListener("input",()=>{ s.muted_color=mutedColor.value; renderPreview(); });
|
||||
fontBody.addEventListener("change",()=>{ s.font_body=fontBody.value; renderPreview(); });
|
||||
fontHeading.addEventListener("change",()=>{ s.font_heading=fontHeading.value; renderPreview(); });
|
||||
bgVideo.addEventListener("input",()=>{ s.bg_video_url=bgVideo.value; renderPreview(); });
|
||||
bgAnim.addEventListener("input",()=>{ s.bg_anim_url=bgAnim.value; renderPreview(); });
|
||||
animToggle.addEventListener("change",()=>{ s.animations=animToggle.checked; renderPreview(); });
|
||||
if (globalBlockPreset){ globalBlockPreset.addEventListener("change",()=>{ s.global_block_preset = normalizeBlockPreset(globalBlockPreset.value || "clean-landing"); renderPreview(); }); }
|
||||
if (globalBlockMotion){ globalBlockMotion.addEventListener("change",()=>{ s.global_block_motion = normalizeBlockMotion(globalBlockMotion.value || "none"); renderPreview(); }); }
|
||||
if (globalBlockMotionSpeed){ globalBlockMotionSpeed.addEventListener("change",()=>{ s.global_block_motion_speed = normalizeMotionSpeed(globalBlockMotionSpeed.value || "normal"); renderPreview(); }); }
|
||||
canvasMinHeight.addEventListener("input",()=>{ s.canvas_min_height = Math.max(700, Number(canvasMinHeight.value || 1200)); renderPreview(); });
|
||||
canvasBottomSpace.addEventListener("input",()=>{ s.canvas_bottom_space = Math.max(0, Number(canvasBottomSpace.value || 0)); renderPreview(); });
|
||||
registeredBrand.addEventListener("input",()=>{ s.registered_brand = registeredBrand.value; renderPreview(); });
|
||||
developerBrand.addEventListener("input",()=>{ s.developer_brand = developerBrand.value; renderPreview(); });
|
||||
siteAuthor.addEventListener("input",()=>{ s.site_author = siteAuthor.value; renderPreview(); });
|
||||
const renderAndSave = ()=>{ renderPreview(); scheduleDraftAutosave(); };
|
||||
siteName.addEventListener("input",()=>{ s.site_name=siteName.value; renderAndSave(); });
|
||||
primary.addEventListener("input",()=>{ s.primary_color=primary.value; renderAndSave(); });
|
||||
bgColor.addEventListener("input",()=>{ s.bg_color=bgColor.value; renderAndSave(); });
|
||||
bgColor2.addEventListener("input",()=>{ s.bg_color2=bgColor2.value; renderAndSave(); });
|
||||
bgGradient.addEventListener("change",()=>{ s.bg_gradient=bgGradient.checked; renderAndSave(); });
|
||||
bgMotion.addEventListener("change",()=>{ s.bg_motion=bgMotion.value; renderAndSave(); });
|
||||
textColor.addEventListener("input",()=>{ s.text_color=textColor.value; renderAndSave(); });
|
||||
mutedColor.addEventListener("input",()=>{ s.muted_color=mutedColor.value; renderAndSave(); });
|
||||
fontBody.addEventListener("change",()=>{ s.font_body=fontBody.value; renderAndSave(); });
|
||||
fontHeading.addEventListener("change",()=>{ s.font_heading=fontHeading.value; renderAndSave(); });
|
||||
bgVideo.addEventListener("input",()=>{ s.bg_video_url=bgVideo.value; renderAndSave(); });
|
||||
bgAnim.addEventListener("input",()=>{ s.bg_anim_url=bgAnim.value; renderAndSave(); });
|
||||
animToggle.addEventListener("change",()=>{ s.animations=animToggle.checked; renderAndSave(); });
|
||||
if (globalBlockPreset){ globalBlockPreset.addEventListener("change",()=>{ s.global_block_preset = normalizeBlockPreset(globalBlockPreset.value || "clean-landing"); renderAndSave(); }); }
|
||||
if (globalBlockMotion){ globalBlockMotion.addEventListener("change",()=>{ s.global_block_motion = normalizeBlockMotion(globalBlockMotion.value || "none"); renderAndSave(); }); }
|
||||
if (globalBlockMotionSpeed){ globalBlockMotionSpeed.addEventListener("change",()=>{ s.global_block_motion_speed = normalizeMotionSpeed(globalBlockMotionSpeed.value || "normal"); renderAndSave(); }); }
|
||||
canvasMinHeight.addEventListener("input",()=>{ s.canvas_min_height = Math.max(700, Number(canvasMinHeight.value || 1200)); renderAndSave(); });
|
||||
canvasBottomSpace.addEventListener("input",()=>{ s.canvas_bottom_space = Math.max(0, Number(canvasBottomSpace.value || 0)); renderAndSave(); });
|
||||
registeredBrand.addEventListener("input",()=>{ s.registered_brand = registeredBrand.value; renderAndSave(); });
|
||||
developerBrand.addEventListener("input",()=>{ s.developer_brand = developerBrand.value; renderAndSave(); });
|
||||
siteAuthor.addEventListener("input",()=>{ s.site_author = siteAuthor.value; renderAndSave(); });
|
||||
|
||||
bindDrop(logoDrop, logoFile, (data)=>{ s.logo_url = data; }, "Logo cargado");
|
||||
bindDrop(bgDrop, bgFile, (data)=>{ s.bg_image_url = data; }, "Fondo cargado");
|
||||
@@ -2794,7 +2830,14 @@ const state = {
|
||||
document.getElementById("btnSizePhone").addEventListener("click",()=>setSize("520px","btnSizePhone"));
|
||||
document.getElementById("btnSizeTablet").addEventListener("click",()=>setSize("820px","btnSizeTablet"));
|
||||
document.getElementById("btnSizeDesktop").addEventListener("click",()=>setSize("100%","btnSizeDesktop"));
|
||||
setShellClass("size-desktop");
|
||||
const requestedDevice = new URLSearchParams(window.location.search).get("device");
|
||||
if (requestedDevice === "phone"){
|
||||
setSize("520px","btnSizePhone");
|
||||
} else if (requestedDevice === "tablet"){
|
||||
setSize("820px","btnSizeTablet");
|
||||
} else {
|
||||
setSize("100%","btnSizeDesktop");
|
||||
}
|
||||
}
|
||||
function wirePreviewToggle(){
|
||||
const btnPreview = document.getElementById("btnPreview");
|
||||
@@ -2806,6 +2849,10 @@ const state = {
|
||||
try{
|
||||
await saveDraftSilently();
|
||||
const url = new URL(`${BUILDER_BASE_PATH}/preview-final`, window.location.origin);
|
||||
const shell = document.querySelector(".preview-shell");
|
||||
if (shell && shell.classList.contains("size-phone")) url.searchParams.set("device", "phone");
|
||||
else if (shell && shell.classList.contains("size-tablet")) url.searchParams.set("device", "tablet");
|
||||
else url.searchParams.set("device", "desktop");
|
||||
window.open(url.toString(), "_blank", "noopener,noreferrer");
|
||||
} catch(_e){
|
||||
window.alert("No se pudo abrir la vista previa. Reintenta.");
|
||||
@@ -2982,10 +3029,10 @@ const state = {
|
||||
if (type === "hero") return 520;
|
||||
if (type === "gallery") return 330;
|
||||
if (type === "map") return 360;
|
||||
if (type === "contact") return 340;
|
||||
if (type === "cards") return 280;
|
||||
if (type === "contact") return 520;
|
||||
if (type === "cards") return 320;
|
||||
if (type === "review") return 250;
|
||||
if (type === "social") return 280;
|
||||
if (type === "social") return 220;
|
||||
return 240;
|
||||
}
|
||||
function hasSevereFreeDragOverlap(blocks){
|
||||
@@ -3030,6 +3077,28 @@ const state = {
|
||||
y += estimateFreeDragBlockHeight(b) + 22;
|
||||
});
|
||||
}
|
||||
function restackFreeDragWithMeasuredHeights(){
|
||||
const canvas = document.getElementById("previewCanvas");
|
||||
if (!canvas || !state.settings.free_drag) return;
|
||||
const nodes = [...canvas.querySelectorAll(".block")];
|
||||
if (!nodes.length) return;
|
||||
const byId = new Map(nodes.map((n)=>[n.dataset.blockId, n]));
|
||||
let y = 20;
|
||||
state.blocks.forEach((b)=>{
|
||||
if (!b) return;
|
||||
b.data = (b.data && typeof b.data === "object") ? b.data : {};
|
||||
if (b.type === "menu"){
|
||||
b.data.width = 100;
|
||||
} else {
|
||||
b.data.width = snapBlockWidth(b.type, Number(b.data.width || 92));
|
||||
}
|
||||
b.pos = { x: 20, y };
|
||||
const node = byId.get(b.id);
|
||||
const measured = node ? Math.max(120, node.offsetHeight || 0) : 0;
|
||||
const fallback = estimateFreeDragBlockHeight(b);
|
||||
y += Math.max(measured, fallback) + 22;
|
||||
});
|
||||
}
|
||||
function applyRestaurantTemplateOrder(blocks){
|
||||
const list = Array.isArray(blocks) ? blocks : [];
|
||||
const rank = {
|
||||
@@ -3066,6 +3135,37 @@ const state = {
|
||||
});
|
||||
return indexed.map((x)=>x.b);
|
||||
}
|
||||
function hasRestaurantCoreBlocks(blocks){
|
||||
const list = Array.isArray(blocks) ? blocks : [];
|
||||
const types = new Set(list.map((b)=>b && b.type).filter(Boolean));
|
||||
return types.has("menu") && types.has("hero") && types.has("contact");
|
||||
}
|
||||
function applyTemplateState(key){
|
||||
if (!key || !templates[key]) return false;
|
||||
const t = templates[key];
|
||||
state.settings = { ...state.settings, ...t.settings };
|
||||
state.settings.business_rubro = normalizeRubro(key);
|
||||
if (BUILDER_MODE === "ub24"){
|
||||
state.settings.free_drag = false;
|
||||
} else {
|
||||
// Mantener modo libre en restaurante segun requerimiento.
|
||||
state.settings.free_drag = state.settings.business_rubro === "restaurante";
|
||||
}
|
||||
state.blocks = t.blocks.map((b)=>({
|
||||
...b,
|
||||
id: makeId(),
|
||||
page: (BUILDER_MODE === "ub24" ? "home" : b.page)
|
||||
}));
|
||||
if (state.settings.free_drag){
|
||||
state.blocks = applyRestaurantTemplateOrder(state.blocks);
|
||||
state.settings.restaurant_layout_repaired_v1 = true;
|
||||
}
|
||||
selectedBlockId = null;
|
||||
renderInspector();
|
||||
renderPreview();
|
||||
wireSettings();
|
||||
return true;
|
||||
}
|
||||
async function saveContent(){
|
||||
if (isSaving) return;
|
||||
isSaving = true;
|
||||
@@ -3102,24 +3202,18 @@ const state = {
|
||||
}
|
||||
async function resetBlocks(){
|
||||
if (isSaving) return;
|
||||
const ok = window.confirm("Esto borrara todos los bloques actuales. Deseas continuar?");
|
||||
const ok = window.confirm("Se restaurara la plantilla base del rubro actual. Deseas continuar?");
|
||||
if (!ok) return;
|
||||
const keepRubro = normalizeRubro(state.settings.business_rubro || SERVER_RUBRO || "restaurante");
|
||||
state.blocks = [];
|
||||
state.settings = { ...defaultSettings, business_rubro: keepRubro };
|
||||
if (BUILDER_MODE === "ub24"){
|
||||
state.settings.free_drag = false;
|
||||
} else {
|
||||
state.settings.free_drag = keepRubro === "restaurante";
|
||||
const restored = applyTemplateState(keepRubro);
|
||||
if (!restored){
|
||||
setSaveStatus("No hay plantilla base para este rubro", "error");
|
||||
return;
|
||||
}
|
||||
selectedBlockId = null;
|
||||
renderInspector();
|
||||
renderPreview();
|
||||
wireSettings();
|
||||
setSaveStatus("Reseteando...", "busy");
|
||||
try{
|
||||
await saveDraftSilently();
|
||||
setSaveStatus("Reset aplicado", "ok");
|
||||
setSaveStatus("Plantilla base restaurada", "ok");
|
||||
} catch(_e){
|
||||
setSaveStatus("Error al resetear", "error");
|
||||
}
|
||||
@@ -3147,7 +3241,7 @@ const state = {
|
||||
if (needsTemplateRepair){
|
||||
state.blocks = applyRestaurantTemplateOrder(state.blocks);
|
||||
state.settings.restaurant_layout_repaired_v1 = true;
|
||||
} else if (!hasMeaningfulFreeDragPositions(state.blocks) || hasSevereFreeDragOverlap(state.blocks)){
|
||||
} else if (!hasMeaningfulFreeDragPositions(state.blocks)){
|
||||
applyStackedFreeDragLayout(state.blocks);
|
||||
normalizeDuplicatedFreeDragPositions(state.blocks);
|
||||
}
|
||||
@@ -3170,23 +3264,7 @@ const state = {
|
||||
}
|
||||
const templateSelect = document.getElementById("templateSelect");
|
||||
const applyTemplate = (key)=>{
|
||||
if (!key || !templates[key]) return;
|
||||
const t = templates[key];
|
||||
state.settings = { ...state.settings, ...t.settings };
|
||||
state.settings.business_rubro = normalizeRubro(key);
|
||||
if (BUILDER_MODE === "ub24"){
|
||||
state.settings.free_drag = false;
|
||||
} else {
|
||||
state.settings.free_drag = state.settings.business_rubro === "restaurante";
|
||||
}
|
||||
state.blocks = t.blocks.map(b=>({ ...b, id: makeId(), page: (BUILDER_MODE==="ub24" ? "home" : b.page) }));
|
||||
if (state.settings.free_drag){
|
||||
state.blocks = applyRestaurantTemplateOrder(state.blocks);
|
||||
state.settings.restaurant_layout_repaired_v1 = true;
|
||||
}
|
||||
selectedBlockId = null;
|
||||
renderInspector(); renderPreview();
|
||||
wireSettings();
|
||||
applyTemplateState(key);
|
||||
};
|
||||
if (templateSelect){
|
||||
templateSelect.value = state.settings.business_rubro || "";
|
||||
@@ -3196,18 +3274,34 @@ const state = {
|
||||
}
|
||||
const hasSavedBlocks = Array.isArray(SERVER_CONTENT && SERVER_CONTENT.blocks) && SERVER_CONTENT.blocks.length > 0;
|
||||
const hasSavedSettings = !!(SERVER_CONTENT && SERVER_CONTENT.settings && Object.keys(SERVER_CONTENT.settings).length);
|
||||
const shouldAutoloadTemplate = !hasSavedBlocks && !hasSavedSettings;
|
||||
const looksIncompleteRestaurant = (
|
||||
initialRubro === "restaurante" &&
|
||||
(
|
||||
state.blocks.length < 5 ||
|
||||
!hasRestaurantCoreBlocks(state.blocks) ||
|
||||
!hasMeaningfulFreeDragPositions(state.blocks)
|
||||
)
|
||||
);
|
||||
const shouldAutoloadTemplate = !hasSavedBlocks || looksIncompleteRestaurant;
|
||||
if (!state.blocks.length && templates[initialRubro] && shouldAutoloadTemplate){
|
||||
applyTemplate(initialRubro);
|
||||
if (templateSelect){ templateSelect.value = initialRubro; }
|
||||
} else if (looksIncompleteRestaurant){
|
||||
applyTemplate(initialRubro);
|
||||
if (templateSelect){ templateSelect.value = initialRubro; }
|
||||
}
|
||||
autosaveReady = true;
|
||||
document.getElementById("previewCanvas").addEventListener("click",()=>{ selectedBlockId=null; renderInspector(); renderPreview(); });
|
||||
document.getElementById("btnSave").addEventListener("click",saveContent);
|
||||
const resetBtn = document.getElementById("btnReset");
|
||||
if (resetBtn){ resetBtn.addEventListener("click", resetBlocks); }
|
||||
renderInspector(); renderPreview();
|
||||
scheduleDraftAutosave();
|
||||
}
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user