@singularsystems/neo-react 1.2.4 → 1.3.1
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 +34 -155
- package/dist/ReactComponents/BootstrapUtils.d.ts +4 -0
- package/dist/ReactComponents/BootstrapUtils.js +36 -0
- package/dist/ReactComponents/Card.d.ts +2 -0
- package/dist/ReactComponents/Card.js +8 -4
- package/dist/ReactComponents/Grid/ButtonColumn.d.ts +1 -0
- package/dist/ReactComponents/Grid/ButtonColumn.js +44 -14
- package/dist/ReactComponents/Grid/Column.d.ts +1 -1
- package/dist/ReactComponents/Grid/Row.d.ts +7 -8
- package/dist/ReactComponents/Grid/Row.js +14 -1
- package/dist/ReactComponents/GridLayout.d.ts +2 -0
- package/dist/ReactComponents/GridLayout.js +15 -3
- package/dist/ReactComponents/Icons/MaterialSymbolsFontFactory.js +3 -1
- package/dist/Routing/RouteProvider.d.ts +1 -0
- package/dist/Routing/RouteProvider.js +5 -4
- package/dist/Views/ViewModelBase.d.ts +1 -1
- package/dist/Views/ViewModelBase.js +6 -2
- package/package.json +3 -3
- package/styles/Card.scss +11 -0
- package/styles/Grid.scss +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,171 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Neo react changelog
|
|
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
|
-
|
|
5
|
+
## Version 1.3.1
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- Update `react-select` to at least 5.10.0.
|
|
9
|
-
- Update `react-custom-scrollbars` and its types to the latest version.
|
|
10
|
-
- If you are using `rc-slider`.
|
|
11
|
-
- Remove the `@types/rc-slider` package, and update `rc-slider` to the latest version.
|
|
12
|
-
- In your app module, remove the `Range` import, and remove this line: `container.bind(Types.Neo.Components.Range).toConstantValue(Range);`
|
|
13
|
-
- If you are using `react-color`.
|
|
14
|
-
- Update the package, and types to the latest version.
|
|
15
|
-
- Under React 19, JSX is no longer a global import. You may receive errors such as "Cannot find namespace 'JSX'."
|
|
16
|
-
- To rectify, change `JSX.Element` to `React.ReactNode`.
|
|
17
|
-
- If that doesn't work, you can import JSX from React. E.g. `import React, { JSX } from 'React';`
|
|
18
|
-
- Finally, run `yarn dedupe` to make sure all old transitive packages reference the project version.
|
|
7
|
+
- Routing - Fixed an issue where the base route would take precedence over other routes if it had parameters, and was declared before other routes.
|
|
19
8
|
|
|
20
|
-
|
|
9
|
+
## Version 1.3.0
|
|
21
10
|
|
|
22
|
-
|
|
11
|
+
- ViewBase and ViewComponentBase now require that the linked view model has a `TaskRunner` as the first argument. See breaking changes for more info.
|
|
12
|
+
- GridLayout
|
|
13
|
+
- Added `arrangeVertically` prop. Allows items to flow from top to bottom instead of left to right. Useful the height of each item differs.
|
|
14
|
+
- Neo.Card
|
|
15
|
+
- Now has a `footerContent` prop. This allows a footer to be added to the card. The footer will be anchored to the bottom of the card, and will be collapsed if the card is collapsed.
|
|
16
|
+
- Settings
|
|
17
|
+
- Added `buttons.backButton` setting to replace the `icons.backIcon` global setting. This allows more customisation of back buttons used in built in neo screens.
|
|
18
|
+
- Added `icons.unDelete` to allow changing the undo delete icon in neo grids.
|
|
19
|
+
- Deferred deleting: see below.
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
### Deferred deleting
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
Previously when clicking the default delete button in a neo grid, the item would be removed from the bound list, and tracked under the `deletedList` property. When serialising the list (usually on clicking save), the deleted items would be included with a tracking state of "deleted" so that they could be deleted from the database.
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
This was slightly confusing for a user, since they might not know that a save is required to actually commit the delete. Additionally, if the save failed, the item would not re-appear in the grid and the user would be stuck, having to reload the screen.
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
"react-select": "5.8.1",
|
|
32
|
-
```
|
|
27
|
+
In this version, a new option has been added to allow the grid to keep displaying the row being deleted, but displayed with ~~strikethrough~~ text and 50% transparency. The delete button will show a different icon when the item is marked as deleted, allowing the user to undo the delete before saving. A tooltip will also show when hovering over the row.
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
To make this work, 3 main changes were made to `ModelBase` and `List`.
|
|
30
|
+
- `ModelBase` now has an `isSelfDeleting` property.
|
|
31
|
+
- This causes the grid to display the item differently.
|
|
32
|
+
- Models with this property set to true will be serialised with a tracking state of deleted.
|
|
33
|
+
- This will cause `isDirty` to return true, however `isSelfDirty` will remain false.
|
|
34
|
+
- `List` has new methods: `beginRemove` and `toggleRemove`.
|
|
35
|
+
- These new methods check whether the list is in "soft delete" mode (see below), and if so, sets `isSelfDeleting` to true on the item being removed.
|
|
36
|
+
- The existing `remove` and `removeWithoutTracking` methods will always remove the item from the main list regardless of the delete mode.
|
|
37
|
+
- `List` now has an `activeLength` property.
|
|
38
|
+
- Use this if you need to check if the list is required to have a certain amount of items. E.g. in validation rules.
|
|
35
39
|
|
|
36
|
-
Version 1 of neo-react upgrades react to version 18, and mobX to version 6. Both of these are major updates and will require you to make code changes to your project.
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
#### Settings:
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
To control the behavior when removing an item, you use the global setting, or you can configure each list individually by passing in options to the list constructor.
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
- To configure globally, set `Misc.Settings.list.removeItemBehavior` to `RemoveItemBehavior.MarkDeleted`.
|
|
46
|
+
- The tooltip to show that the item is being deleted can be customised by setting the `isDeletingTooltip` prop on the grid row, or by using the `Misc.Settings.grid.isDeletingTooltip` global setting.
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
By default this setting will be enabled for catalogue grids, but is not enabled globally.
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
* Update the following packages to the latest version (these were the latest at time of writing):
|
|
48
|
-
* dependencies
|
|
49
|
-
* `"axios": "1.6.7"`,
|
|
50
|
-
* `"decimal.js-light": "2.5.1"`, - See below for alternative
|
|
51
|
-
* `"mobx": "6.11.0"`,
|
|
52
|
-
* `"mobx-react": "7.6.0"`,
|
|
53
|
-
* `"react": "18.2.0"`,
|
|
54
|
-
* `"react-dom": "18.2.0"`,
|
|
55
|
-
* `"react-router": "5.2.1"`, - Must be version 5.x
|
|
56
|
-
* `"react-router-dom": "5.3.0"`, - Must be version 5.x
|
|
57
|
-
* `"react-select": "5.7.3"`,
|
|
58
|
-
|
|
59
|
-
* devDependencies
|
|
60
|
-
* `"@types/react": "18.2.37"`,
|
|
61
|
-
* `"@types/react-dom": "18.2.15"`,
|
|
62
|
-
* `"@types/react-router": "5.1.17"`, - Must be version 5.x
|
|
63
|
-
* `"@types/react-router-dom": "5.3.3"`, - Must be version 5.x
|
|
64
|
-
* resolutions
|
|
65
|
-
* remove all entries
|
|
66
|
-
* delete yarn.lock, and run `yarn install`
|
|
67
|
-
|
|
68
|
-
## Fix errors
|
|
69
|
-
|
|
70
|
-
If any of your components allow children, React 18 requires to you have an explicit `children` prop. Add this to the props definition of the component:
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
interface MyProps {
|
|
74
|
-
...
|
|
75
|
-
children: React.ReactNode;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// or if the component has no other props:
|
|
79
|
-
|
|
80
|
-
MyComponent extends React.Component<{ children: React.ReactNode }> {
|
|
81
|
-
...
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
The React root api has also changed in version 18. Make the following changes to your `index.tsx` file:
|
|
86
|
-
|
|
87
|
-
Your existing code will look something like this:
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
import ReactDOM from 'react-dom';
|
|
91
|
-
...
|
|
92
|
-
// React init
|
|
93
|
-
ReactDOM.render(
|
|
94
|
-
renderComponent,
|
|
95
|
-
document.getElementById('root') as HTMLElement
|
|
96
|
-
);
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Change the `ReactDOM` import, and the react init code:
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import ReactDOM from 'react-dom/client';
|
|
103
|
-
...
|
|
104
|
-
// React init
|
|
105
|
-
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
|
106
|
-
root.render(renderComponent);
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Mobx
|
|
110
|
-
|
|
111
|
-
Before mobx 6, you could mark a property as being observable by adding an `@observable` decorator to the property. In mobx 6 this no longer works ([read more](https://mobx.js.org/migrating-from-4-or-5.html)).
|
|
112
|
-
|
|
113
|
-
For any class that extends `ModelBase`, `ValueObject`, `ViewModelBase` etc, this change is handled by neo, and you will not need to change any code.
|
|
114
|
-
|
|
115
|
-
For other classes, you will need to add initialisation code to the constructor.
|
|
116
|
-
|
|
117
|
-
* Search for `@observable` in your project. (In most projects this will be in the Sidebar and AppLayout components)
|
|
118
|
-
* In the constructor of the class that contains the property, add:
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
makeObservable(this);
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Decorators on neo models
|
|
125
|
-
|
|
126
|
-
If you try use mobx decorators on any models that extend `ModelBase`, `ValueObject` or `ViewModelBase`, you will receive this error:
|
|
127
|
-
|
|
128
|
-
> [MobX] makeObservable second arg must be nullish when using decorators. Mixing @decorator syntax with annotations is not supported.
|
|
129
|
-
|
|
130
|
-
This is most likely because you have used an `@action` decorator on a method. Neo now marks all methods as actions, so you can remove the `@action` decorator.
|
|
131
|
-
|
|
132
|
-
* Search for `@action` in your project. Remove the decorator **only** if the class extends `ModelBase` (and its decendents e.g. `ValueObject`) or `ViewModelBase`.
|
|
133
|
-
|
|
134
|
-
### Array sorting
|
|
135
|
-
|
|
136
|
-
Mobx no longer allows you to call `sort` on an observable array. You first need to call `slice` to create a copy of the array. This change has been made in the neo `sortBy` method, but will cause unexpected behaviour if you relied on the list being sorted in-place, as this no longer happens.
|
|
137
|
-
|
|
138
|
-
* Search for `.sort` in your project. Change to use the neo `sortBy` method.
|
|
139
|
-
* Search for `.sortBy`. Ensure you use the array returned by this method, and not the array passed in.
|
|
140
|
-
|
|
141
|
-
### Viewmodels
|
|
142
|
-
|
|
143
|
-
You may receive errors such as:
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
Argument of type 'typeof MyVM' is not assignable to parameter of type 'VMConstructor<MyVM>'.
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
The `registerViewModel` method on the `ViewModelBase` class now requires the view model passed to it to have a `taskRunner` as the first argument. This task runner instance must be passed to the base constructor.
|
|
150
|
-
|
|
151
|
-
## Decimals
|
|
152
|
-
|
|
153
|
-
Neo no longer includes the `decimal.js-light` package by default. This is to allow for other decimal packages to be used. You will now need to manually register a decimal service in your app module.
|
|
154
|
-
|
|
155
|
-
The easiest would be to continue using `decimal.js-light`:
|
|
156
|
-
|
|
157
|
-
* Ensure your project references `decimal.js-light` directly.
|
|
158
|
-
* In your main app module, add the following code:
|
|
159
|
-
```typescript
|
|
160
|
-
import Decimal from "decimal.js-light";
|
|
161
|
-
...
|
|
162
|
-
container.bind(Types.Neo.Core.DecimalService).toConstantValue(new Numeric.DecimalService(Decimal, Decimal.ROUND_HALF_UP, Decimal.ROUND_DOWN));
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
An alternative is to use `big.js`. This package is slightly smaller than the above, but has less features.
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
import Big from "big.js";
|
|
169
|
-
...
|
|
170
|
-
container.bind(Types.Neo.Core.DecimalService).toConstantValue(new Numeric.DecimalService(Big, Big.roundHalfUp, Big.roundDown))
|
|
171
|
-
```
|
|
50
|
+
To change the styling of deleted rows, target: `tr.neo-item-deleting`.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Components } from "@singularsystems/neo-core";
|
|
1
2
|
import React, { MouseEventHandler } from "react";
|
|
2
3
|
declare class BootstrapUtils {
|
|
3
4
|
constructor();
|
|
5
|
+
private breakPoints;
|
|
4
6
|
/** Bootstrap version is before v5 */
|
|
5
7
|
get isPre5(): boolean;
|
|
6
8
|
/** Class prefix (without -) for start margin / left margin. */
|
|
@@ -12,6 +14,8 @@ declare class BootstrapUtils {
|
|
|
12
14
|
* @param pre5ClassName Class to add if bootstrap version is <5.
|
|
13
15
|
*/
|
|
14
16
|
getCloseButton(onClick: MouseEventHandler<HTMLButtonElement>, pre5ClassName?: string): React.JSX.Element;
|
|
17
|
+
getBreakpoints(): Components.IBootstrapSizes<number>;
|
|
18
|
+
currentDimension<T>(props: Partial<Components.IBootstrapSizes<T>>): T | undefined;
|
|
15
19
|
}
|
|
16
20
|
declare const _default: BootstrapUtils;
|
|
17
21
|
export default _default;
|
|
@@ -2,6 +2,7 @@ import { Misc, Utils } from "@singularsystems/neo-core";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
var BootstrapUtils = /** @class */ (function () {
|
|
4
4
|
function BootstrapUtils() {
|
|
5
|
+
this.breakPoints = null;
|
|
5
6
|
}
|
|
6
7
|
Object.defineProperty(BootstrapUtils.prototype, "isPre5", {
|
|
7
8
|
/** Bootstrap version is before v5 */
|
|
@@ -38,6 +39,41 @@ var BootstrapUtils = /** @class */ (function () {
|
|
|
38
39
|
React.createElement("span", { "aria-hidden": "true" }, "\u00D7")) :
|
|
39
40
|
React.createElement("button", { type: "button", className: "btn-close", "aria-label": "Close", onClick: onClick });
|
|
40
41
|
};
|
|
42
|
+
BootstrapUtils.prototype.getBreakpoints = function () {
|
|
43
|
+
if (!this.breakPoints) {
|
|
44
|
+
var styles = window.getComputedStyle(document.body);
|
|
45
|
+
this.breakPoints = {
|
|
46
|
+
xs: parseInt(styles.getPropertyValue('--bs-breakpoint-xs')),
|
|
47
|
+
sm: parseInt(styles.getPropertyValue('--bs-breakpoint-sm')),
|
|
48
|
+
md: parseInt(styles.getPropertyValue('--bs-breakpoint-md')),
|
|
49
|
+
lg: parseInt(styles.getPropertyValue('--bs-breakpoint-lg')),
|
|
50
|
+
xl: parseInt(styles.getPropertyValue('--bs-breakpoint-xl')),
|
|
51
|
+
xxl: parseInt(styles.getPropertyValue('--bs-breakpoint-xxl'))
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return this.breakPoints;
|
|
55
|
+
};
|
|
56
|
+
BootstrapUtils.prototype.currentDimension = function (props) {
|
|
57
|
+
var _a, _b, _c, _d, _e;
|
|
58
|
+
var width = window.innerWidth;
|
|
59
|
+
var breakpoints = this.getBreakpoints();
|
|
60
|
+
var found = props.xs;
|
|
61
|
+
if (width < breakpoints.sm)
|
|
62
|
+
return found;
|
|
63
|
+
found = (_a = props.sm) !== null && _a !== void 0 ? _a : found;
|
|
64
|
+
if (width < breakpoints.md)
|
|
65
|
+
return found;
|
|
66
|
+
found = (_b = props.md) !== null && _b !== void 0 ? _b : found;
|
|
67
|
+
if (width < breakpoints.lg)
|
|
68
|
+
return found;
|
|
69
|
+
found = (_c = props.lg) !== null && _c !== void 0 ? _c : found;
|
|
70
|
+
if (width < breakpoints.xl)
|
|
71
|
+
return found;
|
|
72
|
+
found = (_d = props.xl) !== null && _d !== void 0 ? _d : found;
|
|
73
|
+
if (width < breakpoints.xxl)
|
|
74
|
+
return found;
|
|
75
|
+
return (_e = props.xxl) !== null && _e !== void 0 ? _e : found;
|
|
76
|
+
};
|
|
41
77
|
return BootstrapUtils;
|
|
42
78
|
}());
|
|
43
79
|
export default new BootstrapUtils();
|
|
@@ -7,6 +7,8 @@ interface ICardProps {
|
|
|
7
7
|
title?: any;
|
|
8
8
|
/** Header elements to show on right hand side of header. */
|
|
9
9
|
headerElements?: JSX.Element;
|
|
10
|
+
/** Footer elements to show on bottom of the card. */
|
|
11
|
+
footerContent?: JSX.Element;
|
|
10
12
|
/** Use a custom scrollbar instead of the browser scrollbar. */
|
|
11
13
|
useCustomScrollbar?: boolean;
|
|
12
14
|
/** Causes the card to be collapsible. Adds a collapse icon to the header. */
|
|
@@ -15,7 +15,7 @@ var Card = /** @class */ (function (_super) {
|
|
|
15
15
|
return _this;
|
|
16
16
|
}
|
|
17
17
|
Card.prototype.render = function () {
|
|
18
|
-
var _a = this.props, icon = _a.icon, title = _a.title, children = _a.children, headerElements = _a.headerElements, useCustomScrollbar = _a.useCustomScrollbar, collapsible = _a.collapsible, initiallyCollapsed = _a.initiallyCollapsed, isExpanded = _a.isExpanded, expandIconPosition = _a.expandIconPosition, domProps = __rest(_a, ["icon", "title", "children", "headerElements", "useCustomScrollbar", "collapsible", "initiallyCollapsed", "isExpanded", "expandIconPosition"]);
|
|
18
|
+
var _a = this.props, icon = _a.icon, title = _a.title, children = _a.children, headerElements = _a.headerElements, footerContent = _a.footerContent, useCustomScrollbar = _a.useCustomScrollbar, collapsible = _a.collapsible, initiallyCollapsed = _a.initiallyCollapsed, isExpanded = _a.isExpanded, expandIconPosition = _a.expandIconPosition, domProps = __rest(_a, ["icon", "title", "children", "headerElements", "footerContent", "useCustomScrollbar", "collapsible", "initiallyCollapsed", "isExpanded", "expandIconPosition"]);
|
|
19
19
|
var className = Utils.joinWithSpaces("neo-card card", domProps.className);
|
|
20
20
|
expandIconPosition !== null && expandIconPosition !== void 0 ? expandIconPosition : (expandIconPosition = "right");
|
|
21
21
|
var isCollapsible = collapsible || isExpanded !== undefined || initiallyCollapsed !== undefined;
|
|
@@ -45,9 +45,13 @@ var Card = /** @class */ (function (_super) {
|
|
|
45
45
|
var body = React.createElement("div", { className: "card-body" },
|
|
46
46
|
useCustomScrollbar && React.createElement(Scrollbar, null, children),
|
|
47
47
|
!useCustomScrollbar && children);
|
|
48
|
+
var footer = footerContent ? (React.createElement("div", { className: 'card-footer' }, footerContent)) : null;
|
|
49
|
+
var content = React.createElement(React.Fragment, null,
|
|
50
|
+
body,
|
|
51
|
+
footer);
|
|
48
52
|
if (isCollapsible) {
|
|
49
|
-
|
|
50
|
-
React.createElement(Collapsable, { show: expandState.value },
|
|
53
|
+
content =
|
|
54
|
+
React.createElement(Collapsable, { show: expandState.value }, content);
|
|
51
55
|
}
|
|
52
56
|
return (React.createElement("div", __assign({}, domProps, { className: className }),
|
|
53
57
|
(icon || title) &&
|
|
@@ -57,7 +61,7 @@ var Card = /** @class */ (function (_super) {
|
|
|
57
61
|
this.iconFactory.getIconComponent(icon),
|
|
58
62
|
React.createElement("span", null, title)),
|
|
59
63
|
headerElements && React.createElement("div", { className: "card-header-right-section", onClick: function (e) { return e.stopPropagation(); } }, headerElements)),
|
|
60
|
-
|
|
64
|
+
content));
|
|
61
65
|
};
|
|
62
66
|
Card = __decorate([
|
|
63
67
|
observer
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { __assign, __awaiter, __extends, __generator, __rest } from "tslib";
|
|
1
|
+
import { __assign, __awaiter, __decorate, __extends, __generator, __metadata, __rest } from "tslib";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { GridContext } from './Grid';
|
|
4
4
|
import NeoButton from '../Button';
|
|
5
5
|
import { Misc, Utils } from '@singularsystems/neo-core';
|
|
6
6
|
import ComponentBase from '../ComponentBase';
|
|
7
7
|
import { ColumnHelper } from './ColumnHelper';
|
|
8
|
+
import { observer } from "mobx-react";
|
|
8
9
|
var ButtonColumn = /** @class */ (function (_super) {
|
|
9
10
|
__extends(ButtonColumn, _super);
|
|
10
11
|
function ButtonColumn(props) {
|
|
@@ -16,33 +17,52 @@ var ButtonColumn = /** @class */ (function (_super) {
|
|
|
16
17
|
this.section = this.context.section;
|
|
17
18
|
this.item = this.context.currentItem;
|
|
18
19
|
};
|
|
20
|
+
Object.defineProperty(ButtonColumn.prototype, "itemAsModel", {
|
|
21
|
+
get: function () {
|
|
22
|
+
var _a;
|
|
23
|
+
var item = (_a = this.props.item) !== null && _a !== void 0 ? _a : this.item;
|
|
24
|
+
if ((item === null || item === void 0 ? void 0 : item.entityIdentifier) !== undefined) {
|
|
25
|
+
return item;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
},
|
|
29
|
+
enumerable: false,
|
|
30
|
+
configurable: true
|
|
31
|
+
});
|
|
19
32
|
ButtonColumn.prototype.onDelete = function () {
|
|
20
33
|
return __awaiter(this, void 0, void 0, function () {
|
|
21
|
-
var item, mustRemove;
|
|
22
|
-
var _a
|
|
23
|
-
return __generator(this, function (
|
|
24
|
-
switch (
|
|
34
|
+
var item, list, mustRemove;
|
|
35
|
+
var _a;
|
|
36
|
+
return __generator(this, function (_b) {
|
|
37
|
+
switch (_b.label) {
|
|
25
38
|
case 0:
|
|
26
|
-
item =
|
|
27
|
-
|
|
39
|
+
item = this.itemAsModel;
|
|
40
|
+
list = this.context.dataView.rawItems;
|
|
41
|
+
if (!(item.isNew === false && ((_a = this.props.deleteButton) === null || _a === void 0 ? void 0 : _a.onDeleteExisting) !== undefined)) return [3 /*break*/, 2];
|
|
28
42
|
return [4 /*yield*/, this.props.deleteButton.onDeleteExisting(item)];
|
|
29
43
|
case 1:
|
|
30
|
-
mustRemove =
|
|
44
|
+
mustRemove = _b.sent();
|
|
31
45
|
if (mustRemove) {
|
|
32
|
-
|
|
46
|
+
list.removeWithoutTracking(item);
|
|
33
47
|
}
|
|
34
48
|
return [3 /*break*/, 3];
|
|
35
49
|
case 2:
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
if (list.toggleRemove !== undefined) {
|
|
51
|
+
list.toggleRemove(item);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.context.dataView.rawItems.remove(item);
|
|
55
|
+
}
|
|
56
|
+
_b.label = 3;
|
|
38
57
|
case 3: return [2 /*return*/];
|
|
39
58
|
}
|
|
40
59
|
});
|
|
41
60
|
});
|
|
42
61
|
};
|
|
43
62
|
ButtonColumn.prototype.render = function () {
|
|
63
|
+
var _a, _b;
|
|
44
64
|
this.onRender();
|
|
45
|
-
var
|
|
65
|
+
var _c = this.props, item = _c.item, label = _c.label, showDefaultButtonText = _c.showDefaultButtonText, buttonSize = _c.buttonSize, showDelete = _c.showDelete, editButton = _c.editButton, deleteButton = _c.deleteButton, viewButton = _c.viewButton, childAlignment = _c.childAlignment, hideBelow = _c.hideBelow, hideAbove = _c.hideAbove, alignment = _c.alignment, domProps = __rest(_c, ["item", "label", "showDefaultButtonText", "buttonSize", "showDelete", "editButton", "deleteButton", "viewButton", "childAlignment", "hideBelow", "hideAbove", "alignment"]);
|
|
46
66
|
domProps.className = Utils.joinWithSpaces("neo-grid-button-column", domProps.className);
|
|
47
67
|
domProps.className = ColumnHelper.addCommonClasses(domProps.className, this.props);
|
|
48
68
|
var buttonColumnDefaults = Misc.Settings.grid.buttonColumnButtons;
|
|
@@ -62,8 +82,14 @@ var ButtonColumn = /** @class */ (function (_super) {
|
|
|
62
82
|
var editButtonOptions = editButton ? Utils.populatePartialWithDefaults(editButton, editButtonDefaults) : null;
|
|
63
83
|
var deleteButtonOptions = (deleteButton || showDelete) ? Utils.populatePartialWithDefaults(deleteButton, deleteButtonDefaults) : null;
|
|
64
84
|
var viewButtonOptions = viewButton ? Utils.populatePartialWithDefaults(viewButton, viewButtonDefaults) : null;
|
|
65
|
-
if (deleteButtonOptions
|
|
66
|
-
deleteButtonOptions.onClick
|
|
85
|
+
if (deleteButtonOptions) {
|
|
86
|
+
if (!deleteButtonOptions.onClick) {
|
|
87
|
+
deleteButtonOptions.onClick = this.onDelete;
|
|
88
|
+
}
|
|
89
|
+
if ((_a = this.itemAsModel) === null || _a === void 0 ? void 0 : _a.isSelfDeleting) {
|
|
90
|
+
deleteButtonOptions.icon = Misc.Settings.icons.unDelete;
|
|
91
|
+
(_b = deleteButtonOptions.tooltip) !== null && _b !== void 0 ? _b : (deleteButtonOptions.tooltip = "Undo delete");
|
|
92
|
+
}
|
|
67
93
|
}
|
|
68
94
|
var buttons = React.createElement(React.Fragment, null,
|
|
69
95
|
viewButtonOptions && React.createElement(NeoButton, __assign({}, viewButtonOptions)),
|
|
@@ -79,6 +105,10 @@ var ButtonColumn = /** @class */ (function (_super) {
|
|
|
79
105
|
};
|
|
80
106
|
ButtonColumn.isColumnComponent = true;
|
|
81
107
|
ButtonColumn.contextType = GridContext;
|
|
108
|
+
ButtonColumn = __decorate([
|
|
109
|
+
observer,
|
|
110
|
+
__metadata("design:paramtypes", [Object])
|
|
111
|
+
], ButtonColumn);
|
|
82
112
|
return ButtonColumn;
|
|
83
113
|
}(ComponentBase));
|
|
84
114
|
export { ButtonColumn };
|
|
@@ -9,7 +9,7 @@ export interface INeoColumnProps<TData> extends INeoCommonColumnProps, IEditorCo
|
|
|
9
9
|
width?: number | string;
|
|
10
10
|
/** Tooltip to show in header. If you want the tooltip to show on the cells as well, use 'data-tip'. */
|
|
11
11
|
headerTooltip?: string;
|
|
12
|
-
/** Tooltip to show in cells. If you want the tooltip to show on the
|
|
12
|
+
/** Tooltip to show in cells. If you want the tooltip to show on the headers as well, use 'data-tip'. */
|
|
13
13
|
cellTooltip?: string;
|
|
14
14
|
/** Allows you to override the default allow sorting setting of the datasource, or make clicking a column heading sort a specific property. */
|
|
15
15
|
sort?: Model.IPropertyInstance | boolean;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { GridContext, GridSection } from './Grid';
|
|
3
3
|
import { Model } from '@singularsystems/neo-core';
|
|
4
|
-
import { StateObserver } from '../StateObserver';
|
|
5
4
|
import ComponentBase from '../ComponentBase';
|
|
6
5
|
interface IRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLTableRowElement>, HTMLTableRowElement> {
|
|
7
6
|
showInHeader?: boolean;
|
|
@@ -15,9 +14,8 @@ interface IRowProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLTab
|
|
|
15
14
|
onHeaderClick?: (section: GridSection, e: React.MouseEvent<HTMLTableRowElement, MouseEvent>) => void;
|
|
16
15
|
/** Class name that will not be applied to headers. */
|
|
17
16
|
bodyClassName?: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
isExpanded: boolean;
|
|
17
|
+
/** Allows overriding the tooltip shown when an item is marked for deletion. */
|
|
18
|
+
isDeletingTooltip?: string;
|
|
21
19
|
}
|
|
22
20
|
export default class Row<TData> extends ComponentBase<IRowProps> {
|
|
23
21
|
static contextType: React.Context<import("./Grid").GridContextParams<unknown>>;
|
|
@@ -27,10 +25,11 @@ export default class Row<TData> extends ComponentBase<IRowProps> {
|
|
|
27
25
|
showInHeader: boolean;
|
|
28
26
|
};
|
|
29
27
|
private setGridContext;
|
|
30
|
-
section
|
|
31
|
-
stateObserver
|
|
32
|
-
expandProperty
|
|
33
|
-
firstRowInGroup
|
|
28
|
+
private section;
|
|
29
|
+
private stateObserver;
|
|
30
|
+
private expandProperty?;
|
|
31
|
+
private firstRowInGroup;
|
|
32
|
+
private item?;
|
|
34
33
|
onFirstRender(): void;
|
|
35
34
|
componentWillUnmount(): void;
|
|
36
35
|
onExpandClick(): void;
|
|
@@ -7,6 +7,7 @@ import Column from './Column';
|
|
|
7
7
|
import NeoButton from '../Button';
|
|
8
8
|
import { StateObserver } from '../StateObserver';
|
|
9
9
|
import ComponentBase from '../ComponentBase';
|
|
10
|
+
import Tooltip from "../Tooltip";
|
|
10
11
|
var Row = /** @class */ (function (_super) {
|
|
11
12
|
__extends(Row, _super);
|
|
12
13
|
function Row(props) {
|
|
@@ -40,6 +41,9 @@ var Row = /** @class */ (function (_super) {
|
|
|
40
41
|
return { isExpanded: _this.expandProperty.value };
|
|
41
42
|
});
|
|
42
43
|
}
|
|
44
|
+
if (this.context.currentItem && this.context.currentItem.isSelfDeleting !== undefined) {
|
|
45
|
+
this.item = this.context.currentItem;
|
|
46
|
+
}
|
|
43
47
|
};
|
|
44
48
|
Row.prototype.componentWillUnmount = function () {
|
|
45
49
|
if (this.stateObserver)
|
|
@@ -52,8 +56,9 @@ var Row = /** @class */ (function (_super) {
|
|
|
52
56
|
};
|
|
53
57
|
Row.prototype.render = function () {
|
|
54
58
|
var _this = this;
|
|
59
|
+
var _a, _b;
|
|
55
60
|
_super.prototype.onRender.call(this);
|
|
56
|
-
var
|
|
61
|
+
var _c = this.props, showInHeader = _c.showInHeader, onHeaderClick = _c.onHeaderClick, bodyClassName = _c.bodyClassName, domProps = __rest(_c, ["showInHeader", "onHeaderClick", "bodyClassName"]);
|
|
57
62
|
var inBody = this.section === "body";
|
|
58
63
|
if (this.stateObserver)
|
|
59
64
|
this.stateObserver.observe();
|
|
@@ -69,6 +74,10 @@ var Row = /** @class */ (function (_super) {
|
|
|
69
74
|
if (bodyClassName) {
|
|
70
75
|
domProps.className = Utils.joinWithSpaces(domProps.className, bodyClassName);
|
|
71
76
|
}
|
|
77
|
+
if ((_a = this.item) === null || _a === void 0 ? void 0 : _a.isSelfDeleting) {
|
|
78
|
+
domProps.className = Utils.joinWithSpaces(domProps.className, "neo-item-deleting");
|
|
79
|
+
Tooltip.setDomProperties(domProps, (_b = this.props.isDeletingTooltip) !== null && _b !== void 0 ? _b : Misc.Settings.grid.isDeletingTooltip);
|
|
80
|
+
}
|
|
72
81
|
}
|
|
73
82
|
else {
|
|
74
83
|
if (onHeaderClick) {
|
|
@@ -91,6 +100,10 @@ var Row = /** @class */ (function (_super) {
|
|
|
91
100
|
Row.defaultProps = {
|
|
92
101
|
showInHeader: true
|
|
93
102
|
};
|
|
103
|
+
Row = __decorate([
|
|
104
|
+
observer,
|
|
105
|
+
__metadata("design:paramtypes", [Object])
|
|
106
|
+
], Row);
|
|
94
107
|
return Row;
|
|
95
108
|
}(ComponentBase));
|
|
96
109
|
export default Row;
|
|
@@ -12,6 +12,8 @@ interface ILayoutGridProps extends Components.IColumnCountBreakpoints {
|
|
|
12
12
|
/** Sets dynamic horizontal spacing. If this is set, normal column spacing is ignored. */
|
|
13
13
|
justifyContent?: Components.flexJustifyContent;
|
|
14
14
|
formGroupProps?: Components.IColumnWidthBreakpoints;
|
|
15
|
+
/** Arranges child elements vertically rather than horizontally. */
|
|
16
|
+
arrangeVertically?: boolean;
|
|
15
17
|
}
|
|
16
18
|
export declare class FormGroupLayoutContextParams {
|
|
17
19
|
formGroupProps?: Components.IColumnWidthBreakpoints;
|
|
@@ -15,7 +15,8 @@ var GridLayout = /** @class */ (function (_super) {
|
|
|
15
15
|
return _super !== null && _super.apply(this, arguments) || this;
|
|
16
16
|
}
|
|
17
17
|
GridLayout.prototype.render = function () {
|
|
18
|
-
var _a
|
|
18
|
+
var _a;
|
|
19
|
+
var _b = this.props, gutterSize = _b.gutterSize, verticalSpace = _b.verticalSpace, withGaps = _b.withGaps, alignItems = _b.alignItems, justifyContent = _b.justifyContent, formGroupProps = _b.formGroupProps, arrangeVertically = _b.arrangeVertically, xs = _b.xs, sm = _b.sm, md = _b.md, lg = _b.lg, xl = _b.xl, xxl = _b.xxl, domProps = __rest(_b, ["gutterSize", "verticalSpace", "withGaps", "alignItems", "justifyContent", "formGroupProps", "arrangeVertically", "xs", "sm", "md", "lg", "xl", "xxl"]);
|
|
19
20
|
var isBsPre5 = BootstrapUtils.isPre5;
|
|
20
21
|
var hasColumns = (xs || sm || md || lg || xl || xxl);
|
|
21
22
|
var columnClassName = undefined;
|
|
@@ -44,9 +45,20 @@ var GridLayout = /** @class */ (function (_super) {
|
|
|
44
45
|
containerStyle.marginTop = "-" + verticalSpace;
|
|
45
46
|
columnStyle = { marginTop: verticalSpace, marginLeft: "auto" };
|
|
46
47
|
}
|
|
47
|
-
var
|
|
48
|
+
var childItems = React.Children.toArray(this.props.children).filter(function (c) { return !!c; });
|
|
49
|
+
var childCount = childItems.length;
|
|
50
|
+
if (arrangeVertically) {
|
|
51
|
+
var maxColumns = (_a = BootstrapUtils.currentDimension(this.props)) !== null && _a !== void 0 ? _a : 2;
|
|
52
|
+
var itemsPerRow = Math.ceil(childCount / maxColumns);
|
|
53
|
+
var rows = [];
|
|
54
|
+
for (var i = 0; i < maxColumns; i++) {
|
|
55
|
+
BootstrapUtils;
|
|
56
|
+
rows.push(React.createElement("div", { key: i }, childItems.splice(0, itemsPerRow)));
|
|
57
|
+
}
|
|
58
|
+
childItems = rows;
|
|
59
|
+
}
|
|
48
60
|
return (React.createElement(FormGroupLayoutContext.Provider, { value: { formGroupProps: formGroupProps } },
|
|
49
|
-
React.createElement("div", __assign({}, domProps, { className: rowClassName, style: containerStyle }),
|
|
61
|
+
React.createElement("div", __assign({}, domProps, { className: rowClassName, style: containerStyle }), childItems.map(function (child, index) { return (child && React.createElement("div", { key: index, className: columnClassName, style: __assign(__assign({}, columnStyle), { marginLeft: childCount === 2 && index === 1 && isJustifyBetween ? "auto" : undefined }) }, child)); }))));
|
|
50
62
|
};
|
|
51
63
|
return GridLayout;
|
|
52
64
|
}(React.Component));
|
|
@@ -85,6 +85,7 @@ var neoFontAwesomeMappings = [
|
|
|
85
85
|
["angle-down", "keyboard_arrow_down"],
|
|
86
86
|
["caret-left", "arrow_left"],
|
|
87
87
|
["trash", "delete"],
|
|
88
|
+
["trash-restore", "delete_forever"],
|
|
88
89
|
["user", "person"],
|
|
89
90
|
["users", "groups"],
|
|
90
91
|
["user-shield", "admin_panel_settings"],
|
|
@@ -117,5 +118,6 @@ var neoFontAwesomeMappings = [
|
|
|
117
118
|
["greater-than-equal", ""],
|
|
118
119
|
["stamp", "approval"],
|
|
119
120
|
["circle-notch", "progress_activity"],
|
|
120
|
-
["cube", "deployed_code"]
|
|
121
|
+
["cube", "deployed_code"],
|
|
122
|
+
["paper-plane", "send"]
|
|
121
123
|
];
|
|
@@ -22,9 +22,7 @@ var RouteProvider = /** @class */ (function () {
|
|
|
22
22
|
this.staticRoutes = this.allRoutes.filter(function (c) { return c.params.length === 0; });
|
|
23
23
|
this.parameterisedRoutes = this.allRoutes
|
|
24
24
|
.filter(function (c) { return c.params.length > 0; })
|
|
25
|
-
.
|
|
26
|
-
.sortBy(function (c) { return c.order; }, "desc")
|
|
27
|
-
.map(function (c) { return c.route; });
|
|
25
|
+
.sortBy(function (c) { return c.order; }, "desc");
|
|
28
26
|
}
|
|
29
27
|
/** Gets the path the component is bound to. If the component is bound to multiple paths, the first path is returned. */
|
|
30
28
|
RouteProvider.prototype.getPathForComponent = function (component) {
|
|
@@ -108,7 +106,7 @@ var RouteProvider = /** @class */ (function () {
|
|
|
108
106
|
* Adds a unique key, and adds route parameters extracted from the component static params object.
|
|
109
107
|
*/
|
|
110
108
|
RouteProvider.prototype.processRoute = function (into, route) {
|
|
111
|
-
var processed = __assign(__assign({}, route), { path: route.path, params: [], key: this.key
|
|
109
|
+
var processed = __assign(__assign({}, route), { path: route.path, params: [], key: this.key++, order: 0 });
|
|
112
110
|
if (route instanceof MenuRoute) {
|
|
113
111
|
processed.path = route.basePath;
|
|
114
112
|
}
|
|
@@ -116,6 +114,9 @@ var RouteProvider = /** @class */ (function () {
|
|
|
116
114
|
if (processed.path.length > 1 && processed.path.endsWith("/")) {
|
|
117
115
|
processed.path = processed.path.substring(0, processed.path.length - 1);
|
|
118
116
|
}
|
|
117
|
+
if (processed.path.length > 1) {
|
|
118
|
+
processed.order = processed.path.split("/").length;
|
|
119
|
+
}
|
|
119
120
|
var viewBaseComponent = route.component;
|
|
120
121
|
if (viewBaseComponent.params) {
|
|
121
122
|
for (var param in viewBaseComponent.params) {
|
|
@@ -5,7 +5,7 @@ import { AxiosPromise } from 'axios';
|
|
|
5
5
|
import { WaitForTaskRunnerOptions } from '@singularsystems/neo-core/dist/Components/TaskRunner/TaskRunner';
|
|
6
6
|
import { BindableBase } from '@singularsystems/neo-core/dist/Model/BindableBase';
|
|
7
7
|
type VMConstructor<T> = (new (taskRunner?: ITaskRunner) => T);
|
|
8
|
-
export type valueOrLazy<T> = T | (() => T) |
|
|
8
|
+
export type valueOrLazy<T> = T | (() => T) | VMConstructor<T>;
|
|
9
9
|
export default abstract class ViewModelBase extends ModelBase implements IViewModel {
|
|
10
10
|
taskRunner: ITaskRunner;
|
|
11
11
|
private disposeHelper;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __awaiter, __extends, __generator } from "tslib";
|
|
2
|
-
import { ModelBase, Misc } from '@singularsystems/neo-core';
|
|
2
|
+
import { ModelBase, Misc, AppServices } from '@singularsystems/neo-core';
|
|
3
3
|
import { NeoReactTypes } from '../Modules/Types';
|
|
4
4
|
var ViewModelBase = /** @class */ (function (_super) {
|
|
5
5
|
__extends(ViewModelBase, _super);
|
|
@@ -106,7 +106,11 @@ var ViewModelBase = /** @class */ (function (_super) {
|
|
|
106
106
|
}
|
|
107
107
|
else if (viewModel.neo) {
|
|
108
108
|
// Constructor
|
|
109
|
-
|
|
109
|
+
var instance = new viewModel(taskRunner);
|
|
110
|
+
if (taskRunner && instance.taskRunner !== taskRunner) {
|
|
111
|
+
AppServices.ConfigService.tryShowError("".concat(viewModel.name, " must pass the task runner parameter to the base constructor."));
|
|
112
|
+
}
|
|
113
|
+
return instance;
|
|
110
114
|
}
|
|
111
115
|
else {
|
|
112
116
|
// Callback
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singularsystems/neo-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@testing-library/react": "16.2.0",
|
|
21
21
|
"@types/jest": "29.5.14",
|
|
22
22
|
"@types/memoize-one": "5.1.2",
|
|
23
|
-
"@types/node": "22.
|
|
23
|
+
"@types/node": "22.15.30",
|
|
24
24
|
"@types/react": "19.0.10",
|
|
25
25
|
"@types/react-custom-scrollbars": "4.0.13",
|
|
26
26
|
"@types/react-dom": "19.0.4",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"tslib": "2.8.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
|
-
"@singularsystems/neo-core": ">=1.
|
|
47
|
+
"@singularsystems/neo-core": ">=1.3.0",
|
|
48
48
|
"axios": ">=1.6.2",
|
|
49
49
|
"inversify": ">=5.0.1",
|
|
50
50
|
"mobx": ">=6.9.0",
|
package/styles/Card.scss
CHANGED
package/styles/Grid.scss
CHANGED
|
@@ -21,6 +21,18 @@ table.neo-grid {
|
|
|
21
21
|
right: 5px;
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
tr.neo-item-deleting {
|
|
26
|
+
td {
|
|
27
|
+
text-decoration: line-through;
|
|
28
|
+
opacity: 0.5;
|
|
29
|
+
|
|
30
|
+
.form-control {
|
|
31
|
+
text-decoration: line-through;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
.number-col {
|
|
25
37
|
text-align: right;
|
|
26
38
|
}
|