Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfca88fc

Browse files
committed
fix(CFocusTrap): add support forelement.ref andelement.props.ref to support both React 18 and 19
1 parentfa15500 commitfca88fc

File tree

2 files changed

+58
-9
lines changed

2 files changed

+58
-9
lines changed

‎packages/coreui-react/src/components/focus-trap/CFocusTrap.tsx‎

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
importReact,{FC,ReactElement,cloneElement,useEffect,useRef}from'react'
2-
import{mergeRefs,focusableChildren}from'./utils'
1+
importReact,{FC,ReactElement,cloneElement,use,useEffect,useRef}from'react'
2+
import{mergeRefs,focusableChildren,getChildRef}from'./utils'
33

44
exportinterfaceCFocusTrapProps{
55
/**
@@ -146,7 +146,10 @@ export const CFocusTrap: FC<CFocusTrapProps> = ({
146146

147147
if(elements.length===0){
148148
container.focus({preventScroll:true})
149-
}elseif(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
constcontainerElements=focusableChildren(container)
169-
constadditionalElements=focusableChildren(_additionalContainer)
168+
constadditionalElements=_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+
constfocusableElements=[...containerElements, ...additionalElements]
177+
178+
constfirstFocusableElement=focusableElements[0]asHTMLElement
179+
constlastFocusableElement=focusableElements.at(-1)asHTMLElement
177180
constactiveElement=document.activeElementasHTMLElement
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
constisInContainer=containerElements.includes(activeElement)
179199
constisInAdditional=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
constonlyChild=React.Children.only(children)
248-
constchildRef=(onlyChildasReact.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+
constchildRef:React.Ref<HTMLElement>|undefined=getChildRef(onlyChild)
273+
249274
constmergedRef=mergeRefs(childRef,(node:HTMLElement|null)=>{
250275
containerRef.current=node
251276
})

‎packages/coreui-react/src/components/focus-trap/utils.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ export const focusableChildren = (element: HTMLElement): HTMLElement[] => {
2323
returnelements.filter((el)=>!isDisabled(el)&&isVisible(el))
2424
}
2525

26+
/**
27+
* Extracts the ref from a React element, handling version differences between React 18 and 19+.
28+
*
29+
* In React 18 and earlier, refs are stored directly on the element object.
30+
* In React 19+, refs are stored in the element's props object due to changes in React's internals.
31+
* This function automatically detects the React version and uses the appropriate access pattern.
32+
*@param child - The React element to extract the ref from
33+
*@returns The ref attached to the element, or undefined if no ref is present
34+
*/
35+
exportconstgetChildRef=(child:React.ReactElement):React.Ref<HTMLElement>|undefined=>{
36+
constmajor=Number(React.version?.split?.('.')[0]??18)
37+
// React 18 stores ref directly on the element
38+
if(major<=18&&'ref'inchild&&child.ref!==undefined){
39+
return(childasReact.ReactElement&{ref?:React.Ref<HTMLElement>}).ref
40+
}
41+
42+
// React 19 stores ref in props
43+
if(child.props&&typeofchild.props==='object'&&'ref'inchild.props){
44+
return(child.propsas{ref?:React.Ref<HTMLElement>}).ref
45+
}
46+
47+
returnundefined
48+
}
49+
2650
/**
2751
* Checks if an element is disabled.
2852
* Considers various ways an element can be disabled including CSS classes and attributes.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp