-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Components: Refactor SlotFill (#19242)
- Loading branch information
1 parent
b39fb21
commit 9b7eb2d
Showing
15 changed files
with
441 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
packages/components/src/slot-fill/bubbles-virtually/fill.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useRef, useEffect, createPortal } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import useSlot from './use-slot'; | ||
|
||
export default function Fill( { name, children } ) { | ||
const slot = useSlot( name ); | ||
const ref = useRef(); | ||
|
||
useEffect( () => { | ||
// We register fills so we can keep track of their existance. | ||
// Some Slot implementations need to know if there're already fills | ||
// registered so they can choose to render themselves or not. | ||
slot.registerFill( ref ); | ||
return () => { | ||
slot.unregisterFill( ref ); | ||
}; | ||
}, [ slot.registerFill, slot.unregisterFill ] ); | ||
|
||
if ( ! slot.ref || ! slot.ref.current ) { | ||
return null; | ||
} | ||
|
||
if ( typeof children === 'function' ) { | ||
children = children( slot.fillProps ); | ||
} | ||
|
||
return createPortal( children, slot.ref.current ); | ||
} |
15 changes: 15 additions & 0 deletions
15
packages/components/src/slot-fill/bubbles-virtually/slot-fill-context.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { createContext } from '@wordpress/element'; | ||
|
||
const SlotFillContext = createContext( { | ||
slots: {}, | ||
fills: {}, | ||
registerSlot: () => {}, | ||
unregisterSlot: () => {}, | ||
registerFill: () => {}, | ||
unregisterFill: () => {}, | ||
} ); | ||
|
||
export default SlotFillContext; |
92 changes: 92 additions & 0 deletions
92
packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useMemo, useCallback, useState } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import SlotFillContext from './slot-fill-context'; | ||
|
||
function useSlotRegistry() { | ||
const [ slots, setSlots ] = useState( {} ); | ||
const [ fills, setFills ] = useState( {} ); | ||
|
||
const registerSlot = useCallback( ( name, ref, fillProps ) => { | ||
setSlots( ( prevSlots ) => ( { | ||
...prevSlots, | ||
[ name ]: { | ||
...prevSlots[ name ], | ||
ref: ref || prevSlots[ name ].ref, | ||
fillProps: fillProps || prevSlots[ name ].fillProps || {}, | ||
}, | ||
} ) ); | ||
}, [] ); | ||
|
||
const unregisterSlot = useCallback( ( name, ref ) => { | ||
setSlots( ( prevSlots ) => { | ||
// eslint-disable-next-line no-unused-vars | ||
const { [ name ]: slot, ...nextSlots } = prevSlots; | ||
// Make sure we're not unregistering a slot registered by another element | ||
// See https://github.com/WordPress/gutenberg/pull/19242#issuecomment-590295412 | ||
if ( slot.ref === ref ) { | ||
return nextSlots; | ||
} | ||
return prevSlots; | ||
} ); | ||
}, [] ); | ||
|
||
const registerFill = useCallback( ( name, ref ) => { | ||
setFills( ( prevFills ) => ( { | ||
...prevFills, | ||
[ name ]: [ ...( prevFills[ name ] || [] ), ref ], | ||
} ) ); | ||
}, [] ); | ||
|
||
const unregisterFill = useCallback( ( name, ref ) => { | ||
setFills( ( prevFills ) => { | ||
if ( prevFills[ name ] ) { | ||
return { | ||
...prevFills, | ||
[ name ]: prevFills[ name ].filter( | ||
( fillRef ) => fillRef !== ref | ||
), | ||
}; | ||
} | ||
return prevFills; | ||
} ); | ||
}, [] ); | ||
|
||
// Memoizing the return value so it can be directly passed to Provider value | ||
const registry = useMemo( | ||
() => ( { | ||
slots, | ||
fills, | ||
registerSlot, | ||
// Just for readability | ||
updateSlot: registerSlot, | ||
unregisterSlot, | ||
registerFill, | ||
unregisterFill, | ||
} ), | ||
[ | ||
slots, | ||
fills, | ||
registerSlot, | ||
unregisterSlot, | ||
registerFill, | ||
unregisterFill, | ||
] | ||
); | ||
|
||
return registry; | ||
} | ||
|
||
export default function SlotFillProvider( { children } ) { | ||
const registry = useSlotRegistry(); | ||
return ( | ||
<SlotFillContext.Provider value={ registry }> | ||
{ children } | ||
</SlotFillContext.Provider> | ||
); | ||
} |
48 changes: 48 additions & 0 deletions
48
packages/components/src/slot-fill/bubbles-virtually/slot.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
useEffect, | ||
useRef, | ||
useLayoutEffect, | ||
useContext, | ||
} from '@wordpress/element'; | ||
import isShallowEqual from '@wordpress/is-shallow-equal'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import SlotFillContext from './slot-fill-context'; | ||
import useSlot from './use-slot'; | ||
|
||
export default function Slot( { | ||
name, | ||
fillProps = {}, | ||
as: Component = 'div', | ||
...props | ||
} ) { | ||
const registry = useContext( SlotFillContext ); | ||
const ref = useRef(); | ||
const slot = useSlot( name ); | ||
|
||
useEffect( () => { | ||
registry.registerSlot( name, ref, fillProps ); | ||
return () => { | ||
registry.unregisterSlot( name, ref ); | ||
}; | ||
// We are not including fillProps in the deps because we don't want to | ||
// unregister and register the slot whenever fillProps change, which would | ||
// cause the fill to be re-mounted. We are only considering the initial value | ||
// of fillProps. | ||
}, [ registry.registerSlot, registry.unregisterSlot, name ] ); | ||
|
||
// fillProps may be an update that interact with the layout, so | ||
// we useLayoutEffect | ||
useLayoutEffect( () => { | ||
if ( slot.fillProps && ! isShallowEqual( slot.fillProps, fillProps ) ) { | ||
registry.updateSlot( name, ref, fillProps ); | ||
} | ||
} ); | ||
|
||
return <Component ref={ ref } { ...props } />; | ||
} |
54 changes: 54 additions & 0 deletions
54
packages/components/src/slot-fill/bubbles-virtually/use-slot.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useCallback, useContext, useMemo } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import SlotFillContext from './slot-fill-context'; | ||
|
||
export default function useSlot( name ) { | ||
const registry = useContext( SlotFillContext ); | ||
|
||
const slot = registry.slots[ name ] || {}; | ||
const slotFills = registry.fills[ name ]; | ||
const fills = useMemo( () => slotFills || [], [ slotFills ] ); | ||
|
||
const updateSlot = useCallback( | ||
( slotRef, slotFillProps ) => { | ||
registry.updateSlot( name, slotRef, slotFillProps ); | ||
}, | ||
[ name, registry.updateSlot ] | ||
); | ||
|
||
const unregisterSlot = useCallback( | ||
( slotRef ) => { | ||
registry.unregisterSlot( name, slotRef ); | ||
}, | ||
[ name, registry.unregisterSlot ] | ||
); | ||
|
||
const registerFill = useCallback( | ||
( fillRef ) => { | ||
registry.registerFill( name, fillRef ); | ||
}, | ||
[ name, registry.registerFill ] | ||
); | ||
|
||
const unregisterFill = useCallback( | ||
( fillRef ) => { | ||
registry.unregisterFill( name, fillRef ); | ||
}, | ||
[ name, registry.unregisterFill ] | ||
); | ||
|
||
return { | ||
...slot, | ||
updateSlot, | ||
unregisterSlot, | ||
fills, | ||
registerFill, | ||
unregisterFill, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.