Files
SPipe/src/NumberField.tsx
T
bjkim1 21ebb16fb7 SPipe 데모 v2 (2026-10-30) 서버 배포본
- SPipe_server_2026-10-30.zip 내용을 풀어서 등록 (압축 파일은 올리지 않음)
- README에 데모 v2 서버 업데이트 순서 추가, 저장소에 없는 문서 목차 정리
- 패치내역을 docs/로 이동, _worklog.md 추가
- 임시 파일(backend/temp.*)과 API 키가 든 파일은 제외
2026-10-30 11:00:46 +09:00

47 lines
1.6 KiB
TypeScript

import React, { useState } from 'react';
import { formatEditableNumber } from './unitSystem';
interface NumberFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> {
/** Value in display units. */
value: number;
/** Called with the parsed display-unit number on blur / Enter only. */
onCommit: (value: number) => void;
}
/**
* Numeric input for unit-converted values. A controlled <input type=number>
* whose value is display(model(typed)) gets rewritten mid-typing whenever the
* ft/m or flow-unit round trip isn't exact (typing "216.4" in a US model ended
* up as 3.9999999999999996). Keeping the typed text as a local draft and
* committing only on blur/Enter avoids that round trip while editing.
*/
export const NumberField: React.FC<NumberFieldProps> = ({ value, onCommit, onBlur, onKeyDown, ...rest }) => {
const [draft, setDraft] = useState<string | null>(null);
const commit = () => {
if (draft === null) return;
const parsed = Number(draft);
setDraft(null);
if (draft.trim() !== '' && Number.isFinite(parsed)) onCommit(parsed);
};
return (
<input
{...rest}
type="text"
inputMode="decimal"
value={draft ?? formatEditableNumber(value)}
onChange={(e) => setDraft(e.target.value)}
onBlur={(e) => {
commit();
onBlur?.(e);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') commit();
if (e.key === 'Escape') setDraft(null);
onKeyDown?.(e);
}}
/>
);
};