From cad610762437ded858d6eb4a29494fdc26c79d17 Mon Sep 17 00:00:00 2001 From: Joffrey JAFFEUX Date: Wed, 16 Aug 2017 00:41:56 +0200 Subject: [PATCH] refactors select-box Note: this commit also now uses select-box for mobile topics controls --- .../admin/templates/customize-themes-show.hbs | 2 +- .../discourse/components/select-box.js.es6 | 205 ++++++++++-------- .../select-box/select-box-row.js.es6 | 10 +- .../topic-footer-mobile-dropdown.js.es6 | 20 +- .../templates/components/select-box.hbs | 8 +- .../select-box/select-box-collection.hbs | 10 +- .../select-box/select-box-header.hbs | 2 +- .../components/select-box/select-box-row.hbs | 6 +- config/locales/client.en.yml | 2 +- .../components/select-box-test.js.es6 | 69 +++--- 10 files changed, 178 insertions(+), 156 deletions(-) diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs index c9b4d43d3d7..2fed8ebdf29 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -36,7 +36,7 @@

{{i18n "admin.customize.theme.color_scheme"}}

{{i18n "admin.customize.theme.color_scheme_select"}}

-

{{d-select-box data=colorSchemes +

{{d-select-box content=colorSchemes textKey="name" filterable=true value=colorSchemeId diff --git a/app/assets/javascripts/discourse/components/select-box.js.es6 b/app/assets/javascripts/discourse/components/select-box.js.es6 index 80469785d69..a56d23aeb3d 100644 --- a/app/assets/javascripts/discourse/components/select-box.js.es6 +++ b/app/assets/javascripts/discourse/components/select-box.js.es6 @@ -1,4 +1,4 @@ -import { observes } from "ember-addons/ember-computed-decorators"; +import { on, observes } from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ classNames: "select-box", @@ -15,10 +15,11 @@ export default Ember.Component.extend({ caretUpIcon: "caret-up", caretDownIcon: "caret-down", + headerText: null, icon: null, value: null, - noDataText: I18n.t("select_box.no_data"), + noContentText: I18n.t("select_box.no_content"), lastHoveredId: null, idKey: "id", @@ -40,116 +41,97 @@ export default Ember.Component.extend({ verticalOffset: 0, horizontalOffset: 0, - _renderBody: false, + renderBody: false, init() { this._super(); - if(!this.get("data")) { - this.set("data", []); + if (!this.get("content")) { + this.set("content", []); } this.setProperties({ componentId: this.elementId, - filteredData: [], - selectedData: {} + filteredContent: [], + selectedContent: {} }); }, + @observes("value") + _valueChanged: function() { + if (Ember.isNone(this.get("value"))) { + this.set("lastHoveredId", null); + } + + this.set("filteredContent", this._remapContent(this.get("content"))); + }, + @observes("filter") - _filter: function() { - if(_.isEmpty(this.get("filter"))) { - this.set("filteredData", this._remapData(this.get("data"))); + _filterChanged: function() { + if (Ember.isEmpty(this.get("filter"))) { + this.set("filteredContent", this._remapContent(this.get("content"))); } else { - const filtered = _.filter(this.get("data"), (data)=> { - return data[this.get("textKey")].toLowerCase().indexOf(this.get("filter")) > -1; + const filtered = _.filter(this.get("content"), (content) => { + return content[this.get("textKey")].toLowerCase().indexOf(this.get("filter")) > -1; }); - this.set("filteredData", this._remapData(filtered)); + this.set("filteredContent", this._remapContent(filtered)); } }, - @observes("expanded", "filteredData") - _expand: function() { - if(this.get("expanded")) { - this.setProperties({focused: false, _renderBody: true}); + @observes("expanded") + _expandedChanged: function() { + if (this.get("expanded")) { + this.setProperties({ focused: false, renderBody: true }); - Ember.$(document).on("keydown.select-box", (event) => { - const keyCode = event.keyCode || event.which; - if (keyCode === 9) { - this.set("expanded", false); - } - }); - - if(_.isUndefined(this.get("lastHoveredId"))) { + if (Ember.isNone(this.get("lastHoveredId"))) { this.set("lastHoveredId", this.get("value")); } - - Ember.run.scheduleOnce("afterRender", this, () => { - this.$(".select-box-filter .filter-query").focus(); - this.$(".select-box-collection").css("max-height", this.get("maxCollectionHeight")); - this.$().removeClass("is-reversed"); - - const offsetTop = this.$()[0].getBoundingClientRect().top; - const windowHeight = Ember.$(window).height(); - const headerHeight = this.$(".select-box-header").outerHeight(); - const filterHeight = this.$(".select-box-filter").outerHeight(); - - if(windowHeight - (offsetTop + this.get("maxCollectionHeight") + filterHeight + headerHeight) < 0) { - this.$().addClass("is-reversed"); - this.$(".select-box-body").css({ - left: this.get("horizontalOffset"), - top: "", - bottom: headerHeight + this.get("verticalOffset") - }); - } else { - this.$(".select-box-body").css({ - left: this.get("horizontalOffset"), - top: headerHeight + this.get("verticalOffset"), - bottom: "" - }); - } - - this.$(".select-box-wrapper").css({ - width: this.get("maxWidth"), - display: "block", - height: headerHeight + this.$(".select-box-body").outerHeight() - }); - }); - } else { - Ember.$(document).off("keydown.select-box"); - this.$(".select-box-wrapper").hide(); }; }, - willDestroyElement() { - this._super(); - - Ember.$(document).off("click.select-box"); - Ember.$(document).off("keydown.select-box"); + @on("willDestroyElement") + _unbindEvents: function() { + $(document).off("click.select-box"); + $(document).off("keydown.select-box"); this.$(".select-box-offscreen").off("focusin.select-box"); this.$(".select-box-offscreen").off("focusout.select-box"); }, - didReceiveAttrs() { - this._super(); + @on("didRender") + _configureSelectBoxDOM: function() { + if (this.get("expanded")) { + this.$(".select-box-body").css('width', this.get("maxWidth")); + this.$(".select-box-filter .filter-query").focus(); + this.$(".select-box-collection").css("max-height", this.get("maxCollectionHeight")); - this.set("lastHoveredId", this.get("data")[this.get("idKey")]); - this.set("filteredData", this._remapData(this.get("data"))); - this._setSelectedData(this.get("data")); + this._bindTab(); + this._applyDirection(); + this._positionSelectBoxWrapper(); + } else { + $(document).off("keydown.select-box"); + this.$(".select-box-wrapper").hide(); + } }, - didRender() { - this._super(); + @observes("content.[]") + @on("didReceiveAttrs") + _contentChanged: function() { + if (!Ember.isNone(this.get("value"))) { + this.set("lastHoveredId", this.get("content")[this.get("idKey")]); + } else { + this.set("lastHoveredId", null); + } - this.$(".select-box-body").css('width', this.get("maxWidth")); - this._expand(); + this.set("filteredContent", this._remapContent(this.get("content"))); + this._setSelectedContent(this.get("content")); + this.set("headerText", this.get("defaultHeaderText") || this.get("selectedContent.text")); }, - didInsertElement() { - this._super(); - - Ember.$(document).on("click.select-box", (event) => { - if(this.get("expanded") && $(event.target).parents(".select-box").attr("id") !== this.$().attr("id")) { + @on("didInsertElement") + _bindEvents: function() { + $(document).on("click.select-box", (event) => { + const clickOutside = $(event.target).parents(".select-box").attr("id") !== this.$().attr("id"); + if (this.get("expanded") && clickOutside) { this.setProperties({ expanded: false, focused: false @@ -187,25 +169,68 @@ export default Ember.Component.extend({ } }, - _setSelectedData(data) { - const selectedData = _.find(data, (d)=> { - return d[this.get("idKey")] === this.get("value"); + _setSelectedContent(content) { + const selectedContent = content.find((c) => { + return c[this.get("idKey")] === this.get("value"); }); - if(!_.isUndefined(selectedData)) { - this.set("selectedData", this._normalizeData(selectedData)); + if (!Ember.isNone(selectedContent)) { + this.set("selectedContent", this._normalizeContent(selectedContent)); } }, - _remapData(data) { - return data.map(d => this._normalizeData(d)); + _remapContent(content) { + return content.map(c => this._normalizeContent(c)); }, - _normalizeData(data) { + _normalizeContent(content) { return { - id: data[this.get("idKey")], - text: data[this.get("textKey")], - icon: data[this.get("iconKey")] + id: content[this.get("idKey")], + text: content[this.get("textKey")], + icon: content[this.get("iconKey")] }; }, + + _bindTab() { + $(document).on("keydown.select-box", (event) => { + const keyCode = event.keyCode || event.which; + if (keyCode === 9) { + this.set("expanded", false); + } + }); + }, + + _positionSelectBoxWrapper() { + const headerHeight = this.$(".select-box-header").outerHeight(); + + this.$(".select-box-wrapper").css({ + width: this.get("maxWidth"), + display: "block", + height: headerHeight + this.$(".select-box-body").outerHeight() + }); + }, + + _applyDirection() { + this.$().removeClass("is-reversed"); + + const offsetTop = this.$()[0].getBoundingClientRect().top; + const windowHeight = $(window).height(); + const headerHeight = this.$(".select-box-header").outerHeight(); + const filterHeight = this.$(".select-box-filter").outerHeight(); + + if (windowHeight - (offsetTop + this.get("maxCollectionHeight") + filterHeight + headerHeight) < 0) { + this.$().addClass("is-reversed"); + this.$(".select-box-body").css({ + left: this.get("horizontalOffset"), + top: "", + bottom: headerHeight + this.get("verticalOffset") + }); + } else { + this.$(".select-box-body").css({ + left: this.get("horizontalOffset"), + top: headerHeight + this.get("verticalOffset"), + bottom: "" + }); + } + }, }); diff --git a/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 index 2b56d94f2e8..ceabb3c0d8c 100644 --- a/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 +++ b/app/assets/javascripts/discourse/components/select-box/select-box-row.js.es6 @@ -10,25 +10,25 @@ export default Ember.Component.extend({ lastHoveredId: null, mouseEnter() { - this.sendAction("onHover", this.get("data.id")); + this.sendAction("onHover", this.get("content.id")); }, click() { - this.sendAction("onSelect", this.get("data.id")); + this.sendAction("onSelect", this.get("content.id")); }, didReceiveAttrs() { this._super(); this.set("isHighlighted", this._isHighlighted()); - this.set("text", this.get("data.text")); + this.set("text", this.get("content.text")); }, _isHighlighted() { if(_.isUndefined(this.get("lastHoveredId"))) { - return this.get("data.id") === this.get("selectedId"); + return this.get("content.id") === this.get("selectedId"); } else { - return this.get("data.id") === this.get("lastHoveredId"); + return this.get("content.id") === this.get("lastHoveredId"); } }, }); diff --git a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 index 553b35191ec..92d811f9b48 100644 --- a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 @@ -1,12 +1,13 @@ -import { iconHTML } from 'discourse-common/lib/icon-library'; -import Combobox from 'discourse-common/components/combo-box'; import { observes } from 'ember-addons/ember-computed-decorators'; +import DiscourseSelectBoxComponent from "discourse/components/d-select-box"; -export default Combobox.extend({ - none: "topic.controls", - +export default DiscourseSelectBoxComponent.extend({ init() { this._super(); + + this.set("textKey", "name"); + this.set("defaultHeaderText", I18n.t("topic.controls")); + this.set("maxCollectionHeight", 300); this._createContent(); }, @@ -24,23 +25,20 @@ export default Combobox.extend({ } else { content.push({ id: 'bookmark', icon: 'bookmark', name: I18n.t('bookmarked.title') }); } + content.push({ id: 'share', icon: 'link', name: I18n.t('topic.share.title') }); if (details.get('can_flag_topic')) { content.push({ id: 'flag', icon: 'flag', name: I18n.t('topic.flag_topic.title') }); } - this.comboTemplate = (item) => { - const contentItem = content.findBy('id', item.id); - if (!contentItem) { return item.text; } - return `${iconHTML(contentItem.icon)}  ${item.text}`; - }; - this.set('content', content); }, @observes('value') _valueChanged() { + this._super(); + const value = this.get('value'); const topic = this.get('topic'); diff --git a/app/assets/javascripts/discourse/templates/components/select-box.hbs b/app/assets/javascripts/discourse/templates/components/select-box.hbs index c15fdd873a9..495b473273a 100644 --- a/app/assets/javascripts/discourse/templates/components/select-box.hbs +++ b/app/assets/javascripts/discourse/templates/components/select-box.hbs @@ -7,7 +7,7 @@ /> {{component selectBoxHeaderComponent - data=selectedData + text=headerText focused=focused caretUpIcon=caretUpIcon caretDownIcon=caretDownIcon @@ -17,7 +17,7 @@ }}

- {{#if _renderBody}} + {{#if renderBody}} {{#if filterable}} {{component selectBoxFilterComponent onFilterChange=(action "onFilterChange") @@ -27,12 +27,12 @@ {{/if}} {{component selectBoxCollectionComponent - filteredData=filteredData + filteredContent=filteredContent selectBoxRowComponent=selectBoxRowComponent lastHoveredId=lastHoveredId onSelectRow=(action "onSelectRow") onHoverRow=(action "onHoverRow") - noDataText=noDataText + noContentText=noContentText selectedId=value }} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs b/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs index 268db6a407f..b973147bb84 100644 --- a/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs +++ b/app/assets/javascripts/discourse/templates/components/select-box/select-box-collection.hbs @@ -1,16 +1,16 @@