@componyx/ui 0.0.0-stage → 1.0.4

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 (169) hide show
  1. package/AppSkin/AppSkin.css +1268 -0
  2. package/AppSkin/AppSkin.js +2748 -0
  3. package/AppSkin/Themes/Default.css +241 -0
  4. package/Base/Base.js +3407 -0
  5. package/Base/ComponentIcons.css +66 -0
  6. package/Base/Font/ComponentIcons.woff +0 -0
  7. package/Base/Font/UI.woff +0 -0
  8. package/Base/Library.js +8432 -0
  9. package/Base/NavigationManager.js +462 -0
  10. package/Base/ResponsiveTemplates.js +371 -0
  11. package/Base/Sanitizer.js +288 -0
  12. package/Base/Themes/Default.css +255 -0
  13. package/Base/UI.css +618 -0
  14. package/Base/UIResourcePath.js +6 -0
  15. package/Bindary/Bindary.js +2011 -0
  16. package/Bindary/Modules/Connection.js +216 -0
  17. package/Bindary/Modules/Core.js +854 -0
  18. package/Bindary/Modules/DataUpdater.js +351 -0
  19. package/Bindary/Modules/ExpressionEngine.js +347 -0
  20. package/Bindary/Modules/ProxyManager.js +482 -0
  21. package/Bindary/Modules/Renderer.js +605 -0
  22. package/Bindary/Modules/Router.js +291 -0
  23. package/Bindary/Modules/Scroller.js +89 -0
  24. package/Bindary/Modules/Template.js +1273 -0
  25. package/BookmarkSpy/BookmarkSpy.js +430 -0
  26. package/Box/Box.css +36 -0
  27. package/Box/Box.js +1685 -0
  28. package/Button/Button.css +470 -0
  29. package/Button/Button.js +1398 -0
  30. package/Button/Themes/Default.css +200 -0
  31. package/Carousel/Carousel.css +273 -0
  32. package/Carousel/Carousel.js +607 -0
  33. package/ColorButton/ColorButton.css +39 -0
  34. package/ColorButton/ColorButton.js +336 -0
  35. package/ColorPicker/ColorPicker.css +359 -0
  36. package/ColorPicker/ColorPicker.js +972 -0
  37. package/ComboBox/ComboBox.css +357 -0
  38. package/ComboBox/ComboBox.js +2342 -0
  39. package/ComboBox/Themes/Default.css +23 -0
  40. package/DataPager/DataPager.css +72 -0
  41. package/DataPager/DataPager.js +674 -0
  42. package/DatePicker/DatePicker.css +227 -0
  43. package/DatePicker/DatePicker.js +1531 -0
  44. package/DatePicker/Themes/Default.css +26 -0
  45. package/Dialog/Dialog.css +106 -0
  46. package/Dialog/Dialog.js +572 -0
  47. package/Dialog/Themes/Default.css +10 -0
  48. package/Editor/Data/Emoji.json +8285 -0
  49. package/Editor/Data/Special.json +1042 -0
  50. package/Editor/Editor.css +1562 -0
  51. package/Editor/Editor.js +4058 -0
  52. package/Editor/Font/Editor.woff +0 -0
  53. package/Editor/Modules/ComponentFactory.js +565 -0
  54. package/Editor/Modules/ContentManager.js +748 -0
  55. package/Editor/Modules/DialogManager.js +1234 -0
  56. package/Editor/Modules/EventManager.js +458 -0
  57. package/Editor/Modules/Format.js +950 -0
  58. package/Editor/Modules/History.js +191 -0
  59. package/Editor/Modules/LayoutState.js +379 -0
  60. package/Editor/Modules/ListManager.js +296 -0
  61. package/Editor/Modules/MenuManager.js +388 -0
  62. package/Editor/Modules/NodeManager.js +681 -0
  63. package/Editor/Modules/ParagraphButtons.js +122 -0
  64. package/Editor/Modules/Resizer.js +176 -0
  65. package/Editor/Modules/SelectionRange.js +564 -0
  66. package/Editor/Modules/SourceViewBuilder.js +212 -0
  67. package/Editor/Modules/SymbolPicker.js +208 -0
  68. package/Editor/Modules/Table.js +2327 -0
  69. package/Editor/Modules/TableDialog.js +757 -0
  70. package/Editor/Modules/Toolbar.js +375 -0
  71. package/Editor/Modules/TooltipMenu.js +125 -0
  72. package/Editor/Modules/Utility.js +131 -0
  73. package/Editor/Themes/Default.css +204 -0
  74. package/FileUpload/FileUpload.css +156 -0
  75. package/FileUpload/FileUpload.js +898 -0
  76. package/FileUpload/Themes/Default.css +17 -0
  77. package/Form/Form.css +1934 -0
  78. package/Form/Form.js +4943 -0
  79. package/Form/Modules/ActionManager.js +368 -0
  80. package/Form/Modules/BuildPanelManager.js +319 -0
  81. package/Form/Modules/ComponentFactory.js +468 -0
  82. package/Form/Modules/ComponentPanel.js +265 -0
  83. package/Form/Modules/ConfigPanelManager.js +685 -0
  84. package/Form/Modules/DataBinder.js +602 -0
  85. package/Form/Modules/DataObserver.js +717 -0
  86. package/Form/Modules/Draggable.js +756 -0
  87. package/Form/Modules/OptionsPanel.js +342 -0
  88. package/Form/Modules/Renderer.js +1889 -0
  89. package/Form/Modules/RuleEngine.js +585 -0
  90. package/Form/Modules/RulesPanel.js +1158 -0
  91. package/Form/Modules/Types.js +1709 -0
  92. package/Form/Modules/ValidationManager.js +194 -0
  93. package/Form/Themes/Default.css +216 -0
  94. package/FormField/FormField.css +468 -0
  95. package/FormField/FormField.js +576 -0
  96. package/FormField/Themes/Default.css +99 -0
  97. package/Grid/Grid.css +544 -0
  98. package/Grid/Grid.js +3761 -0
  99. package/Grid/Themes/Default.css +43 -0
  100. package/LICENSE +126 -0
  101. package/MaskedTextBox/MaskedTextBox.css +28 -0
  102. package/MaskedTextBox/MaskedTextBox.js +642 -0
  103. package/MaskedTextBox/Themes/Default.css +6 -0
  104. package/Menu/Menu.css +328 -0
  105. package/Menu/Menu.js +2386 -0
  106. package/Menu/Themes/Default.css +24 -0
  107. package/NumericBox/NumericBox.css +120 -0
  108. package/NumericBox/NumericBox.js +984 -0
  109. package/PanelBar/PanelBar.css +123 -0
  110. package/PanelBar/PanelBar.js +505 -0
  111. package/PanelBar/Themes/Default.css +51 -0
  112. package/PieChart/PieChart.css +95 -0
  113. package/PieChart/PieChart.js +820 -0
  114. package/PieChart/Themes/Default.css +9 -0
  115. package/README.md +178 -3
  116. package/Slider/Slider.css +348 -0
  117. package/Slider/Slider.js +1306 -0
  118. package/Slider/Themes/Default.css +44 -0
  119. package/TabStrip/TabStrip.css +52 -0
  120. package/TabStrip/TabStrip.js +362 -0
  121. package/TabStrip/Themes/Default.css +35 -0
  122. package/TimePicker/Themes/Default.css +30 -0
  123. package/TimePicker/TimePicker.css +253 -0
  124. package/TimePicker/TimePicker.js +1485 -0
  125. package/TooltipManager/Themes/Default.css +21 -0
  126. package/TooltipManager/TooltipManager.css +227 -0
  127. package/TooltipManager/TooltipManager.js +791 -0
  128. package/Validator/Validator.css +54 -0
  129. package/Validator/Validator.js +2508 -0
  130. package/componyx.html-data.json +619 -0
  131. package/index.js +27 -0
  132. package/package.json +43 -4
  133. package/types/AppSkin.d.ts +842 -0
  134. package/types/Base.d.ts +1034 -0
  135. package/types/Bindary.d.ts +1196 -0
  136. package/types/BookmarkSpy.d.ts +112 -0
  137. package/types/Box.d.ts +374 -0
  138. package/types/Button.d.ts +455 -0
  139. package/types/Carousel.d.ts +136 -0
  140. package/types/ColorButton.d.ts +120 -0
  141. package/types/ColorPicker.d.ts +225 -0
  142. package/types/ComboBox.d.ts +507 -0
  143. package/types/DataPager.d.ts +152 -0
  144. package/types/DatePicker.d.ts +354 -0
  145. package/types/Dialog.d.ts +160 -0
  146. package/types/Editor.d.ts +1289 -0
  147. package/types/FileUpload.d.ts +206 -0
  148. package/types/Form.d.ts +3078 -0
  149. package/types/FormField.d.ts +204 -0
  150. package/types/Grid.d.ts +911 -0
  151. package/types/Library.d.ts +1899 -0
  152. package/types/MaskedTextBox.d.ts +91 -0
  153. package/types/Menu.d.ts +577 -0
  154. package/types/NumericBox.d.ts +310 -0
  155. package/types/PanelBar.d.ts +223 -0
  156. package/types/PieChart.d.ts +224 -0
  157. package/types/ResponsiveTemplates.d.ts +108 -0
  158. package/types/Slider.d.ts +227 -0
  159. package/types/TabStrip.d.ts +185 -0
  160. package/types/TimePicker.d.ts +265 -0
  161. package/types/TooltipManager.d.ts +178 -0
  162. package/types/UI.d.ts +29 -0
  163. package/types/Validator.d.ts +653 -0
  164. package/types/index.d.ts +59 -0
  165. package/ui.css +11783 -0
  166. package/ui.js +79477 -0
  167. package/ui.min.css +138 -0
  168. package/ui.min.js +264 -0
  169. package/ui.min.js.map +1 -0
@@ -0,0 +1,642 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ import '../Base/Base.js';
3
+ import '../Base/Library.js';
4
+
5
+ (function ()
6
+ {
7
+ /**
8
+ * MaskedTextBox class.
9
+ * @class
10
+ * @memberof componyx.UI
11
+ * @augments componyx.UI.base.WebComponent
12
+ * @mixes componyx.UI.base.methods
13
+ * @param {String} id The id of the component.
14
+ * @param {Object|HTMLElement} properties The properties used to initialize the component or the container element.
15
+ * @returns {componyx.UI.MaskedTextBox} An instance of the component.
16
+ */
17
+ class MaskedTextBox extends componyx.UI.base.WebComponent
18
+ {
19
+ #input = null;
20
+ #inputSlots = [];
21
+ #inputData = [];
22
+ #mask;
23
+ #defaultMask = '____-____';
24
+ #firstFocus = false;
25
+ #lastValue;
26
+
27
+ constructor(id, properties)
28
+ {
29
+ super(id, properties);
30
+
31
+ /**
32
+ * @ignore
33
+ */
34
+ this.mask = this.#defaultMask;
35
+
36
+ /**
37
+ * Gets or sets a value indicating which characters are allowed on the underscore (_) slots.
38
+ * @type {componyx.UI.MaskedTextBox.CharacterAllowOption}
39
+ */
40
+ this.allowedCharacters = MaskedTextBox.AllowedCharactersOption.ALPHANUMERIC;
41
+
42
+ /**
43
+ * Gets or sets the regular expression pattern when allowedCharacters is set to CUSTOM.
44
+ * @type {RegExp}
45
+ */
46
+ this.customPattern = /^[A-Z0-9]+$/;
47
+
48
+ /**
49
+ * Gets or sets a value indicating if the mask hint is displayed. If null, it's auto-detected based on the configured mask and character settings.
50
+ * @type {Boolean|Null}
51
+ */
52
+ this.showMaskHint = null;
53
+
54
+ /**
55
+ * Gets or sets the hint text displayed when the input gets focus. This text is appended after the mask pattern. (A=letters, 0=digits)
56
+ * @type {String}
57
+ */
58
+ this.maskHint = ' (A=letters, 0=digits)';
59
+
60
+ /**
61
+ * Gets or sets the id of the HTML input to use.
62
+ * @type {String}
63
+ */
64
+ this.inputId = null;
65
+
66
+ /**
67
+ * @class
68
+ * @augments componyx.UI.base.Events
69
+ * @memberof componyx.UI.MaskedTextBox
70
+ * @property {componyx.UI.base.Event} onChanged - Fires when the input value has changed. @see {@link componyx.UI.MaskedTextBox.MaskedTextBoxEventArgs}
71
+ * @see {@link componyx.UI.base.Events}
72
+ */
73
+ function MaskedTextBoxEvents(events)
74
+ {
75
+ Object.assign(this, events);
76
+
77
+ this.onChanged = $base.static.createEvent('onChanged');
78
+ }
79
+ /**
80
+ * PanelBar events
81
+ * @type {componyx.UI.PanelBar.PanelBarEvents}
82
+ */
83
+ this.events = new MaskedTextBoxEvents(this.events);
84
+
85
+ /**
86
+ * @typedef {Object} MaskedTextBoxEventArgs
87
+ * @memberof componyx.UI.MaskedTextBox
88
+ * @property {componyx.UI.MaskedTextBox} maskedTextBox - The MaskedTextBox instance.
89
+ * @property {Object} eventArgs - The event object containing more detailed information about the event.
90
+ * @property {String} eventArgs.value - The current value.
91
+ * @property {Event} eventArgs.event - The original event object.
92
+ */
93
+ }
94
+
95
+ /**
96
+ * Gets the input element.
97
+ * @type {HTMLInputElement}
98
+ */
99
+ get input()
100
+ {
101
+ return this.#input;
102
+ }
103
+
104
+ /**
105
+ * Gets the raw input value without mask characters.
106
+ * @type {string}
107
+ */
108
+ get value()
109
+ {
110
+ return this.#inputData.join("");
111
+ }
112
+
113
+ /**
114
+ * Gets or sets the text box mask pattern. Use 'A' for letters, '0' for digits, and '_' for any allowed character slot. To include a literal A, 0, or _ in the mask, escape it with a backslash (\).
115
+ * @type {string}
116
+ */
117
+ get mask()
118
+ {
119
+ return this.#mask;
120
+ }
121
+
122
+ /**
123
+ * Sets the mask.
124
+ * @param {string} value
125
+ */
126
+ set mask(value)
127
+ {
128
+ if (value === this.#mask)
129
+ return;
130
+
131
+ if ($lib.isEmpty(value))
132
+ this.#mask = this.#defaultMask;
133
+ else
134
+ this.#mask = value;
135
+
136
+ if (this.renderState === $base.static.RenderState.RENDERED)
137
+ {
138
+ this.setMaskHint();
139
+ this.#initializeMask();
140
+ this.renderValue();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Sets the raw input value with or without mask characters.
146
+ * @param {string} value The value to set.
147
+ */
148
+ set value(value)
149
+ {
150
+ let charIndex = 0;
151
+
152
+ this.#inputData.fill("");
153
+
154
+ if (!this.#inputSlots.length)
155
+ this.#initializeMask();
156
+
157
+ if ($lib.isEmpty(value))
158
+ value = '';
159
+
160
+ for (let valueIndex = 0; valueIndex < value.length && charIndex < this.#inputSlots.length; valueIndex++)
161
+ {
162
+ const char = value[valueIndex];
163
+
164
+ if (this.#isAllowedChar(char, charIndex))
165
+ this.#inputData[charIndex++] = char;
166
+ }
167
+
168
+ if (this.renderState === $base.static.RenderState.RENDERED)
169
+ this.renderValue();
170
+ }
171
+
172
+ /**
173
+ * Gets the raw input value without mask characters.
174
+ * @type {string}
175
+ */
176
+ getValue()
177
+ {
178
+ return this.value;
179
+ }
180
+
181
+ /**
182
+ * Sets the raw input value with or without mask characters.
183
+ * @param {string} value The value to set.
184
+ */
185
+ setValue(value)
186
+ {
187
+ this.value = value;
188
+ }
189
+
190
+ /**
191
+ * Sets the mask hint.
192
+ * @param {string} value The mask value.
193
+ */
194
+ setMaskHint()
195
+ {
196
+ const rawMask = this.mask.replace(/\\./g, ''), // remove escaped characters
197
+ showMaskHint = (this.showMaskHint == null) ? (rawMask.includes('A') || rawMask.includes('0')) : this.showMaskHint;
198
+
199
+ this.classList.remove('mask-hint');
200
+
201
+ if (showMaskHint)
202
+ {
203
+ this.dataset.hint = this.mask.replace(/\\([\\a-zA-Z0-9_])/g, '$1') + this.maskHint;
204
+ this.classList.add('mask-hint');
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Renders the component.
210
+ */
211
+ render()
212
+ {
213
+ if (this.renderState != $base.static.RenderState.RENDERING)
214
+ {
215
+ super.render(this.#preRender, 'masked-textbox');
216
+ return;
217
+ }
218
+
219
+ // render logic after loading resources
220
+ this.#draw();
221
+ this.renderChildren();
222
+ }
223
+
224
+ /**
225
+ * Destroys the component.
226
+ * @param {Boolean} keepEvents A value indicating if component events should be kept.
227
+ * @param {Boolean} removeElement=true A value indicating if the corresponding HTML Element should be removed.
228
+ * @see {@link componyx.UI.base.methods#destroy}
229
+ * @function
230
+ */
231
+ destroy(keepEvents, removeElement = true)
232
+ {
233
+ this.#dispose();
234
+ super.destroy(keepEvents, removeElement);
235
+ }
236
+
237
+ #preRender()
238
+ {
239
+ return [MaskedTextBox.name];
240
+ }
241
+
242
+ #draw()
243
+ {
244
+ this.#input = this.createSyncedInput(this.inputId, function (instance)
245
+ {
246
+ instance.value = this.value; // triggers value setter on component
247
+ }, false);
248
+
249
+ if (typeof this.customPattern === 'string')
250
+ this.customPattern = new RegExp(this.customPattern);
251
+
252
+ this.#input.type = 'text';
253
+ this.element.appendChild(this.#input);
254
+ this.#initializeMask();
255
+ this.setMaskHint();
256
+ this.renderValue();
257
+ this.#bindEvents();
258
+ }
259
+
260
+ #initializeMask()
261
+ {
262
+ let visibleIndex = 0,
263
+ checkEscaped = false;
264
+
265
+ this.#inputSlots = [];
266
+ this.#inputData = [];
267
+
268
+ for (let index = 0; index < this.mask.length; index++)
269
+ {
270
+ const char = this.mask[index],
271
+ prevChar = this.mask[index - 1];
272
+
273
+ if (char == '\\') // Backslashes are used to escape and are removed from the visible mask in the input
274
+ {
275
+ checkEscaped = true;
276
+
277
+ if (prevChar == '\\') // Literal backslash
278
+ {
279
+ checkEscaped = false;
280
+ visibleIndex++;
281
+ }
282
+
283
+ continue;
284
+ }
285
+
286
+ if (this.#isInputSlot(char, (checkEscaped) ? prevChar : null))
287
+ {
288
+ this.#inputSlots.push({
289
+ maskIndex: visibleIndex,
290
+ token: char
291
+ });
292
+
293
+ this.#inputData.push("");
294
+ }
295
+
296
+ visibleIndex++;
297
+ }
298
+ }
299
+
300
+ renderValue()
301
+ {
302
+ this.#input.__setValue(this.#getVisibleText());
303
+ }
304
+
305
+ #getVisibleText()
306
+ {
307
+ let text = [],
308
+ charIndex = 0,
309
+ checkEscaped = false;
310
+
311
+ for (let index = 0; index < this.mask.length; index++)
312
+ {
313
+ const char = this.mask[index],
314
+ prevChar = this.mask[index - 1];
315
+
316
+ if (char === '\\') // Handle escaped characters
317
+ {
318
+ checkEscaped = true;
319
+ if (prevChar == '\\') // Literal backslash
320
+ {
321
+ checkEscaped = false;
322
+ text.push(char);
323
+ }
324
+ }
325
+ else if (this.#isInputSlot(char, (checkEscaped) ? prevChar : null))
326
+ {
327
+ text.push(this.#inputData[charIndex] !== '' ? this.#inputData[charIndex] : '_');
328
+ charIndex++;
329
+ checkEscaped = true;
330
+ }
331
+ else
332
+ {
333
+ text.push(char);
334
+ }
335
+ }
336
+
337
+ return text.join('');
338
+ }
339
+
340
+ #isInputSlot(char, prevChar)
341
+ {
342
+ return prevChar !== '\\' && (char === '_' || char === 'A' || char === '0');
343
+ }
344
+
345
+ #bindEvents()
346
+ {
347
+ $lib.on(this.#input, 'blur', this.#blur, null, this, true);
348
+ $lib.on(this.#input, 'paste', this.#paste, null, this, true);
349
+ $lib.on(this.#input, 'keydown', this.#keyDown, null, this, true);
350
+ $lib.on(this.#input, 'focus click', this.#adjustCaret, null, this, true);
351
+
352
+ }
353
+
354
+ #disposeEvents()
355
+ {
356
+ if (!this.#input)
357
+ return;
358
+
359
+ $lib.off(this.#input, 'blur', this.#blur);
360
+ $lib.off(this.#input, 'paste', this.#paste);
361
+ $lib.off(this.#input, 'keydown', this.#keyDown);
362
+ $lib.off(this.#input, 'focus click', this.#adjustCaret);
363
+ }
364
+
365
+ #blur(e)
366
+ {
367
+ const currentValue = this.value;
368
+
369
+ if (currentValue !== this.#lastValue)
370
+ {
371
+ this.#lastValue = currentValue;
372
+ this.events.onChanged.fire(this, { value: currentValue, event: e });
373
+ }
374
+ }
375
+
376
+ #paste(e)
377
+ {
378
+ e.preventDefault();
379
+ const pasteData = (e.clipboardData || window.clipboardData).getData("text");
380
+ let charIndex = this.#getCharIndexFromCaret(this.#input.selectionStart);
381
+
382
+ if (charIndex === null)
383
+ return;
384
+
385
+ for (let char of pasteData)
386
+ {
387
+ if (charIndex >= this.#inputSlots.length)
388
+ break;
389
+
390
+ if (this.#isAllowedChar(char, charIndex) && charIndex < this.#inputSlots.length)
391
+ {
392
+ this.#inputData[charIndex++] = char;
393
+ }
394
+ }
395
+ this.renderValue();
396
+
397
+ if (charIndex < this.#inputSlots.length)
398
+ this.#setCaret(this.#getCharPosByIndex(charIndex));
399
+ else
400
+ this.#setCaret((this.#inputSlots.at(-1)?.maskIndex ?? (this.mask.length - 1)) + 1);
401
+ }
402
+
403
+ #keyDown(e)
404
+ {
405
+ const caret = this.#input.selectionStart,
406
+ selectionEnd = this.#input.selectionEnd;
407
+
408
+ // Ignore non-character keys for now
409
+ if (["ArrowLeft", "ArrowRight", "Home", "End", "Tab", "Shift", "Control", "Alt", "Meta"].includes(e.key) || (e.ctrlKey && e.key.toLowerCase() === "v"))
410
+ return;
411
+
412
+ e.preventDefault();
413
+
414
+ if (caret !== selectionEnd)
415
+ {
416
+ let startCharIndex = this.#clearSelection();
417
+
418
+ if (e.key === "Backspace" || e.key === "Delete")
419
+ {
420
+ if (startCharIndex !== null)
421
+ this.#setCaret(this.#getCharPosByIndex(startCharIndex));
422
+
423
+ return;
424
+ }
425
+ }
426
+
427
+ if (e.key === "Backspace")
428
+ {
429
+ let charIndex = this.#getPreviousCharIndexFromCaret(caret);
430
+ if (charIndex !== null)
431
+ {
432
+ this.#clearCharacter(charIndex);
433
+ }
434
+ return;
435
+ }
436
+ else if (e.key === "Delete")
437
+ {
438
+ let charIndex = this.#getCharIndexFromCaret(caret);
439
+ if (charIndex !== null)
440
+ {
441
+ this.#shiftCharactersLeft(charIndex);
442
+ }
443
+ return;
444
+ }
445
+
446
+ if (e.key.length === 1)
447
+ {
448
+ if (caret > this.#inputSlots[this.#inputSlots.length - 1].maskIndex)
449
+ return;
450
+
451
+ const charIndex = this.#getCharIndexFromCaret(caret);
452
+
453
+ if (this.#isAllowedChar(e.key, charIndex))
454
+ this.#insertCharacter(caret, e.key);
455
+ }
456
+ }
457
+
458
+ #shiftCharactersLeft(startIndex)
459
+ {
460
+ for (let i = startIndex; i < this.#inputData.length - 1; i++)
461
+ {
462
+ this.#inputData[i] = this.#inputData[i + 1];
463
+ }
464
+ this.#inputData[this.#inputData.length - 1] = ""; // Clear last character
465
+ this.renderValue();
466
+ this.#setCaret(this.#getCharPosByIndex(startIndex));
467
+ }
468
+
469
+ #insertCharacter(caret, char)
470
+ {
471
+ const charIndex = this.#getCharIndexFromCaret(caret);
472
+ if (charIndex !== null)
473
+ {
474
+ this.#inputData[charIndex] = char;
475
+ this.renderValue();
476
+
477
+ if (charIndex < this.#inputSlots.length - 1)
478
+ this.#setCaret(this.#getCharPosByIndex(charIndex + 1));
479
+ else
480
+ this.#setCaret((this.#inputSlots.at(-1)?.maskIndex ?? (this.mask.length - 1)) + 1);
481
+ }
482
+ }
483
+
484
+ #clearCharacter(charIndex)
485
+ {
486
+ if (charIndex !== null)
487
+ {
488
+ this.#inputData[charIndex] = '';
489
+ this.renderValue();
490
+ this.#setCaret(this.#getCharPosByIndex(charIndex));
491
+ }
492
+ }
493
+
494
+ #isAllowedChar(char, index = null)
495
+ {
496
+ let token = null;
497
+
498
+ if (index !== null && this.#inputSlots[index])
499
+ token = this.#inputSlots[index].token;
500
+
501
+ if (token === '0')
502
+ return /\d/.test(char);
503
+ else if (token === 'A')
504
+ return /[a-zA-Z]/.test(char);
505
+ else // "_"
506
+ {
507
+ if (this.allowedCharacters === MaskedTextBox.AllowedCharactersOption.DIGITS)
508
+ return /\d/.test(char);
509
+ else if (this.allowedCharacters === MaskedTextBox.AllowedCharactersOption.LETTERS)
510
+ return /[a-zA-Z]/.test(char);
511
+ else if (this.allowedCharacters === MaskedTextBox.AllowedCharactersOption.CUSTOM && this.customPattern instanceof RegExp)
512
+ return this.customPattern.test(char);
513
+ else
514
+ return /\w/.test(char);
515
+ }
516
+ }
517
+
518
+ #getCharIndexFromCaret(caret)
519
+ {
520
+ let index = this.#inputSlots.findIndex(p => p.maskIndex >= caret);
521
+ return index === -1 ? null : index;
522
+ }
523
+
524
+ #getPreviousCharIndexFromCaret(caret)
525
+ {
526
+ for (var index = this.#inputSlots.length - 1; index >= 0; index--)
527
+ {
528
+ if (this.#inputSlots[index].maskIndex < caret)
529
+ {
530
+ return index;
531
+ }
532
+ }
533
+ return null;
534
+ }
535
+
536
+ #getCharPosByIndex(index)
537
+ {
538
+ return this.#inputSlots[index].maskIndex;
539
+ }
540
+
541
+ #adjustCaret(evt)
542
+ {
543
+ if (evt.type === 'focus')
544
+ this.#firstFocus = true;
545
+
546
+ setTimeout(() =>
547
+ {
548
+ const caret = this.#input.selectionStart;
549
+ const lastSlot = this.#inputSlots.at(-1)?.maskIndex ?? 0;
550
+
551
+ if (this.#firstFocus)
552
+ {
553
+ const emptyIndex = this.#inputData.findIndex(char => char === ""),
554
+ firstFreeSlot = emptyIndex !== -1
555
+ ? this.#inputSlots[emptyIndex].maskIndex
556
+ : this.mask.length;
557
+
558
+ this.#setCaret(firstFreeSlot);
559
+ this.#firstFocus = false;
560
+ return;
561
+ }
562
+
563
+ if (caret >= lastSlot)
564
+ return;
565
+
566
+ if (!this.#inputSlots.some(p => p.maskIndex === caret))
567
+ {
568
+ const charIndex = this.#getCharIndexFromCaret(caret);
569
+
570
+ if (charIndex !== null)
571
+ this.#setCaret(this.#getCharPosByIndex(charIndex));
572
+ else
573
+ this.#setCaret(lastSlot);
574
+ }
575
+ }, 0);
576
+ }
577
+
578
+ #setCaret(pos)
579
+ {
580
+ this.#input.setSelectionRange(pos, pos);
581
+ }
582
+
583
+ #clearSelection()
584
+ {
585
+ const caret = this.#input.selectionStart,
586
+ end = this.#input.selectionEnd,
587
+ startCharIndex = this.#getCharIndexFromCaret(caret);
588
+
589
+ if (caret > this.#inputSlots[this.#inputSlots.length - 1].maskIndex || end < this.#inputSlots[0].maskIndex)
590
+ return null;
591
+
592
+ if (caret !== end)
593
+ {
594
+ this.#inputSlots.forEach((pos, index) =>
595
+ {
596
+ if (pos.maskIndex >= caret && pos.maskIndex < end)
597
+ {
598
+ this.#inputData[index] = "";
599
+ }
600
+ });
601
+
602
+ this.renderValue();
603
+ }
604
+
605
+ return startCharIndex;
606
+ }
607
+
608
+ #dispose()
609
+ {
610
+ this.#disposeEvents();
611
+ }
612
+ }
613
+
614
+ /**
615
+ * AllowedCharactersOption
616
+ * @readonly
617
+ * @enum {number}
618
+ */
619
+ MaskedTextBox.AllowedCharactersOption =
620
+ {
621
+ ALPHANUMERIC: 0,
622
+ DIGITS: 1,
623
+ LETTERS: 2,
624
+ CUSTOM: 3
625
+ };
626
+
627
+
628
+
629
+ // Preserve HTMLElement prototype and extend it with $base.methods
630
+ Object.assign(MaskedTextBox.prototype, Object.fromEntries(Object.entries($base.methods).filter(([key]) => !['render', 'destroy', 'getCssClass'].includes(key))));
631
+
632
+ // Restore the constructor reference
633
+ MaskedTextBox.prototype.constructor = MaskedTextBox;
634
+ componyx.UI.MaskedTextBox = MaskedTextBox;
635
+
636
+ // Define the custom element
637
+ customElements.define(`${componyx.UI.tagPrefix}${MaskedTextBox.name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '')}`, MaskedTextBox); // cui-masked-text-box
638
+ })();
639
+
640
+ const MaskedTextBox = componyx.UI.MaskedTextBox;
641
+
642
+ export { MaskedTextBox };
@@ -0,0 +1,6 @@
1
+ /*! Componyx.UI | (c) E.H. Daanen / Componyx | BUSL-1.1 | componyx.com/license */
2
+ .masked-textbox.theme.theme-default.mask-hint::after
3
+ {
4
+ background-color: var(--cui-tooltip-bg-color);
5
+ color: var(--cui-tooltip-text-color);
6
+ }