@sanity/ui
Advanced tools
Comparing version 2.8.0 to 2.8.1
{ | ||
"name": "@sanity/ui", | ||
"version": "2.8.0", | ||
"version": "2.8.1", | ||
"keywords": [ | ||
@@ -5,0 +5,0 @@ "sanity", |
export * from './useArrayProp' | ||
export * from './useClickOutside' | ||
export * from './useClickOutsideEvent' | ||
export * from './useCustomValidity' | ||
@@ -4,0 +5,0 @@ export * from './useElementRect' |
@@ -1,3 +0,2 @@ | ||
import {useEffect, useState} from 'react' | ||
import {useEffectEvent} from 'use-effect-event' | ||
import {useEffect, useRef, useState} from 'react' | ||
import {EMPTY_ARRAY} from '../constants' | ||
@@ -15,136 +14,104 @@ | ||
/** | ||
* @public | ||
* @deprecated - use your own `const [element, setElement] = useState(null)` logic instead | ||
*/ | ||
export type UseClickOutsideSetElement = (el: HTMLElement | null) => void | ||
function _getElements( | ||
element: HTMLElement | null, | ||
elementsArg: ClickOutsideElements, | ||
): HTMLElement[] { | ||
const ret = [element] | ||
for (const el of elementsArg) { | ||
if (Array.isArray(el)) { | ||
ret.push(...el) | ||
} else { | ||
ret.push(el) | ||
} | ||
} | ||
return ret.filter(Boolean) as HTMLElement[] | ||
} | ||
/** | ||
* @public | ||
*/ | ||
export function useClickOutside( | ||
listener: ClickOutsideListener | false | undefined, | ||
elementsArg: () => ClickOutsideElements, | ||
boundaryElement?: () => HTMLElement | null, | ||
): void | ||
/** | ||
* @public | ||
* @deprecated - change `useClickOutside(handler, () => [...], boundary)` to `useClickOutside(handler, () => [...], () => boundary)` | ||
*/ | ||
export function useClickOutside( | ||
listener: ClickOutsideListener | false | undefined, | ||
elementsArg: () => ClickOutsideElements, | ||
boundaryElement: HTMLElement | null, | ||
): void | ||
/** | ||
* @public | ||
* @deprecated - change `useClickOutside(handler, [...], () => boundary)` to `useClickOutside(handler, () => [...], () => boundary)` | ||
*/ | ||
export function useClickOutside( | ||
listener: ClickOutsideListener | false | undefined, | ||
elementsArg: ClickOutsideElements, | ||
boundaryElement: () => HTMLElement | null, | ||
): UseClickOutsideSetElement | ||
/** | ||
* @public | ||
* @deprecated | ||
* Instead of: | ||
* @deprecated replaced by the new `useClickOutsideEvent` hook, instead of: | ||
* ```tsx | ||
* const buttonRef = useRef(null) | ||
* const setElement = useClickOutside(() => {}, [buttonRef.current]) | ||
* return ( | ||
* <> | ||
* <button ref={buttonRef} /> | ||
* {open && <div ref={setElement} />} | ||
* </> | ||
* ) | ||
* const [button, setButtonElement] = useState(null) | ||
* useClickOutside((event) => {}, [button]) | ||
* return <button ref={setButtonElement} /> | ||
* ``` | ||
* Use: | ||
* do: | ||
* ```tsx | ||
* const buttonRef = useRef() | ||
* const [element, setElement] = useState(null) | ||
* useClickOutside(() => {}, () => [buttonRef.current, element]) | ||
* return ( | ||
* <> | ||
* <button ref={buttonRef} /> | ||
* {open && <div ref={setElement} />} | ||
* </> | ||
* ) | ||
* useClickOutsideEvent((event) => {}, () => [buttonRef.current]) | ||
* return <button ref={buttonRef} /> | ||
* ``` | ||
*/ | ||
export function useClickOutside( | ||
listener: ClickOutsideListener | false | undefined, | ||
elementsArg: ClickOutsideElements, | ||
listener: ClickOutsideListener, | ||
elementsArg: ClickOutsideElements = EMPTY_ARRAY, | ||
boundaryElement?: HTMLElement | null, | ||
): UseClickOutsideSetElement | ||
): (el: HTMLElement | null) => void { | ||
const [element, setElement] = useState<HTMLElement | null>(null) | ||
const [elements, setElements] = useState(() => _getElements(element, elementsArg)) | ||
const elementsRef = useRef(elements) | ||
/** | ||
* @public | ||
*/ | ||
export function useClickOutside( | ||
listener: ClickOutsideListener | false | undefined, | ||
elementsArg: ClickOutsideElements | (() => ClickOutsideElements) = EMPTY_ARRAY, | ||
boundaryElement?: HTMLElement | null | (() => HTMLElement | null), | ||
): UseClickOutsideSetElement | void { | ||
/** @deprecated */ | ||
const [legacyElement, setLegacyElement] = useState<HTMLElement | null>(null) | ||
useEffect(() => { | ||
const prevElements = elementsRef.current | ||
const nextElements = _getElements(element, elementsArg) | ||
/** | ||
* The `useEffectEvent` hook allow us to always see the latest value of `listener`, `elementsArg` and `boundaryElement` without needing to | ||
* juggle `useState`, `useRef` and `useState` to make sure the `mousedown` event listener isn't constantly being added and removed. | ||
*/ | ||
const onEvent = useEffectEvent((evt: MouseEvent) => { | ||
if (!listener) { | ||
if (prevElements.length !== nextElements.length) { | ||
setElements(nextElements) | ||
elementsRef.current = nextElements | ||
return | ||
} | ||
const target = evt.target | ||
for (const el of prevElements) { | ||
if (!nextElements.includes(el)) { | ||
setElements(nextElements) | ||
elementsRef.current = nextElements | ||
if (!(target instanceof Node)) { | ||
return | ||
return | ||
} | ||
} | ||
const resolvedBoundaryElement = | ||
typeof boundaryElement === 'function' ? boundaryElement() : boundaryElement | ||
for (const el of nextElements) { | ||
if (!prevElements.includes(el)) { | ||
setElements(nextElements) | ||
elementsRef.current = nextElements | ||
if (resolvedBoundaryElement && !resolvedBoundaryElement.contains(target)) { | ||
return | ||
return | ||
} | ||
} | ||
}, [element, elementsArg]) | ||
const resolvedElements = Array.isArray(elementsArg) | ||
? [legacyElement, ...elementsArg] | ||
: elementsArg() | ||
const elements = resolvedElements.flat() | ||
useEffect(() => { | ||
if (!listener) return undefined | ||
for (const el of elements) { | ||
if (!el) continue | ||
const handleWindowMouseDown = (evt: MouseEvent) => { | ||
const target = evt.target | ||
if (target === el || el.contains(target)) { | ||
if (!(target instanceof Node)) { | ||
return | ||
} | ||
} | ||
listener(evt) | ||
}) | ||
if (boundaryElement && !boundaryElement.contains(target)) { | ||
return | ||
} | ||
const hasListener = Boolean(listener) | ||
for (const el of elements) { | ||
if (target === el || el.contains(target)) { | ||
return | ||
} | ||
} | ||
useEffect(() => { | ||
if (!hasListener) return undefined | ||
listener(evt) | ||
} | ||
const handleEvent = (evt: MouseEvent) => onEvent(evt) | ||
window.addEventListener('mousedown', handleWindowMouseDown) | ||
document.addEventListener('mousedown', handleEvent) | ||
return () => { | ||
document.removeEventListener('mousedown', handleEvent) | ||
window.removeEventListener('mousedown', handleWindowMouseDown) | ||
} | ||
}, [hasListener, onEvent]) | ||
}, [boundaryElement, listener, elements]) | ||
/** | ||
* Only return the legacy setElement function if the elementsArg is an array | ||
*/ | ||
if (Array.isArray(elementsArg)) { | ||
return setLegacyElement as UseClickOutsideSetElement | ||
} | ||
return setElement | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4555969
651
63689