@geomak/ui 1.8.0 → 1.9.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/dist/index.cjs CHANGED
@@ -2682,91 +2682,117 @@ function NumberInput({
2682
2682
  htmlFor,
2683
2683
  name,
2684
2684
  disabled,
2685
- layout,
2685
+ layout = "horizontal",
2686
2686
  errorMessage,
2687
2687
  inputStyle,
2688
2688
  labelStyle,
2689
2689
  placeholder,
2690
- style = {},
2690
+ style,
2691
2691
  min,
2692
2692
  max,
2693
- readOnly = false
2693
+ readOnly = false,
2694
+ precision
2694
2695
  }) {
2696
+ const errorId = React9.useId();
2697
+ const hasError = errorMessage != null;
2698
+ const inferredPrecision = precision ?? (Number.isInteger(step) ? 0 : String(step).split(".")[1]?.length ?? 0);
2699
+ const round = (n) => {
2700
+ if (inferredPrecision === 0) return n;
2701
+ const factor = 10 ** inferredPrecision;
2702
+ return Math.round(n * factor) / factor;
2703
+ };
2704
+ const numeric = typeof value === "number" ? value : 0;
2695
2705
  const onIncrement = () => {
2696
- let newValue = value ? parseFloat(value) + step : 0 + step;
2697
- if (max !== void 0 && newValue > max) return;
2698
- onChange?.({ target: { value: newValue, id: htmlFor, name } });
2706
+ if (disabled || readOnly) return;
2707
+ const next = round(numeric + step);
2708
+ if (max !== void 0 && next > max) return;
2709
+ onChange?.({ target: { value: next, id: htmlFor, name } });
2699
2710
  };
2700
2711
  const onDecrement = () => {
2701
- let newValue = value ? parseFloat(value) - step : 0 - step;
2702
- if (min !== void 0 && newValue < min) return;
2703
- onChange?.({ target: { value: newValue, id: htmlFor, name } });
2712
+ if (disabled || readOnly) return;
2713
+ const next = round(numeric - step);
2714
+ if (min !== void 0 && next < min) return;
2715
+ onChange?.({ target: { value: next, id: htmlFor, name } });
2704
2716
  };
2705
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2706
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between pr-1 pl-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
2707
- "div",
2708
- {
2709
- className: `flex ${layout === "vertical" ? "flex-col" : "flex-row items-center gap-2"}`,
2710
- children: [
2711
- /* @__PURE__ */ jsxRuntime.jsx(
2712
- "label",
2713
- {
2714
- className: "text-md font-bold ml-1 w-60 select-none text-prussian-blue dark:text-white",
2715
- style: labelStyle,
2716
- htmlFor,
2717
- children: label
2718
- }
2719
- ),
2720
- /* @__PURE__ */ jsxRuntime.jsxs(
2721
- "div",
2722
- {
2723
- style,
2724
- className: `${disabled ? "bg-disabled" : "bg-white"} rounded-lg flex items-center pr-1 pl-2 w-max`,
2725
- children: [
2726
- /* @__PURE__ */ jsxRuntime.jsx(
2727
- "input",
2728
- {
2729
- min,
2730
- max,
2731
- autoComplete: "off",
2732
- disabled,
2733
- name,
2734
- id: htmlFor,
2735
- step,
2736
- value,
2737
- onChange,
2738
- type: "number",
2739
- className: "focus:outline-0 focus-visible:outline-0 h-9 w-60 text-prussian-blue disabled:bg-disabled disabled:cursor-not-allowed transition-all",
2740
- style: inputStyle ?? {},
2741
- placeholder: placeholder ?? "",
2742
- readOnly
2743
- }
2744
- ),
2745
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
2746
- /* @__PURE__ */ jsxRuntime.jsx(
2747
- "span",
2748
- {
2749
- onClick: onIncrement,
2750
- className: "rotate-180 cursor-pointer transition-all duration-300 hover:bg-ice rounded-sm",
2751
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: chunk255PCZIW_cjs.colors_default.PALETTE["prussian-blue"], strokeWidth: 2, className: "h-3 w-3", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
2752
- }
2753
- ),
2754
- /* @__PURE__ */ jsxRuntime.jsx(
2755
- "span",
2756
- {
2757
- onClick: onDecrement,
2758
- className: "cursor-pointer transition-all duration-300 hover:bg-ice rounded-sm",
2759
- children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: chunk255PCZIW_cjs.colors_default.PALETTE["prussian-blue"], strokeWidth: 2, className: "h-3 w-3", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
2760
- }
2761
- )
2762
- ] })
2763
- ]
2764
- }
2765
- )
2766
- ]
2767
- }
2768
- ) }),
2769
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-error min-h-0", children: errorMessage })
2717
+ const handleInputChange = (e) => {
2718
+ const raw = e.target.value;
2719
+ if (raw === "") {
2720
+ onChange?.({ target: { value: void 0, id: htmlFor, name } });
2721
+ return;
2722
+ }
2723
+ const parsed = Number(raw);
2724
+ if (Number.isNaN(parsed)) return;
2725
+ onChange?.({ target: { value: round(parsed), id: htmlFor, name } });
2726
+ };
2727
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
2728
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-center gap-2"}`, children: [
2729
+ label && /* @__PURE__ */ jsxRuntime.jsx(
2730
+ "label",
2731
+ {
2732
+ className: "text-sm font-medium ml-1 max-content select-none text-foreground",
2733
+ style: labelStyle,
2734
+ htmlFor,
2735
+ children: label
2736
+ }
2737
+ ),
2738
+ /* @__PURE__ */ jsxRuntime.jsxs(
2739
+ "div",
2740
+ {
2741
+ style,
2742
+ className: `flex items-center rounded-lg border ${hasError ? "border-status-error" : "border-border"} ${disabled ? "bg-surface-raised text-foreground-muted cursor-not-allowed" : "bg-surface text-foreground"} focus-within:ring-2 focus-within:ring-accent transition-colors`,
2743
+ children: [
2744
+ /* @__PURE__ */ jsxRuntime.jsx(
2745
+ "input",
2746
+ {
2747
+ min,
2748
+ max,
2749
+ autoComplete: "off",
2750
+ disabled,
2751
+ name,
2752
+ id: htmlFor,
2753
+ step,
2754
+ value: value ?? "",
2755
+ onChange: handleInputChange,
2756
+ type: "number",
2757
+ "aria-invalid": hasError || void 0,
2758
+ "aria-describedby": hasError ? errorId : void 0,
2759
+ className: "bg-transparent focus:outline-none h-9 w-full px-3 disabled:cursor-not-allowed [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none",
2760
+ style: inputStyle ?? {},
2761
+ placeholder: placeholder ?? "",
2762
+ readOnly
2763
+ }
2764
+ ),
2765
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col border-l border-border h-9", children: [
2766
+ /* @__PURE__ */ jsxRuntime.jsx(
2767
+ "button",
2768
+ {
2769
+ type: "button",
2770
+ tabIndex: -1,
2771
+ onClick: onIncrement,
2772
+ disabled: disabled || readOnly || max !== void 0 && numeric >= max,
2773
+ "aria-label": "Increase value",
2774
+ className: "flex-1 px-1.5 flex items-center justify-center hover:bg-surface-raised disabled:opacity-30 disabled:cursor-not-allowed transition-colors focus:outline-none focus-visible:bg-surface-raised",
2775
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 15l7-7 7 7" }) })
2776
+ }
2777
+ ),
2778
+ /* @__PURE__ */ jsxRuntime.jsx(
2779
+ "button",
2780
+ {
2781
+ type: "button",
2782
+ tabIndex: -1,
2783
+ onClick: onDecrement,
2784
+ disabled: disabled || readOnly || min !== void 0 && numeric <= min,
2785
+ "aria-label": "Decrease value",
2786
+ className: "flex-1 px-1.5 flex items-center justify-center hover:bg-surface-raised disabled:opacity-30 disabled:cursor-not-allowed transition-colors focus:outline-none focus-visible:bg-surface-raised border-t border-border",
2787
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
2788
+ }
2789
+ )
2790
+ ] })
2791
+ ]
2792
+ }
2793
+ )
2794
+ ] }),
2795
+ hasError && /* @__PURE__ */ jsxRuntime.jsx("div", { id: errorId, className: "text-xs text-status-error ml-1", children: errorMessage })
2770
2796
  ] });
2771
2797
  }
2772
2798
  function Password({