From eb884f992897274bf529371c982c9c33a3869266 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Sun, 1 Jun 2014 16:36:26 +0200 Subject: [PATCH 1/2] Switch Admin Settings Lists to Select2.js - and use jquery.sortable to allow sorting - support for autocompletion --- .../components/list_setting_component.js | 90 ++++++------------- .../components/list_setting_item_component.js | 44 --------- .../site_settings/setting_list.js.handlebars | 2 +- .../components/list-setting.js.handlebars | 9 +- .../stylesheets/common/admin/admin_base.scss | 37 ++++---- 5 files changed, 46 insertions(+), 136 deletions(-) delete mode 100644 app/assets/javascripts/admin/components/list_setting_item_component.js diff --git a/app/assets/javascripts/admin/components/list_setting_component.js b/app/assets/javascripts/admin/components/list_setting_component.js index 9f39d46ce..4676e0905 100644 --- a/app/assets/javascripts/admin/components/list_setting_component.js +++ b/app/assets/javascripts/admin/components/list_setting_component.js @@ -2,81 +2,41 @@ Provide a nice GUI for a pipe-delimited list in the site settings. @param settingValue is a reference to SiteSetting.value. + @param choices is a reference to SiteSetting.choices @class Discourse.ListSettingComponent @extends Ember.Component @namespace Discourse @module Discourse **/ + Discourse.ListSettingComponent = Ember.Component.extend({ - layoutName: 'components/list-setting', + tagName: 'div', - init: function() { - this._super(); - this.on("focusOut", this.uncacheValue); - this.set('children', []); + didInsertElement: function(){ + this.$("input").select2({ + multiple: false, + separator: "|", + tokenSeparators: [",", " ", "|"], + tags : this.get("choices") || [], + width: 'off' + }).on("change", function(obj) { + this.set("settingValue", obj.val.join("|")); + this.refreshSortables(); + }.bind(this)); + + this.refreshSortables(); }, - canAddNew: true, + refreshOnReset: function() { + this.$("input").select2("val", this.get("settingValue").split("|")); + }.observes("settingValue"), - readValues: function() { - return this.get('settingValue').split('|'); - }.property('settingValue'), - - /** - Transfer the debounced value into the settingValue parameter. - - This will cause a redraw of the child textboxes. - - @param newFocus {Number|undefined} Which list index to focus on next, or undefined to not refocus - **/ - uncacheValue: function(newFocus) { - var oldValue = this.get('settingValue'), - newValue = this.get('settingValueCached'), - self = this; - - if (newValue !== undefined && newValue !== oldValue) { - this.set('settingValue', newValue); - } - - if (newFocus !== undefined && newFocus > 0) { - Em.run.schedule('afterRender', function() { - var children = self.get('children'); - if (newFocus < children.length) { - $(children[newFocus].get('element')).focus(); - } else if (newFocus === children.length) { - $(self.get('element')).children().children('.list-add-value').focus(); - } - }); - } - }, - - setItemValue: function(index, item) { - var values = this.get('readValues'); - values[index] = item; - - // Remove blank items - values = values.filter(function(s) { return s !== ''; }); - this.setProperties({ - settingValueCached: values.join('|'), - canAddNew: true - }); - }, - - actions: { - addNewItem: function() { - var newValue = this.get('settingValue') + '|'; - this.setProperties({ - settingValue: newValue, - settingValueCached: newValue, - canAddNew: false - }); - - var self = this; - Em.run.schedule('afterRender', function() { - var children = self.get('children'); - $(children[children.length - 1].get('element')).focus(); - }); - } + refreshSortables: function() { + this.$("ul.select2-choices").sortable().on('sortupdate', function() { + this.$("input").select2("onSortEnd"); + }.bind(this)); } }); + + diff --git a/app/assets/javascripts/admin/components/list_setting_item_component.js b/app/assets/javascripts/admin/components/list_setting_item_component.js deleted file mode 100644 index fac5f339b..000000000 --- a/app/assets/javascripts/admin/components/list_setting_item_component.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - One item in a ListSetting. - - @param parent is the ListSettingComponent. - - @class Discourse.ListSettingItemComponent - @extends Ember.Component, Ember.TextSupport - @namespace Discourse - @module Discourse - **/ -Discourse.ListSettingItemComponent = Ember.Component.extend(Ember.TextSupport, { - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern'], - - _initialize: function() { - // _parentView is the #each - // parent is the ListSettingComponent - this.setProperties({ - value: this.get('_parentView.content'), - index: this.get('_parentView.contentIndex') - }); - this.get('parent').get('children')[this.get('index')] = this; - }.on('init'), - - markTab: function(e) { - var keyCode = e.keyCode || e.which; - - if (keyCode === 9) { - this.set('nextIndex', this.get('index') + (e.shiftKey ? -1 : 1)); - } - }.on('keyDown'), - - reloadList: function() { - var nextIndex = this.get('nextIndex'); - this.set('nextIndex', undefined); // one use only - this.get('parent').uncacheValue(nextIndex); - }.on('focusOut'), - - _elementValueDidChange: function() { - this._super(); - this.get('parent').setItemValue(this.get('index'), this.get('value')); - } -}); diff --git a/app/assets/javascripts/admin/templates/site_settings/setting_list.js.handlebars b/app/assets/javascripts/admin/templates/site_settings/setting_list.js.handlebars index 29bb00c70..6fadce5e2 100644 --- a/app/assets/javascripts/admin/templates/site_settings/setting_list.js.handlebars +++ b/app/assets/javascripts/admin/templates/site_settings/setting_list.js.handlebars @@ -2,7 +2,7 @@

{{unbound setting}}

- {{list-setting settingValue=value}} + {{list-setting settingValue=value choices=choices}}
{{unbound description}}
{{#if dirty}} diff --git a/app/assets/javascripts/discourse/templates/components/list-setting.js.handlebars b/app/assets/javascripts/discourse/templates/components/list-setting.js.handlebars index ddf8dcabe..830982fc4 100644 --- a/app/assets/javascripts/discourse/templates/components/list-setting.js.handlebars +++ b/app/assets/javascripts/discourse/templates/components/list-setting.js.handlebars @@ -1,8 +1,3 @@ -
- {{#each readValues}} - {{list-setting-item parent=view action=update classNames="list-input-item"}} - {{/each}} - {{#if canAddNew}} - - {{/if}} +
+
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 1f279b028..58b989039 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -205,30 +205,29 @@ -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; transition: border linear 0.2s, box-shadow linear 0.2s; - .list-input-item { - width: 90px; - margin: 2px 1px; - background-color: $secondary; - border: 1px solid scale-color-diff(); - border-radius: 3px; - box-shadow: inset 0 1px 1px rgba(51, 51, 51, 0.3); - -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; - transition: border linear 0.2s, box-shadow linear 0.2s; - - &:focus { - border-color: $tertiary; - outline: 0; - box-shadow: inset 0 1px 1px rgba(51, 51, 51, 0.3), 0 0 8px $tertiary; + li.select2-search-choice { + cursor: pointer; + .select2-search-choice-close { + content: "x" } } - .btn.list-add-value { - margin: 0px 3px; - padding: 4px 10px; - color: $tertiary; + li.sortable-placeholder { + padding: 3px 5px 3px 18px; + margin: 3px 0px 3px 5px; + position: relative; + line-height: 13px; + cursor: default; + border: 1px dashed #AAA; + border-radius: 3px; + background-clip: padding-box; + -moz-user-select: none; + background-color: none; + width: 3em; + height: 1em; } - } + } .desc { padding-top: 3px; From 572ee3b94d31481d06d5a5bd94f963fb1f4b58c1 Mon Sep 17 00:00:00 2001 From: Benjamin Kampmann Date: Sun, 1 Jun 2014 16:37:51 +0200 Subject: [PATCH 2/2] add site_setting choices - add site_setting choices lists to list entries - allows for handy autocompletion using the new select2.js UI - automatically merges plugin choices into existing list, allowing for easy extension --- config/site_settings.yml | 23 +++++++++++++++++++++++ lib/site_setting_extension.rb | 17 +++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/config/site_settings.yml b/config/site_settings.yml index 098af1310..b42756001 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -55,14 +55,37 @@ basic: refresh: true list: true default: 'latest|new|unread|starred|top|categories' + choices: + - latest + - new + - unread + - starred + - top + - categories + - read + - posted post_menu: client: true list: true default: 'like|edit|flag|delete|share|bookmark|admin|reply' + choices: + - like + - edit + - flag + - delete + - share + - bookmark + - admin + - reply share_links: client: true list: true default: 'twitter|facebook|google+|email' + choices: + - twitter + - facebook + - google+ + - email category_colors: client: true list: true diff --git a/lib/site_setting_extension.rb b/lib/site_setting_extension.rb index 1975c5513..3a723a5ef 100644 --- a/lib/site_setting_extension.rb +++ b/lib/site_setting_extension.rb @@ -42,6 +42,10 @@ module SiteSettingExtension @lists ||= [] end + def choices + @choices ||= {} + end + def hidden_settings @hidden_settings ||= [] end @@ -60,6 +64,11 @@ module SiteSettingExtension enum = opts[:enum] enums[name] = enum.is_a?(String) ? enum.constantize : enum end + if opts[:choices] + choices.has_key?(name) ? + choices[name].concat(opts[:choices]) : + choices[name] = opts[:choices] + end if opts[:list] lists << name end @@ -111,12 +120,16 @@ module SiteSettingExtension .map do |s, v| value = send(s) type = types[get_data_type(s, value)] - {setting: s, + opts = {setting: s, description: description(s), default: v, type: type.to_s, value: value.to_s, - category: categories[s]}.merge( type == :enum ? {valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?} : {}) + category: categories[s] + } + opts.merge({valid_values: enum_class(s).values, translate_names: enum_class(s).translate_names?}) if type == :enum + opts[:choices] = choices[s] if choices.has_key? s + opts end end