]>
git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/picker/Month.js
2 * A month / year picker component. This class is used by the
3 * {@link Ext.picker.Date Date picker} to allow browsing and selection of year and
4 * months combinations, but may also be used as a standalone component.
8 * xtype: 'monthpicker',
9 * renderTo: document.body,
11 * onSelect: function() {
12 * Ext.Msg.alert('Selected', this.getValue());
15 * okclick: 'onSelect',
16 * monthdblclick: 'onSelect',
17 * yeardblclick: 'onSelect',
18 * cancelclick: function () {
19 * this.setValue(new Date());
24 Ext
.define('Ext.picker.Month', {
25 extend
: 'Ext.Component',
28 'Ext.util.ClickRepeater',
32 alias
: 'widget.monthpicker',
33 alternateClassName
: 'Ext.MonthPicker',
40 'bodyEl', 'prevEl', 'nextEl', 'monthEl', 'yearEl'
44 '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">',
45 '<div id="{id}-monthEl" data-ref="monthEl" class="{baseCls}-months">',
47 '<div class="{parent.baseCls}-item {parent.baseCls}-month">',
48 '<a style="{parent.monthStyle}" role="button" hidefocus="on" class="{parent.baseCls}-item-inner">{.}</a>',
52 '<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">',
53 '<div class="{baseCls}-yearnav">',
54 '<div class="{baseCls}-yearnav-button-ct">',
55 '<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>',
57 '<div class="{baseCls}-yearnav-button-ct">',
58 '<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>',
62 '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
63 '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>',
67 '<div class="' + Ext
.baseCSSPrefix
+ 'clear"></div>',
68 '<tpl if="showButtons">',
69 '<div class="{baseCls}-buttons">{%',
70 'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
71 'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
72 'okBtn.ownerCt = cancelBtn.ownerCt = me;',
73 'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
74 'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
82 * @cfg {String} okText The text to display on the ok button.
89 * @cfg {String} cancelText The text to display on the cancel button.
95 * @cfg {String} [baseCls='x-monthpicker']
96 * The base CSS class to apply to the picker element.
98 baseCls
: Ext
.baseCSSPrefix
+ 'monthpicker',
101 * @cfg {Boolean} showButtons True to show ok and cancel buttons below the picker.
106 * @cfg {String} [selectedCls='x-monthpicker-selected'] The class to be added to selected items in the picker.
110 * @cfg {Date/Number[]} value The default value to set. See {@link #setValue}
115 * The {@link Ext.button.Button#ui} to use for the month picker's footer buttons.
117 footerButtonUI
: 'default',
120 measureMaxHeight
: 20,
122 // used when attached to date picker which isnt showing buttons
123 smallCls
: Ext
.baseCSSPrefix
+ 'monthpicker-small',
129 yearOffset
: 5, // 10 years in total, 2 per row
130 monthOffset
: 6, // 12 months, 2 per row
134 * Fires when the cancel button is pressed.
135 * @param {Ext.picker.Month} this
140 * Fires when a month is clicked.
141 * @param {Ext.picker.Month} this
142 * @param {Array} value The current value
146 * @event monthdblclick
147 * Fires when a month is clicked.
148 * @param {Ext.picker.Month} this
149 * @param {Array} value The current value
154 * Fires when the ok button is pressed.
155 * @param {Ext.picker.Month} this
156 * @param {Array} value The current value
161 * Fires when a month/year is selected.
162 * @param {Ext.picker.Month} this
163 * @param {Array} value The current value
168 * Fires when a year is clicked.
169 * @param {Ext.picker.Month} this
170 * @param {Array} value The current value
174 * @event yeardblclick
175 * Fires when a year is clicked.
176 * @param {Ext.picker.Month} this
177 * @param {Array} value The current value
184 initComponent: function(){
187 me
.selectedCls
= me
.baseCls
+ '-selected';
190 me
.addCls(me
.smallCls
);
192 me
.setValue(me
.value
);
193 me
.activeYear
= me
.getYear(new Date().getFullYear() - 4, -4);
195 if (me
.showButtons
) {
196 me
.okBtn
= new Ext
.button
.Button({
197 ui
: me
.footerButtonUI
,
199 handler
: me
.onOkClick
,
202 me
.cancelBtn
= new Ext
.button
.Button({
203 ui
: me
.footerButtonUI
,
205 handler
: me
.onCancelClick
,
217 beforeRender: function(){
221 shortName
= Ext
.Date
.getShortMonthName
,
222 monthLen
= me
.monthOffset
,
223 margin
= me
.monthMargin
,
226 if (me
.padding
&& !me
.width
) {
232 for (; i
< monthLen
; ++i
) {
233 months
.push(shortName(i
), shortName(i
+ monthLen
));
236 if (Ext
.isDefined(margin
)) {
237 style
= 'margin: 0 ' + margin
+ 'px;';
240 Ext
.apply(me
.renderData
, {
242 years
: me
.getYears(),
243 showButtons
: me
.showButtons
,
248 cacheWidth: function() {
250 padding
= me
.parseBox(me
.padding
),
251 widthEl
= Ext
.getBody().createChild({
252 cls
: me
.baseCls
+ ' ' + me
.borderBoxCls
,
253 style
: 'position:absolute;top:-1000px;left:-1000px;',
254 html
: ' ' // required for opera 11.64 to measure a width
257 me
.self
.prototype.width
= widthEl
.getWidth() + padding
.left
+ padding
.right
;
265 afterRender: function(){
271 // Month picker is not focusable and essentially is pointer only thing.
272 // Clicking on it will focus the document body, which may disrupt the state
273 // of the floating parent such as Date picker or a menu, and cause it to hide.
274 // To work around that, we stop the mousedown events completely.
275 if (me
.up('[floating=true]')) {
276 me
.el
.on('mousedown', me
.onElClick
, me
);
279 me
.mon(body
, 'click', me
.onBodyClick
, me
);
280 me
.mon(body
, 'dblclick', me
.onBodyClick
, me
);
282 // keep a reference to the year/month elements since we'll be re-using them
283 me
.years
= body
.select('.' + me
.baseCls
+ '-year a');
284 me
.months
= body
.select('.' + me
.baseCls
+ '-month a');
286 me
.backRepeater
= new Ext
.util
.ClickRepeater(me
.prevEl
, {
287 handler
: Ext
.Function
.bind(me
.adjustYear
, me
, [-me
.totalYears
])
290 me
.prevEl
.addClsOnOver(me
.baseCls
+ '-yearnav-prev-over');
291 me
.nextRepeater
= new Ext
.util
.ClickRepeater(me
.nextEl
, {
292 handler
: Ext
.Function
.bind(me
.adjustYear
, me
, [me
.totalYears
])
294 me
.nextEl
.addClsOnOver(me
.baseCls
+ '-yearnav-next-over');
297 if (!Ext
.isDefined(me
.monthMargin
)) {
298 Ext
.picker
.Month
.prototype.monthMargin
= me
.calculateMonthMargin();
302 calculateMonthMargin: function(){
303 // We use this method for locales where the short month name
304 // may be longer than we see in English. For example in the
305 // zh_TW locale the month ends up spanning lines, so we loosen
306 // the margins to get some extra space
309 first
= months
.first(),
310 itemMargin
= first
.getMargin('l');
312 while (itemMargin
&& me
.getLargest() > me
.measureMaxHeight
) {
314 months
.setStyle('margin', '0 ' + itemMargin
+ 'px');
319 getLargest: function(months
){
321 this.months
.each(function(item
){
322 var h
= item
.getHeight();
332 * Set the value for the picker.
333 * @param {Date/Number[]} value The value to set. It can be a Date object, where the month/year will be extracted, or
334 * it can be an array, with the month as the first index and the year as the second.
335 * @return {Ext.picker.Month} this
337 setValue: function(value
){
339 active
= me
.activeYear
,
343 me
.value
= [null, null];
344 } else if (Ext
.isDate(value
)) {
345 me
.value
= [value
.getMonth(), value
.getFullYear()];
347 me
.value
= [value
[0], value
[1]];
353 if ((year
< active
|| year
> active
+ me
.yearOffset
)) {
354 me
.activeYear
= year
- me
.yearOffset
+ 1;
364 * Gets the selected value. It is returned as an array [month, year]. It may
365 * be a partial value, for example [null, 2010]. The month is returned as
367 * @return {Number[]} The selected value
369 getValue: function(){
374 * Checks whether the picker has a selection
375 * @return {Boolean} Returns true if both a month and year have been selected
377 hasSelection: function(){
378 var value
= this.value
;
379 return value
[0] !== null && value
[1] !== null;
383 * Get an array of years to be pushed in the template. It is not in strict
384 * numerical order because we want to show them in columns.
386 * @return {Number[]} An array of years
388 getYears: function(){
390 offset
= me
.yearOffset
,
391 start
= me
.activeYear
, // put the "active" year on the left
392 end
= start
+ offset
,
396 for (; i
< end
; ++i
) {
397 years
.push(i
, i
+ offset
);
404 * Update the years in the body based on any change
407 updateBody: function(){
411 yearNumbers
= me
.getYears(),
412 cls
= me
.selectedCls
,
413 value
= me
.getYear(null),
415 monthOffset
= me
.monthOffset
,
417 yearItems
, y
, yLen
, el
;
420 years
.removeCls(cls
);
421 months
.removeCls(cls
);
423 yearItems
= years
.elements
;
424 yLen
= yearItems
.length
;
426 for (y
= 0; y
< yLen
; y
++) {
427 el
= Ext
.fly(yearItems
[y
]);
429 year
= yearNumbers
[y
];
430 el
.dom
.innerHTML
= year
;
431 if (year
=== value
) {
435 if (month
!== null) {
436 if (month
< monthOffset
) {
439 month
= (month
- monthOffset
) * 2 + 1;
441 months
.item(month
).addCls(cls
);
447 * Gets the current year value, or the default.
449 * @param {Number} defaultValue The default value to use if the year is not defined.
450 * @param {Number} offset A number to offset the value by
451 * @return {Number} The year value
453 getYear: function(defaultValue
, offset
) {
454 var year
= this.value
[1];
455 offset
= offset
|| 0;
456 return year
=== null ? defaultValue
: year
+ offset
;
459 onElClick: function(e
) {
464 * React to clicks on the body
467 onBodyClick: function(e
, t
) {
469 isDouble
= e
.type
=== 'dblclick';
471 if (e
.getTarget('.' + me
.baseCls
+ '-month')) {
473 me
.onMonthClick(t
, isDouble
);
474 } else if (e
.getTarget('.' + me
.baseCls
+ '-year')) {
476 me
.onYearClick(t
, isDouble
);
481 * Modify the year display by passing an offset.
482 * @param {Number} [offset=10] The offset to move by.
484 adjustYear: function(offset
){
485 if (typeof offset
!== 'number') {
486 offset
= this.totalYears
;
488 this.activeYear
+= offset
;
493 * React to the ok button being pressed
496 onOkClick: function(){
497 this.fireEvent('okclick', this, this.value
);
501 * React to the cancel button being pressed
504 onCancelClick: function(){
505 this.fireEvent('cancelclick', this);
509 * React to a month being clicked
511 * @param {HTMLElement} target The element that was clicked
512 * @param {Boolean} isDouble True if the event was a doubleclick
514 onMonthClick: function(target
, isDouble
){
516 me
.value
[0] = me
.resolveOffset(me
.months
.indexOf(target
), me
.monthOffset
);
518 me
.fireEvent('month' + (isDouble
? 'dbl' : '') + 'click', me
, me
.value
);
519 me
.fireEvent('select', me
, me
.value
);
523 * React to a year being clicked
525 * @param {HTMLElement} target The element that was clicked
526 * @param {Boolean} isDouble True if the event was a doubleclick
528 onYearClick: function(target
, isDouble
){
530 me
.value
[1] = me
.activeYear
+ me
.resolveOffset(me
.years
.indexOf(target
), me
.yearOffset
);
532 me
.fireEvent('year' + (isDouble
? 'dbl' : '') + 'click', me
, me
.value
);
533 me
.fireEvent('select', me
, me
.value
);
538 * Returns an offsetted number based on the position in the collection. Since our collections aren't
539 * numerically ordered, this function helps to normalize those differences.
541 * @param {Object} index
542 * @param {Object} offset
543 * @return {Number} The correctly offsetted number
545 resolveOffset: function(index
, offset
){
546 if (index
% 2 === 0) {
549 return offset
+ Math
.floor(index
/ 2);
557 beforeDestroy: function(){
559 me
.years
= me
.months
= null;
560 Ext
.destroyMembers(me
, 'backRepeater', 'nextRepeater', 'okBtn', 'cancelBtn');
564 onDestroy: function() {
565 Ext
.destroyMembers(this, 'okBtn', 'cancelBtn');
570 // Do the job of a container layout at this point even though we are not a Container.
571 // TODO: Refactor as a Container.
572 finishRenderChildren: function () {
575 this.callParent(arguments
);
577 if (this.showButtons
) {
578 me
.okBtn
.finishRender();
579 me
.cancelBtn
.finishRender();