cx 24.9.0 → 24.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,516 +1,516 @@
1
- import { DateTimeCulture } from "intl-io";
2
- import { StringTemplate } from "../../data/StringTemplate";
3
- import { Culture } from "../../ui";
4
- import { Cx } from "../../ui/Cx";
5
- import { Localization } from "../../ui/Localization";
6
- import { VDOM, Widget, getContent } from "../../ui/Widget";
7
- import { Console } from "../../util/Console";
8
- import { Format } from "../../util/Format";
9
- import { KeyCode } from "../../util/KeyCode";
10
- import { dateDiff } from "../../util/date/dateDiff";
11
- import { monthStart } from "../../util/date/monthStart";
12
- import { stopPropagation } from "../../util/eventCallbacks";
13
- import { isDefined } from "../../util/isDefined";
14
- import { isTouchDevice } from "../../util/isTouchDevice";
15
- import { isTouchEvent } from "../../util/isTouchEvent";
16
- import { Icon } from "../Icon";
17
- import { autoFocus } from "../autoFocus";
18
- import ClearIcon from "../icons/clear";
19
- import DropdownIcon from "../icons/drop-down";
20
- import { Dropdown } from "../overlay/Dropdown";
21
- import {
22
- tooltipMouseLeave,
23
- tooltipMouseMove,
24
- tooltipParentDidMount,
25
- tooltipParentWillReceiveProps,
26
- tooltipParentWillUnmount,
27
- } from "../overlay/tooltip-ops";
28
- import { Field, getFieldTooltip } from "./Field";
29
- import { MonthPicker } from "./MonthPicker";
30
- import { getActiveElement } from "../../util/getActiveElement";
31
-
32
- export class MonthField extends Field {
33
- declareData() {
34
- if (this.mode == "range") {
35
- this.range = true;
36
- this.mode = "edit";
37
- Console.warn('Please use the range flag on MonthFields. Syntax mode="range" is deprecated.', this);
38
- }
39
-
40
- let values = {};
41
-
42
- if (this.range) {
43
- values = {
44
- from: null,
45
- to: null,
46
- };
47
- } else {
48
- values = {
49
- value: this.emptyValue,
50
- };
51
- }
52
-
53
- super.declareData(
54
- values,
55
- {
56
- disabled: undefined,
57
- readOnly: undefined,
58
- enabled: undefined,
59
- placeholder: undefined,
60
- required: undefined,
61
- minValue: undefined,
62
- minExclusive: undefined,
63
- maxValue: undefined,
64
- maxExclusive: undefined,
65
- icon: undefined,
66
- },
67
- ...arguments,
68
- );
69
- }
70
-
71
- isEmpty(data) {
72
- return this.range ? data.from == null : data.value == null;
73
- }
74
-
75
- init() {
76
- if (!this.culture) this.culture = new DateTimeCulture(Format.culture);
77
-
78
- if (isDefined(this.hideClear)) this.showClear = !this.hideClear;
79
-
80
- if (this.alwaysShowClear) this.showClear = true;
81
-
82
- super.init();
83
- }
84
-
85
- prepareData(context, instance) {
86
- super.prepareData(context, instance);
87
-
88
- let { data } = instance;
89
-
90
- let formatOptions = {
91
- year: "numeric",
92
- month: "short",
93
- };
94
-
95
- if (!this.range && data.value) {
96
- data.date = new Date(data.value);
97
- data.formatted = this.culture.format(data.date, formatOptions);
98
- } else if (this.range && data.from && data.to) {
99
- data.from = new Date(data.from);
100
- data.to = new Date(data.to);
101
- data.to.setDate(data.to.getDate() - 1);
102
- let fromStr = this.culture.format(data.from, formatOptions);
103
- let toStr = this.culture.format(data.to, formatOptions);
104
- if (fromStr != toStr) data.formatted = fromStr + " - " + toStr;
105
- else data.formatted = fromStr;
106
- }
107
-
108
- if (data.refDate) data.refDate = monthStart(new Date(data.refDate));
109
-
110
- if (data.maxValue) data.maxValue = monthStart(new Date(data.maxValue));
111
-
112
- if (data.minValue) data.minValue = monthStart(new Date(data.minValue));
113
-
114
- instance.lastDropdown = context.lastDropdown;
115
- }
116
-
117
- validateRequired(context, instance) {
118
- var { data } = instance;
119
- if (this.range) {
120
- if (!data.from || !data.to) return this.requiredText;
121
- } else return super.validateRequired(context, instance);
122
- }
123
-
124
- validate(context, instance) {
125
- super.validate(context, instance);
126
- var { data } = instance;
127
- if (!data.error && data.date) {
128
- var d;
129
- if (data.maxValue) {
130
- d = dateDiff(data.date, data.maxValue);
131
- if (d > 0) data.error = StringTemplate.format(this.maxValueErrorText, data.maxValue);
132
- else if (d == 0 && data.maxExclusive)
133
- data.error = StringTemplate.format(this.maxExclusiveErrorText, data.maxValue);
134
- }
135
-
136
- if (data.minValue) {
137
- d = dateDiff(data.date, data.minValue);
138
- if (d < 0) data.error = StringTemplate.format(this.minValueErrorText, data.minValue);
139
- else if (d == 0 && data.minExclusive)
140
- data.error = StringTemplate.format(this.minExclusiveErrorText, data.minValue);
141
- }
142
- }
143
- }
144
-
145
- renderInput(context, instance, key) {
146
- return (
147
- <MonthInput
148
- key={key}
149
- data={instance.data}
150
- instance={instance}
151
- monthPicker={{
152
- value: this.value,
153
- from: this.from,
154
- to: this.to,
155
- range: this.range,
156
- minValue: this.minValue,
157
- maxValue: this.maxValue,
158
- minExclusive: this.minExclusive,
159
- maxExclusive: this.maxExclusive,
160
- maxValueErrorText: this.maxValueErrorText,
161
- maxExclusiveErrorText: this.maxExclusiveErrorText,
162
- minValueErrorText: this.minValueErrorText,
163
- minExclusiveErrorText: this.minExclusiveErrorText,
164
- }}
165
- label={this.labelPlacement && getContent(this.renderLabel(context, instance, "label"))}
166
- help={this.helpPlacement && getContent(this.renderHelp(context, instance, "help"))}
167
- icon={this.renderIcon(context, instance, "icon")}
168
- />
169
- );
170
- }
171
-
172
- formatValue(context, { data }) {
173
- return data.formatted || "";
174
- }
175
-
176
- parseDate(date) {
177
- if (!date) return null;
178
- if (date instanceof Date) return date;
179
- date = this.culture.parse(date, { useCurrentDateForDefaults: true });
180
- return date;
181
- }
182
-
183
- handleSelect(instance, date1, date2) {
184
- let { widget } = instance;
185
- let encode = widget.encoding || Culture.getDefaultDateEncoding();
186
- instance.setState({
187
- inputError: false,
188
- });
189
- if (this.range) {
190
- let d1 = date1 ? encode(date1) : this.emptyValue;
191
- let d2 = date2 ? encode(date2) : this.emptyValue;
192
- instance.set("from", d1);
193
- instance.set("to", d2);
194
- } else {
195
- let value = date1 ? encode(date1) : this.emptyValue;
196
- instance.set("value", value);
197
- }
198
- }
199
- }
200
-
201
- MonthField.prototype.baseClass = "monthfield";
202
- MonthField.prototype.maxValueErrorText = "Select {0:d} or before.";
203
- MonthField.prototype.maxExclusiveErrorText = "Select a date before {0:d}.";
204
- MonthField.prototype.minValueErrorText = "Select {0:d} or later.";
205
- MonthField.prototype.minExclusiveErrorText = "Select a date after {0:d}.";
206
- MonthField.prototype.inputErrorText = "Invalid date entered";
207
- MonthField.prototype.suppressErrorsUntilVisited = true;
208
- MonthField.prototype.icon = "calendar";
209
- MonthField.prototype.showClear = true;
210
- MonthField.prototype.alwaysShowClear = false;
211
- MonthField.prototype.range = false;
212
- MonthField.prototype.reactOn = "enter blur";
213
-
214
- Localization.registerPrototype("cx/widgets/MonthField", MonthField);
215
-
216
- Widget.alias("monthfield", MonthField);
217
-
218
- class MonthInput extends VDOM.Component {
219
- constructor(props) {
220
- super(props);
221
- this.props.instance.component = this;
222
- this.state = {
223
- dropdownOpen: false,
224
- focus: false,
225
- };
226
- }
227
-
228
- getDropdown() {
229
- if (this.dropdown) return this.dropdown;
230
-
231
- let { widget, lastDropdown } = this.props.instance;
232
-
233
- var dropdown = {
234
- scrollTracking: true,
235
- inline: !isTouchDevice() || !!lastDropdown,
236
- placementOrder:
237
- "down down-left down-right up up-left up-right right right-up right-down left left-up left-down",
238
- touchFriendly: true,
239
- ...widget.dropdownOptions,
240
- type: Dropdown,
241
- relatedElement: this.input,
242
- items: {
243
- type: MonthPicker,
244
- ...this.props.monthPicker,
245
- encoding: widget.encoding,
246
- autoFocus: true,
247
- onFocusOut: (e) => {
248
- this.closeDropdown(e);
249
- },
250
- onKeyDown: (e) => this.onKeyDown(e),
251
- onSelect: (e) => {
252
- let touch = isTouchEvent(e);
253
- this.closeDropdown(e, () => {
254
- if (!touch) this.input.focus();
255
- });
256
- },
257
- },
258
- constrain: true,
259
- firstChildDefinesWidth: true,
260
- };
261
-
262
- return (this.dropdown = Widget.create(dropdown));
263
- }
264
-
265
- render() {
266
- var { instance, label, help, data, icon: iconVDOM } = this.props;
267
- var { widget, state } = instance;
268
- var { CSS, baseClass, suppressErrorsUntilVisited } = widget;
269
-
270
- let insideButton, icon;
271
-
272
- if (!data.readOnly && !data.disabled) {
273
- if (
274
- widget.showClear &&
275
- (((widget.alwaysShowClear || !data.required) && !data.empty) || instance.state.inputError)
276
- )
277
- insideButton = (
278
- <div
279
- className={CSS.element(baseClass, "clear")}
280
- onMouseDown={(e) => {
281
- e.preventDefault();
282
- e.stopPropagation();
283
- }}
284
- onClick={(e) => {
285
- this.onClearClick(e);
286
- }}
287
- >
288
- <ClearIcon className={CSS.element(baseClass, "icon")} />
289
- </div>
290
- );
291
- else
292
- insideButton = (
293
- <div className={CSS.element(baseClass, "right-icon")}>
294
- <DropdownIcon className={CSS.element(baseClass, "icon")} />
295
- </div>
296
- );
297
- }
298
-
299
- if (iconVDOM) {
300
- icon = <div className={CSS.element(baseClass, "left-icon")}>{iconVDOM}</div>;
301
- }
302
-
303
- var dropdown = false;
304
- if (this.state.dropdownOpen)
305
- dropdown = (
306
- <Cx
307
- widget={this.getDropdown()}
308
- parentInstance={instance}
309
- options={{ name: "monthfield-dropdown" }}
310
- subscribe
311
- />
312
- );
313
-
314
- let empty = this.input ? !this.input.value : data.empty;
315
-
316
- return (
317
- <div
318
- className={CSS.expand(
319
- data.classNames,
320
- CSS.state({
321
- visited: state.visited,
322
- focus: this.state.focus || this.state.dropdownOpen,
323
- icon: !!icon,
324
- empty: empty && !data.placeholder,
325
- error: data.error && (state.visited || !suppressErrorsUntilVisited || !empty),
326
- }),
327
- )}
328
- style={data.style}
329
- onMouseDown={this.onMouseDown.bind(this)}
330
- onTouchStart={stopPropagation}
331
- onClick={stopPropagation}
332
- >
333
- <input
334
- id={data.id}
335
- ref={(el) => {
336
- this.input = el;
337
- }}
338
- type="text"
339
- className={CSS.expand(CSS.element(baseClass, "input"), data.inputClass)}
340
- style={data.inputStyle}
341
- defaultValue={data.formatted}
342
- disabled={data.disabled}
343
- readOnly={data.readOnly}
344
- tabIndex={data.tabIndex}
345
- placeholder={data.placeholder}
346
- onInput={(e) => this.onChange(e.target.value, "input")}
347
- onChange={(e) => this.onChange(e.target.value, "change")}
348
- onKeyDown={(e) => this.onKeyDown(e)}
349
- onBlur={(e) => {
350
- this.onBlur(e);
351
- }}
352
- onFocus={(e) => {
353
- this.onFocus(e);
354
- }}
355
- onMouseMove={(e) => tooltipMouseMove(e, ...getFieldTooltip(this.props.instance))}
356
- onMouseLeave={(e) => tooltipMouseLeave(e, ...getFieldTooltip(this.props.instance))}
357
- />
358
- {icon}
359
- {insideButton}
360
- {dropdown}
361
- {label}
362
- {help}
363
- </div>
364
- );
365
- }
366
-
367
- onMouseDown(e) {
368
- e.stopPropagation();
369
-
370
- if (this.state.dropdownOpen) this.closeDropdown(e);
371
- else {
372
- this.openDropdownOnFocus = true;
373
- }
374
-
375
- //icon click
376
- if (e.target != this.input) {
377
- e.preventDefault();
378
- if (!this.state.dropdownOpen) this.openDropdown(e);
379
- else this.input.focus();
380
- }
381
- }
382
-
383
- onFocus(e) {
384
- let { instance } = this.props;
385
- let { widget } = instance;
386
- if (widget.trackFocus) {
387
- this.setState({
388
- focus: true,
389
- });
390
- }
391
- if (this.openDropdownOnFocus) this.openDropdown(e);
392
- }
393
-
394
- onKeyDown(e) {
395
- let { instance } = this.props;
396
- if (instance.widget.handleKeyDown(e, instance) === false) return;
397
-
398
- switch (e.keyCode) {
399
- case KeyCode.enter:
400
- e.stopPropagation();
401
- this.onChange(e.target.value, "enter");
402
- break;
403
-
404
- case KeyCode.esc:
405
- if (this.state.dropdownOpen) {
406
- e.stopPropagation();
407
- this.closeDropdown(e, () => {
408
- this.input.focus();
409
- });
410
- }
411
- break;
412
-
413
- case KeyCode.left:
414
- case KeyCode.right:
415
- e.stopPropagation();
416
- break;
417
-
418
- case KeyCode.down:
419
- this.openDropdown(e);
420
- e.stopPropagation();
421
- e.preventDefault();
422
- break;
423
- }
424
- }
425
-
426
- onBlur(e) {
427
- if (!this.state.dropdownOpen) this.props.instance.setState({ visited: true });
428
-
429
- if (this.state.focus)
430
- this.setState({
431
- focus: false,
432
- });
433
- this.onChange(e.target.value, "blur");
434
- }
435
-
436
- closeDropdown(e, callback) {
437
- if (this.state.dropdownOpen) {
438
- if (this.scrollableParents)
439
- this.scrollableParents.forEach((el) => {
440
- el.removeEventListener("scroll", this.updateDropdownPosition);
441
- });
442
-
443
- this.props.instance.setState({ visited: true });
444
- this.setState({ dropdownOpen: false }, callback);
445
- } else if (callback) callback();
446
- }
447
-
448
- openDropdown(e) {
449
- var { data } = this.props.instance;
450
- this.openDropdownOnFocus = false;
451
-
452
- if (!this.state.dropdownOpen && !(data.disabled || data.readOnly)) {
453
- this.setState({ dropdownOpen: true });
454
- }
455
- }
456
-
457
- onClearClick(e) {
458
- e.stopPropagation();
459
- e.preventDefault();
460
-
461
- var { instance } = this.props;
462
- var { widget } = instance;
463
-
464
- widget.handleSelect(instance, null, null);
465
- }
466
-
467
- UNSAFE_componentWillReceiveProps(props) {
468
- var { data, state } = props.instance;
469
- if (data.formatted != this.input.value && (data.formatted != this.props.data.formatted || !state.inputError)) {
470
- this.input.value = data.formatted || "";
471
- props.instance.setState({
472
- inputError: false,
473
- });
474
- }
475
- tooltipParentWillReceiveProps(this.input, ...getFieldTooltip(this.props.instance));
476
- }
477
-
478
- componentDidMount() {
479
- tooltipParentDidMount(this.input, ...getFieldTooltip(this.props.instance));
480
- autoFocus(this.input, this);
481
- }
482
-
483
- componentDidUpdate() {
484
- autoFocus(this.input, this);
485
- }
486
-
487
- componentWillUnmount() {
488
- if (this.input == getActiveElement()) {
489
- this.onChange(this.input.value, "blur");
490
- }
491
- tooltipParentWillUnmount(this.props.instance);
492
- }
493
-
494
- onChange(inputValue, eventType) {
495
- var { instance } = this.props;
496
- var { widget } = instance;
497
-
498
- if (widget.reactOn.indexOf(eventType) == -1) return;
499
-
500
- var parts = inputValue.split("-");
501
- var date1 = widget.parseDate(parts[0]);
502
- var date2 = widget.parseDate(parts[1]) || date1;
503
-
504
- if ((date1 != null && isNaN(date1)) || (date2 != null && isNaN(date2))) {
505
- instance.setState({
506
- inputError: widget.inputErrorText,
507
- });
508
- } else if (eventType == "blur" || eventType == "enter") {
509
- if (date2) date2 = new Date(date2.getFullYear(), date2.getMonth() + 1, 1);
510
- instance.setState({
511
- visited: true,
512
- });
513
- widget.handleSelect(instance, date1, date2);
514
- }
515
- }
516
- }
1
+ import { DateTimeCulture } from "intl-io";
2
+ import { StringTemplate } from "../../data/StringTemplate";
3
+ import { Culture } from "../../ui";
4
+ import { Cx } from "../../ui/Cx";
5
+ import { Localization } from "../../ui/Localization";
6
+ import { VDOM, Widget, getContent } from "../../ui/Widget";
7
+ import { Console } from "../../util/Console";
8
+ import { Format } from "../../util/Format";
9
+ import { KeyCode } from "../../util/KeyCode";
10
+ import { dateDiff } from "../../util/date/dateDiff";
11
+ import { monthStart } from "../../util/date/monthStart";
12
+ import { stopPropagation } from "../../util/eventCallbacks";
13
+ import { isDefined } from "../../util/isDefined";
14
+ import { isTouchDevice } from "../../util/isTouchDevice";
15
+ import { isTouchEvent } from "../../util/isTouchEvent";
16
+ import { Icon } from "../Icon";
17
+ import { autoFocus } from "../autoFocus";
18
+ import ClearIcon from "../icons/clear";
19
+ import DropdownIcon from "../icons/drop-down";
20
+ import { Dropdown } from "../overlay/Dropdown";
21
+ import {
22
+ tooltipMouseLeave,
23
+ tooltipMouseMove,
24
+ tooltipParentDidMount,
25
+ tooltipParentWillReceiveProps,
26
+ tooltipParentWillUnmount,
27
+ } from "../overlay/tooltip-ops";
28
+ import { Field, getFieldTooltip } from "./Field";
29
+ import { MonthPicker } from "./MonthPicker";
30
+ import { getActiveElement } from "../../util/getActiveElement";
31
+
32
+ export class MonthField extends Field {
33
+ declareData() {
34
+ if (this.mode == "range") {
35
+ this.range = true;
36
+ this.mode = "edit";
37
+ Console.warn('Please use the range flag on MonthFields. Syntax mode="range" is deprecated.', this);
38
+ }
39
+
40
+ let values = {};
41
+
42
+ if (this.range) {
43
+ values = {
44
+ from: null,
45
+ to: null,
46
+ };
47
+ } else {
48
+ values = {
49
+ value: this.emptyValue,
50
+ };
51
+ }
52
+
53
+ super.declareData(
54
+ values,
55
+ {
56
+ disabled: undefined,
57
+ readOnly: undefined,
58
+ enabled: undefined,
59
+ placeholder: undefined,
60
+ required: undefined,
61
+ minValue: undefined,
62
+ minExclusive: undefined,
63
+ maxValue: undefined,
64
+ maxExclusive: undefined,
65
+ icon: undefined,
66
+ },
67
+ ...arguments,
68
+ );
69
+ }
70
+
71
+ isEmpty(data) {
72
+ return this.range ? data.from == null : data.value == null;
73
+ }
74
+
75
+ init() {
76
+ if (!this.culture) this.culture = new DateTimeCulture(Format.culture);
77
+
78
+ if (isDefined(this.hideClear)) this.showClear = !this.hideClear;
79
+
80
+ if (this.alwaysShowClear) this.showClear = true;
81
+
82
+ super.init();
83
+ }
84
+
85
+ prepareData(context, instance) {
86
+ super.prepareData(context, instance);
87
+
88
+ let { data } = instance;
89
+
90
+ let formatOptions = {
91
+ year: "numeric",
92
+ month: "short",
93
+ };
94
+
95
+ if (!this.range && data.value) {
96
+ data.date = new Date(data.value);
97
+ data.formatted = this.culture.format(data.date, formatOptions);
98
+ } else if (this.range && data.from && data.to) {
99
+ data.from = new Date(data.from);
100
+ data.to = new Date(data.to);
101
+ data.to.setDate(data.to.getDate() - 1);
102
+ let fromStr = this.culture.format(data.from, formatOptions);
103
+ let toStr = this.culture.format(data.to, formatOptions);
104
+ if (fromStr != toStr) data.formatted = fromStr + " - " + toStr;
105
+ else data.formatted = fromStr;
106
+ }
107
+
108
+ if (data.refDate) data.refDate = monthStart(new Date(data.refDate));
109
+
110
+ if (data.maxValue) data.maxValue = monthStart(new Date(data.maxValue));
111
+
112
+ if (data.minValue) data.minValue = monthStart(new Date(data.minValue));
113
+
114
+ instance.lastDropdown = context.lastDropdown;
115
+ }
116
+
117
+ validateRequired(context, instance) {
118
+ var { data } = instance;
119
+ if (this.range) {
120
+ if (!data.from || !data.to) return this.requiredText;
121
+ } else return super.validateRequired(context, instance);
122
+ }
123
+
124
+ validate(context, instance) {
125
+ super.validate(context, instance);
126
+ var { data } = instance;
127
+ if (!data.error && data.date) {
128
+ var d;
129
+ if (data.maxValue) {
130
+ d = dateDiff(data.date, data.maxValue);
131
+ if (d > 0) data.error = StringTemplate.format(this.maxValueErrorText, data.maxValue);
132
+ else if (d == 0 && data.maxExclusive)
133
+ data.error = StringTemplate.format(this.maxExclusiveErrorText, data.maxValue);
134
+ }
135
+
136
+ if (data.minValue) {
137
+ d = dateDiff(data.date, data.minValue);
138
+ if (d < 0) data.error = StringTemplate.format(this.minValueErrorText, data.minValue);
139
+ else if (d == 0 && data.minExclusive)
140
+ data.error = StringTemplate.format(this.minExclusiveErrorText, data.minValue);
141
+ }
142
+ }
143
+ }
144
+
145
+ renderInput(context, instance, key) {
146
+ return (
147
+ <MonthInput
148
+ key={key}
149
+ data={instance.data}
150
+ instance={instance}
151
+ monthPicker={{
152
+ value: this.value,
153
+ from: this.from,
154
+ to: this.to,
155
+ range: this.range,
156
+ minValue: this.minValue,
157
+ maxValue: this.maxValue,
158
+ minExclusive: this.minExclusive,
159
+ maxExclusive: this.maxExclusive,
160
+ maxValueErrorText: this.maxValueErrorText,
161
+ maxExclusiveErrorText: this.maxExclusiveErrorText,
162
+ minValueErrorText: this.minValueErrorText,
163
+ minExclusiveErrorText: this.minExclusiveErrorText,
164
+ }}
165
+ label={this.labelPlacement && getContent(this.renderLabel(context, instance, "label"))}
166
+ help={this.helpPlacement && getContent(this.renderHelp(context, instance, "help"))}
167
+ icon={this.renderIcon(context, instance, "icon")}
168
+ />
169
+ );
170
+ }
171
+
172
+ formatValue(context, { data }) {
173
+ return data.formatted || "";
174
+ }
175
+
176
+ parseDate(date) {
177
+ if (!date) return null;
178
+ if (date instanceof Date) return date;
179
+ date = this.culture.parse(date, { useCurrentDateForDefaults: true });
180
+ return date;
181
+ }
182
+
183
+ handleSelect(instance, date1, date2) {
184
+ let { widget } = instance;
185
+ let encode = widget.encoding || Culture.getDefaultDateEncoding();
186
+ instance.setState({
187
+ inputError: false,
188
+ });
189
+ if (this.range) {
190
+ let d1 = date1 ? encode(date1) : this.emptyValue;
191
+ let d2 = date2 ? encode(date2) : this.emptyValue;
192
+ instance.set("from", d1);
193
+ instance.set("to", d2);
194
+ } else {
195
+ let value = date1 ? encode(date1) : this.emptyValue;
196
+ instance.set("value", value);
197
+ }
198
+ }
199
+ }
200
+
201
+ MonthField.prototype.baseClass = "monthfield";
202
+ MonthField.prototype.maxValueErrorText = "Select {0:d} or before.";
203
+ MonthField.prototype.maxExclusiveErrorText = "Select a date before {0:d}.";
204
+ MonthField.prototype.minValueErrorText = "Select {0:d} or later.";
205
+ MonthField.prototype.minExclusiveErrorText = "Select a date after {0:d}.";
206
+ MonthField.prototype.inputErrorText = "Invalid date entered";
207
+ MonthField.prototype.suppressErrorsUntilVisited = true;
208
+ MonthField.prototype.icon = "calendar";
209
+ MonthField.prototype.showClear = true;
210
+ MonthField.prototype.alwaysShowClear = false;
211
+ MonthField.prototype.range = false;
212
+ MonthField.prototype.reactOn = "enter blur";
213
+
214
+ Localization.registerPrototype("cx/widgets/MonthField", MonthField);
215
+
216
+ Widget.alias("monthfield", MonthField);
217
+
218
+ class MonthInput extends VDOM.Component {
219
+ constructor(props) {
220
+ super(props);
221
+ this.props.instance.component = this;
222
+ this.state = {
223
+ dropdownOpen: false,
224
+ focus: false,
225
+ };
226
+ }
227
+
228
+ getDropdown() {
229
+ if (this.dropdown) return this.dropdown;
230
+
231
+ let { widget, lastDropdown } = this.props.instance;
232
+
233
+ var dropdown = {
234
+ scrollTracking: true,
235
+ inline: !isTouchDevice() || !!lastDropdown,
236
+ placementOrder:
237
+ "down down-left down-right up up-left up-right right right-up right-down left left-up left-down",
238
+ touchFriendly: true,
239
+ ...widget.dropdownOptions,
240
+ type: Dropdown,
241
+ relatedElement: this.input,
242
+ items: {
243
+ type: MonthPicker,
244
+ ...this.props.monthPicker,
245
+ encoding: widget.encoding,
246
+ autoFocus: true,
247
+ onFocusOut: (e) => {
248
+ this.closeDropdown(e);
249
+ },
250
+ onKeyDown: (e) => this.onKeyDown(e),
251
+ onSelect: (e) => {
252
+ let touch = isTouchEvent(e);
253
+ this.closeDropdown(e, () => {
254
+ if (!touch) this.input.focus();
255
+ });
256
+ },
257
+ },
258
+ constrain: true,
259
+ firstChildDefinesWidth: true,
260
+ };
261
+
262
+ return (this.dropdown = Widget.create(dropdown));
263
+ }
264
+
265
+ render() {
266
+ var { instance, label, help, data, icon: iconVDOM } = this.props;
267
+ var { widget, state } = instance;
268
+ var { CSS, baseClass, suppressErrorsUntilVisited } = widget;
269
+
270
+ let insideButton, icon;
271
+
272
+ if (!data.readOnly && !data.disabled) {
273
+ if (
274
+ widget.showClear &&
275
+ (((widget.alwaysShowClear || !data.required) && !data.empty) || instance.state.inputError)
276
+ )
277
+ insideButton = (
278
+ <div
279
+ className={CSS.element(baseClass, "clear")}
280
+ onMouseDown={(e) => {
281
+ e.preventDefault();
282
+ e.stopPropagation();
283
+ }}
284
+ onClick={(e) => {
285
+ this.onClearClick(e);
286
+ }}
287
+ >
288
+ <ClearIcon className={CSS.element(baseClass, "icon")} />
289
+ </div>
290
+ );
291
+ else
292
+ insideButton = (
293
+ <div className={CSS.element(baseClass, "right-icon")}>
294
+ <DropdownIcon className={CSS.element(baseClass, "icon")} />
295
+ </div>
296
+ );
297
+ }
298
+
299
+ if (iconVDOM) {
300
+ icon = <div className={CSS.element(baseClass, "left-icon")}>{iconVDOM}</div>;
301
+ }
302
+
303
+ var dropdown = false;
304
+ if (this.state.dropdownOpen)
305
+ dropdown = (
306
+ <Cx
307
+ widget={this.getDropdown()}
308
+ parentInstance={instance}
309
+ options={{ name: "monthfield-dropdown" }}
310
+ subscribe
311
+ />
312
+ );
313
+
314
+ let empty = this.input ? !this.input.value : data.empty;
315
+
316
+ return (
317
+ <div
318
+ className={CSS.expand(
319
+ data.classNames,
320
+ CSS.state({
321
+ visited: state.visited,
322
+ focus: this.state.focus || this.state.dropdownOpen,
323
+ icon: !!icon,
324
+ empty: empty && !data.placeholder,
325
+ error: data.error && (state.visited || !suppressErrorsUntilVisited || !empty),
326
+ }),
327
+ )}
328
+ style={data.style}
329
+ onMouseDown={this.onMouseDown.bind(this)}
330
+ onTouchStart={stopPropagation}
331
+ onClick={stopPropagation}
332
+ >
333
+ <input
334
+ id={data.id}
335
+ ref={(el) => {
336
+ this.input = el;
337
+ }}
338
+ type="text"
339
+ className={CSS.expand(CSS.element(baseClass, "input"), data.inputClass)}
340
+ style={data.inputStyle}
341
+ defaultValue={data.formatted}
342
+ disabled={data.disabled}
343
+ readOnly={data.readOnly}
344
+ tabIndex={data.tabIndex}
345
+ placeholder={data.placeholder}
346
+ onInput={(e) => this.onChange(e.target.value, "input")}
347
+ onChange={(e) => this.onChange(e.target.value, "change")}
348
+ onKeyDown={(e) => this.onKeyDown(e)}
349
+ onBlur={(e) => {
350
+ this.onBlur(e);
351
+ }}
352
+ onFocus={(e) => {
353
+ this.onFocus(e);
354
+ }}
355
+ onMouseMove={(e) => tooltipMouseMove(e, ...getFieldTooltip(this.props.instance))}
356
+ onMouseLeave={(e) => tooltipMouseLeave(e, ...getFieldTooltip(this.props.instance))}
357
+ />
358
+ {icon}
359
+ {insideButton}
360
+ {dropdown}
361
+ {label}
362
+ {help}
363
+ </div>
364
+ );
365
+ }
366
+
367
+ onMouseDown(e) {
368
+ e.stopPropagation();
369
+
370
+ if (this.state.dropdownOpen) this.closeDropdown(e);
371
+ else {
372
+ this.openDropdownOnFocus = true;
373
+ }
374
+
375
+ //icon click
376
+ if (e.target != this.input) {
377
+ e.preventDefault();
378
+ if (!this.state.dropdownOpen) this.openDropdown(e);
379
+ else this.input.focus();
380
+ }
381
+ }
382
+
383
+ onFocus(e) {
384
+ let { instance } = this.props;
385
+ let { widget } = instance;
386
+ if (widget.trackFocus) {
387
+ this.setState({
388
+ focus: true,
389
+ });
390
+ }
391
+ if (this.openDropdownOnFocus) this.openDropdown(e);
392
+ }
393
+
394
+ onKeyDown(e) {
395
+ let { instance } = this.props;
396
+ if (instance.widget.handleKeyDown(e, instance) === false) return;
397
+
398
+ switch (e.keyCode) {
399
+ case KeyCode.enter:
400
+ e.stopPropagation();
401
+ this.onChange(e.target.value, "enter");
402
+ break;
403
+
404
+ case KeyCode.esc:
405
+ if (this.state.dropdownOpen) {
406
+ e.stopPropagation();
407
+ this.closeDropdown(e, () => {
408
+ this.input.focus();
409
+ });
410
+ }
411
+ break;
412
+
413
+ case KeyCode.left:
414
+ case KeyCode.right:
415
+ e.stopPropagation();
416
+ break;
417
+
418
+ case KeyCode.down:
419
+ this.openDropdown(e);
420
+ e.stopPropagation();
421
+ e.preventDefault();
422
+ break;
423
+ }
424
+ }
425
+
426
+ onBlur(e) {
427
+ if (!this.state.dropdownOpen) this.props.instance.setState({ visited: true });
428
+
429
+ if (this.state.focus)
430
+ this.setState({
431
+ focus: false,
432
+ });
433
+ this.onChange(e.target.value, "blur");
434
+ }
435
+
436
+ closeDropdown(e, callback) {
437
+ if (this.state.dropdownOpen) {
438
+ if (this.scrollableParents)
439
+ this.scrollableParents.forEach((el) => {
440
+ el.removeEventListener("scroll", this.updateDropdownPosition);
441
+ });
442
+
443
+ this.props.instance.setState({ visited: true });
444
+ this.setState({ dropdownOpen: false }, callback);
445
+ } else if (callback) callback();
446
+ }
447
+
448
+ openDropdown(e) {
449
+ var { data } = this.props.instance;
450
+ this.openDropdownOnFocus = false;
451
+
452
+ if (!this.state.dropdownOpen && !(data.disabled || data.readOnly)) {
453
+ this.setState({ dropdownOpen: true });
454
+ }
455
+ }
456
+
457
+ onClearClick(e) {
458
+ e.stopPropagation();
459
+ e.preventDefault();
460
+
461
+ var { instance } = this.props;
462
+ var { widget } = instance;
463
+
464
+ widget.handleSelect(instance, null, null);
465
+ }
466
+
467
+ UNSAFE_componentWillReceiveProps(props) {
468
+ var { data, state } = props.instance;
469
+ if (data.formatted != this.input.value && (data.formatted != this.props.data.formatted || !state.inputError)) {
470
+ this.input.value = data.formatted || "";
471
+ props.instance.setState({
472
+ inputError: false,
473
+ });
474
+ }
475
+ tooltipParentWillReceiveProps(this.input, ...getFieldTooltip(this.props.instance));
476
+ }
477
+
478
+ componentDidMount() {
479
+ tooltipParentDidMount(this.input, ...getFieldTooltip(this.props.instance));
480
+ autoFocus(this.input, this);
481
+ }
482
+
483
+ componentDidUpdate() {
484
+ autoFocus(this.input, this);
485
+ }
486
+
487
+ componentWillUnmount() {
488
+ if (this.input == getActiveElement() && this.input.value != this.props.data.formatted) {
489
+ this.onChange(this.input.value, "blur");
490
+ }
491
+ tooltipParentWillUnmount(this.props.instance);
492
+ }
493
+
494
+ onChange(inputValue, eventType) {
495
+ var { instance } = this.props;
496
+ var { widget } = instance;
497
+
498
+ if (widget.reactOn.indexOf(eventType) == -1) return;
499
+
500
+ var parts = inputValue.split("-");
501
+ var date1 = widget.parseDate(parts[0]);
502
+ var date2 = widget.parseDate(parts[1]) || date1;
503
+
504
+ if ((date1 != null && isNaN(date1)) || (date2 != null && isNaN(date2))) {
505
+ instance.setState({
506
+ inputError: widget.inputErrorText,
507
+ });
508
+ } else if (eventType == "blur" || eventType == "enter") {
509
+ if (date2) date2 = new Date(date2.getFullYear(), date2.getMonth() + 1, 1);
510
+ instance.setState({
511
+ visited: true,
512
+ });
513
+ widget.handleSelect(instance, date1, date2);
514
+ }
515
+ }
516
+ }