@michalrakus/x-react-web-lib 1.37.3 → 1.38.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.
@@ -1,4 +1,4 @@
1
- import { Component } from "react";
1
+ import React, { Component } from "react";
2
2
  import { XObject } from "./XObject";
3
3
  import { OperationType } from "./XUtils";
4
4
  import { XFieldOnChange, XFormComponent } from "./XFormComponent";
@@ -7,19 +7,25 @@ import { XErrorMap, XErrors } from "./XErrors";
7
7
  import { XEntity } from "../serverApi/XEntityMetadata";
8
8
  export type XOnSaveOrCancelProp = (object: XObject | null, objectChange: OperationType) => void;
9
9
  export interface XFormProps {
10
+ ref?: React.Ref<XFormBase>;
10
11
  id?: number;
11
12
  initValues?: object;
12
13
  onSaveOrCancel?: XOnSaveOrCancelProp;
13
14
  isInDialog?: boolean;
14
15
  }
15
- export declare function Form(entity: string): <T extends new (...args: any[]) => {}>(constructor: T) => {
16
+ export declare function Form(entity: string, pessimisticLocking?: boolean): <T extends new (...args: any[]) => {}>(constructor: T) => {
16
17
  new (...args: any[]): {
17
18
  entity: string;
19
+ pessimisticLocking: boolean;
18
20
  };
19
21
  } & T;
20
22
  export declare abstract class XFormBase extends Component<XFormProps> {
21
23
  entity?: string;
22
24
  xEntity: XEntity | undefined;
25
+ formDataChanged: boolean;
26
+ pessimisticLocking?: boolean;
27
+ rowLocked: boolean;
28
+ readOnly: boolean;
23
29
  fields: Set<string>;
24
30
  state: {
25
31
  object: XObject | null;
@@ -38,6 +44,7 @@ export declare abstract class XFormBase extends Component<XFormProps> {
38
44
  getEntity(): string;
39
45
  getXObject(): XObject;
40
46
  getObject(): any;
47
+ setFormDataChanged(formDataChanged: boolean): void;
41
48
  isAddRow(): boolean;
42
49
  isInDialog(): boolean;
43
50
  isTabViewUsed(): boolean;
@@ -63,6 +70,7 @@ export declare abstract class XFormBase extends Component<XFormProps> {
63
70
  onClickSave(): Promise<void>;
64
71
  onClickCancel(): void;
65
72
  openFormNull(): void;
73
+ cancelEdit(): boolean;
66
74
  validateSave(): Promise<boolean>;
67
75
  validateForm(): Promise<XErrorMap>;
68
76
  fieldValidation(): XErrorMap;
@@ -71,8 +79,10 @@ export declare abstract class XFormBase extends Component<XFormProps> {
71
79
  formReadOnly(object: XObject, field: string): boolean;
72
80
  createNewObject(): XObject;
73
81
  createNewObjectAsync(): Promise<XObject>;
82
+ loadObjectLegacy(id: number): Promise<XObject>;
74
83
  preInitForm(object: XObject, operationType: OperationType.Insert | OperationType.Update): void;
75
84
  validate(object: XObject): Promise<XErrors>;
76
85
  preSave(object: XObject): void;
77
86
  saveRow(): Promise<any>;
87
+ unlockRow(): Promise<void>;
78
88
  }
@@ -94,11 +94,13 @@ var react_1 = require("react");
94
94
  var XUtils_1 = require("./XUtils");
95
95
  var XUtilsCommon_1 = require("../serverApi/XUtilsCommon");
96
96
  var XUtilsMetadataCommon_1 = require("../serverApi/XUtilsMetadataCommon");
97
+ var XUtilsConversions_1 = require("../serverApi/XUtilsConversions");
98
+ var XLocale_1 = require("./XLocale");
97
99
  // class decorator ktory nastavuje property entity (dalo by sa to nastavovat v konstruktore ale decorator je menej ukecany)
98
100
  // ma sa pouzivat len na triedach odvodenych od XFormBase - obmedzenie som vsak nevedel nakodit
99
101
  // property sa nastavi az po zbehnuti konstruktora
100
102
  // pozor - decorator je vykopirovany do projektoveho suboru XLibItems.ts, lebo ked je umiestneny tu tak nefunguje pre class-y v projekte!
101
- function Form(entity) {
103
+ function Form(entity, pessimisticLocking) {
102
104
  // sem (mozno) moze prist registracia formu-u
103
105
  return function (constructor) {
104
106
  return /** @class */ (function (_super) {
@@ -106,6 +108,7 @@ function Form(entity) {
106
108
  function class_1() {
107
109
  var _this = _super !== null && _super.apply(this, arguments) || this;
108
110
  _this.entity = entity;
111
+ _this.pessimisticLocking = pessimisticLocking !== null && pessimisticLocking !== void 0 ? pessimisticLocking : false;
109
112
  return _this;
110
113
  }
111
114
  return class_1;
@@ -122,6 +125,10 @@ var XFormBase = /** @class */ (function (_super) {
122
125
  throw "Form cannot have both props id and initValues defined. Only one of them can be defined.";
123
126
  }
124
127
  //this.entity = props.entity; - nastavuje sa cez decorator @Form
128
+ //this.pessimisticLocking - nastavuje sa cez decorator @Form
129
+ _this.formDataChanged = false; // default
130
+ _this.rowLocked = false; // default
131
+ _this.readOnly = false; // default
125
132
  var object = null;
126
133
  // if (props.id === undefined) {
127
134
  // // add row operation
@@ -155,15 +162,14 @@ var XFormBase = /** @class */ (function (_super) {
155
162
  case 0:
156
163
  //console.log("volany XFormBase.componentDidMount()");
157
164
  // kontrola (musi byt tu, v konstruktore este property nie je nastavena)
158
- if (this.entity === undefined) {
165
+ if (this.entity === undefined || this.pessimisticLocking === undefined) {
159
166
  throw "XFormBase: Property entity is not defined - use decorator @Form.";
160
167
  }
161
168
  this.xEntity = XUtilsMetadataCommon_1.XUtilsMetadataCommon.getXEntity(this.entity);
162
169
  if (!(this.props.id !== undefined)) return [3 /*break*/, 2];
163
- return [4 /*yield*/, XUtils_1.XUtils.fetchById(this.entity, Array.from(this.fields), this.props.id)];
170
+ return [4 /*yield*/, this.loadObjectLegacy(this.props.id)];
164
171
  case 1:
165
172
  //console.log('XFormBase.componentDidMount ide nacitat objekt');
166
- //console.log(this.fields);
167
173
  object = _d.sent();
168
174
  operationType = XUtils_1.OperationType.Update;
169
175
  try {
@@ -223,6 +229,9 @@ var XFormBase = /** @class */ (function (_super) {
223
229
  XFormBase.prototype.getObject = function () {
224
230
  return this.getXObject();
225
231
  };
232
+ XFormBase.prototype.setFormDataChanged = function (formDataChanged) {
233
+ this.formDataChanged = formDataChanged;
234
+ };
226
235
  XFormBase.prototype.isAddRow = function () {
227
236
  // povodny kod
228
237
  //return this.props.id === undefined;
@@ -257,8 +266,8 @@ var XFormBase = /** @class */ (function (_super) {
257
266
  if (onChange) {
258
267
  onChange({ object: object, assocObjectChange: assocObjectChange });
259
268
  }
260
- // TODO - tu mozno treba setnut funkciu - koli moznej asynchronicite
261
269
  this.setState({ object: object, errorMap: errorMap });
270
+ this.setFormDataChanged(true);
262
271
  };
263
272
  XFormBase.prototype.onTableFieldChange = function (rowData, field, value, error, onChange, assocObjectChange) {
264
273
  var object = this.getXObject();
@@ -270,8 +279,8 @@ var XFormBase = /** @class */ (function (_super) {
270
279
  if (onChange) {
271
280
  onChange({ object: object, tableRow: rowData, assocObjectChange: assocObjectChange });
272
281
  }
273
- // TODO - tu mozno treba setnut funkciu - koli moznej asynchronicite
274
282
  this.setState({ object: object /*, errorMap: errorMap*/ });
283
+ this.setFormDataChanged(true);
275
284
  };
276
285
  /**
277
286
  * @deprecated - mal by sa pouzivat onTableFieldChange
@@ -319,8 +328,8 @@ var XFormBase = /** @class */ (function (_super) {
319
328
  else {
320
329
  rowList.push(newRow); // na koniec
321
330
  }
322
- // TODO - tu mozno treba setnut funkciu - koli moznej asynchronicite
323
331
  this.setState({ object: object });
332
+ this.setFormDataChanged(true);
324
333
  };
325
334
  XFormBase.getNextRowId = function (rowList, dataKey) {
326
335
  var e_2, _a;
@@ -352,8 +361,8 @@ var XFormBase = /** @class */ (function (_super) {
352
361
  throw "Unexpected error - element 'row' not found in 'rowList'";
353
362
  }
354
363
  rowList.splice(index, 1);
355
- // TODO - tu mozno treba setnut funkciu - koli moznej asynchronicite
356
364
  this.setState({ object: object });
365
+ this.setFormDataChanged(true);
357
366
  };
358
367
  XFormBase.getXRowTechData = function (row) {
359
368
  // ak este nemame rowTechData, tak ho vytvorime
@@ -458,12 +467,7 @@ var XFormBase = /** @class */ (function (_super) {
458
467
  });
459
468
  };
460
469
  XFormBase.prototype.onClickCancel = function () {
461
- if (this.props.onSaveOrCancel !== undefined) {
462
- this.props.onSaveOrCancel(null, XUtils_1.OperationType.None); // formular je zobrazeny v dialogu
463
- }
464
- else {
465
- this.openFormNull();
466
- }
470
+ this.cancelEdit();
467
471
  };
468
472
  XFormBase.prototype.openFormNull = function () {
469
473
  // deprecated functionality used when XFormNavigator (deprecated) used
@@ -478,6 +482,25 @@ var XFormBase = /** @class */ (function (_super) {
478
482
  console.log("Form has no onSaveOrCancel method declared.");
479
483
  }
480
484
  };
485
+ // API function called upon cancel of edit/show of the form
486
+ // also outer components call this function, e.g XFormDialog, (legacy XMenu in depaul project)
487
+ // returns false if cancel was stopped (not confirmed) by user
488
+ XFormBase.prototype.cancelEdit = function () {
489
+ // confirm cancel if data was changed
490
+ if (this.formDataChanged) {
491
+ if (!window.confirm((0, XLocale_1.xLocaleOption)('cancelEditConfirm'))) {
492
+ return false; // stops canceling editing, the form stays open (because this.props.onSaveOrCancel is not called)
493
+ }
494
+ }
495
+ this.unlockRow();
496
+ if (this.props.onSaveOrCancel !== undefined) {
497
+ this.props.onSaveOrCancel(null, XUtils_1.OperationType.None); // formular je zobrazeny v dialogu
498
+ }
499
+ else {
500
+ this.openFormNull();
501
+ }
502
+ return true;
503
+ };
481
504
  XFormBase.prototype.validateSave = function () {
482
505
  return __awaiter(this, void 0, void 0, function () {
483
506
  var xErrorMap, msg, _a, _b, xFormDataTable, _c, _d, assocToValidate, ok;
@@ -649,7 +672,7 @@ var XFormBase = /** @class */ (function (_super) {
649
672
  // (the purpose is to put the whole form to read only mode (maybe with exception a few fields))
650
673
  // if returns true for the param "field", then the field is read only, otherwise the property readOnly of the XInput* is processed
651
674
  XFormBase.prototype.formReadOnly = function (object, field) {
652
- return false;
675
+ return this.readOnly;
653
676
  };
654
677
  // this method can be overriden in subclass if needed (to set some default values for insert)
655
678
  XFormBase.prototype.createNewObject = function () {
@@ -664,6 +687,45 @@ var XFormBase = /** @class */ (function (_super) {
664
687
  });
665
688
  });
666
689
  };
690
+ // this method can be overriden in subclass if needed (custom load object)
691
+ // legacy way of loading object
692
+ XFormBase.prototype.loadObjectLegacy = function (id) {
693
+ var _a;
694
+ return __awaiter(this, void 0, void 0, function () {
695
+ var xFindRowByIdResponse, object, xFindRowByIdResponse_1;
696
+ return __generator(this, function (_b) {
697
+ switch (_b.label) {
698
+ case 0:
699
+ // in constructor, member pessimisticLocking is still not set, that's why here we add the "lockXUser.name"
700
+ if (this.pessimisticLocking) {
701
+ this.addField("lockXUser.name");
702
+ }
703
+ return [4 /*yield*/, XUtils_1.XUtils.fetchByIdWithLock(this.entity, Array.from(this.fields), id, this.pessimisticLocking)];
704
+ case 1:
705
+ xFindRowByIdResponse = _b.sent();
706
+ object = xFindRowByIdResponse.row;
707
+ if (!this.pessimisticLocking) return [3 /*break*/, 6];
708
+ if (!!xFindRowByIdResponse.lockAcquired) return [3 /*break*/, 5];
709
+ if (!window.confirm((0, XLocale_1.xLocaleOption)('pessimisticLockNotAcquired', { lockXUser: (_a = object.lockXUser) === null || _a === void 0 ? void 0 : _a.name, lockDate: (0, XUtilsConversions_1.datetimeAsUI)((0, XUtilsConversions_1.dateFromModel)(object.lockDate)) }))) return [3 /*break*/, 3];
710
+ return [4 /*yield*/, XUtils_1.XUtils.fetchByIdWithLock(this.entity, Array.from(this.fields), id, this.pessimisticLocking, true)];
711
+ case 2:
712
+ xFindRowByIdResponse_1 = _b.sent();
713
+ object = xFindRowByIdResponse_1.row;
714
+ this.rowLocked = true;
715
+ return [3 /*break*/, 4];
716
+ case 3:
717
+ // open form in read only mode
718
+ this.readOnly = true;
719
+ _b.label = 4;
720
+ case 4: return [3 /*break*/, 6];
721
+ case 5:
722
+ this.rowLocked = true;
723
+ _b.label = 6;
724
+ case 6: return [2 /*return*/, object];
725
+ }
726
+ });
727
+ });
728
+ };
667
729
  // this method can be overriden in subclass if needed (to modify/save object after read from DB and before set into the form)
668
730
  XFormBase.prototype.preInitForm = function (object, operationType) {
669
731
  };
@@ -686,6 +748,30 @@ var XFormBase = /** @class */ (function (_super) {
686
748
  });
687
749
  });
688
750
  };
751
+ // this method can be overriden in subclass if needed (custom unlock row)
752
+ XFormBase.prototype.unlockRow = function () {
753
+ return __awaiter(this, void 0, void 0, function () {
754
+ var xUnlockRowRequest;
755
+ return __generator(this, function (_a) {
756
+ switch (_a.label) {
757
+ case 0:
758
+ if (!this.rowLocked) return [3 /*break*/, 2];
759
+ xUnlockRowRequest = {
760
+ entity: this.getEntity(),
761
+ id: this.state.object[this.xEntity.idField],
762
+ lockDate: this.state.object.lockDate,
763
+ lockXUser: this.state.object.lockXUser
764
+ };
765
+ return [4 /*yield*/, XUtils_1.XUtils.post('x-unlock-row', xUnlockRowRequest)];
766
+ case 1:
767
+ _a.sent();
768
+ this.rowLocked = false;
769
+ _a.label = 2;
770
+ case 2: return [2 /*return*/];
771
+ }
772
+ });
773
+ });
774
+ };
689
775
  return XFormBase;
690
776
  }(react_1.Component));
691
777
  exports.XFormBase = XFormBase;
@@ -10,6 +10,7 @@ export type XFilterProp = XCustomFilter | ((object: any) => XCustomFilter | unde
10
10
  export interface XFormComponentProps<T> {
11
11
  form: XFormBase;
12
12
  label?: string;
13
+ labelTooltip?: string;
13
14
  tooltip?: string;
14
15
  placeholder?: string;
15
16
  desc?: string;
@@ -1,22 +1,40 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
4
24
  };
5
25
  Object.defineProperty(exports, "__esModule", { value: true });
6
26
  exports.XFormDialog = void 0;
7
- var react_1 = __importDefault(require("react"));
27
+ var react_1 = __importStar(require("react"));
8
28
  var dialog_1 = require("primereact/dialog");
9
- var XUtils_1 = require("./XUtils");
10
29
  var XFormDialog = function (props) {
11
30
  var _a;
31
+ var xFormBaseRef = (0, react_1.useRef)(null);
12
32
  var onHide = function () {
13
- if (props.dialogState.onSaveOrCancel) {
14
- props.dialogState.onSaveOrCancel(null, XUtils_1.OperationType.None);
15
- }
33
+ xFormBaseRef.current.cancelEdit();
16
34
  };
17
35
  var form = (_a = props.dialogState.form) !== null && _a !== void 0 ? _a : props.form;
18
36
  return (react_1.default.createElement(dialog_1.Dialog, { key: "dialog-form", className: "x-dialog-without-header", visible: props.dialogState.opened, onHide: onHide }, form ? react_1.default.cloneElement(form, {
19
- id: props.dialogState.id, initValues: props.dialogState.initValues, onSaveOrCancel: props.dialogState.onSaveOrCancel, isInDialog: true
37
+ ref: xFormBaseRef, id: props.dialogState.id, initValues: props.dialogState.initValues, onSaveOrCancel: props.dialogState.onSaveOrCancel, isInDialog: true
20
38
  } /*, props.form.children*/) : null));
21
39
  };
22
40
  exports.XFormDialog = XFormDialog;
@@ -1,15 +1,24 @@
1
- import React, { Component } from "react";
1
+ import React, { Component, ReactElement, RefObject } from "react";
2
+ interface XFormElement {
3
+ elem: ReactElement;
4
+ xElemRef: RefObject<unknown>;
5
+ }
2
6
  export interface XFormNavigator3Props {
3
- rootFormElement?: JSX.Element;
7
+ rootFormElement?: ReactElement;
4
8
  }
5
9
  /**
6
10
  * @deprecated use opening form in dialogs instead
7
11
  */
8
12
  export declare class XFormNavigator3 extends Component<XFormNavigator3Props> {
9
13
  state: {
10
- formElements: JSX.Element[];
14
+ formElements: XFormElement[];
11
15
  };
16
+ xRootElemRef: RefObject<unknown>;
12
17
  constructor(props: XFormNavigator3Props);
13
- openForm(newFormElement: JSX.Element | null): void;
18
+ openForm(newFormElement: ReactElement | null): void;
19
+ cancelFormEdit(): boolean;
20
+ isXFormBase(elem: ReactElement): boolean;
21
+ getAllFormElements(): XFormElement[];
14
22
  render(): React.JSX.Element;
15
23
  }
24
+ export {};
@@ -37,6 +37,17 @@ var __importStar = (this && this.__importStar) || function (mod) {
37
37
  __setModuleDefault(result, mod);
38
38
  return result;
39
39
  };
40
+ var __values = (this && this.__values) || function(o) {
41
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
42
+ if (m) return m.call(o);
43
+ if (o && typeof o.length === "number") return {
44
+ next: function () {
45
+ if (o && i >= o.length) o = void 0;
46
+ return { value: o && o[i++], done: !o };
47
+ }
48
+ };
49
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
50
+ };
40
51
  var __read = (this && this.__read) || function (o, n) {
41
52
  var m = typeof Symbol === "function" && o[Symbol.iterator];
42
53
  if (!m) return o;
@@ -65,6 +76,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
65
76
  Object.defineProperty(exports, "__esModule", { value: true });
66
77
  exports.XFormNavigator3 = void 0;
67
78
  var react_1 = __importStar(require("react"));
79
+ var XFormBase_1 = require("./XFormBase");
68
80
  /**
69
81
  * @deprecated use opening form in dialogs instead
70
82
  */
@@ -75,6 +87,7 @@ var XFormNavigator3 = /** @class */ (function (_super) {
75
87
  _this.state = {
76
88
  formElements: []
77
89
  };
90
+ _this.xRootElemRef = react_1.default.createRef();
78
91
  _this.openForm = _this.openForm.bind(_this);
79
92
  return _this;
80
93
  }
@@ -85,7 +98,7 @@ var XFormNavigator3 = /** @class */ (function (_super) {
85
98
  // shallow copy klonovanie (vytvara sa kopia referencii)
86
99
  var formElementsCloned = this.state.formElements.slice();
87
100
  if (newFormElement !== null) {
88
- formElementsCloned.push(newFormElement);
101
+ formElementsCloned.push({ elem: newFormElement, xElemRef: react_1.default.createRef() });
89
102
  }
90
103
  else {
91
104
  // user stlacil cancel/back - vratime sa na predchadzajuci formular
@@ -111,13 +124,50 @@ var XFormNavigator3 = /** @class */ (function (_super) {
111
124
  }
112
125
  });
113
126
  };
127
+ // API function - returns false if cancel was stopped (not confirmed) by user
128
+ XFormNavigator3.prototype.cancelFormEdit = function () {
129
+ var e_1, _a;
130
+ var formElements = this.getAllFormElements();
131
+ try {
132
+ // slice makes a copy
133
+ for (var _b = __values(formElements.slice().reverse()), _c = _b.next(); !_c.done; _c = _b.next()) {
134
+ var formElement = _c.value;
135
+ if (this.isXFormBase(formElement.elem)) {
136
+ if (!(formElement.xElemRef.current.cancelEdit())) {
137
+ return false;
138
+ }
139
+ }
140
+ }
141
+ }
142
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
143
+ finally {
144
+ try {
145
+ if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
146
+ }
147
+ finally { if (e_1) throw e_1.error; }
148
+ }
149
+ return true;
150
+ };
151
+ XFormNavigator3.prototype.isXFormBase = function (elem) {
152
+ var type = elem.type;
153
+ // only class components have prototype.isReactComponent
154
+ return (typeof type === "function" &&
155
+ type.prototype &&
156
+ type.prototype.isReactComponent &&
157
+ type.prototype instanceof XFormBase_1.XFormBase);
158
+ };
159
+ // helper returning all elems including root elem
160
+ XFormNavigator3.prototype.getAllFormElements = function () {
161
+ // rootFormElement can be undefined at the start of app (no form displayed)
162
+ return this.props.rootFormElement ? __spreadArray([{ elem: this.props.rootFormElement, xElemRef: this.xRootElemRef }], __read(this.state.formElements), false) : this.state.formElements;
163
+ };
114
164
  XFormNavigator3.prototype.render = function () {
115
165
  var _this = this;
116
- var formElements = this.props.rootFormElement ? __spreadArray([this.props.rootFormElement], __read(this.state.formElements), false) : this.state.formElements;
166
+ var formElements = this.getAllFormElements();
117
167
  var forms = formElements.map(function (formElement, index) {
118
168
  var displayed = (index === formElements.length - 1);
119
169
  // klonovanim elementu pridame atribut openForm={this.openForm} (nemusime tento atribut pridavat pri vytvarani elementu)
120
- var formElementCloned = react_1.default.cloneElement(formElement, { openForm: _this.openForm, displayed: displayed } /*, (formElement as any).children*/);
170
+ var formElementCloned = react_1.default.cloneElement(formElement.elem, { ref: formElement.xElemRef, openForm: _this.openForm, displayed: displayed } /*, (formElement as any).children*/);
121
171
  // prvych n - 1 komponentov skryjeme cez display: "none" a az posledny vyrenderujeme naozaj (cez display: "block")
122
172
  // TODO - do buducnosti - ak nechceme drzat stav componentu cez display: "none", staci vratit null (komponent vobec nevyrenderujeme)
123
173
  var display = (displayed ? "block" : "none");
@@ -11,4 +11,9 @@ export declare abstract class XInput<T, P extends XInputProps<T>> extends XFormC
11
11
  protected constructor(props: P);
12
12
  getField(): string;
13
13
  isNotNull(): boolean;
14
+ getTooltipsAndLabelElemId(field: string, label: string | undefined, value: string | null, labelTooltip: string | undefined, inputTooltip: string | undefined, desc: string | undefined): {
15
+ labelTooltip: string | undefined;
16
+ labelElemId: string | undefined;
17
+ inputTooltip: string | undefined;
18
+ };
14
19
  }
@@ -33,6 +33,25 @@ var XInput = /** @class */ (function (_super) {
33
33
  XInput.prototype.isNotNull = function () {
34
34
  return !this.xField.isNullable;
35
35
  };
36
+ XInput.prototype.getTooltipsAndLabelElemId = function (field, label, value, labelTooltip, inputTooltip, desc) {
37
+ if (value !== null) {
38
+ // nevidno placeholder, skusime ho umiestnit do tooltip-u
39
+ if (label !== undefined && labelTooltip === undefined) {
40
+ labelTooltip = desc;
41
+ }
42
+ else {
43
+ // nemame label alebo labelTooltip je obsadeny -> dame pripadny desc ako tooltip na input
44
+ if (inputTooltip === undefined) {
45
+ inputTooltip = desc;
46
+ }
47
+ }
48
+ }
49
+ var labelElemId = undefined;
50
+ if (labelTooltip) {
51
+ labelElemId = "".concat(field, "_label_id").replaceAll(".", "_"); // dots must be replaced, otherwise the selector does not work
52
+ }
53
+ return { labelTooltip: labelTooltip, labelElemId: labelElemId, inputTooltip: inputTooltip };
54
+ };
36
55
  return XInput;
37
56
  }(XFormComponent_1.XFormComponent));
38
57
  exports.XInput = XInput;
@@ -61,29 +61,14 @@ var XInputText = /** @class */ (function (_super) {
61
61
  XInputText.prototype.render = function () {
62
62
  var _a, _b;
63
63
  var label = this.getLabel();
64
- var value = this.getValue();
65
- var labelTooltip = undefined;
66
- var labelElemId = undefined;
67
- var inputTooltip = this.props.tooltip;
68
- if (value) {
69
- // nevidno placeholder, tak zobrazime desc ako label tooltip (ak mam label)
70
- if (label !== undefined) {
71
- labelTooltip = this.props.desc;
72
- labelElemId = "".concat(this.props.field, "_label_id");
73
- }
74
- else {
75
- // nemame label, dame pripadny desc ako tooltip na input
76
- if (inputTooltip === undefined) {
77
- inputTooltip = this.props.desc;
78
- }
79
- }
80
- }
64
+ var value = this.getValueFromObject(); // we do not use this.getValue(), because this.getValue() returns "" if the value is null
65
+ var _c = this.getTooltipsAndLabelElemId(this.props.field, label, value, this.props.labelTooltip, this.props.tooltip, this.props.desc), labelTooltip = _c.labelTooltip, labelElemId = _c.labelElemId, inputTooltip = _c.inputTooltip;
81
66
  var size = (_a = this.props.size) !== null && _a !== void 0 ? _a : this.xField.length;
82
67
  // note: style overrides size (width of the input according to character count)
83
68
  return (react_1.default.createElement("div", { className: "field grid" },
84
69
  label !== undefined ? react_1.default.createElement("label", { id: labelElemId, htmlFor: this.props.field, className: "col-fixed", style: this.getLabelStyle() }, label) : null,
85
70
  labelTooltip ? react_1.default.createElement(tooltip_1.Tooltip, { target: "#".concat(labelElemId), content: labelTooltip }) : null,
86
- react_1.default.createElement(inputtext_1.InputText, __assign({ id: this.props.field, value: value, onChange: this.onValueChange, onBlur: this.onBlur, readOnly: this.isReadOnly(), maxLength: this.xField.length, size: size, style: this.props.inputStyle }, XUtils_1.XUtils.createTooltipOrErrorProps(this.getError(), inputTooltip), { placeholder: (_b = this.props.placeholder) !== null && _b !== void 0 ? _b : this.props.desc }))));
71
+ react_1.default.createElement(inputtext_1.InputText, __assign({ id: this.props.field, value: this.getValue(), onChange: this.onValueChange, onBlur: this.onBlur, readOnly: this.isReadOnly(), maxLength: this.xField.length, size: size, style: this.props.inputStyle }, XUtils_1.XUtils.createTooltipOrErrorProps(this.getError(), inputTooltip), { placeholder: (_b = this.props.placeholder) !== null && _b !== void 0 ? _b : this.props.desc }))));
87
72
  };
88
73
  return XInputText;
89
74
  }(XInput_1.XInput));
@@ -55,10 +55,11 @@ var XInputTextarea = exports.XInputTextarea = /** @class */ (function (_super) {
55
55
  }
56
56
  };
57
57
  XInputTextarea.prototype.render = function () {
58
- var _a, _b;
58
+ var _a;
59
+ var _b, _c;
59
60
  var fieldStyle = this.props.fieldStyle;
60
61
  var labelStyle = this.getLabelStyle();
61
- var inputStyle = (_a = this.props.inputStyle) !== null && _a !== void 0 ? _a : {};
62
+ var inputStyle = (_b = this.props.inputStyle) !== null && _b !== void 0 ? _b : {};
62
63
  var cols;
63
64
  if (this.props.cols === "full") {
64
65
  cols = undefined;
@@ -86,20 +87,15 @@ var XInputTextarea = exports.XInputTextarea = /** @class */ (function (_super) {
86
87
  // aj tak sa asi vzdy bude pouzivat "full"
87
88
  cols = this.props.cols;
88
89
  }
90
+ var label = this.getLabel();
89
91
  var value = this.getValue();
90
- var labelTooltip = undefined;
91
- var labelElemId = undefined;
92
- if (value !== null) {
93
- // nevidno placeholder, tak zobrazime desc ako label tooltip
94
- labelTooltip = this.props.desc;
95
- labelElemId = "".concat(this.props.field, "_label_id");
96
- }
92
+ var labelTooltip = (_a = this.getTooltipsAndLabelElemId(this.props.field, label, value, this.props.labelTooltip, this.props.tooltip, this.props.desc), _a.labelTooltip), labelElemId = _a.labelElemId, inputTooltip = _a.inputTooltip;
97
93
  // InputTextarea renderujeme az ked mame nacitany object, lebo inac pri autoResize sa nam nenastavi spravna velkost (hodnota nie je k dispozicii pri prvom renderingu)
98
94
  return (react_1.default.createElement("div", { className: !this.labelOnTop ? 'field grid' : 'field grid x-inputtextarea-label-on-top', style: fieldStyle },
99
- react_1.default.createElement("label", { id: labelElemId, htmlFor: this.props.field, className: !this.labelOnTop ? 'col-fixed' : undefined, style: labelStyle }, this.getLabel()),
95
+ label !== undefined ? react_1.default.createElement("label", { id: labelElemId, htmlFor: this.props.field, className: !this.labelOnTop ? 'col-fixed' : undefined, style: labelStyle }, label) : null,
100
96
  labelTooltip ? react_1.default.createElement(tooltip_1.Tooltip, { target: "#".concat(labelElemId), content: labelTooltip }) : null,
101
97
  this.props.form.state.object ?
102
- react_1.default.createElement(XInputTextareaBase_1.XInputTextareaBase, { ref: this.xInputTextareaBaseRef, id: this.props.field, value: value, onChange: this.onValueChange, readOnly: this.isReadOnly(), maxLength: this.xField.length, style: inputStyle, className: this.props.inputClassName, rows: this.props.rows, cols: cols, autoResize: this.props.autoResize, error: this.getError(), tooltip: this.props.tooltip, placeholder: (_b = this.props.placeholder) !== null && _b !== void 0 ? _b : this.props.desc })
98
+ react_1.default.createElement(XInputTextareaBase_1.XInputTextareaBase, { ref: this.xInputTextareaBaseRef, id: this.props.field, value: value, onChange: this.onValueChange, readOnly: this.isReadOnly(), maxLength: this.xField.length, style: inputStyle, className: this.props.inputClassName, rows: this.props.rows, cols: cols, autoResize: this.props.autoResize, error: this.getError(), tooltip: inputTooltip, placeholder: (_c = this.props.placeholder) !== null && _c !== void 0 ? _c : this.props.desc })
103
99
  : null));
104
100
  };
105
101
  XInputTextarea.defaultProps = {
@@ -1,4 +1,5 @@
1
1
  export interface XLocaleOptions {
2
+ searchInAllFields?: string;
2
3
  filter?: string;
3
4
  resetTable?: string;
4
5
  addRow?: string;
@@ -12,6 +13,9 @@ export interface XLocaleOptions {
12
13
  removeRowFailed?: string;
13
14
  save?: string;
14
15
  cancel?: string;
16
+ optimisticLockFailed?: string;
17
+ formRemoveRowConfirm?: string;
18
+ xAutoComplete?: string;
15
19
  expRowCount?: string;
16
20
  expExportType?: string;
17
21
  expCreateHeaderLine?: string;
@@ -10,6 +10,7 @@ import { XTableFieldReadOnlyProp } from "./XFormDataTable2";
10
10
  import { SelectItem } from "primereact/selectitem";
11
11
  import { XLazyDataTableRef } from "./XLazyDataTable/XLazyDataTable";
12
12
  import { XOnSaveOrCancelProp } from "./XFormBase";
13
+ import { XFindRowByIdResponse } from "../serverApi/x-lib-api";
13
14
  export declare enum OperationType {
14
15
  None = 0,
15
16
  Insert = 1,
@@ -82,6 +83,8 @@ export declare class XUtils {
82
83
  static fetchBasicAuthBasic(path: string, headers: any, body: any, usePublicToken?: boolean | XToken): Promise<Response>;
83
84
  static fetchBasic(path: string, headers: any, body: any, usePublicToken?: boolean | XToken): Promise<Response>;
84
85
  static fetchById(entity: string, fields: string[], id: number): Promise<any>;
86
+ static fetchByIdWithLock(entity: string, fields: string[], id: number, lockRow: boolean, overwriteLock?: boolean): Promise<XFindRowByIdResponse>;
87
+ static fetchByIdWithLockBase(path: string, entity: string, fields: string[], id: number, lockRow: boolean, overwriteLock?: boolean): Promise<XFindRowByIdResponse>;
85
88
  static setXToken(xToken: XToken | null): void;
86
89
  static getXToken(): XToken | null;
87
90
  static getAccessToken(): Promise<string>;
@@ -93,6 +96,7 @@ export declare class XUtils {
93
96
  * @param envVar
94
97
  */
95
98
  static getEnvVarValue(envVarEnum: XEnvVar): string;
99
+ static getEnvVarValueBase(envVarEnum: string): string;
96
100
  static removeRow(entity: string, row: any): Promise<void>;
97
101
  static isReadOnly(path: string, readOnlyInit?: boolean): boolean;
98
102
  static isReadOnlyTableField(path: string | undefined, readOnly: XTableFieldReadOnlyProp | undefined, object: XObject | null, tableRow: any): boolean;
@@ -461,7 +461,29 @@ var XUtils = exports.XUtils = /** @class */ (function () {
461
461
  });
462
462
  };
463
463
  XUtils.fetchById = function (entity, fields, id) {
464
- return XUtils.fetchOne('findRowById', { entity: entity, fields: fields, id: id });
464
+ return __awaiter(this, void 0, void 0, function () {
465
+ var response;
466
+ return __generator(this, function (_a) {
467
+ switch (_a.label) {
468
+ case 0: return [4 /*yield*/, XUtils.fetchByIdWithLock(entity, fields, id, false)];
469
+ case 1:
470
+ response = _a.sent();
471
+ return [2 /*return*/, response.row];
472
+ }
473
+ });
474
+ });
475
+ };
476
+ // more general function - can also lock the row
477
+ XUtils.fetchByIdWithLock = function (entity, fields, id, lockRow, overwriteLock) {
478
+ return XUtils.fetchByIdWithLockBase('x-find-row-by-id', entity, fields, id, lockRow, overwriteLock);
479
+ };
480
+ XUtils.fetchByIdWithLockBase = function (path, entity, fields, id, lockRow, overwriteLock) {
481
+ var _a;
482
+ var request = { entity: entity, fields: fields, id: id };
483
+ if (lockRow) {
484
+ request = __assign(__assign({}, request), { lockDate: new Date(), lockXUser: (_a = XUtils.getXToken()) === null || _a === void 0 ? void 0 : _a.xUser, overwriteLock: overwriteLock !== null && overwriteLock !== void 0 ? overwriteLock : false });
485
+ }
486
+ return XUtils.fetchOne(path, request);
465
487
  };
466
488
  XUtils.setXToken = function (xToken) {
467
489
  XUtils.xToken = xToken;
@@ -515,6 +537,9 @@ var XUtils = exports.XUtils = /** @class */ (function () {
515
537
  * @param envVar
516
538
  */
517
539
  XUtils.getEnvVarValue = function (envVarEnum) {
540
+ return XUtils.getEnvVarValueBase(envVarEnum);
541
+ };
542
+ XUtils.getEnvVarValueBase = function (envVarEnum) {
518
543
  var value = process.env[envVarEnum];
519
544
  if (value === undefined) {
520
545
  throw "Environment variable ".concat(envVarEnum, " - value not found. Check configuration file .env*");
@@ -591,7 +616,7 @@ var XUtils = exports.XUtils = /** @class */ (function () {
591
616
  }
592
617
  else if (e.xResponseErrorBody.exceptionName === 'OptimisticLockVersionMismatchError') {
593
618
  // better error message for optimistic locking
594
- msg += "The optimistic lock failed, someone else has changed the row during the editation. Sorry, you have to cancel the editation and start the editation again.";
619
+ msg += (0, XLocale_1.xLocaleOption)('optimisticLockFailed');
595
620
  }
596
621
  else {
597
622
  msg += e.message + XUtilsCommon_1.XUtilsCommon.newLine;
@@ -16,6 +16,9 @@
16
16
  "ok": "Ok",
17
17
  "save": "Save",
18
18
  "cancel": "Cancel",
19
+ "cancelEditConfirm": "Are you sure to cancel editing? All changes will be lost.",
20
+ "pessimisticLockNotAcquired": "The row is being edited by the user {lockXUser} from {lockDate}\nDo you still want to edit the row? (The choice Cancel will open the form in read only mode).",
21
+ "optimisticLockFailed": "Someone else has changed the row during the editation. Sorry, you have to cancel the editation and start the editation again.",
19
22
  "formRemoveRowConfirm": "Are you sure to remove the row?",
20
23
  "xAutoComplete": "Autocomplete",
21
24
  "expRowCount": "Row count",
@@ -37,6 +37,8 @@ export declare class XUtilsCommon {
37
37
  * @param idField
38
38
  */
39
39
  static arrayIntersect<T>(array1: T[], array2: T[], idField: string): T[];
40
+ static numberArraySymmetricDiff(idListA: number[], idListB: number[]): number[];
41
+ static numberArrayUnion(idListA: number[], idListB: number[]): number[];
40
42
  static createCustomFilter(filter: string | undefined | null): XCustomFilterItem | undefined;
41
43
  static createCustomFilterItems(customFilter: XCustomFilter | undefined): XCustomFilterItem[] | undefined;
42
44
  static createMultiSortMeta(sortField: string | DataTableSortMeta[] | undefined): DataTableSortMeta[] | undefined;
@@ -369,6 +369,47 @@ var XUtilsCommon = exports.XUtilsCommon = /** @class */ (function () {
369
369
  }
370
370
  return array1.filter(function (item) { return item && array2IdSet.has(item[idField]); });
371
371
  };
372
+ // returns ids that are in idListA or in idListB but not in both lists
373
+ XUtilsCommon.numberArraySymmetricDiff = function (idListA, idListB) {
374
+ var e_6, _a, e_7, _b;
375
+ var setA = new Set(idListA);
376
+ var setB = new Set(idListB);
377
+ var result = [];
378
+ try {
379
+ for (var setA_1 = __values(setA), setA_1_1 = setA_1.next(); !setA_1_1.done; setA_1_1 = setA_1.next()) {
380
+ var id = setA_1_1.value;
381
+ if (!setB.has(id)) {
382
+ result.push(id);
383
+ }
384
+ }
385
+ }
386
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
387
+ finally {
388
+ try {
389
+ if (setA_1_1 && !setA_1_1.done && (_a = setA_1.return)) _a.call(setA_1);
390
+ }
391
+ finally { if (e_6) throw e_6.error; }
392
+ }
393
+ try {
394
+ for (var setB_1 = __values(setB), setB_1_1 = setB_1.next(); !setB_1_1.done; setB_1_1 = setB_1.next()) {
395
+ var id = setB_1_1.value;
396
+ if (!setA.has(id)) {
397
+ result.push(id);
398
+ }
399
+ }
400
+ }
401
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
402
+ finally {
403
+ try {
404
+ if (setB_1_1 && !setB_1_1.done && (_b = setB_1.return)) _b.call(setB_1);
405
+ }
406
+ finally { if (e_7) throw e_7.error; }
407
+ }
408
+ return result;
409
+ };
410
+ XUtilsCommon.numberArrayUnion = function (idListA, idListB) {
411
+ return Array.from(new Set(__spreadArray(__spreadArray([], __read(idListA), false), __read(idListB), false))); // new Set(...) removes duplicates automatically
412
+ };
372
413
  // ************* XCustomFilter/XCustomFilterItem/DataTableSortMeta **************
373
414
  // pomocna metodka - aby sme nemuseli v kode vypisovat {where: <filter>, params: {}}
374
415
  XUtilsCommon.createCustomFilter = function (filter) {
@@ -415,7 +456,7 @@ var XUtilsCommon = exports.XUtilsCommon = /** @class */ (function () {
415
456
  };
416
457
  // pomocna metodka
417
458
  XUtilsCommon.filterAnd = function () {
418
- var e_6, _a;
459
+ var e_8, _a;
419
460
  var filters = [];
420
461
  for (var _i = 0; _i < arguments.length; _i++) {
421
462
  filters[_i] = arguments[_i];
@@ -432,12 +473,12 @@ var XUtilsCommon = exports.XUtilsCommon = /** @class */ (function () {
432
473
  }
433
474
  }
434
475
  }
435
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
476
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
436
477
  finally {
437
478
  try {
438
479
  if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
439
480
  }
440
- finally { if (e_6) throw e_6.error; }
481
+ finally { if (e_8) throw e_8.error; }
441
482
  }
442
483
  }
443
484
  return customFilterItemsResult;
@@ -10,3 +10,27 @@ export interface XtRunDocTemplateRequest {
10
10
  rowId: number;
11
11
  xUser?: XUser;
12
12
  }
13
+ export interface XFindRowByIdRequest {
14
+ entity: string;
15
+ fields: string[];
16
+ id: number;
17
+ lockDate?: Date;
18
+ lockXUser?: XUser;
19
+ overwriteLock?: boolean;
20
+ }
21
+ export interface XFindRowByIdResponse {
22
+ row: any;
23
+ lockAcquired?: boolean;
24
+ }
25
+ export interface XUnlockRowRequest {
26
+ entity: string;
27
+ id: number;
28
+ lockDate: Date;
29
+ lockXUser: XUser;
30
+ }
31
+ export interface XLockRowRequest {
32
+ entity: string;
33
+ id: number;
34
+ lockDate: Date;
35
+ lockXUser: XUser;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michalrakus/x-react-web-lib",
3
- "version": "1.37.3",
3
+ "version": "1.38.1",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "clean": "rimraf lib",