Page 1 of 2 12 LastLast
Results 1 to 10 of 18

Thread: Themed checkboxes/radios for Ext 3.0

  1. #1
    Sencha - Community Support Team Condor's Avatar
    Join Date
    Mar 2007
    Location
    The Netherlands
    Posts
    24,246
    Vote Rating
    118
      0  

    Default 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
    0
      0  

    Default

    Will be added this code to SVN?

    Greetings,

  3. #3
    Sencha User VinylFox's Avatar
    Join Date
    Mar 2007
    Location
    Baltimore, MD
    Posts
    1,501
    Vote Rating
    8
      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
      0  

    Question 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
      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 Attached Images

  6. #6
    Ext JS Premium Member sumit.madan's Avatar
    Join Date
    May 2009
    Location
    Bangalore, India
    Posts
    121
    Vote Rating
    24
      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,093
    Vote Rating
    56
      0  

    Default

    nice one - thanks again.
    Lead Trainer / Sencha Specialist
    Community And Learning Systems

    Lead Architect
    DigitalTickets.net

  8. #8
    Ext User tonedeaf's Avatar
    Join Date
    Dec 2007
    Posts
    137
    Vote Rating
    1
      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
    720
    Vote Rating
    36
      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
    720
    Vote Rating
    36
      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 Attached Images

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •