cx 25.6.2 → 25.6.3

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 (103) hide show
  1. package/dist/manifest.js +749 -749
  2. package/dist/widgets.css +5 -0
  3. package/dist/widgets.js +77 -34
  4. package/package.json +1 -1
  5. package/src/charts/Legend.d.ts +45 -45
  6. package/src/charts/LegendEntry.js +128 -128
  7. package/src/charts/LegendEntry.scss +27 -27
  8. package/src/charts/PieChart.d.ts +92 -92
  9. package/src/charts/RangeMarker.js +159 -159
  10. package/src/charts/axis/Axis.d.ts +113 -113
  11. package/src/charts/axis/Axis.js +280 -280
  12. package/src/charts/axis/CategoryAxis.d.ts +30 -30
  13. package/src/charts/axis/CategoryAxis.js +241 -241
  14. package/src/charts/axis/NumericAxis.js +351 -351
  15. package/src/charts/axis/Stack.js +55 -55
  16. package/src/charts/axis/TimeAxis.js +611 -611
  17. package/src/charts/helpers/SnapPointFinder.js +69 -69
  18. package/src/data/Binding.spec.js +69 -69
  19. package/src/data/ExposedValueView.d.ts +19 -19
  20. package/src/data/Expression.js +229 -229
  21. package/src/data/Expression.spec.js +229 -229
  22. package/src/data/StringTemplate.js +92 -92
  23. package/src/data/StringTemplate.spec.js +132 -132
  24. package/src/data/StructuredSelector.js +132 -132
  25. package/src/data/getAccessor.spec.js +11 -11
  26. package/src/data/getSelector.js +49 -49
  27. package/src/hooks/createLocalStorageRef.d.ts +3 -3
  28. package/src/hooks/createLocalStorageRef.js +20 -20
  29. package/src/index.scss +6 -6
  30. package/src/ui/Culture.d.ts +57 -57
  31. package/src/ui/Culture.js +139 -139
  32. package/src/ui/FocusManager.js +171 -171
  33. package/src/ui/Format.js +108 -108
  34. package/src/ui/HoverSync.js +147 -147
  35. package/src/ui/Repeater.d.ts +61 -61
  36. package/src/ui/index.d.ts +42 -42
  37. package/src/ui/layout/ContentPlaceholder.d.ts +19 -19
  38. package/src/ui/layout/ContentPlaceholder.js +105 -105
  39. package/src/ui/layout/ContentPlaceholder.spec.js +579 -579
  40. package/src/ui/layout/LabelsTopLayout.js +134 -134
  41. package/src/util/Format.js +270 -270
  42. package/src/util/date/encodeDate.d.ts +1 -1
  43. package/src/util/date/encodeDate.js +8 -8
  44. package/src/util/date/encodeDateWithTimezoneOffset.d.ts +1 -1
  45. package/src/util/date/index.d.ts +11 -11
  46. package/src/util/date/index.js +11 -11
  47. package/src/util/date/parseDateInvariant.d.ts +3 -3
  48. package/src/util/date/parseDateInvariant.js +20 -20
  49. package/src/util/getSearchQueryPredicate.js +59 -59
  50. package/src/util/index.d.ts +51 -51
  51. package/src/util/index.js +54 -54
  52. package/src/util/isValidIdentifierName.d.ts +1 -1
  53. package/src/util/isValidIdentifierName.js +5 -5
  54. package/src/util/isValidIdentifierName.spec.js +33 -33
  55. package/src/util/scss/add-rules.scss +38 -38
  56. package/src/widgets/CxCredit.scss +37 -37
  57. package/src/widgets/HighlightedSearchText.js +36 -36
  58. package/src/widgets/HighlightedSearchText.scss +18 -18
  59. package/src/widgets/List.scss +91 -91
  60. package/src/widgets/Sandbox.d.ts +18 -16
  61. package/src/widgets/Sandbox.js +65 -63
  62. package/src/widgets/drag-drop/DropZone.js +214 -214
  63. package/src/widgets/form/Calendar.js +618 -618
  64. package/src/widgets/form/Calendar.scss +196 -196
  65. package/src/widgets/form/Checkbox.scss +127 -127
  66. package/src/widgets/form/ColorField.js +397 -397
  67. package/src/widgets/form/ColorField.scss +96 -96
  68. package/src/widgets/form/ColorPicker.scss +283 -283
  69. package/src/widgets/form/DateTimeField.js +576 -576
  70. package/src/widgets/form/DateTimePicker.js +392 -392
  71. package/src/widgets/form/LookupField.d.ts +179 -179
  72. package/src/widgets/form/LookupField.scss +219 -219
  73. package/src/widgets/form/MonthPicker.d.ts +8 -0
  74. package/src/widgets/form/MonthPicker.js +65 -23
  75. package/src/widgets/form/MonthPicker.scss +4 -0
  76. package/src/widgets/form/NumberField.js +459 -459
  77. package/src/widgets/form/NumberField.scss +61 -61
  78. package/src/widgets/form/Radio.scss +121 -121
  79. package/src/widgets/form/Select.scss +99 -99
  80. package/src/widgets/form/Slider.scss +118 -118
  81. package/src/widgets/form/Switch.scss +140 -140
  82. package/src/widgets/form/TextArea.scss +43 -43
  83. package/src/widgets/form/TextField.js +290 -290
  84. package/src/widgets/form/TextField.scss +55 -55
  85. package/src/widgets/form/UploadButton.d.ts +34 -34
  86. package/src/widgets/form/variables.scss +353 -353
  87. package/src/widgets/grid/Grid.d.ts +442 -442
  88. package/src/widgets/grid/GridRow.js +228 -228
  89. package/src/widgets/grid/TreeNode.d.ts +23 -23
  90. package/src/widgets/grid/TreeNode.scss +88 -88
  91. package/src/widgets/grid/variables.scss +133 -133
  92. package/src/widgets/nav/LinkButton.js +128 -128
  93. package/src/widgets/nav/Menu.scss +74 -74
  94. package/src/widgets/overlay/Dropdown.js +612 -612
  95. package/src/widgets/overlay/FlyweightTooltipTracker.js +39 -39
  96. package/src/widgets/overlay/Overlay.d.ts +73 -73
  97. package/src/widgets/overlay/Window.js +202 -202
  98. package/src/widgets/overlay/captureMouse.js +124 -124
  99. package/src/widgets/overlay/createHotPromiseWindowFactory.d.ts +18 -18
  100. package/src/widgets/overlay/createHotPromiseWindowFactory.js +56 -56
  101. package/src/widgets/overlay/index.d.ts +11 -11
  102. package/src/widgets/overlay/index.js +11 -11
  103. package/src/widgets/variables.scss +144 -144
@@ -1,219 +1,219 @@
1
- @mixin cx-lookupfield(
2
- $name: "lookupfield",
3
- $state-style-map: $cx-std-field-state-style-map,
4
- $placeholder: $cx-input-placeholder,
5
- $empty-text: $cx-empty-text,
6
- $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
7
- $clear-state-style-map: $cx-clear-state-style-map,
8
- $tool-state-style-map: $cx-input-right-icon-state-style-map,
9
- $icon-size: $cx-default-input-icon-size,
10
- $width: $cx-default-input-width,
11
- $tag-state-style-map: $cx-input-tag-state-style-map,
12
- $tag-clear-state-style-map: $cx-input-tag-clear-state-style-map,
13
- $besm: $cx-besm
14
- ) {
15
- $block: map-get($besm, block);
16
- $element: map-get($besm, element);
17
- $state: map-get($besm, state);
18
-
19
- $padding: cx-get-state-rule($state-style-map, default, padding);
20
- $border-radius-offset: round(cx-get-state-rule($tag-state-style-map, default, border-radius) * 0.25);
21
-
22
- .#{$block}#{$name} {
23
- cursor: pointer;
24
- width: $width;
25
- font: cx-get-state-rule($state-style-map, default, "font");
26
- font-size: cx-get-state-rule($state-style-map, default, "font-size");
27
-
28
- @include cxb-field($besm, $state-style-map, $input: false);
29
-
30
- &.#{$state}multiple {
31
- min-width: $cx-default-input-width;
32
- width: auto;
33
- }
34
-
35
- &.#{$state}readonly {
36
- pointer-events: none;
37
-
38
- & > .#{$element}#{$name}-input {
39
- padding-right: cx-right($padding);
40
- }
41
- }
42
- }
43
-
44
- .#{$element}#{$name}-input {
45
- @include cxe-field-input(
46
- $besm,
47
- $state-style-map,
48
- $placeholder: $placeholder,
49
- $input: false,
50
- $overrides: (
51
- default: (
52
- height: 100%,
53
- text-overflow: null,
54
- padding: cx-top($padding)
55
- cx-calc(cx-right($padding), $cx-default-clear-size, $cx-default-input-tag-spacing) cx-bottom($padding)
56
- cx-left($padding),
57
- )
58
- )
59
- );
60
-
61
- .#{$state}multiple > & {
62
- padding: 0 cx-calc(cx-right($padding), $cx-default-clear-size, $cx-default-input-tag-spacing)
63
- $cx-default-input-tag-spacing 0;
64
- align-items: center;
65
- flex-wrap: wrap;
66
- display: flex;
67
- }
68
-
69
- .#{$state}single > & {
70
- overflow: hidden;
71
- text-overflow: ellipsis;
72
- white-space: nowrap;
73
- display: block;
74
- flex-basis: 0%;
75
- flex-grow: 1;
76
- }
77
-
78
- .#{$state}icon > & {
79
- padding-left: cx-calc(2 * cx-left($padding), $icon-size);
80
- }
81
- }
82
-
83
- .#{$element}#{$name}-tag {
84
- display: inline-block;
85
- position: relative;
86
- box-sizing: border-box;
87
-
88
- @include cx-add-state-rules($tag-state-style-map, default);
89
-
90
- &:hover {
91
- @include cx-add-state-rules($tag-state-style-map, hover);
92
- }
93
-
94
- &.#{$state}readonly {
95
- padding-right: cx-right($padding);
96
- pointer-events: none;
97
- }
98
- }
99
-
100
- .#{$element}#{$name}-placeholder {
101
- color: $cx-default-input-placeholder-color;
102
-
103
- .#{$block}#{$name}.#{$state}multiple & {
104
- display: inline-block;
105
- margin: $cx-default-input-tag-spacing 0 0 $cx-default-input-tag-spacing;
106
- box-sizing: border-box;
107
- padding: cx-calc(cx-top($cx-default-input-padding), -$cx-default-input-tag-spacing)
108
- cx-calc(cx-right($cx-default-input-padding), -$cx-default-input-tag-spacing)
109
- cx-calc(cx-bottom($cx-default-input-padding), -$cx-default-input-tag-spacing)
110
- cx-calc(cx-left($cx-default-input-padding), -$cx-default-input-tag-spacing);
111
- }
112
- }
113
-
114
- .#{$element}#{$name}-tag-value {
115
- padding: 0 cx-calc($cx-default-input-tag-spacing, $border-radius-offset);
116
- display: inline-block;
117
- }
118
-
119
- .#{$element}#{$name}-tag-clear {
120
- @include cxe-field-button($besm, $tag-clear-state-style-map);
121
- }
122
-
123
- .#{$element}#{$name}-lookup-options {
124
- padding: 0;
125
- margin: 0;
126
- min-width: 100%;
127
- width: 100%;
128
- }
129
-
130
- .#{$element}#{$name}-query {
131
- @include cxe-field-input(
132
- $besm,
133
- $state-style-map,
134
- $placeholder: $placeholder,
135
- $width: calc(100% - 10px),
136
- $overrides: (
137
- default: (
138
- margin: 5px,
139
- flex: 0 0 auto,
140
- height: auto,
141
- )
142
- )
143
- );
144
- //put query field down when placed up
145
- .#{$state}place-up-right &,
146
- .#{$state}place-up &,
147
- .#{$state}place-up-left & {
148
- order: 1;
149
- }
150
- }
151
-
152
- .#{$element}#{$name}-dropdown {
153
- display: flex;
154
- flex-direction: column;
155
- height: 100%;
156
-
157
- &:focus {
158
- outline: none;
159
- }
160
- }
161
-
162
- .#{$element}#{$name}-message {
163
- font-size: smaller;
164
- padding: 10px;
165
- color: gray;
166
- font-style: italic;
167
-
168
- &.#{$state}error {
169
- color: #9e2f2d;
170
- }
171
- }
172
-
173
- .#{$element}#{$name}-scroll-container {
174
- overflow: auto;
175
- flex: 1 1 auto;
176
- }
177
-
178
- .#{$element}#{$name}-clear {
179
- @include cxe-field-button($besm, $clear-state-style-map);
180
-
181
- .#{$state}focus > & {
182
- @include cx-add-state-rules($clear-state-style-map, focus);
183
- }
184
- .#{$state}error > & {
185
- @include cx-add-state-rules($clear-state-style-map, error);
186
- }
187
- }
188
-
189
- .#{$element}#{$name}-tool {
190
- @include cxe-field-button($besm, $tool-state-style-map);
191
-
192
- .#{$state}disabled > & {
193
- border-width: 0;
194
- @include cx-add-state-rules($state-style-map, disabled);
195
- }
196
- .#{$state}focus > & {
197
- @include cx-add-state-rules($tool-state-style-map, focus);
198
- }
199
- .#{$state}error > & {
200
- @include cx-add-state-rules($tool-state-style-map, error);
201
- }
202
- }
203
-
204
- .#{$element}#{$name}-left-icon {
205
- @include cxe-field-button($besm, $left-icon-state-style-map);
206
- }
207
-
208
- .#{$element}#{$name}-icon {
209
- @include cxe-field-button-icon($besm, $icon-size);
210
- }
211
-
212
- .#{$element}#{$name}-empty-text {
213
- @include cxe-field-empty-text($empty-text);
214
- }
215
- }
216
-
217
- @if (cx-should-include("cx/widgets/LookupField")) {
218
- @include cx-lookupfield();
219
- }
1
+ @mixin cx-lookupfield(
2
+ $name: "lookupfield",
3
+ $state-style-map: $cx-std-field-state-style-map,
4
+ $placeholder: $cx-input-placeholder,
5
+ $empty-text: $cx-empty-text,
6
+ $left-icon-state-style-map: $cx-input-left-icon-state-style-map,
7
+ $clear-state-style-map: $cx-clear-state-style-map,
8
+ $tool-state-style-map: $cx-input-right-icon-state-style-map,
9
+ $icon-size: $cx-default-input-icon-size,
10
+ $width: $cx-default-input-width,
11
+ $tag-state-style-map: $cx-input-tag-state-style-map,
12
+ $tag-clear-state-style-map: $cx-input-tag-clear-state-style-map,
13
+ $besm: $cx-besm
14
+ ) {
15
+ $block: map-get($besm, block);
16
+ $element: map-get($besm, element);
17
+ $state: map-get($besm, state);
18
+
19
+ $padding: cx-get-state-rule($state-style-map, default, padding);
20
+ $border-radius-offset: round(cx-get-state-rule($tag-state-style-map, default, border-radius) * 0.25);
21
+
22
+ .#{$block}#{$name} {
23
+ cursor: pointer;
24
+ width: $width;
25
+ font: cx-get-state-rule($state-style-map, default, "font");
26
+ font-size: cx-get-state-rule($state-style-map, default, "font-size");
27
+
28
+ @include cxb-field($besm, $state-style-map, $input: false);
29
+
30
+ &.#{$state}multiple {
31
+ min-width: $cx-default-input-width;
32
+ width: auto;
33
+ }
34
+
35
+ &.#{$state}readonly {
36
+ pointer-events: none;
37
+
38
+ & > .#{$element}#{$name}-input {
39
+ padding-right: cx-right($padding);
40
+ }
41
+ }
42
+ }
43
+
44
+ .#{$element}#{$name}-input {
45
+ @include cxe-field-input(
46
+ $besm,
47
+ $state-style-map,
48
+ $placeholder: $placeholder,
49
+ $input: false,
50
+ $overrides: (
51
+ default: (
52
+ height: 100%,
53
+ text-overflow: null,
54
+ padding: cx-top($padding)
55
+ cx-calc(cx-right($padding), $cx-default-clear-size, $cx-default-input-tag-spacing) cx-bottom($padding)
56
+ cx-left($padding),
57
+ )
58
+ )
59
+ );
60
+
61
+ .#{$state}multiple > & {
62
+ padding: 0 cx-calc(cx-right($padding), $cx-default-clear-size, $cx-default-input-tag-spacing)
63
+ $cx-default-input-tag-spacing 0;
64
+ align-items: center;
65
+ flex-wrap: wrap;
66
+ display: flex;
67
+ }
68
+
69
+ .#{$state}single > & {
70
+ overflow: hidden;
71
+ text-overflow: ellipsis;
72
+ white-space: nowrap;
73
+ display: block;
74
+ flex-basis: 0%;
75
+ flex-grow: 1;
76
+ }
77
+
78
+ .#{$state}icon > & {
79
+ padding-left: cx-calc(2 * cx-left($padding), $icon-size);
80
+ }
81
+ }
82
+
83
+ .#{$element}#{$name}-tag {
84
+ display: inline-block;
85
+ position: relative;
86
+ box-sizing: border-box;
87
+
88
+ @include cx-add-state-rules($tag-state-style-map, default);
89
+
90
+ &:hover {
91
+ @include cx-add-state-rules($tag-state-style-map, hover);
92
+ }
93
+
94
+ &.#{$state}readonly {
95
+ padding-right: cx-right($padding);
96
+ pointer-events: none;
97
+ }
98
+ }
99
+
100
+ .#{$element}#{$name}-placeholder {
101
+ color: $cx-default-input-placeholder-color;
102
+
103
+ .#{$block}#{$name}.#{$state}multiple & {
104
+ display: inline-block;
105
+ margin: $cx-default-input-tag-spacing 0 0 $cx-default-input-tag-spacing;
106
+ box-sizing: border-box;
107
+ padding: cx-calc(cx-top($cx-default-input-padding), -$cx-default-input-tag-spacing)
108
+ cx-calc(cx-right($cx-default-input-padding), -$cx-default-input-tag-spacing)
109
+ cx-calc(cx-bottom($cx-default-input-padding), -$cx-default-input-tag-spacing)
110
+ cx-calc(cx-left($cx-default-input-padding), -$cx-default-input-tag-spacing);
111
+ }
112
+ }
113
+
114
+ .#{$element}#{$name}-tag-value {
115
+ padding: 0 cx-calc($cx-default-input-tag-spacing, $border-radius-offset);
116
+ display: inline-block;
117
+ }
118
+
119
+ .#{$element}#{$name}-tag-clear {
120
+ @include cxe-field-button($besm, $tag-clear-state-style-map);
121
+ }
122
+
123
+ .#{$element}#{$name}-lookup-options {
124
+ padding: 0;
125
+ margin: 0;
126
+ min-width: 100%;
127
+ width: 100%;
128
+ }
129
+
130
+ .#{$element}#{$name}-query {
131
+ @include cxe-field-input(
132
+ $besm,
133
+ $state-style-map,
134
+ $placeholder: $placeholder,
135
+ $width: calc(100% - 10px),
136
+ $overrides: (
137
+ default: (
138
+ margin: 5px,
139
+ flex: 0 0 auto,
140
+ height: auto,
141
+ )
142
+ )
143
+ );
144
+ //put query field down when placed up
145
+ .#{$state}place-up-right &,
146
+ .#{$state}place-up &,
147
+ .#{$state}place-up-left & {
148
+ order: 1;
149
+ }
150
+ }
151
+
152
+ .#{$element}#{$name}-dropdown {
153
+ display: flex;
154
+ flex-direction: column;
155
+ height: 100%;
156
+
157
+ &:focus {
158
+ outline: none;
159
+ }
160
+ }
161
+
162
+ .#{$element}#{$name}-message {
163
+ font-size: smaller;
164
+ padding: 10px;
165
+ color: gray;
166
+ font-style: italic;
167
+
168
+ &.#{$state}error {
169
+ color: #9e2f2d;
170
+ }
171
+ }
172
+
173
+ .#{$element}#{$name}-scroll-container {
174
+ overflow: auto;
175
+ flex: 1 1 auto;
176
+ }
177
+
178
+ .#{$element}#{$name}-clear {
179
+ @include cxe-field-button($besm, $clear-state-style-map);
180
+
181
+ .#{$state}focus > & {
182
+ @include cx-add-state-rules($clear-state-style-map, focus);
183
+ }
184
+ .#{$state}error > & {
185
+ @include cx-add-state-rules($clear-state-style-map, error);
186
+ }
187
+ }
188
+
189
+ .#{$element}#{$name}-tool {
190
+ @include cxe-field-button($besm, $tool-state-style-map);
191
+
192
+ .#{$state}disabled > & {
193
+ border-width: 0;
194
+ @include cx-add-state-rules($state-style-map, disabled);
195
+ }
196
+ .#{$state}focus > & {
197
+ @include cx-add-state-rules($tool-state-style-map, focus);
198
+ }
199
+ .#{$state}error > & {
200
+ @include cx-add-state-rules($tool-state-style-map, error);
201
+ }
202
+ }
203
+
204
+ .#{$element}#{$name}-left-icon {
205
+ @include cxe-field-button($besm, $left-icon-state-style-map);
206
+ }
207
+
208
+ .#{$element}#{$name}-icon {
209
+ @include cxe-field-button-icon($besm, $icon-size);
210
+ }
211
+
212
+ .#{$element}#{$name}-empty-text {
213
+ @include cxe-field-empty-text($empty-text);
214
+ }
215
+ }
216
+
217
+ @if (cx-should-include("cx/widgets/LookupField")) {
218
+ @include cx-lookupfield();
219
+ }
@@ -84,6 +84,14 @@ interface MonthPickerProps extends FieldProps {
84
84
  * When true, the quarters section will not render.
85
85
  */
86
86
  hideQuarters?: boolean;
87
+
88
+ /**
89
+ * Callback to create a function that determines if a date is selectable.
90
+ * Return `false` on factory method to disable specific month, quarter or a whole year.
91
+ *
92
+ * Note: Use the `onValidate` callback for validation purposes.
93
+ */
94
+ onCreateIsMonthDateSelectable?: (validationParams: Cx.Config, instance: Instance) => (monthDate: Date) => boolean;
87
95
  }
88
96
 
89
97
  export class MonthPicker extends Cx.Widget<MonthPickerProps> {}
@@ -68,7 +68,8 @@ export class MonthPicker extends Field {
68
68
  super.init();
69
69
  }
70
70
 
71
- prepareData(context, { data }) {
71
+ prepareData(context, instance) {
72
+ let { data } = instance;
72
73
  data.stateMods = {
73
74
  disabled: data.disabled,
74
75
  };
@@ -91,6 +92,14 @@ export class MonthPicker extends Field {
91
92
 
92
93
  if (data.minValue) data.minValue = monthStart(parseDateInvariant(data.minValue));
93
94
 
95
+ if (this.onCreateIsMonthDateSelectable) {
96
+ instance.isMonthDateSelectable = instance.invoke(
97
+ "onCreateIsMonthDateSelectable",
98
+ data.validationParams,
99
+ instance,
100
+ );
101
+ }
102
+
94
103
  super.prepareData(...arguments);
95
104
  }
96
105
 
@@ -129,12 +138,13 @@ export class MonthPicker extends Field {
129
138
  }
130
139
 
131
140
  handleSelect(e, instance, date1, date2) {
132
- let { data, widget } = instance;
141
+ let { data, widget, isMonthDateSelectable } = instance;
133
142
  let encode = widget.encoding || Culture.getDefaultDateEncoding();
134
143
 
135
144
  if (data.disabled) return;
136
145
 
137
- if (!validationCheck(date1, data)) return;
146
+ if (isMonthDateSelectable && !isMonthDateSelectable(date1)) return;
147
+ if (!dateSelectableCheck(date1, data)) return;
138
148
 
139
149
  if (this.onBeforeSelect && instance.invoke("onBeforeSelect", e, instance, date1, date2) === false) return;
140
150
 
@@ -167,7 +177,7 @@ Localization.registerPrototype("cx/widgets/MonthPicker", MonthPicker);
167
177
 
168
178
  Widget.alias("month-picker", MonthPicker);
169
179
 
170
- const validationCheck = (date, data) => {
180
+ const dateSelectableCheck = (date, data) => {
171
181
  if (data.maxValue && !upperBoundCheck(date, data.maxValue, data.maxExclusive)) return false;
172
182
 
173
183
  if (data.minValue && !lowerBoundCheck(date, data.minValue, data.minExclusive)) return false;
@@ -429,7 +439,7 @@ export class MonthPickerComponent extends VDOM.Component {
429
439
 
430
440
  render() {
431
441
  let { instance } = this.props;
432
- let { data, widget } = instance;
442
+ let { data, widget, isMonthDateSelectable } = instance;
433
443
  let { CSS, baseClass, startYear, endYear, hideQuarters } = widget;
434
444
 
435
445
  let years = [];
@@ -464,29 +474,48 @@ export class MonthPickerComponent extends VDOM.Component {
464
474
  let showCursor = this.state.hover || this.state.focused;
465
475
 
466
476
  for (let y = start; y <= end; y++) {
477
+ let selectableMonths = 0b111111111111;
478
+ // Loop through the months in a year to check if all months are unselectable
479
+ for (let i = 0; i < 12; i++) {
480
+ if (
481
+ (isMonthDateSelectable && !isMonthDateSelectable(new Date(y, i, 1))) ||
482
+ !dateSelectableCheck(new Date(y, i, 1), data)
483
+ ) {
484
+ // Set month as unselectable at specified bit
485
+ selectableMonths &= ~(1 << i);
486
+ }
487
+ }
488
+
489
+ // All bits are 0 - all months are unselectable
490
+ const unselectableYear = selectableMonths === 0;
491
+
467
492
  let rows = [];
468
493
  for (let q = 0; q < 4; q++) {
469
494
  let row = [];
470
- if (q == 0)
495
+ if (q == 0) {
471
496
  row.push(
472
497
  <th
473
498
  key="year"
474
499
  rowSpan={4}
475
500
  data-point={`Y-${y}`}
476
- className={CSS.element(baseClass, "year", {
477
- cursor: showCursor && this.state.column == "Y" && y == this.state.cursorYear,
478
- })}
479
- onMouseEnter={this.handleMouseEnter}
480
- onMouseDown={this.handleMouseDown}
481
- onMouseUp={this.handleMouseUp}
501
+ className={CSS.expand(
502
+ CSS.element(baseClass, "year", {
503
+ cursor: showCursor && this.state.column == "Y" && y == this.state.cursorYear,
504
+ }),
505
+ CSS.state({ unselectable: unselectableYear }),
506
+ )}
507
+ onMouseEnter={unselectableYear ? null : this.handleMouseEnter}
508
+ onMouseDown={unselectableYear ? null : this.handleMouseDown}
509
+ onMouseUp={unselectableYear ? null : this.handleMouseUp}
482
510
  >
483
511
  {y}
484
512
  </th>,
485
513
  );
514
+ }
486
515
 
487
516
  for (let i = 0; i < 3; i++) {
488
517
  let m = q * 3 + i + 1;
489
- let unselectable = !validationCheck(new Date(y, m - 1, 1), data);
518
+ const unselectableMonth = (selectableMonths & (1 << (m - 1))) === 0;
490
519
  let mno = y * 12 + m - 1;
491
520
  let handle = true; //isTouchDevice(); //mno === from || mno === to - 1;
492
521
  row.push(
@@ -500,14 +529,14 @@ export class MonthPickerComponent extends VDOM.Component {
500
529
  m == this.state.cursorMonth,
501
530
  handle,
502
531
  selected: mno >= from && mno < to,
503
- unselectable,
532
+ unselectable: unselectableMonth,
504
533
  })}
505
534
  data-point={`Y-${y}-M-${m}`}
506
- onMouseEnter={unselectable ? null : this.handleMouseEnter}
507
- onMouseDown={unselectable ? null : this.handleMouseDown}
508
- onMouseUp={unselectable ? null : this.handleMouseUp}
509
- onTouchStart={unselectable ? null : this.handleMouseDown}
510
- onTouchMove={unselectable ? null : this.handleTouchMove}
535
+ onMouseEnter={unselectableMonth ? null : this.handleMouseEnter}
536
+ onMouseDown={unselectableMonth ? null : this.handleMouseDown}
537
+ onMouseUp={unselectableMonth ? null : this.handleMouseUp}
538
+ onTouchStart={unselectableMonth ? null : this.handleMouseDown}
539
+ onTouchMove={unselectableMonth ? null : this.handleTouchMove}
511
540
  onTouchEnd={this.handleMouseUp}
512
541
  >
513
542
  {monthNames[m - 1].substr(0, 3)}
@@ -515,7 +544,17 @@ export class MonthPickerComponent extends VDOM.Component {
515
544
  );
516
545
  }
517
546
 
518
- if (!hideQuarters)
547
+ if (!hideQuarters) {
548
+ let unselectableQuarter = true;
549
+ const start = q * 3;
550
+ for (let i = start; i < start + 3; i++) {
551
+ if ((selectableMonths & (1 << i)) !== 0) {
552
+ // found a selectable month in a quarter
553
+ unselectableQuarter = false;
554
+ break;
555
+ }
556
+ }
557
+
519
558
  row.push(
520
559
  <th
521
560
  key={`q${q}`}
@@ -525,15 +564,18 @@ export class MonthPickerComponent extends VDOM.Component {
525
564
  this.state.column == "Q" &&
526
565
  y == this.state.cursorYear &&
527
566
  q == this.state.cursorQuarter,
567
+ unselectable: unselectableQuarter,
528
568
  })}
529
569
  data-point={`Y-${y}-Q-${q}`}
530
- onMouseEnter={this.handleMouseEnter}
531
- onMouseDown={this.handleMouseDown}
532
- onMouseUp={this.handleMouseUp}
570
+ onMouseEnter={unselectableQuarter ? null : this.handleMouseEnter}
571
+ onMouseDown={unselectableQuarter ? null : this.handleMouseDown}
572
+ onMouseUp={unselectableQuarter ? null : this.handleMouseUp}
533
573
  >
534
574
  {`Q${q + 1}`}
535
575
  </th>,
536
576
  );
577
+ }
578
+
537
579
  rows.push(row);
538
580
  }
539
581
  years.push(rows);
@@ -57,6 +57,10 @@
57
57
  color: lightgray;
58
58
  }
59
59
 
60
+ th.#{$state}unselectable {
61
+ @include cx-add-state-rules($cx-list-item, disabled);
62
+ }
63
+
60
64
  .#{$element}#{$name}-year {
61
65
  font-size: 120%;
62
66
  color: gray;