]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * The text field is the basis for most of the input fields. It provides a baseline of shared\r | |
3 | * functionality such as input validation, standard events, state management and look and feel. Typically we create\r | |
4 | * text fields inside a form, like this:\r | |
5 | *\r | |
6 | * @example\r | |
7 | * Ext.create('Ext.form.Panel', {\r | |
8 | * fullscreen: true,\r | |
9 | * items: [\r | |
10 | * {\r | |
11 | * xtype: 'fieldset',\r | |
12 | * title: 'Enter your name',\r | |
13 | * items: [\r | |
14 | * {\r | |
15 | * xtype: 'textfield',\r | |
16 | * label: 'First Name',\r | |
17 | * name: 'firstName'\r | |
18 | * },\r | |
19 | * {\r | |
20 | * xtype: 'textfield',\r | |
21 | * label: 'Last Name',\r | |
22 | * name: 'lastName'\r | |
23 | * }\r | |
24 | * ]\r | |
25 | * }\r | |
26 | * ]\r | |
27 | * });\r | |
28 | *\r | |
29 | * This creates two text fields inside a form. Text Fields can also be created outside of a Form, like this:\r | |
30 | *\r | |
31 | * Ext.create('Ext.field.Text', {\r | |
32 | * label: 'Your Name',\r | |
33 | * value: 'Ed Spencer'\r | |
34 | * });\r | |
35 | *\r | |
36 | * ## Configuring\r | |
37 | *\r | |
38 | * Text field offers several configuration options, including {@link #placeHolder}, {@link #maxLength},\r | |
39 | * {@link #autoComplete}, {@link #autoCapitalize} and {@link #autoCorrect}. For example, here is how we would configure\r | |
40 | * a text field to have a maximum length of 10 characters, with placeholder text that disappears when the field is\r | |
41 | * focused:\r | |
42 | *\r | |
43 | * Ext.create('Ext.field.Text', {\r | |
44 | * label: 'Username',\r | |
45 | * maxLength: 10,\r | |
46 | * placeHolder: 'Enter your username'\r | |
47 | * });\r | |
48 | *\r | |
49 | * The autoComplete, autoCapitalize and autoCorrect configs simply set those attributes on the text field and allow the\r | |
50 | * native browser to provide those capabilities. For example, to enable auto complete and auto correct, simply\r | |
51 | * configure your text field like this:\r | |
52 | *\r | |
53 | * Ext.create('Ext.field.Text', {\r | |
54 | * label: 'Username',\r | |
55 | * autoComplete: true,\r | |
56 | * autoCorrect: true\r | |
57 | * });\r | |
58 | *\r | |
59 | * These configurations will be picked up by the native browser, which will enable the options at the OS level.\r | |
60 | *\r | |
61 | * Text field inherits from {@link Ext.field.Field}, which is the base class for all fields and provides\r | |
62 | * a lot of shared functionality for all fields, including setting values, clearing and basic validation. See the\r | |
63 | * {@link Ext.field.Field} documentation to see how to leverage its capabilities.\r | |
64 | */\r | |
65 | Ext.define('Ext.field.Text', {\r | |
66 | extend: 'Ext.field.Field',\r | |
67 | xtype: 'textfield',\r | |
68 | alternateClassName: 'Ext.form.Text',\r | |
69 | \r | |
70 | /**\r | |
71 | * @event focus\r | |
72 | * Fires when this field receives input focus\r | |
73 | * @param {Ext.field.Text} this This field\r | |
74 | * @param {Ext.event.Event} e\r | |
75 | */\r | |
76 | \r | |
77 | /**\r | |
78 | * @event blur\r | |
79 | * Fires when this field loses input focus\r | |
80 | * @param {Ext.field.Text} this This field\r | |
81 | * @param {Ext.event.Event} e\r | |
82 | */\r | |
83 | \r | |
84 | /**\r | |
85 | * @event paste\r | |
86 | * Fires when this field is pasted.\r | |
87 | * @param {Ext.field.Text} this This field\r | |
88 | * @param {Ext.event.Event} e\r | |
89 | */\r | |
90 | \r | |
91 | /**\r | |
92 | * @event mousedown\r | |
93 | * Fires when this field receives a mousedown\r | |
94 | * @param {Ext.field.Text} this This field\r | |
95 | * @param {Ext.event.Event} e\r | |
96 | */\r | |
97 | \r | |
98 | /**\r | |
99 | * @event keyup\r | |
100 | * @preventable\r | |
101 | * Fires when a key is released on the input element\r | |
102 | * @param {Ext.field.Text} this This field\r | |
103 | * @param {Ext.event.Event} e\r | |
104 | */\r | |
105 | \r | |
106 | /**\r | |
107 | * @event clearicontap\r | |
108 | * @preventable\r | |
109 | * Fires when the clear icon is tapped\r | |
110 | * @param {Ext.field.Text} this This field\r | |
111 | * @param {Ext.field.Input} input The field's input component.\r | |
112 | * @param {Ext.event.Event} e\r | |
113 | */\r | |
114 | \r | |
115 | /**\r | |
116 | * @event change\r | |
117 | * Fires when the value has changed.\r | |
118 | * @param {Ext.field.Text} this This field\r | |
119 | * @param {String} newValue The new value\r | |
120 | * @param {String} oldValue The original value\r | |
121 | */\r | |
122 | \r | |
123 | /**\r | |
124 | * @event action\r | |
125 | * @preventable\r | |
126 | * Fires whenever the return key or go is pressed. FormPanel listeners\r | |
127 | * for this event, and submits itself whenever it fires. Also note\r | |
128 | * that this event bubbles up to parent containers.\r | |
129 | * @param {Ext.field.Text} this This field\r | |
130 | * @param {Mixed} e The key event object\r | |
131 | */\r | |
132 | \r | |
133 | config: {\r | |
134 | /**\r | |
135 | * @cfg\r | |
136 | * @inheritdoc\r | |
137 | */\r | |
138 | ui: 'text',\r | |
139 | \r | |
140 | /**\r | |
141 | * @cfg\r | |
142 | * @inheritdoc\r | |
143 | */\r | |
144 | clearIcon: true,\r | |
145 | \r | |
146 | /**\r | |
147 | * @cfg {String} placeHolder A string value displayed in the input (if supported) when the control is empty.\r | |
148 | * @accessor\r | |
149 | */\r | |
150 | placeHolder: null,\r | |
151 | \r | |
152 | /**\r | |
153 | * @cfg {Number} maxLength The maximum number of permitted input characters.\r | |
154 | * @accessor\r | |
155 | */\r | |
156 | maxLength: null,\r | |
157 | \r | |
158 | /**\r | |
159 | * True to set the field's DOM element autocomplete attribute to "on", false to set to "off".\r | |
160 | * @cfg {Boolean} autoComplete\r | |
161 | * @accessor\r | |
162 | */\r | |
163 | autoComplete: null,\r | |
164 | \r | |
165 | /**\r | |
166 | * True to set the field's DOM element autocapitalize attribute to "on", false to set to "off".\r | |
167 | * @cfg {Boolean} autoCapitalize\r | |
168 | * @accessor\r | |
169 | */\r | |
170 | autoCapitalize: null,\r | |
171 | \r | |
172 | /**\r | |
173 | * True to set the field DOM element autocorrect attribute to "on", false to set to "off".\r | |
174 | * @cfg {Boolean} autoCorrect\r | |
175 | * @accessor\r | |
176 | */\r | |
177 | autoCorrect: null,\r | |
178 | \r | |
179 | /**\r | |
180 | * True to set the field DOM element readonly attribute to true.\r | |
181 | * @cfg {Boolean} readOnly\r | |
182 | * @accessor\r | |
183 | */\r | |
184 | readOnly: null,\r | |
185 | \r | |
186 | /**\r | |
187 | * @cfg {Object} component The inner component for this field, which defaults to an input text.\r | |
188 | * @accessor\r | |
189 | */\r | |
190 | component: {\r | |
191 | xtype: 'input',\r | |
192 | type: 'text',\r | |
193 | fastFocus: false\r | |
194 | },\r | |
195 | \r | |
196 | bubbleEvents: ['action']\r | |
197 | },\r | |
198 | \r | |
199 | defaultBindProperty: 'value',\r | |
200 | twoWayBindable: {\r | |
201 | value: 1\r | |
202 | },\r | |
203 | \r | |
204 | publishes: {\r | |
205 | value: 1\r | |
206 | },\r | |
207 | \r | |
208 | focusedCls: Ext.baseCSSPrefix + 'field-focused',\r | |
209 | clearableCls: Ext.baseCSSPrefix + 'field-clearable',\r | |
210 | emptyCls: Ext.baseCSSPrefix + 'empty',\r | |
211 | \r | |
212 | /**\r | |
213 | * @private\r | |
214 | */\r | |
215 | initialize: function() {\r | |
216 | var me = this;\r | |
217 | \r | |
218 | me.callParent();\r | |
219 | \r | |
220 | me.getComponent().on({\r | |
221 | scope: this,\r | |
222 | \r | |
223 | keyup : 'onKeyUp',\r | |
224 | input : 'onInput',\r | |
225 | focus : 'onFocus',\r | |
226 | blur : 'onBlur',\r | |
227 | paste : 'onPaste',\r | |
228 | mousedown : 'onMouseDown',\r | |
229 | clearicontap: 'onClearIconTap'\r | |
230 | });\r | |
231 | \r | |
232 | // set the originalValue of the textfield, if one exists\r | |
233 | me.originalValue = me.getValue() || "";\r | |
234 | me.getComponent().originalValue = me.originalValue;\r | |
235 | \r | |
236 | me.syncEmptyCls();\r | |
237 | },\r | |
238 | \r | |
239 | syncEmptyCls: function() {\r | |
240 | var val = this._value,\r | |
241 | empty = val ? val.length : false;\r | |
242 | \r | |
243 | this.toggleCls(this.emptyCls, !empty);\r | |
244 | },\r | |
245 | \r | |
246 | applyValue: function(value) {\r | |
247 | return Ext.isEmpty(value) ? '' : value;\r | |
248 | },\r | |
249 | \r | |
250 | /**\r | |
251 | * @private\r | |
252 | */\r | |
253 | updateValue: function(value, oldValue) {\r | |
254 | var me = this,\r | |
255 | component = me.getComponent(),\r | |
256 | // allows value to be zero but not undefined or null (other falsey values)\r | |
257 | valueValid = value !== undefined && value !== null && value !== '';\r | |
258 | \r | |
259 | if (component) {\r | |
260 | component.setValue(value);\r | |
261 | }\r | |
262 | \r | |
263 | me.toggleClearIcon(valueValid && me.isDirty())\r | |
264 | \r | |
265 | me.syncEmptyCls();\r | |
266 | \r | |
267 | if (me.initialized) {\r | |
268 | me.fireEvent('change', me, value, oldValue);\r | |
269 | }\r | |
270 | },\r | |
271 | \r | |
272 | /**\r | |
273 | * @private\r | |
274 | */\r | |
275 | updatePlaceHolder: function(newPlaceHolder) {\r | |
276 | this.getComponent().setPlaceHolder(newPlaceHolder);\r | |
277 | },\r | |
278 | \r | |
279 | /**\r | |
280 | * @private\r | |
281 | */\r | |
282 | updateMaxLength: function(newMaxLength) {\r | |
283 | this.getComponent().setMaxLength(newMaxLength);\r | |
284 | },\r | |
285 | \r | |
286 | /**\r | |
287 | * @private\r | |
288 | */\r | |
289 | updateAutoComplete: function(newAutoComplete) {\r | |
290 | this.getComponent().setAutoComplete(newAutoComplete);\r | |
291 | },\r | |
292 | \r | |
293 | /**\r | |
294 | * @private\r | |
295 | */\r | |
296 | updateAutoCapitalize: function(newAutoCapitalize) {\r | |
297 | this.getComponent().setAutoCapitalize(newAutoCapitalize);\r | |
298 | },\r | |
299 | \r | |
300 | /**\r | |
301 | * @private\r | |
302 | */\r | |
303 | updateAutoCorrect: function(newAutoCorrect) {\r | |
304 | this.getComponent().setAutoCorrect(newAutoCorrect);\r | |
305 | },\r | |
306 | \r | |
307 | /**\r | |
308 | * @private\r | |
309 | */\r | |
310 | updateReadOnly: function(newReadOnly) {\r | |
311 | this.toggleClearIcon(!newReadOnly);\r | |
312 | this.getComponent().setReadOnly(newReadOnly);\r | |
313 | },\r | |
314 | \r | |
315 | /**\r | |
316 | * @private\r | |
317 | */\r | |
318 | updateInputType: function(newInputType) {\r | |
319 | var component = this.getComponent();\r | |
320 | if (component) {\r | |
321 | component.setType(newInputType);\r | |
322 | }\r | |
323 | },\r | |
324 | \r | |
325 | /**\r | |
326 | * @private\r | |
327 | */\r | |
328 | updateName: function(newName) {\r | |
329 | var component = this.getComponent();\r | |
330 | if (component) {\r | |
331 | component.setName(newName);\r | |
332 | }\r | |
333 | },\r | |
334 | \r | |
335 | /**\r | |
336 | * @private\r | |
337 | */\r | |
338 | updateTabIndex: function(newTabIndex) {\r | |
339 | var component = this.getComponent();\r | |
340 | if (component) {\r | |
341 | component.setTabIndex(newTabIndex);\r | |
342 | }\r | |
343 | },\r | |
344 | \r | |
345 | /**\r | |
346 | * Updates the {@link #inputCls} configuration on this fields {@link #component}\r | |
347 | * @private\r | |
348 | */\r | |
349 | updateInputCls: function(newInputCls, oldInputCls) {\r | |
350 | var component = this.getComponent();\r | |
351 | if (component) {\r | |
352 | component.replaceCls(oldInputCls, newInputCls);\r | |
353 | }\r | |
354 | },\r | |
355 | \r | |
356 | updateDisabled: function(disabled, oldDisabled) {\r | |
357 | this.callParent([disabled, oldDisabled]);\r | |
358 | \r | |
359 | var component = this.getComponent();\r | |
360 | if (component) {\r | |
361 | component.setDisabled(disabled);\r | |
362 | }\r | |
363 | \r | |
364 | this.toggleClearIcon(!disabled);\r | |
365 | },\r | |
366 | \r | |
367 | /**\r | |
368 | * @private\r | |
369 | */\r | |
370 | showClearIcon: function() {\r | |
371 | var me = this,\r | |
372 | value = me.getValue(),\r | |
373 | // allows value to be zero but not undefined or null (other falsey values)\r | |
374 | valueValid = value !== undefined && value !== null && value !== "";\r | |
375 | \r | |
376 | if (me.getClearIcon() && !me.getDisabled() && !me.getReadOnly() && valueValid) {\r | |
377 | me.element.addCls(me.clearableCls);\r | |
378 | }\r | |
379 | \r | |
380 | return me;\r | |
381 | },\r | |
382 | \r | |
383 | /**\r | |
384 | * @private\r | |
385 | */\r | |
386 | hideClearIcon: function() {\r | |
387 | if (this.getClearIcon()) {\r | |
388 | this.element.removeCls(this.clearableCls);\r | |
389 | }\r | |
390 | },\r | |
391 | \r | |
392 | onKeyUp: function(e) {\r | |
393 | this.fireAction('keyup', [this, e], 'doKeyUp');\r | |
394 | },\r | |
395 | \r | |
396 | /**\r | |
397 | * Called when a key has been pressed in the `<input>`\r | |
398 | * @private\r | |
399 | */\r | |
400 | doKeyUp: function(me, e) {\r | |
401 | // getValue to ensure that we are in sync with the dom\r | |
402 | var value = me.getValue(),\r | |
403 | // allows value to be zero but not undefined or null (other falsey values)\r | |
404 | valueValid = value !== undefined && value !== null && value !== '';\r | |
405 | \r | |
406 | me.toggleClearIcon(valueValid);\r | |
407 | \r | |
408 | if (e.browserEvent.keyCode === 13) {\r | |
409 | me.fireAction('action', [me, e], 'doAction');\r | |
410 | }\r | |
411 | },\r | |
412 | \r | |
413 | doAction: function() {\r | |
414 | this.blur();\r | |
415 | },\r | |
416 | \r | |
417 | onClearIconTap: function(input, e) {\r | |
418 | this.fireAction('clearicontap', [this, input, e], 'doClearIconTap');\r | |
419 | },\r | |
420 | \r | |
421 | /**\r | |
422 | * @private\r | |
423 | */\r | |
424 | doClearIconTap: function(me, e) {\r | |
425 | me.setValue('');\r | |
426 | },\r | |
427 | \r | |
428 | onInput: function(component, value) {\r | |
429 | this.setValue(value);\r | |
430 | },\r | |
431 | \r | |
432 | onFocus: function(e) {\r | |
433 | var me = this;\r | |
434 | \r | |
435 | me.addCls(me.focusedCls);\r | |
436 | me.isFocused = true;\r | |
437 | me.fireEvent('focus', me, e);\r | |
438 | },\r | |
439 | \r | |
440 | onBlur: function(e) {\r | |
441 | var me = this;\r | |
442 | \r | |
443 | me.removeCls(me.focusedCls);\r | |
444 | me.isFocused = false;\r | |
445 | \r | |
446 | me.fireEvent('blur', me, e);\r | |
447 | \r | |
448 | Ext.defer(function() {\r | |
449 | me.isFocused = false;\r | |
450 | }, 50);\r | |
451 | },\r | |
452 | \r | |
453 | onPaste: function(e) {\r | |
454 | this.fireEvent('paste', this, e);\r | |
455 | },\r | |
456 | \r | |
457 | onMouseDown: function(e) {\r | |
458 | this.fireEvent('mousedown', this, e);\r | |
459 | },\r | |
460 | \r | |
461 | /**\r | |
462 | * Attempts to set the field as the active input focus.\r | |
463 | * @return {Ext.field.Text} This field\r | |
464 | */\r | |
465 | focus: function() {\r | |
466 | this.getComponent().focus();\r | |
467 | return this;\r | |
468 | },\r | |
469 | \r | |
470 | /**\r | |
471 | * Attempts to forcefully blur input focus for the field.\r | |
472 | * @return {Ext.field.Text} This field\r | |
473 | */\r | |
474 | blur: function() {\r | |
475 | this.getComponent().blur();\r | |
476 | return this;\r | |
477 | },\r | |
478 | \r | |
479 | /**\r | |
480 | * Attempts to forcefully select all the contents of the input field.\r | |
481 | * @return {Ext.field.Text} this\r | |
482 | */\r | |
483 | select: function() {\r | |
484 | this.getComponent().select();\r | |
485 | return this;\r | |
486 | },\r | |
487 | \r | |
488 | resetOriginalValue: function() {\r | |
489 | var me = this,\r | |
490 | comp;\r | |
491 | \r | |
492 | me.callParent();\r | |
493 | component = me.getComponent();\r | |
494 | if(component && component.hasOwnProperty("originalValue")) {\r | |
495 | me.getComponent().originalValue = me.originalValue;\r | |
496 | }\r | |
497 | me.reset();\r | |
498 | },\r | |
499 | \r | |
500 | reset: function() {\r | |
501 | var me = this;\r | |
502 | me.getComponent().reset();\r | |
503 | \r | |
504 | //we need to call this to sync the input with this field\r | |
505 | me.getValue();\r | |
506 | \r | |
507 | me.toggleClearIcon(me.isDirty());\r | |
508 | },\r | |
509 | \r | |
510 | isDirty: function() {\r | |
511 | var component = this.getComponent();\r | |
512 | if (component) {\r | |
513 | return component.isDirty();\r | |
514 | }\r | |
515 | return false;\r | |
516 | },\r | |
517 | \r | |
518 | privates: {\r | |
519 | toggleClearIcon: function(state) {\r | |
520 | if (state) {\r | |
521 | this.showClearIcon();\r | |
522 | } else {\r | |
523 | this.hideClearIcon();\r | |
524 | }\r | |
525 | }\r | |
526 | }\r | |
527 | });\r | |
528 | \r |