@nanahoshi/mona-ui 0.18.5 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +71 -13
  2. package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +11 -3
  3. package/fesm2022/nanahoshi-mona-ui-breadcrumb.mjs +17 -9
  4. package/fesm2022/nanahoshi-mona-ui-button-group.mjs +16 -7
  5. package/fesm2022/nanahoshi-mona-ui-calendar.mjs +122 -48
  6. package/fesm2022/nanahoshi-mona-ui-card.mjs +22 -14
  7. package/fesm2022/nanahoshi-mona-ui-chart.mjs +78 -18
  8. package/fesm2022/nanahoshi-mona-ui-check-box.mjs +1 -1
  9. package/fesm2022/nanahoshi-mona-ui-chip.mjs +199 -77
  10. package/fesm2022/nanahoshi-mona-ui-circular-progress-bar.mjs +1 -2
  11. package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +23 -5
  12. package/fesm2022/nanahoshi-mona-ui-color-palette.mjs +49 -28
  13. package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +42 -26
  14. package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +11 -3
  15. package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +28 -16
  16. package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +74 -51
  17. package/fesm2022/nanahoshi-mona-ui-dialog.mjs +57 -25
  18. package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +13 -5
  19. package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +17 -6
  20. package/fesm2022/nanahoshi-mona-ui-editor.mjs +178 -59
  21. package/fesm2022/nanahoshi-mona-ui-filter.mjs +137 -54
  22. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-cell.component-WEDbZ5vy.mjs → nanahoshi-mona-ui-grid-grid-footer-cell.component-Bg7rckst.mjs} +2 -2
  23. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-ibH5osCg.mjs → nanahoshi-mona-ui-grid-grid-footer-table-cell.directive-BSSpaCLL.mjs} +2 -2
  24. package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-D9AwQFDV.mjs +145 -0
  25. package/fesm2022/{nanahoshi-mona-ui-grid-grid-toggle.component-hc-LeIb0.mjs → nanahoshi-mona-ui-grid-grid-toggle.component-Byb9iTes.mjs} +21 -17
  26. package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-DYajpOb9.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-CBmdtrXm.mjs} +5 -5
  27. package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Pm8Fbp6M.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-DPmJmCap.mjs} +90 -42
  28. package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
  29. package/fesm2022/nanahoshi-mona-ui-i18n.mjs +1168 -0
  30. package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +12 -4
  31. package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +48 -21
  32. package/fesm2022/nanahoshi-mona-ui-list-box.mjs +33 -18
  33. package/fesm2022/nanahoshi-mona-ui-list-view.mjs +10 -10
  34. package/fesm2022/nanahoshi-mona-ui-list.mjs +2 -0
  35. package/fesm2022/nanahoshi-mona-ui-menubar.mjs +29 -16
  36. package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +12 -3
  37. package/fesm2022/nanahoshi-mona-ui-notification.mjs +23 -18
  38. package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +211 -106
  39. package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +14 -6
  40. package/fesm2022/nanahoshi-mona-ui-pager.mjs +46 -19
  41. package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +57 -46
  42. package/fesm2022/nanahoshi-mona-ui-popup.mjs +35 -15
  43. package/fesm2022/nanahoshi-mona-ui-progress-bar.mjs +38 -17
  44. package/fesm2022/nanahoshi-mona-ui-rating.mjs +25 -7
  45. package/fesm2022/nanahoshi-mona-ui-scroll-view.mjs +431 -101
  46. package/fesm2022/nanahoshi-mona-ui-segmented.mjs +1 -1
  47. package/fesm2022/nanahoshi-mona-ui-sheet.mjs +26 -8
  48. package/fesm2022/nanahoshi-mona-ui-sidebar.mjs +222 -28
  49. package/fesm2022/nanahoshi-mona-ui-slider.mjs +91 -36
  50. package/fesm2022/nanahoshi-mona-ui-spinner.mjs +34 -15
  51. package/fesm2022/nanahoshi-mona-ui-split-button.mjs +39 -23
  52. package/fesm2022/nanahoshi-mona-ui-splitter.mjs +50 -25
  53. package/fesm2022/nanahoshi-mona-ui-stepper.mjs +32 -14
  54. package/fesm2022/nanahoshi-mona-ui-switch.mjs +9 -9
  55. package/fesm2022/nanahoshi-mona-ui-tabs.mjs +74 -56
  56. package/fesm2022/nanahoshi-mona-ui-text-box.mjs +16 -8
  57. package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +33 -19
  58. package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +66 -27
  59. package/fesm2022/nanahoshi-mona-ui-tree-view.mjs +23 -15
  60. package/fesm2022/nanahoshi-mona-ui-window.mjs +71 -40
  61. package/i18n/package.json +4 -0
  62. package/package.json +5 -1
  63. package/types/nanahoshi-mona-ui-auto-complete.d.ts +6 -1
  64. package/types/nanahoshi-mona-ui-breadcrumb.d.ts +9 -3
  65. package/types/nanahoshi-mona-ui-button-group.d.ts +9 -3
  66. package/types/nanahoshi-mona-ui-calendar.d.ts +13 -3
  67. package/types/nanahoshi-mona-ui-card.d.ts +8 -2
  68. package/types/nanahoshi-mona-ui-chart.d.ts +10 -1
  69. package/types/nanahoshi-mona-ui-chip.d.ts +14 -5
  70. package/types/nanahoshi-mona-ui-color-gradient.d.ts +6 -1
  71. package/types/nanahoshi-mona-ui-color-palette.d.ts +21 -10
  72. package/types/nanahoshi-mona-ui-color-picker.d.ts +30 -20
  73. package/types/nanahoshi-mona-ui-combo-box.d.ts +6 -1
  74. package/types/nanahoshi-mona-ui-date-picker.d.ts +7 -2
  75. package/types/nanahoshi-mona-ui-datetime-picker.d.ts +33 -8
  76. package/types/nanahoshi-mona-ui-dialog.d.ts +16 -7
  77. package/types/nanahoshi-mona-ui-dropdown-list.d.ts +6 -1
  78. package/types/nanahoshi-mona-ui-dropdowns.d.ts +4 -1
  79. package/types/nanahoshi-mona-ui-editor.d.ts +6 -1
  80. package/types/nanahoshi-mona-ui-filter.d.ts +16 -10
  81. package/types/nanahoshi-mona-ui-grid.d.ts +38 -1
  82. package/types/nanahoshi-mona-ui-i18n.d.ts +633 -0
  83. package/types/nanahoshi-mona-ui-internal-list.d.ts +7 -2
  84. package/types/nanahoshi-mona-ui-internal-tree.d.ts +7 -3
  85. package/types/nanahoshi-mona-ui-list-box.d.ts +9 -4
  86. package/types/nanahoshi-mona-ui-list-view.d.ts +6 -6
  87. package/types/nanahoshi-mona-ui-list.d.ts +1 -1
  88. package/types/nanahoshi-mona-ui-menubar.d.ts +59 -58
  89. package/types/nanahoshi-mona-ui-multi-select.d.ts +6 -1
  90. package/types/nanahoshi-mona-ui-notification.d.ts +11 -5
  91. package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +24 -15
  92. package/types/nanahoshi-mona-ui-otp-input.d.ts +9 -3
  93. package/types/nanahoshi-mona-ui-pager.d.ts +19 -6
  94. package/types/nanahoshi-mona-ui-popup.d.ts +2 -1
  95. package/types/nanahoshi-mona-ui-progress-bar.d.ts +9 -7
  96. package/types/nanahoshi-mona-ui-rating.d.ts +19 -12
  97. package/types/nanahoshi-mona-ui-scroll-view.d.ts +34 -28
  98. package/types/nanahoshi-mona-ui-sheet.d.ts +8 -2
  99. package/types/nanahoshi-mona-ui-sidebar.d.ts +3 -0
  100. package/types/nanahoshi-mona-ui-slider.d.ts +44 -26
  101. package/types/nanahoshi-mona-ui-spinner.d.ts +13 -4
  102. package/types/nanahoshi-mona-ui-split-button.d.ts +62 -56
  103. package/types/nanahoshi-mona-ui-splitter.d.ts +9 -6
  104. package/types/nanahoshi-mona-ui-stepper.d.ts +9 -3
  105. package/types/nanahoshi-mona-ui-switch.d.ts +1 -1
  106. package/types/nanahoshi-mona-ui-tabs.d.ts +96 -35
  107. package/types/nanahoshi-mona-ui-text-box.d.ts +11 -5
  108. package/types/nanahoshi-mona-ui-time-picker.d.ts +7 -2
  109. package/types/nanahoshi-mona-ui-time-selector.d.ts +16 -5
  110. package/types/nanahoshi-mona-ui-tree-view.d.ts +15 -10
  111. package/types/nanahoshi-mona-ui-window.d.ts +26 -20
  112. package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-ZM3CneJ0.mjs +0 -145
@@ -0,0 +1,1168 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, signal, computed, Injectable, ElementRef, DestroyRef, makeEnvironmentProviders } from '@angular/core';
3
+ import { Directionality } from '@angular/cdk/bidi';
4
+ import { DateTime } from 'luxon';
5
+
6
+ const MONA_DEFAULT_LOCALE = {
7
+ direction: "ltr",
8
+ id: "en-US",
9
+ messages: {}
10
+ };
11
+
12
+ const MONA_I18N_CONFIG = new InjectionToken("MONA_I18N_CONFIG", {
13
+ factory: () => ({})
14
+ });
15
+
16
+ function isPlainObject(value) {
17
+ return value !== null && typeof value === "object" && !Array.isArray(value);
18
+ }
19
+ function mergeTwo(base, source) {
20
+ if (!source) {
21
+ return base;
22
+ }
23
+ const result = { ...base };
24
+ const sourceObj = source;
25
+ for (const key of Object.keys(sourceObj)) {
26
+ const sourceVal = sourceObj[key];
27
+ if (sourceVal === undefined) {
28
+ continue;
29
+ }
30
+ const baseVal = result[key];
31
+ if (isPlainObject(baseVal) && isPlainObject(sourceVal)) {
32
+ result[key] = mergeTwo(baseVal, sourceVal);
33
+ }
34
+ else {
35
+ result[key] = sourceVal;
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ function mergeMessages(fallback, localeMessages, overrideMessages) {
41
+ const withLocale = mergeTwo(fallback, localeMessages);
42
+ return mergeTwo(withLocale, overrideMessages);
43
+ }
44
+
45
+ class MonaI18nService {
46
+ constructor() {
47
+ this.#config = inject(MONA_I18N_CONFIG);
48
+ this.#locale = signal(this.#config.locale ?? MONA_DEFAULT_LOCALE, /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "#locale" }] : /* istanbul ignore next */ []));
50
+ this.#overrides = signal(this.#config.messages ?? {}, /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "#overrides" }] : /* istanbul ignore next */ []));
52
+ this.direction = computed(() => this.#locale().direction, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
54
+ this.locale = this.#locale.asReadonly();
55
+ this.localeId = computed(() => this.#locale().id, /* @ts-ignore */
56
+ ...(ngDevMode ? [{ debugName: "localeId" }] : /* istanbul ignore next */ []));
57
+ }
58
+ #config;
59
+ #locale;
60
+ #overrides;
61
+ clearMessages() {
62
+ this.#overrides.set({});
63
+ }
64
+ componentMessages(namespace, fallback) {
65
+ return computed(() => {
66
+ const localeMessages = this.#locale().messages[namespace];
67
+ const overrideMessages = this.#overrides()[namespace];
68
+ return mergeMessages(fallback, localeMessages, overrideMessages);
69
+ });
70
+ }
71
+ patchMessages(messages) {
72
+ this.#overrides.update(current => mergeTwo(current, messages));
73
+ }
74
+ setMessages(messages) {
75
+ this.#overrides.set(messages);
76
+ }
77
+ use(locale) {
78
+ this.#locale.set(locale);
79
+ }
80
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaI18nService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
81
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaI18nService, providedIn: "root" }); }
82
+ }
83
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaI18nService, decorators: [{
84
+ type: Injectable,
85
+ args: [{
86
+ providedIn: "root"
87
+ }]
88
+ }] });
89
+
90
+ /**
91
+ * Reads semantic document direction for a connected browser element using `:dir(rtl)` and `:dir(ltr)`.
92
+ *
93
+ * Mona UI direction-sensitive behaviors follow semantic document directionality
94
+ * (`:dir(...)`, `[dir]`, and CDK Directionality), matching Tailwind's `rtl:`/`ltr:`
95
+ * variant matching. Styling-only CSS `direction` property is not treated as the
96
+ * authoritative Mona direction switch.
97
+ */
98
+ function readSemanticDirection(element) {
99
+ if (!element || !element.isConnected) {
100
+ return null;
101
+ }
102
+ try {
103
+ if (element.matches(":dir(rtl)")) {
104
+ return "rtl";
105
+ }
106
+ if (element.matches(":dir(ltr)")) {
107
+ return "ltr";
108
+ }
109
+ }
110
+ catch {
111
+ // selector unsupported / environment limitation
112
+ }
113
+ return null;
114
+ }
115
+ function findNearestValidDirState(element) {
116
+ let current = element;
117
+ while (current) {
118
+ const rawDir = current.getAttribute?.("dir")?.trim().toLowerCase();
119
+ if (rawDir === "rtl" || rawDir === "ltr") {
120
+ return rawDir;
121
+ }
122
+ if (rawDir === "auto") {
123
+ const semanticDir = readSemanticDirection(current) ?? readSemanticDirection(element);
124
+ if (semanticDir) {
125
+ return semanticDir;
126
+ }
127
+ }
128
+ current = current.parentElement;
129
+ }
130
+ return null;
131
+ }
132
+ function resolveComponentDirection(hostElement, directionality) {
133
+ const element = hostElement instanceof ElementRef ? hostElement.nativeElement : hostElement;
134
+ // 1. Connected browser semantic direction (:dir(rtl) / :dir(ltr))
135
+ const semantic = readSemanticDirection(element);
136
+ if (semantic) {
137
+ return semantic;
138
+ }
139
+ // 2. Non-browser / unattached DOM fallback: nearest ancestor explicit dir attribute ("ltr", "rtl", or "auto")
140
+ const explicit = findNearestValidDirState(element);
141
+ if (explicit) {
142
+ return explicit;
143
+ }
144
+ // 3. Angular CDK Directionality fallback
145
+ if (directionality?.value === "rtl" || directionality?.value === "ltr") {
146
+ return directionality.value;
147
+ }
148
+ return "ltr";
149
+ }
150
+ const documentObservers = new WeakMap();
151
+ function observeDocumentDirChanges(doc, callback) {
152
+ let entry = documentObservers.get(doc);
153
+ if (!entry) {
154
+ const callbacks = new Set();
155
+ const observer = new MutationObserver(() => {
156
+ for (const cb of Array.from(callbacks)) {
157
+ cb();
158
+ }
159
+ });
160
+ if (doc.documentElement) {
161
+ observer.observe(doc.documentElement, {
162
+ attributes: true,
163
+ attributeFilter: ["dir"],
164
+ subtree: true
165
+ });
166
+ }
167
+ entry = { refCount: 0, observer, callbacks };
168
+ documentObservers.set(doc, entry);
169
+ }
170
+ entry.refCount++;
171
+ entry.callbacks.add(callback);
172
+ let cleanedUp = false;
173
+ return () => {
174
+ if (cleanedUp || !entry) {
175
+ return;
176
+ }
177
+ cleanedUp = true;
178
+ entry.callbacks.delete(callback);
179
+ entry.refCount--;
180
+ if (entry.refCount <= 0) {
181
+ entry.observer.disconnect();
182
+ documentObservers.delete(doc);
183
+ }
184
+ };
185
+ }
186
+ const autoDirectionObservers = new WeakMap();
187
+ function observeAutoDirectionChanges(autoRoot, callback) {
188
+ let entry = autoDirectionObservers.get(autoRoot);
189
+ if (!entry) {
190
+ const callbacks = new Set();
191
+ const observer = new MutationObserver(() => {
192
+ const currentDir = readSemanticDirection(autoRoot);
193
+ if (entry && (entry.lastDirection === null || currentDir !== entry.lastDirection)) {
194
+ entry.lastDirection = currentDir;
195
+ for (const cb of Array.from(callbacks)) {
196
+ cb();
197
+ }
198
+ }
199
+ });
200
+ observer.observe(autoRoot, {
201
+ childList: true,
202
+ characterData: true,
203
+ subtree: true
204
+ });
205
+ entry = {
206
+ refCount: 0,
207
+ observer,
208
+ callbacks,
209
+ lastDirection: readSemanticDirection(autoRoot)
210
+ };
211
+ autoDirectionObservers.set(autoRoot, entry);
212
+ }
213
+ entry.refCount++;
214
+ entry.callbacks.add(callback);
215
+ let cleanedUp = false;
216
+ return () => {
217
+ if (cleanedUp || !entry) {
218
+ return;
219
+ }
220
+ cleanedUp = true;
221
+ entry.callbacks.delete(callback);
222
+ entry.refCount--;
223
+ if (entry.refCount <= 0) {
224
+ entry.observer.disconnect();
225
+ autoDirectionObservers.delete(autoRoot);
226
+ }
227
+ };
228
+ }
229
+ function observeComponentDirection(hostElement, directionality, onChange) {
230
+ const element = hostElement instanceof ElementRef ? hostElement.nativeElement : hostElement;
231
+ let currentDir = resolveComponentDirection(element, directionality);
232
+ let unobserveAuto = null;
233
+ let observedAutoElement = null;
234
+ const updateAutoContentObserver = () => {
235
+ if (typeof MutationObserver === "undefined" || !element) {
236
+ return;
237
+ }
238
+ let autoRoot = null;
239
+ let current = element;
240
+ while (current) {
241
+ const rawDir = current.getAttribute?.("dir")?.trim().toLowerCase();
242
+ if (rawDir === "auto") {
243
+ autoRoot = current;
244
+ break;
245
+ }
246
+ if (rawDir === "rtl" || rawDir === "ltr") {
247
+ break;
248
+ }
249
+ current = current.parentElement;
250
+ }
251
+ if (autoRoot) {
252
+ if (observedAutoElement !== autoRoot) {
253
+ if (unobserveAuto) {
254
+ unobserveAuto();
255
+ }
256
+ observedAutoElement = autoRoot;
257
+ unobserveAuto = observeAutoDirectionChanges(autoRoot, () => check());
258
+ }
259
+ }
260
+ else {
261
+ if (unobserveAuto) {
262
+ unobserveAuto();
263
+ unobserveAuto = null;
264
+ observedAutoElement = null;
265
+ }
266
+ }
267
+ };
268
+ const check = () => {
269
+ updateAutoContentObserver();
270
+ const nextDir = resolveComponentDirection(element, directionality);
271
+ if (nextDir !== currentDir) {
272
+ currentDir = nextDir;
273
+ onChange(nextDir);
274
+ }
275
+ };
276
+ updateAutoContentObserver();
277
+ const cleanupFns = [];
278
+ if (directionality) {
279
+ const sub = directionality.change.subscribe(dir => {
280
+ if (dir === "rtl" || dir === "ltr") {
281
+ check();
282
+ }
283
+ });
284
+ cleanupFns.push(() => sub.unsubscribe());
285
+ }
286
+ if (typeof MutationObserver !== "undefined") {
287
+ if (element) {
288
+ const elObserver = new MutationObserver(() => check());
289
+ elObserver.observe(element, { attributes: true, attributeFilter: ["dir"] });
290
+ cleanupFns.push(() => elObserver.disconnect());
291
+ const root = element.getRootNode?.();
292
+ if (root && root !== element && root !== element.ownerDocument) {
293
+ const rootObserver = new MutationObserver(() => check());
294
+ rootObserver.observe(root, { attributes: true, attributeFilter: ["dir"], subtree: true });
295
+ cleanupFns.push(() => rootObserver.disconnect());
296
+ }
297
+ if (element.ownerDocument) {
298
+ cleanupFns.push(observeDocumentDirChanges(element.ownerDocument, check));
299
+ }
300
+ }
301
+ }
302
+ cleanupFns.push(() => {
303
+ if (unobserveAuto) {
304
+ unobserveAuto();
305
+ unobserveAuto = null;
306
+ observedAutoElement = null;
307
+ }
308
+ });
309
+ const handle = Object.assign(() => {
310
+ for (const fn of cleanupFns) {
311
+ fn();
312
+ }
313
+ }, {
314
+ check,
315
+ updateAutoObserver: updateAutoContentObserver
316
+ });
317
+ return handle;
318
+ }
319
+ function injectComponentDirection(hostElementRef) {
320
+ const directionality = inject(Directionality, { optional: true });
321
+ const hostRef = hostElementRef ?? inject((ElementRef), { optional: true });
322
+ const destroyRef = inject(DestroyRef, { optional: true });
323
+ const domVersion = signal(0, /* @ts-ignore */
324
+ ...(ngDevMode ? [{ debugName: "domVersion" }] : /* istanbul ignore next */ []));
325
+ let updateAutoObserverFn = () => { };
326
+ if (destroyRef) {
327
+ const cleanup = observeComponentDirection(hostRef, directionality, () => {
328
+ domVersion.update(v => v + 1);
329
+ });
330
+ updateAutoObserverFn = cleanup.updateAutoObserver;
331
+ destroyRef.onDestroy(() => cleanup());
332
+ }
333
+ return computed(() => {
334
+ domVersion();
335
+ updateAutoObserverFn();
336
+ return resolveComponentDirection(hostRef, directionality);
337
+ });
338
+ }
339
+ class MonaDirectionService {
340
+ #directionality = inject(Directionality, { optional: true });
341
+ getDirection(element) {
342
+ return resolveComponentDirection(element, this.#directionality);
343
+ }
344
+ isRtl(element) {
345
+ return this.getDirection(element) === "rtl";
346
+ }
347
+ observeDirection(element, onChange) {
348
+ return observeComponentDirection(element, this.#directionality, onChange);
349
+ }
350
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaDirectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
351
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaDirectionService, providedIn: "root" }); }
352
+ }
353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: MonaDirectionService, decorators: [{
354
+ type: Injectable,
355
+ args: [{
356
+ providedIn: "root"
357
+ }]
358
+ }] });
359
+
360
+ function provideMonaI18n(config = {}) {
361
+ return makeEnvironmentProviders([
362
+ {
363
+ provide: MONA_I18N_CONFIG,
364
+ useValue: config
365
+ }
366
+ ]);
367
+ }
368
+
369
+ const numberSymbolsCache = new Map();
370
+ const numberFormatterCache = new Map();
371
+ const numberGroupingCache = new Map();
372
+ function serializeOptions(options) {
373
+ if (!options) {
374
+ return "";
375
+ }
376
+ const keys = Object.keys(options).sort();
377
+ return keys.map(k => `${k}:${String(options[k])}`).join(";");
378
+ }
379
+ function getNumberGroupingPattern(localeId) {
380
+ let pattern = numberGroupingCache.get(localeId);
381
+ if (!pattern) {
382
+ try {
383
+ const formatter = new Intl.NumberFormat(localeId, {
384
+ useGrouping: true,
385
+ maximumFractionDigits: 0
386
+ });
387
+ const parts = formatter.formatToParts(123456789012345);
388
+ const integerParts = parts.filter(p => p.type === "integer");
389
+ const groupParts = parts.filter(p => p.type === "group");
390
+ const groupSeparator = groupParts[0]?.value ?? getNumberSymbols(localeId).group ?? ",";
391
+ let minimumGroupedIntegerDigits = 4;
392
+ try {
393
+ const defaultFormatter = new Intl.NumberFormat(localeId);
394
+ let probe = 1000;
395
+ let digits = 4;
396
+ while (digits <= 8) {
397
+ if (defaultFormatter.formatToParts(probe).some(p => p.type === "group")) {
398
+ minimumGroupedIntegerDigits = digits;
399
+ break;
400
+ }
401
+ probe *= 10;
402
+ digits++;
403
+ }
404
+ }
405
+ catch {
406
+ minimumGroupedIntegerDigits = 4;
407
+ }
408
+ if (integerParts.length >= 2) {
409
+ const primaryStr = integerParts[integerParts.length - 1].value.replace(/[\u061C\u200E\u200F\s]/g, "");
410
+ const secondaryStr = integerParts[integerParts.length - 2].value.replace(/[\u061C\u200E\u200F\s]/g, "");
411
+ const primaryGroupSize = Array.from(primaryStr).length;
412
+ const secondaryGroupSize = Array.from(secondaryStr).length;
413
+ pattern = {
414
+ primaryGroupSize: primaryGroupSize > 0 ? primaryGroupSize : 3,
415
+ secondaryGroupSize: secondaryGroupSize > 0 ? secondaryGroupSize : 3,
416
+ groupSeparator,
417
+ minimumGroupedIntegerDigits
418
+ };
419
+ }
420
+ else {
421
+ pattern = {
422
+ primaryGroupSize: 3,
423
+ secondaryGroupSize: 3,
424
+ groupSeparator,
425
+ minimumGroupedIntegerDigits
426
+ };
427
+ }
428
+ }
429
+ catch {
430
+ const groupSeparator = getNumberSymbols(localeId).group ?? ",";
431
+ pattern = {
432
+ primaryGroupSize: 3,
433
+ secondaryGroupSize: 3,
434
+ groupSeparator,
435
+ minimumGroupedIntegerDigits: 4
436
+ };
437
+ }
438
+ numberGroupingCache.set(localeId, pattern);
439
+ }
440
+ return pattern;
441
+ }
442
+ function getNumberSymbols(localeId) {
443
+ let symbols = numberSymbolsCache.get(localeId);
444
+ if (!symbols) {
445
+ const digitMap = new Map();
446
+ // Seed digit map with common localized scripts as fallback
447
+ for (let i = 0; i <= 9; i++) {
448
+ digitMap.set(String(i), String(i));
449
+ digitMap.set(String.fromCharCode(0x0660 + i), String(i)); // Arabic-Indic
450
+ digitMap.set(String.fromCharCode(0x06f0 + i), String(i)); // Eastern Arabic-Indic / Persian
451
+ digitMap.set(String.fromCharCode(0x09e6 + i), String(i)); // Bengali
452
+ digitMap.set(String.fromCharCode(0x0966 + i), String(i)); // Devanagari
453
+ }
454
+ try {
455
+ const parts = new Intl.NumberFormat(localeId).formatToParts(-12345.6);
456
+ const decimal = parts.find(p => p.type === "decimal")?.value ?? ".";
457
+ const group = parts.find(p => p.type === "group")?.value ?? ",";
458
+ const minus = parts.find(p => p.type === "minusSign")?.value ?? "-";
459
+ // Dynamically query localized digit glyphs for this locale
460
+ try {
461
+ const digitFormatter = new Intl.NumberFormat(localeId, {
462
+ useGrouping: false,
463
+ maximumFractionDigits: 0
464
+ });
465
+ for (let i = 0; i <= 9; i++) {
466
+ const glyph = digitFormatter.format(i).replace(/[\u061C\u200E\u200F\s]/g, "");
467
+ if (glyph) {
468
+ digitMap.set(glyph, String(i));
469
+ }
470
+ }
471
+ }
472
+ catch {
473
+ // Ignore formatter failure
474
+ }
475
+ symbols = { decimal, group, minus, digits: digitMap };
476
+ }
477
+ catch {
478
+ symbols = { decimal: ".", group: ",", minus: "-", digits: digitMap };
479
+ }
480
+ numberSymbolsCache.set(localeId, symbols);
481
+ }
482
+ return symbols;
483
+ }
484
+ function getNumberFormatter(locale, options) {
485
+ const key = `${locale}:${serializeOptions(options)}`;
486
+ let formatter = numberFormatterCache.get(key);
487
+ if (!formatter) {
488
+ formatter = new Intl.NumberFormat(locale, options);
489
+ numberFormatterCache.set(key, formatter);
490
+ }
491
+ return formatter;
492
+ }
493
+ function formatNumber(value, locale, options) {
494
+ return getNumberFormatter(locale, options).format(value);
495
+ }
496
+ function normalizeLocalizedDigits(text, localeId) {
497
+ if (!text) {
498
+ return text;
499
+ }
500
+ const symbols = localeId ? getNumberSymbols(localeId) : null;
501
+ let result = "";
502
+ for (const char of text) {
503
+ if (symbols?.digits.has(char)) {
504
+ result += symbols.digits.get(char);
505
+ }
506
+ else {
507
+ const code = char.charCodeAt(0);
508
+ if (code >= 0x0660 && code <= 0x0669) {
509
+ result += String(code - 0x0660);
510
+ }
511
+ else if (code >= 0x06f0 && code <= 0x06f9) {
512
+ result += String(code - 0x06f0);
513
+ }
514
+ else if (code >= 0x09e6 && code <= 0x09ef) {
515
+ result += String(code - 0x09e6);
516
+ }
517
+ else if (code >= 0x0966 && code <= 0x096f) {
518
+ result += String(code - 0x0966);
519
+ }
520
+ else {
521
+ result += char;
522
+ }
523
+ }
524
+ }
525
+ return result;
526
+ }
527
+ function normalizeLocalizedMinus(text, symbols) {
528
+ if (!text) {
529
+ return text;
530
+ }
531
+ let result = text
532
+ .replace(/[\u2212\uFE63\uFF0D]/g, "-")
533
+ .replace(/[\u061C\u200E\u200F]/g, "");
534
+ if (symbols?.minus && symbols.minus !== "-") {
535
+ result = result.replaceAll(symbols.minus, "-");
536
+ }
537
+ return result;
538
+ }
539
+ function normalizeLocalizedInput(text, localeId) {
540
+ const symbols = getNumberSymbols(localeId);
541
+ let cleaned = normalizeLocalizedMinus(text, symbols);
542
+ cleaned = normalizeLocalizedDigits(cleaned, localeId);
543
+ return cleaned;
544
+ }
545
+ function parseLocalizedNumber(text, localeId, options) {
546
+ if (text == null) {
547
+ return null;
548
+ }
549
+ let cleaned = String(text).trim();
550
+ if (cleaned === "") {
551
+ return null;
552
+ }
553
+ const symbols = getNumberSymbols(localeId);
554
+ // Normalize minus signs, bidi controls, and localized digits
555
+ cleaned = normalizeLocalizedMinus(cleaned, symbols);
556
+ cleaned = normalizeLocalizedDigits(cleaned, localeId);
557
+ if (cleaned === "-" || cleaned === "+") {
558
+ return null;
559
+ }
560
+ // Only one leading sign is permitted
561
+ let sign = "";
562
+ if (cleaned.startsWith("-")) {
563
+ sign = "-";
564
+ cleaned = cleaned.slice(1);
565
+ }
566
+ else if (cleaned.startsWith("+")) {
567
+ cleaned = cleaned.slice(1);
568
+ }
569
+ if (cleaned.includes("-") || cleaned.includes("+")) {
570
+ return null;
571
+ }
572
+ const mode = options?.mode ?? (options?.alternateDecimal ? "edit" : "locale");
573
+ if (mode === "locale") {
574
+ const grouping = getNumberGroupingPattern(localeId);
575
+ const groupSep = grouping.groupSeparator;
576
+ const isSpaceGroup = /[ \u00A0\u202F]/.test(groupSep);
577
+ const decimalSep = symbols.decimal;
578
+ // Reject non-accepted control or whitespace characters (tabs, newlines, formfeeds)
579
+ if (/[^\S\u0020\u00A0\u202F]/.test(cleaned)) {
580
+ return null;
581
+ }
582
+ // In strict locale mode, if grouping separator is not space-like, any internal whitespace is invalid
583
+ if (!isSpaceGroup && /[\s\u00A0\u202F]/.test(cleaned)) {
584
+ return null;
585
+ }
586
+ // Split integer and fraction by canonical decimal separator
587
+ let intPart;
588
+ let fracPart = null;
589
+ if (decimalSep && cleaned.includes(decimalSep)) {
590
+ if (cleaned.indexOf(decimalSep) !== cleaned.lastIndexOf(decimalSep)) {
591
+ return null;
592
+ }
593
+ const decimalIdx = cleaned.indexOf(decimalSep);
594
+ intPart = cleaned.slice(0, decimalIdx);
595
+ fracPart = cleaned.slice(decimalIdx + decimalSep.length);
596
+ }
597
+ else {
598
+ // If the string contains an unexpected decimal-like character that is not the group separator, reject
599
+ if (decimalSep === "," && groupSep !== "." && cleaned.includes(".")) {
600
+ return null;
601
+ }
602
+ if (decimalSep === "." && groupSep !== "," && cleaned.includes(",")) {
603
+ return null;
604
+ }
605
+ intPart = cleaned;
606
+ }
607
+ // Fractional part validation
608
+ if (fracPart !== null) {
609
+ // Group separators or whitespace are never permitted in the fractional part
610
+ if (groupSep && fracPart.includes(groupSep)) {
611
+ return null;
612
+ }
613
+ if (/[\s\u00A0\u202F]/.test(fracPart)) {
614
+ return null;
615
+ }
616
+ if (fracPart.length > 0 && !/^\d+$/.test(fracPart)) {
617
+ return null;
618
+ }
619
+ }
620
+ // Integer part validation
621
+ if (intPart.length === 0) {
622
+ if (fracPart === null || fracPart.length === 0) {
623
+ return null;
624
+ }
625
+ intPart = "0";
626
+ }
627
+ else {
628
+ let intGroups;
629
+ let hasGroup = false;
630
+ if (isSpaceGroup) {
631
+ if (/[ \u00A0\u202F]/.test(intPart)) {
632
+ hasGroup = true;
633
+ // Splitting by single accepted space character preserves empty strings for consecutive spaces
634
+ intGroups = intPart.split(/[ \u00A0\u202F]/);
635
+ }
636
+ else {
637
+ intGroups = [intPart];
638
+ }
639
+ }
640
+ else {
641
+ if (groupSep && intPart.includes(groupSep)) {
642
+ hasGroup = true;
643
+ intGroups = intPart.split(groupSep);
644
+ }
645
+ else {
646
+ intGroups = [intPart];
647
+ }
648
+ }
649
+ if (hasGroup) {
650
+ if (intGroups.length < 2) {
651
+ return null;
652
+ }
653
+ // Canonical strict check: must meet locale's minimum grouping threshold
654
+ const totalGroupedDigits = intGroups.join("").length;
655
+ if (totalGroupedDigits < grouping.minimumGroupedIntegerDigits) {
656
+ return null;
657
+ }
658
+ // Last (rightmost) group: must match primaryGroupSize digits
659
+ const lastGroup = intGroups[intGroups.length - 1];
660
+ if (lastGroup.length !== grouping.primaryGroupSize || !/^\d+$/.test(lastGroup)) {
661
+ return null;
662
+ }
663
+ // Intermediate groups: must match secondaryGroupSize digits
664
+ for (let i = 1; i < intGroups.length - 1; i++) {
665
+ const midGroup = intGroups[i];
666
+ if (midGroup.length !== grouping.secondaryGroupSize || !/^\d+$/.test(midGroup)) {
667
+ return null;
668
+ }
669
+ }
670
+ // First group: must have 1..secondaryGroupSize digits
671
+ const firstGroup = intGroups[0];
672
+ if (firstGroup.length < 1 ||
673
+ firstGroup.length > grouping.secondaryGroupSize ||
674
+ !/^\d+$/.test(firstGroup)) {
675
+ return null;
676
+ }
677
+ intPart = intGroups.join("");
678
+ }
679
+ else {
680
+ // Ungrouped integer must consist solely of digits
681
+ if (!/^\d+$/.test(intPart)) {
682
+ return null;
683
+ }
684
+ }
685
+ }
686
+ const normalizedStr = sign + intPart + (fracPart && fracPart.length > 0 ? "." + fracPart : "");
687
+ const num = Number(normalizedStr);
688
+ return Number.isFinite(num) ? num : null;
689
+ }
690
+ else {
691
+ // mode === "edit": editor buffer grammar with exactly one decimal separator (locale primary or alternate)
692
+ // No group separators or internal whitespace permitted in editor buffer
693
+ if (/[\s\u00A0\u202F]/.test(cleaned)) {
694
+ return null;
695
+ }
696
+ const primaryDec = symbols.decimal || ".";
697
+ const candidateSeparators = new Set();
698
+ candidateSeparators.add(primaryDec);
699
+ if (primaryDec === ".") {
700
+ candidateSeparators.add(",");
701
+ }
702
+ else if (primaryDec === ",") {
703
+ candidateSeparators.add(".");
704
+ }
705
+ else {
706
+ candidateSeparators.add(".");
707
+ candidateSeparators.add(",");
708
+ }
709
+ if (primaryDec === "\u066B") {
710
+ candidateSeparators.add(".");
711
+ candidateSeparators.add(",");
712
+ }
713
+ if (primaryDec === "/") {
714
+ candidateSeparators.add(".");
715
+ candidateSeparators.add(",");
716
+ }
717
+ let foundSep = null;
718
+ let sepIndex = -1;
719
+ for (let i = 0; i < cleaned.length; i++) {
720
+ for (const sep of candidateSeparators) {
721
+ if (cleaned.startsWith(sep, i)) {
722
+ if (foundSep !== null) {
723
+ // More than one separator (or repeated alternate separators) is rejected
724
+ return null;
725
+ }
726
+ foundSep = sep;
727
+ sepIndex = i;
728
+ i += sep.length - 1;
729
+ break;
730
+ }
731
+ }
732
+ }
733
+ // Reject if any non-candidate dot or comma remains
734
+ if (cleaned.includes(".") && !candidateSeparators.has(".")) {
735
+ return null;
736
+ }
737
+ if (cleaned.includes(",") && !candidateSeparators.has(",")) {
738
+ return null;
739
+ }
740
+ let intPart;
741
+ let fracPart;
742
+ if (foundSep !== null) {
743
+ intPart = cleaned.slice(0, sepIndex);
744
+ fracPart = cleaned.slice(sepIndex + foundSep.length);
745
+ if (intPart.length > 0 && !/^\d+$/.test(intPart)) {
746
+ return null;
747
+ }
748
+ if (fracPart.length > 0 && !/^\d+$/.test(fracPart)) {
749
+ return null;
750
+ }
751
+ if (intPart.length === 0 && fracPart.length === 0) {
752
+ return null;
753
+ }
754
+ }
755
+ else {
756
+ if (!/^\d+$/.test(cleaned)) {
757
+ return null;
758
+ }
759
+ intPart = cleaned;
760
+ fracPart = "";
761
+ }
762
+ const normalizedStr = sign +
763
+ (intPart.length > 0 ? intPart : "0") +
764
+ (fracPart.length > 0 ? "." + fracPart : cleaned.endsWith(foundSep ?? "") ? "" : "");
765
+ const num = Number(normalizedStr);
766
+ return Number.isFinite(num) ? num : null;
767
+ }
768
+ }
769
+ function validateLocalizedNumber(text, localeId, options) {
770
+ if (text == null) {
771
+ return { value: null, valid: true, fractionDigits: 0 };
772
+ }
773
+ const trimmed = String(text).trim();
774
+ if (trimmed === "" || trimmed === "-" || trimmed === "+") {
775
+ return { value: null, valid: true, fractionDigits: 0 };
776
+ }
777
+ const mode = options?.mode ?? "locale";
778
+ const parsed = parseLocalizedNumber(trimmed, localeId, { mode });
779
+ if (parsed === null || !Number.isFinite(parsed)) {
780
+ return { value: null, valid: false, fractionDigits: 0 };
781
+ }
782
+ // Determine the number of fraction digits from the source string
783
+ const symbols = getNumberSymbols(localeId);
784
+ let normalized = normalizeLocalizedMinus(trimmed, symbols);
785
+ normalized = normalizeLocalizedDigits(normalized, localeId);
786
+ normalized = normalized.replace(/[ \u00A0\u202F]/g, "");
787
+ let fractionDigits = 0;
788
+ if (mode === "locale") {
789
+ if (symbols.decimal && normalized.includes(symbols.decimal)) {
790
+ const fracPart = normalized
791
+ .slice(normalized.lastIndexOf(symbols.decimal) + symbols.decimal.length)
792
+ .replace(/\D/g, "");
793
+ fractionDigits = fracPart.length;
794
+ }
795
+ else if (normalized.includes("\u066B")) {
796
+ const fracPart = normalized.slice(normalized.lastIndexOf("\u066B") + 1).replace(/\D/g, "");
797
+ fractionDigits = fracPart.length;
798
+ }
799
+ }
800
+ else {
801
+ let sep = null;
802
+ if (symbols.decimal === ",") {
803
+ if (normalized.includes(",")) {
804
+ sep = ",";
805
+ }
806
+ else if (normalized.includes(".")) {
807
+ const dotParts = normalized.split(".");
808
+ if (dotParts.length === 2) {
809
+ sep = ".";
810
+ }
811
+ }
812
+ }
813
+ else {
814
+ if (symbols.decimal && symbols.decimal !== "." && symbols.decimal !== "," && normalized.includes(symbols.decimal)) {
815
+ sep = symbols.decimal;
816
+ }
817
+ else if (normalized.includes(".")) {
818
+ sep = ".";
819
+ }
820
+ else if (normalized.includes("\u066B")) {
821
+ sep = "\u066B";
822
+ }
823
+ else if (normalized.includes("/")) {
824
+ sep = "/";
825
+ }
826
+ else if (normalized.includes(",")) {
827
+ const commaParts = normalized.split(",");
828
+ if (commaParts.length === 2) {
829
+ sep = ",";
830
+ }
831
+ }
832
+ }
833
+ if (sep != null) {
834
+ const fracPart = normalized.slice(normalized.lastIndexOf(sep) + sep.length).replace(/\D/g, "");
835
+ fractionDigits = fracPart.length;
836
+ }
837
+ }
838
+ if (options?.decimals !== undefined && fractionDigits > options.decimals) {
839
+ return { value: parsed, valid: false, fractionDigits };
840
+ }
841
+ return { value: parsed, valid: true, fractionDigits };
842
+ }
843
+ function validateLocalizedNumberEdit(text, localeId, options) {
844
+ if (text == null) {
845
+ return { valid: true, complete: false, value: null };
846
+ }
847
+ const raw = String(text);
848
+ if (raw === "") {
849
+ return { valid: true, complete: false, value: null };
850
+ }
851
+ // Reject control or whitespace characters in interactive typing
852
+ if (/[\s\u00A0\u202F]/.test(raw)) {
853
+ return { valid: false, complete: false, value: null };
854
+ }
855
+ const symbols = getNumberSymbols(localeId);
856
+ // Normalize minus signs and bidi controls
857
+ let normalized = normalizeLocalizedMinus(raw, symbols);
858
+ // Normalize localized digits to ASCII
859
+ normalized = normalizeLocalizedDigits(normalized, localeId);
860
+ // Sign handling
861
+ const allowSign = options?.allowSign ?? true;
862
+ if (normalized.startsWith("-") || normalized.startsWith("+")) {
863
+ if (!allowSign) {
864
+ return { valid: false, complete: false, value: null };
865
+ }
866
+ normalized = normalized.slice(1);
867
+ }
868
+ if (normalized.includes("-") || normalized.includes("+")) {
869
+ return { valid: false, complete: false, value: null };
870
+ }
871
+ // If input was just "-" or "+"
872
+ if (normalized === "") {
873
+ return { valid: true, complete: false, value: null };
874
+ }
875
+ // Identify allowed decimal separators
876
+ const primaryDec = symbols.decimal || ".";
877
+ // Candidate separators: primary decimal, plus alternate '.' or ','
878
+ const candidateSeparators = new Set();
879
+ candidateSeparators.add(primaryDec);
880
+ if (primaryDec === ".") {
881
+ candidateSeparators.add(",");
882
+ }
883
+ else if (primaryDec === ",") {
884
+ candidateSeparators.add(".");
885
+ }
886
+ else {
887
+ candidateSeparators.add(".");
888
+ candidateSeparators.add(",");
889
+ }
890
+ if (primaryDec === "\u066B") {
891
+ candidateSeparators.add(".");
892
+ candidateSeparators.add(",");
893
+ }
894
+ // Count how many separators from the candidate set appear
895
+ let foundSep = null;
896
+ let sepIndex = -1;
897
+ for (let i = 0; i < normalized.length; i++) {
898
+ for (const sep of candidateSeparators) {
899
+ if (normalized.startsWith(sep, i)) {
900
+ if (foundSep !== null) {
901
+ // More than one separator is rejected in edit mode
902
+ return { valid: false, complete: false, value: null };
903
+ }
904
+ foundSep = sep;
905
+ sepIndex = i;
906
+ i += sep.length - 1;
907
+ break;
908
+ }
909
+ }
910
+ }
911
+ const decimals = options?.decimals ?? 0;
912
+ if (foundSep !== null) {
913
+ if (decimals === 0) {
914
+ // No decimal point permitted when decimals === 0
915
+ return { valid: false, complete: false, value: null };
916
+ }
917
+ const intPart = normalized.slice(0, sepIndex);
918
+ const fracPart = normalized.slice(sepIndex + foundSep.length);
919
+ // Integer part before separator must consist purely of ASCII digits (or empty, e.g. ".5")
920
+ if (intPart.length > 0 && !/^\d+$/.test(intPart)) {
921
+ return { valid: false, complete: false, value: null };
922
+ }
923
+ // Fractional part after separator must consist purely of ASCII digits (or empty, e.g. "12.")
924
+ if (fracPart.length > 0 && !/^\d+$/.test(fracPart)) {
925
+ return { valid: false, complete: false, value: null };
926
+ }
927
+ // Count fraction digits using normalized ASCII characters (safe against UTF-16 astral splitting)
928
+ if (fracPart.length > decimals) {
929
+ return { valid: false, complete: false, value: null };
930
+ }
931
+ const parsed = parseLocalizedNumber(raw, localeId, { mode: "edit" });
932
+ const complete = fracPart.length > 0 && (intPart.length > 0 || fracPart.length > 0);
933
+ return {
934
+ valid: true,
935
+ complete,
936
+ value: parsed
937
+ };
938
+ }
939
+ // No separator found: must be purely ASCII digits
940
+ if (!/^\d+$/.test(normalized)) {
941
+ return { valid: false, complete: false, value: null };
942
+ }
943
+ const parsed = parseLocalizedNumber(raw, localeId, { mode: "edit" });
944
+ return {
945
+ valid: true,
946
+ complete: true,
947
+ value: parsed
948
+ };
949
+ }
950
+
951
+ /**
952
+ * Normalizes a locale string to ensure it uses the Gregorian calendar system
953
+ * and doesn't contain a conflicting -u-ca-* extension.
954
+ */
955
+ function normalizeGregorianLocale(localeId) {
956
+ if (!localeId) {
957
+ return localeId;
958
+ }
959
+ try {
960
+ if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
961
+ const loc = new Intl.Locale(localeId, { calendar: "gregory" });
962
+ return loc.toString();
963
+ }
964
+ }
965
+ catch {
966
+ // Fallback safely if localeId is malformed
967
+ }
968
+ return localeId;
969
+ }
970
+ /**
971
+ * Creates a Luxon DateTime from a Date instance, reconfigured to the specified locale
972
+ * with an explicit Gregorian output calendar.
973
+ */
974
+ function gregorianDateTime(date, localeId) {
975
+ const locale = normalizeGregorianLocale(localeId);
976
+ return DateTime.fromJSDate(date).reconfigure({
977
+ locale,
978
+ outputCalendar: "gregory"
979
+ });
980
+ }
981
+ /**
982
+ * Creates a Luxon DateTime from object components (year, month, etc.), reconfigured to the
983
+ * specified locale with an explicit Gregorian output calendar.
984
+ */
985
+ function gregorianDateTimeFromObject(obj, localeId) {
986
+ const locale = normalizeGregorianLocale(localeId);
987
+ return DateTime.fromObject(obj).reconfigure({
988
+ locale,
989
+ outputCalendar: "gregory"
990
+ });
991
+ }
992
+ /**
993
+ * Formats a date using locale-native token ordering and explicit Gregorian calendar.
994
+ */
995
+ function formatGregorianDateToLocaleString(date, localeId, options) {
996
+ const locale = normalizeGregorianLocale(localeId);
997
+ return gregorianDateTime(date, locale).toLocaleString({
998
+ ...options
999
+ });
1000
+ }
1001
+ /**
1002
+ * Parses a date string using the specified format and locale, forcing Gregorian calendar.
1003
+ * Localized digits are normalized first to support locales that use native numbering systems.
1004
+ */
1005
+ function parseGregorianDate(text, format, localeId) {
1006
+ const locale = normalizeGregorianLocale(localeId);
1007
+ let dt = DateTime.fromFormat(text, format, { locale, outputCalendar: "gregory" });
1008
+ if (!dt.isValid) {
1009
+ const normalizedText = normalizeLocalizedDigits(text, locale);
1010
+ let latnLocale = locale;
1011
+ try {
1012
+ if (typeof Intl !== "undefined" && typeof Intl.Locale === "function") {
1013
+ latnLocale = new Intl.Locale(locale, { numberingSystem: "latn" }).toString();
1014
+ }
1015
+ }
1016
+ catch {
1017
+ // fallback
1018
+ }
1019
+ dt = DateTime.fromFormat(normalizedText, format, { locale: latnLocale, outputCalendar: "gregory" });
1020
+ if (!dt.isValid) {
1021
+ dt = DateTime.fromFormat(normalizedText, format, { locale, outputCalendar: "gregory" });
1022
+ }
1023
+ }
1024
+ return dt;
1025
+ }
1026
+
1027
+ const PSEUDO_MAP = {
1028
+ a: "å",
1029
+ b: "ƀ",
1030
+ c: "ç",
1031
+ d: "đ",
1032
+ e: "é",
1033
+ f: "ƒ",
1034
+ g: "ĝ",
1035
+ h: "ĥ",
1036
+ i: "î",
1037
+ j: "ĵ",
1038
+ k: "ķ",
1039
+ l: "ļ",
1040
+ m: "m",
1041
+ n: "ñ",
1042
+ o: "ö",
1043
+ p: "þ",
1044
+ q: "q",
1045
+ r: "ř",
1046
+ s: "š",
1047
+ t: "ţ",
1048
+ u: "û",
1049
+ v: "ṽ",
1050
+ w: "ŵ",
1051
+ x: "x",
1052
+ y: "ŷ",
1053
+ z: "ž",
1054
+ A: "Å",
1055
+ B: "Ɓ",
1056
+ C: "Ç",
1057
+ D: "Đ",
1058
+ E: "É",
1059
+ F: "Ƒ",
1060
+ G: "Ĝ",
1061
+ H: "Ĥ",
1062
+ I: "Î",
1063
+ J: "Ĵ",
1064
+ K: "Ķ",
1065
+ L: "Ļ",
1066
+ M: "M",
1067
+ N: "Ñ",
1068
+ O: "Ö",
1069
+ P: "Þ",
1070
+ Q: "Q",
1071
+ R: "Ř",
1072
+ S: "Š",
1073
+ T: "Ţ",
1074
+ U: "Û",
1075
+ V: "Ṽ",
1076
+ W: "Ŵ",
1077
+ X: "X",
1078
+ Y: "Ŷ",
1079
+ Z: "Ž"
1080
+ };
1081
+ /**
1082
+ * Transforms a text string into pseudo-localized text (e.g. "Apply" -> "[!! Åþþļÿ~~ !!]").
1083
+ * Tokens enclosed in single braces {placeholder}, double braces {{placeholder}}, or HTML tags <tag>
1084
+ * are preserved verbatim so formatting / parameter substitution continues to function.
1085
+ */
1086
+ function pseudoLocalize(text, options = {}) {
1087
+ if (!text || typeof text !== "string") {
1088
+ return text;
1089
+ }
1090
+ const { prefix = "[!! ", suffix = " !!]", expand = true, expansionFactor = 0.3 } = options;
1091
+ if (text.startsWith(prefix) && text.endsWith(suffix)) {
1092
+ return text;
1093
+ }
1094
+ // Preserve tokens like {0}, {name}, {{val}}, <span ...>, </b>
1095
+ const tokens = text.split(/(\{[^{}]+\}|\{\{[^{}]+\}|<[^>]+>)/g);
1096
+ const convertedTokens = tokens.map((segment, index) => {
1097
+ // Odd indices match the split token regex
1098
+ if (index % 2 === 1) {
1099
+ return segment;
1100
+ }
1101
+ return segment
1102
+ .split("")
1103
+ .map(char => PSEUDO_MAP[char] ?? char)
1104
+ .join("");
1105
+ });
1106
+ let transformed = convertedTokens.join("");
1107
+ if (expand) {
1108
+ const expansionLen = Math.max(1, Math.round(text.length * expansionFactor));
1109
+ transformed += "~".repeat(expansionLen);
1110
+ }
1111
+ return `${prefix}${transformed}${suffix}`;
1112
+ }
1113
+ /**
1114
+ * Recursively pseudo-localizes a message catalog or nested object of message catalogs.
1115
+ * Plain strings are pseudo-localized, and message functions (e.g. (page: number) => string)
1116
+ * are wrapped so their string outputs are pseudo-localized when invoked.
1117
+ */
1118
+ function pseudoLocalizeCatalog(catalog, options) {
1119
+ const result = {};
1120
+ for (const [key, value] of Object.entries(catalog)) {
1121
+ if (typeof value === "string") {
1122
+ result[key] = pseudoLocalize(value, options);
1123
+ }
1124
+ else if (typeof value === "function") {
1125
+ result[key] = (...args) => {
1126
+ const out = value(...args);
1127
+ return typeof out === "string" ? pseudoLocalize(out, options) : out;
1128
+ };
1129
+ }
1130
+ else if (value && typeof value === "object" && !Array.isArray(value)) {
1131
+ result[key] = pseudoLocalizeCatalog(value, options);
1132
+ }
1133
+ else {
1134
+ result[key] = value;
1135
+ }
1136
+ }
1137
+ return result;
1138
+ }
1139
+ /**
1140
+ * Generates a full pseudo-localized MonaLocale (e.g. en-XA) from a dictionary of message catalogs.
1141
+ */
1142
+ function generatePseudoLocale(catalogs, options) {
1143
+ const id = options?.id ?? "en-XA";
1144
+ const direction = options?.direction ?? "ltr";
1145
+ const pseudoOptions = options?.pseudoOptions;
1146
+ const messages = {};
1147
+ for (const [namespace, catalog] of Object.entries(catalogs)) {
1148
+ if (catalog && typeof catalog === "object") {
1149
+ messages[namespace] = pseudoLocalizeCatalog(catalog, pseudoOptions);
1150
+ }
1151
+ }
1152
+ return {
1153
+ direction,
1154
+ id,
1155
+ messages: messages
1156
+ };
1157
+ }
1158
+
1159
+ /*
1160
+ * Public API Surface of @nanahoshi/mona-ui/i18n
1161
+ */
1162
+
1163
+ /**
1164
+ * Generated bundle index. Do not edit.
1165
+ */
1166
+
1167
+ export { MONA_DEFAULT_LOCALE, MONA_I18N_CONFIG, MonaDirectionService, MonaI18nService, formatGregorianDateToLocaleString, formatNumber, generatePseudoLocale, getNumberFormatter, getNumberGroupingPattern, getNumberSymbols, gregorianDateTime, gregorianDateTimeFromObject, injectComponentDirection, mergeMessages, mergeTwo, normalizeGregorianLocale, normalizeLocalizedDigits, normalizeLocalizedInput, normalizeLocalizedMinus, observeComponentDirection, parseGregorianDate, parseLocalizedNumber, provideMonaI18n, pseudoLocalize, pseudoLocalizeCatalog, resolveComponentDirection, validateLocalizedNumber, validateLocalizedNumberEdit };
1168
+ //# sourceMappingURL=nanahoshi-mona-ui-i18n.mjs.map