@alfalab/core-components-number-input 1.3.0 → 2.0.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.
Files changed (110) hide show
  1. package/Component.desktop.d.ts +1 -1
  2. package/Component.desktop.js +9 -1
  3. package/Component.mobile.d.ts +1 -1
  4. package/Component.mobile.js +9 -1
  5. package/Component.responsive.d.ts +1 -1
  6. package/Component.responsive.js +14 -1
  7. package/components/number-input/Component.d.ts +21 -46
  8. package/components/number-input/Component.js +97 -84
  9. package/components/number-input/default.css +24 -0
  10. package/components/number-input/index.css +23 -0
  11. package/components/number-input/index.js +8 -0
  12. package/components/number-input/inverted.css +24 -0
  13. package/components/steppers/Component.d.ts +15 -0
  14. package/components/steppers/Component.js +43 -0
  15. package/components/steppers/default.css +20 -0
  16. package/components/steppers/index.css +30 -0
  17. package/components/steppers/index.d.ts +1 -0
  18. package/components/steppers/index.js +15 -0
  19. package/components/steppers/inverted.css +20 -0
  20. package/cssm/Component.desktop.d.ts +1 -1
  21. package/cssm/Component.desktop.js +15 -1
  22. package/cssm/Component.mobile.d.ts +1 -1
  23. package/cssm/Component.mobile.js +15 -1
  24. package/cssm/Component.responsive.d.ts +1 -1
  25. package/cssm/Component.responsive.js +20 -1
  26. package/cssm/components/number-input/Component.d.ts +21 -46
  27. package/cssm/components/number-input/Component.js +97 -84
  28. package/cssm/components/number-input/default.module.css +23 -0
  29. package/cssm/components/number-input/index.js +14 -0
  30. package/cssm/components/number-input/index.module.css +22 -0
  31. package/cssm/components/number-input/inverted.module.css +23 -0
  32. package/cssm/components/steppers/Component.d.ts +15 -0
  33. package/cssm/components/steppers/Component.js +40 -0
  34. package/cssm/components/steppers/default.module.css +19 -0
  35. package/cssm/components/steppers/index.d.ts +1 -0
  36. package/cssm/components/steppers/index.js +18 -0
  37. package/cssm/components/steppers/index.module.css +29 -0
  38. package/cssm/components/steppers/inverted.module.css +19 -0
  39. package/cssm/desktop/index.js +14 -0
  40. package/cssm/index.js +15 -0
  41. package/cssm/mobile/index.js +14 -0
  42. package/cssm/utils.d.ts +19 -9
  43. package/cssm/utils.js +244 -21
  44. package/desktop/index.js +8 -0
  45. package/esm/Component.desktop.d.ts +1 -1
  46. package/esm/Component.desktop.js +9 -1
  47. package/esm/Component.mobile.d.ts +1 -1
  48. package/esm/Component.mobile.js +9 -1
  49. package/esm/Component.responsive.d.ts +1 -1
  50. package/esm/Component.responsive.js +15 -2
  51. package/esm/components/number-input/Component.d.ts +21 -46
  52. package/esm/components/number-input/Component.js +98 -86
  53. package/esm/components/number-input/default.css +24 -0
  54. package/esm/components/number-input/index.css +23 -0
  55. package/esm/components/number-input/index.js +8 -0
  56. package/esm/components/number-input/inverted.css +24 -0
  57. package/esm/components/steppers/Component.d.ts +15 -0
  58. package/esm/components/steppers/Component.js +34 -0
  59. package/esm/components/steppers/default.css +20 -0
  60. package/esm/components/steppers/index.css +30 -0
  61. package/esm/components/steppers/index.d.ts +1 -0
  62. package/esm/components/steppers/index.js +7 -0
  63. package/esm/components/steppers/inverted.css +20 -0
  64. package/esm/desktop/index.js +8 -0
  65. package/esm/index.js +9 -0
  66. package/esm/mobile/index.js +8 -0
  67. package/esm/utils.d.ts +19 -9
  68. package/esm/utils.js +236 -19
  69. package/index.js +9 -0
  70. package/mobile/index.js +8 -0
  71. package/modern/Component.desktop.d.ts +1 -1
  72. package/modern/Component.desktop.js +9 -1
  73. package/modern/Component.mobile.d.ts +1 -1
  74. package/modern/Component.mobile.js +9 -1
  75. package/modern/Component.responsive.d.ts +1 -1
  76. package/modern/Component.responsive.js +13 -1
  77. package/modern/components/number-input/Component.d.ts +21 -46
  78. package/modern/components/number-input/Component.js +94 -86
  79. package/modern/components/number-input/default.css +24 -0
  80. package/modern/components/number-input/index.css +23 -0
  81. package/modern/components/number-input/index.js +8 -0
  82. package/modern/components/number-input/inverted.css +24 -0
  83. package/modern/components/steppers/Component.d.ts +15 -0
  84. package/modern/components/steppers/Component.js +33 -0
  85. package/modern/components/steppers/default.css +20 -0
  86. package/modern/components/steppers/index.css +30 -0
  87. package/modern/components/steppers/index.d.ts +1 -0
  88. package/modern/components/steppers/index.js +7 -0
  89. package/modern/components/steppers/inverted.css +20 -0
  90. package/modern/desktop/index.js +8 -0
  91. package/modern/index.js +9 -0
  92. package/modern/mobile/index.js +8 -0
  93. package/modern/utils.d.ts +19 -9
  94. package/modern/utils.js +223 -18
  95. package/package.json +9 -2
  96. package/src/Component.desktop.tsx +2 -2
  97. package/src/Component.mobile.tsx +2 -2
  98. package/src/Component.responsive.tsx +16 -2
  99. package/src/components/number-input/Component.tsx +204 -128
  100. package/src/components/number-input/default.module.css +10 -0
  101. package/src/components/number-input/index.module.css +9 -0
  102. package/src/components/number-input/inverted.module.css +10 -0
  103. package/src/components/steppers/Component.tsx +75 -0
  104. package/src/components/steppers/default.module.css +5 -0
  105. package/src/components/steppers/index.module.css +20 -0
  106. package/src/components/steppers/index.ts +1 -0
  107. package/src/components/steppers/inverted.module.css +5 -0
  108. package/src/utils.ts +302 -24
  109. package/utils.d.ts +19 -9
  110. package/utils.js +244 -21
package/src/utils.ts CHANGED
@@ -1,46 +1,324 @@
1
- export const SIGNS = ['-', '+'];
1
+ /* eslint-disable no-param-reassign */
2
+ import {
3
+ MaskitoOptions,
4
+ MaskitoPlugin,
5
+ MaskitoPostprocessor,
6
+ MaskitoPreprocessor,
7
+ maskitoTransform,
8
+ } from '@maskito/core';
9
+
10
+ import { fnUtils } from '@alfalab/core-components-shared';
11
+
12
+ export const MINUS_SIGN = '-';
2
13
  export const SEPARATORS = [',', '.'];
14
+ export const MAX_SAFE_INTEGER = 2 ** 53 - 1;
15
+ export const MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
16
+ export const MAX_DIGITS = 15; // с 16 уже упираемся в MAX_SAFE_INTEGER
17
+
18
+ export function parseNumber(value: string | number | null = '') {
19
+ if (typeof value === 'number') return value;
20
+
21
+ const pseudoSeparatorsRegExp = new RegExp(`[${SEPARATORS.join('')}]`, 'gi');
22
+
23
+ return value
24
+ ? parseFloat(
25
+ value
26
+ .replace(new RegExp(`[^${MINUS_SIGN}${SEPARATORS.join('')}0-9]`, 'gi'), '')
27
+ .replace(pseudoSeparatorsRegExp, '.'),
28
+ )
29
+ : NaN;
30
+ }
31
+
32
+ /**
33
+ * Преобразовать число в строку с заменой экспоненты на десятичную дробь
34
+ */
35
+ export function stringifyNumberWithoutExp(value: number): string {
36
+ const valueString = String(value);
37
+ const [numberPart, expPart] = valueString.split('e-');
38
+
39
+ let valueWithoutExp = valueString;
3
40
 
4
- export function createSeparatorsRegExp() {
5
- return new RegExp(`[${SEPARATORS.map((s) => `\\${s}`).join('')}]`, 'g');
41
+ if (expPart) {
42
+ const [, fractionalPart] = numberPart.split('.');
43
+ const decimalDigits = Number(expPart) + (fractionalPart?.length || 0);
44
+
45
+ valueWithoutExp = value.toFixed(decimalDigits);
46
+ }
47
+
48
+ return valueWithoutExp;
6
49
  }
7
50
 
8
- const getNumberRegExp = (fractionLength?: number): RegExp => {
9
- let reStr = '[0-9]+';
51
+ const getNumberRegExp = (min: number, fractionLength: number): RegExp => {
52
+ let reStr = '[0-9]*';
53
+
54
+ if (min < 0) {
55
+ reStr = `(\\${MINUS_SIGN})?${reStr}`;
56
+ }
10
57
 
11
58
  if (fractionLength !== 0) {
12
59
  reStr = `${reStr}[${SEPARATORS.map((s) => `\\${s}`).join('')}]?[0-9]{0,${
13
- fractionLength || Number.MAX_SAFE_INTEGER
60
+ fractionLength || MAX_DIGITS
14
61
  }}`;
15
62
  }
16
63
 
17
64
  return new RegExp(`^${reStr}$`);
18
65
  };
19
66
 
67
+ export function createMaskOptions({
68
+ separator,
69
+ fractionLength,
70
+ min,
71
+ max,
72
+ }: {
73
+ separator: string;
74
+ fractionLength: number;
75
+ min: number;
76
+ max: number;
77
+ }): MaskitoOptions {
78
+ return {
79
+ mask: getNumberRegExp(min, fractionLength),
80
+ preprocessors: [
81
+ createPseudoSeparatorPreprocessor(separator),
82
+ createNotEmptyIntegerPartPreprocessor({ separator, fractionLength }),
83
+ createZeroFractionLengthPreprocessor(fractionLength, separator),
84
+ createRepeatedSeparatorPreprocessor(separator),
85
+ ],
86
+ postprocessors: [
87
+ createLeadingZeroesValidationPostprocessor(separator),
88
+ createMinMaxPostprocessor({ min, max, separator }),
89
+ ],
90
+ plugins: [createNotEmptyPartsPlugin(separator), createMinMaxPlugin({ min, max })],
91
+ };
92
+ }
93
+
20
94
  /**
21
- * Проверка вводимых значений.
95
+ * Заполняет целочисленную часть при вводе separator.
96
+ * @example Type , => 0,
22
97
  */
23
- export const getAllowedValue = ({
24
- value = '',
25
- fractionLength,
98
+ function createNotEmptyIntegerPartPreprocessor({
26
99
  separator,
27
- allowSigns,
100
+ fractionLength,
28
101
  }: {
29
- value: string;
30
- fractionLength?: number;
31
102
  separator: string;
32
- allowSigns: boolean;
33
- }): string => {
34
- const sign = allowSigns && SIGNS.some((s) => s === value[0]) ? value[0] : '';
35
- const testedValue = sign ? value.slice(1) : value;
103
+ fractionLength: number;
104
+ }): MaskitoPreprocessor {
105
+ const startWithDecimalSepRegExp = new RegExp(`^\\D*\\${separator}`);
106
+
107
+ return ({ elementState, data }) => {
108
+ const { value, selection } = elementState;
109
+ const [from] = selection;
110
+
111
+ if (
112
+ fractionLength <= 0 ||
113
+ value.includes(separator) ||
114
+ !data.match(startWithDecimalSepRegExp)
115
+ ) {
116
+ return { elementState, data };
117
+ }
36
118
 
37
- if (getNumberRegExp(fractionLength).test(testedValue)) {
38
- return value;
119
+ const digitsBeforeCursor = value.slice(0, from).match(/\d+/);
120
+
121
+ return {
122
+ elementState,
123
+ data: digitsBeforeCursor ? data : `0${data}`,
124
+ };
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Не позволяет вводить невалидный разделитель.
130
+ */
131
+ function createPseudoSeparatorPreprocessor(separator: string): MaskitoPreprocessor {
132
+ const pseudoSeparatorsRegExp = new RegExp(`[${SEPARATORS.join('')}]`, 'gi');
133
+
134
+ return ({ elementState, data }) => {
135
+ const { value, selection } = elementState;
136
+
137
+ return {
138
+ elementState: {
139
+ selection,
140
+ value: value.replace(pseudoSeparatorsRegExp, separator),
141
+ },
142
+ data: data.replace(pseudoSeparatorsRegExp, separator),
143
+ };
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Помогает верно обрезать значения при вставке, если fractionLength===0
149
+ * @example paste 123,123 -> 123
150
+ */
151
+ function createZeroFractionLengthPreprocessor(
152
+ fractionLength: number,
153
+ separator: string,
154
+ ): MaskitoPreprocessor {
155
+ if (fractionLength > 0) {
156
+ return (state) => state;
39
157
  }
40
158
 
41
- const [majorPart, minorPart] = testedValue
42
- .split(separator)
43
- .map((v) => v.replace(/[^0-9]/g, ''));
159
+ const decimalPartRegExp = new RegExp(`\\${separator}.*$`, 'g');
44
160
 
45
- return `${sign}${majorPart}${minorPart ? separator + minorPart.slice(0, fractionLength) : ''}`;
46
- };
161
+ return ({ elementState, data }) => {
162
+ const { value, selection } = elementState;
163
+ const [from, to] = selection;
164
+ const newValue = value.replace(decimalPartRegExp, '');
165
+
166
+ return {
167
+ elementState: {
168
+ selection: [Math.min(from, newValue.length), Math.min(to, newValue.length)],
169
+ value: newValue,
170
+ },
171
+ data: data.replace(decimalPartRegExp, ''),
172
+ };
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Запрещает вводить второй сепаратор
178
+ */
179
+ function createRepeatedSeparatorPreprocessor(separator: string): MaskitoPreprocessor {
180
+ return ({ elementState, data }) => {
181
+ const { value, selection } = elementState;
182
+ const [from, to] = selection;
183
+
184
+ return {
185
+ elementState,
186
+ data:
187
+ !value.includes(separator) || value.slice(from, to + 1).includes(separator)
188
+ ? data
189
+ : data.replace(new RegExp(`\\${separator}`), ''),
190
+ };
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Удаляет лишние нули в начале целой части.
196
+ * @example 0,|00005 => Backspace => |5
197
+ * @example -0,|00005 => Backspace => -|5
198
+ * @example "000000" => 0|
199
+ * @example 0| => Type "5" => 5|
200
+ */
201
+ function createLeadingZeroesValidationPostprocessor(separator: string): MaskitoPostprocessor {
202
+ const trimLeadingZeroes = (value: string): string =>
203
+ value
204
+ .replace(new RegExp('^(\\D+)?0+(?=0)'), '$1')
205
+ .replace(new RegExp('^(\\D+)?0+(?=[1-9])'), '$1');
206
+
207
+ const countTrimmedZeroesBefore = (value: string, index: number): number => {
208
+ const valueBefore = value.slice(0, index);
209
+ const followedByZero = value.slice(index).startsWith('0');
210
+
211
+ return (
212
+ valueBefore.length - trimLeadingZeroes(valueBefore).length + (followedByZero ? 1 : 0)
213
+ );
214
+ };
215
+
216
+ return ({ value, selection }) => {
217
+ const [from, to] = selection;
218
+ const hasSeparator = value.includes(separator);
219
+ const [integerPart, decimalPart = ''] = value.split(separator);
220
+ const zeroTrimmedIntegerPart = trimLeadingZeroes(integerPart);
221
+
222
+ if (integerPart === zeroTrimmedIntegerPart) {
223
+ return { value, selection };
224
+ }
225
+
226
+ const newFrom = from - countTrimmedZeroesBefore(value, from);
227
+ const newTo = to - countTrimmedZeroesBefore(value, to);
228
+
229
+ return {
230
+ value: zeroTrimmedIntegerPart + (hasSeparator ? separator : '') + decimalPart,
231
+ selection: [Math.max(newFrom, 0), Math.max(newTo, 0)],
232
+ };
233
+ };
234
+ }
235
+
236
+ /**
237
+ * Валидирует значение с учетом min max значений.
238
+ * Работает совместно с createMinMaxPlugin
239
+ */
240
+ function createMinMaxPostprocessor({
241
+ min,
242
+ max,
243
+ separator,
244
+ }: {
245
+ min: number;
246
+ max: number;
247
+ separator: string;
248
+ }): MaskitoPostprocessor {
249
+ return ({ value, selection }) => {
250
+ const parsedNumber = parseNumber(value);
251
+
252
+ const limitedValue =
253
+ /**
254
+ * Здесь невозможно ограничить нижнюю границу, если пользователь вводит положительное число.
255
+ * То же самое для верхней границы и отрицательного числа.
256
+ * Если min=5, то без этого условия не получится ввести 40, похожая ситуация и с отрицательным max
257
+ */
258
+ parsedNumber > 0 ? Math.min(parsedNumber, max) : Math.max(parsedNumber, min);
259
+
260
+ if (!Number.isNaN(parsedNumber) && limitedValue !== parsedNumber) {
261
+ const newValue = `${limitedValue}`.replace('.', separator);
262
+
263
+ return {
264
+ value: newValue,
265
+ selection: [newValue.length, newValue.length],
266
+ };
267
+ }
268
+
269
+ return {
270
+ value,
271
+ selection,
272
+ };
273
+ };
274
+ }
275
+
276
+ export function createMinMaxPlugin({ min, max }: { min: number; max: number }): MaskitoPlugin {
277
+ return (element, options) => {
278
+ const listener = () => {
279
+ const parsedNumber = parseNumber(element.value);
280
+
281
+ const clampedNumber = fnUtils.clamp(parsedNumber, min, max);
282
+
283
+ if (!Number.isNaN(parsedNumber) && parsedNumber !== clampedNumber) {
284
+ element.value = maskitoTransform(stringifyNumberWithoutExp(clampedNumber), options);
285
+ element.dispatchEvent(new Event('input', { bubbles: true }));
286
+ }
287
+ };
288
+
289
+ const evListenerOptions = { capture: true };
290
+
291
+ element.addEventListener('blur', listener, evListenerOptions);
292
+
293
+ return () => element.removeEventListener('blur', listener, evListenerOptions);
294
+ };
295
+ }
296
+
297
+ export function createNotEmptyPartsPlugin(separator: string): MaskitoPlugin {
298
+ return (element) => {
299
+ const listener = () => {
300
+ const newValue = element.value
301
+ // 0,9000000 -> 0,9
302
+ .replace(new RegExp(`(\\${separator}\\d*?)(0+$)`), '$1')
303
+ // ,2 => 0,2
304
+ .replace(new RegExp(`^(\\D+)?\\${separator}`), `$10${separator}`)
305
+ // 0, -> 0
306
+ .replace(new RegExp(`\\${separator}$`), '')
307
+ // -0 -> 0
308
+ .replace(new RegExp(`^${MINUS_SIGN}0$`), '0')
309
+ // - -> ''
310
+ .replace(new RegExp(`^${MINUS_SIGN}$`), '');
311
+
312
+ if (newValue !== element.value) {
313
+ element.value = newValue;
314
+ element.dispatchEvent(new Event('input', { bubbles: true }));
315
+ }
316
+ };
317
+
318
+ const evListenerOptions = { capture: true };
319
+
320
+ element.addEventListener('blur', listener, evListenerOptions);
321
+
322
+ return () => element.removeEventListener('blur', listener, evListenerOptions);
323
+ };
324
+ }
package/utils.d.ts CHANGED
@@ -1,13 +1,23 @@
1
- declare const SIGNS: string[];
1
+ import { MaskitoOptions, MaskitoPlugin } from '@maskito/core';
2
+ declare const MINUS_SIGN = "-";
2
3
  declare const SEPARATORS: string[];
3
- declare function createSeparatorsRegExp(): RegExp;
4
+ declare const MAX_SAFE_INTEGER: number;
5
+ declare const MIN_SAFE_INTEGER: number;
6
+ declare const MAX_DIGITS = 15;
7
+ declare function parseNumber(value?: string | number | null): number;
4
8
  /**
5
- * Проверка вводимых значений.
9
+ * Преобразовать число в строку с заменой экспоненты на десятичную дробь
6
10
  */
7
- declare const getAllowedValue: ({ value, fractionLength, separator, allowSigns, }: {
8
- value: string;
9
- fractionLength?: number | undefined;
11
+ declare function stringifyNumberWithoutExp(value: number): string;
12
+ declare function createMaskOptions({ separator, fractionLength, min, max, }: {
10
13
  separator: string;
11
- allowSigns: boolean;
12
- }) => string;
13
- export { SIGNS, SEPARATORS, createSeparatorsRegExp, getAllowedValue };
14
+ fractionLength: number;
15
+ min: number;
16
+ max: number;
17
+ }): MaskitoOptions;
18
+ declare function createMinMaxPlugin({ min, max }: {
19
+ min: number;
20
+ max: number;
21
+ }): MaskitoPlugin;
22
+ declare function createNotEmptyPartsPlugin(separator: string): MaskitoPlugin;
23
+ export { MINUS_SIGN, SEPARATORS, MAX_SAFE_INTEGER, MIN_SAFE_INTEGER, MAX_DIGITS, parseNumber, stringifyNumberWithoutExp, createMaskOptions, createMinMaxPlugin, createNotEmptyPartsPlugin };
package/utils.js CHANGED
@@ -2,35 +2,258 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var SIGNS = ['-', '+'];
5
+ var core = require('@maskito/core');
6
+ var coreComponentsShared = require('@alfalab/core-components-shared');
7
+
8
+ /* eslint-disable no-param-reassign */
9
+ var MINUS_SIGN = '-';
6
10
  var SEPARATORS = [',', '.'];
7
- function createSeparatorsRegExp() {
8
- return new RegExp("[".concat(SEPARATORS.map(function (s) { return "\\".concat(s); }).join(''), "]"), 'g');
11
+ var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1;
12
+ var MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER;
13
+ var MAX_DIGITS = 15; // с 16 уже упираемся в MAX_SAFE_INTEGER
14
+ function parseNumber(value) {
15
+ if (value === void 0) { value = ''; }
16
+ if (typeof value === 'number')
17
+ return value;
18
+ var pseudoSeparatorsRegExp = new RegExp("[".concat(SEPARATORS.join(''), "]"), 'gi');
19
+ return value
20
+ ? parseFloat(value
21
+ .replace(new RegExp("[^".concat(MINUS_SIGN).concat(SEPARATORS.join(''), "0-9]"), 'gi'), '')
22
+ .replace(pseudoSeparatorsRegExp, '.'))
23
+ : NaN;
9
24
  }
10
- var getNumberRegExp = function (fractionLength) {
11
- var reStr = '[0-9]+';
25
+ /**
26
+ * Преобразовать число в строку с заменой экспоненты на десятичную дробь
27
+ */
28
+ function stringifyNumberWithoutExp(value) {
29
+ var valueString = String(value);
30
+ var _a = valueString.split('e-'), numberPart = _a[0], expPart = _a[1];
31
+ var valueWithoutExp = valueString;
32
+ if (expPart) {
33
+ var _b = numberPart.split('.'), fractionalPart = _b[1];
34
+ var decimalDigits = Number(expPart) + ((fractionalPart === null || fractionalPart === void 0 ? void 0 : fractionalPart.length) || 0);
35
+ valueWithoutExp = value.toFixed(decimalDigits);
36
+ }
37
+ return valueWithoutExp;
38
+ }
39
+ var getNumberRegExp = function (min, fractionLength) {
40
+ var reStr = '[0-9]*';
41
+ if (min < 0) {
42
+ reStr = "(\\".concat(MINUS_SIGN, ")?").concat(reStr);
43
+ }
12
44
  if (fractionLength !== 0) {
13
- reStr = "".concat(reStr, "[").concat(SEPARATORS.map(function (s) { return "\\".concat(s); }).join(''), "]?[0-9]{0,").concat(fractionLength || Number.MAX_SAFE_INTEGER, "}");
45
+ reStr = "".concat(reStr, "[").concat(SEPARATORS.map(function (s) { return "\\".concat(s); }).join(''), "]?[0-9]{0,").concat(fractionLength || MAX_DIGITS, "}");
14
46
  }
15
47
  return new RegExp("^".concat(reStr, "$"));
16
48
  };
49
+ function createMaskOptions(_a) {
50
+ var separator = _a.separator, fractionLength = _a.fractionLength, min = _a.min, max = _a.max;
51
+ return {
52
+ mask: getNumberRegExp(min, fractionLength),
53
+ preprocessors: [
54
+ createPseudoSeparatorPreprocessor(separator),
55
+ createNotEmptyIntegerPartPreprocessor({ separator: separator, fractionLength: fractionLength }),
56
+ createZeroFractionLengthPreprocessor(fractionLength, separator),
57
+ createRepeatedSeparatorPreprocessor(separator),
58
+ ],
59
+ postprocessors: [
60
+ createLeadingZeroesValidationPostprocessor(separator),
61
+ createMinMaxPostprocessor({ min: min, max: max, separator: separator }),
62
+ ],
63
+ plugins: [createNotEmptyPartsPlugin(separator), createMinMaxPlugin({ min: min, max: max })],
64
+ };
65
+ }
17
66
  /**
18
- * Проверка вводимых значений.
67
+ * Заполняет целочисленную часть при вводе separator.
68
+ * @example Type , => 0,
19
69
  */
20
- var getAllowedValue = function (_a) {
21
- var _b = _a.value, value = _b === void 0 ? '' : _b, fractionLength = _a.fractionLength, separator = _a.separator, allowSigns = _a.allowSigns;
22
- var sign = allowSigns && SIGNS.some(function (s) { return s === value[0]; }) ? value[0] : '';
23
- var testedValue = sign ? value.slice(1) : value;
24
- if (getNumberRegExp(fractionLength).test(testedValue)) {
25
- return value;
70
+ function createNotEmptyIntegerPartPreprocessor(_a) {
71
+ var separator = _a.separator, fractionLength = _a.fractionLength;
72
+ var startWithDecimalSepRegExp = new RegExp("^\\D*\\".concat(separator));
73
+ return function (_a) {
74
+ var elementState = _a.elementState, data = _a.data;
75
+ var value = elementState.value, selection = elementState.selection;
76
+ var from = selection[0];
77
+ if (fractionLength <= 0 ||
78
+ value.includes(separator) ||
79
+ !data.match(startWithDecimalSepRegExp)) {
80
+ return { elementState: elementState, data: data };
81
+ }
82
+ var digitsBeforeCursor = value.slice(0, from).match(/\d+/);
83
+ return {
84
+ elementState: elementState,
85
+ data: digitsBeforeCursor ? data : "0".concat(data),
86
+ };
87
+ };
88
+ }
89
+ /**
90
+ * Не позволяет вводить невалидный разделитель.
91
+ */
92
+ function createPseudoSeparatorPreprocessor(separator) {
93
+ var pseudoSeparatorsRegExp = new RegExp("[".concat(SEPARATORS.join(''), "]"), 'gi');
94
+ return function (_a) {
95
+ var elementState = _a.elementState, data = _a.data;
96
+ var value = elementState.value, selection = elementState.selection;
97
+ return {
98
+ elementState: {
99
+ selection: selection,
100
+ value: value.replace(pseudoSeparatorsRegExp, separator),
101
+ },
102
+ data: data.replace(pseudoSeparatorsRegExp, separator),
103
+ };
104
+ };
105
+ }
106
+ /**
107
+ * Помогает верно обрезать значения при вставке, если fractionLength===0
108
+ * @example paste 123,123 -> 123
109
+ */
110
+ function createZeroFractionLengthPreprocessor(fractionLength, separator) {
111
+ if (fractionLength > 0) {
112
+ return function (state) { return state; };
26
113
  }
27
- var _c = testedValue
28
- .split(separator)
29
- .map(function (v) { return v.replace(/[^0-9]/g, ''); }), majorPart = _c[0], minorPart = _c[1];
30
- return "".concat(sign).concat(majorPart).concat(minorPart ? separator + minorPart.slice(0, fractionLength) : '');
31
- };
114
+ var decimalPartRegExp = new RegExp("\\".concat(separator, ".*$"), 'g');
115
+ return function (_a) {
116
+ var elementState = _a.elementState, data = _a.data;
117
+ var value = elementState.value, selection = elementState.selection;
118
+ var from = selection[0], to = selection[1];
119
+ var newValue = value.replace(decimalPartRegExp, '');
120
+ return {
121
+ elementState: {
122
+ selection: [Math.min(from, newValue.length), Math.min(to, newValue.length)],
123
+ value: newValue,
124
+ },
125
+ data: data.replace(decimalPartRegExp, ''),
126
+ };
127
+ };
128
+ }
129
+ /**
130
+ * Запрещает вводить второй сепаратор
131
+ */
132
+ function createRepeatedSeparatorPreprocessor(separator) {
133
+ return function (_a) {
134
+ var elementState = _a.elementState, data = _a.data;
135
+ var value = elementState.value, selection = elementState.selection;
136
+ var from = selection[0], to = selection[1];
137
+ return {
138
+ elementState: elementState,
139
+ data: !value.includes(separator) || value.slice(from, to + 1).includes(separator)
140
+ ? data
141
+ : data.replace(new RegExp("\\".concat(separator)), ''),
142
+ };
143
+ };
144
+ }
145
+ /**
146
+ * Удаляет лишние нули в начале целой части.
147
+ * @example 0,|00005 => Backspace => |5
148
+ * @example -0,|00005 => Backspace => -|5
149
+ * @example "000000" => 0|
150
+ * @example 0| => Type "5" => 5|
151
+ */
152
+ function createLeadingZeroesValidationPostprocessor(separator) {
153
+ var trimLeadingZeroes = function (value) {
154
+ return value
155
+ .replace(new RegExp('^(\\D+)?0+(?=0)'), '$1')
156
+ .replace(new RegExp('^(\\D+)?0+(?=[1-9])'), '$1');
157
+ };
158
+ var countTrimmedZeroesBefore = function (value, index) {
159
+ var valueBefore = value.slice(0, index);
160
+ var followedByZero = value.slice(index).startsWith('0');
161
+ return (valueBefore.length - trimLeadingZeroes(valueBefore).length + (followedByZero ? 1 : 0));
162
+ };
163
+ return function (_a) {
164
+ var value = _a.value, selection = _a.selection;
165
+ var from = selection[0], to = selection[1];
166
+ var hasSeparator = value.includes(separator);
167
+ var _b = value.split(separator), integerPart = _b[0], _c = _b[1], decimalPart = _c === void 0 ? '' : _c;
168
+ var zeroTrimmedIntegerPart = trimLeadingZeroes(integerPart);
169
+ if (integerPart === zeroTrimmedIntegerPart) {
170
+ return { value: value, selection: selection };
171
+ }
172
+ var newFrom = from - countTrimmedZeroesBefore(value, from);
173
+ var newTo = to - countTrimmedZeroesBefore(value, to);
174
+ return {
175
+ value: zeroTrimmedIntegerPart + (hasSeparator ? separator : '') + decimalPart,
176
+ selection: [Math.max(newFrom, 0), Math.max(newTo, 0)],
177
+ };
178
+ };
179
+ }
180
+ /**
181
+ * Валидирует значение с учетом min max значений.
182
+ * Работает совместно с createMinMaxPlugin
183
+ */
184
+ function createMinMaxPostprocessor(_a) {
185
+ var min = _a.min, max = _a.max, separator = _a.separator;
186
+ return function (_a) {
187
+ var value = _a.value, selection = _a.selection;
188
+ var parsedNumber = parseNumber(value);
189
+ var limitedValue =
190
+ /**
191
+ * Здесь невозможно ограничить нижнюю границу, если пользователь вводит положительное число.
192
+ * То же самое для верхней границы и отрицательного числа.
193
+ * Если min=5, то без этого условия не получится ввести 40, похожая ситуация и с отрицательным max
194
+ */
195
+ parsedNumber > 0 ? Math.min(parsedNumber, max) : Math.max(parsedNumber, min);
196
+ if (!Number.isNaN(parsedNumber) && limitedValue !== parsedNumber) {
197
+ var newValue = "".concat(limitedValue).replace('.', separator);
198
+ return {
199
+ value: newValue,
200
+ selection: [newValue.length, newValue.length],
201
+ };
202
+ }
203
+ return {
204
+ value: value,
205
+ selection: selection,
206
+ };
207
+ };
208
+ }
209
+ function createMinMaxPlugin(_a) {
210
+ var min = _a.min, max = _a.max;
211
+ return function (element, options) {
212
+ var listener = function () {
213
+ var parsedNumber = parseNumber(element.value);
214
+ var clampedNumber = coreComponentsShared.fnUtils.clamp(parsedNumber, min, max);
215
+ if (!Number.isNaN(parsedNumber) && parsedNumber !== clampedNumber) {
216
+ element.value = core.maskitoTransform(stringifyNumberWithoutExp(clampedNumber), options);
217
+ element.dispatchEvent(new Event('input', { bubbles: true }));
218
+ }
219
+ };
220
+ var evListenerOptions = { capture: true };
221
+ element.addEventListener('blur', listener, evListenerOptions);
222
+ return function () { return element.removeEventListener('blur', listener, evListenerOptions); };
223
+ };
224
+ }
225
+ function createNotEmptyPartsPlugin(separator) {
226
+ return function (element) {
227
+ var listener = function () {
228
+ var newValue = element.value
229
+ // 0,9000000 -> 0,9
230
+ .replace(new RegExp("(\\".concat(separator, "\\d*?)(0+$)")), '$1')
231
+ // ,2 => 0,2
232
+ .replace(new RegExp("^(\\D+)?\\".concat(separator)), "$10".concat(separator))
233
+ // 0, -> 0
234
+ .replace(new RegExp("\\".concat(separator, "$")), '')
235
+ // -0 -> 0
236
+ .replace(new RegExp("^".concat(MINUS_SIGN, "0$")), '0')
237
+ // - -> ''
238
+ .replace(new RegExp("^".concat(MINUS_SIGN, "$")), '');
239
+ if (newValue !== element.value) {
240
+ element.value = newValue;
241
+ element.dispatchEvent(new Event('input', { bubbles: true }));
242
+ }
243
+ };
244
+ var evListenerOptions = { capture: true };
245
+ element.addEventListener('blur', listener, evListenerOptions);
246
+ return function () { return element.removeEventListener('blur', listener, evListenerOptions); };
247
+ };
248
+ }
32
249
 
250
+ exports.MAX_DIGITS = MAX_DIGITS;
251
+ exports.MAX_SAFE_INTEGER = MAX_SAFE_INTEGER;
252
+ exports.MINUS_SIGN = MINUS_SIGN;
253
+ exports.MIN_SAFE_INTEGER = MIN_SAFE_INTEGER;
33
254
  exports.SEPARATORS = SEPARATORS;
34
- exports.SIGNS = SIGNS;
35
- exports.createSeparatorsRegExp = createSeparatorsRegExp;
36
- exports.getAllowedValue = getAllowedValue;
255
+ exports.createMaskOptions = createMaskOptions;
256
+ exports.createMinMaxPlugin = createMinMaxPlugin;
257
+ exports.createNotEmptyPartsPlugin = createNotEmptyPartsPlugin;
258
+ exports.parseNumber = parseNumber;
259
+ exports.stringifyNumberWithoutExp = stringifyNumberWithoutExp;