@genesislcap/foundation-ui 15.45.0 → 15.46.0-canary.17

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,7 +28,17 @@ export declare class NumberField extends NumberField_base {
28
28
  stepUp(): void;
29
29
  stepDown(): void;
30
30
  hasSeparatorWithoutDecimals(): RegExp;
31
+ /**
32
+ * Writes text the user is editing back to the control, keeping the caret next to the same
33
+ * value characters instead of letting the assignment move it to the end.
34
+ */
35
+ private writeControlValue;
31
36
  private restoreControlToCommittedValue;
37
+ /**
38
+ * Backspace/Delete next to a group separator would only delete the separator, which
39
+ * reformatting puts straight back. Step the caret over it so the key deletes the digit.
40
+ */
41
+ private skipGroupSeparator;
32
42
  handleKeyDown(e: KeyboardEvent): boolean;
33
43
  handleTextInput(): void;
34
44
  valueChanged(previous: any, next: any): 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;;;;;;;;;;;AAqBrE;;;;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,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO;IAOxC,eAAe,IAAI,IAAI;IA+DvB,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAsC5C,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;;;;;;;;;;;AAuBrE;;;;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;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,8BAA8B;IAatC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAe1B,aAAa,CAAC,CAAC,EAAE,aAAa,GAAG,OAAO;IAUxC,eAAe,IAAI,IAAI;IA+DvB,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAsC5C,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,13 @@
1
1
  export declare const getSeparatorsFromParser: (value: string, locale: string) => {
2
2
  decimalSeparator: string;
3
+ groupSeparator: string;
3
4
  };
5
+ /**
6
+ * Maps a caret position in `oldValue` (the raw control text) to the equivalent position in
7
+ * `newValue` (the text after formatting or restoring), keeping the caret next to the same
8
+ * value characters.
9
+ */
10
+ export declare const calculateCursorPosition: (oldValue: string, newValue: string, oldCursor: number, locale: string) => number;
4
11
  /**
5
12
  * Strips locale grouping and normalizes the decimal separator to `.` without using `Number`
6
13
  * (so integers beyond `Number.MAX_SAFE_INTEGER` keep every digit).
@@ -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;;;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"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/number-field/utils.ts"],"names":[],"mappings":"AAuCA,eAAO,MAAM,uBAAuB,GAClC,OAAO,MAAM,EACb,QAAQ,MAAM,KACb;IAAE,gBAAgB,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAGpD,CAAC;AAiCF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,EAChB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,QAAQ,MAAM,KACb,MA+CF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,KAAG,MAW9E,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,4BAA4B,GAAI,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,MAS5E,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;AAkFxD,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"}
@@ -133,14 +133,14 @@ interface HTMLWCProps extends React.AriaAttributes {
133
133
  onInput?(e: Event): void;
134
134
  }
135
135
 
136
- export declare const AccordionItem: React.ForwardRefExoticComponent<
136
+ export declare const Accordion: React.ForwardRefExoticComponent<
137
137
  React.PropsWithChildren<
138
- Omit<PublicOf<AccordionItemWC>, 'children' | 'style'> &
138
+ Omit<PublicOf<AccordionWC>, 'children' | 'style'> &
139
139
  HTMLWCProps & {
140
140
  }
141
- > & React.RefAttributes<AccordionItemWC>
141
+ > & React.RefAttributes<AccordionWC>
142
142
  >;
143
- export type AccordionItemRef = AccordionItemWC;
143
+ export type AccordionRef = AccordionWC;
144
144
 
145
145
  export declare const ActionsMenu: React.ForwardRefExoticComponent<
146
146
  React.PropsWithChildren<
@@ -171,14 +171,14 @@ export declare const AiIndicator: React.ForwardRefExoticComponent<
171
171
  >;
172
172
  export type AiIndicatorRef = AiIndicatorWC;
173
173
 
174
- export declare const Accordion: React.ForwardRefExoticComponent<
174
+ export declare const AccordionItem: React.ForwardRefExoticComponent<
175
175
  React.PropsWithChildren<
176
- Omit<PublicOf<AccordionWC>, 'children' | 'style'> &
176
+ Omit<PublicOf<AccordionItemWC>, 'children' | 'style'> &
177
177
  HTMLWCProps & {
178
178
  }
179
- > & React.RefAttributes<AccordionWC>
179
+ > & React.RefAttributes<AccordionItemWC>
180
180
  >;
181
- export type AccordionRef = AccordionWC;
181
+ export type AccordionItemRef = AccordionItemWC;
182
182
 
183
183
  export declare const Anchor: React.ForwardRefExoticComponent<
184
184
  React.PropsWithChildren<
@@ -2,11 +2,11 @@ import { __decorate } from "tslib";
2
2
  import { NumberParser } from '@genesislcap/foundation-utils';
3
3
  import { NumberField as FASTNumberField } from '@microsoft/fast-components';
4
4
  import { DOM, attr } from '@microsoft/fast-element';
5
- import { keyEnter } from '@microsoft/fast-web-utilities';
5
+ import { keyBackspace, keyDelete, keyEnter } from '@microsoft/fast-web-utilities';
6
6
  import { AffixMixin } from '../_common';
7
7
  import { foundationNumberFieldStyles as styles } from './number-field.styles';
8
8
  import { foundationNumberFieldTemplate as template } from './number-field.template';
9
- import { applyDecimalPrecisionLimits, calculateAutoStepPrecision, decrementWithPrecision, formatControlValue, incrementWithPrecision, toIntlNumberFormat, validateInput, validateMinMax, hasMultipleDecimalSeparators, rejectsDecimalAtMaximumIntegerPrecision, } from './utils';
9
+ import { applyDecimalPrecisionLimits, calculateAutoStepPrecision, calculateCursorPosition, decrementWithPrecision, formatControlValue, getSeparatorsFromParser, incrementWithPrecision, toIntlNumberFormat, validateInput, validateMinMax, hasMultipleDecimalSeparators, rejectsDecimalAtMaximumIntegerPrecision, } from './utils';
10
10
  const MAX_DECIMAL_PLACES = 3;
11
11
  const MAX_INPUT_PRECISION = 12;
12
12
  /**
@@ -95,25 +95,67 @@ export class NumberField extends AffixMixin(FASTNumberField) {
95
95
  const regexString = `[.]$`;
96
96
  return new RegExp(regexString);
97
97
  }
98
+ /**
99
+ * Writes text the user is editing back to the control, keeping the caret next to the same
100
+ * value characters instead of letting the assignment move it to the end.
101
+ */
102
+ writeControlValue(next) {
103
+ const control = this.control;
104
+ const previous = control.value;
105
+ if (previous === next)
106
+ return;
107
+ const { selectionStart, selectionEnd } = control;
108
+ control.value = next;
109
+ // Only move the caret of a focused field: setting selection can focus it in WebKit, and
110
+ // programmatic stepUp()/stepDown() or a callback running after blur must not do that.
111
+ if (selectionStart === null ||
112
+ control.getRootNode().activeElement !== control) {
113
+ return;
114
+ }
115
+ const start = calculateCursorPosition(previous, next, selectionStart, this.locale);
116
+ const end = selectionEnd === null || selectionEnd === selectionStart
117
+ ? start
118
+ : calculateCursorPosition(previous, next, selectionEnd, this.locale);
119
+ control.setSelectionRange(start, end);
120
+ }
98
121
  restoreControlToCommittedValue() {
99
122
  if (!this.control)
100
123
  return;
101
124
  const previousValue = this.value;
102
- if (!previousValue) {
103
- this.control.value = '';
104
- this['isUserInput'] = false;
105
- return;
106
- }
107
- this.control.value =
108
- this.withFormatting && previousValue
125
+ this.writeControlValue(!previousValue
126
+ ? ''
127
+ : this.withFormatting
109
128
  ? toIntlNumberFormat(previousValue, this.locale, this.options)
110
- : formatControlValue(String(previousValue), this.locale);
129
+ : formatControlValue(String(previousValue), this.locale));
111
130
  this['isUserInput'] = false;
112
131
  }
132
+ /**
133
+ * Backspace/Delete next to a group separator would only delete the separator, which
134
+ * reformatting puts straight back. Step the caret over it so the key deletes the digit.
135
+ */
136
+ skipGroupSeparator(e) {
137
+ if (!this.withFormatting || e.ctrlKey || e.metaKey || e.altKey)
138
+ return;
139
+ const { value, selectionStart, selectionEnd } = this.control;
140
+ if (selectionStart === null || selectionStart !== selectionEnd)
141
+ return;
142
+ const { groupSeparator } = getSeparatorsFromParser(value, this.locale);
143
+ if (!groupSeparator)
144
+ return;
145
+ if (e.key === keyBackspace && value[selectionStart - 1] === groupSeparator) {
146
+ this.control.setSelectionRange(selectionStart - 1, selectionStart - 1);
147
+ }
148
+ else if (e.key === keyDelete && value[selectionStart] === groupSeparator) {
149
+ this.control.setSelectionRange(selectionStart + 1, selectionStart + 1);
150
+ }
151
+ }
113
152
  handleKeyDown(e) {
114
153
  if (e.key === keyEnter) {
115
154
  this['isUserInput'] = false;
116
155
  }
156
+ if (this.control && (e.key === keyBackspace || e.key === keyDelete)) {
157
+ this.skipGroupSeparator(e);
158
+ }
117
159
  return super.handleKeyDown(e);
118
160
  }
119
161
  handleTextInput() {
@@ -142,7 +184,7 @@ export class NumberField extends AffixMixin(FASTNumberField) {
142
184
  const displayValue = this.withFormatting && validValue !== ''
143
185
  ? toIntlNumberFormat(validValue, this.locale, this.options)
144
186
  : formatControlValue(validValue, this.locale);
145
- this.control.value = applyDecimalPrecisionLimits(displayValue, this.options);
187
+ this.writeControlValue(applyDecimalPrecisionLimits(displayValue, this.options));
146
188
  }
147
189
  this.value = applyDecimalPrecisionLimits(validValue, this.options);
148
190
  this.$emit('input');
@@ -153,7 +195,7 @@ export class NumberField extends AffixMixin(FASTNumberField) {
153
195
  DOM.queueUpdate(() => {
154
196
  var _a;
155
197
  if (this.control) {
156
- this.control.value = this.control.value.replace(/[^0-9\-+e.]/g, '');
198
+ this.writeControlValue(this.control.value.replace(/[^0-9\-+e.]/g, ''));
157
199
  }
158
200
  this.value = ((_a = this.control) === null || _a === void 0 ? void 0 : _a.value) || '';
159
201
  this.$emit('input');
@@ -3,21 +3,113 @@ const formattedInputPrecision = 12;
3
3
  const FORMAT_TEST_NUMBER = 12345.6;
4
4
  const DECIMAL_BASE = 10;
5
5
  const MAX_DIGIT = DECIMAL_BASE - 1;
6
- export const getSeparatorsFromParser = (value, locale) => {
7
- var _a;
6
+ const DIGIT_PROBE = 9876543210;
7
+ const localeSymbolsCache = new Map();
8
+ /** Separators and digits for a locale; cached because it runs several times per keystroke. */
9
+ const getLocaleNumberSymbols = (locale) => {
10
+ var _a, _b;
11
+ const cached = localeSymbolsCache.get(locale);
12
+ if (cached) {
13
+ return cached;
14
+ }
8
15
  const parts = new Intl.NumberFormat(locale).formatToParts(FORMAT_TEST_NUMBER);
9
- const decimalSeparator = ((_a = parts.find((d) => d.type === 'decimal')) === null || _a === void 0 ? void 0 : _a.value) || '.';
10
- return { decimalSeparator };
16
+ const localeDigits = [
17
+ ...new Intl.NumberFormat(locale, { useGrouping: false }).format(DIGIT_PROBE),
18
+ ];
19
+ const symbols = {
20
+ decimalSeparator: ((_a = parts.find((d) => d.type === 'decimal')) === null || _a === void 0 ? void 0 : _a.value) || '.',
21
+ groupSeparator: ((_b = parts.find((d) => d.type === 'group')) === null || _b === void 0 ? void 0 : _b.value) || '',
22
+ digits: new Set([...'0123456789', ...localeDigits]),
23
+ zeroDigits: new Set(['0', localeDigits[localeDigits.length - 1]]),
24
+ };
25
+ localeSymbolsCache.set(locale, symbols);
26
+ return symbols;
27
+ };
28
+ export const getSeparatorsFromParser = (value, locale) => {
29
+ const { decimalSeparator, groupSeparator } = getLocaleNumberSymbols(locale);
30
+ return { decimalSeparator, groupSeparator };
31
+ };
32
+ /** Matches the `k`/`m`/`b` suffixes `NumberParser` expands (e.g. `12k` to `12,000`). */
33
+ const SHORTCUT_SUFFIX = /[kmb]\s*$/i;
34
+ /**
35
+ * Returns a predicate for the characters that carry a number's value: digits, the decimal
36
+ * separator and minus signs. Group separators, currency/percent symbols, bidi marks and
37
+ * leading integer zeros (which formatting adds or strips) are ignored.
38
+ */
39
+ const valueCharMatcher = (value, locale) => {
40
+ const { decimalSeparator, digits, zeroDigits } = getLocaleNumberSymbols(locale);
41
+ let inLeadingZeros = true;
42
+ return (index) => {
43
+ const ch = value[index];
44
+ if (ch === decimalSeparator) {
45
+ inLeadingZeros = false;
46
+ return true;
47
+ }
48
+ if (ch === '-' || ch === '\u2212') {
49
+ return true;
50
+ }
51
+ if (!digits.has(ch)) {
52
+ return false;
53
+ }
54
+ if (inLeadingZeros && zeroDigits.has(ch)) {
55
+ return false;
56
+ }
57
+ inLeadingZeros = false;
58
+ return true;
59
+ };
60
+ };
61
+ /**
62
+ * Maps a caret position in `oldValue` (the raw control text) to the equivalent position in
63
+ * `newValue` (the text after formatting or restoring), keeping the caret next to the same
64
+ * value characters.
65
+ */
66
+ export const calculateCursorPosition = (oldValue, newValue, oldCursor, locale) => {
67
+ const cursor = Math.min(Math.max(oldCursor, 0), oldValue.length);
68
+ // The characters just typed were rejected or dropped (letter, second decimal separator,
69
+ // leading zero): put the caret back where it was before they were typed.
70
+ const removed = oldValue.length - newValue.length;
71
+ if (removed > 0 &&
72
+ removed <= cursor &&
73
+ oldValue.slice(0, cursor - removed) + oldValue.slice(cursor) === newValue) {
74
+ return cursor - removed;
75
+ }
76
+ if (cursor === oldValue.length && SHORTCUT_SUFFIX.test(oldValue)) {
77
+ return newValue.length;
78
+ }
79
+ const isOldValueChar = valueCharMatcher(oldValue, locale);
80
+ let valueCharsBefore = 0;
81
+ for (let i = 0; i < cursor; i += 1) {
82
+ if (isOldValueChar(i)) {
83
+ valueCharsBefore += 1;
84
+ }
85
+ }
86
+ const isNewValueChar = valueCharMatcher(newValue, locale);
87
+ if (valueCharsBefore === 0) {
88
+ // Caret before the number: place it before the first value character, past any prefix.
89
+ for (let i = 0; i < newValue.length; i += 1) {
90
+ if (isNewValueChar(i)) {
91
+ return i;
92
+ }
93
+ }
94
+ return cursor === oldValue.length ? newValue.length : 0;
95
+ }
96
+ let seen = 0;
97
+ for (let i = 0; i < newValue.length; i += 1) {
98
+ if (isNewValueChar(i)) {
99
+ seen += 1;
100
+ if (seen === valueCharsBefore) {
101
+ return i + 1;
102
+ }
103
+ }
104
+ }
105
+ return newValue.length;
11
106
  };
12
107
  /**
13
108
  * Strips locale grouping and normalizes the decimal separator to `.` without using `Number`
14
109
  * (so integers beyond `Number.MAX_SAFE_INTEGER` keep every digit).
15
110
  */
16
111
  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 : '.';
112
+ const { groupSeparator: groupChar, decimalSeparator: decimalChar } = getLocaleNumberSymbols(locale);
21
113
  let normalized = inputVal.trim();
22
114
  if (groupChar) {
23
115
  normalized = normalized.split(groupChar).join('');
@@ -29,9 +121,7 @@ export const normalizeLocaleNumberString = (inputVal, locale) => {
29
121
  };
30
122
  /** Counts locale decimal separator characters in the raw control text (ignores grouping). */
31
123
  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 : '.';
124
+ const { decimalSeparator: decimalChar } = getLocaleNumberSymbols(locale);
35
125
  let count = 0;
36
126
  for (const ch of input) {
37
127
  if (ch === decimalChar) {
@@ -53,15 +143,11 @@ export const isIntegerAtMaximumInputPrecision = (value, locale, precision) => {
53
143
  export const rejectsDecimalAtMaximumIntegerPrecision = (committedValue, controlText, locale, precision) => isIntegerAtMaximumInputPrecision(committedValue, locale, precision) &&
54
144
  countLocaleDecimalSeparators(controlText, locale) > 0;
55
145
  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
146
  const DIGITS_PER_GROUP = 3;
59
147
  const formatLargeIntegerString = (value, locale) => {
60
- var _a, _b;
61
148
  const negative = value.startsWith('-');
62
149
  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 : ',';
150
+ const group = getLocaleNumberSymbols(locale).groupSeparator || ',';
65
151
  const groups = [];
66
152
  let rest = digits;
67
153
  while (rest.length > DIGITS_PER_GROUP) {
@@ -5874,7 +5874,17 @@ export declare class NumberField extends NumberField_base {
5874
5874
  stepUp(): void;
5875
5875
  stepDown(): void;
5876
5876
  hasSeparatorWithoutDecimals(): RegExp;
5877
+ /**
5878
+ * Writes text the user is editing back to the control, keeping the caret next to the same
5879
+ * value characters instead of letting the assignment move it to the end.
5880
+ */
5881
+ private writeControlValue;
5877
5882
  private restoreControlToCommittedValue;
5883
+ /**
5884
+ * Backspace/Delete next to a group separator would only delete the separator, which
5885
+ * reformatting puts straight back. Step the caret over it so the key deletes the digit.
5886
+ */
5887
+ private skipGroupSeparator;
5878
5888
  handleKeyDown(e: KeyboardEvent): boolean;
5879
5889
  handleTextInput(): void;
5880
5890
  valueChanged(previous: any, next: any): void;
package/dist/react.cjs CHANGED
@@ -113,9 +113,9 @@ function _mergeRefs(...refs) {
113
113
  };
114
114
  }
115
115
 
116
- const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
116
+ const Accordion = React.forwardRef(function Accordion(props, ref) {
117
117
  const { children, ...rest } = props;
118
- return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
118
+ return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
119
119
  });
120
120
 
121
121
  const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
@@ -150,9 +150,9 @@ const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
150
150
  return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
151
151
  });
152
152
 
153
- const Accordion = React.forwardRef(function Accordion(props, ref) {
153
+ const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
154
154
  const { children, ...rest } = props;
155
- return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
155
+ return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
156
156
  });
157
157
 
158
158
  const Anchor = React.forwardRef(function Anchor(props, ref) {
@@ -1143,11 +1143,11 @@ const WeeklyRecurrence = React.forwardRef(function WeeklyRecurrence(props, ref)
1143
1143
  });
1144
1144
 
1145
1145
  module.exports = {
1146
- AccordionItem,
1146
+ Accordion,
1147
1147
  ActionsMenu,
1148
1148
  AiCriteriaSearch,
1149
1149
  AiIndicator,
1150
- Accordion,
1150
+ AccordionItem,
1151
1151
  Anchor,
1152
1152
  AnchoredRegion,
1153
1153
  Avatar,
package/dist/react.mjs CHANGED
@@ -111,9 +111,9 @@ function _mergeRefs(...refs) {
111
111
  };
112
112
  }
113
113
 
114
- export const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
114
+ export const Accordion = React.forwardRef(function Accordion(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(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
117
117
  });
118
118
 
119
119
  export const ActionsMenu = React.forwardRef(function ActionsMenu(props, ref) {
@@ -148,9 +148,9 @@ export const AiIndicator = React.forwardRef(function AiIndicator(props, ref) {
148
148
  return React.createElement(customElements.getName(AiIndicatorWC) ?? '%%prefix%%-ai-indicator', { ...rest, ref }, children);
149
149
  });
150
150
 
151
- export const Accordion = React.forwardRef(function Accordion(props, ref) {
151
+ export const AccordionItem = React.forwardRef(function AccordionItem(props, ref) {
152
152
  const { children, ...rest } = props;
153
- return React.createElement(customElements.getName(AccordionWC) ?? '%%prefix%%-accordion', { ...rest, ref }, children);
153
+ return React.createElement(customElements.getName(AccordionItemWC) ?? '%%prefix%%-accordion-item', { ...rest, ref }, children);
154
154
  });
155
155
 
156
156
  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": "15.45.0",
4
+ "version": "15.46.0-canary.17",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -51,15 +51,12 @@
51
51
  },
52
52
  "scripts": {
53
53
  "build": "npm run build:copy-ms-manifest && genx build -b ts",
54
- "build:rollup": "genx build -b rollup",
55
- "build:rollup:stats": "genx analyze -b rollup",
56
54
  "build:webpack": "genx build",
57
55
  "build:webpack:stats": "genx analyze",
58
56
  "build:copy-ms-manifest": "copyfiles -u 1 ms-fast-components/custom-elements.json ../../../node_modules/@microsoft/fast-components/dist/",
59
57
  "circular": "npx -y madge --extensions ts --circular ./src",
60
58
  "clean": "rimraf dist temp tsconfig.tsbuildinfo",
61
59
  "dev": "npm run dev:tsc",
62
- "dev:rollup": "genx dev -b rollup",
63
60
  "dev:tsc": "genx dev -b ts",
64
61
  "dev:webpack": "genx dev -b webpack",
65
62
  "serve": "genx serve",
@@ -85,13 +82,12 @@
85
82
  }
86
83
  },
87
84
  "devDependencies": {
88
- "@genesislcap/foundation-testing": "15.45.0",
89
- "@genesislcap/genx": "15.45.0",
90
- "@genesislcap/rollup-builder": "15.45.0",
91
- "@genesislcap/ts-builder": "15.45.0",
92
- "@genesislcap/uvu-playwright-builder": "15.45.0",
93
- "@genesislcap/vite-builder": "15.45.0",
94
- "@genesislcap/webpack-builder": "15.45.0",
85
+ "@genesislcap/foundation-testing": "15.46.0-canary.17",
86
+ "@genesislcap/genx": "15.46.0-canary.17",
87
+ "@genesislcap/ts-builder": "15.46.0-canary.17",
88
+ "@genesislcap/uvu-playwright-builder": "15.46.0-canary.17",
89
+ "@genesislcap/vite-builder": "15.46.0-canary.17",
90
+ "@genesislcap/webpack-builder": "15.46.0-canary.17",
95
91
  "copyfiles": "^2.4.1"
96
92
  },
97
93
  "dependencies": {
@@ -100,16 +96,16 @@
100
96
  "@fortawesome/free-regular-svg-icons": "^6.2.1",
101
97
  "@fortawesome/free-solid-svg-icons": "^6.2.1",
102
98
  "@genesiscommunitysuccess/analyzer-import-alias-plugin": "^5.0.3",
103
- "@genesislcap/expression-builder": "15.45.0",
104
- "@genesislcap/foundation-ai": "15.45.0",
105
- "@genesislcap/foundation-comms": "15.45.0",
106
- "@genesislcap/foundation-criteria": "15.45.0",
107
- "@genesislcap/foundation-errors": "15.45.0",
108
- "@genesislcap/foundation-events": "15.45.0",
109
- "@genesislcap/foundation-logger": "15.45.0",
110
- "@genesislcap/foundation-notifications": "15.45.0",
111
- "@genesislcap/foundation-user": "15.45.0",
112
- "@genesislcap/foundation-utils": "15.45.0",
99
+ "@genesislcap/expression-builder": "15.46.0-canary.17",
100
+ "@genesislcap/foundation-ai": "15.46.0-canary.17",
101
+ "@genesislcap/foundation-comms": "15.46.0-canary.17",
102
+ "@genesislcap/foundation-criteria": "15.46.0-canary.17",
103
+ "@genesislcap/foundation-errors": "15.46.0-canary.17",
104
+ "@genesislcap/foundation-events": "15.46.0-canary.17",
105
+ "@genesislcap/foundation-logger": "15.46.0-canary.17",
106
+ "@genesislcap/foundation-notifications": "15.46.0-canary.17",
107
+ "@genesislcap/foundation-user": "15.46.0-canary.17",
108
+ "@genesislcap/foundation-utils": "15.46.0-canary.17",
113
109
  "@microsoft/fast-colors": "5.3.1",
114
110
  "@microsoft/fast-components": "2.30.6",
115
111
  "@microsoft/fast-element": "1.14.0",