@servicetitan/anvil2 1.43.3 → 1.44.0

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 (99) hide show
  1. package/CHANGELOG.md +8 -5
  2. package/dist/{Breadcrumbs-B5bqrZ5D-BqK-Qfh9.js → Breadcrumbs-BMbkH9hL-BUjQksuj.js} +3 -3
  3. package/dist/{Breadcrumbs-B5bqrZ5D-BqK-Qfh9.js.map → Breadcrumbs-BMbkH9hL-BUjQksuj.js.map} +1 -1
  4. package/dist/{Breadcrumbs-De8uoLi8.js → Breadcrumbs-fthw3qaL.js} +2 -2
  5. package/dist/{Breadcrumbs-De8uoLi8.js.map → Breadcrumbs-fthw3qaL.js.map} +1 -1
  6. package/dist/Breadcrumbs.js +1 -1
  7. package/dist/{Calendar-DD5kmVd3-CBGTR11R.js → Calendar-BvftKznl-D4_xp9Rv.js} +3 -3
  8. package/dist/{Calendar-DD5kmVd3-CBGTR11R.js.map → Calendar-BvftKznl-D4_xp9Rv.js.map} +1 -1
  9. package/dist/{Calendar-Bgpz99F2.js → Calendar-DdOR9T_K.js} +2 -2
  10. package/dist/{Calendar-Bgpz99F2.js.map → Calendar-DdOR9T_K.js.map} +1 -1
  11. package/dist/Calendar.js +1 -1
  12. package/dist/{DateField-_TqCdcYv.js → DateField-Bqsnpt8S.js} +4 -4
  13. package/dist/{DateField-_TqCdcYv.js.map → DateField-Bqsnpt8S.js.map} +1 -1
  14. package/dist/DateField.js +1 -1
  15. package/dist/{DateFieldRange-Dk8WA52F.js → DateFieldRange-Cax7Kl9A.js} +2 -2
  16. package/dist/{DateFieldRange-Dk8WA52F.js.map → DateFieldRange-Cax7Kl9A.js.map} +1 -1
  17. package/dist/DateFieldRange.js +1 -1
  18. package/dist/{DateFieldSingle-D3xD8YZk.js → DateFieldSingle-bMX770gU.js} +2 -2
  19. package/dist/{DateFieldSingle-D3xD8YZk.js.map → DateFieldSingle-bMX770gU.js.map} +1 -1
  20. package/dist/DateFieldSingle.js +1 -1
  21. package/dist/{DateFieldYearless-BxkCSNk5.js → DateFieldYearless-ClNYTBK_.js} +2 -2
  22. package/dist/{DateFieldYearless-BxkCSNk5.js.map → DateFieldYearless-ClNYTBK_.js.map} +1 -1
  23. package/dist/{DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js → DateFieldYearless-pejtv7-x-DBHpW83U.js} +7 -65
  24. package/dist/DateFieldYearless-pejtv7-x-DBHpW83U.js.map +1 -0
  25. package/dist/DateFieldYearless.js +1 -1
  26. package/dist/{InputMask-wUro4G2j.js → InputMask-BOUlnmiF.js} +2 -2
  27. package/dist/{InputMask-wUro4G2j.js.map → InputMask-BOUlnmiF.js.map} +1 -1
  28. package/dist/{InputMask-CE3pdXwL-B8XVrj36.js → InputMask-BuO8lNAB-DdxdO5Jw.js} +3 -3
  29. package/dist/{InputMask-CE3pdXwL-B8XVrj36.js.map → InputMask-BuO8lNAB-DdxdO5Jw.js.map} +1 -1
  30. package/dist/InputMask.js +1 -1
  31. package/dist/{Link-DZa0wmkA.js → Link-C4N_cV3i.js} +2 -2
  32. package/dist/{Link-DZa0wmkA.js.map → Link-C4N_cV3i.js.map} +1 -1
  33. package/dist/{Link-Dqp_XXl_-Bv20l_tY.js → Link-MiffPrW9-Cx1zYWSF.js} +3 -3
  34. package/dist/{Link-Dqp_XXl_-Bv20l_tY.js.map → Link-MiffPrW9-Cx1zYWSF.js.map} +1 -1
  35. package/dist/Link.js +1 -1
  36. package/dist/LinkButton.css +5 -0
  37. package/dist/LinkButton.d.ts +6 -0
  38. package/dist/LinkButton.js +76 -0
  39. package/dist/LinkButton.js.map +1 -0
  40. package/dist/{ListView-DdHpJHTc.js → ListView-DVPhWDCM.js} +24 -5
  41. package/dist/ListView-DVPhWDCM.js.map +1 -0
  42. package/dist/ListView.js +1 -1
  43. package/dist/NumberField-C9lMsPMD.js +1582 -0
  44. package/dist/NumberField-C9lMsPMD.js.map +1 -0
  45. package/dist/NumberField.css +49 -0
  46. package/dist/NumberField.d.ts +6 -0
  47. package/dist/NumberField.js +2 -0
  48. package/dist/NumberField.js.map +1 -0
  49. package/dist/{Page-CxB5N9dR.js → Page-CGAylFKz.js} +2 -2
  50. package/dist/{Page-CxB5N9dR.js.map → Page-CGAylFKz.js.map} +1 -1
  51. package/dist/Page.js +1 -1
  52. package/dist/{SegmentedControl-DKMQuf7s.js → SegmentedControl-DY1PSYmd.js} +2 -2
  53. package/dist/{SegmentedControl-DKMQuf7s.js.map → SegmentedControl-DY1PSYmd.js.map} +1 -1
  54. package/dist/SegmentedControl.js +1 -1
  55. package/dist/{SelectCard-DUNwYa5y-BSi21Aba.js → SelectCard-LSAl_7Lh-BYtfe9PC.js} +3 -3
  56. package/dist/{SelectCard-DUNwYa5y-BSi21Aba.js.map → SelectCard-LSAl_7Lh-BYtfe9PC.js.map} +1 -1
  57. package/dist/SelectCard.js +1 -1
  58. package/dist/{SelectCardGroup-YGYiBv5M.js → SelectCardGroup-u_QKdeRZ.js} +2 -2
  59. package/dist/{SelectCardGroup-YGYiBv5M.js.map → SelectCardGroup-u_QKdeRZ.js.map} +1 -1
  60. package/dist/{TextField-gYAqTpcX.js → TextField-B364hxo8.js} +2 -2
  61. package/dist/{TextField-gYAqTpcX.js.map → TextField-B364hxo8.js.map} +1 -1
  62. package/dist/{TextField-CRTh0gL_-D2CjcYXX.js → TextField-DoPP1CQ--UWAOTYiv.js} +2 -2
  63. package/dist/{TextField-CRTh0gL_-D2CjcYXX.js.map → TextField-DoPP1CQ--UWAOTYiv.js.map} +1 -1
  64. package/dist/TextField.js +1 -1
  65. package/dist/{Textarea-DohNOiIp.js → Textarea-D2AAZ1L3.js} +2 -2
  66. package/dist/{Textarea-DohNOiIp.js.map → Textarea-D2AAZ1L3.js.map} +1 -1
  67. package/dist/Textarea.js +1 -1
  68. package/dist/{TimeField-DRHLRqN3.js → TimeField-tA9uo4lK.js} +5 -4
  69. package/dist/{TimeField-DRHLRqN3.js.map → TimeField-tA9uo4lK.js.map} +1 -1
  70. package/dist/TimeField.js +1 -1
  71. package/dist/assets/icons/st/gnav_home_outline.svg +1 -1
  72. package/dist/assets/icons/st.ts +0 -2
  73. package/dist/components/LinkButton/LinkButton.d.ts +57 -0
  74. package/dist/components/LinkButton/index.d.ts +2 -0
  75. package/dist/components/NumberField/NumberField.d.ts +11 -0
  76. package/dist/components/NumberField/index.d.ts +2 -0
  77. package/dist/components/index.d.ts +1 -0
  78. package/dist/{usePopoverSupport-B9Lsqryr-DhZHMoNb.js → index.esm-dyoPQhi6.js} +735 -13
  79. package/dist/index.esm-dyoPQhi6.js.map +1 -0
  80. package/dist/index.js +17 -16
  81. package/dist/index.js.map +1 -1
  82. package/dist/useFocusWithin-Cxz14Zyz-DEudRcSI.js +64 -0
  83. package/dist/useFocusWithin-Cxz14Zyz-DEudRcSI.js.map +1 -0
  84. package/dist/useLinkStyles.js +1 -1
  85. package/dist/{useOptionallyControlledState-DAv5LXXh-DAv5LXXh.js → useOptionallyControlledState-DbDuos5L-DbDuos5L.js} +9 -3
  86. package/dist/useOptionallyControlledState-DbDuos5L-DbDuos5L.js.map +1 -0
  87. package/dist/usePopoverSupport-B9Lsqryr-CYJxXiun.js +15 -0
  88. package/dist/usePopoverSupport-B9Lsqryr-CYJxXiun.js.map +1 -0
  89. package/package.json +2 -2
  90. package/dist/DateFieldYearless-CAUHW6Ow-EUWxJ0OY.js.map +0 -1
  91. package/dist/ListView-DdHpJHTc.js.map +0 -1
  92. package/dist/assets/icons/st/ai_mark.svg +0 -1
  93. package/dist/assets/icons/st/ai_mark_gradient.svg +0 -1
  94. package/dist/useOptionallyControlledState-DAv5LXXh-DAv5LXXh.js.map +0 -1
  95. package/dist/usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map +0 -1
  96. /package/dist/{Breadcrumbs-B5bqrZ5D.css → Breadcrumbs-BMbkH9hL.css} +0 -0
  97. /package/dist/{Calendar-DD5kmVd3.css → Calendar-BvftKznl.css} +0 -0
  98. /package/dist/{Link-Dqp_XXl_.css → Link-MiffPrW9.css} +0 -0
  99. /package/dist/{SelectCard-DUNwYa5y.css → SelectCard-LSAl_7Lh.css} +0 -0
@@ -1,5 +1,4 @@
1
1
  import { useState, useCallback, useRef, useLayoutEffect, useEffect } from 'react';
2
- import { s as supportsPopover } from './ProgressBar-BRvB-bD4-DppwBrFg.js';
3
2
 
4
3
  function getContentEditableSelection(element) {
5
4
  const { anchorOffset = 0, focusOffset = 0 } = element.ownerDocument.getSelection() || {};
@@ -972,6 +971,9 @@ const DATE_SEGMENTS_MAX_VALUES = {
972
971
  year: 9999,
973
972
  };
974
973
 
974
+ // eslint-disable-next-line i18n/no-russian-character
975
+ const DEFAULT_DECIMAL_PSEUDO_SEPARATORS = ['.', ',', 'б', 'ю'];
976
+
975
977
  const DEFAULT_MIN_DATE = new Date('0001-01-01T00:00');
976
978
  const DEFAULT_MAX_DATE = new Date('9999-12-31T23:59:59.999');
977
979
 
@@ -992,6 +994,10 @@ const DEFAULT_TIME_SEGMENT_MIN_VALUES = {
992
994
  * {@link https://unicode-table.com/en/00A0/ Non-breaking space}.
993
995
  */
994
996
  const CHAR_NO_BREAK_SPACE = '\u00A0';
997
+ /**
998
+ * {@link https://symbl.cc/en/200B/ Zero width space}.
999
+ */
1000
+ const CHAR_ZERO_WIDTH_SPACE = '\u200B';
995
1001
  /**
996
1002
  * {@link https://unicode-table.com/en/2013/ EN dash}
997
1003
  * is used to indicate a range of numbers or a span of time.
@@ -1119,6 +1125,21 @@ function escapeRegExp(str) {
1119
1125
  : str;
1120
1126
  }
1121
1127
 
1128
+ function extractAffixes(value, { prefix, postfix }) {
1129
+ var _a, _b;
1130
+ const prefixRegExp = new RegExp(`^${escapeRegExp(prefix)}`);
1131
+ const postfixRegExp = new RegExp(`${escapeRegExp(postfix)}$`);
1132
+ const [extractedPrefix = ''] = (_a = value.match(prefixRegExp)) !== null && _a !== void 0 ? _a : [];
1133
+ const [extractedPostfix = ''] = (_b = value.match(postfixRegExp)) !== null && _b !== void 0 ? _b : [];
1134
+ return {
1135
+ extractedPrefix,
1136
+ extractedPostfix,
1137
+ cleanValue: extractedPrefix || extractedPostfix
1138
+ ? value.slice(extractedPrefix.length, extractedPostfix.length ? -extractedPostfix.length : Infinity)
1139
+ : value,
1140
+ };
1141
+ }
1142
+
1122
1143
  function findCommonBeginningSubstr(a, b) {
1123
1144
  let res = '';
1124
1145
  for (let i = 0; i < a.length; i++) {
@@ -2108,6 +2129,717 @@ function cutExpression(expression, value) {
2108
2129
  return expression.slice(0, afterLastDigit);
2109
2130
  }
2110
2131
 
2132
+ /**
2133
+ * It drops prefix and postfix from data
2134
+ * Needed for case, when prefix or postfix contain decimalSeparator, to ignore it in resulting number
2135
+ * @example User pastes '{prefix}123.45{postfix}' => 123.45
2136
+ */
2137
+ function createAffixesFilterPreprocessor({ prefix, postfix, }) {
2138
+ return ({ elementState, data }) => {
2139
+ const { cleanValue: cleanData } = extractAffixes(data, {
2140
+ prefix,
2141
+ postfix,
2142
+ });
2143
+ return {
2144
+ elementState,
2145
+ data: cleanData,
2146
+ };
2147
+ };
2148
+ }
2149
+
2150
+ function generateMaskExpression({ decimalPseudoSeparators, decimalSeparator, maximumFractionDigits, min, minusSign, postfix, prefix, pseudoMinuses, thousandSeparator, }) {
2151
+ const computedPrefix = min < 0 && [minusSign, ...pseudoMinuses].includes(prefix)
2152
+ ? ''
2153
+ : computeAllOptionalCharsRegExp(prefix);
2154
+ const digit = String.raw `\d`;
2155
+ const optionalMinus = min < 0 ? `[${minusSign}${pseudoMinuses.map((x) => `\\${x}`).join('')}]?` : '';
2156
+ const integerPart = thousandSeparator
2157
+ ? `[${digit}${escapeRegExp(thousandSeparator).replaceAll(/\s/g, String.raw `\s`)}]*`
2158
+ : `[${digit}]*`;
2159
+ const precisionPart = Number.isFinite(maximumFractionDigits)
2160
+ ? maximumFractionDigits
2161
+ : '';
2162
+ const decimalPart = maximumFractionDigits > 0
2163
+ ? `([${escapeRegExp(decimalSeparator)}${decimalPseudoSeparators
2164
+ .map(escapeRegExp)
2165
+ .join('')}]${digit}{0,${precisionPart}})?`
2166
+ : '';
2167
+ const computedPostfix = computeAllOptionalCharsRegExp(postfix);
2168
+ return new RegExp(`^${computedPrefix}${optionalMinus}${integerPart}${decimalPart}${computedPostfix}$`);
2169
+ }
2170
+ function computeAllOptionalCharsRegExp(str) {
2171
+ return str
2172
+ ? `${str
2173
+ .split('')
2174
+ .map((char) => `${escapeRegExp(char)}?`)
2175
+ .join('')}`
2176
+ : '';
2177
+ }
2178
+
2179
+ function maskitoParseNumber(maskedNumber,
2180
+ // TODO(v4): decimalSeparatorOrParams: MaskitoNumberParams | string => params: MaskitoNumberParams = {}
2181
+ decimalSeparatorOrParams = {}) {
2182
+ const { decimalSeparator = '.', minusSign = '' } = typeof decimalSeparatorOrParams === 'string'
2183
+ ? { decimalSeparator: decimalSeparatorOrParams }
2184
+ : decimalSeparatorOrParams;
2185
+ const hasNegativeSign = !!new RegExp(`^\\D*[${escapeRegExp(minusSign)}\\${DEFAULT_PSEUDO_MINUSES.join('\\')}]`).exec(maskedNumber);
2186
+ const escapedDecimalSeparator = escapeRegExp(decimalSeparator);
2187
+ const unmaskedNumber = maskedNumber
2188
+ // drop all decimal separators not followed by a digit
2189
+ .replaceAll(new RegExp(`${escapedDecimalSeparator}(?!\\d)`, 'g'), '')
2190
+ // drop all non-digit characters except decimal separator
2191
+ .replaceAll(new RegExp(`[^\\d${escapedDecimalSeparator}]`, 'g'), '')
2192
+ .replace(decimalSeparator, decimalSeparator && '.');
2193
+ if (unmaskedNumber) {
2194
+ const sign = hasNegativeSign ? CHAR_HYPHEN : '';
2195
+ return Number(`${sign}${unmaskedNumber}`);
2196
+ }
2197
+ return NaN;
2198
+ }
2199
+
2200
+ function maskitoStringifyNumber(number, params) {
2201
+ if (Number.isNaN(number) || number === null) {
2202
+ return '';
2203
+ }
2204
+ const { min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER, decimalSeparator = '.', } = params;
2205
+ const value = clamp(number, min, max).toString().replace('.', decimalSeparator);
2206
+ return maskitoTransform(value, maskitoNumberOptionsGenerator(params));
2207
+ }
2208
+
2209
+ /**
2210
+ * Convert number to string with replacing exponent part on decimals
2211
+ *
2212
+ * @param value the number
2213
+ * @return string representation of a number
2214
+ */
2215
+ function stringifyNumberWithoutExp(value) {
2216
+ var _a;
2217
+ const valueAsString = String(value);
2218
+ const [numberPart = '', expPart] = valueAsString.split('e-');
2219
+ let valueWithoutExp = valueAsString;
2220
+ if (expPart) {
2221
+ const [, fractionalPart] = numberPart.split('.');
2222
+ const decimalDigits = Number(expPart) + ((_a = fractionalPart === null || fractionalPart === void 0 ? void 0 : fractionalPart.length) !== null && _a !== void 0 ? _a : 0);
2223
+ valueWithoutExp = value.toFixed(decimalDigits);
2224
+ }
2225
+ return valueWithoutExp;
2226
+ }
2227
+
2228
+ function toNumberParts(value, { decimalSeparator, minusSign }) {
2229
+ const [integerWithMinus = '', decimalPart = ''] = decimalSeparator
2230
+ ? value.split(decimalSeparator)
2231
+ : [value];
2232
+ const escapedMinus = escapeRegExp(minusSign);
2233
+ const [, minus = '', integerPart = ''] = new RegExp(`^(?:[^\\d${escapedMinus}])?(${escapedMinus})?(.*)`).exec(integerWithMinus) || [];
2234
+ return { minus, integerPart, decimalPart };
2235
+ }
2236
+
2237
+ function validateDecimalPseudoSeparators({ decimalSeparator, thousandSeparator, decimalPseudoSeparators = DEFAULT_DECIMAL_PSEUDO_SEPARATORS, }) {
2238
+ return decimalPseudoSeparators.filter((char) => char !== thousandSeparator && char !== decimalSeparator);
2239
+ }
2240
+
2241
+ /**
2242
+ * If `minimumFractionDigits` is `>0`, it pads decimal part with zeroes
2243
+ * (until number of digits after decimalSeparator is equal to the `minimumFractionDigits`).
2244
+ * @example 1,42 => (`minimumFractionDigits` is equal to 4) => 1,4200.
2245
+ */
2246
+ function createDecimalZeroPaddingPostprocessor({ decimalSeparator, minimumFractionDigits, prefix, postfix, minusSign, }) {
2247
+ if (!minimumFractionDigits) {
2248
+ return identity;
2249
+ }
2250
+ return ({ value, selection }) => {
2251
+ const { cleanValue, extractedPrefix, extractedPostfix } = extractAffixes(value, {
2252
+ prefix,
2253
+ postfix,
2254
+ });
2255
+ if (Number.isNaN(maskitoParseNumber(cleanValue, { decimalSeparator, minusSign }))) {
2256
+ return { value, selection };
2257
+ }
2258
+ const [integerPart, decimalPart = ''] = cleanValue.split(decimalSeparator);
2259
+ return {
2260
+ value: extractedPrefix +
2261
+ integerPart +
2262
+ decimalSeparator +
2263
+ decimalPart.padEnd(minimumFractionDigits, '0') +
2264
+ extractedPostfix,
2265
+ selection,
2266
+ };
2267
+ };
2268
+ }
2269
+
2270
+ /**
2271
+ * Make textfield empty if there is no integer part and all decimal digits are zeroes.
2272
+ * @example 0|,00 => Backspace => Empty.
2273
+ * @example -0|,00 => Backspace => -.
2274
+ * @example ,42| => Backspace x2 => ,|00 => Backspace => Empty
2275
+ */
2276
+ function emptyPostprocessor({ prefix, postfix, decimalSeparator, minusSign, }) {
2277
+ return ({ value, selection }) => {
2278
+ const [caretIndex] = selection;
2279
+ const { cleanValue, extractedPrefix, extractedPostfix } = extractAffixes(value, {
2280
+ prefix,
2281
+ postfix,
2282
+ });
2283
+ const { minus, integerPart, decimalPart } = toNumberParts(cleanValue, {
2284
+ decimalSeparator,
2285
+ minusSign,
2286
+ });
2287
+ const aloneDecimalSeparator = !integerPart &&
2288
+ !decimalPart &&
2289
+ Boolean(decimalSeparator) &&
2290
+ cleanValue.includes(decimalSeparator);
2291
+ if ((!integerPart &&
2292
+ !Number(decimalPart) &&
2293
+ caretIndex === (minus + extractedPrefix).length) ||
2294
+ aloneDecimalSeparator) {
2295
+ return {
2296
+ selection,
2297
+ value: extractedPrefix + minus + extractedPostfix,
2298
+ };
2299
+ }
2300
+ return { value, selection };
2301
+ };
2302
+ }
2303
+
2304
+ /**
2305
+ * This preprocessor works only once at initialization phase (when `new Maskito(...)` is executed).
2306
+ * This preprocessor helps to avoid conflicts during transition from one mask to another (for the same input).
2307
+ * For example, the developer changes postfix (or other mask's props) during run-time.
2308
+ * ```
2309
+ * let maskitoOptions = maskitoNumberOptionsGenerator({postfix: ' year'});
2310
+ * // [3 seconds later]
2311
+ * maskitoOptions = maskitoNumberOptionsGenerator({postfix: ' years'});
2312
+ * ```
2313
+ */
2314
+ function createInitializationOnlyPreprocessor({ decimalPseudoSeparators, decimalSeparator, minusSign, postfix, prefix, pseudoMinuses, }) {
2315
+ let isInitializationPhase = true;
2316
+ const cleanNumberMask = generateMaskExpression({
2317
+ decimalSeparator,
2318
+ decimalPseudoSeparators,
2319
+ pseudoMinuses,
2320
+ prefix: '',
2321
+ postfix: '',
2322
+ thousandSeparator: '',
2323
+ maximumFractionDigits: Infinity,
2324
+ min: Number.MIN_SAFE_INTEGER,
2325
+ minusSign,
2326
+ });
2327
+ return ({ elementState, data }) => {
2328
+ if (!isInitializationPhase) {
2329
+ return { elementState, data };
2330
+ }
2331
+ isInitializationPhase = false;
2332
+ const { value, selection } = elementState;
2333
+ const [from, to] = selection;
2334
+ const { extractedPrefix, cleanValue, extractedPostfix } = extractAffixes(value, {
2335
+ prefix,
2336
+ postfix,
2337
+ });
2338
+ const cleanState = maskitoTransform({
2339
+ selection: [
2340
+ Math.max(from - extractedPrefix.length, 0),
2341
+ clamp(to - extractedPrefix.length, 0, cleanValue.length),
2342
+ ],
2343
+ value: cleanValue,
2344
+ }, {
2345
+ mask: cleanNumberMask,
2346
+ });
2347
+ const [cleanFrom, cleanTo] = cleanState.selection;
2348
+ return {
2349
+ elementState: {
2350
+ selection: [
2351
+ cleanFrom + extractedPrefix.length,
2352
+ cleanTo + extractedPrefix.length,
2353
+ ],
2354
+ value: extractedPrefix + cleanState.value + extractedPostfix,
2355
+ },
2356
+ data,
2357
+ };
2358
+ };
2359
+ }
2360
+
2361
+ /**
2362
+ * It removes repeated leading zeroes for integer part.
2363
+ * @example 0,|00005 => Backspace => |5
2364
+ * @example -0,|00005 => Backspace => -|5
2365
+ * @example User types "000000" => 0|
2366
+ * @example 0| => User types "5" => 5|
2367
+ */
2368
+ function createLeadingZeroesValidationPostprocessor({ decimalSeparator, thousandSeparator, prefix, postfix, }) {
2369
+ const trimLeadingZeroes = (value) => {
2370
+ const escapedThousandSeparator = escapeRegExp(thousandSeparator);
2371
+ return value
2372
+ .replace(
2373
+ // all leading zeroes followed by another zero
2374
+ new RegExp(`^(\\D+)?[0${escapedThousandSeparator}]+(?=0)`), '$1')
2375
+ .replace(
2376
+ // zero followed by not-zero digit
2377
+ new RegExp(`^(\\D+)?[0${escapedThousandSeparator}]+(?=[1-9])`), '$1');
2378
+ };
2379
+ const countTrimmedZeroesBefore = (value, index) => {
2380
+ const valueBefore = value.slice(0, index);
2381
+ const followedByZero = value.slice(index).startsWith('0');
2382
+ return (valueBefore.length -
2383
+ trimLeadingZeroes(valueBefore).length +
2384
+ (followedByZero ? 1 : 0));
2385
+ };
2386
+ return ({ value, selection }) => {
2387
+ const [from, to] = selection;
2388
+ const { cleanValue, extractedPrefix, extractedPostfix } = extractAffixes(value, {
2389
+ prefix,
2390
+ postfix,
2391
+ });
2392
+ const hasDecimalSeparator = Boolean(decimalSeparator) && cleanValue.includes(decimalSeparator);
2393
+ const [integerPart = '', decimalPart = ''] = decimalSeparator
2394
+ ? cleanValue.split(decimalSeparator)
2395
+ : [cleanValue];
2396
+ const zeroTrimmedIntegerPart = trimLeadingZeroes(integerPart);
2397
+ if (integerPart === zeroTrimmedIntegerPart) {
2398
+ return { value, selection };
2399
+ }
2400
+ const newFrom = from - countTrimmedZeroesBefore(value, from);
2401
+ const newTo = to - countTrimmedZeroesBefore(value, to);
2402
+ return {
2403
+ value: extractedPrefix +
2404
+ zeroTrimmedIntegerPart +
2405
+ (hasDecimalSeparator ? decimalSeparator : '') +
2406
+ decimalPart +
2407
+ extractedPostfix,
2408
+ selection: [Math.max(newFrom, 0), Math.max(newTo, 0)],
2409
+ };
2410
+ };
2411
+ }
2412
+
2413
+ /**
2414
+ * This postprocessor is connected with {@link createMinMaxPlugin}:
2415
+ * both validate `min`/`max` bounds of entered value (but at the different point of time).
2416
+ */
2417
+ function createMinMaxPostprocessor({ min, max, decimalSeparator, minusSign, }) {
2418
+ return ({ value, selection }) => {
2419
+ const parsedNumber = maskitoParseNumber(value, { decimalSeparator, minusSign });
2420
+ const limitedValue =
2421
+ /**
2422
+ * We cannot limit lower bound if user enters positive number.
2423
+ * The same for upper bound and negative number.
2424
+ * ___
2425
+ * @example (min = 5)
2426
+ * Empty input => Without this condition user cannot type 42 (the first digit will be rejected)
2427
+ * ___
2428
+ * @example (max = -10)
2429
+ * Value is -10 => Without this condition user cannot delete 0 to enter another digit
2430
+ */
2431
+ parsedNumber > 0 ? Math.min(parsedNumber, max) : Math.max(parsedNumber, min);
2432
+ if (parsedNumber && limitedValue !== parsedNumber) {
2433
+ const newValue = `${limitedValue}`
2434
+ .replace('.', decimalSeparator)
2435
+ .replace(CHAR_HYPHEN, minusSign);
2436
+ return {
2437
+ value: newValue,
2438
+ selection: [newValue.length, newValue.length],
2439
+ };
2440
+ }
2441
+ return {
2442
+ value,
2443
+ selection,
2444
+ };
2445
+ };
2446
+ }
2447
+
2448
+ /**
2449
+ * Manage caret-navigation when user "deletes" non-removable digits or separators
2450
+ * @example 1,|42 => Backspace => 1|,42 (only if `minimumFractionDigits` is `>0`)
2451
+ * @example 1|,42 => Delete => 1,|42 (only if `minimumFractionDigits` is `>0`)
2452
+ * @example 0,|00 => Delete => 0,0|0 (only if `minimumFractionDigits` is `>0`)
2453
+ * @example 1 |000 => Backspace => 1| 000 (always)
2454
+ */
2455
+ function createNonRemovableCharsDeletionPreprocessor({ decimalSeparator, thousandSeparator, minimumFractionDigits, }) {
2456
+ return ({ elementState, data }, actionType) => {
2457
+ const { value, selection } = elementState;
2458
+ const [from, to] = selection;
2459
+ const selectedCharacters = value.slice(from, to);
2460
+ const nonRemovableSeparators = minimumFractionDigits
2461
+ ? [decimalSeparator, thousandSeparator]
2462
+ : [thousandSeparator];
2463
+ const areNonRemovableZeroesSelected = Boolean(minimumFractionDigits) &&
2464
+ from > value.indexOf(decimalSeparator) &&
2465
+ Boolean(selectedCharacters.match(/^0+$/gi));
2466
+ if ((actionType !== 'deleteBackward' && actionType !== 'deleteForward') ||
2467
+ (!nonRemovableSeparators.includes(selectedCharacters) &&
2468
+ !areNonRemovableZeroesSelected)) {
2469
+ return {
2470
+ elementState,
2471
+ data,
2472
+ };
2473
+ }
2474
+ return {
2475
+ elementState: {
2476
+ value,
2477
+ selection: actionType === 'deleteForward' ? [to, to] : [from, from],
2478
+ },
2479
+ data,
2480
+ };
2481
+ };
2482
+ }
2483
+
2484
+ /**
2485
+ * It pads integer part with zero if user types decimal separator (for empty input).
2486
+ * @example Empty input => User types "," (decimal separator) => 0,|
2487
+ */
2488
+ function createNotEmptyIntegerPartPreprocessor({ decimalSeparator, maximumFractionDigits, prefix, postfix, }) {
2489
+ const startWithDecimalSepRegExp = new RegExp(`^\\D*${escapeRegExp(decimalSeparator)}`);
2490
+ return ({ elementState, data }) => {
2491
+ const { value, selection } = elementState;
2492
+ const { cleanValue, extractedPrefix } = extractAffixes(value, {
2493
+ prefix,
2494
+ postfix,
2495
+ });
2496
+ const [from, to] = selection;
2497
+ const cleanFrom = clamp(from - extractedPrefix.length, 0, cleanValue.length);
2498
+ const cleanTo = clamp(to - extractedPrefix.length, 0, cleanValue.length);
2499
+ if (maximumFractionDigits <= 0 ||
2500
+ cleanValue.slice(0, cleanFrom).includes(decimalSeparator) ||
2501
+ cleanValue.slice(cleanTo).includes(decimalSeparator) ||
2502
+ !data.match(startWithDecimalSepRegExp)) {
2503
+ return { elementState, data };
2504
+ }
2505
+ const digitsBeforeCursor = /\d+/.exec(cleanValue.slice(0, cleanFrom));
2506
+ return {
2507
+ elementState,
2508
+ data: digitsBeforeCursor ? data : `0${data}`,
2509
+ };
2510
+ };
2511
+ }
2512
+
2513
+ /**
2514
+ * It replaces pseudo characters with valid one.
2515
+ * @example User types '.' (but separator is equal to comma) => dot is replaced with comma.
2516
+ * @example User types hyphen / en-dash / em-dash => it is replaced with minus.
2517
+ */
2518
+ function createPseudoCharactersPreprocessor({ validCharacter, pseudoCharacters, prefix, postfix, }) {
2519
+ const pseudoCharactersRegExp = new RegExp(`[${pseudoCharacters.join('')}]`, 'gi');
2520
+ return ({ elementState, data }) => {
2521
+ const { value, selection } = elementState;
2522
+ const { cleanValue, extractedPostfix, extractedPrefix } = extractAffixes(value, {
2523
+ prefix,
2524
+ postfix,
2525
+ });
2526
+ return {
2527
+ elementState: {
2528
+ selection,
2529
+ value: extractedPrefix +
2530
+ cleanValue.replace(pseudoCharactersRegExp, validCharacter) +
2531
+ extractedPostfix,
2532
+ },
2533
+ data: data.replace(pseudoCharactersRegExp, validCharacter),
2534
+ };
2535
+ };
2536
+ }
2537
+
2538
+ /**
2539
+ * It rejects new typed decimal separator if it already exists in text field.
2540
+ * Behaviour is similar to native <input type="number"> (Chrome).
2541
+ * @example 1|23,45 => Press comma (decimal separator) => 1|23,45 (do nothing).
2542
+ */
2543
+ function createRepeatedDecimalSeparatorPreprocessor({ decimalSeparator, prefix, postfix, }) {
2544
+ if (!decimalSeparator) {
2545
+ return identity;
2546
+ }
2547
+ return ({ elementState, data }) => {
2548
+ const { value, selection } = elementState;
2549
+ const [from, to] = selection;
2550
+ const { cleanValue } = extractAffixes(value, { prefix, postfix });
2551
+ return {
2552
+ elementState,
2553
+ data: !cleanValue.includes(decimalSeparator) ||
2554
+ value.slice(from, to + 1).includes(decimalSeparator)
2555
+ ? data
2556
+ : data.replaceAll(new RegExp(escapeRegExp(decimalSeparator), 'gi'), ''),
2557
+ };
2558
+ };
2559
+ }
2560
+
2561
+ /**
2562
+ * It adds symbol for separating thousands.
2563
+ * @example 1000000 => (thousandSeparator is equal to space) => 1 000 000.
2564
+ */
2565
+ function createThousandSeparatorPostprocessor({ thousandSeparator, decimalSeparator, prefix, postfix, minusSign, }) {
2566
+ if (!thousandSeparator) {
2567
+ return identity;
2568
+ }
2569
+ const isAllSpaces = (...chars) => chars.every((x) => /\s/.test(x));
2570
+ return ({ value, selection }) => {
2571
+ const [initialFrom, initialTo] = selection;
2572
+ let [from, to] = selection;
2573
+ const { cleanValue, extractedPostfix, extractedPrefix } = extractAffixes(value, {
2574
+ prefix,
2575
+ postfix,
2576
+ });
2577
+ const { minus, integerPart, decimalPart } = toNumberParts(cleanValue, {
2578
+ decimalSeparator,
2579
+ minusSign,
2580
+ });
2581
+ const hasDecimalSeparator = decimalSeparator && cleanValue.includes(decimalSeparator);
2582
+ const deletedChars = cleanValue.length -
2583
+ (minus +
2584
+ integerPart +
2585
+ (hasDecimalSeparator ? decimalSeparator + decimalPart : '')).length;
2586
+ if (deletedChars > 0 && initialFrom && initialFrom <= deletedChars) {
2587
+ from -= deletedChars;
2588
+ }
2589
+ if (deletedChars > 0 && initialTo && initialTo <= deletedChars) {
2590
+ to -= deletedChars;
2591
+ }
2592
+ const processedIntegerPart = Array.from(integerPart).reduceRight((formattedValuePart, char, i) => {
2593
+ const isLeadingThousandSeparator = !i && char === thousandSeparator;
2594
+ const isPositionForSeparator = !isLeadingThousandSeparator &&
2595
+ Boolean(formattedValuePart.length) &&
2596
+ (formattedValuePart.length + 1) % 4 === 0;
2597
+ const isSeparator = char === thousandSeparator || isAllSpaces(char, thousandSeparator);
2598
+ if (isPositionForSeparator && isSeparator) {
2599
+ return thousandSeparator + formattedValuePart;
2600
+ }
2601
+ if (!isPositionForSeparator && isSeparator) {
2602
+ if (i && i <= initialFrom) {
2603
+ from--;
2604
+ }
2605
+ if (i && i <= initialTo) {
2606
+ to--;
2607
+ }
2608
+ return formattedValuePart;
2609
+ }
2610
+ if (!isPositionForSeparator) {
2611
+ return char + formattedValuePart;
2612
+ }
2613
+ if (i < initialFrom) {
2614
+ from++;
2615
+ }
2616
+ if (i < initialTo) {
2617
+ to++;
2618
+ }
2619
+ return char + thousandSeparator + formattedValuePart;
2620
+ }, '');
2621
+ return {
2622
+ value: extractedPrefix +
2623
+ minus +
2624
+ processedIntegerPart +
2625
+ (hasDecimalSeparator ? decimalSeparator : '') +
2626
+ decimalPart +
2627
+ extractedPostfix,
2628
+ selection: [from, to],
2629
+ };
2630
+ };
2631
+ }
2632
+
2633
+ /**
2634
+ * It drops decimal part if `maximumFractionDigits` is zero.
2635
+ * @example User pastes '123.45' (but `maximumFractionDigits` is zero) => 123
2636
+ */
2637
+ function createZeroPrecisionPreprocessor({ maximumFractionDigits, decimalSeparator, prefix, postfix, }) {
2638
+ if (maximumFractionDigits > 0 ||
2639
+ !decimalSeparator // all separators should be treated only as thousand separators
2640
+ ) {
2641
+ return identity;
2642
+ }
2643
+ const decimalPartRegExp = new RegExp(`${escapeRegExp(decimalSeparator)}.*$`, 'g');
2644
+ return ({ elementState, data }) => {
2645
+ const { value, selection } = elementState;
2646
+ const { cleanValue, extractedPrefix, extractedPostfix } = extractAffixes(value, {
2647
+ prefix,
2648
+ postfix,
2649
+ });
2650
+ const [from, to] = selection;
2651
+ const newValue = extractedPrefix +
2652
+ cleanValue.replace(decimalPartRegExp, '') +
2653
+ extractedPostfix;
2654
+ return {
2655
+ elementState: {
2656
+ selection: [
2657
+ Math.min(from, newValue.length),
2658
+ Math.min(to, newValue.length),
2659
+ ],
2660
+ value: newValue,
2661
+ },
2662
+ data: data.replace(decimalPartRegExp, ''),
2663
+ };
2664
+ };
2665
+ }
2666
+
2667
+ const DUMMY_SELECTION = [0, 0];
2668
+ /**
2669
+ * It removes repeated leading zeroes for integer part on blur-event.
2670
+ * @example 000000 => blur => 0
2671
+ * @example 00005 => blur => 5
2672
+ */
2673
+ function createLeadingZeroesValidationPlugin({ decimalSeparator, thousandSeparator, prefix, postfix, }) {
2674
+ const dropRepeatedLeadingZeroes = createLeadingZeroesValidationPostprocessor({
2675
+ decimalSeparator,
2676
+ thousandSeparator,
2677
+ prefix,
2678
+ postfix,
2679
+ });
2680
+ return maskitoEventHandler('blur', (element) => {
2681
+ const newValue = dropRepeatedLeadingZeroes({
2682
+ value: element.value,
2683
+ selection: DUMMY_SELECTION,
2684
+ }, { value: '', selection: DUMMY_SELECTION }).value;
2685
+ maskitoUpdateElement(element, newValue);
2686
+ }, { capture: true });
2687
+ }
2688
+
2689
+ /**
2690
+ * This plugin is connected with {@link createMinMaxPostprocessor}:
2691
+ * both validate `min`/`max` bounds of entered value (but at the different point of time).
2692
+ */
2693
+ function createMinMaxPlugin({ min, max, decimalSeparator, minusSign, }) {
2694
+ return maskitoEventHandler('blur', (element, options) => {
2695
+ const parsedNumber = maskitoParseNumber(element.value, {
2696
+ decimalSeparator,
2697
+ minusSign,
2698
+ });
2699
+ const clampedNumber = clamp(parsedNumber, min, max);
2700
+ if (!Number.isNaN(parsedNumber) && parsedNumber !== clampedNumber) {
2701
+ maskitoUpdateElement(element, maskitoTransform(stringifyNumberWithoutExp(clampedNumber), options));
2702
+ }
2703
+ }, { capture: true });
2704
+ }
2705
+
2706
+ /**
2707
+ * It pads EMPTY integer part with zero if decimal parts exists.
2708
+ * It works on blur event only!
2709
+ * @example 1|,23 => Backspace => Blur => 0,23
2710
+ */
2711
+ function createNotEmptyIntegerPlugin({ decimalSeparator, prefix, postfix, }) {
2712
+ if (!decimalSeparator) {
2713
+ return noop;
2714
+ }
2715
+ return maskitoEventHandler('blur', (element) => {
2716
+ const { cleanValue, extractedPostfix, extractedPrefix } = extractAffixes(element.value, { prefix, postfix });
2717
+ const newValue = extractedPrefix +
2718
+ cleanValue.replace(new RegExp(`^(\\D+)?${escapeRegExp(decimalSeparator)}`), `$10${decimalSeparator}`) +
2719
+ extractedPostfix;
2720
+ maskitoUpdateElement(element, newValue);
2721
+ }, { capture: true });
2722
+ }
2723
+
2724
+ const DEFAULT_PSEUDO_MINUSES = [
2725
+ CHAR_HYPHEN,
2726
+ CHAR_EN_DASH,
2727
+ CHAR_EM_DASH,
2728
+ CHAR_JP_HYPHEN,
2729
+ CHAR_MINUS,
2730
+ ];
2731
+ function maskitoNumberOptionsGenerator({ max = Number.MAX_SAFE_INTEGER, min = Number.MIN_SAFE_INTEGER, precision = 0, thousandSeparator = CHAR_NO_BREAK_SPACE, decimalSeparator = '.', decimalPseudoSeparators, decimalZeroPadding = false, prefix: unsafePrefix = '', postfix = '', minusSign = CHAR_MINUS, maximumFractionDigits = precision, minimumFractionDigits = decimalZeroPadding ? maximumFractionDigits : 0, } = {}) {
2732
+ const pseudoMinuses = DEFAULT_PSEUDO_MINUSES.filter((char) => char !== thousandSeparator && char !== decimalSeparator && char !== minusSign);
2733
+ const validatedDecimalPseudoSeparators = validateDecimalPseudoSeparators({
2734
+ decimalSeparator,
2735
+ thousandSeparator,
2736
+ decimalPseudoSeparators,
2737
+ });
2738
+ const prefix = unsafePrefix.endsWith(decimalSeparator) && maximumFractionDigits > 0
2739
+ ? `${unsafePrefix}${CHAR_ZERO_WIDTH_SPACE}`
2740
+ : unsafePrefix;
2741
+ const initializationOnlyPreprocessor = createInitializationOnlyPreprocessor({
2742
+ decimalSeparator,
2743
+ decimalPseudoSeparators: validatedDecimalPseudoSeparators,
2744
+ pseudoMinuses,
2745
+ prefix,
2746
+ postfix,
2747
+ minusSign,
2748
+ });
2749
+ decimalSeparator =
2750
+ maximumFractionDigits <= 0 && decimalSeparator === thousandSeparator
2751
+ ? ''
2752
+ : decimalSeparator;
2753
+ return Object.assign(Object.assign({}, MASKITO_DEFAULT_OPTIONS), { mask: generateMaskExpression({
2754
+ decimalSeparator,
2755
+ maximumFractionDigits,
2756
+ min,
2757
+ minusSign,
2758
+ postfix,
2759
+ prefix,
2760
+ pseudoMinuses,
2761
+ thousandSeparator,
2762
+ decimalPseudoSeparators: validatedDecimalPseudoSeparators,
2763
+ }), preprocessors: [
2764
+ createFullWidthToHalfWidthPreprocessor(),
2765
+ initializationOnlyPreprocessor,
2766
+ createAffixesFilterPreprocessor({ prefix, postfix }),
2767
+ createPseudoCharactersPreprocessor({
2768
+ validCharacter: minusSign,
2769
+ pseudoCharacters: pseudoMinuses,
2770
+ prefix,
2771
+ postfix,
2772
+ }),
2773
+ createPseudoCharactersPreprocessor({
2774
+ validCharacter: decimalSeparator,
2775
+ pseudoCharacters: validatedDecimalPseudoSeparators,
2776
+ prefix,
2777
+ postfix,
2778
+ }),
2779
+ createNotEmptyIntegerPartPreprocessor({
2780
+ decimalSeparator,
2781
+ maximumFractionDigits,
2782
+ prefix,
2783
+ postfix,
2784
+ }),
2785
+ createNonRemovableCharsDeletionPreprocessor({
2786
+ decimalSeparator,
2787
+ minimumFractionDigits,
2788
+ thousandSeparator,
2789
+ }),
2790
+ createZeroPrecisionPreprocessor({
2791
+ maximumFractionDigits,
2792
+ decimalSeparator,
2793
+ prefix,
2794
+ postfix,
2795
+ }),
2796
+ createRepeatedDecimalSeparatorPreprocessor({
2797
+ decimalSeparator,
2798
+ prefix,
2799
+ postfix,
2800
+ }),
2801
+ ], postprocessors: [
2802
+ createMinMaxPostprocessor({ decimalSeparator, min, max, minusSign }),
2803
+ maskitoPrefixPostprocessorGenerator(prefix),
2804
+ maskitoPostfixPostprocessorGenerator(postfix),
2805
+ createThousandSeparatorPostprocessor({
2806
+ decimalSeparator,
2807
+ thousandSeparator,
2808
+ prefix,
2809
+ postfix,
2810
+ minusSign,
2811
+ }),
2812
+ createDecimalZeroPaddingPostprocessor({
2813
+ decimalSeparator,
2814
+ prefix,
2815
+ postfix,
2816
+ minusSign,
2817
+ minimumFractionDigits: Math.min(minimumFractionDigits, maximumFractionDigits),
2818
+ }),
2819
+ emptyPostprocessor({
2820
+ prefix,
2821
+ postfix,
2822
+ decimalSeparator,
2823
+ minusSign,
2824
+ }),
2825
+ ], plugins: [
2826
+ createLeadingZeroesValidationPlugin({
2827
+ decimalSeparator,
2828
+ thousandSeparator,
2829
+ prefix,
2830
+ postfix,
2831
+ }),
2832
+ createNotEmptyIntegerPlugin({
2833
+ decimalSeparator,
2834
+ prefix,
2835
+ postfix,
2836
+ }),
2837
+ createMinMaxPlugin({ min, max, decimalSeparator, minusSign }),
2838
+ ], overwriteMode: minimumFractionDigits > 0
2839
+ ? ({ value, selection: [from] }) => from <= value.indexOf(decimalSeparator) ? 'shift' : 'replace'
2840
+ : 'shift' });
2841
+ }
2842
+
2111
2843
  /**
2112
2844
  * React adds `_valueTracker` property to every textfield elements for its internal logic with controlled inputs.
2113
2845
  * Also, React monkey-patches `value`-setter of the native textfield elements to update state inside its `_valueTracker`.
@@ -2221,15 +2953,5 @@ const useMaskito = ({
2221
2953
  return onRefChange;
2222
2954
  };
2223
2955
 
2224
- function usePopoverSupport() {
2225
- const [popoverSupported, setPopoverSupported] = useState(
2226
- void 0
2227
- );
2228
- useEffect(() => {
2229
- setPopoverSupported(supportsPopover());
2230
- }, []);
2231
- return popoverSupported;
2232
- }
2233
-
2234
- export { useMaskito as a, maskitoWithPlaceholder as b, maskitoParseDate as c, maskitoDateOptionsGenerator as d, maskitoTimeOptionsGenerator as e, maskitoDateRangeOptionsGenerator as m, usePopoverSupport as u };
2235
- //# sourceMappingURL=usePopoverSupport-B9Lsqryr-DhZHMoNb.js.map
2956
+ export { maskitoWithPlaceholder as a, maskitoParseDate as b, maskitoDateOptionsGenerator as c, maskitoTimeOptionsGenerator as d, maskitoNumberOptionsGenerator as e, maskitoParseNumber as f, maskitoStringifyNumber as g, maskitoDateRangeOptionsGenerator as m, useMaskito as u };
2957
+ //# sourceMappingURL=index.esm-dyoPQhi6.js.map