3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
17 * @class Ext.picker.Month
18 * @extends Ext.Component
19 * <p>A month picker component. This class is used by the {@link Ext.picker.Date DatePicker} class
20 * to allow browsing and selection of year/months combinations.</p>
22 Ext.define('Ext.picker.Month', {
23 extend: 'Ext.Component',
24 requires: ['Ext.XTemplate', 'Ext.util.ClickRepeater', 'Ext.Date', 'Ext.button.Button'],
25 alias: 'widget.monthpicker',
26 alternateClassName: 'Ext.MonthPicker',
29 '<div class="{baseCls}-body">',
30 '<div class="{baseCls}-months">',
32 '<div class="{parent.baseCls}-item {parent.baseCls}-month"><a href="#" hidefocus="on">{.}</a></div>',
35 '<div class="{baseCls}-years">',
36 '<div class="{baseCls}-yearnav">',
37 '<button class="{baseCls}-yearnav-prev"></button>',
38 '<button class="{baseCls}-yearnav-next"></button>',
41 '<div class="{parent.baseCls}-item {parent.baseCls}-year"><a href="#" hidefocus="on">{.}</a></div>',
45 '<div class="' + Ext.baseCSSPrefix + 'clear"></div>',
46 '<tpl if="showButtons">',
47 '<div class="{baseCls}-buttons"></div>',
52 * @cfg {String} okText The text to display on the ok button. Defaults to <tt>'OK'</tt>
57 * @cfg {String} cancelText The text to display on the cancel button. Defaults to <tt>'Cancel'</tt>
62 * @cfg {String} baseCls The base CSS class to apply to the picker element. Defaults to <tt>'x-monthpicker'</tt>
64 baseCls: Ext.baseCSSPrefix + 'monthpicker',
67 * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker. Defaults to <tt>true</tt>.
72 * @cfg {String} selectedCls The class to be added to selected items in the picker. Defaults to
73 * <tt>'x-monthpicker-selected'</tt>
77 * @cfg {Date/Array} value The default value to set. See {#setValue setValue}
81 // used when attached to date picker which isnt showing buttons
82 smallCls: Ext.baseCSSPrefix + 'monthpicker-small',
86 yearOffset: 5, // 10 years in total, 2 per row
87 monthOffset: 6, // 12 months, 2 per row
89 // private, inherit docs
90 initComponent: function(){
93 me.selectedCls = me.baseCls + '-selected';
97 * Fires when the cancel button is pressed.
98 * @param {Ext.picker.Month} this
104 * Fires when a month is clicked.
105 * @param {Ext.picker.Month} this
106 * @param {Array} value The current value
111 * @event monthdblclick
112 * Fires when a month is clicked.
113 * @param {Ext.picker.Month} this
114 * @param {Array} value The current value
120 * Fires when the ok button is pressed.
121 * @param {Ext.picker.Month} this
122 * @param {Array} value The current value
128 * Fires when a month/year is selected.
129 * @param {Ext.picker.Month} this
130 * @param {Array} value The current value
136 * Fires when a year is clicked.
137 * @param {Ext.picker.Month} this
138 * @param {Array} value The current value
143 * @event yeardblclick
144 * Fires when a year is clicked.
145 * @param {Ext.picker.Month} this
146 * @param {Array} value The current value
151 me.addCls(me.smallCls);
153 me.setValue(me.value);
154 me.activeYear = me.getYear(new Date().getFullYear() - 4, -4);
158 // private, inherit docs
159 onRender: function(ct, position){
163 shortName = Ext.Date.getShortMonthName,
164 monthLen = me.monthOffset;
166 for (; i < monthLen; ++i) {
167 months.push(shortName(i), shortName(i + monthLen));
170 Ext.apply(me.renderData, {
172 years: me.getYears(),
173 showButtons: me.showButtons
176 Ext.apply(me.renderSelectors, {
177 bodyEl: '.' + me.baseCls + '-body',
178 prevEl: '.' + me.baseCls + '-yearnav-prev',
179 nextEl: '.' + me.baseCls + '-yearnav-next',
180 buttonsEl: '.' + me.baseCls + '-buttons'
182 this.callParent([ct, position]);
185 // private, inherit docs
186 afterRender: function(){
189 buttonsEl = me.buttonsEl;
193 me.mon(body, 'click', me.onBodyClick, me);
194 me.mon(body, 'dblclick', me.onBodyClick, me);
196 // keep a reference to the year/month elements since we'll be re-using them
197 me.years = body.select('.' + me.baseCls + '-year a');
198 me.months = body.select('.' + me.baseCls + '-month a');
200 if (me.showButtons) {
201 me.okBtn = Ext.create('Ext.button.Button', {
204 handler: me.onOkClick,
207 me.cancelBtn = Ext.create('Ext.button.Button', {
210 handler: me.onCancelClick,
215 me.backRepeater = Ext.create('Ext.util.ClickRepeater', me.prevEl, {
216 handler: Ext.Function.bind(me.adjustYear, me, [-me.totalYears])
219 me.prevEl.addClsOnOver(me.baseCls + '-yearnav-prev-over');
220 me.nextRepeater = Ext.create('Ext.util.ClickRepeater', me.nextEl, {
221 handler: Ext.Function.bind(me.adjustYear, me, [me.totalYears])
223 me.nextEl.addClsOnOver(me.baseCls + '-yearnav-next-over');
228 * Set the value for the picker.
229 * @param {Date/Array} value The value to set. It can be a Date object, where the month/year will be extracted, or
230 * it can be an array, with the month as the first index and the year as the second.
231 * @return {Ext.picker.Month} this
233 setValue: function(value){
235 active = me.activeYear,
236 offset = me.monthOffset,
241 me.value = [null, null];
242 } else if (Ext.isDate(value)) {
243 me.value = [value.getMonth(), value.getFullYear()];
245 me.value = [value[0], value[1]];
251 if ((year < active || year > active + me.yearOffset)) {
252 me.activeYear = year - me.yearOffset + 1;
262 * Gets the selected value. It is returned as an array [month, year]. It may
263 * be a partial value, for example [null, 2010]. The month is returned as
265 * @return {Array} The selected value
267 getValue: function(){
272 * Checks whether the picker has a selection
273 * @return {Boolean} Returns true if both a month and year have been selected
275 hasSelection: function(){
276 var value = this.value;
277 return value[0] !== null && value[1] !== null;
281 * Get an array of years to be pushed in the template. It is not in strict
282 * numerical order because we want to show them in columns.
284 * @return {Array} An array of years
286 getYears: function(){
288 offset = me.yearOffset,
289 start = me.activeYear, // put the "active" year on the left
290 end = start + offset,
294 for (; i < end; ++i) {
295 years.push(i, i + offset);
302 * Update the years in the body based on any change
305 updateBody: function(){
309 yearNumbers = me.getYears(),
310 cls = me.selectedCls,
311 value = me.getYear(null),
313 monthOffset = me.monthOffset,
317 years.removeCls(cls);
318 months.removeCls(cls);
319 years.each(function(el, all, index){
320 year = yearNumbers[index];
321 el.dom.innerHTML = year;
323 el.dom.className = cls;
326 if (month !== null) {
327 if (month < monthOffset) {
330 month = (month - monthOffset) * 2 + 1;
332 months.item(month).addCls(cls);
338 * Gets the current year value, or the default.
340 * @param {Number} defaultValue The default value to use if the year is not defined.
341 * @param {Number} offset A number to offset the value by
342 * @return {Number} The year value
344 getYear: function(defaultValue, offset) {
345 var year = this.value[1];
346 offset = offset || 0;
347 return year === null ? defaultValue : year + offset;
351 * React to clicks on the body
354 onBodyClick: function(e, t) {
356 isDouble = e.type == 'dblclick';
358 if (e.getTarget('.' + me.baseCls + '-month')) {
360 me.onMonthClick(t, isDouble);
361 } else if (e.getTarget('.' + me.baseCls + '-year')) {
363 me.onYearClick(t, isDouble);
368 * Modify the year display by passing an offset.
369 * @param {Number} offset The offset to move by. If not specified, it defaults to 10.
371 adjustYear: function(offset){
372 if (typeof offset != 'number') {
373 offset = this.totalYears;
375 this.activeYear += offset;
380 * React to the ok button being pressed
383 onOkClick: function(){
384 this.fireEvent('okclick', this, this.value);
388 * React to the cancel button being pressed
391 onCancelClick: function(){
392 this.fireEvent('cancelclick', this);
396 * React to a month being clicked
398 * @param {HTMLElement} target The element that was clicked
399 * @param {Boolean} isDouble True if the event was a doubleclick
401 onMonthClick: function(target, isDouble){
403 me.value[0] = me.resolveOffset(me.months.indexOf(target), me.monthOffset);
405 me.fireEvent('month' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
406 me.fireEvent('select', me, me.value);
410 * React to a year being clicked
412 * @param {HTMLElement} target The element that was clicked
413 * @param {Boolean} isDouble True if the event was a doubleclick
415 onYearClick: function(target, isDouble){
417 me.value[1] = me.activeYear + me.resolveOffset(me.years.indexOf(target), me.yearOffset);
419 me.fireEvent('year' + (isDouble ? 'dbl' : '') + 'click', me, me.value);
420 me.fireEvent('select', me, me.value);
425 * Returns an offsetted number based on the position in the collection. Since our collections aren't
426 * numerically ordered, this function helps to normalize those differences.
428 * @param {Object} index
429 * @param {Object} offset
430 * @return {Number} The correctly offsetted number
432 resolveOffset: function(index, offset){
433 if (index % 2 === 0) {
436 return offset + Math.floor(index / 2);
440 // private, inherit docs
441 beforeDestroy: function(){
443 me.years = me.months = null;
444 Ext.destroyMembers('backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');