@genesislcap/foundation-ui 14.482.1-alpha-c9fc44ab6.0 → 14.482.1

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.
@@ -28,6 +28,7 @@ export declare class NumberField extends NumberField_base {
28
28
  stepUp(): void;
29
29
  stepDown(): void;
30
30
  hasSeparatorWithoutDecimals(): RegExp;
31
+ private restoreControlToCommittedValue;
31
32
  handleTextInput(): void;
32
33
  valueChanged(previous: any, next: any): void;
33
34
  handleBlur(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"number-field.d.ts","sourceRoot":"","sources":["../../../src/number-field/number-field.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE5E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;;;;;;;;;;;AAkBrE;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,gBAA2B;IACpD,YAAY,EAAE,MAAM,CAAS;IACnC,OAAO,CAAC,mBAAmB;IAKA,QAAQ,EAAE,OAAO,CAAS;IAE/C,KAAK,EAAE,MAAM,CAAM;IACE,cAAc,EAAE,OAAO,CAAS;IACrD,MAAM,EAAE,MAAM,CAAW;IACJ,iBAAiB,EAAE,OAAO,CAAS;IACxD,qBAAqB,EAAE,MAAM,CAAuB;IAE1D,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAG/B;IAEF,MAAM,IAAI,IAAI;IAuCd,QAAQ,IAAI,IAAI;IA0ChB,2BAA2B;IAK3B,eAAe,IAAI,IAAI;IAoCvB,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IA+C5C,UAAU,IAAI,IAAI;CA4BnB;AAED,eAAO,MAAM,kCAAkC,EAAE,cAGhD,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;CAWpC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,oTAMhC,CAAC"}
1
+ {"version":3,"file":"number-field.d.ts","sourceRoot":"","sources":["../../../src/number-field/number-field.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE5E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;;;;;;;;;;;AAoBrE;;;;GAIG;AACH,qBAAa,WAAY,SAAQ,gBAA2B;IACpD,YAAY,EAAE,MAAM,CAAS;IACnC,OAAO,CAAC,mBAAmB;IAKA,QAAQ,EAAE,OAAO,CAAS;IAQrD,KAAK,EAAE,MAAM,CAAM;IACQ,cAAc,EAAE,OAAO,CAAS;IACrD,MAAM,EAAE,MAAM,CAAW;IACJ,iBAAiB,EAAE,OAAO,CAAS;IACxD,qBAAqB,EAAE,MAAM,CAAuB;IAE1D,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAG/B;IAEF,MAAM,IAAI,IAAI;IAuCd,QAAQ,IAAI,IAAI;IA0ChB,2BAA2B;IAK3B,OAAO,CAAC,8BAA8B;IAetC,eAAe,IAAI,IAAI;IA+DvB,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAqC5C,UAAU,IAAI,IAAI;CA4BnB;AAED,eAAO,MAAM,kCAAkC,EAAE,cAGhD,CAAC;AAEF,eAAO,MAAM,wBAAwB;;;CAWpC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,oTAMhC,CAAC"}
@@ -1,6 +1,18 @@
1
1
  export declare const getSeparatorsFromParser: (value: string, locale: string) => {
2
2
  decimalSeparator: string;
3
3
  };
4
+ /**
5
+ * Strips locale grouping and normalizes the decimal separator to `.` without using `Number`
6
+ * (so integers beyond `Number.MAX_SAFE_INTEGER` keep every digit).
7
+ */
8
+ export declare const normalizeLocaleNumberString: (inputVal: string, locale: string) => string;
9
+ /** Counts locale decimal separator characters in the raw control text (ignores grouping). */
10
+ export declare const countLocaleDecimalSeparators: (input: string, locale: string) => number;
11
+ export declare const hasMultipleDecimalSeparators: (input: string, locale: string) => boolean;
12
+ /** True when the committed value is a whole number already using the full significant-digit budget. */
13
+ export declare const isIntegerAtMaximumInputPrecision: (value: string, locale: string, precision: number) => boolean;
14
+ /** User typed a decimal separator while the integer part is already at max precision. */
15
+ export declare const rejectsDecimalAtMaximumIntegerPrecision: (committedValue: string, controlText: string, locale: string, precision: number) => boolean;
4
16
  export declare const formatControlValue: (value: string, locale: string) => string;
5
17
  export declare const sanitiseInputVal: (inputVal: string, locale: string) => string;
6
18
  export declare const toIntlNumberFormat: (value: string, locale: string, options: Intl.NumberFormatOptions) => string;
@@ -22,7 +34,7 @@ export declare const preserveTrailingZerosAfterParse: (value: string, parsedValu
22
34
  * @returns The validated and formatted value
23
35
  */
24
36
  export declare const validateValue: (value: string | number, locale: string, withFormatting?: boolean, precision?: number) => string;
25
- export declare const validateInput: (value: string, locale: string, precision?: number) => string;
37
+ export declare const validateInput: (value: string, locale: string, precision?: number, committedValue?: string) => string;
26
38
  export declare const validateMinMax: (value: string, locale: string, withFormatting: boolean, min: number, max: number, precision?: number) => string;
27
39
  /**
28
40
  * Performs high precision addition using Pure JavaScript (NO BigInt needed!)
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/number-field/utils.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,uBAAuB,GAClC,OAAO,MAAM,EACb,QAAQ,MAAM,KACb;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAI5B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAWlE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,KAAG,MAEnE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,SAAS,IAAI,CAAC,mBAAmB,KAChC,MAEF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,MAAM,EACb,SAAS,IAAI,CAAC,mBAAmB,KAChC,MAyCF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,OAAO,MAAM,EACb,aAAa,MAAM,EACnB,QAAQ,MAAM,KACb,MA+BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,GAAG,MAAM,EACtB,QAAQ,MAAM,EACd,wBAAqB,EACrB,YAAY,MAAM,KACjB,MA8DF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,MAGjF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,uBAAqB,EACrB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,YAAY,MAAM,KACjB,MAaF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAyBvD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAyB5D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,KAAG,MAgDnF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,KAAG,MA+CnF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,MAAM,GAAG,MAAM,KAAG,MA8BnE,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/number-field/utils.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,uBAAuB,GAClC,OAAO,MAAM,EACb,QAAQ,MAAM,KACb;IAAE,gBAAgB,EAAE,MAAM,CAAA;CAI5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,KAAG,MAY9E,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,4BAA4B,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAU5E,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,OAC5B,CAAC;AAElD,uGAAuG;AACvG,eAAO,MAAM,gCAAgC,GAC3C,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,WAAW,MAAM,KAChB,OAMF,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,uCAAuC,GAClD,gBAAgB,MAAM,EACtB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,WAAW,MAAM,KAChB,OAEoD,CAAC;AAqFxD,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAWlE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,KAAG,MAOnE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,SAAS,IAAI,CAAC,mBAAmB,KAChC,MAQF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,MAAM,EACb,SAAS,IAAI,CAAC,mBAAmB,KAChC,MAyCF,CAAC;AAEF,eAAO,MAAM,+BAA+B,GAC1C,OAAO,MAAM,EACb,aAAa,MAAM,EACnB,QAAQ,MAAM,KACb,MA+BF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,GAAG,MAAM,EACtB,QAAQ,MAAM,EACd,wBAAqB,EACrB,YAAY,MAAM,KACjB,MAkEF,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,YAAY,MAAM,EAClB,iBAAiB,MAAM,KACtB,MAaF,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,uBAAqB,EACrB,KAAK,MAAM,EACX,KAAK,MAAM,EACX,YAAY,MAAM,KACjB,MAyBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAyBvD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MAyB5D,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,KAAG,MAgDnF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,GAAG,MAAM,EAAE,MAAM,MAAM,KAAG,MA+CnF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,MAAM,GAAG,MAAM,KAAG,MA8BnE,CAAC"}
@@ -129,23 +129,23 @@ interface HTMLWCProps extends React.AriaAttributes {
129
129
  onInput?(e: Event): void;
130
130
  }
131
131
 
132
- export declare const AccordionItem: React.ForwardRefExoticComponent<
132
+ export declare const ActionsMenu: React.ForwardRefExoticComponent<
133
133
  React.PropsWithChildren<
134
- Omit<PublicOf<AccordionItemWC>, 'children' | 'style'> &
134
+ Omit<PublicOf<ActionsMenuWC>, 'children' | 'style'> &
135
135
  HTMLWCProps & {
136
136
  }
137
- > & React.RefAttributes<AccordionItemWC>
137
+ > & React.RefAttributes<ActionsMenuWC>
138
138
  >;
139
- export type AccordionItemRef = AccordionItemWC;
139
+ export type ActionsMenuRef = ActionsMenuWC;
140
140
 
141
- export declare const ActionsMenu: React.ForwardRefExoticComponent<
141
+ export declare const AccordionItem: React.ForwardRefExoticComponent<
142
142
  React.PropsWithChildren<
143
- Omit<PublicOf<ActionsMenuWC>, 'children' | 'style'> &
143
+ Omit<PublicOf<AccordionItemWC>, 'children' | 'style'> &
144
144
  HTMLWCProps & {
145
145
  }
146
- > & React.RefAttributes<ActionsMenuWC>
146
+ > & React.RefAttributes<AccordionItemWC>
147
147
  >;
148
- export type ActionsMenuRef = ActionsMenuWC;
148
+ export type AccordionItemRef = AccordionItemWC;
149
149
 
150
150
  export declare const AiCriteriaSearch: React.ForwardRefExoticComponent<
151
151
  React.PropsWithChildren<
@@ -158,23 +158,23 @@ export declare const AiCriteriaSearch: React.ForwardRefExoticComponent<
158
158
  >;
159
159
  export type AiCriteriaSearchRef = AiCriteriaSearchWC;
160
160
 
161
- export declare const Accordion: React.ForwardRefExoticComponent<
161
+ export declare const AiIndicator: React.ForwardRefExoticComponent<
162
162
  React.PropsWithChildren<
163
- Omit<PublicOf<AccordionWC>, 'children' | 'style'> &
163
+ Omit<PublicOf<AiIndicatorWC>, 'children' | 'style'> &
164
164
  HTMLWCProps & {
165
165
  }
166
- > & React.RefAttributes<AccordionWC>
166
+ > & React.RefAttributes<AiIndicatorWC>
167
167
  >;
168
- export type AccordionRef = AccordionWC;
168
+ export type AiIndicatorRef = AiIndicatorWC;
169
169
 
170
- export declare const AiIndicator: React.ForwardRefExoticComponent<
170
+ export declare const Accordion: React.ForwardRefExoticComponent<
171
171
  React.PropsWithChildren<
172
- Omit<PublicOf<AiIndicatorWC>, 'children' | 'style'> &
172
+ Omit<PublicOf<AccordionWC>, 'children' | 'style'> &
173
173
  HTMLWCProps & {
174
174
  }
175
- > & React.RefAttributes<AiIndicatorWC>
175
+ > & React.RefAttributes<AccordionWC>
176
176
  >;
177
- export type AiIndicatorRef = AiIndicatorWC;
177
+ export type AccordionRef = AccordionWC;
178
178
 
179
179
  export declare const Anchor: React.ForwardRefExoticComponent<
180
180
  React.PropsWithChildren<
@@ -5,7 +5,7 @@ import { DOM, attr } from '@microsoft/fast-element';
5
5
  import { AffixMixin } from '../_common';
6
6
  import { foundationNumberFieldStyles as styles } from './number-field.styles';
7
7
  import { foundationNumberFieldTemplate as template } from './number-field.template';
8
- import { applyDecimalPrecisionLimits, calculateAutoStepPrecision, decrementWithPrecision, formatControlValue, incrementWithPrecision, toIntlNumberFormat, validateInput, validateMinMax, } from './utils';
8
+ import { applyDecimalPrecisionLimits, calculateAutoStepPrecision, decrementWithPrecision, formatControlValue, incrementWithPrecision, toIntlNumberFormat, validateInput, validateMinMax, hasMultipleDecimalSeparators, rejectsDecimalAtMaximumIntegerPrecision, } from './utils';
9
9
  const MAX_DECIMAL_PLACES = 3;
10
10
  const MAX_INPUT_PRECISION = 12;
11
11
  /**
@@ -94,19 +94,50 @@ export class NumberField extends AffixMixin(FASTNumberField) {
94
94
  const regexString = `[.]$`;
95
95
  return new RegExp(regexString);
96
96
  }
97
+ restoreControlToCommittedValue() {
98
+ if (!this.control)
99
+ return;
100
+ const previousValue = this.value;
101
+ if (!previousValue) {
102
+ this.control.value = '';
103
+ this['isUserInput'] = false;
104
+ return;
105
+ }
106
+ this.control.value =
107
+ this.withFormatting && previousValue
108
+ ? toIntlNumberFormat(previousValue, this.locale, this.options)
109
+ : formatControlValue(String(previousValue), this.locale);
110
+ this['isUserInput'] = false;
111
+ }
97
112
  handleTextInput() {
98
113
  if (!this.control)
99
114
  return;
100
115
  if (this.control.value === '-' || this.control.value === '-0')
101
116
  return;
117
+ if (hasMultipleDecimalSeparators(this.control.value, this.locale)) {
118
+ this.restoreControlToCommittedValue();
119
+ return;
120
+ }
121
+ if (rejectsDecimalAtMaximumIntegerPrecision(this.value, this.control.value, this.locale, this.maximumInputPrecision)) {
122
+ this.restoreControlToCommittedValue();
123
+ return;
124
+ }
102
125
  this['isUserInput'] = true;
103
126
  if (this.withFormatting && !new NumberParser(this.locale).hasSeparator(this.control.value)) {
104
127
  DOM.queueUpdate(() => {
105
- const validValue = validateInput(this.control.value, this.locale, this.maximumInputPrecision);
106
- this.value = applyDecimalPrecisionLimits(validValue, this.options);
128
+ const previousValue = this.value;
129
+ const validValue = validateInput(this.control.value, this.locale, this.maximumInputPrecision, this.value);
130
+ if (!validValue && previousValue && this.control.value !== '') {
131
+ this.restoreControlToCommittedValue();
132
+ return;
133
+ }
107
134
  if (this.control) {
108
- this.control.value = applyDecimalPrecisionLimits(formatControlValue(validValue, this.locale), this.options);
135
+ const displayValue = this.withFormatting && validValue !== ''
136
+ ? toIntlNumberFormat(validValue, this.locale, this.options)
137
+ : formatControlValue(validValue, this.locale);
138
+ this.control.value = applyDecimalPrecisionLimits(displayValue, this.options);
109
139
  }
140
+ this.value = applyDecimalPrecisionLimits(validValue, this.options);
110
141
  this.$emit('input');
111
142
  });
112
143
  }
@@ -131,6 +162,9 @@ export class NumberField extends AffixMixin(FASTNumberField) {
131
162
  if (!this['isUserInput']) {
132
163
  this.value = validateMinMax(this.value, this.locale, false, this.min, this.max, this.maximumInputPrecision);
133
164
  DOM.queueUpdate(() => {
165
+ if (this['isUserInput']) {
166
+ return;
167
+ }
134
168
  if (this.control) {
135
169
  this.control.value =
136
170
  this.withFormatting && this.value
@@ -140,19 +174,8 @@ export class NumberField extends AffixMixin(FASTNumberField) {
140
174
  });
141
175
  return;
142
176
  }
143
- const value = next;
144
- DOM.queueUpdate(() => {
145
- if (!this.control)
146
- return;
147
- if (this.control.value === '' || this.value === '') {
148
- this.control.value = value;
149
- return;
150
- }
151
- this.control.value = formatControlValue(String(value), this.locale);
152
- this.value = this.withFormatting
153
- ? validateInput(this.control.value, this.locale, this.maximumInputPrecision)
154
- : this.control.value;
155
- });
177
+ // While the user is typing, handleTextInput (and handleBlur on exit) own value + display.
178
+ return;
156
179
  }
157
180
  }
158
181
  handleBlur() {
@@ -181,7 +204,12 @@ __decorate([
181
204
  attr({ mode: 'boolean' })
182
205
  ], NumberField.prototype, "hideStep", void 0);
183
206
  __decorate([
184
- attr
207
+ attr({
208
+ converter: {
209
+ fromView: (value) => (value === null || value === undefined ? '' : String(value)),
210
+ toView: (value) => value,
211
+ },
212
+ })
185
213
  ], NumberField.prototype, "value", void 0);
186
214
  __decorate([
187
215
  attr({ mode: 'boolean' })
@@ -9,6 +9,119 @@ export const getSeparatorsFromParser = (value, locale) => {
9
9
  const decimalSeparator = ((_a = parts.find((d) => d.type === 'decimal')) === null || _a === void 0 ? void 0 : _a.value) || '.';
10
10
  return { decimalSeparator };
11
11
  };
12
+ /**
13
+ * Strips locale grouping and normalizes the decimal separator to `.` without using `Number`
14
+ * (so integers beyond `Number.MAX_SAFE_INTEGER` keep every digit).
15
+ */
16
+ export const normalizeLocaleNumberString = (inputVal, locale) => {
17
+ var _a, _b, _c;
18
+ const parts = new Intl.NumberFormat(locale).formatToParts(FORMAT_TEST_NUMBER);
19
+ const groupChar = (_a = parts.find((d) => d.type === 'group')) === null || _a === void 0 ? void 0 : _a.value;
20
+ const decimalChar = (_c = (_b = parts.find((d) => d.type === 'decimal')) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : '.';
21
+ let normalized = inputVal.trim();
22
+ if (groupChar) {
23
+ normalized = normalized.split(groupChar).join('');
24
+ }
25
+ if (decimalChar !== '.') {
26
+ normalized = normalized.replace(decimalChar, '.');
27
+ }
28
+ return normalized;
29
+ };
30
+ /** Counts locale decimal separator characters in the raw control text (ignores grouping). */
31
+ export const countLocaleDecimalSeparators = (input, locale) => {
32
+ var _a, _b;
33
+ const parts = new Intl.NumberFormat(locale).formatToParts(FORMAT_TEST_NUMBER);
34
+ const decimalChar = (_b = (_a = parts.find((d) => d.type === 'decimal')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '.';
35
+ let count = 0;
36
+ for (const ch of input) {
37
+ if (ch === decimalChar) {
38
+ count += 1;
39
+ }
40
+ }
41
+ return count;
42
+ };
43
+ export const hasMultipleDecimalSeparators = (input, locale) => countLocaleDecimalSeparators(input, locale) > 1;
44
+ /** True when the committed value is a whole number already using the full significant-digit budget. */
45
+ export const isIntegerAtMaximumInputPrecision = (value, locale, precision) => {
46
+ const normalized = normalizeLocaleNumberString(String(value !== null && value !== void 0 ? value : ''), locale);
47
+ if (!normalized || !/^-?\d+$/.test(normalized)) {
48
+ return false;
49
+ }
50
+ return normalized.replace(/^-/, '').length >= precision;
51
+ };
52
+ /** User typed a decimal separator while the integer part is already at max precision. */
53
+ export const rejectsDecimalAtMaximumIntegerPrecision = (committedValue, controlText, locale, precision) => isIntegerAtMaximumInputPrecision(committedValue, locale, precision) &&
54
+ countLocaleDecimalSeparators(controlText, locale) > 0;
55
+ const SAFE_INTEGER_DIGIT_COUNT = 15;
56
+ /** Sample integer used only to read locale grouping separator from Intl. */
57
+ const LOCALE_GROUPING_PROBE = 1234567;
58
+ const DIGITS_PER_GROUP = 3;
59
+ const formatLargeIntegerString = (value, locale) => {
60
+ var _a, _b;
61
+ const negative = value.startsWith('-');
62
+ const digits = negative ? value.slice(1) : value;
63
+ const parts = new Intl.NumberFormat(locale).formatToParts(LOCALE_GROUPING_PROBE);
64
+ const group = (_b = (_a = parts.find((p) => p.type === 'group')) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : ',';
65
+ const groups = [];
66
+ let rest = digits;
67
+ while (rest.length > DIGITS_PER_GROUP) {
68
+ groups.unshift(rest.slice(-DIGITS_PER_GROUP));
69
+ rest = rest.slice(0, -DIGITS_PER_GROUP);
70
+ }
71
+ groups.unshift(rest);
72
+ const formatted = groups.join(group);
73
+ return negative ? `-${formatted}` : formatted;
74
+ };
75
+ const hasLongIntegerPart = (value) => {
76
+ const unsigned = value.replace(/^-/, '');
77
+ const dotIndex = unsigned.indexOf('.');
78
+ const intPart = dotIndex === -1 ? unsigned : unsigned.slice(0, dotIndex);
79
+ return intPart.length > SAFE_INTEGER_DIGIT_COUNT;
80
+ };
81
+ const formatLongNumericStringForDisplay = (value, locale, options) => {
82
+ const negative = value.startsWith('-');
83
+ const unsigned = negative ? value.slice(1) : value;
84
+ const dotIndex = unsigned.indexOf('.');
85
+ const intDigits = dotIndex === -1 ? unsigned : unsigned.slice(0, dotIndex);
86
+ let decDigits = dotIndex === -1 ? '' : unsigned.slice(dotIndex + 1);
87
+ const minFractionDigits = options.minimumFractionDigits;
88
+ const maxFractionDigits = options.maximumFractionDigits;
89
+ if (maxFractionDigits !== undefined && decDigits.length > maxFractionDigits) {
90
+ decDigits = decDigits.slice(0, maxFractionDigits);
91
+ }
92
+ if (minFractionDigits !== undefined && decDigits.length < minFractionDigits) {
93
+ decDigits = decDigits.padEnd(minFractionDigits, '0');
94
+ }
95
+ const intValue = negative ? `-${intDigits}` : intDigits;
96
+ const formattedInt = formatLargeIntegerString(intValue, locale);
97
+ const { decimalSeparator } = getSeparatorsFromParser(value, locale);
98
+ if (dotIndex === -1) {
99
+ if (minFractionDigits !== undefined && minFractionDigits > 0) {
100
+ return `${formattedInt}${decimalSeparator}${'0'.repeat(minFractionDigits)}`;
101
+ }
102
+ return formattedInt;
103
+ }
104
+ if (maxFractionDigits === 0) {
105
+ return formattedInt;
106
+ }
107
+ return `${formattedInt}${decimalSeparator}${decDigits}`;
108
+ };
109
+ const clampIntegerStringToMinMax = (value, min, max) => {
110
+ let clamped = BigInt(value);
111
+ if (max !== undefined && !Number.isNaN(max)) {
112
+ const maxBig = BigInt(Math.trunc(max));
113
+ if (clamped > maxBig) {
114
+ clamped = maxBig;
115
+ }
116
+ }
117
+ if (min !== undefined && !Number.isNaN(min)) {
118
+ const minBig = BigInt(Math.trunc(min));
119
+ if (clamped < minBig) {
120
+ clamped = minBig;
121
+ }
122
+ }
123
+ return clamped.toString();
124
+ };
12
125
  export const formatControlValue = (value, locale) => {
13
126
  // Get locale-specific decimal separator
14
127
  const { decimalSeparator } = getSeparatorsFromParser(value, locale);
@@ -20,9 +133,20 @@ export const formatControlValue = (value, locale) => {
20
133
  return value.replace(/\./g, decimalSeparator);
21
134
  };
22
135
  export const sanitiseInputVal = (inputVal, locale) => {
136
+ const normalized = normalizeLocaleNumberString(String(inputVal), locale);
137
+ const digitCount = normalized.replace(/^-/, '').replace('.', '').length;
138
+ if (/^-?\d+(\.\d+)?$/.test(normalized) && digitCount > SAFE_INTEGER_DIGIT_COUNT) {
139
+ return normalized;
140
+ }
23
141
  return String(new NumberParser(locale).parse(String(inputVal)));
24
142
  };
25
143
  export const toIntlNumberFormat = (value, locale, options) => {
144
+ if (value === '') {
145
+ return '';
146
+ }
147
+ if (hasLongIntegerPart(value) && /^-?\d+(\.\d*)?$/.test(value)) {
148
+ return formatLongNumericStringForDisplay(value, locale, options);
149
+ }
26
150
  return Intl.NumberFormat(locale, options).format(Number(value));
27
151
  };
28
152
  /**
@@ -110,6 +234,9 @@ export const validateValue = (value, locale, withFormatting = true, precision) =
110
234
  }
111
235
  const stringValue = typeof value === 'number' ? value.toString() : value;
112
236
  const inputPrecision = precision !== null && precision !== void 0 ? precision : formattedInputPrecision;
237
+ if (hasMultipleDecimalSeparators(stringValue, locale)) {
238
+ return '';
239
+ }
113
240
  let cleanValue;
114
241
  if (withFormatting) {
115
242
  cleanValue = sanitiseInputVal(stringValue, locale);
@@ -159,12 +286,34 @@ export const validateValue = (value, locale, withFormatting = true, precision) =
159
286
  }
160
287
  return cleanValue;
161
288
  };
162
- export const validateInput = (value, locale, precision) => {
163
- const parsedValue = validateValue(value, locale, undefined, precision);
289
+ export const validateInput = (value, locale, precision, committedValue) => {
290
+ if (precision !== undefined && countLocaleDecimalSeparators(value, locale) > 0) {
291
+ const normalized = normalizeLocaleNumberString(value, locale);
292
+ const dotIdx = normalized.indexOf('.');
293
+ const integerPart = dotIdx === -1 ? normalized : normalized.slice(0, dotIdx);
294
+ const committed = committedValue !== undefined ? committedValue : integerPart;
295
+ if (rejectsDecimalAtMaximumIntegerPrecision(committed, value, locale, precision)) {
296
+ return '';
297
+ }
298
+ }
299
+ const parsedValue = validateValue(value, locale, true, precision);
164
300
  return preserveTrailingZerosAfterParse(value, parsedValue, locale);
165
301
  };
166
302
  export const validateMinMax = (value, locale, withFormatting = true, min, max, precision) => {
167
- let validValue = parseFloat(validateValue(value, locale, withFormatting, precision));
303
+ const validatedString = validateValue(value, locale, withFormatting, precision);
304
+ if (!validatedString) {
305
+ return '';
306
+ }
307
+ const digitCount = validatedString.replace(/^-/, '').replace('.', '').length;
308
+ if (/^-?\d+$/.test(validatedString) && digitCount > SAFE_INTEGER_DIGIT_COUNT) {
309
+ try {
310
+ return clampIntegerStringToMinMax(validatedString, min, max);
311
+ }
312
+ catch (_a) {
313
+ return validatedString;
314
+ }
315
+ }
316
+ let validValue = parseFloat(validatedString);
168
317
  if (isNaN(validValue)) {
169
318
  validValue = '';
170
319
  }
package/dist/react.cjs CHANGED
@@ -111,14 +111,14 @@ function _mergeRefs(...refs) {
111
111
  };
112
112
  }
113
113
 
114
- const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
114
+ const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
115
115
  const { children, ...rest } = props;
116
- return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
116
+ return React.createElement(customElements.getName(ActionsMenuWC) ?? '%%prefix%%-actions-menu', { ...rest, ref }, children);
117
117
  });
118
118
 
119
- const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
119
+ const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
120
120
  const { children, ...rest } = props;
121
- return React.createElement(customElements.getName(ActionsMenuWC) ?? '%%prefix%%-actions-menu', { ...rest, ref }, children);
121
+ return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
122
122
  });
123
123
 
124
124
  const AiCriteriaSearch = React.forwardRef(function AiCriteriaSearch(props, ref) {
@@ -143,14 +143,14 @@ const AiCriteriaSearch = React.forwardRef(function AiCriteriaSearch(props, ref)
143
143
  return React.createElement(customElements.getName(AiCriteriaSearchWC) ?? '%%prefix%%-ai-criteria-search', { ...rest, ref: _mergeRefs(_innerRef, ref) }, children);
144
144
  });
145
145
 
146
- const Accordion = React.forwardRef(function Accordion(props, ref) {
146
+ const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
147
147
  const { children, ...rest } = props;
148
- return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
148
+ return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
149
149
  });
150
150
 
151
- const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
151
+ const Accordion = React.forwardRef(function Accordion(props, ref) {
152
152
  const { children, ...rest } = props;
153
- return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
153
+ return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
154
154
  });
155
155
 
156
156
  const Anchor = React.forwardRef(function Anchor(props, ref) {
@@ -1078,11 +1078,11 @@ const WeeklyRecurrence = React.forwardRef(function WeeklyRecurrence(props, ref)
1078
1078
  });
1079
1079
 
1080
1080
  module.exports = {
1081
- AccordionItem,
1082
1081
  ActionsMenu,
1082
+ AccordionItem,
1083
1083
  AiCriteriaSearch,
1084
- Accordion,
1085
1084
  AiIndicator,
1085
+ Accordion,
1086
1086
  Anchor,
1087
1087
  AnchoredRegion,
1088
1088
  Avatar,
package/dist/react.mjs CHANGED
@@ -109,14 +109,14 @@ function _mergeRefs(...refs) {
109
109
  };
110
110
  }
111
111
 
112
- export const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
112
+ export const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
113
113
  const { children, ...rest } = props;
114
- return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
114
+ return React.createElement(customElements.getName(ActionsMenuWC) ?? '%%prefix%%-actions-menu', { ...rest, ref }, children);
115
115
  });
116
116
 
117
- export const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
117
+ export const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
118
118
  const { children, ...rest } = props;
119
- return React.createElement(customElements.getName(ActionsMenuWC) ?? '%%prefix%%-actions-menu', { ...rest, ref }, children);
119
+ return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
120
120
  });
121
121
 
122
122
  export const AiCriteriaSearch = React.forwardRef(function AiCriteriaSearch(props, ref) {
@@ -141,14 +141,14 @@ export const AiCriteriaSearch = React.forwardRef(function AiCriteriaSearch(props
141
141
  return React.createElement(customElements.getName(AiCriteriaSearchWC) ?? '%%prefix%%-ai-criteria-search', { ...rest, ref: _mergeRefs(_innerRef, ref) }, children);
142
142
  });
143
143
 
144
- export const Accordion = React.forwardRef(function Accordion(props, ref) {
144
+ export const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
145
145
  const { children, ...rest } = props;
146
- return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
146
+ return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
147
147
  });
148
148
 
149
- export const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
149
+ export const Accordion = React.forwardRef(function Accordion(props, ref) {
150
150
  const { children, ...rest } = props;
151
- return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
151
+ return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
152
152
  });
153
153
 
154
154
  export const Anchor = React.forwardRef(function Anchor(props, ref) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-ui",
3
3
  "description": "Genesis Foundation UI",
4
- "version": "14.482.1-alpha-c9fc44ab6.0",
4
+ "version": "14.482.1",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -85,13 +85,13 @@
85
85
  }
86
86
  },
87
87
  "devDependencies": {
88
- "@genesislcap/foundation-testing": "14.482.1-alpha-c9fc44ab6.0",
89
- "@genesislcap/genx": "14.482.1-alpha-c9fc44ab6.0",
90
- "@genesislcap/rollup-builder": "14.482.1-alpha-c9fc44ab6.0",
91
- "@genesislcap/ts-builder": "14.482.1-alpha-c9fc44ab6.0",
92
- "@genesislcap/uvu-playwright-builder": "14.482.1-alpha-c9fc44ab6.0",
93
- "@genesislcap/vite-builder": "14.482.1-alpha-c9fc44ab6.0",
94
- "@genesislcap/webpack-builder": "14.482.1-alpha-c9fc44ab6.0",
88
+ "@genesislcap/foundation-testing": "14.482.1",
89
+ "@genesislcap/genx": "14.482.1",
90
+ "@genesislcap/rollup-builder": "14.482.1",
91
+ "@genesislcap/ts-builder": "14.482.1",
92
+ "@genesislcap/uvu-playwright-builder": "14.482.1",
93
+ "@genesislcap/vite-builder": "14.482.1",
94
+ "@genesislcap/webpack-builder": "14.482.1",
95
95
  "copyfiles": "^2.4.1"
96
96
  },
97
97
  "dependencies": {
@@ -100,16 +100,16 @@
100
100
  "@fortawesome/free-regular-svg-icons": "^6.2.1",
101
101
  "@fortawesome/free-solid-svg-icons": "^6.2.1",
102
102
  "@genesiscommunitysuccess/analyzer-import-alias-plugin": "^5.0.3",
103
- "@genesislcap/expression-builder": "14.482.1-alpha-c9fc44ab6.0",
104
- "@genesislcap/foundation-ai": "14.482.1-alpha-c9fc44ab6.0",
105
- "@genesislcap/foundation-comms": "14.482.1-alpha-c9fc44ab6.0",
106
- "@genesislcap/foundation-criteria": "14.482.1-alpha-c9fc44ab6.0",
107
- "@genesislcap/foundation-errors": "14.482.1-alpha-c9fc44ab6.0",
108
- "@genesislcap/foundation-events": "14.482.1-alpha-c9fc44ab6.0",
109
- "@genesislcap/foundation-logger": "14.482.1-alpha-c9fc44ab6.0",
110
- "@genesislcap/foundation-notifications": "14.482.1-alpha-c9fc44ab6.0",
111
- "@genesislcap/foundation-user": "14.482.1-alpha-c9fc44ab6.0",
112
- "@genesislcap/foundation-utils": "14.482.1-alpha-c9fc44ab6.0",
103
+ "@genesislcap/expression-builder": "14.482.1",
104
+ "@genesislcap/foundation-ai": "14.482.1",
105
+ "@genesislcap/foundation-comms": "14.482.1",
106
+ "@genesislcap/foundation-criteria": "14.482.1",
107
+ "@genesislcap/foundation-errors": "14.482.1",
108
+ "@genesislcap/foundation-events": "14.482.1",
109
+ "@genesislcap/foundation-logger": "14.482.1",
110
+ "@genesislcap/foundation-notifications": "14.482.1",
111
+ "@genesislcap/foundation-user": "14.482.1",
112
+ "@genesislcap/foundation-utils": "14.482.1",
113
113
  "@microsoft/fast-colors": "5.3.1",
114
114
  "@microsoft/fast-components": "2.30.6",
115
115
  "@microsoft/fast-element": "1.14.0",
@@ -142,5 +142,5 @@
142
142
  "require": "./dist/react.cjs"
143
143
  }
144
144
  },
145
- "gitHead": "f2102d255d1581e519792fb2a4f5ebecc38fbb39"
145
+ "gitHead": "43a902838587d527b6786bbb41a5c3784c638e70"
146
146
  }