From fdd83b53c906e983cfb4b21d476897fc0d63f731 Mon Sep 17 00:00:00 2001 From: alekseybobkov Date: Thu, 28 Aug 2014 19:03:47 +1100 Subject: [PATCH] Updates in the back-end UX --- modules/backend/assets/css/october.css | 127 +++++++++++++++++- .../assets/images/treeview-submenu-tabs.png | Bin 0 -> 2164 bytes .../backend/assets/js/october.inputpreset.js | 51 +++---- modules/backend/assets/js/october.treelist.js | 7 +- modules/backend/assets/js/october.treeview.js | 82 ++++++----- .../assets/less/controls/treeview.less | 110 +++++++++++++-- .../backend/assets/less/core/variables.less | 1 + .../richeditor/assets/js/richeditor.js | 15 ++- modules/cms/classes/CmsCompoundObject.php | 19 ++- 9 files changed, 322 insertions(+), 90 deletions(-) create mode 100644 modules/backend/assets/images/treeview-submenu-tabs.png diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index b361ea112..2427f87ce 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -13467,6 +13467,9 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { top: 8px; z-index: 2000; } +.control-treeview { + margin-bottom: 40px; +} .control-treeview ol { margin: 0; padding: 0; @@ -13480,12 +13483,18 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { .control-treeview ol > li > div { font-size: 13px; font-weight: 600; - color: #2b3e50; background: #ffffff; border-bottom: 1px solid #ecf0f1; - padding: 16px 16px 15px 61px; position: relative; - cursor: pointer; +} +.control-treeview ol > li > div > a { + color: #2b3e50; + padding: 16px 45px 15px 61px; + display: block; + text-decoration: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } .control-treeview ol > li > div:before { content: ' '; @@ -13549,7 +13558,7 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { -webkit-transition: opacity 0.4s; transition: opacity 0.4s; position: absolute; - right: 10px; + right: 24px; bottom: 5px; width: 14px; height: 14px; @@ -13571,9 +13580,68 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { .control-treeview ol > li > div span.borders { font-size: 0; } +.control-treeview ol > li > div > ul.submenu { + position: absolute; + left: 20px; + bottom: -37px; + padding: 0; + list-style: none; + z-index: 200; + height: 37px; + display: none; + margin-left: 15px; + background: transparent url(../images/treeview-submenu-tabs.png) repeat-x left -39px; +} +.control-treeview ol > li > div > ul.submenu:before, +.control-treeview ol > li > div > ul.submenu:after { + background: transparent url(../images/treeview-submenu-tabs.png) no-repeat left top; + content: ' '; + display: block; + width: 20px; + height: 37px; + position: absolute; + top: 0; +} +.control-treeview ol > li > div > ul.submenu:before { + left: -20px; +} +.control-treeview ol > li > div > ul.submenu:after { + background-position: -100px top; + right: -20px; +} +.control-treeview ol > li > div > ul.submenu li { + font-size: 12px; +} +.control-treeview ol > li > div > ul.submenu li a { + display: block; + padding: 4px 3px 0 3px; + color: #ffffff; + text-decoration: none; + outline: none; +} +.control-treeview ol > li > div > ul.submenu li a i { + margin-right: 5px; +} +.control-treeview ol > li > div:hover > ul.submenu { + display: block; +} +.control-treeview ol > li > div .checkbox { + position: absolute; + top: 19px; + right: 17px; +} +.control-treeview ol > li > div .checkbox label { + margin-right: 0; +} +.control-treeview ol > li > div .checkbox label:before { + border-color: #cccccc; +} .control-treeview ol > li.dragged div, .control-treeview ol > li > div:hover { background-color: #58b6f7 !important; +} +.control-treeview ol > li.dragged div > a, +.control-treeview ol > li > div:hover > a { color: #ffffff !important; } .control-treeview ol > li.dragged div:before, @@ -13603,6 +13671,9 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { .control-treeview ol > li.dragged.has-subitems > div:before { background-position: 0px -52px; } +.control-treeview ol > li.dragged div > ul.submenu { + display: none!important; +} .control-treeview ol > li > ol { padding-left: 20px; } @@ -13669,14 +13740,16 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { } .control-treeview ol > li.drop-target > div { background-color: #2581b8!important; +} +.control-treeview ol > li.drop-target > div > a { + color: #ffffff; +} +.control-treeview ol > li.drop-target > div > a > span.comment { color: #ffffff; } .control-treeview ol > li.drop-target > div:before { background-position: 0px -80px; } -.control-treeview ol > li.drop-target > div > span.comment { - color: #ffffff; -} .control-treeview ol > li.drop-target.has-subitems > div:before { background-position: 0px -52px; } @@ -13726,6 +13799,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -20px; padding-left: 71px; } +.control-treeview ol > li > ol > li > div > a { + margin-left: -71px; + padding-left: 71px; +} .control-treeview ol > li > ol > li > div:before { margin-left: 10px; } @@ -13736,6 +13813,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -40px; padding-left: 81px; } +.control-treeview ol > li > ol > li > ol > li > div > a { + margin-left: -81px; + padding-left: 81px; +} .control-treeview ol > li > ol > li > ol > li > div:before { margin-left: 20px; } @@ -13746,6 +13827,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -60px; padding-left: 91px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -91px; + padding-left: 91px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 30px; } @@ -13756,6 +13841,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -80px; padding-left: 101px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -101px; + padding-left: 101px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 40px; } @@ -13766,6 +13855,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -100px; padding-left: 111px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -111px; + padding-left: 111px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 50px; } @@ -13776,6 +13869,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -120px; padding-left: 121px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -121px; + padding-left: 121px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 60px; } @@ -13786,6 +13883,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -140px; padding-left: 131px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -131px; + padding-left: 131px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 70px; } @@ -13796,6 +13897,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -160px; padding-left: 141px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -141px; + padding-left: 141px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 80px; } @@ -13806,6 +13911,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -180px; padding-left: 151px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -151px; + padding-left: 151px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 90px; } @@ -13816,6 +13925,10 @@ div[data-control="balloon-selector"]:not(.control-disabled) ul li:hover { margin-left: -200px; padding-left: 161px; } +.control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div > a { + margin-left: -161px; + padding-left: 161px; +} .control-treeview ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > div:before { margin-left: 100px; } diff --git a/modules/backend/assets/images/treeview-submenu-tabs.png b/modules/backend/assets/images/treeview-submenu-tabs.png new file mode 100644 index 0000000000000000000000000000000000000000..198f46cb447866b4e993f5695d84359249eeb64b GIT binary patch literal 2164 zcma)8X;@QN8V-W8iIkwoGSwIq#F!NVBoY{sfJ6wpCJdltxj=v{Bo_$*15Yn=A39I(^W1aJcb@lszxBt-^!MFs zWMF9kgTag#Ui3g{mqR~OZymG>d%nE`Z5E&h8w?Uhf{7d{0CVSyxd4J8Bb-PWJkf!K$L~fEaCmzx9)~56(0DxA!HJA_M7;Zu zkT)qmk{n3)eCG?YD9C6Kl#sF5#Kc5QqCG||jl$wdBoY=!z!C^(2!WO*i9n7VEs|NU zD$oHLPb!dr0~`x zr<3PL_1S6E-hdS3U7#W`~Ehvy4np!z} zr-WCW9HwU2%@wM8Vsycm6I$Nx_kRA()+qJ(hB@E#s;c>oSr*0VtxH*8EuM}u zp>Y}5be)%myOzdo{cEpck5(bPOh58+XW#bx+WD4OML=@r{>2<0lReyMxn<=(s_TIi z`}1|EN>u#cIEQP;Vvg5KhErJy-j}tT;dvu_Zg!q*L~8{lu)e?8a*3Z=5E77fBtUD- z)cyvqcKoYrwIwC}pKgm>Gf58Gvz&3PC_KDD>sj`;NWi12I(5tAg>x|Vsnq%2nMU}8 za_MS6%A{=PwVRH+THJx+ONAk!O}d5;(Oi1@S@R6rklBn|n;gD6_uVg3$Ugt&F?;LN z2XAz#W6U~+t2ggbvtR6QYOhcmZGC9RrJbZcSpP*qtCu zdUwot?Y)t~&FX=XA%}^R_qwBnX9m0uG|X9J!4rcHNB%=ZyLWXr&}x>Cur0ABrd-q3 zJ78CU{yg1V!c;mRl&kW5Fu6WeA(mHmaFL2IR>^?Ur_w7vXkz2UKwq5maLw{3=hcd7PaGi8oTbSV4K`gCKky%%c)=3tS9(7bniO`T;GGL;6G5;sBx9s~i@%^VE<|{#J6Q!c))>zrgD;tNEhqn9O z4xM2uFwV};yRBwn_obe$>^v1<2?v2IgxsmYS6LQ!|Iy>NH85(a<-GRXBh@dXf6wj* zIhhy}Z2bPkm38(4tMiA$s~cDiyWl>y7-`3|S2u~e^iN}b7fIL@j8ezc@M5^4SszDp zOhi3Rfotbt6n7q<=_RUS*QbzwHcuwq9x>B-Y+x@bU@S3)%zWc)!ce~^6xW|jzq&zE zXOvJHKY6qxJG$Mp?D-nUhgGCw?Y1R@!$-Fi+K={BmG+!%WByEv!#nKDXgjc~C}KRWb>4C+|f2GvTaYp&{o+11`0)W6fS zo*z3Ep_jG|mH{(g6Y+NalcrvpmifL+f_ygMC#{7?s!@}cC%%&DA7MLN(_TD2m!tjK z*s;WFS|`rSDZ=Hf+oLp8<+jG_yqrLldaXI^bwI=H^=ETFns)%h! ol').sortable($.extend(sortableOptions, options)) } + TreeListWidget.prototype.unbind = function() { + this.$el.find('> ol').sortable('destroy') + this.$el.removeData('oc.treelist') + } + TreeListWidget.DEFAULTS = { handle: null } @@ -51,7 +56,7 @@ var data = $this.data('oc.treelist') var options = $.extend({}, TreeListWidget.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('oc.treelist', (data = new TreeListWidget(this, options))) - if (typeof option == 'string') result = data[option].call($this) + if (typeof option == 'string') result = data[option].call(data) if (typeof result != 'undefined') return false }) diff --git a/modules/backend/assets/js/october.treeview.js b/modules/backend/assets/js/october.treeview.js index a85421a9c..0e6e5a3a2 100644 --- a/modules/backend/assets/js/october.treeview.js +++ b/modules/backend/assets/js/october.treeview.js @@ -30,36 +30,7 @@ * Init the sortable */ - this.$el.treeListWidget({ - tweakCursorAdjustment: function(adjustment) { - if (!adjustment) - return adjustment - - if (self.$scrollbar.length > 0) { - adjustment.top -= self.$scrollbar.scrollTop() - } - - return adjustment; - }, - isValidTarget: function($item, container) { - return $(container.el).closest('li').attr('data-status') != 'collapsed' - }, - useAnimation: true, - handle: 'span.drag-handle' - }) - - this.$el.on('move.oc.treelist', function(){ - setTimeout(function(){ - self.$allItems.removeClass('drop-target') - self.fixSubItems() - self.sendReorderRequest() - }, 50) - }) - - this.$el.on('aftermove.oc.treelist', function(event, data) { - self.$allItems.removeClass('drop-target') - data.container.el.closest('li').addClass('drop-target') - }) + this.initSortable() /* * Create expand/collapse icons and drag handles @@ -71,8 +42,15 @@ * Bind the click events */ - this.$el.on('click', 'li > div', function(event) { - var e = $.Event('open.oc.treeview', {relatedTarget: $(this).parent().get(0), clickEvent: event}) + this.$el.on('click', 'li > div > ul.submenu li a', function(event) { + var e = $.Event('submenu.oc.treeview', {relatedTarget: this, clickEvent: event}) + self.$el.trigger(e, this) + + return false + }) + + this.$el.on('click', 'li > div > a', function(event) { + var e = $.Event('open.oc.treeview', {relatedTarget: $(this).closest('li').get(0), clickEvent: event}) self.$el.trigger(e, this) return false @@ -192,6 +170,44 @@ this.$el.request(this.options.reorderHandler, {data: {structure: JSON.stringify(groups)}}) } + TreeView.prototype.initSortable = function() { + var self = this + + if (this.$el.data('oc.treelist')) + this.$el.treeListWidget('unbind') + + this.$el.treeListWidget({ + tweakCursorAdjustment: function(adjustment) { + if (!adjustment) + return adjustment + + if (self.$scrollbar.length > 0) { + adjustment.top -= self.$scrollbar.scrollTop() + } + + return adjustment; + }, + isValidTarget: function($item, container) { + return $(container.el).closest('li').attr('data-status') != 'collapsed' + }, + useAnimation: true, + handle: 'span.drag-handle' + }) + + this.$el.on('move.oc.treelist', function(){ + setTimeout(function(){ + self.$allItems.removeClass('drop-target') + self.fixSubItems() + self.sendReorderRequest() + }, 50) + }) + + this.$el.on('aftermove.oc.treelist', function(event, data) { + self.$allItems.removeClass('drop-target') + data.container.el.closest('li').addClass('drop-target') + }) + } + TreeView.prototype.markActive = function(dataId) { $('li', this.$el).removeClass('active') @@ -202,7 +218,9 @@ } TreeView.prototype.update = function() { + this.$allItems = $('li', this.$el) this.createItemControls() + this.initSortable() if (this.dataId !== undefined) this.markActive(this.dataId) diff --git a/modules/backend/assets/less/controls/treeview.less b/modules/backend/assets/less/controls/treeview.less index e97c8e6d6..35e14b090 100644 --- a/modules/backend/assets/less/controls/treeview.less +++ b/modules/backend/assets/less/controls/treeview.less @@ -1,4 +1,6 @@ .control-treeview { + margin-bottom: 40px; + ol { margin: 0; padding: 0; @@ -11,12 +13,17 @@ > div { font-size: 13px; font-weight: 600; - color: @color-treeview-item-title; background: @color-treeview-item-bg; border-bottom: 1px solid @color-panel-light; - padding: 16px 16px 15px 61px; position: relative; - cursor: pointer; + + > a { + color: @color-treeview-item-title; + padding: 16px 45px 15px 61px; + display: block; + text-decoration: none; + .box-sizing(border-box); + } &:before { content: ' '; @@ -71,7 +78,7 @@ .transition(opacity 0.4s); position: absolute; - right: 10px; + right: 24px; bottom: 5px; width: 14px; height: 14px; @@ -88,11 +95,83 @@ span.borders { font-size: 0; } + + > ul.submenu { + position: absolute; + left: 20px; + bottom: -37px; + padding: 0; + list-style: none; + z-index: 200; + height: 37px; + display: none; + margin-left: 15px; + + background: transparent url(../images/treeview-submenu-tabs.png) repeat-x left -39px; + + &:before, &:after { + background: transparent url(../images/treeview-submenu-tabs.png) no-repeat left top; + content: ' '; + display: block; + width: 20px; + height: 37px; + position: absolute; + top: 0; + } + + &:before { + left: -20px; + } + + &:after { + background-position: -100px top; + right: -20px; + } + + li { + font-size: 12px; + + a { + display: block; + padding: 4px 3px 0 3px; + color: @color-treeview-submenu-text; + text-decoration: none; + outline: none; + + i { + margin-right: 5px; + } + } + } + } + + &:hover { + > ul.submenu { + display: block; + } + } + + .checkbox { + position: absolute; + top: 19px; + right: 17px; + + label { + margin-right: 0; + + &:before { + border-color: @color-filelist-cb-border; + } + } + } } &.dragged div, > div:hover { background-color: @color-treeview-hover-bg!important; - color: @color-treeview-hover-text!important; + + > a { + color: @color-treeview-hover-text!important; + } &:before { background-position: 0px -80px; @@ -123,6 +202,10 @@ background-position: 0px -52px; } } + + div > ul.submenu { + display: none!important; + } } > ol { @@ -193,15 +276,17 @@ &.drop-target { > div { background-color: #2581b8!important; - color: @color-treeview-hover-text; + + > a { + color: @color-treeview-hover-text; + > span.comment { + color: @color-treeview-hover-text; + } + } &:before { background-position: 0px -80px; } - - > span.comment { - color: @color-treeview-hover-text; - } } &.has-subitems > div:before { @@ -264,6 +349,11 @@ margin-left: -20-(@max-level - @level)*20px; padding-left: 61+(@max-level - @level + 1)*10px; + > a { + margin-left: -61-(@max-level - @level + 1)*10px; + padding-left: 61+(@max-level - @level + 1)*10px; + } + &:before { margin-left: (@max-level - @level + 1)*10px; } diff --git a/modules/backend/assets/less/core/variables.less b/modules/backend/assets/less/core/variables.less index 9162120a0..63b648d53 100644 --- a/modules/backend/assets/less/core/variables.less +++ b/modules/backend/assets/less/core/variables.less @@ -270,6 +270,7 @@ @color-treeview-hover-bg: #58b6f7; @color-treeview-hover-text: #ffffff; @color-treeview-item-active-comment: #8f8f8f; +@color-treeview-submenu-text: #ffffff; // // Sizes diff --git a/modules/backend/formwidgets/richeditor/assets/js/richeditor.js b/modules/backend/formwidgets/richeditor/assets/js/richeditor.js index 10eb2c037..9fecb45ee 100644 --- a/modules/backend/formwidgets/richeditor/assets/js/richeditor.js +++ b/modules/backend/formwidgets/richeditor/assets/js/richeditor.js @@ -19,6 +19,7 @@ this.options = options this.$el = $(element) this.$textarea = this.$el.find('>textarea:first') + this.$form = this.$el.closest('form') this.editor = null this.init(); @@ -45,7 +46,8 @@ var redactorOptions = { focusCallback: function() { self.$el.addClass('editor-focus') }, blurCallback: function() { self.$el.removeClass('editor-focus') }, - initCallback: function() { self.build() } + initCallback: function() { self.build() }, + changeCallback: function() { self.onChange() } } if (this.options.stylesheet) { @@ -61,11 +63,11 @@ } RichEditor.prototype.build = function() { - var $iframe = this.$textarea.redactor('getIframe'), - $toolbar = this.$textarea.redactor('getToolbar'), + var $iframe = $('iframe', this.$el), + $toolbar = $('.redactor_toolbar', this.$el), $html = $('html') - if (!$iframe) + if (!$iframe.length) return if (this.$el.hasClass('stretch')) { @@ -84,6 +86,11 @@ }) } + RichEditor.prototype.onChange = function() { + this.$form.trigger('change') + + } + // RICHEDITOR PLUGIN DEFINITION // ============================ diff --git a/modules/cms/classes/CmsCompoundObject.php b/modules/cms/classes/CmsCompoundObject.php index 980040c44..7d2c50bf1 100644 --- a/modules/cms/classes/CmsCompoundObject.php +++ b/modules/cms/classes/CmsCompoundObject.php @@ -5,6 +5,7 @@ use Cms\Classes\CodeBase; use System\Classes\SystemException; use Cms\Classes\FileHelper; use October\Rain\Support\ValidationException; +use Cms\Classes\ViewBag; /** * This is a base class for CMS objects that have multiple sections - pages, partials and layouts. @@ -47,6 +48,10 @@ class CmsCompoundObject extends CmsObject protected $settingsValidationMessages = []; + protected $viewBagValidationRules = []; + + protected $viewBagValidationMessages = []; + protected $viewBagCache = false; /** @@ -217,8 +222,12 @@ class CmsCompoundObject extends CmsObject $componentName = 'viewBag'; - if (!isset($this->settings['components'][$componentName])) - return $this->viewBagCache = null; + if (!isset($this->settings['components'][$componentName])) { + $viewBag = new ViewBag(null, []); + $viewBag->name = $componentName; + + return $this->viewBagCache = $viewBag; + } return $this->viewBagCache = ComponentManager::instance()->makeComponent( $componentName, @@ -266,5 +275,11 @@ class CmsCompoundObject extends CmsObject $validation = Validator::make($this->settings, $this->settingsValidationRules, $this->settingsValidationMessages); if ($validation->fails()) throw new ValidationException($validation); + + if ($this->viewBagValidationRules && isset($this->settings['viewBag'])) { + $validation = Validator::make($this->settings['viewBag'], $this->viewBagValidationRules, $this->viewBagValidationMessages); + if ($validation->fails()) + throw new ValidationException($validation); + } } } \ No newline at end of file