/** * @private * * A general {@link Ext.picker.Picker} slot class. Slots are used to organize multiple scrollable slots into * a single {@link Ext.picker.Picker}. * * { * name : 'limit_speed', * title: 'Speed Limit', * data : [ * {text: '50 KB/s', value: 50}, * {text: '100 KB/s', value: 100}, * {text: '200 KB/s', value: 200}, * {text: '300 KB/s', value: 300} * ] * } * * See the {@link Ext.picker.Picker} documentation on how to use slots. */ Ext.define('Ext.picker.Slot', { extend: 'Ext.DataView', xtype : 'pickerslot', alternateClassName: 'Ext.Picker.Slot', requires: [ 'Ext.XTemplate', 'Ext.data.Store', 'Ext.Component', 'Ext.data.StoreManager' ], /** * @event slotpick * Fires whenever an slot is picked * @param {Ext.picker.Slot} this * @param {Mixed} value The value of the pick * @param {HTMLElement} node The node element of the pick */ isSlot: true, config: { /** * @cfg {String} title * The title to use for this slot. Null for no title * @accessor */ title: null, /** * @private * @cfg {Boolean} showTitle * @accessor */ showTitle: true, /** * @private * @cfg {String} cls * The main component class * @accessor */ cls: Ext.baseCSSPrefix + 'picker-slot', /** * @cfg {String} name * The name of this slot. This config option is required. * @accessor */ name: null, /** * @cfg {Number} value The value of this slot * @accessor */ value: null, /** * @hide * @cfg {Number} flex * @accessor */ flex: 1, /** * @cfg {String} align * The horizontal alignment of the slot's contents. Valid values are "left", "center", * and "right". Defaults to "left". * @accessor */ align: 'left', /** * @hide * @cfg {String} itemSelector * @accessor */ itemSelector: 'div.' + Ext.baseCSSPrefix + 'picker-item', /** * @cfg {String} displayField * The display field in the store. * Defaults to 'text'. * @accessor */ displayField: 'text', /** * @cfg {String} valueField * The value field in the store. * Defaults to 'value'. * @accessor */ valueField: 'value', /** * @hide * @cfg {Object} scrollable * @accessor */ scrollable: { direction : 'vertical', indicators: false } }, constructor: function() { /** * @property selectedIndex * @type Number * The current selectedIndex of the picker slot * @private */ this.selectedIndex = 0; /** * @property picker * @type Ext.picker.Picker * A reference to the owner Picker * @private */ this.callParent(arguments); }, /** * Sets the title for this dataview by creating element */ applyTitle: function(title) { //check if the title isnt defined if (title) { //create a new title element title = Ext.create('Ext.Component', { cls: Ext.baseCSSPrefix + 'picker-slot-title', docked : 'top', html : title }); } return title; }, updateTitle: function(newTitle, oldTitle) { if (newTitle) { this.add(newTitle); this.setupBar(); } if (oldTitle) { this.remove(oldTitle); } }, updateShowTitle: function(showTitle) { var title = this.getTitle(); if (title) { title[showTitle ? 'show' : 'hide'](); this.setupBar(); } }, updateDisplayField: function(newDisplayField) { this.setItemTpl('<div class="' + Ext.baseCSSPrefix + 'picker-item {cls} <tpl if="extra">' + Ext.baseCSSPrefix + 'picker-invalid</tpl>">{' + newDisplayField + '}</div>'); }, /** * Updates the {@link #align} configuration */ updateAlign: function(newAlign, oldAlign) { var element = this.element; element.addCls(Ext.baseCSSPrefix + 'picker-' + newAlign); element.removeCls(Ext.baseCSSPrefix + 'picker-' + oldAlign); }, /** * Looks at the {@link #data} configuration and turns it into {@link #store} */ applyData: function(data) { var parsedData = [], ln = data && data.length, i, item, obj; if (data && Ext.isArray(data) && ln) { for (i = 0; i < ln; i++) { item = data[i]; obj = {}; if (Ext.isArray(item)) { obj[this.valueField] = item[0]; obj[this.displayField] = item[1]; } else if (Ext.isString(item)) { obj[this.valueField] = item; obj[this.displayField] = item; } else if (Ext.isObject(item)) { obj = item; } parsedData.push(obj); } } return data; }, updateData: function(data) { this.setStore(Ext.create('Ext.data.Store', { model: 'x-textvalue', data : data })); }, // @private initialize: function() { this.callParent(); var scroller = this.getScrollable().getScroller(); this.on({ scope: this, painted: 'onPainted', itemtap: 'doItemTap' }); scroller.on({ scope: this, scrollend: 'onScrollEnd' }); }, // @private onPainted: function() { this.setupBar(); }, /** * Returns an instance of the owner picker * @private */ getPicker: function() { if (!this.picker) { this.picker = this.getParent(); } return this.picker; }, // @private setupBar: function() { if (!this.rendered) { //if the component isnt rendered yet, there is no point in calculating the padding just eyt return; } var element = this.element, innerElement = this.innerElement, picker = this.getPicker(), bar = picker.bar, value = this.getValue(), showTitle = this.getShowTitle(), title = this.getTitle(), scrollable = this.getScrollable(), scroller = scrollable.getScroller(), barY, elY, barHeight, padding, titleHeight, paddingBottom; barY = bar.getY(); elY = element.getY(); if (showTitle && title) { elY += title.element.getHeight(); } padding = paddingBottom = Math.abs(elY - barY); this.slotPadding = padding; if (showTitle && title) { titleHeight = title.element.getHeight(); paddingBottom += titleHeight; } innerElement.setStyle({ padding: padding + 'px 0 ' + paddingBottom + 'px' }); barHeight = bar.getHeight(); scroller.refresh(); scroller.setSnap(barHeight); this.setValue(value); }, // @private doItemTap: function(list, index, item, e) { var me = this; me.selectedIndex = index; me.selectedNode = item; me.scrollToItem(item, true); me.fireEvent('slotpick', me, me.getValue(), me.selectedNode); }, // @private scrollToItem: function(item, animated) { var y = item.getY(), parentEl = item.parent(), parentY = parentEl.getY(), // padding = this.slotPadding, scrollView = this.getScrollable(), scroller = scrollView.getScroller(), difference; difference = y - parentY; if (animated) { scroller.scrollToAnimated(0, difference); } else { scroller.scrollTo(0, difference); } }, // @private onScrollEnd: function(scroller, position) { var me = this, index = Math.round(position.y / me.picker.bar.getHeight()), viewItems = me.getViewItems(), item = viewItems[index]; if (item) { me.selectedIndex = index; me.selectedNode = item; me.fireEvent('slotpick', me, me.getValue(), me.selectedNode); } }, /** * Returns the vlaue of this slot * @private */ getValue: function() { var store = this.getStore(), record, value; if (!store) { return; } record = store.getAt(this.selectedIndex); value = record ? record.get(this.getValueField()) : null; this._value = value; return value; }, /** * Sets the value of this slot * @private */ setValue: function(value) { if (!Ext.isDefined(value)){ return; } if (!this.rendered) { //we don't want to call this until the slot has been rendered this._value = value; return; } var store = this.getStore(), viewItems = this.getViewItems(), valueField = this.getValueField(), index, item; index = store.find(valueField, value); if (index != -1) { item = Ext.get(viewItems[index]); this.selectedIndex = index; this.scrollToItem(item); this._value = value; } }, /** * Sets the value of this slot * @private */ setValueAnimated: function(value) { if (!value) { return; } if (!this.rendered) { //we don't want to call this until the slot has been rendered this._value = value; return; } var store = this.getStore(), viewItems = this.getViewItems(), valueField = this.getValueField(), index, item; index = store.find(valueField, value); if (index != -1) { item = Ext.get(viewItems[index]); this.selectedIndex = index; this.scrollToItem(item, true); this._value = value; } } });