@@ -12,7 +12,11 @@ import type { Axis, Size } from "../types.js";
1212import type { LayoutResult } from "../validateProps.js" ;
1313import {
1414 validateButtonProps ,
15+ validateCheckboxProps ,
1516 validateInputProps ,
17+ validateRadioGroupProps ,
18+ validateSelectProps ,
19+ validateSliderProps ,
1620 validateSpacerProps ,
1721 validateTextProps ,
1822} from "../validateProps.js" ;
@@ -56,29 +60,20 @@ export function measureLeaf(
5660 return ok ( { w, h } ) ;
5761 }
5862 case "slider" : {
59- const props = vnode . props as {
60- value ?: number ;
61- min ?: number ;
62- max ?: number ;
63- step ?: number ;
64- width ?: number ;
65- label ?: string ;
66- showValue ?: boolean ;
67- } ;
63+ const propsRes = validateSliderProps ( vnode . props ) ;
64+ if ( ! propsRes . ok ) return propsRes ;
65+ const props = propsRes . value ;
6866 const normalized = normalizeSliderState ( {
69- value : props . value ?? Number . NaN ,
67+ value : props . value ,
7068 min : props . min ,
7169 max : props . max ,
7270 step : props . step ,
7371 } ) ;
7472 const labelText =
75- typeof props . label === "string" && props . label . length > 0 ? `${ props . label } ` : "" ;
76- const showValue = props . showValue !== false ;
73+ props . label !== undefined && props . label . length > 0 ? `${ props . label } ` : "" ;
74+ const showValue = props . showValue ;
7775 const valueText = showValue ? ` ${ formatSliderValue ( normalized . value , normalized . step ) } ` : "" ;
78- const explicitTrack =
79- typeof props . width === "number" && Number . isFinite ( props . width ) && props . width > 0
80- ? Math . trunc ( props . width )
81- : undefined ;
76+ const explicitTrack = props . width !== undefined && props . width > 0 ? props . width : undefined ;
8277 const trackWidth = explicitTrack ?? DEFAULT_SLIDER_TRACK_WIDTH ;
8378 const intrinsicW = measureTextCells ( labelText ) + 2 + trackWidth + measureTextCells ( valueText ) ;
8479 return ok ( { w : Math . min ( maxW , intrinsicW ) , h : Math . min ( maxH , 1 ) } ) ;
@@ -290,32 +285,40 @@ export function measureLeaf(
290285 return ok ( { w : Math . min ( maxW , totalW ) , h : Math . min ( maxH , 1 ) } ) ;
291286 }
292287 case "select" : {
293- // Select dropdowns show selected value in a fixed-width area.
294- const textW = measureTextCells ( vnode . props . placeholder ?? "Select..." ) ;
288+ const propsRes = validateSelectProps ( vnode . props ) ;
289+ if ( ! propsRes . ok ) return propsRes ;
290+ const selected = propsRes . value . options . find (
291+ ( option ) => option . value === propsRes . value . value ,
292+ ) ;
293+ const displayText = selected ?. label ?? propsRes . value . placeholder ?? "Select…" ;
294+ const textW = measureTextCells ( displayText ) ;
295295 return ok ( { w : Math . min ( maxW , textW + 4 ) , h : Math . min ( maxH , 1 ) } ) ;
296296 }
297297 case "checkbox" : {
298- // Checkbox: [x] + optional label
299- const labelW = vnode . props . label ? measureTextCells ( vnode . props . label ) + 1 : 0 ;
298+ const propsRes = validateCheckboxProps ( vnode . props ) ;
299+ if ( ! propsRes . ok ) return propsRes ;
300+ const labelW =
301+ propsRes . value . label === undefined ? 0 : measureTextCells ( propsRes . value . label ) + 1 ;
300302 return ok ( { w : Math . min ( maxW , 3 + labelW ) , h : Math . min ( maxH , 1 ) } ) ;
301303 }
302304 case "radioGroup" : {
303- // Radio group: stack of options
304- const direction = vnode . props . direction ?? "vertical" ;
305+ const propsRes = validateRadioGroupProps ( vnode . props ) ;
306+ if ( ! propsRes . ok ) return propsRes ;
307+ const direction = propsRes . value . direction ;
305308 if ( direction === "horizontal" ) {
306309 let totalW = 0 ;
307- for ( const opt of vnode . props . options ) {
310+ for ( const opt of propsRes . value . options ) {
308311 totalW += measureTextCells ( opt . label ) + 5 ; // "(x) label "
309312 }
310313 return ok ( { w : Math . min ( maxW , totalW ) , h : Math . min ( maxH , 1 ) } ) ;
311314 }
312315 // Vertical: max width of options, height = num options
313316 let maxOptW = 0 ;
314- for ( const opt of vnode . props . options ) {
317+ for ( const opt of propsRes . value . options ) {
315318 const w = measureTextCells ( opt . label ) + 4 ; // "(x) label"
316319 if ( w > maxOptW ) maxOptW = w ;
317320 }
318- return ok ( { w : Math . min ( maxW , maxOptW ) , h : Math . min ( maxH , vnode . props . options . length ) } ) ;
321+ return ok ( { w : Math . min ( maxW , maxOptW ) , h : Math . min ( maxH , propsRes . value . options . length ) } ) ;
319322 }
320323 default :
321324 return {
@@ -425,34 +428,36 @@ export function layoutLeafKind(
425428 } ) ;
426429 }
427430 case "select" : {
428- // Select dropdown is an interactive leaf widget.
431+ const propsRes = validateSelectProps ( vnode . props ) ;
432+ if ( ! propsRes . ok ) return propsRes ;
429433 return ok ( {
430434 vnode,
431435 rect : { x, y, w : rectW , h : Math . min ( rectH , 1 ) } ,
432436 children : Object . freeze ( [ ] ) ,
433437 } ) ;
434438 }
435439 case "slider" : {
436- // Slider is an interactive leaf widget.
440+ const propsRes = validateSliderProps ( vnode . props ) ;
441+ if ( ! propsRes . ok ) return propsRes ;
437442 return ok ( {
438443 vnode,
439444 rect : { x, y, w : rectW , h : Math . min ( rectH , 1 ) } ,
440445 children : Object . freeze ( [ ] ) ,
441446 } ) ;
442447 }
443448 case "checkbox" : {
444- // Checkbox is an interactive leaf widget.
449+ const propsRes = validateCheckboxProps ( vnode . props ) ;
450+ if ( ! propsRes . ok ) return propsRes ;
445451 return ok ( {
446452 vnode,
447453 rect : { x, y, w : rectW , h : Math . min ( rectH , 1 ) } ,
448454 children : Object . freeze ( [ ] ) ,
449455 } ) ;
450456 }
451457 case "radioGroup" : {
452- // Radio group layout depends on direction.
453- const direction = vnode . props . direction ?? "vertical" ;
454- const optionCount = Array . isArray ( vnode . props . options ) ? vnode . props . options . length : 0 ;
455- const naturalH = direction === "vertical" ? optionCount : 1 ;
458+ const propsRes = validateRadioGroupProps ( vnode . props ) ;
459+ if ( ! propsRes . ok ) return propsRes ;
460+ const naturalH = propsRes . value . direction === "vertical" ? propsRes . value . options . length : 1 ;
456461 return ok ( {
457462 vnode,
458463 rect : { x, y, w : rectW , h : Math . min ( rectH , naturalH ) } ,
0 commit comments