@jobber/components 9.6.1 → 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/Autocomplete/index.cjs +2 -0
- package/dist/Autocomplete/index.mjs +2 -0
- package/dist/Autocomplete-cjs.js +5 -4
- package/dist/Autocomplete-es.js +2 -1
- 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/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/DataDump/index.cjs +2 -1
- package/dist/DataDump/index.mjs +2 -1
- 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/DatePicker/index.cjs +2 -0
- package/dist/DatePicker/index.mjs +2 -0
- package/dist/DatePicker-cjs.js +5 -4
- package/dist/DatePicker-es.js +2 -1
- 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/DrawerTitle-cjs.js +6 -2
- package/dist/DrawerTitle-es.js +7 -3
- 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/FormatFile/index.cjs +2 -0
- package/dist/FormatFile/index.mjs +2 -0
- package/dist/Gallery/index.cjs +2 -0
- package/dist/Gallery/index.mjs +2 -0
- package/dist/InputDate/index.cjs +2 -0
- package/dist/InputDate/index.mjs +2 -0
- package/dist/InputNumber/index.cjs +2 -1
- package/dist/InputNumber/index.mjs +2 -1
- package/dist/LightBox/index.cjs +2 -0
- package/dist/LightBox/index.mjs +2 -0
- package/dist/Menu/index.cjs +2 -1
- package/dist/Menu/index.mjs +2 -1
- package/dist/Menu-cjs.js +5 -4
- package/dist/Menu-es.js +2 -1
- package/dist/MenuSubmenuTrigger-cjs.js +164 -206
- package/dist/MenuSubmenuTrigger-es.js +152 -194
- package/dist/Modal/index.cjs +2 -0
- package/dist/Modal/index.mjs +3 -1
- 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/Popover/index.cjs +2 -0
- package/dist/Popover/index.mjs +2 -0
- package/dist/Popover-cjs.js +8 -7
- package/dist/Popover-es.js +2 -1
- 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/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/unstyledPrimitives/index.cjs +478 -720
- package/dist/unstyledPrimitives/index.mjs +421 -663
- 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
|
@@ -5,6 +5,7 @@ var useScrollLock = require('./useScrollLock-cjs.js');
|
|
|
5
5
|
var useRenderElement = require('./useRenderElement-cjs.js');
|
|
6
6
|
var useCompositeListItem = require('./useCompositeListItem-cjs.js');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
var floatingUi_utils = require('./floating-ui.utils-cjs.js');
|
|
8
9
|
var stringifyLocale = require('./stringifyLocale-cjs.js');
|
|
9
10
|
var scrollEdges = require('./scrollEdges-cjs.js');
|
|
10
11
|
|
|
@@ -74,112 +75,121 @@ const stateAttributesMapping = {
|
|
|
74
75
|
...useCompositeListItem.fieldValidityMapping
|
|
75
76
|
};
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
//
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
const HAN_NUMERALS = ['零', '〇', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
79
|
+
// Map Han numeral characters to ASCII digits. Includes both forms of zero.
|
|
80
|
+
const HAN_NUMERAL_TO_DIGIT = {
|
|
81
|
+
零: '0',
|
|
82
|
+
〇: '0',
|
|
83
|
+
一: '1',
|
|
84
|
+
二: '2',
|
|
85
|
+
三: '3',
|
|
86
|
+
四: '4',
|
|
87
|
+
五: '5',
|
|
88
|
+
六: '6',
|
|
89
|
+
七: '7',
|
|
90
|
+
八: '8',
|
|
91
|
+
九: '9'
|
|
92
|
+
};
|
|
93
|
+
const ARABIC_NUMERALS = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
|
|
94
|
+
const PERSIAN_NUMERALS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
|
95
|
+
const FULLWIDTH_NUMERALS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
|
86
96
|
const PERCENTAGES = ['%', '٪', '%', '﹪'];
|
|
87
97
|
const PERMILLE = ['‰', '؉'];
|
|
98
|
+
const UNICODE_MINUS_SIGNS = ['−', '-', '‒', '–', '—', '﹣'];
|
|
99
|
+
const UNICODE_PLUS_SIGNS = ['+', '﹢'];
|
|
88
100
|
|
|
89
101
|
// Fullwidth punctuation common in CJK inputs
|
|
90
102
|
const FULLWIDTH_DECIMAL = '.'; // U+FF0E
|
|
91
103
|
const FULLWIDTH_GROUP = ','; // U+FF0C
|
|
92
104
|
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
const ARABIC_RE = new RegExp(`[${ARABIC_NUMERALS.join('')}]`, 'g');
|
|
106
|
+
const PERSIAN_RE = new RegExp(`[${PERSIAN_NUMERALS.join('')}]`, 'g');
|
|
107
|
+
const FULLWIDTH_RE = new RegExp(`[${FULLWIDTH_NUMERALS.join('')}]`, 'g');
|
|
108
|
+
const HAN_RE = new RegExp(`[${HAN_NUMERALS.join('')}]`, 'g');
|
|
109
|
+
const PERCENT_RE = new RegExp(`[${PERCENTAGES.join('')}]`);
|
|
110
|
+
const PERMILLE_RE = new RegExp(`[${PERMILLE.join('')}]`);
|
|
111
|
+
const PERCENT_GLOBAL_RE = new RegExp(PERCENT_RE.source, 'g');
|
|
112
|
+
const PERMILLE_GLOBAL_RE = new RegExp(PERMILLE_RE.source, 'g');
|
|
113
|
+
|
|
114
|
+
// Detection regexes (non-global to avoid lastIndex side effects), derived from the numeral arrays
|
|
115
|
+
// so they can't drift out of sync.
|
|
116
|
+
const ARABIC_DETECT_RE = new RegExp(`[${ARABIC_NUMERALS.join('')}]`);
|
|
117
|
+
const PERSIAN_DETECT_RE = new RegExp(`[${PERSIAN_NUMERALS.join('')}]`);
|
|
118
|
+
const HAN_DETECT_RE = new RegExp(`[${HAN_NUMERALS.join('')}]`);
|
|
119
|
+
const FULLWIDTH_DETECT_RE = new RegExp(`[${FULLWIDTH_NUMERALS.join('')}]`);
|
|
103
120
|
|
|
104
121
|
// Whether the character is a digit in any numeral system the field accepts.
|
|
105
122
|
function isNumeralChar(char) {
|
|
106
|
-
return
|
|
123
|
+
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);
|
|
107
124
|
}
|
|
108
125
|
const BASE_NON_NUMERIC_SYMBOLS = ['.', ',', FULLWIDTH_DECIMAL, FULLWIDTH_GROUP, '٫', '٬'];
|
|
109
126
|
const SPACE_SEPARATOR_RE = /\p{Zs}/u;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// rather than rejecting the typed string. Non-global so it's safe for repeated `.test(char)`.
|
|
113
|
-
const FORMAT_CONTROL_DETECT_RE = /\p{Cf}/u;
|
|
114
|
-
const FORMAT_CONTROL_GLOBAL_RE = /\p{Cf}/gu;
|
|
115
|
-
const PLUS_SIGNS_WITH_ASCII = ['+', '+', '﹢'];
|
|
116
|
-
const MINUS_SIGNS_WITH_ASCII = ['-', '−', '-', '‒', '–', '—', '﹣'];
|
|
127
|
+
const PLUS_SIGNS_WITH_ASCII = ['+', ...UNICODE_PLUS_SIGNS];
|
|
128
|
+
const MINUS_SIGNS_WITH_ASCII = ['-', ...UNICODE_MINUS_SIGNS];
|
|
117
129
|
const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
130
|
+
const escapeClassChar = s => s.replace(/[-\\\]^]/g, m => `\\${m}`); // escape for use inside [...]
|
|
131
|
+
|
|
118
132
|
function shiftDecimal(value, exponentDelta) {
|
|
119
133
|
const [coefficient, exponent = '0'] = String(value).split('e');
|
|
120
134
|
return Number(`${coefficient}e${Number(exponent) + exponentDelta}`);
|
|
121
135
|
}
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// so the locale-detail and allowed-character derivations enumerate the same parts.
|
|
130
|
-
const SAMPLE_FORMAT_NUMBER = 11111.1;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Returns the `Intl.NumberFormat` parts of a representative number, which surface every
|
|
134
|
-
* non-numeric symbol a given locale/format renders.
|
|
135
|
-
*/
|
|
136
|
-
function getFormatParts(locale, options) {
|
|
137
|
-
return getFormatter(locale, options).formatToParts(SAMPLE_FORMAT_NUMBER);
|
|
138
|
-
}
|
|
136
|
+
const charClassFrom = chars => `[${chars.map(escapeClassChar).join('')}]`;
|
|
137
|
+
const ANY_MINUS_CLASS = charClassFrom(['-'].concat(UNICODE_MINUS_SIGNS));
|
|
138
|
+
const ANY_PLUS_CLASS = charClassFrom(['+'].concat(UNICODE_PLUS_SIGNS));
|
|
139
|
+
const ANY_MINUS_RE = new RegExp(ANY_MINUS_CLASS, 'gu');
|
|
140
|
+
const ANY_PLUS_RE = new RegExp(ANY_PLUS_CLASS, 'gu');
|
|
141
|
+
const ANY_MINUS_DETECT_RE = new RegExp(ANY_MINUS_CLASS);
|
|
142
|
+
const ANY_PLUS_DETECT_RE = new RegExp(ANY_PLUS_CLASS);
|
|
139
143
|
function getNumberLocaleDetails(locale, options) {
|
|
140
|
-
const parts =
|
|
144
|
+
const parts = getFormatter(locale, options).formatToParts(11111.1);
|
|
141
145
|
const result = {};
|
|
142
146
|
parts.forEach(part => {
|
|
143
147
|
result[part.type] = part.value;
|
|
144
148
|
});
|
|
145
149
|
|
|
146
150
|
// The formatting options may omit the decimal separator (e.g. integer formats), so resolve it
|
|
147
|
-
// from the plain locale formatter
|
|
148
|
-
//
|
|
149
|
-
// locale-determined and identical across format styles. The ASCII seed is a formality to keep
|
|
150
|
-
// the separator non-optional for callers; it is always replaced below.
|
|
151
|
-
let decimal = '.';
|
|
151
|
+
// from the plain locale formatter. This overrides any options-derived decimal too, which is
|
|
152
|
+
// safe because the separator is locale-determined and identical across format styles.
|
|
152
153
|
getFormatter(locale).formatToParts(0.1).forEach(part => {
|
|
153
154
|
if (part.type === 'decimal') {
|
|
154
|
-
|
|
155
|
+
result[part.type] = part.value;
|
|
155
156
|
}
|
|
156
157
|
});
|
|
157
|
-
return
|
|
158
|
-
...result,
|
|
159
|
-
decimal
|
|
160
|
-
};
|
|
158
|
+
return result;
|
|
161
159
|
}
|
|
162
160
|
function parseNumber(formattedNumber, locale, options) {
|
|
161
|
+
if (formattedNumber == null) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
163
165
|
// Normalize control characters and whitespace; remove bidi/format controls
|
|
164
|
-
let input = formattedNumber.replace(
|
|
166
|
+
let input = String(formattedNumber).replace(/\p{Cf}/gu, '').trim();
|
|
165
167
|
|
|
166
168
|
// Normalize unicode minus/plus to ASCII, handle leading/trailing signs
|
|
167
169
|
input = input.replace(ANY_MINUS_RE, '-').replace(ANY_PLUS_RE, '+');
|
|
168
170
|
let isNegative = false;
|
|
169
171
|
|
|
170
|
-
//
|
|
171
|
-
const
|
|
172
|
-
|
|
172
|
+
// Trailing sign, e.g. "1234-" / "1234+"
|
|
173
|
+
const trailing = input.match(/([+-])\s*$/);
|
|
174
|
+
if (trailing) {
|
|
175
|
+
if (trailing[1] === '-') {
|
|
173
176
|
isNegative = true;
|
|
174
177
|
}
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
+
input = input.replace(/([+-])\s*$/, '');
|
|
179
|
+
}
|
|
180
|
+
// Leading sign
|
|
181
|
+
const leading = input.match(/^\s*([+-])/);
|
|
182
|
+
if (leading) {
|
|
183
|
+
if (leading[1] === '-') {
|
|
184
|
+
isNegative = true;
|
|
185
|
+
}
|
|
186
|
+
input = input.replace(/^\s*[+-]/, '');
|
|
187
|
+
}
|
|
178
188
|
|
|
179
189
|
// Heuristic locale detection
|
|
180
190
|
let computedLocale = locale;
|
|
181
191
|
if (computedLocale === undefined) {
|
|
182
|
-
if (
|
|
192
|
+
if (ARABIC_DETECT_RE.test(input) || PERSIAN_DETECT_RE.test(input)) {
|
|
183
193
|
computedLocale = 'ar';
|
|
184
194
|
} else if (HAN_DETECT_RE.test(input)) {
|
|
185
195
|
computedLocale = 'zh';
|
|
@@ -211,17 +221,70 @@ function parseNumber(formattedNumber, locale, options) {
|
|
|
211
221
|
groupRegex = new RegExp(escapeRegExp(group), 'g');
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
|
-
const replacements = [
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
224
|
+
const replacements = [{
|
|
225
|
+
regex: groupRegex,
|
|
226
|
+
replacement: ''
|
|
227
|
+
}, {
|
|
228
|
+
regex: decimal ? new RegExp(escapeRegExp(decimal), 'g') : null,
|
|
229
|
+
replacement: '.'
|
|
230
|
+
},
|
|
231
|
+
// Fullwidth punctuation
|
|
232
|
+
{
|
|
233
|
+
regex: /./g,
|
|
234
|
+
replacement: '.'
|
|
235
|
+
},
|
|
236
|
+
// FULLWIDTH_DECIMAL
|
|
237
|
+
{
|
|
238
|
+
regex: /,/g,
|
|
239
|
+
replacement: ''
|
|
240
|
+
},
|
|
241
|
+
// FULLWIDTH_GROUP
|
|
242
|
+
// Arabic punctuation
|
|
243
|
+
{
|
|
244
|
+
regex: /٫/g,
|
|
245
|
+
replacement: '.'
|
|
246
|
+
},
|
|
247
|
+
// ARABIC DECIMAL SEPARATOR (U+066B)
|
|
248
|
+
{
|
|
249
|
+
regex: /٬/g,
|
|
250
|
+
replacement: ''
|
|
251
|
+
},
|
|
252
|
+
// ARABIC THOUSANDS SEPARATOR (U+066C)
|
|
220
253
|
// Currency & unit labels
|
|
221
|
-
|
|
254
|
+
{
|
|
255
|
+
regex: currency ? new RegExp(escapeRegExp(currency), 'g') : null,
|
|
256
|
+
replacement: ''
|
|
257
|
+
}, {
|
|
258
|
+
regex: unitRegex,
|
|
259
|
+
replacement: ''
|
|
260
|
+
}, {
|
|
261
|
+
regex: PERCENT_GLOBAL_RE,
|
|
262
|
+
replacement: ''
|
|
263
|
+
}, {
|
|
264
|
+
regex: PERMILLE_GLOBAL_RE,
|
|
265
|
+
replacement: ''
|
|
266
|
+
}, {
|
|
267
|
+
regex: exponentSeparator ? new RegExp(escapeRegExp(exponentSeparator), 'g') : null,
|
|
268
|
+
replacement: 'e'
|
|
269
|
+
},
|
|
222
270
|
// Numeral systems to ASCII digits
|
|
223
|
-
|
|
224
|
-
|
|
271
|
+
{
|
|
272
|
+
regex: ARABIC_RE,
|
|
273
|
+
replacement: ch => String(ARABIC_NUMERALS.indexOf(ch))
|
|
274
|
+
}, {
|
|
275
|
+
regex: PERSIAN_RE,
|
|
276
|
+
replacement: ch => String(PERSIAN_NUMERALS.indexOf(ch))
|
|
277
|
+
}, {
|
|
278
|
+
regex: FULLWIDTH_RE,
|
|
279
|
+
replacement: ch => String(FULLWIDTH_NUMERALS.indexOf(ch))
|
|
280
|
+
}, {
|
|
281
|
+
regex: HAN_RE,
|
|
282
|
+
replacement: ch => HAN_NUMERAL_TO_DIGIT[ch]
|
|
283
|
+
}];
|
|
284
|
+
let unformatted = replacements.reduce((acc, {
|
|
285
|
+
regex,
|
|
286
|
+
replacement
|
|
287
|
+
}) => {
|
|
225
288
|
return regex ? acc.replace(regex, replacement) : acc;
|
|
226
289
|
}, input);
|
|
227
290
|
|
|
@@ -299,18 +362,27 @@ function removeFloatingPointErrors(value, format) {
|
|
|
299
362
|
}
|
|
300
363
|
return formatter.format(roundedValue) === roundedText ? roundedValue : value;
|
|
301
364
|
}
|
|
302
|
-
function snapToStep(value, base, step,
|
|
365
|
+
function snapToStep(value, base, step, mode = 'directional') {
|
|
303
366
|
const stepSize = Math.abs(step);
|
|
304
367
|
const direction = Math.sign(step);
|
|
305
368
|
const tolerance = stepSize * STEP_EPSILON_FACTOR * direction;
|
|
306
369
|
const rawSteps = value - base + tolerance;
|
|
307
|
-
if (nearest) {
|
|
370
|
+
if (mode === 'nearest') {
|
|
308
371
|
return base + Math.round(rawSteps / step) * step;
|
|
309
372
|
}
|
|
310
373
|
const snappedSteps = direction > 0 ? Math.floor(rawSteps / stepSize) : Math.ceil(rawSteps / stepSize);
|
|
311
374
|
return base + snappedSteps * stepSize;
|
|
312
375
|
}
|
|
313
|
-
function toValidatedNumber(value,
|
|
376
|
+
function toValidatedNumber(value, {
|
|
377
|
+
step,
|
|
378
|
+
minWithDefault,
|
|
379
|
+
maxWithDefault,
|
|
380
|
+
minWithZeroDefault,
|
|
381
|
+
format,
|
|
382
|
+
snapOnStep,
|
|
383
|
+
small,
|
|
384
|
+
clamp: shouldClamp
|
|
385
|
+
}) {
|
|
314
386
|
if (value === null) {
|
|
315
387
|
return value;
|
|
316
388
|
}
|
|
@@ -318,9 +390,8 @@ function toValidatedNumber(value, step, minWithDefault, maxWithDefault, minWithZ
|
|
|
318
390
|
if (step != null && snapOnStep && step !== 0) {
|
|
319
391
|
const base = small || minWithDefault === Number.MIN_SAFE_INTEGER ? minWithZeroDefault : minWithDefault;
|
|
320
392
|
|
|
321
|
-
// Snap before clamping so non-step-aligned boundaries stay reachable.
|
|
322
|
-
|
|
323
|
-
nextValue = snapToStep(nextValue, base, step, small);
|
|
393
|
+
// Snap before clamping so non-step-aligned boundaries stay reachable.
|
|
394
|
+
nextValue = snapToStep(nextValue, base, step, small ? 'nearest' : 'directional');
|
|
324
395
|
}
|
|
325
396
|
|
|
326
397
|
// Clamp before rounding so a value just outside a fractional boundary (e.g. `0.4` with
|
|
@@ -375,6 +446,7 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
375
446
|
validityData,
|
|
376
447
|
disabled: fieldDisabled,
|
|
377
448
|
setFilled,
|
|
449
|
+
invalid,
|
|
378
450
|
name: fieldName,
|
|
379
451
|
state: fieldState,
|
|
380
452
|
validation
|
|
@@ -423,46 +495,48 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
423
495
|
const [inputValue, setInputValue] = React__namespace.useState(() => formatNumber(value, locale, format));
|
|
424
496
|
const [inputMode, setInputMode] = React__namespace.useState('numeric');
|
|
425
497
|
const getAllowedNonNumericKeys = useScrollLock.useStableCallback(() => {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const
|
|
433
|
-
keys.add(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
// them, so allowing them would yield a silently incorrect value.
|
|
441
|
-
parts.forEach(part => {
|
|
442
|
-
if (part.type === 'integer' || part.type === 'fraction' || part.type === 'exponentInteger' || part.type === 'compact') {
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
addAll(Array.from(part.value));
|
|
446
|
-
if (SPACE_SEPARATOR_RE.test(part.value)) {
|
|
498
|
+
const {
|
|
499
|
+
decimal,
|
|
500
|
+
group,
|
|
501
|
+
currency,
|
|
502
|
+
literal
|
|
503
|
+
} = getNumberLocaleDetails(locale, format);
|
|
504
|
+
const keys = new Set();
|
|
505
|
+
BASE_NON_NUMERIC_SYMBOLS.forEach(symbol => keys.add(symbol));
|
|
506
|
+
if (decimal) {
|
|
507
|
+
keys.add(decimal);
|
|
508
|
+
}
|
|
509
|
+
if (group) {
|
|
510
|
+
keys.add(group);
|
|
511
|
+
if (SPACE_SEPARATOR_RE.test(group)) {
|
|
447
512
|
keys.add(' ');
|
|
448
513
|
}
|
|
449
|
-
}
|
|
514
|
+
}
|
|
450
515
|
const allowPercentSymbols = formatStyle === 'percent' || formatStyle === 'unit' && format?.unit === 'percent';
|
|
451
516
|
const allowPermilleSymbols = formatStyle === 'percent' || formatStyle === 'unit' && format?.unit === 'permille';
|
|
452
|
-
|
|
453
|
-
// Tolerate percent/permille variants the formatter doesn't emit but users may type or paste.
|
|
454
517
|
if (allowPercentSymbols) {
|
|
455
|
-
|
|
518
|
+
PERCENTAGES.forEach(key => keys.add(key));
|
|
456
519
|
}
|
|
457
520
|
if (allowPermilleSymbols) {
|
|
458
|
-
|
|
521
|
+
PERMILLE.forEach(key => keys.add(key));
|
|
522
|
+
}
|
|
523
|
+
if (formatStyle === 'currency' && currency) {
|
|
524
|
+
keys.add(currency);
|
|
525
|
+
}
|
|
526
|
+
if (literal) {
|
|
527
|
+
// Some locales (e.g. de-DE) insert a literal space character between the number
|
|
528
|
+
// and the symbol, so allow those characters to be typed/removed.
|
|
529
|
+
Array.from(literal).forEach(char => keys.add(char));
|
|
530
|
+
if (SPACE_SEPARATOR_RE.test(literal)) {
|
|
531
|
+
keys.add(' ');
|
|
532
|
+
}
|
|
459
533
|
}
|
|
460
534
|
|
|
461
535
|
// Allow plus sign in all cases; minus sign when negatives are valid, or when out-of-range
|
|
462
536
|
// entry is allowed so native underflow validation can be triggered from the keyboard.
|
|
463
|
-
|
|
537
|
+
PLUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key));
|
|
464
538
|
if (minWithDefault < 0 || allowOutOfRange) {
|
|
465
|
-
|
|
539
|
+
MINUS_SIGNS_WITH_ASCII.forEach(key => keys.add(key));
|
|
466
540
|
}
|
|
467
541
|
return keys;
|
|
468
542
|
});
|
|
@@ -480,13 +554,21 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
480
554
|
const dir = details.direction;
|
|
481
555
|
|
|
482
556
|
// Direct text entry (typing, pasting, clearing, autofill) behaves natively; step-based
|
|
483
|
-
// interactions (keyboard arrows, buttons, wheel, scrub) do not.
|
|
484
|
-
|
|
485
|
-
const isInputReason = details.reason.startsWith('input-') || details.reason === useScrollLock.none;
|
|
557
|
+
// interactions (keyboard arrows, buttons, wheel, scrub) do not.
|
|
558
|
+
const isInputReason = details.reason === useScrollLock.inputChange || details.reason === useScrollLock.inputClear || details.reason === useScrollLock.inputBlur || details.reason === useScrollLock.inputPaste || details.reason === useScrollLock.none;
|
|
486
559
|
|
|
487
560
|
// Only allow out-of-range values for direct text entry. Step-based interactions still clamp.
|
|
488
561
|
const shouldClampValue = !allowOutOfRange || !isInputReason;
|
|
489
|
-
const validatedValue = toValidatedNumber(unvalidatedValue,
|
|
562
|
+
const validatedValue = toValidatedNumber(unvalidatedValue, {
|
|
563
|
+
step: dir ? getStepAmount(eventWithOptionalKeyState) * dir : undefined,
|
|
564
|
+
format: formatOptionsRef.current,
|
|
565
|
+
minWithDefault,
|
|
566
|
+
maxWithDefault,
|
|
567
|
+
minWithZeroDefault,
|
|
568
|
+
snapOnStep,
|
|
569
|
+
small: eventWithOptionalKeyState?.altKey ?? false,
|
|
570
|
+
clamp: shouldClampValue
|
|
571
|
+
});
|
|
490
572
|
|
|
491
573
|
// Notify about a change even when the numeric value is unchanged for input reasons: the
|
|
492
574
|
// typed text may clamp/snap to the current value, or differ while validation normalizes
|
|
@@ -596,10 +678,10 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
596
678
|
const changed = incrementValue(amount, {
|
|
597
679
|
direction: event.deltaY > 0 ? -1 : 1,
|
|
598
680
|
event,
|
|
599
|
-
reason:
|
|
681
|
+
reason: 'wheel'
|
|
600
682
|
});
|
|
601
683
|
if (changed) {
|
|
602
|
-
onValueCommitted(lastChangedValueRef.current, useScrollLock.createGenericEventDetails(useScrollLock.wheel, event));
|
|
684
|
+
onValueCommitted(lastChangedValueRef.current ?? valueRef.current, useScrollLock.createGenericEventDetails(useScrollLock.wheel, event));
|
|
603
685
|
}
|
|
604
686
|
}
|
|
605
687
|
return useScrollLock.addEventListener(element, 'wheel', handleWheel);
|
|
@@ -615,8 +697,12 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
615
697
|
}), [fieldState, disabled, readOnly, required, value, inputValue, isScrubbing]);
|
|
616
698
|
const contextValue = React__namespace.useMemo(() => ({
|
|
617
699
|
inputRef,
|
|
700
|
+
inputValue,
|
|
701
|
+
value,
|
|
618
702
|
minWithDefault,
|
|
619
703
|
maxWithDefault,
|
|
704
|
+
disabled,
|
|
705
|
+
readOnly,
|
|
620
706
|
id,
|
|
621
707
|
setValue,
|
|
622
708
|
incrementValue,
|
|
@@ -628,16 +714,19 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
628
714
|
hasPendingCommitRef,
|
|
629
715
|
name,
|
|
630
716
|
nameProp,
|
|
717
|
+
required,
|
|
718
|
+
invalid,
|
|
631
719
|
inputMode,
|
|
632
720
|
getAllowedNonNumericKeys,
|
|
633
721
|
min,
|
|
634
722
|
max,
|
|
635
723
|
setInputValue,
|
|
636
724
|
locale,
|
|
725
|
+
isScrubbing,
|
|
637
726
|
setIsScrubbing,
|
|
638
727
|
state,
|
|
639
728
|
onValueCommitted
|
|
640
|
-
}), [inputRef, minWithDefault, maxWithDefault, id, setValue, incrementValue, getStepAmount, formatOptionsRef, valueRef, name, nameProp, inputMode, getAllowedNonNumericKeys, min, max, setInputValue, locale, state, onValueCommitted]);
|
|
729
|
+
}), [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]);
|
|
641
730
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
642
731
|
ref: forwardedRef,
|
|
643
732
|
state,
|
|
@@ -652,7 +741,7 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
652
741
|
inputRef.current?.focus();
|
|
653
742
|
},
|
|
654
743
|
onChange(event) {
|
|
655
|
-
// Workaround for https://github.com/
|
|
744
|
+
// Workaround for https://github.com/facebook/react/issues/9023
|
|
656
745
|
if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
|
|
657
746
|
return;
|
|
658
747
|
}
|
|
@@ -677,7 +766,7 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
677
766
|
min: min,
|
|
678
767
|
max: max
|
|
679
768
|
// stepMismatch validation is broken unless an explicit `min` is added.
|
|
680
|
-
// See https://github.com/
|
|
769
|
+
// See https://github.com/facebook/react/issues/12334.
|
|
681
770
|
,
|
|
682
771
|
step: stepProp,
|
|
683
772
|
disabled: disabled,
|
|
@@ -774,6 +863,7 @@ function isTouchLikePointerType(pointerType) {
|
|
|
774
863
|
function usePressAndHold(params) {
|
|
775
864
|
const {
|
|
776
865
|
disabled,
|
|
866
|
+
readOnly = false,
|
|
777
867
|
tick,
|
|
778
868
|
onStop,
|
|
779
869
|
tickDelay = DEFAULT_TICK_DELAY,
|
|
@@ -808,7 +898,7 @@ function usePressAndHold(params) {
|
|
|
808
898
|
if (!element) {
|
|
809
899
|
return;
|
|
810
900
|
}
|
|
811
|
-
const win =
|
|
901
|
+
const win = floatingUi_utils.getWindow(element);
|
|
812
902
|
function handleContextMenu(event) {
|
|
813
903
|
event.preventDefault();
|
|
814
904
|
}
|
|
@@ -853,7 +943,8 @@ function usePressAndHold(params) {
|
|
|
853
943
|
isTouchingButtonRef.current = false;
|
|
854
944
|
},
|
|
855
945
|
onPointerDown(event) {
|
|
856
|
-
|
|
946
|
+
const isMainButton = !event.button || event.button === 0;
|
|
947
|
+
if (event.defaultPrevented || !isMainButton || disabled || readOnly) {
|
|
857
948
|
return;
|
|
858
949
|
}
|
|
859
950
|
pointerTypeRef.current = event.pointerType;
|
|
@@ -896,10 +987,12 @@ function usePressAndHold(params) {
|
|
|
896
987
|
}
|
|
897
988
|
},
|
|
898
989
|
onPointerMove(event) {
|
|
899
|
-
if (disabled || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
|
|
990
|
+
if (disabled || readOnly || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
|
|
900
991
|
return;
|
|
901
992
|
}
|
|
902
|
-
movesAfterTouchRef.current
|
|
993
|
+
if (movesAfterTouchRef.current != null) {
|
|
994
|
+
movesAfterTouchRef.current += 1;
|
|
995
|
+
}
|
|
903
996
|
const {
|
|
904
997
|
x,
|
|
905
998
|
y
|
|
@@ -911,7 +1004,7 @@ function usePressAndHold(params) {
|
|
|
911
1004
|
}
|
|
912
1005
|
},
|
|
913
1006
|
onMouseEnter(event) {
|
|
914
|
-
if (event.defaultPrevented || disabled || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
|
|
1007
|
+
if (event.defaultPrevented || disabled || readOnly || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
|
|
915
1008
|
return;
|
|
916
1009
|
}
|
|
917
1010
|
startAutoChange(event.nativeEvent);
|
|
@@ -944,47 +1037,32 @@ function usePressAndHold(params) {
|
|
|
944
1037
|
};
|
|
945
1038
|
}
|
|
946
1039
|
|
|
1040
|
+
const CHANGE_VALUE_TICK_DELAY = 60;
|
|
1041
|
+
const START_AUTO_CHANGE_DELAY = 400;
|
|
1042
|
+
const SCROLLING_POINTER_MOVE_DISTANCE = 8;
|
|
1043
|
+
|
|
947
1044
|
const SELECT_NONE_STYLE = {
|
|
948
1045
|
WebkitUserSelect: 'none',
|
|
949
1046
|
userSelect: 'none'
|
|
950
1047
|
};
|
|
951
|
-
|
|
952
|
-
* Shared implementation for the increment and decrement stepper buttons. They differ only in the
|
|
953
|
-
* direction they step and the boundary (`max` vs `min`) at which they become disabled.
|
|
954
|
-
*/
|
|
955
|
-
function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement) {
|
|
956
|
-
const {
|
|
957
|
-
render,
|
|
958
|
-
className,
|
|
959
|
-
disabled: disabledProp = false,
|
|
960
|
-
nativeButton = true,
|
|
961
|
-
style,
|
|
962
|
-
...elementProps
|
|
963
|
-
} = componentProps;
|
|
1048
|
+
function useNumberFieldButton(params) {
|
|
964
1049
|
const {
|
|
965
1050
|
allowInputSyncRef,
|
|
1051
|
+
disabled,
|
|
966
1052
|
formatOptionsRef,
|
|
967
1053
|
getStepAmount,
|
|
968
1054
|
id,
|
|
969
1055
|
incrementValue,
|
|
970
1056
|
inputRef,
|
|
971
|
-
|
|
972
|
-
|
|
1057
|
+
inputValue,
|
|
1058
|
+
isIncrement,
|
|
1059
|
+
locale,
|
|
1060
|
+
readOnly,
|
|
973
1061
|
setValue,
|
|
974
|
-
state,
|
|
975
1062
|
valueRef,
|
|
976
|
-
locale,
|
|
977
1063
|
lastChangedValueRef,
|
|
978
1064
|
onValueCommitted
|
|
979
|
-
} =
|
|
980
|
-
const {
|
|
981
|
-
disabled: contextDisabled,
|
|
982
|
-
readOnly,
|
|
983
|
-
value,
|
|
984
|
-
inputValue
|
|
985
|
-
} = state;
|
|
986
|
-
const isAtBoundary = value != null && (isIncrement ? value >= maxWithDefault : value <= minWithDefault);
|
|
987
|
-
const disabled = disabledProp || contextDisabled || isAtBoundary;
|
|
1065
|
+
} = params;
|
|
988
1066
|
const pressReason = isIncrement ? useScrollLock.incrementPress : useScrollLock.decrementPress;
|
|
989
1067
|
function commitValue(nativeEvent) {
|
|
990
1068
|
const shouldCommitInputValue = !allowInputSyncRef.current;
|
|
@@ -1020,6 +1098,9 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
1020
1098
|
} = usePressAndHold({
|
|
1021
1099
|
disabled: disabled || readOnly,
|
|
1022
1100
|
elementRef: inputRef,
|
|
1101
|
+
tickDelay: CHANGE_VALUE_TICK_DELAY,
|
|
1102
|
+
startDelay: START_AUTO_CHANGE_DELAY,
|
|
1103
|
+
scrollDistance: SCROLLING_POINTER_MOVE_DISTANCE,
|
|
1023
1104
|
tick(triggerEvent) {
|
|
1024
1105
|
const amount = getStepAmount(triggerEvent);
|
|
1025
1106
|
return incrementValue(amount, {
|
|
@@ -1064,7 +1145,8 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
1064
1145
|
}
|
|
1065
1146
|
},
|
|
1066
1147
|
onPointerDown(event) {
|
|
1067
|
-
|
|
1148
|
+
const isMainButton = !event.button || event.button === 0;
|
|
1149
|
+
if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
|
|
1068
1150
|
return;
|
|
1069
1151
|
}
|
|
1070
1152
|
|
|
@@ -1080,6 +1162,61 @@ function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement)
|
|
|
1080
1162
|
pointerHandlers.onPointerDown(event);
|
|
1081
1163
|
}
|
|
1082
1164
|
};
|
|
1165
|
+
return props;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Shared implementation for the increment and decrement stepper buttons. They differ only in the
|
|
1170
|
+
* direction they step and the boundary (`max` vs `min`) at which they become disabled.
|
|
1171
|
+
*/
|
|
1172
|
+
function useNumberFieldStepperButton(componentProps, forwardedRef, isIncrement) {
|
|
1173
|
+
const {
|
|
1174
|
+
render,
|
|
1175
|
+
className,
|
|
1176
|
+
disabled: disabledProp = false,
|
|
1177
|
+
nativeButton = true,
|
|
1178
|
+
style,
|
|
1179
|
+
...elementProps
|
|
1180
|
+
} = componentProps;
|
|
1181
|
+
const {
|
|
1182
|
+
allowInputSyncRef,
|
|
1183
|
+
disabled: contextDisabled,
|
|
1184
|
+
formatOptionsRef,
|
|
1185
|
+
getStepAmount,
|
|
1186
|
+
id,
|
|
1187
|
+
incrementValue,
|
|
1188
|
+
inputRef,
|
|
1189
|
+
inputValue,
|
|
1190
|
+
maxWithDefault,
|
|
1191
|
+
minWithDefault,
|
|
1192
|
+
readOnly,
|
|
1193
|
+
setValue,
|
|
1194
|
+
state,
|
|
1195
|
+
value,
|
|
1196
|
+
valueRef,
|
|
1197
|
+
locale,
|
|
1198
|
+
lastChangedValueRef,
|
|
1199
|
+
onValueCommitted
|
|
1200
|
+
} = useNumberFieldRootContext();
|
|
1201
|
+
const isAtBoundary = value != null && (isIncrement ? value >= maxWithDefault : value <= minWithDefault);
|
|
1202
|
+
const disabled = disabledProp || contextDisabled || isAtBoundary;
|
|
1203
|
+
const props = useNumberFieldButton({
|
|
1204
|
+
isIncrement,
|
|
1205
|
+
inputRef,
|
|
1206
|
+
inputValue,
|
|
1207
|
+
disabled,
|
|
1208
|
+
readOnly,
|
|
1209
|
+
id,
|
|
1210
|
+
setValue,
|
|
1211
|
+
getStepAmount,
|
|
1212
|
+
incrementValue,
|
|
1213
|
+
allowInputSyncRef,
|
|
1214
|
+
formatOptionsRef,
|
|
1215
|
+
valueRef,
|
|
1216
|
+
locale,
|
|
1217
|
+
lastChangedValueRef,
|
|
1218
|
+
onValueCommitted
|
|
1219
|
+
});
|
|
1083
1220
|
const {
|
|
1084
1221
|
getButtonProps,
|
|
1085
1222
|
buttonRef
|
|
@@ -1142,33 +1279,31 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1142
1279
|
} = componentProps;
|
|
1143
1280
|
const {
|
|
1144
1281
|
allowInputSyncRef,
|
|
1282
|
+
disabled,
|
|
1145
1283
|
formatOptionsRef,
|
|
1146
1284
|
getAllowedNonNumericKeys,
|
|
1147
1285
|
getStepAmount,
|
|
1148
1286
|
id,
|
|
1149
1287
|
incrementValue,
|
|
1150
1288
|
inputMode,
|
|
1289
|
+
inputValue,
|
|
1151
1290
|
max,
|
|
1152
1291
|
min,
|
|
1153
1292
|
name,
|
|
1154
1293
|
nameProp,
|
|
1294
|
+
readOnly,
|
|
1295
|
+
required,
|
|
1155
1296
|
setValue,
|
|
1156
1297
|
state,
|
|
1157
1298
|
setInputValue,
|
|
1158
1299
|
locale,
|
|
1159
1300
|
inputRef,
|
|
1301
|
+
value,
|
|
1160
1302
|
onValueCommitted,
|
|
1161
1303
|
lastChangedValueRef,
|
|
1162
1304
|
hasPendingCommitRef,
|
|
1163
1305
|
valueRef
|
|
1164
1306
|
} = useNumberFieldRootContext();
|
|
1165
|
-
const {
|
|
1166
|
-
disabled,
|
|
1167
|
-
readOnly,
|
|
1168
|
-
required,
|
|
1169
|
-
value,
|
|
1170
|
-
inputValue
|
|
1171
|
-
} = state;
|
|
1172
1307
|
const {
|
|
1173
1308
|
clearErrors
|
|
1174
1309
|
} = useCompositeListItem.useFormContext();
|
|
@@ -1303,7 +1438,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1303
1438
|
blockRevalidationRef.current = false;
|
|
1304
1439
|
return;
|
|
1305
1440
|
}
|
|
1306
|
-
committedValue = lastChangedValueRef.current;
|
|
1441
|
+
committedValue = lastChangedValueRef.current ?? committed;
|
|
1307
1442
|
// If validation normalized back to the current value, `useValueChanged` won't fire to
|
|
1308
1443
|
// reset the flag, so reset it here or the next external change won't revalidate.
|
|
1309
1444
|
if (committedValue === value) {
|
|
@@ -1324,7 +1459,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1324
1459
|
}
|
|
1325
1460
|
},
|
|
1326
1461
|
onChange(event) {
|
|
1327
|
-
// Workaround for https://github.com/
|
|
1462
|
+
// Workaround for https://github.com/facebook/react/issues/9023
|
|
1328
1463
|
if (event.nativeEvent.defaultPrevented) {
|
|
1329
1464
|
return;
|
|
1330
1465
|
}
|
|
@@ -1340,10 +1475,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1340
1475
|
// currently parseable into a number. This preserves good UX for IME
|
|
1341
1476
|
// composition/partial input while still providing live numeric updates when possible.
|
|
1342
1477
|
const allowedNonNumericKeys = getAllowedNonNumericKeys();
|
|
1343
|
-
const isValidCharacterString = Array.from(targetValue).every(ch => isNumeralChar(ch) || ANY_MINUS_DETECT_RE.test(ch) || allowedNonNumericKeys.has(ch)
|
|
1344
|
-
// Bidi/format controls are stripped by `parseNumber`; don't let them reject the string
|
|
1345
|
-
// (RTL locales insert them around exponent/currency signs, e.g. scientific notation).
|
|
1346
|
-
FORMAT_CONTROL_DETECT_RE.test(ch));
|
|
1478
|
+
const isValidCharacterString = Array.from(targetValue).every(ch => isNumeralChar(ch) || ANY_MINUS_DETECT_RE.test(ch) || allowedNonNumericKeys.has(ch));
|
|
1347
1479
|
if (!isValidCharacterString) {
|
|
1348
1480
|
return;
|
|
1349
1481
|
}
|
|
@@ -1393,7 +1525,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1393
1525
|
if (event.key === symbol) {
|
|
1394
1526
|
const symbolIndex = inputValue.indexOf(symbol);
|
|
1395
1527
|
const isSymbolHighlighted = selectionContainsIndex(symbolIndex);
|
|
1396
|
-
isAllowedNonNumericKey =
|
|
1528
|
+
isAllowedNonNumericKey = !inputValue.includes(symbol) || isAllSelected || isSymbolHighlighted;
|
|
1397
1529
|
}
|
|
1398
1530
|
});
|
|
1399
1531
|
const isNavigateKey = NAVIGATE_KEYS.has(event.key);
|
|
@@ -1406,18 +1538,12 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1406
1538
|
event.which === 229 || event.altKey && !isStepKey || event.ctrlKey || event.metaKey || isAllowedNonNumericKey || isNumeralChar(event.key) || isNavigateKey) {
|
|
1407
1539
|
return;
|
|
1408
1540
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
let boundaryValue = null;
|
|
1412
|
-
if (event.key === 'Home' && min != null) {
|
|
1413
|
-
boundaryValue = min;
|
|
1414
|
-
} else if (event.key === 'End' && max != null) {
|
|
1415
|
-
boundaryValue = max;
|
|
1416
|
-
}
|
|
1541
|
+
const willSetHome = event.key === 'Home' && min != null;
|
|
1542
|
+
const willSetEnd = event.key === 'End' && max != null;
|
|
1417
1543
|
|
|
1418
1544
|
// Let the browser handle multi-character keys we don't act on (PageUp, Insert, F-keys,
|
|
1419
1545
|
// Home/End without min/max); invalid single characters are still blocked below.
|
|
1420
|
-
if (event.key.length > 1 && !isStepKey &&
|
|
1546
|
+
if (event.key.length > 1 && !isStepKey && !willSetHome && !willSetEnd) {
|
|
1421
1547
|
return;
|
|
1422
1548
|
}
|
|
1423
1549
|
|
|
@@ -1429,14 +1555,12 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1429
1555
|
const amount = getStepAmount(event);
|
|
1430
1556
|
|
|
1431
1557
|
// Prevent insertion of text or caret from moving.
|
|
1432
|
-
|
|
1433
|
-
event.stopPropagation();
|
|
1558
|
+
useScrollLock.stopEvent(event);
|
|
1434
1559
|
const commitDetails = useScrollLock.createGenericEventDetails(useScrollLock.keyboard, nativeEvent);
|
|
1435
1560
|
let changed = false;
|
|
1436
|
-
if (
|
|
1561
|
+
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
|
1437
1562
|
allowInputSyncRef.current = true;
|
|
1438
|
-
|
|
1439
|
-
if (isStepKey) {
|
|
1563
|
+
|
|
1440
1564
|
// When stepping from the synced numeric state, refresh the commit ref to the current
|
|
1441
1565
|
// value so a canceled step can't commit a stale `lastChangedValueRef` left over from an
|
|
1442
1566
|
// earlier change (mirrors the button path).
|
|
@@ -1449,14 +1573,15 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1449
1573
|
event: nativeEvent,
|
|
1450
1574
|
reason: useScrollLock.keyboard
|
|
1451
1575
|
});
|
|
1452
|
-
} else if (
|
|
1453
|
-
|
|
1576
|
+
} else if (willSetHome) {
|
|
1577
|
+
allowInputSyncRef.current = true;
|
|
1578
|
+
changed = setValue(min, useScrollLock.createChangeEventDetails(useScrollLock.keyboard, nativeEvent));
|
|
1579
|
+
} else if (willSetEnd) {
|
|
1580
|
+
allowInputSyncRef.current = true;
|
|
1581
|
+
changed = setValue(max, useScrollLock.createChangeEventDetails(useScrollLock.keyboard, nativeEvent));
|
|
1454
1582
|
}
|
|
1455
|
-
|
|
1456
|
-
// `changed` is only true when `setValue` applied the change, which records the stored
|
|
1457
|
-
// (clamped/snapped) value, so commit that rather than the pre-validation input.
|
|
1458
1583
|
if (changed) {
|
|
1459
|
-
onValueCommitted(lastChangedValueRef.current, commitDetails);
|
|
1584
|
+
onValueCommitted(lastChangedValueRef.current ?? valueRef.current, commitDetails);
|
|
1460
1585
|
}
|
|
1461
1586
|
},
|
|
1462
1587
|
onPaste(event) {
|
|
@@ -1467,7 +1592,6 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1467
1592
|
try {
|
|
1468
1593
|
pastedData = event.clipboardData?.getData('text/plain') ?? '';
|
|
1469
1594
|
} catch {
|
|
1470
|
-
/* istanbul ignore else -- `process.env.NODE_ENV` is a build-time constant under test */
|
|
1471
1595
|
if (process.env.NODE_ENV !== 'production') {
|
|
1472
1596
|
const ownerStackMessage = useScrollLock.SafeReact.captureOwnerStack?.() || '';
|
|
1473
1597
|
useRenderElement.warn('<NumberField.Input> could not read clipboard text during paste handling.', ownerStackMessage);
|
|
@@ -1480,12 +1604,9 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1480
1604
|
|
|
1481
1605
|
// Insert the pasted text at the caret/selection instead of replacing the entire value,
|
|
1482
1606
|
// matching native input behavior (e.g. pasting "5" into "123|" yields "1235").
|
|
1483
|
-
// The component renders `type="text"`, which always reports a selection range. Overriding
|
|
1484
|
-
// `type` with a selection-less one (`email`, `number`) is unsupported either way: the caret
|
|
1485
|
-
// restore above throws on those, so there is no working behavior to preserve here.
|
|
1486
1607
|
const input = event.currentTarget;
|
|
1487
|
-
const selectionStart = input.selectionStart;
|
|
1488
|
-
const selectionEnd = input.selectionEnd;
|
|
1608
|
+
const selectionStart = input.selectionStart ?? inputValue.length;
|
|
1609
|
+
const selectionEnd = input.selectionEnd ?? inputValue.length;
|
|
1489
1610
|
const nextText = inputValue.slice(0, selectionStart) + pastedData + inputValue.slice(selectionEnd);
|
|
1490
1611
|
const parsedValue = parseNumber(nextText, locale, formatOptionsRef.current);
|
|
1491
1612
|
if (parsedValue !== null) {
|