@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
110 lines • 5.24 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
import { PropertyValueFormat } from "@itwin/appui-abstract";
import { Input } from "@itwin/itwinui-react";
import { getDecimalRoundingError } from "./Utils.js";
/** @internal */
export const NumericPropertyInput = forwardRef((props, ref) => {
const { onCommit, propertyRecord, setFocus } = props;
const property = propertyRecord.property;
const [inputValue, setInputValue] = useState(() => getInputTargetFromPropertyRecord(propertyRecord) ?? "");
const handleChange = (newVal) => {
setInputValue(newVal);
};
const { min, max } = property.constraints ? getMinMaxFromPropertyConstraints(property.constraints) : { min: undefined, max: undefined };
const commitInput = () => {
const formattedInputValue = applyConstraints(inputValue, min, max);
setInputValue(formattedInputValue);
onCommit &&
onCommit({
propertyRecord,
newValue: parsePrimitiveValue(formattedInputValue),
});
};
return (_jsx(NumericInput, { onChange: handleChange, value: inputValue, onBlur: commitInput, isDisabled: propertyRecord.isReadonly, setFocus: setFocus, ref: ref, min: min, max: max }));
});
NumericPropertyInput.displayName = "NumericPropertyInput";
function parsePrimitiveValue(value) {
const isValid = value && !isNaN(Number(value));
return {
valueFormat: PropertyValueFormat.Primitive,
value: isValid ? Number(value) : undefined,
displayValue: value,
roundingError: isValid ? getDecimalRoundingError(value) : undefined,
};
}
function getInputTargetFromPropertyRecord(propertyRecord) {
const value = propertyRecord.value;
/* c8 ignore next 3 */
if (value.valueFormat !== PropertyValueFormat.Primitive) {
return undefined;
}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return value.value?.toString();
}
/** @internal */
export const NumericInput = forwardRef(({ value, onChange, onBlur, isDisabled, setFocus, min, max }, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
getValue: () => parsePrimitiveValue(value),
htmlElement: inputRef.current,
}), [value]);
const handleChange = (e) => {
const val = e.currentTarget.value;
// Check if it is a correct number and it is not infinity.
if (!isNaN(Number(val)) && isFinite(Number(val))) {
onChange(val);
}
// Number{"+"), Number("-") and Number(".") returns NaN, but if input is only `.`, `-` or `+`, we should fire `onChange` function.
else if (val.length === 1 && "+-.".includes(val)) {
onChange(val);
}
// Number("+.") and Number("-.") returns NaN, but if input is only `+.` or `-.`, we want to fire `onChange` function.
else if (val === "+." || val === "-.") {
onChange(val);
}
// Let user write scientific numbers. Number("1e") returns NaN, but we want to fire `onChange` function when input before `e` is a correct number.
else if (val.endsWith("e") && !isNaN(Number(val.slice(0, val.length - 1))) && val.length !== 1) {
onChange(val);
}
// Let user write scientific numbers. Number("1e-") returns NaN, but we want to fire `onChange` function when input before `e-` is a correct number.
// We don't need to check if string before `e-` is a valid number, because there is a check if string before `e` is a correct number.
else if (val.endsWith("e-")) {
onChange(val);
}
};
useEffect(() => {
if (setFocus) {
inputRef.current && inputRef.current.focus();
}
}, [setFocus]);
return (_jsx(Input, { ref: inputRef, disabled: isDisabled, "data-testid": "numeric-input", size: "small", value: value, min: min, max: max, onChange: handleChange, onBlur: onBlur, onFocus: () => inputRef.current?.setSelectionRange(0, 9999) }));
});
NumericInput.displayName = "NumericInput";
function applyConstraints(inputAsNumber, min, max) {
if (min === undefined && max === undefined) {
return inputAsNumber;
}
if (!isFinite(Number(inputAsNumber))) {
return inputAsNumber;
}
let valAsNumber = Number(inputAsNumber);
if (min !== undefined) {
valAsNumber = Math.max(valAsNumber, min);
}
if (max !== undefined) {
valAsNumber = Math.min(valAsNumber, max);
}
return valAsNumber.toString();
}
function getMinMaxFromPropertyConstraints(constraints) {
if ("minimumValue" in constraints) {
return { min: constraints.minimumValue, max: constraints.maximumValue };
}
return { min: undefined, max: undefined };
}
//# sourceMappingURL=NumericPropertyInput.js.map