From be8bfa1ebfd9e9b410d2226af68a2b3b72bcdf4e Mon Sep 17 00:00:00 2001 From: Artur Arseniev Date: Sun, 22 Jan 2017 16:05:55 +0100 Subject: [PATCH] Add toolbar to the component --- src/canvas/main.js | 34 ++++++++++------- src/canvas/view/CanvasView.js | 38 ++++++++++--------- src/commands/view/MoveComponent.js | 3 +- src/commands/view/SelectComponent.js | 48 +++++++++++++++++++++++- src/dom_components/model/Component.js | 11 ++++++ src/dom_components/view/ComponentView.js | 3 +- src/editor/config/config.js | 3 ++ styles/css/main.css | 10 +++++ styles/scss/main.scss | 12 ++++++ 9 files changed, 129 insertions(+), 33 deletions(-) diff --git a/src/canvas/main.js b/src/canvas/main.js index 486fb5c9c..b048c5e96 100644 --- a/src/canvas/main.js +++ b/src/canvas/main.js @@ -136,6 +136,14 @@ define(function(require) { return CanvasView.ghostEl; }, + /** + * Returns toolbar element + * @return {HTMLElement} + */ + getToolbarEl: function(){ + return CanvasView.toolbarEl; + }, + /** * Render canvas * */ @@ -158,18 +166,18 @@ define(function(require) { }, /** - * Get the offset of the element - * @param {HTMLElement} el - * @return {Object} - * @private - */ - offset: function(el){ - var rect = el.getBoundingClientRect(); - return { - top: rect.top + document.body.scrollTop, - left: rect.left + document.body.scrollLeft - }; - }, + * Get the offset of the element + * @param {HTMLElement} el + * @return {Object} + * @private + */ + offset: function(el){ + var rect = el.getBoundingClientRect(); + return { + top: rect.top + document.body.scrollTop, + left: rect.left + document.body.scrollLeft + }; + }, /** * Returns wrapper element @@ -182,4 +190,4 @@ define(function(require) { }; }; -}); \ No newline at end of file +}); diff --git a/src/canvas/view/CanvasView.js b/src/canvas/view/CanvasView.js index 001078062..a074eff5b 100644 --- a/src/canvas/view/CanvasView.js +++ b/src/canvas/view/CanvasView.js @@ -36,17 +36,18 @@ function(Backbone, FrameView) { renderBody: function(){ var wrap = this.model.get('frame').get('wrapper'); if(wrap){ + var ppfx = this.ppfx; var body = this.frame.$el.contents().find('body'); var cssc = this.config.em.get('CssComposer'); var conf = this.config.em.get('Config'); body.append(wrap.render()).append(cssc.render()); var protCss = conf.protectedCss; - var frameCss = '.' + this.ppfx + 'dashed *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' + - '.' + this.ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + - '.' + this.ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ - '.' + this.ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + - '.' + this.ppfx + 'no-pointer{pointer-events: none}' + - '.' + this.ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}'; + var frameCss = '.' + ppfx + 'dashed *{outline: 1px dashed rgba(170,170,170,0.7); outline-offset: -2px}' + + '.' + ppfx + 'comp-selected{outline: 3px solid #3b97e3 !important}' + + '.' + ppfx + 'no-select{user-select: none; -webkit-user-select:none; -moz-user-select: none}'+ + '.' + ppfx + 'freezed{opacity: 0.5; pointer-events: none}' + + '.' + ppfx + 'no-pointer{pointer-events: none}' + + '.' + ppfx + 'plh-image{background:#f5f5f5; border:none; height:50px; width:50px; display:block; outline:3px solid #ffca6f; cursor:pointer}'; if(protCss) body.append(''); this.config.em.trigger('loaded'); @@ -135,17 +136,20 @@ function(Backbone, FrameView) { var frame = this.frame; frame.el.onload = this.renderBody; } - this.toolsEl = $('
', { id: this.ppfx + 'tools' }).get(0); - this.hlEl = $('
', { class: this.ppfx + 'highlighter' }).get(0); - this.badgeEl = $('
', {class: this.ppfx + 'badge'}).get(0); - this.placerEl = $('
', {class: this.ppfx + 'placeholder'}).get(0); - this.placerIntEl = $('
', {class: this.ppfx + 'placeholder-int'}).get(0); - this.ghostEl = $('
', {class: this.ppfx + 'ghost'}).get(0); - this.placerEl.appendChild(this.placerIntEl); - this.toolsEl.appendChild(this.hlEl); - this.toolsEl.appendChild(this.badgeEl); - this.toolsEl.appendChild(this.placerEl); - this.toolsEl.appendChild(this.ghostEl); + var ppfx = this.ppfx; + this.toolsEl = $('
', { id: ppfx + 'tools' }).get(0); + this.hlEl = $('
', { class: ppfx + 'highlighter' }).get(0); + this.badgeEl = $('
', {class: ppfx + 'badge'}).get(0); + this.placerEl = $('
', {class: ppfx + 'placeholder'}).get(0); + this.placerIntEl = $('
', {class: ppfx + 'placeholder-int'}).get(0); + this.ghostEl = $('
', {class: ppfx + 'ghost'}).get(0); + this.toolbarEl = $('
', {class: ppfx + 'toolbar'}).get(0); + this.placerEl.appendChild(this.placerIntEl); + this.toolsEl.appendChild(this.hlEl); + this.toolsEl.appendChild(this.badgeEl); + this.toolsEl.appendChild(this.placerEl); + this.toolsEl.appendChild(this.ghostEl); + this.toolsEl.appendChild(this.toolbarEl); this.$el.append(this.toolsEl); var rte = this.em.get('rte'); diff --git a/src/commands/view/MoveComponent.js b/src/commands/view/MoveComponent.js index 32b482161..d44d10899 100644 --- a/src/commands/view/MoveComponent.js +++ b/src/commands/view/MoveComponent.js @@ -12,7 +12,7 @@ define(['backbone', './SelectComponent','./SelectPosition'], this.noSelClass = this.ppfx + 'no-select'; }, - enable: function(){ + enable: function() { SelectComponent.enable.apply(this, arguments); this.getBadgeEl().addClass(this.badgeClass); this.getHighlighterEl().addClass(this.hoverClass); @@ -40,6 +40,7 @@ define(['backbone', './SelectComponent','./SelectPosition'], var drag = el.get('draggable'); if(!drag) return; + // Avoid badge showing on move this.cacheEl = null; this.startSelectPosition(e.target, this.frameEl.contentDocument); diff --git a/src/commands/view/SelectComponent.js b/src/commands/view/SelectComponent.js index 17be8dda2..f1c9ba03b 100644 --- a/src/commands/view/SelectComponent.js +++ b/src/commands/view/SelectComponent.js @@ -232,7 +232,7 @@ define(function() { m.set('open', 0); } var parent = nMd.collection ? nMd.collection.parent : null; - while(parent){ + while(parent) { parent.set('open', 1); opened[parent.cid] = parent; parent = parent.collection ? parent.collection.parent : null; @@ -240,9 +240,54 @@ define(function() { this.editorModel.set('selectedComponent', nMd); nMd.set('status','selected'); + this.updateToolbar(nMd); } }, + /** + * Update toolbar if the component has one + * @param {Object} model + */ + updateToolbar: function(model) { + var toolbar = model.get('toolbar'); + var ppfx = this.ppfx; + var showToolbar = this.config.em.get('Config').showToolbar; + + // TODO to refactor + if (showToolbar && toolbar && toolbar.length) { + var toolbarEl = this.canvas.getToolbarEl(); + toolbarEl.innerHTML = ''; + toolbar.forEach(function (item) { + var className = ppfx + 'toolbar-item'; + var addClass = item.className || ''; + toolbarEl.innerHTML += '
'; + }); + + var view = model.get('view'); + if(view) { + var canvasPos = this.getCanvasPosition(); + var pos = this.getElementPos(view.el); + var toolbarStyle = toolbarEl.style; + var unit = 'px'; + // TODO Fix toolbar over the canvas (the problem is with the fisrt top el) + var topPos = (pos.top - toolbarEl.offsetHeight) < canvasPos.top ? + canvasPos.top : pos.top - toolbarEl.offsetHeight; + //var left = pos.left + badgeW < canvasPos.left ? canvasPos.left : pos.left; + toolbarStyle.display = 'flex'; + toolbarStyle.left = (pos.left + pos.width - toolbarEl.offsetWidth) + unit; // + toolbarStyle.top = (pos.top - toolbarEl.offsetHeight) + unit; + } + } + }, + + /** + * Return canvas dimensions and positions + * @return {Object} + */ + getCanvasPosition: function () { + return this.canvas.getCanvasView().getPosition(); + }, + /** * Removes all highlighting effects on components * @private @@ -323,6 +368,7 @@ define(function() { this.em.set('selectedComponent', null); this.toggleClipboard(); this.hideBadge(); + this.canvas.getToolbarEl().style.display = 'none'; } }; }); diff --git a/src/dom_components/model/Component.js b/src/dom_components/model/Component.js index c61b3ab18..54de29dad 100644 --- a/src/dom_components/model/Component.js +++ b/src/dom_components/model/Component.js @@ -41,6 +41,16 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana attributes: {}, classes: '', traits: ['id', 'title'], + toolbar: [{ + className: 'fa fa-arrows', + command: 'tlb-move', + },{ + className: 'fa fa-clone', + command: 'tlb-clone', + },{ + className: 'fa fa-trash-o', + command: 'tlb-delete', + }], }, initialize: function(o, opt) { @@ -132,6 +142,7 @@ define(['backbone','./Components', 'SelectorManager/model/Selectors', 'TraitMana }); } attr.status = ''; + attr.view = ''; return new this.constructor(attr, {sm: this.sm}); }, diff --git a/src/dom_components/view/ComponentView.js b/src/dom_components/view/ComponentView.js index faac603e5..d1a3da9c0 100644 --- a/src/dom_components/view/ComponentView.js +++ b/src/dom_components/view/ComponentView.js @@ -25,7 +25,8 @@ define(['backbone', './ComponentsView'], this.listenTo(this.model, 'change:status', this.updateStatus); this.listenTo(this.model, 'change:state', this.updateState); this.listenTo(this.model.get('classes'), 'add remove change', this.updateClasses); - this.$el.data("model", this.model); + this.$el.data('model', this.model); + this.model.set('view', this); this.$el.data("collection", this.components); if(this.model.get('classes').length) diff --git a/src/editor/config/config.js b/src/editor/config/config.js index 2d0487aa9..919ae131d 100644 --- a/src/editor/config/config.js +++ b/src/editor/config/config.js @@ -26,6 +26,9 @@ define(function () { // Default command defaultCommand: 'select-comp', + // Show a toolbar when the component is selected + showToolbar: 1, + // If true render a select of available devices showDevices: 1, diff --git a/styles/css/main.css b/styles/css/main.css index f1356d75a..76c069b5e 100644 --- a/styles/css/main.css +++ b/styles/css/main.css @@ -2801,6 +2801,16 @@ div.gjs-select { .gjs-frame { transition: width 0.35s ease; } +.gjs-toolbar { + position: absolute; + background-color: #3b97e3; + color: white; } + +.gjs-toolbar-item { + padding: 5px 7px; + font-size: 0.8rem; + cursor: pointer; } + .btn-cl, .gjs-mdl-dialog .gjs-mdl-btn-close, .gjs-am-assets-cont #gjs-am-close { font-size: 25px; opacity: 0.3; diff --git a/styles/scss/main.scss b/styles/scss/main.scss index c9aad5339..f6144a539 100644 --- a/styles/scss/main.scss +++ b/styles/scss/main.scss @@ -330,6 +330,18 @@ div.#{$app-prefix}select { transition: width 0.35s ease; } +.#{$app-prefix}toolbar { + position: absolute; + background-color: $colorBlue; + color: white; +} + +.#{$app-prefix}toolbar-item { + padding: 5px 7px; + font-size: 0.8rem; + cursor: pointer; +} + .btn-cl { font-size: 25px; @include opacity(0.3);