tmw-numeric 0.1.1
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/AI_BUILD_PLAN.md +127 -0
- package/esm2020/lib/models/tmw-numeric-options.model.mjs +37 -0
- package/esm2020/lib/tmw-numeric-mask.directive.mjs +272 -0
- package/esm2020/public-api.mjs +6 -0
- package/esm2020/tmw-numeric.mjs +5 -0
- package/fesm2015/tmw-numeric.mjs +320 -0
- package/fesm2015/tmw-numeric.mjs.map +1 -0
- package/fesm2020/tmw-numeric.mjs +319 -0
- package/fesm2020/tmw-numeric.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/models/tmw-numeric-options.model.d.ts +47 -0
- package/lib/tmw-numeric-mask.directive.d.ts +90 -0
- package/package.json +36 -0
- package/public-api.d.ts +2 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { forwardRef, Directive, Input, HostListener } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Modalità di digitazione — nomi/valori/comportamento invariati rispetto a
|
|
7
|
+
* `@eqproject/eqp-numeric` (`EqpNumericInputMode`), per parità di comportamento.
|
|
8
|
+
*/
|
|
9
|
+
var TmwNumericInputMode;
|
|
10
|
+
(function (TmwNumericInputMode) {
|
|
11
|
+
/**
|
|
12
|
+
* I numeri partono dalla precisione più alta (i decimali): digitare uno shifta tutto a
|
|
13
|
+
* sinistra, il carattere decimale digitato viene ignorato. Es. `'12'` → `'0.12'`,
|
|
14
|
+
* `'1234'` → `'12.34'`.
|
|
15
|
+
*/
|
|
16
|
+
TmwNumericInputMode["FINANCIAL"] = "FINANCIAL";
|
|
17
|
+
/**
|
|
18
|
+
* I numeri partono a sinistra del separatore decimale, come un campo di testo qualunque.
|
|
19
|
+
* Es. `'1234'` → `'1234'`, `'12.34'` → `'12.34'`.
|
|
20
|
+
*/
|
|
21
|
+
TmwNumericInputMode["NATURAL"] = "NATURAL";
|
|
22
|
+
})(TmwNumericInputMode || (TmwNumericInputMode = {}));
|
|
23
|
+
/**
|
|
24
|
+
* Default identici a `@eqproject/eqp-numeric` — nessun consumer TMED chiamava
|
|
25
|
+
* `EqpNumericModule.forRoot(...)` né passava `[options]`, quindi questi default SONO il
|
|
26
|
+
* comportamento reale in produzione (v. AI_BUILD_PLAN.md per i 5 punti d'uso reali).
|
|
27
|
+
*/
|
|
28
|
+
const TMW_NUMERIC_DEFAULT_OPTIONS = {
|
|
29
|
+
align: 'right',
|
|
30
|
+
allowNegative: true,
|
|
31
|
+
decimal: '.',
|
|
32
|
+
precision: 2,
|
|
33
|
+
prefix: '$ ',
|
|
34
|
+
suffix: '',
|
|
35
|
+
thousands: ',',
|
|
36
|
+
nullable: false,
|
|
37
|
+
min: undefined,
|
|
38
|
+
max: undefined,
|
|
39
|
+
inputMode: TmwNumericInputMode.NATURAL,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Sostituto di `[eqpNumericMask]` (`@eqproject/eqp-numeric`) — stesso `[options]`, stessi nomi
|
|
44
|
+
* di opzione, stessi default (v. `TMW_NUMERIC_DEFAULT_OPTIONS`). Nessun `forRoot()`: nessun
|
|
45
|
+
* consumer reale in TMED lo usava, i default del pacchetto originale erano già il comportamento
|
|
46
|
+
* effettivo in produzione.
|
|
47
|
+
*
|
|
48
|
+
* Formatta un `<input>` come numero mascherato (valuta/decimale) mantenendo il model
|
|
49
|
+
* (`ngModel`/`formControlName`) come `number | null` — l'utente vede `'$ 1,234.56'`, il form
|
|
50
|
+
* riceve `1234.56`.
|
|
51
|
+
*
|
|
52
|
+
* In modalità NATURAL la formattazione "live" (durante la digitazione) NON forza il padding a
|
|
53
|
+
* piena precisione: farlo ad ogni tasto arrotonderebbe via le cifre appena digitate (es. digitare
|
|
54
|
+
* "1" su un campo vuoto renderizzerebbe subito "0.00", perdendo la cifra) — il padding/
|
|
55
|
+
* arrotondamento pieno avviene solo al `blur`/`writeValue`. In modalità FINANCIAL questo problema
|
|
56
|
+
* non esiste (il valore si ricalcola sempre da zero dalle sole cifre digitate, insensibile alla
|
|
57
|
+
* formattazione precedente), quindi lì si usa direttamente il formato pieno ad ogni tasto.
|
|
58
|
+
*/
|
|
59
|
+
class TmwNumericMaskDirective {
|
|
60
|
+
set options(value) {
|
|
61
|
+
this._options = { ...TMW_NUMERIC_DEFAULT_OPTIONS, ...(value ?? {}) };
|
|
62
|
+
this._applyAlignment();
|
|
63
|
+
this._renderFinal(this._value);
|
|
64
|
+
}
|
|
65
|
+
constructor(_elementRef, _renderer) {
|
|
66
|
+
this._elementRef = _elementRef;
|
|
67
|
+
this._renderer = _renderer;
|
|
68
|
+
this._options = { ...TMW_NUMERIC_DEFAULT_OPTIONS };
|
|
69
|
+
this._value = null;
|
|
70
|
+
this._onChange = () => { };
|
|
71
|
+
this._onTouched = () => { };
|
|
72
|
+
this._disabled = false;
|
|
73
|
+
/**
|
|
74
|
+
* Stato PERSISTENTE (non ri-derivato ogni volta dalla stringa già formattata): true dal
|
|
75
|
+
* momento in cui l'utente ha digitato/incollato un carattere decimale, false quando il campo
|
|
76
|
+
* è vuoto o quel carattere viene cancellato. Necessario perché in formato euro `thousands` e
|
|
77
|
+
* l'alternativa mobile-friendly per `decimal` possono coincidere entrambi su `'.'` — v.
|
|
78
|
+
* `_tokenize` per come risolve l'ambiguità usando questo stato invece di ri-cercare "il primo
|
|
79
|
+
* punto" nella stringa (che potrebbe essere un separatore delle migliaia già inserito da noi).
|
|
80
|
+
*/
|
|
81
|
+
this._hasDecimalState = false;
|
|
82
|
+
this._applyAlignment();
|
|
83
|
+
// Senza un hint esplicito, mobile/tablet mostrano la tastiera alfanumerica completa per un
|
|
84
|
+
// <input> senza type/inputmode — sbagliato per un campo numerico. Impostato solo se il
|
|
85
|
+
// consumer non ha già messo il proprio `type`/`inputmode` nel template (es. `type="tel"`,
|
|
86
|
+
// suggerito anche dal README dell'originale `eqp-numeric` per Ionic).
|
|
87
|
+
const el = this._elementRef.nativeElement;
|
|
88
|
+
if (!el.hasAttribute('inputmode') && !el.hasAttribute('type')) {
|
|
89
|
+
this._renderer.setAttribute(el, 'inputmode', 'decimal');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//#region ControlValueAccessor
|
|
93
|
+
writeValue(value) {
|
|
94
|
+
this._value = this._roundToPrecision(this._clampRange(value == null ? null : Number(value)));
|
|
95
|
+
this._hasDecimalState = false;
|
|
96
|
+
this._renderFinal(this._value);
|
|
97
|
+
}
|
|
98
|
+
registerOnChange(fn) {
|
|
99
|
+
this._onChange = fn;
|
|
100
|
+
}
|
|
101
|
+
registerOnTouched(fn) {
|
|
102
|
+
this._onTouched = fn;
|
|
103
|
+
}
|
|
104
|
+
setDisabledState(isDisabled) {
|
|
105
|
+
this._disabled = isDisabled;
|
|
106
|
+
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
onInput(event) {
|
|
110
|
+
if (this._disabled) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const rawValue = event.target.value;
|
|
114
|
+
const caretFromEnd = this._captureCaretFromEnd();
|
|
115
|
+
if (this._options.inputMode === TmwNumericInputMode.FINANCIAL) {
|
|
116
|
+
this._value = this._clampRange(this._parseFinancial(rawValue));
|
|
117
|
+
this._onChange(this._value);
|
|
118
|
+
this._renderFinal(this._value);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const { decimal } = this._options;
|
|
122
|
+
const insertedText = event.data;
|
|
123
|
+
// Il carattere/testo appena digitato o incollato (`event.data`) dice INEQUIVOCABILMENTE se
|
|
124
|
+
// l'utente sta esprimendo l'intento "questo è il separatore decimale" — a differenza di
|
|
125
|
+
// ri-cercare "il primo punto" nell'intera stringa già formattata, che in formato euro
|
|
126
|
+
// (`thousands: '.'`) confonderebbe un punto delle migliaia già inserito da noi con il
|
|
127
|
+
// punto decimale appena digitato (v. commento su `_hasDecimalState`).
|
|
128
|
+
if (insertedText && (insertedText.includes(decimal) || insertedText.includes('.'))) {
|
|
129
|
+
this._hasDecimalState = true;
|
|
130
|
+
}
|
|
131
|
+
if (rawValue.length === 0) {
|
|
132
|
+
this._hasDecimalState = false;
|
|
133
|
+
}
|
|
134
|
+
const { integerDigits, decimalDigits, hasDecimalSeparator, isNegative } = this._tokenize(rawValue, this._hasDecimalState);
|
|
135
|
+
this._hasDecimalState = hasDecimalSeparator;
|
|
136
|
+
this._value = this._clampRange(this._toNumber(integerDigits, decimalDigits, isNegative));
|
|
137
|
+
this._onChange(this._value);
|
|
138
|
+
this._renderLive(integerDigits, decimalDigits, hasDecimalSeparator, isNegative);
|
|
139
|
+
}
|
|
140
|
+
this._restoreCaretFromEnd(caretFromEnd);
|
|
141
|
+
}
|
|
142
|
+
onBlur() {
|
|
143
|
+
this._onTouched();
|
|
144
|
+
this._value = this._roundToPrecision(this._value);
|
|
145
|
+
this._onChange(this._value);
|
|
146
|
+
this._renderFinal(this._value);
|
|
147
|
+
}
|
|
148
|
+
_applyAlignment() {
|
|
149
|
+
this._renderer.setStyle(this._elementRef.nativeElement, 'text-align', this._options.align);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Modalità FINANCIAL: TUTTE le cifre della stringa (incluse quelle già formattate dal render
|
|
153
|
+
* precedente — prefisso/migliaia/decimali) vengono lette come un unico intero in centesimi
|
|
154
|
+
* (o nella più piccola unità di `precision`) e ridivise per `10^precision` — è quello che dà
|
|
155
|
+
* l'effetto "shift da destra" tipico delle casse, insensibile a qualunque formattazione
|
|
156
|
+
* precedente (a differenza di NATURAL non serve un formato "live" separato).
|
|
157
|
+
*/
|
|
158
|
+
_parseFinancial(rawValue) {
|
|
159
|
+
const { precision, allowNegative, nullable } = this._options;
|
|
160
|
+
const isNegative = allowNegative && this._hasTypedMinus(rawValue);
|
|
161
|
+
const digitsOnly = rawValue.replace(/[^0-9]/g, '');
|
|
162
|
+
if (digitsOnly.length === 0) {
|
|
163
|
+
return nullable ? null : 0;
|
|
164
|
+
}
|
|
165
|
+
const units = parseInt(digitsOnly, 10);
|
|
166
|
+
const value = units / Math.pow(10, precision);
|
|
167
|
+
return isNegative ? -value : value;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* `rawValue.startsWith('-')` non basta: il segno digitato dall'utente finisce SEMPRE dopo un
|
|
171
|
+
* eventuale prefisso (es. `"$ -5"`), mai a inizio stringa assoluto — si cerca quindi il
|
|
172
|
+
* carattere ovunque nella stringa.
|
|
173
|
+
*/
|
|
174
|
+
_hasTypedMinus(rawValue) {
|
|
175
|
+
return rawValue.includes('-');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Estrae dalla stringa grezza le sole cifre intere/decimali digitate. Usato SOLO in modalità
|
|
179
|
+
* NATURAL. `hasDecimalHint` (stato persistente, v. `_hasDecimalState`) dice se un separatore
|
|
180
|
+
* decimale è già stato digitato in un keystroke precedente:
|
|
181
|
+
*
|
|
182
|
+
* - `false`: nessun separatore ancora — si scartano TUTTI i caratteri non-cifra (incluso un
|
|
183
|
+
* eventuale punto delle migliaia già presente dal render precedente), tutto ciò che resta è
|
|
184
|
+
* parte intera.
|
|
185
|
+
* - `true`: si cerca l'ULTIMA occorrenza di `decimal` o `'.'` nella stringa — mai la prima. Le
|
|
186
|
+
* migliaia (anche quando coincidono con `'.'`, formato euro) possono comparire SOLO PRIMA del
|
|
187
|
+
* separatore decimale reale (la formattazione non raggruppa mai le cifre decimali), quindi
|
|
188
|
+
* l'ultima occorrenza è sempre quella vera, senza ambiguità. Se non se ne trova più nessuna
|
|
189
|
+
* (l'utente ha cancellato il separatore), si ritorna a "nessun decimale".
|
|
190
|
+
*/
|
|
191
|
+
_tokenize(rawValue, hasDecimalHint) {
|
|
192
|
+
const { decimal, allowNegative, precision } = this._options;
|
|
193
|
+
const isNegative = allowNegative && this._hasTypedMinus(rawValue);
|
|
194
|
+
if (!hasDecimalHint) {
|
|
195
|
+
return { integerDigits: rawValue.replace(/[^0-9]/g, ''), decimalDigits: '', hasDecimalSeparator: false, isNegative };
|
|
196
|
+
}
|
|
197
|
+
let splitIndex = -1;
|
|
198
|
+
for (let i = rawValue.length - 1; i >= 0; i--) {
|
|
199
|
+
if (rawValue[i] === decimal || rawValue[i] === '.') {
|
|
200
|
+
splitIndex = i;
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (splitIndex === -1) {
|
|
205
|
+
return { integerDigits: rawValue.replace(/[^0-9]/g, ''), decimalDigits: '', hasDecimalSeparator: false, isNegative };
|
|
206
|
+
}
|
|
207
|
+
const integerDigits = rawValue.slice(0, splitIndex).replace(/[^0-9]/g, '');
|
|
208
|
+
const decimalDigits = rawValue.slice(splitIndex + 1).replace(/[^0-9]/g, '').slice(0, precision);
|
|
209
|
+
return { integerDigits, decimalDigits, hasDecimalSeparator: true, isNegative };
|
|
210
|
+
}
|
|
211
|
+
_toNumber(integerDigits, decimalDigits, isNegative) {
|
|
212
|
+
if (integerDigits.length === 0 && decimalDigits.length === 0) {
|
|
213
|
+
return this._options.nullable ? null : 0;
|
|
214
|
+
}
|
|
215
|
+
const value = parseFloat(`${integerDigits || '0'}.${decimalDigits || '0'}`);
|
|
216
|
+
return isNegative ? -value : value;
|
|
217
|
+
}
|
|
218
|
+
/** Applica solo `allowNegative`/`min`/`max` — NON arrotonda a `precision` (v. commento di classe). */
|
|
219
|
+
_clampRange(value) {
|
|
220
|
+
if (value == null) {
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
const { min, max, allowNegative } = this._options;
|
|
224
|
+
let result = value;
|
|
225
|
+
if (!allowNegative && result < 0) {
|
|
226
|
+
result = -result;
|
|
227
|
+
}
|
|
228
|
+
if (min != null && result < min) {
|
|
229
|
+
result = min;
|
|
230
|
+
}
|
|
231
|
+
if (max != null && result > max) {
|
|
232
|
+
result = max;
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
_roundToPrecision(value) {
|
|
237
|
+
if (value == null) {
|
|
238
|
+
return value;
|
|
239
|
+
}
|
|
240
|
+
const factor = Math.pow(10, this._options.precision);
|
|
241
|
+
return Math.round(value * factor) / factor;
|
|
242
|
+
}
|
|
243
|
+
/** Formato "live", usato in NATURAL durante la digitazione: nessun padding/arrotondamento. */
|
|
244
|
+
_renderLive(integerDigits, decimalDigits, hasDecimalSeparator, isNegative) {
|
|
245
|
+
const { prefix, suffix, thousands, decimal, precision } = this._options;
|
|
246
|
+
const groupedInteger = integerDigits.replace(/\B(?=(\d{3})+(?!\d))/g, thousands);
|
|
247
|
+
const decimalSegment = precision > 0 && hasDecimalSeparator ? decimal + decimalDigits : '';
|
|
248
|
+
const formatted = `${isNegative ? '-' : ''}${prefix}${groupedInteger}${decimalSegment}${suffix}`;
|
|
249
|
+
this._renderer.setProperty(this._elementRef.nativeElement, 'value', formatted);
|
|
250
|
+
}
|
|
251
|
+
/** Formato pieno (padding a `precision`, arrotondato) — usato a `blur`/`writeValue`/opzioni cambiate. */
|
|
252
|
+
_renderFinal(value) {
|
|
253
|
+
const formatted = value == null ? '' : this._formatFull(value);
|
|
254
|
+
this._renderer.setProperty(this._elementRef.nativeElement, 'value', formatted);
|
|
255
|
+
}
|
|
256
|
+
_formatFull(value) {
|
|
257
|
+
const { prefix, suffix, thousands, decimal, precision } = this._options;
|
|
258
|
+
const isNegative = value < 0;
|
|
259
|
+
const fixed = Math.abs(value).toFixed(precision);
|
|
260
|
+
const [integerPart, decimalPart] = fixed.split('.');
|
|
261
|
+
const groupedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousands);
|
|
262
|
+
const decimalSegment = precision > 0 ? decimal + decimalPart : '';
|
|
263
|
+
return `${isNegative ? '-' : ''}${prefix}${groupedInteger}${decimalSegment}${suffix}`;
|
|
264
|
+
}
|
|
265
|
+
_captureCaretFromEnd() {
|
|
266
|
+
const el = this._elementRef.nativeElement;
|
|
267
|
+
const caret = el.selectionStart ?? el.value.length;
|
|
268
|
+
return el.value.length - caret;
|
|
269
|
+
}
|
|
270
|
+
_restoreCaretFromEnd(distanceFromEnd) {
|
|
271
|
+
const el = this._elementRef.nativeElement;
|
|
272
|
+
const position = Math.max(0, el.value.length - distanceFromEnd);
|
|
273
|
+
// Il reformat avviene sincronamente nello stesso ciclo dell'evento `input`: il browser
|
|
274
|
+
// applica la posizione del cursore SUBITO dopo, senza bisogno di un setTimeout/microtask.
|
|
275
|
+
el.setSelectionRange(position, position);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
TmwNumericMaskDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TmwNumericMaskDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
|
|
279
|
+
TmwNumericMaskDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.9", type: TmwNumericMaskDirective, isStandalone: true, selector: "[tmwNumericMask]", inputs: { options: "options" }, host: { listeners: { "input": "onInput($event)", "blur": "onBlur()" } }, providers: [
|
|
280
|
+
{
|
|
281
|
+
provide: NG_VALUE_ACCESSOR,
|
|
282
|
+
useExisting: forwardRef(() => TmwNumericMaskDirective),
|
|
283
|
+
multi: true,
|
|
284
|
+
},
|
|
285
|
+
], ngImport: i0 });
|
|
286
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: TmwNumericMaskDirective, decorators: [{
|
|
287
|
+
type: Directive,
|
|
288
|
+
args: [{
|
|
289
|
+
selector: '[tmwNumericMask]',
|
|
290
|
+
standalone: true,
|
|
291
|
+
providers: [
|
|
292
|
+
{
|
|
293
|
+
provide: NG_VALUE_ACCESSOR,
|
|
294
|
+
useExisting: forwardRef(() => TmwNumericMaskDirective),
|
|
295
|
+
multi: true,
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
}]
|
|
299
|
+
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { options: [{
|
|
300
|
+
type: Input,
|
|
301
|
+
args: ['options']
|
|
302
|
+
}], onInput: [{
|
|
303
|
+
type: HostListener,
|
|
304
|
+
args: ['input', ['$event']]
|
|
305
|
+
}], onBlur: [{
|
|
306
|
+
type: HostListener,
|
|
307
|
+
args: ['blur']
|
|
308
|
+
}] } });
|
|
309
|
+
|
|
310
|
+
/*
|
|
311
|
+
* Public API Surface of tmw-numeric
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Generated bundle index. Do not edit.
|
|
316
|
+
*/
|
|
317
|
+
|
|
318
|
+
export { TMW_NUMERIC_DEFAULT_OPTIONS, TmwNumericInputMode, TmwNumericMaskDirective };
|
|
319
|
+
//# sourceMappingURL=tmw-numeric.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmw-numeric.mjs","sources":["../../../projects/tmw-numeric/src/lib/models/tmw-numeric-options.model.ts","../../../projects/tmw-numeric/src/lib/tmw-numeric-mask.directive.ts","../../../projects/tmw-numeric/src/public-api.ts","../../../projects/tmw-numeric/src/tmw-numeric.ts"],"sourcesContent":["/**\n * Modalità di digitazione — nomi/valori/comportamento invariati rispetto a\n * `@eqproject/eqp-numeric` (`EqpNumericInputMode`), per parità di comportamento.\n */\nexport enum TmwNumericInputMode {\n /**\n * I numeri partono dalla precisione più alta (i decimali): digitare uno shifta tutto a\n * sinistra, il carattere decimale digitato viene ignorato. Es. `'12'` → `'0.12'`,\n * `'1234'` → `'12.34'`.\n */\n FINANCIAL = 'FINANCIAL',\n /**\n * I numeri partono a sinistra del separatore decimale, come un campo di testo qualunque.\n * Es. `'1234'` → `'1234'`, `'12.34'` → `'12.34'`.\n */\n NATURAL = 'NATURAL',\n}\n\nexport interface TmwNumericOptions {\n /** Allineamento testo nell'input. Default `'right'`. */\n align?: 'left' | 'right';\n /** Se `true` consente valori negativi. Default `true`. */\n allowNegative?: boolean;\n /** Separatore dei decimali. Default `'.'`. */\n decimal?: string;\n /** Numero di cifre decimali. Default `2`. */\n precision?: number;\n /** Prefisso (es. simbolo valuta). Default `'$ '`. */\n prefix?: string;\n /** Suffisso. Default `''`. */\n suffix?: string;\n /** Separatore delle migliaia. Default `','`. */\n thousands?: string;\n /** Se `true`, un campo vuoto produce `null` invece di `0`. Default `false`. */\n nullable?: boolean;\n /** Valore minimo consentito. Default `undefined` (nessun limite). */\n min?: number;\n /** Valore massimo consentito. Default `undefined` (nessun limite). */\n max?: number;\n /** Modalità di digitazione. Default `NATURAL`. */\n inputMode?: TmwNumericInputMode;\n}\n\n/**\n * Default identici a `@eqproject/eqp-numeric` — nessun consumer TMED chiamava\n * `EqpNumericModule.forRoot(...)` né passava `[options]`, quindi questi default SONO il\n * comportamento reale in produzione (v. AI_BUILD_PLAN.md per i 5 punti d'uso reali).\n */\nexport const TMW_NUMERIC_DEFAULT_OPTIONS: Required<TmwNumericOptions> = {\n align: 'right',\n allowNegative: true,\n decimal: '.',\n precision: 2,\n prefix: '$ ',\n suffix: '',\n thousands: ',',\n nullable: false,\n min: undefined as unknown as number,\n max: undefined as unknown as number,\n inputMode: TmwNumericInputMode.NATURAL,\n};\n","import { Directive, ElementRef, HostListener, Input, Renderer2, forwardRef } from '@angular/core';\nimport { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';\nimport { TMW_NUMERIC_DEFAULT_OPTIONS, TmwNumericInputMode, TmwNumericOptions } from './models/tmw-numeric-options.model';\n\n/**\n * Sostituto di `[eqpNumericMask]` (`@eqproject/eqp-numeric`) — stesso `[options]`, stessi nomi\n * di opzione, stessi default (v. `TMW_NUMERIC_DEFAULT_OPTIONS`). Nessun `forRoot()`: nessun\n * consumer reale in TMED lo usava, i default del pacchetto originale erano già il comportamento\n * effettivo in produzione.\n *\n * Formatta un `<input>` come numero mascherato (valuta/decimale) mantenendo il model\n * (`ngModel`/`formControlName`) come `number | null` — l'utente vede `'$ 1,234.56'`, il form\n * riceve `1234.56`.\n *\n * In modalità NATURAL la formattazione \"live\" (durante la digitazione) NON forza il padding a\n * piena precisione: farlo ad ogni tasto arrotonderebbe via le cifre appena digitate (es. digitare\n * \"1\" su un campo vuoto renderizzerebbe subito \"0.00\", perdendo la cifra) — il padding/\n * arrotondamento pieno avviene solo al `blur`/`writeValue`. In modalità FINANCIAL questo problema\n * non esiste (il valore si ricalcola sempre da zero dalle sole cifre digitate, insensibile alla\n * formattazione precedente), quindi lì si usa direttamente il formato pieno ad ogni tasto.\n */\n@Directive({\n selector: '[tmwNumericMask]',\n standalone: true,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => TmwNumericMaskDirective),\n multi: true,\n },\n ],\n})\nexport class TmwNumericMaskDirective implements ControlValueAccessor {\n private _options: Required<TmwNumericOptions> = { ...TMW_NUMERIC_DEFAULT_OPTIONS };\n\n @Input('options')\n set options(value: TmwNumericOptions | undefined) {\n this._options = { ...TMW_NUMERIC_DEFAULT_OPTIONS, ...(value ?? {}) };\n this._applyAlignment();\n this._renderFinal(this._value);\n }\n\n private _value: number | null = null;\n private _onChange: (value: number | null) => void = () => {};\n private _onTouched: () => void = () => {};\n private _disabled = false;\n /**\n * Stato PERSISTENTE (non ri-derivato ogni volta dalla stringa già formattata): true dal\n * momento in cui l'utente ha digitato/incollato un carattere decimale, false quando il campo\n * è vuoto o quel carattere viene cancellato. Necessario perché in formato euro `thousands` e\n * l'alternativa mobile-friendly per `decimal` possono coincidere entrambi su `'.'` — v.\n * `_tokenize` per come risolve l'ambiguità usando questo stato invece di ri-cercare \"il primo\n * punto\" nella stringa (che potrebbe essere un separatore delle migliaia già inserito da noi).\n */\n private _hasDecimalState = false;\n\n constructor(private _elementRef: ElementRef<HTMLInputElement>, private _renderer: Renderer2) {\n this._applyAlignment();\n // Senza un hint esplicito, mobile/tablet mostrano la tastiera alfanumerica completa per un\n // <input> senza type/inputmode — sbagliato per un campo numerico. Impostato solo se il\n // consumer non ha già messo il proprio `type`/`inputmode` nel template (es. `type=\"tel\"`,\n // suggerito anche dal README dell'originale `eqp-numeric` per Ionic).\n const el = this._elementRef.nativeElement;\n if (!el.hasAttribute('inputmode') && !el.hasAttribute('type')) {\n this._renderer.setAttribute(el, 'inputmode', 'decimal');\n }\n }\n\n //#region ControlValueAccessor\n\n writeValue(value: number | null): void {\n this._value = this._roundToPrecision(this._clampRange(value == null ? null : Number(value)));\n this._hasDecimalState = false;\n this._renderFinal(this._value);\n }\n\n registerOnChange(fn: (value: number | null) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._disabled = isDisabled;\n this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);\n }\n\n //#endregion\n\n @HostListener('input', ['$event'])\n onInput(event: InputEvent): void {\n if (this._disabled) {\n return;\n }\n const rawValue = (event.target as HTMLInputElement).value;\n const caretFromEnd = this._captureCaretFromEnd();\n\n if (this._options.inputMode === TmwNumericInputMode.FINANCIAL) {\n this._value = this._clampRange(this._parseFinancial(rawValue));\n this._onChange(this._value);\n this._renderFinal(this._value);\n } else {\n const { decimal } = this._options;\n const insertedText = event.data;\n // Il carattere/testo appena digitato o incollato (`event.data`) dice INEQUIVOCABILMENTE se\n // l'utente sta esprimendo l'intento \"questo è il separatore decimale\" — a differenza di\n // ri-cercare \"il primo punto\" nell'intera stringa già formattata, che in formato euro\n // (`thousands: '.'`) confonderebbe un punto delle migliaia già inserito da noi con il\n // punto decimale appena digitato (v. commento su `_hasDecimalState`).\n if (insertedText && (insertedText.includes(decimal) || insertedText.includes('.'))) {\n this._hasDecimalState = true;\n }\n if (rawValue.length === 0) {\n this._hasDecimalState = false;\n }\n const { integerDigits, decimalDigits, hasDecimalSeparator, isNegative } = this._tokenize(rawValue, this._hasDecimalState);\n this._hasDecimalState = hasDecimalSeparator;\n this._value = this._clampRange(this._toNumber(integerDigits, decimalDigits, isNegative));\n this._onChange(this._value);\n this._renderLive(integerDigits, decimalDigits, hasDecimalSeparator, isNegative);\n }\n this._restoreCaretFromEnd(caretFromEnd);\n }\n\n @HostListener('blur')\n onBlur(): void {\n this._onTouched();\n this._value = this._roundToPrecision(this._value);\n this._onChange(this._value);\n this._renderFinal(this._value);\n }\n\n private _applyAlignment(): void {\n this._renderer.setStyle(this._elementRef.nativeElement, 'text-align', this._options.align);\n }\n\n /**\n * Modalità FINANCIAL: TUTTE le cifre della stringa (incluse quelle già formattate dal render\n * precedente — prefisso/migliaia/decimali) vengono lette come un unico intero in centesimi\n * (o nella più piccola unità di `precision`) e ridivise per `10^precision` — è quello che dà\n * l'effetto \"shift da destra\" tipico delle casse, insensibile a qualunque formattazione\n * precedente (a differenza di NATURAL non serve un formato \"live\" separato).\n */\n private _parseFinancial(rawValue: string): number | null {\n const { precision, allowNegative, nullable } = this._options;\n const isNegative = allowNegative && this._hasTypedMinus(rawValue);\n const digitsOnly = rawValue.replace(/[^0-9]/g, '');\n if (digitsOnly.length === 0) {\n return nullable ? null : 0;\n }\n const units = parseInt(digitsOnly, 10);\n const value = units / Math.pow(10, precision);\n return isNegative ? -value : value;\n }\n\n /**\n * `rawValue.startsWith('-')` non basta: il segno digitato dall'utente finisce SEMPRE dopo un\n * eventuale prefisso (es. `\"$ -5\"`), mai a inizio stringa assoluto — si cerca quindi il\n * carattere ovunque nella stringa.\n */\n private _hasTypedMinus(rawValue: string): boolean {\n return rawValue.includes('-');\n }\n\n /**\n * Estrae dalla stringa grezza le sole cifre intere/decimali digitate. Usato SOLO in modalità\n * NATURAL. `hasDecimalHint` (stato persistente, v. `_hasDecimalState`) dice se un separatore\n * decimale è già stato digitato in un keystroke precedente:\n *\n * - `false`: nessun separatore ancora — si scartano TUTTI i caratteri non-cifra (incluso un\n * eventuale punto delle migliaia già presente dal render precedente), tutto ciò che resta è\n * parte intera.\n * - `true`: si cerca l'ULTIMA occorrenza di `decimal` o `'.'` nella stringa — mai la prima. Le\n * migliaia (anche quando coincidono con `'.'`, formato euro) possono comparire SOLO PRIMA del\n * separatore decimale reale (la formattazione non raggruppa mai le cifre decimali), quindi\n * l'ultima occorrenza è sempre quella vera, senza ambiguità. Se non se ne trova più nessuna\n * (l'utente ha cancellato il separatore), si ritorna a \"nessun decimale\".\n */\n private _tokenize(rawValue: string, hasDecimalHint: boolean): {\n integerDigits: string;\n decimalDigits: string;\n hasDecimalSeparator: boolean;\n isNegative: boolean;\n } {\n const { decimal, allowNegative, precision } = this._options;\n const isNegative = allowNegative && this._hasTypedMinus(rawValue);\n\n if (!hasDecimalHint) {\n return { integerDigits: rawValue.replace(/[^0-9]/g, ''), decimalDigits: '', hasDecimalSeparator: false, isNegative };\n }\n\n let splitIndex = -1;\n for (let i = rawValue.length - 1; i >= 0; i--) {\n if (rawValue[i] === decimal || rawValue[i] === '.') {\n splitIndex = i;\n break;\n }\n }\n if (splitIndex === -1) {\n return { integerDigits: rawValue.replace(/[^0-9]/g, ''), decimalDigits: '', hasDecimalSeparator: false, isNegative };\n }\n const integerDigits = rawValue.slice(0, splitIndex).replace(/[^0-9]/g, '');\n const decimalDigits = rawValue.slice(splitIndex + 1).replace(/[^0-9]/g, '').slice(0, precision);\n return { integerDigits, decimalDigits, hasDecimalSeparator: true, isNegative };\n }\n\n private _toNumber(integerDigits: string, decimalDigits: string, isNegative: boolean): number | null {\n if (integerDigits.length === 0 && decimalDigits.length === 0) {\n return this._options.nullable ? null : 0;\n }\n const value = parseFloat(`${integerDigits || '0'}.${decimalDigits || '0'}`);\n return isNegative ? -value : value;\n }\n\n /** Applica solo `allowNegative`/`min`/`max` — NON arrotonda a `precision` (v. commento di classe). */\n private _clampRange(value: number | null): number | null {\n if (value == null) {\n return value;\n }\n const { min, max, allowNegative } = this._options;\n let result = value;\n if (!allowNegative && result < 0) {\n result = -result;\n }\n if (min != null && result < min) {\n result = min;\n }\n if (max != null && result > max) {\n result = max;\n }\n return result;\n }\n\n private _roundToPrecision(value: number | null): number | null {\n if (value == null) {\n return value;\n }\n const factor = Math.pow(10, this._options.precision);\n return Math.round(value * factor) / factor;\n }\n\n /** Formato \"live\", usato in NATURAL durante la digitazione: nessun padding/arrotondamento. */\n private _renderLive(integerDigits: string, decimalDigits: string, hasDecimalSeparator: boolean, isNegative: boolean): void {\n const { prefix, suffix, thousands, decimal, precision } = this._options;\n const groupedInteger = integerDigits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, thousands);\n const decimalSegment = precision > 0 && hasDecimalSeparator ? decimal + decimalDigits : '';\n const formatted = `${isNegative ? '-' : ''}${prefix}${groupedInteger}${decimalSegment}${suffix}`;\n this._renderer.setProperty(this._elementRef.nativeElement, 'value', formatted);\n }\n\n /** Formato pieno (padding a `precision`, arrotondato) — usato a `blur`/`writeValue`/opzioni cambiate. */\n private _renderFinal(value: number | null): void {\n const formatted = value == null ? '' : this._formatFull(value);\n this._renderer.setProperty(this._elementRef.nativeElement, 'value', formatted);\n }\n\n private _formatFull(value: number): string {\n const { prefix, suffix, thousands, decimal, precision } = this._options;\n const isNegative = value < 0;\n const fixed = Math.abs(value).toFixed(precision);\n const [integerPart, decimalPart] = fixed.split('.');\n const groupedInteger = integerPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, thousands);\n const decimalSegment = precision > 0 ? decimal + decimalPart : '';\n return `${isNegative ? '-' : ''}${prefix}${groupedInteger}${decimalSegment}${suffix}`;\n }\n\n private _captureCaretFromEnd(): number {\n const el = this._elementRef.nativeElement;\n const caret = el.selectionStart ?? el.value.length;\n return el.value.length - caret;\n }\n\n private _restoreCaretFromEnd(distanceFromEnd: number): void {\n const el = this._elementRef.nativeElement;\n const position = Math.max(0, el.value.length - distanceFromEnd);\n // Il reformat avviene sincronamente nello stesso ciclo dell'evento `input`: il browser\n // applica la posizione del cursore SUBITO dopo, senza bisogno di un setTimeout/microtask.\n el.setSelectionRange(position, position);\n }\n}\n","/*\n * Public API Surface of tmw-numeric\n */\n\nexport * from './lib/tmw-numeric-mask.directive';\nexport * from './lib/models/tmw-numeric-options.model';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAAA;;;AAGG;IACS,oBAYX;AAZD,CAAA,UAAY,mBAAmB,EAAA;AAC7B;;;;AAIG;AACH,IAAA,mBAAA,CAAA,WAAA,CAAA,GAAA,WAAuB,CAAA;AACvB;;;AAGG;AACH,IAAA,mBAAA,CAAA,SAAA,CAAA,GAAA,SAAmB,CAAA;AACrB,CAAC,EAZW,mBAAmB,KAAnB,mBAAmB,GAY9B,EAAA,CAAA,CAAA,CAAA;AA2BD;;;;AAIG;AACU,MAAA,2BAA2B,GAAgC;AACtE,IAAA,KAAK,EAAE,OAAO;AACd,IAAA,aAAa,EAAE,IAAI;AACnB,IAAA,OAAO,EAAE,GAAG;AACZ,IAAA,SAAS,EAAE,CAAC;AACZ,IAAA,MAAM,EAAE,IAAI;AACZ,IAAA,MAAM,EAAE,EAAE;AACV,IAAA,SAAS,EAAE,GAAG;AACd,IAAA,QAAQ,EAAE,KAAK;AACf,IAAA,GAAG,EAAE,SAA8B;AACnC,IAAA,GAAG,EAAE,SAA8B;IACnC,SAAS,EAAE,mBAAmB,CAAC,OAAO;;;ACvDxC;;;;;;;;;;;;;;;;AAgBG;MAYU,uBAAuB,CAAA;IAGlC,IACI,OAAO,CAAC,KAAoC,EAAA;AAC9C,QAAA,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,2BAA2B,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,eAAe,EAAE,CAAC;AACvB,QAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;KAChC;IAgBD,WAAoB,CAAA,WAAyC,EAAU,SAAoB,EAAA;QAAvE,IAAW,CAAA,WAAA,GAAX,WAAW,CAA8B;QAAU,IAAS,CAAA,SAAA,GAAT,SAAS,CAAW;AAvBnF,QAAA,IAAA,CAAA,QAAQ,GAAgC,EAAE,GAAG,2BAA2B,EAAE,CAAC;QAS3E,IAAM,CAAA,MAAA,GAAkB,IAAI,CAAC;AAC7B,QAAA,IAAA,CAAA,SAAS,GAAmC,MAAK,GAAG,CAAC;AACrD,QAAA,IAAA,CAAA,UAAU,GAAe,MAAK,GAAG,CAAC;QAClC,IAAS,CAAA,SAAA,GAAG,KAAK,CAAC;AAC1B;;;;;;;AAOG;QACK,IAAgB,CAAA,gBAAA,GAAG,KAAK,CAAC;QAG/B,IAAI,CAAC,eAAe,EAAE,CAAC;;;;;AAKvB,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;AAC1C,QAAA,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE;YAC7D,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;AACzD,SAAA;KACF;;AAID,IAAA,UAAU,CAAC,KAAoB,EAAA;QAC7B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC7F,QAAA,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;AAC9B,QAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;KAChC;AAED,IAAA,gBAAgB,CAAC,EAAkC,EAAA;AACjD,QAAA,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;KACrB;AAED,IAAA,iBAAiB,CAAC,EAAc,EAAA;AAC9B,QAAA,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;KACtB;AAED,IAAA,gBAAgB,CAAC,UAAmB,EAAA;AAClC,QAAA,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC;AAC5B,QAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;KACpF;;AAKD,IAAA,OAAO,CAAC,KAAiB,EAAA;QACvB,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,OAAO;AACR,SAAA;AACD,QAAA,MAAM,QAAQ,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK,CAAC;AAC1D,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAEjD,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,KAAK,mBAAmB,CAAC,SAAS,EAAE;AAC7D,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC/D,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,YAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAChC,SAAA;AAAM,aAAA;AACL,YAAA,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;AAClC,YAAA,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC;;;;;;AAMhC,YAAA,IAAI,YAAY,KAAK,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE;AAClF,gBAAA,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;AAC9B,aAAA;AACD,YAAA,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,gBAAA,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;AAC/B,aAAA;YACD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAC1H,YAAA,IAAI,CAAC,gBAAgB,GAAG,mBAAmB,CAAC;AAC5C,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;AACzF,YAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC;AACjF,SAAA;AACD,QAAA,IAAI,CAAC,oBAAoB,CAAC,YAAY,CAAC,CAAC;KACzC;IAGD,MAAM,GAAA;QACJ,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAClD,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,QAAA,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;KAChC;IAEO,eAAe,GAAA;AACrB,QAAA,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;KAC5F;AAED;;;;;;AAMG;AACK,IAAA,eAAe,CAAC,QAAgB,EAAA;QACtC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC7D,MAAM,UAAU,GAAG,aAAa,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACnD,QAAA,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE;YAC3B,OAAO,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC;AAC5B,SAAA;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AACvC,QAAA,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC9C,OAAO,UAAU,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;KACpC;AAED;;;;AAIG;AACK,IAAA,cAAc,CAAC,QAAgB,EAAA;AACrC,QAAA,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;KAC/B;AAED;;;;;;;;;;;;;AAaG;IACK,SAAS,CAAC,QAAgB,EAAE,cAAuB,EAAA;QAMzD,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5D,MAAM,UAAU,GAAG,aAAa,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAElE,IAAI,CAAC,cAAc,EAAE;YACnB,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACtH,SAAA;AAED,QAAA,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;AACpB,QAAA,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,YAAA,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;gBAClD,UAAU,GAAG,CAAC,CAAC;gBACf,MAAM;AACP,aAAA;AACF,SAAA;AACD,QAAA,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE;YACrB,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;AACtH,SAAA;AACD,QAAA,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3E,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAChG,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,mBAAmB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;KAChF;AAEO,IAAA,SAAS,CAAC,aAAqB,EAAE,aAAqB,EAAE,UAAmB,EAAA;QACjF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE;AAC5D,YAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC;AAC1C,SAAA;AACD,QAAA,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,aAAa,IAAI,GAAG,CAAA,CAAA,EAAI,aAAa,IAAI,GAAG,CAAA,CAAE,CAAC,CAAC;QAC5E,OAAO,UAAU,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;KACpC;;AAGO,IAAA,WAAW,CAAC,KAAoB,EAAA;QACtC,IAAI,KAAK,IAAI,IAAI,EAAE;AACjB,YAAA,OAAO,KAAK,CAAC;AACd,SAAA;QACD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAClD,IAAI,MAAM,GAAG,KAAK,CAAC;AACnB,QAAA,IAAI,CAAC,aAAa,IAAI,MAAM,GAAG,CAAC,EAAE;YAChC,MAAM,GAAG,CAAC,MAAM,CAAC;AAClB,SAAA;AACD,QAAA,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG,GAAG,EAAE;YAC/B,MAAM,GAAG,GAAG,CAAC;AACd,SAAA;AACD,QAAA,IAAI,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG,GAAG,EAAE;YAC/B,MAAM,GAAG,GAAG,CAAC;AACd,SAAA;AACD,QAAA,OAAO,MAAM,CAAC;KACf;AAEO,IAAA,iBAAiB,CAAC,KAAoB,EAAA;QAC5C,IAAI,KAAK,IAAI,IAAI,EAAE;AACjB,YAAA,OAAO,KAAK,CAAC;AACd,SAAA;AACD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC;KAC5C;;AAGO,IAAA,WAAW,CAAC,aAAqB,EAAE,aAAqB,EAAE,mBAA4B,EAAE,UAAmB,EAAA;AACjH,QAAA,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QACxE,MAAM,cAAc,GAAG,aAAa,CAAC,OAAO,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC;AACjF,QAAA,MAAM,cAAc,GAAG,SAAS,GAAG,CAAC,IAAI,mBAAmB,GAAG,OAAO,GAAG,aAAa,GAAG,EAAE,CAAC;QAC3F,MAAM,SAAS,GAAG,CAAG,EAAA,UAAU,GAAG,GAAG,GAAG,EAAE,CAAA,EAAG,MAAM,CAAG,EAAA,cAAc,GAAG,cAAc,CAAA,EAAG,MAAM,CAAA,CAAE,CAAC;AACjG,QAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;KAChF;;AAGO,IAAA,YAAY,CAAC,KAAoB,EAAA;AACvC,QAAA,MAAM,SAAS,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AAC/D,QAAA,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;KAChF;AAEO,IAAA,WAAW,CAAC,KAAa,EAAA;AAC/B,QAAA,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;AACxE,QAAA,MAAM,UAAU,GAAG,KAAK,GAAG,CAAC,CAAC;AAC7B,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AACjD,QAAA,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpD,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,CAAC,uBAAuB,EAAE,SAAS,CAAC,CAAC;AAC/E,QAAA,MAAM,cAAc,GAAG,SAAS,GAAG,CAAC,GAAG,OAAO,GAAG,WAAW,GAAG,EAAE,CAAC;AAClE,QAAA,OAAO,GAAG,UAAU,GAAG,GAAG,GAAG,EAAE,CAAG,EAAA,MAAM,GAAG,cAAc,CAAA,EAAG,cAAc,CAAG,EAAA,MAAM,EAAE,CAAC;KACvF;IAEO,oBAAoB,GAAA;AAC1B,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;QAC1C,MAAM,KAAK,GAAG,EAAE,CAAC,cAAc,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC;AACnD,QAAA,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;KAChC;AAEO,IAAA,oBAAoB,CAAC,eAAuB,EAAA;AAClD,QAAA,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;AAC1C,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;;;AAGhE,QAAA,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;KAC1C;;oHAxPU,uBAAuB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,UAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,SAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAvB,uBAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,uBAAuB,EARvB,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,OAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,EAAA,EAAA,SAAA,EAAA;AACT,QAAA;AACE,YAAA,OAAO,EAAE,iBAAiB;AAC1B,YAAA,WAAW,EAAE,UAAU,CAAC,MAAM,uBAAuB,CAAC;AACtD,YAAA,KAAK,EAAE,IAAI;AACZ,SAAA;AACF,KAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;2FAEU,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAXnC,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,kBAAkB;AAC5B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,SAAS,EAAE;AACT,wBAAA;AACE,4BAAA,OAAO,EAAE,iBAAiB;AAC1B,4BAAA,WAAW,EAAE,UAAU,CAAC,6BAA6B,CAAC;AACtD,4BAAA,KAAK,EAAE,IAAI;AACZ,yBAAA;AACF,qBAAA;AACF,iBAAA,CAAA;yHAKK,OAAO,EAAA,CAAA;sBADV,KAAK;uBAAC,SAAS,CAAA;gBAyDhB,OAAO,EAAA,CAAA;sBADN,YAAY;uBAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAA;gBAoCjC,MAAM,EAAA,CAAA;sBADL,YAAY;uBAAC,MAAM,CAAA;;;AC9HtB;;AAEG;;ACFH;;AAEG;;;;"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modalità di digitazione — nomi/valori/comportamento invariati rispetto a
|
|
3
|
+
* `@eqproject/eqp-numeric` (`EqpNumericInputMode`), per parità di comportamento.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum TmwNumericInputMode {
|
|
6
|
+
/**
|
|
7
|
+
* I numeri partono dalla precisione più alta (i decimali): digitare uno shifta tutto a
|
|
8
|
+
* sinistra, il carattere decimale digitato viene ignorato. Es. `'12'` → `'0.12'`,
|
|
9
|
+
* `'1234'` → `'12.34'`.
|
|
10
|
+
*/
|
|
11
|
+
FINANCIAL = "FINANCIAL",
|
|
12
|
+
/**
|
|
13
|
+
* I numeri partono a sinistra del separatore decimale, come un campo di testo qualunque.
|
|
14
|
+
* Es. `'1234'` → `'1234'`, `'12.34'` → `'12.34'`.
|
|
15
|
+
*/
|
|
16
|
+
NATURAL = "NATURAL"
|
|
17
|
+
}
|
|
18
|
+
export interface TmwNumericOptions {
|
|
19
|
+
/** Allineamento testo nell'input. Default `'right'`. */
|
|
20
|
+
align?: 'left' | 'right';
|
|
21
|
+
/** Se `true` consente valori negativi. Default `true`. */
|
|
22
|
+
allowNegative?: boolean;
|
|
23
|
+
/** Separatore dei decimali. Default `'.'`. */
|
|
24
|
+
decimal?: string;
|
|
25
|
+
/** Numero di cifre decimali. Default `2`. */
|
|
26
|
+
precision?: number;
|
|
27
|
+
/** Prefisso (es. simbolo valuta). Default `'$ '`. */
|
|
28
|
+
prefix?: string;
|
|
29
|
+
/** Suffisso. Default `''`. */
|
|
30
|
+
suffix?: string;
|
|
31
|
+
/** Separatore delle migliaia. Default `','`. */
|
|
32
|
+
thousands?: string;
|
|
33
|
+
/** Se `true`, un campo vuoto produce `null` invece di `0`. Default `false`. */
|
|
34
|
+
nullable?: boolean;
|
|
35
|
+
/** Valore minimo consentito. Default `undefined` (nessun limite). */
|
|
36
|
+
min?: number;
|
|
37
|
+
/** Valore massimo consentito. Default `undefined` (nessun limite). */
|
|
38
|
+
max?: number;
|
|
39
|
+
/** Modalità di digitazione. Default `NATURAL`. */
|
|
40
|
+
inputMode?: TmwNumericInputMode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Default identici a `@eqproject/eqp-numeric` — nessun consumer TMED chiamava
|
|
44
|
+
* `EqpNumericModule.forRoot(...)` né passava `[options]`, quindi questi default SONO il
|
|
45
|
+
* comportamento reale in produzione (v. AI_BUILD_PLAN.md per i 5 punti d'uso reali).
|
|
46
|
+
*/
|
|
47
|
+
export declare const TMW_NUMERIC_DEFAULT_OPTIONS: Required<TmwNumericOptions>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ElementRef, Renderer2 } from '@angular/core';
|
|
2
|
+
import { ControlValueAccessor } from '@angular/forms';
|
|
3
|
+
import { TmwNumericOptions } from './models/tmw-numeric-options.model';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Sostituto di `[eqpNumericMask]` (`@eqproject/eqp-numeric`) — stesso `[options]`, stessi nomi
|
|
7
|
+
* di opzione, stessi default (v. `TMW_NUMERIC_DEFAULT_OPTIONS`). Nessun `forRoot()`: nessun
|
|
8
|
+
* consumer reale in TMED lo usava, i default del pacchetto originale erano già il comportamento
|
|
9
|
+
* effettivo in produzione.
|
|
10
|
+
*
|
|
11
|
+
* Formatta un `<input>` come numero mascherato (valuta/decimale) mantenendo il model
|
|
12
|
+
* (`ngModel`/`formControlName`) come `number | null` — l'utente vede `'$ 1,234.56'`, il form
|
|
13
|
+
* riceve `1234.56`.
|
|
14
|
+
*
|
|
15
|
+
* In modalità NATURAL la formattazione "live" (durante la digitazione) NON forza il padding a
|
|
16
|
+
* piena precisione: farlo ad ogni tasto arrotonderebbe via le cifre appena digitate (es. digitare
|
|
17
|
+
* "1" su un campo vuoto renderizzerebbe subito "0.00", perdendo la cifra) — il padding/
|
|
18
|
+
* arrotondamento pieno avviene solo al `blur`/`writeValue`. In modalità FINANCIAL questo problema
|
|
19
|
+
* non esiste (il valore si ricalcola sempre da zero dalle sole cifre digitate, insensibile alla
|
|
20
|
+
* formattazione precedente), quindi lì si usa direttamente il formato pieno ad ogni tasto.
|
|
21
|
+
*/
|
|
22
|
+
export declare class TmwNumericMaskDirective implements ControlValueAccessor {
|
|
23
|
+
private _elementRef;
|
|
24
|
+
private _renderer;
|
|
25
|
+
private _options;
|
|
26
|
+
set options(value: TmwNumericOptions | undefined);
|
|
27
|
+
private _value;
|
|
28
|
+
private _onChange;
|
|
29
|
+
private _onTouched;
|
|
30
|
+
private _disabled;
|
|
31
|
+
/**
|
|
32
|
+
* Stato PERSISTENTE (non ri-derivato ogni volta dalla stringa già formattata): true dal
|
|
33
|
+
* momento in cui l'utente ha digitato/incollato un carattere decimale, false quando il campo
|
|
34
|
+
* è vuoto o quel carattere viene cancellato. Necessario perché in formato euro `thousands` e
|
|
35
|
+
* l'alternativa mobile-friendly per `decimal` possono coincidere entrambi su `'.'` — v.
|
|
36
|
+
* `_tokenize` per come risolve l'ambiguità usando questo stato invece di ri-cercare "il primo
|
|
37
|
+
* punto" nella stringa (che potrebbe essere un separatore delle migliaia già inserito da noi).
|
|
38
|
+
*/
|
|
39
|
+
private _hasDecimalState;
|
|
40
|
+
constructor(_elementRef: ElementRef<HTMLInputElement>, _renderer: Renderer2);
|
|
41
|
+
writeValue(value: number | null): void;
|
|
42
|
+
registerOnChange(fn: (value: number | null) => void): void;
|
|
43
|
+
registerOnTouched(fn: () => void): void;
|
|
44
|
+
setDisabledState(isDisabled: boolean): void;
|
|
45
|
+
onInput(event: InputEvent): void;
|
|
46
|
+
onBlur(): void;
|
|
47
|
+
private _applyAlignment;
|
|
48
|
+
/**
|
|
49
|
+
* Modalità FINANCIAL: TUTTE le cifre della stringa (incluse quelle già formattate dal render
|
|
50
|
+
* precedente — prefisso/migliaia/decimali) vengono lette come un unico intero in centesimi
|
|
51
|
+
* (o nella più piccola unità di `precision`) e ridivise per `10^precision` — è quello che dà
|
|
52
|
+
* l'effetto "shift da destra" tipico delle casse, insensibile a qualunque formattazione
|
|
53
|
+
* precedente (a differenza di NATURAL non serve un formato "live" separato).
|
|
54
|
+
*/
|
|
55
|
+
private _parseFinancial;
|
|
56
|
+
/**
|
|
57
|
+
* `rawValue.startsWith('-')` non basta: il segno digitato dall'utente finisce SEMPRE dopo un
|
|
58
|
+
* eventuale prefisso (es. `"$ -5"`), mai a inizio stringa assoluto — si cerca quindi il
|
|
59
|
+
* carattere ovunque nella stringa.
|
|
60
|
+
*/
|
|
61
|
+
private _hasTypedMinus;
|
|
62
|
+
/**
|
|
63
|
+
* Estrae dalla stringa grezza le sole cifre intere/decimali digitate. Usato SOLO in modalità
|
|
64
|
+
* NATURAL. `hasDecimalHint` (stato persistente, v. `_hasDecimalState`) dice se un separatore
|
|
65
|
+
* decimale è già stato digitato in un keystroke precedente:
|
|
66
|
+
*
|
|
67
|
+
* - `false`: nessun separatore ancora — si scartano TUTTI i caratteri non-cifra (incluso un
|
|
68
|
+
* eventuale punto delle migliaia già presente dal render precedente), tutto ciò che resta è
|
|
69
|
+
* parte intera.
|
|
70
|
+
* - `true`: si cerca l'ULTIMA occorrenza di `decimal` o `'.'` nella stringa — mai la prima. Le
|
|
71
|
+
* migliaia (anche quando coincidono con `'.'`, formato euro) possono comparire SOLO PRIMA del
|
|
72
|
+
* separatore decimale reale (la formattazione non raggruppa mai le cifre decimali), quindi
|
|
73
|
+
* l'ultima occorrenza è sempre quella vera, senza ambiguità. Se non se ne trova più nessuna
|
|
74
|
+
* (l'utente ha cancellato il separatore), si ritorna a "nessun decimale".
|
|
75
|
+
*/
|
|
76
|
+
private _tokenize;
|
|
77
|
+
private _toNumber;
|
|
78
|
+
/** Applica solo `allowNegative`/`min`/`max` — NON arrotonda a `precision` (v. commento di classe). */
|
|
79
|
+
private _clampRange;
|
|
80
|
+
private _roundToPrecision;
|
|
81
|
+
/** Formato "live", usato in NATURAL durante la digitazione: nessun padding/arrotondamento. */
|
|
82
|
+
private _renderLive;
|
|
83
|
+
/** Formato pieno (padding a `precision`, arrotondato) — usato a `blur`/`writeValue`/opzioni cambiate. */
|
|
84
|
+
private _renderFinal;
|
|
85
|
+
private _formatFull;
|
|
86
|
+
private _captureCaretFromEnd;
|
|
87
|
+
private _restoreCaretFromEnd;
|
|
88
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TmwNumericMaskDirective, never>;
|
|
89
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<TmwNumericMaskDirective, "[tmwNumericMask]", never, { "options": "options"; }, {}, never, never, true, never>;
|
|
90
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tmw-numeric",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Masked numeric/currency input directive for Angular — no Material, no CDK, standalone, form-ready",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "EqProject"
|
|
7
|
+
},
|
|
8
|
+
"peerDependencies": {
|
|
9
|
+
"@angular/common": "^15.2.0",
|
|
10
|
+
"@angular/core": "^15.2.0",
|
|
11
|
+
"@angular/forms": "^15.2.0"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"tslib": "^2.3.0"
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"module": "fesm2015/tmw-numeric.mjs",
|
|
18
|
+
"es2020": "fesm2020/tmw-numeric.mjs",
|
|
19
|
+
"esm2020": "esm2020/tmw-numeric.mjs",
|
|
20
|
+
"fesm2020": "fesm2020/tmw-numeric.mjs",
|
|
21
|
+
"fesm2015": "fesm2015/tmw-numeric.mjs",
|
|
22
|
+
"typings": "index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": {
|
|
25
|
+
"default": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./index.d.ts",
|
|
29
|
+
"esm2020": "./esm2020/tmw-numeric.mjs",
|
|
30
|
+
"es2020": "./fesm2020/tmw-numeric.mjs",
|
|
31
|
+
"es2015": "./fesm2015/tmw-numeric.mjs",
|
|
32
|
+
"node": "./fesm2015/tmw-numeric.mjs",
|
|
33
|
+
"default": "./fesm2020/tmw-numeric.mjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/public-api.d.ts
ADDED