]> git.proxmox.com Git - sencha-touch.git/blob - src/src/picker/Picker.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / picker / Picker.js
1 /**
2 * A general picker class. {@link Ext.picker.Slot}s are used to organize multiple scrollable slots into a single picker. {@link #slots} is
3 * the only necessary configuration.
4 *
5 * The {@link #slots} configuration with a few key values:
6 *
7 * - `name`: The name of the slot (will be the key when using {@link #getValues} in this {@link Ext.picker.Picker}).
8 * - `title`: The title of this slot (if {@link #useTitles} is set to `true`).
9 * - `data`/`store`: The data or store to use for this slot.
10 *
11 * Remember, {@link Ext.picker.Slot} class extends from {@link Ext.dataview.DataView}.
12 *
13 * ## Examples
14 *
15 * @example miniphone preview
16 * var picker = Ext.create('Ext.Picker', {
17 * slots: [
18 * {
19 * name : 'limit_speed',
20 * title: 'Speed',
21 * data : [
22 * {text: '50 KB/s', value: 50},
23 * {text: '100 KB/s', value: 100},
24 * {text: '200 KB/s', value: 200},
25 * {text: '300 KB/s', value: 300}
26 * ]
27 * }
28 * ]
29 * });
30 * Ext.Viewport.add(picker);
31 * picker.show();
32 *
33 * You can also customize the top toolbar on the {@link Ext.picker.Picker} by changing the {@link #doneButton} and {@link #cancelButton} configurations:
34 *
35 * @example miniphone preview
36 * var picker = Ext.create('Ext.Picker', {
37 * doneButton: 'I\'m done!',
38 * cancelButton: false,
39 * slots: [
40 * {
41 * name : 'limit_speed',
42 * title: 'Speed',
43 * data : [
44 * {text: '50 KB/s', value: 50},
45 * {text: '100 KB/s', value: 100},
46 * {text: '200 KB/s', value: 200},
47 * {text: '300 KB/s', value: 300}
48 * ]
49 * }
50 * ]
51 * });
52 * Ext.Viewport.add(picker);
53 * picker.show();
54 *
55 * Or by passing a custom {@link #toolbar} configuration:
56 *
57 * @example miniphone preview
58 * var picker = Ext.create('Ext.Picker', {
59 * doneButton: false,
60 * cancelButton: false,
61 * toolbar: {
62 * ui: 'light',
63 * title: 'My Picker!'
64 * },
65 * slots: [
66 * {
67 * name : 'limit_speed',
68 * title: 'Speed',
69 * data : [
70 * {text: '50 KB/s', value: 50},
71 * {text: '100 KB/s', value: 100},
72 * {text: '200 KB/s', value: 200},
73 * {text: '300 KB/s', value: 300}
74 * ]
75 * }
76 * ]
77 * });
78 * Ext.Viewport.add(picker);
79 * picker.show();
80 */
81 Ext.define('Ext.picker.Picker', {
82 extend: 'Ext.Sheet',
83 alias : 'widget.picker',
84 alternateClassName: 'Ext.Picker',
85 requires: ['Ext.picker.Slot', 'Ext.TitleBar', 'Ext.data.Model', 'Ext.util.InputBlocker'],
86
87 isPicker: true,
88
89 /**
90 * @event pick
91 * Fired when a slot has been picked
92 * @param {Ext.Picker} this This Picker.
93 * @param {Object} The values of this picker's slots, in `{name:'value'}` format.
94 * @param {Ext.Picker.Slot} slot An instance of Ext.Picker.Slot that has been picked.
95 */
96
97 /**
98 * @event change
99 * Fired when the value of this picker has changed the Done button has been pressed.
100 * @param {Ext.picker.Picker} this This Picker.
101 * @param {Object} value The values of this picker's slots, in `{name:'value'}` format.
102 */
103
104 /**
105 * @event cancel
106 * Fired when the cancel button is tapped and the values are reverted back to
107 * what they were.
108 * @param {Ext.Picker} this This Picker.
109 */
110
111 config: {
112 /**
113 * @cfg
114 * @inheritdoc
115 */
116 baseCls: Ext.baseCSSPrefix + 'picker',
117
118 /**
119 * @cfg {String/Mixed} doneButton
120 * Can be either:
121 *
122 * - A {String} text to be used on the Done button.
123 * - An {Object} as config for {@link Ext.Button}.
124 * - `false` or `null` to hide it.
125 * @accessor
126 */
127 doneButton: true,
128
129 /**
130 * @cfg {String/Mixed} cancelButton
131 * Can be either:
132 *
133 * - A {String} text to be used on the Cancel button.
134 * - An {Object} as config for {@link Ext.Button}.
135 * - `false` or `null` to hide it.
136 * @accessor
137 */
138 cancelButton: true,
139
140 /**
141 * @cfg {Boolean} useTitles
142 * Generate a title header for each individual slot and use
143 * the title configuration of the slot.
144 * @accessor
145 */
146 useTitles: false,
147
148 /**
149 * @cfg {Array} slots
150 * An array of slot configurations.
151 *
152 * - `name` {String} - Name of the slot
153 * - `data` {Array} - An array of text/value pairs in the format `{text: 'myKey', value: 'myValue'}`
154 * - `title` {String} - Title of the slot. This is used in conjunction with `useTitles: true`.
155 *
156 * @accessor
157 */
158 slots: null,
159
160 /**
161 * @cfg {String/Number} value The value to initialize the picker with.
162 * @accessor
163 */
164 value: null,
165
166 /**
167 * @cfg {Number} height
168 * The height of the picker.
169 * @accessor
170 */
171 height: 220,
172
173 /**
174 * @cfg
175 * @inheritdoc
176 */
177 layout: {
178 type : 'hbox',
179 align: 'stretch'
180 },
181
182 /**
183 * @cfg
184 * @hide
185 */
186 centered: false,
187
188 /**
189 * @cfg
190 * @inheritdoc
191 */
192 left : 0,
193
194 /**
195 * @cfg
196 * @inheritdoc
197 */
198 right: 0,
199
200 /**
201 * @cfg
202 * @inheritdoc
203 */
204 bottom: 0,
205
206 // @private
207 defaultType: 'pickerslot',
208
209 toolbarPosition: 'top',
210
211 /**
212 * @cfg {Ext.TitleBar/Ext.Toolbar/Object} toolbar
213 * The toolbar which contains the {@link #doneButton} and {@link #cancelButton} buttons.
214 * You can override this if you wish, and add your own configurations. Just ensure that you take into account
215 * the {@link #doneButton} and {@link #cancelButton} configurations.
216 *
217 * The default xtype is a {@link Ext.TitleBar}:
218 *
219 * toolbar: {
220 * items: [
221 * {
222 * xtype: 'button',
223 * text: 'Left',
224 * align: 'left'
225 * },
226 * {
227 * xtype: 'button',
228 * text: 'Right',
229 * align: 'left'
230 * }
231 * ]
232 * }
233 *
234 * Or to use a {@link Ext.Toolbar instead}:
235 *
236 * toolbar: {
237 * xtype: 'toolbar',
238 * items: [
239 * {
240 * xtype: 'button',
241 * text: 'Left'
242 * },
243 * {
244 * xtype: 'button',
245 * text: 'Left Two'
246 * }
247 * ]
248 * }
249 *
250 * @accessor
251 */
252 toolbar: {
253 xtype: 'titlebar'
254 }
255 },
256
257 platformConfig: [{
258 theme: ['Windows'],
259 height: '100%',
260 toolbarPosition: 'bottom',
261 toolbar: {
262 xtype: 'toolbar',
263 layout: {
264 type: 'hbox',
265 pack: 'center'
266 }
267 },
268 doneButton: {
269 iconCls: 'check2',
270 ui: 'round',
271 text: ''
272 },
273 cancelButton: {
274 iconCls: 'delete',
275 ui: 'round',
276 text: ''
277 }
278 }, {
279 theme: ['CupertinoClassic'],
280 toolbar: {
281 ui: 'black'
282 }
283 }, {
284 theme: ['MountainView'],
285 toolbarPosition: 'bottom',
286 toolbar: {
287 defaults: {
288 flex: 1
289 }
290 }
291 }],
292
293 initialize: function() {
294 var me = this,
295 clsPrefix = Ext.baseCSSPrefix,
296 innerElement = this.innerElement;
297
298 //insert the mask, and the picker bar
299 this.mask = innerElement.createChild({
300 cls: clsPrefix + 'picker-mask'
301 });
302
303 this.bar = this.mask.createChild({
304 cls: clsPrefix + 'picker-bar'
305 });
306
307 me.on({
308 scope : this,
309 delegate: 'pickerslot',
310 slotpick: 'onSlotPick'
311 });
312 },
313
314 /**
315 * @private
316 */
317 applyToolbar: function(config) {
318 if (config === true) {
319 config = {};
320 }
321
322 Ext.applyIf(config, {
323 docked: this.getToolbarPosition()
324 });
325
326 return Ext.factory(config, 'Ext.TitleBar', this.getToolbar());
327 },
328
329 /**
330 * @private
331 */
332 updateToolbar: function(newToolbar, oldToolbar) {
333 if (newToolbar) {
334 this.add(newToolbar);
335 }
336
337 if (oldToolbar) {
338 this.remove(oldToolbar);
339 }
340 },
341
342 /**
343 * Updates the {@link #doneButton} configuration. Will change it into a button when appropriate, or just update the text if needed.
344 * @param {Object} config
345 * @return {Object}
346 */
347 applyDoneButton: function(config) {
348 if (config) {
349 if (Ext.isBoolean(config)) {
350 config = {};
351 }
352
353 if (typeof config == "string") {
354 config = {
355 text: config
356 };
357 }
358
359 Ext.applyIf(config, {
360 ui: 'action',
361 align: 'right',
362 text: 'Done'
363 });
364 }
365
366 return Ext.factory(config, 'Ext.Button', this.getDoneButton());
367 },
368
369 updateDoneButton: function(newDoneButton, oldDoneButton) {
370 var toolbar = this.getToolbar();
371
372 if (newDoneButton) {
373 toolbar.add(newDoneButton);
374 newDoneButton.on('tap', this.onDoneButtonTap, this);
375 } else if (oldDoneButton) {
376 toolbar.remove(oldDoneButton);
377 }
378 },
379
380 /**
381 * Updates the {@link #cancelButton} configuration. Will change it into a button when appropriate, or just update the text if needed.
382 * @param {Object} config
383 * @return {Object}
384 */
385 applyCancelButton: function(config) {
386 if (config) {
387 if (Ext.isBoolean(config)) {
388 config = {};
389 }
390
391 if (typeof config == "string") {
392 config = {
393 text: config
394 };
395 }
396
397 Ext.applyIf(config, {
398 align: 'left',
399 text: 'Cancel'
400 });
401 }
402
403 return Ext.factory(config, 'Ext.Button', this.getCancelButton());
404 },
405
406 updateCancelButton: function(newCancelButton, oldCancelButton) {
407 var toolbar = this.getToolbar();
408
409 if (newCancelButton) {
410 toolbar.add(newCancelButton);
411 newCancelButton.on('tap', this.onCancelButtonTap, this);
412 } else if (oldCancelButton) {
413 toolbar.remove(oldCancelButton);
414 }
415 },
416
417 /**
418 * @private
419 */
420 updateUseTitles: function(useTitles) {
421 var innerItems = this.getInnerItems(),
422 ln = innerItems.length,
423 cls = Ext.baseCSSPrefix + 'use-titles',
424 i, innerItem;
425
426 //add a cls onto the picker
427 if (useTitles) {
428 this.addCls(cls);
429 } else {
430 this.removeCls(cls);
431 }
432
433 //show the time on each of the slots
434 for (i = 0; i < ln; i++) {
435 innerItem = innerItems[i];
436
437 if (innerItem.isSlot) {
438 innerItem.setShowTitle(useTitles);
439 }
440 }
441 },
442
443 applySlots: function(slots) {
444 //loop through each of the slots and add a reference to this picker
445 if (slots) {
446 var ln = slots.length,
447 i;
448
449 for (i = 0; i < ln; i++) {
450 slots[i].picker = this;
451 }
452 }
453
454 return slots;
455 },
456
457 /**
458 * Adds any new {@link #slots} to this picker, and removes existing {@link #slots}
459 * @private
460 */
461 updateSlots: function(newSlots) {
462 var bcss = Ext.baseCSSPrefix,
463 innerItems;
464
465 this.removeAll();
466
467 if (newSlots) {
468 this.add(newSlots);
469 }
470
471 innerItems = this.getInnerItems();
472 if (innerItems.length > 0) {
473 innerItems[0].addCls(bcss + 'first');
474 innerItems[innerItems.length - 1].addCls(bcss + 'last');
475 }
476
477 this.updateUseTitles(this.getUseTitles());
478 },
479
480 /**
481 * @private
482 * Called when the done button has been tapped.
483 */
484 onDoneButtonTap: function() {
485 var oldValue = this._value,
486 newValue = this.getValue(true);
487
488 if (newValue != oldValue) {
489 this.fireEvent('change', this, newValue);
490 }
491
492 this.hide();
493 Ext.util.InputBlocker.unblockInputs();
494 },
495
496 /**
497 * @private
498 * Called when the cancel button has been tapped.
499 */
500 onCancelButtonTap: function() {
501 this.fireEvent('cancel', this);
502 this.hide();
503 Ext.util.InputBlocker.unblockInputs();
504 },
505
506 /**
507 * @private
508 * Called when a slot has been picked.
509 */
510 onSlotPick: function(slot) {
511 this.fireEvent('pick', this, this.getValue(true), slot);
512 },
513
514 show: function() {
515 if (this.getParent() === undefined) {
516 Ext.Viewport.add(this);
517 }
518
519 this.callParent(arguments);
520
521 if (!this.isHidden()) {
522 this.setValue(this._value);
523 }
524 Ext.util.InputBlocker.blockInputs();
525 },
526
527 /**
528 * Sets the values of the pickers slots.
529 * @param {Object} values The values in a {name:'value'} format.
530 * @param {Boolean} animated `true` to animate setting the values.
531 * @return {Ext.Picker} this This picker.
532 */
533 setValue: function(values, animated) {
534 var me = this,
535 slots = me.getInnerItems(),
536 ln = slots.length,
537 key, slot, loopSlot, i, value;
538
539 if (!values) {
540 values = {};
541 for (i = 0; i < ln; i++) {
542 //set the value to false so the slot will return null when getValue is called
543 values[slots[i].config.name] = null;
544 }
545 }
546
547 for (key in values) {
548 slot = null;
549 value = values[key];
550 for (i = 0; i < slots.length; i++) {
551 loopSlot = slots[i];
552 if (loopSlot.config.name == key) {
553 slot = loopSlot;
554 break;
555 }
556 }
557
558 if (slot) {
559 if (animated) {
560 slot.setValueAnimated(value);
561 } else {
562 slot.setValue(value);
563 }
564 }
565 }
566
567 me._values = me._value = values;
568
569 return me;
570 },
571
572 setValueAnimated: function(values) {
573 this.setValue(values, true);
574 },
575
576 /**
577 * Returns the values of each of the pickers slots
578 * @return {Object} The values of the pickers slots
579 */
580 getValue: function(useDom) {
581 var values = {},
582 items = this.getItems().items,
583 ln = items.length,
584 item, i;
585
586 if (useDom) {
587 for (i = 0; i < ln; i++) {
588 item = items[i];
589 if (item && item.isSlot) {
590 values[item.getName()] = item.getValue(useDom);
591 }
592 }
593
594 this._values = values;
595 }
596
597 return this._values;
598 },
599
600 /**
601 * Returns the values of each of the pickers slots.
602 * @return {Object} The values of the pickers slots.
603 */
604 getValues: function() {
605 return this.getValue();
606 },
607
608 destroy: function() {
609 this.callParent();
610 Ext.destroy(this.mask, this.bar);
611 }
612 }, function() {
613 //<deprecated product=touch since=2.0>
614 /**
615 * @member Ext.picker.Picker
616 * @cfg {String} activeCls
617 * CSS class to be applied to individual list items when they have been chosen.
618 * @removed 2.0.0
619 */
620 Ext.deprecateProperty(this, 'activeCls', null, "Ext.picker.Picker.activeCls has been removed");
621
622 /**
623 * @method getCard
624 * @inheritdoc Ext.picker.Picker#getActiveItem
625 * @deprecated 2.0.0 Please use {@link #getActiveItem} instead
626 */
627 Ext.deprecateClassMethod(this, 'getCard', 'getActiveItem');
628
629 /**
630 * @method setCard
631 * @inheritdoc Ext.picker.Picker#setActiveItem
632 * @deprecated 2.0.0 Please use {@link #setActiveItem} instead
633 */
634 Ext.deprecateClassMethod(this, 'setCard', 'setActiveItem');
635 //</deprecated>
636 });
637