1- import React , { FC , ReactElement , cloneElement , useEffect , useRef } from 'react'
2- import { mergeRefs , focusableChildren } from './utils'
1+ import React , { FC , ReactElement , cloneElement , use , useEffect , useRef } from 'react'
2+ import { mergeRefs , focusableChildren , getChildRef } from './utils'
33
44export interface CFocusTrapProps {
55 /**
@@ -146,7 +146,10 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
146146
147147 if ( elements . length === 0 ) {
148148 container . focus ( { preventScroll : true } )
149- } else if ( lastTabNavDirectionRef . current === 'backward' ) {
149+ return
150+ }
151+
152+ if ( lastTabNavDirectionRef . current === 'backward' ) {
150153 elements . at ( - 1 ) ?. focus ( { preventScroll : true } )
151154 } else {
152155 elements [ 0 ] . focus ( { preventScroll : true } )
@@ -161,20 +164,37 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
161164 tabEventSourceRef . current = container
162165 lastTabNavDirectionRef . current = event . shiftKey ? 'backward' : 'forward'
163166
164- if ( ! _additionalContainer ) {
165- return
166- }
167-
168167 const containerElements = focusableChildren ( container )
169- const additionalElements = focusableChildren ( _additionalContainer )
168+ const additionalElements = _additionalContainer ? focusableChildren ( _additionalContainer ) : [ ]
170169
171170 if ( containerElements . length === 0 && additionalElements . length === 0 ) {
172171 // No focusable elements, prevent tab
173172 event . preventDefault ( )
174173 return
175174 }
176175
176+ const focusableElements = [ ...containerElements , ...additionalElements ]
177+
178+ const firstFocusableElement = focusableElements [ 0 ] as HTMLElement
179+ const lastFocusableElement = focusableElements . at ( - 1 ) as HTMLElement
177180 const activeElement = document . activeElement as HTMLElement
181+
182+ if ( event . shiftKey && activeElement === firstFocusableElement ) {
183+ event . preventDefault ( )
184+ lastFocusableElement . focus ( )
185+ return
186+ }
187+
188+ if ( ! event . shiftKey && activeElement === lastFocusableElement ) {
189+ event . preventDefault ( )
190+ firstFocusableElement . focus ( )
191+ return
192+ }
193+
194+ if ( ! _additionalContainer ) {
195+ return
196+ }
197+
178198 const isInContainer = containerElements . includes ( activeElement )
179199 const isInAdditional = additionalElements . includes ( activeElement )
180200
@@ -245,7 +265,12 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
245265
246266 // Attach our ref to the ONLY child — no extra wrappers
247267 const onlyChild = React . Children . only ( children )
248- const childRef = ( onlyChild as React . ReactElement & { ref ?: React . Ref < HTMLElement > } ) . ref
268+
269+ // Handle different ref access patterns between React versions
270+ // React 19+: ref is accessed via element.props.ref
271+ // React 18 and earlier: ref is accessed via element.ref
272+ const childRef : React . Ref < HTMLElement > | undefined = getChildRef ( onlyChild )
273+
249274 const mergedRef = mergeRefs ( childRef , ( node : HTMLElement | null ) => {
250275 containerRef . current = node
251276 } )
0 commit comments