]>
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',
26 alias
: 'widget.monthpicker',
27 alternateClassName
: 'Ext.MonthPicker',
31 'Ext.util.ClickRepeater',
49 'bodyEl', 'prevEl', 'nextEl', 'monthEl', 'yearEl', 'buttons'
52 /* eslint-disable indent, max-len */
58 '<div id="{id}-bodyEl" data-ref="bodyEl" class="{baseCls}-body">',
59 '<div id="{id}-monthEl" data-ref="monthEl" class="{baseCls}-months">',
61 '<div class="{parent.baseCls}-item {parent.baseCls}-month">',
62 '<a style="{parent.monthStyle}" role="button" hidefocus="on" class="{parent.baseCls}-item-inner">{.}</a>',
66 '<div id="{id}-yearEl" data-ref="yearEl" class="{baseCls}-years">',
67 '<div class="{baseCls}-yearnav">',
68 '<div class="{baseCls}-yearnav-button-ct">',
69 '<a id="{id}-prevEl" data-ref="prevEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-prev" hidefocus="on" role="button"></a>',
71 '<div class="{baseCls}-yearnav-button-ct">',
72 '<a id="{id}-nextEl" data-ref="nextEl" class="{baseCls}-yearnav-button {baseCls}-yearnav-next" hidefocus="on" role="button"></a>',
76 '<div class="{parent.baseCls}-item {parent.baseCls}-year">',
77 '<a hidefocus="on" class="{parent.baseCls}-item-inner" role="button">{.}</a>',
81 '<div class="' + Ext
.baseCSSPrefix
+ 'clear"></div>',
82 '<tpl if="showButtons">',
83 '<div id="{id}-buttons" data-ref="buttons" class="{baseCls}-buttons">{%',
84 'var me=values.$comp, okBtn=me.okBtn, cancelBtn=me.cancelBtn;',
85 'okBtn.ownerLayout = cancelBtn.ownerLayout = me.componentLayout;',
86 'okBtn.ownerCt = cancelBtn.ownerCt = me;',
87 'Ext.DomHelper.generateMarkup(okBtn.getRenderTree(), out);',
88 'Ext.DomHelper.generateMarkup(cancelBtn.getRenderTree(), out);',
93 /* eslint-enable indent, max-len */
96 * @cfg {String} okText
97 * The text to display on the OK button.
103 * @cfg {String} cancelText
104 * The text to display on the Cancel button.
107 cancelText
: 'Cancel',
110 * @cfg {String} baseCls
111 * The base CSS class to apply to the picker element.
113 baseCls
: Ext
.baseCSSPrefix
+ 'monthpicker',
116 * @cfg {Boolean} showButtons
117 * True to show ok and cancel buttons below the picker.
122 * @property {String} selectedCls
123 * The class to be added to selected items in the picker.
128 * @cfg {Date/Number[]} value
129 * The default value to set. See {@link #setValue}
133 * @cfg {String} footerButtonUI
134 * The {@link Ext.button.Button#ui} to use for the month picker's footer buttons.
136 footerButtonUI
: 'default',
139 measureMaxHeight
: 20,
141 // used when attached to date picker which isnt showing buttons
142 smallCls
: Ext
.baseCSSPrefix
+ 'monthpicker-small',
148 yearOffset
: 5, // 10 years in total, 2 per row
149 monthOffset
: 6, // 12 months, 2 per row
155 alignOnScroll
: false,
159 * Fires when the cancel button is pressed.
160 * @param {Ext.picker.Month} this
165 * Fires when a month is clicked.
166 * @param {Ext.picker.Month} this
167 * @param {Array} value The current value
171 * @event monthdblclick
172 * Fires when a month is clicked.
173 * @param {Ext.picker.Month} this
174 * @param {Array} value The current value
179 * Fires when the ok button is pressed.
180 * @param {Ext.picker.Month} this
181 * @param {Array} value The current value
186 * Fires when a month/year is selected.
187 * @param {Ext.picker.Month} this
188 * @param {Array} value The current value
193 * Fires when a year is clicked.
194 * @param {Ext.picker.Month} this
195 * @param {Array} value The current value
199 * @event yeardblclick
200 * Fires when a year is clicked.
201 * @param {Ext.picker.Month} this
202 * @param {Array} value The current value
206 * @method initComponent
210 initComponent: function() {
213 me
.selectedCls
= me
.baseCls
+ '-selected';
216 me
.addCls(me
.smallCls
);
219 me
.setValue(me
.value
);
220 me
.activeYear
= me
.getYear(new Date().getFullYear() - 4, -4);
222 if (me
.showButtons
) {
223 me
.okBtn
= new Ext
.button
.Button({
224 ui
: me
.footerButtonUI
,
226 handler
: me
.onOkClick
,
229 me
.cancelBtn
= new Ext
.button
.Button({
230 ui
: me
.footerButtonUI
,
232 handler
: me
.onCancelClick
,
241 * @method beforeRender
245 beforeRender: function() {
249 shortName
= Ext
.Date
.getShortMonthName
,
250 monthLen
= me
.monthOffset
,
251 margin
= me
.monthMargin
,
254 if (me
.padding
&& !me
.width
) {
260 for (; i
< monthLen
; ++i
) {
261 months
.push(shortName(i
), shortName(i
+ monthLen
));
264 if (Ext
.isDefined(margin
)) {
265 style
= 'margin: 0 ' + margin
+ 'px;';
268 Ext
.apply(me
.renderData
, {
270 years
: me
.getYears(),
271 showButtons
: me
.showButtons
,
276 cacheWidth: function() {
278 padding
= me
.parseBox(me
.padding
),
279 widthEl
= Ext
.getBody().createChild({
280 cls
: me
.baseCls
+ ' ' + me
.borderBoxCls
,
281 style
: 'position:absolute;top:-1000px;left:-1000px;',
282 html
: ' ' // required for opera 11.64 to measure a width
285 me
.self
.prototype.width
= widthEl
.getWidth() + padding
.left
+ padding
.right
;
290 * @method afterRender
294 afterRender: function() {
300 // Month picker is not focusable and essentially is pointer only thing.
301 // Clicking on it will focus the document body, which may disrupt the state
302 // of the floating parent such as Date picker or a menu, and cause it to hide.
303 // To work around that, we stop the mousedown events completely.
304 if (me
.up('[floating=true]')) {
305 me
.el
.on('mousedown', me
.onElClick
, me
, { translate
: false });
310 click
: 'onBodyClick',
311 dblclick
: 'onBodyClick'
314 // keep a reference to the year/month elements since we'll be re-using them
315 me
.years
= body
.select('.' + me
.baseCls
+ '-year a');
316 me
.months
= body
.select('.' + me
.baseCls
+ '-month a');
318 me
.backRepeater
= new Ext
.util
.ClickRepeater(me
.prevEl
, {
319 handler
: Ext
.Function
.bind(me
.adjustYear
, me
, [-me
.totalYears
])
322 me
.prevEl
.addClsOnOver(me
.baseCls
+ '-yearnav-prev-over');
323 me
.nextRepeater
= new Ext
.util
.ClickRepeater(me
.nextEl
, {
324 handler
: Ext
.Function
.bind(me
.adjustYear
, me
, [me
.totalYears
])
326 me
.nextEl
.addClsOnOver(me
.baseCls
+ '-yearnav-next-over');
329 if (!Ext
.isDefined(me
.monthMargin
)) {
330 Ext
.picker
.Month
.prototype.monthMargin
= me
.calculateMonthMargin();
334 calculateMonthMargin: function() {
335 // We use this method for locales where the short month name
336 // may be longer than we see in English. For example in the
337 // zh_TW locale the month ends up spanning lines, so we loosen
338 // the margins to get some extra space
341 first
= months
.first(),
342 itemMargin
= first
.getMargin('l');
344 while (itemMargin
&& me
.getLargest() > me
.measureMaxHeight
) {
346 months
.setStyle('margin', '0 ' + itemMargin
+ 'px');
352 getLargest: function(months
) {
355 this.months
.each(function(item
) {
356 var h
= item
.getHeight();
368 * Set the value for the picker.
369 * @param {Date/Number[]} value The value to set. It can be a Date object,
370 * where the month/year will be extracted, or it can be an array, with the month
371 * as the first index and the year as the second.
372 * @return {Ext.picker.Month} this
374 setValue: function(value
) {
376 active
= me
.activeYear
,
380 me
.value
= [null, null];
382 else if (Ext
.isDate(value
)) {
383 me
.value
= [value
.getMonth(), value
.getFullYear()];
386 me
.value
= [value
[0], value
[1]];
393 if ((year
< active
|| year
> active
+ me
.yearOffset
)) {
394 me
.activeYear
= year
- me
.yearOffset
+ 1;
405 * Gets the selected value. It is returned as an array [month, year]. It may
406 * be a partial value, for example [null, 2010]. The month is returned as
408 * @return {Number[]} The selected value
410 getValue: function() {
415 * Checks whether the picker has a selection
416 * @return {Boolean} Returns true if both a month and year have been selected
418 hasSelection: function() {
419 var value
= this.value
;
421 return value
[0] !== null && value
[1] !== null;
425 * Get an array of years to be pushed in the template. It is not in strict
426 * numerical order because we want to show them in columns.
428 * @return {Number[]} An array of years
430 getYears: function() {
432 offset
= me
.yearOffset
,
433 start
= me
.activeYear
, // put the "active" year on the left
434 end
= start
+ offset
,
438 for (; i
< end
; ++i
) {
439 years
.push(i
, i
+ offset
);
446 * Update the years in the body based on any change
449 updateBody: function() {
453 yearNumbers
= me
.getYears(),
454 cls
= me
.selectedCls
,
455 value
= me
.getYear(null),
457 monthOffset
= me
.monthOffset
,
459 yearItems
, y
, yLen
, el
;
462 years
.removeCls(cls
);
463 months
.removeCls(cls
);
465 yearItems
= years
.elements
;
466 yLen
= yearItems
.length
;
468 for (y
= 0; y
< yLen
; y
++) {
469 el
= Ext
.fly(yearItems
[y
]);
471 year
= yearNumbers
[y
];
472 el
.dom
.innerHTML
= year
;
474 if (year
=== value
) {
479 if (month
!== null) {
480 if (month
< monthOffset
) {
484 month
= (month
- monthOffset
) * 2 + 1;
487 months
.item(month
).addCls(cls
);
493 * Gets the current year value, or the default.
495 * @param {Number} defaultValue The default value to use if the year is not defined.
496 * @param {Number} offset A number to offset the value by
497 * @return {Number} The year value
499 getYear: function(defaultValue
, offset
) {
500 var year
= this.value
[1];
502 offset
= offset
|| 0;
504 return year
=== null ? defaultValue
: year
+ offset
;
507 onElClick: function(e
) {
512 * React to clicks on the body
515 onBodyClick: function(e
, t
) {
517 isDouble
= e
.type
=== 'dblclick';
519 if (e
.getTarget('.' + me
.baseCls
+ '-month')) {
521 me
.onMonthClick(t
, isDouble
);
523 else if (e
.getTarget('.' + me
.baseCls
+ '-year')) {
525 me
.onYearClick(t
, isDouble
);
530 * Modify the year display by passing an offset.
531 * @param {Number} [offset=10] The offset to move by.
533 adjustYear: function(offset
) {
534 if (typeof offset
!== 'number') {
535 offset
= this.totalYears
;
538 this.activeYear
+= offset
;
543 * React to the ok button being pressed
546 onOkClick: function() {
547 this.fireEvent('okclick', this, this.value
);
551 * React to the cancel button being pressed
554 onCancelClick: function() {
555 this.fireEvent('cancelclick', this);
559 * React to a month being clicked
561 * @param {HTMLElement} target The element that was clicked
562 * @param {Boolean} isDouble True if the event was a doubleclick
564 onMonthClick: function(target
, isDouble
) {
567 me
.value
[0] = me
.resolveOffset(me
.months
.indexOf(target
), me
.monthOffset
);
569 me
.fireEvent('month' + (isDouble
? 'dbl' : '') + 'click', me
, me
.value
);
570 me
.fireEvent('select', me
, me
.value
);
574 * React to a year being clicked
576 * @param {HTMLElement} target The element that was clicked
577 * @param {Boolean} isDouble True if the event was a doubleclick
579 onYearClick: function(target
, isDouble
) {
582 me
.value
[1] = me
.activeYear
+ me
.resolveOffset(me
.years
.indexOf(target
), me
.yearOffset
);
584 me
.fireEvent('year' + (isDouble
? 'dbl' : '') + 'click', me
, me
.value
);
585 me
.fireEvent('select', me
, me
.value
);
590 * Returns an offsetted number based on the position in the collection.
591 * Since our collections aren't numerically ordered, this function helps
592 * to normalize those differences.
594 * @param {Object} index
595 * @param {Object} offset
596 * @return {Number} The correctly offsetted number
598 resolveOffset: function(index
, offset
) {
599 if (index
% 2 === 0) {
603 return offset
+ Math
.floor(index
/ 2);
607 doDestroy: function() {
608 Ext
.destroy(this.backRepeater
, this.nextRepeater
, this.okBtn
, this.cancelBtn
);
614 // Do the job of a container layout at this point even though we are not a Container.
615 // TODO: Refactor as a Container.
616 finishRenderChildren: function() {
619 this.callParent(arguments
);
621 if (this.showButtons
) {
622 me
.okBtn
.finishRender();
623 me
.cancelBtn
.finishRender();