-
Notifications
You must be signed in to change notification settings - Fork 28
/
FormValueWidget.js
145 lines (135 loc) · 5.26 KB
/
FormValueWidget.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/** @module delite/FormValueWidget */
define([
"dcl/dcl",
"./FormWidget",
"./activationTracker"
], function (dcl, FormWidget) {
/**
* Returns a method to set a new value and fire an event (change or input) if the value changed since the last
* call. Widget should use `handleOnChange()` or `handleOnInput()`.
* @param {string} eventType - The event type. Can be "change" or "input".
* @param {string} prevValueProp - The name of the property to hold the previous value.
* @param {string} deferHandleProp - The name of the property to hold the defer method that fire the event.
* @returns {Function}
* @private
*/
function genHandler (eventType, prevValueProp, deferHandleProp) {
// Set value and fire an input event if the value changed since the last call.
// @param {*} newValue - The new value.
return function (newValue) {
this.value = newValue;
// defer allows debounce, hidden value processing to run, and
// also the onChange handler can safely adjust focus, etc.
if (this[deferHandleProp]) {
this[deferHandleProp].remove();
}
this[deferHandleProp] = this.defer(function () {
delete this[deferHandleProp];
if (typeof newValue !== typeof this[prevValueProp] ||
this.compare(newValue, this[prevValueProp]) !== 0) { // ignore if value [eventually] set to orig val
this[prevValueProp] = newValue;
this.deliver(); // make sure rendering is in sync when event handlers are called
this.emit(eventType);
}
});
};
}
/**
* Base class intended for form widgets that have end user changeable values, i.e.
* widgets where the user can interactively change the value property by using the mouse, keyboard, touch, etc.
*
* FormValueWidget extends FormWidget to:
*
* 1. Provide helper functions to emit `change` and `input` events when the widget's value is interactively changed
* by the end user. Subclasses of FormValueWidget should call `handleOnChange()` and
* `handleOnInput()` to fire `change` and `input` events as the value changes. See
* https://html.spec.whatwg.org/multipage/forms.html#common-input-element-events for details.
* 2. Provide handling for the `readOnly` property.
*
* @mixin module:delite/FormValueWidget
* @augments module:delite/FormWidget
*/
return dcl(FormWidget, /** @lends module:delite/FormValueWidget# */{
declaredClass: "delite/FormValueWidget",
/**
* If true, this widget won't respond to user input. Similar to `disabled` except
* `readOnly` form values are submitted. FormValueWidget automatically updates
* `focusNode`'s `readOnly` property to match the widget's `readOnly` property.
* @member {boolean}
* @default false
*/
readOnly: false,
refreshRendering: function (oldValues) {
if ("tabStops" in oldValues || "readOnly" in oldValues) {
this.forEachFocusNode(function (node) {
node.readOnly = this.readOnly;
});
}
},
/**
* Compare two values (of this widget).
* @param {*} val1
* @param {*} val2
* @returns {number}
* @protected
*/
compare: function (val1, val2) {
if (typeof val1 === "number" && typeof val2 === "number") {
return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2;
} else if (val1 > val2) {
return 1;
} else if (val1 < val2) {
return -1;
} else {
return 0;
}
},
constructor: function () {
this.on("delite-activated", function () {
// Called when user may be about to start input.
// Saves the widget's current value, which is the most recent of:
//
// 1. the original value set on widget construction
// 2. the value the user set when he previously used the widget
// 3. the value the application set programatically
//
// This is all to avoid firing unnecessary change/input events in the corner case where the
// user just selects and releases the Slider handle for example.
this._previousOnChangeValue = this.value;
this._previousOnInputValue = this.value;
});
},
/**
* Sets value and fires a "change" event if the value changed since the last call.
*
* This method should be called when the value is committed,
* if that makes sense for the control, or else when the control loses focus.
* For example, it should be called when the user releases a slider's handle after dragging it,
* or when the user blurs a textbox.
* See https://html.spec.whatwg.org/multipage/forms.html#common-input-element-events for details.
*
* @param {*} newValue - The new value.
* @function
* @protected
*/
handleOnChange: genHandler("change", "_previousOnChangeValue", "_onChangeHandle"),
/**
* Sets value and fires an "input" event if the value changed since the last call.
*
* This method should be called whenever the value is changed interactively by the end user.
* For example, it should be called repeatedly as the user drags the handle of a slider,
* or on every keystroke for a textbox.
* See https://html.spec.whatwg.org/multipage/forms.html#common-input-element-events for details.
*
* @param {*} newValue - The new value.
* @function
* @protected
*/
handleOnInput: genHandler("input", "_previousOnInputValue", "_onInputHandle"),
afterFormResetCallback: function () {
if (this.value !== this.valueNode.value) {
this.value = this.valueNode.value;
}
}
});
});