@singularsystems/neo-react 0.10.40 → 0.10.43

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,9 +10,11 @@ import { AutoRunnerFactory } from '../React/AutoRunner';
10
10
  import { RoutingState } from '../Routing/RoutingState';
11
11
  import { ContextMenuState } from '../ReactComponents/ContextMenu/ContextMenuState';
12
12
  import { DisposeHelper } from '../Services/DisposeHelper';
13
+ import Decimal from 'decimal.js-light';
13
14
  var neoReactModule = new AppServices.Module("neo-react", function (container) {
14
15
  container.bind(AppServices.NeoTypes.Core.AutoRunnerFactory).to(AutoRunnerFactory).inSingletonScope();
15
16
  container.bind(AppServices.NeoTypes.Core.ModelCreator).to(ReactModelCreator).inTransientScope();
17
+ container.bind(AppServices.NeoTypes.Misc.DecimalType).toConstantValue(Decimal);
16
18
  container.bind(AppServices.NeoTypes.UI.ErrorHandler).to(ErrorHandler).inSingletonScope();
17
19
  container.bind(AppServices.NeoTypes.UI.GlobalContextMenuState).toConstantValue(new ContextMenuState());
18
20
  container.bind(AppServices.NeoTypes.Routing.PageLeaveHandler).to(PageLeaveHandler).inSingletonScope();
@@ -5,6 +5,7 @@ import Slider, { Range } from 'rc-slider';
5
5
  import { SketchPicker } from 'react-color';
6
6
  import AsyncSelect from 'react-select/async';
7
7
  import { AxiosStatic } from 'axios';
8
+ import Decimal from 'decimal.js-light';
8
9
  import Select from 'react-select';
9
10
  import { IDisposeHelper } from '../Services/IDisposeHelper';
10
11
  declare const NeoReactTypes: {
@@ -38,6 +39,9 @@ declare const NeoReactTypes: {
38
39
  Localisation: {
39
40
  LocalisationService: AppServices.ServiceIdentifier<import("@singularsystems/neo-core/dist/Localisation").ILocalisationService>;
40
41
  };
42
+ Misc: {
43
+ DecimalType: AppServices.ServiceIdentifier<typeof Decimal>;
44
+ };
41
45
  Security: {
42
46
  AuthenticationService: AppServices.ServiceIdentifier<import("@singularsystems/neo-core/dist/Security").IAuthenticationService>;
43
47
  AuthorisationService: AppServices.ServiceIdentifier<import("@singularsystems/neo-core/dist/Security").IAuthorisationService>;
@@ -1,5 +1,5 @@
1
1
  import { Misc, Model } from '@singularsystems/neo-core';
2
2
  import { IAutoRunnerFactory } from '@singularsystems/neo-core/dist/Model/IAutoRunner';
3
3
  export declare class AutoRunnerFactory implements IAutoRunnerFactory {
4
- create<T extends Model.IModelBase>(model: T, callback: (model: T) => void, options?: Misc.IAutoRunnerOptions): Misc.IAutoRunner;
4
+ create<T extends Model.IModelBase>(model: T, callback: (model: T) => void, options?: Misc.IAutoRunnerOptions<T>): Misc.IAutoRunner;
5
5
  }
@@ -55,9 +55,10 @@ var AutoRunner = /** @class */ (function () {
55
55
  if (instance && !trackedInstances.has(instance)) {
56
56
  trackedInstances.set(instance, true);
57
57
  var info = instance.constructor.neo;
58
+ var filterProperties = path === "" ? this.options.rootProperties : undefined;
58
59
  for (var _i = 0, _b = info.properties; _i < _b.length; _i++) {
59
60
  var propertyInfo = _b[_i];
60
- if (!propertyInfo.noTrack && instance.meta[propertyInfo.propertyName]) {
61
+ if (!propertyInfo.noTrack && instance.meta[propertyInfo.propertyName] && (!filterProperties || filterProperties.indexOf(propertyInfo.propertyName) >= 0)) {
61
62
  if (propertyInfo.relationType === Misc.RelationType.None) {
62
63
  var key = path + propertyInfo.propertyName;
63
64
  var useDelay = this.propertyMap[key];
@@ -49,7 +49,7 @@ var Button = /** @class */ (function (_super) {
49
49
  Button.prototype.render = function () {
50
50
  var _this = this;
51
51
  var _a;
52
- var _b = this.props, isSubmit = _b.isSubmit, variant = _b.variant, size = _b.size, isOutline = _b.isOutline, icon = _b.icon, iconAlignment = _b.iconAlignment, text = _b.text, menuItems = _b.menuItems, splitMenu = _b.splitMenu, menuAlignment = _b.menuAlignment, buttonGroupClassName = _b.buttonGroupClassName, tooltip = _b.tooltip, task = _b.task, pulse = _b.pulse, native = __rest(_b, ["isSubmit", "variant", "size", "isOutline", "icon", "iconAlignment", "text", "menuItems", "splitMenu", "menuAlignment", "buttonGroupClassName", "tooltip", "task", "pulse"]);
52
+ var _b = this.props, isSubmit = _b.isSubmit, variant = _b.variant, size = _b.size, isOutline = _b.isOutline, icon = _b.icon, iconAlignment = _b.iconAlignment, text = _b.text, menuItems = _b.menuItems, splitMenu = _b.splitMenu, menuAlignment = _b.menuAlignment, buttonGroupClassName = _b.buttonGroupClassName, tooltip = _b.tooltip, task = _b.task, isBusy = _b.isBusy, pulse = _b.pulse, native = __rest(_b, ["isSubmit", "variant", "size", "isOutline", "icon", "iconAlignment", "text", "menuItems", "splitMenu", "menuAlignment", "buttonGroupClassName", "tooltip", "task", "isBusy", "pulse"]);
53
53
  native.type = isSubmit ? "submit" : "button";
54
54
  variant = (variant !== null && variant !== void 0 ? variant : "primary");
55
55
  var isDropDown = menuItems && menuItems.length;
@@ -60,7 +60,7 @@ var Button = /** @class */ (function (_super) {
60
60
  buttonClasses += " dropdown-toggle";
61
61
  native.onClick = this.showMenu;
62
62
  }
63
- if ((_a = task) === null || _a === void 0 ? void 0 : _a.isBusy) {
63
+ if (isBusy || ((_a = task) === null || _a === void 0 ? void 0 : _a.isBusy)) {
64
64
  icon = "fa-circle-notch fa-spin";
65
65
  native.disabled = true;
66
66
  }
@@ -20,6 +20,12 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
20
20
  items?: ItemSource<T>;
21
21
  valueMember?: keyof T;
22
22
  displayMember?: keyof T;
23
+ /**
24
+ * Property of an item to use as the description.
25
+ *
26
+ * Description will be shown in muted text below the item name.
27
+ */
28
+ descriptionMember?: keyof T | false;
23
29
  /**
24
30
  * Callback used to provide a custom option component instead of displaying the display member value.
25
31
  */
@@ -72,5 +78,6 @@ export default class AutoCompleteDropDown<T> extends DropDownBase<T, IAutoComple
72
78
  private mapTArrayToOptions;
73
79
  private onItemSelected;
74
80
  render(): JSX.Element;
81
+ private getOption;
75
82
  }
76
83
  export {};
@@ -1,6 +1,6 @@
1
1
  import { __assign, __decorate, __extends, __metadata, __rest } from "tslib";
2
2
  import React from 'react';
3
- import { Misc, Utils } from '@singularsystems/neo-core';
3
+ import { EnumHelper, Misc, Utils } from '@singularsystems/neo-core';
4
4
  import { observer } from 'mobx-react';
5
5
  import { DropDownBase } from './DropDownBase';
6
6
  import { BindPropsHelper } from '../PropTypes';
@@ -98,7 +98,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
98
98
  AutoCompleteDropDown.prototype.render = function () {
99
99
  var _this = this;
100
100
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
101
- 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, 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", "option", "noRecordsPrompt", "typeToSearchPrompt", "delayMs", "minCharacters", "onItemSelected"]);
101
+ 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"]);
102
102
  var splitProps = BindPropsHelper.splitCommonEditorProps(otherProps);
103
103
  var nativeProps = splitProps.rest;
104
104
  var bindItemsProperty = bindItems;
@@ -112,7 +112,13 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
112
112
  this.valueMember = (_c = this.props.valueMember, (_c !== null && _c !== void 0 ? _c : ""));
113
113
  var defaultItems = undefined;
114
114
  if (this.props.items) {
115
- defaultItems = this.mapTArrayToOptions(this.getItems(this.props.items));
115
+ var items_1 = this.getItems(this.props.items);
116
+ defaultItems = this.mapTArrayToOptions(items_1);
117
+ if (descriptionMember === undefined && items_1.length > 0) {
118
+ if (EnumHelper.isEnumItem(items_1[0])) {
119
+ descriptionMember = "description";
120
+ }
121
+ }
116
122
  }
117
123
  var value = null;
118
124
  if (bindItems) {
@@ -135,11 +141,15 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
135
141
  }
136
142
  var components = (_j = nativeProps.components, (_j !== null && _j !== void 0 ? _j : {}));
137
143
  if (this.props.option) {
138
- components.Option = function (item) { return (React.createElement("div", __assign({ ref: item.innerRef }, item.innerProps, { className: "react-select-row" + (item.isFocused ? " focused" : "") }), _this.props.option(item.data))); };
144
+ components.Option = this.getOption(this.props.option);
139
145
  }
140
146
  else if (components.Option) {
141
- var originalOption_1 = components.Option;
142
- components.Option = function (item) { return (React.createElement("div", __assign({ ref: item.innerRef }, item.innerProps, { className: "react-select-row" + (item.isFocused ? " focused" : "") }), originalOption_1(item))); };
147
+ components.Option = this.getOption(components.Option);
148
+ }
149
+ else if (descriptionMember) {
150
+ components.Option = this.getOption(function (item) { return React.createElement(React.Fragment, null,
151
+ React.createElement("div", { className: "react-select-name" }, item[_this.displayMember]),
152
+ React.createElement("div", { className: "react-select-description" }, item[descriptionMember])); }, "with-description");
143
153
  }
144
154
  if (nativeProps.isClearable === undefined) {
145
155
  nativeProps.isClearable = true;
@@ -165,6 +175,9 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
165
175
  return Tooltip.fromDataAttributes(InputHelpers.surroundWithInputGroup(React.createElement(this.selectImplementation, __assign({}, nativeProps, { placeholder: (_l = this.props.placeholder, (_l !== null && _l !== void 0 ? _l : "")), classNamePrefix: "react-select-bs", options: defaultItems, isLoading: this.isLoading, isMulti: bindItems !== undefined, noOptionsMessage: function (c) { var _a; return _a = _this.props.noRecordsPrompt, (_a !== null && _a !== void 0 ? _a : "No records found"); }, value: value, onChange: this.onItemSelected, components: components })), splitProps.editor), nativeProps);
166
176
  }
167
177
  };
178
+ AutoCompleteDropDown.prototype.getOption = function (content, additionalClass) {
179
+ return function (item) { return (React.createElement("div", __assign({ ref: item.innerRef }, item.innerProps, { className: Utils.joinWithSpaces("react-select-row", additionalClass, item.isFocused ? "focused" : undefined, item.isSelected ? "selected" : undefined) }), content(item.data))); };
180
+ };
168
181
  AutoCompleteDropDown.contextType = FormContext;
169
182
  AutoCompleteDropDown = __decorate([
170
183
  observer,
@@ -80,7 +80,7 @@ var DropDown = /** @class */ (function (_super) {
80
80
  };
81
81
  DropDown.prototype.render = function () {
82
82
  var _this = this;
83
- var _a, _b, _c, _d, _e;
83
+ var _a, _b, _c, _d, _e, _f;
84
84
  var splitProps = BindPropsHelper.normaliseEditorProps(this, this.props);
85
85
  var domProps = BindPropsHelper.autoSetDomProperties(splitProps, this.context);
86
86
  domProps.className = Utils.joinWithSpaces(domProps.className, splitProps.isBootstrap ? "form-control" : "neo-forms-editor");
@@ -96,7 +96,7 @@ var DropDown = /** @class */ (function (_super) {
96
96
  var selectedItem = this.getSelectedOption(this.props.bind.value); // This sets the sort value to allow sorting of drop downs in grids.
97
97
  var value = this.props.bind.value;
98
98
  var addBlankItem = (_b = (_a = splitProps.select) === null || _a === void 0 ? void 0 : _a.allowNulls, (_b !== null && _b !== void 0 ? _b : this.props.bind.propertyInfo.allowNulls)) || !value;
99
- if (addBlankItem && items.find(function (item) { return item[_this.valueMember] === null; })) {
99
+ if (addBlankItem && items.find(function (item) { return item[_this.valueMember] === null || item[_this.valueMember] === ""; })) {
100
100
  // If there is an item with a null value, don't add a blank drop down item.
101
101
  addBlankItem = false;
102
102
  }
@@ -116,9 +116,10 @@ var DropDown = /** @class */ (function (_super) {
116
116
  domProps.disabled = true;
117
117
  }
118
118
  var valueIsBlank = value === undefined || value === null;
119
+ 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;
119
120
  control =
120
121
  React.createElement("select", __assign({ value: valueIsBlank ? "" : value, onChange: this.onItemSelected, ref: splitProps.editor.inputElement }, domProps),
121
- addBlankItem && React.createElement("option", null, valueIsBlank ? (_d = splitProps.select) === null || _d === void 0 ? void 0 : _d.nullText : (_e = splitProps.select) === null || _e === void 0 ? void 0 : _e.deSelectText),
122
+ addBlankItem && React.createElement("option", null, nullText),
122
123
  items.map(function (item) { return (React.createElement("option", { value: item[_this.valueMember], key: item[_this.valueMember] }, item[_this.displayMember])); }));
123
124
  }
124
125
  return (InputHelpers.surroundWithInputGroup(control, splitProps.editor));
@@ -1,5 +1,5 @@
1
1
  import { __awaiter, __extends, __generator } from "tslib";
2
- import { Data, Model } from '@singularsystems/neo-core';
2
+ import { Data, EnumHelper, Model } from '@singularsystems/neo-core';
3
3
  import ComponentBase from '../ComponentBase';
4
4
  var DropDownBase = /** @class */ (function (_super) {
5
5
  __extends(DropDownBase, _super);
@@ -13,6 +13,10 @@ var DropDownBase = /** @class */ (function (_super) {
13
13
  }
14
14
  DropDownBase.prototype.findMembers = function (item) {
15
15
  if (!this.valueMember || !this.displayMember) {
16
+ if (EnumHelper.isEnumItem(item)) {
17
+ this.valueMember = "id";
18
+ this.displayMember = "name";
19
+ }
16
20
  if (!this.displayMember) {
17
21
  var neoTypeInfo = item.constructor.neo;
18
22
  if (neoTypeInfo && neoTypeInfo.displayMember) {
@@ -1,4 +1,4 @@
1
- import { __assign, __decorate, __extends, __metadata, __rest } from "tslib";
1
+ import { __assign, __awaiter, __decorate, __extends, __generator, __metadata, __rest } from "tslib";
2
2
  import React from 'react';
3
3
  import { observer } from 'mobx-react';
4
4
  import { FileContext } from './FileContext';
@@ -14,9 +14,30 @@ var FileBasicInput = /** @class */ (function (_super) {
14
14
  return _this;
15
15
  }
16
16
  FileBasicInput.prototype.onFileSelected = function (e) {
17
- if (e.target.files) {
18
- this.fileManager.processFiles(e.target.files, { handleErrors: !this.fileManager.options.taskRunner });
19
- }
17
+ var _a, _b;
18
+ return __awaiter(this, void 0, void 0, function () {
19
+ var input;
20
+ return __generator(this, function (_c) {
21
+ switch (_c.label) {
22
+ case 0:
23
+ if (!e.target.files) return [3 /*break*/, 4];
24
+ _c.label = 1;
25
+ case 1:
26
+ _c.trys.push([1, , 3, 4]);
27
+ return [4 /*yield*/, this.fileManager.processFiles(e.target.files, { handleErrors: !this.fileManager.options.taskRunner })];
28
+ case 2:
29
+ _c.sent();
30
+ return [3 /*break*/, 4];
31
+ case 3:
32
+ input = (_a = e.target, (_a !== null && _a !== void 0 ? _a : (_b = this.props.inputRef) === null || _b === void 0 ? void 0 : _b.current));
33
+ if (input) {
34
+ input.value = "";
35
+ }
36
+ return [7 /*endfinally*/];
37
+ case 4: return [2 /*return*/];
38
+ }
39
+ });
40
+ });
20
41
  };
21
42
  FileBasicInput.prototype.render = function () {
22
43
  var _a = this.props, fileManager = _a.fileManager, isHidden = _a.isHidden, inputRef = _a.inputRef, native = __rest(_a, ["fileManager", "isHidden", "inputRef"]);
@@ -3,6 +3,16 @@ import { GridContext } from './Grid';
3
3
  import { IButtonOptions } from '@singularsystems/neo-core/dist/Components/ButtonOptions';
4
4
  import ComponentBase from '../ComponentBase';
5
5
  import { INeoCommonColumnProps } from './ColumnHelper';
6
+ interface IDeleteButtonOptions extends IButtonOptions {
7
+ /**
8
+ * Callback to run when a non new item is deleted.
9
+ * * Use this to call an api to delete the item in the database.
10
+ * * Return true if the item was deleted, and must be removed from the list.
11
+ * * The list will not be marked dirty.
12
+ * * If the item is new, it will be removed without invoking this callback.
13
+ */
14
+ onDeleteExisting?: (item: any) => Promise<boolean>;
15
+ }
6
16
  interface IButtonColumnProps extends INeoCommonColumnProps, React.HTMLProps<HTMLTableCellElement> {
7
17
  /** Item to pass to edit and delete callbacks. Defaults to the current rows item */
8
18
  item?: any;
@@ -15,7 +25,7 @@ interface IButtonColumnProps extends INeoCommonColumnProps, React.HTMLProps<HTML
15
25
  /** Show delete button? Not required if deleteButton options are set. */
16
26
  showDelete?: boolean;
17
27
  /** Delete button options. Will cause delete button to be shown. */
18
- deleteButton?: IButtonOptions;
28
+ deleteButton?: IDeleteButtonOptions;
19
29
  /** Edit button options. Will cause edit button to be shown. */
20
30
  editButton?: IButtonOptions;
21
31
  /** View button options. Will cause view button to be shown. */
@@ -31,7 +41,7 @@ export declare class ButtonColumn extends ComponentBase<IButtonColumnProps> {
31
41
  private item;
32
42
  private section;
33
43
  protected onFirstRender(): void;
34
- onDelete(): void;
44
+ onDelete(): Promise<void>;
35
45
  render(): JSX.Element;
36
46
  }
37
47
  export {};
@@ -1,4 +1,4 @@
1
- import { __assign, __extends, __rest } from "tslib";
1
+ import { __assign, __awaiter, __extends, __generator, __rest } from "tslib";
2
2
  import * as React from 'react';
3
3
  import { GridContext } from './Grid';
4
4
  import NeoButton from '../Button';
@@ -17,7 +17,28 @@ var ButtonColumn = /** @class */ (function (_super) {
17
17
  this.item = this.context.currentItem;
18
18
  };
19
19
  ButtonColumn.prototype.onDelete = function () {
20
- this.context.dataView.rawItems.remove(this.props.item || this.item);
20
+ var _a, _b;
21
+ return __awaiter(this, void 0, void 0, function () {
22
+ var item, mustRemove;
23
+ return __generator(this, function (_c) {
24
+ switch (_c.label) {
25
+ case 0:
26
+ item = (_a = this.props.item, (_a !== null && _a !== void 0 ? _a : this.item));
27
+ if (!(item.isNew === false && ((_b = this.props.deleteButton) === null || _b === void 0 ? void 0 : _b.onDeleteExisting) !== undefined)) return [3 /*break*/, 2];
28
+ return [4 /*yield*/, this.props.deleteButton.onDeleteExisting(item)];
29
+ case 1:
30
+ mustRemove = _c.sent();
31
+ if (mustRemove) {
32
+ this.context.dataView.rawItems.removeWithoutTracking(item);
33
+ }
34
+ return [3 /*break*/, 3];
35
+ case 2:
36
+ this.context.dataView.rawItems.remove(item);
37
+ _c.label = 3;
38
+ case 3: return [2 /*return*/];
39
+ }
40
+ });
41
+ });
21
42
  };
22
43
  ButtonColumn.prototype.render = function () {
23
44
  this.onRender();
@@ -67,6 +67,12 @@ export interface IGridProps<T> extends React.HTMLProps<HTMLTableElement> {
67
67
  isSmallTable?: boolean;
68
68
  /** Additional footer row(s) to display below the main footer row. */
69
69
  footerContent?: (data: T[]) => React.ReactNode;
70
+ /**
71
+ * Additional content to show next to the add new button.
72
+ *
73
+ * Set `showAddButton = false` if you don't want the default button.
74
+ */
75
+ footerButtons?: () => React.ReactNode;
70
76
  }
71
77
  export default class Grid<T> extends React.Component<IGridProps<T>> {
72
78
  static contextType: React.Context<import("../../React/DataView").DataViewContextParams<unknown>>;
@@ -32,7 +32,7 @@ var Grid = /** @class */ (function (_super) {
32
32
  }
33
33
  Grid.prototype.render = function () {
34
34
  var _this = this;
35
- var _a = this.props, items = _a.items, showAddButton = _a.showAddButton, addButton = _a.addButton, filterPredicate = _a.filterPredicate, keyProperty = _a.keyProperty, allowSort = _a.allowSort, expandAll = _a.expandAll, collapseAll = _a.collapseAll, initialSort = _a.initialSort, initialSortAscending = _a.initialSortAscending, isBordered = _a.isBordered, isStriped = _a.isStriped, isBorderless = _a.isBorderless, showHover = _a.showHover, isSmallTable = _a.isSmallTable, footerContent = _a.footerContent, domProps = __rest(_a, ["items", "showAddButton", "addButton", "filterPredicate", "keyProperty", "allowSort", "expandAll", "collapseAll", "initialSort", "initialSortAscending", "isBordered", "isStriped", "isBorderless", "showHover", "isSmallTable", "footerContent"]);
35
+ var _a = this.props, items = _a.items, showAddButton = _a.showAddButton, addButton = _a.addButton, filterPredicate = _a.filterPredicate, keyProperty = _a.keyProperty, allowSort = _a.allowSort, expandAll = _a.expandAll, collapseAll = _a.collapseAll, initialSort = _a.initialSort, initialSortAscending = _a.initialSortAscending, isBordered = _a.isBordered, isStriped = _a.isStriped, isBorderless = _a.isBorderless, showHover = _a.showHover, isSmallTable = _a.isSmallTable, footerContent = _a.footerContent, footerButtons = _a.footerButtons, domProps = __rest(_a, ["items", "showAddButton", "addButton", "filterPredicate", "keyProperty", "allowSort", "expandAll", "collapseAll", "initialSort", "initialSortAscending", "isBordered", "isStriped", "isBorderless", "showHover", "isSmallTable", "footerContent", "footerButtons"]);
36
36
  if (expandAll === true) {
37
37
  this.gridContext.expandAll = true;
38
38
  }
@@ -90,11 +90,12 @@ var Grid = /** @class */ (function (_super) {
90
90
  React.createElement(GridContext.Provider, { value: this.gridContext },
91
91
  React.createElement(TableSection, { type: "header" }, this.headerItem && this.props.children(this.headerItem, this.headerItem.meta)),
92
92
  React.createElement(TableSection, { type: "body" }, this.gridContext.dataView.getItems().map(function (item) { return (React.createElement(ItemGroup, { item: item, key: keyProperty ? item[keyProperty] : item.entityIdentifier, template: _this.props.children })); })),
93
- React.createElement(Observer, null, function () { return (showAddButton || addButton || _this.gridContext.hasSummaries || footerContent !== undefined) &&
93
+ React.createElement(Observer, null, function () { return (showAddButton || addButton || footerButtons || _this.gridContext.hasSummaries || footerContent !== undefined) &&
94
94
  React.createElement(TableSection, { type: "footer" },
95
95
  _this.gridContext.hasSummaries && _this.headerItem && _this.props.children(_this.headerItem, _this.headerItem.meta),
96
96
  footerContent !== undefined && footerContent(_this.gridContext.dataView.getItems()),
97
- (showAddButton || addButton) && React.createElement(AddButtonRow, { addButton: addButton })); }))));
97
+ (showAddButton || addButton || footerButtons) &&
98
+ React.createElement(AddButtonRow, { addButton: addButton, showAddButton: showAddButton, additionalContent: footerButtons })); }))));
98
99
  };
99
100
  Grid.prototype.setInitialSort = function (initialSort, initialSortAscending) {
100
101
  var _a;
@@ -171,12 +172,15 @@ var AddButtonRow = /** @class */ (function (_super) {
171
172
  }
172
173
  };
173
174
  AddButtonRow.prototype.render = function () {
174
- var addButtonOptions = Utils.populatePartialWithDefaults(this.props.addButton, __assign({}, Misc.Settings.grid.addButton));
175
+ var _a = this.props, addButton = _a.addButton, showAddButton = _a.showAddButton, additionalContent = _a.additionalContent;
176
+ var addButtonOptions = Utils.populatePartialWithDefaults(addButton, __assign({}, Misc.Settings.grid.addButton));
175
177
  if (!addButtonOptions.onClick)
176
178
  addButtonOptions.onClick = this.onAddNew;
177
179
  return (React.createElement("tr", { ref: this.domRef },
178
180
  React.createElement("td", { colSpan: this.context.columnCount + (this.context.expandProperty ? 1 : 0) },
179
- React.createElement(NeoButton, __assign({}, addButtonOptions)))));
181
+ React.createElement("div", { className: "neo-grid-button-row" },
182
+ showAddButton !== false && React.createElement(NeoButton, __assign({}, addButtonOptions)),
183
+ additionalContent !== undefined && additionalContent()))));
180
184
  };
181
185
  AddButtonRow.contextType = GridContext;
182
186
  return AddButtonRow;
@@ -41,13 +41,16 @@ var Modal = /** @class */ (function (_super) {
41
41
  }
42
42
  }
43
43
  show = !!propertyInstance.value;
44
- onClose = function () {
44
+ onClose = function (event) {
45
45
  if (propertyInstance.value === true) {
46
46
  propertyInstance.value = false;
47
47
  }
48
48
  else {
49
49
  propertyInstance.value = null;
50
50
  }
51
+ if (_this.props.onClose) {
52
+ _this.props.onClose(event);
53
+ }
51
54
  };
52
55
  }
53
56
  else {
@@ -45,7 +45,7 @@ var FormatState = /** @class */ (function () {
45
45
  return this.zeroText;
46
46
  }
47
47
  if (value !== undefined && value !== null) {
48
- if (value instanceof Decimal) {
48
+ if (value instanceof Misc.Globals.decimalType) {
49
49
  value = value.toNumber();
50
50
  }
51
51
  if (typeof value === "number") {
@@ -65,6 +65,8 @@ var FormatState = /** @class */ (function () {
65
65
  return FormatState;
66
66
  }());
67
67
  export { FormatState };
68
+ // Taken from https://github.com/MikeMcl/decimal.js-light/blob/master/decimal.js since it is not exported.
69
+ var validDecimalRegex = /^(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;
68
70
  var NumberInput = /** @class */ (function (_super) {
69
71
  __extends(NumberInput, _super);
70
72
  function NumberInput(props) {
@@ -82,8 +84,10 @@ var NumberInput = /** @class */ (function (_super) {
82
84
  }
83
85
  NumberInput.prototype.onChanged = function (e) {
84
86
  var newValue;
85
- if (e.target.value !== "" && isNaN(parseFloat(e.target.value))) {
86
- return;
87
+ if (e.target.value !== "") {
88
+ if (isNaN(parseFloat(e.target.value)) || !validDecimalRegex.test(e.target.value)) {
89
+ return;
90
+ }
87
91
  }
88
92
  if (e.target.value == "") {
89
93
  newValue = this.props.bind.propertyInfo.allowNulls ? null : new Decimal(0);
@@ -122,7 +126,7 @@ var NumberInput = /** @class */ (function (_super) {
122
126
  };
123
127
  NumberInput.prototype.getBindValueAsNumber = function () {
124
128
  var propertyValue = this.props.bind.value;
125
- if (propertyValue instanceof Decimal) {
129
+ if (propertyValue instanceof Misc.Globals.decimalType) {
126
130
  return propertyValue.toNumber();
127
131
  }
128
132
  else {
@@ -36,6 +36,7 @@ import Tab from './Tabs/Tab';
36
36
  import Toast from './Notifications/Toast';
37
37
  import ToastContainer from './Notifications/ToastContainer';
38
38
  import Tooltip from './Tooltip';
39
+ import { TooltipHelper } from './TooltipHelper';
39
40
  import TooltipProvider from './TooltipProvider';
40
41
  import ValidationSummary from './ValidationSummary';
41
- export { Alert, AutoCompleteDropDown, Button, Card, Checkbox, Collapsable, ColorPicker, ContextMenuContainer, DatePicker, DropDown, FileBasicInput, FileContext, FileDropArea, FileInput, FileUploadButton, FileUploadProgress, Form, FormContext, FormGroup, FormGroupFloating, FormGroupInline, GridLayout, Input, Link, Loader, Modal, ModalContainer, NumberInput, Pager, PagerControlsBasic, PageSizeControl, Passwordbox, ProgressBar, RadioList, Slider, TabContainer, Tab, Textbox, Toast, ToastContainer, Tooltip, TooltipProvider, ValidationSummary };
42
+ export { Alert, AutoCompleteDropDown, Button, Card, Checkbox, Collapsable, ColorPicker, ContextMenuContainer, DatePicker, DropDown, FileBasicInput, FileContext, FileDropArea, FileInput, FileUploadButton, FileUploadProgress, Form, FormContext, FormGroup, FormGroupFloating, FormGroupInline, GridLayout, Input, Link, Loader, Modal, ModalContainer, NumberInput, Pager, PagerControlsBasic, PageSizeControl, Passwordbox, ProgressBar, RadioList, Slider, TabContainer, Tab, Textbox, Toast, ToastContainer, Tooltip, TooltipHelper, TooltipProvider, ValidationSummary };
@@ -36,6 +36,7 @@ import Tab from './Tabs/Tab';
36
36
  import Toast from './Notifications/Toast';
37
37
  import ToastContainer from './Notifications/ToastContainer';
38
38
  import Tooltip from './Tooltip';
39
+ import { TooltipHelper } from './TooltipHelper';
39
40
  import TooltipProvider from './TooltipProvider';
40
41
  import ValidationSummary from './ValidationSummary';
41
- export { Alert, AutoCompleteDropDown, Button, Card, Checkbox, Collapsable, ColorPicker, ContextMenuContainer, DatePicker, DropDown, FileBasicInput, FileContext, FileDropArea, FileInput, FileUploadButton, FileUploadProgress, Form, FormContext, FormGroup, FormGroupFloating, FormGroupInline, GridLayout, Input, Link, Loader, Modal, ModalContainer, NumberInput, Pager, PagerControlsBasic, PageSizeControl, Passwordbox, ProgressBar, RadioList, Slider, TabContainer, Tab, Textbox, Toast, ToastContainer, Tooltip, TooltipProvider, ValidationSummary };
42
+ export { Alert, AutoCompleteDropDown, Button, Card, Checkbox, Collapsable, ColorPicker, ContextMenuContainer, DatePicker, DropDown, FileBasicInput, FileContext, FileDropArea, FileInput, FileUploadButton, FileUploadProgress, Form, FormContext, FormGroup, FormGroupFloating, FormGroupInline, GridLayout, Input, Link, Loader, Modal, ModalContainer, NumberInput, Pager, PagerControlsBasic, PageSizeControl, Passwordbox, ProgressBar, RadioList, Slider, TabContainer, Tab, Textbox, Toast, ToastContainer, Tooltip, TooltipHelper, TooltipProvider, ValidationSummary };
@@ -19,6 +19,7 @@ export default abstract class ViewModelBase extends ModelBase implements IViewMo
19
19
  registerReaction<T>(expression: () => T, effect: (value: T) => void): IReactionDisposer;
20
20
  /**
21
21
  * Instantiates, and registers a child view model that will be disposed when this view model is disposed.
22
+ * The task runner will be passed to the new vm from this one.
22
23
  * @param viewModel View model type.
23
24
  */
24
25
  registerViewModel<TViewModel extends IViewModel>(viewModel: new () => TViewModel, options?: {
@@ -37,7 +38,14 @@ export default abstract class ViewModelBase extends ModelBase implements IViewMo
37
38
  * Uses the task runner to call an api endpoint. The returned data is deserialised into an instance of the provided model type.
38
39
  */
39
40
  waitForModel<T extends BindableBase>(modelType: typeof BindableBase, task: AxiosPromise<Model.PlainTrackedObject<T>>, options?: Partial<WaitForTaskRunnerOptions<Model.PlainTrackedObject<T>>>): Promise<T>;
41
+ /**
42
+ * Registers the object for disposal when this viewmodel is disposed.
43
+ */
40
44
  autoDispose<T extends Misc.IDisposable>(disposable: T): T;
45
+ /**
46
+ * Binds a list of functions to the viewmodel instance.
47
+ */
48
+ protected bindMethods(...fns: Function[]): void;
41
49
  dispose(): void;
42
50
  /**
43
51
  * Creates a view model instance.
@@ -59,9 +59,30 @@ var ViewModelBase = /** @class */ (function (_super) {
59
59
  });
60
60
  });
61
61
  };
62
+ /**
63
+ * Registers the object for disposal when this viewmodel is disposed.
64
+ */
62
65
  ViewModelBase.prototype.autoDispose = function (disposable) {
63
66
  return this.disposeHelper.autoDispose(disposable);
64
67
  };
68
+ /**
69
+ * Binds a list of functions to the viewmodel instance.
70
+ */
71
+ ViewModelBase.prototype.bindMethods = function () {
72
+ var fns = [];
73
+ for (var _i = 0; _i < arguments.length; _i++) {
74
+ fns[_i] = arguments[_i];
75
+ }
76
+ for (var _a = 0, fns_1 = fns; _a < fns_1.length; _a++) {
77
+ var fn = fns_1[_a];
78
+ if (fn.name) {
79
+ this[fn.name] = fn.bind(this);
80
+ }
81
+ else {
82
+ throw "Cannot bind a function without a name.";
83
+ }
84
+ }
85
+ };
65
86
  ViewModelBase.prototype.dispose = function () {
66
87
  this.disposeHelper.dispose();
67
88
  for (var _i = 0, _a = this.trackedViewModels; _i < _a.length; _i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singularsystems/neo-react",
3
- "version": "0.10.40",
3
+ "version": "0.10.43",
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.47",
36
+ "@singularsystems/neo-core": "^0.10.50",
37
37
  "axios": "^0.21.1",
38
38
  "decimal.js-light": "2.5.1",
39
39
  "history": "4.10.1",
package/styles/Forms.scss CHANGED
@@ -139,8 +139,26 @@ div.neo-form-group-floating {
139
139
  justify-content: space-between;
140
140
  border-bottom: 1px solid #eee;
141
141
  cursor: pointer;
142
-
142
+
143
+ &.with-description {
144
+ div.react-select-name {
145
+ width: 100%;
146
+ }
147
+ div.react-select-description {
148
+ font-size: 0.9rem;
149
+ color: #999;
150
+ }
151
+ }
152
+
143
153
  &.focused, &:hover {
144
154
  background: $input-btn-focus-color;
145
155
  }
156
+ &.selected {
157
+ background: #1E90FF;
158
+ color: #fff;
159
+
160
+ div.react-select-description {
161
+ color: #C7E4FF;
162
+ }
163
+ }
146
164
  }