mirror of
https://github.com/vercel/commerce.git
synced 2025-06-08 17:26:59 +00:00
feat: Add forwardRef for compatibility
This commit is contained in:
parent
1ccf5b3870
commit
e42c1bfa2d
@ -1,59 +1,83 @@
|
|||||||
import React, { useRef, useEffect, MouseEvent, ReactElement } from 'react'
|
import React, {
|
||||||
|
useRef,
|
||||||
|
useEffect,
|
||||||
|
MouseEvent,
|
||||||
|
FC,
|
||||||
|
ReactElement,
|
||||||
|
forwardRef,
|
||||||
|
Ref,
|
||||||
|
} from 'react'
|
||||||
import mergeRefs from 'react-merge-refs'
|
import mergeRefs from 'react-merge-refs'
|
||||||
import hasParent from './has-parent'
|
import hasParent from './has-parent'
|
||||||
|
|
||||||
interface ClickOutsideProps {
|
interface ClickOutsideProps {
|
||||||
active: boolean
|
active: boolean
|
||||||
onClick: (e?: MouseEvent) => void
|
onClick: (e?: MouseEvent) => void
|
||||||
children: any
|
ref?: Ref<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClickOutside = ({
|
/**
|
||||||
active = true,
|
* Use forward ref to allow this component to be used with other components like
|
||||||
onClick,
|
* focus-trap-react, that rely on the same type of ref forwarding to direct children
|
||||||
children,
|
*/
|
||||||
}: ClickOutsideProps) => {
|
const ClickOutside: FC<ClickOutsideProps> = forwardRef(
|
||||||
const innerRef = useRef()
|
({ active = true, onClick, children }, forwardedRef) => {
|
||||||
const child = children ? React.Children.only(children) : undefined
|
const innerRef = useRef()
|
||||||
if (!child) {
|
|
||||||
throw new Error('A valid non Fragment React Children should be provided')
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
const child = children ? (React.Children.only(children) as any) : undefined
|
||||||
if (active) {
|
|
||||||
document.addEventListener('mousedown', handleClick)
|
if (!child || child.type === React.Fragment) {
|
||||||
document.addEventListener('touchstart', handleClick)
|
/**
|
||||||
|
* React Fragments can't be used, as it would not be possible to pass the ref
|
||||||
|
* created here to them.
|
||||||
|
*/
|
||||||
|
throw new Error('A valid non Fragment React Children should be provided')
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
if (typeof onClick != 'function') {
|
||||||
|
throw new Error('onClick must be a valid function')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (active) {
|
if (active) {
|
||||||
document.removeEventListener('mousedown', handleClick)
|
document.addEventListener('mousedown', handleClick)
|
||||||
document.removeEventListener('touchstart', handleClick)
|
document.addEventListener('touchstart', handleClick)
|
||||||
}
|
}
|
||||||
}
|
return () => {
|
||||||
})
|
if (active) {
|
||||||
|
document.removeEventListener('mousedown', handleClick)
|
||||||
|
document.removeEventListener('touchstart', handleClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const handleClick = (event: any) => {
|
const handleClick = (event: any) => {
|
||||||
if (!hasParent(event.target, innerRef?.current)) {
|
/**
|
||||||
if (typeof onClick === 'function') {
|
* Check if the clicked element is contained by the top level tag provided to the
|
||||||
|
* ClickOutside component, if not, Outside clicked! Fire onClick cb
|
||||||
|
*/
|
||||||
|
if (!hasParent(event.target, innerRef?.current)) {
|
||||||
onClick(event)
|
onClick(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const composedRefCallback = (element: ReactElement) => {
|
/**
|
||||||
if (child) {
|
* Preserve the child ref prop if exists and merge it with the one used here and the
|
||||||
|
* proxied by the forwardRef method
|
||||||
|
*/
|
||||||
|
const composedRefCallback = (element: ReactElement) => {
|
||||||
if (typeof child.ref === 'function') {
|
if (typeof child.ref === 'function') {
|
||||||
child.ref(element)
|
child.ref(element)
|
||||||
} else if (child.ref) {
|
} else if (child.ref) {
|
||||||
child.ref.current = element
|
child.ref.current = element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return React.cloneElement(child, {
|
||||||
|
ref: mergeRefs([composedRefCallback, innerRef, forwardedRef]),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return React.cloneElement(child, {
|
ClickOutside.displayName = 'ClickOutside'
|
||||||
ref: mergeRefs([composedRefCallback, innerRef]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ClickOutside
|
export default ClickOutside
|
||||||
|
Loading…
x
Reference in New Issue
Block a user