33import { useScene } from '@pascal-app/core'
44import { useViewer } from '@pascal-app/viewer'
55import { useCallback , useEffect , useRef , useState } from 'react'
6+ import {
7+ getLinearUnitLabel ,
8+ linearUnitToMeters ,
9+ metersToLinearUnit ,
10+ } from '../../../lib/measurements'
611import { cn } from '../../../lib/utils'
712
813interface MetricControlProps {
@@ -34,10 +39,30 @@ export function MetricControl({
3439} : MetricControlProps ) {
3540 const viewerUnit = useViewer ( ( state ) => state . unit )
3641 const isImperial = viewerUnit === 'imperial' && unit === 'm'
37- const multiplier = isImperial ? 3.280_84 : 1
38- const displayUnit = isImperial ? 'ft' : unit
42+ const displayUnit = isImperial ? getLinearUnitLabel ( 'imperial' ) : unit
3943
40- const displayValue = value * multiplier
44+ const toDisplayValue = useCallback (
45+ ( storedValue : number ) => ( isImperial ? metersToLinearUnit ( storedValue , 'imperial' ) : storedValue ) ,
46+ [ isImperial ] ,
47+ )
48+ const toStoredValue = useCallback (
49+ ( displayValue : number ) =>
50+ isImperial ? linearUnitToMeters ( displayValue , 'imperial' ) : displayValue ,
51+ [ isImperial ] ,
52+ )
53+ const clamp = useCallback (
54+ ( val : number ) => {
55+ return Math . min ( Math . max ( val , min ) , max )
56+ } ,
57+ [ min , max ] ,
58+ )
59+ const roundStoredValueForDisplayPrecision = useCallback (
60+ ( storedValue : number ) =>
61+ clamp ( toStoredValue ( Number . parseFloat ( toDisplayValue ( storedValue ) . toFixed ( precision ) ) ) ) ,
62+ [ clamp , precision , toDisplayValue , toStoredValue ] ,
63+ )
64+
65+ const displayValue = toDisplayValue ( value )
4166
4267 const [ isEditing , setIsEditing ] = useState ( false )
4368 const [ isDragging , setIsDragging ] = useState ( false )
@@ -50,13 +75,6 @@ export function MetricControl({
5075 const valueRef = useRef ( value )
5176 valueRef . current = value
5277
53- const clamp = useCallback (
54- ( val : number ) => {
55- return Math . min ( Math . max ( val , min ) , max )
56- } ,
57- [ min , max ] ,
58- )
59-
6078 const applyCommittedValue = useCallback (
6179 ( nextValue : number ) => {
6280 if ( onCommit ) {
@@ -84,12 +102,12 @@ export function MetricControl({
84102 e . preventDefault ( )
85103
86104 const direction = e . deltaY < 0 ? 1 : - 1
87- let scrollStep = step / multiplier
88- if ( e . shiftKey ) scrollStep = ( step * 10 ) / multiplier
89- else if ( e . altKey ) scrollStep = ( step * 0.1 ) / multiplier
105+ let scrollStep = toStoredValue ( step )
106+ if ( e . shiftKey ) scrollStep = toStoredValue ( step * 10 )
107+ else if ( e . altKey ) scrollStep = toStoredValue ( step * 0.1 )
90108
91109 const newValue = clamp ( valueRef . current + direction * scrollStep )
92- const finalValue = Number . parseFloat ( ( newValue * multiplier ) . toFixed ( precision ) ) / multiplier
110+ const finalValue = roundStoredValueForDisplayPrecision ( newValue )
93111
94112 if ( Math . abs ( finalValue - valueRef . current ) > 1e-6 ) {
95113 applyCommittedValue ( finalValue )
@@ -98,7 +116,7 @@ export function MetricControl({
98116
99117 container . addEventListener ( 'wheel' , handleWheel , { passive : false } )
100118 return ( ) => container . removeEventListener ( 'wheel' , handleWheel )
101- } , [ isEditing , step , clamp , applyCommittedValue , precision , multiplier ] )
119+ } , [ isEditing , step , clamp , applyCommittedValue , toStoredValue , roundStoredValueForDisplayPrecision ] )
102120
103121 useEffect ( ( ) => {
104122 if ( ! isHovered || isEditing ) return
@@ -110,13 +128,12 @@ export function MetricControl({
110128
111129 if ( direction !== 0 ) {
112130 e . preventDefault ( )
113- let scrollStep = step / multiplier
114- if ( e . shiftKey ) scrollStep = ( step * 10 ) / multiplier
115- else if ( e . altKey ) scrollStep = ( step * 0.1 ) / multiplier
131+ let scrollStep = toStoredValue ( step )
132+ if ( e . shiftKey ) scrollStep = toStoredValue ( step * 10 )
133+ else if ( e . altKey ) scrollStep = toStoredValue ( step * 0.1 )
116134
117135 const newValue = clamp ( valueRef . current + direction * scrollStep )
118- const finalValue =
119- Number . parseFloat ( ( newValue * multiplier ) . toFixed ( precision ) ) / multiplier
136+ const finalValue = roundStoredValueForDisplayPrecision ( newValue )
120137
121138 if ( Math . abs ( finalValue - valueRef . current ) > 1e-6 ) {
122139 applyCommittedValue ( finalValue )
@@ -126,7 +143,15 @@ export function MetricControl({
126143
127144 window . addEventListener ( 'keydown' , handleKeyDown )
128145 return ( ) => window . removeEventListener ( 'keydown' , handleKeyDown )
129- } , [ isHovered , isEditing , step , clamp , applyCommittedValue , precision , multiplier ] )
146+ } , [
147+ isHovered ,
148+ isEditing ,
149+ step ,
150+ clamp ,
151+ applyCommittedValue ,
152+ toStoredValue ,
153+ roundStoredValueForDisplayPrecision ,
154+ ] )
130155
131156 const handlePointerDown = useCallback (
132157 ( e : React . PointerEvent ) => {
@@ -143,14 +168,13 @@ export function MetricControl({
143168 const handlePointerMove = ( moveEvent : PointerEvent ) => {
144169 const deltaX = moveEvent . clientX - startXRef . current
145170
146- let dragStep = step / multiplier
147- if ( moveEvent . shiftKey ) dragStep = ( step * 10 ) / multiplier
148- else if ( moveEvent . altKey ) dragStep = ( step * 0.1 ) / multiplier
171+ let dragStep = toStoredValue ( step )
172+ if ( moveEvent . shiftKey ) dragStep = toStoredValue ( step * 10 )
173+ else if ( moveEvent . altKey ) dragStep = toStoredValue ( step * 0.1 )
149174
150175 const deltaValue = deltaX * dragStep
151176 const newValue = clamp ( startValueRef . current + deltaValue )
152- const newFinalValue =
153- Number . parseFloat ( ( newValue * multiplier ) . toFixed ( precision ) ) / multiplier
177+ const newFinalValue = roundStoredValueForDisplayPrecision ( newValue )
154178
155179 if ( Math . abs ( newFinalValue - finalValue ) > 1e-6 ) {
156180 finalValue = newFinalValue
@@ -182,13 +206,23 @@ export function MetricControl({
182206 document . addEventListener ( 'pointermove' , handlePointerMove )
183207 document . addEventListener ( 'pointerup' , handlePointerUp )
184208 } ,
185- [ isEditing , value , onChange , onCommit , restoreOnCommit , clamp , precision , step , multiplier ] ,
209+ [
210+ isEditing ,
211+ value ,
212+ onChange ,
213+ onCommit ,
214+ restoreOnCommit ,
215+ clamp ,
216+ step ,
217+ toStoredValue ,
218+ roundStoredValueForDisplayPrecision ,
219+ ] ,
186220 )
187221
188222 const handleValueClick = useCallback ( ( ) => {
189223 setIsEditing ( true )
190- setInputValue ( ( value * multiplier ) . toFixed ( precision ) )
191- } , [ value , multiplier , precision ] )
224+ setInputValue ( toDisplayValue ( value ) . toFixed ( precision ) )
225+ } , [ value , toDisplayValue , precision ] )
192226
193227 const handleInputChange = useCallback ( ( e : React . ChangeEvent < HTMLInputElement > ) => {
194228 setInputValue ( e . target . value )
@@ -197,12 +231,12 @@ export function MetricControl({
197231 const submitValue = useCallback ( ( ) => {
198232 const numValue = Number . parseFloat ( inputValue )
199233 if ( Number . isNaN ( numValue ) ) {
200- setInputValue ( ( value * multiplier ) . toFixed ( precision ) )
234+ setInputValue ( toDisplayValue ( value ) . toFixed ( precision ) )
201235 } else {
202- applyCommittedValue ( clamp ( numValue / multiplier ) )
236+ applyCommittedValue ( clamp ( toStoredValue ( numValue ) ) )
203237 }
204238 setIsEditing ( false )
205- } , [ inputValue , applyCommittedValue , clamp , multiplier , value , precision ] )
239+ } , [ inputValue , applyCommittedValue , clamp , toStoredValue , value , precision , toDisplayValue ] )
206240
207241 const handleInputBlur = useCallback ( ( ) => {
208242 submitValue ( )
@@ -213,21 +247,21 @@ export function MetricControl({
213247 if ( e . key === 'Enter' ) {
214248 submitValue ( )
215249 } else if ( e . key === 'Escape' ) {
216- setInputValue ( ( value * multiplier ) . toFixed ( precision ) )
250+ setInputValue ( toDisplayValue ( value ) . toFixed ( precision ) )
217251 setIsEditing ( false )
218252 } else if ( e . key === 'ArrowUp' ) {
219253 e . preventDefault ( )
220- const newV = clamp ( value + step / multiplier )
254+ const newV = clamp ( value + toStoredValue ( step ) )
221255 applyCommittedValue ( newV )
222- setInputValue ( ( newV * multiplier ) . toFixed ( precision ) )
256+ setInputValue ( toDisplayValue ( newV ) . toFixed ( precision ) )
223257 } else if ( e . key === 'ArrowDown' ) {
224258 e . preventDefault ( )
225- const newV = clamp ( value - step / multiplier )
259+ const newV = clamp ( value - toStoredValue ( step ) )
226260 applyCommittedValue ( newV )
227- setInputValue ( ( newV * multiplier ) . toFixed ( precision ) )
261+ setInputValue ( toDisplayValue ( newV ) . toFixed ( precision ) )
228262 }
229263 } ,
230- [ submitValue , value , multiplier , precision , step , clamp , applyCommittedValue ] ,
264+ [ submitValue , value , toDisplayValue , precision , step , clamp , applyCommittedValue , toStoredValue ] ,
231265 )
232266
233267 return (
0 commit comments