@@ -273,6 +273,7 @@ export type CollectedZone = Readonly<{
273273 columns : number ;
274274 wrapAround : boolean ;
275275 focusableIds : readonly string [ ] ;
276+ parentZoneId ?: string ;
276277 onEnter ?: ( ) => void ;
277278 onExit ?: ( ) => void ;
278279} > ;
@@ -323,10 +324,13 @@ function collectFocusableIdsInSubtree(node: RuntimeInstance): readonly string[]
323324export function collectFocusZones ( tree : RuntimeInstance ) : ReadonlyMap < string , CollectedZone > {
324325 const m = new Map < string , CollectedZone > ( ) ;
325326
326- const stack : RuntimeInstance [ ] = [ tree ] ;
327+ const stack : Array < { node : RuntimeInstance ; parentZoneId : string | null } > = [
328+ { node : tree , parentZoneId : null } ,
329+ ] ;
327330 while ( stack . length > 0 ) {
328- const node = stack . pop ( ) ;
329- if ( ! node ) continue ;
331+ const item = stack . pop ( ) ;
332+ if ( ! item ) continue ;
333+ const node = item . node ;
330334
331335 if ( node . vnode . kind === "focusZone" ) {
332336 const props = node . vnode . props as {
@@ -373,6 +377,9 @@ export function collectFocusZones(tree: RuntimeInstance): ReadonlyMap<string, Co
373377 wrapAround,
374378 focusableIds : Object . freeze ( focusableIds ) ,
375379 } ;
380+ if ( item . parentZoneId !== null ) {
381+ ( zone as { parentZoneId ?: string } ) . parentZoneId = item . parentZoneId ;
382+ }
376383 if ( onEnter !== undefined ) {
377384 ( zone as { onEnter ?: ( ) => void } ) . onEnter = onEnter ;
378385 }
@@ -384,9 +391,16 @@ export function collectFocusZones(tree: RuntimeInstance): ReadonlyMap<string, Co
384391 }
385392
386393 // Continue traversing children for nested zones
394+ let childParentZoneId = item . parentZoneId ;
395+ if ( node . vnode . kind === "focusZone" ) {
396+ const zoneId = ( node . vnode . props as { id ?: unknown } ) . id ;
397+ if ( typeof zoneId === "string" && zoneId . length > 0 ) {
398+ childParentZoneId = zoneId ;
399+ }
400+ }
387401 for ( let i = node . children . length - 1 ; i >= 0 ; i -- ) {
388402 const c = node . children [ i ] ;
389- if ( c ) stack . push ( c ) ;
403+ if ( c ) stack . push ( { node : c , parentZoneId : childParentZoneId ?? null } ) ;
390404 }
391405 }
392406
@@ -534,7 +548,11 @@ export class WidgetMetadataCollector {
534548 // Zone/trap intermediate data (reused)
535549 private readonly _zoneDataById = new Map <
536550 string ,
537- Omit < CollectedZone , "focusableIds" > & { onEnter ?: ( ) => void ; onExit ?: ( ) => void }
551+ Omit < CollectedZone , "focusableIds" > & {
552+ parentZoneId ?: string ;
553+ onEnter ?: ( ) => void ;
554+ onExit ?: ( ) => void ;
555+ }
538556 > ( ) ;
539557 private readonly _trapDataById = new Map < string , Omit < CollectedTrap , "focusableIds" > > ( ) ;
540558 private readonly _zoneFocusables = new Map < string , string [ ] > ( ) ;
@@ -698,10 +716,23 @@ export class WidgetMetadataCollector {
698716 const onExit =
699717 typeof props . onExit === "function" ? ( props . onExit as ( ) => void ) : undefined ;
700718
719+ let parentZoneId : string | null = null ;
720+ for ( let i = this . _containerStack . length - 1 ; i >= 0 ; i -- ) {
721+ const container = this . _containerStack [ i ] ;
722+ if ( container ?. kind === "zone" ) {
723+ parentZoneId = container . id ;
724+ break ;
725+ }
726+ }
727+
701728 const zoneData : Omit < CollectedZone , "focusableIds" > & {
729+ parentZoneId ?: string ;
702730 onEnter ?: ( ) => void ;
703731 onExit ?: ( ) => void ;
704732 } = { id, tabIndex, navigation, columns, wrapAround } ;
733+ if ( parentZoneId !== null ) {
734+ zoneData . parentZoneId = parentZoneId ;
735+ }
705736 if ( onEnter !== undefined ) {
706737 zoneData . onEnter = onEnter ;
707738 }
0 commit comments