1 | /*! |
---|
2 | * Ext JS Library 3.4.0 |
---|
3 | * Copyright(c) 2006-2011 Sencha Inc. |
---|
4 | * licensing@sencha.com |
---|
5 | * http://www.sencha.com/license |
---|
6 | */ |
---|
7 | /** |
---|
8 | * @class Ext.layout.FormLayout |
---|
9 | * @extends Ext.layout.AnchorLayout |
---|
10 | * <p>This layout manager is specifically designed for rendering and managing child Components of |
---|
11 | * {@link Ext.form.FormPanel forms}. It is responsible for rendering the labels of |
---|
12 | * {@link Ext.form.Field Field}s.</p> |
---|
13 | * |
---|
14 | * <p>This layout manager is used when a Container is configured with the <tt>layout:'form'</tt> |
---|
15 | * {@link Ext.Container#layout layout} config option, and should generally not need to be created directly |
---|
16 | * via the new keyword. See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p> |
---|
17 | * |
---|
18 | * <p>In an application, it will usually be preferrable to use a {@link Ext.form.FormPanel FormPanel} |
---|
19 | * (which is configured with FormLayout as its layout class by default) since it also provides built-in |
---|
20 | * functionality for {@link Ext.form.BasicForm#doAction loading, validating and submitting} the form.</p> |
---|
21 | * |
---|
22 | * <p>A {@link Ext.Container Container} <i>using</i> the FormLayout layout manager (e.g. |
---|
23 | * {@link Ext.form.FormPanel} or specifying <tt>layout:'form'</tt>) can also accept the following |
---|
24 | * layout-specific config properties:<div class="mdetail-params"><ul> |
---|
25 | * <li><b><tt>{@link Ext.form.FormPanel#hideLabels hideLabels}</tt></b></li> |
---|
26 | * <li><b><tt>{@link Ext.form.FormPanel#labelAlign labelAlign}</tt></b></li> |
---|
27 | * <li><b><tt>{@link Ext.form.FormPanel#labelPad labelPad}</tt></b></li> |
---|
28 | * <li><b><tt>{@link Ext.form.FormPanel#labelSeparator labelSeparator}</tt></b></li> |
---|
29 | * <li><b><tt>{@link Ext.form.FormPanel#labelWidth labelWidth}</tt></b></li> |
---|
30 | * </ul></div></p> |
---|
31 | * |
---|
32 | * <p>Any Component (including Fields) managed by FormLayout accepts the following as a config option: |
---|
33 | * <div class="mdetail-params"><ul> |
---|
34 | * <li><b><tt>{@link Ext.Component#anchor anchor}</tt></b></li> |
---|
35 | * </ul></div></p> |
---|
36 | * |
---|
37 | * <p>Any Component managed by FormLayout may be rendered as a form field (with an associated label) by |
---|
38 | * configuring it with a non-null <b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b>. Components configured |
---|
39 | * in this way may be configured with the following options which affect the way the FormLayout renders them: |
---|
40 | * <div class="mdetail-params"><ul> |
---|
41 | * <li><b><tt>{@link Ext.Component#clearCls clearCls}</tt></b></li> |
---|
42 | * <li><b><tt>{@link Ext.Component#fieldLabel fieldLabel}</tt></b></li> |
---|
43 | * <li><b><tt>{@link Ext.Component#hideLabel hideLabel}</tt></b></li> |
---|
44 | * <li><b><tt>{@link Ext.Component#itemCls itemCls}</tt></b></li> |
---|
45 | * <li><b><tt>{@link Ext.Component#labelSeparator labelSeparator}</tt></b></li> |
---|
46 | * <li><b><tt>{@link Ext.Component#labelStyle labelStyle}</tt></b></li> |
---|
47 | * </ul></div></p> |
---|
48 | * |
---|
49 | * <p>Example usage:</p> |
---|
50 | * <pre><code> |
---|
51 | // Required if showing validation messages |
---|
52 | Ext.QuickTips.init(); |
---|
53 | |
---|
54 | // While you can create a basic Panel with layout:'form', practically |
---|
55 | // you should usually use a FormPanel to also get its form functionality |
---|
56 | // since it already creates a FormLayout internally. |
---|
57 | var form = new Ext.form.FormPanel({ |
---|
58 | title: 'Form Layout', |
---|
59 | bodyStyle: 'padding:15px', |
---|
60 | width: 350, |
---|
61 | defaultType: 'textfield', |
---|
62 | defaults: { |
---|
63 | // applied to each contained item |
---|
64 | width: 230, |
---|
65 | msgTarget: 'side' |
---|
66 | }, |
---|
67 | items: [{ |
---|
68 | fieldLabel: 'First Name', |
---|
69 | name: 'first', |
---|
70 | allowBlank: false, |
---|
71 | {@link Ext.Component#labelSeparator labelSeparator}: ':' // override labelSeparator layout config |
---|
72 | },{ |
---|
73 | fieldLabel: 'Last Name', |
---|
74 | name: 'last' |
---|
75 | },{ |
---|
76 | fieldLabel: 'Email', |
---|
77 | name: 'email', |
---|
78 | vtype:'email' |
---|
79 | }, { |
---|
80 | xtype: 'textarea', |
---|
81 | hideLabel: true, // override hideLabels layout config |
---|
82 | name: 'msg', |
---|
83 | anchor: '100% -53' |
---|
84 | } |
---|
85 | ], |
---|
86 | buttons: [ |
---|
87 | {text: 'Save'}, |
---|
88 | {text: 'Cancel'} |
---|
89 | ], |
---|
90 | layoutConfig: { |
---|
91 | {@link #labelSeparator}: '~' // superseded by assignment below |
---|
92 | }, |
---|
93 | // config options applicable to container when layout='form': |
---|
94 | hideLabels: false, |
---|
95 | labelAlign: 'left', // or 'right' or 'top' |
---|
96 | {@link Ext.form.FormPanel#labelSeparator labelSeparator}: '>>', // takes precedence over layoutConfig value |
---|
97 | labelWidth: 65, // defaults to 100 |
---|
98 | labelPad: 8 // defaults to 5, must specify labelWidth to be honored |
---|
99 | }); |
---|
100 | </code></pre> |
---|
101 | */ |
---|
102 | Ext.layout.FormLayout = Ext.extend(Ext.layout.AnchorLayout, { |
---|
103 | |
---|
104 | /** |
---|
105 | * @cfg {String} labelSeparator |
---|
106 | * See {@link Ext.form.FormPanel}.{@link Ext.form.FormPanel#labelSeparator labelSeparator}. Configuration |
---|
107 | * of this property at the <b>container</b> level takes precedence. |
---|
108 | */ |
---|
109 | labelSeparator : ':', |
---|
110 | |
---|
111 | /** |
---|
112 | * Read only. The CSS style specification string added to field labels in this layout if not |
---|
113 | * otherwise {@link Ext.Component#labelStyle specified by each contained field}. |
---|
114 | * @type String |
---|
115 | * @property labelStyle |
---|
116 | */ |
---|
117 | |
---|
118 | /** |
---|
119 | * @cfg {Boolean} trackLabels |
---|
120 | * True to show/hide the field label when the field is hidden. Defaults to <tt>true</tt>. |
---|
121 | */ |
---|
122 | trackLabels: true, |
---|
123 | |
---|
124 | type: 'form', |
---|
125 | |
---|
126 | onRemove: function(c){ |
---|
127 | Ext.layout.FormLayout.superclass.onRemove.call(this, c); |
---|
128 | if(this.trackLabels){ |
---|
129 | c.un('show', this.onFieldShow, this); |
---|
130 | c.un('hide', this.onFieldHide, this); |
---|
131 | } |
---|
132 | // check for itemCt, since we may be removing a fieldset or something similar |
---|
133 | var el = c.getPositionEl(), |
---|
134 | ct = c.getItemCt && c.getItemCt(); |
---|
135 | if (c.rendered && ct) { |
---|
136 | if (el && el.dom) { |
---|
137 | el.insertAfter(ct); |
---|
138 | } |
---|
139 | Ext.destroy(ct); |
---|
140 | Ext.destroyMembers(c, 'label', 'itemCt'); |
---|
141 | if (c.customItemCt) { |
---|
142 | Ext.destroyMembers(c, 'getItemCt', 'customItemCt'); |
---|
143 | } |
---|
144 | } |
---|
145 | }, |
---|
146 | |
---|
147 | // private |
---|
148 | setContainer : function(ct){ |
---|
149 | Ext.layout.FormLayout.superclass.setContainer.call(this, ct); |
---|
150 | if(ct.labelAlign){ |
---|
151 | ct.addClass('x-form-label-'+ct.labelAlign); |
---|
152 | } |
---|
153 | |
---|
154 | if(ct.hideLabels){ |
---|
155 | Ext.apply(this, { |
---|
156 | labelStyle: 'display:none', |
---|
157 | elementStyle: 'padding-left:0;', |
---|
158 | labelAdjust: 0 |
---|
159 | }); |
---|
160 | }else{ |
---|
161 | this.labelSeparator = Ext.isDefined(ct.labelSeparator) ? ct.labelSeparator : this.labelSeparator; |
---|
162 | ct.labelWidth = ct.labelWidth || 100; |
---|
163 | if(Ext.isNumber(ct.labelWidth)){ |
---|
164 | var pad = Ext.isNumber(ct.labelPad) ? ct.labelPad : 5; |
---|
165 | Ext.apply(this, { |
---|
166 | labelAdjust: ct.labelWidth + pad, |
---|
167 | labelStyle: 'width:' + ct.labelWidth + 'px;', |
---|
168 | elementStyle: 'padding-left:' + (ct.labelWidth + pad) + 'px' |
---|
169 | }); |
---|
170 | } |
---|
171 | if(ct.labelAlign == 'top'){ |
---|
172 | Ext.apply(this, { |
---|
173 | labelStyle: 'width:auto;', |
---|
174 | labelAdjust: 0, |
---|
175 | elementStyle: 'padding-left:0;' |
---|
176 | }); |
---|
177 | } |
---|
178 | } |
---|
179 | }, |
---|
180 | |
---|
181 | // private |
---|
182 | isHide: function(c){ |
---|
183 | return c.hideLabel || this.container.hideLabels; |
---|
184 | }, |
---|
185 | |
---|
186 | onFieldShow: function(c){ |
---|
187 | c.getItemCt().removeClass('x-hide-' + c.hideMode); |
---|
188 | |
---|
189 | // Composite fields will need to layout after the container is made visible |
---|
190 | if (c.isComposite) { |
---|
191 | c.doLayout(); |
---|
192 | } |
---|
193 | }, |
---|
194 | |
---|
195 | onFieldHide: function(c){ |
---|
196 | c.getItemCt().addClass('x-hide-' + c.hideMode); |
---|
197 | }, |
---|
198 | |
---|
199 | //private |
---|
200 | getLabelStyle: function(s){ |
---|
201 | var ls = '', items = [this.labelStyle, s]; |
---|
202 | for (var i = 0, len = items.length; i < len; ++i){ |
---|
203 | if (items[i]){ |
---|
204 | ls += items[i]; |
---|
205 | if (ls.substr(-1, 1) != ';'){ |
---|
206 | ls += ';'; |
---|
207 | } |
---|
208 | } |
---|
209 | } |
---|
210 | return ls; |
---|
211 | }, |
---|
212 | |
---|
213 | /** |
---|
214 | * @cfg {Ext.Template} fieldTpl |
---|
215 | * A {@link Ext.Template#compile compile}d {@link Ext.Template} for rendering |
---|
216 | * the fully wrapped, labeled and styled form Field. Defaults to:</p><pre><code> |
---|
217 | new Ext.Template( |
---|
218 | '<div class="x-form-item {itemCls}" tabIndex="-1">', |
---|
219 | '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>', |
---|
220 | '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">', |
---|
221 | '</div><div class="{clearCls}"></div>', |
---|
222 | '</div>' |
---|
223 | ); |
---|
224 | </code></pre> |
---|
225 | * <p>This may be specified to produce a different DOM structure when rendering form Fields.</p> |
---|
226 | * <p>A description of the properties within the template follows:</p><div class="mdetail-params"><ul> |
---|
227 | * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper |
---|
228 | * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt> |
---|
229 | * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt> |
---|
230 | * supplied at the container level.</div></li> |
---|
231 | * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li> |
---|
232 | * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc"> |
---|
233 | * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the |
---|
234 | * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li> |
---|
235 | * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this |
---|
236 | * field (defaults to <tt>''</tt>)</div></li> |
---|
237 | * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after |
---|
238 | * the text of the label for this field (defaults to a colon <tt>':'</tt> or the |
---|
239 | * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li> |
---|
240 | * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li> |
---|
241 | * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div |
---|
242 | * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li> |
---|
243 | * </ul></div> |
---|
244 | * <p>Also see <tt>{@link #getTemplateArgs}</tt></p> |
---|
245 | */ |
---|
246 | |
---|
247 | /** |
---|
248 | * @private |
---|
249 | * |
---|
250 | */ |
---|
251 | renderItem : function(c, position, target){ |
---|
252 | if(c && (c.isFormField || c.fieldLabel) && c.inputType != 'hidden'){ |
---|
253 | var args = this.getTemplateArgs(c); |
---|
254 | if(Ext.isNumber(position)){ |
---|
255 | position = target.dom.childNodes[position] || null; |
---|
256 | } |
---|
257 | if(position){ |
---|
258 | c.itemCt = this.fieldTpl.insertBefore(position, args, true); |
---|
259 | }else{ |
---|
260 | c.itemCt = this.fieldTpl.append(target, args, true); |
---|
261 | } |
---|
262 | if(!c.getItemCt){ |
---|
263 | // Non form fields don't have getItemCt, apply it here |
---|
264 | // This will get cleaned up in onRemove |
---|
265 | Ext.apply(c, { |
---|
266 | getItemCt: function(){ |
---|
267 | return c.itemCt; |
---|
268 | }, |
---|
269 | customItemCt: true |
---|
270 | }); |
---|
271 | } |
---|
272 | c.label = c.getItemCt().child('label.x-form-item-label'); |
---|
273 | if(!c.rendered){ |
---|
274 | c.render('x-form-el-' + c.id); |
---|
275 | }else if(!this.isValidParent(c, target)){ |
---|
276 | Ext.fly('x-form-el-' + c.id).appendChild(c.getPositionEl()); |
---|
277 | } |
---|
278 | if(this.trackLabels){ |
---|
279 | if(c.hidden){ |
---|
280 | this.onFieldHide(c); |
---|
281 | } |
---|
282 | c.on({ |
---|
283 | scope: this, |
---|
284 | show: this.onFieldShow, |
---|
285 | hide: this.onFieldHide |
---|
286 | }); |
---|
287 | } |
---|
288 | this.configureItem(c); |
---|
289 | }else { |
---|
290 | Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments); |
---|
291 | } |
---|
292 | }, |
---|
293 | |
---|
294 | /** |
---|
295 | * <p>Provides template arguments for rendering the fully wrapped, labeled and styled form Field.</p> |
---|
296 | * <p>This method returns an object hash containing properties used by the layout's {@link #fieldTpl} |
---|
297 | * to create a correctly wrapped, labeled and styled form Field. This may be overriden to |
---|
298 | * create custom layouts. The properties which must be returned are:</p><div class="mdetail-params"><ul> |
---|
299 | * <li><b><tt>itemCls</tt></b> : String<div class="sub-desc">The CSS class applied to the outermost div wrapper |
---|
300 | * that contains this field label and field element (the default class is <tt>'x-form-item'</tt> and <tt>itemCls</tt> |
---|
301 | * will be added to that). If supplied, <tt>itemCls</tt> at the field level will override the default <tt>itemCls</tt> |
---|
302 | * supplied at the container level.</div></li> |
---|
303 | * <li><b><tt>id</tt></b> : String<div class="sub-desc">The id of the Field</div></li> |
---|
304 | * <li><b><tt>{@link #labelStyle}</tt></b> : String<div class="sub-desc"> |
---|
305 | * A CSS style specification string to add to the field label for this field (defaults to <tt>''</tt> or the |
---|
306 | * {@link #labelStyle layout's value for <tt>labelStyle</tt>}).</div></li> |
---|
307 | * <li><b><tt>label</tt></b> : String<div class="sub-desc">The text to display as the label for this |
---|
308 | * field (defaults to the field's configured fieldLabel property)</div></li> |
---|
309 | * <li><b><tt>{@link #labelSeparator}</tt></b> : String<div class="sub-desc">The separator to display after |
---|
310 | * the text of the label for this field (defaults to a colon <tt>':'</tt> or the |
---|
311 | * {@link #labelSeparator layout's value for labelSeparator}). To hide the separator use empty string ''.</div></li> |
---|
312 | * <li><b><tt>elementStyle</tt></b> : String<div class="sub-desc">The styles text for the input element's wrapper.</div></li> |
---|
313 | * <li><b><tt>clearCls</tt></b> : String<div class="sub-desc">The CSS class to apply to the special clearing div |
---|
314 | * rendered directly after each form field wrapper (defaults to <tt>'x-form-clear-left'</tt>)</div></li> |
---|
315 | * </ul></div> |
---|
316 | * @param (Ext.form.Field} field The {@link Ext.form.Field Field} being rendered. |
---|
317 | * @return {Object} An object hash containing the properties required to render the Field. |
---|
318 | */ |
---|
319 | getTemplateArgs: function(field) { |
---|
320 | var noLabelSep = !field.fieldLabel || field.hideLabel, |
---|
321 | itemCls = (field.itemCls || this.container.itemCls || '') + (field.hideLabel ? ' x-hide-label' : ''); |
---|
322 | |
---|
323 | // IE9 quirks needs an extra, identifying class on wrappers of TextFields |
---|
324 | if (Ext.isIE9 && Ext.isIEQuirks && field instanceof Ext.form.TextField) { |
---|
325 | itemCls += ' x-input-wrapper'; |
---|
326 | } |
---|
327 | |
---|
328 | return { |
---|
329 | id : field.id, |
---|
330 | label : field.fieldLabel, |
---|
331 | itemCls : itemCls, |
---|
332 | clearCls : field.clearCls || 'x-form-clear-left', |
---|
333 | labelStyle : this.getLabelStyle(field.labelStyle), |
---|
334 | elementStyle : this.elementStyle || '', |
---|
335 | labelSeparator: noLabelSep ? '' : (Ext.isDefined(field.labelSeparator) ? field.labelSeparator : this.labelSeparator) |
---|
336 | }; |
---|
337 | }, |
---|
338 | |
---|
339 | // private |
---|
340 | adjustWidthAnchor: function(value, c){ |
---|
341 | if(c.label && !this.isHide(c) && (this.container.labelAlign != 'top')){ |
---|
342 | var adjust = Ext.isIE6 || (Ext.isIE && !Ext.isStrict); |
---|
343 | return value - this.labelAdjust + (adjust ? -3 : 0); |
---|
344 | } |
---|
345 | return value; |
---|
346 | }, |
---|
347 | |
---|
348 | adjustHeightAnchor : function(value, c){ |
---|
349 | if(c.label && !this.isHide(c) && (this.container.labelAlign == 'top')){ |
---|
350 | return value - c.label.getHeight(); |
---|
351 | } |
---|
352 | return value; |
---|
353 | }, |
---|
354 | |
---|
355 | // private |
---|
356 | isValidParent : function(c, target){ |
---|
357 | return target && this.container.getEl().contains(c.getPositionEl()); |
---|
358 | } |
---|
359 | |
---|
360 | /** |
---|
361 | * @property activeItem |
---|
362 | * @hide |
---|
363 | */ |
---|
364 | }); |
---|
365 | |
---|
366 | Ext.Container.LAYOUTS['form'] = Ext.layout.FormLayout; |
---|