@open-mercato/ui 0.7.1-develop.7153.1.7145d295e6 → 0.7.1-develop.7170.1.d95074d7ba
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/dist/backend/CrudForm.js +45 -21
- package/dist/backend/CrudForm.js.map +2 -2
- package/dist/backend/DataTable.js +7 -6
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/SectionHeader.js +17 -8
- package/dist/backend/SectionHeader.js.map +2 -2
- package/dist/backend/injection/InjectedField.js +55 -9
- package/dist/backend/injection/InjectedField.js.map +2 -2
- package/dist/backend/utils/nav.js +34 -4
- package/dist/backend/utils/nav.js.map +2 -2
- package/dist/primitives/date-format.js +68 -1
- package/dist/primitives/date-format.js.map +2 -2
- package/package.json +3 -3
- package/src/backend/CrudForm.tsx +40 -9
- package/src/backend/DataTable.tsx +11 -15
- package/src/backend/SectionHeader.tsx +28 -5
- package/src/backend/__tests__/CrudForm.localeNumber.test.tsx +159 -0
- package/src/backend/__tests__/CrudForm.validation.test.tsx +1 -1
- package/src/backend/__tests__/DataTable.dateCells.test.tsx +87 -0
- package/src/backend/__tests__/SectionHeader.test.tsx +105 -0
- package/src/backend/__tests__/nav-utils.test.ts +180 -0
- package/src/backend/injection/InjectedField.tsx +69 -9
- package/src/backend/injection/__tests__/InjectedField.localeNumber.test.tsx +94 -0
- package/src/backend/utils/nav.ts +75 -5
- package/src/primitives/__tests__/date-format.test.ts +185 -0
- package/src/primitives/date-format.ts +145 -0
package/dist/backend/CrudForm.js
CHANGED
|
@@ -71,7 +71,7 @@ import {
|
|
|
71
71
|
import { loadGeneratedFieldRegistrations } from "./fields/registry.js";
|
|
72
72
|
import { isDefVisible } from "./utils/customFieldDefs.js";
|
|
73
73
|
import { buildFormFieldFromCustomFieldDef } from "./utils/customFieldForms.js";
|
|
74
|
-
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
74
|
+
import { useT, useOptionalLocale } from "@open-mercato/shared/lib/i18n/context";
|
|
75
75
|
import { TagsInput } from "./inputs/TagsInput.js";
|
|
76
76
|
import { ComboboxInput } from "./inputs/ComboboxInput.js";
|
|
77
77
|
import { format } from "date-fns/format";
|
|
@@ -90,6 +90,7 @@ import { useInjectionSpotEvents, InjectionSpot, useInjectionWidgets } from "./in
|
|
|
90
90
|
import { dispatchBackendMutationError } from "./injection/mutationEvents.js";
|
|
91
91
|
import { VersionHistoryAction } from "./version-history/VersionHistoryAction.js";
|
|
92
92
|
import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
|
|
93
|
+
import { parseLocaleNumber, resolveLocaleNumberSeparators } from "@open-mercato/shared/lib/number";
|
|
93
94
|
import { cn } from "@open-mercato/shared/lib/utils";
|
|
94
95
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
95
96
|
import { useInjectionDataWidgets } from "./injection/useInjectionDataWidgets.js";
|
|
@@ -3156,14 +3157,21 @@ function NumberInput({
|
|
|
3156
3157
|
autoFocus,
|
|
3157
3158
|
onSubmit
|
|
3158
3159
|
}) {
|
|
3160
|
+
const locale = useOptionalLocale();
|
|
3159
3161
|
const serializedValue = value !== void 0 && value !== null ? String(value) : "";
|
|
3160
3162
|
const [local, setLocal] = React.useState(serializedValue);
|
|
3161
3163
|
const isFocusedRef = React.useRef(false);
|
|
3164
|
+
const parse = React.useCallback(
|
|
3165
|
+
(raw) => {
|
|
3166
|
+
if (raw === "") return void 0;
|
|
3167
|
+
return parseLocaleNumber(raw, locale) ?? void 0;
|
|
3168
|
+
},
|
|
3169
|
+
[locale]
|
|
3170
|
+
);
|
|
3162
3171
|
const commitIfChanged = React.useCallback(() => {
|
|
3163
3172
|
if (local === serializedValue) return;
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
}, [local, onChange, serializedValue]);
|
|
3173
|
+
onChange(parse(local));
|
|
3174
|
+
}, [local, onChange, parse, serializedValue]);
|
|
3167
3175
|
React.useEffect(() => {
|
|
3168
3176
|
if (!isFocusedRef.current) {
|
|
3169
3177
|
setLocal(serializedValue);
|
|
@@ -3172,16 +3180,24 @@ function NumberInput({
|
|
|
3172
3180
|
const handleChange = React.useCallback((e) => {
|
|
3173
3181
|
const next = e.target.value;
|
|
3174
3182
|
setLocal(next);
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}, [onChange]);
|
|
3183
|
+
onChange(parse(next));
|
|
3184
|
+
}, [onChange, parse]);
|
|
3178
3185
|
const handleKeyDown = React.useCallback((e) => {
|
|
3179
3186
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
3180
3187
|
e.preventDefault();
|
|
3181
3188
|
commitIfChanged();
|
|
3182
3189
|
onSubmit?.();
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
|
3193
|
+
e.preventDefault();
|
|
3194
|
+
const base = parse(local) ?? 0;
|
|
3195
|
+
const stepped = Number((base + (e.key === "ArrowUp" ? 1 : -1)).toFixed(10));
|
|
3196
|
+
const { decimal } = resolveLocaleNumberSeparators(locale);
|
|
3197
|
+
setLocal(decimal === "." ? String(stepped) : String(stepped).replace(".", decimal));
|
|
3198
|
+
onChange(stepped);
|
|
3183
3199
|
}
|
|
3184
|
-
}, [commitIfChanged, onSubmit]);
|
|
3200
|
+
}, [commitIfChanged, local, locale, onChange, onSubmit, parse]);
|
|
3185
3201
|
const handleFocus = React.useCallback(() => {
|
|
3186
3202
|
isFocusedRef.current = true;
|
|
3187
3203
|
}, []);
|
|
@@ -3189,19 +3205,27 @@ function NumberInput({
|
|
|
3189
3205
|
isFocusedRef.current = false;
|
|
3190
3206
|
commitIfChanged();
|
|
3191
3207
|
}, [commitIfChanged]);
|
|
3192
|
-
return
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3208
|
+
return (
|
|
3209
|
+
// type="text" rather than type="number": the browser sanitizes a type="number" value
|
|
3210
|
+
// against the BROWSER locale, so it discards the separator the app locale displays
|
|
3211
|
+
// before React ever sees it (issue #5552). inputMode keeps the mobile numeric keypad.
|
|
3212
|
+
/* @__PURE__ */ jsx(
|
|
3213
|
+
Input,
|
|
3214
|
+
{
|
|
3215
|
+
type: "text",
|
|
3216
|
+
inputMode: "decimal",
|
|
3217
|
+
autoComplete: "off",
|
|
3218
|
+
spellCheck: false,
|
|
3219
|
+
placeholder,
|
|
3220
|
+
value: local,
|
|
3221
|
+
onChange: handleChange,
|
|
3222
|
+
onKeyDown: handleKeyDown,
|
|
3223
|
+
onFocus: handleFocus,
|
|
3224
|
+
onBlur: handleBlur,
|
|
3225
|
+
autoFocus,
|
|
3226
|
+
"data-crud-focus-target": ""
|
|
3227
|
+
}
|
|
3228
|
+
)
|
|
3205
3229
|
);
|
|
3206
3230
|
}
|
|
3207
3231
|
function TextAreaInput({
|