cx 26.4.3 → 26.5.1

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 (74) hide show
  1. package/build/charts/axis/Axis.d.ts +8 -0
  2. package/build/charts/axis/Axis.d.ts.map +1 -1
  3. package/build/charts/axis/Axis.js +18 -1
  4. package/build/charts/axis/TimeAxis.js +1 -0
  5. package/build/data/AugmentedViewBase.d.ts.map +1 -1
  6. package/build/data/AugmentedViewBase.js +5 -4
  7. package/build/data/View.d.ts +0 -1
  8. package/build/data/View.d.ts.map +1 -1
  9. package/build/data/View.js +1 -3
  10. package/build/ui/Format.d.ts.map +1 -1
  11. package/build/ui/Format.js +26 -2
  12. package/build/util/Format.d.ts.map +1 -1
  13. package/build/util/Format.js +6 -0
  14. package/build/util/date/dateQuarter.d.ts +7 -0
  15. package/build/util/date/dateQuarter.d.ts.map +1 -0
  16. package/build/util/date/dateQuarter.js +8 -0
  17. package/build/util/date/dayBefore.d.ts +12 -0
  18. package/build/util/date/dayBefore.d.ts.map +1 -0
  19. package/build/util/date/dayBefore.js +15 -0
  20. package/build/util/date/index.d.ts +2 -0
  21. package/build/util/date/index.d.ts.map +1 -1
  22. package/build/util/date/index.js +2 -0
  23. package/build/widgets/form/DateTimePicker.d.ts.map +1 -1
  24. package/build/widgets/form/DateTimePicker.js +53 -31
  25. package/build/widgets/form/Field.d.ts.map +1 -1
  26. package/build/widgets/form/Field.js +2 -1
  27. package/build/widgets/form/LookupField.js +2 -1
  28. package/build/widgets/form/Wheel.d.ts +8 -0
  29. package/build/widgets/form/Wheel.d.ts.map +1 -1
  30. package/build/widgets/form/Wheel.js +30 -7
  31. package/build/widgets/grid/Grid.d.ts +1 -1
  32. package/build/widgets/grid/Grid.d.ts.map +1 -1
  33. package/dist/charts.css +6 -0
  34. package/dist/charts.js +18 -1
  35. package/dist/data.js +5 -4
  36. package/dist/manifest.js +772 -763
  37. package/dist/ui.js +33 -1
  38. package/dist/util.js +32 -0
  39. package/dist/widgets.css +9 -3
  40. package/dist/widgets.js +230 -174
  41. package/package.json +1 -1
  42. package/src/charts/RangeMarker.scss +3 -0
  43. package/src/charts/axis/Axis.tsx +31 -1
  44. package/src/charts/axis/TimeAxis.tsx +1 -0
  45. package/src/charts/index.scss +1 -0
  46. package/src/data/AugmentedViewBase.ts +5 -4
  47. package/src/data/View.ts +16 -61
  48. package/src/ui/DataProxy.ts +55 -55
  49. package/src/ui/Format.spec.ts +32 -0
  50. package/src/ui/Format.ts +27 -2
  51. package/src/ui/Rescope.ts +50 -50
  52. package/src/ui/adapter/ArrayAdapter.ts +229 -229
  53. package/src/ui/exprHelpers.ts +96 -96
  54. package/src/util/Format.spec.ts +11 -0
  55. package/src/util/Format.ts +7 -0
  56. package/src/util/date/dateQuarter.ts +8 -0
  57. package/src/util/date/dayBefore.ts +15 -0
  58. package/src/util/date/index.ts +2 -0
  59. package/src/widgets/Sandbox.ts +104 -104
  60. package/src/widgets/form/ColorField.scss +112 -112
  61. package/src/widgets/form/DateTimeField.scss +111 -111
  62. package/src/widgets/form/DateTimePicker.tsx +453 -392
  63. package/src/widgets/form/Field.tsx +2 -1
  64. package/src/widgets/form/LookupField.maps.scss +26 -26
  65. package/src/widgets/form/LookupField.scss +10 -3
  66. package/src/widgets/form/LookupField.tsx +4 -1
  67. package/src/widgets/form/MonthField.scss +113 -113
  68. package/src/widgets/form/NumberField.scss +72 -72
  69. package/src/widgets/form/Select.scss +104 -104
  70. package/src/widgets/form/TextField.scss +66 -66
  71. package/src/widgets/form/ValidationGroup.spec.tsx +30 -1
  72. package/src/widgets/form/Wheel.tsx +36 -7
  73. package/src/widgets/grid/Grid.tsx +1 -1
  74. package/src/widgets/nav/MenuItem.tsx +525 -525
@@ -1,104 +1,104 @@
1
- @use "sass:math";
2
- @use "sass:map";
3
- @use "../variables" as *;
4
- @use "../maps" as *;
5
- @use "../../util/scss/add-rules.scss" as *;
6
- @use "../../util/scss/calc.scss" as *;
7
- @use "../../util/scss/clockwise.scss" as *;
8
- @use "../../util/scss/besm.scss" as *;
9
- @use "../../util/scss/include.scss" as *;
10
- @use "./Field.scss" as *;
11
-
12
- @mixin cx-select(
13
- $name: "select",
14
- $state-style-map: $cx-std-field-state-style-map,
15
- $placeholder: $cx-input-placeholder,
16
- $empty-text: $cx-empty-text,
17
- $width: $cx-default-input-width,
18
- $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
19
- $clear-state-style-map: $cx-clear-state-style-map,
20
- $tool-state-style-map: $cx-input-right-icon-state-style-map,
21
- $icon-size: $cx-default-input-icon-size,
22
- $besm: $cx-besm
23
- ) {
24
- $block: map.get($besm, block);
25
- $element: map.get($besm, element);
26
- $state: map.get($besm, state);
27
- $mod: map.get($besm, mod);
28
-
29
- .#{$block}#{$name} {
30
- @include cxb-field($besm, $state-style-map: $state-style-map, $width: $width, $input: true);
31
-
32
- .#{$element}#{$name}-left-icon {
33
- pointer-events: none;
34
- @include cxe-field-button($besm, $left-icon-state-style-map);
35
- }
36
- }
37
-
38
- $padding: cx-get-state-rule($state-style-map, default, "padding");
39
-
40
- .#{$element}#{$name}-select {
41
- @include cxe-field-input(
42
- $besm,
43
- $state-style-map,
44
- $placeholder: $placeholder,
45
- $overrides: (
46
- padding: $cx-default-input-padding-top
47
- calc(#{$cx-default-input-padding-right} + #{$cx-default-clear-size} - #{$cx-default-input-padding-right})
48
- $cx-default-input-padding-bottom $cx-default-input-padding-left,
49
- -webkit-appearance: none,
50
- -moz-appearance: none,
51
- -ms-appearance: none,
52
- appearance: none
53
- )
54
- );
55
-
56
- .#{$state}icon > & {
57
- padding-left: cx-calc(cx-top($padding), $cx-default-input-left-tool-size, $cx-default-input-left-tool-spacing);
58
- }
59
-
60
- .#{$state}empty > & {
61
- @include cx-add-rules($placeholder);
62
- }
63
-
64
- &::-ms-expand {
65
- display: none;
66
- margin: 0;
67
- padding: 0;
68
- }
69
-
70
- option {
71
- background-color: $cx-default-dropdown-background-color;
72
- color: cx-get-state-rule($state-style-map, default, color);
73
- }
74
- }
75
-
76
- .#{$element}#{$name}-clear {
77
- @include cxe-field-button($besm, $clear-state-style-map);
78
- }
79
-
80
- .#{$element}#{$name}-placeholder {
81
- @include cx-add-rules($placeholder);
82
- }
83
-
84
- .#{$element}#{$name}-tool {
85
- pointer-events: none; //cannot call open-dropdown on native element
86
- @include cxe-field-button($besm, $tool-state-style-map);
87
-
88
- .#{$state}disabled > & {
89
- border-width: 0;
90
- @include cx-add-state-rules($state-style-map, disabled);
91
- }
92
- .#{$state}focus > & {
93
- @include cx-add-state-rules($tool-state-style-map, focus);
94
- }
95
- }
96
-
97
- .#{$element}#{$name}-icon {
98
- @include cxe-field-button-icon($besm, $icon-size);
99
- }
100
- }
101
-
102
- @if (cx-should-include("cx/widgets/Select")) {
103
- @include cx-select;
104
- }
1
+ @use "sass:math";
2
+ @use "sass:map";
3
+ @use "../variables" as *;
4
+ @use "../maps" as *;
5
+ @use "../../util/scss/add-rules.scss" as *;
6
+ @use "../../util/scss/calc.scss" as *;
7
+ @use "../../util/scss/clockwise.scss" as *;
8
+ @use "../../util/scss/besm.scss" as *;
9
+ @use "../../util/scss/include.scss" as *;
10
+ @use "./Field.scss" as *;
11
+
12
+ @mixin cx-select(
13
+ $name: "select",
14
+ $state-style-map: $cx-std-field-state-style-map,
15
+ $placeholder: $cx-input-placeholder,
16
+ $empty-text: $cx-empty-text,
17
+ $width: $cx-default-input-width,
18
+ $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
19
+ $clear-state-style-map: $cx-clear-state-style-map,
20
+ $tool-state-style-map: $cx-input-right-icon-state-style-map,
21
+ $icon-size: $cx-default-input-icon-size,
22
+ $besm: $cx-besm
23
+ ) {
24
+ $block: map.get($besm, block);
25
+ $element: map.get($besm, element);
26
+ $state: map.get($besm, state);
27
+ $mod: map.get($besm, mod);
28
+
29
+ .#{$block}#{$name} {
30
+ @include cxb-field($besm, $state-style-map: $state-style-map, $width: $width, $input: true);
31
+
32
+ .#{$element}#{$name}-left-icon {
33
+ pointer-events: none;
34
+ @include cxe-field-button($besm, $left-icon-state-style-map);
35
+ }
36
+ }
37
+
38
+ $padding: cx-get-state-rule($state-style-map, default, "padding");
39
+
40
+ .#{$element}#{$name}-select {
41
+ @include cxe-field-input(
42
+ $besm,
43
+ $state-style-map,
44
+ $placeholder: $placeholder,
45
+ $overrides: (
46
+ padding: $cx-default-input-padding-top
47
+ calc(#{$cx-default-input-padding-right} + #{$cx-default-clear-size} - #{$cx-default-input-padding-right})
48
+ $cx-default-input-padding-bottom $cx-default-input-padding-left,
49
+ -webkit-appearance: none,
50
+ -moz-appearance: none,
51
+ -ms-appearance: none,
52
+ appearance: none
53
+ )
54
+ );
55
+
56
+ .#{$state}icon > & {
57
+ padding-left: cx-calc(cx-top($padding), $cx-default-input-left-tool-size, $cx-default-input-left-tool-spacing);
58
+ }
59
+
60
+ .#{$state}empty > & {
61
+ @include cx-add-rules($placeholder);
62
+ }
63
+
64
+ &::-ms-expand {
65
+ display: none;
66
+ margin: 0;
67
+ padding: 0;
68
+ }
69
+
70
+ option {
71
+ background-color: $cx-default-dropdown-background-color;
72
+ color: cx-get-state-rule($state-style-map, default, color);
73
+ }
74
+ }
75
+
76
+ .#{$element}#{$name}-clear {
77
+ @include cxe-field-button($besm, $clear-state-style-map);
78
+ }
79
+
80
+ .#{$element}#{$name}-placeholder {
81
+ @include cx-add-rules($placeholder);
82
+ }
83
+
84
+ .#{$element}#{$name}-tool {
85
+ pointer-events: none; //cannot call open-dropdown on native element
86
+ @include cxe-field-button($besm, $tool-state-style-map);
87
+
88
+ .#{$state}disabled > & {
89
+ border-width: 0;
90
+ @include cx-add-state-rules($state-style-map, disabled);
91
+ }
92
+ .#{$state}focus > & {
93
+ @include cx-add-state-rules($tool-state-style-map, focus);
94
+ }
95
+ }
96
+
97
+ .#{$element}#{$name}-icon {
98
+ @include cxe-field-button-icon($besm, $icon-size);
99
+ }
100
+ }
101
+
102
+ @if (cx-should-include("cx/widgets/Select")) {
103
+ @include cx-select;
104
+ }
@@ -1,66 +1,66 @@
1
- @use "sass:math";
2
- @use "sass:map";
3
- @use "../variables" as *;
4
- @use "../maps" as *;
5
- @use "../../util/scss/add-rules.scss" as *;
6
- @use "../../util/scss/calc.scss" as *;
7
- @use "../../util/scss/clockwise.scss" as *;
8
- @use "../../util/scss/besm.scss" as *;
9
- @use "../../util/scss/include.scss" as *;
10
- @use "./Field.scss" as *;
11
-
12
- @mixin cx-textfield(
13
- $name: "textfield",
14
- $state-style-map: $cx-std-field-state-style-map,
15
- $placeholder: $cx-input-placeholder,
16
- $empty-text: $cx-empty-text,
17
- $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
18
- $clear-state-style-map: $cx-clear-state-style-map,
19
- $icon-size: $cx-default-input-icon-size,
20
- $width: $cx-default-input-width,
21
- $besm: $cx-besm
22
- ) {
23
- $block: map.get($besm, block);
24
- $element: map.get($besm, element);
25
- $state: map.get($besm, state);
26
- $mod: map.get($besm, mod);
27
-
28
- .#{$block}#{$name} {
29
- @include cxb-field($besm, $state-style-map, $width: $width, $input: true);
30
- }
31
-
32
- $padding: cx-get-state-rule($state-style-map, default, "padding");
33
-
34
- .#{$element}#{$name}-input {
35
- @include cxe-field-input($besm, $state-style-map, $placeholder: $placeholder);
36
-
37
- .#{$state}icon > & {
38
- padding-left: cx-calc(cx-top($padding), $cx-default-input-left-tool-size, $cx-default-input-left-tool-spacing);
39
- }
40
-
41
- .#{$state}clear > & {
42
- padding-right: cx-calc(cx-top($padding), $cx-default-clear-size, $cx-default-clear-spacing);
43
- }
44
- }
45
-
46
- .#{$element}#{$name}-left-icon {
47
- pointer-events: none;
48
- @include cxe-field-button($besm, $left-icon-state-style-map);
49
- }
50
-
51
- .#{$element}#{$name}-clear {
52
- @include cxe-field-button($besm, $clear-state-style-map);
53
- }
54
-
55
- .#{$element}#{$name}-icon {
56
- @include cxe-field-button-icon($besm, $icon-size);
57
- }
58
-
59
- .#{$element}#{$name}-empty-text {
60
- @include cxe-field-empty-text($empty-text);
61
- }
62
- }
63
-
64
- @if (cx-should-include("cx/widgets/TextField")) {
65
- @include cx-textfield();
66
- }
1
+ @use "sass:math";
2
+ @use "sass:map";
3
+ @use "../variables" as *;
4
+ @use "../maps" as *;
5
+ @use "../../util/scss/add-rules.scss" as *;
6
+ @use "../../util/scss/calc.scss" as *;
7
+ @use "../../util/scss/clockwise.scss" as *;
8
+ @use "../../util/scss/besm.scss" as *;
9
+ @use "../../util/scss/include.scss" as *;
10
+ @use "./Field.scss" as *;
11
+
12
+ @mixin cx-textfield(
13
+ $name: "textfield",
14
+ $state-style-map: $cx-std-field-state-style-map,
15
+ $placeholder: $cx-input-placeholder,
16
+ $empty-text: $cx-empty-text,
17
+ $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
18
+ $clear-state-style-map: $cx-clear-state-style-map,
19
+ $icon-size: $cx-default-input-icon-size,
20
+ $width: $cx-default-input-width,
21
+ $besm: $cx-besm
22
+ ) {
23
+ $block: map.get($besm, block);
24
+ $element: map.get($besm, element);
25
+ $state: map.get($besm, state);
26
+ $mod: map.get($besm, mod);
27
+
28
+ .#{$block}#{$name} {
29
+ @include cxb-field($besm, $state-style-map, $width: $width, $input: true);
30
+ }
31
+
32
+ $padding: cx-get-state-rule($state-style-map, default, "padding");
33
+
34
+ .#{$element}#{$name}-input {
35
+ @include cxe-field-input($besm, $state-style-map, $placeholder: $placeholder);
36
+
37
+ .#{$state}icon > & {
38
+ padding-left: cx-calc(cx-top($padding), $cx-default-input-left-tool-size, $cx-default-input-left-tool-spacing);
39
+ }
40
+
41
+ .#{$state}clear > & {
42
+ padding-right: cx-calc(cx-top($padding), $cx-default-clear-size, $cx-default-clear-spacing);
43
+ }
44
+ }
45
+
46
+ .#{$element}#{$name}-left-icon {
47
+ pointer-events: none;
48
+ @include cxe-field-button($besm, $left-icon-state-style-map);
49
+ }
50
+
51
+ .#{$element}#{$name}-clear {
52
+ @include cxe-field-button($besm, $clear-state-style-map);
53
+ }
54
+
55
+ .#{$element}#{$name}-icon {
56
+ @include cxe-field-button-icon($besm, $icon-size);
57
+ }
58
+
59
+ .#{$element}#{$name}-empty-text {
60
+ @include cxe-field-empty-text($empty-text);
61
+ }
62
+ }
63
+
64
+ @if (cx-should-include("cx/widgets/TextField")) {
65
+ @include cx-textfield();
66
+ }
@@ -1,8 +1,9 @@
1
1
  import { Store } from "../../data/Store";
2
2
  import { ValidationGroup } from "./ValidationGroup";
3
3
  import { Validator } from "./Validator";
4
+ import { TextField } from "./TextField";
4
5
  import { bind } from "../../ui/bind";
5
- import { createTestRenderer } from "../../util/test/createTestRenderer";
6
+ import { createTestRenderer, act } from "../../util/test/createTestRenderer";
6
7
 
7
8
  import assert from "assert";
8
9
 
@@ -107,6 +108,34 @@ describe("ValidationGroup", () => {
107
108
  assert(visited);
108
109
  });
109
110
 
111
+ it("visited flag changes are propagated to fields after being initialized to false", async () => {
112
+ let widget = (
113
+ <cx>
114
+ <ValidationGroup errors={bind("errors")} visited={bind("form.visited")}>
115
+ <TextField required value={bind("value1")} />
116
+ </ValidationGroup>
117
+ </cx>
118
+ );
119
+
120
+ let store = new Store();
121
+ //initializing the bound value to false used to shadow later updates
122
+ store.set("form.visited", false);
123
+
124
+ const component = await createTestRenderer(store, widget);
125
+
126
+ let errors = store.get("errors");
127
+ assert.equal(errors.length, 1);
128
+ assert.equal(errors[0].visited, false);
129
+
130
+ await act(async () => {
131
+ store.set("form.visited", true);
132
+ });
133
+
134
+ errors = store.get("errors");
135
+ assert.equal(errors.length, 1);
136
+ assert.equal(errors[0].visited, true);
137
+ });
138
+
110
139
  it("disabled flag can be overruled by the field props", async () => {
111
140
  let widget = (
112
141
  <cx>
@@ -65,6 +65,10 @@ Wheel.prototype.baseClass = "wheel";
65
65
  Wheel.prototype.size = 3;
66
66
  Wheel.prototype.styled = true;
67
67
 
68
+ /** A cyclic wheel renders its option list this many times, leaving scroll
69
+ * headroom on both sides of the centre copy. */
70
+ export const WHEEL_BUFFER_COPIES = 3;
71
+
68
72
  export interface WheelComponentProps {
69
73
  size: number;
70
74
  children: React.ReactNode[];
@@ -74,6 +78,11 @@ export interface WheelComponentProps {
74
78
  className?: string;
75
79
  style?: React.CSSProperties;
76
80
  index?: number;
81
+ /** Set to render the option list as an endlessly scrolling wheel. The list
82
+ * is rendered `WHEEL_BUFFER_COPIES` times and the wheel snaps to whichever
83
+ * copy of the selected value is nearest its current position, so a wrap
84
+ * scrolls seamlessly into the adjacent identical copy. */
85
+ cycle?: boolean;
77
86
  onChange: (newIndex: number) => void;
78
87
  onPipeKeyDown?: (fn: (e: React.KeyboardEvent) => void) => void;
79
88
  onMouseDown?: () => void;
@@ -109,23 +118,30 @@ export class WheelComponent extends VDOM.Component<WheelComponentProps, WheelCom
109
118
  scrollRef: (el: HTMLDivElement | null) => void;
110
119
 
111
120
  render(): React.ReactNode {
112
- let { size, children, CSS, baseClass, active, className, style, onMouseDown } = this.props;
121
+ let { size, children, CSS, baseClass, active, className, style, onMouseDown, cycle } = this.props;
113
122
  let optionClass = CSS.element(baseClass, "option");
114
123
  let dummyClass = CSS.element(baseClass, "option", { dummy: true });
115
124
 
125
+ // A cyclic wheel repeats the option list so it can scroll past either end.
126
+ let options = children;
127
+ if (cycle) {
128
+ options = [];
129
+ for (let i = 0; i < WHEEL_BUFFER_COPIES; i++) options.push(...children);
130
+ }
131
+
116
132
  let tpad = [],
117
133
  bpad = [],
118
134
  padSize = 0;
119
135
 
120
136
  for (let i = 0; i < (size - 1) / 2; i++) {
121
- tpad.push({ key: -1 - i, child: children[0], cls: dummyClass });
122
- bpad.push({ key: -100 - i, child: children[0], cls: dummyClass });
137
+ tpad.push({ key: -1 - i, child: options[0], cls: dummyClass });
138
+ bpad.push({ key: -100 - i, child: options[0], cls: dummyClass });
123
139
  padSize++;
124
140
  }
125
141
 
126
142
  let displayedOptions = [
127
143
  ...tpad,
128
- ...children.map((c, i) => ({
144
+ ...options.map((c, i) => ({
129
145
  key: i,
130
146
  child: c,
131
147
  cls: optionClass,
@@ -228,7 +244,19 @@ export class WheelComponent extends VDOM.Component<WheelComponentProps, WheelCom
228
244
  }
229
245
 
230
246
  UNSAFE_componentWillReceiveProps(props: WheelComponentProps): void {
231
- this.index = props.index || 0;
247
+ let newIndex = props.index || 0;
248
+ // A cyclic wheel renders the option list several times, so the same value
249
+ // appears in several copies. Snap to the copy nearest the wheel's current
250
+ // position rather than the one `props.index` names — a wrap then scrolls
251
+ // one item into the adjacent (identical) copy with no jump, instead of
252
+ // animating all the way back to the centre copy.
253
+ if (props.cycle) {
254
+ let period = props.children.length;
255
+ let value = ((newIndex % period) + period) % period;
256
+ newIndex = value + period * Math.round((this.index - value) / period);
257
+ newIndex = Math.max(0, Math.min(period * WHEEL_BUFFER_COPIES - 1, newIndex));
258
+ }
259
+ this.index = newIndex;
232
260
  this.scrollTo();
233
261
  }
234
262
 
@@ -272,8 +300,9 @@ export class WheelComponent extends VDOM.Component<WheelComponentProps, WheelCom
272
300
  }
273
301
 
274
302
  select(newIndex: number): void {
275
- let { children } = this.props;
276
- newIndex = Math.max(0, Math.min(children.length - 1, newIndex));
303
+ let { children, cycle } = this.props;
304
+ let length = children.length * (cycle ? WHEEL_BUFFER_COPIES : 1);
305
+ newIndex = Math.max(0, Math.min(length - 1, newIndex));
277
306
  if (this.index !== newIndex) {
278
307
  this.index = newIndex;
279
308
  this.props.onChange(newIndex);
@@ -405,7 +405,7 @@ export interface GridConfig<T = any> extends StyledContainerConfig {
405
405
  onColumnContextMenu?: string | ((e: React.MouseEvent<any>, columnInstance: Instance) => void);
406
406
 
407
407
  /** Callback to create a filter function for given filter params. */
408
- onCreateFilter?: (filterParams: any, instance?: Instance) => (record: T) => boolean;
408
+ onCreateFilter?: (filterParams: any, instance?: Instance) => undefined | null | ((record: T) => boolean);
409
409
 
410
410
  /** Enable infinite scrolling */
411
411
  infinite?: boolean;