@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 +100 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -7
- package/dist/index.d.ts +40 -7
- package/dist/index.js +100 -74
- package/dist/index.js.map +1 -1
- package/dist/styles.css +18 -19
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1457,11 +1457,14 @@ interface TextInputProps {
|
|
|
1457
1457
|
declare function TextInput({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, }: TextInputProps): react_jsx_runtime.JSX.Element;
|
|
1458
1458
|
|
|
1459
1459
|
interface NumberInputProps {
|
|
1460
|
+
/** Step size for the up/down buttons and native arrow-key handling. Default `1`. */
|
|
1460
1461
|
step?: number;
|
|
1461
|
-
value
|
|
1462
|
+
/** Current value. `undefined` renders an empty field; a number renders that value. */
|
|
1463
|
+
value?: number | '';
|
|
1464
|
+
/** Fires with the next number. Empty input resolves to `undefined`. */
|
|
1462
1465
|
onChange?: (e: {
|
|
1463
1466
|
target: {
|
|
1464
|
-
value: number;
|
|
1467
|
+
value: number | undefined;
|
|
1465
1468
|
id?: string;
|
|
1466
1469
|
name?: string;
|
|
1467
1470
|
};
|
|
@@ -1470,8 +1473,8 @@ interface NumberInputProps {
|
|
|
1470
1473
|
htmlFor?: string;
|
|
1471
1474
|
name?: string;
|
|
1472
1475
|
disabled?: boolean;
|
|
1473
|
-
/**
|
|
1474
|
-
layout?:
|
|
1476
|
+
/** Label/input orientation. Defaults to `'horizontal'`. */
|
|
1477
|
+
layout?: 'horizontal' | 'vertical';
|
|
1475
1478
|
errorMessage?: React$1.ReactNode;
|
|
1476
1479
|
inputStyle?: React$1.CSSProperties;
|
|
1477
1480
|
labelStyle?: React$1.CSSProperties;
|
|
@@ -1480,12 +1483,42 @@ interface NumberInputProps {
|
|
|
1480
1483
|
min?: number;
|
|
1481
1484
|
max?: number;
|
|
1482
1485
|
readOnly?: boolean;
|
|
1483
|
-
|
|
1486
|
+
/** Optional precision for floating-point steps (number of decimal places to round to). */
|
|
1487
|
+
precision?: number;
|
|
1484
1488
|
}
|
|
1485
1489
|
/**
|
|
1486
|
-
*
|
|
1490
|
+
* Numeric input with keyboard-accessible increment / decrement buttons.
|
|
1491
|
+
*
|
|
1492
|
+
* **What's improved over the previous version**
|
|
1493
|
+
* - Step buttons are real `<button>` elements with `aria-label`, focus rings,
|
|
1494
|
+
* and proper keyboard activation (Enter / Space). The previous version used
|
|
1495
|
+
* `<span onClick>` which keyboard-only users could not reach.
|
|
1496
|
+
* - Floating-point drift on decimal steps (`0.1 + 0.2 = 0.30000000000000004`)
|
|
1497
|
+
* is rounded out via a `precision` prop or auto-inferred from the step.
|
|
1498
|
+
* - Empty input resolves to `undefined` instead of `NaN` — works with form
|
|
1499
|
+
* libraries (RHF, Formik) that treat empty as "no value".
|
|
1500
|
+
* - The decrement chevron actually points down (the previous SVG was the up
|
|
1501
|
+
* chevron rotated, with the up chevron itself wrongly using the same path).
|
|
1502
|
+
* - Width is a prop, not hardcoded `w-60`. Default is `w-full` so the input
|
|
1503
|
+
* flows with its parent.
|
|
1504
|
+
*
|
|
1505
|
+
* @example
|
|
1506
|
+
* ```tsx
|
|
1507
|
+
* const [qty, setQty] = useState<number | undefined>(1)
|
|
1508
|
+
* <NumberInput
|
|
1509
|
+
* label="Quantity"
|
|
1510
|
+
* value={qty ?? ''}
|
|
1511
|
+
* onChange={({ target }) => setQty(target.value)}
|
|
1512
|
+
* min={0} max={99}
|
|
1513
|
+
* />
|
|
1514
|
+
* ```
|
|
1515
|
+
*
|
|
1516
|
+
* @example Decimal step
|
|
1517
|
+
* ```tsx
|
|
1518
|
+
* <NumberInput label="Tonnage" step={0.25} precision={2} />
|
|
1519
|
+
* ```
|
|
1487
1520
|
*/
|
|
1488
|
-
declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
1521
|
+
declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, precision, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
1489
1522
|
|
|
1490
1523
|
interface PasswordProps {
|
|
1491
1524
|
value?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1457,11 +1457,14 @@ interface TextInputProps {
|
|
|
1457
1457
|
declare function TextInput({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout, onBlur, errorMessage, labelColor, }: TextInputProps): react_jsx_runtime.JSX.Element;
|
|
1458
1458
|
|
|
1459
1459
|
interface NumberInputProps {
|
|
1460
|
+
/** Step size for the up/down buttons and native arrow-key handling. Default `1`. */
|
|
1460
1461
|
step?: number;
|
|
1461
|
-
value
|
|
1462
|
+
/** Current value. `undefined` renders an empty field; a number renders that value. */
|
|
1463
|
+
value?: number | '';
|
|
1464
|
+
/** Fires with the next number. Empty input resolves to `undefined`. */
|
|
1462
1465
|
onChange?: (e: {
|
|
1463
1466
|
target: {
|
|
1464
|
-
value: number;
|
|
1467
|
+
value: number | undefined;
|
|
1465
1468
|
id?: string;
|
|
1466
1469
|
name?: string;
|
|
1467
1470
|
};
|
|
@@ -1470,8 +1473,8 @@ interface NumberInputProps {
|
|
|
1470
1473
|
htmlFor?: string;
|
|
1471
1474
|
name?: string;
|
|
1472
1475
|
disabled?: boolean;
|
|
1473
|
-
/**
|
|
1474
|
-
layout?:
|
|
1476
|
+
/** Label/input orientation. Defaults to `'horizontal'`. */
|
|
1477
|
+
layout?: 'horizontal' | 'vertical';
|
|
1475
1478
|
errorMessage?: React$1.ReactNode;
|
|
1476
1479
|
inputStyle?: React$1.CSSProperties;
|
|
1477
1480
|
labelStyle?: React$1.CSSProperties;
|
|
@@ -1480,12 +1483,42 @@ interface NumberInputProps {
|
|
|
1480
1483
|
min?: number;
|
|
1481
1484
|
max?: number;
|
|
1482
1485
|
readOnly?: boolean;
|
|
1483
|
-
|
|
1486
|
+
/** Optional precision for floating-point steps (number of decimal places to round to). */
|
|
1487
|
+
precision?: number;
|
|
1484
1488
|
}
|
|
1485
1489
|
/**
|
|
1486
|
-
*
|
|
1490
|
+
* Numeric input with keyboard-accessible increment / decrement buttons.
|
|
1491
|
+
*
|
|
1492
|
+
* **What's improved over the previous version**
|
|
1493
|
+
* - Step buttons are real `<button>` elements with `aria-label`, focus rings,
|
|
1494
|
+
* and proper keyboard activation (Enter / Space). The previous version used
|
|
1495
|
+
* `<span onClick>` which keyboard-only users could not reach.
|
|
1496
|
+
* - Floating-point drift on decimal steps (`0.1 + 0.2 = 0.30000000000000004`)
|
|
1497
|
+
* is rounded out via a `precision` prop or auto-inferred from the step.
|
|
1498
|
+
* - Empty input resolves to `undefined` instead of `NaN` — works with form
|
|
1499
|
+
* libraries (RHF, Formik) that treat empty as "no value".
|
|
1500
|
+
* - The decrement chevron actually points down (the previous SVG was the up
|
|
1501
|
+
* chevron rotated, with the up chevron itself wrongly using the same path).
|
|
1502
|
+
* - Width is a prop, not hardcoded `w-60`. Default is `w-full` so the input
|
|
1503
|
+
* flows with its parent.
|
|
1504
|
+
*
|
|
1505
|
+
* @example
|
|
1506
|
+
* ```tsx
|
|
1507
|
+
* const [qty, setQty] = useState<number | undefined>(1)
|
|
1508
|
+
* <NumberInput
|
|
1509
|
+
* label="Quantity"
|
|
1510
|
+
* value={qty ?? ''}
|
|
1511
|
+
* onChange={({ target }) => setQty(target.value)}
|
|
1512
|
+
* min={0} max={99}
|
|
1513
|
+
* />
|
|
1514
|
+
* ```
|
|
1515
|
+
*
|
|
1516
|
+
* @example Decimal step
|
|
1517
|
+
* ```tsx
|
|
1518
|
+
* <NumberInput label="Tonnage" step={0.25} precision={2} />
|
|
1519
|
+
* ```
|
|
1487
1520
|
*/
|
|
1488
|
-
declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
1521
|
+
declare function NumberInput({ step, value, onChange, label, htmlFor, name, disabled, layout, errorMessage, inputStyle, labelStyle, placeholder, style, min, max, readOnly, precision, }: NumberInputProps): react_jsx_runtime.JSX.Element;
|
|
1489
1522
|
|
|
1490
1523
|
interface PasswordProps {
|
|
1491
1524
|
value?: string;
|
package/dist/index.js
CHANGED
|
@@ -2650,91 +2650,117 @@ function NumberInput({
|
|
|
2650
2650
|
htmlFor,
|
|
2651
2651
|
name,
|
|
2652
2652
|
disabled,
|
|
2653
|
-
layout,
|
|
2653
|
+
layout = "horizontal",
|
|
2654
2654
|
errorMessage,
|
|
2655
2655
|
inputStyle,
|
|
2656
2656
|
labelStyle,
|
|
2657
2657
|
placeholder,
|
|
2658
|
-
style
|
|
2658
|
+
style,
|
|
2659
2659
|
min,
|
|
2660
2660
|
max,
|
|
2661
|
-
readOnly = false
|
|
2661
|
+
readOnly = false,
|
|
2662
|
+
precision
|
|
2662
2663
|
}) {
|
|
2664
|
+
const errorId = useId();
|
|
2665
|
+
const hasError = errorMessage != null;
|
|
2666
|
+
const inferredPrecision = precision ?? (Number.isInteger(step) ? 0 : String(step).split(".")[1]?.length ?? 0);
|
|
2667
|
+
const round = (n) => {
|
|
2668
|
+
if (inferredPrecision === 0) return n;
|
|
2669
|
+
const factor = 10 ** inferredPrecision;
|
|
2670
|
+
return Math.round(n * factor) / factor;
|
|
2671
|
+
};
|
|
2672
|
+
const numeric = typeof value === "number" ? value : 0;
|
|
2663
2673
|
const onIncrement = () => {
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2674
|
+
if (disabled || readOnly) return;
|
|
2675
|
+
const next = round(numeric + step);
|
|
2676
|
+
if (max !== void 0 && next > max) return;
|
|
2677
|
+
onChange?.({ target: { value: next, id: htmlFor, name } });
|
|
2667
2678
|
};
|
|
2668
2679
|
const onDecrement = () => {
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2680
|
+
if (disabled || readOnly) return;
|
|
2681
|
+
const next = round(numeric - step);
|
|
2682
|
+
if (min !== void 0 && next < min) return;
|
|
2683
|
+
onChange?.({ target: { value: next, id: htmlFor, name } });
|
|
2672
2684
|
};
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
{
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2685
|
+
const handleInputChange = (e) => {
|
|
2686
|
+
const raw = e.target.value;
|
|
2687
|
+
if (raw === "") {
|
|
2688
|
+
onChange?.({ target: { value: void 0, id: htmlFor, name } });
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
const parsed = Number(raw);
|
|
2692
|
+
if (Number.isNaN(parsed)) return;
|
|
2693
|
+
onChange?.({ target: { value: round(parsed), id: htmlFor, name } });
|
|
2694
|
+
};
|
|
2695
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2696
|
+
/* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1" : "flex-row items-center gap-2"}`, children: [
|
|
2697
|
+
label && /* @__PURE__ */ jsx(
|
|
2698
|
+
"label",
|
|
2699
|
+
{
|
|
2700
|
+
className: "text-sm font-medium ml-1 max-content select-none text-foreground",
|
|
2701
|
+
style: labelStyle,
|
|
2702
|
+
htmlFor,
|
|
2703
|
+
children: label
|
|
2704
|
+
}
|
|
2705
|
+
),
|
|
2706
|
+
/* @__PURE__ */ jsxs(
|
|
2707
|
+
"div",
|
|
2708
|
+
{
|
|
2709
|
+
style,
|
|
2710
|
+
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`,
|
|
2711
|
+
children: [
|
|
2712
|
+
/* @__PURE__ */ jsx(
|
|
2713
|
+
"input",
|
|
2714
|
+
{
|
|
2715
|
+
min,
|
|
2716
|
+
max,
|
|
2717
|
+
autoComplete: "off",
|
|
2718
|
+
disabled,
|
|
2719
|
+
name,
|
|
2720
|
+
id: htmlFor,
|
|
2721
|
+
step,
|
|
2722
|
+
value: value ?? "",
|
|
2723
|
+
onChange: handleInputChange,
|
|
2724
|
+
type: "number",
|
|
2725
|
+
"aria-invalid": hasError || void 0,
|
|
2726
|
+
"aria-describedby": hasError ? errorId : void 0,
|
|
2727
|
+
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",
|
|
2728
|
+
style: inputStyle ?? {},
|
|
2729
|
+
placeholder: placeholder ?? "",
|
|
2730
|
+
readOnly
|
|
2731
|
+
}
|
|
2732
|
+
),
|
|
2733
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col border-l border-border h-9", children: [
|
|
2734
|
+
/* @__PURE__ */ jsx(
|
|
2735
|
+
"button",
|
|
2736
|
+
{
|
|
2737
|
+
type: "button",
|
|
2738
|
+
tabIndex: -1,
|
|
2739
|
+
onClick: onIncrement,
|
|
2740
|
+
disabled: disabled || readOnly || max !== void 0 && numeric >= max,
|
|
2741
|
+
"aria-label": "Increase value",
|
|
2742
|
+
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",
|
|
2743
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 15l7-7 7 7" }) })
|
|
2744
|
+
}
|
|
2745
|
+
),
|
|
2746
|
+
/* @__PURE__ */ jsx(
|
|
2747
|
+
"button",
|
|
2748
|
+
{
|
|
2749
|
+
type: "button",
|
|
2750
|
+
tabIndex: -1,
|
|
2751
|
+
onClick: onDecrement,
|
|
2752
|
+
disabled: disabled || readOnly || min !== void 0 && numeric <= min,
|
|
2753
|
+
"aria-label": "Decrease value",
|
|
2754
|
+
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",
|
|
2755
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, className: "h-3 w-3", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" }) })
|
|
2756
|
+
}
|
|
2757
|
+
)
|
|
2758
|
+
] })
|
|
2759
|
+
]
|
|
2760
|
+
}
|
|
2761
|
+
)
|
|
2762
|
+
] }),
|
|
2763
|
+
hasError && /* @__PURE__ */ jsx("div", { id: errorId, className: "text-xs text-status-error ml-1", children: errorMessage })
|
|
2738
2764
|
] });
|
|
2739
2765
|
}
|
|
2740
2766
|
function Password({
|