@jobber/components 9.5.3 → 9.6.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.
- package/Combobox.d.ts +1 -0
- package/Combobox.js +17 -0
- package/dist/Autocomplete/index.cjs +0 -2
- package/dist/Autocomplete/index.mjs +0 -2
- package/dist/Autocomplete-cjs.js +4 -5
- package/dist/Autocomplete-es.js +1 -2
- package/dist/Card/index.cjs +2 -2
- package/dist/Card/index.mjs +2 -2
- package/dist/Chip/index.cjs +0 -2
- package/dist/Chip/index.mjs +0 -2
- package/dist/Chips/InternalChipDismissible/hooks/index.cjs +0 -2
- package/dist/Chips/InternalChipDismissible/hooks/index.mjs +0 -2
- package/dist/Chips/InternalChipDismissible/index.cjs +0 -2
- package/dist/Chips/InternalChipDismissible/index.mjs +0 -2
- package/dist/Chips/index.cjs +0 -2
- package/dist/Chips/index.mjs +0 -2
- package/dist/Combobox/Combobox.contentSlots.d.ts +5 -0
- package/dist/Combobox/Combobox.context.d.ts +34 -0
- package/dist/Combobox/Combobox.d.ts +69 -0
- package/dist/Combobox/Combobox.footer.d.ts +7 -0
- package/dist/Combobox/Combobox.items.d.ts +25 -0
- package/dist/Combobox/Combobox.responsive.d.ts +54 -0
- package/dist/Combobox/Combobox.selectAll.d.ts +4 -0
- package/dist/Combobox/Combobox.slotFactory.d.ts +7 -0
- package/dist/Combobox/Combobox.testUtils.d.ts +12 -0
- package/dist/Combobox/Combobox.types.d.ts +166 -0
- package/dist/Combobox/index.cjs +35 -0
- package/dist/Combobox/index.d.ts +2 -0
- package/dist/Combobox/index.mjs +29 -0
- package/dist/Combobox-cjs.js +675 -0
- package/dist/Combobox-es.js +673 -0
- package/dist/ComboboxPrimitive-cjs.js +88 -47
- package/dist/ComboboxPrimitive-es.js +39 -23
- package/dist/{ComboboxChipRemove-cjs.js → ComboboxSeparator-cjs.js} +501 -455
- package/dist/{ComboboxChipRemove-es.js → ComboboxSeparator-es.js} +500 -456
- package/dist/ConfirmationModal/index.cjs +0 -2
- package/dist/ConfirmationModal/index.mjs +0 -2
- package/dist/ContentBlock/index.cjs +1 -0
- package/dist/ContentBlock/index.mjs +1 -0
- package/dist/ContentBlock-cjs.js +2 -18
- package/dist/ContentBlock-es.js +1 -17
- package/dist/DataDump/index.cjs +2 -2
- package/dist/DataDump/index.mjs +2 -2
- package/dist/DataList/components/DataListActions/index.cjs +0 -2
- package/dist/DataList/components/DataListActions/index.mjs +0 -2
- package/dist/DataList/components/DataListBulkActions/index.cjs +0 -2
- package/dist/DataList/components/DataListBulkActions/index.mjs +0 -2
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.cjs +0 -2
- package/dist/DataList/components/DataListFilters/components/DataListSort/index.mjs +0 -2
- package/dist/DataList/components/DataListFilters/index.cjs +0 -2
- package/dist/DataList/components/DataListFilters/index.mjs +0 -2
- package/dist/DataList/components/DataListHeader/index.cjs +0 -2
- package/dist/DataList/components/DataListHeader/index.mjs +0 -2
- package/dist/DataList/components/DataListItem/index.cjs +0 -2
- package/dist/DataList/components/DataListItem/index.mjs +0 -2
- package/dist/DataList/components/DataListItemActions/index.cjs +0 -2
- package/dist/DataList/components/DataListItemActions/index.mjs +0 -2
- package/dist/DataList/components/DataListItemActionsOverflow/index.cjs +0 -2
- package/dist/DataList/components/DataListItemActionsOverflow/index.mjs +0 -2
- package/dist/DataList/components/DataListItems/index.cjs +0 -2
- package/dist/DataList/components/DataListItems/index.mjs +0 -2
- package/dist/DataList/components/DataListLayout/index.cjs +0 -2
- package/dist/DataList/components/DataListLayout/index.mjs +0 -2
- package/dist/DataList/components/DataListLayoutActions/index.cjs +0 -2
- package/dist/DataList/components/DataListLayoutActions/index.mjs +0 -2
- package/dist/DataList/index.cjs +0 -2
- package/dist/DataList/index.mjs +0 -2
- package/dist/DatePicker/index.cjs +0 -2
- package/dist/DatePicker/index.mjs +0 -2
- package/dist/DatePicker-cjs.js +4 -5
- package/dist/DatePicker-es.js +1 -2
- package/dist/Dialog/index.cjs +2 -2
- package/dist/Dialog/index.mjs +2 -2
- package/dist/DrawerDescription-cjs.js +268 -99
- package/dist/DrawerDescription-es.js +270 -103
- package/dist/DrawerTitle-cjs.js +2 -6
- package/dist/DrawerTitle-es.js +3 -7
- package/dist/FieldDescription-cjs.js +184 -181
- package/dist/FieldDescription-es.js +187 -184
- package/dist/FilterPicker/components/FilterPickerActivator/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerActivator/index.mjs +0 -2
- package/dist/FilterPicker/components/FilterPickerContent/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerContent/index.mjs +0 -2
- package/dist/FilterPicker/components/FilterPickerTrigger/index.cjs +0 -2
- package/dist/FilterPicker/components/FilterPickerTrigger/index.mjs +0 -2
- package/dist/FilterPicker/index.cjs +0 -2
- package/dist/FilterPicker/index.mjs +0 -2
- package/dist/FilterPickerContent-cjs.js +4 -5
- package/dist/FilterPickerContent-es.js +1 -2
- package/dist/FormatFile/index.cjs +0 -2
- package/dist/FormatFile/index.mjs +0 -2
- package/dist/Gallery/index.cjs +0 -2
- package/dist/Gallery/index.mjs +0 -2
- package/dist/InputDate/index.cjs +0 -2
- package/dist/InputDate/index.mjs +0 -2
- package/dist/InputNumber/index.cjs +2 -2
- package/dist/InputNumber/index.mjs +2 -2
- package/dist/InputNumber/types.d.ts +5 -0
- package/dist/InputNumber-cjs.js +2 -2
- package/dist/InputNumber-es.js +2 -2
- package/dist/LightBox/index.cjs +0 -2
- package/dist/LightBox/index.mjs +0 -2
- package/dist/Menu/index.cjs +2 -2
- package/dist/Menu/index.mjs +2 -2
- package/dist/Menu-cjs.js +4 -5
- package/dist/Menu-es.js +1 -2
- package/dist/MenuSubmenuTrigger-cjs.js +206 -164
- package/dist/MenuSubmenuTrigger-es.js +194 -152
- package/dist/Modal/index.cjs +0 -2
- package/dist/Modal/index.mjs +1 -3
- package/dist/NumberFieldInput-cjs.js +187 -308
- package/dist/NumberFieldInput-es.js +187 -308
- package/dist/Page/index.cjs +2 -2
- package/dist/Page/index.mjs +2 -2
- package/dist/Popover/index.cjs +0 -2
- package/dist/Popover/index.mjs +0 -2
- package/dist/Popover-cjs.js +7 -8
- package/dist/Popover-es.js +1 -2
- package/dist/ScrollAreaViewport-cjs.js +747 -1093
- package/dist/ScrollAreaViewport-es.js +734 -1081
- package/dist/Select/index.cjs +2 -2
- package/dist/Select/index.mjs +2 -2
- package/dist/SelectGroupLabel-cjs.js +207 -306
- package/dist/SelectGroupLabel-es.js +203 -302
- package/dist/SelectPrimitive-cjs.js +11 -17
- package/dist/SelectPrimitive-es.js +1 -7
- package/dist/Tooltip/index.cjs +0 -2
- package/dist/Tooltip/index.mjs +0 -2
- package/dist/Tooltip-cjs.js +6 -7
- package/dist/Tooltip-es.js +1 -2
- package/dist/buttonRenderAdapter-cjs.js +2 -1
- package/dist/buttonRenderAdapter-es.js +2 -1
- package/dist/dialogReturnFocus-cjs.js +1 -1
- package/dist/dialogReturnFocus-es.js +2 -2
- package/dist/docs/InputNumber/InputNumber.md +2 -2
- package/dist/docs/SelectPrimitive/SelectPrimitive.md +1 -1
- package/dist/floating-ui.react-cjs.js +2557 -185
- package/dist/floating-ui.react-es.js +2512 -149
- package/dist/getMappedBreakpointWidth-cjs.js +21 -0
- package/dist/getMappedBreakpointWidth-es.js +18 -0
- package/dist/index.cjs +15 -10
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +14 -10
- package/dist/mergeClassName-cjs.js +12 -0
- package/dist/mergeClassName-es.js +10 -0
- package/dist/primitives/BottomSheet/index.cjs +1 -1
- package/dist/primitives/BottomSheet/index.mjs +1 -1
- package/dist/primitives/ComboboxPrimitive/ComboboxPrimitive.d.ts +2 -2
- package/dist/primitives/ComboboxPrimitive/index.cjs +4 -5
- package/dist/primitives/ComboboxPrimitive/index.mjs +4 -5
- package/dist/primitives/SelectPrimitive/index.cjs +1 -2
- package/dist/primitives/SelectPrimitive/index.mjs +1 -2
- package/dist/primitives/index.cjs +3 -3
- package/dist/primitives/index.mjs +3 -3
- package/dist/scrollEdges-cjs.js +1 -11
- package/dist/scrollEdges-es.js +1 -11
- package/dist/scrollable-cjs.js +58 -0
- package/dist/scrollable-es.js +54 -0
- package/dist/styles.css +759 -42
- package/dist/unstyledPrimitives/index.cjs +720 -478
- package/dist/unstyledPrimitives/index.mjs +663 -421
- package/dist/useCompositeListItem-cjs.js +2108 -361
- package/dist/useCompositeListItem-es.js +2076 -331
- package/dist/useLabel-cjs.js +2 -3
- package/dist/useLabel-es.js +2 -3
- package/dist/useRenderElement-cjs.js +9 -7
- package/dist/useRenderElement-es.js +9 -7
- package/dist/useScrollLock-cjs.js +819 -344
- package/dist/useScrollLock-es.js +732 -296
- package/dist/useScrollToActive-cjs.js +4 -5
- package/dist/useScrollToActive-es.js +1 -2
- package/dist/utils/meta/meta.json +43 -1
- package/package.json +8 -3
- package/dist/floating-ui.react-dom-cjs.js +0 -2103
- package/dist/floating-ui.react-dom-es.js +0 -2072
- package/dist/floating-ui.utils-cjs.js +0 -342
- package/dist/floating-ui.utils-es.js +0 -300
|
@@ -5,7 +5,6 @@ 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');
|
|
9
8
|
var stringifyLocale = require('./stringifyLocale-cjs.js');
|
|
10
9
|
var scrollEdges = require('./scrollEdges-cjs.js');
|
|
11
10
|
|
|
@@ -75,121 +74,112 @@ const stateAttributesMapping = {
|
|
|
75
74
|
...useCompositeListItem.fieldValidityMapping
|
|
76
75
|
};
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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'];
|
|
77
|
+
// Han numerals in digit order, with both zero forms first ('零' at 0, '〇' at 1), so a
|
|
78
|
+
// character's digit value is `max(indexOf - 1, 0)`.
|
|
79
|
+
const HAN_NUMERALS = '零〇一二三四五六七八九';
|
|
80
|
+
|
|
81
|
+
// Arabic-Indic (U+0660–0669), Persian (U+06F0–06F9), and fullwidth (U+FF10–FF19) digits are
|
|
82
|
+
// contiguous ranges whose bases are divisible by 16, so `charCode % 16` is the digit value for
|
|
83
|
+
// all three systems.
|
|
84
|
+
const NON_ASCII_DIGIT_RE = /[٠-٩۰-۹0-9]/g;
|
|
85
|
+
const HAN_RE = /[零〇一二三四五六七八九]/g;
|
|
96
86
|
const PERCENTAGES = ['%', '٪', '%', '﹪'];
|
|
97
87
|
const PERMILLE = ['‰', '؉'];
|
|
98
|
-
const UNICODE_MINUS_SIGNS = ['−', '-', '‒', '–', '—', '﹣'];
|
|
99
|
-
const UNICODE_PLUS_SIGNS = ['+', '﹢'];
|
|
100
88
|
|
|
101
89
|
// Fullwidth punctuation common in CJK inputs
|
|
102
90
|
const FULLWIDTH_DECIMAL = '.'; // U+FF0E
|
|
103
91
|
const FULLWIDTH_GROUP = ','; // U+FF0C
|
|
104
92
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
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('')}]`);
|
|
93
|
+
const PERCENT_RE = /[%٪%﹪]/;
|
|
94
|
+
const PERMILLE_RE = /[‰؉]/;
|
|
95
|
+
const PERCENT_GLOBAL_RE = /[%٪%﹪]/g;
|
|
96
|
+
const PERMILLE_GLOBAL_RE = /[‰؉]/g;
|
|
97
|
+
|
|
98
|
+
// Detection regexes (non-global to avoid lastIndex side effects). Arabic-Indic and Persian
|
|
99
|
+
// digits share one regex because both resolve to the `ar` locale heuristic.
|
|
100
|
+
const ARABIC_PERSIAN_DETECT_RE = /[٠-٩۰-۹]/;
|
|
101
|
+
const HAN_DETECT_RE = /[零〇一二三四五六七八九]/;
|
|
102
|
+
const ANY_NUMERAL_DETECT_RE = /[0-9٠-٩۰-۹0-9零〇一二三四五六七八九]/;
|
|
120
103
|
|
|
121
104
|
// Whether the character is a digit in any numeral system the field accepts.
|
|
122
105
|
function isNumeralChar(char) {
|
|
123
|
-
return
|
|
106
|
+
return ANY_NUMERAL_DETECT_RE.test(char);
|
|
124
107
|
}
|
|
125
108
|
const BASE_NON_NUMERIC_SYMBOLS = ['.', ',', FULLWIDTH_DECIMAL, FULLWIDTH_GROUP, '٫', '٬'];
|
|
126
109
|
const SPACE_SEPARATOR_RE = /\p{Zs}/u;
|
|
127
|
-
|
|
128
|
-
|
|
110
|
+
// Format/bidi control characters (e.g. the LRM/ALM marks RTL locales insert around exponent and
|
|
111
|
+
// currency signs). `parseNumber` strips these, so input validation must treat them as ignorable
|
|
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 = ['-', '−', '-', '‒', '–', '—', '﹣'];
|
|
129
117
|
const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
130
|
-
const escapeClassChar = s => s.replace(/[-\\\]^]/g, m => `\\${m}`); // escape for use inside [...]
|
|
131
|
-
|
|
132
118
|
function shiftDecimal(value, exponentDelta) {
|
|
133
119
|
const [coefficient, exponent = '0'] = String(value).split('e');
|
|
134
120
|
return Number(`${coefficient}e${Number(exponent) + exponentDelta}`);
|
|
135
121
|
}
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
122
|
+
const ANY_MINUS_RE = /[-−-‒–—﹣]/gu;
|
|
123
|
+
const ANY_PLUS_RE = /[++﹢]/gu;
|
|
124
|
+
const ANY_MINUS_DETECT_RE = /[-−-‒–—﹣]/;
|
|
125
|
+
const ANY_PLUS_DETECT_RE = /[++﹢]/;
|
|
126
|
+
|
|
127
|
+
// A representative value with a grouping separator and a fractional part, so that the formatter
|
|
128
|
+
// emits every locale-specific part (group, decimal, currency, unit, literal, exponent, …). Shared
|
|
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
|
+
}
|
|
143
139
|
function getNumberLocaleDetails(locale, options) {
|
|
144
|
-
const parts =
|
|
140
|
+
const parts = getFormatParts(locale, options);
|
|
145
141
|
const result = {};
|
|
146
142
|
parts.forEach(part => {
|
|
147
143
|
result[part.type] = part.value;
|
|
148
144
|
});
|
|
149
145
|
|
|
150
146
|
// The formatting options may omit the decimal separator (e.g. integer formats), so resolve it
|
|
151
|
-
// from the plain locale formatter
|
|
152
|
-
//
|
|
147
|
+
// from the plain locale formatter, which renders one for every locale and numbering system. This
|
|
148
|
+
// overrides any options-derived decimal too, which is safe because the separator is
|
|
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 = '.';
|
|
153
152
|
getFormatter(locale).formatToParts(0.1).forEach(part => {
|
|
154
153
|
if (part.type === 'decimal') {
|
|
155
|
-
|
|
154
|
+
decimal = part.value;
|
|
156
155
|
}
|
|
157
156
|
});
|
|
158
|
-
return
|
|
157
|
+
return {
|
|
158
|
+
...result,
|
|
159
|
+
decimal
|
|
160
|
+
};
|
|
159
161
|
}
|
|
160
162
|
function parseNumber(formattedNumber, locale, options) {
|
|
161
|
-
if (formattedNumber == null) {
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
163
|
// Normalize control characters and whitespace; remove bidi/format controls
|
|
166
|
-
let input =
|
|
164
|
+
let input = formattedNumber.replace(FORMAT_CONTROL_GLOBAL_RE, '').trim();
|
|
167
165
|
|
|
168
166
|
// Normalize unicode minus/plus to ASCII, handle leading/trailing signs
|
|
169
167
|
input = input.replace(ANY_MINUS_RE, '-').replace(ANY_PLUS_RE, '+');
|
|
170
168
|
let isNegative = false;
|
|
171
169
|
|
|
172
|
-
//
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
if (trailing[1] === '-') {
|
|
170
|
+
// Strips a matched sign (leading "-12" / trailing "1234-") while recording negativity.
|
|
171
|
+
const takeSign = (match, sign) => {
|
|
172
|
+
if (sign === '-') {
|
|
176
173
|
isNegative = true;
|
|
177
174
|
}
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const leading = input.match(/^\s*([+-])/);
|
|
182
|
-
if (leading) {
|
|
183
|
-
if (leading[1] === '-') {
|
|
184
|
-
isNegative = true;
|
|
185
|
-
}
|
|
186
|
-
input = input.replace(/^\s*[+-]/, '');
|
|
187
|
-
}
|
|
175
|
+
return '';
|
|
176
|
+
};
|
|
177
|
+
input = input.replace(/([+-])\s*$/, takeSign).replace(/^\s*([+-])/, takeSign);
|
|
188
178
|
|
|
189
179
|
// Heuristic locale detection
|
|
190
180
|
let computedLocale = locale;
|
|
191
181
|
if (computedLocale === undefined) {
|
|
192
|
-
if (
|
|
182
|
+
if (ARABIC_PERSIAN_DETECT_RE.test(input)) {
|
|
193
183
|
computedLocale = 'ar';
|
|
194
184
|
} else if (HAN_DETECT_RE.test(input)) {
|
|
195
185
|
computedLocale = 'zh';
|
|
@@ -221,70 +211,17 @@ function parseNumber(formattedNumber, locale, options) {
|
|
|
221
211
|
groupRegex = new RegExp(escapeRegExp(group), 'g');
|
|
222
212
|
}
|
|
223
213
|
}
|
|
224
|
-
const replacements = [
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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)
|
|
214
|
+
const replacements = [[groupRegex, ''], [new RegExp(escapeRegExp(decimal), 'g'), '.'],
|
|
215
|
+
// Fullwidth/Arabic punctuation
|
|
216
|
+
[/[.٫]/g, '.'],
|
|
217
|
+
// FULLWIDTH_DECIMAL, ARABIC DECIMAL SEPARATOR (U+066B)
|
|
218
|
+
[/[,٬]/g, ''],
|
|
219
|
+
// FULLWIDTH_GROUP, ARABIC THOUSANDS SEPARATOR (U+066C)
|
|
253
220
|
// Currency & unit labels
|
|
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
|
-
},
|
|
221
|
+
[currency ? new RegExp(escapeRegExp(currency), 'g') : null, ''], [unitRegex, ''], [PERCENT_GLOBAL_RE, ''], [PERMILLE_GLOBAL_RE, ''], [exponentSeparator ? new RegExp(escapeRegExp(exponentSeparator), 'g') : null, 'e'],
|
|
270
222
|
// Numeral systems to ASCII digits
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
}) => {
|
|
223
|
+
[NON_ASCII_DIGIT_RE, ch => String(ch.charCodeAt(0) % 16)], [HAN_RE, ch => String(Math.max(HAN_NUMERALS.indexOf(ch) - 1, 0))]];
|
|
224
|
+
let unformatted = replacements.reduce((acc, [regex, replacement]) => {
|
|
288
225
|
return regex ? acc.replace(regex, replacement) : acc;
|
|
289
226
|
}, input);
|
|
290
227
|
|
|
@@ -362,27 +299,18 @@ function removeFloatingPointErrors(value, format) {
|
|
|
362
299
|
}
|
|
363
300
|
return formatter.format(roundedValue) === roundedText ? roundedValue : value;
|
|
364
301
|
}
|
|
365
|
-
function snapToStep(value, base, step,
|
|
302
|
+
function snapToStep(value, base, step, nearest) {
|
|
366
303
|
const stepSize = Math.abs(step);
|
|
367
304
|
const direction = Math.sign(step);
|
|
368
305
|
const tolerance = stepSize * STEP_EPSILON_FACTOR * direction;
|
|
369
306
|
const rawSteps = value - base + tolerance;
|
|
370
|
-
if (
|
|
307
|
+
if (nearest) {
|
|
371
308
|
return base + Math.round(rawSteps / step) * step;
|
|
372
309
|
}
|
|
373
310
|
const snappedSteps = direction > 0 ? Math.floor(rawSteps / stepSize) : Math.ceil(rawSteps / stepSize);
|
|
374
311
|
return base + snappedSteps * stepSize;
|
|
375
312
|
}
|
|
376
|
-
function toValidatedNumber(value, {
|
|
377
|
-
step,
|
|
378
|
-
minWithDefault,
|
|
379
|
-
maxWithDefault,
|
|
380
|
-
minWithZeroDefault,
|
|
381
|
-
format,
|
|
382
|
-
snapOnStep,
|
|
383
|
-
small,
|
|
384
|
-
clamp: shouldClamp
|
|
385
|
-
}) {
|
|
313
|
+
function toValidatedNumber(value, step, minWithDefault, maxWithDefault, minWithZeroDefault, format, snapOnStep, small, shouldClamp) {
|
|
386
314
|
if (value === null) {
|
|
387
315
|
return value;
|
|
388
316
|
}
|
|
@@ -390,8 +318,9 @@ function toValidatedNumber(value, {
|
|
|
390
318
|
if (step != null && snapOnStep && step !== 0) {
|
|
391
319
|
const base = small || minWithDefault === Number.MIN_SAFE_INTEGER ? minWithZeroDefault : minWithDefault;
|
|
392
320
|
|
|
393
|
-
// Snap before clamping so non-step-aligned boundaries stay reachable.
|
|
394
|
-
|
|
321
|
+
// Snap before clamping so non-step-aligned boundaries stay reachable. Small (alt-key) steps
|
|
322
|
+
// snap to the nearest multiple; regular steps snap directionally.
|
|
323
|
+
nextValue = snapToStep(nextValue, base, step, small);
|
|
395
324
|
}
|
|
396
325
|
|
|
397
326
|
// Clamp before rounding so a value just outside a fractional boundary (e.g. `0.4` with
|
|
@@ -446,7 +375,6 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
446
375
|
validityData,
|
|
447
376
|
disabled: fieldDisabled,
|
|
448
377
|
setFilled,
|
|
449
|
-
invalid,
|
|
450
378
|
name: fieldName,
|
|
451
379
|
state: fieldState,
|
|
452
380
|
validation
|
|
@@ -495,48 +423,46 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
495
423
|
const [inputValue, setInputValue] = React__namespace.useState(() => formatNumber(value, locale, format));
|
|
496
424
|
const [inputMode, setInputMode] = React__namespace.useState('numeric');
|
|
497
425
|
const getAllowedNonNumericKeys = useScrollLock.useStableCallback(() => {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
426
|
+
const parts = getFormatParts(locale, format);
|
|
427
|
+
const keys = new Set(BASE_NON_NUMERIC_SYMBOLS);
|
|
428
|
+
const addAll = chars => chars.forEach(char => keys.add(char));
|
|
429
|
+
|
|
430
|
+
// Integer formats omit the decimal from `parts`, so fall back to the locale's separator in that
|
|
431
|
+
// case; it must stay typeable regardless of whether the format renders a fraction.
|
|
432
|
+
const decimal = parts.find(part => part.type === 'decimal')?.value ?? getNumberLocaleDetails(locale, format).decimal;
|
|
433
|
+
keys.add(decimal);
|
|
434
|
+
|
|
435
|
+
// Allow every non-digit character the formatter renders — separators, currency symbols, units
|
|
436
|
+
// (e.g. `km/h`, `°C`), exponent separators, and locale literals — decomposed per character
|
|
437
|
+
// because the input validates the typed string one character at a time. Deriving these from
|
|
438
|
+
// the formatter covers multi-character and locale-specific symbols of every part type
|
|
439
|
+
// uniformly. `compact` suffixes (e.g. `K`/`M`) are excluded because `parseNumber` can't reverse
|
|
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)) {
|
|
512
447
|
keys.add(' ');
|
|
513
448
|
}
|
|
514
|
-
}
|
|
449
|
+
});
|
|
515
450
|
const allowPercentSymbols = formatStyle === 'percent' || formatStyle === 'unit' && format?.unit === 'percent';
|
|
516
451
|
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.
|
|
517
454
|
if (allowPercentSymbols) {
|
|
518
|
-
PERCENTAGES
|
|
455
|
+
addAll(PERCENTAGES);
|
|
519
456
|
}
|
|
520
457
|
if (allowPermilleSymbols) {
|
|
521
|
-
PERMILLE
|
|
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
|
-
}
|
|
458
|
+
addAll(PERMILLE);
|
|
533
459
|
}
|
|
534
460
|
|
|
535
461
|
// Allow plus sign in all cases; minus sign when negatives are valid, or when out-of-range
|
|
536
462
|
// entry is allowed so native underflow validation can be triggered from the keyboard.
|
|
537
|
-
PLUS_SIGNS_WITH_ASCII
|
|
463
|
+
addAll(PLUS_SIGNS_WITH_ASCII);
|
|
538
464
|
if (minWithDefault < 0 || allowOutOfRange) {
|
|
539
|
-
MINUS_SIGNS_WITH_ASCII
|
|
465
|
+
addAll(MINUS_SIGNS_WITH_ASCII);
|
|
540
466
|
}
|
|
541
467
|
return keys;
|
|
542
468
|
});
|
|
@@ -554,21 +480,13 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
554
480
|
const dir = details.direction;
|
|
555
481
|
|
|
556
482
|
// Direct text entry (typing, pasting, clearing, autofill) behaves natively; step-based
|
|
557
|
-
// interactions (keyboard arrows, buttons, wheel, scrub) do not.
|
|
558
|
-
|
|
483
|
+
// interactions (keyboard arrows, buttons, wheel, scrub) do not. All direct-entry reasons
|
|
484
|
+
// (`input-change`, `input-clear`, `input-blur`, `input-paste`) share the `input-` prefix.
|
|
485
|
+
const isInputReason = details.reason.startsWith('input-') || details.reason === useScrollLock.none;
|
|
559
486
|
|
|
560
487
|
// Only allow out-of-range values for direct text entry. Step-based interactions still clamp.
|
|
561
488
|
const shouldClampValue = !allowOutOfRange || !isInputReason;
|
|
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
|
-
});
|
|
489
|
+
const validatedValue = toValidatedNumber(unvalidatedValue, dir ? getStepAmount(eventWithOptionalKeyState) * dir : undefined, minWithDefault, maxWithDefault, minWithZeroDefault, formatOptionsRef.current, snapOnStep, eventWithOptionalKeyState?.altKey ?? false, shouldClampValue);
|
|
572
490
|
|
|
573
491
|
// Notify about a change even when the numeric value is unchanged for input reasons: the
|
|
574
492
|
// typed text may clamp/snap to the current value, or differ while validation normalizes
|
|
@@ -678,10 +596,10 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
678
596
|
const changed = incrementValue(amount, {
|
|
679
597
|
direction: event.deltaY > 0 ? -1 : 1,
|
|
680
598
|
event,
|
|
681
|
-
reason:
|
|
599
|
+
reason: useScrollLock.wheel
|
|
682
600
|
});
|
|
683
601
|
if (changed) {
|
|
684
|
-
onValueCommitted(lastChangedValueRef.current
|
|
602
|
+
onValueCommitted(lastChangedValueRef.current, useScrollLock.createGenericEventDetails(useScrollLock.wheel, event));
|
|
685
603
|
}
|
|
686
604
|
}
|
|
687
605
|
return useScrollLock.addEventListener(element, 'wheel', handleWheel);
|
|
@@ -697,12 +615,8 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
697
615
|
}), [fieldState, disabled, readOnly, required, value, inputValue, isScrubbing]);
|
|
698
616
|
const contextValue = React__namespace.useMemo(() => ({
|
|
699
617
|
inputRef,
|
|
700
|
-
inputValue,
|
|
701
|
-
value,
|
|
702
618
|
minWithDefault,
|
|
703
619
|
maxWithDefault,
|
|
704
|
-
disabled,
|
|
705
|
-
readOnly,
|
|
706
620
|
id,
|
|
707
621
|
setValue,
|
|
708
622
|
incrementValue,
|
|
@@ -714,19 +628,16 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
714
628
|
hasPendingCommitRef,
|
|
715
629
|
name,
|
|
716
630
|
nameProp,
|
|
717
|
-
required,
|
|
718
|
-
invalid,
|
|
719
631
|
inputMode,
|
|
720
632
|
getAllowedNonNumericKeys,
|
|
721
633
|
min,
|
|
722
634
|
max,
|
|
723
635
|
setInputValue,
|
|
724
636
|
locale,
|
|
725
|
-
isScrubbing,
|
|
726
637
|
setIsScrubbing,
|
|
727
638
|
state,
|
|
728
639
|
onValueCommitted
|
|
729
|
-
}), [inputRef,
|
|
640
|
+
}), [inputRef, minWithDefault, maxWithDefault, id, setValue, incrementValue, getStepAmount, formatOptionsRef, valueRef, name, nameProp, inputMode, getAllowedNonNumericKeys, min, max, setInputValue, locale, state, onValueCommitted]);
|
|
730
641
|
const element = useRenderElement.useRenderElement('div', componentProps, {
|
|
731
642
|
ref: forwardedRef,
|
|
732
643
|
state,
|
|
@@ -741,7 +652,7 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
741
652
|
inputRef.current?.focus();
|
|
742
653
|
},
|
|
743
654
|
onChange(event) {
|
|
744
|
-
// Workaround for https://github.com/
|
|
655
|
+
// Workaround for https://github.com/react/react/issues/9023
|
|
745
656
|
if (event.nativeEvent.defaultPrevented || disabled || readOnly) {
|
|
746
657
|
return;
|
|
747
658
|
}
|
|
@@ -766,7 +677,7 @@ const NumberFieldRoot = /*#__PURE__*/React__namespace.forwardRef(function Number
|
|
|
766
677
|
min: min,
|
|
767
678
|
max: max
|
|
768
679
|
// stepMismatch validation is broken unless an explicit `min` is added.
|
|
769
|
-
// See https://github.com/
|
|
680
|
+
// See https://github.com/react/react/issues/12334.
|
|
770
681
|
,
|
|
771
682
|
step: stepProp,
|
|
772
683
|
disabled: disabled,
|
|
@@ -863,7 +774,6 @@ function isTouchLikePointerType(pointerType) {
|
|
|
863
774
|
function usePressAndHold(params) {
|
|
864
775
|
const {
|
|
865
776
|
disabled,
|
|
866
|
-
readOnly = false,
|
|
867
777
|
tick,
|
|
868
778
|
onStop,
|
|
869
779
|
tickDelay = DEFAULT_TICK_DELAY,
|
|
@@ -898,7 +808,7 @@ function usePressAndHold(params) {
|
|
|
898
808
|
if (!element) {
|
|
899
809
|
return;
|
|
900
810
|
}
|
|
901
|
-
const win =
|
|
811
|
+
const win = useScrollLock.getWindow(element);
|
|
902
812
|
function handleContextMenu(event) {
|
|
903
813
|
event.preventDefault();
|
|
904
814
|
}
|
|
@@ -943,8 +853,7 @@ function usePressAndHold(params) {
|
|
|
943
853
|
isTouchingButtonRef.current = false;
|
|
944
854
|
},
|
|
945
855
|
onPointerDown(event) {
|
|
946
|
-
|
|
947
|
-
if (event.defaultPrevented || !isMainButton || disabled || readOnly) {
|
|
856
|
+
if (event.defaultPrevented || event.button || disabled) {
|
|
948
857
|
return;
|
|
949
858
|
}
|
|
950
859
|
pointerTypeRef.current = event.pointerType;
|
|
@@ -987,12 +896,10 @@ function usePressAndHold(params) {
|
|
|
987
896
|
}
|
|
988
897
|
},
|
|
989
898
|
onPointerMove(event) {
|
|
990
|
-
if (disabled ||
|
|
899
|
+
if (disabled || !isTouchLikePointerType(event.pointerType) || !isPressedRef.current) {
|
|
991
900
|
return;
|
|
992
901
|
}
|
|
993
|
-
|
|
994
|
-
movesAfterTouchRef.current += 1;
|
|
995
|
-
}
|
|
902
|
+
movesAfterTouchRef.current += 1;
|
|
996
903
|
const {
|
|
997
904
|
x,
|
|
998
905
|
y
|
|
@@ -1004,7 +911,7 @@ function usePressAndHold(params) {
|
|
|
1004
911
|
}
|
|
1005
912
|
},
|
|
1006
913
|
onMouseEnter(event) {
|
|
1007
|
-
if (event.defaultPrevented || disabled ||
|
|
914
|
+
if (event.defaultPrevented || disabled || !isPressedRef.current || isTouchingButtonRef.current || isTouchLikePointerType(pointerTypeRef.current)) {
|
|
1008
915
|
return;
|
|
1009
916
|
}
|
|
1010
917
|
startAutoChange(event.nativeEvent);
|
|
@@ -1037,32 +944,47 @@ function usePressAndHold(params) {
|
|
|
1037
944
|
};
|
|
1038
945
|
}
|
|
1039
946
|
|
|
1040
|
-
const CHANGE_VALUE_TICK_DELAY = 60;
|
|
1041
|
-
const START_AUTO_CHANGE_DELAY = 400;
|
|
1042
|
-
const SCROLLING_POINTER_MOVE_DISTANCE = 8;
|
|
1043
|
-
|
|
1044
947
|
const SELECT_NONE_STYLE = {
|
|
1045
948
|
WebkitUserSelect: 'none',
|
|
1046
949
|
userSelect: 'none'
|
|
1047
950
|
};
|
|
1048
|
-
|
|
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;
|
|
1049
964
|
const {
|
|
1050
965
|
allowInputSyncRef,
|
|
1051
|
-
disabled,
|
|
1052
966
|
formatOptionsRef,
|
|
1053
967
|
getStepAmount,
|
|
1054
968
|
id,
|
|
1055
969
|
incrementValue,
|
|
1056
970
|
inputRef,
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
locale,
|
|
1060
|
-
readOnly,
|
|
971
|
+
maxWithDefault,
|
|
972
|
+
minWithDefault,
|
|
1061
973
|
setValue,
|
|
974
|
+
state,
|
|
1062
975
|
valueRef,
|
|
976
|
+
locale,
|
|
1063
977
|
lastChangedValueRef,
|
|
1064
978
|
onValueCommitted
|
|
1065
|
-
} =
|
|
979
|
+
} = useNumberFieldRootContext();
|
|
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;
|
|
1066
988
|
const pressReason = isIncrement ? useScrollLock.incrementPress : useScrollLock.decrementPress;
|
|
1067
989
|
function commitValue(nativeEvent) {
|
|
1068
990
|
const shouldCommitInputValue = !allowInputSyncRef.current;
|
|
@@ -1098,9 +1020,6 @@ function useNumberFieldButton(params) {
|
|
|
1098
1020
|
} = usePressAndHold({
|
|
1099
1021
|
disabled: disabled || readOnly,
|
|
1100
1022
|
elementRef: inputRef,
|
|
1101
|
-
tickDelay: CHANGE_VALUE_TICK_DELAY,
|
|
1102
|
-
startDelay: START_AUTO_CHANGE_DELAY,
|
|
1103
|
-
scrollDistance: SCROLLING_POINTER_MOVE_DISTANCE,
|
|
1104
1023
|
tick(triggerEvent) {
|
|
1105
1024
|
const amount = getStepAmount(triggerEvent);
|
|
1106
1025
|
return incrementValue(amount, {
|
|
@@ -1145,8 +1064,7 @@ function useNumberFieldButton(params) {
|
|
|
1145
1064
|
}
|
|
1146
1065
|
},
|
|
1147
1066
|
onPointerDown(event) {
|
|
1148
|
-
|
|
1149
|
-
if (event.defaultPrevented || readOnly || !isMainButton || disabled) {
|
|
1067
|
+
if (event.defaultPrevented || readOnly || event.button || disabled) {
|
|
1150
1068
|
return;
|
|
1151
1069
|
}
|
|
1152
1070
|
|
|
@@ -1162,61 +1080,6 @@ function useNumberFieldButton(params) {
|
|
|
1162
1080
|
pointerHandlers.onPointerDown(event);
|
|
1163
1081
|
}
|
|
1164
1082
|
};
|
|
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
|
-
});
|
|
1220
1083
|
const {
|
|
1221
1084
|
getButtonProps,
|
|
1222
1085
|
buttonRef
|
|
@@ -1279,31 +1142,33 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1279
1142
|
} = componentProps;
|
|
1280
1143
|
const {
|
|
1281
1144
|
allowInputSyncRef,
|
|
1282
|
-
disabled,
|
|
1283
1145
|
formatOptionsRef,
|
|
1284
1146
|
getAllowedNonNumericKeys,
|
|
1285
1147
|
getStepAmount,
|
|
1286
1148
|
id,
|
|
1287
1149
|
incrementValue,
|
|
1288
1150
|
inputMode,
|
|
1289
|
-
inputValue,
|
|
1290
1151
|
max,
|
|
1291
1152
|
min,
|
|
1292
1153
|
name,
|
|
1293
1154
|
nameProp,
|
|
1294
|
-
readOnly,
|
|
1295
|
-
required,
|
|
1296
1155
|
setValue,
|
|
1297
1156
|
state,
|
|
1298
1157
|
setInputValue,
|
|
1299
1158
|
locale,
|
|
1300
1159
|
inputRef,
|
|
1301
|
-
value,
|
|
1302
1160
|
onValueCommitted,
|
|
1303
1161
|
lastChangedValueRef,
|
|
1304
1162
|
hasPendingCommitRef,
|
|
1305
1163
|
valueRef
|
|
1306
1164
|
} = useNumberFieldRootContext();
|
|
1165
|
+
const {
|
|
1166
|
+
disabled,
|
|
1167
|
+
readOnly,
|
|
1168
|
+
required,
|
|
1169
|
+
value,
|
|
1170
|
+
inputValue
|
|
1171
|
+
} = state;
|
|
1307
1172
|
const {
|
|
1308
1173
|
clearErrors
|
|
1309
1174
|
} = useCompositeListItem.useFormContext();
|
|
@@ -1438,7 +1303,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1438
1303
|
blockRevalidationRef.current = false;
|
|
1439
1304
|
return;
|
|
1440
1305
|
}
|
|
1441
|
-
committedValue = lastChangedValueRef.current
|
|
1306
|
+
committedValue = lastChangedValueRef.current;
|
|
1442
1307
|
// If validation normalized back to the current value, `useValueChanged` won't fire to
|
|
1443
1308
|
// reset the flag, so reset it here or the next external change won't revalidate.
|
|
1444
1309
|
if (committedValue === value) {
|
|
@@ -1459,7 +1324,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1459
1324
|
}
|
|
1460
1325
|
},
|
|
1461
1326
|
onChange(event) {
|
|
1462
|
-
// Workaround for https://github.com/
|
|
1327
|
+
// Workaround for https://github.com/react/react/issues/9023
|
|
1463
1328
|
if (event.nativeEvent.defaultPrevented) {
|
|
1464
1329
|
return;
|
|
1465
1330
|
}
|
|
@@ -1475,7 +1340,10 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1475
1340
|
// currently parseable into a number. This preserves good UX for IME
|
|
1476
1341
|
// composition/partial input while still providing live numeric updates when possible.
|
|
1477
1342
|
const allowedNonNumericKeys = getAllowedNonNumericKeys();
|
|
1478
|
-
const isValidCharacterString = Array.from(targetValue).every(ch => isNumeralChar(ch) || ANY_MINUS_DETECT_RE.test(ch) || allowedNonNumericKeys.has(ch)
|
|
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));
|
|
1479
1347
|
if (!isValidCharacterString) {
|
|
1480
1348
|
return;
|
|
1481
1349
|
}
|
|
@@ -1525,7 +1393,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1525
1393
|
if (event.key === symbol) {
|
|
1526
1394
|
const symbolIndex = inputValue.indexOf(symbol);
|
|
1527
1395
|
const isSymbolHighlighted = selectionContainsIndex(symbolIndex);
|
|
1528
|
-
isAllowedNonNumericKey =
|
|
1396
|
+
isAllowedNonNumericKey = symbolIndex === -1 || isAllSelected || isSymbolHighlighted;
|
|
1529
1397
|
}
|
|
1530
1398
|
});
|
|
1531
1399
|
const isNavigateKey = NAVIGATE_KEYS.has(event.key);
|
|
@@ -1538,12 +1406,18 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1538
1406
|
event.which === 229 || event.altKey && !isStepKey || event.ctrlKey || event.metaKey || isAllowedNonNumericKey || isNumeralChar(event.key) || isNavigateKey) {
|
|
1539
1407
|
return;
|
|
1540
1408
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1409
|
+
|
|
1410
|
+
// Home/End jump to the corresponding bound, but only when that bound is defined.
|
|
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
|
+
}
|
|
1543
1417
|
|
|
1544
1418
|
// Let the browser handle multi-character keys we don't act on (PageUp, Insert, F-keys,
|
|
1545
1419
|
// Home/End without min/max); invalid single characters are still blocked below.
|
|
1546
|
-
if (event.key.length > 1 && !isStepKey &&
|
|
1420
|
+
if (event.key.length > 1 && !isStepKey && boundaryValue === null) {
|
|
1547
1421
|
return;
|
|
1548
1422
|
}
|
|
1549
1423
|
|
|
@@ -1555,12 +1429,14 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1555
1429
|
const amount = getStepAmount(event);
|
|
1556
1430
|
|
|
1557
1431
|
// Prevent insertion of text or caret from moving.
|
|
1558
|
-
|
|
1432
|
+
event.preventDefault();
|
|
1433
|
+
event.stopPropagation();
|
|
1559
1434
|
const commitDetails = useScrollLock.createGenericEventDetails(useScrollLock.keyboard, nativeEvent);
|
|
1560
1435
|
let changed = false;
|
|
1561
|
-
if (
|
|
1436
|
+
if (isStepKey || boundaryValue !== null) {
|
|
1562
1437
|
allowInputSyncRef.current = true;
|
|
1563
|
-
|
|
1438
|
+
}
|
|
1439
|
+
if (isStepKey) {
|
|
1564
1440
|
// When stepping from the synced numeric state, refresh the commit ref to the current
|
|
1565
1441
|
// value so a canceled step can't commit a stale `lastChangedValueRef` left over from an
|
|
1566
1442
|
// earlier change (mirrors the button path).
|
|
@@ -1573,15 +1449,14 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1573
1449
|
event: nativeEvent,
|
|
1574
1450
|
reason: useScrollLock.keyboard
|
|
1575
1451
|
});
|
|
1576
|
-
} else if (
|
|
1577
|
-
|
|
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));
|
|
1452
|
+
} else if (boundaryValue !== null) {
|
|
1453
|
+
changed = setValue(boundaryValue, useScrollLock.createChangeEventDetails(useScrollLock.keyboard, nativeEvent));
|
|
1582
1454
|
}
|
|
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.
|
|
1583
1458
|
if (changed) {
|
|
1584
|
-
onValueCommitted(lastChangedValueRef.current
|
|
1459
|
+
onValueCommitted(lastChangedValueRef.current, commitDetails);
|
|
1585
1460
|
}
|
|
1586
1461
|
},
|
|
1587
1462
|
onPaste(event) {
|
|
@@ -1592,6 +1467,7 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1592
1467
|
try {
|
|
1593
1468
|
pastedData = event.clipboardData?.getData('text/plain') ?? '';
|
|
1594
1469
|
} catch {
|
|
1470
|
+
/* istanbul ignore else -- `process.env.NODE_ENV` is a build-time constant under test */
|
|
1595
1471
|
if (process.env.NODE_ENV !== 'production') {
|
|
1596
1472
|
const ownerStackMessage = useScrollLock.SafeReact.captureOwnerStack?.() || '';
|
|
1597
1473
|
useRenderElement.warn('<NumberField.Input> could not read clipboard text during paste handling.', ownerStackMessage);
|
|
@@ -1604,9 +1480,12 @@ const NumberFieldInput = /*#__PURE__*/React__namespace.forwardRef(function Numbe
|
|
|
1604
1480
|
|
|
1605
1481
|
// Insert the pasted text at the caret/selection instead of replacing the entire value,
|
|
1606
1482
|
// 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.
|
|
1607
1486
|
const input = event.currentTarget;
|
|
1608
|
-
const selectionStart = input.selectionStart
|
|
1609
|
-
const selectionEnd = input.selectionEnd
|
|
1487
|
+
const selectionStart = input.selectionStart;
|
|
1488
|
+
const selectionEnd = input.selectionEnd;
|
|
1610
1489
|
const nextText = inputValue.slice(0, selectionStart) + pastedData + inputValue.slice(selectionEnd);
|
|
1611
1490
|
const parsedValue = parseNumber(nextText, locale, formatOptionsRef.current);
|
|
1612
1491
|
if (parsedValue !== null) {
|