cx 24.11.3 → 25.1.0

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