@jobber/components 9.6.0 → 9.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ActivityIndicator-cjs.js +1 -0
- package/dist/ActivityIndicator-es.js +1 -0
- package/dist/Autocomplete/index.cjs +2 -0
- package/dist/Autocomplete/index.mjs +2 -0
- package/dist/Autocomplete-cjs.js +6 -4
- package/dist/Autocomplete-es.js +3 -1
- package/dist/Banner-cjs.js +1 -0
- package/dist/Banner-es.js +1 -0
- package/dist/Card/index.cjs +2 -1
- package/dist/Card/index.mjs +2 -1
- package/dist/Chip/index.cjs +2 -0
- package/dist/Chip/index.mjs +2 -0
- package/dist/Chip-cjs.js +1 -0
- package/dist/Chip-es.js +1 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.cjs +2 -0
- package/dist/Chips/InternalChipDismissible/index.mjs +2 -0
- package/dist/Chips/index.cjs +2 -0
- package/dist/Chips/index.mjs +2 -0
- package/dist/Combobox/index.cjs +4 -2
- package/dist/Combobox/index.mjs +4 -2
- package/dist/{ComboboxSeparator-cjs.js → ComboboxChipRemove-cjs.js} +455 -501
- package/dist/{ComboboxSeparator-es.js → ComboboxChipRemove-es.js} +456 -500
- package/dist/ComboboxPrimitive-cjs.js +30 -29
- package/dist/ComboboxPrimitive-es.js +3 -2
- package/dist/ConfirmationModal/index.cjs +2 -0
- package/dist/ConfirmationModal/index.mjs +2 -0
- package/dist/ConfirmationModal-cjs.js +1 -0
- package/dist/ConfirmationModal-es.js +1 -0
- package/dist/DataDump/index.cjs +2 -1
- package/dist/DataDump/index.mjs +2 -1
- package/dist/DataDump-cjs.js +1 -0
- package/dist/DataDump-es.js +1 -0
- package/dist/DataList/components/DataListActions/index.cjs +2 -0
- package/dist/DataList/components/DataListActions/index.mjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.cjs +2 -0
- package/dist/DataList/components/DataListBulkActions/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +2 -0
- package/dist/DataList/components/DataListFilters/index.cjs +2 -0
- package/dist/DataList/components/DataListFilters/index.mjs +2 -0
- package/dist/DataList/components/DataListHeader/index.cjs +2 -0
- package/dist/DataList/components/DataListHeader/index.mjs +2 -0
- package/dist/DataList/components/DataListItem/index.cjs +2 -0
- package/dist/DataList/components/DataListItem/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActions/index.mjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +2 -0
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +2 -0
- package/dist/DataList/components/DataListItems/index.cjs +2 -0
- package/dist/DataList/components/DataListItems/index.mjs +2 -0
- package/dist/DataList/components/DataListLayout/index.cjs +2 -0
- package/dist/DataList/components/DataListLayout/index.mjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.cjs +2 -0
- package/dist/DataList/components/DataListLayoutActions/index.mjs +2 -0
- package/dist/DataList/index.cjs +2 -0
- package/dist/DataList/index.mjs +2 -0
- package/dist/DataTable-cjs.js +1 -0
- package/dist/DataTable-es.js +1 -0
- package/dist/DatePicker/index.cjs +2 -0
- package/dist/DatePicker/index.mjs +2 -0
- package/dist/DatePicker-cjs.js +8 -7
- package/dist/DatePicker-es.js +6 -5
- package/dist/Dialog/index.cjs +2 -1
- package/dist/Dialog/index.mjs +2 -1
- package/dist/DrawerDescription-cjs.js +99 -268
- package/dist/DrawerDescription-es.js +103 -270
- package/dist/DrawerGrid-cjs.js +1 -0
- package/dist/DrawerGrid-es.js +1 -0
- package/dist/DrawerTitle-cjs.js +6 -2
- package/dist/DrawerTitle-es.js +7 -3
- package/dist/FeatureSwitch-cjs.js +1 -0
- package/dist/FeatureSwitch-es.js +1 -0
- package/dist/FieldDescription-cjs.js +181 -184
- package/dist/FieldDescription-es.js +184 -187
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +2 -0
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +2 -0
- package/dist/FilterPicker/index.cjs +2 -0
- package/dist/FilterPicker/index.mjs +2 -0
- package/dist/FilterPickerContent-cjs.js +5 -4
- package/dist/FilterPickerContent-es.js +2 -1
- package/dist/FormFieldWrapper-cjs.js +1 -0
- package/dist/FormFieldWrapper-es.js +1 -0
- package/dist/FormatFile/index.cjs +2 -0
- package/dist/FormatFile/index.mjs +2 -0
- package/dist/FormatFile-cjs.js +1 -0
- package/dist/FormatFile-es.js +1 -0
- package/dist/Gallery/index.cjs +2 -0
- package/dist/Gallery/index.mjs +2 -0
- package/dist/InputAvatar-cjs.js +1 -0
- package/dist/InputAvatar-es.js +1 -0
- package/dist/InputDate/index.cjs +2 -0
- package/dist/InputDate/index.mjs +2 -0
- package/dist/InputDate-cjs.js +1 -0
- package/dist/InputDate-es.js +1 -0
- package/dist/InputNumber/index.cjs +2 -1
- package/dist/InputNumber/index.mjs +2 -1
- package/dist/InternalChipDismissible-cjs.js +1 -0
- package/dist/InternalChipDismissible-es.js +1 -0
- package/dist/LightBox/index.cjs +2 -0
- package/dist/LightBox/index.mjs +2 -0
- package/dist/LightBox-cjs.js +1 -0
- package/dist/LightBox-es.js +1 -0
- package/dist/Menu/index.cjs +2 -1
- package/dist/Menu/index.mjs +2 -1
- package/dist/Menu-cjs.js +6 -4
- package/dist/Menu-es.js +3 -1
- package/dist/MenuSubmenuTrigger-cjs.js +164 -206
- package/dist/MenuSubmenuTrigger-es.js +152 -194
- package/dist/Modal/index.cjs +3 -1
- package/dist/Modal/index.mjs +4 -2
- package/dist/NumberFieldInput-cjs.js +308 -187
- package/dist/NumberFieldInput-es.js +308 -187
- package/dist/Page/index.cjs +2 -1
- package/dist/Page/index.mjs +2 -1
- package/dist/Page-cjs.js +1 -0
- package/dist/Page-es.js +1 -0
- package/dist/Popover/index.cjs +2 -0
- package/dist/Popover/index.mjs +2 -0
- package/dist/Popover-cjs.js +9 -7
- package/dist/Popover-es.js +3 -1
- package/dist/ProgressBar-cjs.js +1 -0
- package/dist/ProgressBar-es.js +1 -0
- package/dist/ScrollAreaViewport-cjs.js +1093 -747
- package/dist/ScrollAreaViewport-es.js +1081 -734
- package/dist/Select/index.cjs +2 -1
- package/dist/Select/index.mjs +2 -1
- package/dist/SelectGroupLabel-cjs.js +306 -207
- package/dist/SelectGroupLabel-es.js +302 -203
- package/dist/SideDrawer-cjs.js +1 -0
- package/dist/SideDrawer-es.js +1 -0
- package/dist/Spinner-cjs.js +1 -0
- package/dist/Spinner-es.js +1 -0
- package/dist/Tooltip/index.cjs +2 -0
- package/dist/Tooltip/index.mjs +2 -0
- package/dist/Tooltip-cjs.js +7 -6
- package/dist/Tooltip-es.js +2 -1
- package/dist/dialogReturnFocus-cjs.js +1 -1
- package/dist/dialogReturnFocus-es.js +2 -2
- package/dist/floating-ui.react-cjs.js +192 -2564
- package/dist/floating-ui.react-dom-cjs.js +2103 -0
- package/dist/floating-ui.react-dom-es.js +2072 -0
- package/dist/floating-ui.react-es.js +156 -2519
- package/dist/floating-ui.utils-cjs.js +342 -0
- package/dist/floating-ui.utils-es.js +300 -0
- package/dist/index.cjs +4 -3
- package/dist/index.mjs +4 -3
- package/dist/primitives/BottomSheet/index.cjs +1 -1
- package/dist/primitives/BottomSheet/index.mjs +1 -1
- package/dist/primitives/ComboboxPrimitive/index.cjs +4 -2
- package/dist/primitives/ComboboxPrimitive/index.mjs +4 -2
- package/dist/primitives/SelectPrimitive/index.cjs +2 -0
- package/dist/primitives/SelectPrimitive/index.mjs +2 -0
- package/dist/primitives/index.cjs +3 -2
- package/dist/primitives/index.mjs +3 -2
- package/dist/scrollEdges-cjs.js +11 -1
- package/dist/scrollEdges-es.js +11 -1
- package/dist/sharedHelpers/i18n/index.cjs +1 -0
- package/dist/sharedHelpers/i18n/index.d.ts +1 -0
- package/dist/sharedHelpers/i18n/index.mjs +1 -1
- package/dist/sharedHelpers/i18n/releasedLanguages.d.ts +12 -0
- package/dist/sharedHelpers/i18n/useReleasedLocale.d.ts +6 -0
- package/dist/showToast-cjs.js +1 -1
- package/dist/showToast-es.js +1 -1
- package/dist/testUtils/releasedLanguages.d.ts +16 -0
- package/dist/unstyledPrimitives/index.cjs +478 -720
- package/dist/unstyledPrimitives/index.mjs +421 -663
- package/dist/useAtlantisI18n-cjs.js +44 -11
- package/dist/useAtlantisI18n-es.js +44 -12
- package/dist/useCompositeListItem-cjs.js +361 -2108
- package/dist/useCompositeListItem-es.js +331 -2076
- package/dist/useLabel-cjs.js +3 -2
- package/dist/useLabel-es.js +3 -2
- package/dist/useRenderElement-cjs.js +7 -9
- package/dist/useRenderElement-es.js +7 -9
- package/dist/useScrollLock-cjs.js +344 -819
- package/dist/useScrollLock-es.js +296 -732
- package/dist/useScrollToActive-cjs.js +5 -4
- package/dist/useScrollToActive-es.js +2 -1
- package/package.json +3 -3
- package/dist/scrollable-cjs.js +0 -58
- package/dist/scrollable-es.js +0 -54
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { n as useControlled, q as useValueAsRef, a as useIsoLayoutEffect, b as useStableCallback, r as ios, s as addEventListener, d as createChangeEventDetails, v as none, w as visuallyHiddenInput, x as visuallyHidden, y as inputChange, z as inputClear, A as inputBlur, B as inputPaste, D as activeElement, o as ownerDocument, E as createGenericEventDetails, G as wheel, H as useOnMount, T as Timeout, k as useTimeout, I as incrementPress, J as decrementPress, c as useButton, K as useValueChanged, L as stopEvent, M as keyboard, S as SafeReact } from './useScrollLock-es.js';
|
|
3
3
|
import { f as formatErrorMessage, b as useMergedRefs, u as useRenderElement, a as useRefWithInit, N as NOOP, w as warn } from './useRenderElement-es.js';
|
|
4
4
|
import { f as fieldValidityMapping, b as useFieldRootContext, a as useFormContext, c as useLabelableId, u as useLabelableContext, d as useRegisterFieldControl } from './useCompositeListItem-es.js';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
import { K as getWindow } from './floating-ui.utils-es.js';
|
|
6
7
|
import { s as stringifyLocale } from './stringifyLocale-es.js';
|
|
7
8
|
import { c as clamp } from './scrollEdges-es.js';
|
|
8
9
|
|
|
@@ -53,112 +54,121 @@ const stateAttributesMapping = {
|
|
|
53
54
|
...fieldValidityMapping
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
const HAN_NUMERALS = ['零', '〇', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
58
|
+
// Map Han numeral characters to ASCII digits. Includes both forms of zero.
|
|
59
|
+
const HAN_NUMERAL_TO_DIGIT = {
|
|
60
|
+
零: '0',
|
|
61
|
+
〇: '0',
|
|
62
|
+
一: '1',
|
|
63
|
+
二: '2',
|
|
64
|
+
三: '3',
|
|
65
|
+
四: '4',
|
|
66
|
+
五: '5',
|
|
67
|
+
六: '6',
|
|
68
|
+
七: '7',
|
|
69
|
+
八: '8',
|
|
70
|
+
九: '9'
|
|
71
|
+
};
|
|
72
|
+
const ARABIC_NUMERALS = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
|
|
73
|
+
const PERSIAN_NUMERALS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
|
74
|
+
const FULLWIDTH_NUMERALS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
65
75
|
const PERCENTAGES = ['%', '٪', '%', '﹪'];
|
|
66
76
|
const PERMILLE = ['‰', '؉'];
|
|
77
|
+
const UNICODE_MINUS_SIGNS = ['−', '-', '‒', '–', '—', '﹣'];
|
|
78
|
+
const UNICODE_PLUS_SIGNS = ['+', '﹢'];
|
|
67
79
|
|
|
68
80
|
// Fullwidth punctuation common in CJK inputs
|
|
69
81
|
const FULLWIDTH_DECIMAL = '.'; // U+FF0E
|
|
70
82
|
const FULLWIDTH_GROUP = ','; // U+FF0C
|
|
71
83
|
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const ARABIC_RE = new RegExp(`[${ARABIC_NUMERALS.join('')}]`, 'g');
|
|
85
|
+
const PERSIAN_RE = new RegExp(`[${PERSIAN_NUMERALS.join('')}]`, 'g');
|
|
86
|
+
const FULLWIDTH_RE = new RegExp(`[${FULLWIDTH_NUMERALS.join('')}]`, 'g');
|
|
87
|
+
const HAN_RE = new RegExp(`[${HAN_NUMERALS.join('')}]`, 'g');
|
|
88
|
+
const PERCENT_RE = new RegExp(`[${PERCENTAGES.join('')}]`);
|
|
89
|
+
const PERMILLE_RE = new RegExp(`[${PERMILLE.join('')}]`);
|
|
90
|
+
const PERCENT_GLOBAL_RE = new RegExp(PERCENT_RE.source, 'g');
|
|
91
|
+
const PERMILLE_GLOBAL_RE = new RegExp(PERMILLE_RE.source, 'g');
|
|
92
|
+
|
|
93
|
+
// Detection regexes (non-global to avoid lastIndex side effects), derived from the numeral arrays
|
|
94
|
+
// so they can't drift out of sync.
|
|
95
|
+
const ARABIC_DETECT_RE = new RegExp(`[${ARABIC_NUMERALS.join('')}]`);
|
|
96
|
+
const PERSIAN_DETECT_RE = new RegExp(`[${PERSIAN_NUMERALS.join('')}]`);
|
|
97
|
+
const HAN_DETECT_RE = new RegExp(`[${HAN_NUMERALS.join('')}]`);
|
|
98
|
+
const FULLWIDTH_DETECT_RE = new RegExp(`[${FULLWIDTH_NUMERALS.join('')}]`);
|
|
82
99
|
|
|
83
100
|
// Whether the character is a digit in any numeral system the field accepts.
|
|
84
101
|
function isNumeralChar(char) {
|
|
85
|
-
return
|
|
102
|
+
return char >= '0' && char <= '9' || ARABIC_DETECT_RE.test(char) || PERSIAN_DETECT_RE.test(char) || HAN_DETECT_RE.test(char) || FULLWIDTH_DETECT_RE.test(char);
|
|
86
103
|
}
|
|
87
104
|
const BASE_NON_NUMERIC_SYMBOLS = ['.', ',', FULLWIDTH_DECIMAL, FULLWIDTH_GROUP, '٫', '٬'];
|
|
88
105
|
const SPACE_SEPARATOR_RE = /\p{Zs}/u;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// rather than rejecting the typed string. Non-global so it's safe for repeated `.test(char)`.
|
|
92
|
-
const FORMAT_CONTROL_DETECT_RE = /\p{Cf}/u;
|
|
93
|
-
const FORMAT_CONTROL_GLOBAL_RE = /\p{Cf}/gu;
|
|
94
|
-
const PLUS_SIGNS_WITH_ASCII = ['+', '+', '﹢'];
|
|
95
|
-
const MINUS_SIGNS_WITH_ASCII = ['-', '−', '-', '‒', '–', '—', '﹣'];
|
|
106
|
+
const PLUS_SIGNS_WITH_ASCII = ['+', ...UNICODE_PLUS_SIGNS];
|
|
107
|
+
const MINUS_SIGNS_WITH_ASCII = ['-', ...UNICODE_MINUS_SIGNS];
|
|
96
108
|
const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
109
|
+
const escapeClassChar = s => s.replace(/[-\\\]^]/g, m => `\\${m}`); // escape for use inside [...]
|
|
110
|
+
|
|
97
111
|
function shiftDecimal(value, exponentDelta) {
|
|
98
112
|
const [coefficient, exponent = '0'] = String(value).split('e');
|
|
99
113
|
return Number(`${coefficient}e${Number(exponent) + exponentDelta}`);
|
|
100
114
|
}
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// so the locale-detail and allowed-character derivations enumerate the same parts.
|
|
109
|
-
const SAMPLE_FORMAT_NUMBER = 11111.1;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Returns the `Intl.NumberFormat` parts of a representative number, which surface every
|
|
113
|
-
* non-numeric symbol a given locale/format renders.
|
|
114
|
-
*/
|
|
115
|
-
function getFormatParts(locale, options) {
|
|
116
|
-
return getFormatter(locale, options).formatToParts(SAMPLE_FORMAT_NUMBER);
|
|
117
|
-
}
|
|
115
|
+
const charClassFrom = chars => `[${chars.map(escapeClassChar).join('')}]`;
|
|
116
|
+
const ANY_MINUS_CLASS = charClassFrom(['-'].concat(UNICODE_MINUS_SIGNS));
|
|
117
|
+
const ANY_PLUS_CLASS = charClassFrom(['+'].concat(UNICODE_PLUS_SIGNS));
|
|
118
|
+
const ANY_MINUS_RE = new RegExp(ANY_MINUS_CLASS, 'gu');
|
|
119
|
+
const ANY_PLUS_RE = new RegExp(ANY_PLUS_CLASS, 'gu');
|
|
120
|
+
const ANY_MINUS_DETECT_RE = new RegExp(ANY_MINUS_CLASS);
|
|
121
|
+
const ANY_PLUS_DETECT_RE = new RegExp(ANY_PLUS_CLASS);
|
|
118
122
|
function getNumberLocaleDetails(locale, options) {
|
|
119
|
-
const parts =
|
|
123
|
+
const parts = getFormatter(locale, options).formatToParts(11111.1);
|
|
120
124
|
const result = {};
|
|
121
125
|
parts.forEach(part => {
|
|
122
126
|
result[part.type] = part.value;
|
|
123
127
|
});
|
|
124
128
|
|
|
125
129
|
// The formatting options may omit the decimal separator (e.g. integer formats), so resolve it
|
|
126
|
-
// from the plain locale formatter
|
|
127
|
-
//
|
|
128
|
-
// locale-determined and identical across format styles. The ASCII seed is a formality to keep
|
|
129
|
-
// the separator non-optional for callers; it is always replaced below.
|
|
130
|
-
let decimal = '.';
|
|
130
|
+
// from the plain locale formatter. This overrides any options-derived decimal too, which is
|
|
131
|
+
// safe because the separator is locale-determined and identical across format styles.
|
|
131
132
|
getFormatter(locale).formatToParts(0.1).forEach(part => {
|
|
132
133
|
if (part.type === 'decimal') {
|
|
133
|
-
|
|
134
|
+
result[part.type] = part.value;
|
|
134
135
|
}
|
|
135
136
|
});
|
|
136
|
-
return
|
|
137
|
-
...result,
|
|
138
|
-
decimal
|
|
139
|
-
};
|
|
137
|
+
return result;
|
|
140
138
|
}
|
|
141
139
|
function parseNumber(formattedNumber, locale, options) {
|
|
140
|
+
if (formattedNumber == null) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
|
|
142
144
|
// Normalize control characters and whitespace; remove bidi/format controls
|
|
143
|
-
let input = formattedNumber.replace(
|
|
145
|
+
let input = String(formattedNumber).replace(/\p{Cf}/gu, '').trim();
|
|
144
146
|
|
|
145
147
|
// Normalize unicode minus/plus to ASCII, handle leading/trailing signs
|
|
146
148
|
input = input.replace(ANY_MINUS_RE, '-').replace(ANY_PLUS_RE, '+');
|
|
147
149
|
let isNegative = false;
|
|
148
150
|
|
|
149
|
-
//
|
|
150
|
-
const
|
|
151
|
-
|
|
151
|
+
// Trailing sign, e.g. "1234-" / "1234+"
|
|
152
|
+
const trailing = input.match(/([+-])\s*$/);
|
|
153
|
+
if (trailing) {
|
|
154
|
+
if (trailing[1] === '-') {
|
|
152
155
|
isNegative = true;
|
|
153
156
|
}
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
+
input = input.replace(/([+-])\s*$/, '');
|
|
158
|
+
}
|
|
159
|
+
// Leading sign
|
|
160
|
+
const leading = input.match(/^\s*([+-])/);
|
|
161
|
+
if (leading) {
|
|
162
|
+
if (leading[1] === '-') {
|
|
163
|
+
isNegative = true;
|
|
164
|
+
}
|
|
165
|
+
input = input.replace(/^\s*[+-]/, '');
|
|
166
|
+
}
|
|
157
167
|
|
|
158
168
|
// Heuristic locale detection
|
|
159
169
|
let computedLocale = locale;
|
|
160
170
|
if (computedLocale === undefined) {
|
|
161
|
-
if (
|
|
171
|
+
if (ARABIC_DETECT_RE.test(input) || PERSIAN_DETECT_RE.test(input)) {
|
|
162
172
|
computedLocale = 'ar';
|
|
163
173
|
} else if (HAN_DETECT_RE.test(input)) {
|
|
164
174
|
computedLocale = 'zh';
|
|
@@ -190,17 +200,70 @@ function parseNumber(formattedNumber, locale, options) {
|
|
|
190
200
|
groupRegex = new RegExp(escapeRegExp(group), 'g');
|
|
191
201
|
}
|
|
192
202
|
}
|
|
193
|
-
const replacements = [
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
203
|
+
const replacements = [{
|
|
204
|
+
regex: groupRegex,
|
|
205
|
+
replacement: ''
|
|
206
|
+
}, {
|
|
207
|
+
regex: decimal ? new RegExp(escapeRegExp(decimal), 'g') : null,
|
|
208
|
+
replacement: '.'
|
|
209
|
+
},
|
|
210
|
+
// Fullwidth punctuation
|
|
211
|
+
{
|
|
212
|
+
regex: /./g,
|
|
213
|
+
replacement: '.'
|
|
214
|
+
},
|
|
215
|
+
// FULLWIDTH_DECIMAL
|
|
216
|
+
{
|
|
217
|
+
regex: /,/g,
|
|
218
|
+
replacement: ''
|
|
219
|
+
},
|
|
220
|
+
// FULLWIDTH_GROUP
|
|
221
|
+
// Arabic punctuation
|
|
222
|
+
{
|
|
223
|
+
regex: /٫/g,
|
|
224
|
+
replacement: '.'
|
|
225
|
+
},
|
|
226
|
+
// ARABIC DECIMAL SEPARATOR (U+066B)
|
|
227
|
+
{
|
|
228
|
+
regex: /٬/g,
|
|
229
|
+
replacement: ''
|
|
230
|
+
},
|
|
231
|
+
// ARABIC THOUSANDS SEPARATOR (U+066C)
|
|
199
232
|
// Currency & unit labels
|
|
200
|
-
|
|
233
|
+
{
|
|
234
|
+
regex: currency ? new RegExp(escapeRegExp(currency), 'g') : null,
|
|
235
|
+
replacement: ''
|
|
236
|
+
}, {
|
|
237
|
+
regex: unitRegex,
|
|
238
|
+
replacement: ''
|
|
239
|
+
}, {
|
|
240
|
+
regex: PERCENT_GLOBAL_RE,
|
|
241
|
+
replacement: ''
|
|
242
|
+
}, {
|
|
243
|
+
regex: PERMILLE_GLOBAL_RE,
|
|
244
|
+
replacement: ''
|
|
245
|
+
}, {
|
|
246
|
+
regex: exponentSeparator ? new RegExp(escapeRegExp(exponentSeparator), 'g') : null,
|
|
247
|
+
replacement: 'e'
|
|
248
|
+
},
|
|
201
249
|
// Numeral systems to ASCII digits
|
|
202
|
-
|
|
203
|
-
|
|
250
|
+
{
|
|
251
|
+
regex: ARABIC_RE,
|
|
252
|
+
replacement: ch => String(ARABIC_NUMERALS.indexOf(ch))
|
|
253
|
+
}, {
|
|
254
|
+
regex: PERSIAN_RE,
|
|
255
|
+
replacement: ch => String(PERSIAN_NUMERALS.indexOf(ch))
|
|
256
|
+
}, {
|
|
257
|
+
regex: FULLWIDTH_RE,
|
|
258
|
+
replacement: ch => String(FULLWIDTH_NUMERALS.indexOf(ch))
|
|
259
|
+
}, {
|
|
260
|
+
regex: HAN_RE,
|
|
261
|
+
replacement: ch => HAN_NUMERAL_TO_DIGIT[ch]
|
|
262
|
+
}];
|
|
263
|
+
let unformatted = replacements.reduce((acc, {
|
|
264
|
+
regex,
|
|
265
|
+
replacement
|
|
266
|
+
}) => {
|
|
204
267
|
return regex ? acc.replace(regex, replacement) : acc;
|
|
205
268
|
}, input);
|
|
206
269
|
|
|
@@ -278,18 +341,27 @@ function removeFloatingPointErrors(value, format) {
|
|
|
278
341
|
}
|
|
279
342
|
return formatter.format(roundedValue) === roundedText ? roundedValue : value;
|
|
280
343
|
}
|
|
281
|
-
function snapToStep(value, base, step,
|
|
344
|
+
function snapToStep(value, base, step, mode = 'directional') {
|
|
282
345
|
const stepSize = Math.abs(step);
|
|
283
346
|
const direction = Math.sign(step);
|
|
284
347
|
const tolerance = stepSize * STEP_EPSILON_FACTOR * direction;
|
|
285
348
|
const rawSteps = value - base + tolerance;
|
|
286
|
-
if (nearest) {
|
|
349
|
+
if (mode === 'nearest') {
|
|
287
350
|
return base + Math.round(rawSteps / step) * step;
|
|
288
351
|
}
|
|
289
352
|
const snappedSteps = direction > 0 ? Math.floor(rawSteps / stepSize) : Math.ceil(rawSteps / stepSize);
|
|
290
353
|
return base + snappedSteps * stepSize;
|
|
291
354
|
}
|
|
292
|
-
function toValidatedNumber(value,
|
|
355
|
+
function toValidatedNumber(value, {
|
|
356
|
+
step,
|
|
357
|
+
minWithDefault,
|
|
358
|
+
maxWithDefault,
|
|
359
|
+
minWithZeroDefault,
|
|
360
|
+
format,
|
|
361
|
+
snapOnStep,
|
|
362
|
+
small,
|
|
363
|
+
clamp: shouldClamp
|
|
364
|
+
}) {
|
|
293
365
|
if (value === null) {
|
|
294
366
|
return value;
|
|
295
367
|
}
|
|
@@ -297,9 +369,8 @@ function toValidatedNumber(value, step, minWithDefault, maxWithDefault, minWithZ
|
|
|
297
369
|
if (step != null && snapOnStep && step !== 0) {
|
|
298
370
|
const base = small || minWithDefault === Number.MIN_SAFE_INTEGER ? minWithZeroDefault : minWithDefault;
|
|
299
371
|
|
|
300
|
-
// Snap before clamping so non-step-aligned boundaries stay reachable.
|
|
301
|
-
|
|
302
|
-
nextValue = snapToStep(nextValue, base, step, small);
|
|
372
|
+
// Snap before clamping so non-step-aligned boundaries stay reachable.
|
|
373
|
+
nextValue = snapToStep(nextValue, base, step, small ? 'nearest' : 'directional');
|
|
303
374
|
}
|
|
304
375
|
|
|
305
376
|
// Clamp before rounding so a value just outside a fractional boundary (e.g. `0.4` with
|
|
@@ -354,6 +425,7 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
354
425
|
validityData,
|
|
355
426
|
disabled: fieldDisabled,
|
|
356
427
|
setFilled,
|
|
428
|
+
invalid,
|
|
357
429
|
name: fieldName,
|
|
358
430
|
state: fieldState,
|
|
359
431
|
validation
|
|
@@ -402,46 +474,48 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
402
474
|
const [inputValue, setInputValue] = React.useState(() => formatNumber(value, locale, format));
|
|
403
475
|
const [inputMode, setInputMode] = React.useState('numeric');
|
|
404
476
|
const getAllowedNonNumericKeys = useStableCallback(() => {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
keys.add(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// them, so allowing them would yield a silently incorrect value.
|
|
420
|
-
parts.forEach(part => {
|
|
421
|
-
if (part.type === 'integer' || part.type === 'fraction' || part.type === 'exponentInteger' || part.type === 'compact') {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
addAll(Array.from(part.value));
|
|
425
|
-
if (SPACE_SEPARATOR_RE.test(part.value)) {
|
|
477
|
+
const {
|
|
478
|
+
decimal,
|
|
479
|
+
group,
|
|
480
|
+
currency,
|
|
481
|
+
literal
|
|
482
|
+
} = getNumberLocaleDetails(locale, format);
|
|
483
|
+
const keys = new Set();
|
|
484
|
+
BASE_NON_NUMERIC_SYMBOLS.forEach(symbol => keys.add(symbol));
|
|
485
|
+
if (decimal) {
|
|
486
|
+
keys.add(decimal);
|
|
487
|
+
}
|
|
488
|
+
if (group) {
|
|
489
|
+
keys.add(group);
|
|
490
|
+
if (SPACE_SEPARATOR_RE.test(group)) {
|
|
426
491
|
keys.add(' ');
|
|
427
492
|
}
|
|
428
|
-
}
|
|
493
|
+
}
|
|
429
494
|
const allowPercentSymbols = formatStyle === 'percent' || formatStyle === 'unit' && format?.unit === 'percent';
|
|
430
495
|
const allowPermilleSymbols = formatStyle === 'percent' || formatStyle === 'unit' && format?.unit === 'permille';
|
|
431
|
-
|
|
432
|
-
// Tolerate percent/permille variants the formatter doesn't emit but users may type or paste.
|
|
433
496
|
if (allowPercentSymbols) {
|
|
434
|
-
|
|
497
|
+
PERCENTAGES.forEach(key => keys.add(key));
|
|
435
498
|
}
|
|
436
499
|
if (allowPermilleSymbols) {
|
|
437
|
-
|
|
500
|
+
PERMILLE.forEach(key => keys.add(key));
|
|
501
|
+
}
|
|
502
|
+
if (formatStyle === 'currency' && currency) {
|
|
503
|
+
keys.add(currency);
|
|
504
|
+
}
|
|
505
|
+
if (literal) {
|
|
506
|
+
// Some locales (e.g. de-DE) insert a literal space character between the number
|
|
507
|
+
// and the symbol, so allow those characters to be typed/removed.
|
|
508
|
+
Array.from(literal).forEach(char => keys.add(char));
|
|
509
|
+
if (SPACE_SEPARATOR_RE.test(literal)) {
|
|
510
|
+
keys.add(' ');
|
|
511
|
+
}
|
|
438
512
|
}
|
|
439
513
|
|
|
440
514
|
// Allow plus sign in all cases; minus sign when negatives are valid, or when out-of-range
|
|
441
515
|
// entry is allowed so native underflow validation can be triggered from the keyboard.
|
|
442
|
-
|
|
516
|
+
PLUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key));
|
|
443
517
|
if (minWithDefault < 0 || allowOutOfRange) {
|
|
444
|
-
|
|
518
|
+
MINUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key));
|
|
445
519
|
}
|
|
446
520
|
return keys;
|
|
447
521
|
});
|
|
@@ -459,13 +533,21 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
459
533
|
const dir = details.direction;
|
|
460
534
|
|
|
461
535
|
// Direct text entry (typing, pasting, clearing, autofill) behaves natively; step-based
|
|
462
|
-
// interactions (keyboard arrows, buttons, wheel, scrub) do not.
|
|
463
|
-
|
|
464
|
-
const isInputReason = details.reason.startsWith('input-') || details.reason === none;
|
|
536
|
+
// interactions (keyboard arrows, buttons, wheel, scrub) do not.
|
|
537
|
+
const isInputReason = details.reason === inputChange || details.reason === inputClear || details.reason === inputBlur || details.reason === inputPaste || details.reason === none;
|
|
465
538
|
|
|
466
539
|
// Only allow out-of-range values for direct text entry. Step-based interactions still clamp.
|
|
467
540
|
const shouldClampValue = !allowOutOfRange || !isInputReason;
|
|
468
|
-
const validatedValue = toValidatedNumber(unvalidatedValue,
|
|
541
|
+
const validatedValue = toValidatedNumber(unvalidatedValue, {
|
|
542
|
+
step: dir ? getStepAmount(eventWithOptionalKeyState) * dir : undefined,
|
|
543
|
+
format: formatOptionsRef.current,
|
|
544
|
+
minWithDefault,
|
|
545
|
+
maxWithDefault,
|
|
546
|
+
minWithZeroDefault,
|
|
547
|
+
snapOnStep,
|
|
548
|
+
small: eventWithOptionalKeyState?.altKey ?? false,
|
|
549
|
+
clamp: shouldClampValue
|
|
550
|
+
});
|
|
469
551
|
|
|
470
552
|
// Notify about a change even when the numeric value is unchanged for input reasons: the
|
|
471
553
|
// typed text may clamp/snap to the current value, or differ while validation normalizes
|
|
@@ -575,10 +657,10 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
575
657
|
const changed = incrementValue(amount, {
|
|
576
658
|
direction: event.deltaY > 0 ? -1 : 1,
|
|
577
659
|
event,
|
|
578
|
-
reason: wheel
|
|
660
|
+
reason: 'wheel'
|
|
579
661
|
});
|
|
580
662
|
if (changed) {
|
|
581
|
-
onValueCommitted(lastChangedValueRef.current, createGenericEventDetails(wheel, event));
|
|
663
|
+
onValueCommitted(lastChangedValueRef.current ?? valueRef.current, createGenericEventDetails(wheel, event));
|
|
582
664
|
}
|
|
583
665
|
}
|
|
584
666
|
return addEventListener(element, 'wheel', handleWheel);
|
|
@@ -594,8 +676,12 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
594
676
|
}), [fieldState, disabled, readOnly, required, value, inputValue, isScrubbing]);
|
|
595
677
|
const contextValue = React.useMemo(() => ({
|
|
596
678
|
inputRef,
|
|
679
|
+
inputValue,
|
|
680
|
+
value,
|
|
597
681
|
minWithDefault,
|
|
598
682
|
maxWithDefault,
|
|
683
|
+
disabled,
|
|
684
|
+
readOnly,
|
|
599
685
|
id,
|
|
600
686
|
setValue,
|
|
601
687
|
incrementValue,
|
|
@@ -607,16 +693,19 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
607
693
|
hasPendingCommitRef,
|
|
608
694
|
name,
|
|
609
695
|
nameProp,
|
|
696
|
+
required,
|
|
697
|
+
invalid,
|
|
610
698
|
inputMode,
|
|
611
699
|
getAllowedNonNumericKeys,
|
|
612
700
|
min,
|
|
613
701
|
max,
|
|
614
702
|
setInputValue,
|
|
615
703
|
locale,
|
|
704
|
+
isScrubbing,
|
|
616
705
|
setIsScrubbing,
|
|
617
706
|
state,
|
|
618
707
|
onValueCommitted
|
|
619
|
-
}), [inputRef, minWithDefault, maxWithDefault, id, setValue, incrementValue, getStepAmount, formatOptionsRef, valueRef, name, nameProp, inputMode, getAllowedNonNumericKeys, min, max, setInputValue, locale, state, onValueCommitted]);
|
|
708
|
+
}), [inputRef, inputValue, value, minWithDefault, maxWithDefault, disabled, readOnly, id, setValue, incrementValue, getStepAmount, formatOptionsRef, valueRef, name, nameProp, required, invalid, inputMode, getAllowedNonNumericKeys, min, max, setInputValue, locale, isScrubbing, state, onValueCommitted]);
|
|
620
709
|
const element = useRenderElement('div', componentProps, {
|
|
621
710
|
ref: forwardedRef,
|
|
622
711
|
state,
|
|
@@ -631,7 +720,7 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
631
720
|
inputRef.current?.focus();
|
|
632
721
|
},
|
|
633
722
|
onChange(event) {
|
|
634
|
-
// Workaround for https://github.com/
|
|
723
|
+
// Workaround for https://github.com/facebook/react/issues/9023
|
|
635
724
|
if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
|
|
636
725
|
return;
|
|
637
726
|
}
|
|
@@ -656,7 +745,7 @@ const NumberFieldRoot = /*#__PURE__*/React.forwardRef(function NumberFieldRoot(c
|
|
|
656
745
|
min: min,
|
|
657
746
|
max: max
|
|
658
747
|
// stepMismatch validation is broken unless an explicit `min` is added.
|
|
659
|
-
// See https://github.com/
|
|
748
|
+
// See https://github.com/facebook/react/issues/12334.
|
|
660
749
|
,
|
|
661
750
|
step: stepProp,
|
|
662
751
|
disabled: disabled,
|
|
@@ -753,6 +842,7 @@ function isTouchLikePointerType(pointerType) {
|
|
|
753
842
|
function usePressAndHold(params) {
|
|
754
843
|
const {
|
|
755
844
|
disabled,
|
|
845
|
+
readOnly = false,
|
|
756
846
|
tick,
|
|
757
847
|
onStop,
|
|
758
848
|
tickDelay = DEFAULT_TICK_DELAY,
|
|
@@ -832,7 +922,8 @@ function usePressAndHold(params) {
|
|
|
832
922
|
isTouchingButtonRef.current = false;
|
|
833
923
|
},
|
|
834
924
|
onPointerDown(event) {
|
|
835
|
-
|
|
925
|
+
const isMainButton = !event.button || event.button === 0;
|
|
926
|
+
if (event.defaultPrevented || !isMainButton || disabled || readOnly) {
|
|
836
927
|
return;
|
|
837
928
|
}
|
|
838
929
|
pointerTypeRef.current = event.pointerType;
|
|
@@ -875,10 +966,12 @@ function usePressAndHold(params) {
|
|
|
875
966
|
}
|
|
876
967
|
},
|
|
877
968
|
onPointerMove(event) {
|
|
878
|
-
if (disabled || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
|
|
969
|
+
if (disabled || readOnly || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
|
|
879
970
|
return;
|
|
880
971
|
}
|
|
881
|
-
movesAfterTouchRef.current
|
|
972
|
+
if (movesAfterTouchRef.current != null) {
|
|
973
|
+
movesAfterTouchRef.current += 1;
|
|
974
|
+
}
|
|
882
975
|
const {
|
|
883
976
|
x,
|
|
884
977
|
y
|
|
@@ -890,7 +983,7 @@ function usePressAndHold(params) {
|
|
|
890
983
|
}
|
|
891
984
|
},
|
|
892
985
|
onMouseEnter(event) {
|
|
893
|
-
if (event.defaultPrevented || disabled || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
|
|
986
|
+
if (event.defaultPrevented || disabled || readOnly || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
|
|
894
987
|
return;
|
|
895
988
|
}
|
|
896
989
|
startAutoChange(event.nativeEvent);
|
|
@@ -923,47 +1016,32 @@ function usePressAndHold(params) {
|
|
|
923
1016
|
};
|
|
924
1017
|
}
|
|
925
1018
|
|
|
1019
|
+
const CHANGE_VALUE_TICK_DELAY = 60;
|
|
1020
|
+
const START_AUTO_CHANGE_DELAY = 400;
|
|
1021
|
+
const SCROLLING_POINTER_MOVE_DISTANCE = 8;
|
|
1022
|
+
|
|
926
1023
|
const SELECT_NONE_STYLE = {
|
|
927
1024
|
WebkitUserSelect: 'none',
|
|
928
1025
|
userSelect: 'none'
|
|
929
1026
|
};
|
|
930
|
-
|
|
931
|
-
* Shared implementation for the increment and decrement stepper buttons. They differ only in the
|
|
932
|
-
* direction they step and the boundary (`max` vs `min`) at which they become disabled.
|
|
933
|
-
*/
|
|
934
|
-
function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement) {
|
|
935
|
-
const {
|
|
936
|
-
render,
|
|
937
|
-
className,
|
|
938
|
-
disabled: disabledProp = false,
|
|
939
|
-
nativeButton = true,
|
|
940
|
-
style,
|
|
941
|
-
...elementProps
|
|
942
|
-
} = componentProps;
|
|
1027
|
+
function useNumberFieldButton(params) {
|
|
943
1028
|
const {
|
|
944
1029
|
allowInputSyncRef,
|
|
1030
|
+
disabled,
|
|
945
1031
|
formatOptionsRef,
|
|
946
1032
|
getStepAmount,
|
|
947
1033
|
id,
|
|
948
1034
|
incrementValue,
|
|
949
1035
|
inputRef,
|
|
950
|
-
|
|
951
|
-
|
|
1036
|
+
inputValue,
|
|
1037
|
+
isIncrement,
|
|
1038
|
+
locale,
|
|
1039
|
+
readOnly,
|
|
952
1040
|
setValue,
|
|
953
|
-
state,
|
|
954
1041
|
valueRef,
|
|
955
|
-
locale,
|
|
956
1042
|
lastChangedValueRef,
|
|
957
1043
|
onValueCommitted
|
|
958
|
-
} =
|
|
959
|
-
const {
|
|
960
|
-
disabled: contextDisabled,
|
|
961
|
-
readOnly,
|
|
962
|
-
value,
|
|
963
|
-
inputValue
|
|
964
|
-
} = state;
|
|
965
|
-
const isAtBoundary = value != null && (isIncrement ? value >= maxWithDefault : value <= minWithDefault);
|
|
966
|
-
const disabled = disabledProp || contextDisabled || isAtBoundary;
|
|
1044
|
+
} = params;
|
|
967
1045
|
const pressReason = isIncrement ? incrementPress : decrementPress;
|
|
968
1046
|
function commitValue(nativeEvent) {
|
|
969
1047
|
const shouldCommitInputValue = !allowInputSyncRef.current;
|
|
@@ -999,6 +1077,9 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
999
1077
|
} = usePressAndHold({
|
|
1000
1078
|
disabled: disabled || readOnly,
|
|
1001
1079
|
elementRef: inputRef,
|
|
1080
|
+
tickDelay: CHANGE_VALUE_TICK_DELAY,
|
|
1081
|
+
startDelay: START_AUTO_CHANGE_DELAY,
|
|
1082
|
+
scrollDistance: SCROLLING_POINTER_MOVE_DISTANCE,
|
|
1002
1083
|
tick(triggerEvent) {
|
|
1003
1084
|
const amount = getStepAmount(triggerEvent);
|
|
1004
1085
|
return incrementValue(amount, {
|
|
@@ -1043,7 +1124,8 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
1043
1124
|
}
|
|
1044
1125
|
},
|
|
1045
1126
|
onPointerDown(event) {
|
|
1046
|
-
|
|
1127
|
+
const isMainButton = !event.button || event.button === 0;
|
|
1128
|
+
if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
|
|
1047
1129
|
return;
|
|
1048
1130
|
}
|
|
1049
1131
|
|
|
@@ -1059,6 +1141,61 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
1059
1141
|
pointerHandlers.onPointerDown(event);
|
|
1060
1142
|
}
|
|
1061
1143
|
};
|
|
1144
|
+
return props;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Shared implementation for the increment and decrement stepper buttons. They differ only in the
|
|
1149
|
+
* direction they step and the boundary (`max` vs `min`) at which they become disabled.
|
|
1150
|
+
*/
|
|
1151
|
+
function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement) {
|
|
1152
|
+
const {
|
|
1153
|
+
render,
|
|
1154
|
+
className,
|
|
1155
|
+
disabled: disabledProp = false,
|
|
1156
|
+
nativeButton = true,
|
|
1157
|
+
style,
|
|
1158
|
+
...elementProps
|
|
1159
|
+
} = componentProps;
|
|
1160
|
+
const {
|
|
1161
|
+
allowInputSyncRef,
|
|
1162
|
+
disabled: contextDisabled,
|
|
1163
|
+
formatOptionsRef,
|
|
1164
|
+
getStepAmount,
|
|
1165
|
+
id,
|
|
1166
|
+
incrementValue,
|
|
1167
|
+
inputRef,
|
|
1168
|
+
inputValue,
|
|
1169
|
+
maxWithDefault,
|
|
1170
|
+
minWithDefault,
|
|
1171
|
+
readOnly,
|
|
1172
|
+
setValue,
|
|
1173
|
+
state,
|
|
1174
|
+
value,
|
|
1175
|
+
valueRef,
|
|
1176
|
+
locale,
|
|
1177
|
+
lastChangedValueRef,
|
|
1178
|
+
onValueCommitted
|
|
1179
|
+
} = useNumberFieldRootContext();
|
|
1180
|
+
const isAtBoundary = value != null && (isIncrement ? value >= maxWithDefault : value <= minWithDefault);
|
|
1181
|
+
const disabled = disabledProp || contextDisabled || isAtBoundary;
|
|
1182
|
+
const props = useNumberFieldButton({
|
|
1183
|
+
isIncrement,
|
|
1184
|
+
inputRef,
|
|
1185
|
+
inputValue,
|
|
1186
|
+
disabled,
|
|
1187
|
+
readOnly,
|
|
1188
|
+
id,
|
|
1189
|
+
setValue,
|
|
1190
|
+
getStepAmount,
|
|
1191
|
+
incrementValue,
|
|
1192
|
+
allowInputSyncRef,
|
|
1193
|
+
formatOptionsRef,
|
|
1194
|
+
valueRef,
|
|
1195
|
+
locale,
|
|
1196
|
+
lastChangedValueRef,
|
|
1197
|
+
onValueCommitted
|
|
1198
|
+
});
|
|
1062
1199
|
const {
|
|
1063
1200
|
getButtonProps,
|
|
1064
1201
|
buttonRef
|
|
@@ -1121,33 +1258,31 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1121
1258
|
} = componentProps;
|
|
1122
1259
|
const {
|
|
1123
1260
|
allowInputSyncRef,
|
|
1261
|
+
disabled,
|
|
1124
1262
|
formatOptionsRef,
|
|
1125
1263
|
getAllowedNonNumericKeys,
|
|
1126
1264
|
getStepAmount,
|
|
1127
1265
|
id,
|
|
1128
1266
|
incrementValue,
|
|
1129
1267
|
inputMode,
|
|
1268
|
+
inputValue,
|
|
1130
1269
|
max,
|
|
1131
1270
|
min,
|
|
1132
1271
|
name,
|
|
1133
1272
|
nameProp,
|
|
1273
|
+
readOnly,
|
|
1274
|
+
required,
|
|
1134
1275
|
setValue,
|
|
1135
1276
|
state,
|
|
1136
1277
|
setInputValue,
|
|
1137
1278
|
locale,
|
|
1138
1279
|
inputRef,
|
|
1280
|
+
value,
|
|
1139
1281
|
onValueCommitted,
|
|
1140
1282
|
lastChangedValueRef,
|
|
1141
1283
|
hasPendingCommitRef,
|
|
1142
1284
|
valueRef
|
|
1143
1285
|
} = useNumberFieldRootContext();
|
|
1144
|
-
const {
|
|
1145
|
-
disabled,
|
|
1146
|
-
readOnly,
|
|
1147
|
-
required,
|
|
1148
|
-
value,
|
|
1149
|
-
inputValue
|
|
1150
|
-
} = state;
|
|
1151
1286
|
const {
|
|
1152
1287
|
clearErrors
|
|
1153
1288
|
} = useFormContext();
|
|
@@ -1282,7 +1417,7 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1282
1417
|
blockRevalidationRef.current = false;
|
|
1283
1418
|
return;
|
|
1284
1419
|
}
|
|
1285
|
-
committedValue = lastChangedValueRef.current;
|
|
1420
|
+
committedValue = lastChangedValueRef.current ?? committed;
|
|
1286
1421
|
// If validation normalized back to the current value, `useValueChanged` won't fire to
|
|
1287
1422
|
// reset the flag, so reset it here or the next external change won't revalidate.
|
|
1288
1423
|
if (committedValue === value) {
|
|
@@ -1303,7 +1438,7 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1303
1438
|
}
|
|
1304
1439
|
},
|
|
1305
1440
|
onChange(event) {
|
|
1306
|
-
// Workaround for https://github.com/
|
|
1441
|
+
// Workaround for https://github.com/facebook/react/issues/9023
|
|
1307
1442
|
if (event.nativeEvent.defaultPrevented) {
|
|
1308
1443
|
return;
|
|
1309
1444
|
}
|
|
@@ -1319,10 +1454,7 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1319
1454
|
// currently parseable into a number. This preserves good UX for IME
|
|
1320
1455
|
// composition/partial input while still providing live numeric updates when possible.
|
|
1321
1456
|
const allowedNonNumericKeys = getAllowedNonNumericKeys();
|
|
1322
|
-
const isValidCharacterString = Array.from(targetValue).every(ch => isNumeralChar(ch) || ANY_MINUS_DETECT_RE.test(ch) || allowedNonNumericKeys.has(ch)
|
|
1323
|
-
// Bidi/format controls are stripped by `parseNumber`; don't let them reject the string
|
|
1324
|
-
// (RTL locales insert them around exponent/currency signs, e.g. scientific notation).
|
|
1325
|
-
FORMAT_CONTROL_DETECT_RE.test(ch));
|
|
1457
|
+
const isValidCharacterString = Array.from(targetValue).every(ch => isNumeralChar(ch) || ANY_MINUS_DETECT_RE.test(ch) || allowedNonNumericKeys.has(ch));
|
|
1326
1458
|
if (!isValidCharacterString) {
|
|
1327
1459
|
return;
|
|
1328
1460
|
}
|
|
@@ -1372,7 +1504,7 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1372
1504
|
if (event.key === symbol) {
|
|
1373
1505
|
const symbolIndex = inputValue.indexOf(symbol);
|
|
1374
1506
|
const isSymbolHighlighted = selectionContainsIndex(symbolIndex);
|
|
1375
|
-
isAllowedNonNumericKey =
|
|
1507
|
+
isAllowedNonNumericKey = !inputValue.includes(symbol) || isAllSelected || isSymbolHighlighted;
|
|
1376
1508
|
}
|
|
1377
1509
|
});
|
|
1378
1510
|
const isNavigateKey = NAVIGATE_KEYS.has(event.key);
|
|
@@ -1385,18 +1517,12 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1385
1517
|
event.which === 229 || event.altKey && !isStepKey || event.ctrlKey || event.metaKey || isAllowedNonNumericKey || isNumeralChar(event.key) || isNavigateKey) {
|
|
1386
1518
|
return;
|
|
1387
1519
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
let boundaryValue = null;
|
|
1391
|
-
if (event.key === 'Home' && min != null) {
|
|
1392
|
-
boundaryValue = min;
|
|
1393
|
-
} else if (event.key === 'End' && max != null) {
|
|
1394
|
-
boundaryValue = max;
|
|
1395
|
-
}
|
|
1520
|
+
const willSetHome = event.key === 'Home' && min != null;
|
|
1521
|
+
const willSetEnd = event.key === 'End' && max != null;
|
|
1396
1522
|
|
|
1397
1523
|
// Let the browser handle multi-character keys we don't act on (PageUp, Insert, F-keys,
|
|
1398
1524
|
// Home/End without min/max); invalid single characters are still blocked below.
|
|
1399
|
-
if (event.key.length > 1 && !isStepKey &&
|
|
1525
|
+
if (event.key.length > 1 && !isStepKey && !willSetHome && !willSetEnd) {
|
|
1400
1526
|
return;
|
|
1401
1527
|
}
|
|
1402
1528
|
|
|
@@ -1408,14 +1534,12 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1408
1534
|
const amount = getStepAmount(event);
|
|
1409
1535
|
|
|
1410
1536
|
// Prevent insertion of text or caret from moving.
|
|
1411
|
-
event
|
|
1412
|
-
event.stopPropagation();
|
|
1537
|
+
stopEvent(event);
|
|
1413
1538
|
const commitDetails = createGenericEventDetails(keyboard, nativeEvent);
|
|
1414
1539
|
let changed = false;
|
|
1415
|
-
if (
|
|
1540
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
1416
1541
|
allowInputSyncRef.current = true;
|
|
1417
|
-
|
|
1418
|
-
if (isStepKey) {
|
|
1542
|
+
|
|
1419
1543
|
// When stepping from the synced numeric state, refresh the commit ref to the current
|
|
1420
1544
|
// value so a canceled step can't commit a stale `lastChangedValueRef` left over from an
|
|
1421
1545
|
// earlier change (mirrors the button path).
|
|
@@ -1428,14 +1552,15 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1428
1552
|
event: nativeEvent,
|
|
1429
1553
|
reason: keyboard
|
|
1430
1554
|
});
|
|
1431
|
-
} else if (
|
|
1432
|
-
|
|
1555
|
+
} else if (willSetHome) {
|
|
1556
|
+
allowInputSyncRef.current = true;
|
|
1557
|
+
changed = setValue(min, createChangeEventDetails(keyboard, nativeEvent));
|
|
1558
|
+
} else if (willSetEnd) {
|
|
1559
|
+
allowInputSyncRef.current = true;
|
|
1560
|
+
changed = setValue(max, createChangeEventDetails(keyboard, nativeEvent));
|
|
1433
1561
|
}
|
|
1434
|
-
|
|
1435
|
-
// `changed` is only true when `setValue` applied the change, which records the stored
|
|
1436
|
-
// (clamped/snapped) value, so commit that rather than the pre-validation input.
|
|
1437
1562
|
if (changed) {
|
|
1438
|
-
onValueCommitted(lastChangedValueRef.current, commitDetails);
|
|
1563
|
+
onValueCommitted(lastChangedValueRef.current ?? valueRef.current, commitDetails);
|
|
1439
1564
|
}
|
|
1440
1565
|
},
|
|
1441
1566
|
onPaste(event) {
|
|
@@ -1446,7 +1571,6 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1446
1571
|
try {
|
|
1447
1572
|
pastedData = event.clipboardData?.getData('text/plain') ?? '';
|
|
1448
1573
|
} catch {
|
|
1449
|
-
/* istanbul ignore else -- `process.env.NODE_ENV` is a build-time constant under test */
|
|
1450
1574
|
if (process.env.NODE_ENV !== 'production') {
|
|
1451
1575
|
const ownerStackMessage = SafeReact.captureOwnerStack?.() || '';
|
|
1452
1576
|
warn('<NumberField.Input> could not read clipboard text during paste handling.', ownerStackMessage);
|
|
@@ -1459,12 +1583,9 @@ const NumberFieldInput = /*#__PURE__*/React.forwardRef(function NumberFieldInput
|
|
|
1459
1583
|
|
|
1460
1584
|
// Insert the pasted text at the caret/selection instead of replacing the entire value,
|
|
1461
1585
|
// matching native input behavior (e.g. pasting "5" into "123|" yields "1235").
|
|
1462
|
-
// The component renders `type="text"`, which always reports a selection range. Overriding
|
|
1463
|
-
// `type` with a selection-less one (`email`, `number`) is unsupported either way: the caret
|
|
1464
|
-
// restore above throws on those, so there is no working behavior to preserve here.
|
|
1465
1586
|
const input = event.currentTarget;
|
|
1466
|
-
const selectionStart = input.selectionStart;
|
|
1467
|
-
const selectionEnd = input.selectionEnd;
|
|
1587
|
+
const selectionStart = input.selectionStart ?? inputValue.length;
|
|
1588
|
+
const selectionEnd = input.selectionEnd ?? inputValue.length;
|
|
1468
1589
|
const nextText = inputValue.slice(0, selectionStart) + pastedData + inputValue.slice(selectionEnd);
|
|
1469
1590
|
const parsedValue = parseNumber(nextText, locale, formatOptionsRef.current);
|
|
1470
1591
|
if (parsedValue !== null) {
|