[internal] Move enabled parameter in hooks to first argument (#3245)
* move `enabled` parameter in hooks to front
Whenever a hook requires an `enabled` state, the `enabled` parameter is
moved to the front. Initially this was the last argument and enabled by
default but everywhere that we use these hooks we have to pass a
dedicated boolean anyway.
This makes sure these hooks follow a similar pattern. Bonus points
because Prettier can now improve formatting the usage of these hooks.
The reason why is because there is no additional argument after the
potential last callback.
Before:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(
{
allowed: useEvent(() => [
data.inputRef.current,
data.buttonRef.current,
data.optionsRef.current,
]),
},
enabled
)
```
After:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(enabled, {
allowed: useEvent(() => [
data.inputRef.current,
data.buttonRef.current,
data.optionsRef.current,
]),
})
```
Much better!
* inline variables
This commit is contained in:
@@ -770,10 +770,9 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
|
||||
}, [data])
|
||||
|
||||
// Handle outside click
|
||||
useOutsideClick(
|
||||
[data.buttonRef, data.inputRef, data.optionsRef],
|
||||
() => actions.closeCombobox(),
|
||||
data.comboboxState === ComboboxState.Open
|
||||
let outsideClickEnabled = data.comboboxState === ComboboxState.Open
|
||||
useOutsideClick(outsideClickEnabled, [data.buttonRef, data.inputRef, data.optionsRef], () =>
|
||||
actions.closeCombobox()
|
||||
)
|
||||
|
||||
let slot = useMemo(() => {
|
||||
@@ -1623,25 +1622,25 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
})()
|
||||
|
||||
// Ensure we close the combobox as soon as the input becomes hidden
|
||||
useOnDisappear(data.inputRef, actions.closeCombobox, visible)
|
||||
useOnDisappear(visible, data.inputRef, actions.closeCombobox)
|
||||
|
||||
// Enable scroll locking when the combobox is visible, and `modal` is enabled
|
||||
useScrollLock(
|
||||
ownerDocument,
|
||||
data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
|
||||
)
|
||||
let scrollLockEnabled = data.__demoMode
|
||||
? false
|
||||
: modal && data.comboboxState === ComboboxState.Open
|
||||
useScrollLock(scrollLockEnabled, ownerDocument)
|
||||
|
||||
// Mark other elements as inert when the combobox is visible, and `modal` is enabled
|
||||
useInertOthers(
|
||||
{
|
||||
allowed: useEvent(() => [
|
||||
data.inputRef.current,
|
||||
data.buttonRef.current,
|
||||
data.optionsRef.current,
|
||||
]),
|
||||
},
|
||||
data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
|
||||
)
|
||||
let inertOthersEnabled = data.__demoMode
|
||||
? false
|
||||
: modal && data.comboboxState === ComboboxState.Open
|
||||
useInertOthers(inertOthersEnabled, {
|
||||
allowed: useEvent(() => [
|
||||
data.inputRef.current,
|
||||
data.buttonRef.current,
|
||||
data.optionsRef.current,
|
||||
]),
|
||||
})
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
data.optionsPropsRef.current.static = props.static ?? false
|
||||
@@ -1650,9 +1649,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
data.optionsPropsRef.current.hold = hold
|
||||
}, [data.optionsPropsRef, hold])
|
||||
|
||||
useTreeWalker({
|
||||
useTreeWalker(data.comboboxState === ComboboxState.Open, {
|
||||
container: data.optionsRef.current,
|
||||
enabled: data.comboboxState === ComboboxState.Open,
|
||||
accept(node) {
|
||||
if (node.getAttribute('role') === 'option') return NodeFilter.FILTER_REJECT
|
||||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP
|
||||
|
||||
@@ -260,28 +260,25 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
|
||||
usesOpenClosedState !== null ? (usesOpenClosedState & State.Closing) === State.Closing : false
|
||||
|
||||
// Ensure other elements can't be interacted with
|
||||
let inertEnabled = (() => {
|
||||
let inertOthersEnabled = (() => {
|
||||
if (__demoMode) return false
|
||||
// Only the top-most dialog should be allowed, all others should be inert
|
||||
if (hasNestedDialogs) return false
|
||||
if (isClosing) return false
|
||||
return enabled
|
||||
})()
|
||||
|
||||
useInertOthers(
|
||||
{
|
||||
allowed: useEvent(() => [
|
||||
// Allow the headlessui-portal of the Dialog to be interactive. This
|
||||
// contains the current dialog and the necessary focus guard elements.
|
||||
internalDialogRef.current?.closest<HTMLElement>('[data-headlessui-portal]') ?? null,
|
||||
]),
|
||||
disallowed: useEvent(() => [
|
||||
// Disallow the "main" tree root node
|
||||
mainTreeNodeRef.current?.closest<HTMLElement>('body > *:not(#headlessui-portal-root)') ??
|
||||
null,
|
||||
]),
|
||||
},
|
||||
__demoMode ? false : inertEnabled
|
||||
)
|
||||
useInertOthers(inertOthersEnabled, {
|
||||
allowed: useEvent(() => [
|
||||
// Allow the headlessui-portal of the Dialog to be interactive. This
|
||||
// contains the current dialog and the necessary focus guard elements.
|
||||
internalDialogRef.current?.closest<HTMLElement>('[data-headlessui-portal]') ?? null,
|
||||
]),
|
||||
disallowed: useEvent(() => [
|
||||
// Disallow the "main" tree root node
|
||||
mainTreeNodeRef.current?.closest<HTMLElement>('body > *:not(#headlessui-portal-root)') ??
|
||||
null,
|
||||
]),
|
||||
})
|
||||
|
||||
// Close Dialog on outside click
|
||||
let outsideClickEnabled = (() => {
|
||||
@@ -289,14 +286,10 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
|
||||
if (hasNestedDialogs) return false
|
||||
return true
|
||||
})()
|
||||
useOutsideClick(
|
||||
resolveRootContainers,
|
||||
(event) => {
|
||||
event.preventDefault()
|
||||
close()
|
||||
},
|
||||
outsideClickEnabled
|
||||
)
|
||||
useOutsideClick(outsideClickEnabled, resolveRootContainers, (event) => {
|
||||
event.preventDefault()
|
||||
close()
|
||||
})
|
||||
|
||||
// Handle `Escape` to close
|
||||
let escapeToCloseEnabled = (() => {
|
||||
@@ -335,10 +328,10 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
|
||||
if (hasParentDialog) return false
|
||||
return true
|
||||
})()
|
||||
useScrollLock(ownerDocument, __demoMode ? false : scrollLockEnabled, resolveRootContainers)
|
||||
useScrollLock(scrollLockEnabled, ownerDocument, resolveRootContainers)
|
||||
|
||||
// Ensure we close the dialog as soon as the dialog itself becomes hidden
|
||||
useOnDisappear(internalDialogRef, close, dialogState === DialogStates.Open)
|
||||
useOnDisappear(enabled, internalDialogRef, close)
|
||||
|
||||
let [describedby, DescriptionProvider] = useDescriptions()
|
||||
|
||||
|
||||
@@ -560,18 +560,15 @@ function ListboxFn<
|
||||
}, [data])
|
||||
|
||||
// Handle outside click
|
||||
useOutsideClick(
|
||||
[data.buttonRef, data.optionsRef],
|
||||
(event, target) => {
|
||||
dispatch({ type: ActionTypes.CloseListbox })
|
||||
let outsideClickEnabled = data.listboxState === ListboxStates.Open
|
||||
useOutsideClick(outsideClickEnabled, [data.buttonRef, data.optionsRef], (event, target) => {
|
||||
dispatch({ type: ActionTypes.CloseListbox })
|
||||
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
data.buttonRef.current?.focus()
|
||||
}
|
||||
},
|
||||
data.listboxState === ListboxStates.Open
|
||||
)
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
data.buttonRef.current?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
let slot = useMemo(() => {
|
||||
return {
|
||||
@@ -927,19 +924,21 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
})()
|
||||
|
||||
// Ensure we close the listbox as soon as the button becomes hidden
|
||||
useOnDisappear(data.buttonRef, actions.closeListbox, visible)
|
||||
useOnDisappear(visible, data.buttonRef, actions.closeListbox)
|
||||
|
||||
// Enable scroll locking when the listbox is visible, and `modal` is enabled
|
||||
useScrollLock(
|
||||
ownerDocument,
|
||||
data.__demoMode ? false : modal && data.listboxState === ListboxStates.Open
|
||||
)
|
||||
let scrollLockEnabled = data.__demoMode
|
||||
? false
|
||||
: modal && data.listboxState === ListboxStates.Open
|
||||
useScrollLock(scrollLockEnabled, ownerDocument)
|
||||
|
||||
// Mark other elements as inert when the listbox is visible, and `modal` is enabled
|
||||
useInertOthers(
|
||||
{ allowed: useEvent(() => [data.buttonRef.current, data.optionsRef.current]) },
|
||||
data.__demoMode ? false : modal && data.listboxState === ListboxStates.Open
|
||||
)
|
||||
let inertOthersEnabled = data.__demoMode
|
||||
? false
|
||||
: modal && data.listboxState === ListboxStates.Open
|
||||
useInertOthers(inertOthersEnabled, {
|
||||
allowed: useEvent(() => [data.buttonRef.current, data.optionsRef.current]),
|
||||
})
|
||||
|
||||
let initialOption = useRef<number | null>(null)
|
||||
|
||||
@@ -970,7 +969,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
//
|
||||
// This can be solved by only transitioning the `opacity` instead of everything, but if you _do_
|
||||
// want to transition the y-axis for example you will run into the same issue again.
|
||||
let didButtonMove = useDidElementMove(data.buttonRef, data.listboxState !== ListboxStates.Open)
|
||||
let didElementMoveEnabled = data.listboxState !== ListboxStates.Open
|
||||
let didButtonMove = useDidElementMove(didElementMoveEnabled, data.buttonRef)
|
||||
|
||||
// Now that we know that the button did move or not, we can either disable the panel and all of
|
||||
// its transitions, or rely on the `visible` state to hide the panel whenever necessary.
|
||||
|
||||
@@ -388,18 +388,15 @@ function MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(
|
||||
let menuRef = useSyncRefs(ref)
|
||||
|
||||
// Handle outside click
|
||||
useOutsideClick(
|
||||
[buttonRef, itemsRef],
|
||||
(event, target) => {
|
||||
dispatch({ type: ActionTypes.CloseMenu })
|
||||
let outsideClickEnabled = menuState === MenuStates.Open
|
||||
useOutsideClick(outsideClickEnabled, [buttonRef, itemsRef], (event, target) => {
|
||||
dispatch({ type: ActionTypes.CloseMenu })
|
||||
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
buttonRef.current?.focus()
|
||||
}
|
||||
},
|
||||
menuState === MenuStates.Open
|
||||
)
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
buttonRef.current?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
let close = useEvent(() => {
|
||||
dispatch({ type: ActionTypes.CloseMenu })
|
||||
@@ -624,19 +621,19 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
|
||||
})()
|
||||
|
||||
// Ensure we close the menu as soon as the button becomes hidden
|
||||
useOnDisappear(state.buttonRef, () => dispatch({ type: ActionTypes.CloseMenu }), visible)
|
||||
useOnDisappear(visible, state.buttonRef, () => {
|
||||
dispatch({ type: ActionTypes.CloseMenu })
|
||||
})
|
||||
|
||||
// Enable scroll locking when the menu is visible, and `modal` is enabled
|
||||
useScrollLock(
|
||||
ownerDocument,
|
||||
state.__demoMode ? false : modal && state.menuState === MenuStates.Open
|
||||
)
|
||||
let scrollLockEnabled = state.__demoMode ? false : modal && state.menuState === MenuStates.Open
|
||||
useScrollLock(scrollLockEnabled, ownerDocument)
|
||||
|
||||
// Mark other elements as inert when the menu is visible, and `modal` is enabled
|
||||
useInertOthers(
|
||||
{ allowed: useEvent(() => [state.buttonRef.current, state.itemsRef.current]) },
|
||||
state.__demoMode ? false : modal && state.menuState === MenuStates.Open
|
||||
)
|
||||
let inertOthersEnabled = state.__demoMode ? false : modal && state.menuState === MenuStates.Open
|
||||
useInertOthers(inertOthersEnabled, {
|
||||
allowed: useEvent(() => [state.buttonRef.current, state.itemsRef.current]),
|
||||
})
|
||||
|
||||
// We keep track whether the button moved or not, we only check this when the menu state becomes
|
||||
// closed. If the button moved, then we want to cancel pending transitions to prevent that the
|
||||
@@ -647,7 +644,8 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
|
||||
//
|
||||
// This can be solved by only transitioning the `opacity` instead of everything, but if you _do_
|
||||
// want to transition the y-axis for example you will run into the same issue again.
|
||||
let didButtonMove = useDidElementMove(state.buttonRef, state.menuState !== MenuStates.Open)
|
||||
let didButtonMoveEnabled = state.menuState !== MenuStates.Open
|
||||
let didButtonMove = useDidElementMove(didButtonMoveEnabled, state.buttonRef)
|
||||
|
||||
// Now that we know that the button did move or not, we can either disable the panel and all of
|
||||
// its transitions, or rely on the `visible` state to hide the panel whenever necessary.
|
||||
@@ -662,9 +660,8 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
|
||||
container.focus({ preventScroll: true })
|
||||
}, [state.menuState, state.itemsRef, ownerDocument, state.itemsRef.current])
|
||||
|
||||
useTreeWalker({
|
||||
useTreeWalker(state.menuState === MenuStates.Open, {
|
||||
container: state.itemsRef.current,
|
||||
enabled: state.menuState === MenuStates.Open,
|
||||
accept(node) {
|
||||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT
|
||||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP
|
||||
|
||||
@@ -365,18 +365,15 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
|
||||
)
|
||||
|
||||
// Handle outside click
|
||||
useOutsideClick(
|
||||
root.resolveContainers,
|
||||
(event, target) => {
|
||||
dispatch({ type: ActionTypes.ClosePopover })
|
||||
let outsideClickEnabled = popoverState === PopoverStates.Open
|
||||
useOutsideClick(outsideClickEnabled, root.resolveContainers, (event, target) => {
|
||||
dispatch({ type: ActionTypes.ClosePopover })
|
||||
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
button?.focus()
|
||||
}
|
||||
},
|
||||
popoverState === PopoverStates.Open
|
||||
)
|
||||
if (!isFocusableElement(target, FocusableMode.Loose)) {
|
||||
event.preventDefault()
|
||||
button?.focus()
|
||||
}
|
||||
})
|
||||
|
||||
let close = useEvent(
|
||||
(
|
||||
@@ -868,10 +865,13 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
})()
|
||||
|
||||
// Ensure we close the popover as soon as the button becomes hidden
|
||||
useOnDisappear(state.button, () => dispatch({ type: ActionTypes.ClosePopover }), visible)
|
||||
useOnDisappear(visible, state.button, () => {
|
||||
dispatch({ type: ActionTypes.ClosePopover })
|
||||
})
|
||||
|
||||
// Enable scroll locking when the popover is visible, and `modal` is enabled
|
||||
useScrollLock(ownerDocument, state.__demoMode ? false : modal && visible)
|
||||
let scrollLockEnabled = state.__demoMode ? false : modal && visible
|
||||
useScrollLock(scrollLockEnabled, ownerDocument)
|
||||
|
||||
let handleKeyDown = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {
|
||||
switch (event.key) {
|
||||
|
||||
@@ -586,7 +586,7 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
)
|
||||
|
||||
// Ensure we change the tree state to hidden once the transition becomes hidden
|
||||
useOnDisappear(internalTransitionRef, () => setState(TreeStates.Hidden))
|
||||
useOnDisappear(show, internalTransitionRef, () => setState(TreeStates.Hidden))
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
if (show) {
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { useRef, type MutableRefObject } from 'react'
|
||||
import { useIsoMorphicEffect } from './use-iso-morphic-effect'
|
||||
|
||||
export function useDidElementMove(
|
||||
element: MutableRefObject<HTMLElement | null>,
|
||||
enabled: boolean = true
|
||||
) {
|
||||
export function useDidElementMove(enabled: boolean, element: MutableRefObject<HTMLElement | null>) {
|
||||
let elementPosition = useRef({ left: 0, top: 0 })
|
||||
useIsoMorphicEffect(() => {
|
||||
let el = element.current
|
||||
|
||||
@@ -13,7 +13,7 @@ it('should be possible to inert an element', async () => {
|
||||
function Example() {
|
||||
let ref = useRef(null)
|
||||
let [enabled, setEnabled] = useState(true)
|
||||
useInertOthers({ disallowed: () => [ref.current] }, enabled)
|
||||
useInertOthers(enabled, { disallowed: () => [ref.current] })
|
||||
|
||||
return (
|
||||
<div ref={ref} id="main">
|
||||
@@ -59,7 +59,7 @@ it('should not mark an element as inert when the hook is disabled', async () =>
|
||||
function Example() {
|
||||
let ref = useRef(null)
|
||||
let [enabled, setEnabled] = useState(false)
|
||||
useInertOthers({ disallowed: () => [ref.current] }, enabled)
|
||||
useInertOthers(enabled, { disallowed: () => [ref.current] })
|
||||
|
||||
return (
|
||||
<div ref={ref} id="main">
|
||||
@@ -95,7 +95,7 @@ it('should mark the element as not inert anymore, once all references are gone',
|
||||
let ref = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
let [enabled, setEnabled] = useState(false)
|
||||
useInertOthers({ disallowed: () => [ref.current?.parentElement ?? null] }, enabled)
|
||||
useInertOthers(enabled, { disallowed: () => [ref.current?.parentElement ?? null] })
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
@@ -143,10 +143,9 @@ it('should mark the element as not inert anymore, once all references are gone',
|
||||
it('should be possible to mark everything but allowed containers as inert', async () => {
|
||||
function Example({ children }: { children: ReactNode }) {
|
||||
let [enabled, setEnabled] = useState(false)
|
||||
useInertOthers(
|
||||
{ allowed: () => [document.getElementById('a-a-b')!, document.getElementById('a-a-c')!] },
|
||||
enabled
|
||||
)
|
||||
useInertOthers(enabled, {
|
||||
allowed: () => [document.getElementById('a-a-b')!, document.getElementById('a-a-c')!],
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -73,11 +73,11 @@ function markNotInert(element: HTMLElement) {
|
||||
* ```
|
||||
*/
|
||||
export function useInertOthers(
|
||||
enabled: boolean,
|
||||
{
|
||||
allowed,
|
||||
disallowed,
|
||||
}: { allowed?: () => (HTMLElement | null)[]; disallowed?: () => (HTMLElement | null)[] } = {},
|
||||
enabled = true
|
||||
}: { allowed?: () => (HTMLElement | null)[]; disallowed?: () => (HTMLElement | null)[] } = {}
|
||||
) {
|
||||
useIsoMorphicEffect(() => {
|
||||
if (!enabled) return
|
||||
|
||||
@@ -10,9 +10,9 @@ import { useLatestValue } from './use-latest-value'
|
||||
* viewport is smaller than `md` the element will disappear.
|
||||
*/
|
||||
export function useOnDisappear(
|
||||
enabled: boolean,
|
||||
ref: MutableRefObject<HTMLElement | null> | HTMLElement | null,
|
||||
cb: () => void,
|
||||
enabled = true
|
||||
cb: () => void
|
||||
) {
|
||||
let listenerRef = useLatestValue((element: HTMLElement) => {
|
||||
let rect = element.getBoundingClientRect()
|
||||
|
||||
@@ -9,9 +9,9 @@ type ContainerCollection = Container[] | Set<Container>
|
||||
type ContainerInput = Container | ContainerCollection
|
||||
|
||||
export function useOutsideClick(
|
||||
enabled: boolean,
|
||||
containers: ContainerInput | (() => ContainerInput),
|
||||
cb: (event: MouseEvent | PointerEvent | FocusEvent | TouchEvent, target: HTMLElement) => void,
|
||||
enabled: boolean = true
|
||||
cb: (event: MouseEvent | PointerEvent | FocusEvent | TouchEvent, target: HTMLElement) => void
|
||||
) {
|
||||
// TODO: remove this once the React bug has been fixed: https://github.com/facebook/react/issues/24657
|
||||
let enabledRef = useRef(false)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useDocumentOverflowLockedEffect } from './document-overflow/use-document-overflow'
|
||||
|
||||
export function useScrollLock(
|
||||
ownerDocument: Document | null,
|
||||
enabled: boolean,
|
||||
ownerDocument: Document | null,
|
||||
resolveAllowedContainers: () => HTMLElement[] = () => [document.body]
|
||||
) {
|
||||
useDocumentOverflowLockedEffect(ownerDocument, enabled, (meta) => ({
|
||||
|
||||
@@ -9,17 +9,18 @@ type AcceptNode = (
|
||||
| typeof NodeFilter.FILTER_SKIP
|
||||
| typeof NodeFilter.FILTER_REJECT
|
||||
|
||||
export function useTreeWalker({
|
||||
container,
|
||||
accept,
|
||||
walk,
|
||||
enabled = true,
|
||||
}: {
|
||||
container: HTMLElement | null
|
||||
accept: AcceptNode
|
||||
walk(node: HTMLElement): void
|
||||
enabled?: boolean
|
||||
}) {
|
||||
export function useTreeWalker(
|
||||
enabled: boolean,
|
||||
{
|
||||
container,
|
||||
accept,
|
||||
walk,
|
||||
}: {
|
||||
container: HTMLElement | null
|
||||
accept: AcceptNode
|
||||
walk(node: HTMLElement): void
|
||||
}
|
||||
) {
|
||||
let acceptRef = useRef(accept)
|
||||
let walkRef = useRef(walk)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user