@carbon/react 1.94.0 → 1.94.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.
@@ -39,6 +39,69 @@ const defaultTranslations = {
39
39
  const defaultTranslateWithId = messageId => {
40
40
  return defaultTranslations[messageId];
41
41
  };
42
+ const getSeparators = locale => {
43
+ const numberWithGroupAndDecimal = 1234567.89;
44
+ const formatted = new Intl.NumberFormat(locale).format(numberWithGroupAndDecimal);
45
+
46
+ // Extract separators using regex
47
+ const match = formatted.match(/(\D+)\d{3}(\D+)\d{2}$/);
48
+ if (match) {
49
+ const groupSeparator = match[1];
50
+ const decimalSeparator = match[2];
51
+ return {
52
+ groupSeparator,
53
+ decimalSeparator
54
+ };
55
+ } else {
56
+ return {
57
+ groupSeparator: null,
58
+ decimalSeparator: null
59
+ };
60
+ }
61
+ };
62
+ const validateNumberSeparators = (input, locale) => {
63
+ // allow empty string
64
+ if (input === '' || Number.isNaN(input)) {
65
+ return true;
66
+ }
67
+ const {
68
+ groupSeparator,
69
+ decimalSeparator
70
+ } = getSeparators(locale);
71
+ if (!decimalSeparator) {
72
+ return !isNaN(Number(input));
73
+ }
74
+ const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
75
+ let group = '';
76
+ if (groupSeparator) {
77
+ if (groupSeparator.trim() === '') {
78
+ group = '[\\u00A0\\u202F\\s]'; // handle NBSP, narrow NBSP, space
79
+ } else {
80
+ group = esc(groupSeparator);
81
+ }
82
+ }
83
+ const decimal = esc(decimalSeparator);
84
+
85
+ // Regex for:
86
+ // - integers (with/without grouping)
87
+ // - optional decimal with 0+ digits after separator
88
+ const regex = new RegExp(`^-?\\d{1,3}(${group}\\d{3})*(${decimal}\\d*)?$|^-?\\d+(${decimal}\\d*)?$`);
89
+ if (!regex.test(input)) {
90
+ return false;
91
+ }
92
+
93
+ // Normalize
94
+ let normalized = input;
95
+ if (groupSeparator) {
96
+ if (groupSeparator.trim() === '') {
97
+ normalized = normalized?.replace(/[\u00A0\u202F\s]/g, '');
98
+ } else {
99
+ normalized = normalized?.split(groupSeparator).join('');
100
+ }
101
+ }
102
+ normalized = normalized?.replace(decimalSeparator, '.');
103
+ return !isNaN(Number(normalized));
104
+ };
42
105
 
43
106
  // eslint-disable-next-line react/display-name -- https://github.com/carbon-design-system/carbon/issues/20452
44
107
  const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
@@ -726,4 +789,4 @@ function disableWheel(e) {
726
789
  e.preventDefault();
727
790
  }
728
791
 
729
- export { NumberInput };
792
+ export { NumberInput, validateNumberSeparators };
@@ -5,4 +5,4 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  export { default as NumberInputSkeleton } from './NumberInput.Skeleton';
8
- export { NumberInput, type NumberInputProps } from './NumberInput';
8
+ export { NumberInput, validateNumberSeparators, type NumberInputProps, } from './NumberInput';
package/es/index.js CHANGED
@@ -102,7 +102,7 @@ export { FilterableMultiSelect } from './components/MultiSelect/FilterableMultiS
102
102
  export { MultiSelect } from './components/MultiSelect/MultiSelect.js';
103
103
  export { ActionableNotification, Callout, InlineNotification, NotificationActionButton, NotificationButton, StaticNotification, ToastNotification } from './components/Notification/Notification.js';
104
104
  export { default as NumberInputSkeleton } from './components/NumberInput/NumberInput.Skeleton.js';
105
- export { NumberInput } from './components/NumberInput/NumberInput.js';
105
+ export { NumberInput, validateNumberSeparators } from './components/NumberInput/NumberInput.js';
106
106
  export { default as OrderedList } from './components/OrderedList/OrderedList.js';
107
107
  export { default as OverflowMenu } from './components/OverflowMenu/index.js';
108
108
  export { default as OverflowMenuItem } from './components/OverflowMenuItem/OverflowMenuItem.js';
@@ -41,6 +41,69 @@ const defaultTranslations = {
41
41
  const defaultTranslateWithId = messageId => {
42
42
  return defaultTranslations[messageId];
43
43
  };
44
+ const getSeparators = locale => {
45
+ const numberWithGroupAndDecimal = 1234567.89;
46
+ const formatted = new Intl.NumberFormat(locale).format(numberWithGroupAndDecimal);
47
+
48
+ // Extract separators using regex
49
+ const match = formatted.match(/(\D+)\d{3}(\D+)\d{2}$/);
50
+ if (match) {
51
+ const groupSeparator = match[1];
52
+ const decimalSeparator = match[2];
53
+ return {
54
+ groupSeparator,
55
+ decimalSeparator
56
+ };
57
+ } else {
58
+ return {
59
+ groupSeparator: null,
60
+ decimalSeparator: null
61
+ };
62
+ }
63
+ };
64
+ const validateNumberSeparators = (input, locale) => {
65
+ // allow empty string
66
+ if (input === '' || Number.isNaN(input)) {
67
+ return true;
68
+ }
69
+ const {
70
+ groupSeparator,
71
+ decimalSeparator
72
+ } = getSeparators(locale);
73
+ if (!decimalSeparator) {
74
+ return !isNaN(Number(input));
75
+ }
76
+ const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
77
+ let group = '';
78
+ if (groupSeparator) {
79
+ if (groupSeparator.trim() === '') {
80
+ group = '[\\u00A0\\u202F\\s]'; // handle NBSP, narrow NBSP, space
81
+ } else {
82
+ group = esc(groupSeparator);
83
+ }
84
+ }
85
+ const decimal = esc(decimalSeparator);
86
+
87
+ // Regex for:
88
+ // - integers (with/without grouping)
89
+ // - optional decimal with 0+ digits after separator
90
+ const regex = new RegExp(`^-?\\d{1,3}(${group}\\d{3})*(${decimal}\\d*)?$|^-?\\d+(${decimal}\\d*)?$`);
91
+ if (!regex.test(input)) {
92
+ return false;
93
+ }
94
+
95
+ // Normalize
96
+ let normalized = input;
97
+ if (groupSeparator) {
98
+ if (groupSeparator.trim() === '') {
99
+ normalized = normalized?.replace(/[\u00A0\u202F\s]/g, '');
100
+ } else {
101
+ normalized = normalized?.split(groupSeparator).join('');
102
+ }
103
+ }
104
+ normalized = normalized?.replace(decimalSeparator, '.');
105
+ return !isNaN(Number(normalized));
106
+ };
44
107
 
45
108
  // eslint-disable-next-line react/display-name -- https://github.com/carbon-design-system/carbon/issues/20452
46
109
  const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
@@ -729,3 +792,4 @@ function disableWheel(e) {
729
792
  }
730
793
 
731
794
  exports.NumberInput = NumberInput;
795
+ exports.validateNumberSeparators = validateNumberSeparators;
@@ -5,4 +5,4 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  export { default as NumberInputSkeleton } from './NumberInput.Skeleton';
8
- export { NumberInput, type NumberInputProps } from './NumberInput';
8
+ export { NumberInput, validateNumberSeparators, type NumberInputProps, } from './NumberInput';
package/lib/index.js CHANGED
@@ -366,6 +366,7 @@ exports.StaticNotification = Notification.StaticNotification;
366
366
  exports.ToastNotification = Notification.ToastNotification;
367
367
  exports.NumberInputSkeleton = NumberInput_Skeleton.default;
368
368
  exports.NumberInput = NumberInput.NumberInput;
369
+ exports.validateNumberSeparators = NumberInput.validateNumberSeparators;
369
370
  exports.OrderedList = OrderedList.default;
370
371
  exports.OverflowMenu = index$c.default;
371
372
  exports.OverflowMenuItem = OverflowMenuItem.default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@carbon/react",
3
3
  "description": "React components for the Carbon Design System",
4
- "version": "1.94.0",
4
+ "version": "1.94.2",
5
5
  "license": "Apache-2.0",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -52,10 +52,10 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@babel/runtime": "^7.27.3",
55
- "@carbon/feature-flags": "^0.32.0",
55
+ "@carbon/feature-flags": "0.32.0",
56
56
  "@carbon/icons-react": "^11.70.0",
57
57
  "@carbon/layout": "^11.43.0",
58
- "@carbon/styles": "^1.93.0",
58
+ "@carbon/styles": "^1.93.1",
59
59
  "@carbon/utilities": "^0.12.0",
60
60
  "@floating-ui/react": "^0.27.4",
61
61
  "@ibm/telemetry-js": "^1.5.0",
@@ -139,5 +139,5 @@
139
139
  "**/*.scss",
140
140
  "**/*.css"
141
141
  ],
142
- "gitHead": "44f7fd230585f66d32c839d1c11d2992d5929294"
142
+ "gitHead": "28d42f04dad50ed299b0587f7a586a0b1780cebc"
143
143
  }