From: Moriel Schottlender Date: Sat, 10 Dec 2016 00:18:59 +0000 (-0800) Subject: RCFilters UI: Add 'remove' and 'restore defaults' to filter list X-Git-Tag: 1.31.0-rc.0~4237^2 X-Git-Url: http://git.cyclocoop.org/fichier?a=commitdiff_plain;h=dc3a4da2c5a3fcb52d44fe0679d74a02294d2de7;p=lhc%2Fweb%2Fwiklou.git RCFilters UI: Add 'remove' and 'restore defaults' to filter list Bug: T144448 Bug: T149391 Change-Id: I418ad6b34ae8a7456a7e66bc703d4dccf36764a5 --- diff --git a/languages/i18n/en.json b/languages/i18n/en.json index f2ff14baa6..9f03ce98d1 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -1360,8 +1360,11 @@ "recentchanges-legend-plusminus": "(±123)", "recentchanges-submit": "Show", "rcfilters-activefilters": "Active filters", + "rcfilters-restore-default-filters": "Restore default filters", + "rcfilters-clear-all-filters": "Clear all filters", "rcfilters-search-placeholder": "Filter recent changes (browse or start typing)", "rcfilters-invalid-filter": "Invalid filter", + "rcfilters-empty-filter": "No active filters. All contributions are shown.", "rcfilters-filterlist-title": "Filters", "rcfilters-filterlist-noresults": "No filters found", "rcfilters-filtergroup-registration": "User registration", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 1e71b88201..2219ef6775 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -1546,8 +1546,11 @@ "recentchanges-legend-plusminus": "{{optional}}\nA plus/minus sign with a number for the legend.", "recentchanges-submit": "Label for submit button in [[Special:RecentChanges]]\n{{Identical|Show}}", "rcfilters-activefilters": "Title for the filters selection showing the active filters.", + "rcfilters-restore-default-filters": "Label for the button that resets filters to defaults", + "rcfilters-clear-all-filters": "Title for the button that clears all filters", "rcfilters-search-placeholder": "Placeholder for the filter search input.", "rcfilters-invalid-filter": "A label for an invalid filter.", + "rcfilters-empty-filter": "Placeholder for the filter list when no filters were chosen.", "rcfilters-filterlist-title": "Title for the filters list.\n{{Identical|Filter}}", "rcfilters-filterlist-noresults": "Message showing no results found for searching a filter.", "rcfilters-filtergroup-registration": "Title for the filter group for editor registration type.", diff --git a/resources/Resources.php b/resources/Resources.php index bd7f68e055..f7aa97fc66 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1756,8 +1756,11 @@ return [ ], 'messages' => [ 'rcfilters-activefilters', + 'rcfilters-restore-default-filters', + 'rcfilters-clear-all-filters', 'rcfilters-search-placeholder', 'rcfilters-invalid-filter', + 'rcfilters-empty-filter', 'rcfilters-filterlist-title', 'rcfilters-filterlist-noresults', 'rcfilters-filtergroup-registration', @@ -1800,6 +1803,7 @@ return [ 'dependencies' => [ 'oojs-ui', 'mediawiki.Uri', + 'oojs-ui.styles.icons-moderation' ], ], 'mediawiki.special' => [ diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js index 2496961de0..3f7fa53322 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js @@ -15,6 +15,7 @@ this.groups = {}; this.excludedByMap = {}; this.defaultParams = {}; + this.defaultFiltersEmpty = null; // Events this.aggregate( { update: 'filterItemUpdate' } ); @@ -431,6 +432,39 @@ return result; }; + /** + * Check whether the current filter state is set to all false. + * + * @return {boolean} Current filters are all empty + */ + mw.rcfilters.dm.FiltersViewModel.prototype.areCurrentFiltersEmpty = function () { + var currFilters = this.getSelectedState(); + + return Object.keys( currFilters ).every( function ( filterName ) { + return !currFilters[ filterName ]; + } ); + }; + + /** + * Check whether the default values of the filters are all false. + * + * @return {boolean} Default filters are all false + */ + mw.rcfilters.dm.FiltersViewModel.prototype.areDefaultFiltersEmpty = function () { + var defaultFilters; + + if ( this.defaultFiltersEmpty !== null ) { + // We only need to do this test once, + // because defaults are set once per session + defaultFilters = this.getFiltersFromParameters(); + this.defaultFiltersEmpty = Object.keys( defaultFilters ).every( function ( filterName ) { + return !defaultFilters[ filterName ]; + } ); + } + + return this.defaultFiltersEmpty; + }; + /** * This is the opposite of the #getParametersFromFilters method; this goes over * the given parameters and translates into a selected/unselected value in the filters. @@ -524,6 +558,21 @@ } )[ 0 ]; }; + /** + * Set all filters to false or empty/all + * This is equivalent to display all. + */ + mw.rcfilters.dm.FiltersViewModel.prototype.emptyAllFilters = function () { + var filters = {}; + + this.getItems().forEach( function ( filterItem ) { + filters[ filterItem.getName() ] = false; + } ); + + // Update filters + this.updateFilters( filters ); + }; + /** * Toggle selected state of items by their names * diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js index 025dc89bc3..28d9f28738 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js @@ -27,6 +27,20 @@ ); }; + /** + * Reset to default filters + */ + mw.rcfilters.Controller.prototype.resetToDefaults = function () { + this.model.setFiltersToDefaults(); + }; + + /** + * Empty all selected filters + */ + mw.rcfilters.Controller.prototype.emptyFilters = function () { + this.model.emptyAllFilters(); + }; + /** * Update the state of a filter * diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less index ea1671b003..2ff731c89d 100644 --- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less @@ -8,6 +8,27 @@ opacity: 0.5; } + &-emptyFilters { + color: #72777d; + } + + &-table { + display: table; + width: 100%; + } + + &-row { + display: table-row; + } + + &-cell { + display: table-cell; + + &:last-child { + text-align: right; + } + } + .oo-ui-capsuleItemWidget { color: #222; background-color: #fff; diff --git a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less index a610e8f956..f138d7e55b 100644 --- a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less +++ b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less @@ -17,7 +17,7 @@ &-popup { // We have to override OOUI's definition, which is set // on the inline style of the popup - margin-top: 2em !important; + margin-top: 2.4em !important; max-width: 650px; } diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.js index db2154231b..c498ce9d19 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.js @@ -5,15 +5,19 @@ * @extends OO.ui.CapsuleMultiselectWidget * * @constructor + * @param {mw.rcfilters.Controller} controller RCFilters controller + * @param {mw.rcfilters.dm.FiltersViewModel} model RCFilters view model * @param {OO.ui.InputWidget} filterInput A filter input that focuses the capsule widget * @param {Object} config Configuration object */ - mw.rcfilters.ui.FilterCapsuleMultiselectWidget = function MwRcfiltersUiFilterCapsuleMultiselectWidget( filterInput, config ) { + mw.rcfilters.ui.FilterCapsuleMultiselectWidget = function MwRcfiltersUiFilterCapsuleMultiselectWidget( controller, model, filterInput, config ) { // Parent mw.rcfilters.ui.FilterCapsuleMultiselectWidget.parent.call( this, $.extend( { $autoCloseIgnore: filterInput.$element }, config ) ); + this.controller = controller; + this.model = model; this.filterInput = filterInput; this.$content.prepend( @@ -22,13 +26,54 @@ .text( mw.msg( 'rcfilters-activefilters' ) ) ); + this.resetButton = new OO.ui.ButtonWidget( { + icon: 'trash', + framed: false, + title: mw.msg( 'rcfilters-clear-all-filters' ), + classes: [ 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-resetButton' ] + } ); + + this.emptyFilterMessage = new OO.ui.LabelWidget( { + label: mw.msg( 'rcfilters-empty-filter' ), + classes: [ 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-emptyFilters' ] + } ); + // Events + this.resetButton.connect( this, { click: 'onResetButtonClick' } ); + this.model.connect( this, { itemUpdate: 'onModelItemUpdate' } ); // Add the filterInput as trigger this.filterInput.$input .on( 'focus', this.focus.bind( this ) ); + // Initialize + this.$content.append( this.emptyFilterMessage.$element ); + this.$handle + .append( + // The content and button should appear side by side regardless of how + // wide the button is; the button also changes its width depending + // on language and its state, so the safest way to present both side + // by side is with a table layout + $( '
' ) + .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-table' ) + .append( + $( '
' ) + .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-row' ) + .append( + $( '
' ) + .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-content' ) + .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell' ) + .append( this.$content ), + $( '
' ) + .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell' ) + .append( this.resetButton.$element ) + ) + ) + ); + this.$element .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget' ); + + this.reevaluateResetRestoreState(); }; /* Initialization */ @@ -46,6 +91,44 @@ /* Methods */ + mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onModelItemUpdate = function () { + // Re-evaluate reset state + this.reevaluateResetRestoreState(); + }; + + /** + * Respond to click event on the reset button + */ + mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onResetButtonClick = function () { + if ( this.model.areCurrentFiltersEmpty() ) { + // Reset to default filters + this.controller.resetToDefaults(); + } else { + // Reset to have no filters + this.controller.emptyFilters(); + } + }; + + /** + * Reevaluate the restore state for the widget between setting to defaults and clearing all filters + */ + mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.reevaluateResetRestoreState = function () { + var defaultsAreEmpty = this.model.areDefaultFiltersEmpty(), + currFiltersAreEmpty = this.model.areCurrentFiltersEmpty(), + hideResetButton = currFiltersAreEmpty && defaultsAreEmpty; + + this.resetButton.setIcon( + currFiltersAreEmpty ? 'history' : 'trash' + ); + + this.resetButton.setLabel( + currFiltersAreEmpty ? mw.msg( 'rcfilters-restore-default-filters' ) : '' + ); + + this.resetButton.toggle( !hideResetButton ); + this.emptyFilterMessage.toggle( currFiltersAreEmpty ); + }; + /** * @inheritdoc */ diff --git a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js index 44531a136f..34cc240779 100644 --- a/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js +++ b/resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js @@ -37,7 +37,7 @@ placeholder: mw.msg( 'rcfilters-search-placeholder' ) } ); - this.capsule = new mw.rcfilters.ui.FilterCapsuleMultiselectWidget( this.textInput, { + this.capsule = new mw.rcfilters.ui.FilterCapsuleMultiselectWidget( controller, this.model, this.textInput, { popup: { $content: this.filterPopup.$element, classes: [ 'mw-rcfilters-ui-filterWrapperWidget-popup' ]