Skip to content

Commit 71c3ec2

Browse files
committed
Components: Add slots proxying support to SlotFillProvider
1 parent 210dc42 commit 71c3ec2

4 files changed

Lines changed: 71 additions & 48 deletions

File tree

packages/components/src/slot-fill/context.js

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/**
22
* External dependencies
33
*/
4-
import { sortBy, forEach, without } from 'lodash';
4+
import { sortBy, forEach, without, some } from 'lodash';
55

66
/**
77
* WordPress dependencies
88
*/
99
import { Component, createContext } from '@wordpress/element';
10+
import { createHigherOrderComponent } from '@wordpress/compose';
1011

1112
const { Provider, Consumer } = createContext( {
1213
registerSlot: () => {},
@@ -21,22 +22,49 @@ class SlotFillProvider extends Component {
2122
constructor() {
2223
super( ...arguments );
2324

24-
this.registerSlot = this.registerSlot.bind( this );
25-
this.registerFill = this.registerFill.bind( this );
26-
this.unregisterSlot = this.unregisterSlot.bind( this );
27-
this.unregisterFill = this.unregisterFill.bind( this );
28-
this.getSlot = this.getSlot.bind( this );
29-
this.getFills = this.getFills.bind( this );
30-
3125
this.slots = {};
3226
this.fills = {};
3327
this.state = {
34-
registerSlot: this.registerSlot,
35-
unregisterSlot: this.unregisterSlot,
36-
registerFill: this.registerFill,
37-
unregisterFill: this.unregisterFill,
38-
getSlot: this.getSlot,
39-
getFills: this.getFills,
28+
registerSlot: this.proxy( 'registerSlot' ).bind( this ),
29+
registerFill: this.proxy( 'registerFill' ).bind( this ),
30+
unregisterSlot: this.proxy( 'unregisterSlot' ).bind( this ),
31+
unregisterFill: this.proxy( 'unregisterFill' ).bind( this ),
32+
getSlot: this.proxy( 'getSlot' ).bind( this ),
33+
getFills: this.proxy( 'getFills' ).bind( this ),
34+
};
35+
}
36+
37+
/**
38+
* Given a function name for a function on the SlotFillProvider prototype,
39+
* returns a new function which either passes the arguments to the current
40+
* instance, or to the context ancestor, dependent on whether the provider
41+
* is configured to handle the given slot.
42+
*
43+
* @param {string} functionName SlotFillProvider function name.
44+
*
45+
* @return {Function} Proxying function.
46+
*/
47+
proxy( functionName ) {
48+
return ( name, ...args ) => {
49+
const { slots: handledSlots } = this.props;
50+
51+
let handler = this[ functionName ];
52+
53+
if ( Array.isArray( handledSlots ) ) {
54+
const isHandled = some( handledSlots, ( slotNameOrComponent ) => {
55+
const { slotName = slotNameOrComponent } = slotNameOrComponent;
56+
return typeof slotName === 'string' && (
57+
slotName === name ||
58+
name.startsWith( slotName + '.' )
59+
);
60+
} );
61+
62+
if ( ! isHandled ) {
63+
handler = this.props[ functionName ];
64+
}
65+
}
66+
67+
return handler.call( this, name, ...args );
4068
};
4169
}
4270

@@ -129,5 +157,25 @@ class SlotFillProvider extends Component {
129157
}
130158
}
131159

132-
export default SlotFillProvider;
133-
export { Consumer };
160+
const withConsumerContext = createHigherOrderComponent(
161+
( WrappedComponent ) => ( props ) => (
162+
<Consumer>
163+
{ ( context ) => (
164+
<WrappedComponent
165+
{ ...props }
166+
registerSlot={ context.registerSlot }
167+
unregisterSlot={ context.unregisterSlot }
168+
registerFill={ context.registerFill }
169+
unregisterFill={ context.unregisterFill }
170+
getSlot={ context.getSlot }
171+
getFills={ context.getFills }
172+
/>
173+
) }
174+
</Consumer>
175+
),
176+
'withConsumerContext'
177+
);
178+
179+
export default withConsumerContext( SlotFillProvider );
180+
181+
export { Consumer, withConsumerContext };

packages/components/src/slot-fill/fill.js

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import { createPortal, useLayoutEffect, useRef, useState } from '@wordpress/elem
1111
/**
1212
* Internal dependencies
1313
*/
14-
import { Consumer } from './context';
14+
import { withConsumerContext } from './context';
1515

1616
let occurrences = 0;
1717

18-
function FillComponent( { name, getSlot, children, registerFill, unregisterFill } ) {
18+
function Fill( { name, getSlot, children, registerFill, unregisterFill } ) {
1919
// Random state used to rerender the component if needed, ideally we don't need this
2020
const [ , updateRerenderState ] = useState( {} );
2121
const rerender = () => updateRerenderState( {} );
@@ -67,17 +67,4 @@ function FillComponent( { name, getSlot, children, registerFill, unregisterFill
6767
return createPortal( children, slot.node );
6868
}
6969

70-
const Fill = ( props ) => (
71-
<Consumer>
72-
{ ( { getSlot, registerFill, unregisterFill } ) => (
73-
<FillComponent
74-
{ ...props }
75-
getSlot={ getSlot }
76-
registerFill={ registerFill }
77-
unregisterFill={ unregisterFill }
78-
/>
79-
) }
80-
</Consumer>
81-
);
82-
83-
export default Fill;
70+
export default withConsumerContext( Fill );

packages/components/src/slot-fill/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function createSlotFill( name ) {
1515

1616
const SlotComponent = ( props ) => <Slot name={ name } { ...props } />;
1717
SlotComponent.displayName = name + 'Slot';
18+
SlotComponent.slotName = name;
1819

1920
return {
2021
Fill: FillComponent,

packages/components/src/slot-fill/slot.js

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import {
2121
/**
2222
* Internal dependencies
2323
*/
24-
import { Consumer } from './context';
24+
import { withConsumerContext } from './context';
2525

26-
class SlotComponent extends Component {
26+
class Slot extends Component {
2727
constructor() {
2828
super( ...arguments );
2929

@@ -89,17 +89,4 @@ class SlotComponent extends Component {
8989
}
9090
}
9191

92-
const Slot = ( props ) => (
93-
<Consumer>
94-
{ ( { registerSlot, unregisterSlot, getFills } ) => (
95-
<SlotComponent
96-
{ ...props }
97-
registerSlot={ registerSlot }
98-
unregisterSlot={ unregisterSlot }
99-
getFills={ getFills }
100-
/>
101-
) }
102-
</Consumer>
103-
);
104-
105-
export default Slot;
92+
export default withConsumerContext( Slot );

0 commit comments

Comments
 (0)