tsgrid-ui 2.7.0 → 2.9.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/dist/base.d.ts +148 -0
  3. package/dist/base.es6.js +11 -0
  4. package/dist/base.es6.js.map +1 -0
  5. package/dist/chunks/chunk-26XP2XU3.js +1795 -0
  6. package/dist/chunks/chunk-26XP2XU3.js.map +1 -0
  7. package/dist/chunks/chunk-3NYH6545.js +2423 -0
  8. package/dist/chunks/chunk-3NYH6545.js.map +1 -0
  9. package/dist/chunks/chunk-BIB3X2TW.js +1638 -0
  10. package/dist/chunks/chunk-BIB3X2TW.js.map +1 -0
  11. package/dist/chunks/chunk-DXZJHS4M.js +1283 -0
  12. package/dist/chunks/chunk-DXZJHS4M.js.map +1 -0
  13. package/dist/chunks/chunk-EVZMMVXO.js +1212 -0
  14. package/dist/chunks/chunk-EVZMMVXO.js.map +1 -0
  15. package/dist/chunks/chunk-GJD5NFWQ.js +2305 -0
  16. package/dist/chunks/chunk-GJD5NFWQ.js.map +1 -0
  17. package/dist/chunks/chunk-IYF3Q7GX.js +127 -0
  18. package/dist/chunks/chunk-IYF3Q7GX.js.map +1 -0
  19. package/dist/chunks/chunk-OFASTA2A.js +2980 -0
  20. package/dist/chunks/chunk-OFASTA2A.js.map +1 -0
  21. package/dist/chunks/chunk-OMLGN735.js +677 -0
  22. package/dist/chunks/chunk-OMLGN735.js.map +1 -0
  23. package/dist/chunks/chunk-WKSLGUB3.js +1127 -0
  24. package/dist/chunks/chunk-WKSLGUB3.js.map +1 -0
  25. package/dist/chunks/chunk-YBY52G2U.js +849 -0
  26. package/dist/chunks/chunk-YBY52G2U.js.map +1 -0
  27. package/dist/field.d.ts +329 -0
  28. package/dist/field.es6.js +11 -0
  29. package/dist/field.es6.js.map +1 -0
  30. package/dist/form.d.ts +162 -0
  31. package/dist/form.es6.js +14 -0
  32. package/dist/form.es6.js.map +1 -0
  33. package/dist/layout.d.ts +108 -0
  34. package/dist/layout.es6.js +13 -0
  35. package/dist/layout.es6.js.map +1 -0
  36. package/dist/locale.d.ts +30 -0
  37. package/dist/locale.es6.js +7 -0
  38. package/dist/locale.es6.js.map +1 -0
  39. package/dist/metafile-esm.json +1 -0
  40. package/dist/popup.d.ts +92 -0
  41. package/dist/popup.es6.js +18 -0
  42. package/dist/popup.es6.js.map +1 -0
  43. package/dist/query-CKGg5Ugv.d.ts +81 -0
  44. package/dist/sidebar.d.ts +138 -0
  45. package/dist/sidebar.es6.js +11 -0
  46. package/dist/sidebar.es6.js.map +1 -0
  47. package/dist/tabs.d.ts +63 -0
  48. package/dist/tabs.es6.js +11 -0
  49. package/dist/tabs.es6.js.map +1 -0
  50. package/dist/toolbar.d.ts +97 -0
  51. package/dist/toolbar.es6.js +11 -0
  52. package/dist/toolbar.es6.js.map +1 -0
  53. package/dist/tooltip.d.ts +322 -0
  54. package/dist/tooltip.es6.js +18 -0
  55. package/dist/tooltip.es6.js.map +1 -0
  56. package/dist/tsgrid-ui.css +2 -2
  57. package/dist/tsgrid-ui.d.ts +16 -2004
  58. package/dist/tsgrid-ui.es6.js +7750 -23831
  59. package/dist/tsgrid-ui.es6.js.map +1 -1
  60. package/dist/tsgrid-ui.es6.min.js +28 -28
  61. package/dist/tsgrid-ui.js +103 -25
  62. package/dist/tsgrid-ui.min.css +2 -2
  63. package/dist/tsgrid-ui.min.js +24 -24
  64. package/dist/tsutils-message-CogFtVtO.d.ts +82 -0
  65. package/dist/utils.d.ts +418 -0
  66. package/dist/utils.es6.js +14 -0
  67. package/dist/utils.es6.js.map +1 -0
  68. package/package.json +26 -5
@@ -0,0 +1,2305 @@
1
+ import {
2
+ TsField
3
+ } from "./chunk-26XP2XU3.js";
4
+ import {
5
+ TsTabs
6
+ } from "./chunk-OMLGN735.js";
7
+ import {
8
+ TsToolbar
9
+ } from "./chunk-EVZMMVXO.js";
10
+ import {
11
+ TsTooltip
12
+ } from "./chunk-OFASTA2A.js";
13
+ import {
14
+ TsUtils
15
+ } from "./chunk-3NYH6545.js";
16
+ import {
17
+ TsBase,
18
+ TsUi,
19
+ query
20
+ } from "./chunk-DXZJHS4M.js";
21
+
22
+ // src/tsform.ts
23
+ var TsTooltip2 = TsTooltip;
24
+ var query2 = query;
25
+ var TsForm = class extends TsBase {
26
+ header;
27
+ url;
28
+ method;
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ routeData;
31
+ // any: route params are user-supplied
32
+ formURL;
33
+ formHTML;
34
+ page;
35
+ pageStyle;
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ recid;
38
+ // any: recid can be string, number, or null
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ fields;
41
+ // any: field definitions vary by type
42
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
+ actions;
44
+ // any: action can be function or object
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ record;
47
+ // any: record values depend on field definitions
48
+ // any: Record<string, any> — dynamic property bag; TsForm field schema is user-defined at runtime
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ original;
51
+ dataType;
52
+ saveCleanRecord;
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ postData;
55
+ // any: user-defined post data
56
+ httpHeaders;
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ toolbar;
59
+ // any: TsToolbar instance or config object
60
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
+ tabs;
62
+ // any: TsTabs instance or config object
63
+ style;
64
+ focus;
65
+ autosize;
66
+ nestedFields;
67
+ tabindexBase;
68
+ isGenerated;
69
+ last;
70
+ onRequest;
71
+ onLoad;
72
+ onValidate;
73
+ onSubmit;
74
+ onProgress;
75
+ onSave;
76
+ onChange;
77
+ onInput;
78
+ onRender;
79
+ onRefresh;
80
+ onResize;
81
+ onDestroy;
82
+ onAction;
83
+ onToolbar;
84
+ onError;
85
+ msgRefresh;
86
+ msgSaving;
87
+ msgServerError;
88
+ ALL_TYPES;
89
+ LIST_TYPES;
90
+ TsFIELD_TYPES;
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
+ constructor(options) {
93
+ super(options["name"]);
94
+ this.name = "";
95
+ this.header = "";
96
+ this.box = null;
97
+ this.url = "";
98
+ this.method = null;
99
+ this.routeData = {};
100
+ this.formURL = "";
101
+ this.formHTML = "";
102
+ this.page = 0;
103
+ this.pageStyle = "";
104
+ this.recid = null;
105
+ this.fields = [];
106
+ this.actions = {};
107
+ this.record = {};
108
+ this.original = null;
109
+ this.dataType = null;
110
+ this.saveCleanRecord = true;
111
+ this.postData = {};
112
+ this.httpHeaders = {};
113
+ this["toolbar"] = {};
114
+ this["tabs"] = {};
115
+ this.style = "";
116
+ this.focus = 0;
117
+ this.autosize = true;
118
+ this.nestedFields = true;
119
+ this.tabindexBase = 0;
120
+ this.isGenerated = false;
121
+ this.last = {
122
+ fetchCtrl: null,
123
+ // last fetch AbortController
124
+ fetchOptions: null,
125
+ // last fetch options
126
+ errors: []
127
+ };
128
+ this.onRequest = null;
129
+ this.onLoad = null;
130
+ this.onValidate = null;
131
+ this.onSubmit = null;
132
+ this.onProgress = null;
133
+ this.onSave = null;
134
+ this.onChange = null;
135
+ this.onInput = null;
136
+ this.onRender = null;
137
+ this.onRefresh = null;
138
+ this.onResize = null;
139
+ this.onDestroy = null;
140
+ this.onAction = null;
141
+ this.onToolbar = null;
142
+ this.onError = null;
143
+ this.msgRefresh = "Loading...";
144
+ this.msgSaving = "Saving...";
145
+ this.msgServerError = "Server error";
146
+ this.ALL_TYPES = [
147
+ "text",
148
+ "textarea",
149
+ "email",
150
+ "pass",
151
+ "password",
152
+ "int",
153
+ "float",
154
+ "money",
155
+ "currency",
156
+ "percent",
157
+ "hex",
158
+ "alphanumeric",
159
+ "color",
160
+ "date",
161
+ "time",
162
+ "datetime",
163
+ "toggle",
164
+ "checkbox",
165
+ "radio",
166
+ "check",
167
+ "checks",
168
+ "list",
169
+ "combo",
170
+ "enum",
171
+ "file",
172
+ "select",
173
+ "switch",
174
+ "map",
175
+ "array",
176
+ "div",
177
+ "custom",
178
+ "html",
179
+ "empty",
180
+ "columns"
181
+ ];
182
+ this.LIST_TYPES = ["select", "radio", "check", "checks", "list", "combo", "enum", "switch"];
183
+ this.TsFIELD_TYPES = [
184
+ "int",
185
+ "float",
186
+ "money",
187
+ "currency",
188
+ "percent",
189
+ "hex",
190
+ "alphanumeric",
191
+ "color",
192
+ "date",
193
+ "time",
194
+ "datetime",
195
+ "list",
196
+ "combo",
197
+ "enum",
198
+ "file"
199
+ ];
200
+ TsUtils.extend(this, options);
201
+ const record = options["record"];
202
+ const original = options["original"];
203
+ const fields = options["fields"];
204
+ const toolbar = options["toolbar"];
205
+ let tabs = options["tabs"];
206
+ Object.assign(this, { record: {}, original: null, fields: [], tabs: {}, toolbar: {}, handlers: [] });
207
+ if (fields) {
208
+ const sub = _processFields(fields);
209
+ this.fields = sub.fields;
210
+ if (!tabs && sub.tabs.length > 0) {
211
+ tabs = sub.tabs;
212
+ }
213
+ }
214
+ if (Array.isArray(tabs)) {
215
+ TsUtils.extend(this.tabs, { tabs: [] });
216
+ for (let t = 0; t < tabs.length; t++) {
217
+ const tmp = tabs[t];
218
+ if (typeof tmp === "object") {
219
+ this.tabs.tabs.push(tmp);
220
+ if (tmp.active === true) {
221
+ this.tabs.active = tmp.id;
222
+ }
223
+ } else {
224
+ this.tabs.tabs.push({ id: tmp, text: tmp });
225
+ }
226
+ }
227
+ } else {
228
+ TsUtils.extend(this.tabs, tabs);
229
+ }
230
+ TsUtils.extend(this.toolbar, toolbar);
231
+ for (const p in record) {
232
+ if (TsUtils.isPlainObject(record[p])) {
233
+ this.record[p] = TsUtils.clone(record[p]);
234
+ } else {
235
+ this.record[p] = record[p];
236
+ }
237
+ }
238
+ for (const p in original) {
239
+ if (this.original == null) this.original = {};
240
+ if (TsUtils.isPlainObject(original[p])) {
241
+ this.original[p] = TsUtils.clone(original[p]);
242
+ } else {
243
+ this.original[p] = original[p];
244
+ }
245
+ }
246
+ if (this.formURL !== "") {
247
+ fetch(this.formURL).then((resp) => resp.text()).then((text) => {
248
+ this.formHTML = text;
249
+ this.isGenerated = true;
250
+ if (this.box) this.render(this.box);
251
+ });
252
+ } else if (!this.formURL && !this.formHTML) {
253
+ this.formHTML = this.generateHTML();
254
+ this.isGenerated = true;
255
+ } else if (this.formHTML) {
256
+ this.isGenerated = true;
257
+ }
258
+ if (typeof this.box == "string") this.box = query2(this.box).get(0);
259
+ if (this.box) this.render(this.box);
260
+ function _processFields(fields2) {
261
+ const newFields = [];
262
+ const tabs2 = [];
263
+ if (TsUtils.isPlainObject(fields2)) {
264
+ let _process3 = function(fld) {
265
+ const ignore = ["html"];
266
+ if (fld.html == null) fld.html = {};
267
+ Object.keys(fld).forEach(((key) => {
268
+ if (ignore.includes(key)) return;
269
+ if ([
270
+ "label",
271
+ "attr",
272
+ "style",
273
+ "text",
274
+ "span",
275
+ "page",
276
+ "column",
277
+ "anchor",
278
+ "group",
279
+ "groupStyle",
280
+ "groupTitleStyle",
281
+ "groupCollapsible"
282
+ ].includes(key)) {
283
+ fld.html[key] = fld[key];
284
+ delete fld[key];
285
+ }
286
+ }));
287
+ return fld;
288
+ }, _process22 = function(fld, fld2) {
289
+ const ignore = ["style", "html"];
290
+ Object.keys(fld).forEach(((key) => {
291
+ if (ignore.includes(key)) return;
292
+ if (["span", "column", "attr", "text", "label"].includes(key)) {
293
+ if (fld[key] && !fld2.html[key]) {
294
+ fld2.html[key] = fld[key];
295
+ }
296
+ }
297
+ }));
298
+ };
299
+ var _process = _process3, _process2 = _process22;
300
+ const tmp = fields2;
301
+ fields2 = [];
302
+ Object.keys(tmp).forEach((key) => {
303
+ const fld = tmp[key];
304
+ if (fld.type == "group") {
305
+ fld.text = key;
306
+ if (TsUtils.isPlainObject(fld.fields)) {
307
+ const tmp2 = fld.fields;
308
+ fld.fields = [];
309
+ Object.keys(tmp2).forEach((key2) => {
310
+ const fld2 = tmp2[key2];
311
+ fld2.field = key2;
312
+ fld.fields.push(_process3(fld2));
313
+ });
314
+ }
315
+ fields2.push(fld);
316
+ } else if (fld.type == "tab") {
317
+ const tab = { id: key, text: key };
318
+ if (fld.style) {
319
+ tab.style = fld.style;
320
+ }
321
+ tabs2.push(tab);
322
+ const sub = _processFields(fld.fields).fields;
323
+ sub.forEach((fld2) => {
324
+ fld2.html = fld2.html || {};
325
+ fld2.html.page = tabs2.length - 1;
326
+ _process22(fld, fld2);
327
+ });
328
+ fields2.push(...sub);
329
+ } else {
330
+ fld.field = key;
331
+ fields2.push(_process3(fld));
332
+ }
333
+ });
334
+ }
335
+ fields2.forEach((field) => {
336
+ if (field.type == "group") {
337
+ const group = {
338
+ group: field.text || "",
339
+ groupStyle: field.style || "",
340
+ groupTitleStyle: field.titleStyle || "",
341
+ groupCollapsible: field.collapsible === true ? true : false
342
+ };
343
+ if (Array.isArray(field.fields)) {
344
+ field.fields.forEach((gfield) => {
345
+ const fld = TsUtils.clone(gfield);
346
+ if (fld.html == null) fld.html = {};
347
+ TsUtils.extend(fld.html, group);
348
+ ["span", "column", "attr", "label", "page"].forEach((key) => {
349
+ if (fld.html[key] == null && field[key] != null) {
350
+ fld.html[key] = field[key];
351
+ }
352
+ });
353
+ if (fld.field == null && fld.name != null) {
354
+ console.log("NOTICE: form field.name property is deprecated, please use field.field. Field ->", field);
355
+ fld.field = fld.name;
356
+ }
357
+ newFields.push(fld);
358
+ });
359
+ }
360
+ } else {
361
+ const fld = TsUtils.clone(field);
362
+ if (fld.field == null && fld.name != null) {
363
+ console.log("NOTICE: form field.name property is deprecated, please use field.field. Field ->", field);
364
+ fld.field = fld.name;
365
+ }
366
+ newFields.push(fld);
367
+ }
368
+ });
369
+ return { fields: newFields, tabs: tabs2 };
370
+ }
371
+ }
372
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
373
+ get(field, returnIndex) {
374
+ if (arguments.length === 0) {
375
+ const all = [];
376
+ for (let f1 = 0; f1 < this.fields.length; f1++) {
377
+ if (this.fields[f1].field != null) all.push(this.fields[f1].field);
378
+ }
379
+ return all;
380
+ } else {
381
+ for (let f2 = 0; f2 < this.fields.length; f2++) {
382
+ if (this.fields[f2].field == field) {
383
+ if (returnIndex === true) return f2;
384
+ else return this.fields[f2];
385
+ }
386
+ }
387
+ return null;
388
+ }
389
+ }
390
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
391
+ set(field, obj) {
392
+ for (let f = 0; f < this.fields.length; f++) {
393
+ if (this.fields[f].field == field) {
394
+ TsUtils.extend(this.fields[f], obj);
395
+ delete this.fields[f].TsField;
396
+ this.refresh(field);
397
+ return true;
398
+ }
399
+ }
400
+ return false;
401
+ }
402
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
403
+ getValue(field, original) {
404
+ if (this.nestedFields) {
405
+ let val = void 0;
406
+ try {
407
+ const rec = original === true ? this.original : this.record;
408
+ val = String(field).split(".").reduce((rec2, i) => {
409
+ return rec2[i];
410
+ }, rec);
411
+ } catch (_event) {
412
+ }
413
+ return val;
414
+ } else {
415
+ return this.record[field];
416
+ }
417
+ }
418
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
419
+ setValue(field, value, noRefresh) {
420
+ if (value === "" || value == null || Array.isArray(value) && value.length === 0 || TsUtils.isPlainObject(value) && Object.keys(value).length == 0) {
421
+ value = null;
422
+ }
423
+ if (this.nestedFields) {
424
+ try {
425
+ let rec = this.record;
426
+ String(field).split(".").map((fld, i, arr) => {
427
+ if (arr.length - 1 !== i) {
428
+ if (rec[fld]) rec = rec[fld];
429
+ else {
430
+ rec[fld] = {};
431
+ rec = rec[fld];
432
+ }
433
+ } else {
434
+ rec[fld] = value;
435
+ }
436
+ });
437
+ if (!noRefresh) this.setFieldValue(field, value);
438
+ return true;
439
+ } catch (_event) {
440
+ return false;
441
+ }
442
+ } else {
443
+ this.record[field] = value;
444
+ if (!noRefresh) this.setFieldValue(field, value);
445
+ return true;
446
+ }
447
+ }
448
+ rememberOriginal() {
449
+ if (this.original == null) {
450
+ if (Object.keys(this.record).length > 0) {
451
+ this.original = TsUtils.clone(this.record);
452
+ } else {
453
+ this.original = {};
454
+ }
455
+ }
456
+ }
457
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
458
+ getFieldValue(name) {
459
+ const field = this.get(name);
460
+ if (field == null) return void 0;
461
+ const el = field.el;
462
+ let previous = this.getValue(name);
463
+ const original = this.getValue(name, true);
464
+ let current = el.value;
465
+ if (["int", "float", "percent", "money", "currency"].includes(field.type)) {
466
+ current = field.TsField.clean(current);
467
+ }
468
+ if (["radio"].includes(field.type)) {
469
+ const selected2 = query2(el).closest(".tsg-field-group").find("input:checked").get(0);
470
+ if (selected2) {
471
+ const item = field.options.items[query2(selected2).data("index")];
472
+ current = item.id;
473
+ } else {
474
+ current = null;
475
+ }
476
+ }
477
+ if (["toggle", "checkbox"].includes(field.type)) {
478
+ current = el.checked;
479
+ }
480
+ if (["check", "checks"].includes(field.type)) {
481
+ current = [];
482
+ const selected2 = query2(el).closest(".tsg-field-group").find("input:checked");
483
+ if (selected2.length > 0) {
484
+ selected2.each((node) => {
485
+ const el2 = node;
486
+ const item = field.options.items[query2(el2).data("index")];
487
+ current.push(item.id);
488
+ });
489
+ }
490
+ if (!Array.isArray(previous)) previous = [];
491
+ }
492
+ const selected = field.TsField?.selected;
493
+ if (["list", "enum", "file"].includes(field.type) && selected) {
494
+ const nv = selected;
495
+ const cv = previous;
496
+ if (Array.isArray(nv)) {
497
+ current = [];
498
+ for (let i = 0; i < nv.length; i++) current[i] = TsUtils.clone(nv[i]);
499
+ }
500
+ if (Array.isArray(cv)) {
501
+ previous = [];
502
+ for (let i = 0; i < cv.length; i++) previous[i] = TsUtils.clone(cv[i]);
503
+ }
504
+ if (TsUtils.isPlainObject(nv)) {
505
+ current = TsUtils.clone(nv);
506
+ }
507
+ if (TsUtils.isPlainObject(cv)) {
508
+ previous = TsUtils.clone(cv);
509
+ }
510
+ }
511
+ if (["map", "array"].includes(field.type)) {
512
+ current = field.type == "map" ? {} : [];
513
+ field.$el.parent().find(".tsg-map-field").each((div, _ind) => {
514
+ const key = query2(div).find(".tsg-map.key").val();
515
+ const value = query2(div).find(".tsg-map.value").val();
516
+ if (typeof field.html?.render == "function") {
517
+ current[_ind] ??= {};
518
+ query2(div).find("input, textarea").each((node) => {
519
+ const inp = node;
520
+ const name2 = inp.dataset["name"] ?? inp["name"];
521
+ if (name2 != null && name2 != "") {
522
+ current[_ind][name2] = ["checkbox", "radio"].includes(inp.type) ? inp.checked : inp.value;
523
+ }
524
+ });
525
+ } else if (field.type == "map") {
526
+ current[key] = value;
527
+ } else {
528
+ current.push(value);
529
+ }
530
+ });
531
+ }
532
+ return { current, previous, original };
533
+ }
534
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
535
+ findItem(item, items) {
536
+ return items.find((it) => it.id === item || it.id === item?.id);
537
+ }
538
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
539
+ setFieldValue(name, value) {
540
+ const field = this.get(name);
541
+ if (field == null) return;
542
+ const el = field.el;
543
+ switch (field.type) {
544
+ case "toggle":
545
+ case "checkbox": {
546
+ el.checked = value ? true : false;
547
+ break;
548
+ }
549
+ case "radio": {
550
+ value = value?.id ?? value;
551
+ const inputs = query2(el).closest(".tsg-field-group").find("input");
552
+ const items = field.options.items;
553
+ items.forEach((it, ind) => {
554
+ const input = inputs.filter(`[data-index="${ind}"]`);
555
+ if (it.id === value) {
556
+ input.prop("checked", true);
557
+ } else {
558
+ input.prop("checked", false);
559
+ }
560
+ if (it.hidden === true) {
561
+ input.closest(".tsg-field-item").hide();
562
+ } else {
563
+ input.closest(".tsg-field-item").show();
564
+ }
565
+ });
566
+ break;
567
+ }
568
+ case "check":
569
+ case "checks": {
570
+ if (!Array.isArray(value)) {
571
+ if (value != null) {
572
+ value = [value];
573
+ } else {
574
+ value = [];
575
+ }
576
+ }
577
+ value = value.map((val) => val?.id ?? val);
578
+ const inputs = query2(el).closest("div.tsg-field-group").find("input");
579
+ const items = field.options.items;
580
+ items.forEach((it, ind) => {
581
+ const input = inputs.filter(`[data-index="${ind}"]`);
582
+ input.prop("checked", value.includes(it.id) ? true : false);
583
+ if (it.hidden === true) {
584
+ input.closest(".tsg-field-item").hide();
585
+ } else {
586
+ input.closest(".tsg-field-item").show();
587
+ }
588
+ });
589
+ break;
590
+ }
591
+ case "list":
592
+ case "combo": {
593
+ let item = value;
594
+ const map = field.options?.itemMap;
595
+ if (item?.id == null && Array.isArray(field.options?.items)) {
596
+ field.options.items.forEach((it) => {
597
+ const val = TsUtils.getNested(it, map?.id ?? "id");
598
+ if (val === value) item = it;
599
+ });
600
+ }
601
+ if (item?.id != null && item?.text == null && Array.isArray(field.options?.items)) {
602
+ field.options.items.forEach((it) => {
603
+ const id = TsUtils.getNested(it, map?.id ?? "id");
604
+ if (id === item.id) {
605
+ item.text = TsUtils.getNested(it, map.text ?? "text");
606
+ }
607
+ });
608
+ }
609
+ if (item != value) {
610
+ this.setValue(field.name, item, true);
611
+ }
612
+ if (field.type == "list") {
613
+ field.TsField.selected = item;
614
+ field.TsField.refresh();
615
+ } else {
616
+ el.value = item?.text ?? value;
617
+ }
618
+ break;
619
+ }
620
+ case "switch": {
621
+ el.value = value;
622
+ field.toolbar.uncheck(...field.toolbar.get());
623
+ field.toolbar.check(value);
624
+ break;
625
+ }
626
+ case "enum":
627
+ case "file": {
628
+ if (!Array.isArray(value)) {
629
+ value = value != null ? [value] : [];
630
+ }
631
+ const items = [...value];
632
+ let updated = false;
633
+ items.forEach((item, ind) => {
634
+ if (item?.id == null && Array.isArray(field.options.items)) {
635
+ field.options.items.forEach((it) => {
636
+ if (it.id == item) {
637
+ items[ind] = it;
638
+ updated = true;
639
+ }
640
+ });
641
+ }
642
+ });
643
+ if (updated) {
644
+ this.setValue(field.name, items, true);
645
+ }
646
+ field.TsField.selected = items;
647
+ field.TsField.refresh();
648
+ break;
649
+ }
650
+ case "map":
651
+ case "array": {
652
+ if (field.type == "map" && (value == null || !TsUtils.isPlainObject(value))) {
653
+ this.setValue(field.field, {}, true);
654
+ value = this.getValue(field.field);
655
+ }
656
+ if (field.type == "array" && (value == null || !Array.isArray(value))) {
657
+ this.setValue(field.field, [], true);
658
+ value = this.getValue(field.field);
659
+ }
660
+ const container = query2(field.el).parent().find(".tsg-map-container");
661
+ field.el.mapRefresh(value, container);
662
+ break;
663
+ }
664
+ case "div":
665
+ case "custom": {
666
+ query2(el).html(value);
667
+ break;
668
+ }
669
+ case "color": {
670
+ el.value = value ?? "";
671
+ field.TsField.refresh();
672
+ break;
673
+ }
674
+ case "html":
675
+ case "empty":
676
+ break;
677
+ default:
678
+ if (value != null && el._w2field?.format) {
679
+ const obj = el._w2field;
680
+ value = obj.format(obj.clean(value));
681
+ }
682
+ el.value = value ?? "";
683
+ break;
684
+ }
685
+ this.fields.forEach((fld) => {
686
+ if (fld?.options?.parentList != null) {
687
+ let updated;
688
+ let values = this.getValue(fld.options.parentList);
689
+ if (Array.isArray(values)) {
690
+ values = values.map((vv) => vv.id);
691
+ } else {
692
+ values = values?.id != null ? [values.id] : [];
693
+ }
694
+ fld.options?.items?.forEach?.((item) => {
695
+ const parent = TsUtils.getNested(item, fld.options.parentField ?? "parentId");
696
+ if (parent == null) {
697
+ return;
698
+ }
699
+ const possible = TsUtils.clone(Array.isArray(parent) ? parent : [parent]);
700
+ possible.unshift("");
701
+ const includes = values.some((item2) => possible.includes(item2));
702
+ if (includes && item.hidden === true) {
703
+ item.hidden = false;
704
+ updated = true;
705
+ } else if (!includes && item.hidden !== true) {
706
+ item.hidden = true;
707
+ updated = true;
708
+ }
709
+ });
710
+ if (updated) {
711
+ let value2 = this.getValue(fld.field);
712
+ if (value2?.id != null) value2 = value2.id;
713
+ if (fld.type == "enum") {
714
+ const valid = fld.options.items.filter((it) => !it.hidden).map((it) => it.id);
715
+ let values2 = this.getValue(fld.field);
716
+ if (!Array.isArray(values2)) values2 = [values2];
717
+ values2 = values2.map((it) => {
718
+ if (typeof it == "string" || typeof it == "number") {
719
+ it = fld.options.items.find((ii) => ii.id == it);
720
+ }
721
+ return it;
722
+ });
723
+ const new_values = values2.filter((it) => valid.includes(it.id));
724
+ this.setValue(fld.field, new_values, true);
725
+ } else {
726
+ fld.options.items.forEach((it) => {
727
+ if (it.id == value2 && it.hidden) {
728
+ this.setValue(fld.field, null, true);
729
+ }
730
+ });
731
+ }
732
+ this.set(fld.field, { items: fld.options.items });
733
+ }
734
+ }
735
+ });
736
+ }
737
+ show(...args) {
738
+ const effected = [];
739
+ for (let a = 0; a < args.length; a++) {
740
+ const fld = this.get(args[a]);
741
+ if (fld && fld.hidden) {
742
+ fld.hidden = false;
743
+ effected.push(fld.field);
744
+ }
745
+ }
746
+ if (effected.length > 0) this.refresh(...effected);
747
+ this.updateEmptyGroups();
748
+ return effected;
749
+ }
750
+ hide(...args) {
751
+ const effected = [];
752
+ for (let a = 0; a < args.length; a++) {
753
+ const fld = this.get(args[a]);
754
+ if (fld && !fld.hidden) {
755
+ fld.hidden = true;
756
+ effected.push(fld.field);
757
+ }
758
+ }
759
+ if (effected.length > 0) this.refresh(...effected);
760
+ this.updateEmptyGroups();
761
+ return effected;
762
+ }
763
+ enable(...args) {
764
+ const effected = [];
765
+ for (let a = 0; a < args.length; a++) {
766
+ const fld = this.get(args[a]);
767
+ if (fld && fld.disabled) {
768
+ fld.disabled = false;
769
+ effected.push(fld.field);
770
+ }
771
+ }
772
+ if (effected.length > 0) this.refresh(...effected);
773
+ return effected;
774
+ }
775
+ disable(...args) {
776
+ const effected = [];
777
+ for (let a = 0; a < args.length; a++) {
778
+ const fld = this.get(args[a]);
779
+ if (fld && !fld.disabled) {
780
+ fld.disabled = true;
781
+ effected.push(fld.field);
782
+ }
783
+ }
784
+ if (effected.length > 0) this.refresh(...effected);
785
+ return effected;
786
+ }
787
+ updateEmptyGroups() {
788
+ query2(this.box).find(".tsg-group").each((node) => {
789
+ const group = node;
790
+ if (isHidden(query2(group).find(".tsg-field"))) {
791
+ query2(group).hide();
792
+ } else {
793
+ query2(group).show();
794
+ }
795
+ });
796
+ function isHidden($els) {
797
+ let flag = true;
798
+ $els.each((node) => {
799
+ const el = node;
800
+ if (el.style.display != "none") flag = false;
801
+ });
802
+ return flag;
803
+ }
804
+ }
805
+ hideGroup(groupName) {
806
+ const fields = [];
807
+ let current = "";
808
+ this.fields.forEach((fld) => {
809
+ if (fld.html.group != null && fld.html.group !== "") {
810
+ current = String(fld.html.group).toLowerCase();
811
+ }
812
+ if (groupName.toLowerCase() == current) {
813
+ fields.push(fld.field);
814
+ }
815
+ });
816
+ this.hide(...fields);
817
+ this.resize();
818
+ }
819
+ showGroup(groupName) {
820
+ const fields = [];
821
+ let current = "";
822
+ this.fields.forEach((fld) => {
823
+ if (fld.html.group != null && fld.html.group !== "") {
824
+ current = String(fld.html.group).toLowerCase();
825
+ }
826
+ if (groupName.toLowerCase() == current) {
827
+ fields.push(fld.field);
828
+ }
829
+ });
830
+ this.show(...fields);
831
+ this.resize();
832
+ }
833
+ /**
834
+ * When user clicks on group title, it will toggle the group (collapse or expand it).
835
+ */
836
+ toggleGroup(groupName, show) {
837
+ const el = query2(this.box).find('.tsg-group-title[data-group="' + TsUtils.base64encode(groupName) + '"]');
838
+ if (el.length === 0) return;
839
+ const el_next = query2(el.prop("nextElementSibling"));
840
+ if (typeof show === "undefined") {
841
+ show = el_next.css("display") == "none";
842
+ }
843
+ if (show) {
844
+ el_next.show();
845
+ el.find("span").addClass("tsg-icon-collapse").removeClass("tsg-icon-expand");
846
+ } else {
847
+ el_next.hide();
848
+ el.find("span").addClass("tsg-icon-expand").removeClass("tsg-icon-collapse");
849
+ }
850
+ }
851
+ change(...args) {
852
+ args.forEach((field) => {
853
+ const tmp = this.get(field);
854
+ if (tmp.$el) tmp.$el.change();
855
+ });
856
+ }
857
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
858
+ reload(callBack) {
859
+ const url = typeof this.url !== "object" ? this.url : this.url.get;
860
+ if (url && this.recid != null) {
861
+ return this.request(callBack);
862
+ } else {
863
+ if (typeof callBack === "function") callBack();
864
+ return new Promise((resolve) => {
865
+ resolve(void 0);
866
+ });
867
+ }
868
+ }
869
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
870
+ clear(...args) {
871
+ if (args.length != 0) {
872
+ args.forEach((field) => {
873
+ let rec = this.record;
874
+ String(field).split(".").map((fld, i, arr) => {
875
+ if (arr.length - 1 !== i) rec = rec[fld];
876
+ else delete rec[fld];
877
+ });
878
+ this.refresh(field);
879
+ });
880
+ } else {
881
+ this.recid = null;
882
+ this.record = {};
883
+ this.original = null;
884
+ this.refresh();
885
+ this.hideErrors();
886
+ }
887
+ }
888
+ error(msg) {
889
+ const edata = this.trigger("error", {
890
+ target: this.name,
891
+ message: msg,
892
+ fetchCtrl: this.last.fetchCtrl,
893
+ fetchOptions: this.last.fetchOptions
894
+ });
895
+ if (edata.isCancelled === true) return;
896
+ setTimeout(() => {
897
+ this.message(msg);
898
+ }, 1);
899
+ edata.finish();
900
+ }
901
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
902
+ message(options) {
903
+ return TsUtils.message({
904
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
905
+ owner: this,
906
+ // any: TsForm has [key:string]:any but TS can't verify lock/unlock signature match
907
+ box: this.box,
908
+ after: ".tsg-form-header"
909
+ }, options);
910
+ }
911
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
912
+ confirm(options) {
913
+ return TsUtils.confirm({
914
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
915
+ owner: this,
916
+ // any: same as message() above
917
+ box: this.box,
918
+ after: ".tsg-form-header"
919
+ }, options);
920
+ }
921
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
922
+ validate(showErrors) {
923
+ if (showErrors == null) showErrors = true;
924
+ const errors = [];
925
+ for (let f = 0; f < this.fields.length; f++) {
926
+ const field = this.fields[f];
927
+ if (field.type == "columns" || field.field == null) {
928
+ continue;
929
+ }
930
+ if (this.getValue(field.field) == null) this.setValue(field.field, "");
931
+ if (["int", "float", "currency", "money"].includes(field.type)) {
932
+ const val2 = this.getValue(field.field);
933
+ const min = field.options.min;
934
+ const max = field.options.max;
935
+ if (min != null && val2 != null && val2 < min) {
936
+ errors.push({ field, error: TsUtils.lang("Should be more than ${min}", { min }) });
937
+ }
938
+ if (max != null && val2 != null && val2 > max) {
939
+ errors.push({ field, error: TsUtils.lang("Should be less than ${max}", { max }) });
940
+ }
941
+ }
942
+ switch (field.type) {
943
+ case "alphanumeric":
944
+ if (this.getValue(field.field) && !TsUtils.isAlphaNumeric(this.getValue(field.field))) {
945
+ errors.push({ field, error: TsUtils.lang("Not alpha-numeric") });
946
+ }
947
+ break;
948
+ case "int":
949
+ if (this.getValue(field.field) && !TsUtils.isInt(this.getValue(field.field))) {
950
+ errors.push({ field, error: TsUtils.lang("Not an integer") });
951
+ }
952
+ break;
953
+ case "percent":
954
+ case "float":
955
+ if (this.getValue(field.field) && !TsUtils.isFloat(this.getValue(field.field))) {
956
+ errors.push({ field, error: TsUtils.lang("Not a float") });
957
+ }
958
+ break;
959
+ case "currency":
960
+ case "money":
961
+ if (this.getValue(field.field) && !TsUtils.isMoney(this.getValue(field.field))) {
962
+ errors.push({ field, error: TsUtils.lang("Not in money format") });
963
+ }
964
+ break;
965
+ case "color":
966
+ case "hex":
967
+ if (this.getValue(field.field) && !TsUtils.isHex(this.getValue(field.field))) {
968
+ errors.push({ field, error: TsUtils.lang("Not a hex number") });
969
+ }
970
+ break;
971
+ case "email":
972
+ if (this.getValue(field.field) && !TsUtils.isEmail(this.getValue(field.field))) {
973
+ errors.push({ field, error: TsUtils.lang("Not a valid email") });
974
+ }
975
+ break;
976
+ case "checkbox":
977
+ if (this.getValue(field.field) == true) {
978
+ this.setValue(field.field, true);
979
+ } else {
980
+ this.setValue(field.field, false);
981
+ }
982
+ break;
983
+ case "date":
984
+ if (!field.options.format) field.options.format = TsUtils.settings.dateFormat;
985
+ if (this.getValue(field.field) && !TsUtils.isDate(this.getValue(field.field), field.options.format)) {
986
+ errors.push({ field, error: TsUtils.lang("Not a valid date") + ": " + field.options.format });
987
+ }
988
+ break;
989
+ case "list":
990
+ case "combo":
991
+ case "switch":
992
+ break;
993
+ case "enum":
994
+ break;
995
+ }
996
+ const val = this.getValue(field.field);
997
+ if (field.hidden !== true && field.required && !["div", "custom", "html", "empty"].includes(field.type) && (val == null || val === "" || Array.isArray(val) && val.length === 0 || TsUtils.isPlainObject(val) && Object.keys(val).length == 0)) {
998
+ errors.push({ field, error: TsUtils.lang("Required field") });
999
+ }
1000
+ if (field.hidden !== true && field.options?.minLength > 0 && !["enum", "list", "combo"].includes(field.type) && (val == null || val.length < field.options.minLength)) {
1001
+ errors.push({ field, error: TsUtils.lang(
1002
+ "Field should be at least ${count} characters.",
1003
+ { count: field.options.minLength }
1004
+ ) });
1005
+ }
1006
+ }
1007
+ const edata = this.trigger("validate", { target: this.name, errors });
1008
+ if (edata.isCancelled === true) return;
1009
+ this.last.errors = errors;
1010
+ if (showErrors) this.showErrors();
1011
+ edata.finish();
1012
+ return errors;
1013
+ }
1014
+ showErrors() {
1015
+ const errors = this.last.errors;
1016
+ if (errors.length <= 0) return;
1017
+ this.goto(errors[0].field.page);
1018
+ query2(errors[0].field.$el).parents(".tsg-field").get(0).scrollIntoView({ block: "nearest", inline: "nearest" });
1019
+ errors.forEach((error) => {
1020
+ const opt = TsUtils.extend({
1021
+ anchorClass: "tsg-error",
1022
+ class: "tsg-light",
1023
+ position: "right|left",
1024
+ hideOn: ["input", "tooltip-click"]
1025
+ }, error.options);
1026
+ if (error.field == null) return;
1027
+ let anchor = error.field.el;
1028
+ if (error.field.type === "radio") {
1029
+ anchor = query2(error.field.el).closest("div").get(0);
1030
+ } else if (["enum", "file"].includes(error.field.type)) {
1031
+ }
1032
+ TsTooltip2.show(TsUtils.extend({
1033
+ anchor,
1034
+ name: `${this.name}-${error.field.field}-error`,
1035
+ html: error.error
1036
+ }, opt));
1037
+ });
1038
+ this.last.errorsShown = true;
1039
+ query2(errors[0].field.$el).parents(".tsg-page").off(".hideErrors").on("scroll.hideErrors", (_evt) => {
1040
+ if (this.last.errorsShown) {
1041
+ this.showErrors();
1042
+ }
1043
+ });
1044
+ }
1045
+ hideErrors() {
1046
+ this.last.errorsShown = false;
1047
+ this.fields.forEach((field) => {
1048
+ TsTooltip2.hide(`${this.name}-${field.field}-error`);
1049
+ });
1050
+ }
1051
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1052
+ getChanges() {
1053
+ let diff = {};
1054
+ if (this.original != null && typeof this.original == "object" && Object.keys(this.record).length !== 0) {
1055
+ diff = doDiff(this.record, this.original, {});
1056
+ }
1057
+ return diff;
1058
+ function doDiff(record, original, result) {
1059
+ if (Array.isArray(record) && Array.isArray(original)) {
1060
+ while (record.length < original.length) {
1061
+ record.push(null);
1062
+ }
1063
+ }
1064
+ for (const i in record) {
1065
+ if (record[i] != null && typeof record[i] === "object") {
1066
+ result[i] = doDiff(record[i], original[i] || {}, {});
1067
+ if (!result[i] || Object.keys(result[i]).length == 0 && Object.keys(original[i].length == 0)) delete result[i];
1068
+ } else if (record[i] != original[i] || record[i] == null && original[i] != null) {
1069
+ result[i] = record[i];
1070
+ }
1071
+ }
1072
+ return Object.keys(result).length != 0 ? result : null;
1073
+ }
1074
+ }
1075
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1076
+ getCleanRecord(strict) {
1077
+ const data = TsUtils.clone(this.record);
1078
+ this.fields.forEach((fld) => {
1079
+ if (fld.type == "columns" || fld.field == null) {
1080
+ return;
1081
+ }
1082
+ if (["list", "combo", "enum"].includes(fld.type)) {
1083
+ const tmp = { nestedFields: true, record: data };
1084
+ const val = this.getValue.call(tmp, fld.field);
1085
+ if (TsUtils.isPlainObject(val) && val.id != null) {
1086
+ this.setValue.call(tmp, fld.field, val.id);
1087
+ }
1088
+ if (Array.isArray(val)) {
1089
+ val.forEach((item, ind) => {
1090
+ if (TsUtils.isPlainObject(item) && item.id) {
1091
+ val[ind] = item.id;
1092
+ }
1093
+ });
1094
+ }
1095
+ }
1096
+ if (fld.type == "map") {
1097
+ const tmp = { nestedFields: true, record: data };
1098
+ const val = this.getValue.call(tmp, fld.field);
1099
+ if (val._order) delete val._order;
1100
+ }
1101
+ if (fld.type == "file") {
1102
+ const tmp = { nestedFields: true, record: data };
1103
+ const val = this.getValue.call(tmp, fld.field) ?? [];
1104
+ val.forEach((v) => {
1105
+ delete v.file;
1106
+ delete v.modified;
1107
+ });
1108
+ this.setValue.call(tmp, fld.field, val);
1109
+ }
1110
+ });
1111
+ if (strict === true) {
1112
+ Object.keys(data).forEach((key) => {
1113
+ if (!this.get(key)) delete data[key];
1114
+ });
1115
+ }
1116
+ return data;
1117
+ }
1118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1119
+ request(postData, callBack) {
1120
+ const self = this;
1121
+ let resolve, reject;
1122
+ const responseProm = new Promise((res, rej) => {
1123
+ resolve = res;
1124
+ reject = rej;
1125
+ });
1126
+ if (typeof postData === "function") {
1127
+ callBack = postData;
1128
+ postData = null;
1129
+ }
1130
+ if (postData == null) postData = {};
1131
+ if (!this.url || typeof this.url === "object" && !this.url.get) return;
1132
+ const params = {};
1133
+ params.action = "get";
1134
+ params.recid = this.recid;
1135
+ params.name = this.name;
1136
+ TsUtils.extend(params, this.postData);
1137
+ TsUtils.extend(params, postData);
1138
+ const edata = this.trigger("request", {
1139
+ target: this.name,
1140
+ url: this.url,
1141
+ httpMethod: "GET",
1142
+ postData: params,
1143
+ httpHeaders: this.httpHeaders
1144
+ });
1145
+ if (edata.isCancelled === true) return;
1146
+ this.record = {};
1147
+ this.original = null;
1148
+ this.lock(TsUtils.lang(this.msgRefresh));
1149
+ let url = edata.detail["url"];
1150
+ if (typeof url === "object" && url.get) url = url.get;
1151
+ if (this.last.fetchCtrl) try {
1152
+ this.last.fetchCtrl.abort();
1153
+ } catch (_e) {
1154
+ }
1155
+ if (Object.keys(this.routeData).length != 0) {
1156
+ const info = TsUtils.parseRoute(url);
1157
+ if (info.keys.length > 0) {
1158
+ for (let k = 0; k < info.keys.length; k++) {
1159
+ const routeKey = info.keys[k];
1160
+ if (routeKey == null || this.routeData[routeKey.name] == null) continue;
1161
+ url = url.replace(new RegExp(":" + routeKey.name, "g"), this.routeData[routeKey.name]);
1162
+ }
1163
+ }
1164
+ }
1165
+ url = new URL(url, location.href);
1166
+ const fetchOptions = TsUtils.prepareParams(url, {
1167
+ method: edata.detail["httpMethod"],
1168
+ headers: edata.detail["httpHeaders"],
1169
+ body: edata.detail["postData"]
1170
+ }, { dataType: this.dataType, caller: this, action: "request" });
1171
+ this.last.fetchCtrl = new AbortController();
1172
+ fetchOptions["signal"] = this.last.fetchCtrl.signal;
1173
+ this.last.fetchOptions = fetchOptions;
1174
+ fetch(url, fetchOptions).catch(processError).then((resp) => {
1175
+ if (resp?.status != 200) {
1176
+ if (resp) processError(resp);
1177
+ return;
1178
+ }
1179
+ resp.json().catch(processError).then((data) => {
1180
+ const edata2 = self.trigger("load", {
1181
+ target: self.name,
1182
+ fetchCtrl: self.last.fetchCtrl,
1183
+ fetchOptions: self.last.fetchOptions,
1184
+ data
1185
+ });
1186
+ if (edata2.isCancelled === true) return;
1187
+ if (data.error == null && data.status === "error") {
1188
+ data.error = true;
1189
+ }
1190
+ if (!data.record) {
1191
+ Object.assign(data, { record: TsUtils.clone(data) });
1192
+ }
1193
+ if (data.error === true) {
1194
+ self.error(TsUtils.lang(data.message ?? self.msgServerError));
1195
+ } else {
1196
+ self.record = TsUtils.clone(data.record);
1197
+ }
1198
+ self.unlock();
1199
+ edata2.finish();
1200
+ self.refresh();
1201
+ self.setFocus();
1202
+ if (typeof callBack === "function") callBack(data);
1203
+ resolve(data);
1204
+ });
1205
+ });
1206
+ edata.finish();
1207
+ return responseProm;
1208
+ function processError(response) {
1209
+ if (response.name === "AbortError") {
1210
+ return;
1211
+ }
1212
+ self.unlock();
1213
+ const edata2 = self.trigger("error", { response, fetchCtrl: self.last.fetchCtrl, fetchOptions: self.last.fetchOptions });
1214
+ if (edata2.isCancelled === true) return;
1215
+ if (response.status && response.status != 200) {
1216
+ self.error(response.status + ": " + response.statusText);
1217
+ } else {
1218
+ console.log(
1219
+ "ERROR: Server request failed.",
1220
+ response,
1221
+ ". ",
1222
+ "Expected Response:",
1223
+ { error: false, record: { field1: 1, field2: "item" } },
1224
+ "OR:",
1225
+ { error: true, message: "Error description" }
1226
+ );
1227
+ self.error(String(response));
1228
+ }
1229
+ edata2.finish();
1230
+ reject(response);
1231
+ }
1232
+ }
1233
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1234
+ submit(postData, callBack) {
1235
+ return this.save(postData, callBack);
1236
+ }
1237
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1238
+ save(postData, callBack) {
1239
+ const self = this;
1240
+ let resolve, reject;
1241
+ const saveProm = new Promise((res, rej) => {
1242
+ resolve = res;
1243
+ reject = rej;
1244
+ });
1245
+ if (typeof postData === "function") {
1246
+ callBack = postData;
1247
+ postData = null;
1248
+ }
1249
+ const errors = self.validate(true);
1250
+ if ((errors?.length ?? 0) !== 0) return;
1251
+ if (postData == null) postData = {};
1252
+ if (!self.url || typeof self.url === "object" && !self.url.save) {
1253
+ console.log("ERROR: Form cannot be saved because no url is defined.");
1254
+ return;
1255
+ }
1256
+ self.lock(TsUtils.lang(self.msgSaving) + ' <span id="' + self.name + '_progress"></span>');
1257
+ const params = {};
1258
+ params.action = "save";
1259
+ params.recid = self.recid;
1260
+ params.name = self.name;
1261
+ TsUtils.extend(params, self.postData);
1262
+ TsUtils.extend(params, postData);
1263
+ params.record = TsUtils.clone(self.saveCleanRecord ? self.getCleanRecord() : self.record);
1264
+ const edata = self.trigger("submit", {
1265
+ target: self.name,
1266
+ url: self.url,
1267
+ httpMethod: this.method ?? "POST",
1268
+ postData: params,
1269
+ httpHeaders: self.httpHeaders
1270
+ });
1271
+ if (edata.isCancelled === true) return;
1272
+ let url = edata.detail["url"];
1273
+ if (typeof url === "object" && url.save) url = url.save;
1274
+ if (self.last.fetchCtrl) self.last.fetchCtrl.abort();
1275
+ if (Object.keys(self.routeData).length > 0) {
1276
+ const info = TsUtils.parseRoute(url);
1277
+ if (info.keys.length > 0) {
1278
+ for (let k = 0; k < info.keys.length; k++) {
1279
+ const routeKey = info.keys[k];
1280
+ if (routeKey == null || self.routeData[routeKey.name] == null) continue;
1281
+ url = url.replace(new RegExp(":" + routeKey.name, "g"), self.routeData[routeKey.name]);
1282
+ }
1283
+ }
1284
+ }
1285
+ url = new URL(url, location.href);
1286
+ const fetchOptions = TsUtils.prepareParams(url, {
1287
+ method: edata.detail["httpMethod"],
1288
+ headers: edata.detail["httpHeaders"],
1289
+ body: edata.detail["postData"]
1290
+ }, { dataType: this.dataType, caller: this, action: "save" });
1291
+ this.last.fetchCtrl = new AbortController();
1292
+ fetchOptions["signal"] = this.last.fetchCtrl.signal;
1293
+ this.last.fetchOptions = fetchOptions;
1294
+ fetch(url, fetchOptions).catch(processError).then((resp) => {
1295
+ self.unlock();
1296
+ if (resp?.status != 200) {
1297
+ processError(resp ?? {});
1298
+ return;
1299
+ }
1300
+ resp.json().catch(processError).then((data) => {
1301
+ const edata2 = self.trigger("save", {
1302
+ target: self.name,
1303
+ fetchCtrl: self.last.fetchCtrl,
1304
+ fetchOptions: self.last.fetchOptions,
1305
+ data
1306
+ });
1307
+ if (edata2.isCancelled === true) return;
1308
+ if (data.error === true) {
1309
+ self.error(TsUtils.lang(data.message ?? self.msgServerError));
1310
+ } else {
1311
+ self.original = null;
1312
+ }
1313
+ edata2.finish();
1314
+ self.refresh();
1315
+ if (typeof callBack === "function") callBack(data);
1316
+ resolve(data);
1317
+ });
1318
+ });
1319
+ edata.finish();
1320
+ return saveProm;
1321
+ function processError(response) {
1322
+ if (response?.name === "AbortError") {
1323
+ return;
1324
+ }
1325
+ self.unlock();
1326
+ const edata2 = self.trigger("error", { response, fetchCtrl: self.last.fetchCtrl, fetchOptions: self.last.fetchOptions });
1327
+ if (edata2.isCancelled === true) return;
1328
+ if (response.status && response.status != 200) {
1329
+ response.json().then((data) => {
1330
+ self.error(response.status + ": " + (data.message || response.statusText));
1331
+ }).catch(() => {
1332
+ self.error(response.status + ": " + response.statusText);
1333
+ });
1334
+ } else {
1335
+ console.log(
1336
+ "ERROR: Server request failed.",
1337
+ response,
1338
+ ". ",
1339
+ "Expected Response:",
1340
+ { error: false, record: { field1: 1, field2: "item" } },
1341
+ "OR:",
1342
+ { error: true, message: "Error description" }
1343
+ );
1344
+ self.error(String(response));
1345
+ }
1346
+ edata2.finish();
1347
+ reject();
1348
+ }
1349
+ }
1350
+ lock(msg, showSpinner) {
1351
+ TsUtils.lock(this.box, msg, showSpinner);
1352
+ }
1353
+ unlock(speed) {
1354
+ const box = this.box;
1355
+ TsUtils.unlock(box, speed);
1356
+ }
1357
+ lockPage(page, msg, spinner) {
1358
+ const $page = query2(this.box).find(".page-" + page);
1359
+ if ($page.length) {
1360
+ TsUtils.lock($page, msg, spinner);
1361
+ return true;
1362
+ }
1363
+ return false;
1364
+ }
1365
+ unlockPage(page, speed) {
1366
+ const $page = query2(this.box).find(".page-" + page);
1367
+ if ($page.length) {
1368
+ TsUtils.unlock($page, speed);
1369
+ return true;
1370
+ }
1371
+ return false;
1372
+ }
1373
+ goto(page) {
1374
+ if (this.page === page) return;
1375
+ if (page != null) this.page = page;
1376
+ if (query2(this.box).data("autoSize") === true) {
1377
+ query2(this.box).get(0).clientHeight = 0;
1378
+ }
1379
+ this.refresh();
1380
+ }
1381
+ generateHTML() {
1382
+ const pages = [];
1383
+ let group = "";
1384
+ let page;
1385
+ let column;
1386
+ let tabindex;
1387
+ let tabindex_str;
1388
+ for (let f = 0; f < this.fields.length; f++) {
1389
+ let html2 = "";
1390
+ tabindex = this.tabindexBase + f + 1;
1391
+ tabindex_str = ' tabindex="' + tabindex + '"';
1392
+ const field = this.fields[f];
1393
+ if (field.html == null) field.html = {};
1394
+ if (typeof field.html == "string") {
1395
+ field.html = {
1396
+ html: field.html,
1397
+ span: 0,
1398
+ attr: "tabindex"
1399
+ };
1400
+ tabindex_str = "";
1401
+ }
1402
+ if (field.options == null) field.options = {};
1403
+ if (field.html.caption != null && field.html.label == null) {
1404
+ console.log("NOTICE: form field.html.caption property is deprecated, please use field.html.label. Field ->", field);
1405
+ field.html.label = field.html.caption;
1406
+ }
1407
+ if (field.html.label == null) field.html.label = field.field;
1408
+ if (field.html.anchor != null && field.html.span == null) {
1409
+ field.html.span = "";
1410
+ }
1411
+ field.html = TsUtils.extend({ label: "", span: 6, attr: "", text: "", style: "", page: 0, column: 0 }, field.html);
1412
+ if (page == null) page = field.html.page;
1413
+ if (column == null) column = field.html.column;
1414
+ let input = `<input id="${field.field}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" type="text" ${field.html.attr + tabindex_str}>`;
1415
+ switch (field.type) {
1416
+ case "pass":
1417
+ case "password":
1418
+ input = input.replace('type="text"', 'type="password"');
1419
+ break;
1420
+ case "checkbox": {
1421
+ input = `
1422
+ <label class="tsg-box-label">
1423
+ <input id="${field.field}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" type="checkbox" ${field.html.attr + tabindex_str}>
1424
+ <span>${field.html.label}</span>
1425
+ </label>`;
1426
+ break;
1427
+ }
1428
+ case "check":
1429
+ case "checks": {
1430
+ if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
1431
+ let items = field.options.items;
1432
+ input = `<div class="tsg-field-group" ${field.html.attr}>`;
1433
+ if (!Array.isArray(items)) items = [];
1434
+ if (items.length > 0) {
1435
+ items = TsUtils.normMenu.call(this, items, field.options);
1436
+ }
1437
+ for (let i = 0; i < items.length; i++) {
1438
+ input += `
1439
+ <div class="tsg-field-item">
1440
+ <label class="tsg-box-label">
1441
+ <input id="${field.field + i}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" type="checkbox"
1442
+ ${tabindex_str} data-value="${items[i].id}" data-index="${i}">
1443
+ <span>&#160;${items[i].text}</span>
1444
+ </label>
1445
+ </div>`;
1446
+ }
1447
+ input += "</div>";
1448
+ break;
1449
+ }
1450
+ case "radio": {
1451
+ input = `<div class="tsg-field-group"${field.html.attr}>`;
1452
+ if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
1453
+ let items = field.options.items;
1454
+ if (!Array.isArray(items)) items = [];
1455
+ if (items.length > 0) {
1456
+ items = TsUtils.normMenu.call(this, items, field.options);
1457
+ }
1458
+ for (let i = 0; i < items.length; i++) {
1459
+ input += `
1460
+ <div class="tsg-field-item">
1461
+ <label class="tsg-box-label">
1462
+ <input id="${field.field + i}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" type="radio"
1463
+ ${i === 0 ? tabindex_str : ""}
1464
+ data-value="${items[i].id}" data-index="${i}">
1465
+ <span>&#160;${items[i].text}</span>
1466
+ </label>
1467
+ </div>`;
1468
+ }
1469
+ input += "</div>";
1470
+ break;
1471
+ }
1472
+ case "select": {
1473
+ input = `<select id="${field.field}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" ${field.html.attr + tabindex_str}>`;
1474
+ if (field.options.items == null && field.html.items != null) field.options.items = field.html.items;
1475
+ let items = field.options.items;
1476
+ if (!Array.isArray(items)) items = [];
1477
+ if (items.length > 0) {
1478
+ items = TsUtils.normMenu.call(this, items, field.options);
1479
+ }
1480
+ for (let i = 0; i < items.length; i++) {
1481
+ input += `<option value="${items[i].id}">${items[i].text}</option>`;
1482
+ }
1483
+ input += "</select>";
1484
+ break;
1485
+ }
1486
+ case "switch": {
1487
+ input = `
1488
+ <div>
1489
+ <div id="${field.field}-tb" class="tsg-form-switch ${field.html.class ?? ""}" ${field.html.attr}></div>
1490
+ <input id="${field.field}" name="${field.field}" ${tabindex_str} class="tsg-input"
1491
+ style="position: absolute; right: 0px; margin-top: -30px; width: 1px; padding: 0; opacity: 0">
1492
+ <span style="position: absolute; margin-top: -2px;">${field.html.text ?? ""}</span>
1493
+ </div>
1494
+ `;
1495
+ break;
1496
+ }
1497
+ case "textarea":
1498
+ input = `<textarea id="${field.field}" name="${field.field}" class="tsg-input ${field.html.class ?? ""}" ${field.html.attr + tabindex_str}></textarea>`;
1499
+ break;
1500
+ case "toggle":
1501
+ input = `<input id="${field.field}" name="${field.field}" class="tsg-input tsg-toggle ${field.html.class ?? ""}"
1502
+ type="checkbox" ${field.html.attr + tabindex_str}>
1503
+ <div><div></div></div>`;
1504
+ break;
1505
+ case "map":
1506
+ case "array":
1507
+ field.html.key = field.html.key || {};
1508
+ field.html.value = field.html.value || {};
1509
+ field.html.tabindex = tabindex;
1510
+ field.html.tabindex_str = tabindex_str;
1511
+ input = '<span style="float: right">' + (field.html.text || "") + '</span><input id="' + field.field + '" name="' + field.field + '" type="hidden" ' + field.html.attr + tabindex_str + '><div class="tsg-map-container"></div>';
1512
+ break;
1513
+ case "div":
1514
+ case "custom":
1515
+ input = `<div id="${field.field}" name="${field.field}" ${field.html.attr + tabindex_str} class="tsg-input ${field.html.class ?? ""}">` + (field && field.html && field.html.html ? field.html.html : "") + "</div>";
1516
+ break;
1517
+ case "html":
1518
+ case "empty":
1519
+ input = `<div id="${field.field}" name="${field.field}" ${field.html.attr + tabindex_str} class="tsg-input ${field.html.class ?? ""}">` + (field && field.html ? (field.html.html || "") + (field.html.text || "") : "") + "</div>";
1520
+ break;
1521
+ }
1522
+ if (group !== "") {
1523
+ if (page != field.html.page || column != field.html.column || field.html.group && group != field.html.group) {
1524
+ pages[page][column] += "\n </div>\n </div>";
1525
+ group = "";
1526
+ }
1527
+ }
1528
+ if (field.html.group && group != field.html.group) {
1529
+ let collapsible = "";
1530
+ if (field.html.groupCollapsible) {
1531
+ collapsible = '<span class="tsg-icon-collapse" style="width: 15px; display: inline-block; position: relative; top: -2px;"></span>';
1532
+ }
1533
+ html2 += '\n <div class="tsg-group">\n <div class="tsg-group-title tsg-eaction" style="' + (field.html.groupTitleStyle || "") + "; " + (collapsible != "" ? "cursor: pointer; user-select: none" : "") + '"' + (collapsible != "" ? 'data-group="' + TsUtils.base64encode(field.html.group) + '"' : "") + (collapsible != "" ? 'data-click="toggleGroup|' + field.html.group + '"' : "") + ">" + collapsible + TsUtils.lang(field.html.group) + '</div>\n <div class="tsg-group-fields" style="' + (field.html.groupStyle || "") + '">';
1534
+ group = field.html.group;
1535
+ }
1536
+ if (field.type == "columns") {
1537
+ html2 += `<div class="tsg-field-columns" style="${field.style ?? ""}">`;
1538
+ field.columns.forEach((col) => {
1539
+ html2 += `<div style="${col.style}"> ${col.content} </div>`;
1540
+ });
1541
+ html2 += "</div>";
1542
+ } else if (field.html.col_anchor != null) {
1543
+ let span = field.html.span != null ? "tsg-span" + field.html.span : "";
1544
+ if (field.html.span == -1) span = "tsg-span-none";
1545
+ let label = `
1546
+ <label ${span == "none" ? ' style="display: none"' : ""}>
1547
+ ${TsUtils.lang(field.type != "checkbox" ? field.html.label : field.html.text)}
1548
+ </label>`;
1549
+ if (!field.html.label) label = "";
1550
+ const text = field.type != "array" && field.type != "map" ? TsUtils.lang(field.type != "checkbox" ? field.html.text : "") : "";
1551
+ pages[field.html.page].anchors ??= {};
1552
+ pages[field.html.page].anchors[field.html.col_anchor] = `
1553
+ <div class="tsg-field ${span}" style="${(field.hidden ? "display: none;" : "") + field.html.style}">
1554
+ ${label}
1555
+ ${["empty", "switch", "radio", "check", "checks"].includes(field.type) ? input : `<div>${input + text}</div>`}
1556
+ </div>`;
1557
+ } else if (field.html.anchor != null) {
1558
+ const span = field.html.span != null ? "tsg-span" + field.html.span : "tsg-span0";
1559
+ let label = TsUtils.lang(field.type != "checkbox" ? field.html.label : field.html.text, true);
1560
+ const text = TsUtils.lang(field.type != "checkbox" ? field.html.text : "");
1561
+ if (field.html.span == -1) {
1562
+ label = `<span style="position: absolute"> <span class="tsg-anchor-span-none tsg-inline-label"> ${label} </span> </span>`;
1563
+ } else {
1564
+ label = `<span class="tsg-inline-label"> ${label} </span>`;
1565
+ }
1566
+ pages[field.html.page].anchors ??= {};
1567
+ pages[field.html.page].anchors[field.html.anchor] = `
1568
+ <div class="tsg-field tsg-field-inline ${span}" style="${(field.hidden ? "display: none;" : "") + field.html.style}">
1569
+ ${field.type === "empty" || field.type == "switch" ? input : ` <div>
1570
+ ${label} ${input} ${text}
1571
+ </div>`}
1572
+ </div>`;
1573
+ } else {
1574
+ let span = field.html.span != null ? "tsg-span" + field.html.span : "";
1575
+ if (field.html.span == -1) span = "tsg-span-none";
1576
+ let label = `
1577
+ <label ${span == "none" ? ' style="display: none"' : ""}>
1578
+ ${TsUtils.lang(field.type != "checkbox" ? field.html.label : field.html.text)}
1579
+ </label>`;
1580
+ if (!field.html.label) label = "";
1581
+ const text = field.type != "array" && field.type != "map" ? TsUtils.lang(field.type != "checkbox" ? field.html.text : "") : "";
1582
+ html2 += `
1583
+ <div class="tsg-field ${span}" style="${(field.hidden ? "display: none;" : "") + field.html.style}">
1584
+ ${label}
1585
+ ${["empty", "switch", "radio", "check", "checks"].includes(field.type) ? input : `<div>${input + text}</div>`}
1586
+ </div>`;
1587
+ }
1588
+ if (pages[field.html.page] == null) pages[field.html.page] = {};
1589
+ if (pages[field.html.page][field.html.column] == null) pages[field.html.page][field.html.column] = "";
1590
+ pages[field.html.page][field.html.column] += html2;
1591
+ page = field.html.page;
1592
+ column = field.html.column;
1593
+ }
1594
+ if (group !== "") pages[page][column] += "\n </div>\n </div>";
1595
+ if (this.tabs.tabs) {
1596
+ for (let i = 0; i < this.tabs.tabs.length; i++) if (pages[i] == null) pages[i] = [];
1597
+ }
1598
+ let buttons = "";
1599
+ if (Object.keys(this.actions).length > 0) {
1600
+ buttons += '\n<div class="tsg-buttons">';
1601
+ tabindex = this.tabindexBase + this.fields.length + 1;
1602
+ for (const a in this.actions) {
1603
+ const act = this.actions[a];
1604
+ const info = { text: "", style: "", "class": "" };
1605
+ if (TsUtils.isPlainObject(act)) {
1606
+ if (act.text == null && act.caption != null) {
1607
+ console.log("NOTICE: form action.caption property is deprecated, please use action.text. Action ->", act);
1608
+ act.text = act.caption;
1609
+ }
1610
+ if (act.text) info.text = act.text;
1611
+ if (act.style) info.style = act.style;
1612
+ if (act.class) info.class = act.class;
1613
+ } else {
1614
+ info.text = a;
1615
+ if (["save", "update", "create"].includes(a.toLowerCase())) info.class = "tsg-btn-blue";
1616
+ else info.class = "";
1617
+ }
1618
+ buttons += '\n <button name="' + a + '" class="tsg-btn ' + info.class + '" style="' + info.style + '" tabindex="' + tabindex + '">' + TsUtils.lang(info.text) + "</button>";
1619
+ tabindex++;
1620
+ }
1621
+ buttons += "\n</div>";
1622
+ }
1623
+ let html = "";
1624
+ for (let p = 0; p < pages.length; p++) {
1625
+ html += '<div class="tsg-page page-' + p + '" style="' + (p !== 0 ? "display: none;" : "") + this.pageStyle + '">';
1626
+ if (!pages[p]) {
1627
+ console.log(`ERROR: Page ${p} does not exist`);
1628
+ return false;
1629
+ }
1630
+ if (pages[p].before) {
1631
+ html += pages[p].before;
1632
+ }
1633
+ html += '<div class="tsg-column-container">';
1634
+ Object.keys(pages[p]).sort().forEach((c, _ind) => {
1635
+ if (c == String(parseInt(c))) {
1636
+ html += '<div class="tsg-column col-' + c + '">' + (pages[p][c] || "") + "\n</div>";
1637
+ }
1638
+ });
1639
+ html += "\n</div>";
1640
+ if (pages[p].after) {
1641
+ html += pages[p].after;
1642
+ }
1643
+ html += "\n</div>";
1644
+ if (pages[p].anchors) {
1645
+ Object.keys(pages[p].anchors).forEach((key, _ind) => {
1646
+ html = html.replace(key, pages[p].anchors[key]);
1647
+ });
1648
+ }
1649
+ }
1650
+ html += buttons;
1651
+ return html;
1652
+ }
1653
+ action(action, event) {
1654
+ const act = this.actions[action];
1655
+ let click = act;
1656
+ if (TsUtils.isPlainObject(act) && act.onClick) click = act.onClick;
1657
+ const edata = this.trigger("action", { target: action, action: act, originalEvent: event });
1658
+ if (edata.isCancelled === true) return;
1659
+ if (typeof click === "function") click.call(this, event);
1660
+ edata.finish();
1661
+ }
1662
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1663
+ getAction(action) {
1664
+ const ret = query2(this.box).find('.tsg-buttons button[name="' + action + '"]');
1665
+ if (ret.length === 0) {
1666
+ console.log('ERROR: Action "' + action + '" not found. Valid actions are: ' + Object.keys(this.actions).join(", "));
1667
+ }
1668
+ return ret;
1669
+ }
1670
+ actionHide(action) {
1671
+ this.getAction(action).hide();
1672
+ }
1673
+ actionShow(action) {
1674
+ this.getAction(action).show();
1675
+ }
1676
+ actionDisable(action) {
1677
+ this.getAction(action).prop("disabled", true);
1678
+ }
1679
+ actionEnable(action) {
1680
+ this.getAction(action).prop("disabled", false);
1681
+ }
1682
+ resize() {
1683
+ const self = this;
1684
+ const edata = this.trigger("resize", { target: this.name });
1685
+ if (edata.isCancelled === true) return;
1686
+ if (this.box != null) {
1687
+ let resizeElements2 = function() {
1688
+ const headerHeight2 = self.header !== "" ? TsUtils.getSize(header, "height") : 0;
1689
+ const tbHeight2 = Array.isArray(self.toolbar?.items) && self.toolbar?.items?.length > 0 ? TsUtils.getSize(toolbar, "height") : 0;
1690
+ const tabsHeight2 = Array.isArray(self.tabs?.tabs) && self.tabs?.tabs?.length > 0 ? TsUtils.getSize(tabs, "height") : 0;
1691
+ toolbar.css({ top: headerHeight2 + "px" });
1692
+ tabs.css({ top: headerHeight2 + tbHeight2 + "px" });
1693
+ page.css({
1694
+ top: headerHeight2 + tbHeight2 + tabsHeight2 + "px",
1695
+ bottom: (buttons.length > 0 ? TsUtils.getSize(buttons, "height") : 0) + "px"
1696
+ });
1697
+ return { headerHeight: headerHeight2, tbHeight: tbHeight2, tabsHeight: tabsHeight2 };
1698
+ };
1699
+ var resizeElements = resizeElements2;
1700
+ const header = query2(this.box).find(":scope > div .tsg-form-header");
1701
+ const toolbar = query2(this.box).find(":scope > div .tsg-form-toolbar");
1702
+ const tabs = query2(this.box).find(":scope > div .tsg-form-tabs");
1703
+ const page = query2(this.box).find(":scope > div .tsg-page");
1704
+ const dpage = query2(this.box).find(":scope > div .tsg-page.page-" + this.page + " > div");
1705
+ const buttons = query2(this.box).find(":scope > div .tsg-buttons");
1706
+ const { headerHeight, tbHeight, tabsHeight } = resizeElements2();
1707
+ if (this.autosize) {
1708
+ const cHeight = query2(this.box).get(0).clientHeight;
1709
+ if (cHeight === 0 || query2(this.box).data("autosize") == "yes") {
1710
+ query2(this.box).css({
1711
+ height: headerHeight + tbHeight + tabsHeight + 15 + (page.length > 0 ? TsUtils.getSize(dpage, "height") : 0) + (buttons.length > 0 ? TsUtils.getSize(buttons, "height") : 0) + "px"
1712
+ });
1713
+ query2(this.box).data("autosize", "yes");
1714
+ }
1715
+ resizeElements2();
1716
+ }
1717
+ this.tabs?.resize?.();
1718
+ this.toolbar?.resize?.();
1719
+ this.fields.forEach((field) => {
1720
+ if (field.type == "switch") {
1721
+ field.toolbar?.resize?.();
1722
+ }
1723
+ });
1724
+ }
1725
+ edata.finish();
1726
+ }
1727
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1728
+ refresh(...args) {
1729
+ const time = Date.now();
1730
+ const self = this;
1731
+ if (!this.box) return 0;
1732
+ if (!this.isGenerated || !query2(this.box).html()) return 0;
1733
+ const edata = this.trigger("refresh", { target: this.name, page: this.page, field: args[0], fields: args });
1734
+ if (edata.isCancelled === true) return 0;
1735
+ let fields = Array.from(this.fields.keys());
1736
+ if (args.length > 0) {
1737
+ fields = args.map((fld, _ind) => {
1738
+ if (typeof fld != "string") console.log("ERROR: Arguments in refresh functions should be field names");
1739
+ return this.get(fld, true);
1740
+ }).filter((fld, _ind) => {
1741
+ if (fld != null) return true;
1742
+ else return false;
1743
+ });
1744
+ } else {
1745
+ query2(this.box).find("input, textarea, select").each((node) => {
1746
+ const el = node;
1747
+ const name = query2(el).attr("name") != null ? query2(el).attr("name") : query2(el).attr("id");
1748
+ const field = this.get(name);
1749
+ if (field) {
1750
+ const div = query2(el).closest(".tsg-page");
1751
+ if (div.length > 0) {
1752
+ for (let i = 0; i < 100; i++) {
1753
+ if (div.hasClass("page-" + i)) {
1754
+ field.page = i;
1755
+ break;
1756
+ }
1757
+ }
1758
+ }
1759
+ }
1760
+ });
1761
+ query2(this.box).find(".tsg-page").hide();
1762
+ query2(this.box).find(".tsg-page.page-" + this.page).show();
1763
+ query2(this.box).find(".tsg-form-header").html(TsUtils.lang(this.header));
1764
+ if (typeof this.tabs === "object" && Array.isArray(this.tabs.tabs) && this.tabs.tabs.length > 0) {
1765
+ query2(this.box).find("#form_" + this.name + "_tabs").show();
1766
+ this.tabs.active = this.tabs.tabs[this.page].id;
1767
+ this.tabs.refresh();
1768
+ } else {
1769
+ query2(this.box).find("#form_" + this.name + "_tabs").hide();
1770
+ }
1771
+ if (typeof this.toolbar === "object" && Array.isArray(this.toolbar.items) && this.toolbar.items.length > 0) {
1772
+ query2(this.box).find("#form_" + this.name + "_toolbar").show();
1773
+ this.toolbar.refresh();
1774
+ } else {
1775
+ query2(this.box).find("#form_" + this.name + "_toolbar").hide();
1776
+ }
1777
+ }
1778
+ for (let f = 0; f < fields.length; f++) {
1779
+ const fieldIdx = fields[f];
1780
+ if (fieldIdx == null) continue;
1781
+ const field = this.fields[fieldIdx];
1782
+ if (field == null) continue;
1783
+ if (field.name == null && field.field != null) field.name = field.field;
1784
+ if (field.field == null && field.name != null) field.field = field.name;
1785
+ field.$el = query2(this.box).find(`[name='${String(field.name).replace(/\\/g, "\\\\")}']`);
1786
+ field.el = field.$el.get(0);
1787
+ if (field.el) field.el.id = field.name;
1788
+ if (field.TsField) {
1789
+ field.TsField.reset();
1790
+ }
1791
+ field.$el.off(".TsForm").on("change.TsForm", function(event) {
1792
+ const value = self.getFieldValue(field.field);
1793
+ if (value == null) return;
1794
+ if (["enum", "file"].includes(field.type)) {
1795
+ const helper = field.TsField?.helpers?.multi;
1796
+ query2(helper).removeClass("tsg-error");
1797
+ }
1798
+ if (this._previous != null) {
1799
+ value.previous = this._previous;
1800
+ delete this._previous;
1801
+ }
1802
+ const edata2 = self.trigger("change", { target: this.name, field: this.name, value, originalEvent: event });
1803
+ if (edata2.isCancelled === true) return;
1804
+ self.setValue(this.name, value.current);
1805
+ edata2.finish();
1806
+ }).on("input.TsForm", function(event) {
1807
+ self.rememberOriginal();
1808
+ const value = self.getFieldValue(field.field);
1809
+ if (value == null) return;
1810
+ if (this._previous == null) {
1811
+ this._previous = value.previous;
1812
+ }
1813
+ const edata2 = self.trigger("input", { target: self.name, field, value, originalEvent: event });
1814
+ if (edata2.isCancelled === true) return;
1815
+ self.setValue(this.name, value.current, true);
1816
+ edata2.finish();
1817
+ });
1818
+ if (field.required) {
1819
+ field.$el.closest(".tsg-field").addClass("tsg-required");
1820
+ } else {
1821
+ field.$el.closest(".tsg-field").removeClass("tsg-required");
1822
+ }
1823
+ if (field.disabled != null) {
1824
+ if (field.disabled) {
1825
+ if (field.$el.data("tabIndex") == null) {
1826
+ field.$el.data("tabIndex", field.$el.prop("tabIndex"));
1827
+ }
1828
+ field.$el.prop("disabled", true).prop("tabIndex", -1).closest(".tsg-field").addClass("tsg-disabled");
1829
+ } else {
1830
+ field.$el.prop("disabled", false).prop("tabIndex", field.$el.data("tabIndex") ?? field.$el.prop("tabIndex") ?? 0).closest(".tsg-field").removeClass("tsg-disabled");
1831
+ }
1832
+ }
1833
+ let tmp = field.el;
1834
+ if (!tmp) tmp = query2(this.box).find("#" + field.field);
1835
+ if (field.hidden) {
1836
+ query2(tmp).closest(".tsg-field").hide();
1837
+ } else {
1838
+ query2(tmp).closest(".tsg-field").show();
1839
+ }
1840
+ }
1841
+ query2(this.box).find("button, input[type=button]").each((node) => {
1842
+ const el = node;
1843
+ query2(el).off("click").on("click", function(event) {
1844
+ let action = this.value;
1845
+ if (this.id) action = this.id;
1846
+ if (this["name"]) action = this["name"];
1847
+ self.action(action, event);
1848
+ });
1849
+ });
1850
+ for (let f = 0; f < fields.length; f++) {
1851
+ const fieldIdx2 = fields[f];
1852
+ if (fieldIdx2 == null) continue;
1853
+ const field = this.fields[fieldIdx2];
1854
+ if (field == null) continue;
1855
+ if (!field.el) continue;
1856
+ if (!field.$el.hasClass("tsg-input")) field.$el.addClass("tsg-input");
1857
+ field.type = String(field.type).toLowerCase();
1858
+ if (!field.options) field.options = {};
1859
+ if (this.LIST_TYPES.includes(field.type)) {
1860
+ const items = field.options.items;
1861
+ if (items == null) field.options.items = [];
1862
+ if (field.type == "switch") {
1863
+ items.forEach((item, ind) => {
1864
+ return items[ind] = typeof item != "object" ? { id: item, text: item } : item;
1865
+ });
1866
+ } else {
1867
+ field.options.items = TsUtils.normMenu.call(this, items ?? [], field.options);
1868
+ }
1869
+ }
1870
+ if (field.type == "switch") {
1871
+ if (field.toolbar) {
1872
+ ;
1873
+ TsUi[this.name + "_" + field.name + "_tb"].destroy();
1874
+ }
1875
+ field.options?.items?.forEach?.((it) => it.text == null ? it.text = "" : "");
1876
+ const items = TsUtils.normMenu.call(this, field.options.items, field.options) ?? [];
1877
+ items.forEach((item) => item.type ??= "radio");
1878
+ field.toolbar = new TsToolbar({
1879
+ box: field.$el.prev().get(0),
1880
+ name: this.name + "_" + field.name + "_tb",
1881
+ items,
1882
+ onClick(event) {
1883
+ self.rememberOriginal();
1884
+ const value = self.getFieldValue(field.name);
1885
+ if (value == null) return;
1886
+ value.current = event.detail["item"].id;
1887
+ const edata2 = self.trigger("change", { target: field.name, field: field.name, value, originalEvent: event });
1888
+ if (edata2.isCancelled === true) {
1889
+ return;
1890
+ }
1891
+ self.record[field.name] = value.current;
1892
+ self.setFieldValue(field.name, value.current);
1893
+ edata2.finish();
1894
+ }
1895
+ });
1896
+ field.$el.prev().addClass("tsg-form-switch");
1897
+ field.toolbar.resize();
1898
+ field.$el.off(".form-input").on("focus.form-input", (event) => {
1899
+ const ind = field.toolbar.get(field.$el.val(), true);
1900
+ query2(event.target).prop("_index", ind);
1901
+ query2(field.toolbar.box).addClass("tsg-tb-focus");
1902
+ }).on("blur.form-input", (event) => {
1903
+ query2(event.target).removeProp("_index");
1904
+ query2(`#${field.name}-tb .tsg-tb-button`).removeClass("over");
1905
+ query2(field.toolbar.box).removeClass("tsg-tb-focus");
1906
+ }).on("keydown.form-input", (event) => {
1907
+ let ind = query2(event.target).prop("_index");
1908
+ switch (event.key) {
1909
+ case "ArrowLeft": {
1910
+ if (ind > 0) ind--;
1911
+ query2(`#${field.name}-tb .tsg-tb-button`).removeClass("over").eq(ind).addClass("over");
1912
+ query2(event.target).prop("_index", ind);
1913
+ break;
1914
+ }
1915
+ case "ArrowRight": {
1916
+ if (ind < field.toolbar.items.length - 1) ind++;
1917
+ query2(`#${field.name}-tb .tsg-tb-button`).removeClass("over").eq(ind).addClass("over");
1918
+ query2(event.target).prop("_index", ind);
1919
+ break;
1920
+ }
1921
+ }
1922
+ if (event.keyCode == 32 || event.keyCode == 13) {
1923
+ self.rememberOriginal();
1924
+ const value = self.getFieldValue(field.name);
1925
+ if (value == null) return;
1926
+ const tbItem = field.toolbar.items[ind];
1927
+ value.current = tbItem?.id;
1928
+ const edata2 = self.trigger("change", { target: field.name, field: field.name, value, originalEvent: event });
1929
+ if (edata2.isCancelled === true) {
1930
+ return;
1931
+ }
1932
+ self.record[field.name] = value.current;
1933
+ self.setFieldValue(field.name, value.current);
1934
+ edata2.finish();
1935
+ query2(`#${field.name}-tb .tsg-tb-button`).removeClass("over");
1936
+ }
1937
+ if (!event.metaKey && !event.ctrlKey && event.keyCode != 9) {
1938
+ event.preventDefault();
1939
+ }
1940
+ });
1941
+ }
1942
+ if (field.type == "select") {
1943
+ const items = field.options.items;
1944
+ let options = "";
1945
+ items.forEach((item) => {
1946
+ options += `<option value="${item.id}">${item.text}</option>`;
1947
+ });
1948
+ field.$el.html(options);
1949
+ }
1950
+ if (this.TsFIELD_TYPES.includes(field.type)) {
1951
+ field.TsField = field.TsField ?? new TsField(TsUtils.extend({}, field.options, { type: field.type }));
1952
+ field.TsField.render(field.el);
1953
+ }
1954
+ if (["map", "array"].includes(field.type)) {
1955
+ (function(obj, field2) {
1956
+ field2.el.mapAdd = function(field3, div, cnt, empty) {
1957
+ const attr = (field3.disabled ? " readOnly " : "") + (field3.html.tabindex_str || "");
1958
+ let html = `<input type="text" ${(field3.html.value.attr ?? "") + attr} class="tsg-input ${field3.html.class ?? ""} tsg-map value">${field3.html.value.text || ""}`;
1959
+ if (typeof field3.html.render == "function") {
1960
+ html = field3.html.render.call(self, { empty: empty === true, ind: cnt, field: field3, div });
1961
+ if (!field3.el._errorDisplayed) {
1962
+ query.html(html).filter("input, textarea").each((node) => {
1963
+ const inp = node;
1964
+ const name = inp.dataset["name"] ?? inp["name"];
1965
+ if (name == null || name == "") {
1966
+ console.log(
1967
+ `ERROR: All inputs of the field %c"${field3.name}"%c must have name attribute defined. No name for %c${inp.outerHTML}`,
1968
+ "color: blue",
1969
+ "",
1970
+ "color: red"
1971
+ );
1972
+ }
1973
+ });
1974
+ field3.el._errorDisplayed = true;
1975
+ }
1976
+ } else if (field3.type == "map") {
1977
+ html = `<input type="text" ${(field3.html.key.attr ?? "") + attr} class="tsg-input ${field3.html.class ?? ""} tsg-map key">
1978
+ ${field3.html.key.text || ""}
1979
+ ` + html;
1980
+ }
1981
+ div.append(`<div class="tsg-map-field" style="margin-bottom: 5px" data-index="${cnt}">${html}</div>`);
1982
+ if (typeof field3.html.render == "function") {
1983
+ const box = div.find(`[data-index="${cnt}"]`);
1984
+ box.find("input, textarea").each((el) => {
1985
+ if (query2(el).attr("tabindex") == null) {
1986
+ query2(el).attr("tabindex", field3.html.tabindex);
1987
+ }
1988
+ });
1989
+ if (typeof field3.html.onRefresh == "function") {
1990
+ field3.html.onRefresh.call(self, { index: cnt, empty, box: box.get(0) });
1991
+ }
1992
+ }
1993
+ };
1994
+ field2.el.mapRefresh = function(map, div) {
1995
+ let keys = [], $k, $v;
1996
+ if (field2.type == "map") {
1997
+ if (!TsUtils.isPlainObject(map)) map = {};
1998
+ if (map._order == null) map._order = Object.keys(map);
1999
+ keys = map._order;
2000
+ }
2001
+ if (field2.type == "array") {
2002
+ if (!Array.isArray(map)) map = [];
2003
+ keys = map.map((item, ind) => {
2004
+ return ind;
2005
+ });
2006
+ }
2007
+ div.find(".tsg-map-field").remove();
2008
+ for (let ind = 0; ind < keys.length; ind++) {
2009
+ const key = keys[ind];
2010
+ let fld = div.find(`div[data-index='${ind}']`);
2011
+ if (fld.length == 0) {
2012
+ field2.el.mapAdd(field2, div, ind);
2013
+ fld = div.find(`div[data-index='${ind}']`);
2014
+ }
2015
+ fld.attr("data-key", key);
2016
+ if (typeof field2.html?.render == "function") {
2017
+ const val = map[key];
2018
+ fld.find("input, textarea").each((node) => {
2019
+ const inp = node;
2020
+ const name = inp.dataset["name"] ?? inp["name"];
2021
+ if (inp.type == "checkbox") {
2022
+ inp.checked = val[name] ?? false;
2023
+ } else if (inp.type == "radio") {
2024
+ inp.checked = val[name] ?? false;
2025
+ } else {
2026
+ inp.value = val[name] ?? "";
2027
+ }
2028
+ });
2029
+ } else {
2030
+ $k = fld.find(".tsg-map.key");
2031
+ $v = fld.find(".tsg-map.value");
2032
+ let val = map[key];
2033
+ if (field2.type == "array") {
2034
+ const tmp = map.filter((it) => {
2035
+ return it?.key == key ? true : false;
2036
+ });
2037
+ if (tmp.length > 0) val = tmp[0].value;
2038
+ }
2039
+ $k.val(key);
2040
+ $v.val(val);
2041
+ if (field2.disabled === true || field2.disabled === false) {
2042
+ $k.prop("readOnly", field2.disabled ? true : false);
2043
+ $v.prop("readOnly", field2.disabled ? true : false);
2044
+ }
2045
+ }
2046
+ if (typeof field2.html.onRefresh == "function") {
2047
+ field2.html.onRefresh.call(self, { index: ind, box: div.find(`[data-index="${ind}"]`).get(0) });
2048
+ }
2049
+ }
2050
+ if (typeof field2.html.render == "function") {
2051
+ $v = div.find(".tsg-map-field:last-child input:first-child");
2052
+ }
2053
+ const cnt = keys.length;
2054
+ const curr = div.find(`div[data-index='${cnt}']`);
2055
+ if (curr.length === 0 && (!$k || $k.val() != "" || $v.val() != "") && !($k && ($k.prop("readOnly") === true || $k.prop("disabled") === true))) {
2056
+ field2.el.mapAdd(field2, div, cnt, true);
2057
+ }
2058
+ if (field2.disabled === true || field2.disabled === false) {
2059
+ curr.find(".key").prop("readOnly", field2.disabled ? true : false);
2060
+ curr.find(".value").prop("readOnly", field2.disabled ? true : false);
2061
+ }
2062
+ let lastKey = null;
2063
+ const container = query2(field2.el).get(0)?.nextSibling;
2064
+ query2(container).off(".mapChange").on("mouseup.mapChange", { delegate: "input, textarea" }, function(event) {
2065
+ if (document.activeElement != event.target) {
2066
+ event.target.focus();
2067
+ }
2068
+ }).on("keyup.mapChange", { delegate: "input, textarea" }, function(event) {
2069
+ const kbdEvent = event;
2070
+ const $div = query2(kbdEvent.target).closest(".tsg-map-field");
2071
+ const next = $div.get(0).nextElementSibling;
2072
+ const prev = $div.get(0).previousElementSibling;
2073
+ const className = query2(kbdEvent.target).hasClass("key") ? "key" : "value";
2074
+ if (kbdEvent.keyCode == 38 && prev) {
2075
+ query2(prev).find(`input.${className}, textarea.${className}, input[name="${kbdEvent.target["name"]}"] textarea[name="${kbdEvent.target["name"]}"]`).get(0)?.select();
2076
+ kbdEvent.preventDefault();
2077
+ }
2078
+ if (kbdEvent.keyCode == 40 && next) {
2079
+ ;
2080
+ kbdEvent.target.blur();
2081
+ const next2 = $div.get(0).nextElementSibling;
2082
+ query2(next2).find(`input.${className}, textarea.${className}, input[name="${kbdEvent.target["name"]}"] textarea[name="${kbdEvent.target["name"]}"]`).get(0)?.select();
2083
+ kbdEvent.preventDefault();
2084
+ }
2085
+ }).on("keydown.mapChange", { delegate: "input, textarea" }, function(_event) {
2086
+ const event = _event;
2087
+ lastKey = null;
2088
+ if (event.keyCode == 9) {
2089
+ lastKey = "tab";
2090
+ }
2091
+ if (event.keyCode == 13) {
2092
+ lastKey = "enter";
2093
+ }
2094
+ if (event.keyCode == 38 || event.keyCode == 40) {
2095
+ lastKey = event.keyCode == 38 ? "up" : "down";
2096
+ event.preventDefault();
2097
+ }
2098
+ }).on("input.mapChange", { delegate: "input, textarea" }, function(event) {
2099
+ const fld = query2(event.target).closest("div.tsg-map-field");
2100
+ const cnt2 = fld.data("index");
2101
+ const next = fld.get(0).nextElementSibling;
2102
+ let isEmpty = true;
2103
+ query2(fld).find("input, textarea").each((node) => {
2104
+ const el = node;
2105
+ if (!["checkbox", "button"].includes(el.type) && el.value != "") isEmpty = false;
2106
+ });
2107
+ let isNextEmpty = true;
2108
+ query2(next).find("input, textarea").each((node) => {
2109
+ const el = node;
2110
+ if (!["checkbox", "button"].includes(el.type) && el.value != "") isNextEmpty = false;
2111
+ });
2112
+ if (!isEmpty && !next) {
2113
+ field2.el.mapAdd(field2, div, parseInt(cnt2) + 1, true);
2114
+ } else if (isEmpty && next && isNextEmpty) {
2115
+ query2(next).remove();
2116
+ }
2117
+ }).on("change.mapChange", { delegate: "input, textarea" }, function(_event) {
2118
+ const event = _event;
2119
+ self.rememberOriginal();
2120
+ const _fieldValue = self.getFieldValue(field2.field);
2121
+ if (_fieldValue == null) return;
2122
+ let { current, previous, original } = _fieldValue;
2123
+ const $cnt = query2(event.target).closest(".tsg-map-container");
2124
+ if (typeof field2.html?.render == "function") {
2125
+ current = current.filter((kk) => {
2126
+ const val = [...new Set(Object.values(kk).filter((vv) => typeof vv != "boolean"))];
2127
+ return !(val.length == 0 || val.length == 1 && val[0] === "");
2128
+ });
2129
+ } else if (field2.type == "map") {
2130
+ current._order = [];
2131
+ $cnt.find(".tsg-map.key").each((node) => {
2132
+ current._order.push(node.value);
2133
+ });
2134
+ current._order = current._order.filter((k) => k !== "");
2135
+ delete current[""];
2136
+ } else if (field2.type == "array") {
2137
+ current = current.filter((k) => k !== "");
2138
+ }
2139
+ const edata2 = self.trigger("change", {
2140
+ target: field2.field,
2141
+ field: field2.field,
2142
+ originalEvent: event,
2143
+ value: { current, previous, original }
2144
+ });
2145
+ if (edata2.isCancelled === true) {
2146
+ return;
2147
+ }
2148
+ let index;
2149
+ let className = "";
2150
+ const cnt2 = query2(event.target).closest(".tsg-map-container");
2151
+ if (field2.type == "array" || lastKey == "tab") {
2152
+ cnt2.find("input, textarea").each((node, ind) => {
2153
+ if (node == event.target) index = ind;
2154
+ });
2155
+ } else {
2156
+ className = query2(event.target).hasClass("key") ? ".key" : ".value";
2157
+ cnt2.find("input" + className + ", textarea" + className).each((node, ind) => {
2158
+ if (node == event.target) index = ind;
2159
+ });
2160
+ }
2161
+ self.setValue(field2.field, current);
2162
+ let el;
2163
+ const safeIdx = index ?? 0;
2164
+ if (lastKey == "tab") {
2165
+ el = cnt2.find("input, textarea").get(safeIdx + 1);
2166
+ } else if (lastKey == "enter" && cnt2.find("input.value, textarea.value").length > 0) {
2167
+ if (className == ".key") {
2168
+ el = cnt2.find("input.key, textarea.key").get(safeIdx + 1);
2169
+ } else {
2170
+ el = cnt2.find("input.value, textarea.value").get(safeIdx + 1);
2171
+ }
2172
+ if (el == null) {
2173
+ el = cnt2.find("input, textarea").get(safeIdx + (event.shiftKey ? -1 : 1));
2174
+ }
2175
+ } else {
2176
+ el = cnt2.find("input" + className + ", textarea" + className).eq(safeIdx + (lastKey == "up" ? -1 : 1)).get(0);
2177
+ }
2178
+ if (el) {
2179
+ el.focus();
2180
+ el.select();
2181
+ }
2182
+ edata2.finish();
2183
+ });
2184
+ };
2185
+ })(this, field);
2186
+ }
2187
+ this.setFieldValue(field.field, this.getValue(field.name));
2188
+ }
2189
+ edata.finish();
2190
+ this.resize();
2191
+ return Date.now() - time;
2192
+ }
2193
+ render(box) {
2194
+ const time = Date.now();
2195
+ const self = this;
2196
+ if (typeof box == "string") box = query2(box).get(0);
2197
+ const edata = this.trigger("render", { target: this.name, box: box ?? this.box });
2198
+ if (edata.isCancelled === true) return;
2199
+ if (box != null) {
2200
+ this.unmount();
2201
+ this.box = box;
2202
+ }
2203
+ if (!this.isGenerated && !this.formHTML) return;
2204
+ if (!this.box) return;
2205
+ const html = '<div class="tsg-form-box">' + (this.header !== "" ? '<div class="tsg-form-header">' + TsUtils.lang(this.header) + "</div>" : "") + ' <div id="form_' + this.name + '_toolbar" class="tsg-form-toolbar" style="display: none"></div> <div id="form_' + this.name + '_tabs" class="tsg-form-tabs" style="display: none"></div>' + this.formHTML + "</div>";
2206
+ query2(this.box).attr("name", this.name).addClass("tsg-reset tsg-form").html(html);
2207
+ if (query2(this.box).length > 0) query2(this.box).get(0).style.cssText += this.style;
2208
+ TsUtils.bindEvents(query2(this.box).find(".tsg-eaction"), this);
2209
+ if (typeof this.toolbar.render !== "function") {
2210
+ this.toolbar = new TsToolbar(TsUtils.extend({}, this.toolbar, { name: this.name + "_toolbar", owner: this }));
2211
+ this.toolbar.on("click", function(event) {
2212
+ const edata2 = self.trigger("toolbar", { target: event.target, originalEvent: event });
2213
+ if (edata2.isCancelled === true) return;
2214
+ edata2.finish();
2215
+ });
2216
+ }
2217
+ if (typeof this.toolbar === "object" && typeof this.toolbar.render === "function") {
2218
+ this.toolbar.render(query2(this.box).find("#form_" + this.name + "_toolbar").get(0));
2219
+ }
2220
+ if (typeof this.tabs.render !== "function") {
2221
+ this.tabs = new TsTabs(TsUtils.extend({}, this.tabs, { name: this.name + "_tabs", owner: this, active: this.tabs.active }));
2222
+ this.tabs.on("click", function(event) {
2223
+ self.goto(this.get(event.target, true));
2224
+ });
2225
+ }
2226
+ if (typeof this.tabs === "object" && typeof this.tabs.render === "function") {
2227
+ this.tabs.render(query2(this.box).find("#form_" + this.name + "_tabs").get(0));
2228
+ if (this.tabs.active) this.tabs.click(this.tabs.active);
2229
+ }
2230
+ edata.finish();
2231
+ this.resize();
2232
+ const url = typeof this.url !== "object" ? this.url : this.url.get;
2233
+ if (url && this.recid != null) {
2234
+ ;
2235
+ this.request().catch((_error) => this.refresh());
2236
+ } else {
2237
+ this.refresh();
2238
+ }
2239
+ this.last.observeResize = new ResizeObserver(() => {
2240
+ this.resize();
2241
+ });
2242
+ this.last.observeResize.observe(this.box);
2243
+ if (this.focus != -1) {
2244
+ let setCount = 0;
2245
+ const setFocus = () => {
2246
+ if (query2(self.box).find("input, select, textarea").length > 0) {
2247
+ self.setFocus();
2248
+ } else {
2249
+ setCount++;
2250
+ if (setCount < 20) setTimeout(setFocus, 50);
2251
+ }
2252
+ };
2253
+ setFocus();
2254
+ }
2255
+ return Date.now() - time;
2256
+ }
2257
+ unmount() {
2258
+ super.unmount();
2259
+ this.tabs?.unmount?.();
2260
+ this.toolbar?.unmount?.();
2261
+ this.last.observeResize?.disconnect();
2262
+ }
2263
+ destroy() {
2264
+ const edata = this.trigger("destroy", { target: this.name });
2265
+ if (edata.isCancelled === true) return;
2266
+ this.tabs?.destroy?.();
2267
+ this.toolbar?.destroy?.();
2268
+ if (query2(this.box).find("#form_" + this.name + "_tabs").length > 0) {
2269
+ this.unmount();
2270
+ }
2271
+ this.last.observeResize?.disconnect();
2272
+ delete TsUi[this.name];
2273
+ edata.finish();
2274
+ }
2275
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2276
+ setFocus(focus) {
2277
+ if (typeof focus === "undefined") {
2278
+ focus = this.focus;
2279
+ }
2280
+ let $input;
2281
+ if (TsUtils.isInt(focus)) {
2282
+ if (focus < 0) {
2283
+ return;
2284
+ }
2285
+ const inputs = query2(this.box).find("div:not(.tsg-field-helper) > input, select, textarea, div > label:nth-child(1) > [type=radio]").filter(":not(.file-input)");
2286
+ while (inputs.get(focus)?.offsetParent == null && inputs.length > focus) {
2287
+ focus++;
2288
+ }
2289
+ if (inputs.get(focus)) {
2290
+ $input = query2(inputs.get(focus));
2291
+ }
2292
+ } else if (typeof focus === "string") {
2293
+ $input = query2(this.box).find(`[name='${focus}']`);
2294
+ }
2295
+ if ($input?.length > 0) {
2296
+ $input.get(0).focus();
2297
+ }
2298
+ return $input;
2299
+ }
2300
+ };
2301
+
2302
+ export {
2303
+ TsForm
2304
+ };
2305
+ //# sourceMappingURL=chunk-GJD5NFWQ.js.map