cx 24.10.5 → 24.10.7

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/dist/ui.js CHANGED
@@ -3906,21 +3906,41 @@ var ContentPlaceholder = /*#__PURE__*/ (function (_PureContainer) {
3906
3906
  };
3907
3907
  _proto.prepare = function prepare(context, instance) {
3908
3908
  var content = instance.content;
3909
- if (instance.cache("content", content) || (content && content.shouldUpdate)) instance.markShouldUpdate(context);
3909
+ if (this.allowMultiple) {
3910
+ var contentId = "";
3911
+ var shouldUpdate = false;
3912
+ if (content) {
3913
+ for (var i = 0; i < content.length; i++) {
3914
+ var c = content[i];
3915
+ contentId += c.id + "+";
3916
+ shouldUpdate = shouldUpdate || c.shouldUpdate;
3917
+ }
3918
+ }
3919
+ if (instance.cache("content", contentId) || shouldUpdate) instance.markShouldUpdate(context);
3920
+ } else if (instance.cache("content", content) || (content && content.shouldUpdate))
3921
+ instance.markShouldUpdate(context);
3910
3922
  };
3911
3923
  _proto.setContent = function setContent(context, instance, content) {
3912
- instance.content = content;
3924
+ if (this.allowMultiple) {
3925
+ if (instance.content == null) instance.content = [];
3926
+ instance.content.push(content);
3927
+ } else instance.content = content;
3913
3928
  content.contentPlaceholder = instance;
3914
3929
  };
3915
3930
  _proto.render = function render(context, instance, key) {
3916
3931
  var content = instance.content;
3917
- if (content) return content.contentVDOM;
3918
- return _PureContainer.prototype.render.call(this, context, instance, key);
3932
+ if (!content) return _PureContainer.prototype.render.call(this, context, instance, key);
3933
+ if (this.allowMultiple)
3934
+ return content.map(function (x) {
3935
+ return x.contentVDOM;
3936
+ });
3937
+ return content.contentVDOM;
3919
3938
  };
3920
3939
  return ContentPlaceholder;
3921
3940
  })(PureContainer);
3922
3941
  ContentPlaceholder.prototype.name = "body";
3923
3942
  ContentPlaceholder.prototype.scoped = false;
3943
+ ContentPlaceholder.prototype.allowMultiple = false;
3924
3944
  Widget.alias("content-placeholder", ContentPlaceholder);
3925
3945
  var ContentPlaceholderScope = /*#__PURE__*/ (function (_PureContainer2) {
3926
3946
  function ContentPlaceholderScope() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cx",
3
- "version": "24.10.5",
3
+ "version": "24.10.7",
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",
@@ -56,13 +56,13 @@ export class Axis extends BoundedObject {
56
56
 
57
57
  reportData(context, instance) {}
58
58
 
59
- renderTicksAndLabels(context, instance, valueFormatter) {
59
+ renderTicksAndLabels(context, instance, valueFormatter, minLabelDistance) {
60
60
  if (this.hidden) return false;
61
61
 
62
62
  var { data, calculator, labelFormatter } = instance;
63
63
  var { bounds } = data;
64
64
  let { CSS, baseClass } = this;
65
- var size = calculator.findTickSize(this.minLabelDistance);
65
+ var size = calculator.findTickSize(minLabelDistance);
66
66
 
67
67
  var labelClass = CSS.expand(CSS.element(baseClass, "label"), data.labelClass);
68
68
  var offsetClass = CSS.element(baseClass, "label-offset");
@@ -101,7 +101,7 @@ export class Axis extends BoundedObject {
101
101
  }
102
102
 
103
103
  var t = [];
104
- if (size > 0 && !data.hideLabels) {
104
+ if (!!size && !data.hideLabels) {
105
105
  var ticks = calculator.getTicks([size]);
106
106
  ticks.forEach((serie, si) => {
107
107
  serie.forEach((v, i) => {
@@ -59,7 +59,7 @@ export class NumericAxis extends Axis {
59
59
 
60
60
  return (
61
61
  <g key={key} className={data.classNames} style={data.style}>
62
- {this.renderTicksAndLabels(context, instance, formatter)}
62
+ {this.renderTicksAndLabels(context, instance, formatter, this.minLabelDistance)}
63
63
  </g>
64
64
  );
65
65
  }
@@ -1,24 +1,28 @@
1
- import * as Cx from '../../core';
2
- import {AxisProps} from './Axis';
3
-
4
- interface TimeAxisProps extends AxisProps {
5
-
6
- /** Minimum value. */
7
- min?: Cx.NumberProp,
8
-
9
- /** Maximum value. */
10
- max?: Cx.NumberProp,
11
-
12
- /** Base CSS class to be applied to the element. Defaults to `timeaxis`. */
13
- baseClass?: string,
14
-
15
- /** A number ranged between `0-2`. `0` means that the range is aligned with the lowest ticks. Default value is `1`, which means that the range is aligned with medium ticks. Use value `2` to align with major ticks. */
16
- snapToTicks?: 0 | 1 | 2 | false,
17
-
18
- tickDivisions?: { [prop: string]: Array<number[]> },
19
- minLabelDistance?: number,
20
- minTickUnit?: string,
21
-
22
- }
23
-
24
- export class TimeAxis extends Cx.Widget<TimeAxisProps> {}
1
+ import * as Cx from "../../core";
2
+ import { AxisProps } from "./Axis";
3
+
4
+ interface TimeAxisProps extends AxisProps {
5
+ /** Minimum value. */
6
+ min?: Cx.NumberProp;
7
+
8
+ /** Maximum value. */
9
+ max?: Cx.NumberProp;
10
+
11
+ /** Base CSS class to be applied to the element. Defaults to `timeaxis`. */
12
+ baseClass?: string;
13
+
14
+ /** A number ranged between `0-2`. `0` means that the range is aligned with the lowest ticks. Default value is `1`, which means that the range is aligned with medium ticks. Use value `2` to align with major ticks. */
15
+ snapToTicks?: 0 | 1 | 2 | false;
16
+
17
+ tickDivisions?: { [prop: string]: Array<number[]> };
18
+ minLabelDistance?: number;
19
+ minTickUnit?: string;
20
+
21
+ /** Set to true to apply precise label distances from minLabelDistanceFormatOverride based on the resolved label format. */
22
+ useLabelDistanceFormatOverrides?: boolean;
23
+
24
+ /** Mapping of formats to label distances, i.e. { "datetime;YYYYMM": 80 } */
25
+ minLabelDistanceFormatOverride?: Record<string, number>;
26
+ }
27
+
28
+ export class TimeAxis extends Cx.Widget<TimeAxisProps> {}
@@ -33,9 +33,14 @@ export class TimeAxis extends Axis {
33
33
 
34
34
  if (this.deadZone) {
35
35
  this.lowerDeadZone = this.deadZone;
36
- this.upperDeadZone = this.deadZone;
36
+ pperDeadZone = this.deadZone;
37
37
  }
38
38
 
39
+ this.minLabelDistanceFormatOverride = {
40
+ ...this.minLabelDistanceFormatOverrideDefaults,
41
+ ...this.minLabelDistanceFormatOverride,
42
+ };
43
+
39
44
  super.init();
40
45
  }
41
46
 
@@ -62,14 +67,16 @@ export class TimeAxis extends Axis {
62
67
  max,
63
68
  this.snapToTicks,
64
69
  this.tickDivisions,
65
- this.minTickDistance,
66
- this.minLabelDistance,
70
+ Math.max(1, this.minTickDistance),
71
+ Math.max(1, this.minLabelDistance),
67
72
  normalized,
68
73
  inverted,
69
74
  this.minTickUnit,
70
75
  lowerDeadZone,
71
76
  upperDeadZone,
72
- this.decode
77
+ this.decode,
78
+ this.useLabelDistanceFormatOverrides ? this.minLabelDistanceFormatOverride : {},
79
+ this.format,
73
80
  );
74
81
  }
75
82
 
@@ -80,12 +87,13 @@ export class TimeAxis extends Axis {
80
87
 
81
88
  if (!data.bounds.valid()) return null;
82
89
 
83
- let format = this.format || calculator.getFormat();
90
+ let format = calculator.resolvedFormat;
91
+ let minLabelDistance = calculator.resolvedMinLabelDistance;
84
92
  let formatter = Format.parse(format);
85
93
 
86
94
  return (
87
95
  <g key={key} className={data.classNames} style={data.style}>
88
- {this.renderTicksAndLabels(context, instance, formatter)}
96
+ {this.renderTicksAndLabels(context, instance, formatter, minLabelDistance)}
89
97
  </g>
90
98
  );
91
99
  }
@@ -112,11 +120,21 @@ TimeAxis.prototype.tickDivisions = {
112
120
  ],
113
121
  };
114
122
 
123
+ const TimeFormats = {
124
+ fullDateAndTime: "datetime;yyyy MMM dd HH mm ss n",
125
+ shortMonthDate: "datetime;yyyy MMM dd",
126
+ };
127
+
115
128
  TimeAxis.prototype.snapToTicks = 0;
116
129
  TimeAxis.prototype.tickSize = 15;
117
130
  TimeAxis.prototype.minLabelDistance = 60;
118
131
  TimeAxis.prototype.minTickDistance = 60;
119
132
  TimeAxis.prototype.minTickUnit = "second";
133
+ TimeAxis.prototype.useLabelDistanceFormatOverrides = false;
134
+ TimeAxis.prototype.minLabelDistanceFormatOverrideDefaults = {
135
+ [TimeFormats.fullDateAndTime]: 150,
136
+ [TimeFormats.shortMonthDate]: 90,
137
+ };
120
138
 
121
139
  function monthNumber(date) {
122
140
  return date.getFullYear() * 12 + date.getMonth() + (date.getDate() - 1) / 31;
@@ -149,7 +167,9 @@ class TimeScale {
149
167
  minTickUnit,
150
168
  lowerDeadZone,
151
169
  upperDeadZone,
152
- decode
170
+ decode,
171
+ minLabelDistanceFormatOverride,
172
+ format,
153
173
  ) {
154
174
  this.dateCache = {};
155
175
  this.min = min != null ? this.decodeValue(min) : null;
@@ -170,6 +190,8 @@ class TimeScale {
170
190
  delete this.maxValuePadded;
171
191
  this.stacks = {};
172
192
  this.decode = decode;
193
+ this.minLabelDistanceFormatOverride = minLabelDistanceFormatOverride;
194
+ this.format = format;
173
195
  }
174
196
 
175
197
  decodeValue(date) {
@@ -193,13 +215,13 @@ class TimeScale {
193
215
  return new Date(v).toISOString();
194
216
  }
195
217
 
196
- getFormat() {
197
- switch (this.tickMeasure) {
218
+ getFormat(unit, scale) {
219
+ switch (unit) {
198
220
  case "year":
199
221
  return "datetime;yyyy";
200
222
 
201
223
  case "month":
202
- if (new Date(this.scale.min).getFullYear() != new Date(this.scale.max).getFullYear()) return "yearOrMonth";
224
+ if (new Date(scale.min).getFullYear() != new Date(scale.max).getFullYear()) return "yearOrMonth";
203
225
  return "datetime;yyyy MMM";
204
226
 
205
227
  case "week":
@@ -207,12 +229,12 @@ class TimeScale {
207
229
 
208
230
  case "day":
209
231
  if (
210
- new Date(this.scale.min).getFullYear() != new Date(this.scale.max).getFullYear() ||
211
- new Date(this.scale.min).getMonth() != new Date(this.scale.max).getMonth()
232
+ new Date(scale.min).getFullYear() != new Date(scale.max).getFullYear() ||
233
+ new Date(scale.min).getMonth() != new Date(scale.max).getMonth()
212
234
  )
213
235
  return "monthOrDay";
214
236
 
215
- return "datetime;yyyy MMM dd";
237
+ return TimeFormats.shortMonthDate;
216
238
 
217
239
  case "hour":
218
240
  return "datetime;HH mm n";
@@ -224,7 +246,7 @@ class TimeScale {
224
246
  return "datetime;mm ss";
225
247
 
226
248
  default:
227
- return "datetime;yyyy MMM dd HH mm ss n";
249
+ return TimeFormats.fullDateAndTime;
228
250
  }
229
251
  }
230
252
 
@@ -288,31 +310,30 @@ class TimeScale {
288
310
 
289
311
  this.origin = this.inverted ? this.b : this.a;
290
312
 
291
- this.scale = this.getScale();
292
-
293
313
  this.calculateTicks();
314
+ if (this.scale == null) {
315
+ this.scale = this.getScale();
316
+ }
294
317
  }
295
318
 
296
319
  getTimezoneOffset(date) {
297
320
  return date.getTimezoneOffset() * 60 * 1000;
298
321
  }
299
322
 
300
- getScale(tickSizes, measure) {
323
+ getScale(tickSize, measure, minRange = 1000) {
301
324
  let { min, max, upperDeadZone, lowerDeadZone } = this;
302
325
 
303
326
  let smin = min;
304
327
  let smax = max;
305
328
 
306
- if (isNumber(this.snapToTicks) && measure && tickSizes && 0 <= this.snapToTicks && tickSizes.length > 0) {
307
- let tickSize = tickSizes[Math.min(tickSizes.length - 1, this.snapToTicks)];
308
-
329
+ if (tickSize) {
309
330
  let minDate = new Date(min);
310
331
  let maxDate = new Date(max);
311
332
 
312
333
  switch (measure) {
313
334
  case "second":
314
335
  case "minute":
315
- case "hours":
336
+ case "hour":
316
337
  case "day":
317
338
  default:
318
339
  let minOffset = this.getTimezoneOffset(minDate);
@@ -347,6 +368,12 @@ class TimeScale {
347
368
  if (this.maxValue == max) smax = this.maxValuePadded;
348
369
  }
349
370
 
371
+ if (smax - smin < minRange) {
372
+ let delta = (minRange - (smax - smin)) / 2;
373
+ smin -= delta;
374
+ smax += delta;
375
+ }
376
+
350
377
  //padding should be activated only if using min/max obtained from the data
351
378
  let minPadding = this.minValue === min ? Math.max(0, smin - this.minValuePadded) : 0;
352
379
  let maxPadding = this.maxValue === max ? Math.max(0, this.maxValuePadded - smax) : 0;
@@ -399,7 +426,7 @@ class TimeScale {
399
426
  }
400
427
 
401
428
  findTickSize(minPxDist) {
402
- return this.tickSizes.find((a) => a * Math.abs(this.scale.factor) >= minPxDist);
429
+ return this.tickSizes.find(({ size, noLabels }) => !noLabels && size * Math.abs(this.scale.factor) >= minPxDist);
403
430
  }
404
431
 
405
432
  getTickSizes() {
@@ -409,6 +436,8 @@ class TimeScale {
409
436
  calculateTicks() {
410
437
  let minReached = false;
411
438
 
439
+ let minRange = 1000;
440
+
412
441
  for (let unit in miliSeconds) {
413
442
  if (!minReached) {
414
443
  if (unit == this.minTickUnit) minReached = true;
@@ -420,62 +449,125 @@ class TimeScale {
420
449
 
421
450
  if (this.tickSizes.length > 0) {
422
451
  //add ticks from higher levels
423
- this.tickSizes.push(...divisions[0].map((s) => s * unitSize));
424
- continue;
452
+ this.tickSizes.push(...divisions[0].map((s) => ({ size: s * unitSize, measure: unit })));
453
+ break;
425
454
  }
426
455
 
427
456
  let bestLabelDistance = Infinity;
457
+ let bestMinLabelDistance = this.minLabelDistance;
428
458
  let bestTicks = [];
429
- let bestScale = this.scale;
459
+ let bestScale = null;
460
+ let bestFormat = null;
430
461
 
431
462
  this.tickMeasure = unit;
432
463
 
433
464
  for (let i = 0; i < divisions.length; i++) {
434
465
  let divs = divisions[i];
435
- let tickSizes = divs.map((s) => s * unitSize);
436
- let scale = this.getScale(tickSizes, unit);
437
- tickSizes.forEach((size, level) => {
438
- let tickDistance = size * Math.abs(scale.factor);
439
- if (tickDistance >= this.minTickDistance && tickDistance < bestLabelDistance) {
466
+ for (let d = 0; d < divs.length; d++) {
467
+ //if (useSnapToTicks && d < Math.min(divs.length - 1, this.snapToTicks)) continue;
468
+ let tickSize = divs[d] * unitSize;
469
+ let scale = this.getScale(null, unit, tickSize);
470
+ let format = this.format ?? this.getFormat(unit, scale);
471
+ let minLabelDistance = this.minLabelDistanceFormatOverride[format] ?? this.minLabelDistance;
472
+ let labelDistance = tickSize * Math.abs(scale.factor);
473
+ if (labelDistance >= minLabelDistance && labelDistance < bestLabelDistance) {
440
474
  bestScale = scale;
441
- bestTicks = tickSizes;
442
- bestLabelDistance = tickDistance;
475
+ bestTicks = divs.map((s) => s * unitSize);
476
+ bestLabelDistance = labelDistance;
477
+ bestFormat = format;
478
+ bestMinLabelDistance = minLabelDistance;
479
+ minRange = tickSize;
443
480
  }
444
- });
481
+ }
445
482
  }
483
+
446
484
  this.scale = bestScale;
447
- this.tickSizes = bestTicks.filter((ts) => ts * Math.abs(bestScale.factor) >= this.minTickDistance);
485
+ this.tickSizes = bestTicks
486
+ .filter((ts) => ts * Math.abs(bestScale.factor) >= this.minTickDistance)
487
+ .map((size) => ({ size, measure: this.tickMeasure }));
488
+ this.resolvedFormat = bestFormat;
489
+ this.resolvedMinLabelDistance = bestMinLabelDistance;
490
+ }
491
+
492
+ let lowerTickUnit = null;
493
+ switch (this.tickMeasure) {
494
+ case "year":
495
+ lowerTickUnit = "month";
496
+ break;
497
+ case "month":
498
+ lowerTickUnit = "day";
499
+ break;
500
+ case "week":
501
+ lowerTickUnit = "day";
502
+ break;
503
+ case "day":
504
+ lowerTickUnit = "hour";
505
+ break;
506
+ case "hour":
507
+ lowerTickUnit = "minute";
508
+ break;
509
+ case "minute":
510
+ lowerTickUnit = "second";
511
+ break;
512
+ }
513
+
514
+ if (lowerTickUnit != null && this.scale) {
515
+ let bestMinorTickSize = Infinity;
516
+ let divisions = this.tickDivisions[lowerTickUnit];
517
+ let unitSize = miliSeconds[lowerTickUnit];
518
+ for (let i = 0; i < divisions.length; i++) {
519
+ let divs = divisions[i];
520
+ for (let d = 0; d < divs.length; d++) {
521
+ let tickSize = divs[d] * unitSize;
522
+ if (tickSize * Math.abs(this.scale.factor) >= this.minTickDistance && tickSize < bestMinorTickSize) {
523
+ bestMinorTickSize = tickSize;
524
+ }
525
+ }
526
+ }
527
+ if (bestMinorTickSize != Infinity) {
528
+ this.tickSizes.unshift({ size: bestMinorTickSize, measure: lowerTickUnit, noLabels: true });
529
+ if (this.tickSizes.length > 1) {
530
+ let labelStep = this.tickSizes[1].size;
531
+ let lowerScale = this.getScale(null, lowerTickUnit, minRange);
532
+ if (lowerScale.max - lowerScale.min >= labelStep) this.scale = lowerScale;
533
+ }
534
+ }
535
+ }
536
+
537
+ if (isNumber(this.snapToTicks) && this.snapToTicks >= 0) {
538
+ let tickSize = this.tickSizes[Math.min(this.tickSizes.length - 1, this.snapToTicks)];
539
+ this.scale = this.getScale(tickSize.size, tickSize.measure, minRange);
448
540
  }
449
541
  }
450
542
 
451
543
  getTicks(tickSizes) {
452
- return tickSizes.map((size) => {
544
+ return tickSizes.map(({ size, measure }) => {
453
545
  let result = [],
454
546
  start,
455
547
  end,
456
548
  minDate,
457
549
  maxDate;
458
- if (this.tickMeasure == "year") {
550
+ if (measure == "year") {
459
551
  size /= miliSeconds.year;
460
552
  minDate = new Date(this.scale.min - this.scale.minPadding);
461
553
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
462
554
  start = Math.ceil(yearNumber(minDate) / size) * size;
463
555
  end = Math.floor(yearNumber(maxDate) / size) * size;
464
556
  for (let i = start; i <= end; i += size) result.push(new Date(i, 0, 1).getTime());
465
- } else if (this.tickMeasure == "month") {
557
+ } else if (measure == "month") {
466
558
  size /= miliSeconds.month;
467
559
  minDate = new Date(this.scale.min - this.scale.minPadding);
468
560
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
469
561
  start = Math.ceil(monthNumber(minDate) / size) * size;
470
562
  end = Math.floor(monthNumber(maxDate) / size) * size;
471
563
  for (let i = start; i <= end; i += size) result.push(new Date(Math.floor(i / 12), i % 12, 1).getTime());
472
- } else if (this.tickMeasure == "day" || this.tickMeasure == "week") {
473
- let multiplier = this.tickMeasure == "week" ? 7 : 1;
564
+ } else if (measure == "day" || measure == "week") {
565
+ let multiplier = measure == "week" ? 7 : 1;
474
566
  size /= miliSeconds.day;
475
567
  minDate = new Date(this.scale.min - this.scale.minPadding);
476
568
  maxDate = new Date(this.scale.max + this.scale.maxPadding);
477
569
  let date = zeroTime(minDate);
478
- if (this.tickMeasure == "week") {
570
+ if (measure == "week") {
479
571
  //start on monday
480
572
  while (date.getDay() != 1) {
481
573
  date.setDate(date.getDate() + 1);
@@ -504,7 +596,6 @@ class TimeScale {
504
596
 
505
597
  mapGridlines() {
506
598
  if (this.tickSizes.length == 0) return [];
507
-
508
599
  return this.getTicks([this.tickSizes[0]])[0].map((x) => this.map(x));
509
600
  }
510
601
  }
@@ -13,8 +13,12 @@ export class PointReducer extends PureContainer {
13
13
  if (this.onInitAccumulator) instance.invoke("onInitAccumulator", accumulator, instance);
14
14
  };
15
15
 
16
+ let pointFilter = null;
17
+ if (this.onCreatePointFilter) pointFilter = instance.invoke("onCreatePointFilter", instance);
18
+
16
19
  instance.pointReducer = (x, y, name, data, array, index) => {
17
- onMap(accumulator, x, y, name, data, array, index);
20
+ if (!pointFilter || pointFilter(x, y, name, data, array, index))
21
+ onMap(accumulator, x, y, name, data, array, index);
18
22
  if (parentPointReducer) parentPointReducer(x, y, name, data, array, index);
19
23
  };
20
24
  instance.write = () => {
@@ -1,18 +1,19 @@
1
- import * as Cx from '../../core';
2
- import {RenderingContext} from '../RenderingContext';
3
-
4
- interface ContentPlaceholderProps extends Cx.PureContainerProps {
5
-
6
- name?: Cx.StringProp,
7
-
8
- scoped?: boolean
9
- }
10
-
11
- export class ContentPlaceholder extends Cx.Widget<ContentPlaceholderProps> {}
12
-
13
-
14
- interface ContentPlaceholderScopeProps extends Cx.PureContainerProps {
15
- name?: string | string[]
16
- }
17
-
18
- export class ContentPlaceholderScope extends Cx.Widget<ContentPlaceholderScopeProps> {}
1
+ import * as Cx from "../../core";
2
+ import { RenderingContext } from "../RenderingContext";
3
+
4
+ interface ContentPlaceholderProps extends Cx.PureContainerProps {
5
+ name?: Cx.StringProp;
6
+
7
+ scoped?: boolean;
8
+
9
+ /* Set to true to allow all registered content elements to render inside the placeholder. Otherwise only one element is rendered. */
10
+ allowMultiple?: boolean;
11
+ }
12
+
13
+ export class ContentPlaceholder extends Cx.Widget<ContentPlaceholderProps> {}
14
+
15
+ interface ContentPlaceholderScopeProps extends Cx.PureContainerProps {
16
+ name?: string | string[];
17
+ }
18
+
19
+ export class ContentPlaceholderScope extends Cx.Widget<ContentPlaceholderScopeProps> {}