cx 25.6.2 → 25.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cx",
3
- "version": "25.6.2",
3
+ "version": "25.7.0",
4
4
  "description": "Advanced JavaScript UI framework for admin and dashboard applications with ready to use grid, form and chart components.",
5
5
  "main": "index.js",
6
6
  "jsnext:main": "src/index.js",
@@ -3,7 +3,7 @@ import { BoundedObject, BoundedObjectProps } from "../svg/BoundedObject";
3
3
 
4
4
  interface MarkerProps extends BoundedObjectProps {
5
5
  /** The `x` value binding or expression. */
6
- x?: Cx.Prop<string | number>;
6
+ x?: Cx.Prop<string | number | Date>;
7
7
 
8
8
  /** The `y` value binding or expression. */
9
9
  y?: Cx.Prop<string | number>;
@@ -1,58 +1,56 @@
1
- import * as Cx from '../core';
2
- import { BoundedObject, BoundedObjectProps } from '../svg/BoundedObject';
1
+ import * as Cx from "../core";
2
+ import { BoundedObject, BoundedObjectProps } from "../svg/BoundedObject";
3
3
 
4
4
  interface MarkerLineProps extends BoundedObjectProps {
5
-
6
5
  /** The `x1` value binding or expression. */
7
- x1?: Cx.NumberProp,
6
+ x1?: Cx.Prop<number | string | Date>;
8
7
 
9
8
  /** The `y1` value binding or expression. */
10
- y1?: Cx.NumberProp,
9
+ y1?: Cx.NumberProp;
11
10
 
12
11
  /** The `x2` value binding or expression. */
13
- x2?: Cx.NumberProp,
12
+ x2?: Cx.Prop<number | string | Date>;
14
13
 
15
14
  /** The `y2` value binding or expression. */
16
- y2?: Cx.NumberProp,
15
+ y2?: Cx.NumberProp;
17
16
 
18
17
  /** Used to indicate if the data should affect axis span. */
19
- affectsAxes?: Cx.BooleanProp,
18
+ affectsAxes?: Cx.BooleanProp;
20
19
 
21
20
  /** Index of a color from the standard palette of colors. 0-15. */
22
- colorIndex?: Cx.NumberProp,
21
+ colorIndex?: Cx.NumberProp;
23
22
 
24
23
  /** Used to indicate if an item is active or not. Inactive items are shown only in the legend. */
25
- active?: Cx.BooleanProp,
24
+ active?: Cx.BooleanProp;
26
25
 
27
26
  /** Name of the item as it will appear in the legend. */
28
- name?: Cx.StringProp,
27
+ name?: Cx.StringProp;
29
28
 
30
29
  /** Name of the legend to be used. Default is `legend`. */
31
- legend?: Cx.StringProp,
30
+ legend?: Cx.StringProp;
32
31
 
33
32
  /** Shared `x1` and `x2` value binding or expression. */
34
- x?: Cx.NumberProp,
33
+ x?: Cx.Prop<number | string | Date>;
35
34
 
36
35
  /** Shared `y1` and `y2` value binding or expression. */
37
- y?: Cx.NumberProp,
36
+ y?: Cx.NumberProp;
38
37
 
39
- /**
40
- * Name of the horizontal axis. The value should match one of the horizontal axes set
41
- * in the `axes` configuration of the parent `Chart` component. Default value is `x`.
38
+ /**
39
+ * Name of the horizontal axis. The value should match one of the horizontal axes set
40
+ * in the `axes` configuration of the parent `Chart` component. Default value is `x`.
42
41
  */
43
- xAxis?: string,
44
-
45
- /**
42
+ xAxis?: string;
43
+
44
+ /**
46
45
  * Name of the vertical axis. The value should match one of the vertical axes set
47
- * in the axes configuration if the parent Chart component. Default value is y.
46
+ * in the axes configuration if the parent Chart component. Default value is y.
48
47
  */
49
- yAxis?: string,
50
-
51
- /** Base CSS class to be applied to the element. Defaults to `markerline`. */
52
- baseClass?: string,
48
+ yAxis?: string;
53
49
 
54
- legendAction?: string
50
+ /** Base CSS class to be applied to the element. Defaults to `markerline`. */
51
+ baseClass?: string;
55
52
 
53
+ legendAction?: string;
56
54
  }
57
55
 
58
- export class MarkerLine extends Cx.Widget<MarkerLineProps> {}
56
+ export class MarkerLine extends Cx.Widget<MarkerLineProps> {}
@@ -1,6 +1,6 @@
1
- import {View, ViewConfig} from './View';
1
+ import { View, ViewConfig } from "./View";
2
2
 
3
- import { Binding } from './Binding';
3
+ import { Binding } from "./Binding";
4
4
 
5
5
  interface ExposedValueViewConfig extends ViewConfig {
6
6
  containerBinding: Binding;
@@ -1 +1 @@
1
- export function isFunction(f: any): f is function;
1
+ export function isFunction(f: any): f is Function;
@@ -10,7 +10,9 @@ interface SandboxProps extends Cx.PureContainerProps {
10
10
 
11
11
  recordName?: Cx.RecordAlias;
12
12
  recordAlias?: Cx.RecordAlias;
13
- immutable?: Cx.BooleanProp;
13
+
14
+ immutable?: boolean;
15
+ sealed?: boolean;
14
16
  }
15
17
 
16
18
  export class Sandbox extends Cx.Widget<SandboxProps> {}
@@ -50,6 +50,7 @@ export class Sandbox extends PureContainer {
50
50
  key: data.key,
51
51
  recordName: this.recordName,
52
52
  immutable: this.immutable,
53
+ sealed: this.sealed,
53
54
  });
54
55
  instance.clearChildrenCache();
55
56
  }
@@ -59,5 +60,6 @@ export class Sandbox extends PureContainer {
59
60
 
60
61
  Sandbox.prototype.recordName = "$page";
61
62
  Sandbox.prototype.immutable = false;
63
+ Sandbox.prototype.sealed = false;
62
64
 
63
65
  Widget.alias("sandbox", Sandbox);
@@ -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;
@@ -102,6 +102,7 @@ export class Grid extends Container {
102
102
  colWidth: {},
103
103
  lockedColWidth: {},
104
104
  dimensionsVersion: 0,
105
+ disableDefaultSort: false
105
106
  };
106
107
  instance.v = 0;
107
108
  if (this.infinite)
@@ -182,16 +183,16 @@ export class Grid extends Container {
182
183
  value: isDefined(c.aggregateValue)
183
184
  ? c.aggregateValue
184
185
  : isDefined(c.value)
185
- ? c.value
186
- : c.aggregateField
187
- ? { bind: this.recordName + "." + c.aggregateField }
188
- : null,
186
+ ? c.value
187
+ : c.aggregateField
188
+ ? { bind: this.recordName + "." + c.aggregateField }
189
+ : null,
189
190
  weight:
190
191
  c.weight != null
191
192
  ? c.weight
192
193
  : c.weightField && {
193
- bind: this.recordName + "." + c.weightField,
194
- },
194
+ bind: this.recordName + "." + c.weightField,
195
+ },
195
196
  type: c.aggregate,
196
197
  };
197
198
  } else if (c.footer && !showFooter) {
@@ -295,7 +296,8 @@ export class Grid extends Container {
295
296
  data.sorters = [sorter];
296
297
  }
297
298
 
298
- if (!isNonEmptyArray(data.sorters) && this.defaultSortField) {
299
+ let skipDefaultSorting = this.clearableSort && instance.state.disableDefaultSort;
300
+ if (!skipDefaultSorting && !isNonEmptyArray(data.sorters) && this.defaultSortField) {
299
301
  let sorter = {
300
302
  field: this.defaultSortField,
301
303
  direction: this.defaultSortDirection || "ASC",
@@ -553,9 +555,8 @@ export class Grid extends Container {
553
555
  let initialPosition = getCursorPos(e);
554
556
  resizeOverlayEl.className = CSS.element(baseClass, "resize-overlay");
555
557
  resizeOverlayEl.style.width = `${initialWidth}px`;
556
- resizeOverlayEl.style.left = `${
557
- headerCell.getBoundingClientRect().left - gridEl.getBoundingClientRect().left
558
- }px`;
558
+ resizeOverlayEl.style.left = `${headerCell.getBoundingClientRect().left - gridEl.getBoundingClientRect().left
559
+ }px`;
559
560
  gridEl.appendChild(resizeOverlayEl);
560
561
  captureMouse2(e, {
561
562
  onMouseMove: (e) => {
@@ -817,7 +818,7 @@ export class Grid extends Container {
817
818
  widget: () => <div className={CSS.element(baseClass, "col-header-drag-clone")}>{data.text}</div>,
818
819
  },
819
820
  },
820
- () => {},
821
+ () => { },
821
822
  );
822
823
  }
823
824
  }
@@ -852,19 +853,22 @@ export class Grid extends Container {
852
853
  if (data.sorters[0].direction == "ASC" && (!this.clearableSort || direction == "ASC")) direction = "DESC";
853
854
  else if (data.sorters[0].direction == "DESC" && (!this.clearableSort || direction == "DESC"))
854
855
  direction = "ASC";
855
- else direction = null;
856
+ else {
857
+ direction = null;
858
+ instance.state.disableDefaultSort = true;
859
+ }
856
860
  }
857
861
 
858
862
  let sorters = direction
859
863
  ? [
860
- {
861
- field,
862
- direction,
863
- value,
864
- comparer,
865
- sortOptions,
866
- },
867
- ]
864
+ {
865
+ field,
866
+ direction,
867
+ value,
868
+ comparer,
869
+ sortOptions,
870
+ },
871
+ ]
868
872
  : null;
869
873
 
870
874
  if (sorters == null) field = null;
@@ -1315,8 +1319,8 @@ class GridComponent extends VDOM.Component {
1315
1319
  style={
1316
1320
  this.rowHeight > 0
1317
1321
  ? {
1318
- height: this.rowHeight + 1,
1319
- }
1322
+ height: this.rowHeight + 1,
1323
+ }
1320
1324
  : null
1321
1325
  }
1322
1326
  >
@@ -2598,6 +2602,7 @@ class GridComponent extends VDOM.Component {
2598
2602
  instance.buffer.totalRecordCount = 0;
2599
2603
  instance.buffer.page = 1;
2600
2604
  this.prevFetchRecordsState = null;
2605
+ this.loading = false;
2601
2606
  }
2602
2607
  }
2603
2608
 
@@ -2726,7 +2731,7 @@ class GridComponent extends VDOM.Component {
2726
2731
  hscroll = true;
2727
2732
  item =
2728
2733
  item.firstChild.children[
2729
- this.state.cursorCellIndex - this.props.instance.fixedColumnCount
2734
+ this.state.cursorCellIndex - this.props.instance.fixedColumnCount
2730
2735
  ];
2731
2736
  } else {
2732
2737
  let fixedItem = this.dom.fixedTable.querySelector(`tbody[data-record-key="${record.key}"]`);