diff --git a/addons/html_builder/static/src/builder_sidebar/tabs/block_tab/block_tab.js b/addons/html_builder/static/src/builder_sidebar/tabs/block_tab/block_tab.js index c813bcb1df95d..107968af1f1d2 100644 --- a/addons/html_builder/static/src/builder_sidebar/tabs/block_tab/block_tab.js +++ b/addons/html_builder/static/src/builder_sidebar/tabs/block_tab/block_tab.js @@ -91,6 +91,9 @@ export class BlockTab extends Component { this.dropzonePlugin.displayDropZone(snippet); } const addElement = this.dropzonePlugin.getAddElement(position); + if (addElement.noDrop) { + return; + } this.dialog.add( AddSnippetDialog, { diff --git a/addons/html_builder/static/src/plugins/drop_zone_plugin.inside.scss b/addons/html_builder/static/src/plugins/drop_zone_plugin.inside.scss index fdd8f32911025..b64d7d8d3b1f7 100644 --- a/addons/html_builder/static/src/plugins/drop_zone_plugin.inside.scss +++ b/addons/html_builder/static/src/plugins/drop_zone_plugin.inside.scss @@ -1,3 +1,20 @@ +#wrap.o_editable { + .oe_drop_zone.oe_insert:only-child { + $-margin-inline: 2%; + height: 100%; + width: 100% - 2 * $-margin-inline; + padding: 112px 0; + margin: 20px $-margin-inline; + + &::before { + content: attr(data-editor-message); + display: block; + font-size: 1.5rem; + text-align: center; + } + } +} + .oe_drop_zone { height: 10px; background: $o-we-dropzone-bg-color; diff --git a/addons/html_builder/static/src/plugins/drop_zone_plugin.js b/addons/html_builder/static/src/plugins/drop_zone_plugin.js index 1b05b197423ea..55b90a5393eb0 100644 --- a/addons/html_builder/static/src/plugins/drop_zone_plugin.js +++ b/addons/html_builder/static/src/plugins/drop_zone_plugin.js @@ -1,5 +1,6 @@ import { Plugin } from "@html_editor/plugin"; import { isBlock } from "@html_editor/utils/blocks"; +import { _t } from "@web/core/l10n/translation"; import { closest, touching } from "@web/core/utils/ui"; function filterFunction(el, exclude) { @@ -37,6 +38,7 @@ export class DropZonePlugin extends Plugin { resources = { savable_mutation_record_predicates: this.isMutationRecordSavable.bind(this), + normalize_handlers: this.updateEmptyDropZone.bind(this), }; setup() { @@ -89,6 +91,21 @@ export class DropZonePlugin extends Plugin { }; } + get wrapEl() { + return this.document.getElementById("wrap"); + } + + get emptyDropZoneEl() { + return this.wrapEl.querySelector(".oe_drop_zone.oe_insert[data-editor-message]"); + } + + createDropZone() { + const dropZone = this.document.createElement("div"); + dropZone.className = "oe_drop_zone oe_insert"; + this.dropZoneElements.push(dropZone); + return dropZone; + } + displayDropZone(snippet) { this.clearDropZone(); @@ -100,20 +117,16 @@ export class DropZonePlugin extends Plugin { } targets.push(...selectorSiblings); - const createDropZone = () => { - const dropZone = this.document.createElement("div"); - dropZone.className = "oe_drop_zone oe_insert"; - this.dropZoneElements.push(dropZone); - return dropZone; - }; - + if (this.emptyDropZoneEl) { + return; + } for (const target of targets) { if (!target.nextElementSibling?.classList.contains("oe_drop_zone")) { - target.after(createDropZone()); + target.after(this.createDropZone()); } if (!target.previousElementSibling?.classList.contains("oe_drop_zone")) { - target.before(createDropZone()); + target.before(this.createDropZone()); } } } @@ -126,6 +139,17 @@ export class DropZonePlugin extends Plugin { } this.dropZoneElements = []; + this.updateEmptyDropZone(); + } + + updateEmptyDropZone() { + if (this.wrapEl.matches(":empty")) { + const emptyDropZoneEl = this.createDropZone(); + emptyDropZoneEl.dataset.editorMessage = _t("DRAG BUILDING BLOCKS HERE"); + this.wrapEl.appendChild(emptyDropZoneEl); + } else { + this.emptyDropZoneEl?.remove(); + } } dragElement(element) { @@ -142,25 +166,42 @@ export class DropZonePlugin extends Plugin { } getAddElement(position) { + const cancel = () => { + this.clearDropZone(); + const fn = () => {}; + fn.noDrop = true; + return fn; + }; + if (position && !touching([this.document.body], position).length) { + return cancel(); + } const dropZone = position - ? closest(touching(this.dropZoneElements, position), position) + ? closest(touching(this.dropZoneElements, position), position) || + closest(this.dropZoneElements, position) : closest(this.dropZoneElements, { x: window.innerWidth / 2, y: window.innerHeight / 2, }); if (!dropZone) { - this.clearDropZone(); - return () => {}; + return cancel(); + } + + let target, insertMethod; + if (dropZone === this.emptyDropZoneEl) { + insertMethod = "appendChild"; + target = dropZone.parentElement; + } + if (!target) { + insertMethod = "after"; + target = dropZone.previousSibling; } - let target = dropZone.previousSibling; - let addAfter = true; if (!target) { - addAfter = false; + insertMethod = "before"; target = dropZone.nextSibling; } this.clearDropZone(); return (elementToAdd) => { - addAfter ? target.after(elementToAdd) : target.before(elementToAdd); + target[insertMethod](elementToAdd); scrollToWindow(elementToAdd, { behavior: "smooth", offset: 50 }); this.dependencies.history.addStep(); };