@singularsystems/neo-react 0.10.57 → 0.10.61

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.
@@ -67,7 +67,12 @@ export interface IAutoCompleteMultiSelectProps<T> extends IAutoCompleteCommonPro
67
67
  /**
68
68
  * Optional list of ids. This will be kept in sync with the bindItems prop.
69
69
  */
70
- bindIds?: (string | number)[];
70
+ bindIds?: (string | number)[] | Model.IPropertyInstance<number[]>;
71
+ /**
72
+ * The max no of items to record in the field info if bound to a field info class. (Defaults to 5)
73
+ * If the selected items exceeds this limit, the field info will be recorded as "x items selected".
74
+ */
75
+ fieldInfoLimit?: number;
71
76
  }
72
77
  export default class AutoCompleteDropDown<T> extends DropDownBase<T, IAutoCompleteProps<T> | IAutoCompleteMultiSelectProps<T>> {
73
78
  static contextType: React.Context<import("../FormContext").FormContextParams>;
@@ -75,6 +80,9 @@ export default class AutoCompleteDropDown<T> extends DropDownBase<T, IAutoComple
75
80
  private asyncSelectImplementation;
76
81
  private selectImplementation;
77
82
  constructor(props: IAutoCompleteProps<T> | IAutoCompleteMultiSelectProps<T>);
83
+ private bindProperty;
84
+ private bindIdsList?;
85
+ private bindIdsProperty?;
78
86
  static getBindProp<T>(props: IAutoCompleteProps<T> | IAutoCompleteMultiSelectProps<T>): any;
79
87
  private onKeyPress;
80
88
  private mapTArrayToOptions;
@@ -1,6 +1,6 @@
1
1
  import { __assign, __decorate, __extends, __metadata, __rest } from "tslib";
2
2
  import React from 'react';
3
- import { EnumHelper, Misc, Utils } from '@singularsystems/neo-core';
3
+ import { EnumHelper, Misc, Model, Utils } from '@singularsystems/neo-core';
4
4
  import { observer } from 'mobx-react';
5
5
  import { DropDownBase } from './DropDownBase';
6
6
  import { BindPropsHelper } from '../PropTypes';
@@ -11,11 +11,27 @@ import { NeoReactTypes } from '../../Modules/Types';
11
11
  var AutoCompleteDropDown = /** @class */ (function (_super) {
12
12
  __extends(AutoCompleteDropDown, _super);
13
13
  function AutoCompleteDropDown(props) {
14
+ var _a;
14
15
  var _this = _super.call(this, props) || this;
15
16
  _this.asyncSelectImplementation = Misc.Globals.appService.get(NeoReactTypes.Components.AsyncSelect);
16
17
  _this.selectImplementation = Misc.Globals.appService.get(NeoReactTypes.Components.ReactSelect);
17
18
  _this.onKeyPress = _this.onKeyPress.bind(_this);
18
19
  _this.onItemSelected = _this.onItemSelected.bind(_this);
20
+ _this.bindProperty = (_a = _this.props.bind, (_a !== null && _a !== void 0 ? _a : _this.props.bindItems));
21
+ _this.fieldInfoContainer = Model.ModelFieldInfo.asModelFieldInfoContainer(_this.bindProperty.attachedTo);
22
+ if (_this.props.bindIds) {
23
+ if (_this.props.bindIds.propertyInfo) {
24
+ _this.bindIdsProperty = _this.props.bindIds;
25
+ _this.bindIdsList = _this.props.bindIds.value;
26
+ }
27
+ else {
28
+ _this.bindIdsList = _this.props.bindIds;
29
+ if (_this.fieldInfoContainer) {
30
+ console.error("Warning: Pass a property instance as 'bindIds', otherwise the display values will not be captured.");
31
+ _this.fieldInfoContainer = null;
32
+ }
33
+ }
34
+ }
19
35
  return _this;
20
36
  }
21
37
  AutoCompleteDropDown.getBindProp = function (props) {
@@ -58,11 +74,10 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
58
74
  var itemsProperty = this.props.bindItems;
59
75
  // Multi select
60
76
  var itemList = itemsProperty.value;
61
- var bindIdsList = this.props.bindIds;
62
77
  if (action.action === "select-option") {
63
78
  itemList.push({ id: action.option.value, display: action.option.label });
64
- if (bindIdsList) {
65
- bindIdsList.push(action.option.value);
79
+ if (this.bindIdsList) {
80
+ this.bindIdsList.push(action.option.value);
66
81
  }
67
82
  if (onItemSelected) {
68
83
  onItemSelected((_a = action.option) === null || _a === void 0 ? void 0 : _a.item);
@@ -72,8 +87,8 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
72
87
  var removeItem = itemList.find(function (c) { return c.id === action.removedValue.value; });
73
88
  if (removeItem) {
74
89
  itemList.remove(removeItem);
75
- if (bindIdsList) {
76
- bindIdsList.remove(removeItem.id);
90
+ if (this.bindIdsList) {
91
+ this.bindIdsList.remove(removeItem.id);
77
92
  }
78
93
  }
79
94
  if (onItemSelected) {
@@ -82,8 +97,8 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
82
97
  }
83
98
  else if (action.action === "clear") {
84
99
  itemList.length = 0;
85
- if (bindIdsList) {
86
- bindIdsList.length = 0;
100
+ if (this.bindIdsList) {
101
+ this.bindIdsList.length = 0;
87
102
  }
88
103
  if (onItemSelected) {
89
104
  onItemSelected(undefined);
@@ -105,15 +120,14 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
105
120
  AutoCompleteDropDown.prototype.render = function () {
106
121
  var _this = this;
107
122
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
108
- var _m = this.props, bind = _m.bind, bindDisplay = _m.bindDisplay, bindItems = _m.bindItems, bindIds = _m.bindIds, itemSource = _m.itemSource, items = _m.items, displayMember = _m.displayMember, valueMember = _m.valueMember, descriptionMember = _m.descriptionMember, option = _m.option, noRecordsPrompt = _m.noRecordsPrompt, typeToSearchPrompt = _m.typeToSearchPrompt, delayMs = _m.delayMs, minCharacters = _m.minCharacters, onItemSelected = _m.onItemSelected, otherProps = __rest(_m, ["bind", "bindDisplay", "bindItems", "bindIds", "itemSource", "items", "displayMember", "valueMember", "descriptionMember", "option", "noRecordsPrompt", "typeToSearchPrompt", "delayMs", "minCharacters", "onItemSelected"]);
123
+ var _m = this.props, bind = _m.bind, bindDisplay = _m.bindDisplay, bindItems = _m.bindItems, bindIds = _m.bindIds, itemSource = _m.itemSource, items = _m.items, displayMember = _m.displayMember, valueMember = _m.valueMember, descriptionMember = _m.descriptionMember, option = _m.option, noRecordsPrompt = _m.noRecordsPrompt, typeToSearchPrompt = _m.typeToSearchPrompt, delayMs = _m.delayMs, minCharacters = _m.minCharacters, onItemSelected = _m.onItemSelected, fieldInfoLimit = _m.fieldInfoLimit, otherProps = __rest(_m, ["bind", "bindDisplay", "bindItems", "bindIds", "itemSource", "items", "displayMember", "valueMember", "descriptionMember", "option", "noRecordsPrompt", "typeToSearchPrompt", "delayMs", "minCharacters", "onItemSelected", "fieldInfoLimit"]);
109
124
  var splitProps = BindPropsHelper.splitCommonEditorProps(otherProps);
110
125
  var nativeProps = splitProps.rest;
111
126
  var bindItemsProperty = bindItems;
112
127
  var itemList = (_a = bindItemsProperty) === null || _a === void 0 ? void 0 : _a.value;
113
128
  var bindValue = bind;
114
129
  var bindDisplayProperty = bindDisplay;
115
- var bindProperty = (bindItemsProperty !== null && bindItemsProperty !== void 0 ? bindItemsProperty : bindValue);
116
- var displayInfo = bindProperty.validator.getDisplayState(this.context.validationDisplayMode);
130
+ var displayInfo = this.bindProperty.validator.getDisplayState(this.context.validationDisplayMode);
117
131
  displayInfo.applyToDom(nativeProps, this.context.parentType !== EditorParentType.FormGroup);
118
132
  this.displayMember = (_b = this.props.displayMember, (_b !== null && _b !== void 0 ? _b : ""));
119
133
  this.valueMember = (_c = this.props.valueMember, (_c !== null && _c !== void 0 ? _c : ""));
@@ -130,6 +144,16 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
130
144
  var value = null;
131
145
  if (bindItems) {
132
146
  value = itemList.map(function (c) { return ({ value: c.id, label: c.display }); });
147
+ if (this.fieldInfoContainer && this.bindIdsProperty) {
148
+ var itemListString = "";
149
+ if (value.length > ((fieldInfoLimit !== null && fieldInfoLimit !== void 0 ? fieldInfoLimit : 5))) {
150
+ itemListString = value.length + " items selected";
151
+ }
152
+ else if (value.length > 0) {
153
+ itemListString = value.map(function (c) { return c.label; }).join(", ");
154
+ }
155
+ this.setFieldInfo(this.bindIdsProperty, itemListString, bindItemsProperty.propertyInfo.readableName);
156
+ }
133
157
  }
134
158
  else {
135
159
  if (bindValue.value !== null) {
@@ -170,7 +194,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
170
194
  }
171
195
  var userOnBlur = nativeProps.onBlur;
172
196
  nativeProps.onBlur = function (e) {
173
- bindProperty.validator.hasBlurred = true;
197
+ _this.bindProperty.validator.hasBlurred = true;
174
198
  if (userOnBlur !== undefined) {
175
199
  userOnBlur(e);
176
200
  }
@@ -1,7 +1,7 @@
1
1
  import { __assign, __decorate, __extends, __metadata } from "tslib";
2
2
  import * as React from 'react';
3
3
  import { BindPropsHelper } from '../PropTypes';
4
- import { Utils } from '@singularsystems/neo-core';
4
+ import { Model, Utils } from '@singularsystems/neo-core';
5
5
  import { observer } from 'mobx-react';
6
6
  import { InputHelpers } from '../InputHelpers';
7
7
  import { DropDownBase } from './DropDownBase';
@@ -12,6 +12,7 @@ var DropDown = /** @class */ (function (_super) {
12
12
  var _this = _super.call(this, props) || this;
13
13
  _this.listIndexSymbol = Symbol("ValueIndex");
14
14
  _this.onItemSelected = _this.onItemSelected.bind(_this);
15
+ _this.fieldInfoContainer = Model.ModelFieldInfo.asModelFieldInfoContainer(_this.props.bind.attachedTo);
15
16
  return _this;
16
17
  }
17
18
  Object.defineProperty(DropDown.prototype, "items", {
@@ -117,9 +118,12 @@ var DropDown = /** @class */ (function (_super) {
117
118
  }
118
119
  var valueIsBlank = value === undefined || value === null;
119
120
  var nullText = (valueIsBlank || ((_d = splitProps.select) === null || _d === void 0 ? void 0 : _d.deSelectText) === undefined) ? (_e = splitProps.select) === null || _e === void 0 ? void 0 : _e.nullText : (_f = splitProps.select) === null || _f === void 0 ? void 0 : _f.deSelectText;
121
+ if (valueIsBlank) {
122
+ domProps.className = Utils.joinWithSpaces(domProps.className, "select-no-value");
123
+ }
120
124
  control =
121
125
  React.createElement("select", __assign({ value: valueIsBlank ? "" : value, onChange: this.onItemSelected, ref: splitProps.editor.inputElement }, domProps),
122
- addBlankItem && React.createElement("option", null, nullText),
126
+ addBlankItem && React.createElement("option", { className: "option-default" }, nullText),
123
127
  items.map(function (item) { return (React.createElement("option", { value: item[_this.valueMember], key: item[_this.valueMember] }, item[_this.displayMember])); }));
124
128
  }
125
129
  return (InputHelpers.surroundWithInputGroup(control, splitProps.editor));
@@ -8,8 +8,9 @@ export declare abstract class DropDownBase<T, TProps> extends ComponentBase<TPro
8
8
  protected isLoading: boolean;
9
9
  protected dataSource: Data.IAsyncDataSource<T[]> | null;
10
10
  protected cachedDataSource: Data.IDataCacheEntry | null;
11
+ protected fieldInfoContainer: Model.IModelFieldInfoContainer | null;
11
12
  protected getItems(itemSource: ItemSource<T>): T[];
12
13
  afterRender(): Promise<void>;
13
14
  private fieldDisplayInfo;
14
- protected setFieldInfo(bindProperty: Model.IPropertyInstance, displayValue: string): void;
15
+ protected setFieldInfo(bindProperty: Model.IPropertyInstance, displayValue: string, readableName?: string): void;
15
16
  }
@@ -1,5 +1,5 @@
1
1
  import { __awaiter, __extends, __generator } from "tslib";
2
- import { Data, Model } from '@singularsystems/neo-core';
2
+ import { Data } from '@singularsystems/neo-core';
3
3
  import ComponentBase from '../ComponentBase';
4
4
  import LookupHelper from './LookupHelper';
5
5
  var DropDownBase = /** @class */ (function (_super) {
@@ -9,6 +9,7 @@ var DropDownBase = /** @class */ (function (_super) {
9
9
  _this.isLoading = false;
10
10
  _this.dataSource = null;
11
11
  _this.cachedDataSource = null;
12
+ _this.fieldInfoContainer = null;
12
13
  _this.fieldDisplayInfo = null;
13
14
  return _this;
14
15
  }
@@ -53,7 +54,7 @@ var DropDownBase = /** @class */ (function (_super) {
53
54
  };
54
55
  DropDownBase.prototype.afterRender = function () {
55
56
  return __awaiter(this, void 0, void 0, function () {
56
- var _a, bindProperty, displayValue, fieldInfo;
57
+ var _a, bindProperty, displayValue, readableName, fieldInfo;
57
58
  return __generator(this, function (_b) {
58
59
  if (this.cachedDataSource) {
59
60
  this.dataSource = this.cachedDataSource.get();
@@ -62,16 +63,12 @@ var DropDownBase = /** @class */ (function (_super) {
62
63
  this.dataSource.fetchInitial();
63
64
  }
64
65
  if (this.fieldDisplayInfo) {
65
- _a = this.fieldDisplayInfo, bindProperty = _a.bindProperty, displayValue = _a.displayValue;
66
+ _a = this.fieldDisplayInfo, bindProperty = _a.bindProperty, displayValue = _a.displayValue, readableName = _a.readableName;
66
67
  this.fieldDisplayInfo = null;
67
- if (bindProperty.attachedTo instanceof Model.CriteriaBase) {
68
- fieldInfo = bindProperty.attachedTo.fieldInfo[bindProperty.propertyInfo.propertyName];
69
- if (!fieldInfo) {
70
- fieldInfo = new Model.ModelFieldInfo(bindProperty.propertyInfo.readableName, displayValue);
71
- bindProperty.attachedTo.fieldInfo[bindProperty.propertyInfo.propertyName] = fieldInfo;
72
- }
73
- else {
74
- fieldInfo.valueString = displayValue;
68
+ if (this.fieldInfoContainer) {
69
+ fieldInfo = this.fieldInfoContainer.setModelFieldInfo(bindProperty.propertyInfo, displayValue);
70
+ if (readableName) {
71
+ fieldInfo.displayName = readableName;
75
72
  }
76
73
  }
77
74
  }
@@ -79,8 +76,10 @@ var DropDownBase = /** @class */ (function (_super) {
79
76
  });
80
77
  });
81
78
  };
82
- DropDownBase.prototype.setFieldInfo = function (bindProperty, displayValue) {
83
- this.fieldDisplayInfo = { bindProperty: bindProperty, displayValue: displayValue };
79
+ DropDownBase.prototype.setFieldInfo = function (bindProperty, displayValue, readableName) {
80
+ if (this.fieldInfoContainer) {
81
+ this.fieldDisplayInfo = { bindProperty: bindProperty, displayValue: displayValue, readableName: readableName };
82
+ }
84
83
  };
85
84
  return DropDownBase;
86
85
  }(ComponentBase));
@@ -40,7 +40,7 @@ var Form = /** @class */ (function (_super) {
40
40
  var onClose = this.disposeReaction.bind(this);
41
41
  var codeModal_1 = ModalUtils.showModal(this.props.invalidSummaryTitle || "Invalid data", React.createElement(Observer, null, function () {
42
42
  return React.createElement("div", null,
43
- _this.props.invalidSummaryText || "Cannot continue due to the following errors:",
43
+ _this.props.invalidSummaryText || "Cannot continue due to the following issues:",
44
44
  React.createElement(ValidationSummary, { graph: Validation.getValidationSummary(model), hideTopLevelTitle: !(model instanceof Array) }));
45
45
  }), { onClose: onClose, closeButton: { text: "Ok", onClick: onClose } });
46
46
  // Listen for when isValid changes to true, in case there is an async rule running.
@@ -143,7 +143,7 @@ var Column = /** @class */ (function (_super) {
143
143
  if (sortProperty && !this.suppressSort) {
144
144
  var sortInfo = this.context.dataView.sortInfo.find(function (c) { return c.column === sortProperty.propertyInfo; });
145
145
  if (sortInfo) {
146
- sortIcon = sortInfo.sortAscending ? "sort-alpha-down" : "sort-alpha-up";
146
+ sortIcon = sortInfo.sortAscending ? Misc.Settings.icons.sortAsc : Misc.Settings.icons.sortDesc;
147
147
  domProps.className = Utils.joinWithSpaces(domProps.className, "column-is-sorted");
148
148
  }
149
149
  domProps.className = Utils.joinWithSpaces(domProps.className, "column-sortable");
@@ -163,7 +163,7 @@ var Column = /** @class */ (function (_super) {
163
163
  if (editorProps.isReadOnly && !editorProps.requiresGroup) {
164
164
  var rawValue = editorProps.bind.value;
165
165
  if (this.controlType == ControlType.Number) {
166
- displayValue = this.numFormatState(editorProps.bind.propertyInfo.propertyType, props.numProps).getFormattedValue(rawValue);
166
+ displayValue = this.numFormatState(editorProps.bind.propertyInfo, editorProps.bind.propertyInfo.propertyType, props.numProps).getFormattedValue(rawValue);
167
167
  }
168
168
  else if (this.controlType == ControlType.Date) {
169
169
  displayValue = DateUtils.format(rawValue, props.dateProps ? props.dateProps.formatString : undefined);
@@ -197,7 +197,7 @@ var Column = /** @class */ (function (_super) {
197
197
  var hasSummary = (colProps.sum && !!props.bind) || colProps.footerContent;
198
198
  return (React.createElement("th", __assign({}, domProps), hasSummary &&
199
199
  React.createElement(Observer, null, function () { return colProps.footerContent ? colProps.footerContent(_this.context.dataView.filteredItems) :
200
- _this.numFormatState(props.bind.propertyInfo.propertyType, props.numProps).getFormattedValue(_this.context.dataView.filteredItems.sum(props.bind.propertyInfo)); })));
200
+ _this.numFormatState(props.bind.propertyInfo, props.bind.propertyInfo.propertyType, props.numProps).getFormattedValue(_this.context.dataView.filteredItems.sum(props.bind.propertyInfo)); })));
201
201
  };
202
202
  Column.isColumnComponent = true;
203
203
  Column.contextType = GridContext;
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { IBindableEditorProps } from './PropTypes';
3
3
  import { FormContext } from './FormContext';
4
- import { Misc } from '@singularsystems/neo-core';
4
+ import { Misc, Model } from '@singularsystems/neo-core';
5
5
  import Decimal from 'decimal.js-light';
6
6
  import ComponentBase from './ComponentBase';
7
7
  export interface INumberInputSpecificProps {
@@ -25,10 +25,14 @@ export declare class FormatState {
25
25
  isPercent: boolean;
26
26
  hasExternalPercentSymbol: boolean;
27
27
  formatString: string;
28
+ /** How many decimals the value is allowed. */
29
+ allowedDecimals: number;
28
30
  zeroText?: string | undefined;
29
31
  useNative?: boolean | undefined;
30
- constructor(isPercent: boolean, hasExternalPercentSymbol: boolean, formatString: string, zeroText?: string | undefined, useNative?: boolean | undefined);
31
- static create(numericType: Misc.DataType, numProps?: INumberInputSpecificProps, appendText?: string): FormatState;
32
+ constructor(isPercent: boolean, hasExternalPercentSymbol: boolean, formatString: string,
33
+ /** How many decimals the value is allowed. */
34
+ allowedDecimals: number, zeroText?: string | undefined, useNative?: boolean | undefined);
35
+ static create(propertyInfo: Model.PropertyInfo, numericType: Misc.DataType, numProps?: INumberInputSpecificProps, appendText?: string): FormatState;
32
36
  getFormattedValue(value?: number | Decimal | null): string;
33
37
  }
34
38
  export default class NumberInput extends ComponentBase<INumberInputProps> {
@@ -9,37 +9,49 @@ import { observer } from 'mobx-react';
9
9
  import memoize from 'memoize-one';
10
10
  import ComponentBase from './ComponentBase';
11
11
  var FormatState = /** @class */ (function () {
12
- function FormatState(isPercent, hasExternalPercentSymbol, formatString, zeroText, useNative) {
12
+ function FormatState(isPercent, hasExternalPercentSymbol, formatString,
13
+ /** How many decimals the value is allowed. */
14
+ allowedDecimals, zeroText, useNative) {
13
15
  this.isPercent = isPercent;
14
16
  this.hasExternalPercentSymbol = hasExternalPercentSymbol;
15
17
  this.formatString = formatString;
18
+ this.allowedDecimals = allowedDecimals;
16
19
  this.zeroText = zeroText;
17
20
  this.useNative = useNative;
18
21
  }
19
- FormatState.create = function (numericType, numProps, appendText) {
20
- var _a, _b, _c, _d;
22
+ FormatState.create = function (propertyInfo, numericType, numProps, appendText) {
23
+ var _a, _b, _c, _d, _e, _f, _g;
21
24
  var isPercent = false;
22
25
  var hasExternalPercentSymbol = false;
23
26
  var formatString = numProps ? numProps.formatString : "";
27
+ var displayDecimals = 2;
28
+ if ((formatString && formatString.indexOf("%") >= 0)
29
+ || appendText === "%"
30
+ || ((_a = numProps) === null || _a === void 0 ? void 0 : _a.format) === Misc.NumberFormat.PercentDecimals || ((_b = numProps) === null || _b === void 0 ? void 0 : _b.format) === Misc.NumberFormat.PercentNoDecimals) {
31
+ isPercent = true;
32
+ }
33
+ if (propertyInfo.allowedDecimals !== undefined) {
34
+ displayDecimals = propertyInfo.allowedDecimals - (isPercent ? 2 : 0);
35
+ }
36
+ else if ((_c = numProps) === null || _c === void 0 ? void 0 : _c.formatString) {
37
+ displayDecimals = NumberUtils.getDecimalPlaces(numProps.formatString);
38
+ }
24
39
  if (!formatString) {
25
40
  if (numProps && numProps.format) {
26
- formatString = NumberUtils.getFormatString(numProps.format, numProps.currencySymbol);
41
+ formatString = NumberUtils.getFormatString(numProps.format, numProps.currencySymbol, displayDecimals);
27
42
  }
28
43
  else {
29
44
  var hasDecimals = numericType === Misc.DataType.Float || numericType === Misc.DataType.Decimal;
30
- var format = ((_a = numProps) === null || _a === void 0 ? void 0 : _a.currencySymbol) ?
45
+ var format = ((_d = numProps) === null || _d === void 0 ? void 0 : _d.currencySymbol) ?
31
46
  (hasDecimals ? Misc.NumberFormat.CurrencyDecimals : Misc.NumberFormat.CurrencyNoDecimals) :
32
47
  (hasDecimals ? Misc.NumberFormat.Decimals : Misc.NumberFormat.NoDecimals);
33
- formatString = NumberUtils.getFormatString(format, (_b = numProps) === null || _b === void 0 ? void 0 : _b.currencySymbol);
48
+ formatString = NumberUtils.getFormatString(format, (_e = numProps) === null || _e === void 0 ? void 0 : _e.currencySymbol, displayDecimals);
34
49
  }
35
50
  }
36
51
  if (formatString.indexOf("%") < 0 && appendText === "%") {
37
52
  hasExternalPercentSymbol = true;
38
53
  }
39
- if (formatString.indexOf("%") >= 0 || appendText === "%") {
40
- isPercent = true;
41
- }
42
- return new FormatState(isPercent, hasExternalPercentSymbol, formatString, (_c = numProps) === null || _c === void 0 ? void 0 : _c.zeroText, (_d = numProps) === null || _d === void 0 ? void 0 : _d.native);
54
+ return new FormatState(isPercent, hasExternalPercentSymbol, formatString, displayDecimals + (isPercent ? 2 : 0), (_f = numProps) === null || _f === void 0 ? void 0 : _f.zeroText, (_g = numProps) === null || _g === void 0 ? void 0 : _g.native);
43
55
  };
44
56
  FormatState.prototype.getFormattedValue = function (value) {
45
57
  if (!value && this.zeroText !== undefined) {
@@ -112,7 +124,7 @@ var NumberInput = /** @class */ (function (_super) {
112
124
  }
113
125
  else {
114
126
  if (!this.props.numProps || this.props.numProps.autoRound !== false) {
115
- newValue = NumberUtils.roundUsingFormat(newValue, this.formatState.formatString, this.formatState.hasExternalPercentSymbol);
127
+ newValue = NumberUtils.round(newValue, this.formatState.allowedDecimals);
116
128
  }
117
129
  if (this.numericType === Misc.DataType.Decimal) {
118
130
  this.props.bind.value = newValue;
@@ -147,7 +159,7 @@ var NumberInput = /** @class */ (function (_super) {
147
159
  }
148
160
  else {
149
161
  if (element.readOnly || !this.props.numProps || this.props.numProps.autoRound !== false) {
150
- stringValue = NumberUtils.formatRaw(numberValue, this.formatState.formatString, this.formatState.hasExternalPercentSymbol);
162
+ stringValue = NumberUtils.formatRaw(numberValue, this.formatState.formatString, this.formatState.hasExternalPercentSymbol, this.formatState.allowedDecimals);
151
163
  }
152
164
  else {
153
165
  if (this.formatState.isPercent && NumberUtils.transformPercent) {
@@ -222,7 +234,7 @@ var NumberInput = /** @class */ (function (_super) {
222
234
  var useNative = (_a = splitProps.numProps) === null || _a === void 0 ? void 0 : _a.native;
223
235
  var displayInfo = this.props.bind.validator.getDisplayState(this.context.validationDisplayMode);
224
236
  displayInfo.applyToDom(domProps, !splitProps.inGroup);
225
- this.formatState = this.recalcFormatState(this.numericType, splitProps.numProps, editorProps.appendText);
237
+ this.formatState = this.recalcFormatState(this.props.bind.propertyInfo, this.numericType, splitProps.numProps, editorProps.appendText);
226
238
  this.isReadOnly = domProps.readOnly === true;
227
239
  if (editorProps.inputElement) {
228
240
  this.inputElement = editorProps.inputElement;
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { IRouteParameter } from './IRouteParameter';
2
3
  import { IViewParameterListInternal } from './ViewParameterList';
3
4
  import { Model } from '@singularsystems/neo-core';
@@ -16,6 +17,12 @@ export interface IViewParameter {
16
17
  * Used by the breadcrumb.
17
18
  */
18
19
  description: string;
20
+ /**
21
+ * Sets the description, and an optional list of prefixes.
22
+ * @param description Description of the current value.
23
+ * @param prefixes Function that returns prefixes for this breadcrumb item. The previous prefixes are passed into the function.
24
+ */
25
+ setDescription(description: string, getPrefixes?: (currentPrefixes: IViewParameterPrefix[]) => IViewParameterPrefix[]): void;
19
26
  /**
20
27
  * Returns value as a nullable int.
21
28
  */
@@ -41,6 +48,7 @@ export declare class ViewParameter implements IViewParameter {
41
48
  private hasPendingValue;
42
49
  private pendingValue;
43
50
  private _value;
51
+ prefixes: IViewParameterPrefix[];
44
52
  constructor(parent: IViewParameterListInternal, name: string, routeParameter: IRouteParameter);
45
53
  bindTo(propertyOrCallback: Model.IPropertyInstance | (() => (string | number | null | undefined))): void;
46
54
  get value(): number | string | null;
@@ -49,6 +57,7 @@ export declare class ViewParameter implements IViewParameter {
49
57
  _description: string;
50
58
  get description(): string;
51
59
  set description(value: string);
60
+ setDescription(description: string, getPrefixes?: (currentPrefixes: IViewParameterPrefix[]) => IViewParameterPrefix[]): void;
52
61
  asNullableInt(): number | null;
53
62
  asString(): string;
54
63
  /** Updates the current value, and returns true if the value changed. */
@@ -58,3 +67,14 @@ export declare class ViewParameter implements IViewParameter {
58
67
  }): string;
59
68
  get latestValue(): string | null;
60
69
  }
70
+ interface IViewParameterPrefix {
71
+ /** Label to show on breadcrumb. */
72
+ label: string;
73
+ /** Value to use in the breadcrumb link. Clicking on the link will set the view parameter to this value. */
74
+ value?: number | string | null;
75
+ /** Custom link to use in the breadcrumb. */
76
+ link?: string;
77
+ /** Click event for the breadcrumb. */
78
+ onClick?: (e: React.MouseEvent) => void;
79
+ }
80
+ export {};
@@ -8,6 +8,7 @@ var ViewParameter = /** @class */ (function () {
8
8
  this.hasPendingValue = false;
9
9
  this.pendingValue = null;
10
10
  this._value = null;
11
+ this.prefixes = [];
11
12
  this._description = "";
12
13
  }
13
14
  ViewParameter.prototype.bindTo = function (propertyOrCallback) {
@@ -28,7 +29,6 @@ var ViewParameter = /** @class */ (function () {
28
29
  if (stringValue !== this._value) {
29
30
  this.hasPendingValue = true;
30
31
  this.pendingValue = stringValue;
31
- this._description = "";
32
32
  this.parent.setUrl(this);
33
33
  }
34
34
  },
@@ -55,6 +55,10 @@ var ViewParameter = /** @class */ (function () {
55
55
  enumerable: true,
56
56
  configurable: true
57
57
  });
58
+ ViewParameter.prototype.setDescription = function (description, getPrefixes) {
59
+ this.prefixes = getPrefixes ? getPrefixes(this.prefixes) : [];
60
+ this.description = description;
61
+ };
58
62
  ViewParameter.prototype.asNullableInt = function () {
59
63
  if (this._value) {
60
64
  return parseInt(this._value, 10);
@@ -66,13 +70,17 @@ var ViewParameter = /** @class */ (function () {
66
70
  };
67
71
  /** Updates the current value, and returns true if the value changed. */
68
72
  ViewParameter.prototype.update = function (value) {
69
- if (value)
73
+ if (value) {
70
74
  value = decodeURIComponent(value);
75
+ }
71
76
  if (value != this._value) {
72
77
  this.hasPendingValue = false;
73
78
  this.pendingValue = value;
74
- this._description = "";
75
79
  this._value = value;
80
+ if (!value) {
81
+ this._description = "";
82
+ this.prefixes = [];
83
+ }
76
84
  return true;
77
85
  }
78
86
  return false;
@@ -28,6 +28,22 @@ var ViewParameterList = /** @class */ (function () {
28
28
  for (var _i = 0, _b = this.params; _i < _b.length; _i++) {
29
29
  var viewParam = _b[_i];
30
30
  if (!viewParam.routeParameter.isQuery) {
31
+ if (!upTo) {
32
+ for (var _c = 0, _d = viewParam.prefixes; _c < _d.length; _c++) {
33
+ var prefix = _d[_c];
34
+ var breadcrumbItem = new BreadCrumbItem(prefix.label);
35
+ if (prefix.onClick) {
36
+ breadcrumbItem.onClick = prefix.onClick;
37
+ }
38
+ else if (prefix.link) {
39
+ breadcrumbItem.link = prefix.link;
40
+ }
41
+ else if (prefix.value) {
42
+ breadcrumbItem.link = paramPath + "/" + prefix.value;
43
+ }
44
+ breadCrumbs.push(breadcrumbItem);
45
+ }
46
+ }
31
47
  paramPath += viewParam.getParamString(state);
32
48
  if ((_a = viewParam.routeParameter.selection, (_a !== null && _a !== void 0 ? _a : BreadCrumbSelectMode.Default)) === BreadCrumbSelectMode.Default) {
33
49
  selectablePath = paramPath;
@@ -46,7 +46,7 @@ export default abstract class ViewBase<TViewModel extends IViewModel = EmptyView
46
46
  /**
47
47
  * viewParamsUpdated() is called when the component is mounted, and whenever a route parameter or query parameter gets a changed value.
48
48
  */
49
- protected viewParamsUpdated(): void;
49
+ protected viewParamsUpdated(): void | Promise<void>;
50
50
  /** Gets the parent breadcrumbs, and the breadcrumb for this view. */
51
51
  protected getBreadCrumbStart(): BreadCrumbItem[];
52
52
  /**
@@ -1,6 +1,6 @@
1
- import { __decorate, __extends, __metadata } from "tslib";
1
+ import { __awaiter, __decorate, __extends, __generator, __metadata } from "tslib";
2
2
  import React from 'react';
3
- import { observable } from 'mobx';
3
+ import { observable, runInAction } from 'mobx';
4
4
  import { Misc } from '@singularsystems/neo-core';
5
5
  import { ViewParameterList } from '../Routing/ViewParameterList';
6
6
  import { ViewState } from './ViewState';
@@ -79,9 +79,20 @@ var ViewBase = /** @class */ (function (_super) {
79
79
  this.disposeViewModel();
80
80
  };
81
81
  ViewBase.prototype.updateViewParams = function (match) {
82
- if (this.viewState.viewParams.update(match)) {
83
- this.viewParamsUpdated();
84
- }
82
+ var _this = this;
83
+ runInAction(function () { return __awaiter(_this, void 0, void 0, function () {
84
+ return __generator(this, function (_a) {
85
+ switch (_a.label) {
86
+ case 0:
87
+ if (!this.viewState.viewParams.update(match)) return [3 /*break*/, 2];
88
+ return [4 /*yield*/, Promise.resolve(this.viewParamsUpdated())];
89
+ case 1:
90
+ _a.sent();
91
+ _a.label = 2;
92
+ case 2: return [2 /*return*/];
93
+ }
94
+ });
95
+ }); });
85
96
  };
86
97
  /**
87
98
  * viewParamsUpdated() is called when the component is mounted, and whenever a route parameter or query parameter gets a changed value.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singularsystems/neo-react",
3
- "version": "0.10.57",
3
+ "version": "0.10.61",
4
4
  "description": "React application logic and components for the Neo client library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "@types/rc-slider": "^8.6.5",
34
34
  "@types/react-color": "^3.0.1",
35
35
  "@types/react-select": "3.1.0",
36
- "@singularsystems/neo-core": "^0.10.62",
36
+ "@singularsystems/neo-core": "^0.10.63",
37
37
  "axios": "^0.21.1",
38
38
  "decimal.js-light": "2.5.1",
39
39
  "history": "4.10.1",
@@ -47,7 +47,7 @@
47
47
  "react-router-dom": "5.1.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@singularsystems/neo-core": ">=0.10.62",
50
+ "@singularsystems/neo-core": ">=0.10.69",
51
51
  "axios": ">=0.21.1",
52
52
  "inversify": ">=5.0.1",
53
53
  "react": ">=16.13.1",
@@ -124,7 +124,7 @@
124
124
  margin: 8px 5px;
125
125
 
126
126
  td {
127
- padding: 13px 12px;
127
+ padding: 13px 0px;
128
128
  }
129
129
  }
130
130
 
@@ -132,7 +132,7 @@
132
132
  margin: 5px 8px;
133
133
 
134
134
  td {
135
- padding: 8px 10px;
135
+ padding: 8px 0px;
136
136
  }
137
137
  }
138
138
  }
package/styles/Forms.scss CHANGED
@@ -131,6 +131,11 @@ div.neo-form-group-floating {
131
131
  }
132
132
  }
133
133
 
134
+ .react-select-bs {
135
+ .react-select-bs__menu {
136
+ z-index: 2; /* Without this, checkboxes appear in front of the drop down menu. */
137
+ }
138
+ }
134
139
  .react-select-row {
135
140
  padding: 0.15rem 1rem 0.25rem 1rem;
136
141
  display: flex;