new periodPicker form widget
authorCornelius Weiß <c.weiss@metaways.de>
Tue, 4 Jul 2017 22:10:05 +0000 (00:10 +0200)
committerCornelius Weiss <c.weiss@metaways.de>
Tue, 4 Jul 2017 22:15:58 +0000 (00:15 +0200)
Change-Id: Ia2b55f83f33cc34dc29159d23e00990b86339b13
Reviewed-on: http://gerrit.tine20.com/customers/5015
Reviewed-by: Cornelius Weiss <c.weiss@metaways.de>
Tested-by: Cornelius Weiss <c.weiss@metaways.de>
tine20/Tinebase/Tinebase.jsb2
tine20/Tinebase/css/ux/form/PeriodPicker.css [new file with mode: 0644]
tine20/Tinebase/js/ux/form/PeriodPicker.js [new file with mode: 0644]

index 61fba0c..b118051 100644 (file)
           "path": "js/ux/form/"
         },
         {
+          "text": "PeriodPicker.js",
+          "path": "js/ux/form/"
+        },
+        {
           "text": "MoneyField.js",
           "path": "js/ux/form/"
         },
           "path": "css/ux/form/"
         },
         {
+          "text": "PeriodPicker.css",
+          "path": "css/ux/form/"
+        },
+        {
           "text": "DisplayPanel.css",
           "path": "css/ux/display/"
         },
diff --git a/tine20/Tinebase/css/ux/form/PeriodPicker.css b/tine20/Tinebase/css/ux/form/PeriodPicker.css
new file mode 100644 (file)
index 0000000..03ab476
--- /dev/null
@@ -0,0 +1,32 @@
+.ux-pp-field {
+    widht: 100%;
+}
+
+.ux-pp-range {
+    width: 40%;
+}
+
+.ux-pp-period {
+    width: 60%;
+    text-align: center;
+    cursor: pointer;
+}
+
+.ux-pp-prev {
+    background-position: 0 -105px;
+}
+
+.ux-pp-next {
+    background-position: 0 -120px;
+}
+
+.ux-pp-prev, .ux-pp-next {
+    background-image: url(../../../../themes/tine20/resources/images/tine20/panel/tool-sprites.gif);
+    overflow: hidden;
+    width: 15px;
+    height: 15px;
+    cursor: pointer;
+    display: block;
+    margin: 2px;
+    opacity: 0.6;
+}
\ No newline at end of file
diff --git a/tine20/Tinebase/js/ux/form/PeriodPicker.js b/tine20/Tinebase/js/ux/form/PeriodPicker.js
new file mode 100644 (file)
index 0000000..065cb01
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Tine 2.0
+ *
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Cornelius Weiss <c.weiss@metaways.de>
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+
+/*global Ext*/
+
+Ext.ns('Ext.ux', 'Ext.ux.form');
+
+/**
+ * A combination range and paging control
+ *
+ * @namespace   Ext.ux.form
+ * @class       Ext.ux.form.PeriodPicker
+ * @extends     Ext.form.Field
+ */
+Ext.ux.form.PeriodPicker = Ext.extend(Ext.form.Field, {
+
+    /**
+     * @cfg {String} availableRanges
+     * ranges available in range picker
+     * _('Day'), _('Week'), _('Month'), _('Year')
+     */
+    availableRanges: 'day,week,month',
+
+    /**
+     * @cfg {String} range
+     * initial range
+     */
+    range: 'month',
+
+    /**
+     * @cfg {Date} startDate
+     * defaults to toDay, will be constraint to period, gets overwritten by value
+     */
+    startDate: null,
+
+    defaultAutoCreate: {
+        tag: 'table',
+        cls: 'ux-pp-field',
+        cn: [{
+            tag: 'tr',
+            cn: [{
+                tag: 'td',
+                cls: 'ux-pp-range'
+            }, {
+                tag: 'td',
+                cls: 'ux-pp-prev'
+            }, {
+                tag: 'td',
+                cls: 'ux-pp-period'
+            }, {
+                tag: 'td',
+                cls: 'ux-pp-next'
+            }]
+        }]
+    },
+
+    initComponent: function() {
+        Ext.ux.form.PeriodPicker.superclass.initComponent.call(this);
+
+        this.value = Ext.ux.form.PeriodPicker.getPeriod(this.value ? this.value.from : this.startDate || new Date(), this.range);
+        this.startDate = this.value.from;
+    },
+
+    /**
+     * @return {Object} {from: Date, until: Date}
+     */
+    getValue: function() {
+        return this.value;
+    },
+
+    /**
+     * @param {Object} value {from: Date, until: Date}
+     */
+    setValue: function(value) {
+        this.range = Ext.ux.form.PeriodPicker.getRange(value);
+        this.value = Ext.ux.form.PeriodPicker.getPeriod(value.from, this.range);
+        this.startDate = this.value.from;
+
+        this.getRangeCombo().setValue(this.range);
+        var dateString;
+
+        switch(this.range) {
+            case 'day':
+                dateString = Tine.Tinebase.common.dateRenderer(this.startDate);
+                break;
+            case 'week':
+                // NOTE: '+1' is to ensure we display the ISO8601 based week where weeks always start on monday!
+                var wkStart = this.startDate.add(Date.DAY, this.startDate.getDay() < 1 ? 1 : 0);
+
+                dateString = wkStart.getWeekOfYear() + ' ' + this.startDate.format('Y');
+                break;
+            case 'month':
+                dateString = Ext.DatePicker.prototype.monthNames[this.startDate.getMonth()] + ' ' + this.startDate.format('Y');
+                break;
+            case 'year':
+                dateString = this.startDate.format('Y');
+                break;
+        }
+        this.setPeriodText(dateString);
+
+    },
+
+    setStartDate: function(startDate) {
+        var value = Ext.ux.form.PeriodPicker.getPeriod(startDate, this.range);
+        this.setValue(value);
+    },
+
+    setPeriodText: function(text) {
+        this.el.child('.ux-pp-period').update(Ext.util.Format.htmlEncode(text));
+    },
+
+    // private
+    onRangeComboChange: function() {
+        this.setValue(Ext.ux.form.PeriodPicker.getPeriod(this.startDate, this.getRangeCombo().getValue()));
+        this.fireEvent('change', this, this.value, this.startValue);
+    },
+
+    // private
+    onClick: function(e) {
+        var prev = e.getTarget('.ux-pp-prev'),
+            next = e.getTarget('.ux-pp-next'),
+            period = e.getTarget('.ux-pp-period');
+
+        if (next) {
+            this.setStartDate(this.value.until.add(Date.DAY, 1));
+            this.fireEvent('change', this, this.value, this.startValue);
+        } else if (prev) {
+            this.setStartDate(this.value.from.add(Date.DAY, -1));
+            this.fireEvent('change', this, this.value, this.startValue);
+        } else if (period) {
+            this.getDatePickerMenu().show(period);
+        }
+    },
+
+    // private
+    onRender : function(ct, position){
+        this.doc = Ext.isIE ? Ext.getBody() : Ext.getDoc();
+        Ext.ux.form.PeriodPicker.superclass.onRender.call(this, ct, position);
+
+        var rangeCombo = this.getRangeCombo();
+        rangeCombo.render(this.el.child('.ux-pp-range'));
+        rangeCombo.setWidth(this.getEl().getWidth() * 0.4);
+
+        this.setValue(this.value);
+        this.mon(this.getEl(), 'click', this.onClick, this);
+    },
+
+    // private
+    onResize: function (w, h) {
+        Ext.ux.form.PeriodPicker.superclass.onResize.apply(this, arguments);
+
+        this.getRangeCombo().setWidth(this.getEl().getWidth() * 0.4);
+    },
+
+    getRangeCombo: function() {
+        if (! this.rangeCombo) {
+            var fieldDef = [];
+            Ext.each(this.availableRanges.split(','), function(range) {
+                fieldDef.push([range, i18n._hidden(Ext.util.Format.capitalize(range))]);
+            });
+
+            this.rangeCombo = new Ext.form.ComboBox({
+                typeAhead: true,
+                triggerAction: 'all',
+                mode: 'local',
+                forceSelection: true,
+                allowEmpty: false,
+                editable: false,
+                store: fieldDef,
+                // value: this.range,
+                listeners: {
+                    scope: this,
+                    change: this.onRangeComboChange,
+                    select: this.onRangeComboChange
+                }
+            });
+        }
+
+        return this.rangeCombo;
+    },
+
+    /**
+     * returns a new datepickerMenu
+     *
+     * @returns {Ext.menu.DateMenu}
+     */
+    getDatePickerMenu: function() {
+            var me = this;
+
+            return new Ext.menu.DateMenu({
+                value: this.startDate,
+                hideOnClick: true,
+                focusOnSelect: true,
+                plugins: [new Ext.ux.DatePickerWeekPlugin({
+                    weekHeaderString: Tine.Tinebase.appMgr.get('Calendar').i18n._hidden('WK'),
+                    inspectMonthPickerClick: function(btn, e) {
+                        if (e.getTarget('button')) {
+                            me.getRangeCombo().setValue('month');
+                            me.range = 'month';
+                            me.setStartDate(this.activeDate);
+                            this.destroy();
+                            me.fireEvent('change', me, me.value, me.startValue);
+                            return false;
+                        }
+                    }
+                })],
+                listeners: {
+                    scope: this,
+                    select: function(picker, value, weekNumber) {
+                        this.getRangeCombo().setValue(weekNumber ? 'week' : 'day');
+                        this.range = weekNumber ? 'week' : 'day';
+                        this.setStartDate(value);
+                        this.fireEvent('change', this, this.value, this.startValue);
+                    }
+                }
+            });
+    }
+});
+
+/**
+ * gets period
+ *
+ * @static
+ * @param {Date} startDate date within period
+ * @param {String} range day|week|month|year
+ * @return {Object} {from: Date, until: Date}
+ */
+Ext.ux.form.PeriodPicker.getPeriod = function(startDate, range) {
+    var from, until;
+    switch(range) {
+        case 'day':
+            from = startDate.clearTime(true);
+            until = from.add(Date.DAY, 1);
+            break;
+        case 'week':
+            from = startDate.clearTime(true).add(Date.DAY, -1 * startDate.getDay())
+                .add(Date.DAY, Ext.DatePicker.prototype.startDay - (startDate.getDay() == 0 ? 7 : 0));
+            until = from.add(Date.DAY, 7);
+            break;
+        case 'month':
+            from = startDate.clearTime(true).getFirstDateOfMonth();
+            until = from.getLastDateOfMonth().add(Date.DAY, 1);
+            break;
+        case 'year':
+            var year = this.startDate.format('y');
+            from = Date.parse(year + '-0-0 00:00:00', 'Y-m-d H:i:s');
+            until = Date.parse(++year + '-0-0 00:00:00', 'Y-m-d H:i:s');
+            break;
+    }
+
+    return {from: from, until: until};
+};
+
+/**
+ * gets period
+ *
+ * @static
+ * @param {Object} {from: Date, until: Date}
+ * @return {String} range day|week|month|year
+ */
+Ext.ux.form.PeriodPicker.getRange = function(period) {
+    var ms = period.from.getElapsed(period.until),
+        msDay = 86400000;
+
+    if (ms > msDay * 300) {
+        return 'year';
+    } else if (ms > msDay * 20) {
+        return 'month';
+    } else if (ms > msDay * 5) {
+        return 'week';
+    } else {
+        return 'day'
+    }
+
+}
+Ext.reg('ux-period-picker', Ext.ux.form.PeriodPicker)
\ No newline at end of file