From 2db25fcd95765a01505299404eb21ba7c55b8c88 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 10:17:21 +0100 Subject: [PATCH 01/23] LAYOUTS-637 remove add button since its out of scope for current release --- components/block/element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/block/element.js b/components/block/element.js index 6b16865..8e94157 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -240,7 +240,7 @@ export default class Block extends LitElement {
${this.renderMenu()}
${this.renderBreadcrumbs()} - ${this.renderAddButton()} + `; } From 0d5ba7a58a8e847b66fa233382c2b09b77c34482 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 10:31:14 +0100 Subject: [PATCH 02/23] LAYOUTS-637 format code with prettier and eslint --- components/block/element.js | 164 ++++++++++++++++++++++++++---------- components/block/spec.js | 59 ++++++++++--- components/block/style.js | 46 +++++----- package.json | 3 +- 4 files changed, 192 insertions(+), 80 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index 8e94157..f1de421 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -25,17 +25,17 @@ export default class Block extends LitElement { this.isParent = this.model.attributes.is_container; - this.model.on('all', console.debug) + this.model.on('all', console.debug); this.model.on('change', this.refresh.bind(this)); - this.model.on('sidebar:destroyed', () => this.isSelected = false) + this.model.on('sidebar:destroyed', () => (this.isSelected = false)); } get layout() { return this.core.g.layout; } - get core(){ + get core() { return window.parent.Core; } @@ -59,22 +59,67 @@ export default class Block extends LitElement { deselectIcon() { return html` - - - - + + + + - ` + `; } refreshIcon() { return html` - - - + + + - - ` + `; } renderMenu() { @@ -84,8 +129,7 @@ export default class Block extends LitElement { } renderLinkedBlockMenu() { - return html` - ${this.isSelected ? 'Deselect' : 'Select'} - ` + `; return html` ${this.renderOuterBlokcBreadcrumbs()} - ` + `; } renderOuterBlokcBreadcrumbs() { @@ -134,45 +177,70 @@ export default class Block extends LitElement { - ` + `; } breadcrumbArrowIcon() { return html` - - - + + + - ` + `; } renderAddButton() { - if(this.isInLinkedZone) return; + if (this.isInLinkedZone) return; return html` Add - ` + `; } toggleSelect() { - if(this.isSelected) { + if (this.isSelected) { this.isSelected = false; - this.core.trigger('editing:unmark', { block: this.model }) - + this.core.trigger('editing:unmark', {block: this.model}); } else { this.select(); } } select() { - if(this.isInLinkedZone) return; + if (this.isInLinkedZone) return; this.model.trigger('edit'); this.isSelected = true; @@ -190,18 +258,18 @@ export default class Block extends LitElement { } selectOnBlockClick(e) { - if( this.blockId !== this.getClosestBlockId(e.target) ) return; + if (this.blockId !== this.getClosestBlockId(e.target)) return; this.toggleSelect(); } handleMouseover(e) { - if(this.isInLinkedZone) return; + if (this.isInLinkedZone) return; this.isHovered = this.blockId === this.getClosestBlockId(e.target); } - handleMouseout(e) { + handleMouseout() { this.isHovered = false; } @@ -233,10 +301,20 @@ export default class Block extends LitElement { } render() { - const classes = { loading: this.loading, is_selected: this.isSelected, is_child: Boolean(this.parent), is_hovered: this.isHovered, is_parent: this.isParent }; + const classes = { + loading: this.loading, + is_selected: this.isSelected, + is_child: Boolean(this.parent), + is_hovered: this.isHovered, + is_parent: this.isParent, + }; return html` -
+
${this.renderMenu()}
${this.renderBreadcrumbs()} diff --git a/components/block/spec.js b/components/block/spec.js index 84d0131..0473c8d 100644 --- a/components/block/spec.js +++ b/components/block/spec.js @@ -47,15 +47,27 @@ describe('ngl-block', () => { }, }); - const element = await fixture(html``); + const element = await fixture( + html`` + ); assert.shadowDom.equal( element, `
- - + + +
+
@@ -75,14 +87,18 @@ describe('ngl-block', () => { }, }); - const element = await fixture(html``); + const element = await fixture( + html`` + ); assert.shadowDom.equal( element, `
- +
@@ -102,17 +118,30 @@ describe('ngl-block', () => { }, }); - const element = await fixture(html``); + const element = await fixture( + html`` + ); assert.shadowDom.equal( element, `
- - - - + + +
+
@@ -132,12 +161,18 @@ describe('ngl-block', () => { }, }); - const element = await fixture(html``); + const element = await fixture( + html`` + ); // Here we stub method fetch to return our test HTML sinon .stub(element, 'fetch') - .resolves('Hello world'); + .resolves( + 'Hello world' + ); await element.refresh(); diff --git a/components/block/style.js b/components/block/style.js index 3057312..4001a0f 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -4,24 +4,23 @@ export default css` :host, :after, :before { - --ngl-block-base-z-index: 80000; // was this randomly chosen? - --ngl-block-priamry-color: #9747FF; + --ngl-block-priamry-color: #9747ff; --ngl-block-outline-color: #333; --ngl-block-outline-color-hover: #990099; - --ngl-block-outline-color-selected: #9747FF; - --ngl-block-outline-width: .125rem; + --ngl-block-outline-color-selected: #9747ff; + --ngl-block-outline-width: 0.125rem; --ngl-block-background-color-selected: rgba(151, 71, 255, 0.1); - --ngl-block-button-background-color: #9747FF; - --ngl-block-button-background-color-hover: #7625DF; + --ngl-block-button-background-color: #9747ff; + --ngl-block-button-background-color-hover: #7625df; } :host { - display: contents + display: contents; } main { @@ -57,7 +56,6 @@ export default css` z-index: 2; } - main:not(.is_selected).is_hovered ::slotted(.ngl-block):before { background-color: var(--ngl-block-background-color-selected); z-index: 80000; @@ -68,12 +66,12 @@ export default css` } main.is_selected ::slotted(.ngl-block):after { - border: solid var(--ngl-block-outline-width) var(--ngl-block-outline-color-selected); + border: solid var(--ngl-block-outline-width) + var(--ngl-block-outline-color-selected); } main:not(.is_selected).is_hovered ::slotted(.ngl-block):after { border: solid 1px var(--ngl-block-outline-color-hover); - } .edit-menu { @@ -84,7 +82,7 @@ export default css` position: absolute; right: 0; bottom: 100%; - gap: .25rem; + gap: 0.25rem; } main.is_selected .edit-menu, main.is_hovered .edit-menu { @@ -98,23 +96,23 @@ export default css` background-color: var(--_btn-background-color); border: none; margin: 0; - padding: .125rem .5rem; - font-size: .75rem; + padding: 0.125rem 0.5rem; + font-size: 0.75rem; font-weight: 500; color: #ffffff; - gap: .25rem; + gap: 0.25rem; display: flex; align-items: center; justify-content: center; text-align: center; - border-radius: .125rem .125rem 0 0; + border-radius: 0.125rem 0.125rem 0 0; cursor: pointer; position: relative; } button.refresh-btn { - padding-left: .125rem; - padding-right: .125rem; + padding-left: 0.125rem; + padding-right: 0.125rem; } button:hover { @@ -137,18 +135,18 @@ export default css` } main button.breadcrumb-btn { - --_btn-background-color: var(--ngl-block-button-background-color); - padding: .25rem .5rem; - gap: .25rem; + --_btn-background-color: var(--ngl-block-button-background-color); + padding: 0.25rem 0.5rem; + gap: 0.25rem; color: #fff; - border-radius: .125rem .125rem 0 0; + border-radius: 0.125rem 0.125rem 0 0; } main button.breadcrumb-btn:not(:last-child):hover { --_btn-background-color: var(--ngl-block-button-background-color-hover); } main button.breadcrumb-btn:not(:first-child) { - padding-right: .75rem; + padding-right: 0.75rem; padding-left: 1.5rem; } @@ -163,12 +161,12 @@ export default css` bottom: 0; left: 50%; transform: translate(-50%, calc(50% - 2px)); - padding: .125rem .5rem .125rem .25rem; + padding: 0.125rem 0.5rem 0.125rem 0.25rem; display: none; opacity: 0; z-index: calc(var(--ngl-block-base-z-index) + 3); visibility: hidden; - border-radius: .125rem; + border-radius: 0.125rem; } main.is_selected .add-btn { diff --git a/package.json b/package.json index f32eb85..8da5e48 100644 --- a/package.json +++ b/package.json @@ -74,5 +74,6 @@ "test:prod": "MODE=prod wtr", "test:prod:watch": "MODE=prod wtr --watch", "checksize": "rollup -c ; cat components.bundled.js | gzip -9 | wc -c ; rm components.bundled.js" - } + }, + "customElements": "custom-elements.json" } From 9132550ec98b52b1df1cb94e25886d81989021e2 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 10:35:17 +0100 Subject: [PATCH 03/23] LAYOUTS-637 remove console logs --- components/block/element.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index f1de421..ed329a5 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -25,8 +25,6 @@ export default class Block extends LitElement { this.isParent = this.model.attributes.is_container; - this.model.on('all', console.debug); - this.model.on('change', this.refresh.bind(this)); this.model.on('sidebar:destroyed', () => (this.isSelected = false)); } From 8c552be124ef2760ac9a65861ea4cdc263d9c9e8 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 11:20:14 +0100 Subject: [PATCH 04/23] LAYOUTS-637 move logic for dispalying view type names entirely to js instead of grabing it from html template --- components/block/element.js | 28 ++++++++++++++++++++++++---- components/block/style.js | 1 + 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index ed329a5..a8a2f4d 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -8,7 +8,6 @@ export default class Block extends LitElement { static properties = { loading: {type: Boolean, state: true}, blockId: {type: String}, - templateName: {type: String}, isSelected: {type: Boolean, state: true}, isHovered: {type: Boolean, state: true}, isParent: {type: Boolean, state: true}, @@ -51,10 +50,29 @@ export default class Block extends LitElement { return this.cached_parent; } + get parentName() { + if(!Boolean(this.parent)) return; + + const parentId = this.model.attributes.parent_block_id; + const parentObject = this.layout.blocks.findWhere({ + id: parentId, + }) + + return this.formatViewTypeName(parentObject.attributes.definition_identifier); + } + + get viewTypeName() { + return this.formatViewTypeName(this.model.attributes.definition_identifier) + } + get isInLinkedZone() { return this.model.zone().is_linked(); } + formatViewTypeName(name) { + return name.replace('_', ' ') + } + deselectIcon() { return html` - ${this.parent.getAttribute('templatename')} + ${this.parentName} ${this.breadcrumbArrowIcon()} ${this.renderOuterBlokcBreadcrumbs()} @@ -173,7 +191,7 @@ export default class Block extends LitElement { renderOuterBlokcBreadcrumbs() { return html` `; } @@ -238,6 +256,9 @@ export default class Block extends LitElement { } select() { + console.debug(this.model, this.parent, this.layout.blocks.findWhere({ + id: this.model.attributes.parent_block_id, + })) if (this.isInLinkedZone) return; this.model.trigger('edit'); @@ -302,7 +323,6 @@ export default class Block extends LitElement { const classes = { loading: this.loading, is_selected: this.isSelected, - is_child: Boolean(this.parent), is_hovered: this.isHovered, is_parent: this.isParent, }; diff --git a/components/block/style.js b/components/block/style.js index 4001a0f..38edb88 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -140,6 +140,7 @@ export default css` gap: 0.25rem; color: #fff; border-radius: 0.125rem 0.125rem 0 0; + text-transform: capitalize; } main button.breadcrumb-btn:not(:last-child):hover { --_btn-background-color: var(--ngl-block-button-background-color-hover); From b3f505d09d1d418e7048b0a40eb1c55bce70c942 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 11:21:48 +0100 Subject: [PATCH 05/23] LAYOUTS-637 format code and remove console logs --- components/block/element.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index a8a2f4d..f74cc90 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -51,18 +51,20 @@ export default class Block extends LitElement { } get parentName() { - if(!Boolean(this.parent)) return; + if (!Boolean(this.parent)) return; const parentId = this.model.attributes.parent_block_id; const parentObject = this.layout.blocks.findWhere({ id: parentId, - }) + }); - return this.formatViewTypeName(parentObject.attributes.definition_identifier); + return this.formatViewTypeName( + parentObject.attributes.definition_identifier + ); } get viewTypeName() { - return this.formatViewTypeName(this.model.attributes.definition_identifier) + return this.formatViewTypeName(this.model.attributes.definition_identifier); } get isInLinkedZone() { @@ -70,7 +72,7 @@ export default class Block extends LitElement { } formatViewTypeName(name) { - return name.replace('_', ' ') + return name.replace('_', ' '); } deselectIcon() { @@ -256,9 +258,6 @@ export default class Block extends LitElement { } select() { - console.debug(this.model, this.parent, this.layout.blocks.findWhere({ - id: this.model.attributes.parent_block_id, - })) if (this.isInLinkedZone) return; this.model.trigger('edit'); From e7b4e33c4c4082002f9ff207225a3465ea0e881a Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 11:23:14 +0100 Subject: [PATCH 06/23] LAYOUTS-637 remove add button from render function of ngl blocks --- components/block/element.js | 1 - 1 file changed, 1 deletion(-) diff --git a/components/block/element.js b/components/block/element.js index f74cc90..0286b14 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -335,7 +335,6 @@ export default class Block extends LitElement {
${this.renderMenu()}
${this.renderBreadcrumbs()} -
`; } From 6be4dfdd74196d0b50b98e60d41b6b44946a5988 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 8 Nov 2022 11:30:13 +0100 Subject: [PATCH 07/23] LAYOUTS-637 prevent format function for running if no name is passed to it --- components/block/element.js | 1 + 1 file changed, 1 insertion(+) diff --git a/components/block/element.js b/components/block/element.js index 0286b14..ecd5056 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -72,6 +72,7 @@ export default class Block extends LitElement { } formatViewTypeName(name) { + if (!Boolean(name)) return; return name.replace('_', ' '); } From 41b03365f396fa39af17bf90f4911f75540e1d01 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Mon, 14 Nov 2022 12:46:34 +0100 Subject: [PATCH 08/23] LAYOUTS-637 make small changes according to comments on github --- components/block/element.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index ecd5056..4ad906b 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -51,7 +51,7 @@ export default class Block extends LitElement { } get parentName() { - if (!Boolean(this.parent)) return; + if (!this.parent) return; const parentId = this.model.attributes.parent_block_id; const parentObject = this.layout.blocks.findWhere({ @@ -72,7 +72,7 @@ export default class Block extends LitElement { } formatViewTypeName(name) { - if (!Boolean(name)) return; + if (!name) return; return name.replace('_', ' '); } @@ -148,9 +148,11 @@ export default class Block extends LitElement { } renderLinkedBlockMenu() { - return html` `; + return html` + + `; } renderOuterBlockMenu() { From 918a0b7b268dc2f5a4c562d0bd5f74c13f3896d4 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Mon, 14 Nov 2022 13:31:41 +0100 Subject: [PATCH 09/23] LAYOUTS-637 revert logic for displaying name to data attribute since there is no view type name parametar inside block object --- components/block/element.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index 4ad906b..cf154fe 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -8,6 +8,7 @@ export default class Block extends LitElement { static properties = { loading: {type: Boolean, state: true}, blockId: {type: String}, + viewTypeName: {type: String}, isSelected: {type: Boolean, state: true}, isHovered: {type: Boolean, state: true}, isParent: {type: Boolean, state: true}, @@ -53,29 +54,13 @@ export default class Block extends LitElement { get parentName() { if (!this.parent) return; - const parentId = this.model.attributes.parent_block_id; - const parentObject = this.layout.blocks.findWhere({ - id: parentId, - }); - - return this.formatViewTypeName( - parentObject.attributes.definition_identifier - ); - } - - get viewTypeName() { - return this.formatViewTypeName(this.model.attributes.definition_identifier); + return this.parent.getAttribute('viewTypeName') } get isInLinkedZone() { return this.model.zone().is_linked(); } - formatViewTypeName(name) { - if (!name) return; - return name.replace('_', ' '); - } - deselectIcon() { return html` Date: Tue, 22 Nov 2022 19:34:04 +0100 Subject: [PATCH 10/23] Only initialize component if in preview context --- components/block/element.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/block/element.js b/components/block/element.js index cf154fe..05eca95 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -21,6 +21,10 @@ export default class Block extends LitElement { } connectedCallback() { + if (typeof window.parent.Core === 'undefined') { + return; + } + super.connectedCallback(); this.isParent = this.model.attributes.is_container; From 1bd5da628ce7c992523feb7e3a70c7a1a1b09a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edi=20Modri=C4=87?= Date: Wed, 23 Nov 2022 11:44:07 +0100 Subject: [PATCH 11/23] Rename ngl:refresh event to be more specific --- components/block/element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/block/element.js b/components/block/element.js index 05eca95..0ed911a 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -302,7 +302,7 @@ export default class Block extends LitElement { this.innerHTML = currentBlockHtml.innerHTML; this.dispatchEvent( - new Event('ngl:refresh', {bubbles: true, composed: true}) + new Event('ngl:preview:block:refresh', {bubbles: true, composed: true}) ); } From 55f2a57406702a64e755d34640c3259b6fd58780 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 6 Dec 2022 14:21:29 +0100 Subject: [PATCH 12/23] LAYOUTS-661 cover all cases for different states of blocks in preview mode --- components.js | 2 + components/block-picker/element.js | 218 ++++++++ components/block-picker/spec.js | 0 components/block-picker/style.js | 264 +++++++++ components/block/element.js | 467 +++++++++------- components/block/style.js | 81 ++- components/icons.js | 91 ++++ components/placeholder/element.js | 80 +++ components/placeholder/spec.js | 0 components/placeholder/style.js | 81 +++ custom-elements.json | 835 +++++++++++++++++++++++++++++ 11 files changed, 1909 insertions(+), 210 deletions(-) create mode 100644 components/block-picker/element.js create mode 100644 components/block-picker/spec.js create mode 100644 components/block-picker/style.js create mode 100644 components/icons.js create mode 100644 components/placeholder/element.js create mode 100644 components/placeholder/spec.js create mode 100644 components/placeholder/style.js create mode 100644 custom-elements.json diff --git a/components.js b/components.js index d09a7d7..2e1d14e 100644 --- a/components.js +++ b/components.js @@ -1 +1,3 @@ export {default as Block} from './components/block/element.js'; +export {default as Placeholder} from './components/placeholder/element.js'; +export {default as BlockPicker} from './components/block-picker/element.js'; diff --git a/components/block-picker/element.js b/components/block-picker/element.js new file mode 100644 index 0000000..14919bb --- /dev/null +++ b/components/block-picker/element.js @@ -0,0 +1,218 @@ +import {LitElement, html} from 'lit'; +import {classMap} from 'lit/directives/class-map.js'; +import style from './style.js'; + +export default class BlockPicker extends LitElement { + static styles = [style]; + + static properties = { + blockId: {type: String}, + isActive: {type: Boolean, state: true, default: false}, + isLoading: {type: Boolean, state: true}, + placeholderIdentifier: {type: String} + }; + + constructor() { + super(); + } + + connectedCallback() { + super.connectedCallback(); + } + + + get core() { + return window.parent.Core; + } + + get layout() { + return this.core.g.layout; + } + + get zone() { + return this.layout.zones.models.find(model => model.id === this.block.attributes.zone_identifier) + } + + get blockGroups() { + return this.core.g.block_types.groups.models + } + + get block() { + return this.layout.blocks._byId[this.blockId]; + } + + get iframe() { + const iframe = window.parent.document.querySelector('.preview-iframe-sizer iframe') + return iframe; + } + + handleClose() { + this.isActive = false + this.iframe.contentDocument.querySelector('body').style.overflowY = 'auto'; + } + + handleOpen() { + this.isActive = true + this.iframe.contentDocument.querySelector('body').style.overflowY = 'hidden'; + } + + renderCloseBtn() { + return html` + + close + + ` + } + + renderBlockGroups() { + if(!this.isActive) return; + + return html` +
+
+ ${this.renderCloseBtn()} + ${this.blockGroups.map(group => this.renderBlockGroup(group))} +
+
+ ` + } + + renderBlockGroup(group) { + return html` +
+

${group.attributes.name}

+
+
+ ${group._block_types.map((blockType, index) => this.renderBlockType(blockType, index))} +
+ ` + } + + renderBlockType(blockType, index) { + return html` +
+ + ${blockType.attributes.name} +
+ ` + } + + renderLoader() { + if(!this.isLoading) return; + + return html` +
+
+ + Loading +
+
+ ` + } + + + handleCreateBlockFromType(blockType) { + const identifier = blockType.get('identifier'); + const isContainer = this.block.attributes.is_container && this.placeholderIdentifier; + const parentBlockId = isContainer ? this.blockId : this.block.attributes.parent_block_id + + const attributes = { + block_type: identifier, + zone_identifier: this.zone.id, + layout_id: this.layout.id, + parent_position: isContainer ? 0 : this.block.attributes.parent_position + 1, + parent_placeholder: isContainer ? this.placeholderIdentifier : this.block.attributes.parent_placeholder, + parent_block_id: parentBlockId + }; + + const newBlock = this.core.model_helper.init_block_from_type(blockType, attributes); + + let responseData = {}; + + const saveInContainer = isContainer || this.block.attributes.parent_block_id + this.isLoading = true; + + this.handleSaveNewBlock(newBlock, saveInContainer, parentBlockId) + .then((res) => { + responseData = res + this.layout.reset_blocks_loaded() + return this.layout.load_all_blocks() + }) + .then(() => { + this.isActive = false; + this.handleRefreshView(responseData.id) + }) + } + + handleSaveNewBlock(newBlock, saveInContainer, blockId) { + if(saveInContainer) { + console.warn('Save in container'); + return newBlock.save({}, { url: newBlock.url(blockId) } ) + } else { + console.warn('Save') + return newBlock.save() + } + } + + + async handleRefreshView(blockId) { + + return await fetch(window.location.href) + .then(resp => { + return resp.text() + }) + .then(html => { + const template = document.createElement('template'); + template.innerHTML = html; + + const futurePage = template.content.querySelector( + '#page' + ); + const iframe = window.parent.document.querySelector('.preview-iframe-sizer iframe') + const currentPage = iframe.contentDocument.querySelector( + '#page' + ); + + currentPage.innerHTML = futurePage.innerHTML + + this.dispatchEvent( + new Event('ngl:refresh', {bubbles: true, composed: true}) + ); + + const block = this.layout.blocks._byId[blockId] + const blockElement = iframe.contentDocument.querySelector( + `ngl-block[blockId="${blockId}"]` + ) + blockElement.isSelected = true; + block.trigger('edit'); + }) + .catch(err => console.error(err)) + .finally(() => { + this.isLoading = false + this.placeholderIdentifier = "" + }) + } + + updated(changedProperties) { + if(!this.isActive) return; + + this.handleOpen() + } + + render() { + const classes = { + is_active: this.isActive, + is_loading: this.isLoading, + }; + + return html` +
this.isActive = false}> + ${this.renderBlockGroups()} + +
+ ${this.renderLoader()} + `; + } +} + +customElements.get('ngl-block-picker') || customElements.define('ngl-block-picker', BlockPicker); \ No newline at end of file diff --git a/components/block-picker/spec.js b/components/block-picker/spec.js new file mode 100644 index 0000000..e69de29 diff --git a/components/block-picker/style.js b/components/block-picker/style.js new file mode 100644 index 0000000..baa7c6a --- /dev/null +++ b/components/block-picker/style.js @@ -0,0 +1,264 @@ +import {css} from 'lit'; + +export default css` + :host, + :after, + :before { + --ngl-block-picker-width: 26.625rem; + --ngl-block-picker-gutter: 0.9375rem; + --ngl-block-picker-background-color: #383838; + --ngl-block-picker-border-color: #4f4f4f; + --ngl-block-picker-block-background-color: #4f4f4f; + --ngl-block-picker-text-color: #9c9c9c; + } + + main { + display: none; + position: fixed; + z-index: 80050; + inset: 0; + background-color: rgba(0, 0, 0, 0.7); + } + + main.is_active { + display: block; + } + + .panel { + position: absolute; + left: 50%; + top: 50%; + max-height: 90vh; + border-top: 1px solid var(--ngl-block-picker-border-color); + width: var(--ngl-block-picker-width); + background: var(--ngl-block-picker-background-color); + color: var(--ngl-block-picker-text-color); + overflow-y: hidden; + transform: translate(-50%, -200vh); + } + + main.is_active .panel { + transform: translate(-50%, -50%); + transition: transform 350ms 150ms ease; + } + + .panel-content { + padding: var(--ngl-block-picker-gutter); + max-height: 90vh; + overflow-y: scroll; + } + + .close-panel { + position: absolute; + right: .25em; + top: .25em; + width: 1.5em; + height: 1.5em; + cursor: pointer; + text-align: right; + color: var(--ngl-block-picker-text-color); + z-index: 2; + } + + .close-panel i { + font-size: 1.125rem; + } + + .close-panel:hover { + color: #fff; + } + + .header { + position: relative; + } + + .header h2 { + font-size: .75em; + font-weight: 500; + line-height: 1.25; + text-transform: uppercase; + margin: 0 0 .5em; + color: inherit; + } + + .block-items { + margin: 0 0 1.5em; + display: flex; + flex-wrap: wrap; + } + + .add-block-btn { + border: 0; + border-bottom: 1px solid var(--ngl-block-picker-background-color); + border-right: 1px solid var(--ngl-block-picker-background-color); + width: calc( ( var(--ngl-block-picker-width) - 2 * var(--ngl-block-picker-gutter) - 44px )/ 4); + background-color: var(--ngl-block-picker-block-background-color); + border: 1px solid var(--ngl-block-picker-background-color); + aspect-ratio: 1/1; + color: var(--ngl-block-picker-text-color); + text-align: center; + padding: 0.25em 0.25em 0.625em; + transition: background .15s ease, color .15s ease; + cursor: pointer; + } + + .add-block-btn:hover { + background: hsl(0, 0, 36); + color: #fff; + } + + .add-block-btn .icon { + font-size: 2em; + height: 1.5em; + line-height: 1.25; + margin-bottom: .125em; + padding: .125em; + display: flex; + align-items: center; + justify-content: center; + } + + .add-block-btn .icon img { + max-width: 100%; + max-height: 100%; + display: block; + } + + .add-block-btn:nth-child(4n) { + border-right: 0; + } + + .loader-container { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, .75); + display: flex; + align-items: center; + justify-content: center; + z-index: 80100; + color: white; + } + + .loading-ng-icon { + display:inline-block; + position:relative; + font-size:1em; + width:1.375em; + height:2.375em; + margin:0em 1.5em -0.3em; + transform:rotate(-48deg); + animation:loadRotate 1.5s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95); + } + + .loading-ng-icon::before, + .loading-ng-icon::after { + content:""; + display:block; + background:currentColor; + border-radius:50%; + position:absolute; + left:50%; + } + + .loading-ng-icon::before { + width:1em; + height:1em; + margin-left:-0.5em; + bottom:1.375em; + animation:loadBounceTopSquash 0.75s alternate infinite ease, loadBounceTopFlow 0.75s alternate infinite ease; + } + + .loading-ng-icon::after { + width:1.375em; + height:1.375em; + margin-left:-0.6875em; + bottom:0; + animation:loadBounceBottomSquash 0.75s alternate infinite ease, loadBounceBottomFlow 0.75s alternate infinite ease; + } + + @keyframes loadBounceTopSquash { + 0% { + height:0.375em; + border-radius:3.75em 3.75em 1.25em 1.25em; + transform:scaleX(2); + } + 15% { + height:1em; + border-radius:50%; + transform:scaleX(1); + } + 100% { + height:1em; + border-radius:50%; + transform:scaleX(1); + } + } + + @keyframes loadBounceBottomSquash { + 0% { + height:1em; + border-radius:1.25em 1.25em 3.75em 3.75em; + transform:scaleX(1.5); + } + 15% { + height:1.375em; + border-radius:50%; + transform:scaleX(1); + } + 100% { + height:1.375em; + border-radius:50%; + transform:scaleX(1); + } + } + + @keyframes loadBounceTopFlow { + 0% { + bottom:1.125em; + } + 50% { + bottom:2.25em; + animation-timing-function:cubic-bezier(0.55, 0.06, 0.68, 0.19); + } + 90% { + bottom:1.75em; + } + 100% { + bottom:1.75em; + } + } + + @keyframes loadBounceBottomFlow { + 0% { + bottom:0.1875em; + } + 50% { + bottom:-0.9375em; + animation-timing-function:cubic-bezier(0.55, 0.06, 0.68, 0.19); + } + 90% { + bottom:0em; + } + 100% { + bottom:0em; + } + } + + @keyframes loadRotate { + 0% { + transform:rotate(-228deg); + } + 49% { + transform:rotate(-48deg); + } + 51% { + transform:rotate(-48deg); + } + 92% { + transform:rotate(132deg); + } + 100% { + transform:rotate(132deg); + } + } +` \ No newline at end of file diff --git a/components/block/element.js b/components/block/element.js index 0ed911a..ab595e4 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -2,6 +2,8 @@ import {LitElement, html} from 'lit'; import {classMap} from 'lit/directives/class-map.js'; import style from './style.js'; +import { ArrowDownIcon, ArrowUpIcon, BreadcrumbArrowIcon, PlusIcon, RefreshIcon } from '../icons.js'; + export default class Block extends LitElement { static styles = [style]; @@ -9,46 +11,63 @@ export default class Block extends LitElement { loading: {type: Boolean, state: true}, blockId: {type: String}, viewTypeName: {type: String}, - isSelected: {type: Boolean, state: true}, + isSelected: {type: Boolean}, isHovered: {type: Boolean, state: true}, - isParent: {type: Boolean, state: true}, + isContainer: {type: Boolean, state: true}, + isCollectionEmpty: {type: Boolean}, + isContainerEmpty: {type: Boolean, default: false}, + isContainerSelected: {type: Boolean, default: false}, + isChildSelected: {type: Boolean, default: false}, }; constructor() { super(); - this.loading = false; + this.loading = false; } - + connectedCallback() { if (typeof window.parent.Core === 'undefined') { return; } super.connectedCallback(); - - this.isParent = this.model.attributes.is_container; - + + this.isContainer = this.model.attributes.is_container; this.model.on('change', this.refresh.bind(this)); - this.model.on('sidebar:destroyed', () => (this.isSelected = false)); + this.model.on('sidebar:destroyed', () => { + this.isSelected = false; + this.unmarkPlaceholders(); + this.setIsContainerSelected(false) + this.setIsChildSelected(false) + }); } - get layout() { - return this.core.g.layout; - } + // GETTERS - start get core() { return window.parent.Core; } + get layout() { + return this.core.g.layout; + } + get model() { return (this.cached_model ||= this.layout.blocks.findWhere({ id: this.blockId, })); } - get parent() { + get parentModel() { + const parentId = this.model.attributes.parent_block_id; + + return this.layout.blocks.models.find(model => model.id === parentId) + } + + get parentElement() { const parentId = this.model.attributes.parent_block_id; + this.cached_parent ||= parentId && document.querySelector(`ngl-block[blockId="${parentId}"]`); @@ -65,178 +84,73 @@ export default class Block extends LitElement { return this.model.zone().is_linked(); } - deselectIcon() { - return html` - - - - - - `; + get slot() { + return this.shadowRoot.querySelector('slot'); } - refreshIcon() { - return html` - - - - - `; + get slottedChildren() { + return this.slot.assignedElements({flatten: true}); } - renderMenu() { - if (this.isInLinkedZone) return this.renderLinkedBlockMenu(); - - return this.renderOuterBlockMenu(); + get placeholders() { + return this.slottedChildren[0].querySelectorAll('ngl-placeholder') } - renderLinkedBlockMenu() { - return html` - - `; + get childBlocks() { + return this.slottedChildren[0].querySelectorAll('ngl-block') } + // GETTERS - end - renderOuterBlockMenu() { - const label = html` - ${this.isSelected ? 'Deselect' : 'Select'} - `; - return html` - - - `; + async fetch() { + this.loading = true; + const resp = await fetch(window.location.href); + this.loading = false; + return resp.text(); } - renderBreadcrumbs() { - if (this.isInLinkedZone) return; + async refresh() { + let html = await this.fetch(); - return html` - - `; - } + const template = document.createElement('template'); + template.innerHTML = html; + const currentBlockHtml = template.content.querySelector( + `ngl-block[blockId="${this.blockId}"]` + ); - renderInnerBlokcBreadcrumbs() { - return html` - - ${this.renderOuterBlokcBreadcrumbs()} - - `; + this.innerHTML = currentBlockHtml.innerHTML; + + this.dispatchEvent( + new Event('ngl:preview:block:refresh', {bubbles: true, composed: true}) + ); + this.markPlaceholders(); + this.setIsContainerSelected(true) + this.setIsChildSelected(true) } - renderOuterBlokcBreadcrumbs() { - return html` - - `; + + setIsEmptyState() { + if(!this.isContainer) return; + + const areAllPlaceholdersEmpty = [...this.placeholders].every(el => el.isEmpty) + const areAllChildBlocksEmpty = [...this.childBlocks].every(el => el.isCollectionEmpty) + + this.isCollectionEmpty = areAllPlaceholdersEmpty || areAllChildBlocksEmpty; + + this.setChildBlocksIsEmptyState(); } - breadcrumbArrowIcon() { - return html` - - - - - `; + setChildBlocksIsEmptyState() { + if(!this.isCollectionEmpty) return; + + [...this.placeholders, ...this.childBlocks].map(el => el.isContainerEmpty = true) } - renderAddButton() { - if (this.isInLinkedZone) return; + handleAddButtonClick() { + const blockPicker = document.querySelector('ngl-block-picker'); - return html` - - `; + blockPicker.blockId = this.blockId; + blockPicker.isActive = true; } toggleSelect() { @@ -244,8 +158,14 @@ export default class Block extends LitElement { this.isSelected = false; this.core.trigger('editing:unmark', {block: this.model}); + this.unmarkPlaceholders(); + this.setIsContainerSelected(false) + this.setIsChildSelected(false) } else { this.select(); + this.markPlaceholders(); + this.setIsContainerSelected(true) + this.setIsChildSelected(true) } } @@ -260,6 +180,21 @@ export default class Block extends LitElement { this.parent.select(); } + setIsChildSelected(selected) { + if(this.isContainer) return; + + const parentBlock = document.querySelector(`ngl-block[blockId="${this.blockId}"]`)?.parentElement?.closest('ngl-block') + if(!parentBlock) return; + + parentBlock.isChildSelected = selected; + } + + setIsContainerSelected(selected) { + if(!this.isContainer) return; + + this.childBlocks.forEach(el => el.isContainerSelected = selected) + } + getClosestBlockId(el) { const closestBlock = el.closest('ngl-block'); const closestBlockId = closestBlock?.getAttribute('blockid'); @@ -273,6 +208,19 @@ export default class Block extends LitElement { this.toggleSelect(); } + markPlaceholders() { + this.toggleMarkPlaceholders(true) + } + + unmarkPlaceholders() { + this.toggleMarkPlaceholders(false) + } + + toggleMarkPlaceholders(mark) { + this.placeholders.forEach(el => el.isMarked = mark) + } + + // EVENTS - start handleMouseover(e) { if (this.isInLinkedZone) return; @@ -283,39 +231,187 @@ export default class Block extends LitElement { this.isHovered = false; } - async fetch() { - this.loading = true; - const resp = await fetch(window.location.href); - this.loading = false; - return resp.text(); + handleMoveBlock(direction) { + const directionNumber = direction === 'up' ? -1 : 1; + + + let blockIds = [...this.model.zone().attributes.block_ids]; + const fromIndex = blockIds.findIndex(id => id === this.blockId) + const toIndex = fromIndex - 1; + + blockIds.splice(fromIndex, 1) + blockIds.splice(toIndex, 0, this.blockId) + + if(this.parent) { + this.model.set({ + parent_position: this.model.attributes.parent_position + directionNumber, + zone_identifier: this.model.attributes.zone_identifier, + parent_placeholder: this.model.attributes.placeholder_id, + parent_block_id: this.model.attributes.parent_block_id + }); + this.model.move_to_container(blockIds) + .then(() => { + const iframe = window.parent.document.querySelector('.preview-iframe-sizer iframe') + iframe?.contentWindow.location.reload() + }) + } else { + this.model.set({ + parent_position: this.model.attributes.parent_position + directionNumber, + zone_identifier: this.model.attributes.zone_identifier + }); + this.model.move(blockIds) + .then(() => { + this.handleRefreshView() + }) + } + } - async refresh() { - let html = await this.fetch(); + async handleRefreshView() { + + return await fetch(window.location.href) + .then(resp => { + return resp.text() + }) + .then(html => { + const template = document.createElement('template'); + template.innerHTML = html; + + const futurePage = template.content.querySelector( + '#page' + ); + const iframe = window.parent.document.querySelector('.preview-iframe-sizer iframe') + const currentPage = iframe.contentDocument.querySelector( + '#page' + ); + currentPage.innerHTML = futurePage.innerHTML + + const blockElement = iframe.contentDocument.querySelector( + `ngl-block[blockId="${this.blockId}"]` + ) + blockElement.isSelected = true; + this.model.trigger('edit'); + }) + .catch(err => console.error(err)) + .finally(() => { + this.isLoading = false + this.placeholderIdentifier = "" + }) + } - const template = document.createElement('template'); - template.innerHTML = html; - const currentBlockHtml = template.content.querySelector( - `ngl-block[blockId="${this.blockId}"]` - ); + handleMoveBlockUp() { + const parentPosition = this.model.attributes.parent_position + if(parentPosition === 0) this.handleMoveBlockToZoneAbove() + + this.handleMoveBlock('up'); + } - this.innerHTML = currentBlockHtml.innerHTML; + handleMoveBlockDown() { + const parentPosition = this.model.attributes.parent_position + const numberOfBlocks = this.model.zone().attributes.block_ids.length - 1 + if(parentPosition === numberOfBlocks) this.handleMoveBlockToZoneBelow() + + this.handleMoveBlock('down'); + } - this.dispatchEvent( - new Event('ngl:preview:block:refresh', {bubbles: true, composed: true}) - ); + handleMoveBlockToZoneAbove() { + // @todo: implement move block to zone above + } + + handleMoveBlockToZoneBelow() { + // @todo: implement move block to zone below + } + // EVENTS - end + + // RENDERS - start + renderAddButton() { + if (this.isInLinkedZone) return; + + return html` + + `; + } + + renderMoveButtons() { + return html` +
+ + +
+ ` + } + + renderMenu() { + if (this.isInLinkedZone) return this.renderLinkedBlockMenu(); + + return this.renderOuterBlockMenu(); + } + + renderLinkedBlockMenu() { + return html` + + `; + } + + renderOuterBlockMenu() { + return html` + + `; + } + + renderBreadcrumbs() { + if (this.isInLinkedZone) return; + + return html` + + `; + } + + renderInnerBlokcBreadcrumbs() { + return html` + + ${this.renderOuterBlokcBreadcrumbs()} + + `; } - parentRefresh() { - this.parent.refresh(); + renderOuterBlokcBreadcrumbs() { + return html` + + `; } + // RENDERS - end render() { const classes = { loading: this.loading, is_selected: this.isSelected, is_hovered: this.isHovered, - is_parent: this.isParent, + is_container: this.isContainer, + is_collection_empty: this.isCollectionEmpty, + is_container_empty: this.isContainerEmpty, + is_container_selected: this.isContainerSelected, + is_child_selected: this.isChildSelected, }; return html` @@ -324,9 +420,10 @@ export default class Block extends LitElement { @mouseover=${this.handleMouseover} @mouseout=${this.handleMouseout} > -
${this.renderMenu()}
${this.renderBreadcrumbs()} - +
${this.renderMenu()}
+ + ${this.renderAddButton()}
`; } diff --git a/components/block/style.js b/components/block/style.js index 38edb88..722135e 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -25,57 +25,68 @@ export default css` main { position: relative; - /* z-index: 1; */ } - /* main main { - z-index: 3; - } */ - main.loading { opacity: 0.5; } - main ::slotted(.ngl-block):before { + main ::slotted(.ngl-block):before, + main ::slotted(.ngl-block):after { content: ''; position: absolute; inset: 0; - z-index: 0; + z-index: calc(var(--ngl-block-base-z-index, 80000)); + cursor: pointer; } - main ::slotted(.ngl-block):after { - content: ''; - inset: 0; - position: absolute; + main ::slotted(.ngl-block):before { pointer-events: none; - z-index: calc(var(--ngl-block-base-z-index, 80000) + 1); } - main.is_selected ::slotted(.ngl-block):before { + main.is_selected ::slotted(.ngl-block):before, + main.is_selected ::slotted(.ngl-block):after { pointer-events: none; - z-index: 2; } main:not(.is_selected).is_hovered ::slotted(.ngl-block):before { background-color: var(--ngl-block-background-color-selected); - z-index: 80000; + z-index: calc(var(--ngl-block-base-z-index, 80000)); + } + + main:not(.is_selected):not(.is_child_selected).is_hovered ::slotted(.ngl-block):before { + pointer-events: auto; } - main:not(.is_selected).is_parent.is_hovered ::slotted(.ngl-block):before { - z-index: 0; + main.is_child_selected ::slotted(.ngl-block):after { + pointer-events: none; } main.is_selected ::slotted(.ngl-block):after { - border: solid var(--ngl-block-outline-width) - var(--ngl-block-outline-color-selected); + border: solid var(--ngl-block-outline-width) var(--ngl-block-outline-color-selected); + } + main:not(.is_container).is_selected.is_collection_empty ::slotted(.ngl-block):after { + z-index: calc(var(--ngl-block-base-z-index, 80000) + 10); + pointer-events: auto; } main:not(.is_selected).is_hovered ::slotted(.ngl-block):after { border: solid 1px var(--ngl-block-outline-color-hover); } + main:not(.is_selected):not(.is_container_empty):not(.is_child_selected):not(.is_container_selected).is_collection_empty ::slotted(.ngl-block):before { + background-image: linear-gradient(135deg, #cfcfcf 2.94%, rgba(151, 71, 255, 0) 2.94%, rgba(151, 71, 255, 0) 50%, #cfcfcf 50%, #cfcfcf 52.94%, rgba(151, 71, 255, 0) 52.94%, rgba(151, 71, 255, 0) 100%); + background-size: 24.04px 24.04px; + z-index: calc(var(--ngl-block-base-z-index, 80000) + 4); + } + + main.is_collection_empty, + main.is_collection_empty ::slotted(.ngl-block) { + min-height: 100px; + } + .edit-menu { - z-index: calc(var(--ngl-block-base-z-index) + 2); + z-index: 80010; display: none; visibility: hidden; opacity: 0; @@ -83,7 +94,9 @@ export default css` right: 0; bottom: 100%; gap: 0.25rem; + z-index: calc(var(--ngl-block-base-z-index, 80000) + 7); } + main.is_selected .edit-menu, main.is_hovered .edit-menu { display: inline-flex; @@ -125,7 +138,7 @@ export default css` display: none; visibility: hidden; opacity: 0; - z-index: calc(var(--ngl-block-base-z-index) + 2); + z-index: calc(var(--ngl-block-base-z-index, 80000) + 10); } main.is_selected .breadcrumbs { @@ -142,6 +155,7 @@ export default css` border-radius: 0.125rem 0.125rem 0 0; text-transform: capitalize; } + main button.breadcrumb-btn:not(:last-child):hover { --_btn-background-color: var(--ngl-block-button-background-color-hover); } @@ -154,20 +168,20 @@ export default css` .breadcrumb-btn > svg { position: absolute; left: calc(100% - 5px); - z-index: calc(var(--ngl-block-base-z-index) + 3); + z-index: calc(var(--ngl-block-base-z-index, 80000) + 3); } .add-btn { position: absolute; bottom: 0; left: 50%; - transform: translate(-50%, calc(50% - 2px)); + transform: translate(-50%, 100%); padding: 0.125rem 0.5rem 0.125rem 0.25rem; display: none; opacity: 0; - z-index: calc(var(--ngl-block-base-z-index) + 3); visibility: hidden; - border-radius: 0.125rem; + border-radius: 0 0 .125rem .125rem; + z-index: calc(var(--ngl-block-base-z-index, 80000) + 10); } main.is_selected .add-btn { @@ -175,4 +189,21 @@ export default css` opacity: 1; visibility: visible; } + + .move-btns { + display: none; + } + + main.is_selected .move-btns { + display: flex; + gap: .125rem; + position: absolute; + top: .5rem; + right: .5rem; + z-index: calc(var(--ngl-block-base-z-index, 80000) + 11); + } + + .move-btn { + padding: .25rem; + } `; diff --git a/components/icons.js b/components/icons.js new file mode 100644 index 0000000..ad11d2e --- /dev/null +++ b/components/icons.js @@ -0,0 +1,91 @@ +import {html} from 'lit'; + +export const ArrowUpIcon = () => { + return html` + + + + + + ` +} + +export const ArrowDownIcon = () => { + return html` + + + + + + ` +} + +export const RefreshIcon = () => { + return html` + + + + + `; +} + +export const BreadcrumbArrowIcon = () => { + return html` + + + + + `; +} + +export const PlusIcon = () => { + return html` + + + + + ` +} \ No newline at end of file diff --git a/components/placeholder/element.js b/components/placeholder/element.js new file mode 100644 index 0000000..c934185 --- /dev/null +++ b/components/placeholder/element.js @@ -0,0 +1,80 @@ +import {LitElement, html} from 'lit'; +import {classMap} from 'lit/directives/class-map.js'; +import style from './style.js'; + +export default class Placeholder extends LitElement { + static styles = [style]; + + static properties = { + isEmpty: {type: Boolean}, + isMarked: {type: Boolean, state: true}, + identifier: {type: String}, + isContainerEmpty: {type: Boolean, default: false}, + }; + + constructor() { + super(); + } + + get slottedChildren() { + const slot = this.shadowRoot.querySelector('slot'); + return slot.assignedElements({flatten: true}); + } + + connectedCallback() { + super.connectedCallback(); + } + + renderAddButton() { + + return html` + + `; + } + + handleAddButtonClick(e) { + const blockPicker = document.querySelector('ngl-block-picker'); + const parentBlock = this.slottedChildren[0].closest('ngl-block'); + + blockPicker.blockId = parentBlock.getAttribute('blockid') + blockPicker.isActive = true + blockPicker.placeholderIdentifier = this.identifier; + } + + render() { + const classes = { + is_empty: this.isEmpty, + is_marked: this.isMarked, + is_container_empty: this.isContainerEmpty, + }; + + return html` +
+ ${this.renderAddButton()} + +
+ + `; + } +} + +customElements.get('ngl-placeholder') || customElements.define('ngl-placeholder', Placeholder); \ No newline at end of file diff --git a/components/placeholder/spec.js b/components/placeholder/spec.js new file mode 100644 index 0000000..e69de29 diff --git a/components/placeholder/style.js b/components/placeholder/style.js new file mode 100644 index 0000000..f3fe3fa --- /dev/null +++ b/components/placeholder/style.js @@ -0,0 +1,81 @@ +import {css} from 'lit'; + +export default css` + :host, + :after, + :before { + } + + :host { + display: contents; + } + + main { + min-height: 102px; + position: relative; + } + + main:after { + position: absolute; + inset: 0; + pointer-events: none; + } + + main.is_marked:after { + content: ""; + border: dashed 1px var(--ngl-block-outline-color-hover); + } + + main.is_empty.is_marked:after { + content: ""; + border: dashed 1px var(--ngl-block-outline-color-hover); + min-height: 100px; + } + + main:not(.is_marked):not(.is_container_empty).is_empty { + background-image: linear-gradient(135deg, #cfcfcf 2.94%, rgba(151, 71, 255, 0) 2.94%, rgba(151, 71, 255, 0) 50%, #cfcfcf 50%, #cfcfcf 52.94%, rgba(151, 71, 255, 0) 52.94%, rgba(151, 71, 255, 0) 100%); + background-size: 24.04px 24.04px; + } + + button { + --_btn-background-color: var(--ngl-block-button-background-color); + background-color: var(--_btn-background-color); + border: none; + margin: 0; + padding: 0.125rem 0.5rem; + font-size: 0.75rem; + font-weight: 500; + color: #ffffff; + gap: 0.25rem; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + border-radius: 0.125rem 0.125rem 0 0; + cursor: pointer; + position: relative; + } + + button:hover { + --_btn-background-color: var(--ngl-block-button-background-color-hover); + } + + .add-btn { + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, 0); + padding: 0.125rem 0.5rem 0.125rem 0.25rem; + display: none; + opacity: 0; + z-index: calc(var(--ngl-block-base-z-index) + 3); + visibility: hidden; + border-radius: 0 0 0.125rem 0.125rem; + } + + main.is_marked.is_empty .add-btn { + display: inline-flex; + opacity: 1; + visibility: visible; + } +`; diff --git a/custom-elements.json b/custom-elements.json new file mode 100644 index 0000000..89b1301 --- /dev/null +++ b/custom-elements.json @@ -0,0 +1,835 @@ +{ + "schemaVersion": "1.0.0", + "readme": "", + "modules": [ + { + "kind": "javascript-module", + "path": "components.js", + "declarations": [], + "exports": [ + { + "kind": "js", + "name": "Block", + "declaration": { + "name": "default", + "module": "./components/block/element.js" + } + }, + { + "kind": "js", + "name": "Placeholder", + "declaration": { + "name": "default", + "module": "./components/placeholder/element.js" + } + }, + { + "kind": "js", + "name": "BlockPicker", + "declaration": { + "name": "default", + "module": "./components/block-picker/element.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "dist/dist.js", + "declarations": [ + { + "kind": "class", + "description": "", + "name": "ot", + "members": [ + { + "kind": "field", + "name": "layout" + }, + { + "kind": "field", + "name": "core" + }, + { + "kind": "field", + "name": "model" + }, + { + "kind": "field", + "name": "parent" + }, + { + "kind": "field", + "name": "isInLinkedZone" + }, + { + "kind": "method", + "name": "deselectIcon" + }, + { + "kind": "method", + "name": "refreshIcon" + }, + { + "kind": "method", + "name": "renderMenu" + }, + { + "kind": "method", + "name": "renderLinkedBlockMenu" + }, + { + "kind": "method", + "name": "renderOuterBlockMenu" + }, + { + "kind": "method", + "name": "renderBreadcrumbs" + }, + { + "kind": "method", + "name": "renderInnerBlokcBreadcrumbs" + }, + { + "kind": "method", + "name": "renderOuterBlokcBreadcrumbs" + }, + { + "kind": "method", + "name": "breadcrumbArrowIcon" + }, + { + "kind": "method", + "name": "renderAddButton" + }, + { + "kind": "method", + "name": "toggleSelect" + }, + { + "kind": "method", + "name": "select" + }, + { + "kind": "method", + "name": "parentSelect" + }, + { + "kind": "method", + "name": "getClosestBlockId", + "parameters": [ + { + "name": "t" + } + ] + }, + { + "kind": "method", + "name": "selectOnBlockClick", + "parameters": [ + { + "name": "t" + } + ] + }, + { + "kind": "method", + "name": "handleMouseover", + "parameters": [ + { + "name": "t" + } + ] + }, + { + "kind": "method", + "name": "handleMouseout", + "parameters": [ + { + "name": "t" + } + ] + }, + { + "kind": "method", + "name": "fetch" + }, + { + "kind": "method", + "name": "refresh" + }, + { + "kind": "method", + "name": "parentRefresh" + }, + { + "kind": "field", + "name": "loading", + "type": { + "text": "boolean" + }, + "default": "!1", + "privacy": "public", + "attribute": "loading" + }, + { + "kind": "field", + "name": "blockId", + "privacy": "public", + "attribute": "blockId" + }, + { + "kind": "field", + "name": "templateName", + "privacy": "public", + "attribute": "templateName" + }, + { + "kind": "field", + "name": "isSelected", + "privacy": "public", + "attribute": "isSelected" + }, + { + "kind": "field", + "name": "isHovered", + "privacy": "public", + "attribute": "isHovered" + }, + { + "kind": "field", + "name": "isParent", + "privacy": "public", + "attribute": "isParent" + }, + { + "kind": "method", + "name": "createRenderRoot", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "renderOptions", + "type": { + "text": "object" + }, + "default": "{host:this}", + "inheritedFrom": { + "name": "Y", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "_$Do", + "default": "void 0", + "inheritedFrom": { + "name": "Y", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "addInitializer", + "static": true, + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "createProperty", + "static": true, + "parameters": [ + { + "name": "t" + }, + { + "name": "e", + "default": "v" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "getPropertyDescriptor", + "static": true, + "parameters": [ + { + "name": "t" + }, + { + "name": "e" + }, + { + "name": "i" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "getPropertyOptions", + "static": true, + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "finalize", + "static": true, + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "finalizeStyles", + "static": true, + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$Ep", + "static": true, + "parameters": [ + { + "name": "t" + }, + { + "name": "e" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "u", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "addController", + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "removeController", + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$Eg", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "enableUpdating", + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$EO", + "parameters": [ + { + "name": "t" + }, + { + "name": "e" + }, + { + "name": "i", + "default": "v" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$AK", + "parameters": [ + { + "name": "t" + }, + { + "name": "e" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$Ej", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "scheduleUpdate", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$AE", + "parameters": [ + { + "name": "t" + } + ], + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "_$Ek", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "updateComplete", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "method", + "name": "getUpdateComplete", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "_$Ei", + "default": "new Map", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "isUpdatePending", + "type": { + "text": "boolean" + }, + "default": "!1", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "hasUpdated", + "type": { + "text": "boolean" + }, + "default": "!1", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + }, + { + "kind": "field", + "name": "_$El", + "type": { + "text": "null" + }, + "default": "null", + "inheritedFrom": { + "name": "b", + "module": "dist/dist.js" + } + } + ], + "events": [ + { + "name": "ngl:refresh", + "type": { + "text": "Event" + } + } + ], + "attributes": [ + { + "name": "loading", + "fieldName": "loading" + }, + { + "name": "blockId", + "fieldName": "blockId" + }, + { + "name": "templateName", + "fieldName": "templateName" + }, + { + "name": "isSelected", + "fieldName": "isSelected" + }, + { + "name": "isHovered", + "fieldName": "isHovered" + }, + { + "name": "isParent", + "fieldName": "isParent" + } + ], + "superclass": { + "name": "Y", + "module": "dist/dist.js" + }, + "tagName": "ngl-block", + "customElement": true + } + ], + "exports": [ + { + "kind": "custom-element-definition", + "name": "ngl-block", + "declaration": { + "name": "ot", + "module": "dist/dist.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "components/block/element.js", + "declarations": [ + { + "kind": "class", + "description": "", + "name": "Block", + "members": [ + { + "kind": "field", + "name": "layout" + }, + { + "kind": "field", + "name": "core" + }, + { + "kind": "field", + "name": "model" + }, + { + "kind": "field", + "name": "parent" + }, + { + "kind": "field", + "name": "isInLinkedZone" + }, + { + "kind": "method", + "name": "deselectIcon" + }, + { + "kind": "method", + "name": "refreshIcon" + }, + { + "kind": "method", + "name": "renderMenu" + }, + { + "kind": "method", + "name": "renderLinkedBlockMenu" + }, + { + "kind": "method", + "name": "renderOuterBlockMenu" + }, + { + "kind": "method", + "name": "renderBreadcrumbs" + }, + { + "kind": "method", + "name": "renderInnerBlokcBreadcrumbs" + }, + { + "kind": "method", + "name": "renderOuterBlokcBreadcrumbs" + }, + { + "kind": "method", + "name": "breadcrumbArrowIcon" + }, + { + "kind": "method", + "name": "renderAddButton" + }, + { + "kind": "method", + "name": "toggleSelect" + }, + { + "kind": "method", + "name": "select" + }, + { + "kind": "method", + "name": "parentSelect" + }, + { + "kind": "method", + "name": "getClosestBlockId", + "parameters": [ + { + "name": "el" + } + ] + }, + { + "kind": "method", + "name": "selectOnBlockClick", + "parameters": [ + { + "name": "e" + } + ] + }, + { + "kind": "method", + "name": "handleMouseover", + "parameters": [ + { + "name": "e" + } + ] + }, + { + "kind": "method", + "name": "handleMouseout", + "parameters": [ + { + "name": "e" + } + ] + }, + { + "kind": "method", + "name": "fetch" + }, + { + "kind": "method", + "name": "refresh" + }, + { + "kind": "method", + "name": "parentRefresh" + }, + { + "kind": "field", + "name": "loading", + "type": { + "text": "boolean" + }, + "default": "false", + "privacy": "public", + "attribute": "loading" + }, + { + "kind": "field", + "name": "blockId", + "privacy": "public", + "attribute": "blockId" + }, + { + "kind": "field", + "name": "templateName", + "privacy": "public", + "attribute": "templateName" + }, + { + "kind": "field", + "name": "isSelected", + "privacy": "public", + "attribute": "isSelected" + }, + { + "kind": "field", + "name": "isHovered", + "privacy": "public", + "attribute": "isHovered" + }, + { + "kind": "field", + "name": "isParent", + "privacy": "public", + "attribute": "isParent" + } + ], + "events": [ + { + "name": "ngl:refresh", + "type": { + "text": "Event" + } + } + ], + "attributes": [ + { + "name": "loading", + "fieldName": "loading" + }, + { + "name": "blockId", + "fieldName": "blockId" + }, + { + "name": "templateName", + "fieldName": "templateName" + }, + { + "name": "isSelected", + "fieldName": "isSelected" + }, + { + "name": "isHovered", + "fieldName": "isHovered" + }, + { + "name": "isParent", + "fieldName": "isParent" + } + ], + "superclass": { + "name": "LitElement", + "package": "lit" + }, + "tagName": "ngl-block", + "customElement": true + } + ], + "exports": [ + { + "kind": "js", + "name": "default", + "declaration": { + "name": "Block", + "module": "components/block/element.js" + } + }, + { + "kind": "custom-element-definition", + "name": "ngl-block", + "declaration": { + "name": "Block", + "module": "components/block/element.js" + } + } + ] + }, + { + "kind": "javascript-module", + "path": "components/block/spec.js", + "declarations": [], + "exports": [] + }, + { + "kind": "javascript-module", + "path": "components/block/style.js", + "declarations": [], + "exports": [ + { + "kind": "js", + "name": "default", + "declaration": { + "module": "components/block/style.js" + } + } + ] + } + ] +} From fc68c02576302a33c2bcd94581350525179ce7f1 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Wed, 7 Dec 2022 17:37:54 +0100 Subject: [PATCH 13/23] LAYOUTS-660 refresh component block after it is created in new window --- components/block-picker/element.js | 2 +- components/block/element.js | 39 +++++++++++++++++++++++------- components/block/style.js | 30 ++++++++++++----------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/components/block-picker/element.js b/components/block-picker/element.js index 14919bb..5254ca5 100644 --- a/components/block-picker/element.js +++ b/components/block-picker/element.js @@ -206,7 +206,7 @@ export default class BlockPicker extends LitElement { }; return html` -
this.isActive = false}> +
${this.renderBlockGroups()}
diff --git a/components/block/element.js b/components/block/element.js index ab595e4..6019da7 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -14,10 +14,11 @@ export default class Block extends LitElement { isSelected: {type: Boolean}, isHovered: {type: Boolean, state: true}, isContainer: {type: Boolean, state: true}, - isCollectionEmpty: {type: Boolean}, + isEmpty: {type: Boolean}, isContainerEmpty: {type: Boolean, default: false}, isContainerSelected: {type: Boolean, default: false}, isChildSelected: {type: Boolean, default: false}, + isFullViewBlock: {type: Boolean, default: false}, }; constructor() { @@ -34,6 +35,7 @@ export default class Block extends LitElement { super.connectedCallback(); this.isContainer = this.model.attributes.is_container; + this.isFullViewBlock = this.model.attributes.definition_identifier === 'full_view' this.model.on('change', this.refresh.bind(this)); this.model.on('sidebar:destroyed', () => { this.isSelected = false; @@ -41,6 +43,21 @@ export default class Block extends LitElement { this.setIsContainerSelected(false) this.setIsChildSelected(false) }); + + const bc = new BroadcastChannel('publish_content'); + bc.onmessage = (event) => { + this.handleMessageRecieved(event.data) + }; + } + + handleMessageRecieved(data) { + const { contentId, blockId, locale } = data; + + if(blockId !== this.blockId) return; + + console.debug(data) + this.refresh(); + this.model.trigger('edit'); } @@ -81,7 +98,7 @@ export default class Block extends LitElement { } get isInLinkedZone() { - return this.model.zone().is_linked(); + return this.model.zone()?.is_linked(); } get slot() { @@ -93,11 +110,11 @@ export default class Block extends LitElement { } get placeholders() { - return this.slottedChildren[0].querySelectorAll('ngl-placeholder') + return this.slottedChildren[0]?.querySelectorAll('ngl-placeholder') } get childBlocks() { - return this.slottedChildren[0].querySelectorAll('ngl-block') + return this.slottedChildren[0]?.querySelectorAll('ngl-block') } // GETTERS - end @@ -133,15 +150,18 @@ export default class Block extends LitElement { if(!this.isContainer) return; const areAllPlaceholdersEmpty = [...this.placeholders].every(el => el.isEmpty) - const areAllChildBlocksEmpty = [...this.childBlocks].every(el => el.isCollectionEmpty) + const areAllChildBlocksEmpty = [...this.childBlocks].every(el => el.isEmpty) + + this.isEmpty = areAllPlaceholdersEmpty || areAllChildBlocksEmpty; + this.isChildSelected = [...this.childBlocks].some(el => el.isSelected) - this.isCollectionEmpty = areAllPlaceholdersEmpty || areAllChildBlocksEmpty; - this.setChildBlocksIsEmptyState(); + + if(this.isSelected) this.markPlaceholders() } setChildBlocksIsEmptyState() { - if(!this.isCollectionEmpty) return; + if(!this.isEmpty) return; [...this.placeholders, ...this.childBlocks].map(el => el.isContainerEmpty = true) } @@ -408,7 +428,8 @@ export default class Block extends LitElement { is_selected: this.isSelected, is_hovered: this.isHovered, is_container: this.isContainer, - is_collection_empty: this.isCollectionEmpty, + is_empty: this.isEmpty, + is_full_view_block: this.isFullViewBlock, is_container_empty: this.isContainerEmpty, is_container_selected: this.isContainerSelected, is_child_selected: this.isChildSelected, diff --git a/components/block/style.js b/components/block/style.js index 722135e..8f01559 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -31,8 +31,8 @@ export default css` opacity: 0.5; } - main ::slotted(.ngl-block):before, - main ::slotted(.ngl-block):after { + main ::slotted(.ngl-slotted-block):before, + main ::slotted(.ngl-slotted-block):after { content: ''; position: absolute; inset: 0; @@ -40,48 +40,50 @@ export default css` cursor: pointer; } - main ::slotted(.ngl-block):before { + main ::slotted(.ngl-slotted-block):before { pointer-events: none; } - main.is_selected ::slotted(.ngl-block):before, - main.is_selected ::slotted(.ngl-block):after { + main.is_selected ::slotted(.ngl-slotted-block):before, + main.is_selected ::slotted(.ngl-slotted-block):after { pointer-events: none; } - main:not(.is_selected).is_hovered ::slotted(.ngl-block):before { + main:not(.is_selected).is_hovered ::slotted(.ngl-slotted-block):before { background-color: var(--ngl-block-background-color-selected); z-index: calc(var(--ngl-block-base-z-index, 80000)); } - main:not(.is_selected):not(.is_child_selected).is_hovered ::slotted(.ngl-block):before { + main:not(.is_selected):not(.is_child_selected).is_hovered ::slotted(.ngl-slotted-block):before { pointer-events: auto; } - main.is_child_selected ::slotted(.ngl-block):after { + main.is_child_selected ::slotted(.ngl-slotted-block):after { pointer-events: none; } - main.is_selected ::slotted(.ngl-block):after { + main.is_selected ::slotted(.ngl-slotted-block):after { border: solid var(--ngl-block-outline-width) var(--ngl-block-outline-color-selected); } - main:not(.is_container).is_selected.is_collection_empty ::slotted(.ngl-block):after { + main:not(.is_container).is_selected.is_empty ::slotted(.ngl-slotted-block):after { z-index: calc(var(--ngl-block-base-z-index, 80000) + 10); pointer-events: auto; } - main:not(.is_selected).is_hovered ::slotted(.ngl-block):after { + main:not(.is_selected).is_hovered ::slotted(.ngl-slotted-block):after { border: solid 1px var(--ngl-block-outline-color-hover); } - main:not(.is_selected):not(.is_container_empty):not(.is_child_selected):not(.is_container_selected).is_collection_empty ::slotted(.ngl-block):before { + main:not(.is_selected):not(.is_container_empty):not(.is_child_selected):not(.is_container_selected).is_empty ::slotted(.ngl-slotted-block):before { background-image: linear-gradient(135deg, #cfcfcf 2.94%, rgba(151, 71, 255, 0) 2.94%, rgba(151, 71, 255, 0) 50%, #cfcfcf 50%, #cfcfcf 52.94%, rgba(151, 71, 255, 0) 52.94%, rgba(151, 71, 255, 0) 100%); background-size: 24.04px 24.04px; z-index: calc(var(--ngl-block-base-z-index, 80000) + 4); } - main.is_collection_empty, - main.is_collection_empty ::slotted(.ngl-block) { + main.is_empty, + main.is_empty ::slotted(.ngl-slotted-block), + main.is_full_view_block, + main.is_full_view_block ::slotted(.ngl-slotted-block) { min-height: 100px; } From 3d765cee767f1f3d06698ecfb756703c00cd5ea8 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Thu, 8 Dec 2022 11:32:57 +0100 Subject: [PATCH 14/23] LAYOUSTS-666 fix breadcrumbs and refresh icon --- components/block/element.js | 16 +++++++++------- components/block/style.js | 5 +++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index 6019da7..112e911 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -55,7 +55,6 @@ export default class Block extends LitElement { if(blockId !== this.blockId) return; - console.debug(data) this.refresh(); this.model.trigger('edit'); } @@ -92,9 +91,9 @@ export default class Block extends LitElement { } get parentName() { - if (!this.parent) return; + if (!this.parentElement) return; - return this.parent.getAttribute('viewTypeName') + return this.parentElement.getAttribute('viewTypeName') } get isInLinkedZone() { @@ -135,7 +134,8 @@ export default class Block extends LitElement { `ngl-block[blockId="${this.blockId}"]` ); - this.innerHTML = currentBlockHtml.innerHTML; + this.replaceWith(currentBlockHtml); + currentBlockHtml.isSelected = true; this.dispatchEvent( new Event('ngl:preview:block:refresh', {bubbles: true, composed: true}) @@ -192,12 +192,14 @@ export default class Block extends LitElement { select() { if (this.isInLinkedZone) return; + console.debug(this.model) + this.model.trigger('edit'); this.isSelected = true; } parentSelect() { - this.parent.select(); + this.parentElement.select(); } setIsChildSelected(selected) { @@ -262,7 +264,7 @@ export default class Block extends LitElement { blockIds.splice(fromIndex, 1) blockIds.splice(toIndex, 0, this.blockId) - if(this.parent) { + if(this.parentElement) { this.model.set({ parent_position: this.model.attributes.parent_position + directionNumber, zone_identifier: this.model.attributes.zone_identifier, @@ -395,7 +397,7 @@ export default class Block extends LitElement { return html` diff --git a/components/block/style.js b/components/block/style.js index 8f01559..d0c89a6 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -99,8 +99,7 @@ export default css` z-index: calc(var(--ngl-block-base-z-index, 80000) + 7); } - main.is_selected .edit-menu, - main.is_hovered .edit-menu { + main.is_selected .edit-menu { display: inline-flex; opacity: 1; visibility: visible; @@ -171,6 +170,8 @@ export default css` position: absolute; left: calc(100% - 5px); z-index: calc(var(--ngl-block-base-z-index, 80000) + 3); + height: 21px; + width: 18px; } .add-btn { From 1e757efc2ba27de7c53a3ddc2f2ff4d5ca726e5d Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Thu, 8 Dec 2022 15:49:36 +0100 Subject: [PATCH 15/23] LAYOUTS-666 style block picker --- components/block-picker/element.js | 7 +++++-- components/block-picker/style.js | 13 ++++++++----- components/block/element.js | 7 ++++++- components/block/style.js | 5 +++++ components/icons.js | 8 ++++++++ 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/components/block-picker/element.js b/components/block-picker/element.js index 5254ca5..a72196c 100644 --- a/components/block-picker/element.js +++ b/components/block-picker/element.js @@ -1,5 +1,6 @@ import {LitElement, html} from 'lit'; import {classMap} from 'lit/directives/class-map.js'; +import { CloseIcon } from '../icons.js'; import style from './style.js'; export default class BlockPicker extends LitElement { @@ -59,7 +60,7 @@ export default class BlockPicker extends LitElement { renderCloseBtn() { return html` - close + ${CloseIcon()} ` } @@ -89,9 +90,11 @@ export default class BlockPicker extends LitElement { } renderBlockType(blockType, index) { + const icon = blockType.attributes.icon ? html`` : html`` + return html`
- + ${icon} ${blockType.attributes.name}
` diff --git a/components/block-picker/style.js b/components/block-picker/style.js index baa7c6a..83c974c 100644 --- a/components/block-picker/style.js +++ b/components/block-picker/style.js @@ -33,7 +33,7 @@ export default css` width: var(--ngl-block-picker-width); background: var(--ngl-block-picker-background-color); color: var(--ngl-block-picker-text-color); - overflow-y: hidden; + overflow-y: scroll; transform: translate(-50%, -200vh); } @@ -44,8 +44,6 @@ export default css` .panel-content { padding: var(--ngl-block-picker-gutter); - max-height: 90vh; - overflow-y: scroll; } .close-panel { @@ -58,10 +56,13 @@ export default css` text-align: right; color: var(--ngl-block-picker-text-color); z-index: 2; + display: flex; + justify-content: center; + align-items: center; } - .close-panel i { - font-size: 1.125rem; + .close-panel svg { + fill: var(--ngl-block-picker-text-color) } .close-panel:hover { @@ -100,6 +101,8 @@ export default css` padding: 0.25em 0.25em 0.625em; transition: background .15s ease, color .15s ease; cursor: pointer; + display: flex; + flex-direction: column; } .add-block-btn:hover { diff --git a/components/block/element.js b/components/block/element.js index 112e911..7c19cc3 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -143,6 +143,10 @@ export default class Block extends LitElement { this.markPlaceholders(); this.setIsContainerSelected(true) this.setIsChildSelected(true) + + if(this.parentElement) [ + this.parentElement.setIsEmptyState() + ] } @@ -199,7 +203,7 @@ export default class Block extends LitElement { } parentSelect() { - this.parentElement.select(); + this.parentElement.toggleSelect(); } setIsChildSelected(selected) { @@ -435,6 +439,7 @@ export default class Block extends LitElement { is_container_empty: this.isContainerEmpty, is_container_selected: this.isContainerSelected, is_child_selected: this.isChildSelected, + is_child_block: this.parentElement, }; return html` diff --git a/components/block/style.js b/components/block/style.js index d0c89a6..c191b3e 100644 --- a/components/block/style.js +++ b/components/block/style.js @@ -40,6 +40,10 @@ export default css` cursor: pointer; } + main.is_child_block ::slotted(.ngl-slotted-block):after { + z-index: calc(var(--ngl-block-base-z-index, 80000) + 5); + } + main ::slotted(.ngl-slotted-block):before { pointer-events: none; } @@ -54,6 +58,7 @@ export default css` z-index: calc(var(--ngl-block-base-z-index, 80000)); } + main:not(.is_selected):not(.is_child_selected).is_hovered ::slotted(.ngl-slotted-block):before { pointer-events: auto; } diff --git a/components/icons.js b/components/icons.js index ad11d2e..9ff92ae 100644 --- a/components/icons.js +++ b/components/icons.js @@ -88,4 +88,12 @@ export const PlusIcon = () => { /> ` +} + +export const CloseIcon = () => { + return html` + + + + ` } \ No newline at end of file From 62a30c1c98ba19a45268cc8a1ad5ddbdaa51b47f Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Thu, 8 Dec 2022 17:06:39 +0100 Subject: [PATCH 16/23] LAYOUTS-661 style bold picker and add icons filter oon hover --- components/block-picker/style.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/components/block-picker/style.js b/components/block-picker/style.js index 83c974c..cfded54 100644 --- a/components/block-picker/style.js +++ b/components/block-picker/style.js @@ -4,7 +4,7 @@ export default css` :host, :after, :before { - --ngl-block-picker-width: 26.625rem; + --ngl-block-picker-width: 22.625rem; --ngl-block-picker-gutter: 0.9375rem; --ngl-block-picker-background-color: #383838; --ngl-block-picker-border-color: #4f4f4f; @@ -92,8 +92,8 @@ export default css` border: 0; border-bottom: 1px solid var(--ngl-block-picker-background-color); border-right: 1px solid var(--ngl-block-picker-background-color); - width: calc( ( var(--ngl-block-picker-width) - 2 * var(--ngl-block-picker-gutter) - 44px )/ 4); - background-color: var(--ngl-block-picker-block-background-color); + width: calc( ( var(--ngl-block-picker-width) - 2 * var(--ngl-block-picker-gutter) - 30px)/ 4); + background: #4a4a4a; border: 1px solid var(--ngl-block-picker-background-color); aspect-ratio: 1/1; color: var(--ngl-block-picker-text-color); @@ -103,16 +103,20 @@ export default css` cursor: pointer; display: flex; flex-direction: column; + font-size: .625rem; + line-height: 1.2; + align-itmes: center; + justify-content: center; } .add-block-btn:hover { - background: hsl(0, 0, 36); + background: #5c5c5c; color: #fff; } .add-block-btn .icon { font-size: 2em; - height: 1.5em; + height: 2.5rem; line-height: 1.25; margin-bottom: .125em; padding: .125em; @@ -121,12 +125,16 @@ export default css` justify-content: center; } - .add-block-btn .icon img { + .add-block-btn .icon { max-width: 100%; max-height: 100%; display: block; } + .add-block-btn:hover .icon { + filter: brightness(0) saturate(100%) invert(99%) sepia(97%) saturate(0%) hue-rotate(74deg) brightness(104%) contrast(100%); + } + .add-block-btn:nth-child(4n) { border-right: 0; } From c0b80e75d9a0e92ebc36d6b6147b8e102195aaf9 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Thu, 8 Dec 2022 17:33:21 +0100 Subject: [PATCH 17/23] LAYOUT-667 disable add of container inside another container --- components/block-picker/element.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/block-picker/element.js b/components/block-picker/element.js index a72196c..1188b1b 100644 --- a/components/block-picker/element.js +++ b/components/block-picker/element.js @@ -49,6 +49,7 @@ export default class BlockPicker extends LitElement { handleClose() { this.isActive = false + this.placeholderIdentifier = false this.iframe.contentDocument.querySelector('body').style.overflowY = 'auto'; } @@ -79,12 +80,16 @@ export default class BlockPicker extends LitElement { } renderBlockGroup(group) { + const blockTypes = group._block_types.filter(blockType => this.placeholderIdentifier || this.block.attributes.parent_placeholder ? !blockType.attributes.is_container : true) + + if(blockTypes.length <= 0 ) return; + return html`

${group.attributes.name}

- ${group._block_types.map((blockType, index) => this.renderBlockType(blockType, index))} + ${blockTypes.map((blockType, index) => this.renderBlockType(blockType, index))}
` } From 9298074f002566f0ebc0747ff8a1462a09c9812f Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Wed, 14 Dec 2022 15:01:48 +0100 Subject: [PATCH 18/23] LAYOUTS-637 style scrollbar on block picker component --- components/block-picker/style.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/block-picker/style.js b/components/block-picker/style.js index cfded54..0eeffa5 100644 --- a/components/block-picker/style.js +++ b/components/block-picker/style.js @@ -37,6 +37,14 @@ export default css` transform: translate(-50%, -200vh); } + .panel::-webkit-scrollbar { + width: .3125rem; + } + + .panel::-webkit-scrollbar-thumb { + background: #999; + } + main.is_active .panel { transform: translate(-50%, -50%); transition: transform 350ms 150ms ease; @@ -92,7 +100,7 @@ export default css` border: 0; border-bottom: 1px solid var(--ngl-block-picker-background-color); border-right: 1px solid var(--ngl-block-picker-background-color); - width: calc( ( var(--ngl-block-picker-width) - 2 * var(--ngl-block-picker-gutter) - 30px)/ 4); + width: calc( ( var(--ngl-block-picker-width) - 2 * var(--ngl-block-picker-gutter) - 2rem)/ 4); background: #4a4a4a; border: 1px solid var(--ngl-block-picker-background-color); aspect-ratio: 1/1; From 3b84984ca4d45a48147dfd655d32b989cf827aa6 Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 20 Dec 2022 11:20:34 +0100 Subject: [PATCH 19/23] LAYOUTS-669 fix tests --- components/block/element.js | 15 +++++--------- components/block/spec.js | 39 +++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index 7c19cc3..3fe84a1 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -375,17 +375,12 @@ export default class Block extends LitElement { } renderMenu() { - if (this.isInLinkedZone) return this.renderLinkedBlockMenu(); - - return this.renderOuterBlockMenu(); - } + if (this.isInLinkedZone) return; - renderLinkedBlockMenu() { return html` - - `; +
+ ${this.renderOuterBlockMenu()} +
` } renderOuterBlockMenu() { @@ -449,7 +444,7 @@ export default class Block extends LitElement { @mouseout=${this.handleMouseout} > ${this.renderBreadcrumbs()} -
${this.renderMenu()}
+ ${this.renderMenu()} ${this.renderAddButton()}
diff --git a/components/block/spec.js b/components/block/spec.js index 0473c8d..5524209 100644 --- a/components/block/spec.js +++ b/components/block/spec.js @@ -57,19 +57,23 @@ describe('ngl-block', () => { element, `
-
- - -
+
+ +
+
` ); @@ -97,9 +101,6 @@ describe('ngl-block', () => { element, `
-
- -
` @@ -128,13 +129,6 @@ describe('ngl-block', () => { element, `
-
- - -
+
+ +
+
` ); From 036ed9ec0f3742be4cff4cc6407cab8a24a710db Mon Sep 17 00:00:00 2001 From: Marko Sandalj Date: Tue, 20 Dec 2022 11:34:54 +0100 Subject: [PATCH 20/23] LAYOUTS-669 fix tests --- components/block/element.js | 10 +++++----- components/block/spec.js | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/block/element.js b/components/block/element.js index 3fe84a1..2b8066b 100644 --- a/components/block/element.js +++ b/components/block/element.js @@ -397,24 +397,24 @@ export default class Block extends LitElement { return html` `; } - renderInnerBlokcBreadcrumbs() { + renderInnerBlockBreadcrumbs() { return html` - ${this.renderOuterBlokcBreadcrumbs()} + ${this.renderOuterBlockBreadcrumbs()} `; } - renderOuterBlokcBreadcrumbs() { + renderOuterBlockBreadcrumbs() { return html` -
+