import { useState, useRef } from 'react' import { Input, InputNumber } from 'antd' import PropTypes from 'prop-types' import FunctionIcon from '../../Icons/FunctionIcon' const OPERATOR_KEYS = ['+', '-', '*', '/'] /** * Safely evaluate a math expression. Only allows numbers and +, -, *, / */ function safeEval(expr) { const sanitized = String(expr) .replace(/\s/g, '') .replace(/[^0-9+\-*/().]/g, '') if (!sanitized) return null try { const fn = new Function(`return (${sanitized})`) const result = fn() return typeof result === 'number' && Number.isFinite(result) ? result : null } catch { return null } } const InputNumberCal = ({ value, onChange, onBlur, min, max, prefix, suffix, placeholder, disabled, ...rest }) => { const [isExprMode, setIsExprMode] = useState(false) const [exprValue, setExprValue] = useState('') const inputRef = useRef(null) const switchToExprMode = (initialValue) => { setIsExprMode(true) setExprValue(initialValue) setTimeout(() => { //inputRef.current?.focus() const input = inputRef.current.getElementsByTagName('input')[0] if (input) { input.focus() } }, 0) } const exitExprMode = (result) => { setIsExprMode(false) setExprValue('') if (result != null) { const clamped = min != null && result < min ? min : max != null && result > max ? max : result onChange?.(clamped) } } const handleNumberKeyDown = (e) => { if (OPERATOR_KEYS.includes(e.key)) { e.preventDefault() const current = value ?? '' switchToExprMode(String(current) + e.key) } } const handleInputChange = (e) => { const next = e.target.value setExprValue(next) const num = parseFloat(next) if (!next) { onChange?.(null) } else if (!next.match(/[+\-*/=]/) && !Number.isNaN(num)) { onChange?.(num) } } const handleInputKeyDown = (e) => { if (e.key === '=') { e.preventDefault() const result = safeEval(exprValue) if (result != null) { exitExprMode(result) } } } const handleInputBlur = (e) => { const expr = exprValue.trim() if (expr && expr.match(/[+\-*/]/)) { const result = safeEval(expr) if (result != null) { exitExprMode(result) } else { exitExprMode(null) } } else { const num = parseFloat(expr) if (!Number.isNaN(num)) { exitExprMode(num) } else { exitExprMode(null) } } onBlur?.(e) } const commonProps = { prefix, suffix, placeholder, disabled, min, max, ...rest } if (isExprMode) { return (