@proyecto-viviana/solidaria 0.2.4 → 0.2.8

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.
Files changed (219) hide show
  1. package/LICENSE +21 -0
  2. package/dist/actiongroup/createActionGroup.d.ts +29 -0
  3. package/dist/actiongroup/createActionGroup.d.ts.map +1 -0
  4. package/dist/actiongroup/index.d.ts +2 -0
  5. package/dist/actiongroup/index.d.ts.map +1 -0
  6. package/dist/autocomplete/createAutocomplete.d.ts +6 -2
  7. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  8. package/dist/breadcrumbs/createBreadcrumbs.d.ts +2 -0
  9. package/dist/breadcrumbs/createBreadcrumbs.d.ts.map +1 -1
  10. package/dist/button/createToggleButtonGroup.d.ts +32 -0
  11. package/dist/button/createToggleButtonGroup.d.ts.map +1 -0
  12. package/dist/button/index.d.ts +2 -0
  13. package/dist/button/index.d.ts.map +1 -1
  14. package/dist/calendar/createCalendarCell.d.ts +2 -0
  15. package/dist/calendar/createCalendarCell.d.ts.map +1 -1
  16. package/dist/calendar/createCalendarGrid.d.ts.map +1 -1
  17. package/dist/calendar/createRangeCalendarCell.d.ts +3 -1
  18. package/dist/calendar/createRangeCalendarCell.d.ts.map +1 -1
  19. package/dist/checkbox/createCheckboxGroup.d.ts +5 -1
  20. package/dist/checkbox/createCheckboxGroup.d.ts.map +1 -1
  21. package/dist/collections/index.d.ts +56 -0
  22. package/dist/collections/index.d.ts.map +1 -0
  23. package/dist/color/createColorArea.d.ts.map +1 -1
  24. package/dist/color/createColorSlider.d.ts.map +1 -1
  25. package/dist/color/createColorWheel.d.ts.map +1 -1
  26. package/dist/combobox/createComboBox.d.ts +6 -0
  27. package/dist/combobox/createComboBox.d.ts.map +1 -1
  28. package/dist/datepicker/createDatePicker.d.ts +6 -0
  29. package/dist/datepicker/createDatePicker.d.ts.map +1 -1
  30. package/dist/datepicker/createDateRangePicker.d.ts +40 -0
  31. package/dist/datepicker/createDateRangePicker.d.ts.map +1 -0
  32. package/dist/datepicker/createDateSegment.d.ts +1 -1
  33. package/dist/datepicker/createDateSegment.d.ts.map +1 -1
  34. package/dist/datepicker/createTimeSegment.d.ts +29 -0
  35. package/dist/datepicker/createTimeSegment.d.ts.map +1 -0
  36. package/dist/datepicker/index.d.ts +2 -0
  37. package/dist/datepicker/index.d.ts.map +1 -1
  38. package/dist/disclosure/createDisclosureGroup.d.ts +2 -1
  39. package/dist/disclosure/createDisclosureGroup.d.ts.map +1 -1
  40. package/dist/dnd/createDrag.d.ts.map +1 -1
  41. package/dist/dnd/createDraggableCollection.d.ts +4 -0
  42. package/dist/dnd/createDraggableCollection.d.ts.map +1 -1
  43. package/dist/dnd/createDraggableItem.d.ts.map +1 -1
  44. package/dist/dnd/createDrop.d.ts.map +1 -1
  45. package/dist/dnd/createDroppableCollection.d.ts +32 -1
  46. package/dist/dnd/createDroppableCollection.d.ts.map +1 -1
  47. package/dist/dnd/createDroppableItem.d.ts.map +1 -1
  48. package/dist/dnd/index.d.ts +1 -1
  49. package/dist/dnd/index.d.ts.map +1 -1
  50. package/dist/grid/createGrid.d.ts.map +1 -1
  51. package/dist/gridlist/createGridList.d.ts.map +1 -1
  52. package/dist/index.d.ts +6 -4
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +4659 -3452
  55. package/dist/index.js.map +1 -7
  56. package/dist/index.ssr.js +4659 -3452
  57. package/dist/index.ssr.js.map +1 -7
  58. package/dist/interactions/createFocus.d.ts.map +1 -1
  59. package/dist/interactions/createFocusWithin.d.ts.map +1 -1
  60. package/dist/link/createLink.d.ts +10 -0
  61. package/dist/link/createLink.d.ts.map +1 -1
  62. package/dist/listbox/createListBox.d.ts +1 -0
  63. package/dist/listbox/createListBox.d.ts.map +1 -1
  64. package/dist/listbox/createOption.d.ts.map +1 -1
  65. package/dist/menu/createMenu.d.ts +1 -0
  66. package/dist/menu/createMenu.d.ts.map +1 -1
  67. package/dist/meter/createMeter.d.ts.map +1 -1
  68. package/dist/numberfield/createNumberField.d.ts +18 -0
  69. package/dist/numberfield/createNumberField.d.ts.map +1 -1
  70. package/dist/overlays/createModal.d.ts +16 -0
  71. package/dist/overlays/createModal.d.ts.map +1 -1
  72. package/dist/overlays/createOverlay.d.ts.map +1 -1
  73. package/dist/overlays/index.d.ts +1 -1
  74. package/dist/overlays/index.d.ts.map +1 -1
  75. package/dist/popover/createOverlayPosition.d.ts.map +1 -1
  76. package/dist/popover/createPopover.d.ts.map +1 -1
  77. package/dist/progress/createProgressBar.d.ts.map +1 -1
  78. package/dist/radio/createRadioGroup.d.ts +2 -2
  79. package/dist/radio/createRadioGroup.d.ts.map +1 -1
  80. package/dist/searchfield/createSearchField.d.ts.map +1 -1
  81. package/dist/select/createHiddenSelect.d.ts.map +1 -1
  82. package/dist/select/createSelect.d.ts.map +1 -1
  83. package/dist/slider/createSlider.d.ts.map +1 -1
  84. package/dist/table/createTable.d.ts.map +1 -1
  85. package/dist/tabs/createTabs.d.ts +1 -1
  86. package/dist/tabs/createTabs.d.ts.map +1 -1
  87. package/dist/tag/createTag.d.ts.map +1 -1
  88. package/dist/tag/createTagGroup.d.ts.map +1 -1
  89. package/dist/toast/createToast.d.ts +4 -0
  90. package/dist/toast/createToast.d.ts.map +1 -1
  91. package/dist/toast/createToastRegion.d.ts.map +1 -1
  92. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  93. package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
  94. package/dist/tree/createTree.d.ts.map +1 -1
  95. package/dist/tree/createTreeItem.d.ts.map +1 -1
  96. package/dist/tree/types.d.ts +4 -0
  97. package/dist/tree/types.d.ts.map +1 -1
  98. package/dist/utils/env.d.ts +1 -1
  99. package/dist/utils/env.d.ts.map +1 -1
  100. package/dist/utils/platform.d.ts.map +1 -1
  101. package/dist/visually-hidden/createVisuallyHidden.d.ts.map +1 -1
  102. package/package.json +8 -6
  103. package/src/actiongroup/createActionGroup.ts +324 -0
  104. package/src/actiongroup/index.ts +8 -0
  105. package/src/autocomplete/createAutocomplete.ts +32 -9
  106. package/src/breadcrumbs/createBreadcrumbs.ts +10 -15
  107. package/src/button/createButton.ts +1 -1
  108. package/src/button/createToggleButtonGroup.ts +128 -0
  109. package/src/button/index.ts +9 -0
  110. package/src/calendar/createCalendarCell.ts +6 -4
  111. package/src/calendar/createCalendarGrid.ts +27 -18
  112. package/src/calendar/createRangeCalendarCell.ts +26 -9
  113. package/src/checkbox/createCheckboxGroup.ts +21 -4
  114. package/src/collections/index.ts +242 -0
  115. package/src/color/createColorArea.ts +380 -314
  116. package/src/color/createColorField.ts +137 -137
  117. package/src/color/createColorSlider.ts +286 -197
  118. package/src/color/createColorSwatch.ts +40 -40
  119. package/src/color/createColorWheel.ts +218 -208
  120. package/src/color/index.ts +24 -24
  121. package/src/color/types.ts +116 -116
  122. package/src/combobox/createComboBox.ts +670 -647
  123. package/src/combobox/index.ts +6 -6
  124. package/src/datepicker/createDatePicker.ts +54 -16
  125. package/src/datepicker/createDateRangePicker.ts +246 -0
  126. package/src/datepicker/createDateSegment.ts +185 -31
  127. package/src/datepicker/createTimeSegment.ts +370 -0
  128. package/src/datepicker/index.ts +14 -0
  129. package/src/dialog/createDialog.ts +120 -120
  130. package/src/dialog/index.ts +2 -2
  131. package/src/dialog/types.ts +19 -19
  132. package/src/disclosure/createDisclosureGroup.ts +5 -2
  133. package/src/dnd/createDrag.ts +224 -209
  134. package/src/dnd/createDraggableCollection.ts +96 -63
  135. package/src/dnd/createDraggableItem.ts +259 -243
  136. package/src/dnd/createDrop.ts +322 -321
  137. package/src/dnd/createDroppableCollection.ts +682 -293
  138. package/src/dnd/createDroppableItem.ts +215 -213
  139. package/src/dnd/index.ts +55 -47
  140. package/src/dnd/types.ts +89 -89
  141. package/src/dnd/utils.ts +294 -294
  142. package/src/focus/createAutoFocus.ts +321 -321
  143. package/src/focus/createFocusRestore.ts +313 -313
  144. package/src/focus/createVirtualFocus.ts +396 -396
  145. package/src/form/createFormValidation.ts +224 -224
  146. package/src/form/index.ts +11 -11
  147. package/src/grid/createGrid.ts +3 -1
  148. package/src/gridlist/createGridList.ts +16 -0
  149. package/src/gridlist/createGridListItem.ts +1 -1
  150. package/src/i18n/NumberFormatter.ts +266 -266
  151. package/src/i18n/createCollator.ts +79 -79
  152. package/src/i18n/createDateFormatter.ts +83 -83
  153. package/src/i18n/createFilter.ts +131 -131
  154. package/src/i18n/createNumberFormatter.ts +52 -52
  155. package/src/i18n/index.ts +40 -40
  156. package/src/i18n/locale.tsx +188 -188
  157. package/src/i18n/utils.ts +99 -99
  158. package/src/index.ts +51 -0
  159. package/src/interactions/createFocus.ts +6 -5
  160. package/src/interactions/createFocusWithin.ts +6 -5
  161. package/src/interactions/createLongPress.ts +174 -174
  162. package/src/interactions/createMove.ts +289 -289
  163. package/src/interactions/createPress.ts +5 -5
  164. package/src/landmark/createLandmark.ts +377 -377
  165. package/src/landmark/index.ts +8 -8
  166. package/src/link/createLink.ts +23 -8
  167. package/src/listbox/createListBox.ts +308 -269
  168. package/src/listbox/createOption.ts +162 -151
  169. package/src/listbox/index.ts +12 -12
  170. package/src/live-announcer/announce.ts +322 -322
  171. package/src/live-announcer/index.ts +9 -9
  172. package/src/menu/createMenu.ts +405 -396
  173. package/src/menu/createMenuItem.ts +149 -149
  174. package/src/menu/createMenuTrigger.ts +88 -88
  175. package/src/menu/index.ts +18 -18
  176. package/src/meter/createMeter.ts +1 -6
  177. package/src/numberfield/createNumberField.ts +311 -268
  178. package/src/numberfield/index.ts +5 -5
  179. package/src/overlays/ariaHideOutside.ts +219 -219
  180. package/src/overlays/createInteractOutside.ts +149 -149
  181. package/src/overlays/createModal.tsx +238 -202
  182. package/src/overlays/createOverlay.ts +165 -155
  183. package/src/overlays/createOverlayTrigger.ts +85 -85
  184. package/src/overlays/createPreventScroll.ts +266 -266
  185. package/src/overlays/index.ts +48 -44
  186. package/src/popover/calculatePosition.ts +6 -6
  187. package/src/popover/createOverlayPosition.ts +7 -4
  188. package/src/popover/createPopover.ts +21 -7
  189. package/src/progress/createProgressBar.ts +6 -1
  190. package/src/radio/createRadioGroup.ts +88 -14
  191. package/src/searchfield/createSearchField.ts +241 -186
  192. package/src/searchfield/index.ts +2 -2
  193. package/src/select/createHiddenSelect.tsx +263 -236
  194. package/src/select/createSelect.ts +373 -395
  195. package/src/select/index.ts +14 -14
  196. package/src/slider/createSlider.ts +364 -349
  197. package/src/slider/index.ts +2 -2
  198. package/src/ssr/index.tsx +370 -370
  199. package/src/table/createTable.ts +3 -1
  200. package/src/table/createTableColumnHeader.ts +1 -1
  201. package/src/table/createTableRow.ts +1 -1
  202. package/src/tabs/createTabs.ts +80 -51
  203. package/src/tag/createTag.ts +135 -6
  204. package/src/tag/createTagGroup.ts +7 -2
  205. package/src/toast/createToast.ts +8 -2
  206. package/src/toast/createToastRegion.ts +0 -1
  207. package/src/toolbar/createToolbar.ts +75 -1
  208. package/src/tooltip/createTooltip.ts +79 -79
  209. package/src/tooltip/createTooltipTrigger.ts +226 -222
  210. package/src/tooltip/index.ts +6 -6
  211. package/src/tree/createTree.ts +261 -246
  212. package/src/tree/createTreeItem.ts +282 -233
  213. package/src/tree/createTreeSelectionCheckbox.ts +68 -68
  214. package/src/tree/index.ts +16 -16
  215. package/src/tree/types.ts +91 -87
  216. package/src/utils/env.ts +55 -54
  217. package/src/utils/platform.ts +16 -6
  218. package/src/visually-hidden/createVisuallyHidden.ts +139 -124
  219. package/src/visually-hidden/index.ts +6 -6
@@ -1,266 +1,266 @@
1
- /**
2
- * NumberFormatter for solidaria
3
- *
4
- * A wrapper around Intl.NumberFormat with caching and polyfills.
5
- *
6
- * Port of @internationalized/number NumberFormatter.
7
- */
8
-
9
- // ============================================
10
- // FEATURE DETECTION
11
- // ============================================
12
-
13
- let supportsSignDisplay = false;
14
- try {
15
- supportsSignDisplay =
16
- new Intl.NumberFormat('de-DE', { signDisplay: 'exceptZero' }).resolvedOptions()
17
- .signDisplay === 'exceptZero';
18
- } catch {
19
- // Not supported
20
- }
21
-
22
- let supportsUnit = false;
23
- try {
24
- supportsUnit =
25
- new Intl.NumberFormat('de-DE', { style: 'unit', unit: 'degree' }).resolvedOptions()
26
- .style === 'unit';
27
- } catch {
28
- // Not supported
29
- }
30
-
31
- // ============================================
32
- // POLYFILLS
33
- // ============================================
34
-
35
- // Polyfill for units since Safari doesn't support them yet.
36
- // Currently only polyfilling the unit degree in narrow format.
37
- const UNITS: Record<string, Record<string, Record<string, string>>> = {
38
- degree: {
39
- narrow: {
40
- default: '°',
41
- 'ja-JP': ' 度',
42
- 'zh-TW': '度',
43
- 'sl-SI': ' °',
44
- },
45
- },
46
- };
47
-
48
- // ============================================
49
- // TYPES
50
- // ============================================
51
-
52
- export interface NumberFormatOptions extends Intl.NumberFormatOptions {
53
- /** Overrides default numbering system for the current locale. */
54
- numberingSystem?: string;
55
- }
56
-
57
- interface NumberRangeFormatPart extends Intl.NumberFormatPart {
58
- source: 'startRange' | 'endRange' | 'shared';
59
- }
60
-
61
- // ============================================
62
- // CACHE
63
- // ============================================
64
-
65
- const formatterCache = new Map<string, Intl.NumberFormat>();
66
-
67
- function getCachedNumberFormatter(
68
- locale: string,
69
- options: NumberFormatOptions = {}
70
- ): Intl.NumberFormat {
71
- let processedLocale = locale;
72
- const { numberingSystem } = options;
73
-
74
- if (numberingSystem && !processedLocale.includes('-nu-')) {
75
- if (!processedLocale.includes('-u-')) {
76
- processedLocale += '-u-';
77
- }
78
- processedLocale += `-nu-${numberingSystem}`;
79
- }
80
-
81
- let processedOptions = options;
82
- if (options.style === 'unit' && !supportsUnit) {
83
- const { unit, unitDisplay = 'short' } = options;
84
- if (!unit) {
85
- throw new Error('unit option must be provided with style: "unit"');
86
- }
87
- if (!UNITS[unit]?.[unitDisplay]) {
88
- throw new Error(`Unsupported unit ${unit} with unitDisplay = ${unitDisplay}`);
89
- }
90
- processedOptions = { ...options, style: 'decimal' };
91
- }
92
-
93
- const cacheKey =
94
- processedLocale +
95
- (processedOptions
96
- ? Object.entries(processedOptions)
97
- .sort((a, b) => (a[0] < b[0] ? -1 : 1))
98
- .join()
99
- : '');
100
-
101
- if (formatterCache.has(cacheKey)) {
102
- return formatterCache.get(cacheKey)!;
103
- }
104
-
105
- const numberFormatter = new Intl.NumberFormat(processedLocale, processedOptions);
106
- formatterCache.set(cacheKey, numberFormatter);
107
- return numberFormatter;
108
- }
109
-
110
- // ============================================
111
- // SIGN DISPLAY POLYFILL
112
- // ============================================
113
-
114
- function numberFormatSignDisplayPolyfill(
115
- numberFormat: Intl.NumberFormat,
116
- signDisplay: string,
117
- num: number
118
- ): string {
119
- if (signDisplay === 'auto') {
120
- return numberFormat.format(num);
121
- } else if (signDisplay === 'never') {
122
- return numberFormat.format(Math.abs(num));
123
- } else {
124
- let needsPositiveSign = false;
125
- let processedNum = num;
126
-
127
- if (signDisplay === 'always') {
128
- needsPositiveSign = num > 0 || Object.is(num, 0);
129
- } else if (signDisplay === 'exceptZero') {
130
- if (Object.is(num, -0) || Object.is(num, 0)) {
131
- processedNum = Math.abs(num);
132
- } else {
133
- needsPositiveSign = num > 0;
134
- }
135
- }
136
-
137
- if (needsPositiveSign) {
138
- const negative = numberFormat.format(-processedNum);
139
- const noSign = numberFormat.format(processedNum);
140
- // Ignore RTL/LTR marker characters
141
- const minus = negative.replace(noSign, '').replace(/\u200e|\u061C/, '');
142
- if ([...minus].length !== 1) {
143
- console.warn(
144
- 'solidaria i18n polyfill for NumberFormat signDisplay: Unsupported case'
145
- );
146
- }
147
- const positive = negative
148
- .replace(noSign, '!!!')
149
- .replace(minus, '+')
150
- .replace('!!!', noSign);
151
- return positive;
152
- } else {
153
- return numberFormat.format(processedNum);
154
- }
155
- }
156
- }
157
-
158
- // ============================================
159
- // NUMBER FORMATTER CLASS
160
- // ============================================
161
-
162
- /**
163
- * A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching.
164
- *
165
- * @example
166
- * ```ts
167
- * const formatter = new NumberFormatter('en-US', {
168
- * style: 'currency',
169
- * currency: 'USD',
170
- * });
171
- * formatter.format(1234.56); // '$1,234.56'
172
- * ```
173
- */
174
- export class NumberFormatter implements Intl.NumberFormat {
175
- private numberFormatter: Intl.NumberFormat;
176
- private options: NumberFormatOptions;
177
-
178
- constructor(locale: string, options: NumberFormatOptions = {}) {
179
- this.numberFormatter = getCachedNumberFormatter(locale, options);
180
- this.options = options;
181
- }
182
-
183
- /** Formats a number value as a string, according to the locale and options. */
184
- format(value: number): string {
185
- let res = '';
186
-
187
- if (!supportsSignDisplay && this.options.signDisplay != null) {
188
- res = numberFormatSignDisplayPolyfill(
189
- this.numberFormatter,
190
- this.options.signDisplay,
191
- value
192
- );
193
- } else {
194
- res = this.numberFormatter.format(value);
195
- }
196
-
197
- if (this.options.style === 'unit' && !supportsUnit) {
198
- const { unit, unitDisplay = 'short', locale } = this.resolvedOptions();
199
- if (!unit) {
200
- return res;
201
- }
202
- const values = UNITS[unit]?.[unitDisplay];
203
- res += values[locale] || values.default;
204
- }
205
-
206
- return res;
207
- }
208
-
209
- /** Formats a number to an array of parts. */
210
- formatToParts(value: number): Intl.NumberFormatPart[] {
211
- return this.numberFormatter.formatToParts(value);
212
- }
213
-
214
- /** Formats a number range as a string. */
215
- formatRange(start: number, end: number): string {
216
- if (typeof this.numberFormatter.formatRange === 'function') {
217
- return this.numberFormatter.formatRange(start, end);
218
- }
219
-
220
- if (end < start) {
221
- throw new RangeError('End date must be >= start date');
222
- }
223
-
224
- // Fallback for old browsers
225
- return `${this.format(start)} – ${this.format(end)}`;
226
- }
227
-
228
- /** Formats a number range as an array of parts. */
229
- formatRangeToParts(start: number, end: number): NumberRangeFormatPart[] {
230
- if (typeof this.numberFormatter.formatRangeToParts === 'function') {
231
- return this.numberFormatter.formatRangeToParts(start, end);
232
- }
233
-
234
- if (end < start) {
235
- throw new RangeError('End date must be >= start date');
236
- }
237
-
238
- const startParts = this.numberFormatter.formatToParts(start);
239
- const endParts = this.numberFormatter.formatToParts(end);
240
- return [
241
- ...startParts.map((p) => ({ ...p, source: 'startRange' as const })),
242
- { type: 'literal' as const, value: ' – ', source: 'shared' as const },
243
- ...endParts.map((p) => ({ ...p, source: 'endRange' as const })),
244
- ];
245
- }
246
-
247
- /** Returns the resolved formatting options. */
248
- resolvedOptions(): Intl.ResolvedNumberFormatOptions {
249
- let options = this.numberFormatter.resolvedOptions();
250
-
251
- if (!supportsSignDisplay && this.options.signDisplay != null) {
252
- options = { ...options, signDisplay: this.options.signDisplay };
253
- }
254
-
255
- if (!supportsUnit && this.options.style === 'unit') {
256
- options = {
257
- ...options,
258
- style: 'unit',
259
- unit: this.options.unit,
260
- unitDisplay: this.options.unitDisplay,
261
- };
262
- }
263
-
264
- return options;
265
- }
266
- }
1
+ /**
2
+ * NumberFormatter for solidaria
3
+ *
4
+ * A wrapper around Intl.NumberFormat with caching and polyfills.
5
+ *
6
+ * Port of @internationalized/number NumberFormatter.
7
+ */
8
+
9
+ // ============================================
10
+ // FEATURE DETECTION
11
+ // ============================================
12
+
13
+ let supportsSignDisplay = false;
14
+ try {
15
+ supportsSignDisplay =
16
+ new Intl.NumberFormat('de-DE', { signDisplay: 'exceptZero' }).resolvedOptions()
17
+ .signDisplay === 'exceptZero';
18
+ } catch {
19
+ // Not supported
20
+ }
21
+
22
+ let supportsUnit = false;
23
+ try {
24
+ supportsUnit =
25
+ new Intl.NumberFormat('de-DE', { style: 'unit', unit: 'degree' }).resolvedOptions()
26
+ .style === 'unit';
27
+ } catch {
28
+ // Not supported
29
+ }
30
+
31
+ // ============================================
32
+ // POLYFILLS
33
+ // ============================================
34
+
35
+ // Polyfill for units since Safari doesn't support them yet.
36
+ // Currently only polyfilling the unit degree in narrow format.
37
+ const UNITS: Record<string, Record<string, Record<string, string>>> = {
38
+ degree: {
39
+ narrow: {
40
+ default: '°',
41
+ 'ja-JP': ' 度',
42
+ 'zh-TW': '度',
43
+ 'sl-SI': ' °',
44
+ },
45
+ },
46
+ };
47
+
48
+ // ============================================
49
+ // TYPES
50
+ // ============================================
51
+
52
+ export interface NumberFormatOptions extends Intl.NumberFormatOptions {
53
+ /** Overrides default numbering system for the current locale. */
54
+ numberingSystem?: string;
55
+ }
56
+
57
+ interface NumberRangeFormatPart extends Intl.NumberFormatPart {
58
+ source: 'startRange' | 'endRange' | 'shared';
59
+ }
60
+
61
+ // ============================================
62
+ // CACHE
63
+ // ============================================
64
+
65
+ const formatterCache = new Map<string, Intl.NumberFormat>();
66
+
67
+ function getCachedNumberFormatter(
68
+ locale: string,
69
+ options: NumberFormatOptions = {}
70
+ ): Intl.NumberFormat {
71
+ let processedLocale = locale;
72
+ const { numberingSystem } = options;
73
+
74
+ if (numberingSystem && !processedLocale.includes('-nu-')) {
75
+ if (!processedLocale.includes('-u-')) {
76
+ processedLocale += '-u-';
77
+ }
78
+ processedLocale += `-nu-${numberingSystem}`;
79
+ }
80
+
81
+ let processedOptions = options;
82
+ if (options.style === 'unit' && !supportsUnit) {
83
+ const { unit, unitDisplay = 'short' } = options;
84
+ if (!unit) {
85
+ throw new Error('unit option must be provided with style: "unit"');
86
+ }
87
+ if (!UNITS[unit]?.[unitDisplay]) {
88
+ throw new Error(`Unsupported unit ${unit} with unitDisplay = ${unitDisplay}`);
89
+ }
90
+ processedOptions = { ...options, style: 'decimal' };
91
+ }
92
+
93
+ const cacheKey =
94
+ processedLocale +
95
+ (processedOptions
96
+ ? Object.entries(processedOptions)
97
+ .sort((a, b) => (a[0] < b[0] ? -1 : 1))
98
+ .join()
99
+ : '');
100
+
101
+ if (formatterCache.has(cacheKey)) {
102
+ return formatterCache.get(cacheKey)!;
103
+ }
104
+
105
+ const numberFormatter = new Intl.NumberFormat(processedLocale, processedOptions);
106
+ formatterCache.set(cacheKey, numberFormatter);
107
+ return numberFormatter;
108
+ }
109
+
110
+ // ============================================
111
+ // SIGN DISPLAY POLYFILL
112
+ // ============================================
113
+
114
+ function numberFormatSignDisplayPolyfill(
115
+ numberFormat: Intl.NumberFormat,
116
+ signDisplay: string,
117
+ num: number
118
+ ): string {
119
+ if (signDisplay === 'auto') {
120
+ return numberFormat.format(num);
121
+ } else if (signDisplay === 'never') {
122
+ return numberFormat.format(Math.abs(num));
123
+ } else {
124
+ let needsPositiveSign = false;
125
+ let processedNum = num;
126
+
127
+ if (signDisplay === 'always') {
128
+ needsPositiveSign = num > 0 || Object.is(num, 0);
129
+ } else if (signDisplay === 'exceptZero') {
130
+ if (Object.is(num, -0) || Object.is(num, 0)) {
131
+ processedNum = Math.abs(num);
132
+ } else {
133
+ needsPositiveSign = num > 0;
134
+ }
135
+ }
136
+
137
+ if (needsPositiveSign) {
138
+ const negative = numberFormat.format(-processedNum);
139
+ const noSign = numberFormat.format(processedNum);
140
+ // Ignore RTL/LTR marker characters
141
+ const minus = negative.replace(noSign, '').replace(/\u200e|\u061C/, '');
142
+ if ([...minus].length !== 1) {
143
+ console.warn(
144
+ 'solidaria i18n polyfill for NumberFormat signDisplay: Unsupported case'
145
+ );
146
+ }
147
+ const positive = negative
148
+ .replace(noSign, '!!!')
149
+ .replace(minus, '+')
150
+ .replace('!!!', noSign);
151
+ return positive;
152
+ } else {
153
+ return numberFormat.format(processedNum);
154
+ }
155
+ }
156
+ }
157
+
158
+ // ============================================
159
+ // NUMBER FORMATTER CLASS
160
+ // ============================================
161
+
162
+ /**
163
+ * A wrapper around Intl.NumberFormat providing additional options, polyfills, and caching.
164
+ *
165
+ * @example
166
+ * ```ts
167
+ * const formatter = new NumberFormatter('en-US', {
168
+ * style: 'currency',
169
+ * currency: 'USD',
170
+ * });
171
+ * formatter.format(1234.56); // '$1,234.56'
172
+ * ```
173
+ */
174
+ export class NumberFormatter implements Intl.NumberFormat {
175
+ private numberFormatter: Intl.NumberFormat;
176
+ private options: NumberFormatOptions;
177
+
178
+ constructor(locale: string, options: NumberFormatOptions = {}) {
179
+ this.numberFormatter = getCachedNumberFormatter(locale, options);
180
+ this.options = options;
181
+ }
182
+
183
+ /** Formats a number value as a string, according to the locale and options. */
184
+ format(value: number): string {
185
+ let res = '';
186
+
187
+ if (!supportsSignDisplay && this.options.signDisplay != null) {
188
+ res = numberFormatSignDisplayPolyfill(
189
+ this.numberFormatter,
190
+ this.options.signDisplay,
191
+ value
192
+ );
193
+ } else {
194
+ res = this.numberFormatter.format(value);
195
+ }
196
+
197
+ if (this.options.style === 'unit' && !supportsUnit) {
198
+ const { unit, unitDisplay = 'short', locale } = this.resolvedOptions();
199
+ if (!unit) {
200
+ return res;
201
+ }
202
+ const values = UNITS[unit]?.[unitDisplay];
203
+ res += values[locale] || values.default;
204
+ }
205
+
206
+ return res;
207
+ }
208
+
209
+ /** Formats a number to an array of parts. */
210
+ formatToParts(value: number): Intl.NumberFormatPart[] {
211
+ return this.numberFormatter.formatToParts(value);
212
+ }
213
+
214
+ /** Formats a number range as a string. */
215
+ formatRange(start: number, end: number): string {
216
+ if (typeof this.numberFormatter.formatRange === 'function') {
217
+ return this.numberFormatter.formatRange(start, end);
218
+ }
219
+
220
+ if (end < start) {
221
+ throw new RangeError('End date must be >= start date');
222
+ }
223
+
224
+ // Fallback for old browsers
225
+ return `${this.format(start)} – ${this.format(end)}`;
226
+ }
227
+
228
+ /** Formats a number range as an array of parts. */
229
+ formatRangeToParts(start: number, end: number): NumberRangeFormatPart[] {
230
+ if (typeof this.numberFormatter.formatRangeToParts === 'function') {
231
+ return this.numberFormatter.formatRangeToParts(start, end);
232
+ }
233
+
234
+ if (end < start) {
235
+ throw new RangeError('End date must be >= start date');
236
+ }
237
+
238
+ const startParts = this.numberFormatter.formatToParts(start);
239
+ const endParts = this.numberFormatter.formatToParts(end);
240
+ return [
241
+ ...startParts.map((p) => ({ ...p, source: 'startRange' as const })),
242
+ { type: 'literal' as const, value: ' – ', source: 'shared' as const },
243
+ ...endParts.map((p) => ({ ...p, source: 'endRange' as const })),
244
+ ];
245
+ }
246
+
247
+ /** Returns the resolved formatting options. */
248
+ resolvedOptions(): Intl.ResolvedNumberFormatOptions {
249
+ let options = this.numberFormatter.resolvedOptions();
250
+
251
+ if (!supportsSignDisplay && this.options.signDisplay != null) {
252
+ options = { ...options, signDisplay: this.options.signDisplay };
253
+ }
254
+
255
+ if (!supportsUnit && this.options.style === 'unit') {
256
+ options = {
257
+ ...options,
258
+ style: 'unit',
259
+ unit: this.options.unit,
260
+ unitDisplay: this.options.unitDisplay,
261
+ };
262
+ }
263
+
264
+ return options;
265
+ }
266
+ }