RCFilters: Align trash icon with filter list
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / ui / mw.rcfilters.ui.FilterCapsuleMultiselectWidget.js
1 ( function ( mw, $ ) {
2 /**
3 * Filter-specific CapsuleMultiselectWidget
4 *
5 * @class
6 * @extends OO.ui.CapsuleMultiselectWidget
7 *
8 * @constructor
9 * @param {mw.rcfilters.Controller} controller RCFilters controller
10 * @param {mw.rcfilters.dm.FiltersViewModel} model RCFilters view model
11 * @param {OO.ui.InputWidget} filterInput A filter input that focuses the capsule widget
12 * @param {Object} config Configuration object
13 * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
14 */
15 mw.rcfilters.ui.FilterCapsuleMultiselectWidget = function MwRcfiltersUiFilterCapsuleMultiselectWidget( controller, model, filterInput, config ) {
16 var title = new OO.ui.LabelWidget( {
17 label: mw.msg( 'rcfilters-activefilters' ),
18 classes: [ 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-wrapper-content-title' ]
19 } ),
20 $contentWrapper = $( '<div>' )
21 .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-wrapper' );
22
23 this.$overlay = config.$overlay || this.$element;
24
25 // Parent
26 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.parent.call( this, $.extend( true, {
27 popup: { $autoCloseIgnore: filterInput.$element.add( this.$overlay ) }
28 }, config ) );
29
30 this.controller = controller;
31 this.model = model;
32
33 this.filterInput = filterInput;
34
35 this.resetButton = new OO.ui.ButtonWidget( {
36 icon: 'trash',
37 framed: false,
38 title: mw.msg( 'rcfilters-clear-all-filters' ),
39 classes: [ 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-resetButton' ]
40 } );
41
42 this.emptyFilterMessage = new OO.ui.LabelWidget( {
43 label: mw.msg( 'rcfilters-empty-filter' ),
44 classes: [ 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-emptyFilters' ]
45 } );
46 this.$content.append( this.emptyFilterMessage.$element );
47
48 // Events
49 this.resetButton.connect( this, { click: 'onResetButtonClick' } );
50 this.model.connect( this, {
51 itemUpdate: 'onModelItemUpdate',
52 highlightChange: 'onModelHighlightChange'
53 } );
54 // Add the filterInput as trigger
55 this.filterInput.$input
56 .on( 'focus', this.focus.bind( this ) );
57
58 // Build the content
59 $contentWrapper.append(
60 title.$element,
61 $( '<div>' )
62 .addClass( 'mw-rcfilters-ui-table' )
63 .append(
64 // The filter list and button should appear side by side regardless of how
65 // wide the button is; the button also changes its width depending
66 // on language and its state, so the safest way to present both side
67 // by side is with a table layout
68 $( '<div>' )
69 .addClass( 'mw-rcfilters-ui-row' )
70 .append(
71 this.$content
72 .addClass( 'mw-rcfilters-ui-cell' )
73 .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell-filters' ),
74 $( '<div>' )
75 .addClass( 'mw-rcfilters-ui-cell' )
76 .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell-reset' )
77 .append( this.resetButton.$element )
78 )
79 )
80 );
81
82 // Initialize
83 this.$handle.append( $contentWrapper );
84
85 this.$element
86 .addClass( 'mw-rcfilters-ui-filterCapsuleMultiselectWidget' );
87
88 this.reevaluateResetRestoreState();
89 };
90
91 /* Initialization */
92
93 OO.inheritClass( mw.rcfilters.ui.FilterCapsuleMultiselectWidget, OO.ui.CapsuleMultiselectWidget );
94
95 /* Events */
96
97 /**
98 * @event remove
99 * @param {string[]} filters Array of names of removed filters
100 *
101 * Filters were removed
102 */
103
104 /* Methods */
105
106 /**
107 * Respond to model itemUpdate event
108 *
109 * @param {mw.rcfilters.dm.FilterItem} item Filter item model
110 */
111 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onModelItemUpdate = function ( item ) {
112 if (
113 item.isSelected() ||
114 (
115 this.model.isHighlightEnabled() &&
116 item.isHighlightSupported() &&
117 item.getHighlightColor()
118 )
119 ) {
120 this.addItemByName( item.getName() );
121 } else {
122 this.removeItemByName( item.getName() );
123 }
124
125 // Re-evaluate reset state
126 this.reevaluateResetRestoreState();
127 };
128
129 /**
130 * Respond to highlightChange event
131 *
132 * @param {boolean} isHighlightEnabled Highlight is enabled
133 */
134 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onModelHighlightChange = function ( isHighlightEnabled ) {
135 var highlightedItems = this.model.getHighlightedItems();
136
137 if ( isHighlightEnabled ) {
138 // Add capsule widgets
139 highlightedItems.forEach( function ( filterItem ) {
140 this.addItemByName( filterItem.getName() );
141 }.bind( this ) );
142 } else {
143 // Remove capsule widgets if they're not selected
144 highlightedItems.forEach( function ( filterItem ) {
145 if ( !filterItem.isSelected() ) {
146 this.removeItemByName( filterItem.getName() );
147 }
148 }.bind( this ) );
149 }
150 };
151
152 /**
153 * Respond to click event on the reset button
154 */
155 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onResetButtonClick = function () {
156 if ( this.model.areCurrentFiltersEmpty() ) {
157 // Reset to default filters
158 this.controller.resetToDefaults();
159 } else {
160 // Reset to have no filters
161 this.controller.emptyFilters();
162 }
163 };
164
165 /**
166 * Reevaluate the restore state for the widget between setting to defaults and clearing all filters
167 */
168 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.reevaluateResetRestoreState = function () {
169 var defaultsAreEmpty = this.model.areDefaultFiltersEmpty(),
170 currFiltersAreEmpty = this.model.areCurrentFiltersEmpty(),
171 hideResetButton = currFiltersAreEmpty && defaultsAreEmpty;
172
173 this.resetButton.setIcon(
174 currFiltersAreEmpty ? 'history' : 'trash'
175 );
176
177 this.resetButton.setLabel(
178 currFiltersAreEmpty ? mw.msg( 'rcfilters-restore-default-filters' ) : ''
179 );
180
181 this.resetButton.toggle( !hideResetButton );
182 this.emptyFilterMessage.toggle( currFiltersAreEmpty );
183 };
184
185 /**
186 * @inheritdoc
187 */
188 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.createItemWidget = function ( data ) {
189 var item = this.model.getItemByName( data );
190
191 if ( !item ) {
192 return;
193 }
194
195 return new mw.rcfilters.ui.CapsuleItemWidget(
196 this.controller,
197 item,
198 { $overlay: this.$overlay }
199 );
200 };
201
202 /**
203 * Add items by their filter name
204 *
205 * @param {string} name Filter name
206 */
207 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.addItemByName = function ( name ) {
208 var item = this.model.getItemByName( name );
209
210 if ( !item ) {
211 return;
212 }
213
214 // Check that the item isn't already added
215 if ( !this.getItemFromData( name ) ) {
216 this.addItems( [ this.createItemWidget( name ) ] );
217 }
218 };
219
220 /**
221 * Remove items by their filter name
222 *
223 * @param {string} name Filter name
224 */
225 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.removeItemByName = function ( name ) {
226 this.removeItemsFromData( [ name ] );
227 };
228
229 /**
230 * @inheritdoc
231 */
232 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.focus = function () {
233 // Override this method; we don't want to focus on the popup, and we
234 // don't want to bind the size to the handle.
235 if ( !this.isDisabled() ) {
236 this.popup.toggle( true );
237 this.filterInput.$input.get( 0 ).focus();
238 }
239 return this;
240 };
241
242 /**
243 * @inheritdoc
244 */
245 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onFocusForPopup = function () {
246 // HACK can be removed once I21b8cff4048 is merged in oojs-ui
247 this.focus();
248 };
249
250 /**
251 * @inheritdoc
252 */
253 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onKeyDown = function () {};
254
255 /**
256 * @inheritdoc
257 */
258 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onPopupFocusOut = function () {};
259
260 /**
261 * @inheritdoc
262 */
263 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.clearInput = function () {
264 if ( this.filterInput ) {
265 this.filterInput.setValue( '' );
266 }
267 this.menu.toggle( false );
268 this.menu.selectItem();
269 this.menu.highlightItem();
270 };
271
272 /**
273 * @inheritdoc
274 */
275 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.removeItems = function ( items ) {
276 // Parent call
277 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.parent.prototype.removeItems.call( this, items );
278
279 // Destroy the item widget when it is removed
280 // This is done because we re-add items by recreating them, rather than hiding them
281 // and items include popups, that will just continue to be created and appended
282 // unnecessarily.
283 items.forEach( function ( widget ) {
284 widget.destroy();
285 } );
286 };
287
288 /**
289 * Override 'editItem' since it tries to use $input which does
290 * not exist when a popup is available.
291 */
292 mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.editItem = function () {};
293 }( mediaWiki, jQuery ) );