]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @private\r | |
3 | *\r | |
4 | * A general {@link Ext.picker.Picker} slot class. Slots are used to organize multiple scrollable slots into\r | |
5 | * a single {@link Ext.picker.Picker}.\r | |
6 | *\r | |
7 | * {\r | |
8 | * name : 'limit_speed',\r | |
9 | * title: 'Speed Limit',\r | |
10 | * data : [\r | |
11 | * {text: '50 KB/s', value: 50},\r | |
12 | * {text: '100 KB/s', value: 100},\r | |
13 | * {text: '200 KB/s', value: 200},\r | |
14 | * {text: '300 KB/s', value: 300}\r | |
15 | * ]\r | |
16 | * }\r | |
17 | *\r | |
18 | * See the {@link Ext.picker.Picker} documentation on how to use slots.\r | |
19 | */\r | |
20 | Ext.define('Ext.picker.Slot', {\r | |
21 | extend: 'Ext.dataview.DataView',\r | |
22 | xtype: 'pickerslot',\r | |
23 | \r | |
24 | requires: [\r | |
25 | 'Ext.XTemplate',\r | |
26 | 'Ext.data.Store',\r | |
27 | 'Ext.Component',\r | |
28 | 'Ext.data.StoreManager'\r | |
29 | ],\r | |
30 | \r | |
31 | /**\r | |
32 | * @event slotpick\r | |
33 | * Fires whenever an slot is picked\r | |
34 | * @param {Ext.picker.Slot} this\r | |
35 | * @param {Mixed} value The value of the pick\r | |
36 | * @param {HTMLElement} node The node element of the pick\r | |
37 | */\r | |
38 | \r | |
39 | isSlot: true,\r | |
40 | \r | |
41 | config: {\r | |
42 | /**\r | |
43 | * @cfg {String} title The title to use for this slot, or `null` for no title.\r | |
44 | * @accessor\r | |
45 | */\r | |
46 | title: null,\r | |
47 | \r | |
48 | /**\r | |
49 | * @private\r | |
50 | * @cfg {Boolean} showTitle\r | |
51 | * @accessor\r | |
52 | */\r | |
53 | showTitle: true,\r | |
54 | \r | |
55 | /**\r | |
56 | * @private\r | |
57 | * @cfg {String} cls The main component class\r | |
58 | * @accessor\r | |
59 | */\r | |
60 | cls: Ext.baseCSSPrefix + 'picker-slot',\r | |
61 | \r | |
62 | /**\r | |
63 | * @cfg {String} name (required) The name of this slot.\r | |
64 | * @accessor\r | |
65 | */\r | |
66 | name: null,\r | |
67 | \r | |
68 | /**\r | |
69 | * @cfg {Number} value The value of this slot\r | |
70 | * @accessor\r | |
71 | */\r | |
72 | value: null,\r | |
73 | \r | |
74 | /**\r | |
75 | * @cfg {Number} flex\r | |
76 | * @accessor\r | |
77 | * @private\r | |
78 | */\r | |
79 | flex: 1,\r | |
80 | \r | |
81 | /**\r | |
82 | * @cfg {String} align The horizontal alignment of the slot's contents.\r | |
83 | *\r | |
84 | * Valid values are: "left", "center", and "right".\r | |
85 | * @accessor\r | |
86 | */\r | |
87 | align: 'left',\r | |
88 | \r | |
89 | /**\r | |
90 | * @cfg {String} displayField The display field in the store.\r | |
91 | * @accessor\r | |
92 | */\r | |
93 | displayField: 'text',\r | |
94 | \r | |
95 | /**\r | |
96 | * @cfg {String} valueField The value field in the store.\r | |
97 | * @accessor\r | |
98 | */\r | |
99 | valueField: 'value',\r | |
100 | \r | |
101 | /**\r | |
102 | * @cfg {String} itemTpl The template to be used in this slot.\r | |
103 | * If you set this, {@link #displayField} will be ignored.\r | |
104 | */\r | |
105 | itemTpl: null,\r | |
106 | \r | |
107 | /**\r | |
108 | * @cfg {Object} scrollable\r | |
109 | * @accessor\r | |
110 | * @hide\r | |
111 | */\r | |
112 | scrollable: {\r | |
113 | x: false,\r | |
114 | indicators: false,\r | |
115 | momentumEasing: {\r | |
116 | minVelocity: 2\r | |
117 | },\r | |
118 | slotSnapEasing: {\r | |
119 | duration: 100\r | |
120 | }\r | |
121 | },\r | |
122 | \r | |
123 | /**\r | |
124 | * @cfg {Boolean} verticallyCenterItems\r | |
125 | * @private\r | |
126 | */\r | |
127 | verticallyCenterItems: true\r | |
128 | },\r | |
129 | \r | |
130 | constructor: function() {\r | |
131 | /**\r | |
132 | * @property selectedIndex\r | |
133 | * @type Number\r | |
134 | * The current `selectedIndex` of the picker slot.\r | |
135 | * @private\r | |
136 | */\r | |
137 | this.selectedIndex = 0;\r | |
138 | \r | |
139 | /**\r | |
140 | * @property picker\r | |
141 | * @type Ext.picker.Picker\r | |
142 | * A reference to the owner Picker.\r | |
143 | * @private\r | |
144 | */\r | |
145 | \r | |
146 | this.callParent(arguments);\r | |
147 | },\r | |
148 | \r | |
149 | /**\r | |
150 | * Sets the title for this dataview by creating element.\r | |
151 | * @param {String} title\r | |
152 | * @return {String}\r | |
153 | */\r | |
154 | applyTitle: function(title) {\r | |
155 | //check if the title isnt defined\r | |
156 | if (title) {\r | |
157 | //create a new title element\r | |
158 | title = Ext.create('Ext.Component', {\r | |
159 | cls: Ext.baseCSSPrefix + 'picker-slot-title',\r | |
160 | docked: 'top',\r | |
161 | html: title\r | |
162 | });\r | |
163 | }\r | |
164 | \r | |
165 | return title;\r | |
166 | },\r | |
167 | \r | |
168 | updateTitle: function(newTitle, oldTitle) {\r | |
169 | if (newTitle) {\r | |
170 | this.add(newTitle);\r | |
171 | this.setupBar();\r | |
172 | }\r | |
173 | \r | |
174 | if (oldTitle) {\r | |
175 | this.remove(oldTitle);\r | |
176 | }\r | |
177 | },\r | |
178 | \r | |
179 | updateShowTitle: function(showTitle) {\r | |
180 | var title = this.getTitle(),\r | |
181 | mode = showTitle ? 'show' : 'hide';\r | |
182 | if (title) {\r | |
183 | title.on(mode, this.setupBar, this, { single: true, delay: 50 });\r | |
184 | title[showTitle ? 'show' : 'hide']();\r | |
185 | }\r | |
186 | },\r | |
187 | \r | |
188 | updateDisplayField: function(newDisplayField) {\r | |
189 | if (!this.config.itemTpl) {\r | |
190 | this.setItemTpl('<div class="' + Ext.baseCSSPrefix + 'picker-item {cls} <tpl if="extra">' + Ext.baseCSSPrefix + 'picker-invalid</tpl>">{' + newDisplayField + '}</div>');\r | |
191 | }\r | |
192 | },\r | |
193 | \r | |
194 | /**\r | |
195 | * Updates the {@link #align} configuration\r | |
196 | */\r | |
197 | updateAlign: function(newAlign, oldAlign) {\r | |
198 | var element = this.element;\r | |
199 | element.addCls(Ext.baseCSSPrefix + 'picker-' + newAlign);\r | |
200 | element.removeCls(Ext.baseCSSPrefix + 'picker-' + oldAlign);\r | |
201 | },\r | |
202 | \r | |
203 | /**\r | |
204 | * Looks at the {@link #data} configuration and turns it into {@link #store}.\r | |
205 | * @param {Object} data\r | |
206 | * @return {Object}\r | |
207 | */\r | |
208 | applyData: function(data) {\r | |
209 | var parsedData = [],\r | |
210 | ln = data && data.length,\r | |
211 | i, item, obj;\r | |
212 | \r | |
213 | if (data && Ext.isArray(data) && ln) {\r | |
214 | for (i = 0; i < ln; i++) {\r | |
215 | item = data[i];\r | |
216 | obj = {};\r | |
217 | if (Ext.isArray(item)) {\r | |
218 | obj[this.valueField] = item[0];\r | |
219 | obj[this.displayField] = item[1];\r | |
220 | }\r | |
221 | else if (Ext.isString(item)) {\r | |
222 | obj[this.valueField] = item;\r | |
223 | obj[this.displayField] = item;\r | |
224 | }\r | |
225 | else if (Ext.isObject(item)) {\r | |
226 | obj = item;\r | |
227 | }\r | |
228 | parsedData.push(obj);\r | |
229 | }\r | |
230 | }\r | |
231 | \r | |
232 | return data;\r | |
233 | },\r | |
234 | \r | |
235 | /**\r | |
236 | * @private\r | |
237 | */\r | |
238 | initialize: function() {\r | |
239 | this.callParent();\r | |
240 | \r | |
241 | var scroller = this.getScrollable();\r | |
242 | \r | |
243 | this.on({\r | |
244 | scope: this,\r | |
245 | painted: 'onPainted',\r | |
246 | itemtap: 'doItemTap'\r | |
247 | });\r | |
248 | \r | |
249 | this.element.on({\r | |
250 | scope: this,\r | |
251 | touchstart: 'onTouchStart',\r | |
252 | touchend: 'onTouchEnd'\r | |
253 | });\r | |
254 | \r | |
255 | scroller.on({\r | |
256 | scope: this,\r | |
257 | scrollend: 'onScrollEnd'\r | |
258 | });\r | |
259 | },\r | |
260 | \r | |
261 | /**\r | |
262 | * @private\r | |
263 | */\r | |
264 | onPainted: function() {\r | |
265 | this.setupBar();\r | |
266 | },\r | |
267 | \r | |
268 | /**\r | |
269 | * Returns an instance of the owner picker.\r | |
270 | * @return {Object}\r | |
271 | * @private\r | |
272 | */\r | |
273 | getPicker: function() {\r | |
274 | if (!this.picker) {\r | |
275 | this.picker = this.getParent();\r | |
276 | }\r | |
277 | \r | |
278 | return this.picker;\r | |
279 | },\r | |
280 | \r | |
281 | /**\r | |
282 | * @private\r | |
283 | */\r | |
284 | setupBar: function() {\r | |
285 | if (!this.rendered) {\r | |
286 | //if the component isnt rendered yet, there is no point in calculating the padding just eyt\r | |
287 | return;\r | |
288 | }\r | |
289 | \r | |
290 | var element = this.element,\r | |
291 | containerElement = this.container.element,\r | |
292 | picker = this.getPicker(),\r | |
293 | bar = picker.bar,\r | |
294 | value = this.getValue(),\r | |
295 | showTitle = this.getShowTitle(),\r | |
296 | title = this.getTitle(),\r | |
297 | scroller = this.getScrollable(),\r | |
298 | titleHeight = 0,\r | |
299 | barHeight, padding;\r | |
300 | \r | |
301 | barHeight = bar.dom.getBoundingClientRect().height;\r | |
302 | \r | |
303 | if (showTitle && title) {\r | |
304 | titleHeight = title.element.getHeight();\r | |
305 | }\r | |
306 | \r | |
307 | padding = Math.ceil((element.getHeight() - titleHeight - barHeight) / 2);\r | |
308 | \r | |
309 | if (this.getVerticallyCenterItems()) {\r | |
310 | containerElement.setStyle({\r | |
311 | padding: padding + 'px 0 ' + padding + 'px'\r | |
312 | });\r | |
313 | }\r | |
314 | \r | |
315 | scroller.refresh();\r | |
316 | scroller.setSlotSnapSize(barHeight);\r | |
317 | this.setValue(value);\r | |
318 | },\r | |
319 | \r | |
320 | /**\r | |
321 | * @private\r | |
322 | */\r | |
323 | doItemTap: function(list, index, item, e) {\r | |
324 | var me = this;\r | |
325 | me.selectedIndex = index;\r | |
326 | me.selectedNode = item;\r | |
327 | me.scrollToItem(item, true);\r | |
328 | },\r | |
329 | \r | |
330 | /**\r | |
331 | * @private\r | |
332 | */\r | |
333 | scrollToItem: function(item, animated) {\r | |
334 | var y = item.getY(),\r | |
335 | parentEl = item.parent(),\r | |
336 | parentY = parentEl.getY(),\r | |
337 | scroller = this.getScrollable(),\r | |
338 | difference;\r | |
339 | \r | |
340 | difference = y - parentY;\r | |
341 | \r | |
342 | scroller.scrollTo(0, difference, animated);\r | |
343 | },\r | |
344 | \r | |
345 | /**\r | |
346 | * @private\r | |
347 | */\r | |
348 | onTouchStart: function() {\r | |
349 | this.element.addCls(Ext.baseCSSPrefix + 'scrolling');\r | |
350 | },\r | |
351 | \r | |
352 | /**\r | |
353 | * @private\r | |
354 | */\r | |
355 | onTouchEnd: function() {\r | |
356 | this.element.removeCls(Ext.baseCSSPrefix + 'scrolling');\r | |
357 | },\r | |
358 | \r | |
359 | /**\r | |
360 | * @private\r | |
361 | */\r | |
362 | onScrollEnd: function(scroller, x, y) {\r | |
363 | var me = this,\r | |
364 | index = Math.round(y / me.picker.bar.dom.getBoundingClientRect().height),\r | |
365 | viewItems = me.getViewItems(),\r | |
366 | item = viewItems[index];\r | |
367 | \r | |
368 | if (item) {\r | |
369 | me.selectedIndex = index;\r | |
370 | me.selectedNode = item;\r | |
371 | \r | |
372 | me.fireEvent('slotpick', me, me.getValue(), me.selectedNode);\r | |
373 | }\r | |
374 | },\r | |
375 | \r | |
376 | /**\r | |
377 | * Returns the value of this slot\r | |
378 | * @private\r | |
379 | */\r | |
380 | getValue: function(useDom) {\r | |
381 | var store = this.getStore(),\r | |
382 | record, value;\r | |
383 | \r | |
384 | if (!store) {\r | |
385 | return;\r | |
386 | }\r | |
387 | \r | |
388 | if (!this.rendered || !useDom) {\r | |
389 | return this._value;\r | |
390 | }\r | |
391 | \r | |
392 | //if the value is ever false, that means we do not want to return anything\r | |
393 | if (this._value === false) {\r | |
394 | return null;\r | |
395 | }\r | |
396 | \r | |
397 | record = store.getAt(this.selectedIndex);\r | |
398 | \r | |
399 | value = record ? record.get(this.getValueField()) : null;\r | |
400 | \r | |
401 | return value;\r | |
402 | },\r | |
403 | \r | |
404 | /**\r | |
405 | * Sets the value of this slot\r | |
406 | * @private\r | |
407 | */\r | |
408 | setValue: function(value) {\r | |
409 | return this.doSetValue(value);\r | |
410 | },\r | |
411 | \r | |
412 | /**\r | |
413 | * Sets the value of this slot\r | |
414 | * @private\r | |
415 | */\r | |
416 | setValueAnimated: function(value) {\r | |
417 | return this.doSetValue(value, true);\r | |
418 | },\r | |
419 | \r | |
420 | doSetValue: function(value, animated) {\r | |
421 | if (!this.rendered) {\r | |
422 | //we don't want to call this until the slot has been rendered\r | |
423 | this._value = value;\r | |
424 | return;\r | |
425 | }\r | |
426 | \r | |
427 | var store = this.getStore(),\r | |
428 | viewItems = this.getViewItems(),\r | |
429 | valueField = this.getValueField(),\r | |
430 | index, item;\r | |
431 | \r | |
432 | index = store.findExact(valueField, value);\r | |
433 | \r | |
434 | if (index == -1) {\r | |
435 | index = 0;\r | |
436 | }\r | |
437 | \r | |
438 | item = Ext.get(viewItems[index]);\r | |
439 | \r | |
440 | this.selectedIndex = index;\r | |
441 | if (item) {\r | |
442 | this.scrollToItem(item, (animated) ? {\r | |
443 | duration: 100\r | |
444 | } : false);\r | |
445 | this.select(this.selectedIndex);\r | |
446 | }\r | |
447 | \r | |
448 | this._value = value;\r | |
449 | }\r | |
450 | });\r |