@singularsystems/neo-react 1.3.8 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/ReactComponents/DropDown/AutoCompleteDropDown.d.ts +32 -11
- package/dist/ReactComponents/DropDown/AutoCompleteDropDown.js +97 -16
- package/dist/ReactComponents/DropDown/DropDown.d.ts +19 -9
- package/dist/ReactComponents/DropDown/DropDown.js +66 -10
- package/dist/ReactComponents/DropDown/DropDownBase.d.ts +4 -3
- package/dist/ReactComponents/DropDown/DropDownBase.js +10 -0
- package/dist/ReactComponents/Form.d.ts +2 -2
- package/dist/ReactComponents/Form.js +4 -4
- package/dist/ReactComponents/PropTypes.d.ts +1 -1
- package/dist/ReactComponents/RadioList.js +6 -1
- package/dist/ReactComponents/ValidationSummary.js +10 -4
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This file details all new features and changes to functionality. If you only need to see changes that require updates to your project, see the [breaking changes](./Breaking.md) readme.
|
|
4
4
|
|
|
5
|
+
## Version 1.3.9 (4 March 2026)
|
|
6
|
+
|
|
7
|
+
- Dropdown
|
|
8
|
+
- Added support for grouped drop downs by specifying `groupDisplayMember` and `childListMember` props.
|
|
9
|
+
- ValidationSummary
|
|
10
|
+
- Fixed component not updating when invalid rules change.
|
|
11
|
+
|
|
5
12
|
## Version 1.3.8 (24 Nov 2025)
|
|
6
13
|
|
|
7
14
|
- Deserialiser
|
|
@@ -9,8 +9,10 @@ interface IOption {
|
|
|
9
9
|
value: string | number;
|
|
10
10
|
label: string;
|
|
11
11
|
item?: any;
|
|
12
|
+
itemParent?: any;
|
|
13
|
+
options?: IOption[];
|
|
12
14
|
}
|
|
13
|
-
interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption, true> {
|
|
15
|
+
interface IAutoCompleteCommonProps<T, TChild = T> extends ICommonEditorProps, Props<IOption, true> {
|
|
14
16
|
/**
|
|
15
17
|
* Callback for loading data.
|
|
16
18
|
*/
|
|
@@ -19,28 +21,28 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
|
|
|
19
21
|
* Items to display for non async drop down, or default items to display in async drop down before typing.
|
|
20
22
|
*/
|
|
21
23
|
items?: ItemSource<T>;
|
|
22
|
-
valueMember?: keyof
|
|
23
|
-
displayMember?: keyof
|
|
24
|
+
valueMember?: keyof TChild;
|
|
25
|
+
displayMember?: keyof TChild;
|
|
24
26
|
/**
|
|
25
27
|
* Function used to filter items out of the datasource.
|
|
26
28
|
* **Note:** this will only apply to the `items` prop. */
|
|
27
|
-
filterPredicate?: (item: T) => boolean;
|
|
29
|
+
filterPredicate?: (item: TChild, itemParent?: T) => boolean;
|
|
28
30
|
/**
|
|
29
31
|
* Function used to filter items based on what the user has typed.
|
|
30
32
|
* Applies after `filterPredicate`, and is ignored if `itemSource` is specified.
|
|
31
33
|
* Use if you are displaying multiple columns and need to match on any of the columns.
|
|
32
34
|
*/
|
|
33
|
-
inputFilter?: (input: string, item: T) => boolean;
|
|
35
|
+
inputFilter?: (input: string, item: TChild, parent?: T) => boolean;
|
|
34
36
|
/**
|
|
35
37
|
* Property of an item to use as the description.
|
|
36
38
|
*
|
|
37
39
|
* Description will be shown in muted text below the item name.
|
|
38
40
|
*/
|
|
39
|
-
descriptionMember?: keyof
|
|
41
|
+
descriptionMember?: keyof TChild | false;
|
|
40
42
|
/**
|
|
41
43
|
* Callback used to provide a custom option component instead of displaying the display member value.
|
|
42
44
|
*/
|
|
43
|
-
option?: (item: T) => JSX.Element;
|
|
45
|
+
option?: (item: T | TChild) => JSX.Element;
|
|
44
46
|
/**
|
|
45
47
|
* Text to display when the user has typed something, but no records were returned.
|
|
46
48
|
*/
|
|
@@ -54,7 +56,7 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
|
|
|
54
56
|
* The removedItem parameter will be provided when removing an item from a multi select drop down.
|
|
55
57
|
* The addedItem parameter will be provided when an item is selected in a multi select drop down. This is the item stored in the bindItems list.
|
|
56
58
|
*/
|
|
57
|
-
onItemSelected?: (item?:
|
|
59
|
+
onItemSelected?: (item?: TChild, removedItem?: Model.ISelectedItem, addedItem?: Model.ISelectedItem) => void;
|
|
58
60
|
/**
|
|
59
61
|
* Millisecond delay from the last typed character, to when the itemSource function is called.
|
|
60
62
|
* Default is 500ms.
|
|
@@ -65,6 +67,14 @@ interface IAutoCompleteCommonProps<T> extends ICommonEditorProps, Props<IOption,
|
|
|
65
67
|
* Default is 1.
|
|
66
68
|
*/
|
|
67
69
|
minCharacters?: number;
|
|
70
|
+
/**
|
|
71
|
+
* The group member to use as the display for grouped items.
|
|
72
|
+
*/
|
|
73
|
+
groupDisplayMember?: keyof T;
|
|
74
|
+
/**
|
|
75
|
+
* Name of the property on the parent which contains a child list.
|
|
76
|
+
*/
|
|
77
|
+
childListMember?: keyof T;
|
|
68
78
|
}
|
|
69
79
|
export interface IAutoCompleteProps {
|
|
70
80
|
bind: Model.IPropertyInstance;
|
|
@@ -85,20 +95,31 @@ export interface IAutoCompleteMultiSelectProps {
|
|
|
85
95
|
*/
|
|
86
96
|
fieldInfoLimit?: number;
|
|
87
97
|
}
|
|
88
|
-
export default class AutoCompleteDropDown<T> extends DropDownBase<T, IAutoCompleteCommonProps<T> & (IAutoCompleteProps | IAutoCompleteMultiSelectProps)> {
|
|
98
|
+
export default class AutoCompleteDropDown<T, TChild = T> extends DropDownBase<T, IAutoCompleteCommonProps<T, TChild> & (IAutoCompleteProps | IAutoCompleteMultiSelectProps), TChild> {
|
|
89
99
|
static contextType: React.Context<import("../FormContext").FormContextParams>;
|
|
90
100
|
context: React.ContextType<typeof FormContext>;
|
|
91
101
|
private asyncSelectImplementation;
|
|
92
102
|
private selectImplementation;
|
|
93
|
-
constructor(props: IAutoCompleteCommonProps<T> & (IAutoCompleteProps | IAutoCompleteMultiSelectProps));
|
|
103
|
+
constructor(props: IAutoCompleteCommonProps<T, TChild> & (IAutoCompleteProps | IAutoCompleteMultiSelectProps));
|
|
94
104
|
private bindProperty;
|
|
95
105
|
private bindIdsList?;
|
|
96
106
|
private bindIdsProperty?;
|
|
97
|
-
static getBindProp<
|
|
107
|
+
static getBindProp<TChild>(props: IAutoCompleteProps | IAutoCompleteMultiSelectProps): Model.IPropertyInstance<any>;
|
|
98
108
|
private onKeyPress;
|
|
99
109
|
private setBindProperty;
|
|
100
110
|
private mapTArrayToOptions;
|
|
111
|
+
private mapChildItemsToOptions;
|
|
112
|
+
private getGroupedOptionsFromItems;
|
|
113
|
+
/**
|
|
114
|
+
* Finds an option in the items list by value.
|
|
115
|
+
* Handles both flat lists and grouped (nested) items.
|
|
116
|
+
* @param items The items array to search in
|
|
117
|
+
* @param value The value to search for
|
|
118
|
+
* @returns The matching IOption or undefined if not found
|
|
119
|
+
*/
|
|
120
|
+
private findOptionByValue;
|
|
101
121
|
private onItemSelected;
|
|
122
|
+
private get isGrouped();
|
|
102
123
|
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | JSX.Element | null | undefined;
|
|
103
124
|
private getOption;
|
|
104
125
|
}
|
|
@@ -35,7 +35,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
35
35
|
else {
|
|
36
36
|
items = list;
|
|
37
37
|
}
|
|
38
|
-
dataReceived(_this.mapTArrayToOptions(items));
|
|
38
|
+
dataReceived(_this.mapTArrayToOptions((items).slice()));
|
|
39
39
|
});
|
|
40
40
|
}, (_b = this.props.delayMs) !== null && _b !== void 0 ? _b : 500);
|
|
41
41
|
}
|
|
@@ -68,16 +68,69 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
70
|
AutoCompleteDropDown.prototype.mapTArrayToOptions = function (items) {
|
|
71
|
-
|
|
71
|
+
if (this.isGrouped) {
|
|
72
|
+
var firstChildItem = this.getFirstChildItem(items, this.props.childListMember);
|
|
73
|
+
if (firstChildItem) {
|
|
74
|
+
this.findMembers(firstChildItem);
|
|
75
|
+
}
|
|
76
|
+
return this.getGroupedOptionsFromItems(items);
|
|
77
|
+
}
|
|
72
78
|
if (items.length) {
|
|
73
79
|
this.findMembers(items[0]);
|
|
74
80
|
}
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
return this.mapChildItemsToOptions(items);
|
|
82
|
+
};
|
|
83
|
+
AutoCompleteDropDown.prototype.mapChildItemsToOptions = function (childItems, parentItem) {
|
|
84
|
+
var _this = this;
|
|
85
|
+
return childItems.map(function (childItem) { return ({
|
|
86
|
+
item: childItem,
|
|
87
|
+
itemParent: parentItem,
|
|
88
|
+
value: childItem[_this.valueMember],
|
|
89
|
+
label: childItem[_this.displayMember]
|
|
79
90
|
}); });
|
|
80
91
|
};
|
|
92
|
+
AutoCompleteDropDown.prototype.getGroupedOptionsFromItems = function (groupedItems) {
|
|
93
|
+
var _this = this;
|
|
94
|
+
var groupedOptions = groupedItems.map(function (groupItem) {
|
|
95
|
+
var childOptions = _this.mapChildItemsToOptions(groupItem[_this.props.childListMember], groupItem);
|
|
96
|
+
return {
|
|
97
|
+
item: groupItem,
|
|
98
|
+
itemParent: null,
|
|
99
|
+
label: groupItem[_this.props.groupDisplayMember],
|
|
100
|
+
options: childOptions
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
return groupedOptions;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Finds an option in the items list by value.
|
|
107
|
+
* Handles both flat lists and grouped (nested) items.
|
|
108
|
+
* @param items The items array to search in
|
|
109
|
+
* @param value The value to search for
|
|
110
|
+
* @returns The matching IOption or undefined if not found
|
|
111
|
+
*/
|
|
112
|
+
AutoCompleteDropDown.prototype.findOptionByValue = function (items, value) {
|
|
113
|
+
if (!items) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (this.isGrouped) {
|
|
117
|
+
// For grouped items, search within child options of each group
|
|
118
|
+
for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
|
|
119
|
+
var group = items_1[_i];
|
|
120
|
+
if (group.options) {
|
|
121
|
+
var childMatch = group.options.find(function (option) { return option.value === value; });
|
|
122
|
+
if (childMatch) {
|
|
123
|
+
return childMatch;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// For flat items, search directly
|
|
131
|
+
return items.find(function (item) { return item.value === value; });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
81
134
|
AutoCompleteDropDown.prototype.onItemSelected = function (args, action) {
|
|
82
135
|
var _a;
|
|
83
136
|
var onItemSelected = this.props.onItemSelected, singleProps = this.props, multiProps = this.props;
|
|
@@ -129,6 +182,13 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
129
182
|
}
|
|
130
183
|
}
|
|
131
184
|
};
|
|
185
|
+
Object.defineProperty(AutoCompleteDropDown.prototype, "isGrouped", {
|
|
186
|
+
get: function () {
|
|
187
|
+
return this.props.groupDisplayMember !== undefined && this.props.childListMember !== undefined;
|
|
188
|
+
},
|
|
189
|
+
enumerable: false,
|
|
190
|
+
configurable: true
|
|
191
|
+
});
|
|
132
192
|
AutoCompleteDropDown.prototype.render = function () {
|
|
133
193
|
var _this = this;
|
|
134
194
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
@@ -150,19 +210,40 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
150
210
|
}
|
|
151
211
|
var defaultItems = undefined;
|
|
152
212
|
if (this.props.items) {
|
|
153
|
-
var
|
|
154
|
-
|
|
213
|
+
var items_2 = [];
|
|
214
|
+
if (this.isGrouped) {
|
|
215
|
+
items_2 = this.getItems(this.props.items);
|
|
216
|
+
defaultItems = this.getGroupedOptionsFromItems(items_2);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
items_2 = this.getItems(this.props.items);
|
|
220
|
+
defaultItems = this.mapTArrayToOptions(items_2);
|
|
221
|
+
}
|
|
155
222
|
if (filterPredicate !== undefined) {
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
223
|
+
if (this.isGrouped) {
|
|
224
|
+
var groupsWithFilteredChildren = defaultItems.map(function (group) { return (__assign(__assign({}, group), { options: group.options.filter(function (option) {
|
|
225
|
+
if (bindValue) {
|
|
226
|
+
return bindValue.value === option.value || filterPredicate(option.item, option.itemParent);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
return filterPredicate(option.item, option.itemParent);
|
|
230
|
+
}
|
|
231
|
+
}) })); });
|
|
232
|
+
// Remove groups that have no children after filtering
|
|
233
|
+
defaultItems = groupsWithFilteredChildren.filter(function (group) { return group.options.length > 0; });
|
|
159
234
|
}
|
|
160
235
|
else {
|
|
161
|
-
|
|
236
|
+
if (bindValue) {
|
|
237
|
+
// Dont filter out the current selected item even if excluded by the filter.
|
|
238
|
+
defaultItems = defaultItems.filter(function (option) { return bindValue.value === option.value || filterPredicate(option.item, option.itemParent); });
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
defaultItems = defaultItems.filter(function (option) { return filterPredicate(option.item, option.itemParent); });
|
|
242
|
+
}
|
|
162
243
|
}
|
|
163
244
|
}
|
|
164
|
-
if (descriptionMember === undefined &&
|
|
165
|
-
if (EnumHelper.isEnumItem(
|
|
245
|
+
if (descriptionMember === undefined && items_2.length > 0) {
|
|
246
|
+
if (EnumHelper.isEnumItem(items_2[0])) {
|
|
166
247
|
descriptionMember = "description";
|
|
167
248
|
}
|
|
168
249
|
}
|
|
@@ -186,7 +267,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
186
267
|
var displayValue = bindDisplayProperty === null || bindDisplayProperty === void 0 ? void 0 : bindDisplayProperty.value;
|
|
187
268
|
if (!bindDisplayProperty) {
|
|
188
269
|
if (defaultItems) {
|
|
189
|
-
displayValue = (_d = (_c =
|
|
270
|
+
displayValue = (_d = (_c = this.findOptionByValue(defaultItems, bindValue.value)) === null || _c === void 0 ? void 0 : _c.label) !== null && _d !== void 0 ? _d : "";
|
|
190
271
|
}
|
|
191
272
|
else {
|
|
192
273
|
throw "bindDisplay is required when the items prop is not set.";
|
|
@@ -225,7 +306,7 @@ var AutoCompleteDropDown = /** @class */ (function (_super) {
|
|
|
225
306
|
};
|
|
226
307
|
if (this.props.inputFilter && !this.props.itemSource) {
|
|
227
308
|
nativeProps.filterOption = function (option, input) {
|
|
228
|
-
return !input || _this.props.inputFilter(input, option.data.item);
|
|
309
|
+
return !input || _this.props.inputFilter(input, option.data.item, option.data.itemParent);
|
|
229
310
|
};
|
|
230
311
|
}
|
|
231
312
|
if (this.props.itemSource !== undefined) {
|
|
@@ -3,20 +3,20 @@ import { IBindableEditorProps } from '../PropTypes';
|
|
|
3
3
|
import { Data } from '@singularsystems/neo-core';
|
|
4
4
|
import { DropDownBase } from './DropDownBase';
|
|
5
5
|
import { FormContext } from '../FormContext';
|
|
6
|
-
export interface IDropDownSpecificProps<T> {
|
|
6
|
+
export interface IDropDownSpecificProps<T, TChild = T> {
|
|
7
7
|
items?: T[] | Promise<T[]>;
|
|
8
8
|
itemSource?: Data.IAsyncDataSource<T[]> | Data.IArrayCacheEntry<T>;
|
|
9
|
-
valueMember?: keyof
|
|
10
|
-
displayMember?: keyof
|
|
9
|
+
valueMember?: keyof TChild;
|
|
10
|
+
displayMember?: keyof TChild;
|
|
11
11
|
/**
|
|
12
12
|
* Set to true if your bind property is nullable, and you need to show a blank item in the list.
|
|
13
13
|
* If your property value is null, a blank item will automatically be shown.
|
|
14
14
|
*/
|
|
15
15
|
allowNulls?: boolean;
|
|
16
16
|
/** Function used to filter items out of the datasource. */
|
|
17
|
-
filterPredicate?: (item: T) => boolean;
|
|
17
|
+
filterPredicate?: (item: TChild, itemParent?: T) => boolean;
|
|
18
18
|
/** Callback which fires when an item is selected. */
|
|
19
|
-
onItemSelected?: (item?:
|
|
19
|
+
onItemSelected?: (item?: TChild) => void;
|
|
20
20
|
/**
|
|
21
21
|
* True if the display member of the selected item should display as plain text.
|
|
22
22
|
* Use this if you need a readonly display, and don't want a form control rendered.
|
|
@@ -28,18 +28,28 @@ export interface IDropDownSpecificProps<T> {
|
|
|
28
28
|
deSelectText?: string;
|
|
29
29
|
/** Set to true if the property should be sorted by value instead of display text. */
|
|
30
30
|
sortByValue?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The group member to use as the display for grouped items.
|
|
33
|
+
*/
|
|
34
|
+
groupDisplayMember?: keyof T;
|
|
35
|
+
/**
|
|
36
|
+
* The child list containing the grouped items.
|
|
37
|
+
*/
|
|
38
|
+
childListMember?: keyof T;
|
|
31
39
|
}
|
|
32
|
-
export interface IDropDownProps<T> extends IBindableEditorProps<HTMLSelectElement> {
|
|
33
|
-
select: IDropDownSpecificProps<T>;
|
|
40
|
+
export interface IDropDownProps<T, TChild = T> extends IBindableEditorProps<HTMLSelectElement> {
|
|
41
|
+
select: IDropDownSpecificProps<T, TChild>;
|
|
34
42
|
}
|
|
35
|
-
export default class DropDown<T> extends DropDownBase<T, IDropDownProps<T
|
|
43
|
+
export default class DropDown<T, TChild = T> extends DropDownBase<T, IDropDownProps<T, TChild>, TChild> {
|
|
36
44
|
static contextType: React.Context<import("../FormContext").FormContextParams>;
|
|
37
45
|
context: React.ContextType<typeof FormContext>;
|
|
38
46
|
private selectedItem;
|
|
39
47
|
private sortByValue?;
|
|
40
48
|
private listIndexSymbol;
|
|
41
|
-
|
|
49
|
+
private get isGrouped();
|
|
50
|
+
constructor(props: IDropDownProps<T, TChild>);
|
|
42
51
|
private get items();
|
|
52
|
+
private get groupedItems();
|
|
43
53
|
private createLookupIndex;
|
|
44
54
|
private getSelectedOption;
|
|
45
55
|
private onItemSelected;
|
|
@@ -15,17 +15,36 @@ var DropDown = /** @class */ (function (_super) {
|
|
|
15
15
|
_this.fieldInfoContainer = Model.ModelFieldInfo.asModelFieldInfoContainer(_this.props.bind.attachedTo);
|
|
16
16
|
return _this;
|
|
17
17
|
}
|
|
18
|
+
Object.defineProperty(DropDown.prototype, "isGrouped", {
|
|
19
|
+
get: function () {
|
|
20
|
+
return this.props.select.groupDisplayMember !== undefined
|
|
21
|
+
&& this.props.select.childListMember !== undefined;
|
|
22
|
+
},
|
|
23
|
+
enumerable: false,
|
|
24
|
+
configurable: true
|
|
25
|
+
});
|
|
18
26
|
Object.defineProperty(DropDown.prototype, "items", {
|
|
19
27
|
get: function () {
|
|
20
28
|
var _this = this;
|
|
21
|
-
var _a;
|
|
22
|
-
var items =
|
|
23
|
-
if (
|
|
24
|
-
var
|
|
25
|
-
this.
|
|
29
|
+
var _a, _b;
|
|
30
|
+
var items = [];
|
|
31
|
+
if (this.isGrouped) {
|
|
32
|
+
var groups = this.getItems((_a = this.props.select.items) !== null && _a !== void 0 ? _a : this.props.select.itemSource);
|
|
33
|
+
var firstChildItem = this.getFirstChildItem(groups, this.props.select.childListMember);
|
|
34
|
+
if (firstChildItem) {
|
|
35
|
+
this.findMembers(firstChildItem);
|
|
36
|
+
}
|
|
37
|
+
items = groups.flatMap(function (group) { return group[_this.props.select.childListMember]; });
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
items = this.getItems((_b = this.props.select.items) !== null && _b !== void 0 ? _b : this.props.select.itemSource);
|
|
41
|
+
if (items && items.length > 0) {
|
|
42
|
+
var item = items[0];
|
|
43
|
+
this.findMembers(item);
|
|
44
|
+
}
|
|
26
45
|
}
|
|
27
46
|
if (this.props.select.filterPredicate !== undefined) {
|
|
28
|
-
return items.filter(function (item) { return _this.props.bind.value === item[_this.valueMember] || _this.props.select.filterPredicate(item); });
|
|
47
|
+
return items.filter(function (item) { return _this.props.bind.value === item[_this.valueMember] || _this.props.select.filterPredicate(item, undefined); });
|
|
29
48
|
}
|
|
30
49
|
else {
|
|
31
50
|
return items;
|
|
@@ -34,6 +53,31 @@ var DropDown = /** @class */ (function (_super) {
|
|
|
34
53
|
enumerable: false,
|
|
35
54
|
configurable: true
|
|
36
55
|
});
|
|
56
|
+
Object.defineProperty(DropDown.prototype, "groupedItems", {
|
|
57
|
+
get: function () {
|
|
58
|
+
var _this = this;
|
|
59
|
+
var _a;
|
|
60
|
+
var groups = this.getItems((_a = this.props.select.items) !== null && _a !== void 0 ? _a : this.props.select.itemSource);
|
|
61
|
+
var firstChildItem = this.getFirstChildItem(groups, this.props.select.childListMember);
|
|
62
|
+
if (firstChildItem) {
|
|
63
|
+
this.findMembers(firstChildItem);
|
|
64
|
+
}
|
|
65
|
+
// Handle grouped filtering - filter children in each group
|
|
66
|
+
var groupsWithFilteredChildren = groups.map(function (group) {
|
|
67
|
+
var children = group[_this.props.select.childListMember];
|
|
68
|
+
if (_this.props.select.filterPredicate !== undefined) {
|
|
69
|
+
return __assign(__assign({}, group), { options: children.filter(function (item) { return _this.props.bind.value === (item)[_this.valueMember] || _this.props.select.filterPredicate(item, group); }) });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return __assign(__assign({}, group), { options: children });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Remove groups that have no children after filtering
|
|
76
|
+
return groupsWithFilteredChildren.filter(function (group) { return group.options.length > 0; });
|
|
77
|
+
},
|
|
78
|
+
enumerable: false,
|
|
79
|
+
configurable: true
|
|
80
|
+
});
|
|
37
81
|
DropDown.prototype.createLookupIndex = function (list) {
|
|
38
82
|
var _this = this;
|
|
39
83
|
var index = {};
|
|
@@ -42,12 +86,12 @@ var DropDown = /** @class */ (function (_super) {
|
|
|
42
86
|
list.forEach(function (item) { return index[item[_this.valueMember]] = item; });
|
|
43
87
|
return index;
|
|
44
88
|
};
|
|
45
|
-
DropDown.prototype.getSelectedOption = function (value) {
|
|
89
|
+
DropDown.prototype.getSelectedOption = function (value, items) {
|
|
46
90
|
if (!this.sortByValue) {
|
|
47
91
|
this.props.bind.propertyInfo.hasSortValue = true;
|
|
48
92
|
}
|
|
49
93
|
if (!this.selectedItem || this.selectedItem[this.valueMember] != value) {
|
|
50
|
-
var list = this.items;
|
|
94
|
+
var list = items !== null && items !== void 0 ? items : this.items;
|
|
51
95
|
//@ts-ignore
|
|
52
96
|
var index = list[this.listIndexSymbol];
|
|
53
97
|
var indexJustCreated = false;
|
|
@@ -103,7 +147,7 @@ var DropDown = /** @class */ (function (_super) {
|
|
|
103
147
|
var displayInfo = this.props.bind.validator.getDisplayState(this.context.validationDisplayMode);
|
|
104
148
|
displayInfo.applyToDom(domProps, !splitProps.inGroup);
|
|
105
149
|
var items = this.items;
|
|
106
|
-
var selectedItem = this.getSelectedOption(this.props.bind.value); // This sets the sort value to allow sorting of drop downs in grids.
|
|
150
|
+
var selectedItem = this.getSelectedOption(this.props.bind.value, items); // This sets the sort value to allow sorting of drop downs in grids.
|
|
107
151
|
var value = this.props.bind.value;
|
|
108
152
|
var valueIsEmpty = !value;
|
|
109
153
|
var notFound = items.length > 0 && !valueIsEmpty && selectedItem[this.valueMember] !== value;
|
|
@@ -132,10 +176,22 @@ var DropDown = /** @class */ (function (_super) {
|
|
|
132
176
|
if (valueIsNull) {
|
|
133
177
|
domProps.className = Utils.joinWithSpaces(domProps.className, "select-no-value");
|
|
134
178
|
}
|
|
179
|
+
var childListMember = void 0;
|
|
180
|
+
var groupDisplayMember_1;
|
|
181
|
+
if (this.isGrouped) {
|
|
182
|
+
childListMember = this.props.select.childListMember;
|
|
183
|
+
groupDisplayMember_1 = this.props.select.groupDisplayMember;
|
|
184
|
+
}
|
|
135
185
|
control =
|
|
136
186
|
React.createElement("select", __assign({ value: valueIsNull ? "" : value, onChange: this.onItemSelected, ref: splitProps.editor.inputElement }, domProps),
|
|
137
187
|
addBlankItem && React.createElement("option", { className: "option-default" }, notFound ? "(Not found): ".concat(value) : nullText),
|
|
138
|
-
|
|
188
|
+
!this.isGrouped &&
|
|
189
|
+
this.items.map(function (item) { return (React.createElement("option", { value: item[_this.valueMember], key: item[_this.valueMember] }, item[_this.displayMember])); }),
|
|
190
|
+
this.isGrouped &&
|
|
191
|
+
this.groupedItems.map(function (group) {
|
|
192
|
+
var children = group.options;
|
|
193
|
+
return React.createElement("optgroup", { label: group[groupDisplayMember_1], key: group[groupDisplayMember_1] }, (children.map(function (item) { return (React.createElement("option", { value: item[_this.valueMember], key: item[_this.valueMember] }, item[_this.displayMember])); })));
|
|
194
|
+
}));
|
|
139
195
|
}
|
|
140
196
|
return (InputHelpers.surroundWithInputGroup(control, splitProps.editor, domProps));
|
|
141
197
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Data, Model } from '@singularsystems/neo-core';
|
|
2
2
|
import ComponentBase from '../ComponentBase';
|
|
3
3
|
export type ItemSource<T> = T[] | Promise<T[]> | Data.IAsyncDataSource<T[]> | Data.IArrayCacheEntry<T>;
|
|
4
|
-
export declare abstract class DropDownBase<T, TProps> extends ComponentBase<TProps> {
|
|
5
|
-
protected valueMember: keyof
|
|
6
|
-
protected displayMember: keyof
|
|
4
|
+
export declare abstract class DropDownBase<T, TProps, TChild = T> extends ComponentBase<TProps> {
|
|
5
|
+
protected valueMember: keyof TChild;
|
|
6
|
+
protected displayMember: keyof TChild;
|
|
7
7
|
protected findMembers(item: any): void;
|
|
8
8
|
protected isLoading: boolean;
|
|
9
9
|
protected dataSource: Data.IAsyncDataSource<T[]> | null;
|
|
@@ -13,4 +13,5 @@ export declare abstract class DropDownBase<T, TProps> extends ComponentBase<TPro
|
|
|
13
13
|
afterRender(isInitial: boolean): Promise<void>;
|
|
14
14
|
private fieldDisplayInfo;
|
|
15
15
|
protected setFieldInfo(bindProperty: Model.IPropertyInstance, displayValue: string, readableName?: string): void;
|
|
16
|
+
protected getFirstChildItem(items: T[], childListMember: keyof T): TChild | null;
|
|
16
17
|
}
|
|
@@ -81,6 +81,16 @@ var DropDownBase = /** @class */ (function (_super) {
|
|
|
81
81
|
this.fieldDisplayInfo = { bindProperty: bindProperty, displayValue: displayValue, readableName: readableName };
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
|
+
DropDownBase.prototype.getFirstChildItem = function (items, childListMember) {
|
|
85
|
+
if (items.length > 0) {
|
|
86
|
+
var firstParentItem = items[0];
|
|
87
|
+
var childList = firstParentItem[childListMember];
|
|
88
|
+
if (childList && childList.length > 0) {
|
|
89
|
+
return childList[0];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
84
94
|
return DropDownBase;
|
|
85
95
|
}(ComponentBase));
|
|
86
96
|
export { DropDownBase };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Components, Model } from '@singularsystems/neo-core';
|
|
3
|
-
interface IFormProps<T extends Model.IModelBase> extends Omit<React.FormHTMLAttributes<HTMLElement>, "children">, Components.IFormOptions<T> {
|
|
3
|
+
interface IFormProps<T extends Model.IModelBase> extends Omit<React.FormHTMLAttributes<HTMLElement>, "children" | "onInvalid">, Components.IFormOptions<T> {
|
|
4
4
|
/**
|
|
5
5
|
* Called when the form is submitted, but the model is not valid.
|
|
6
6
|
* The firstSubmit parameter will be true if this is the first submit attempt for the model.
|
|
7
7
|
*/
|
|
8
|
-
onInvalid?: (e?: React.
|
|
8
|
+
onInvalid?: (e?: React.SubmitEvent, firstSubmit?: boolean) => void;
|
|
9
9
|
children: React.ReactNode | ((item: T, meta: Model.TransformMetaType<T>) => React.ReactNode);
|
|
10
10
|
}
|
|
11
11
|
export default class Form<T extends Model.IModelBase> extends React.Component<IFormProps<T>> {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __assign, __awaiter, __decorate, __extends, __generator, __rest } from "tslib";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { observer
|
|
4
|
-
import {
|
|
3
|
+
import { observer } from 'mobx-react';
|
|
4
|
+
import { Model, ModalUtils, Misc } from '@singularsystems/neo-core';
|
|
5
5
|
import ValidationSummary from './ValidationSummary';
|
|
6
6
|
import { reaction } from 'mobx';
|
|
7
7
|
import { FormContextProvider } from './FormContext';
|
|
@@ -82,7 +82,7 @@ var Form = /** @class */ (function (_super) {
|
|
|
82
82
|
}
|
|
83
83
|
codeModal_1 = ModalUtils.showModal(_this.props.invalidSummaryTitle || "Invalid data", React.createElement("div", null,
|
|
84
84
|
_this.props.invalidSummaryText || "Cannot continue due to the following issues:",
|
|
85
|
-
React.createElement(
|
|
85
|
+
React.createElement(ValidationSummary, { model: modelOrModels, hideTopLevelTitle: !(modelOrModels instanceof Array) })), { onClose: onClose, closeButton: closeButton });
|
|
86
86
|
}
|
|
87
87
|
if (someBusy()) {
|
|
88
88
|
// Async rule is busy, listen for when isBusy changes to false.
|
|
@@ -112,7 +112,7 @@ var Form = /** @class */ (function (_super) {
|
|
|
112
112
|
};
|
|
113
113
|
Form.prototype.render = function () {
|
|
114
114
|
var _this = this;
|
|
115
|
-
var _a = this.props, validationDisplayMode = _a.validationDisplayMode, allReadOnly = _a.allReadOnly, allDisabled = _a.allDisabled, model = _a.model, showSummaryModal = _a.showSummaryModal, invalidSummaryTitle = _a.invalidSummaryTitle, invalidSummaryText = _a.invalidSummaryText, labelSizes = _a.labelSizes, suppressLabels = _a.suppressLabels, native = __rest(_a, ["validationDisplayMode", "allReadOnly", "allDisabled", "model", "showSummaryModal", "invalidSummaryTitle", "invalidSummaryText", "labelSizes", "suppressLabels"]);
|
|
115
|
+
var _a = this.props, validationDisplayMode = _a.validationDisplayMode, allReadOnly = _a.allReadOnly, allDisabled = _a.allDisabled, model = _a.model, showSummaryModal = _a.showSummaryModal, invalidSummaryTitle = _a.invalidSummaryTitle, invalidSummaryText = _a.invalidSummaryText, labelSizes = _a.labelSizes, suppressLabels = _a.suppressLabels, onInvalid = _a.onInvalid, native = __rest(_a, ["validationDisplayMode", "allReadOnly", "allDisabled", "model", "showSummaryModal", "invalidSummaryTitle", "invalidSummaryText", "labelSizes", "suppressLabels", "onInvalid"]);
|
|
116
116
|
return (React.createElement("form", __assign({}, native, { onSubmit: function (e) { return _this.onSubmit(e); } }),
|
|
117
117
|
React.createElement(FormContextProvider, { validationDisplayMode: validationDisplayMode !== null && validationDisplayMode !== void 0 ? validationDisplayMode : Misc.Settings.form.validationDisplayMode, allReadOnly: allReadOnly, allDisabled: allDisabled, labelSizes: labelSizes, suppressLabels: suppressLabels }, (this.props.children instanceof Function) ? this.props.children(model, model.meta) : this.props.children)));
|
|
118
118
|
};
|
|
@@ -132,7 +132,7 @@ export declare class BindPropsHelper {
|
|
|
132
132
|
* Gets the correct input control type based on the props. E.g. Input / DatePicker / Select
|
|
133
133
|
* @param props Props passed to a multi type input container. E.g. FormGroup / Column
|
|
134
134
|
*/
|
|
135
|
-
static getFormControl(props: IEditorNormalisedProps<any, any>): React.CElement<import("./DatePicker").INeoDatePickerProps, DatePicker> | React.CElement<import("./NumberInput").INumberInputProps, NumberInput> | React.CElement<import("./DropDown/DropDown").IDropDownProps<unknown>, DropDown<unknown>> | React.CElement<IFileEditorProps, FileInput> | React.CElement<import("./Input").IInputProps, Input> | React.CElement<import("./RadioList").IRadioListProps<unknown>, RadioList<unknown>>;
|
|
135
|
+
static getFormControl(props: IEditorNormalisedProps<any, any>): React.CElement<import("./DatePicker").INeoDatePickerProps, DatePicker> | React.CElement<import("./NumberInput").INumberInputProps, NumberInput> | React.CElement<import("./DropDown/DropDown").IDropDownProps<unknown, unknown>, DropDown<unknown, unknown>> | React.CElement<IFileEditorProps, FileInput> | React.CElement<import("./Input").IInputProps, Input> | React.CElement<import("./RadioList").IRadioListProps<unknown>, RadioList<unknown>>;
|
|
136
136
|
static getControlType(props: IEditorNormalisedProps<any, any>): ControlType;
|
|
137
137
|
static getCheckBoxType(props: IEditorNormalisedProps<any, any>, instance?: any): CheckboxType;
|
|
138
138
|
/**
|
|
@@ -55,7 +55,12 @@ var RadioList = /** @class */ (function (_super) {
|
|
|
55
55
|
return React.createElement("div", { className: className }, this.items.map(function (item, index) {
|
|
56
56
|
var _a, _b;
|
|
57
57
|
return React.createElement("div", { className: "custom-radio " + prefix + (radioProps.inline ? " ".concat(prefix, "-inline") : ""), key: item.id },
|
|
58
|
-
React.createElement("input", { type: "radio", id: splitProps.uniqueID + ((_a = item.id) !== null && _a !== void 0 ? _a : ""), name: splitProps.uniqueID, checked: bindValue === item.id, disabled: domProps.disabled || domProps.readOnly, onChange: function () {
|
|
58
|
+
React.createElement("input", { type: "radio", id: splitProps.uniqueID + ((_a = item.id) !== null && _a !== void 0 ? _a : ""), name: splitProps.uniqueID, checked: bindValue === item.id, disabled: domProps.disabled || domProps.readOnly, onChange: function (e) {
|
|
59
|
+
_this.onItemSelected(item);
|
|
60
|
+
if (domProps.onChange) {
|
|
61
|
+
domProps.onChange(e);
|
|
62
|
+
}
|
|
63
|
+
}, className: "".concat(prefix, "-input") }),
|
|
59
64
|
React.createElement("label", { className: "".concat(prefix, "-label"), htmlFor: splitProps.uniqueID + ((_b = item.id) !== null && _b !== void 0 ? _b : ""), "data-tip": tooltips && index < tooltips.length ? tooltips[index] : undefined }, item.name));
|
|
60
65
|
}));
|
|
61
66
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { __extends } from "tslib";
|
|
1
|
+
import { __decorate, __extends } from "tslib";
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { Validation, Misc } from '@singularsystems/neo-core';
|
|
4
4
|
import { NeoReactTypes } from '../Modules/Types';
|
|
5
|
+
import { observer } from "mobx-react";
|
|
5
6
|
var ValidationSummary = /** @class */ (function (_super) {
|
|
6
7
|
__extends(ValidationSummary, _super);
|
|
7
8
|
function ValidationSummary() {
|
|
@@ -9,6 +10,7 @@ var ValidationSummary = /** @class */ (function (_super) {
|
|
|
9
10
|
_this.iconFactory = Misc.Globals.appService.get(NeoReactTypes.Components.IconFactory);
|
|
10
11
|
return _this;
|
|
11
12
|
}
|
|
13
|
+
ValidationSummary_1 = ValidationSummary;
|
|
12
14
|
ValidationSummary.prototype.getRules = function (modelInfo) {
|
|
13
15
|
return modelInfo.objectDisplayInfo.concat(modelInfo.propertyRules)
|
|
14
16
|
.sort(function (a, b) { return a.order === b.order ? 0 : (a.order > b.order ? 1 : -1); })
|
|
@@ -37,10 +39,14 @@ var ValidationSummary = /** @class */ (function (_super) {
|
|
|
37
39
|
_this.iconFactory.getIconComponent(rule.displayInfo.icon, { fixedWidth: true, spin: !!rule.displayInfo.asyncBusyText }),
|
|
38
40
|
" ",
|
|
39
41
|
rule.displayInfo.displayText)); })),
|
|
40
|
-
React.createElement(
|
|
41
|
-
React.createElement(
|
|
42
|
-
React.createElement(
|
|
42
|
+
React.createElement(ValidationSummary_1, { graph: modelInfo.valueObjects }),
|
|
43
|
+
React.createElement(ValidationSummary_1, { graph: modelInfo.childObjects }),
|
|
44
|
+
React.createElement(ValidationSummary_1, { graph: modelInfo.childLists })))); })));
|
|
43
45
|
};
|
|
46
|
+
var ValidationSummary_1;
|
|
47
|
+
ValidationSummary = ValidationSummary_1 = __decorate([
|
|
48
|
+
observer
|
|
49
|
+
], ValidationSummary);
|
|
44
50
|
return ValidationSummary;
|
|
45
51
|
}(React.Component));
|
|
46
52
|
export default ValidationSummary;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singularsystems/neo-react",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
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",
|
|
@@ -18,26 +18,26 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/jest": "30.0.0",
|
|
20
20
|
"@types/memoize-one": "5.1.2",
|
|
21
|
-
"@types/node": "
|
|
22
|
-
"@types/react": "19.2.
|
|
21
|
+
"@types/node": "25.3.0",
|
|
22
|
+
"@types/react": "19.2.14",
|
|
23
23
|
"@types/react-custom-scrollbars": "4.0.13",
|
|
24
|
-
"@types/react-dom": "19.2.
|
|
24
|
+
"@types/react-dom": "19.2.3",
|
|
25
25
|
"decimal.js-light": "2.5.1",
|
|
26
26
|
"react-scripts": "5.0.1",
|
|
27
|
-
"typescript": "5.
|
|
27
|
+
"typescript": "5.9.3"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@singularsystems/neo-core": "1.3.
|
|
30
|
+
"@singularsystems/neo-core": "1.3.8",
|
|
31
31
|
"@types/react-color": "3.0.13",
|
|
32
|
-
"axios": "1.
|
|
32
|
+
"axios": "1.13.5",
|
|
33
33
|
"inversify": "6.0.2",
|
|
34
34
|
"memoize-one": "6.0.0",
|
|
35
35
|
"mobx": "6.15.0",
|
|
36
36
|
"mobx-react": "9.2.1",
|
|
37
37
|
"rc-slider": "11.1.9",
|
|
38
|
-
"react": "19.2.
|
|
38
|
+
"react": "19.2.4",
|
|
39
39
|
"react-custom-scrollbars": "4.2.1",
|
|
40
|
-
"react-dom": "19.2.
|
|
40
|
+
"react-dom": "19.2.4",
|
|
41
41
|
"react-select": "5.10.2",
|
|
42
42
|
"tslib": "2.8.1"
|
|
43
43
|
},
|