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