@seniorsistemas/angular-components 18.0.1 → 18.0.2
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/badge/lib/badge/badge.component.d.ts +2 -1
- package/badge/lib/badge/badge.module.d.ts +3 -1
- package/esm2022/badge/lib/badge/badge.component.mjs +6 -4
- package/esm2022/badge/lib/badge/badge.module.mjs +6 -4
- package/esm2022/button/lib/button/button.component.mjs +2 -2
- package/esm2022/dynamic-form/lib/dynamic-form/components/lookup/lookup.component.mjs +2 -2
- package/esm2022/ia-insight/lib/ia-insight/components/ia-insight-card/ia-insight-card.component.mjs +1 -1
- package/esm2022/kanban/lib/kanban/components/kanban-item-dragging/kanban-item-dragging.component.mjs +1 -1
- package/esm2022/label-value/lib/label-value/label-value.component.mjs +1 -1
- package/esm2022/numeric-mask/lib/numeric-mask/numeric-mask.directive.mjs +579 -0
- package/esm2022/numeric-mask/lib/numeric-mask/numeric-mask.module.mjs +16 -0
- package/esm2022/numeric-mask/public-api.mjs +3 -0
- package/esm2022/numeric-mask/seniorsistemas-angular-components-numeric-mask.mjs +5 -0
- package/esm2022/object-card/lib/object-card/elements/field/object-card-field.component.mjs +7 -4
- package/esm2022/object-card/lib/object-card/object-card.module.mjs +5 -4
- package/esm2022/table/lib/table/table-column/table-columns.component.mjs +1 -1
- package/esm2022/tooltip/lib/tooltip/tooltip.directive.mjs +26 -20
- package/fesm2022/seniorsistemas-angular-components-badge.mjs +10 -6
- package/fesm2022/seniorsistemas-angular-components-badge.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-button.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-button.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-ia-insight.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-ia-insight.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-kanban.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-kanban.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-label-value.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-label-value.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-numeric-mask.mjs +599 -0
- package/fesm2022/seniorsistemas-angular-components-numeric-mask.mjs.map +1 -0
- package/fesm2022/seniorsistemas-angular-components-object-card.mjs +10 -6
- package/fesm2022/seniorsistemas-angular-components-object-card.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-table.mjs +1 -1
- package/fesm2022/seniorsistemas-angular-components-table.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-tooltip.mjs +23 -17
- package/fesm2022/seniorsistemas-angular-components-tooltip.mjs.map +1 -1
- package/numeric-mask/README.md +367 -0
- package/numeric-mask/index.d.ts +5 -0
- package/numeric-mask/lib/numeric-mask/numeric-mask.directive.d.ts +195 -0
- package/numeric-mask/lib/numeric-mask/numeric-mask.module.d.ts +7 -0
- package/numeric-mask/public-api.d.ts +2 -0
- package/object-card/lib/object-card/elements/field/object-card-field.component.d.ts +4 -1
- package/object-card/lib/object-card/object-card.module.d.ts +2 -1
- package/package.json +7 -1
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, inject, ElementRef, Renderer2, effect, forwardRef, Directive, NgModule } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
|
|
4
|
+
import { LocaleService } from '@seniorsistemas/angular-components/locale';
|
|
5
|
+
import BigNumber from 'bignumber.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Numeric mask directive with internationalization support.
|
|
9
|
+
*
|
|
10
|
+
* Formats numeric values according to the specified locale, applying
|
|
11
|
+
* appropriate thousand and decimal separators, with support for negative values,
|
|
12
|
+
* min/max decimal places control, and scientific notation.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```html
|
|
16
|
+
* <input
|
|
17
|
+
* type="text"
|
|
18
|
+
* sNumericMask
|
|
19
|
+
* [locale]="'pt-BR'"
|
|
20
|
+
* [minDecimalPlaces]="2"
|
|
21
|
+
* [maxDecimalPlaces]="2"
|
|
22
|
+
* [allowNegative]="true"
|
|
23
|
+
* [(ngModel)]="value"
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class NumericMaskDirective {
|
|
28
|
+
/**
|
|
29
|
+
* Locale for formatting (e.g. 'pt-BR', 'en-US', 'de-DE')
|
|
30
|
+
* If not provided, uses the locale from LocaleService
|
|
31
|
+
*/
|
|
32
|
+
locale = input(undefined);
|
|
33
|
+
/**
|
|
34
|
+
* Minimum number of decimal places to display
|
|
35
|
+
* @default 0
|
|
36
|
+
*/
|
|
37
|
+
minDecimalPlaces = input(0);
|
|
38
|
+
/**
|
|
39
|
+
* Maximum number of decimal places allowed
|
|
40
|
+
* @default 10
|
|
41
|
+
*/
|
|
42
|
+
maxDecimalPlaces = input(10);
|
|
43
|
+
/**
|
|
44
|
+
* Allows negative values
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
allowNegative = input(false);
|
|
48
|
+
/**
|
|
49
|
+
* Enables scientific notation support
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
allowScientificNotation = input(true);
|
|
53
|
+
/**
|
|
54
|
+
* Text alignment in input
|
|
55
|
+
* @default 'right'
|
|
56
|
+
*/
|
|
57
|
+
textAlign = input('right');
|
|
58
|
+
/**
|
|
59
|
+
* Minimum value allowed
|
|
60
|
+
*/
|
|
61
|
+
min = input(undefined);
|
|
62
|
+
/**
|
|
63
|
+
* Maximum value allowed
|
|
64
|
+
*/
|
|
65
|
+
max = input(undefined);
|
|
66
|
+
elementRef = inject((ElementRef));
|
|
67
|
+
renderer = inject(Renderer2);
|
|
68
|
+
localeService = inject(LocaleService, { optional: true });
|
|
69
|
+
onChange = () => { };
|
|
70
|
+
onTouched = () => { };
|
|
71
|
+
decimalSeparator = ',';
|
|
72
|
+
thousandSeparator = '.';
|
|
73
|
+
currentLocale = 'pt-BR';
|
|
74
|
+
lastModelValue = null;
|
|
75
|
+
constructor() {
|
|
76
|
+
effect(() => {
|
|
77
|
+
const inputLocale = this.locale();
|
|
78
|
+
if (inputLocale) {
|
|
79
|
+
this.currentLocale = inputLocale;
|
|
80
|
+
this.updateSeparators();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
ngOnInit() {
|
|
85
|
+
if (this.minDecimalPlaces() > this.maxDecimalPlaces()) {
|
|
86
|
+
throw new Error(`NumericMaskDirective: minDecimalPlaces (${this.minDecimalPlaces()}) cannot be greater than maxDecimalPlaces (${this.maxDecimalPlaces()})`);
|
|
87
|
+
}
|
|
88
|
+
if (this.locale()) {
|
|
89
|
+
this.currentLocale = this.locale();
|
|
90
|
+
this.updateSeparators();
|
|
91
|
+
this.setInputMode();
|
|
92
|
+
}
|
|
93
|
+
else if (this.localeService) {
|
|
94
|
+
this.localeService.get().subscribe(() => {
|
|
95
|
+
this.currentLocale = this.localeService.getLocaleOptions()?.locale || 'pt-BR';
|
|
96
|
+
this.updateSeparators();
|
|
97
|
+
this.setInputMode();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.updateSeparators();
|
|
102
|
+
this.setInputMode();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Listener for input events (typing)
|
|
107
|
+
*/
|
|
108
|
+
onInput(event) {
|
|
109
|
+
const input = event.target;
|
|
110
|
+
let value = input.value;
|
|
111
|
+
value = this.normalizeSeparators(value);
|
|
112
|
+
this.processInputValue(value);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Listener for focus events (gaining focus)
|
|
116
|
+
*/
|
|
117
|
+
onFocus() {
|
|
118
|
+
const input = this.elementRef.nativeElement;
|
|
119
|
+
const currentValue = input.value;
|
|
120
|
+
if (!currentValue || currentValue.trim() === '') {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (this.lastModelValue) {
|
|
124
|
+
const displayValue = this.lastModelValue.replace('.', this.decimalSeparator);
|
|
125
|
+
this.renderer.setProperty(input, 'value', displayValue);
|
|
126
|
+
input.select();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const modelValue = this.toModelValue(currentValue);
|
|
130
|
+
if (!modelValue) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const displayValue = modelValue.replace('.', this.decimalSeparator);
|
|
134
|
+
this.renderer.setProperty(input, 'value', displayValue);
|
|
135
|
+
input.select();
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Listener for blur events (focus loss)
|
|
139
|
+
*/
|
|
140
|
+
onBlur() {
|
|
141
|
+
this.onTouched();
|
|
142
|
+
const currentValue = this.elementRef.nativeElement.value;
|
|
143
|
+
if (!currentValue || currentValue.trim() === '') {
|
|
144
|
+
this.onChange(null);
|
|
145
|
+
this.lastModelValue = null;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const modelValue = this.toModelValue(currentValue);
|
|
149
|
+
if (!modelValue) {
|
|
150
|
+
this.onChange(null);
|
|
151
|
+
this.lastModelValue = null;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.lastModelValue = modelValue;
|
|
155
|
+
const displayValue = this.fromModelValue(modelValue);
|
|
156
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'value', displayValue);
|
|
157
|
+
this.onChange(modelValue);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Listener for paste events (clipboard)
|
|
161
|
+
*/
|
|
162
|
+
onPaste(event) {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
const pastedText = event.clipboardData?.getData('text') || '';
|
|
165
|
+
let value = this.parseClipboardValue(pastedText);
|
|
166
|
+
this.processInputValue(value);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Listener for special keys (+ and -)
|
|
170
|
+
*/
|
|
171
|
+
onKeyDown(event) {
|
|
172
|
+
const key = event.key;
|
|
173
|
+
if (key === '-' || key === '+') {
|
|
174
|
+
const input = this.elementRef.nativeElement;
|
|
175
|
+
const value = input.value;
|
|
176
|
+
const cursorPosition = input.selectionStart || 0;
|
|
177
|
+
if (key === '-' && this.allowScientificNotation()) {
|
|
178
|
+
const charBeforeCursor = value[cursorPosition - 1];
|
|
179
|
+
if (charBeforeCursor === 'e' || charBeforeCursor === 'E') {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
if (!this.allowNegative()) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.toggleSign(value, key === '-');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Listener for keypress to block invalid characters
|
|
192
|
+
*/
|
|
193
|
+
onKeyPress(event) {
|
|
194
|
+
const key = event.key;
|
|
195
|
+
const input = this.elementRef.nativeElement;
|
|
196
|
+
const cursorPosition = input.selectionStart || 0;
|
|
197
|
+
const value = input.value;
|
|
198
|
+
if ((key === '-' || key === '+') && this.allowScientificNotation()) {
|
|
199
|
+
const charBeforeCursor = value[cursorPosition - 1];
|
|
200
|
+
if (charBeforeCursor === 'e' || charBeforeCursor === 'E') {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (key === '+' || key === '-') {
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const isDigit = /^[0-9]$/.test(key);
|
|
209
|
+
const isLocaleDecimalSeparator = key === this.decimalSeparator;
|
|
210
|
+
const isOtherSeparator = (key === ',' || key === '.') && key !== this.decimalSeparator;
|
|
211
|
+
const isScientificNotation = (key === 'e' || key === 'E') && this.allowScientificNotation();
|
|
212
|
+
const isControlKey = event.ctrlKey || event.metaKey || event.altKey;
|
|
213
|
+
const isSpecialKey = [
|
|
214
|
+
'Backspace',
|
|
215
|
+
'Tab',
|
|
216
|
+
'Enter',
|
|
217
|
+
'Escape',
|
|
218
|
+
'ArrowLeft',
|
|
219
|
+
'ArrowRight',
|
|
220
|
+
'Delete',
|
|
221
|
+
'Home',
|
|
222
|
+
'End',
|
|
223
|
+
].includes(key);
|
|
224
|
+
if (isOtherSeparator) {
|
|
225
|
+
event.preventDefault();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (isScientificNotation) {
|
|
229
|
+
if (value.includes('e') || value.includes('E')) {
|
|
230
|
+
event.preventDefault();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const charBeforeCursor = value[cursorPosition - 1];
|
|
234
|
+
if (!charBeforeCursor || !/^[0-9]$/.test(charBeforeCursor)) {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (!isDigit && !isLocaleDecimalSeparator && !isScientificNotation && !isControlKey && !isSpecialKey) {
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Updates separators according to the locale
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
updateSeparators() {
|
|
248
|
+
const formatted = new Intl.NumberFormat(this.currentLocale).format(1234.5);
|
|
249
|
+
this.thousandSeparator = formatted[1];
|
|
250
|
+
this.decimalSeparator = formatted[5];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Processes input value and updates the input and model
|
|
254
|
+
* @private
|
|
255
|
+
*/
|
|
256
|
+
processInputValue(value) {
|
|
257
|
+
if (!this.decimalSeparator || !this.thousandSeparator) {
|
|
258
|
+
this.updateSeparators();
|
|
259
|
+
}
|
|
260
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'value', value);
|
|
261
|
+
const modelValue = this.toModelValue(value);
|
|
262
|
+
// During typing, clear the stored model value so onFocus will recalculate
|
|
263
|
+
this.lastModelValue = null;
|
|
264
|
+
this.onChange(modelValue);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Sets inputmode for numeric keyboard on mobile devices
|
|
268
|
+
* @private
|
|
269
|
+
*/
|
|
270
|
+
setInputMode() {
|
|
271
|
+
this.renderer.setAttribute(this.elementRef.nativeElement, 'inputmode', 'decimal');
|
|
272
|
+
this.renderer.setAttribute(this.elementRef.nativeElement, 'autocomplete', 'off');
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Removes separators from a formatted value
|
|
276
|
+
* @private
|
|
277
|
+
*/
|
|
278
|
+
removeSeparators(value) {
|
|
279
|
+
const thousandRegex = new RegExp(`\\${this.escapeRegex(this.thousandSeparator)}`, 'g');
|
|
280
|
+
return value.replace(thousandRegex, '');
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Strips unnecessary trailing zeros from decimal part
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
stripTrailingZeros(value) {
|
|
287
|
+
const parts = value.split(this.decimalSeparator);
|
|
288
|
+
if (parts.length === 2) {
|
|
289
|
+
const decimalPart = parts[1].replace(/0+$/, '');
|
|
290
|
+
return decimalPart.length > 0 ? parts[0] + this.decimalSeparator + decimalPart : parts[0];
|
|
291
|
+
}
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Normalizes separators during typing
|
|
296
|
+
* @private
|
|
297
|
+
*/
|
|
298
|
+
normalizeSeparators(value) {
|
|
299
|
+
value = this.removeSeparators(value);
|
|
300
|
+
if (this.decimalSeparator === ',') {
|
|
301
|
+
value = value.replace(/\./g, ',');
|
|
302
|
+
}
|
|
303
|
+
else if (this.decimalSeparator === '.') {
|
|
304
|
+
value = value.replace(/,/g, '.');
|
|
305
|
+
}
|
|
306
|
+
const parts = value.split(this.decimalSeparator);
|
|
307
|
+
if (parts.length > 2) {
|
|
308
|
+
value = parts[0] + this.decimalSeparator + parts.slice(1).join('');
|
|
309
|
+
}
|
|
310
|
+
return value;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Escapes special characters for use in regex
|
|
314
|
+
* @private
|
|
315
|
+
*/
|
|
316
|
+
escapeRegex(str) {
|
|
317
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Truncates a number to the specified decimal places (never rounds)
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
truncateToDecimalPlaces(bigNumber, decimalPlaces) {
|
|
324
|
+
if (decimalPlaces < 0) {
|
|
325
|
+
return bigNumber;
|
|
326
|
+
}
|
|
327
|
+
const multiplier = new BigNumber(10).pow(decimalPlaces);
|
|
328
|
+
return bigNumber.multipliedBy(multiplier).integerValue(BigNumber.ROUND_DOWN).dividedBy(multiplier);
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Checks if the value has excessive decimal places
|
|
332
|
+
* @private
|
|
333
|
+
*/
|
|
334
|
+
hasExcessiveDecimalPlaces(value) {
|
|
335
|
+
const parts = value.split(this.decimalSeparator);
|
|
336
|
+
if (parts.length > 1) {
|
|
337
|
+
const decimalPart = parts[1].replace(/[^0-9]/g, '');
|
|
338
|
+
return decimalPart.length > this.maxDecimalPlaces();
|
|
339
|
+
}
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Clears the input and notifies the model
|
|
344
|
+
* @private
|
|
345
|
+
*/
|
|
346
|
+
clearInput() {
|
|
347
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'value', '');
|
|
348
|
+
this.lastModelValue = null;
|
|
349
|
+
this.onChange(null);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Parses a string value to its numeric representation, handling separators and sign
|
|
353
|
+
* Returns BigNumber to preserve full precision and optional number conversion
|
|
354
|
+
* @private
|
|
355
|
+
*/
|
|
356
|
+
parseStringToValue(value) {
|
|
357
|
+
const isNegative = value.startsWith('-');
|
|
358
|
+
let numericStr = value
|
|
359
|
+
.replace(/^-/, '')
|
|
360
|
+
.replace(new RegExp(`\\${this.thousandSeparator}`, 'g'), '')
|
|
361
|
+
.replace(new RegExp(`\\${this.decimalSeparator}`, 'g'), '.');
|
|
362
|
+
if (numericStr === '.' || numericStr === '') {
|
|
363
|
+
numericStr = '0';
|
|
364
|
+
}
|
|
365
|
+
else if (numericStr.startsWith('.')) {
|
|
366
|
+
numericStr = '0' + numericStr;
|
|
367
|
+
}
|
|
368
|
+
let bigNumber = new BigNumber(numericStr);
|
|
369
|
+
if (bigNumber.isNaN()) {
|
|
370
|
+
bigNumber = new BigNumber(0);
|
|
371
|
+
}
|
|
372
|
+
if (isNegative && this.allowNegative()) {
|
|
373
|
+
bigNumber = bigNumber.negated();
|
|
374
|
+
}
|
|
375
|
+
return { bigNumber, numeric: bigNumber.toNumber(), isNegative };
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Formats a numeric value according to the locale
|
|
379
|
+
* Truncates (never rounds) to maxDecimalPlaces for display purposes only
|
|
380
|
+
* @private
|
|
381
|
+
*/
|
|
382
|
+
formatNumeric(numeric) {
|
|
383
|
+
let bigNumeric = numeric instanceof BigNumber ? numeric : new BigNumber(numeric);
|
|
384
|
+
const truncatedValue = this.truncateToDecimalPlaces(bigNumeric, this.maxDecimalPlaces());
|
|
385
|
+
const valueStr = truncatedValue.toFixed(this.maxDecimalPlaces());
|
|
386
|
+
let formattedValue = valueStr.replace('.', this.decimalSeparator);
|
|
387
|
+
const parts = formattedValue.split(this.decimalSeparator);
|
|
388
|
+
const integerPart = parts[0];
|
|
389
|
+
const decimalPart = parts[1] || '';
|
|
390
|
+
const formattedInteger = new Intl.NumberFormat(this.currentLocale).format(parseInt(integerPart, 10) || 0).split(this.decimalSeparator)[0];
|
|
391
|
+
const minDecimal = Math.max(this.minDecimalPlaces(), decimalPart.replace(/0+$/, '').length || 0);
|
|
392
|
+
const finalDecimal = decimalPart.padEnd(minDecimal, '0');
|
|
393
|
+
return formattedInteger + this.decimalSeparator + finalDecimal;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Formats the value according to the locale
|
|
397
|
+
* @private
|
|
398
|
+
*/
|
|
399
|
+
formatValue(value) {
|
|
400
|
+
if (!value || value.trim() === '') {
|
|
401
|
+
return '';
|
|
402
|
+
}
|
|
403
|
+
const { numeric } = this.parseStringToValue(value);
|
|
404
|
+
return this.formatNumeric(numeric);
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Converts the formatted value to the model format (international standard string)
|
|
408
|
+
* Always returns decimal notation, never scientific notation
|
|
409
|
+
* Stores the full precision value without truncation
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
toModelValue(formattedValue) {
|
|
413
|
+
if (!formattedValue || formattedValue.trim() === '') {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
const { bigNumber } = this.parseStringToValue(formattedValue);
|
|
417
|
+
return bigNumber.toFixed();
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Converts the model value to display format
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
fromModelValue(modelValue) {
|
|
424
|
+
if (modelValue === null || modelValue === undefined || modelValue === '') {
|
|
425
|
+
return '';
|
|
426
|
+
}
|
|
427
|
+
const bigNumber = new BigNumber(modelValue);
|
|
428
|
+
if (bigNumber.isNaN()) {
|
|
429
|
+
return '';
|
|
430
|
+
}
|
|
431
|
+
return this.formatNumeric(bigNumber);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Processes pasted value from clipboard
|
|
435
|
+
* Converts scientific notation to decimal using BigNumber
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
parseClipboardValue(text) {
|
|
439
|
+
if (this.allowScientificNotation() && /[eE]/.test(text)) {
|
|
440
|
+
try {
|
|
441
|
+
const bigNumeric = new BigNumber(text);
|
|
442
|
+
if (!bigNumeric.isNaN()) {
|
|
443
|
+
text = bigNumeric.toFixed();
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
void 0;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return this.normalizeSeparators(text);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Toggles the sign (- or +) of a value
|
|
454
|
+
* @private
|
|
455
|
+
*/
|
|
456
|
+
toggleSign(value, makeNegative) {
|
|
457
|
+
const input = this.elementRef.nativeElement;
|
|
458
|
+
let newValue = value;
|
|
459
|
+
if (makeNegative && !newValue.startsWith('-')) {
|
|
460
|
+
newValue = '-' + newValue;
|
|
461
|
+
}
|
|
462
|
+
else if (!makeNegative && newValue.startsWith('-')) {
|
|
463
|
+
newValue = newValue.substring(1);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
this.renderer.setProperty(input, 'value', newValue);
|
|
469
|
+
const modelValue = this.toModelValue(newValue);
|
|
470
|
+
this.lastModelValue = null;
|
|
471
|
+
this.onChange(modelValue);
|
|
472
|
+
}
|
|
473
|
+
writeValue(value) {
|
|
474
|
+
if (!this.decimalSeparator || !this.thousandSeparator) {
|
|
475
|
+
this.updateSeparators();
|
|
476
|
+
}
|
|
477
|
+
const hasFocus = document.activeElement === this.elementRef.nativeElement;
|
|
478
|
+
if (hasFocus) {
|
|
479
|
+
const formattedValue = this.fromModelValue(value);
|
|
480
|
+
const rawValue = this.stripTrailingZeros(this.removeSeparators(formattedValue));
|
|
481
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'value', rawValue);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
const formattedValue = this.fromModelValue(value);
|
|
485
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'value', formattedValue);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
registerOnChange(fn) {
|
|
489
|
+
this.onChange = fn;
|
|
490
|
+
}
|
|
491
|
+
registerOnTouched(fn) {
|
|
492
|
+
this.onTouched = fn;
|
|
493
|
+
}
|
|
494
|
+
setDisabledState(isDisabled) {
|
|
495
|
+
this.renderer.setProperty(this.elementRef.nativeElement, 'disabled', isDisabled);
|
|
496
|
+
}
|
|
497
|
+
validate(control) {
|
|
498
|
+
const value = control.value;
|
|
499
|
+
if (!value) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
const numericValue = parseFloat(value);
|
|
503
|
+
if (isNaN(numericValue)) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
if (!this.allowNegative() && numericValue < 0) {
|
|
507
|
+
return { negativeNotAllowed: true };
|
|
508
|
+
}
|
|
509
|
+
const minValue = this.min();
|
|
510
|
+
if (minValue !== undefined && numericValue < minValue) {
|
|
511
|
+
return {
|
|
512
|
+
min: {
|
|
513
|
+
min: minValue,
|
|
514
|
+
actual: numericValue,
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const maxValue = this.max();
|
|
519
|
+
if (maxValue !== undefined && numericValue > maxValue) {
|
|
520
|
+
return {
|
|
521
|
+
max: {
|
|
522
|
+
max: maxValue,
|
|
523
|
+
actual: numericValue,
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
const decimalPart = value.split('.')[1];
|
|
528
|
+
if (decimalPart && decimalPart.length > this.maxDecimalPlaces()) {
|
|
529
|
+
return {
|
|
530
|
+
excessiveDecimalPlaces: {
|
|
531
|
+
max: this.maxDecimalPlaces(),
|
|
532
|
+
actual: decimalPart.length,
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NumericMaskDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
539
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.2.14", type: NumericMaskDirective, isStandalone: true, selector: "[sNumericMask]", inputs: { locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, minDecimalPlaces: { classPropertyName: "minDecimalPlaces", publicName: "minDecimalPlaces", isSignal: true, isRequired: false, transformFunction: null }, maxDecimalPlaces: { classPropertyName: "maxDecimalPlaces", publicName: "maxDecimalPlaces", isSignal: true, isRequired: false, transformFunction: null }, allowNegative: { classPropertyName: "allowNegative", publicName: "allowNegative", isSignal: true, isRequired: false, transformFunction: null }, allowScientificNotation: { classPropertyName: "allowScientificNotation", publicName: "allowScientificNotation", isSignal: true, isRequired: false, transformFunction: null }, textAlign: { classPropertyName: "textAlign", publicName: "textAlign", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "input": "onInput($event)", "focus": "onFocus()", "blur": "onBlur()", "paste": "onPaste($event)", "keydown": "onKeyDown($event)", "keypress": "onKeyPress($event)" }, properties: { "style.text-align": "textAlign()" } }, providers: [
|
|
540
|
+
{
|
|
541
|
+
provide: NG_VALUE_ACCESSOR,
|
|
542
|
+
useExisting: forwardRef(() => NumericMaskDirective),
|
|
543
|
+
multi: true,
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
provide: NG_VALIDATORS,
|
|
547
|
+
useExisting: forwardRef(() => NumericMaskDirective),
|
|
548
|
+
multi: true,
|
|
549
|
+
},
|
|
550
|
+
], ngImport: i0 });
|
|
551
|
+
}
|
|
552
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: NumericMaskDirective, decorators: [{
|
|
553
|
+
type: Directive,
|
|
554
|
+
args: [{
|
|
555
|
+
selector: '[sNumericMask]',
|
|
556
|
+
standalone: true,
|
|
557
|
+
host: {
|
|
558
|
+
'(input)': 'onInput($event)',
|
|
559
|
+
'(focus)': 'onFocus()',
|
|
560
|
+
'(blur)': 'onBlur()',
|
|
561
|
+
'(paste)': 'onPaste($event)',
|
|
562
|
+
'(keydown)': 'onKeyDown($event)',
|
|
563
|
+
'(keypress)': 'onKeyPress($event)',
|
|
564
|
+
'[style.text-align]': 'textAlign()',
|
|
565
|
+
},
|
|
566
|
+
providers: [
|
|
567
|
+
{
|
|
568
|
+
provide: NG_VALUE_ACCESSOR,
|
|
569
|
+
useExisting: forwardRef(() => NumericMaskDirective),
|
|
570
|
+
multi: true,
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
provide: NG_VALIDATORS,
|
|
574
|
+
useExisting: forwardRef(() => NumericMaskDirective),
|
|
575
|
+
multi: true,
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
}]
|
|
579
|
+
}], ctorParameters: () => [] });
|
|
580
|
+
|
|
581
|
+
class CurrencyMaskModule {
|
|
582
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CurrencyMaskModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
583
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: CurrencyMaskModule, imports: [NumericMaskDirective], exports: [NumericMaskDirective] });
|
|
584
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CurrencyMaskModule });
|
|
585
|
+
}
|
|
586
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: CurrencyMaskModule, decorators: [{
|
|
587
|
+
type: NgModule,
|
|
588
|
+
args: [{
|
|
589
|
+
imports: [NumericMaskDirective],
|
|
590
|
+
exports: [NumericMaskDirective],
|
|
591
|
+
}]
|
|
592
|
+
}] });
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Generated bundle index. Do not edit.
|
|
596
|
+
*/
|
|
597
|
+
|
|
598
|
+
export { CurrencyMaskModule, NumericMaskDirective };
|
|
599
|
+
//# sourceMappingURL=seniorsistemas-angular-components-numeric-mask.mjs.map
|