Merge "RCFilters: Trim spaces in saved query names"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / ui / mw.rcfilters.ui.SavedLinksListItemWidget.js
1 ( function ( mw ) {
2 /**
3 * Quick links menu option widget
4 *
5 * @extends OO.ui.Widget
6 * @mixins OO.ui.mixin.LabelElement
7 * @mixins OO.ui.mixin.IconElement
8 * @mixins OO.ui.mixin.TitledElement
9 *
10 * @constructor
11 * @param {mw.rcfilters.dm.SavedQueryItemModel} model View model
12 * @param {Object} [config] Configuration object
13 * @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
14 */
15 mw.rcfilters.ui.SavedLinksListItemWidget = function MwRcfiltersUiSavedLinksListWidget( model, config ) {
16 config = config || {};
17
18 this.model = model;
19
20 // Parent
21 mw.rcfilters.ui.SavedLinksListItemWidget.parent.call( this, $.extend( {
22 data: this.model.getID()
23 }, config ) );
24
25 // Mixin constructors
26 OO.ui.mixin.LabelElement.call( this, $.extend( {
27 label: this.model.getLabel()
28 }, config ) );
29 OO.ui.mixin.IconElement.call( this, $.extend( {
30 icon: ''
31 }, config ) );
32 OO.ui.mixin.TitledElement.call( this, $.extend( {
33 title: this.model.getLabel()
34 }, config ) );
35
36 this.edit = false;
37 this.$overlay = config.$overlay || this.$element;
38
39 this.popupButton = new OO.ui.ButtonWidget( {
40 classes: [ 'mw-rcfilters-ui-savedLinksListItemWidget-button' ],
41 icon: 'ellipsis',
42 framed: false
43 } );
44 this.menu = new OO.ui.MenuSelectWidget( {
45 classes: [ 'mw-rcfilters-ui-savedLinksListItemWidget-menu' ],
46 widget: this.popupButton,
47 width: 200,
48 horizontalPosition: 'end',
49 $floatableContainer: this.popupButton.$element,
50 items: [
51 new OO.ui.MenuOptionWidget( {
52 data: 'edit',
53 icon: 'edit',
54 label: mw.msg( 'rcfilters-savedqueries-rename' )
55 } ),
56 new OO.ui.MenuOptionWidget( {
57 data: 'delete',
58 icon: 'close',
59 label: mw.msg( 'rcfilters-savedqueries-remove' )
60 } ),
61 new OO.ui.MenuOptionWidget( {
62 data: 'default',
63 icon: 'pushPin',
64 label: mw.msg( 'rcfilters-savedqueries-setdefault' )
65 } )
66 ]
67 } );
68
69 this.editInput = new OO.ui.TextInputWidget( {
70 classes: [ 'mw-rcfilters-ui-savedLinksListItemWidget-input' ]
71 } );
72 this.saveButton = new OO.ui.ButtonWidget( {
73 icon: 'check',
74 flags: [ 'primary', 'progressive' ]
75 } );
76 this.toggleEdit( false );
77
78 // Events
79 this.model.connect( this, { update: 'onModelUpdate' } );
80 this.popupButton.connect( this, { click: 'onPopupButtonClick' } );
81 this.menu.connect( this, {
82 choose: 'onMenuChoose'
83 } );
84 this.saveButton.connect( this, { click: 'save' } );
85 this.editInput.connect( this, {
86 change: 'onInputChange',
87 enter: 'save'
88 } );
89 this.editInput.$input.on( {
90 blur: this.onInputBlur.bind( this ),
91 keyup: this.onInputKeyup.bind( this )
92 } );
93 this.$element.on( { click: this.onClick.bind( this ) } );
94 this.$label.on( { click: this.onClick.bind( this ) } );
95 // Prevent propagation on mousedown for the save button
96 // so the menu doesn't close
97 this.saveButton.$element.on( { mousedown: function () { return false; } } );
98
99 // Initialize
100 this.toggleDefault( !!this.model.isDefault() );
101 this.$overlay.append( this.menu.$element );
102 this.$element
103 .addClass( 'mw-rcfilters-ui-savedLinksListItemWidget' )
104 .addClass( 'mw-rcfilters-ui-savedLinksListItemWidget-query-' + this.model.getID() )
105 .append(
106 $( '<div>' )
107 .addClass( 'mw-rcfilters-ui-table' )
108 .append(
109 $( '<div>' )
110 .addClass( 'mw-rcfilters-ui-row' )
111 .append(
112 $( '<div>' )
113 .addClass( 'mw-rcfilters-ui-cell' )
114 .addClass( 'mw-rcfilters-ui-savedLinksListItemWidget-content' )
115 .append(
116 this.$label
117 .addClass( 'mw-rcfilters-ui-savedLinksListItemWidget-label' ),
118 this.editInput.$element,
119 this.saveButton.$element
120 ),
121 $( '<div>' )
122 .addClass( 'mw-rcfilters-ui-cell' )
123 .addClass( 'mw-rcfilters-ui-savedLinksListItemWidget-icon' )
124 .append( this.$icon ),
125 this.popupButton.$element
126 .addClass( 'mw-rcfilters-ui-cell' )
127 )
128 )
129 );
130 };
131
132 /* Initialization */
133 OO.inheritClass( mw.rcfilters.ui.SavedLinksListItemWidget, OO.ui.Widget );
134 OO.mixinClass( mw.rcfilters.ui.SavedLinksListItemWidget, OO.ui.mixin.LabelElement );
135 OO.mixinClass( mw.rcfilters.ui.SavedLinksListItemWidget, OO.ui.mixin.IconElement );
136 OO.mixinClass( mw.rcfilters.ui.SavedLinksListItemWidget, OO.ui.mixin.TitledElement );
137
138 /* Events */
139
140 /**
141 * @event delete
142 *
143 * The delete option was selected for this item
144 */
145
146 /**
147 * @event default
148 * @param {boolean} default Item is default
149 *
150 * The 'make default' option was selected for this item
151 */
152
153 /**
154 * @event edit
155 * @param {string} newLabel New label for the query
156 *
157 * The label has been edited
158 */
159
160 /* Methods */
161
162 /**
163 * Respond to model update event
164 */
165 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onModelUpdate = function () {
166 this.setLabel( this.model.getLabel() );
167 this.toggleDefault( this.model.isDefault() );
168 };
169
170 /**
171 * Respond to click on the element or label
172 *
173 * @fires click
174 */
175 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onClick = function () {
176 if ( !this.editing ) {
177 this.emit( 'click' );
178 }
179 };
180 /**
181 * Respond to popup button click event
182 */
183 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onPopupButtonClick = function () {
184 this.menu.toggle();
185 };
186
187 /**
188 * Respond to menu choose event
189 *
190 * @param {OO.ui.MenuOptionWidget} item Chosen item
191 * @fires delete
192 * @fires default
193 */
194 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onMenuChoose = function ( item ) {
195 var action = item.getData();
196
197 if ( action === 'edit' ) {
198 this.toggleEdit( true );
199 } else if ( action === 'delete' ) {
200 this.emit( 'delete' );
201 } else if ( action === 'default' ) {
202 this.emit( 'default', !this.default );
203 }
204 // Reset selected
205 this.menu.selectItem( null );
206 // Close the menu
207 this.menu.toggle( false );
208 };
209
210 /**
211 * Respond to input keyup event, this is the way to intercept 'escape' key
212 *
213 * @param {jQuery.Event} e Event data
214 * @returns {boolean} false
215 */
216 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onInputKeyup = function ( e ) {
217 if ( e.which === OO.ui.Keys.ESCAPE ) {
218 // Return the input to the original label
219 this.editInput.setValue( this.getLabel() );
220 this.toggleEdit( false );
221 return false;
222 }
223 };
224
225 /**
226 * Respond to blur event on the input
227 */
228 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onInputBlur = function () {
229 this.save();
230
231 // Whether the save succeeded or not, the input-blur event
232 // means we need to cancel editing mode
233 this.toggleEdit( false );
234 };
235
236 /**
237 * Respond to input change event
238 *
239 * @param {string} value Input value
240 */
241 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.onInputChange = function ( value ) {
242 value = value.trim();
243
244 this.saveButton.setDisabled( !value );
245 };
246
247 /**
248 * Save the name of the query
249 *
250 * @param {string} [value] The value to save
251 * @fires edit
252 */
253 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.save = function () {
254 var value = this.editInput.getValue().trim();
255
256 if ( value ) {
257 this.emit( 'edit', value );
258 this.toggleEdit( false );
259 }
260 };
261
262 /**
263 * Toggle edit mode on this widget
264 *
265 * @param {boolean} isEdit Widget is in edit mode
266 */
267 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.toggleEdit = function ( isEdit ) {
268 isEdit = isEdit === undefined ? !this.editing : isEdit;
269
270 if ( this.editing !== isEdit ) {
271 this.$element.toggleClass( 'mw-rcfilters-ui-savedLinksListItemWidget-edit', isEdit );
272 this.editInput.setValue( this.getLabel() );
273
274 this.editInput.toggle( isEdit );
275 this.$label.toggleClass( 'oo-ui-element-hidden', isEdit );
276 this.$icon.toggleClass( 'oo-ui-element-hidden', isEdit );
277 this.popupButton.toggle( !isEdit );
278 this.saveButton.toggle( isEdit );
279
280 if ( isEdit ) {
281 this.editInput.$input.focus();
282 }
283 this.editing = isEdit;
284 }
285 };
286
287 /**
288 * Toggle default this widget
289 *
290 * @param {boolean} isDefault This item is default
291 */
292 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.toggleDefault = function ( isDefault ) {
293 isDefault = isDefault === undefined ? !this.default : isDefault;
294
295 if ( this.default !== isDefault ) {
296 this.default = isDefault;
297 this.setIcon( this.default ? 'pushPin' : '' );
298 this.menu.getItemFromData( 'default' ).setLabel(
299 this.default ?
300 mw.msg( 'rcfilters-savedqueries-unsetdefault' ) :
301 mw.msg( 'rcfilters-savedqueries-setdefault' )
302 );
303 }
304 };
305
306 /**
307 * Get item ID
308 *
309 * @returns {string} Query identifier
310 */
311 mw.rcfilters.ui.SavedLinksListItemWidget.prototype.getID = function () {
312 return this.model.getID();
313 };
314
315 }( mediaWiki ) );