@hilum/ui 3.3.3 → 3.4.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.d.ts CHANGED
@@ -751,17 +751,21 @@ interface InputGroupProps extends React.InputHTMLAttributes<HTMLInputElement> {
751
751
  declare const InputGroup: React.ForwardRefExoticComponent<InputGroupProps & React.RefAttributes<HTMLInputElement>>;
752
752
 
753
753
  interface InputNumberProps extends Omit<React.ComponentProps<"input">, "type" | "value" | "onChange"> {
754
- value: number;
754
+ value: number | null;
755
755
  onChange: (next: number) => void;
756
- min?: number;
757
- max?: number;
758
- step?: number;
756
+ min?: number | undefined;
757
+ max?: number | undefined;
758
+ step?: number | undefined;
759
759
  /** Suffix shown after the number (e.g. "px", "mm", "°", "%"). */
760
- unit?: string;
760
+ unit?: string | undefined;
761
761
  /** Number of decimals to display. Default: 0. */
762
- precision?: number;
762
+ precision?: number | undefined;
763
+ /** Text shown when value is null. Useful for mixed multi-selection values. */
764
+ mixedLabel?: string | undefined;
765
+ /** Calls onChange while typing instead of waiting for blur/Enter. */
766
+ commitOnChange?: boolean | undefined;
763
767
  /** Hides the up/down stepper buttons. */
764
- hideSteppers?: boolean;
768
+ hideSteppers?: boolean | undefined;
765
769
  }
766
770
  /**
767
771
  * Numeric input with up/down steppers, unit suffix, and arrow-key step (Shift = 10×).
package/dist/index.js CHANGED
@@ -3558,32 +3558,49 @@ var InputNumber = React19.forwardRef(
3558
3558
  step = 1,
3559
3559
  unit,
3560
3560
  precision = 0,
3561
+ mixedLabel = "Mixed",
3562
+ commitOnChange = false,
3561
3563
  hideSteppers = false,
3562
3564
  className,
3563
3565
  disabled,
3566
+ onBlur,
3567
+ onFocus,
3568
+ onKeyDown,
3564
3569
  ...rest
3565
3570
  }, ref) => {
3566
- const [text, setText] = React19.useState(value.toFixed(precision));
3571
+ const formatValue = React19.useCallback(
3572
+ (next) => next === null ? mixedLabel : next.toFixed(precision),
3573
+ [mixedLabel, precision]
3574
+ );
3575
+ const [text, setText] = React19.useState(formatValue(value));
3567
3576
  React19.useEffect(() => {
3568
- setText(value.toFixed(precision));
3569
- }, [value, precision]);
3577
+ setText(formatValue(value));
3578
+ }, [formatValue, value]);
3570
3579
  const clamp = React19.useCallback((n) => Math.min(max, Math.max(min, n)), [min, max]);
3571
- const commit = (raw) => {
3580
+ const parseAndClamp = React19.useCallback((raw) => {
3572
3581
  const n = parseFloat(raw);
3573
3582
  if (Number.isFinite(n)) {
3574
- const next = clamp(n);
3575
- onChange(next);
3576
- setText(next.toFixed(precision));
3577
- } else {
3578
- setText(value.toFixed(precision));
3583
+ return clamp(n);
3584
+ }
3585
+ return null;
3586
+ }, [clamp]);
3587
+ const commit = (raw) => {
3588
+ const next = parseAndClamp(raw);
3589
+ if (next === null) {
3590
+ setText(formatValue(value));
3591
+ return;
3579
3592
  }
3593
+ onChange(next);
3594
+ setText(next.toFixed(precision));
3580
3595
  };
3581
3596
  const bump = (delta) => {
3582
- const next = clamp(value + delta);
3597
+ const next = clamp((value ?? 0) + delta);
3583
3598
  onChange(next);
3584
3599
  setText(next.toFixed(precision));
3585
3600
  };
3586
- const onKeyDown = (e) => {
3601
+ const handleKeyDown = (e) => {
3602
+ onKeyDown?.(e);
3603
+ if (e.defaultPrevented) return;
3587
3604
  if (e.key === "ArrowUp") {
3588
3605
  e.preventDefault();
3589
3606
  bump(step * (e.shiftKey ? 10 : 1));
@@ -3612,9 +3629,26 @@ var InputNumber = React19.forwardRef(
3612
3629
  inputMode: "decimal",
3613
3630
  spellCheck: false,
3614
3631
  value: text,
3615
- onChange: (e) => setText(e.target.value),
3616
- onBlur: (e) => commit(e.target.value),
3617
- onKeyDown,
3632
+ onChange: (e) => {
3633
+ setText(e.target.value);
3634
+ if (commitOnChange) {
3635
+ const next = parseAndClamp(e.target.value);
3636
+ if (next !== null) {
3637
+ onChange(next);
3638
+ }
3639
+ }
3640
+ },
3641
+ onFocus: (e) => {
3642
+ if (value === null) {
3643
+ setText("");
3644
+ }
3645
+ onFocus?.(e);
3646
+ },
3647
+ onBlur: (e) => {
3648
+ commit(e.target.value);
3649
+ onBlur?.(e);
3650
+ },
3651
+ onKeyDown: handleKeyDown,
3618
3652
  disabled,
3619
3653
  className: "w-full caption text-foreground px-2 bg-transparent focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none text-right",
3620
3654
  ...rest