| 126 | * no longer focusable in the newly committed tree (docs/10 "Focus"). |
| 127 | */ |
| 128 | export function finalizeFocusForCommittedTree( |
| 129 | state: FocusState, |
| 130 | committedTree: RuntimeInstance, |
| 131 | ): FocusState { |
| 132 | const focusList = computeFocusList(committedTree); |
| 133 | const pending = state.pendingFocusedId; |
| 134 | |
| 135 | let nextFocusedId: string | null = state.focusedId; |
| 136 | if (pending !== undefined) nextFocusedId = pending; |
| 137 | |
| 138 | if (nextFocusedId !== null) { |
| 139 | // Use Set for O(1) membership test instead of O(n) includes() |
| 140 | const focusSet = new Set(focusList); |
| 141 | if (!focusSet.has(nextFocusedId)) { |
| 142 | nextFocusedId = focusList[0] ?? null; |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | return Object.freeze({ focusedId: nextFocusedId }); |
| 147 | } |
| 148 | |
| 149 | /* ========== Zone/Trap Support ========== */ |
| 150 | |