-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement a constrained version of Slot/Fill
- Loading branch information
1 parent
630e7bb
commit 571a2e0
Showing
4 changed files
with
118 additions
and
4 deletions.
There are no files selected for viewing
6 changes: 3 additions & 3 deletions
6
packages/block-editor/src/components/inspector-controls/index.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 |
---|---|---|
@@ -1,17 +1,17 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { createSlotFill } from '@wordpress/components'; | ||
import { createConstrainedSlotFill } from '@wordpress/components'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { ifBlockEditSelected } from '../block-edit/context'; | ||
|
||
const { Fill, Slot } = createSlotFill( 'InspectorControls' ); | ||
const { Fill, Slot, Provider } = createConstrainedSlotFill(); | ||
|
||
const InspectorControls = ifBlockEditSelected( Fill ); | ||
|
||
InspectorControls.Slot = Slot; | ||
InspectorControls.Provider = Provider; | ||
|
||
export default InspectorControls; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { | ||
filter, | ||
isFunction, | ||
isString, | ||
negate, | ||
findIndex, | ||
} from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { | ||
createContext, | ||
useReducer, | ||
useContext, | ||
useEffect, | ||
cloneElement, | ||
Children, | ||
isEmptyElement, | ||
Fragment, | ||
} from '@wordpress/element'; | ||
import { withInstanceId } from '@wordpress/compose'; | ||
|
||
export function createConstrainedSlotFill() { | ||
const Context = createContext( [ [], () => {} ] ); | ||
|
||
const initialFills = []; | ||
function reducer( state, action ) { | ||
switch ( action.type ) { | ||
case 'remove': | ||
return filter( state, ( fill ) => fill.key !== action.key ); | ||
case 'add': { | ||
const index = findIndex( state, ( fill ) => fill.key === action.key ); | ||
if ( index === -1 ) { | ||
return [ | ||
...state, | ||
{ key: action.key, children: action.children }, | ||
]; | ||
} | ||
return [ | ||
...state.slice( 0, index ), | ||
{ key: action.key, children: action.children }, | ||
...state.slice( index + 1 ), | ||
]; | ||
} | ||
default: | ||
throw new Error(); | ||
} | ||
} | ||
|
||
function Provider( { children } ) { | ||
const context = useReducer( reducer, initialFills ); | ||
|
||
return ( | ||
<Context.Provider value={ context }> | ||
{ children } | ||
</Context.Provider> | ||
); | ||
} | ||
|
||
function Slot( { children, fillProps = {} } ) { | ||
const [ fills ] = useContext( Context ); | ||
|
||
const normalizedFills = fills.map( ( fill ) => { | ||
const { children: fillChildren, key } = fill; | ||
const element = isFunction( fillChildren ) ? fillChildren( fillProps ) : fillChildren; | ||
return Children.map( element, ( child, childIndex ) => { | ||
if ( ! child || isString( child ) ) { | ||
return child; | ||
} | ||
|
||
const childKey = `${ key }---${ child.key || childIndex }`; | ||
return cloneElement( child, { key: childKey } ); | ||
} ); | ||
} ).filter( | ||
// In some cases fills are rendered only when some conditions apply. | ||
// This ensures that we only use non-empty fills when rendering, i.e., | ||
// it allows us to render wrappers only when the fills are actually present. | ||
negate( isEmptyElement ) | ||
); | ||
|
||
return ( | ||
<Fragment> | ||
{ isFunction( children ) ? children( normalizedFills ) : normalizedFills } | ||
</Fragment> | ||
); | ||
} | ||
|
||
const Fill = withInstanceId( ( { children, instanceId } ) => { | ||
const [ , dispatch ] = useContext( Context ); | ||
useEffect( () => { | ||
dispatch( { type: 'add', key: instanceId, children } ); | ||
return () => dispatch( { type: 'remove', key: instanceId } ); | ||
}, [ instanceId, children ] ); | ||
return null; | ||
} ); | ||
|
||
return { | ||
Provider, | ||
Slot, | ||
Fill, | ||
}; | ||
} |