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