From 478ddabecac488a4e57bb36daea92b8981974377 Mon Sep 17 00:00:00 2001 From: alekseybobkov Date: Sun, 29 Jun 2014 14:28:07 +1100 Subject: [PATCH] Added the property grouping support to the Inspector --- modules/backend/assets/css/october.css | 82 ++++++- .../backend/assets/js/october.inspector.js | 211 +++++++++++++++--- .../assets/less/controls/inspector.less | 101 +++++++-- 3 files changed, 346 insertions(+), 48 deletions(-) diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 4d69f155f..ba5fa5df2 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -11406,6 +11406,29 @@ html.cssanimations .fancy-layout .form-tabless-fields .loading-indicator-contain -moz-border-radius: 0 0 2px 0; border-radius: 0 0 2px 0; } +.inspector-fields tr.group { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.inspector-fields tr.group th { + background: #e0e4e5; + font-weight: 600; + cursor: pointer; +} +.inspector-fields tr.collapsed { + display: none; +} +.inspector-fields tr.expanded { + display: table-row; +} +.inspector-fields.has-groups th { + padding-left: 20px; +} +.inspector-fields.has-groups tr.grouped th { + padding-left: 35px; +} .inspector-fields td { font-weight: 300; border-left: 1px solid #f2f2f2; @@ -11447,16 +11470,18 @@ html.cssanimations .fancy-layout .form-tabless-fields .loading-indicator-contain } .inspector-fields th > div { position: relative; +} +.inspector-fields th > div > div { white-space: nowrap; padding-right: 10px; text-overflow: ellipsis; overflow: hidden; width: 100%; } -.inspector-fields th > div span.info { +.inspector-fields th > div > div span.info { display: inline-block; position: absolute; - right: -6px; + right: -1px; top: 3px; font-size: 14px; width: 10px; @@ -11465,10 +11490,57 @@ html.cssanimations .fancy-layout .form-tabless-fields .loading-indicator-contain opacity: 0.4; filter: alpha(opacity=40); } -.inspector-fields th > div span.info:hover { +.inspector-fields th > div > div span.info:before { + margin-left: 3px; +} +.inspector-fields th > div > div span.info:hover { opacity: 1; filter: alpha(opacity=100); } +.inspector-fields th > div a.expandControl { + display: block; + position: absolute; + width: 12px; + height: 12px; + left: -15px; + top: 2px; + text-indent: -100000em; +} +.inspector-fields th > div a.expandControl span { + position: absolute; + display: inline-block; + left: 0; + top: 0; + width: 12px; + height: 12px; +} +.inspector-fields th > div a.expandControl span:after { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + *margin-right: .3em; + content: "\f105"; + position: absolute; + left: 4px; + top: -2px; + width: 12px; + height: 12px; + font-size: 13px; + color: #333333; + text-indent: 0; +} +.inspector-fields th > div a.expandControl.expanded span:after { + font-family: FontAwesome; + font-weight: normal; + font-style: normal; + text-decoration: inherit; + -webkit-font-smoothing: antialiased; + *margin-right: .3em; + content: "\f107"; + left: 2px; +} .inspector-fields input[type=text] { display: block; width: 100%; @@ -11560,9 +11632,9 @@ div.control-popover.control-inspector > div:after { top: 5px; color: #95a5a6; } -.select2-drop.ocInspectorDropdown .select2-search .select2-input { +.select2-drop.ocInspectorDropdown .select2-search input.select2-input { min-height: 26px; - background: transparent; + background: transparent!important; padding-left: 12px; padding-right: 12px; } diff --git a/modules/backend/assets/js/october.inspector.js b/modules/backend/assets/js/october.inspector.js index c54e0deb9..1c81373d3 100644 --- a/modules/backend/assets/js/october.inspector.js +++ b/modules/backend/assets/js/october.inspector.js @@ -21,6 +21,7 @@ * Each element in the array is an object with the following properties: * - property * - title + * - group (optional) * - type (currently supported types are: string, checkbox, dropdown) * - description (optional) * - validationPattern (regex pattern for for validating the value, supported by the text editor) @@ -84,42 +85,47 @@ 'an hidden input element with the data-inspector-values property.') } - Inspector.prototype.getPopoverTemplate = function() { - return ' \ -
\ -

{{title}}

\ - {{#description}} \ -

{{description}}

\ - {{/description}} \ - \ -
\ -
\ - \ - {{#properties}} \ - \ - \ - {{#editor}}{{/editor}} \ - \ - {{/properties}} \ -
\ - {{title}} \ - {{#info}}{{/info}} \ -
\ - \ + Inspector.prototype.getPopoverTemplate = function() { + return ' \ +
\ +

{{title}}

\ + {{#description}} \ +

{{description}}

\ + {{/description}} \ + \ +
\ + \ + \ + {{#properties}} \ + \ + \ + {{#editor}}{{/editor}} \ + \ + {{/properties}} \ +
\ + {{#expandControl}}{{/expandControl}} \ + {{title}} \ + {{#info}}{{/info}} \ +
\ + \ ' } Inspector.prototype.init = function() { var self = this, + fieldsConfig = this.preprocessConfig(), data = { title: this.$el.data('inspector-title'), description: this.$el.data('inspector-description'), - properties: this.config, + properties: fieldsConfig.properties, editor: function() { return function(text, render) { - return self.renderEditor(this, render) + if (this.itemType == 'property') + return self.renderEditor(this, render) } }, info: function() { @@ -132,6 +138,41 @@ return function(text, render) { return 'prop-'+render(text).replace('.', '-') } + }, + colspan: function() { + return function(text, render) { + return this.itemType == 'group' ? 'colspan="2"' : null + } + }, + tableClass: function() { + return function(text, render) { + return fieldsConfig.hasGroups ? 'has-groups' : null + } + }, + cellClass: function() { + return function(text, render) { + var result = this.itemType + ((this.itemType == 'property' && this.groupIndex !== undefined) ? ' grouped' : '') + + if (this.itemType == 'property' && this.groupIndex !== undefined) + result += self.groupExpanded(this.group) ? ' expanded' : ' collapsed' + + return result + } + }, + expandControl: function() { + return function(text, render) { + if (this.itemType == 'group') { + this.itemStatus = self.groupExpanded(this.title) ? 'expanded' : '' + + return render('Expand/collapse', this) + } + } + }, + dataGroupIndex: function() { + return function(text, render) { + return this.groupIndex !== undefined && this.itemType == 'property' ? render('data-group-index={{groupIndex}}', this) : '' + } + } } @@ -190,6 +231,12 @@ }) $('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body'}) + + var $container = self.$el.data('oc.popover').$container + $container.on('click', 'tr.group', function(){ + self.toggleGroup($('a.expandControl', this), $container) + return false + }) } var e = $.Event('showing.oc.inspector') @@ -201,6 +248,118 @@ displayPopover() } + // Creates group nodes in the property set + // + Inspector.prototype.preprocessConfig = function() { + var fields = [], + result = { + hasGroups: false, + properties: [] + }, + groupIndex = 0 + + function findGroup(title) { + var groups = $.grep(fields, function(item) { + return item.itemType !== undefined && item.itemType == 'group' && item.title == title + }) + + if (groups.length > 0) + return groups[0] + + return null + } + + $.each(this.config, function() { + this.itemType = 'property' + + if (this.group === undefined) + fields.push(this) + else { + var group = findGroup(this.group) + + if (!group) { + group = { + itemType: 'group', + title: this.group, + properties: [], + groupIndex: groupIndex + } + + groupIndex++ + fields.push(group) + } + + this.groupIndex = group.groupIndex + group.properties.push(this) + } + }) + + $.each(fields, function() { + result.properties.push(this) + + if (this.itemType == 'group') { + result.hasGroups = true + + $.each(this.properties, function() { + result.properties.push(this) + }) + + delete this.properties + } + }) + + return result + } + + Inspector.prototype.toggleGroup = function(link, $container) { + var $link = $(link), + groupIndex = $link.data('group-index'), + propertyRows = $('tr[data-group-index='+groupIndex+']', $container), + duration = Math.round(100 / propertyRows.length), + collapse = true, + statuses = this.loadGroupExpandedStatuses(), + title = $('div.title-element', $link.closest('tr')).attr('title') + + if ($link.hasClass('expanded')) { + $link.removeClass('expanded') + statuses[title] = false + } else { + $link.addClass('expanded') + collapse = false + statuses[title] = true + } + + propertyRows.each(function(index){ + var self = $(this) + setTimeout(function(){ + self.toggleClass('collapsed', collapse) + self.toggleClass('expanded', !collapse) + }, index*duration) + + }) + + this.writeGroupExpandedStatuses(statuses) + } + + Inspector.prototype.loadGroupExpandedStatuses = function() { + var statuses = this.$el.data('inspector-group-statuses') + + return statuses !== undefined ? JSON.parse(statuses) : {} + } + + Inspector.prototype.writeGroupExpandedStatuses = function(statuses) { + this.$el.data('inspector-group-statuses', JSON.stringify(statuses)) + } + + Inspector.prototype.groupExpanded = function(title) { + var statuses = this.loadGroupExpandedStatuses() + + if (statuses[title] !== undefined) + return statuses[title] + + return false + } + Inspector.prototype.initProperties = function() { var propertyValuesStr = $.trim(this.propertyValuesField.val()) diff --git a/modules/backend/assets/less/controls/inspector.less b/modules/backend/assets/less/controls/inspector.less index 4be545d4c..c112085d1 100644 --- a/modules/backend/assets/less/controls/inspector.less +++ b/modules/backend/assets/less/controls/inspector.less @@ -23,6 +23,31 @@ } } + tr.group { + .user-select(none); + + th { + background: #e0e4e5; + font-weight: 600; + cursor: pointer; + } + } + + tr { + &.collapsed {display: none;} + &.expanded {display: table-row;} + } + + &.has-groups { + th { + padding-left: 20px; + } + + tr.grouped th { + padding-left: 35px; + } + } + td { font-weight: 300; border-left: 1px solid @color-inspector-bg; @@ -72,24 +97,66 @@ > div { position: relative; - white-space: nowrap; - padding-right: 10px; - text-overflow: ellipsis; - overflow: hidden; - width: 100%; - span.info { - display: inline-block; + > div { + white-space: nowrap; + padding-right: 10px; + text-overflow: ellipsis; + overflow: hidden; + width: 100%; + + span.info { + display: inline-block; + position: absolute; + right: -1px; + top: 3px; + font-size: 14px; + width: 10px; + height: 12px; + line-height: 80%; + .opacity(0.4); + &:before { + margin-left: 3px; + } + &:hover { + .opacity(1); + } + } + } + + a.expandControl { + display: block; position: absolute; - right: -6px; - top: 3px; - font-size: 14px; - width: 10px; + width: 12px; height: 12px; - line-height: 80%; - .opacity(0.4); - &:hover { - .opacity(1); + left: -15px; + top: 2px; + text-indent: -100000em; + + span { + position: absolute; + display: inline-block; + left: 0; + top: 0; + width: 12px; + height: 12px; + + &:after { + .icon(@angle-right); + position: absolute; + left: 4px; + top: -2px; + width: 12px; + height: 12px; + font-size: 13px; + color: #333333; + text-indent: 0; + } + } + + &.expanded span:after { + .icon(@angle-down); + left: 2px; } } } @@ -210,9 +277,9 @@ div.control-popover { color: #95a5a6; } - .select2-input { + input.select2-input { min-height: 26px; - background: transparent; + background: transparent!important; padding-left: 12px; padding-right: 12px; }