@singularsystems/neo-react 1.0.13 → 1.0.15
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/dist/ReactComponents/Button.d.ts +3 -1
- package/dist/ReactComponents/Button.js +8 -1
- package/dist/ReactComponents/Form.d.ts +5 -3
- package/dist/ReactComponents/Form.js +90 -59
- package/dist/ReactComponents/Grid/StickyTableHeader.d.ts +4 -1
- package/dist/ReactComponents/Grid/StickyTableHeader.js +23 -11
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import ComponentBase from './ComponentBase';
|
|
3
3
|
import { Components } from '@singularsystems/neo-core';
|
|
4
|
-
type IButtonProps = Components.IButtonOptions & Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & React.RefAttributes<Button>;
|
|
4
|
+
type IButtonProps = Components.IButtonOptions & Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref" | "onClick"> & React.RefAttributes<Button>;
|
|
5
5
|
interface IButtonState {
|
|
6
6
|
isExpanded: boolean;
|
|
7
7
|
}
|
|
@@ -13,12 +13,14 @@ export default class Button extends ComponentBase<IButtonProps, IButtonState> {
|
|
|
13
13
|
menuDom: React.RefObject<HTMLDivElement>;
|
|
14
14
|
private iconFactory;
|
|
15
15
|
private windowClickHandler;
|
|
16
|
+
private positionHelper?;
|
|
16
17
|
constructor(props: IButtonProps);
|
|
17
18
|
showMenu(): void;
|
|
18
19
|
hideMenu(): void;
|
|
19
20
|
private menuItemClicked;
|
|
20
21
|
private windowClicked;
|
|
21
22
|
afterRender(): void;
|
|
23
|
+
componentWillUnmount(): void;
|
|
22
24
|
render(): JSX.Element;
|
|
23
25
|
}
|
|
24
26
|
export {};
|
|
@@ -25,10 +25,12 @@ var Button = /** @class */ (function (_super) {
|
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
Button.prototype.hideMenu = function () {
|
|
28
|
+
var _a;
|
|
28
29
|
if (this.state.isExpanded) {
|
|
29
30
|
this.setState({ isExpanded: false });
|
|
30
31
|
}
|
|
31
32
|
window.removeEventListener("click", this.windowClickHandler);
|
|
33
|
+
(_a = this.positionHelper) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
32
34
|
};
|
|
33
35
|
Button.prototype.menuItemClicked = function (e, data, userEvent) {
|
|
34
36
|
this.hideMenu();
|
|
@@ -48,9 +50,14 @@ var Button = /** @class */ (function (_super) {
|
|
|
48
50
|
};
|
|
49
51
|
Button.prototype.afterRender = function () {
|
|
50
52
|
if (this.btnGroupDom.current && this.menuDom.current) {
|
|
51
|
-
|
|
53
|
+
this.menuDom.current.style.position = "fixed"; // override bootstrap style.
|
|
54
|
+
this.positionHelper = new Components.FixedPositionHelper(this.btnGroupDom.current, this.menuDom.current, this.props.menuAlignment === "right");
|
|
52
55
|
}
|
|
53
56
|
};
|
|
57
|
+
Button.prototype.componentWillUnmount = function () {
|
|
58
|
+
var _a;
|
|
59
|
+
(_a = this.positionHelper) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
60
|
+
};
|
|
54
61
|
Button.prototype.render = function () {
|
|
55
62
|
var _this = this;
|
|
56
63
|
var _a;
|
|
@@ -3,15 +3,17 @@ import { Components, Model } from '@singularsystems/neo-core';
|
|
|
3
3
|
interface IFormProps<T extends Model.IModelBase> extends Omit<React.FormHTMLAttributes<HTMLElement>, "children">, Components.IFormOptions<T> {
|
|
4
4
|
/**
|
|
5
5
|
* Called when the form is submitted, but the model is not valid.
|
|
6
|
-
* The
|
|
6
|
+
* The firstSubmit parameter will be true if this is the first submit attempt for the model.
|
|
7
7
|
*/
|
|
8
|
-
onInvalid?: (e
|
|
8
|
+
onInvalid?: (e?: React.FormEvent, 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>> {
|
|
12
12
|
private reactionDisposer?;
|
|
13
|
-
private
|
|
13
|
+
private getModelOrModels;
|
|
14
14
|
private onSubmit;
|
|
15
|
+
/** Checks if the model is valid, waiting for any async rules to complete. Returns true if the model is valid. */
|
|
16
|
+
validateAsync(): Promise<boolean>;
|
|
15
17
|
private disposeReaction;
|
|
16
18
|
componentWillUnmount(): void;
|
|
17
19
|
render(): React.JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __assign, __decorate, __extends, __rest } from "tslib";
|
|
1
|
+
import { __assign, __awaiter, __decorate, __extends, __generator, __rest } from "tslib";
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { observer, Observer } from 'mobx-react';
|
|
4
4
|
import { Validation, Model, ModalUtils, Misc } from '@singularsystems/neo-core';
|
|
@@ -8,59 +8,100 @@ import { FormContextProvider } from './FormContext';
|
|
|
8
8
|
var Form = /** @class */ (function (_super) {
|
|
9
9
|
__extends(Form, _super);
|
|
10
10
|
function Form() {
|
|
11
|
-
|
|
12
|
-
_this.firstSubmit = true;
|
|
13
|
-
return _this;
|
|
11
|
+
return _super !== null && _super.apply(this, arguments) || this;
|
|
14
12
|
}
|
|
15
|
-
Form.prototype.
|
|
16
|
-
var
|
|
17
|
-
var _a;
|
|
18
|
-
e.preventDefault();
|
|
19
|
-
e.stopPropagation();
|
|
20
|
-
this.disposeReaction();
|
|
21
|
-
var canSubmit = true;
|
|
22
|
-
// TODO: if the model and none of its children have blurred, justValidated should be true
|
|
23
|
-
var justValidated = this.props.validationDisplayMode === Validation.DisplayMode.AfterSubmit && this.firstSubmit;
|
|
24
|
-
this.firstSubmit = false;
|
|
13
|
+
Form.prototype.getModelOrModels = function () {
|
|
14
|
+
var model = this.props.model;
|
|
25
15
|
if (model) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
// Allow binding to ObservableProperty
|
|
17
|
+
if (model.attachedTo && !(model instanceof Array)) {
|
|
18
|
+
return model.attachedTo;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return model;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
};
|
|
26
|
+
Form.prototype.onSubmit = function (e) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
28
|
+
var models, isFirstSubmit, isValid;
|
|
29
|
+
return __generator(this, function (_a) {
|
|
30
|
+
switch (_a.label) {
|
|
31
|
+
case 0:
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
models = this.getModelOrModels();
|
|
35
|
+
isFirstSubmit = true;
|
|
36
|
+
if (models) {
|
|
37
|
+
Model.GraphTraverser.traverseItems(models, new Model.GraphTraverser.RecursionState(), function (item, state) {
|
|
38
|
+
if (item.validator.forceShowErrors) {
|
|
39
|
+
isFirstSubmit = false;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
item.validator.forceShowErrors = true;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return [4 /*yield*/, this.validateAsync()];
|
|
47
|
+
case 1:
|
|
48
|
+
isValid = _a.sent();
|
|
49
|
+
if (isValid) {
|
|
50
|
+
if (this.props.onSubmit)
|
|
51
|
+
this.props.onSubmit(e);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (this.props.onInvalid)
|
|
55
|
+
this.props.onInvalid(e, isFirstSubmit);
|
|
56
|
+
}
|
|
57
|
+
return [2 /*return*/];
|
|
34
58
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
/** Checks if the model is valid, waiting for any async rules to complete. Returns true if the model is valid. */
|
|
63
|
+
Form.prototype.validateAsync = function () {
|
|
64
|
+
var _this = this;
|
|
65
|
+
return new Promise(function (resolve, reject) {
|
|
66
|
+
var _a;
|
|
67
|
+
_this.disposeReaction();
|
|
68
|
+
var isValid = true;
|
|
69
|
+
var modelOrModels = _this.getModelOrModels();
|
|
70
|
+
var models = modelOrModels === undefined ? [] : (modelOrModels instanceof Array ? modelOrModels : [modelOrModels]);
|
|
71
|
+
if (models) {
|
|
72
|
+
var allValid_1 = function () { return models.every(function (c) { return c.isValid; }); };
|
|
73
|
+
var someBusy = function () { return models.some(function (c) { return c.validator.isBusy; }); };
|
|
74
|
+
if (!allValid_1()) {
|
|
75
|
+
isValid = false;
|
|
76
|
+
var codeModal_1 = null;
|
|
77
|
+
if (_this.props.showSummaryModal || _this.props.invalidSummaryText || _this.props.invalidSummaryTitle) {
|
|
78
|
+
var onClose = _this.disposeReaction.bind(_this);
|
|
79
|
+
var closeButton = (_a = Misc.Settings.form.validationModalCloseButton) !== null && _a !== void 0 ? _a : { text: "Ok" };
|
|
80
|
+
if (closeButton) {
|
|
81
|
+
closeButton.onClick = onClose;
|
|
82
|
+
}
|
|
83
|
+
codeModal_1 = ModalUtils.showModal(_this.props.invalidSummaryTitle || "Invalid data", React.createElement(Observer, null, function () {
|
|
84
|
+
return React.createElement("div", null,
|
|
85
|
+
_this.props.invalidSummaryText || "Cannot continue due to the following issues:",
|
|
86
|
+
React.createElement(ValidationSummary, { graph: Validation.getValidationSummary(modelOrModels), hideTopLevelTitle: !(modelOrModels instanceof Array) }));
|
|
87
|
+
}), { onClose: onClose, closeButton: closeButton });
|
|
88
|
+
}
|
|
89
|
+
if (someBusy()) {
|
|
90
|
+
// Async rule is busy, listen for when isBusy changes to false.
|
|
91
|
+
_this.reactionDisposer = reaction(someBusy, function () {
|
|
92
|
+
_this.disposeReaction();
|
|
93
|
+
isValid = allValid_1();
|
|
94
|
+
if (codeModal_1 !== null && isValid) {
|
|
95
|
+
codeModal_1.close();
|
|
96
|
+
}
|
|
97
|
+
resolve(isValid);
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
45
100
|
}
|
|
46
|
-
var codeModal_1 = ModalUtils.showModal(this.props.invalidSummaryTitle || "Invalid data", React.createElement(Observer, null, function () {
|
|
47
|
-
return React.createElement("div", null,
|
|
48
|
-
_this.props.invalidSummaryText || "Cannot continue due to the following issues:",
|
|
49
|
-
React.createElement(ValidationSummary, { graph: Validation.getValidationSummary(model), hideTopLevelTitle: !(model instanceof Array) }));
|
|
50
|
-
}), { onClose: onClose, closeButton: closeButton });
|
|
51
|
-
// Listen for when isValid changes to true, in case there is an async rule running.
|
|
52
|
-
this.reactionDisposer = reaction(function () { return model.isValid; }, function () {
|
|
53
|
-
_this.disposeReaction();
|
|
54
|
-
codeModal_1.close();
|
|
55
|
-
if (model.isValid && _this.props.onSubmit)
|
|
56
|
-
_this.props.onSubmit(e);
|
|
57
|
-
});
|
|
58
101
|
}
|
|
59
102
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.props.onSubmit(e);
|
|
63
|
-
}
|
|
103
|
+
resolve(isValid);
|
|
104
|
+
});
|
|
64
105
|
};
|
|
65
106
|
Form.prototype.disposeReaction = function () {
|
|
66
107
|
if (this.reactionDisposer) {
|
|
@@ -74,17 +115,7 @@ var Form = /** @class */ (function (_super) {
|
|
|
74
115
|
Form.prototype.render = function () {
|
|
75
116
|
var _this = this;
|
|
76
117
|
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, native = __rest(_a, ["validationDisplayMode", "allReadOnly", "allDisabled", "model", "showSummaryModal", "invalidSummaryTitle", "invalidSummaryText"]);
|
|
77
|
-
|
|
78
|
-
if (model) {
|
|
79
|
-
// Allow binding to ObservableProperty
|
|
80
|
-
if (model.attachedTo && !(model instanceof Array)) {
|
|
81
|
-
formModel = model.attachedTo;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
formModel = model;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return (React.createElement("form", __assign({}, native, { onSubmit: function (e) { return _this.onSubmit(e, formModel); } }),
|
|
118
|
+
return (React.createElement("form", __assign({}, native, { onSubmit: function (e) { return _this.onSubmit(e); } }),
|
|
88
119
|
React.createElement(FormContextProvider, { validationDisplayMode: validationDisplayMode !== null && validationDisplayMode !== void 0 ? validationDisplayMode : Misc.Settings.form.validationDisplayMode, allReadOnly: allReadOnly, allDisabled: allDisabled }, (this.props.children instanceof Function) ? this.props.children(model, model.meta) : this.props.children)));
|
|
89
120
|
};
|
|
90
121
|
Form = __decorate([
|
|
@@ -11,7 +11,10 @@ export declare class StickyTableHeader {
|
|
|
11
11
|
* @param fixedElement A fixed element at the top of the screen to use as an anchor, rather than the top of the screen itself.
|
|
12
12
|
*/
|
|
13
13
|
constructor(table: HTMLTableElement, fixedElement?: HTMLElement | undefined);
|
|
14
|
-
|
|
14
|
+
/** Parent element that may scroll, requiring a position update. */
|
|
15
|
+
private scrollElement?;
|
|
16
|
+
/** Fixed height parent element that the grid scrolls within. */
|
|
17
|
+
private relativeParent?;
|
|
15
18
|
private header;
|
|
16
19
|
private headerCopy?;
|
|
17
20
|
private headerHeight;
|
|
@@ -16,12 +16,17 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
16
16
|
throw Error("Table must have a header section.");
|
|
17
17
|
}
|
|
18
18
|
this.header = table.tHead;
|
|
19
|
-
this.
|
|
19
|
+
this.scrollElement = StickyTableHeader.getParentScrollElement(table);
|
|
20
|
+
if (this.scrollElement) {
|
|
21
|
+
if (this.scrollElement.style.height || this.scrollElement.style.maxHeight) {
|
|
22
|
+
this.relativeParent = this.scrollElement;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
20
25
|
this.handleScroll = this.handleScroll.bind(this);
|
|
21
26
|
this.onPossibleContentChange = this.onPossibleContentChange.bind(this);
|
|
22
27
|
document.addEventListener("scroll", this.handleScroll);
|
|
23
|
-
if (this.
|
|
24
|
-
this.
|
|
28
|
+
if (this.scrollElement) {
|
|
29
|
+
this.scrollElement.addEventListener("scroll", this.handleScroll);
|
|
25
30
|
}
|
|
26
31
|
table.addEventListener("keydown", this.onPossibleContentChange);
|
|
27
32
|
table.addEventListener("mouseup", this.onPossibleContentChange);
|
|
@@ -31,10 +36,10 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
31
36
|
// otherwise if there is a fixed (non scrolling) element at the top of the page, then the headers must appear below that.
|
|
32
37
|
// otherwise the headers will stick to the top of the screen.
|
|
33
38
|
var fixedElement = null;
|
|
34
|
-
if (!this.
|
|
39
|
+
if (!this.relativeParent && this.fixedElement && getComputedStyle(this.fixedElement).position === "fixed") {
|
|
35
40
|
fixedElement = this.fixedElement;
|
|
36
41
|
}
|
|
37
|
-
var topBoundary = this.
|
|
42
|
+
var topBoundary = this.relativeParent ? this.relativeParent.getBoundingClientRect().top : (fixedElement ? fixedElement.getBoundingClientRect().bottom : 0);
|
|
38
43
|
var tableBounds = this.table.getBoundingClientRect();
|
|
39
44
|
var tbodyBounds = this.table.tBodies[this.table.tBodies.length - 1].getBoundingClientRect();
|
|
40
45
|
if (tableBounds.top < topBoundary && tbodyBounds.bottom - this.headerHeight > topBoundary) {
|
|
@@ -50,6 +55,11 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
50
55
|
}
|
|
51
56
|
this.header.style.left = tableBounds.left + "px";
|
|
52
57
|
this.header.style.top = topBoundary + "px";
|
|
58
|
+
if (this.scrollElement) {
|
|
59
|
+
var left = this.scrollElement.scrollLeft;
|
|
60
|
+
var width = this.scrollElement.clientWidth + left;
|
|
61
|
+
this.header.style.clipPath = "rect(0px ".concat(width, "px 100% ").concat(left, "px)");
|
|
62
|
+
}
|
|
53
63
|
}
|
|
54
64
|
else {
|
|
55
65
|
this.setColumnWidths(true);
|
|
@@ -59,6 +69,7 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
59
69
|
StickyTableHeader.prototype.removeCopy = function () {
|
|
60
70
|
if (this.headerCopy) {
|
|
61
71
|
this.header.classList.remove("neo-grid-fixed-header");
|
|
72
|
+
this.header.style.clipPath = "";
|
|
62
73
|
this.table.removeChild(this.headerCopy);
|
|
63
74
|
this.headerCopy = undefined;
|
|
64
75
|
}
|
|
@@ -81,8 +92,8 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
81
92
|
};
|
|
82
93
|
StickyTableHeader.prototype.dispose = function () {
|
|
83
94
|
document.removeEventListener("scroll", this.handleScroll);
|
|
84
|
-
if (this.
|
|
85
|
-
this.
|
|
95
|
+
if (this.scrollElement) {
|
|
96
|
+
this.scrollElement.removeEventListener("scroll", this.handleScroll);
|
|
86
97
|
}
|
|
87
98
|
this.table.removeEventListener("keydown", this.onPossibleContentChange);
|
|
88
99
|
this.table.removeEventListener("mouseup", this.onPossibleContentChange);
|
|
@@ -92,10 +103,11 @@ var StickyTableHeader = /** @class */ (function () {
|
|
|
92
103
|
* Returns the first parent element that has a scrollbar.
|
|
93
104
|
*/
|
|
94
105
|
StickyTableHeader.getParentScrollElement = function (element) {
|
|
95
|
-
var current = element;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
106
|
+
var current = element.parentElement;
|
|
107
|
+
var depth = 0;
|
|
108
|
+
while (current && depth < 5) {
|
|
109
|
+
var computedStyle = window.getComputedStyle(current);
|
|
110
|
+
if (computedStyle.overflowX === "auto" || computedStyle.overflowX === "scroll" || computedStyle.overflowY === "auto" || computedStyle.overflowY === "scroll") {
|
|
99
111
|
return current;
|
|
100
112
|
}
|
|
101
113
|
current = current.parentElement;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singularsystems/neo-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typescript": "5.4.2"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@singularsystems/neo-core": "1.0.
|
|
34
|
+
"@singularsystems/neo-core": "1.0.12",
|
|
35
35
|
"@types/rc-slider": "^8.6.5",
|
|
36
36
|
"@types/react-color": "^3.0.1",
|
|
37
37
|
"axios": "1.6.7",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"tslib": "2.5.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@singularsystems/neo-core": ">=1.0.
|
|
52
|
+
"@singularsystems/neo-core": ">=1.0.12",
|
|
53
53
|
"axios": ">=1.6.2",
|
|
54
54
|
"inversify": ">=5.0.1",
|
|
55
55
|
"mobx": ">=6.9.0",
|