-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
slot.js
109 lines (91 loc) · 2.37 KB
/
slot.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// @ts-nocheck
/**
* WordPress dependencies
*/
import {
Children,
Component,
cloneElement,
isEmptyElement,
} from '@wordpress/element';
/**
* Internal dependencies
*/
import SlotFillContext from './context';
/**
* Whether the argument is a function.
*
* @param {*} maybeFunc The argument to check.
* @return {boolean} True if the argument is a function, false otherwise.
*/
function isFunction( maybeFunc ) {
return typeof maybeFunc === 'function';
}
class SlotComponent extends Component {
constructor() {
super( ...arguments );
this.isUnmounted = false;
this.bindNode = this.bindNode.bind( this );
}
componentDidMount() {
const { registerSlot } = this.props;
registerSlot( this.props.name, this );
}
componentWillUnmount() {
const { unregisterSlot } = this.props;
this.isUnmounted = true;
unregisterSlot( this.props.name, this );
}
componentDidUpdate( prevProps ) {
const { name, unregisterSlot, registerSlot } = this.props;
if ( prevProps.name !== name ) {
unregisterSlot( prevProps.name );
registerSlot( name, this );
}
}
bindNode( node ) {
this.node = node;
}
forceUpdate() {
if ( this.isUnmounted ) {
return;
}
super.forceUpdate();
}
render() {
const { children, name, fillProps = {}, getFills } = this.props;
const fills = ( getFills( name, this ) ?? [] )
.map( ( fill ) => {
const fillChildren = isFunction( fill.children )
? fill.children( fillProps )
: fill.children;
return Children.map( fillChildren, ( child, childIndex ) => {
if ( ! child || typeof child === 'string' ) {
return child;
}
const childKey = 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.
( element ) => ! isEmptyElement( element )
);
return <>{ isFunction( children ) ? children( fills ) : fills }</>;
}
}
const Slot = ( props ) => (
<SlotFillContext.Consumer>
{ ( { registerSlot, unregisterSlot, getFills } ) => (
<SlotComponent
{ ...props }
registerSlot={ registerSlot }
unregisterSlot={ unregisterSlot }
getFills={ getFills }
/>
) }
</SlotFillContext.Consumer>
);
export default Slot;