@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
115 lines • 5.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NumericInput = exports.NumericPropertyInput = void 0;
const jsx_runtime_1 = require("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.
*--------------------------------------------------------------------------------------------*/
const react_1 = require("react");
const appui_abstract_1 = require("@itwin/appui-abstract");
const itwinui_react_1 = require("@itwin/itwinui-react");
const Utils_js_1 = require("./Utils.js");
/** @internal */
exports.NumericPropertyInput = (0, react_1.forwardRef)((props, ref) => {
const { onCommit, propertyRecord, setFocus, onCancel } = props;
const property = propertyRecord.property;
const [inputValue, setInputValue] = (0, react_1.useState)(() => getInputTargetFromPropertyRecord(propertyRecord) ?? "");
const [isEditing, setEditing] = (0, react_1.useState)(false);
const displayValue = (0, react_1.useMemo)(() => (!isEditing && propertyRecord.isMerged && inputValue === "" ? "--" : inputValue), [isEditing, propertyRecord.isMerged, inputValue]);
const handleChange = (newVal) => {
setInputValue(newVal);
};
const { min, max } = (0, Utils_js_1.getMinMaxFromPropertyConstraints)(property.constraints);
const commitInput = () => {
const formattedInputValue = applyConstraints({ inputAsString: inputValue, min, max });
setInputValue(formattedInputValue);
onCommit && onCommit({ propertyRecord, newValue: parsePrimitiveValue(formattedInputValue) });
};
return ((0, jsx_runtime_1.jsx)(exports.NumericInput, { onChange: handleChange, value: displayValue, onCancel: onCancel, onBlur: () => {
commitInput();
setEditing(false);
}, onFocus: () => setEditing(true), isDisabled: propertyRecord.isReadonly, setFocus: setFocus, ref: ref, min: min, max: max }));
});
exports.NumericPropertyInput.displayName = "NumericPropertyInput";
function parsePrimitiveValue(value) {
const isValid = value && !isNaN(Number(value));
return {
valueFormat: appui_abstract_1.PropertyValueFormat.Primitive,
value: isValid ? Number(value) : undefined,
displayValue: value,
roundingError: isValid ? (0, Utils_js_1.getDecimalRoundingError)(value) : undefined,
};
}
function getInputTargetFromPropertyRecord(propertyRecord) {
const value = propertyRecord.value;
/* v8 ignore next 3 -- @preserve */
if (value.valueFormat !== appui_abstract_1.PropertyValueFormat.Primitive) {
return undefined;
}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return value.value?.toString();
}
/** @internal */
exports.NumericInput = (0, react_1.forwardRef)(({ value, onChange, onBlur, onFocus, isDisabled, setFocus, min, max, onCancel }, ref) => {
const inputRef = (0, react_1.useRef)(null);
(0, react_1.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);
}
};
(0, react_1.useEffect)(() => {
if (setFocus) {
inputRef.current && inputRef.current.focus();
}
}, [setFocus]);
const handleKeyDown = (e) => {
if (e.key === "Escape") {
onCancel?.();
}
if (e.key === "Enter") {
inputRef.current?.blur();
e.stopPropagation();
}
};
return ((0, jsx_runtime_1.jsx)(itwinui_react_1.Input, { ref: inputRef, disabled: isDisabled, "data-testid": "numeric-input", size: "small", value: value, onKeyDown: handleKeyDown, min: min, max: max, onChange: handleChange, onBlur: onBlur, onFocus: (e) => {
onFocus?.(e);
requestAnimationFrame(() => {
inputRef.current?.setSelectionRange(0, 9999);
});
} }));
});
exports.NumericInput.displayName = "NumericInput";
function applyConstraints({ inputAsString, min, max }) {
if (min === undefined && max === undefined) {
return inputAsString;
}
const inputAsNumber = Number(inputAsString);
if (!isFinite(inputAsNumber)) {
return inputAsString;
}
return (0, Utils_js_1.applyNumericConstraints)({ value: inputAsNumber, min, max }).toString();
}
//# sourceMappingURL=NumericPropertyInput.js.map