1. #1
    Sencha User
    Join Date
    Oct 2009
    Posts
    23
    Vote Rating
    2
    ivanleblanc is on a distinguished road

      0  

    Default Ext.ux.form.field.DateRange

    Ext.ux.form.field.DateRange


    This is a form field that has a Date Picker that is always visible allowing you to select multiple dates.
    It is compatible for Ext 4.1
    Its a basic Date Picker with a few extra buttons added.
    "C" will clear all selected dates
    "U" will undo each last action from the last initial setting of the dates. (it keeps an array of undo actions)
    "R" will reset the selected dates to the initial dates that were loaded

    Schedule.png

    Code:
    Ext.define('Ext.ux.picker.DateRange', {
        extend: 'Ext.picker.Date',
        
        undos: [],
        ariaTitle: '',
    
    
        renderTpl: [
            '<div id="{id}-innerEl" role="grid">',
                '<div role="presentation" class="{baseCls}-header">',
                    '<div class="{baseCls}-prev"><a id="{id}-prevEl" href="#" role="button" title="{prevText}"></a></div>',
                    '<div class="{baseCls}-month" id="{id}-middleBtnEl">{%this.renderMonthBtn(values, out)%}</div>',
                    '<div class="{baseCls}-next"><a id="{id}-nextEl" href="#" role="button" title="{nextText}"></a></div>',
                '</div>',
                '<table id="{id}-eventEl" class="{baseCls}-inner" cellspacing="0" role="presentation">',
                    '<thead role="presentation"><tr role="presentation">',
                        '<tpl for="dayNames">',
                            '<th role="columnheader" title="{.}"><span>{.:this.firstInitial}</span></th>',
                        '</tpl>',
                    '</tr></thead>',
                    '<tbody role="presentation"><tr role="presentation">',
                        '<tpl for="days">',
                            '{#:this.isEndOfWeek}',
                            '<td role="gridcell" id="{[Ext.id()]}">',
                                '<a role="presentation" href="#" hidefocus="on" class="{parent.baseCls}-date" tabIndex="1">',
                                    '<em role="presentation"><span role="presentation"></span></em>',
                                '</a>',
                            '</td>',
                        '</tpl>',
                    '</tr></tbody>',
                '</table>',
                    '<div id="{id}-footerEl" role="presentation" class="{baseCls}-footer">',
                        '<tpl if="showToday">{%this.renderTodayBtn(values, out)%}</tpl>',
                        '<tpl if="showClear">{%this.renderClearBtn(values, out)%}</tpl>',
                        '<tpl if="showUndo">{%this.renderUndoBtn(values, out)%}</tpl>',
                        '<tpl if="showReset">{%this.renderResetBtn(values, out)%}</tpl>',
                    '</div>',
            '</div>',
            {
                firstInitial: function(value) {
                    return Ext.picker.Date.prototype.getDayInitial(value);
                },
                isEndOfWeek: function(value) {
                    // convert from 1 based index to 0 based
                    // by decrementing value once.
                    value--;
                    var end = value % 7 === 0 && value !== 0;
                    return end ? '</tr><tr role="row">' : '';
                },
                renderTodayBtn: function(values, out) {
                    Ext.DomHelper.generateMarkup(values.$comp.todayBtn.getRenderTree(), out);
                },
                renderClearBtn: function(values, out) {
                    Ext.DomHelper.generateMarkup(values.$comp.clearBtn.getRenderTree(), out);
                },
                renderUndoBtn: function(values, out) {
                    Ext.DomHelper.generateMarkup(values.$comp.undoBtn.getRenderTree(), out);
                },
                renderResetBtn: function(values, out) {
                    Ext.DomHelper.generateMarkup(values.$comp.resetBtn.getRenderTree(), out);
                },
                renderMonthBtn: function(values, out) {
                    Ext.DomHelper.generateMarkup(values.$comp.monthBtn.getRenderTree(), out);
                }
            }
        ],
    
    
        beforeRender: function() {
            var me = this;
            
            
            if(me.showClear) {
                me.clearBtn = Ext.create('Ext.button.Button', {
                    ownerCt: me,
                    ownerLayout: me.getComponentLayout(),
                    text: me.clearText,
                    tooltip: 'Clear all selected Dates',
                    handler: me.selectClear,
                    scope: me
                });
            }
            if(me.showUndo) {
                me.undoBtn = Ext.create('Ext.button.Button', {
                    ownerCt: me,
                    ownerLayout: me.getComponentLayout(),
                    text: me.undoText,
                    tooltip: 'Undo last action',
                    handler: me.onUndo,
                    scope: me
                });
            }
            if(me.showReset) {
                me.resetBtn = Ext.create('Ext.button.Button', {
                    ownerCt: me,
                    ownerLayout: me.getComponentLayout(),
                    text: me.resetText,
                    tooltip: 'Reset Dates',
                    handler: me.onReset,
                    scope: me
                });
            }
            
            me.callParent();
            
            Ext.apply(me.renderData, {
                showClear: me.showClear,
                showUndo: me.showUndo,
                showReset: me.showReset
            });
        },
        
        finishRenderChildren: function() {
            var me = this;
            me.callParent();
            if (me.showClear) {
                me.clearBtn.finishRender();
            }
            if (me.showUndo) {
                me.undoBtn.finishRender();
            }
            if (me.showReset) {
                me.resetBtn.finishRender();
            }
        },
        
        selectClear: function() {
            var me = this,
                sd = me.selectedDates;
                
            me.undos.push(Ext.Array.clone(sd));
            Ext.Array.erase(sd,0,sd.length);
            me.setValue('');
            me.fireEvent('select', me, me.value);
            me.refresh();
        },
        
        onUndo: function() {
            var me = this;
            me.selectedDates = Ext.Array.clone(me.undos.pop());
            me.refresh();
        },
        
        onReset: function() {
            var me = this;
            me.selectedDates = Ext.Array.clone(me.initialDates);
            me.refresh();
        },
        
        refresh: function() {
            var me = this,
                cells = me.cells,
                cls = me.selectedCls,
                sd = me.selectedDates;
                    
            if(me.rendered) {
                cells.removeCls(cls);
                cells.each(function(c){
                    if (Ext.Array.contains(sd, c.dom.firstChild.dateValue)) {
                        me.el.dom.setAttribute('aria-activedescendent', c.dom.id);
                        c.addCls(cls);
                    }
                }, this);
                me.undoBtn.setDisabled((me.undos.length < 1));
            }
        },
        
        selectedUpdate: function(date, active){
            var me = this,
                t = (date) ? date.getTime() : null,
                cells = me.cells,
                cls = me.selectedCls,
                sd = me.selectedDates;
            
            // Push an undo set of Dates
            me.undos.push(Ext.Array.clone(sd));
            
            if(Ext.Array.contains(sd, t))
                Ext.Array.remove(sd, t);
            else
                sd.push(t);
            
            Ext.Array.sort(sd);
            me.refresh();
        },
        
        fullUpdate: function(date, active){
            var me = this,
                cells = me.cells.elements,
                textNodes = me.textNodes,
                disabledCls = me.disabledCellCls,
                eDate = Ext.Date,
                i = 0,
                extraDays = 0,
                visible = me.isVisible(),
                sel = +eDate.clearTime(date, true),
                today = +eDate.clearTime(new Date()),
                min = me.minDate ? eDate.clearTime(me.minDate, true) : Number.NEGATIVE_INFINITY,
                max = me.maxDate ? eDate.clearTime(me.maxDate, true) : Number.POSITIVE_INFINITY,
                ddMatch = me.disabledDatesRE,
                ddText = me.disabledDatesText,
                ddays = me.disabledDays ? me.disabledDays.join('') : false,
                ddaysText = me.disabledDaysText,
                format = me.format,
                days = eDate.getDaysInMonth(date),
                firstOfMonth = eDate.getFirstDateOfMonth(date),
                startingPos = firstOfMonth.getDay() - me.startDay,
                previousMonth = eDate.add(date, eDate.MONTH, -1),
                longDayFormat = me.longDayFormat,
                prevStart,
                current,
                disableToday,
                tempDate,
                setCellClass,
                html,
                cls,
                formatValue,
                value,
                sd = me.selectedDates;
    
    
            if (startingPos < 0) {
                startingPos += 7;
            }
    
    
            days += startingPos;
            prevStart = eDate.getDaysInMonth(previousMonth) - startingPos;
            current = new Date(previousMonth.getFullYear(), previousMonth.getMonth(), prevStart, me.initHour);
    
    
            if (me.showToday) {
                tempDate = eDate.clearTime(new Date());
                disableToday = (tempDate < min || tempDate > max ||
                    (ddMatch && format && ddMatch.test(eDate.dateFormat(tempDate, format))) ||
                    (ddays && ddays.indexOf(tempDate.getDay()) != -1));
    
    
                if (!me.disabled) {
                    me.todayBtn.setDisabled(disableToday);
                    me.todayKeyListener.setDisabled(disableToday);
                }
            }
    
    
            setCellClass = function(cell){
                value = +eDate.clearTime(current, true);
                cell.title = eDate.format(current, longDayFormat);
                // store dateValue number as an expando
                cell.firstChild.dateValue = value;
                if(value == today){
                    cell.className += ' ' + me.todayCls;
                    cell.title = me.todayText;
                }
                // Code I added
                // Stop the refresh from selecting the active date in the current month
                // And select the dates that are in the selectedDates array
                if(Ext.Array.contains(sd, value)) {
                    cell.className += ' ' + me.selectedCls;
                    me.el.dom.setAttribute('aria-activedescendant', cell.id);
                    if (visible && me.floating) {
                        Ext.fly(cell.firstChild).focus(50);
                    }
                }
                // disabling
                if(value < min) {
                    cell.className = disabledCls;
                    cell.title = me.minText;
                    return;
                }
                if(value > max) {
                    cell.className = disabledCls;
                    cell.title = me.maxText;
                    return;
                }
                if(ddays){
                    if(ddays.indexOf(current.getDay()) != -1){
                        cell.title = ddaysText;
                        cell.className = disabledCls;
                    }
                }
                if(ddMatch && format){
                    formatValue = eDate.dateFormat(current, format);
                    if(ddMatch.test(formatValue)){
                        cell.title = ddText.replace('%0', formatValue);
                        cell.className = disabledCls;
                    }
                }
            };
    
    
            for(; i < me.numDays; ++i) {
                if (i < startingPos) {
                    html = (++prevStart);
                    cls = me.prevCls;
                } else if (i >= days) {
                    html = (++extraDays);
                    cls = me.nextCls;
                } else {
                    html = i - startingPos + 1;
                    cls = me.activeCls;
                }
                textNodes[i].innerHTML = html;
                cells[i].className = cls;
                current.setDate(current.getDate() + 1);
                setCellClass(cells[i]);
            }
    
    
            me.monthBtn.setText(me.monthNames[date.getMonth()] + ' ' + date.getFullYear());
        },
        
        setValue : function(value){        
            var me = this;
            if(value == '') {
                this.selectedDates = new Array();
                this.refresh();
            }
            // Should only get in here on new server data, all other calls should be passing a single Ext.Date paramaeter
            else if(!Ext.isDate(value)) {
                var sd = value.split(this.delimiter),
                    dt;
                Ext.each(sd, function(raw, index) {
                    dt = Ext.Date.parse(raw, me.dateFormat);
                    if(Ext.isDate(dt))
                        sd[index] = dt.getTime();
                });
                this.selectedDates = sd;
                this.initialDates = Ext.Array.clone(sd);
                this.refresh();
            }
            else
                this.callParent(arguments);
        },
    
    
        getValue : function(asObj){
            var me = this;
                sd = me.selectedDates;
            
            if(sd.length > 1)
                return me.getValues(asObj);
                
            return this.value;
        },
        
        getValues : function(asObj){
            var me = this,
                sd = me.selectedDates,
                asObj = asObj || false,
                dates = [];
            
            if(asObj) {
                Ext.each(sd, function(date) {
                    dates.push(new Date(date));
                });
                return dates;
            }
            return sd;
        }
        
    });
    
    
    Ext.define('Ext.ux.form.field.DateRange', {
        extend: 'Ext.form.field.Base',
        alias: 'widget.daterangefield',
        requires: ['Ext.ux.picker.DateRange'],
            
        inputType: 'hidden',
        
        // The format of the dates coming in as a delimited set
        // Ex. "2012-01-01,2012-01-02"
        // This is also the format that will be used to on submits and saves
        dateFormat: 'Y-m-d',
        
        // Picker Config
        showToday: true,
        showClear: true,
        showUndo: true,
        showReset: true,
        clearText: 'C',
        undoText: 'U',
        resetText: 'R',
        delimiter: ',',    
        
        onRender: function() {
            var me = this;
            
            me.callParent(arguments);
            me.picker = me.createPicker();
        },
        
        createPicker: function() {
            var me = this;
            return Ext.create('Ext.ux.picker.DateRange', {
                renderTo: me.bodyEl,
                showToday: me.showToday,
                showClear: me.showClear,
                showUndo: me.showUndo,
                showReset: me.showReset,
                clearText: me.clearText,
                undoText: me.undoText,
                resetText: me.resetText,
                selectedDates: [],
                delimiter: me.delimiter,
                dateFormat: me.dateFormat,
                listeners: {
                    scope: me,
                    select: me.onSelect
                }
            });
        },
        
        onSelect: function(m,d) {
            var me = this,
                sd = m.selectedDates;
            
            me.setRawValue(me.revertData(sd));
            me.fireEvent('select', me, d);
        },
            
        setValue: function(value) {
            this.callParent(arguments);
            
            var me = this;
    
    
            if(me.picker)
                me.picker.setValue(value);
        },
        
        revertData: function(sd) {
            var me = this,
                arr = [];
            for(var i=0; i<sd.length; i++) {
                arr[i] = Ext.Date.format(new Date(sd[i]), me.dateFormat);
            }
            return arr.join(me.delimiter);
        },
        
        enable: function(silent) {
            this.callParent(arguments);
            this.picker.el.unmask();
        },
        
        disable: function(silent) {
            this.callParent(arguments);
            this.picker.el.mask();
        }
        
    })
    Example of how to use:
    Code:
    var dateRange = Ext.widget('daterangefield', {
        fieldLabel: 'Date Range',
        name: 'dateRange',
        dateFormat: 'Y-m-d',
        delimiter: '|'
    });
    In your model you would simple have a string field named "dateRange"

    Your data from the server or where ever your data is coming from would look like this:
    depending on the config setting of delimiter and dateFormat
    Code:
    { "success": true, "data": [{ "dateRange": "2012-01-01|2012-01-02" }] }

  2. #2
    Sencha - Support Team scottmartin's Avatar
    Join Date
    Jul 2010
    Location
    Houston, Tx
    Posts
    9,069
    Vote Rating
    467
    scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future

      0  

    Default


    Thank you for the contribution.

    Regards,
    Scott.

  3. #3
    Sencha User relson's Avatar
    Join Date
    Dec 2011
    Location
    São Paulo, SP, Brasil
    Posts
    10
    Vote Rating
    1
    relson is on a distinguished road

      0  

    Default


    ivanleblanc. Do you have github?

  4. #4
    Sencha User
    Join Date
    Oct 2009
    Posts
    23
    Vote Rating
    2
    ivanleblanc is on a distinguished road

      0  

    Default


    Quote Originally Posted by relson View Post
    ivanleblanc. Do you have github?
    No. I probably should look into it. I see that it how most people share their code.

Thread Participants: 2