@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.
@@ -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
- const numValue = local === "" ? void 0 : Number(local);
3165
- onChange(numValue);
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
- const numValue = next === "" ? void 0 : Number(next);
3176
- onChange(numValue);
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 /* @__PURE__ */ jsx(
3193
- Input,
3194
- {
3195
- type: "number",
3196
- placeholder,
3197
- value: local,
3198
- onChange: handleChange,
3199
- onKeyDown: handleKeyDown,
3200
- onFocus: handleFocus,
3201
- onBlur: handleBlur,
3202
- autoFocus,
3203
- "data-crud-focus-target": ""
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({