1. #1
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    92
    Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of Condor has much to be proud of

      0  

    Default Themed checkboxes/radios for Ext 3.0

    Themed checkboxes/radios for Ext 3.0


    Just as we were getting used to the new themed checkboxes/radios in Ext 2.2+, Jack removed them from Ext 3.0.

    Because Ext 2.2+ has all kinds of problems with checkboxes and radios I decided to start from scratch and write all-new themed checkbox/radio support for Ext 3.0:

    Include the following code after loading ext-all.js:
    Code:
    Ext.override(Ext.form.Checkbox, {
    	checked: undefined,
    	actionMode: 'wrap',
    	innerCls: 'x-form-checkbox-inner',
    	onResize: function(){
    		Ext.form.Checkbox.superclass.onResize.apply(this, arguments);
    		if(!this.boxLabel){
    			this.innerWrap.alignTo(this.wrap, 'c-c');
    		}
    	},
    	initEvents: function(){
    		Ext.form.Checkbox.superclass.initEvents.call(this);
    		this.mon(this.el, {
    			click: this.onClick,
    			change: this.onClick,
    			mouseenter: this.onMouseEnter,
    			mouseleave: this.onMouseLeave,
    			mousedown: this.onMouseDown,
    			mouseup: this.onMouseUp,
    			scope: this
    		});
    	},
    	onRender: function(ct, position){
    		Ext.form.Checkbox.superclass.onRender.call(this, ct, position);
    		if(this.inputValue !== undefined){
    			this.el.dom.value = this.inputValue;
    		}else{
    			this.inputValue = this.el.dom.value;
    		}
    		this.innerWrap = this.el.wrap({
    			cls: this.innerCls
    		});
    		this.wrap = this.innerWrap.wrap({
    			cls: 'x-form-check-wrap'
    		});
    		if(this.boxLabel){
    			this.labelEl = this.wrap.createChild({
    				tag: 'label',
    				htmlFor: this.el.id,
    				cls: 'x-form-cb-label',
    				html: this.boxLabel
    			});
    		}else{
    			this.innerWrap.addClass('x-form-check-no-label');
    		}
    	},
    	initValue: function(){
    		if(this.checked !== undefined){
    			this.setValue(this.checked);
    		}else{
    			if(this.value !== undefined){
    				this.setValue(this.value);
    			}
    			this.checked = this.el.dom.checked;
    		}
    		this.originalValue = this.getValue();
    	},
    	getRawValue: function(){
    		return this.rendered ? this.el.dom.checked : this.checked;
    	},
    	getValue: function(){
    		return this.getRawValue() ? this.inputValue : undefined;
    	},
    	onClick: function(){
    		if(Ext.isSafari){
    			this.focus();
    		}
    		if(this.el.dom.checked != this.checked){
    			this.setValue(this.el.dom.checked);
    		}
    	},
    	setValue: function(v){
    		this.checked = typeof v == 'boolean' ? v : v == this.inputValue;
    		if(this.rendered){
    			this.el.dom.checked = this.checked;
    			this.el.dom.defaultChecked = this.checked;
    			this.innerWrap[this.checked ? 'addClass' : 'removeClass']('x-form-check-checked');
    			this.validate();
    		}
    		this.fireEvent('check', this, this.checked);
    		return this;
    	},
    	onMouseEnter: function(){
    		this.wrap.addClass('x-form-check-over');
    	},
    	onMouseLeave: function(){
    		this.wrap.removeClass('x-form-check-over');
    	},
    	onMouseDown: function(){
    		this.wrap.addClass('x-form-check-down');
    	},
    	onMouseUp: function(){
    		this.wrap.removeClass('x-form-check-down');
    	},
    	onFocus: function(){
    		Ext.form.Checkbox.superclass.onFocus.call(this);
    		this.wrap.addClass('x-form-check-focus');
    	},
    	onBlur: function(){
    		Ext.form.Checkbox.superclass.onBlur.call(this);
    		this.wrap.removeClass('x-form-check-focus');
    	}
    });
    Ext.override(Ext.form.Radio, {
    	innerCls: 'x-form-radio-inner',
    	onClick: Ext.form.Radio.superclass.onClick,
    	setValue: function(v){
    		Ext.form.Radio.superclass.setValue.call(this, v);
    		if(this.rendered && this.checked){
    			var p = this.el.up('form') || Ext.getBody(),
    				els = p.select('input[name=' + this.el.dom.name + ']'),
    				id = this.el.dom.id;
    			els.each(function(el){
    				if(el.dom.id != id){
    					Ext.getCmp(el.dom.id).setValue(false);
    				}
    			});
    		}
    		return this;
    	}
    });
    And include the following stylesheet:
    (don't forget to adjust the image paths to your own file locations)
    Code:
    .x-form-check-wrap {
    	position: relative;
    	padding: 3px 0;
    	height: auto;
    	line-height: 14px;
    }
    .x-form-check-wrap label.x-form-cb-label {
    	margin-left: 17px;
    	padding: 0 3px 0 0;
    }
    .x-form-checkbox, .x-form-radio {
    	width: 13px;
    	height: 13px;
    	-moz-opacity: 0;
    	opacity: 0;
    	-ms-filter: 'alpha(opacity=0)';
    	filter: alpha(opacity=0);
    }
    .ext-ie .x-form-check-wrap input {
    	width: 13px;
    	height: 13px;
    }
    .x-form-checkbox-inner, .x-form-radio-inner {
    	position: absolute;
    	left: 0px;
    	top: 4px;
    	width: 13px;
    	height: 13px;
    }
    .x-form-check-no-label {
    	position: relative;
    }
    .x-form-checkbox-inner {
    	background: url('../images/default/form/checkbox.gif') no-repeat 0 0;
    }
    .x-form-radio-inner {
    	background: url('../images/default/form/radio.gif') no-repeat 0 0;
    }
    .x-form-check-focus .x-form-checkbox-inner, .x-form-check-over .x-form-checkbox-inner,
    .x-form-check-focus .x-form-radio-inner, .x-form-check-over .x-form-radio-inner {
    	background-position: -13px 0;
    }
    .x-form-check-down .x-form-checkbox-inner,
    .x-form-check-down .x-form-radio-inner {
    	background-position: -26px 0;
    }
    .x-form-check-checked {
    	background-position: 0 -13px;
    }
    .x-form-check-focus .x-form-check-checked, .x-form-check-over .x-form-check-checked {
    	background-position: -13px -13px;
    }
    .x-form-check-down .x-form-check-checked {
    	background-position: -26px -13px;
    }
    If you also want validation support you need the following code:
    Code:
    Ext.override(Ext.form.Field, {
    	markEl: 'el',
    	markInvalid: function(msg){
    		if(!this.rendered || this.preventMark){
    			return;
    		}
    		msg = msg || this.invalidText;
    		var mt = this.getMessageHandler();
    		if(mt){
    			mt.mark(this, msg);
    		}else if(this.msgTarget){
    			this[this.markEl].addClass(this.invalidClass);
    			var t = Ext.getDom(this.msgTarget);
    			if(t){
    				t.innerHTML = msg;
    				t.style.display = this.msgDisplay;
    			}
    		}
    		this.fireEvent('invalid', this, msg);
    	},
    	clearInvalid : function(){
    		if(!this.rendered || this.preventMark){
    			return;
    		}
    		var mt = this.getMessageHandler();
    		if(mt){
    			mt.clear(this);
    		}else if(this.msgTarget){
    			this[this.markEl].removeClass(this.invalidClass);
    			var t = Ext.getDom(this.msgTarget);
    			if(t){
    				t.innerHTML = '';
    				t.style.display = 'none';
    			}
    		}
    		this.fireEvent('valid', this);
    	}
    });
    Ext.apply(Ext.form.MessageTargets, {
    	'qtip': {
    		mark: function(field, msg){
    			var markEl = field[field.markEl];
    			markEl.addClass(field.invalidClass);
    			markEl.dom.qtip = msg;
    			markEl.dom.qclass = 'x-form-invalid-tip';
    			if(Ext.QuickTips){
    				Ext.QuickTips.enable();
    			}
    		},
    		clear: function(field){
    			var markEl = field[field.markEl];
    			markEl.removeClass(field.invalidClass);
    			markEl.dom.qtip = '';
    		}
    	},
    	'title': {
    		mark: function(field, msg){
    			var markEl = field[field.markEl];
    			markEl.addClass(field.invalidClass);
    			markEl.dom.title = msg;
    		},
    		clear: function(field){
    			field[field.markEl].dom.title = '';
    		}
    	},
    	'under': {
    		mark: function(field, msg){
    			var markEl = field[field.markEl], errorEl = field.errorEl;
    			markEl.addClass(field.invalidClass);
    			if(!errorEl){
    				var elp = field.getErrorCt();
    				if(!elp){
    					markEl.dom.title = msg;
    					return;
    				}
    				errorEl = field.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
    				errorEl.setWidth(elp.getWidth(true) - 20);
    			}
    			errorEl.update(msg);
    			Ext.form.Field.msgFx[field.msgFx].show(errorEl, field);
    		},
    		clear: function(field){
    			var markEl = field[field.markEl], errorEl = field.errorEl;
    			markEl.removeClass(field.invalidClass);
    			if(errorEl){
    				Ext.form.Field.msgFx[field.msgFx].hide(errorEl, field);
    			}else{
    				markEl.dom.title = '';
    			}
    		}
    	},
    	'side': {
    		mark: function(field, msg){
    			var markEl = field[field.markEl], errorIcon = field.errorIcon;
    			markEl.addClass(field.invalidClass);
    			if(!errorIcon){
    				var elp = field.getErrorCt();
    				if(!elp){
    					markEl.dom.title = msg;
    					return;
    				}
    				errorIcon = field.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
    			}
    			field.alignErrorIcon();
    			errorIcon.dom.qtip = msg;
    			errorIcon.dom.qclass = 'x-form-invalid-tip';
    			errorIcon.show();
    			field.on('resize', field.alignErrorIcon, field);
    		},
    		clear: function(field){
    			var markEl = field[field.markEl], errorIcon = field.errorIcon;
    			markEl.removeClass(field.invalidClass);
    			if(errorIcon){
    				errorIcon.dom.qtip = '';
    				errorIcon.hide();
    				field.un('resize', field.alignErrorIcon, field);
    			}else{
    				markEl.dom.title = '';
    			}
    		}
    	}
    });
    Ext.override(Ext.form.Checkbox, {
    	markEl: 'wrap',
    	mustCheckText: 'This field is required',
    	alignErrorIcon: function(){
    		this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
    	},
    	markInvalid: Ext.form.Checkbox.superclass.markInvalid,
    	clearInvalid: Ext.form.Checkbox.superclass.clearInvalid,
    	validateValue : function(value){
    		if(this.mustCheck && !value){
    			this.markInvalid(this.mustCheckText);
    			return false;
    		}
    		if(this.vtype){
    			var vt = Ext.form.VTypes;
    			if(!vt[this.vtype](value, this)){
    				this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
    				return false;
    			}
    		}
    		if(typeof this.validator == "function"){
    			var msg = this.validator(value);
    			if(msg !== true){
    				this.markInvalid(msg);
    				return false;
    			}
    		}
    		if(this.regex && !this.regex.test(value)){
    			this.markInvalid(this.regexText);
    			return false;
    		}
    		return true;
    	}
    });
    Ext.override(Ext.form.Radio, {
    	markInvalid: Ext.form.Radio.superclass.markInvalid,
    	clearInvalid: Ext.form.Radio.superclass.clearInvalid
    });
    Note: I modified Checkbox.getValue and setValue so they more closely mimic traditional checkboxes and radios.
    - If you currently load a checkbox with a value 'true' or '1' (string) then you need to set the inputValue to this value, otherwise the checkbox won't be checked.
    - getValue returns the current value (inputValue or undefined). If you want the checked state (true or false) then you can use getRawValue.
    Last edited by Condor; 3 May 2009 at 8:48 AM. Reason: Adjusted CSS for display in toolbars

  2. #2
    Sencha User galdaka's Avatar
    Join Date
    Mar 2007
    Location
    Spain
    Posts
    1,166
    Vote Rating
    -1
    galdaka is an unknown quantity at this point

      0  

    Default


    Will be added this code to SVN?

    Greetings,

  3. #3
    Sencha - Community Support Team VinylFox's Avatar
    Join Date
    Mar 2007
    Location
    Baltimore, MD
    Posts
    1,501
    Vote Rating
    8
    VinylFox will become famous soon enough VinylFox will become famous soon enough

      0  

    Default


    The consensus is no. Jack said it was to keep the look of a checkbox/radio consistent with what the user is use to in their OS. I think the motivation had more to do with tabbing and focus issues that surfaced with the introduction of the sprite version.

    With a little css, its not hard to get the OS's checkboxes to line up with labels properly. I still think the sprite versions are way better looking tho.

    PS. Thanks Condor for posting this.

  4. #4
    Sencha User nayato's Avatar
    Join Date
    Mar 2008
    Posts
    15
    Vote Rating
    0
    nayato is on a distinguished road

      0  

    Question minor suggestions

    minor suggestions


    Good work, Condor!

    I would also change onMouseLeave this way:
    Code:
    	onMouseLeave: function(){
    		this.wrap.removeClass('x-form-check-over');
    		this.wrap.removeClass('x-form-check-down');
    	},
    I also found that top in CSS-class for x-form-checkbox-inner class caused an odd layout so I removed it and now it works like a charm. Is it only me or does anyone else having this problem?

    Cheers!

  5. #5
    Sencha Premium Member
    Join Date
    Jul 2008
    Location
    Melbourne, Australia
    Posts
    77
    Vote Rating
    0
    DamianHartin is on a distinguished road

      0  

    Default


    Thanks for this Condor - saved me a lot of heartache in FF

    However IE seems to cause it's usual problems and not play nice.

    See image for what's happening

    I have the following:

    Main aspx page
    Code:
    <%@ Page Language="VB" AutoEventWireup="false" CodeFile="_Scratch.aspx.vb" Inherits="_Scratch" %>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        
        <link href="resources/css/ext-all.css" rel="stylesheet" type="text/css" /> 
        <link href="resources/css/RadioGroup.css" rel="stylesheet" type="text/css" />
        
        <script src="adapter/ext/ext-base.js" type="text/javascript"></script>    
        <script src="ext-all-debug.js" type="text/javascript"></script>
    
        <script src="resources/js/Application.Overrides.js" type="text/javascript"></script>
        <script src="resources/js/_Scratch-RadioButtonTest.js" type="text/javascript"></script>
    
        <title id="PageTitle">Scratch</title></head>
    <body>
        <form id="form1" runat="server">
        <div>
        
        </div>
        </form>
    </body>
    </html>
    Test JS:
    Code:
    Ext.BLANK_IMAGE_URL = 'resources/images/default/s.gif';
    Ext.ns('Application');
    
     
    // application main entry point
    Ext.onReady(function() {
     
        Ext.QuickTips.init();
        
            var vptMain = new Ext.Viewport({
                id:'Scratch_ViewPort'
                ,layout:'border'
                ,frame: false
                ,title:'Scratch Main Header'
                ,items:[{
                    region:'north'
                    ,id: 'srcNorthPanel'
                    ,height:10
                    ,border: false
                    ,margins: '0 0 0 0'
                    ,collapsible:false
                },{
    
                    region:'center'
                    ,id: 'scratchCenterPanel'
                    ,layout:'form'
                    ,margins:'5 5 5 5'
                    ,title:'Scratch Search'
                    ,items:[{
                                xtype:'fieldset'
                                ,title: 'Radio Groups'
                                ,autoHeight: true
                                ,items: [{
                                    xtype: 'radiogroup',
                                    fieldLabel: 'Multi-Column<br />(horiz. auto-width)',
                                    columns: 3,
                                    items: [
                                        {boxLabel: 'Item 1', name: 'rb-horiz', inputValue: 1},
                                        {boxLabel: 'Item 2', name: 'rb-horiz', inputValue: 2, checked: true},
                                        {boxLabel: 'Item 3', name: 'rb-horiz', inputValue: 3},
                                        {boxLabel: 'Item 4', name: 'rb-horiz', inputValue: 4},
                                        {boxLabel: 'Item 5', name: 'rb-horiz', inputValue: 5}
                                    ]
                                }]
                            }]
    
                   }]            
            })   
     
    }); // eo function onReady
    Used in conjunction with the overides provide on this thread

    Any idea's as to what the problem may be?

    Thanks,
    Damian
    Attached Images

  6. #6
    Ext JS Premium Member sumit.madan's Avatar
    Join Date
    May 2009
    Location
    Bangalore, India
    Posts
    121
    Vote Rating
    24
    sumit.madan has a spectacular aura about sumit.madan has a spectacular aura about

      0  

    Default


    If you're using ExtJS 3.0.3, the CSS for the <INPUT> element for checkboxes and radios was changed. This results in the sprites being shown in IE, when using the themed checkboxes and radios

    For checkboxes apply the following fixes:

    > In ext-all.css comment out the following CSS rule (line 915):
    Code:
    .x-form-check-wrap input{
        vertical-align: bottom;
    }
    > When creating a checkbox in a form, assign width : 13 in the config. ExtJS 3.0.3 has started assigning the config width to the input element, which results in the sprites being shown.

    I believe the fixes for radios would also be similiar. I'm not using them atm.

  7. #7
    Sencha Premium Member dawesi's Avatar
    Join Date
    Mar 2007
    Location
    Melbourne, Australia (aka GMT+10)
    Posts
    1,083
    Vote Rating
    44
    dawesi has a spectacular aura about dawesi has a spectacular aura about

      0  

    Default


    nice one - thanks again.
    Teahouse Training Company
    Official Certified Sencha Trainer

    Australia / New Zealand / Singapore / Hong Kong & APAC



    SenchaWorld.com - Sencha webinars, videos, etc
    SenchaForge.org - (coming soon)
    TeahouseHQ.com - Sencha ecosystem training portal

    Code Validation : JSLint | JSONLint | JSONPLint

  8. #8
    Ext User tonedeaf's Avatar
    Join Date
    Dec 2007
    Posts
    137
    Vote Rating
    1
    tonedeaf is on a distinguished road

      0  

    Default


    For ExtJS 3.1 additional CSS rules for displaying themed checkboxes, in addition to Condor's first post:

    Code:
    .ext-ie6 .x-form-check-wrap input, .ext-border-box .x-form-check-wrap input {
       margin-top: 0px;
    }
    
    .x-form-check-wrap input {
        vertical-align: baseline;
    }
    Also, all ExtJS checkboxes have to be created with a width : 13 in their config parameters, for the sprites to show correctly.

  9. #9
    Ext JS Premium Member
    Join Date
    Mar 2007
    Location
    Germany
    Posts
    671
    Vote Rating
    0
    Dumbledore is on a distinguished road

      0  

    Default


    For Ext 3.2 i must replace setValue for have the same functions as the original function. Without that the handler is not called:

    Code:
        setValue : function(v){
            var checked = this.checked ;
            this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
            if(this.rendered){
                this.el.dom.checked = this.checked;
                this.el.dom.defaultChecked = this.checked;
    			this.innerWrap[this.checked ? 'addClass' : 'removeClass']('x-form-check-checked');
    			this.validate();
            }
            if(checked != this.checked){
                this.fireEvent('check', this, this.checked);
                if(this.handler){
                    this.handler.call(this.scope || this, this, this.checked);
                }
            }
            return this;
        },

  10. #10
    Ext JS Premium Member
    Join Date
    Mar 2007
    Location
    Germany
    Posts
    671
    Vote Rating
    0
    Dumbledore is on a distinguished road

      0  

    Default


    when using this override there is a design glitch if a checkbox is inside a toolbar (see picture). And the checkbox can not direct clicked.

    Any idea? (The border is from firebug)
    Attached Images