@singularsystems/neo-core 1.0.12 → 1.0.14

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.
@@ -10,7 +10,7 @@ export default class DatePicker {
10
10
  private headerAnimationHelper;
11
11
  private inputHasChanged;
12
12
  private input;
13
- private popup;
13
+ private popup?;
14
14
  private headerContainer;
15
15
  private currentHeaderLabel;
16
16
  private prevButton;
@@ -22,6 +22,7 @@ export default class DatePicker {
22
22
  private boundDateNoTime;
23
23
  private today;
24
24
  private currentViewDate;
25
+ private positionHelper?;
25
26
  constructor(container: HTMLDivElement, options?: IPartialDateProps);
26
27
  static mustShowInput(options?: IPartialDateProps): boolean | undefined;
27
28
  inputFocused(e: FocusEvent): void;
@@ -1,7 +1,7 @@
1
1
  import DateUtils from '../../Dates/DateUtils';
2
2
  import AnimationHelper from '../AnimationHelper';
3
3
  import { YearsInYearView, ProcessedDatePickerOptions } from './DatePickerOptions';
4
- import PositionHelper from '../PositionHelper';
4
+ import { FixedPositionHelper } from '../PositionHelper';
5
5
  // TODO: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
6
6
  // TODO: Allow input to be shown with inline picker, and specify position.
7
7
  var ViewType;
@@ -114,6 +114,7 @@ var DatePicker = /** @class */ (function () {
114
114
  }
115
115
  };
116
116
  DatePicker.prototype.removePopup = function (animate) {
117
+ var _a;
117
118
  if (this.popup && !this.options.alwaysShow) {
118
119
  if (animate) {
119
120
  AnimationHelper.scale(this.popup, DatePicker.AnimationClassName, 1, 0);
@@ -124,7 +125,9 @@ var DatePicker = /** @class */ (function () {
124
125
  }
125
126
  this.currentHeaderLabel = null;
126
127
  this.currentContentTable = null;
127
- this.popup = null;
128
+ this.popup = undefined;
129
+ (_a = this.positionHelper) === null || _a === void 0 ? void 0 : _a.dispose();
130
+ this.positionHelper = undefined;
128
131
  }
129
132
  };
130
133
  DatePicker.prototype.setBindValue = function (dateValue, options) {
@@ -140,7 +143,7 @@ var DatePicker = /** @class */ (function () {
140
143
  if (options.close) {
141
144
  this.removePopup(true);
142
145
  }
143
- if (!options.close || this.options.alwaysShow) {
146
+ if (!options.close || options.forceDraw) {
144
147
  this.resetCurrentViewDate();
145
148
  this.populateContentContainer(true);
146
149
  }
@@ -173,9 +176,12 @@ var DatePicker = /** @class */ (function () {
173
176
  }
174
177
  };
175
178
  DatePicker.prototype.positionPopup = function (fromBottom) {
176
- if (this.popup) {
179
+ if (this.popup && !this.options.alwaysShow) {
177
180
  var textAlign = this.options.popupAlignRight ? undefined : window.getComputedStyle(this.input).textAlign;
178
- PositionHelper.checkScreenBounds(this.container, this.popup, fromBottom, this.options.popupAlignRight, textAlign);
181
+ if (!this.positionHelper) {
182
+ this.positionHelper = new FixedPositionHelper(this.container, this.popup);
183
+ }
184
+ this.positionHelper.positionPopup(this.options.popupAlignRight, textAlign, fromBottom);
179
185
  }
180
186
  };
181
187
  /**
@@ -234,15 +240,15 @@ var DatePicker = /** @class */ (function () {
234
240
  this.popup.addEventListener("mouseenter", function () { return _this.positionPopup(false); });
235
241
  this.popup.addEventListener("mouseleave", function () { return _this.positionPopup(true); });
236
242
  }
237
- this.createHeader();
238
- this.createContentContainer();
243
+ this.createHeader(this.popup);
244
+ this.createContentContainer(this.popup);
239
245
  }
240
246
  else {
241
247
  this.populateContentContainer(true);
242
248
  }
243
249
  };
244
- DatePicker.prototype.createHeader = function () {
245
- var header = this.createAndAppend(this.popup, "div", "neo-datepicker-header");
250
+ DatePicker.prototype.createHeader = function (parent) {
251
+ var header = this.createAndAppend(parent, "div", "neo-datepicker-header");
246
252
  header.addEventListener("mousedown", function (e) { return e.preventDefault(); });
247
253
  this.prevButton = this.createAndAppend(header, "button", "left-button");
248
254
  this.prevButton.type = "button";
@@ -258,7 +264,7 @@ var DatePicker = /** @class */ (function () {
258
264
  this.nextButton.tabIndex = -1;
259
265
  this.nextButton.addEventListener("mousedown", this.nextClick.bind(this));
260
266
  };
261
- DatePicker.prototype.createHeaderLabel = function () {
267
+ DatePicker.prototype.createHeaderLabel = function (displayImmediately) {
262
268
  this.currentHeaderLabel = this.createAndAppend(this.headerContainer, "div", "header-text");
263
269
  if (this.currentViewType == ViewType.Days) {
264
270
  this.currentHeaderLabel.innerText = DateUtils.format(this.currentViewDate, "MMMM yyyy");
@@ -273,10 +279,15 @@ var DatePicker = /** @class */ (function () {
273
279
  this.currentHeaderLabel.className += " selectable";
274
280
  }
275
281
  this.headerAnimationHelper.addChild(this.currentHeaderLabel, false);
276
- this.headerAnimationHelper.fade();
282
+ if (displayImmediately) {
283
+ this.headerAnimationHelper.replace();
284
+ }
285
+ else {
286
+ this.headerAnimationHelper.fade();
287
+ }
277
288
  };
278
- DatePicker.prototype.createContentContainer = function () {
279
- this.contentContainer = this.createAndAppend(this.popup, "div", "neo-datepicker-content");
289
+ DatePicker.prototype.createContentContainer = function (parent) {
290
+ this.contentContainer = this.createAndAppend(parent, "div", "neo-datepicker-content");
280
291
  this.contentContainer.addEventListener("mousedown", this.contentClick.bind(this));
281
292
  this.contentAnimationHelper = new AnimationHelper(this.contentContainer, DatePicker.AnimationClassName);
282
293
  this.populateContentContainer(!!this.options.alwaysShow);
@@ -298,7 +309,7 @@ var DatePicker = /** @class */ (function () {
298
309
  this.currentViewDate.setFullYear(this.currentViewDate.getFullYear() - this.options.yearsBeforeInYearView);
299
310
  this.createYearsView();
300
311
  }
301
- this.createHeaderLabel();
312
+ this.createHeaderLabel(displayImmediately);
302
313
  this.contentAnimationHelper.addChild(this.currentContentTable, true);
303
314
  if (displayImmediately) {
304
315
  this.contentAnimationHelper.replace();
@@ -412,12 +423,12 @@ var DatePicker = /** @class */ (function () {
412
423
  };
413
424
  DatePicker.prototype.dayClicked = function (datePartsString) {
414
425
  var dateParts = datePartsString.split(",");
415
- this.setBindValue(new Date(parseInt(dateParts[0]), parseInt(dateParts[1]), parseInt(dateParts[2])), { preserveTime: true, close: true });
426
+ this.setBindValue(new Date(parseInt(dateParts[0]), parseInt(dateParts[1]), parseInt(dateParts[2])), { preserveTime: true, close: true, forceDraw: this.options.alwaysShow });
416
427
  };
417
428
  DatePicker.prototype.monthClicked = function (monthString) {
418
429
  this.currentViewDate.setMonth(parseInt(monthString));
419
430
  if (this.options.monthView) {
420
- this.setBindValue(this.currentViewDate, { preserveTime: true, close: true });
431
+ this.setBindValue(this.currentViewDate, { preserveTime: true, close: true, forceDraw: this.options.alwaysShow });
421
432
  }
422
433
  else {
423
434
  this.currentViewType = ViewType.Days;
@@ -428,7 +439,7 @@ var DatePicker = /** @class */ (function () {
428
439
  DatePicker.prototype.yearClicked = function (yearString) {
429
440
  this.currentViewDate.setFullYear(parseInt(yearString));
430
441
  if (this.options.yearView) {
431
- this.setBindValue(this.currentViewDate, { preserveTime: true, close: true });
442
+ this.setBindValue(this.currentViewDate, { preserveTime: true, close: true, forceDraw: this.options.alwaysShow });
432
443
  }
433
444
  else {
434
445
  this.currentViewType = ViewType.Months;
@@ -17,6 +17,10 @@ export default class PositionHelper {
17
17
  * @param hAlign Horizontal alignment of the popup edge relative to the anchor edge. Overrides alignRight
18
18
  */
19
19
  static checkScreenBounds(anchor: HTMLElement, popup: HTMLElement, fromBottom?: boolean, alignRight?: boolean, hAlign?: "left" | "center" | "right"): void;
20
+ /**
21
+ * Used internally by neo. Use `checkScreenBounds` instead of this method.
22
+ */
23
+ static positionElement(popup: HTMLElement, position: IRelativePosition, fromBottom?: boolean): void;
20
24
  /**
21
25
  * Ensures that a popup element is positioned so that it remains in the bounds of the screen by left/right/top/bottom aligning it relative to the anchor element.
22
26
  * @param anchor The element to which the popup is anchored
@@ -26,14 +30,19 @@ export default class PositionHelper {
26
30
  */
27
31
  static getRelativePosition(anchor: HTMLElement, popup: HTMLElement, alignRight?: boolean, hAlign?: "left" | "center" | "right"): IRelativePosition;
28
32
  }
29
- /** Helper used to position elements with position: fixed relative to another element, while maintaining position on scroll. */
33
+ /**
34
+ * Helper used to position elements with position: fixed relative to another element, while maintaining position on scroll.
35
+ * If popupElement has a display of 'absolute', then normal positioning is performed.
36
+ */
30
37
  export declare class FixedPositionHelper implements IDisposable {
31
38
  private anchorElement;
32
39
  private popupElement;
40
+ private isFixed;
33
41
  private topOffset;
34
42
  private positionInfo;
35
- constructor(anchorElement: HTMLElement, popupElement: HTMLElement, alignRight?: boolean, hAlign?: "left" | "center" | "right");
36
- private positionPopup;
43
+ constructor(anchorElement: HTMLElement, popupElement: HTMLElement);
44
+ positionPopup(alignRight?: boolean, hAlign?: "left" | "center" | "right", fromBottom?: boolean): void;
45
+ private positionFixedPopup;
37
46
  dispose(): void;
38
47
  }
39
48
  export {};
@@ -13,6 +13,13 @@ var PositionHelper = /** @class */ (function () {
13
13
  if (fromBottom === void 0) { fromBottom = true; }
14
14
  if (alignRight === void 0) { alignRight = false; }
15
15
  var position = this.getRelativePosition(anchor, popup, alignRight, hAlign);
16
+ this.positionElement(popup, position, fromBottom);
17
+ };
18
+ /**
19
+ * Used internally by neo. Use `checkScreenBounds` instead of this method.
20
+ */
21
+ PositionHelper.positionElement = function (popup, position, fromBottom) {
22
+ if (fromBottom === void 0) { fromBottom = true; }
16
23
  if (position.onTop) {
17
24
  if (fromBottom) {
18
25
  popup.style.top = "auto";
@@ -84,28 +91,45 @@ var PositionHelper = /** @class */ (function () {
84
91
  return PositionHelper;
85
92
  }());
86
93
  export default PositionHelper;
87
- /** Helper used to position elements with position: fixed relative to another element, while maintaining position on scroll. */
94
+ /**
95
+ * Helper used to position elements with position: fixed relative to another element, while maintaining position on scroll.
96
+ * If popupElement has a display of 'absolute', then normal positioning is performed.
97
+ */
88
98
  var FixedPositionHelper = /** @class */ (function () {
89
- function FixedPositionHelper(anchorElement, popupElement, alignRight, hAlign) {
90
- if (alignRight === void 0) { alignRight = false; }
99
+ function FixedPositionHelper(anchorElement, popupElement) {
91
100
  this.anchorElement = anchorElement;
92
101
  this.popupElement = popupElement;
102
+ this.isFixed = false;
93
103
  this.topOffset = 0;
94
- this.positionInfo = PositionHelper.getRelativePosition(anchorElement, popupElement, alignRight, hAlign);
95
- if (this.positionInfo.onTop) {
96
- var style = window.getComputedStyle(popupElement);
97
- var marginHeight = parseFloat(style.marginBottom) + parseFloat(style.marginTop);
98
- this.topOffset = -(this.positionInfo.popupHeight + marginHeight);
104
+ if (window.getComputedStyle(popupElement).position === "fixed") {
105
+ this.isFixed = true;
106
+ window.addEventListener("scroll", this.positionFixedPopup.bind(this), { capture: true });
107
+ }
108
+ }
109
+ FixedPositionHelper.prototype.positionPopup = function (alignRight, hAlign, fromBottom) {
110
+ if (alignRight === void 0) { alignRight = false; }
111
+ if (fromBottom === void 0) { fromBottom = true; }
112
+ this.positionInfo = PositionHelper.getRelativePosition(this.anchorElement, this.popupElement, alignRight, hAlign);
113
+ if (this.isFixed) {
114
+ // Fixed, need to add scroll listener.
115
+ if (this.positionInfo.onTop) {
116
+ var style = window.getComputedStyle(this.popupElement);
117
+ var marginHeight = parseFloat(style.marginBottom) + parseFloat(style.marginTop);
118
+ this.topOffset = -(this.positionInfo.popupHeight + marginHeight);
119
+ }
120
+ else {
121
+ this.topOffset = this.positionInfo.anchorHeight;
122
+ }
123
+ this.positionFixedPopup();
99
124
  }
100
125
  else {
101
- this.topOffset = this.positionInfo.anchorHeight;
126
+ // Simple positioning relative to container.
127
+ PositionHelper.positionElement(this.popupElement, this.positionInfo, fromBottom);
102
128
  }
103
- window.addEventListener("scroll", this.positionPopup.bind(this), { capture: true });
104
- this.positionPopup();
105
- }
106
- FixedPositionHelper.prototype.positionPopup = function () {
129
+ };
130
+ FixedPositionHelper.prototype.positionFixedPopup = function () {
107
131
  var rect = this.anchorElement.getBoundingClientRect();
108
- this.popupElement.style.top = (rect.top + this.topOffset) + "px";
132
+ this.popupElement.style.top = (Math.floor(rect.top) + this.topOffset) + "px";
109
133
  this.popupElement.style.right = "auto";
110
134
  if (this.positionInfo.left !== undefined) {
111
135
  this.popupElement.style.left = (rect.left + this.positionInfo.left) + "px";
@@ -115,7 +139,9 @@ var FixedPositionHelper = /** @class */ (function () {
115
139
  }
116
140
  };
117
141
  FixedPositionHelper.prototype.dispose = function () {
118
- window.removeEventListener("scroll", this.positionPopup);
142
+ if (this.isFixed) {
143
+ window.removeEventListener("scroll", this.positionFixedPopup);
144
+ }
119
145
  };
120
146
  return FixedPositionHelper;
121
147
  }());
@@ -63,7 +63,7 @@ var Deserialiser = /** @class */ (function () {
63
63
  }
64
64
  });
65
65
  };
66
- Deserialiser.prototype.deserialiseArray = function (from, to, type) {
66
+ Deserialiser.prototype.deserialiseArray = function (from, to, type, itemFactory) {
67
67
  if (!to) {
68
68
  throw "Cannot deserialise array into a non initialised property.";
69
69
  }
@@ -99,7 +99,10 @@ var Deserialiser = /** @class */ (function () {
99
99
  deserialise = false;
100
100
  }
101
101
  else {
102
- if (type) {
102
+ if (itemFactory) {
103
+ toItem = itemFactory(fromItem);
104
+ }
105
+ else if (type) {
103
106
  toItem = new type();
104
107
  }
105
108
  else {
@@ -130,13 +133,14 @@ var Deserialiser = /** @class */ (function () {
130
133
  else {
131
134
  var mustCreate = !to[key];
132
135
  if (mustCreate) {
133
- to[key] = parentProperty.createValueInstance();
136
+ to[key] = parentProperty.createValueInstance(fromItem);
134
137
  }
135
138
  this.deserialiseObject(fromItem, to[key], mustCreate);
136
139
  }
137
140
  }
138
141
  };
139
142
  Deserialiser.prototype.deserialiseObject = function (from, to, justCreated) {
143
+ var _a;
140
144
  if (!from || !to) {
141
145
  throw "From and to objects are required.";
142
146
  }
@@ -173,9 +177,9 @@ var Deserialiser = /** @class */ (function () {
173
177
  }
174
178
  else {
175
179
  if (fromValue instanceof Array && !propertyInfo.assignOnDeserialise) {
176
- if (!to[key] || !(to[key] instanceof Array) || to[key].childObjectType) {
180
+ if (!to[key] || !(to[key] instanceof Array) || to[key].canDeserialise) {
177
181
  // Deserialise into full list.
178
- this.deserialiseArray(fromValue, to[key], undefined);
182
+ this.deserialiseArray(fromValue, to[key], undefined, (_a = to[key]) === null || _a === void 0 ? void 0 : _a.itemFactory);
179
183
  }
180
184
  else {
181
185
  // Plain array
@@ -197,7 +201,7 @@ var Deserialiser = /** @class */ (function () {
197
201
  else {
198
202
  var mustCreate = !to[key];
199
203
  if (mustCreate) {
200
- to[key] = propertyInfo.createValueInstance();
204
+ to[key] = propertyInfo.createValueInstance(fromValue);
201
205
  }
202
206
  this.deserialiseObject(fromValue, to[key], mustCreate);
203
207
  }
@@ -227,7 +231,7 @@ var Deserialiser = /** @class */ (function () {
227
231
  from.entityIdentifier && from.entityIdentifier.indexOf('000000000000') === -1) {
228
232
  toModel.entityIdentifier = from.entityIdentifier;
229
233
  }
230
- if (!this.options.isMap) {
234
+ if (!this.options.isMap && to.isSelfDirty !== undefined) {
231
235
  if (!from.trackingState) {
232
236
  toModel.isSelfDirty = false;
233
237
  toModel.isNew = false;
@@ -1,4 +1,5 @@
1
1
  import { SerialiseType, DataType, TimeZoneFormat } from './Misc';
2
+ import { IObjectFactoryOptions } from './IModelBase';
2
3
  export declare function Display(name: string): (type: any, propertyKey: string) => void;
3
4
  /** Indicates that this is a date property. Json strings will be converted to date objects on deserialisation. */
4
5
  export declare function Date(timeZoneSerialisation?: TimeZoneFormat): (type: any, propertyKey: string, descriptor?: PropertyDescriptor) => void;
@@ -20,10 +21,11 @@ export declare function Observable(isObservable?: boolean): (type: any, property
20
21
  export declare function NoTracking(serialisation?: SerialiseType): (type: any, propertyKey: string) => void;
21
22
  export declare function Serialisation(serialisation: SerialiseType): (type: any, propertyKey: string) => void;
22
23
  /** Indicates that this is a list property, and specifies the item type. This is not required if the list is initialised with a value. */
23
- export declare function ChildList(childType: any): (type: any, propertyKey: string) => any;
24
+ export declare function ChildList(childType?: (new () => any) | (() => any) | IObjectFactoryOptions): (type: any, propertyKey: string) => any;
24
25
  /** Indicates that this is a child object property, and specifies the child type. This is not required if the property is initialised with a value. */
25
26
  export declare function ChildObject(childType: new () => any, isValueObject?: boolean): Function;
26
27
  export declare function ChildObject(childType: () => any, isValueObject?: boolean): Function;
28
+ export declare function ChildObject(options: IObjectFactoryOptions, isValueObject?: boolean): Function;
27
29
  export declare function Dictionary(valueType: any): (type: any, propertyKey: string) => any;
28
30
  export declare function Parent(parentType: () => any): (type: any, propertyKey: string) => void;
29
31
  /**
@@ -85,7 +85,9 @@ export function Serialisation(serialisation) {
85
85
  export function ChildList(childType) {
86
86
  return function (type, propertyKey) {
87
87
  var pi = getOrAddPropertyInfo(type, propertyKey);
88
- pi.setPropertyType(childType);
88
+ if (childType) {
89
+ pi.setPropertyType(childType);
90
+ }
89
91
  if (pi.relationType === RelationType.None) {
90
92
  pi.relationType = RelationType.ChildList;
91
93
  pi.propertyType = DataType.Array;
@@ -101,4 +101,9 @@ export interface IModelBase extends INonObservableModelBase {
101
101
  readonly isDirty: boolean;
102
102
  readonly readyToSave: boolean;
103
103
  }
104
+ export type ObjectFactory = (rawData: object) => object;
105
+ export interface IObjectFactoryOptions {
106
+ /** Function that creates an instance of an object based on some input data. */
107
+ itemFactory: ObjectFactory;
108
+ }
104
109
  export {};
@@ -1,13 +1,19 @@
1
1
  import { ISerialiseOptions } from '../Data/Serialiser';
2
- import { PlainObject, PartialPlainObject, PlainTrackedObject, IMetaItem } from './IModelBase';
2
+ import { PlainObject, PartialPlainObject, PlainTrackedObject, IMetaItem, ObjectFactory, IObjectFactoryOptions } from './IModelBase';
3
3
  import { IDeserialiseOptions } from '../Data/Deserialiser';
4
4
  export default class List<T> extends Array<T> {
5
- childObjectType: (new () => T);
6
5
  attachedTo: object | null;
6
+ childObjectType?: new () => T;
7
+ itemFactory?: ObjectFactory;
7
8
  private _deletedList;
8
9
  get deletedList(): T[];
10
+ get canDeserialise(): ObjectFactory | (new () => T) | undefined;
9
11
  private baseMethods;
10
- constructor(childObjectType: (new () => T));
12
+ /**
13
+ * Creates an instance of a list. A List is an array with the ability to auto create child instances, and track removed items.
14
+ * @param childObjectType Usually the type that the list contains. Can also pass in options for advanced usage.
15
+ */
16
+ constructor(childObjectType?: (new () => T) | IObjectFactoryOptions);
11
17
  /**
12
18
  * Copies the methods and properties from the prototype to the new instance.
13
19
  * This is needed because vue / mobx changes the prototype to its own.
@@ -69,7 +75,7 @@ export default class List<T> extends Array<T> {
69
75
  removeWithoutTracking(item: T): void;
70
76
  private removeItem;
71
77
  /** Returns a static meta object containing all the properties of the lists item type. */
72
- getMetaItem(): IMetaItem;
78
+ getMetaItem(): IMetaItem | null;
73
79
  /** True if all items in the list are valid. */
74
80
  get isValid(): boolean;
75
81
  /** True if any item in the list is dirty, or the list contains any deleted items. */
@@ -5,10 +5,19 @@ import { ObservableValue } from './ObservableValue';
5
5
  import { NeoTypeInfo } from './NeoTypeInfo';
6
6
  var List = /** @class */ (function (_super) {
7
7
  __extends(List, _super);
8
+ /**
9
+ * Creates an instance of a list. A List is an array with the ability to auto create child instances, and track removed items.
10
+ * @param childObjectType Usually the type that the list contains. Can also pass in options for advanced usage.
11
+ */
8
12
  function List(childObjectType) {
9
13
  var _this = _super.call(this) || this;
10
- _this.childObjectType = childObjectType;
11
14
  _this._deletedList = new ObservableValue([]);
15
+ if (typeof childObjectType === "function") {
16
+ _this.childObjectType = childObjectType;
17
+ }
18
+ else {
19
+ _this.itemFactory = childObjectType === null || childObjectType === void 0 ? void 0 : childObjectType.itemFactory;
20
+ }
12
21
  List.copyMethods(_this);
13
22
  return _this;
14
23
  }
@@ -19,6 +28,13 @@ var List = /** @class */ (function (_super) {
19
28
  enumerable: false,
20
29
  configurable: true
21
30
  });
31
+ Object.defineProperty(List.prototype, "canDeserialise", {
32
+ get: function () {
33
+ return this.itemFactory || this.childObjectType;
34
+ },
35
+ enumerable: false,
36
+ configurable: true
37
+ });
22
38
  /**
23
39
  * Copies the methods and properties from the prototype to the new instance.
24
40
  * This is needed because vue / mobx changes the prototype to its own.
@@ -80,6 +96,9 @@ var List = /** @class */ (function (_super) {
80
96
  };
81
97
  /** Creates a new item of type T */
82
98
  List.prototype.createChild = function () {
99
+ if (!this.childObjectType) {
100
+ throw "Cannot create child, type was not passed to constructor.";
101
+ }
83
102
  return new this.childObjectType();
84
103
  };
85
104
  List.prototype.push = function () {
@@ -89,7 +108,7 @@ var List = /** @class */ (function (_super) {
89
108
  items[_i] = arguments[_i];
90
109
  }
91
110
  items.forEach(function (item) {
92
- if (!(item instanceof _this.childObjectType)) {
111
+ if (_this.childObjectType && !(item instanceof _this.childObjectType)) {
93
112
  throw "Type error, cannot add item of type " + Utils.getTypeName(item.constructor) + " to list of type " + Utils.getTypeName(_this.childObjectType) + ".";
94
113
  }
95
114
  _this.setParent(item);
@@ -101,7 +120,7 @@ var List = /** @class */ (function (_super) {
101
120
  };
102
121
  List.prototype.setParent = function (item) {
103
122
  if (this.attachedTo) {
104
- var itemConstructor = this.childObjectType.prototype.constructor;
123
+ var itemConstructor = item.constructor.prototype.constructor;
105
124
  // set the parent property
106
125
  if (itemConstructor.neo) {
107
126
  var parentConstructor = this.attachedTo.constructor.prototype.constructor;
@@ -162,10 +181,13 @@ var List = /** @class */ (function (_super) {
162
181
  if (this.length) {
163
182
  return this[0];
164
183
  }
165
- else {
184
+ else if (this.childObjectType) {
166
185
  var ctor = this.childObjectType.prototype.constructor;
167
186
  return NeoTypeInfo.ensureInitialised(ctor).metaItem;
168
187
  }
188
+ else {
189
+ return null;
190
+ }
169
191
  };
170
192
  Object.defineProperty(List.prototype, "isValid", {
171
193
  /** True if all items in the list are valid. */
@@ -83,7 +83,7 @@ export default abstract class ModelBase extends BindableBase implements IModelBa
83
83
  * ```
84
84
  * protected static addBusinessRules(rules: ValidationRules<MyType>) {
85
85
  * ``` */
86
- protected addBusinessRules(validationRules: ValidationRules<this>): void;
86
+ protected addBusinessRules(validationRules: ValidationRules<ModelBase>): void;
87
87
  /**
88
88
  * Add custom business rules for this type.
89
89
  */
@@ -61,11 +61,12 @@ var ModelCreatorBase = /** @class */ (function () {
61
61
  // @ts-ignore
62
62
  var propertyValue = instance[propertyInfo.propertyName];
63
63
  if (propertyValue) {
64
- if (propertyValue.childObjectType) {
64
+ var list = propertyValue;
65
+ if (list.itemFactory || list.childObjectType) {
65
66
  // This is a list.
66
67
  if (propertyInfo.relationType != RelationType.ChildList) {
67
68
  // add missing annotation
68
- propertyInfo = ChildList(propertyValue.childObjectType)(instance, propertyInfo.propertyName);
69
+ propertyInfo = ChildList(list.itemFactory ? { itemFactory: list.itemFactory } : list.childObjectType)(instance, propertyInfo.propertyName);
69
70
  }
70
71
  }
71
72
  else if (propertyValue instanceof ModelBase) {
@@ -22,6 +22,7 @@ export declare class NeoTypeInfo {
22
22
  get inheritsBindableBase(): boolean;
23
23
  get inheritsModelBase(): boolean;
24
24
  get metaItem(): IMetaItem;
25
+ get typeName(): string;
25
26
  initialise(objectConstructor: NeoModelConstructor): void;
26
27
  private addPropertyInfo;
27
28
  getOrAddProperty(propertyName: string): PropertyInfo;
@@ -55,6 +55,13 @@ var NeoTypeInfo = /** @class */ (function () {
55
55
  enumerable: false,
56
56
  configurable: true
57
57
  });
58
+ Object.defineProperty(NeoTypeInfo.prototype, "typeName", {
59
+ get: function () {
60
+ return this.objectConstructor.name;
61
+ },
62
+ enumerable: false,
63
+ configurable: true
64
+ });
58
65
  NeoTypeInfo.prototype.initialise = function (objectConstructor) {
59
66
  this.objectConstructor = objectConstructor;
60
67
  };
@@ -7,3 +7,11 @@ export declare class ObservableValue<T> implements IObservableValue<T> {
7
7
  constructor(initialValue: T);
8
8
  get peek(): T;
9
9
  }
10
+ /**
11
+ * Creates a wrapper around a value with a getter and setter for reading and writing the value.
12
+ */
13
+ export declare class ValueWrapper<T> implements IObservableValue<T> {
14
+ constructor(getter: () => T, setter: (value: T) => void);
15
+ value: T;
16
+ get peek(): T;
17
+ }
@@ -24,3 +24,20 @@ var ObservableValue = /** @class */ (function () {
24
24
  return ObservableValue;
25
25
  }());
26
26
  export { ObservableValue };
27
+ /**
28
+ * Creates a wrapper around a value with a getter and setter for reading and writing the value.
29
+ */
30
+ var ValueWrapper = /** @class */ (function () {
31
+ function ValueWrapper(getter, setter) {
32
+ Object.defineProperty(this, "value", { get: getter, set: setter });
33
+ }
34
+ Object.defineProperty(ValueWrapper.prototype, "peek", {
35
+ get: function () {
36
+ return this.value;
37
+ },
38
+ enumerable: false,
39
+ configurable: true
40
+ });
41
+ return ValueWrapper;
42
+ }());
43
+ export { ValueWrapper };
@@ -1,6 +1,6 @@
1
1
  import { SerialiseType, RelationType, OnChangedOptions, DataType, TimeZoneFormat } from './Misc';
2
2
  import { Nullable } from '../Utils';
3
- import { INonObservableModelBase } from './IModelBase';
3
+ import { INonObservableModelBase, IObjectFactoryOptions, ObjectFactory } from './IModelBase';
4
4
  import { Rule } from '../Validation/Rules';
5
5
  export type PropertyInfoMap = {
6
6
  [index: string]: PropertyInfo;
@@ -58,6 +58,8 @@ export default class PropertyInfo {
58
58
  rules: Rule[];
59
59
  /** Indicates that this property must be assigned to on de/serialisation. The de/serialiser will not try create a model instance of the object / array. */
60
60
  assignOnDeserialise: boolean;
61
+ /** Object factory used for deserialisation. Set by ChildList / ChildObject decorators. */
62
+ itemFactory?: ObjectFactory;
61
63
  constructor(propertyName: string);
62
64
  /** Gets the readable name of the property. Uses string.readable if Display decorator was not specified. */
63
65
  get readableName(): string;
@@ -67,7 +69,7 @@ export default class PropertyInfo {
67
69
  get parent(): PropertyInfo | undefined;
68
70
  addAttributeRule(rule: Rule): void;
69
71
  clone(): PropertyInfo;
70
- setPropertyType(propertyType: (() => any) | (new () => any)): void;
72
+ setPropertyType(propertyType: (() => any) | (new () => any) | IObjectFactoryOptions): void;
71
73
  /**
72
74
  * Gets the constructor function of the return type.
73
75
  * Returns null if the property has not been decorated with a ChildObject / Parent attribute.
@@ -78,6 +80,6 @@ export default class PropertyInfo {
78
80
  * Raises an error if the property has not been decorated with a ChildObject / Parent attribute.
79
81
  */
80
82
  get typeWithAssert(): new () => any;
81
- createValueInstance(): any;
83
+ createValueInstance(data: any): any;
82
84
  setupFromInstance(instance: any): void;
83
85
  }
@@ -75,11 +75,16 @@ var PropertyInfo = /** @class */ (function () {
75
75
  return clone;
76
76
  };
77
77
  PropertyInfo.prototype.setPropertyType = function (propertyType) {
78
- if (!propertyType.prototype || !propertyType.prototype.constructor.name) {
79
- this.typeCallback = propertyType;
78
+ if (typeof propertyType === "function") {
79
+ if (!propertyType.prototype || !propertyType.prototype.constructor.name) {
80
+ this.typeCallback = propertyType;
81
+ }
82
+ else {
83
+ this._type = propertyType;
84
+ }
80
85
  }
81
86
  else {
82
- this._type = propertyType;
87
+ this.itemFactory = propertyType.itemFactory;
83
88
  }
84
89
  };
85
90
  Object.defineProperty(PropertyInfo.prototype, "type", {
@@ -116,7 +121,10 @@ var PropertyInfo = /** @class */ (function () {
116
121
  enumerable: false,
117
122
  configurable: true
118
123
  });
119
- PropertyInfo.prototype.createValueInstance = function () {
124
+ PropertyInfo.prototype.createValueInstance = function (data) {
125
+ if (this.itemFactory) {
126
+ return this.itemFactory(data);
127
+ }
120
128
  try {
121
129
  return new this.typeWithAssert();
122
130
  }
@@ -2,7 +2,7 @@ export { IBindableBase, IModelBase, NeoModelConstructor, PlainObject, PlainTrack
2
2
  export { ILookupBase } from './LookupBase';
3
3
  export { IPropertyInstance, PropertyAccessor } from './PropertyInstance';
4
4
  import PropertyInfo from './PropertyInfo';
5
- export { IObservableValue, ObservableValue } from './ObservableValue';
5
+ export { IObservableValue, ObservableValue, ValueWrapper } from './ObservableValue';
6
6
  export { ObservableProperty } from './ObservableProperty';
7
7
  export { ISelectedItem } from './Misc';
8
8
  export { ModelFieldInfo, IModelFieldInfoContainer } from './ModelFieldInfo';
@@ -1,6 +1,6 @@
1
1
  export { PropertyAccessor } from './PropertyInstance';
2
2
  import PropertyInfo from './PropertyInfo';
3
- export { ObservableValue } from './ObservableValue';
3
+ export { ObservableValue, ValueWrapper } from './ObservableValue';
4
4
  export { ObservableProperty } from './ObservableProperty';
5
5
  export { ModelFieldInfo } from './ModelFieldInfo';
6
6
  import * as GraphTraverser from './ModelGraphTraverser';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singularsystems/neo-core",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Core functionality for the Neo client library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@microsoft/signalr": "^6.0.1",
28
- "axios": "1.6.7",
28
+ "axios": "1.7.4",
29
29
  "inversify": "^6.0.2",
30
30
  "reflect-metadata": "0.1.13",
31
31
  "tslib": "2.5.0"