@sustaina/shared-ui 1.53.0 → 1.54.1

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.mts CHANGED
@@ -1735,8 +1735,9 @@ type InputNumberProps = NumericFormatProps<InputProps> & {
1735
1735
  min?: number;
1736
1736
  max?: number;
1737
1737
  onStepChange?: (value: number) => void;
1738
+ autoFormatDecimal?: boolean;
1738
1739
  };
1739
- declare const InputNumber: ({ customInputProps, showStepper, step, min, max, onStepChange, onValueChange, value, defaultValue, disabled, onBlur, ...props }: InputNumberProps) => react_jsx_runtime.JSX.Element;
1740
+ declare const InputNumber: ({ customInputProps, showStepper, step, min, max, onStepChange, onValueChange, value, defaultValue, disabled, onBlur, isAllowed: isAllowedProp, autoFormatDecimal, decimalScale: decimalScaleProp, fixedDecimalScale: fixedDecimalScaleProp, ...props }: InputNumberProps) => react_jsx_runtime.JSX.Element;
1740
1741
 
1741
1742
  type PermissionAction = "CREATE" | "READ" | "EDIT" | "DELETE" | "NOTIFY" | "CREATE_DRAFT" | "REQUIRE_SITE";
1742
1743
  type PermissionString = `${string}.${PermissionAction}`;
package/dist/index.d.ts CHANGED
@@ -1735,8 +1735,9 @@ type InputNumberProps = NumericFormatProps<InputProps> & {
1735
1735
  min?: number;
1736
1736
  max?: number;
1737
1737
  onStepChange?: (value: number) => void;
1738
+ autoFormatDecimal?: boolean;
1738
1739
  };
1739
- declare const InputNumber: ({ customInputProps, showStepper, step, min, max, onStepChange, onValueChange, value, defaultValue, disabled, onBlur, ...props }: InputNumberProps) => react_jsx_runtime.JSX.Element;
1740
+ declare const InputNumber: ({ customInputProps, showStepper, step, min, max, onStepChange, onValueChange, value, defaultValue, disabled, onBlur, isAllowed: isAllowedProp, autoFormatDecimal, decimalScale: decimalScaleProp, fixedDecimalScale: fixedDecimalScaleProp, ...props }: InputNumberProps) => react_jsx_runtime.JSX.Element;
1740
1741
 
1741
1742
  type PermissionAction = "CREATE" | "READ" | "EDIT" | "DELETE" | "NOTIFY" | "CREATE_DRAFT" | "REQUIRE_SITE";
1742
1743
  type PermissionString = `${string}.${PermissionAction}`;
package/dist/index.js CHANGED
@@ -5227,6 +5227,7 @@ function DatePicker({
5227
5227
  "h-9 w-full p-0 text-sm font-normal rounded-md transition-all duration-150",
5228
5228
  "border border-transparent",
5229
5229
  !inCurrentMonth && "opacity-50 pointer-events-none",
5230
+ disabled && inCurrentMonth && "opacity-50 pointer-events-none cursor-event-none",
5230
5231
  selected || isRangeStart || isRangeEnd ? "bg-[#379a2a] text-white" : "bg-white text-black hover:border-[#379a2a] hover:bg-green-50 active:bg-green-100",
5231
5232
  isInRange && !isRangeStart && !isRangeEnd && "bg-green-50 border-green-200"
5232
5233
  ),
@@ -15857,8 +15858,11 @@ var Input2 = React__namespace.forwardRef(
15857
15858
  }
15858
15859
  );
15859
15860
  Input2.displayName = "Input";
15860
- var createSourceInfo = () => ({
15861
- source: "prop"
15861
+
15862
+ // src/components/inputNumber/helper.ts
15863
+ var createSourceInfo = (event) => ({
15864
+ event,
15865
+ source: event ? "event" : "prop"
15862
15866
  });
15863
15867
  var parseToNumber = (val) => {
15864
15868
  if (typeof val === "number") return val;
@@ -15868,6 +15872,112 @@ var parseToNumber = (val) => {
15868
15872
  }
15869
15873
  return void 0;
15870
15874
  };
15875
+ var truncateToFixed = (num, scale) => {
15876
+ if (scale === 0) return Math.trunc(num).toString();
15877
+ const sign = num < 0 ? "-" : "";
15878
+ const abs = Math.abs(num);
15879
+ const str = abs.toString();
15880
+ const dotIndex = str.indexOf(".");
15881
+ let intPart;
15882
+ let fracPart;
15883
+ if (dotIndex === -1) {
15884
+ intPart = str;
15885
+ fracPart = "";
15886
+ } else {
15887
+ intPart = str.slice(0, dotIndex);
15888
+ fracPart = str.slice(dotIndex + 1, dotIndex + 1 + scale);
15889
+ }
15890
+ fracPart = fracPart.padEnd(scale, "0");
15891
+ return `${sign}${intPart}.${fracPart}`;
15892
+ };
15893
+ var truncateStringToFixed = (str, scale) => {
15894
+ const trimmed = str.trim();
15895
+ if (trimmed === "" || trimmed === "-") return "0" + (scale > 0 ? "." + "0".repeat(scale) : "");
15896
+ const negative = trimmed.startsWith("-");
15897
+ const unsigned = negative ? trimmed.slice(1) : trimmed;
15898
+ const dotIndex = unsigned.indexOf(".");
15899
+ let intPart;
15900
+ let fracPart;
15901
+ if (dotIndex === -1) {
15902
+ intPart = unsigned || "0";
15903
+ fracPart = "";
15904
+ } else {
15905
+ intPart = unsigned.slice(0, dotIndex) || "0";
15906
+ fracPart = unsigned.slice(dotIndex + 1, dotIndex + 1 + scale);
15907
+ }
15908
+ if (scale === 0) return (negative ? "-" : "") + intPart;
15909
+ fracPart = fracPart.padEnd(scale, "0");
15910
+ return (negative ? "-" : "") + intPart + "." + fracPart;
15911
+ };
15912
+ var clamp = (value, min, max) => {
15913
+ if (max !== void 0 && value > max) return max;
15914
+ if (min !== void 0 && value < min) return min;
15915
+ return value;
15916
+ };
15917
+ function useAutoFormatDecimal({
15918
+ enabled,
15919
+ decimalScale,
15920
+ value,
15921
+ defaultValue
15922
+ }) {
15923
+ const blurScale = enabled ? decimalScale ?? 2 : void 0;
15924
+ const isUserEditingRef = React__namespace.useRef(false);
15925
+ const [formattedValue, setFormattedValue] = React__namespace.useState(() => {
15926
+ if (!enabled) return void 0;
15927
+ const scale = decimalScale ?? 2;
15928
+ const initial = parseToNumber(value) ?? parseToNumber(defaultValue);
15929
+ if (initial !== void 0) return truncateToFixed(initial, scale);
15930
+ return void 0;
15931
+ });
15932
+ React__namespace.useEffect(() => {
15933
+ if (!enabled) return;
15934
+ const parsed = parseToNumber(value);
15935
+ if (parsed !== void 0) {
15936
+ if (blurScale !== void 0 && !isUserEditingRef.current) {
15937
+ setFormattedValue(truncateToFixed(parsed, blurScale));
15938
+ }
15939
+ } else if (value === null || value === "") {
15940
+ setFormattedValue(void 0);
15941
+ }
15942
+ }, [value, enabled, blurScale]);
15943
+ const onEdit = React__namespace.useCallback(
15944
+ (values, sourceInfo) => {
15945
+ if (!enabled || sourceInfo.source !== "event") return;
15946
+ isUserEditingRef.current = true;
15947
+ setFormattedValue(values.value || void 0);
15948
+ },
15949
+ [enabled]
15950
+ );
15951
+ const onBlur = React__namespace.useCallback(
15952
+ (truncatedStr) => {
15953
+ isUserEditingRef.current = false;
15954
+ if (!enabled || blurScale === void 0) return;
15955
+ setFormattedValue(truncatedStr);
15956
+ },
15957
+ [enabled, blurScale]
15958
+ );
15959
+ const resetEditing = React__namespace.useCallback(() => {
15960
+ isUserEditingRef.current = false;
15961
+ }, []);
15962
+ return { formattedValue, blurScale, onEdit, onBlur, resetEditing, setFormattedValue };
15963
+ }
15964
+ function useStepper({ value, step, min, max, disabled, onStep }) {
15965
+ const [changed, setChanged] = React__namespace.useState(false);
15966
+ const changeValue = React__namespace.useCallback(
15967
+ (delta) => {
15968
+ const current = value ?? 0;
15969
+ const clamped = clamp(current + delta, min, max);
15970
+ setChanged(true);
15971
+ onStep(clamped);
15972
+ },
15973
+ [value, max, min, onStep]
15974
+ );
15975
+ const increment = React__namespace.useCallback(() => changeValue(step), [changeValue, step]);
15976
+ const decrement = React__namespace.useCallback(() => changeValue(-step), [changeValue, step]);
15977
+ const isIncrementDisabled = disabled || max !== void 0 && (value ?? 0) >= max;
15978
+ const isDecrementDisabled = disabled || min !== void 0 && (value ?? 0) <= min;
15979
+ return { changed, increment, decrement, isIncrementDisabled, isDecrementDisabled };
15980
+ }
15871
15981
  var InputNumber = ({
15872
15982
  customInputProps,
15873
15983
  showStepper = false,
@@ -15880,58 +15990,106 @@ var InputNumber = ({
15880
15990
  defaultValue,
15881
15991
  disabled,
15882
15992
  onBlur,
15993
+ isAllowed: isAllowedProp,
15994
+ autoFormatDecimal = false,
15995
+ decimalScale: decimalScaleProp,
15996
+ fixedDecimalScale: fixedDecimalScaleProp,
15883
15997
  ...props
15884
15998
  }) => {
15885
15999
  const [internalValue, setInternalValue] = React__namespace.useState(
15886
16000
  () => parseToNumber(value) ?? parseToNumber(defaultValue)
15887
16001
  );
15888
- const [stepperChanged, setStepperChanged] = React__namespace.useState(false);
16002
+ const internalValueRef = React__namespace.useRef(internalValue);
16003
+ const rawValueRef = React__namespace.useRef("");
16004
+ const isBlurClampedRef = React__namespace.useRef(false);
15889
16005
  React__namespace.useEffect(() => {
15890
16006
  const parsed = parseToNumber(value);
15891
- if (parsed !== void 0) setInternalValue(parsed);
16007
+ if (parsed !== void 0) {
16008
+ internalValueRef.current = parsed;
16009
+ setInternalValue(parsed);
16010
+ }
16011
+ isBlurClampedRef.current = false;
15892
16012
  }, [value]);
15893
- const notifyChange = (newValue) => {
15894
- onStepChange?.(newValue);
15895
- onValueChange?.(
15896
- {
15897
- floatValue: newValue,
15898
- formattedValue: String(newValue),
15899
- value: String(newValue)
16013
+ const autoFormat = useAutoFormatDecimal({
16014
+ enabled: autoFormatDecimal,
16015
+ decimalScale: decimalScaleProp,
16016
+ value,
16017
+ defaultValue
16018
+ });
16019
+ const notifyChange = React__namespace.useCallback(
16020
+ (newValue, event) => {
16021
+ internalValueRef.current = newValue;
16022
+ setInternalValue(newValue);
16023
+ onStepChange?.(newValue);
16024
+ onValueChange?.(
16025
+ { floatValue: newValue, formattedValue: String(newValue), value: String(newValue) },
16026
+ createSourceInfo(event)
16027
+ );
16028
+ },
16029
+ [onStepChange, onValueChange]
16030
+ );
16031
+ const stepper = useStepper({
16032
+ value: internalValue,
16033
+ step,
16034
+ min,
16035
+ max,
16036
+ disabled,
16037
+ onStep: React__namespace.useCallback(
16038
+ (clamped) => {
16039
+ setInternalValue(clamped);
16040
+ if (autoFormat.blurScale !== void 0) {
16041
+ autoFormat.setFormattedValue(truncateToFixed(clamped, autoFormat.blurScale));
16042
+ }
16043
+ notifyChange(clamped);
15900
16044
  },
15901
- createSourceInfo()
15902
- );
15903
- };
15904
- const handleValueChange = (values, sourceInfo) => {
15905
- setInternalValue(values.floatValue);
15906
- onValueChange?.(values, sourceInfo);
15907
- if (values.floatValue !== void 0) {
15908
- onStepChange?.(values.floatValue);
15909
- }
15910
- };
15911
- const handleBlur = (event) => {
15912
- onBlur?.(event);
15913
- if (internalValue === void 0) return;
15914
- const clamped = max !== void 0 && internalValue > max ? max : min !== void 0 && internalValue < min ? min : internalValue;
15915
- if (clamped !== internalValue) {
15916
- setInternalValue(clamped);
15917
- notifyChange(clamped);
15918
- }
15919
- };
15920
- const isAllowed = (values) => {
15921
- const { floatValue } = values;
15922
- if (floatValue !== void 0 && max !== void 0 && floatValue > max) return false;
15923
- if (props.isAllowed && !props.isAllowed(values)) return false;
15924
- return true;
15925
- };
15926
- const changeValue = (delta) => {
15927
- const current = internalValue ?? 0;
15928
- const clamped = delta > 0 && max !== void 0 ? Math.min(current + delta, max) : delta < 0 && min !== void 0 ? Math.max(current + delta, min) : current + delta;
15929
- setInternalValue(clamped);
15930
- setStepperChanged(true);
15931
- notifyChange(clamped);
15932
- };
15933
- const isIncrementDisabled = disabled || max !== void 0 && (internalValue ?? 0) >= max;
15934
- const isDecrementDisabled = disabled || min !== void 0 && (internalValue ?? 0) <= min;
16045
+ [autoFormat, notifyChange]
16046
+ )
16047
+ });
16048
+ const handleValueChange = React__namespace.useCallback(
16049
+ (values, sourceInfo) => {
16050
+ internalValueRef.current = values.floatValue;
16051
+ rawValueRef.current = values.value;
16052
+ setInternalValue(values.floatValue);
16053
+ if (sourceInfo.source === "event") {
16054
+ isBlurClampedRef.current = false;
16055
+ }
16056
+ onValueChange?.(values, sourceInfo);
16057
+ if (values.floatValue !== void 0) onStepChange?.(values.floatValue);
16058
+ autoFormat.onEdit(values, sourceInfo);
16059
+ },
16060
+ [onValueChange, onStepChange, autoFormat]
16061
+ );
16062
+ const handleBlur = React__namespace.useCallback(
16063
+ (event) => {
16064
+ onBlur?.(event);
16065
+ const latestValue = internalValueRef.current;
16066
+ if (latestValue === void 0) {
16067
+ autoFormat.resetEditing();
16068
+ return;
16069
+ }
16070
+ const clamped = clamp(latestValue, min, max);
16071
+ const wasClamped = clamped !== latestValue;
16072
+ if (wasClamped) {
16073
+ isBlurClampedRef.current = true;
16074
+ internalValueRef.current = clamped;
16075
+ rawValueRef.current = String(clamped);
16076
+ setInternalValue(clamped);
16077
+ onValueChange?.(
16078
+ { floatValue: clamped, formattedValue: String(clamped), value: String(clamped) },
16079
+ createSourceInfo()
16080
+ );
16081
+ onStepChange?.(clamped);
16082
+ }
16083
+ if (autoFormatDecimal && autoFormat.blurScale !== void 0) {
16084
+ const rawStr = wasClamped ? String(clamped) : rawValueRef.current || String(clamped);
16085
+ autoFormat.onBlur(truncateStringToFixed(rawStr, autoFormat.blurScale));
16086
+ } else {
16087
+ autoFormat.resetEditing();
16088
+ }
16089
+ },
16090
+ [onBlur, autoFormatDecimal, autoFormat, min, max, onValueChange, onStepChange]
16091
+ );
16092
+ const effectiveValue = autoFormat.formattedValue !== void 0 ? autoFormat.formattedValue : isBlurClampedRef.current || stepper.changed || autoFormatDecimal ? internalValue : value;
15935
16093
  const buttonClass = cn(
15936
16094
  "flex items-center justify-center h-3 w-5 transition-colors outline-none",
15937
16095
  "text-neutral-400 hover:text-neutral-600 active:text-neutral-900",
@@ -15941,11 +16099,16 @@ var InputNumber = ({
15941
16099
  reactNumberFormat.NumericFormat,
15942
16100
  {
15943
16101
  customInput: Input2,
15944
- value: stepperChanged ? internalValue : value,
16102
+ value: effectiveValue?.toString(),
15945
16103
  defaultValue,
15946
16104
  onValueChange: handleValueChange,
15947
16105
  onBlur: handleBlur,
15948
- isAllowed,
16106
+ ...isAllowedProp && { isAllowed: isAllowedProp },
16107
+ ...!autoFormatDecimal && {
16108
+ decimalScale: decimalScaleProp,
16109
+ fixedDecimalScale: fixedDecimalScaleProp
16110
+ },
16111
+ ...autoFormat.formattedValue !== void 0 && { valueIsNumericString: true },
15949
16112
  ...props,
15950
16113
  disabled,
15951
16114
  ...customInputProps,
@@ -15954,8 +16117,8 @@ var InputNumber = ({
15954
16117
  "button",
15955
16118
  {
15956
16119
  type: "button",
15957
- onClick: () => changeValue(step),
15958
- disabled: isIncrementDisabled,
16120
+ onClick: stepper.increment,
16121
+ disabled: stepper.isIncrementDisabled,
15959
16122
  className: buttonClass,
15960
16123
  tabIndex: -1,
15961
16124
  "aria-label": "Increment",
@@ -15966,8 +16129,8 @@ var InputNumber = ({
15966
16129
  "button",
15967
16130
  {
15968
16131
  type: "button",
15969
- onClick: () => changeValue(-step),
15970
- disabled: isDecrementDisabled,
16132
+ onClick: stepper.decrement,
16133
+ disabled: stepper.isDecrementDisabled,
15971
16134
  className: buttonClass,
15972
16135
  tabIndex: -1,
15973
16136
  "aria-label": "Decrement",