]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/form/RadioGroup.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / form / RadioGroup.js
CommitLineData
6527f429
DM
1/**\r
2 * A {@link Ext.form.FieldContainer field container} which has a specialized layout for arranging\r
3 * {@link Ext.form.field.Radio} controls into columns, and provides convenience {@link Ext.form.field.Field}\r
4 * methods for {@link #getValue getting}, {@link #setValue setting}, and {@link #validate validating} the\r
5 * group of radio buttons as a whole.\r
6 *\r
7 * # Validation\r
8 *\r
9 * Individual radio buttons themselves have no default validation behavior, but\r
10 * sometimes you want to require a user to select one of a group of radios. RadioGroup\r
11 * allows this by setting the config `{@link #allowBlank}:false`; when the user does not check at\r
12 * one of the radio buttons, the entire group will be highlighted as invalid and the\r
13 * {@link #blankText error message} will be displayed according to the {@link #msgTarget} config.\r
14 *\r
15 * # Layout\r
16 *\r
17 * The default layout for RadioGroup makes it easy to arrange the radio buttons into\r
18 * columns; see the {@link #columns} and {@link #vertical} config documentation for details. You may also\r
19 * use a completely different layout by setting the {@link #cfg-layout} to one of the \r
20 * other supported layout types; for instance you may wish to use a custom arrangement \r
21 * of hbox and vbox containers. In that case the Radio components at any depth will \r
22 * still be managed by the RadioGroup's validation.\r
23 *\r
24 * # Example usage\r
25 *\r
26 * @example\r
27 * Ext.create('Ext.form.Panel', {\r
28 * title: 'RadioGroup Example',\r
29 * width: 300,\r
30 * height: 125,\r
31 * bodyPadding: 10,\r
32 * renderTo: Ext.getBody(),\r
33 * items:[{\r
34 * xtype: 'radiogroup',\r
35 * fieldLabel: 'Two Columns',\r
36 * // Arrange radio buttons into two columns, distributed vertically\r
37 * columns: 2,\r
38 * vertical: true,\r
39 * items: [\r
40 * { boxLabel: 'Item 1', name: 'rb', inputValue: '1' },\r
41 * { boxLabel: 'Item 2', name: 'rb', inputValue: '2', checked: true},\r
42 * { boxLabel: 'Item 3', name: 'rb', inputValue: '3' },\r
43 * { boxLabel: 'Item 4', name: 'rb', inputValue: '4' },\r
44 * { boxLabel: 'Item 5', name: 'rb', inputValue: '5' },\r
45 * { boxLabel: 'Item 6', name: 'rb', inputValue: '6' }\r
46 * ]\r
47 * }]\r
48 * });\r
49 *\r
50 */\r
51Ext.define('Ext.form.RadioGroup', {\r
52 extend: 'Ext.form.CheckboxGroup',\r
53 alias: 'widget.radiogroup',\r
54\r
55 requires: [\r
56 'Ext.form.field.Radio'\r
57 ],\r
58 \r
59 mixins: [\r
60 'Ext.util.FocusableContainer'\r
61 ],\r
62\r
63 /**\r
64 * @cfg {Ext.form.field.Radio[]/Object[]} items\r
65 * An Array of {@link Ext.form.field.Radio Radio}s or Radio config objects to arrange in the group.\r
66 */\r
67 /**\r
68 * @cfg {Boolean} allowBlank\r
69 * True to allow every item in the group to be blank.\r
70 * If allowBlank = false and no items are selected at validation time, {@link #blankText} will\r
71 * be used as the error text.\r
72 */\r
73 allowBlank : true,\r
74 //<locale>\r
75 /**\r
76 * @cfg {String} blankText\r
77 * Error text to display if the {@link #allowBlank} validation fails\r
78 */\r
79 blankText : 'You must select one item in this group',\r
80 //</locale>\r
81\r
82 defaultType : 'radiofield',\r
83\r
84 /**\r
85 * @cfg {Boolean} [local=false]\r
86 * By default, child {@link Ext.form.field.Radio radio} `name`s are scoped to the encapsulating {@link Ext.form.Panel form panel}\r
87 * if any, of the document.\r
88 *\r
89 * If you are using multiple `RadioGroup`s each of which uses the same `name` configuration in child {@link Ext.form.field.Radio radio}s, configure this as `true` to scope\r
90 * the names to within this `RadioGroup`\r
91 */\r
92 local: false,\r
93\r
94 defaultBindProperty: 'value',\r
95\r
96 /**\r
97 * @private\r
98 */\r
99 groupCls : Ext.baseCSSPrefix + 'form-radio-group',\r
100 \r
101 ariaRole: 'radiogroup',\r
102 \r
103 initRenderData: function() {\r
104 var me = this,\r
105 data, ariaAttr;\r
106 \r
107 data = me.callParent();\r
108 ariaAttr = data.ariaAttributes;\r
109 \r
110 if (ariaAttr) {\r
111 ariaAttr['aria-required'] = !me.allowBlank;\r
112 ariaAttr['aria-invalid'] = false;\r
113 }\r
114 \r
115 return data;\r
116 },\r
117\r
118 lookupComponent: function(config) {\r
119 var result = this.callParent([config]);\r
120\r
121 // Local means that the exclusivity of checking by name is scoped to this RadioGroup.\r
122 // So multiple RadioGroups can be used which use the same Radio names.\r
123 // This enables their use as a grid widget.\r
124 if (this.local) {\r
125 result.formId = this.getId();\r
126 }\r
127 return result;\r
128 },\r
129 \r
130 getBoxes: function(query, root) {\r
131 return (root || this).query('[isRadio]' + (query||''));\r
132 },\r
133 \r
134 checkChange: function() {\r
135 var value = this.getValue(),\r
136 key = Ext.Object.getKeys(value)[0];\r
137 \r
138 // If the value is an array we skip out here because it's during a change\r
139 // between multiple items, so we never want to fire a change\r
140 if (Ext.isArray(value[key])) {\r
141 return;\r
142 }\r
143 this.callParent(arguments); \r
144 },\r
145\r
146 /**\r
147 * Sets the value of the radio group. The radio with corresponding name and value will be set.\r
148 * This method is simpler than {@link Ext.form.CheckboxGroup#setValue} because only 1 value is allowed\r
149 * for each name. You can use the setValue method as:\r
150 *\r
151 * var form = Ext.create('Ext.form.Panel', {\r
152 * title : 'RadioGroup Example',\r
153 * width : 300,\r
154 * bodyPadding : 10,\r
155 * renderTo : Ext.getBody(),\r
156 * items : [\r
157 * {\r
158 * xtype : 'radiogroup',\r
159 * fieldLabel : 'Group',\r
160 * items : [\r
161 * { boxLabel : 'Item 1', name : 'rb', inputValue : 1 },\r
162 * { boxLabel : 'Item 2', name : 'rb', inputValue : 2 }\r
163 * ]\r
164 * }\r
165 * ],\r
166 * tbar : [\r
167 * {\r
168 * text : 'setValue on RadioGroup',\r
169 * handler : function () {\r
170 * form.child('radiogroup').setValue({\r
171 * rb : 2\r
172 * });\r
173 * }\r
174 * }\r
175 * ]\r
176 * });\r
177 *\r
178 * @param {Object} value The map from names to values to be set.\r
179 * @return {Ext.form.RadioGroup} this\r
180 */\r
181 setValue: function(value) {\r
182 var cbValue, first, formId, radios,\r
183 i, len, name;\r
184\r
185 if (Ext.isObject(value)) {\r
186 Ext.suspendLayouts();\r
187 first = this.items.first();\r
188 formId = first ? first.getFormId() : null;\r
189\r
190 for (name in value) {\r
191 cbValue = value[name];\r
192 radios = Ext.form.RadioManager.getWithValue(name, cbValue, formId).items;\r
193 len = radios.length;\r
194\r
195 for (i = 0; i < len; ++i) {\r
196 radios[i].setValue(true);\r
197 }\r
198 }\r
199 Ext.resumeLayouts(true);\r
200 }\r
201 return this;\r
202 },\r
203 \r
204 markInvalid: function(errors) {\r
205 var ariaDom = this.ariaEl.dom;\r
206 \r
207 this.callParent([errors]);\r
208 \r
209 if (ariaDom){\r
210 ariaDom.setAttribute('aria-invalid', true);\r
211 }\r
212 },\r
213 \r
214 clearInvalid: function() {\r
215 var ariaDom = this.ariaEl.dom;\r
216 \r
217 this.callParent();\r
218 \r
219 if (ariaDom) {\r
220 ariaDom.setAttribute('aria-invalid', false);\r
221 }\r
222 },\r
223 \r
224 privates: {\r
225 getFocusables: function() {\r
226 return this.getBoxes();\r
227 },\r
228 \r
229 initDefaultFocusable: function(beforeRender) {\r
230 var me = this,\r
231 checked, item;\r
232\r
233 checked = me.getChecked();\r
234 \r
235 // In a Radio group, only one button is supposed to be checked\r
236 //<debug>\r
237 if (checked.length > 1) {\r
238 Ext.log.error("RadioGroup " + me.id + " has more than one checked button");\r
239 }\r
240 //</debug>\r
241 \r
242 // If we have a checked button, it gets the initial childTabIndex,\r
243 // otherwise the first button gets it\r
244 if (checked.length) {\r
245 item = checked[0];\r
246 }\r
247 else {\r
248 item = me.findNextFocusableChild({\r
249 beforeRender: beforeRender,\r
250 step: 1\r
251 });\r
252 }\r
253 \r
254 if (item) {\r
255 me.activateFocusable(item);\r
256 }\r
257 \r
258 return item;\r
259 },\r
260 \r
261 getFocusableContainerEl: function() {\r
262 return this.containerEl;\r
263 },\r
264 \r
265 onFocusableContainerFocusLeave: function() {\r
266 this.clearFocusables();\r
267 this.initDefaultFocusable();\r
268 },\r
269 \r
270 doFocusableChildAdd: function(child) {\r
271 var me = this,\r
272 mixin = me.mixins.focusablecontainer,\r
273 boxes, i, len;\r
274 \r
275 boxes = child.isContainer ? me.getBoxes('', child) : [child];\r
276 \r
277 for (i = 0, len = boxes.length; i < len; i++) {\r
278 mixin.doFocusableChildAdd.call(me, boxes[i]);\r
279 }\r
280 },\r
281 \r
282 doFocusableChildRemove: function(child) {\r
283 var me = this,\r
284 mixin = me.mixins.focusablecontainer,\r
285 boxes, i, len;\r
286 \r
287 boxes = child.isContainer ? me.getBoxes('', child) : [child];\r
288 \r
289 for (i = 0, len = boxes.length; i < len; i++) {\r
290 mixin.doFocusableChildRemove.call(me, boxes[i]);\r
291 }\r
292 },\r
293 \r
294 focusChild: function(radio, forward, e) {\r
295 var nextRadio = this.mixins.focusablecontainer.focusChild.apply(this, arguments);\r
296 \r
297 // Ctrl-arrow does not select the radio that is going to be focused\r
298 if (!e.ctrlKey) {\r
299 nextRadio.setValue(true);\r
300 }\r
301 }\r
302 }\r
303});\r