@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.
- package/README.md +71 -13
- package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +11 -3
- package/fesm2022/nanahoshi-mona-ui-breadcrumb.mjs +17 -9
- package/fesm2022/nanahoshi-mona-ui-button-group.mjs +16 -7
- package/fesm2022/nanahoshi-mona-ui-calendar.mjs +122 -48
- package/fesm2022/nanahoshi-mona-ui-card.mjs +22 -14
- package/fesm2022/nanahoshi-mona-ui-chart.mjs +78 -18
- package/fesm2022/nanahoshi-mona-ui-check-box.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-chip.mjs +199 -77
- package/fesm2022/nanahoshi-mona-ui-circular-progress-bar.mjs +1 -2
- package/fesm2022/nanahoshi-mona-ui-color-gradient.mjs +23 -5
- package/fesm2022/nanahoshi-mona-ui-color-palette.mjs +49 -28
- package/fesm2022/nanahoshi-mona-ui-color-picker.mjs +42 -26
- package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +11 -3
- package/fesm2022/nanahoshi-mona-ui-date-picker.mjs +28 -16
- package/fesm2022/nanahoshi-mona-ui-datetime-picker.mjs +74 -51
- package/fesm2022/nanahoshi-mona-ui-dialog.mjs +57 -25
- package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +13 -5
- package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +17 -6
- package/fesm2022/nanahoshi-mona-ui-editor.mjs +178 -59
- package/fesm2022/nanahoshi-mona-ui-filter.mjs +137 -54
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-cell.component-WEDbZ5vy.mjs → nanahoshi-mona-ui-grid-grid-footer-cell.component-Bg7rckst.mjs} +2 -2
- 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
- package/fesm2022/nanahoshi-mona-ui-grid-grid-list.component-D9AwQFDV.mjs +145 -0
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-toggle.component-hc-LeIb0.mjs → nanahoshi-mona-ui-grid-grid-toggle.component-Byb9iTes.mjs} +21 -17
- package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-DYajpOb9.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-CBmdtrXm.mjs} +5 -5
- package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Pm8Fbp6M.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-DPmJmCap.mjs} +90 -42
- package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-i18n.mjs +1168 -0
- package/fesm2022/nanahoshi-mona-ui-internal-list.mjs +12 -4
- package/fesm2022/nanahoshi-mona-ui-internal-tree.mjs +48 -21
- package/fesm2022/nanahoshi-mona-ui-list-box.mjs +33 -18
- package/fesm2022/nanahoshi-mona-ui-list-view.mjs +10 -10
- package/fesm2022/nanahoshi-mona-ui-list.mjs +2 -0
- package/fesm2022/nanahoshi-mona-ui-menubar.mjs +29 -16
- package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +12 -3
- package/fesm2022/nanahoshi-mona-ui-notification.mjs +23 -18
- package/fesm2022/nanahoshi-mona-ui-numeric-text-box.mjs +211 -106
- package/fesm2022/nanahoshi-mona-ui-otp-input.mjs +14 -6
- package/fesm2022/nanahoshi-mona-ui-pager.mjs +46 -19
- package/fesm2022/nanahoshi-mona-ui-popup-menu.mjs +57 -46
- package/fesm2022/nanahoshi-mona-ui-popup.mjs +35 -15
- package/fesm2022/nanahoshi-mona-ui-progress-bar.mjs +38 -17
- package/fesm2022/nanahoshi-mona-ui-rating.mjs +25 -7
- package/fesm2022/nanahoshi-mona-ui-scroll-view.mjs +431 -101
- package/fesm2022/nanahoshi-mona-ui-segmented.mjs +1 -1
- package/fesm2022/nanahoshi-mona-ui-sheet.mjs +26 -8
- package/fesm2022/nanahoshi-mona-ui-sidebar.mjs +222 -28
- package/fesm2022/nanahoshi-mona-ui-slider.mjs +91 -36
- package/fesm2022/nanahoshi-mona-ui-spinner.mjs +34 -15
- package/fesm2022/nanahoshi-mona-ui-split-button.mjs +39 -23
- package/fesm2022/nanahoshi-mona-ui-splitter.mjs +50 -25
- package/fesm2022/nanahoshi-mona-ui-stepper.mjs +32 -14
- package/fesm2022/nanahoshi-mona-ui-switch.mjs +9 -9
- package/fesm2022/nanahoshi-mona-ui-tabs.mjs +74 -56
- package/fesm2022/nanahoshi-mona-ui-text-box.mjs +16 -8
- package/fesm2022/nanahoshi-mona-ui-time-picker.mjs +33 -19
- package/fesm2022/nanahoshi-mona-ui-time-selector.mjs +66 -27
- package/fesm2022/nanahoshi-mona-ui-tree-view.mjs +23 -15
- package/fesm2022/nanahoshi-mona-ui-window.mjs +71 -40
- package/i18n/package.json +4 -0
- package/package.json +5 -1
- package/types/nanahoshi-mona-ui-auto-complete.d.ts +6 -1
- package/types/nanahoshi-mona-ui-breadcrumb.d.ts +9 -3
- package/types/nanahoshi-mona-ui-button-group.d.ts +9 -3
- package/types/nanahoshi-mona-ui-calendar.d.ts +13 -3
- package/types/nanahoshi-mona-ui-card.d.ts +8 -2
- package/types/nanahoshi-mona-ui-chart.d.ts +10 -1
- package/types/nanahoshi-mona-ui-chip.d.ts +14 -5
- package/types/nanahoshi-mona-ui-color-gradient.d.ts +6 -1
- package/types/nanahoshi-mona-ui-color-palette.d.ts +21 -10
- package/types/nanahoshi-mona-ui-color-picker.d.ts +30 -20
- package/types/nanahoshi-mona-ui-combo-box.d.ts +6 -1
- package/types/nanahoshi-mona-ui-date-picker.d.ts +7 -2
- package/types/nanahoshi-mona-ui-datetime-picker.d.ts +33 -8
- package/types/nanahoshi-mona-ui-dialog.d.ts +16 -7
- package/types/nanahoshi-mona-ui-dropdown-list.d.ts +6 -1
- package/types/nanahoshi-mona-ui-dropdowns.d.ts +4 -1
- package/types/nanahoshi-mona-ui-editor.d.ts +6 -1
- package/types/nanahoshi-mona-ui-filter.d.ts +16 -10
- package/types/nanahoshi-mona-ui-grid.d.ts +38 -1
- package/types/nanahoshi-mona-ui-i18n.d.ts +633 -0
- package/types/nanahoshi-mona-ui-internal-list.d.ts +7 -2
- package/types/nanahoshi-mona-ui-internal-tree.d.ts +7 -3
- package/types/nanahoshi-mona-ui-list-box.d.ts +9 -4
- package/types/nanahoshi-mona-ui-list-view.d.ts +6 -6
- package/types/nanahoshi-mona-ui-list.d.ts +1 -1
- package/types/nanahoshi-mona-ui-menubar.d.ts +59 -58
- package/types/nanahoshi-mona-ui-multi-select.d.ts +6 -1
- package/types/nanahoshi-mona-ui-notification.d.ts +11 -5
- package/types/nanahoshi-mona-ui-numeric-text-box.d.ts +24 -15
- package/types/nanahoshi-mona-ui-otp-input.d.ts +9 -3
- package/types/nanahoshi-mona-ui-pager.d.ts +19 -6
- package/types/nanahoshi-mona-ui-popup.d.ts +2 -1
- package/types/nanahoshi-mona-ui-progress-bar.d.ts +9 -7
- package/types/nanahoshi-mona-ui-rating.d.ts +19 -12
- package/types/nanahoshi-mona-ui-scroll-view.d.ts +34 -28
- package/types/nanahoshi-mona-ui-sheet.d.ts +8 -2
- package/types/nanahoshi-mona-ui-sidebar.d.ts +3 -0
- package/types/nanahoshi-mona-ui-slider.d.ts +44 -26
- package/types/nanahoshi-mona-ui-spinner.d.ts +13 -4
- package/types/nanahoshi-mona-ui-split-button.d.ts +62 -56
- package/types/nanahoshi-mona-ui-splitter.d.ts +9 -6
- package/types/nanahoshi-mona-ui-stepper.d.ts +9 -3
- package/types/nanahoshi-mona-ui-switch.d.ts +1 -1
- package/types/nanahoshi-mona-ui-tabs.d.ts +96 -35
- package/types/nanahoshi-mona-ui-text-box.d.ts +11 -5
- package/types/nanahoshi-mona-ui-time-picker.d.ts +7 -2
- package/types/nanahoshi-mona-ui-time-selector.d.ts +16 -5
- package/types/nanahoshi-mona-ui-tree-view.d.ts +15 -10
- package/types/nanahoshi-mona-ui-window.d.ts +26 -20
- 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
|