Repository: couchdb-fauxton Updated Branches: refs/heads/master e08faa5dd -> 25ea5bdf0
Create generic Tray component This moves a lot of the logic from the individual trays (Add Database, Query Options, API Url) and into a generic Tray component. Closes COUCHDB-2401 Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/25ea5bdf Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/25ea5bdf Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/25ea5bdf Branch: refs/heads/master Commit: 25ea5bdf0ee3f73a499d943ddcb92bcc414d0363 Parents: e08faa5 Author: Benjamin Keen <[email protected]> Authored: Fri Nov 7 13:43:58 2014 -0800 Committer: Benjamin Keen <[email protected]> Committed: Tue Nov 11 10:21:37 2014 -0800 ---------------------------------------------------------------------- app/addons/databases/views.js | 54 +--------- app/addons/documents/views-queryoptions.js | 69 +++--------- app/addons/fauxton/components.js | 138 +++++++++++++++++------- app/constants.js | 6 +- 4 files changed, 121 insertions(+), 146 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25ea5bdf/app/addons/databases/views.js ---------------------------------------------------------------------- diff --git a/app/addons/databases/views.js b/app/addons/databases/views.js index cefc72d..a9c4c61 100644 --- a/app/addons/databases/views.js +++ b/app/addons/databases/views.js @@ -185,32 +185,15 @@ function(app, Components, FauxtonAPI, Databases) { } }); - var NewDatabaseView = FauxtonAPI.View.extend({ + var NewDatabaseView = Components.Tray.extend({ template: 'addons/databases/templates/newdatabase', events: { - 'click #add-new-database': 'toggleTray', 'click #js-create-database': 'createDatabase', 'keyup #js-new-database-name': 'processKey' }, initialize: function () { - var hideTray = _.bind(this.hideTray, this), - trayVisible = _.bind(this.trayVisible, this); - - $('body').on('click.add-new-database', function(e) { - var $clickEl = $(e.target); - - if (!trayVisible()) { return; } - if ($clickEl.closest('.add-new-database-btn').length) { return; } - - if (!$clickEl.closest('.new-database-tray').length) { - hideTray(); - } - }); - }, - - cleanup: function() { - $('body').off('click.add-new-database'); + this.initTray({ toggleTrayBtnSelector: '#add-new-database' }); }, processKey: function (e) { @@ -219,39 +202,6 @@ function(app, Components, FauxtonAPI, Databases) { } }, - toggleTray: function (e) { - e.preventDefault(); - - // curious. If we don't prevent bubbling, the parent View is redrawn (?) - e.stopImmediatePropagation(); - - if (this.trayVisible()) { - this.hideTray(); - } else { - this.showTray(); - } - }, - - hideTray: function () { - var $tray = this.$('.tray'); - $tray.velocity('reverse', FauxtonAPI.constants.TRAY_TOGGLE_SPEED, function () { - $tray.hide(); - }); - this.$('#add-new-database').removeClass('enabled'); - }, - - showTray: function () { - // boo! to be refactored out later (see COUCHDB-2401) - FauxtonAPI.Events.trigger("APIbar:closeTray"); - - this.$('.tray').velocity('transition.slideDownIn', FauxtonAPI.constants.TRAY_TOGGLE_SPEED); - this.$('#add-new-database').addClass('enabled'); - }, - - trayVisible: function () { - return this.$('.tray').is(':visible'); - }, - createDatabase: function (e) { e.preventDefault(); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25ea5bdf/app/addons/documents/views-queryoptions.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/views-queryoptions.js b/app/addons/documents/views-queryoptions.js index 425f470..dd1e536 100644 --- a/app/addons/documents/views-queryoptions.js +++ b/app/addons/documents/views-queryoptions.js @@ -15,10 +15,11 @@ define([ "api", // libs + 'addons/fauxton/components', "addons/fauxton/resizeColumns" ], - function (app, FauxtonAPI) { + function (app, FauxtonAPI, Components) { // our default settings for the Query Options tray var defaultOptions = { @@ -50,7 +51,7 @@ define([ var Views = {}; // our main View. This is the only View exposed externally - Views.QueryOptionsTray = FauxtonAPI.View.extend({ + Views.QueryOptionsTray = Components.Tray.extend({ template: "addons/documents/templates/query_options", className: "query-options", @@ -61,7 +62,16 @@ define([ this.options = $.extend(true, {}, defaultOptions, options); // add any general events relating to the Query Options tray - this.addEvents(); + FauxtonAPI.Events.on('QueryOptions:openTray', this.showTray, this); + $(window).on("resize", this.onResize); + + // initialize the parent View + this.initTray({ + toggleTrayBtnSelector: '#toggle-query', + onShowTray: function () { + this.$('#query-options-tray button[type="submit"]').removeAttr("disabled"); + } + }); // add the sub-views this.mainFieldsView = this.setView("#query-options-main-fields", new MainFieldsView(this.options)); @@ -69,50 +79,18 @@ define([ this.additionalParamsView = this.setView("#query-options-additional-params", new AdditionalParamsView(this.options)); }, - addEvents: function () { - FauxtonAPI.Events.on('QueryOptions:closeTray', this.closeTray, this); - FauxtonAPI.Events.on('QueryOptions:openTray', this.toggleQueryOptionsTray, this); - - // if the user just clicked outside the tray, close it [TODO be nice to generalize for all trays] - var trayIsVisible = this.trayIsVisible; - var closeTray = this.closeTray; - - $("body").on("click.queryOptions", function (e) { - if (!trayIsVisible()) { return; } - if ($(e.target).closest("#query-options-tray").length === 0) { - closeTray(); - } - }); - - $(window).on("resize", this.onResize); - }, - afterRender: function () { this.onResize(); }, cleanup: function () { - FauxtonAPI.Events.unbind("QueryOptions:closeTray"); - FauxtonAPI.Events.unbind("QueryOptions:openTray"); + FauxtonAPI.Events.off("QueryOptions:openTray"); $(window).off("resize", this.onResize); }, events: { - "click #toggle-query": "toggleQueryOptionsTray", // hide/show the Query Options tray "submit form.js-view-query-update": "onSubmit", // submits the form - "click .btn-cancel": "onCancel" // closes the tray (doesn't reset the fields) - }, - - toggleQueryOptionsTray: function () { - if (!this.trayIsVisible()) { - $("#query-options-tray").velocity("transition.slideDownIn", FauxtonAPI.constants.TRAY_TOGGLE_SPEED); - FauxtonAPI.Events.trigger("APIbar:closeTray"); - - // make sure the query button is active again. As we can only expand for completed results, this is sufficient - // to prevent double submission - this.$('#query-options-tray button[type="submit"]').removeAttr("disabled"); - this.$('.query-options-btn').addClass('enabled'); - } + "click .btn-cancel": "hideTray" // closes the tray (doesn't reset the fields) }, // returns all applicable query parameters for the Query Options tray @@ -137,7 +115,7 @@ define([ // make sure we can not submit twice which results in chaos (and no result ever) this.$('#query-options-tray button[type="submit"]').attr("disabled", "disabled"); - this.closeTray(); + this.hideTray(); // this may be empty. That's ok! Perhaps the user just did a search with params, then removed them & wants the default var params = this.getQueryParams(); @@ -153,10 +131,6 @@ define([ } }, - onCancel: function () { - this.closeTray(); - }, - // if the screen is so small there isn't space for the full tray height we manually shrink the height to allow scrolling. // Technically this should handle width as well, but we won't bother because there are way bigger issues with a screen // with such a small width! @@ -190,17 +164,6 @@ define([ this.mainFieldsView.update(this.options); this.keySearchFieldsView.update(this.options); this.additionalParamsView.update(this.options); - }, - - trayIsVisible: function () { - return $("#query-options-tray").is(":visible"); - }, - - closeTray: function () { - $("#query-options-tray").velocity("reverse", FauxtonAPI.constants.TRAY_TOGGLE_SPEED, function () { - $("#query-options-tray").hide(); - }); - this.$('.query-options-btn').removeClass('enabled'); } }); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25ea5bdf/app/addons/fauxton/components.js ---------------------------------------------------------------------- diff --git a/app/addons/fauxton/components.js b/app/addons/fauxton/components.js index 19c49c4..6621604 100644 --- a/app/addons/fauxton/components.js +++ b/app/addons/fauxton/components.js @@ -157,61 +157,121 @@ function(app, FauxtonAPI, ace, spin, ZeroClipboard) { } }); + /** + * Our generic Tray component. All trays should extend this guy - it offers some convenient boilerplate code for + * hiding/showing, event publishing and so on. The important functions that can be called on the child Views are: + * - hideTray + * - showTray + * - toggleTray + */ + Components.Tray = FauxtonAPI.View.extend({ + + // populated dynamically + events: {}, + + initTray: function (opts) { + this.toggleTrayBtnSelector = (_.has(opts, 'toggleTrayBtnSelector')) ? opts.toggleTrayBtnSelector : null; + this.onShowTray = (_.has(opts, 'onShowTray')) ? opts.onShowTray : null; + + // if the component extending this one passed along the selector of the element that toggles the tray, + // add the appropriate events + if (!_.isNull(this.toggleTrayBtnSelector)) { + this.events['click ' + this.toggleTrayBtnSelector] = 'toggleTray'; + } - Components.ApiBar = FauxtonAPI.View.extend({ - template: "addons/fauxton/templates/api_bar", - - events: { - "click .api-url-btn": "showAPIbar" - }, - - initialize: function (options) { - var _options = options || {}; - this.endpoint = _options.endpoint || '_all_docs'; - this.documentation = _options.documentation || 'docs'; - - var hideAPIbar = _.bind(this.hideAPIbar, this), - navbarVisible = _.bind(this.navbarVisible, this); + _.bind(this.toggleTray, this); + _.bind(this.trayVisible, this); + _.bind(this.hideTray, this); + _.bind(this.showTray, this); - $('body').on('click.apibar', function(e) { - var $navbar = $(e.target); + // a unique identifier for this tray + this.trayId = 'tray-' + this.cid; - if (!navbarVisible()) { return;} - if ($navbar.hasClass('.api-url-btn')) { return; } + var that = this; + $('body').on('click.' + this.trayId, function(e) { + var $clickEl = $(e.target); - if (!$navbar.closest('#api-navbar').length){ - hideAPIbar(); + if (!that.trayVisible()) { + return; + } + if (!_.isNull(that.toggleTrayBtnSelector) && $clickEl.closest(that.toggleTrayBtnSelector).length) { + return; + } + if (!$clickEl.closest('.tray').length) { + that.hideTray(); } }); - FauxtonAPI.Events.on('APIbar:closeTray', this.hideAPIbar, this); + FauxtonAPI.Events.on(FauxtonAPI.constants.EVENT_TRAY_OPENED, this.onTrayOpenEvent, this); }, - navbarVisible: function () { - return this.$('.api-navbar').is(':visible'); + cleanup: function() { + $('body').off('click.' + this.trayId); }, - cleanup: function () { - $('body').off('click.apibar'); - FauxtonAPI.Events.off('APIbar:closeTray'); + // all trays publish a EVENT_TRAY_OPENED event containing their unique ID. This listens for those events and + // closes the current tray if it's already open + onTrayOpenEvent: function (msg) { + if (!_.has(msg, 'trayId')) { + return; + } + if (msg.trayId !== this.trayId && this.trayVisible()) { + this.hideTray(); + } }, - hideAPIbar: function () { - var $navBar = this.$('.api-navbar'); - $navBar.velocity("reverse", FauxtonAPI.constants.TRAY_TOGGLE_SPEED, function () { - $navBar.hide(); + toggleTray: function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + + if (this.trayVisible()) { + this.hideTray(); + } else { + this.showTray(); + } + }, + + hideTray: function () { + var $tray = this.$('.tray'); + $tray.velocity('reverse', FauxtonAPI.constants.TRAY_TOGGLE_SPEED, function () { + $tray.hide(); }); - this.$('.api-url-btn').removeClass('enabled'); + + if (!_.isNull(this.toggleTrayBtnSelector)) { + this.$(this.toggleTrayBtnSelector).removeClass('enabled'); + } + // announce that the tray is being closed + FauxtonAPI.Events.trigger(FauxtonAPI.constants.EVENT_TRAY_CLOSED, { trayId: this.trayId }); }, - //we only need to show the api-bar here. The `click.apibar` event - //in the initialize will close the api bar if a user clicks the api button - //and the api bar is visible. - showAPIbar: function() { - if (!this.navbarVisible()) { - this.$('.api-navbar').velocity("transition.slideDownIn", FauxtonAPI.constants.TRAY_TOGGLE_SPEED); - this.$('.api-url-btn').addClass('enabled'); + showTray: function () { + this.$('.tray').velocity('transition.slideDownIn', FauxtonAPI.constants.TRAY_TOGGLE_SPEED); + if (!_.isNull(this.toggleTrayBtnSelector)) { + this.$(this.toggleTrayBtnSelector).addClass('enabled'); + } + + if (!_.isNull(this.onShowTray)) { + this.onShowTray(); } + + FauxtonAPI.Events.trigger(FauxtonAPI.constants.EVENT_TRAY_OPENED, { trayId: this.trayId }); + }, + + trayVisible: function () { + return this.$('.tray').is(':visible'); + } + }); + + + Components.ApiBar = Components.Tray.extend({ + template: "addons/fauxton/templates/api_bar", + + initialize: function (options) { + var _options = options || {}; + this.endpoint = _options.endpoint || '_all_docs'; + this.documentation = _options.documentation || 'docs'; + + this.initTray({ toggleTrayBtnSelector: '.api-url-btn' }); }, serialize: function() { @@ -232,7 +292,6 @@ function(app, FauxtonAPI, ace, spin, ZeroClipboard) { update: function(endpoint) { this.endpoint = endpoint[0]; this.documentation = endpoint[1]; - this.render(); }, @@ -255,7 +314,6 @@ function(app, FauxtonAPI, ace, spin, ZeroClipboard) { } }); - Components.Pagination = FauxtonAPI.View.extend({ tagName: "ul", className: "pagination pagination-centered", http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25ea5bdf/app/constants.js ---------------------------------------------------------------------- diff --git a/app/constants.js b/app/constants.js index a316154..2e57536 100644 --- a/app/constants.js +++ b/app/constants.js @@ -14,7 +14,11 @@ define([], function () { var constants = { TRAY_TOGGLE_SPEED: 250, - DEFAULT_PAGE_SIZE: 20 + DEFAULT_PAGE_SIZE: 20, + + // events + EVENT_TRAY_CLOSED: 'tray:closed', + EVENT_TRAY_OPENED: 'tray:opened' }; return constants;
