@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.
- package/AppSkin/AppSkin.css +1268 -0
- package/AppSkin/AppSkin.js +2748 -0
- package/AppSkin/Themes/Default.css +241 -0
- package/Base/Base.js +3407 -0
- package/Base/ComponentIcons.css +66 -0
- package/Base/Font/ComponentIcons.woff +0 -0
- package/Base/Font/UI.woff +0 -0
- package/Base/Library.js +8432 -0
- package/Base/NavigationManager.js +462 -0
- package/Base/ResponsiveTemplates.js +371 -0
- package/Base/Sanitizer.js +288 -0
- package/Base/Themes/Default.css +255 -0
- package/Base/UI.css +618 -0
- package/Base/UIResourcePath.js +6 -0
- package/Bindary/Bindary.js +2011 -0
- package/Bindary/Modules/Connection.js +216 -0
- package/Bindary/Modules/Core.js +854 -0
- package/Bindary/Modules/DataUpdater.js +351 -0
- package/Bindary/Modules/ExpressionEngine.js +347 -0
- package/Bindary/Modules/ProxyManager.js +482 -0
- package/Bindary/Modules/Renderer.js +605 -0
- package/Bindary/Modules/Router.js +291 -0
- package/Bindary/Modules/Scroller.js +89 -0
- package/Bindary/Modules/Template.js +1273 -0
- package/BookmarkSpy/BookmarkSpy.js +430 -0
- package/Box/Box.css +36 -0
- package/Box/Box.js +1685 -0
- package/Button/Button.css +470 -0
- package/Button/Button.js +1398 -0
- package/Button/Themes/Default.css +200 -0
- package/Carousel/Carousel.css +273 -0
- package/Carousel/Carousel.js +607 -0
- package/ColorButton/ColorButton.css +39 -0
- package/ColorButton/ColorButton.js +336 -0
- package/ColorPicker/ColorPicker.css +359 -0
- package/ColorPicker/ColorPicker.js +972 -0
- package/ComboBox/ComboBox.css +357 -0
- package/ComboBox/ComboBox.js +2342 -0
- package/ComboBox/Themes/Default.css +23 -0
- package/DataPager/DataPager.css +72 -0
- package/DataPager/DataPager.js +674 -0
- package/DatePicker/DatePicker.css +227 -0
- package/DatePicker/DatePicker.js +1531 -0
- package/DatePicker/Themes/Default.css +26 -0
- package/Dialog/Dialog.css +106 -0
- package/Dialog/Dialog.js +572 -0
- package/Dialog/Themes/Default.css +10 -0
- package/Editor/Data/Emoji.json +8285 -0
- package/Editor/Data/Special.json +1042 -0
- package/Editor/Editor.css +1562 -0
- package/Editor/Editor.js +4058 -0
- package/Editor/Font/Editor.woff +0 -0
- package/Editor/Modules/ComponentFactory.js +565 -0
- package/Editor/Modules/ContentManager.js +748 -0
- package/Editor/Modules/DialogManager.js +1234 -0
- package/Editor/Modules/EventManager.js +458 -0
- package/Editor/Modules/Format.js +950 -0
- package/Editor/Modules/History.js +191 -0
- package/Editor/Modules/LayoutState.js +379 -0
- package/Editor/Modules/ListManager.js +296 -0
- package/Editor/Modules/MenuManager.js +388 -0
- package/Editor/Modules/NodeManager.js +681 -0
- package/Editor/Modules/ParagraphButtons.js +122 -0
- package/Editor/Modules/Resizer.js +176 -0
- package/Editor/Modules/SelectionRange.js +564 -0
- package/Editor/Modules/SourceViewBuilder.js +212 -0
- package/Editor/Modules/SymbolPicker.js +208 -0
- package/Editor/Modules/Table.js +2327 -0
- package/Editor/Modules/TableDialog.js +757 -0
- package/Editor/Modules/Toolbar.js +375 -0
- package/Editor/Modules/TooltipMenu.js +125 -0
- package/Editor/Modules/Utility.js +131 -0
- package/Editor/Themes/Default.css +204 -0
- package/FileUpload/FileUpload.css +156 -0
- package/FileUpload/FileUpload.js +898 -0
- package/FileUpload/Themes/Default.css +17 -0
- package/Form/Form.css +1934 -0
- package/Form/Form.js +4943 -0
- package/Form/Modules/ActionManager.js +368 -0
- package/Form/Modules/BuildPanelManager.js +319 -0
- package/Form/Modules/ComponentFactory.js +468 -0
- package/Form/Modules/ComponentPanel.js +265 -0
- package/Form/Modules/ConfigPanelManager.js +685 -0
- package/Form/Modules/DataBinder.js +602 -0
- package/Form/Modules/DataObserver.js +717 -0
- package/Form/Modules/Draggable.js +756 -0
- package/Form/Modules/OptionsPanel.js +342 -0
- package/Form/Modules/Renderer.js +1889 -0
- package/Form/Modules/RuleEngine.js +585 -0
- package/Form/Modules/RulesPanel.js +1158 -0
- package/Form/Modules/Types.js +1709 -0
- package/Form/Modules/ValidationManager.js +194 -0
- package/Form/Themes/Default.css +216 -0
- package/FormField/FormField.css +468 -0
- package/FormField/FormField.js +576 -0
- package/FormField/Themes/Default.css +99 -0
- package/Grid/Grid.css +544 -0
- package/Grid/Grid.js +3761 -0
- package/Grid/Themes/Default.css +43 -0
- package/LICENSE +126 -0
- package/MaskedTextBox/MaskedTextBox.css +28 -0
- package/MaskedTextBox/MaskedTextBox.js +642 -0
- package/MaskedTextBox/Themes/Default.css +6 -0
- package/Menu/Menu.css +328 -0
- package/Menu/Menu.js +2386 -0
- package/Menu/Themes/Default.css +24 -0
- package/NumericBox/NumericBox.css +120 -0
- package/NumericBox/NumericBox.js +984 -0
- package/PanelBar/PanelBar.css +123 -0
- package/PanelBar/PanelBar.js +505 -0
- package/PanelBar/Themes/Default.css +51 -0
- package/PieChart/PieChart.css +95 -0
- package/PieChart/PieChart.js +820 -0
- package/PieChart/Themes/Default.css +9 -0
- package/README.md +178 -3
- package/Slider/Slider.css +348 -0
- package/Slider/Slider.js +1306 -0
- package/Slider/Themes/Default.css +44 -0
- package/TabStrip/TabStrip.css +52 -0
- package/TabStrip/TabStrip.js +362 -0
- package/TabStrip/Themes/Default.css +35 -0
- package/TimePicker/Themes/Default.css +30 -0
- package/TimePicker/TimePicker.css +253 -0
- package/TimePicker/TimePicker.js +1485 -0
- package/TooltipManager/Themes/Default.css +21 -0
- package/TooltipManager/TooltipManager.css +227 -0
- package/TooltipManager/TooltipManager.js +791 -0
- package/Validator/Validator.css +54 -0
- package/Validator/Validator.js +2508 -0
- package/componyx.html-data.json +619 -0
- package/index.js +27 -0
- package/package.json +43 -4
- package/types/AppSkin.d.ts +842 -0
- package/types/Base.d.ts +1034 -0
- package/types/Bindary.d.ts +1196 -0
- package/types/BookmarkSpy.d.ts +112 -0
- package/types/Box.d.ts +374 -0
- package/types/Button.d.ts +455 -0
- package/types/Carousel.d.ts +136 -0
- package/types/ColorButton.d.ts +120 -0
- package/types/ColorPicker.d.ts +225 -0
- package/types/ComboBox.d.ts +507 -0
- package/types/DataPager.d.ts +152 -0
- package/types/DatePicker.d.ts +354 -0
- package/types/Dialog.d.ts +160 -0
- package/types/Editor.d.ts +1289 -0
- package/types/FileUpload.d.ts +206 -0
- package/types/Form.d.ts +3078 -0
- package/types/FormField.d.ts +204 -0
- package/types/Grid.d.ts +911 -0
- package/types/Library.d.ts +1899 -0
- package/types/MaskedTextBox.d.ts +91 -0
- package/types/Menu.d.ts +577 -0
- package/types/NumericBox.d.ts +310 -0
- package/types/PanelBar.d.ts +223 -0
- package/types/PieChart.d.ts +224 -0
- package/types/ResponsiveTemplates.d.ts +108 -0
- package/types/Slider.d.ts +227 -0
- package/types/TabStrip.d.ts +185 -0
- package/types/TimePicker.d.ts +265 -0
- package/types/TooltipManager.d.ts +178 -0
- package/types/UI.d.ts +29 -0
- package/types/Validator.d.ts +653 -0
- package/types/index.d.ts +59 -0
- package/ui.css +11783 -0
- package/ui.js +79477 -0
- package/ui.min.css +138 -0
- package/ui.min.js +264 -0
- 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 };
|