@@ -36,6 +36,25 @@ export default function PoseControls ({
3636 }
3737 }
3838
39+ function handleInputChange ( e : React . ChangeEvent < HTMLInputElement > , poseId : number , field : keyof Pose ) {
40+ let final = null ;
41+ if ( e . target . value !== "" ) {
42+ const parsed = parseFloat ( e . target . value ) ;
43+ if ( ! isNaN ( parsed ) ) { final = Math . max ( - 70.75 , Math . min ( 70.75 , parsed ) ) ; }
44+ }
45+ updatePose ( poseId , { [ field ] : final } ) ;
46+ }
47+
48+ function handleInputBlur ( e : React . FocusEvent < HTMLInputElement > , poseId : number , field : keyof Pose , min : number , max : number ) {
49+ if ( e . target . value !== "" ) {
50+ const parsed = parseFloat ( e . target . value ) ;
51+ if ( ! isNaN ( parsed ) ) {
52+ const final = Math . max ( min , Math . min ( max , parsed ) ) ;
53+ e . target . value = final . toString ( ) ;
54+ }
55+ }
56+ }
57+
3958 return (
4059 < div className = "flex h-full flex-col" >
4160 < Button className = "flex mt-4 mx-4" onClick = { addPose } >
@@ -106,17 +125,12 @@ export default function PoseControls ({
106125 id = { `x-${ pose . id } ` }
107126 type = "number"
108127 placeholder = "X"
109- min = { - 70.5 }
110- max = { 70.5 }
111- className = "w-20 h-7 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
112- value = { pose . x }
113- onChange = { ( e ) => {
114- const val = e . target . value ;
115- updatePose ( pose . id , { x : val === "" ? 0 : Math . max ( - 70.5 , Math . min ( 70.5 , parseFloat ( val ) ) ) } ) ;
116- } }
117- onClick = { ( ) => {
118- if ( pose . x === 0 ) updatePose ( pose . id , { x : 0 } ) ;
119- } }
128+ min = { - 70.75 }
129+ max = { 70.75 }
130+ className = "w-20 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
131+ defaultValue = { pose . x ?? 0 }
132+ onChange = { ( e ) => handleInputChange ( e , pose . id , 'x' ) }
133+ onBlur = { ( e ) => handleInputBlur ( e , pose . id , 'x' , - 70.75 , 70.75 ) }
120134 />
121135 </ Field >
122136
@@ -128,17 +142,12 @@ export default function PoseControls ({
128142 id = { `y-${ pose . id } ` }
129143 type = "number"
130144 placeholder = "Y"
131- min = { - 70.5 }
132- max = { 70.5 }
133- className = "w-20 h-7 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
134- value = { pose . y }
135- onClick = { ( ) => {
136- if ( pose . y === 0 ) updatePose ( pose . id , { y : 0 } ) ;
137- } }
138- onChange = { ( e ) => {
139- const val = e . target . value ;
140- updatePose ( pose . id , { y : val === "" ? 0 : Math . max ( - 70.5 , Math . min ( 70.5 , parseFloat ( val ) ) ) } ) ;
141- } }
145+ min = { - 70.75 }
146+ max = { 70.75 }
147+ className = "w-20 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
148+ defaultValue = { pose . y ?? 0 }
149+ onChange = { ( e ) => handleInputChange ( e , pose . id , 'y' ) }
150+ onBlur = { ( e ) => handleInputBlur ( e , pose . id , 'y' , - 70.75 , 70.75 ) }
142151 />
143152 </ Field >
144153 </ div >
@@ -154,15 +163,10 @@ export default function PoseControls ({
154163 placeholder = "Heading"
155164 min = { 0 }
156165 max = { 360 }
157- className = "w-20 h-7 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
158- value = { pose . heading }
159- onClick = { ( ) => {
160- if ( pose . heading === 0 ) updatePose ( pose . id , { heading : 0 } ) ;
161- } }
162- onChange = { ( e ) => {
163- const val = e . target . value ;
164- updatePose ( pose . id , { heading : val === "" ? 0 : Number ( val ) % 360 } ) ;
165- } }
166+ className = "w-20 transition-colors focus-visible:border-red-500 focus-visible:ring-red-500 bg-zinc-900"
167+ defaultValue = { pose . heading ?? 0 }
168+ onChange = { ( e ) => handleInputChange ( e , pose . id , 'heading' ) }
169+ onBlur = { ( e ) => handleInputBlur ( e , pose . id , 'heading' , 0 , 360 ) }
166170 />
167171 </ Field >
168172
@@ -174,17 +178,11 @@ export default function PoseControls ({
174178 id = { `radius-${ pose . id } ` }
175179 type = "number"
176180 placeholder = "Radius"
177- disabled = { ! pose . arcPose }
178- min = { 2 }
179- className = "w-20 h-7 transition-all duration-300 ease-in-out focus-visible:border-red-500 focus-visible:ring-red-500 disabled:cursor-not-allowed disabled:opacity-40 bg-zinc-900"
180- value = { pose . radius }
181- onClick = { ( ) => {
182- if ( pose . radius === 2 ) updatePose ( pose . id , { radius : 0 } ) ;
183- } }
184- onChange = { ( e ) => {
185- const val = e . target . value ;
186- updatePose ( pose . id , { radius : val === "" ? 0 : ( Number ( val ) <= 2 && pose . arcPose ) ? 2 : Number ( val ) } ) ;
187- } }
181+ disabled = { ! pose . arcPose } // TODO: Add limits and clamping for radius (make sure to account for units)
182+ className = "w-20 transition-all duration-300 ease-in-out focus-visible:border-red-500 focus-visible:ring-red-500 disabled:cursor-not-allowed disabled:opacity-40 bg-zinc-900"
183+ defaultValue = { pose . radius ?? 0 }
184+ onChange = { ( e ) => handleInputChange ( e , pose . id , 'radius' ) }
185+ onBlur = { ( e ) => handleInputBlur ( e , pose . id , 'radius' , 0 , 100 ) } // TODO: Proper limits
188186 />
189187 </ Field >
190188 </ div >
0 commit comments