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,1795 @@
1
+ import {
2
+ TsColor,
3
+ TsDate,
4
+ TsMenu,
5
+ TsTooltip
6
+ } from "./chunk-OFASTA2A.js";
7
+ import {
8
+ TsUtils
9
+ } from "./chunk-3NYH6545.js";
10
+ import {
11
+ TsBase,
12
+ query
13
+ } from "./chunk-DXZJHS4M.js";
14
+
15
+ // src/tsfield.ts
16
+ var query2 = query;
17
+ var TsMenu2 = TsMenu;
18
+ var TsColor2 = TsColor;
19
+ var TsDate2 = TsDate;
20
+ var TsTooltip2 = TsTooltip;
21
+ var TsField = class extends TsBase {
22
+ el;
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ selected;
25
+ // any: can be null, an object (list), or an array (enum/file)
26
+ helpers;
27
+ type;
28
+ options;
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ onClick;
31
+ // any: event callback
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ onAdd;
34
+ // any: event callback
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ onNew;
37
+ // any: event callback
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ onRemove;
40
+ // any: event callback
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ onMouseEnter;
43
+ // any: event callback
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ onMouseLeave;
46
+ // any: event callback
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ onScroll;
49
+ // any: event callback
50
+ tmp;
51
+ constructor(type, options) {
52
+ super();
53
+ if (typeof type == "string" && options == null) {
54
+ options = { type };
55
+ }
56
+ if (typeof type == "object" && options == null) {
57
+ options = TsUtils.clone(type);
58
+ }
59
+ if (typeof type == "string" && typeof options == "object") {
60
+ options.type = type;
61
+ }
62
+ const opts = options;
63
+ opts.type = String(opts.type).toLowerCase();
64
+ this.el = opts.el ?? null;
65
+ this.selected = null;
66
+ this.helpers = {};
67
+ this.type = opts.type ?? "text";
68
+ this.options = TsUtils.clone(opts);
69
+ this.onClick = opts.onClick ?? null;
70
+ this.onAdd = opts.onAdd ?? null;
71
+ this.onNew = opts.onNew ?? null;
72
+ this.onRemove = opts.onRemove ?? null;
73
+ this.onMouseEnter = opts.onMouseEnter ?? null;
74
+ this.onMouseLeave = opts.onMouseLeave ?? null;
75
+ this.onScroll = opts.onScroll ?? null;
76
+ this.tmp = {};
77
+ delete this.options.type;
78
+ delete this.options.onClick;
79
+ delete this.options.onMouseEnter;
80
+ delete this.options.onMouseLeave;
81
+ delete this.options.onScroll;
82
+ if (this.el) {
83
+ this.render(this.el);
84
+ }
85
+ }
86
+ render(el) {
87
+ if (!(el instanceof HTMLElement)) {
88
+ console.log("ERROR: Cannot init TsField on empty subject");
89
+ return;
90
+ }
91
+ const fieldEl = el;
92
+ fieldEl._w2field?.reset?.();
93
+ fieldEl._w2field = this;
94
+ this.el = fieldEl;
95
+ this.init();
96
+ }
97
+ init() {
98
+ let options = this.options;
99
+ let defaults;
100
+ if (this.el == null || !["INPUT", "TEXTAREA"].includes(this.el.tagName.toUpperCase())) {
101
+ console.log("ERROR: TsField could only be applied to INPUT or TEXTAREA.", this.el);
102
+ return;
103
+ }
104
+ const _fieldEl = this.el;
105
+ switch (this.type) {
106
+ case "text":
107
+ case "int":
108
+ case "float":
109
+ case "money":
110
+ case "currency":
111
+ case "percent":
112
+ case "alphanumeric":
113
+ case "bin":
114
+ case "hex": {
115
+ defaults = {
116
+ min: null,
117
+ max: null,
118
+ step: 1,
119
+ autoFormat: true,
120
+ autoCorrect: true,
121
+ currency: {
122
+ prefix: TsUtils.settings.currencyPrefix,
123
+ suffix: TsUtils.settings.currencySuffix,
124
+ precision: TsUtils.settings.currencyPrecision
125
+ },
126
+ decimalSymbol: TsUtils.settings.decimalSymbol,
127
+ groupSymbol: TsUtils.settings.groupSymbol,
128
+ arrows: false,
129
+ keyboard: true,
130
+ precision: null,
131
+ prefix: "",
132
+ suffix: ""
133
+ };
134
+ this.options = TsUtils.extend({}, defaults, options);
135
+ options = this.options;
136
+ options.numberRE = new RegExp("[" + options.groupSymbol + "]", "g");
137
+ options.moneyRE = new RegExp("[" + options.currency.prefix + options.currency.suffix + options.groupSymbol + "]", "g");
138
+ options.percentRE = new RegExp("[" + options.groupSymbol + "%]", "g");
139
+ if (["text", "alphanumeric", "hex", "bin"].includes(this.type)) {
140
+ options.arrows = false;
141
+ options.keyboard = false;
142
+ }
143
+ break;
144
+ }
145
+ case "color": {
146
+ const size = parseInt(getComputedStyle(this.el)["font-size"] ?? "12") || 12;
147
+ defaults = {
148
+ prefix: "#",
149
+ suffix: `<div style="width: ${size}px; height: ${size}px; margin-top: -2px;
150
+ position: relative; top: 50%; transform: translateY(-50%);">&#160;</div>`,
151
+ arrows: false,
152
+ advanced: null,
153
+ // open advanced by default
154
+ transparent: true
155
+ };
156
+ this.options = TsUtils.extend({}, defaults, options);
157
+ options = this.options;
158
+ break;
159
+ }
160
+ case "date": {
161
+ defaults = {
162
+ format: TsUtils.settings.dateFormat,
163
+ // date format
164
+ keyboard: true,
165
+ // if true, allows to select date with format
166
+ autoCorrect: true,
167
+ // correc date or shows the error
168
+ start: null,
169
+ // first date allowed to select
170
+ end: null,
171
+ // last date allowed to select
172
+ blockDates: [],
173
+ // array of blocked dates
174
+ blockWeekdays: [],
175
+ // blocked weekdays 0 - sunday, 1 - monday, etc
176
+ colored: {},
177
+ // ex: { '3/13/2022': 'bg-color|text-color' }
178
+ btnNow: true
179
+ // if true, displays Now button
180
+ };
181
+ this.options = TsUtils.extend({ type: "date" }, defaults, options);
182
+ options = this.options;
183
+ if (query2(this.el).attr("placeholder") == null) {
184
+ query2(this.el).attr("placeholder", options.format);
185
+ }
186
+ break;
187
+ }
188
+ case "time": {
189
+ defaults = {
190
+ format: TsUtils.settings.timeFormat,
191
+ keyboard: true,
192
+ autoCorrect: true,
193
+ start: null,
194
+ end: null,
195
+ btnNow: true,
196
+ noMinutes: false
197
+ };
198
+ this.options = TsUtils.extend({ type: "time" }, defaults, options);
199
+ options = this.options;
200
+ if (query2(this.el).attr("placeholder") == null) {
201
+ query2(this.el).attr("placeholder", options.format);
202
+ }
203
+ break;
204
+ }
205
+ case "datetime": {
206
+ defaults = {
207
+ format: TsUtils.settings.dateFormat + "|" + TsUtils.settings.timeFormat,
208
+ keyboard: true,
209
+ autoCorrect: true,
210
+ start: null,
211
+ end: null,
212
+ startTime: null,
213
+ endTime: null,
214
+ blockDates: [],
215
+ // array of blocked dates
216
+ blockWeekdays: [],
217
+ // blocked weekdays 0 - sunday, 1 - monday, etc
218
+ colored: {},
219
+ // ex: { '3/13/2022': 'bg-color|text-color' }
220
+ btnNow: true,
221
+ noMinutes: false
222
+ };
223
+ this.options = TsUtils.extend({ type: "datetime" }, defaults, options);
224
+ options = this.options;
225
+ if (query2(this.el).attr("placeholder") == null) {
226
+ query2(this.el).attr("placeholder", options.placeholder || options.format);
227
+ }
228
+ break;
229
+ }
230
+ case "list":
231
+ case "combo": {
232
+ defaults = {
233
+ items: [],
234
+ // array of items, can be a function
235
+ selected: {},
236
+ // selected item
237
+ itemMap: null,
238
+ // can be { id: 'id', text: 'text' } to specify field mapping for an item
239
+ match: "begins",
240
+ // ['contains', 'is', 'begins', 'ends']
241
+ filter: true,
242
+ // weather to filter at all
243
+ compare: null,
244
+ // compare function for filtering
245
+ prefix: "",
246
+ // prefix for input
247
+ suffix: "",
248
+ // sufix for input
249
+ icon: null,
250
+ // icon class for selected item
251
+ iconStyle: "",
252
+ // icon style for selected item
253
+ // -- remote items --
254
+ url: null,
255
+ // remove data source for items
256
+ method: null,
257
+ // default comes from TsUtils.settings.dataType
258
+ postData: {},
259
+ // additional data to submit to URL
260
+ recId: null,
261
+ // map retrieved data from url to id, can be string or function
262
+ recText: null,
263
+ // map retrieved data from url to text, can be string or function
264
+ debounce: 250,
265
+ // number of ms to wait before sending server call on search
266
+ minLength: 1,
267
+ // min number of chars when trigger search
268
+ cacheMax: 250,
269
+ // -- drop items --
270
+ renderDrop: null,
271
+ // render function for drop down item
272
+ maxDropHeight: 350,
273
+ // max height for drop down menu
274
+ maxDropWidth: null,
275
+ // if null then auto set
276
+ minDropWidth: null,
277
+ // if null then auto set
278
+ // -- misc --
279
+ markSearch: false,
280
+ // if true, highlights search phrase
281
+ align: "both",
282
+ // align with the input ['left', 'right', 'both', 'none']
283
+ altRows: true,
284
+ // alternate row color for drop itesm
285
+ openOnFocus: false,
286
+ // if true, shows drop items on focus
287
+ hideSelected: false,
288
+ // hide selected item from drop down
289
+ msgNoItems: "No matches",
290
+ msgSearch: "Type to search...",
291
+ // -- events --
292
+ onSearch: null,
293
+ // when search needs to be performed
294
+ onRequest: null,
295
+ // when request is submitted
296
+ onLoad: null,
297
+ // when data is received
298
+ onError: null
299
+ // when data fails to load due to server error
300
+ };
301
+ if (typeof options.items == "function") {
302
+ options._items_fun = options.items;
303
+ }
304
+ options.items = TsUtils.normMenu.call(this, options.items, options);
305
+ if (this.type === "list") {
306
+ query2(this.el).addClass("tsg-select");
307
+ if (!TsUtils.isPlainObject(options.selected) && Array.isArray(options.items)) {
308
+ options.items.forEach((item) => {
309
+ if (item && item.id === options.selected) {
310
+ options.selected = TsUtils.clone(item);
311
+ }
312
+ });
313
+ }
314
+ }
315
+ options = TsUtils.extend({}, defaults, options);
316
+ const valid = ["is", "begins", "contains", "ends"];
317
+ if (!valid.includes(options.match)) {
318
+ console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(", ")}.`);
319
+ }
320
+ this.options = options;
321
+ if (!TsUtils.isPlainObject(options.selected)) options.selected = {};
322
+ this.selected = options.selected;
323
+ query2(this.el).attr("autocapitalize", "off").attr("autocomplete", "off").attr("autocorrect", "off").attr("spellcheck", "false");
324
+ if (options.selected.text != null) {
325
+ query2(this.el).val(options.selected.text);
326
+ }
327
+ break;
328
+ }
329
+ case "enum": {
330
+ defaults = {
331
+ items: [],
332
+ // id, text, tooltip, icon
333
+ selected: [],
334
+ itemMap: null,
335
+ // can be { id: 'id', text: 'text' } to specify field mapping for an item
336
+ max: 0,
337
+ // max number of selected items, 0 - unlimited
338
+ match: "begins",
339
+ // ['contains', 'is', 'begins', 'ends']
340
+ filter: true,
341
+ // if true, will apply filtering
342
+ compare: null,
343
+ // compare function for filtering
344
+ // -- remote items --
345
+ url: null,
346
+ // remove source for items
347
+ method: null,
348
+ // default httpMethod
349
+ postData: {},
350
+ recId: null,
351
+ // map retrieved data from url to id, can be string or function
352
+ recText: null,
353
+ // map retrieved data from url to text, can be string or function
354
+ debounce: 250,
355
+ // number of ms to wait before sending server call on search
356
+ minLength: 1,
357
+ // min number of chars when trigger search
358
+ cacheMax: 250,
359
+ // -- item and drop items --
360
+ maxItemWidth: 250,
361
+ // max width for a single item
362
+ maxDropHeight: 350,
363
+ // max height for drop down menu
364
+ maxDropWidth: null,
365
+ // if null then auto set
366
+ renderItem: null,
367
+ // render selected item
368
+ renderDrop: null,
369
+ // render function for drop down item
370
+ // -- misc --
371
+ style: "",
372
+ // style for container div
373
+ openOnFocus: false,
374
+ // if true, opens drop down on focus
375
+ markSearch: false,
376
+ // if true, highlights search phrase
377
+ align: "both",
378
+ // align with the input ['left', 'right', 'both', 'none']
379
+ altRows: true,
380
+ // if ture, will use alternate row colors
381
+ hideSelected: true,
382
+ // hide selected items from drop down
383
+ msgNoItems: "No matches",
384
+ msgSearch: "Type to search...",
385
+ // -- events --
386
+ onAdd: null,
387
+ // when item is selected from drop down
388
+ onNew: null,
389
+ // when new item should be added
390
+ onRemove: null,
391
+ // when item is removed
392
+ onSearch: null,
393
+ // when search is triggered
394
+ onClick: null,
395
+ // when item is clicked
396
+ onRequest: null,
397
+ // when data is requested
398
+ onLoad: null,
399
+ // when data is received
400
+ onError: null,
401
+ // when data fails to load due to server error
402
+ onScroll: null,
403
+ // when div with selected items is scrolled
404
+ onMouseEnter: null,
405
+ // when mouse enters item
406
+ onMouseLeave: null
407
+ // when mouse leaves item
408
+ };
409
+ options = TsUtils.extend({}, defaults, options, { suffix: "" });
410
+ if (typeof options.items == "function") {
411
+ options._items_fun = options.items;
412
+ }
413
+ const valid = ["is", "begins", "contains", "ends"];
414
+ if (!valid.includes(options.match)) {
415
+ console.log(`ERROR: invalid value "${options.match}" for option.match. It should be one of following: ${valid.join(", ")}.`);
416
+ }
417
+ options.items = TsUtils.normMenu.call(this, options.items, options);
418
+ options.selected = TsUtils.normMenu.call(this, options.selected, options);
419
+ this.options = options;
420
+ if (!Array.isArray(options.selected)) options.selected = [];
421
+ this.selected = options.selected;
422
+ break;
423
+ }
424
+ case "file": {
425
+ defaults = {
426
+ selected: [],
427
+ // array of selected files
428
+ max: 0,
429
+ // max number of selected files, 0 - unlim
430
+ maxSize: 0,
431
+ // max size of all files, 0 - unlimited
432
+ maxFileSize: 0,
433
+ // max size of a single file, 0 -unlimited
434
+ renderItem: null,
435
+ // render function fo the selected item
436
+ // -- misc --
437
+ maxItemWidth: 250,
438
+ // max width for a single item
439
+ maxDropHeight: 350,
440
+ // max height for drop down menu
441
+ maxDropWidth: null,
442
+ // if null then auto set
443
+ readContent: true,
444
+ // if true, it will readAsDataURL content of the file
445
+ showErrors: true,
446
+ // if not true, will show errors
447
+ align: "both",
448
+ // align with the input ['left', 'right', 'both', 'none']
449
+ altRows: true,
450
+ // alternate row color for drop itesm
451
+ style: "",
452
+ // style for container div
453
+ // -- events --
454
+ onClick: null,
455
+ // when item is clicked
456
+ onAdd: null,
457
+ // when item is added
458
+ onRemove: null,
459
+ // when item is removed
460
+ onMouseEnter: null,
461
+ // when item is mouse over
462
+ onMouseLeave: null
463
+ // when item is mouse out
464
+ };
465
+ options = TsUtils.extend({}, defaults, options);
466
+ this.options = options;
467
+ if (!Array.isArray(options.selected)) options.selected = [];
468
+ this.selected = options.selected;
469
+ if (query2(this.el).attr("placeholder") == null) {
470
+ query2(this.el).attr("placeholder", TsUtils.lang("Attach files by dragging and dropping or Click to Select"));
471
+ }
472
+ break;
473
+ }
474
+ default: {
475
+ console.log(`ERROR: field type "${this.type}" is not supported.`);
476
+ break;
477
+ }
478
+ }
479
+ const $elInit = query2(this.el);
480
+ $elInit.css("box-sizing", "border-box");
481
+ $elInit.addClass("TsField tsg-input").off(".TsField").on("change.TsField", (event) => {
482
+ this.change(event);
483
+ }).on("click.TsField", (event) => {
484
+ this.click(event);
485
+ }).on("focus.TsField", (event) => {
486
+ this.focus(event);
487
+ }).on("blur.TsField", (event) => {
488
+ if (this.type !== "list") this.blur(event);
489
+ }).on("keydown.TsField", (event) => {
490
+ this.keyDown(event);
491
+ }).on("keyup.TsField", (event) => {
492
+ this.keyUp(event);
493
+ });
494
+ this.addPrefix();
495
+ this.addSuffix();
496
+ this.addSearch();
497
+ this.addMultiSearch();
498
+ this.change(new Event("change"));
499
+ }
500
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
501
+ get() {
502
+ let ret;
503
+ if (["list", "enum", "file"].indexOf(this.type) !== -1) {
504
+ ret = this.selected;
505
+ } else {
506
+ ret = query2(this.el).val();
507
+ }
508
+ return ret;
509
+ }
510
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
511
+ set(val, append) {
512
+ if (["list", "enum", "file"].indexOf(this.type) !== -1) {
513
+ const overlay = TsMenu2.get(this.el.id + "_menu");
514
+ overlay?.hide();
515
+ if (this.type !== "list" && append) {
516
+ if (!Array.isArray(this.selected)) this.selected = [];
517
+ this.selected.push(val);
518
+ if (overlay) overlay.options.selected = this.selected;
519
+ query2(this.el).trigger("input").trigger("change");
520
+ } else {
521
+ if (val == null) val = [];
522
+ const it = this.type === "enum" && !Array.isArray(val) ? [val] : val;
523
+ this.selected = it;
524
+ query2(this.el).trigger("input").trigger("change");
525
+ }
526
+ this.refresh();
527
+ } else {
528
+ query2(this.el).val(val);
529
+ }
530
+ }
531
+ setIndex(ind, append) {
532
+ if (["list", "enum"].indexOf(this.type) !== -1) {
533
+ const overlay = TsMenu2.get(this.el.id + "_menu");
534
+ overlay?.hide();
535
+ const items = this.options.items;
536
+ if (items && items[ind]) {
537
+ if (this.type == "list") {
538
+ this.selected = items[ind];
539
+ }
540
+ if (this.type == "enum") {
541
+ if (!append) this.selected = [];
542
+ this.selected.push(items[ind]);
543
+ }
544
+ if (overlay) overlay.options.selected = this.selected;
545
+ query2(this.el).trigger("input").trigger("change");
546
+ this.refresh();
547
+ return true;
548
+ }
549
+ }
550
+ return false;
551
+ }
552
+ refresh() {
553
+ const options = this.options;
554
+ const time = Date.now();
555
+ const styles = getComputedStyle(this.el);
556
+ if (this.type == "color") {
557
+ let color = this.el.value;
558
+ if (color.substr(0, 1) != "#" && color.substr(0, 3) != "rgb") {
559
+ color = "#" + color;
560
+ }
561
+ query2(this.helpers.suffix).find(":scope > div").css("background-color", color);
562
+ }
563
+ if (this.type == "list") {
564
+ if (this.helpers.prefix) query2(this.helpers.prefix).hide();
565
+ if (!this.helpers.search) return Date.now() - time;
566
+ if (this.selected == null && options.icon) {
567
+ options.prefix = `
568
+ <span class="tsg-icon ${options.icon} "style="cursor: pointer; font-size: 14px;
569
+ display: inline-block; margin-top: -1px; color: #7F98AD; ${options.iconStyle}">
570
+ </span>`;
571
+ this.addPrefix();
572
+ } else {
573
+ options.prefix = "";
574
+ this.addPrefix();
575
+ }
576
+ const focus = query2(this.helpers.search_focus);
577
+ const icon = query2(focus.get(0).previousElementSibling);
578
+ focus.css({ outline: "none" });
579
+ if (focus.val() === "") {
580
+ focus.css("opacity", 0);
581
+ icon.css("opacity", 0);
582
+ if (this.selected?.id != null) {
583
+ const text = this.selected.text;
584
+ const ind = this.findItemIndex(options.items, this.selected.id);
585
+ if (text != null) {
586
+ ;
587
+ query2(this.el).val(TsUtils.lang(text)).data({
588
+ selected: text,
589
+ selectedIndex: ind[0]
590
+ });
591
+ }
592
+ } else {
593
+ this.el.value = "";
594
+ query2(this.el).removeData("selected selectedIndex");
595
+ }
596
+ } else {
597
+ focus.css("opacity", 1);
598
+ icon.css("opacity", 1);
599
+ query2(this.el).val("");
600
+ setTimeout(() => {
601
+ if (this.helpers.prefix) query2(this.helpers.prefix).hide();
602
+ if (options.icon) {
603
+ focus.css("margin-left", "17px");
604
+ query2(this.helpers.search).find(".tsg-icon-search").addClass("show-search");
605
+ } else {
606
+ focus.css("margin-left", "0px");
607
+ query2(this.helpers.search).find(".tsg-icon-search").removeClass("show-search");
608
+ }
609
+ }, 1);
610
+ }
611
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) {
612
+ setTimeout(() => {
613
+ if (this.helpers.prefix) query2(this.helpers.prefix).css("opacity", "0.6");
614
+ if (this.helpers.suffix) query2(this.helpers.suffix).css("opacity", "0.6");
615
+ }, 1);
616
+ } else {
617
+ setTimeout(() => {
618
+ if (this.helpers.prefix) query2(this.helpers.prefix).css("opacity", "1");
619
+ if (this.helpers.suffix) query2(this.helpers.suffix).css("opacity", "1");
620
+ }, 1);
621
+ }
622
+ }
623
+ const div = this.helpers.multi;
624
+ if (["enum", "file"].includes(this.type) && div) {
625
+ let html = "";
626
+ if (Array.isArray(this.selected)) {
627
+ this.selected.forEach((it, ind) => {
628
+ if (it == null) return;
629
+ html += `
630
+ <div class="li-item" index="${ind}" style="max-width: ${parseInt(options.maxItemWidth)}px; ${it.style ? it.style : ""}">
631
+ ${typeof options.renderItem === "function" ? options.renderItem(it, ind, `<div class="tsg-list-remove" index="${ind}">&#160;&#160;</div>`) : `
632
+ ${it.icon ? `<span class="tsg-icon ${it.icon}"></span>` : ""}
633
+ <div class="tsg-list-remove" index="${ind}">&#160;&#160;</div>
634
+ ${(this.type === "enum" ? it.text : it.name) ?? it.id ?? it}
635
+ ${it.size ? `<span class="file-size"> - ${TsUtils.formatSize(it.size)}</span>` : ""}
636
+ `}
637
+ </div>`;
638
+ });
639
+ }
640
+ const ul = div.find(".tsg-multi-items");
641
+ if (options.style) {
642
+ div.attr("style", div.attr("style") + ";" + options.style);
643
+ }
644
+ query2(this.el).css("z-index", "-1");
645
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) {
646
+ setTimeout(() => {
647
+ div.get(0).scrollTop = 0;
648
+ div.addClass("tsg-readonly").find(".li-item").css("opacity", "0.9");
649
+ div.find(".li-item").parent().find(".li-search").hide().find("input").prop("readOnly", true).closest(".tsg-multi-items").find(".tsg-list-remove").hide();
650
+ }, 1);
651
+ } else {
652
+ setTimeout(() => {
653
+ div.removeClass("tsg-readonly").find(".li-item").css("opacity", "1");
654
+ div.find(".li-item").parent().find(".li-search").show().find("input").prop("readOnly", false).closest(".tsg-multi-items").find(".tsg-list-remove").show();
655
+ }, 1);
656
+ }
657
+ if (this.selected?.length > 0) {
658
+ query2(this.el).attr("placeholder", "");
659
+ }
660
+ div.find(".tsg-enum-placeholder").remove();
661
+ ul.find(".li-item").remove();
662
+ if (html !== "") {
663
+ ul.prepend(html);
664
+ } else if (query2(this.el).attr("placeholder") != null && div.find("input").val() === "") {
665
+ const style = TsUtils.stripSpaces(`
666
+ padding-top: ${styles["padding-top"]};
667
+ padding-left: ${styles["padding-left"]};
668
+ box-sizing: ${styles["box-sizing"]};
669
+ line-height: ${styles["line-height"]};
670
+ font-size: ${styles["font-size"]};
671
+ font-family: ${styles["font-family"]};
672
+ `);
673
+ div.prepend(`<div class="tsg-enum-placeholder" style="${style}">${query2(this.el).attr("placeholder")}</div>`);
674
+ }
675
+ div.off(".w2item").on("scroll.w2item", (event) => {
676
+ const edata = this.trigger("scroll", { target: this.el, originalEvent: event });
677
+ if (edata.isCancelled === true) return;
678
+ TsTooltip2.hide(this.el.id + "_preview");
679
+ edata.finish();
680
+ }).find(".li-item").on("click.w2item", (event) => {
681
+ const mouseEvent = event;
682
+ const target = query2(mouseEvent.target).closest(".li-item");
683
+ const index = target.attr("index");
684
+ const item = index != null ? this.selected[Number(index)] : void 0;
685
+ if (query2(target).hasClass("li-search")) return;
686
+ mouseEvent.stopPropagation();
687
+ let edata;
688
+ if (query2(mouseEvent.target).hasClass("tsg-list-remove")) {
689
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
690
+ edata = this.trigger("remove", { target: this.el, originalEvent: mouseEvent, item });
691
+ if (edata.isCancelled === true) return;
692
+ const transfer = new DataTransfer();
693
+ const input = query2(mouseEvent.target).closest(".tsg-list").find("input.file-input").get(0);
694
+ if (input) {
695
+ Array.from(input.files ?? []).filter((f) => f.name != item.name).forEach((f) => transfer.items.add(f));
696
+ input.files = transfer.files;
697
+ }
698
+ if (index != null) this.selected.splice(Number(index), 1);
699
+ query2(this.el).trigger("input").trigger("change");
700
+ query2(mouseEvent.target).remove();
701
+ } else {
702
+ edata = this.trigger("click", { target: this.el, originalEvent: mouseEvent.originalEvent, item });
703
+ if (edata.isCancelled === true) return;
704
+ let preview = item.tooltip;
705
+ if (this.type === "file") {
706
+ if (/image/i.test(item.type)) {
707
+ preview = `
708
+ <div class="tsg-file-preview">
709
+ <img src="${item.content ? "data:" + item.type + ";base64," + item.content : ""}"
710
+ style="max-width: 300px">
711
+ </div>`;
712
+ }
713
+ preview += `
714
+ <div class="tsg-file-info">
715
+ <div class="file-caption">${TsUtils.lang("Name")}:</div>
716
+ <div class="file-value">${item.name}</div>
717
+ <div class="file-caption">${TsUtils.lang("Size")}:</div>
718
+ <div class="file-value">${TsUtils.formatSize(item.size)}</div>
719
+ <div class="file-caption">${TsUtils.lang("Type")}:</div>
720
+ <div class="file-value file-type">${item.type}</div>
721
+ <div class="file-caption">${TsUtils.lang("Modified")}:</div>
722
+ <div class="file-value">${TsUtils.date(item.modified)}</div>
723
+ </div>`;
724
+ }
725
+ if (preview) {
726
+ const name = this.el.id + "_preview";
727
+ TsTooltip2.show({
728
+ name,
729
+ anchor: target.get(0),
730
+ html: preview,
731
+ hideOn: ["doc-click"],
732
+ class: ""
733
+ }).show((_event) => {
734
+ const $img = query2(`#w2overlay-${name} img`);
735
+ $img.on("load", function(_event2) {
736
+ const w = this.clientWidth;
737
+ const h = this.clientHeight;
738
+ if (w < 300 && h < 300) return;
739
+ if (w >= h && w > 300) query2(this).css("width", "300px");
740
+ if (w < h && h > 300) query2(this).css("height", "300px");
741
+ }).on("error", function(_event2) {
742
+ this.style.display = "none";
743
+ });
744
+ });
745
+ }
746
+ }
747
+ edata.finish();
748
+ }).on("mouseenter.w2item", (event) => {
749
+ const mouseEvent = event;
750
+ const target = query2(mouseEvent.target).closest(".li-item");
751
+ if (query2(target).hasClass("li-search")) return;
752
+ const idx = query2(mouseEvent.target).attr("index");
753
+ const item = idx != null ? this.selected[Number(idx)] : void 0;
754
+ const edata = this.trigger("mouseEnter", { target: this.el, originalEvent: mouseEvent, item });
755
+ if (edata.isCancelled === true) return;
756
+ edata.finish();
757
+ }).on("mouseleave.w2item", (event) => {
758
+ const mouseEvent = event;
759
+ const target = query2(mouseEvent.target).closest(".li-item");
760
+ if (query2(target).hasClass("li-search")) return;
761
+ const idx = query2(mouseEvent.target).attr("index");
762
+ const item = idx != null ? this.selected[Number(idx)] : void 0;
763
+ const edata = this.trigger("mouseLeave", { target: this.el, originalEvent: mouseEvent, item });
764
+ if (edata.isCancelled === true) return;
765
+ edata.finish();
766
+ });
767
+ if (this.type === "enum") {
768
+ const search = this.helpers.multi?.find("input");
769
+ search?.css({ width: "15px" });
770
+ } else {
771
+ this.helpers.multi?.find(".li-search").hide();
772
+ }
773
+ this.resize();
774
+ }
775
+ return Date.now() - time;
776
+ }
777
+ // resizing width of list, enum, file controls
778
+ resize() {
779
+ const width = this.el.clientWidth;
780
+ const styles = getComputedStyle(this.el);
781
+ const focus = this.helpers.search;
782
+ const multi = this.helpers.multi;
783
+ const suffix = this.helpers.suffix;
784
+ const prefix = this.helpers.prefix;
785
+ if (focus) {
786
+ query2(focus).css("width", width);
787
+ }
788
+ if (multi) {
789
+ query2(multi).css("width", width - parseInt(styles["margin-left"], 10) - parseInt(styles["margin-right"], 10));
790
+ }
791
+ if (suffix) {
792
+ this.addSuffix();
793
+ }
794
+ if (prefix) {
795
+ this.addPrefix();
796
+ }
797
+ const div = this.helpers.multi;
798
+ if (["enum", "file"].includes(this.type) && div) {
799
+ query2(this.el).css("height", "");
800
+ let cntHeight = query2(div).find(":scope div.tsg-multi-items").get(0).clientHeight + 5;
801
+ if (cntHeight < 20) cntHeight = 20;
802
+ if (this.tmp["max-height"] != null && cntHeight > this.tmp["max-height"]) {
803
+ cntHeight = this.tmp["max-height"] ?? cntHeight;
804
+ }
805
+ if (this.tmp["min-height"] != null && cntHeight < this.tmp["min-height"]) {
806
+ cntHeight = this.tmp["min-height"] ?? cntHeight;
807
+ }
808
+ const inpHeight = TsUtils.getSize(this.el, "height") - 2;
809
+ if (inpHeight > cntHeight) cntHeight = inpHeight;
810
+ query2(div).css({
811
+ "height": cntHeight + "px",
812
+ overflow: cntHeight == this.tmp["max-height"] ? "auto" : "hidden"
813
+ });
814
+ query2(div).css("height", cntHeight + "px");
815
+ query2(this.el).css({ "height": cntHeight + "px" });
816
+ }
817
+ this.tmp.current_width = width;
818
+ }
819
+ reset() {
820
+ if (this.tmp != null) {
821
+ query2(this.el).css("height", "");
822
+ ["padding-left", "padding-right", "background-color", "border-color"].forEach((prop) => {
823
+ if (this.tmp && this.tmp["old-" + prop] != null) {
824
+ query2(this.el).css(prop, this.tmp["old-" + prop]);
825
+ delete this.tmp["old-" + prop];
826
+ }
827
+ });
828
+ clearInterval(this.tmp.sizeTimer);
829
+ }
830
+ ;
831
+ query2(this.el).val(this.clean(query2(this.el).val())).removeClass("TsField tsg-input").removeData("selected selectedIndex").off(".TsField");
832
+ Object.keys(this.helpers).forEach((key) => {
833
+ query2(this.helpers[key]).remove();
834
+ });
835
+ this.helpers = {};
836
+ delete this.el._w2field;
837
+ }
838
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
839
+ clean(val) {
840
+ if (typeof val === "number") {
841
+ return val;
842
+ }
843
+ const options = this.options;
844
+ val = String(val).trim();
845
+ if (["int", "float", "money", "currency", "percent"].includes(this.type)) {
846
+ if (typeof val === "string") {
847
+ if (options.autoFormat) {
848
+ if (["money", "currency"].includes(this.type)) {
849
+ val = String(val).replace(options.moneyRE, "");
850
+ }
851
+ if (this.type === "percent") {
852
+ val = String(val).replace(options.percentRE, "");
853
+ }
854
+ if (["int", "float"].includes(this.type)) {
855
+ val = String(val).replace(options.numberRE, "");
856
+ }
857
+ }
858
+ const esc_gsroupSymbol = options.groupSymbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
859
+ const esc_decimalSymbol = options.decimalSymbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
860
+ val = val.replace(/\s+/g, "").replace(new RegExp(esc_gsroupSymbol, "g"), "").replace(new RegExp(esc_decimalSymbol, "g"), ".");
861
+ }
862
+ if (val !== "" && TsUtils.isFloat(val)) val = Number(val);
863
+ else val = "";
864
+ }
865
+ return val;
866
+ }
867
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
868
+ format(val) {
869
+ const options = this.options;
870
+ if (options.autoFormat && val !== "") {
871
+ switch (this.type) {
872
+ case "money":
873
+ case "currency":
874
+ val = TsUtils.formatNumber(val, options.currency.precision, true);
875
+ if (val !== "") val = options.currency.prefix + val + options.currency.suffix;
876
+ break;
877
+ case "percent":
878
+ val = TsUtils.formatNumber(val, options.precision, true);
879
+ if (val !== "") val += "%";
880
+ break;
881
+ case "float":
882
+ val = TsUtils.formatNumber(val, options.precision, true);
883
+ break;
884
+ case "int":
885
+ val = TsUtils.formatNumber(val, 0, true);
886
+ break;
887
+ }
888
+ const group = 1e3.toLocaleString(TsUtils.settings.locale, { useGrouping: true }).slice(1, 2);
889
+ if (group !== this.options.groupSymbol) {
890
+ val = val.replaceAll(group, this.options.groupSymbol);
891
+ }
892
+ }
893
+ return val;
894
+ }
895
+ change(event) {
896
+ if (["int", "float", "money", "currency", "percent"].indexOf(this.type) !== -1) {
897
+ const val = query2(this.el).val();
898
+ const new_val = this.format(this.clean(query2(this.el).val()));
899
+ if (val !== "" && val != new_val) {
900
+ query2(this.el).val(new_val);
901
+ event.stopPropagation();
902
+ event.preventDefault();
903
+ return false;
904
+ }
905
+ }
906
+ if (this.type === "color") {
907
+ let color = query2(this.el).val();
908
+ if (color.substr(0, 3).toLowerCase() !== "rgb") {
909
+ color = "#" + color;
910
+ const len = query2(this.el).val().length;
911
+ if (len !== 8 && len !== 6 && len !== 3) color = "";
912
+ }
913
+ const next = query2(this.el).get(0).nextElementSibling;
914
+ query2(next).find("div").css("background-color", color);
915
+ if (query2(this.el).hasClass("has-focus")) {
916
+ this.updateOverlay();
917
+ }
918
+ }
919
+ if (["list", "enum", "file"].indexOf(this.type) !== -1) {
920
+ this.refresh();
921
+ }
922
+ if (["date", "time", "datetime"].indexOf(this.type) !== -1) {
923
+ let tmp = parseInt(this.el.value);
924
+ if (TsUtils.isInt(this.el.value) && tmp > 3e3) {
925
+ if (this.type === "time") tmp = TsUtils.formatTime(new Date(tmp), this.options.format);
926
+ if (this.type === "date") tmp = TsUtils.formatDate(new Date(tmp), this.options.format);
927
+ if (this.type === "datetime") tmp = TsUtils.formatDateTime(new Date(tmp), this.options.format);
928
+ query2(this.el).val(String(tmp)).trigger("input").trigger("change");
929
+ }
930
+ }
931
+ }
932
+ click(event) {
933
+ if (["list", "combo", "enum"].includes(this.type)) {
934
+ if (!query2(this.el).hasClass("has-focus")) {
935
+ this.focus(event);
936
+ }
937
+ if (this.type == "list" || this.type == "combo") {
938
+ if (!this.tmp.openedOnFocus) {
939
+ const name = this.el.id + "_menu";
940
+ const overlay = TsMenu2.get(name);
941
+ if (overlay?.displayed) {
942
+ TsMenu2.hide(name);
943
+ } else {
944
+ this.updateOverlay();
945
+ }
946
+ }
947
+ delete this.tmp.openedOnFocus;
948
+ if (this.type == "list") {
949
+ event.stopPropagation();
950
+ }
951
+ }
952
+ if (this.type == "enum") {
953
+ this.updateOverlay();
954
+ }
955
+ }
956
+ if (["date", "time", "datetime", "color"].includes(this.type)) {
957
+ this.updateOverlay();
958
+ }
959
+ }
960
+ focus(event) {
961
+ if (this.type == "list" && document.activeElement == this.el) {
962
+ this.helpers.search_focus?.focus();
963
+ if (event.showMenu !== false && this.options.openOnFocus !== false && query2(this.el).hasClass("has-focus") && !this.tmp.overlay?.overlay?.displayed) {
964
+ setTimeout(() => {
965
+ this.tmp.openedOnFocus = true;
966
+ this.updateOverlay();
967
+ }, 0);
968
+ }
969
+ return;
970
+ }
971
+ if (["color", "date", "time", "datetime"].indexOf(this.type) !== -1) {
972
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
973
+ this.updateOverlay();
974
+ }
975
+ if (["list", "combo", "enum"].indexOf(this.type) !== -1) {
976
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) {
977
+ query2(this.el).addClass("has-focus");
978
+ return;
979
+ }
980
+ if (typeof this.options._items_fun == "function") {
981
+ ;
982
+ this.options.items = TsUtils.normMenu.call(this, this.options._items_fun, this.options);
983
+ }
984
+ if (this.helpers.search) {
985
+ const search = this.helpers.search_focus;
986
+ if (search) {
987
+ search.value = "";
988
+ search.select();
989
+ }
990
+ }
991
+ if (this.type == "enum") {
992
+ const search = query2(this.el.previousElementSibling).find(".li-search input").get(0);
993
+ if (document.activeElement !== search) {
994
+ search.focus();
995
+ }
996
+ }
997
+ this.resize();
998
+ if (event.showMenu !== false && this.options.openOnFocus !== false && query2(this.el).hasClass("has-focus") && !this.tmp.overlay?.overlay?.displayed) {
999
+ setTimeout(() => {
1000
+ this.tmp.openedOnFocus = true;
1001
+ this.updateOverlay();
1002
+ }, 0);
1003
+ }
1004
+ }
1005
+ if (this.type == "file") {
1006
+ const prev = query2(this.el).get(0).previousElementSibling;
1007
+ query2(prev).addClass("has-focus");
1008
+ }
1009
+ query2(this.el).addClass("has-focus");
1010
+ }
1011
+ blur(_event) {
1012
+ const val = query2(this.el).val().trim();
1013
+ query2(this.el).removeClass("has-focus");
1014
+ if (["int", "float", "money", "currency", "percent"].includes(this.type)) {
1015
+ if (val !== "") {
1016
+ let newVal = val;
1017
+ let error = "";
1018
+ if (!this.isStrValid(val)) {
1019
+ newVal = "";
1020
+ } else {
1021
+ const rVal = this.clean(val);
1022
+ const options = this.options;
1023
+ if (options.min != null && rVal < options.min) {
1024
+ newVal = options.min;
1025
+ error = `Should be >= ${options.min}`;
1026
+ }
1027
+ if (options.max != null && rVal > options.max) {
1028
+ newVal = options.max;
1029
+ error = `Should be <= ${options.max}`;
1030
+ }
1031
+ }
1032
+ if (this.options.autoCorrect) {
1033
+ ;
1034
+ query2(this.el).val(newVal).trigger("input").trigger("change");
1035
+ if (error) {
1036
+ TsTooltip2.show({
1037
+ name: this.el.id + "_error",
1038
+ anchor: this.el,
1039
+ html: error
1040
+ });
1041
+ setTimeout(() => {
1042
+ TsTooltip2.hide(this.el.id + "_error");
1043
+ }, 3e3);
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ if (["date", "time", "datetime"].includes(this.type) && this.options.autoCorrect) {
1049
+ if (val !== "") {
1050
+ const check = this.type == "date" ? TsUtils.isDate : this.type == "time" ? TsUtils.isTime : TsUtils.isDateTime;
1051
+ if (!TsDate2.inRange(this.el.value, this.options) || !check.bind(TsUtils)(this.el.value, this.options.format)) {
1052
+ ;
1053
+ query2(this.el).val("").trigger("input").trigger("change");
1054
+ }
1055
+ }
1056
+ }
1057
+ if (this.type === "enum") {
1058
+ ;
1059
+ query2(this.helpers.multi).find("input").val("").css("width", "15px");
1060
+ }
1061
+ if (this.type == "file") {
1062
+ const prev = this.el.previousElementSibling;
1063
+ query2(prev).removeClass("has-focus");
1064
+ }
1065
+ if (this.type === "list") {
1066
+ this.el.value = this.selected?.text ?? "";
1067
+ }
1068
+ }
1069
+ keyDown(event, extra) {
1070
+ const options = this.options;
1071
+ const key = event.keyCode || extra && extra.keyCode;
1072
+ let cancel = false;
1073
+ let val, inc, daymil, dt, newValue, newDT;
1074
+ if (["int", "float", "money", "currency", "percent", "hex", "bin", "color", "alphanumeric"].includes(this.type)) {
1075
+ if (!event.metaKey && !event.ctrlKey && !event.altKey) {
1076
+ if (!this.isStrValid(event.key ?? "1", true) && // valid & is not arrows, dot, comma, etc keys
1077
+ ![9, 8, 13, 27, 37, 38, 39, 40, 46].includes(event.keyCode)) {
1078
+ event.preventDefault();
1079
+ if (event.stopPropagation) event.stopPropagation();
1080
+ else event.cancelBubble = true;
1081
+ return false;
1082
+ }
1083
+ }
1084
+ }
1085
+ if (["int", "float", "money", "currency", "percent"].includes(this.type)) {
1086
+ if (!options.keyboard || query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1087
+ val = parseFloat(query2(this.el).val().replace(options.moneyRE, "")) || 0;
1088
+ inc = options.step;
1089
+ if (event.ctrlKey || event.metaKey) inc = options.step * 10;
1090
+ switch (key) {
1091
+ case 38:
1092
+ if (event.shiftKey) break;
1093
+ newValue = val + inc <= options.max || options.max == null ? Number((val + inc).toFixed(12)) : options.max;
1094
+ query2(this.el).val(String(newValue)).trigger("input").trigger("change");
1095
+ cancel = true;
1096
+ break;
1097
+ case 40:
1098
+ if (event.shiftKey) break;
1099
+ newValue = val - inc >= options.min || options.min == null ? Number((val - inc).toFixed(12)) : options.min;
1100
+ query2(this.el).val(String(newValue)).trigger("input").trigger("change");
1101
+ cancel = true;
1102
+ break;
1103
+ }
1104
+ if (cancel) {
1105
+ event.preventDefault();
1106
+ this.moveCaret2end();
1107
+ }
1108
+ }
1109
+ if (["date", "datetime"].includes(this.type)) {
1110
+ if (!options.keyboard || query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1111
+ const is = (this.type == "date" ? TsUtils.isDate : TsUtils.isDateTime).bind(TsUtils);
1112
+ const format = (this.type == "date" ? TsUtils.formatDate : TsUtils.formatDateTime).bind(TsUtils);
1113
+ daymil = 24 * 60 * 60 * 1e3;
1114
+ inc = 1;
1115
+ if (event.ctrlKey || event.metaKey) inc = 10;
1116
+ dt = is(query2(this.el).val(), options.format, true);
1117
+ if (!dt) {
1118
+ dt = /* @__PURE__ */ new Date();
1119
+ daymil = 0;
1120
+ }
1121
+ switch (key) {
1122
+ case 38:
1123
+ if (event.shiftKey) break;
1124
+ if (inc == 10) {
1125
+ dt.setMonth(dt.getMonth() + 1);
1126
+ } else {
1127
+ dt.setTime(dt.getTime() + daymil);
1128
+ }
1129
+ newDT = format(dt.getTime(), options.format);
1130
+ query2(this.el).val(newDT).trigger("input").trigger("change");
1131
+ cancel = true;
1132
+ break;
1133
+ case 40:
1134
+ if (event.shiftKey) break;
1135
+ if (inc == 10) {
1136
+ dt.setMonth(dt.getMonth() - 1);
1137
+ } else {
1138
+ dt.setTime(dt.getTime() - daymil);
1139
+ }
1140
+ newDT = format(dt.getTime(), options.format);
1141
+ query2(this.el).val(newDT).trigger("input").trigger("change");
1142
+ cancel = true;
1143
+ break;
1144
+ }
1145
+ if (cancel) {
1146
+ event.preventDefault();
1147
+ this.moveCaret2end();
1148
+ this.updateOverlay();
1149
+ }
1150
+ }
1151
+ if (this.type === "time") {
1152
+ if (!options.keyboard || query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1153
+ inc = event.ctrlKey || event.metaKey ? 60 : 1;
1154
+ val = query2(this.el).val();
1155
+ let time = TsDate2.str2min(val) || TsDate2.str2min((/* @__PURE__ */ new Date()).getHours() + ":" + ((/* @__PURE__ */ new Date()).getMinutes() - 1));
1156
+ switch (key) {
1157
+ case 38:
1158
+ if (event.shiftKey) break;
1159
+ time += inc;
1160
+ cancel = true;
1161
+ break;
1162
+ case 40:
1163
+ if (event.shiftKey) break;
1164
+ time -= inc;
1165
+ cancel = true;
1166
+ break;
1167
+ }
1168
+ if (cancel) {
1169
+ event.preventDefault();
1170
+ query2(this.el).val(TsDate2.min2str(time)).trigger("input").trigger("change");
1171
+ this.moveCaret2end();
1172
+ }
1173
+ }
1174
+ if (["list", "enum"].includes(this.type)) {
1175
+ switch (key) {
1176
+ case 8:
1177
+ // delete
1178
+ case 46:
1179
+ if (this.type == "list") {
1180
+ const search = query2(this.helpers.search_focus);
1181
+ if (search.val() == "") {
1182
+ const edata = this.trigger("remove", { target: this.el, originalEvent: event, item: this.selected });
1183
+ if (edata.isCancelled === true) return;
1184
+ this.selected = null;
1185
+ TsMenu2.hide(this.el.id + "_menu");
1186
+ query2(this.el).val("").trigger("input").trigger("change");
1187
+ edata.finish();
1188
+ }
1189
+ } else {
1190
+ const search = query2(this.helpers.multi).find("input");
1191
+ if (search.val() == "") {
1192
+ const edata = this.trigger("remove", { target: this.el, originalEvent: event, item: this.selected[this.selected.length - 1] });
1193
+ if (edata.isCancelled === true) return;
1194
+ TsMenu2.hide(this.el.id + "_menu");
1195
+ this.selected.pop();
1196
+ const overlay = TsMenu2.get(this.el.id + "_menu");
1197
+ if (overlay) overlay.options.selected = this.selected;
1198
+ this.refresh();
1199
+ edata.finish();
1200
+ }
1201
+ }
1202
+ break;
1203
+ case 9:
1204
+ // tab key
1205
+ case 16:
1206
+ TsMenu2.hide(this.el.id + "_menu");
1207
+ break;
1208
+ case 27:
1209
+ TsMenu2.hide(this.el.id + "_menu");
1210
+ this.refresh();
1211
+ break;
1212
+ default: {
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+ keyUp(event) {
1218
+ if (this.type == "list") {
1219
+ const search = query2(this.helpers.search_focus);
1220
+ if (search.val() !== "") {
1221
+ query2(this.el).attr("placeholder", "");
1222
+ } else {
1223
+ query2(this.el).attr("placeholder", this.tmp.pholder);
1224
+ }
1225
+ if (event.keyCode == 13) {
1226
+ setTimeout(() => {
1227
+ search.val("");
1228
+ TsMenu2.hide(this.el.id + "_menu");
1229
+ this.refresh();
1230
+ }, 1);
1231
+ }
1232
+ if ([38, 40].includes(event.keyCode) && !this.tmp.overlay?.overlay?.displayed) {
1233
+ this.updateOverlay();
1234
+ }
1235
+ this.refresh();
1236
+ }
1237
+ if (this.type == "combo") {
1238
+ if (![9, 16, 27].includes(event.keyCode) && this.options.openOnFocus !== true) {
1239
+ this.updateOverlay();
1240
+ }
1241
+ if ([38, 40].includes(event.keyCode) && !this.tmp.overlay?.overlay?.displayed) {
1242
+ this.updateOverlay();
1243
+ }
1244
+ }
1245
+ if (this.type == "enum") {
1246
+ const search = this.helpers.multi?.find("input");
1247
+ const styles = getComputedStyle(search?.get(0));
1248
+ const width = TsUtils.getStrWidth(
1249
+ search?.val(),
1250
+ `font-family: ${styles["font-family"]}; font-size: ${styles["font-size"]};`,
1251
+ void 0
1252
+ );
1253
+ search?.css({ width: width + 15 + "px" });
1254
+ this.resize();
1255
+ if ([8, 46, 9, 16, 27].includes(event.keyCode)) {
1256
+ if (this.tmp.overlay?.overlay?.displayed) {
1257
+ TsMenu2.hide(this.el.id + "_menu");
1258
+ }
1259
+ } else {
1260
+ this.updateOverlay();
1261
+ }
1262
+ }
1263
+ }
1264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1265
+ findItemIndex(items, id, parents) {
1266
+ let inds = [];
1267
+ if (!parents) parents = [];
1268
+ if (["list", "combo", "enum"].includes(this.type) && this.options.url) {
1269
+ const overlay = TsMenu2.get(this.el.id + "_menu");
1270
+ if (overlay) {
1271
+ items = overlay.options.items;
1272
+ this.options.items = items;
1273
+ }
1274
+ }
1275
+ items.forEach((item, ind) => {
1276
+ if (item.id === id) {
1277
+ inds = parents.concat([ind]);
1278
+ this.options.index = [ind];
1279
+ }
1280
+ if (inds.length == 0 && item.items && item.items.length > 0) {
1281
+ parents.push(ind);
1282
+ inds = this.findItemIndex(item.items, id, parents);
1283
+ parents.pop();
1284
+ }
1285
+ });
1286
+ return inds;
1287
+ }
1288
+ updateOverlay(_indexOnly) {
1289
+ const options = this.options;
1290
+ let params;
1291
+ if (this.type === "color") {
1292
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1293
+ TsColor2.show(TsUtils.extend({
1294
+ name: this.el.id + "_color",
1295
+ anchor: this.el,
1296
+ transparent: options.transparent,
1297
+ advanced: options.advanced,
1298
+ color: this.el.value,
1299
+ liveUpdate: true
1300
+ }, this.options)).select((event) => {
1301
+ const color = event.detail["color"];
1302
+ query2(this.el).val(color).trigger("input").trigger("change");
1303
+ }).liveUpdate((event) => {
1304
+ const color = event.detail["color"];
1305
+ query2(this.helpers.suffix).find(":scope > div").css("background-color", "#" + color);
1306
+ });
1307
+ }
1308
+ if (["list", "combo", "enum"].includes(this.type)) {
1309
+ let el = this.el;
1310
+ let input = this.el;
1311
+ if (this.type === "enum") {
1312
+ el = this.helpers.multi?.get(0) ?? this.el;
1313
+ input = query2(el).find("input").get(0) ?? this.el;
1314
+ }
1315
+ if (this.type === "list") {
1316
+ const sel = this.selected;
1317
+ if (TsUtils.isPlainObject(sel) && Object.keys(sel).length > 0) {
1318
+ const ind = this.findItemIndex(options.items, sel.id);
1319
+ if (ind.length > 0) {
1320
+ options.index = ind;
1321
+ }
1322
+ }
1323
+ input = this.helpers.search_focus ?? this.el;
1324
+ }
1325
+ if (query2(this.el).hasClass("has-focus") && !this.el.readOnly && !this.el.disabled) {
1326
+ params = TsUtils.extend({}, options, {
1327
+ name: this.el.id + "_menu",
1328
+ anchor: input,
1329
+ selected: this.selected,
1330
+ search: false,
1331
+ render: options.renderDrop,
1332
+ anchorClass: "",
1333
+ offsetY: 5,
1334
+ maxHeight: options.maxDropHeight,
1335
+ // TODO: check
1336
+ maxWidth: options.maxDropWidth,
1337
+ // TODO: check
1338
+ minWidth: options.minDropWidth
1339
+ // TODO: check
1340
+ });
1341
+ this.tmp.overlay = TsMenu2.show(params).select((event) => {
1342
+ if (["list", "combo"].includes(this.type)) {
1343
+ this.selected = event.detail["item"];
1344
+ query2(input).val("");
1345
+ query2(this.el).val(this.selected.text).trigger("input").trigger("change");
1346
+ this.focus({ showMenu: false });
1347
+ } else {
1348
+ const selected = this.selected;
1349
+ const newItem = event.detail?.["item"];
1350
+ if (newItem) {
1351
+ const edata = this.trigger("add", { target: this.el, item: newItem, originalEvent: event });
1352
+ if (edata.isCancelled === true) return;
1353
+ if (selected.length >= options.max && options.max > 0) selected.pop();
1354
+ delete newItem.hidden;
1355
+ selected.push(newItem);
1356
+ query2(this.el).trigger("input").trigger("change");
1357
+ query2(this.helpers.multi).find("input").val("");
1358
+ const overlay = TsMenu2.get(this.el.id + "_menu");
1359
+ if (overlay) overlay.options.selected = this.selected;
1360
+ edata.finish();
1361
+ }
1362
+ }
1363
+ });
1364
+ }
1365
+ }
1366
+ if (["date", "time", "datetime"].includes(this.type)) {
1367
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1368
+ TsDate2.show(TsUtils.extend({
1369
+ name: this.el.id + "_date",
1370
+ anchor: this.el,
1371
+ value: this.el.value
1372
+ }, this.options)).select((event) => {
1373
+ const date = event.detail["date"];
1374
+ if (date != null) {
1375
+ ;
1376
+ query2(this.el).val(date).trigger("input").trigger("change");
1377
+ }
1378
+ });
1379
+ }
1380
+ }
1381
+ /*
1382
+ * INTERNAL FUNCTIONS
1383
+ */
1384
+ isStrValid(ch, loose) {
1385
+ let isValid = true;
1386
+ switch (this.type) {
1387
+ case "int":
1388
+ if (loose && ["-", this.options.groupSymbol].includes(ch)) {
1389
+ isValid = true;
1390
+ } else {
1391
+ isValid = TsUtils.isInt(ch.replace(this.options.numberRE, ""));
1392
+ }
1393
+ break;
1394
+ case "percent":
1395
+ ch = ch.replace(/%/g, "");
1396
+ // falls through to float
1397
+ case "float":
1398
+ if (loose && ["-", "", this.options.decimalSymbol, this.options.groupSymbol].includes(ch)) {
1399
+ isValid = true;
1400
+ } else {
1401
+ isValid = TsUtils.isFloat(ch.replace(this.options.numberRE, ""));
1402
+ }
1403
+ break;
1404
+ case "money":
1405
+ case "currency":
1406
+ if (loose && [
1407
+ "-",
1408
+ this.options.decimalSymbol,
1409
+ this.options.groupSymbol,
1410
+ this.options.currency.prefix,
1411
+ // any: cast-to-any for dynamic dispatch; TsField instance shape varies by `type` (text/list/date/color/etc) at runtime
1412
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1413
+ this.options.currency.suffix
1414
+ ].includes(ch)) {
1415
+ isValid = true;
1416
+ } else {
1417
+ isValid = TsUtils.isFloat(ch.replace(this.options.moneyRE, ""));
1418
+ }
1419
+ break;
1420
+ case "bin":
1421
+ isValid = TsUtils.isBin(ch);
1422
+ break;
1423
+ case "color":
1424
+ case "hex":
1425
+ isValid = TsUtils.isHex(ch);
1426
+ break;
1427
+ case "alphanumeric":
1428
+ isValid = TsUtils.isAlphaNumeric(ch);
1429
+ break;
1430
+ }
1431
+ return isValid;
1432
+ }
1433
+ addPrefix() {
1434
+ if (!this.options.prefix) {
1435
+ return;
1436
+ }
1437
+ const styles = getComputedStyle(this.el);
1438
+ if (this.tmp["old-padding-left"] == null) {
1439
+ this.tmp["old-padding-left"] = styles["padding-left"];
1440
+ }
1441
+ if (this.helpers.prefix) query2(this.helpers.prefix).remove();
1442
+ query2(this.el).before(`<div class="tsg-field-helper">${this.options.prefix}</div>`);
1443
+ const helper = query2(this.el).get(0).previousElementSibling;
1444
+ query2(helper).css({
1445
+ "color": styles["color"],
1446
+ "font-family": styles["font-family"],
1447
+ "font-size": styles["font-size"],
1448
+ "height": this.el.clientHeight + "px",
1449
+ "padding-top": parseInt(styles["padding-top"], 10) + 1 + "px",
1450
+ "padding-bottom": parseInt(styles["padding-bottom"], 10) - 1 + "px",
1451
+ "padding-left": this.tmp["old-padding-left"] ?? "",
1452
+ "padding-right": 0,
1453
+ "margin-top": parseInt(styles["margin-top"], 10) + "px",
1454
+ "margin-bottom": parseInt(styles["margin-bottom"], 10) + "px",
1455
+ "margin-left": styles["margin-left"],
1456
+ "margin-right": 0,
1457
+ "z-index": 1,
1458
+ "display": "inline-flex",
1459
+ "align-items": "center"
1460
+ });
1461
+ query2(this.el).css("padding-left", helper.clientWidth + "px !important");
1462
+ this.helpers.prefix = helper;
1463
+ }
1464
+ addSuffix() {
1465
+ if (!this.options.suffix && !this.options.arrows) {
1466
+ return;
1467
+ }
1468
+ const styles = getComputedStyle(this.el);
1469
+ if (this.tmp["old-padding-right"] == null) {
1470
+ this.tmp["old-padding-right"] = styles["padding-right"];
1471
+ }
1472
+ let pr = parseInt(styles["padding-right"] || "0");
1473
+ if (this.options.arrows) {
1474
+ if (this.helpers.arrows) query2(this.helpers.arrows).remove();
1475
+ query2(this.el).after(
1476
+ '<div class="tsg-field-helper" style="border: 1px solid transparent">&#160; <div class="tsg-field-up" type="up"> <div class="arrow-up" type="up"></div> </div> <div class="tsg-field-down" type="down"> <div class="arrow-down" type="down"></div> </div></div>'
1477
+ );
1478
+ const arrowHelper = query2(this.el).get(0).nextElementSibling;
1479
+ const $arrowHelper = query2(arrowHelper);
1480
+ $arrowHelper.css({
1481
+ "color": styles["color"],
1482
+ "font-family": styles["font-family"],
1483
+ "font-size": styles["font-size"],
1484
+ "height": this.el.clientHeight + "px",
1485
+ "padding": 0,
1486
+ "margin-top": parseInt(styles["margin-top"], 10) + 1 + "px",
1487
+ "margin-bottom": 0,
1488
+ "border-left": "1px solid silver",
1489
+ "width": "16px",
1490
+ "transform": "translateX(-100%)"
1491
+ });
1492
+ $arrowHelper.on("mousedown", (event) => {
1493
+ const mouseEvent = event;
1494
+ if (query2(mouseEvent.target).hasClass("arrow-up")) {
1495
+ this.keyDown(mouseEvent, { keyCode: 38 });
1496
+ }
1497
+ if (query2(mouseEvent.target).hasClass("arrow-down")) {
1498
+ this.keyDown(mouseEvent, { keyCode: 40 });
1499
+ }
1500
+ });
1501
+ pr += arrowHelper.clientWidth;
1502
+ query2(this.el).css("padding-right", pr + "px !important");
1503
+ this.helpers.arrows = arrowHelper;
1504
+ }
1505
+ if (this.options.suffix !== "") {
1506
+ if (this.helpers.suffix) query2(this.helpers.suffix).remove();
1507
+ query2(this.el).after(`<div class="tsg-field-helper">${this.options.suffix}</div>`);
1508
+ const suffixHelper = query2(this.el).get(0).nextElementSibling;
1509
+ query2(suffixHelper).css({
1510
+ "color": styles["color"],
1511
+ "font-family": styles["font-family"],
1512
+ "font-size": styles["font-size"],
1513
+ "height": this.el.clientHeight + "px",
1514
+ "padding-top": styles["padding-top"],
1515
+ "padding-bottom": styles["padding-bottom"],
1516
+ "padding-left": 0,
1517
+ "padding-right": styles["padding-right"],
1518
+ "margin-top": parseInt(styles["margin-top"], 10) + 2 + "px",
1519
+ "margin-bottom": parseInt(styles["margin-bottom"], 10) + 1 + "px",
1520
+ "transform": "translateX(-100%)"
1521
+ });
1522
+ query2(this.el).css("padding-right", suffixHelper.clientWidth + "px !important");
1523
+ this.helpers.suffix = suffixHelper;
1524
+ }
1525
+ }
1526
+ // Only used for list
1527
+ addSearch() {
1528
+ if (this.type !== "list") return;
1529
+ if (this.helpers.search) query2(this.helpers.search).remove();
1530
+ let tabIndex = parseInt(query2(this.el).attr("tabIndex"));
1531
+ if (!isNaN(tabIndex) && tabIndex !== -1) this.tmp["old-tabIndex"] = tabIndex;
1532
+ if (this.tmp["old-tabIndex"]) tabIndex = this.tmp["old-tabIndex"];
1533
+ if (tabIndex == null || isNaN(tabIndex)) tabIndex = 0;
1534
+ let searchId = "";
1535
+ if (query2(this.el).attr("id") != null) {
1536
+ searchId = 'id="' + query2(this.el).attr("id") + '_search"';
1537
+ }
1538
+ const html = `
1539
+ <div class="tsg-field-helper">
1540
+ <span class="tsg-icon tsg-icon-search"></span>
1541
+ <input ${searchId} type="text" tabIndex="${tabIndex}" ${query2(this.el).prop("readOnly") ? "readonly" : ""}
1542
+ autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"/>
1543
+ </div>`;
1544
+ query2(this.el).attr("tabindex", String(-1)).before(html);
1545
+ const helper = query2(this.el).get(0).previousElementSibling;
1546
+ this.helpers.search = helper;
1547
+ this.helpers.search_focus = query2(helper).find("input").get(0);
1548
+ const styles = getComputedStyle(this.el);
1549
+ const $helperSearch = query2(helper);
1550
+ $helperSearch.css({
1551
+ width: this.el.clientWidth + "px",
1552
+ "margin-top": styles["margin-top"],
1553
+ "margin-left": styles["margin-left"],
1554
+ "margin-bottom": styles["margin-bottom"],
1555
+ "margin-right": styles["margin-right"]
1556
+ });
1557
+ $helperSearch.find("input").css({
1558
+ cursor: "default",
1559
+ width: "100%",
1560
+ opacity: 1,
1561
+ padding: styles["padding"],
1562
+ margin: styles["margin"],
1563
+ border: "1px solid transparent",
1564
+ "background-color": "transparent"
1565
+ });
1566
+ query2(helper).find("input").off(".tsg-helper").on("focus.tsg-helper", (event) => {
1567
+ const focusEvent = event;
1568
+ query2(focusEvent.target).val("");
1569
+ this.tmp.pholder = query2(this.el).attr("placeholder") ?? "";
1570
+ this.focus(focusEvent);
1571
+ focusEvent.stopPropagation();
1572
+ }).on("blur.tsg-helper", (event) => {
1573
+ const focusEvent = event;
1574
+ query2(focusEvent.target).val("");
1575
+ if (this.tmp.pholder != null) query2(this.el).attr("placeholder", this.tmp.pholder);
1576
+ this.blur(focusEvent);
1577
+ focusEvent.stopPropagation();
1578
+ }).on("keydown.tsg-helper", (event) => {
1579
+ this.keyDown(event);
1580
+ }).on("keyup.tsg-helper", (event) => {
1581
+ this.keyUp(event);
1582
+ });
1583
+ query2(helper).off(".tsg-helper").on("click.tsg-helper", (_event) => {
1584
+ query2(helper).find("input").get(0).focus();
1585
+ });
1586
+ }
1587
+ // Used in enum/file
1588
+ addMultiSearch() {
1589
+ if (!["enum", "file"].includes(this.type)) {
1590
+ return;
1591
+ }
1592
+ query2(this.helpers.multi).remove();
1593
+ let html = "";
1594
+ const styles = getComputedStyle(this.el);
1595
+ const margin = TsUtils.stripSpaces(`
1596
+ margin-top: 0px;
1597
+ margin-bottom: 0px;
1598
+ margin-left: ${styles["margin-left"]};
1599
+ margin-right: ${styles["margin-right"]};
1600
+ width: ${TsUtils.getSize(this.el, "width") - parseInt(styles["margin-left"], 10) - parseInt(styles["margin-right"], 10)}px;
1601
+ `);
1602
+ if (this.tmp["min-height"] == null) {
1603
+ const min = this.tmp["min-height"] = parseInt((styles["min-height"] != "none" ? styles["min-height"] : "0") || "0");
1604
+ const current = parseInt(styles["height"]);
1605
+ this.tmp["min-height"] = Math.max(min, current);
1606
+ }
1607
+ if (this.tmp["max-height"] == null && styles["max-height"] != "none") {
1608
+ this.tmp["max-height"] = parseInt(styles["max-height"]);
1609
+ }
1610
+ let searchId = "";
1611
+ if (query2(this.el).attr("id") != null) {
1612
+ searchId = `id="${query2(this.el).attr("id")}_search"`;
1613
+ }
1614
+ let tabIndex = parseInt(query2(this.el).attr("tabIndex"));
1615
+ if (!isNaN(tabIndex) && tabIndex !== -1) this.tmp["old-tabIndex"] = tabIndex;
1616
+ if (this.tmp["old-tabIndex"]) tabIndex = this.tmp["old-tabIndex"];
1617
+ if (tabIndex == null || isNaN(tabIndex)) tabIndex = 0;
1618
+ if (this.type === "enum") {
1619
+ html = `
1620
+ <div class="tsg-field-helper tsg-list" style="${margin}">
1621
+ <div class="tsg-multi-items">
1622
+ <div class="li-search">
1623
+ <input ${searchId} type="text" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
1624
+ tabindex="${tabIndex}"
1625
+ ${query2(this.el).prop("readOnly") ? "readonly" : ""}
1626
+ ${query2(this.el).prop("disabled") ? "disabled" : ""}>
1627
+ </div>
1628
+ </div>
1629
+ </div>`;
1630
+ }
1631
+ if (this.type === "file") {
1632
+ html = `
1633
+ <div class="tsg-field-helper tsg-list" style="${margin}">
1634
+ <div class="tsg-multi-file">
1635
+ <input name="attachment" class="file-input" type="file" tabindex="-1"'
1636
+ style="width: 100%; height: 100%; opacity: 0" title=""
1637
+ ${this.options.max !== 1 ? "multiple" : ""}
1638
+ ${query2(this.el).prop("readOnly") || query2(this.el).prop("disabled") ? "disabled" : ""}
1639
+ ${query2(this.el).attr("accept") ? ' accept="' + query2(this.el).attr("accept") + '"' : ""}>
1640
+ </div>
1641
+ <div class="tsg-multi-items">
1642
+ <div class="li-search" style="display: none">
1643
+ <input ${searchId} type="text" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false"
1644
+ tabindex="${tabIndex}"
1645
+ ${query2(this.el).prop("readOnly") ? "readonly" : ""}
1646
+ ${query2(this.el).prop("disabled") ? "disabled" : ""}>
1647
+ </div>
1648
+ </div>
1649
+ </div>`;
1650
+ }
1651
+ this.tmp["old-background-color"] = styles["background-color"];
1652
+ this.tmp["old-border-color"] = styles["border-color"];
1653
+ query2(this.el).before(html).css({
1654
+ "border-color": "transparent",
1655
+ "background-color": "transparent"
1656
+ });
1657
+ const div = query2(this.el.previousElementSibling);
1658
+ this.helpers.multi = div;
1659
+ query2(this.el).attr("tabindex", String(-1));
1660
+ div.on("mousedown", (event) => {
1661
+ query2(event.target).addClass("has-focus");
1662
+ }).on("mouseup", (event) => {
1663
+ query2(event.target).removeClass("has-focus");
1664
+ }).on("click", (event) => {
1665
+ this.focus(event);
1666
+ this.updateOverlay();
1667
+ });
1668
+ div.find("input:not(.file-input)").on("click", (event) => {
1669
+ this.click(event);
1670
+ }).on("focus", (event) => {
1671
+ this.focus(event);
1672
+ }).on("blur", (event) => {
1673
+ this.blur(event);
1674
+ }).on("keydown", (event) => {
1675
+ this.keyDown(event);
1676
+ }).on("keyup", (event) => {
1677
+ this.keyUp(event);
1678
+ });
1679
+ if (this.type === "file") {
1680
+ div.find("input.file-input").off(".drag").on("click.drag", (event) => {
1681
+ const mouseEvent = event;
1682
+ mouseEvent.stopPropagation();
1683
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1684
+ this.focus(mouseEvent);
1685
+ }).on("dragenter.drag", (_event) => {
1686
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1687
+ div.addClass("tsg-file-dragover");
1688
+ }).on("dragleave.drag", (_event) => {
1689
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1690
+ div.removeClass("tsg-file-dragover");
1691
+ }).on("drop.drag", (event) => {
1692
+ const dragEvent = event;
1693
+ if (query2(this.el).prop("readOnly") || query2(this.el).prop("disabled")) return;
1694
+ div.removeClass("tsg-file-dragover");
1695
+ const files = Array.from(dragEvent.dataTransfer?.files ?? []);
1696
+ files.forEach((file) => {
1697
+ this.addFile(file);
1698
+ });
1699
+ this.focus(dragEvent);
1700
+ dragEvent.preventDefault();
1701
+ dragEvent.stopPropagation();
1702
+ }).on("dragover.drag", (event) => {
1703
+ const dragEvent = event;
1704
+ dragEvent.preventDefault();
1705
+ dragEvent.stopPropagation();
1706
+ }).on("change.drag", (event) => {
1707
+ const target = event.target;
1708
+ if (target.files != null) {
1709
+ Array.from(target.files).forEach((file) => {
1710
+ this.addFile(file);
1711
+ });
1712
+ }
1713
+ this.focus(event);
1714
+ });
1715
+ }
1716
+ this.refresh();
1717
+ }
1718
+ addFile(file) {
1719
+ const options = this.options;
1720
+ const selected = this.selected;
1721
+ const newItem = {
1722
+ name: file.name,
1723
+ type: file.type,
1724
+ modified: new Date(file.lastModified),
1725
+ size: file.size,
1726
+ content: null,
1727
+ file
1728
+ };
1729
+ let size = 0;
1730
+ let cnt = 0;
1731
+ const errors = [];
1732
+ if (Array.isArray(selected)) {
1733
+ selected.forEach((item) => {
1734
+ if (item.name == file.name && item.size == file.size) {
1735
+ errors.push(TsUtils.lang('The file "${name}" (${size}) is already added.', {
1736
+ name: file.name,
1737
+ size: String(TsUtils.formatSize(file.size))
1738
+ }));
1739
+ }
1740
+ size += item.size;
1741
+ cnt++;
1742
+ });
1743
+ }
1744
+ if (options.maxFileSize !== 0 && newItem.size > options.maxFileSize) {
1745
+ errors.push(TsUtils.lang("Maximum file size is ${size}", { size: String(TsUtils.formatSize(options.maxFileSize)) }));
1746
+ }
1747
+ if (options.maxSize !== 0 && size + newItem.size > options.maxSize) {
1748
+ errors.push(TsUtils.lang("Maximum total size is ${size}", { size: String(TsUtils.formatSize(options.maxSize)) }));
1749
+ }
1750
+ if (options.max !== 0 && cnt >= options.max) {
1751
+ errors.push(TsUtils.lang("Maximum number of files is ${count}", { count: options.max }));
1752
+ }
1753
+ const edata = this.trigger("add", { target: this.el, file: newItem, total: cnt, totalSize: size, errors });
1754
+ if (edata.isCancelled === true) return;
1755
+ if (errors.length > 0) {
1756
+ if (options.showErrors) {
1757
+ TsTooltip2.show({
1758
+ anchor: this.el,
1759
+ html: "Errors: " + errors.join("<br>"),
1760
+ hideOn: ["input", "doc-click"]
1761
+ });
1762
+ }
1763
+ console.log("ERRORS (while adding files): ", errors);
1764
+ return;
1765
+ }
1766
+ selected.push(newItem);
1767
+ if (typeof FileReader !== "undefined" && options.readContent === true) {
1768
+ const reader = new FileReader();
1769
+ reader.onload = (event) => {
1770
+ const fl = event.target?.result ?? "";
1771
+ const ind = fl.indexOf(",");
1772
+ newItem.content = fl.substr(ind + 1);
1773
+ this.refresh();
1774
+ query2(this.el).trigger("input").trigger("change");
1775
+ edata.finish();
1776
+ };
1777
+ reader.readAsDataURL(file);
1778
+ } else {
1779
+ this.refresh();
1780
+ query2(this.el).trigger("input").trigger("change");
1781
+ edata.finish();
1782
+ }
1783
+ }
1784
+ // move cursror to end
1785
+ moveCaret2end() {
1786
+ setTimeout(() => {
1787
+ this.el.setSelectionRange(this.el.value.length, this.el.value.length);
1788
+ }, 0);
1789
+ }
1790
+ };
1791
+
1792
+ export {
1793
+ TsField
1794
+ };
1795
+ //# sourceMappingURL=chunk-26XP2XU3.js.map