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