Skip to content

Commit

Permalink
refactor SlotFillProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
torounit committed Jun 9, 2023
1 parent 524d6ef commit 386f9d3
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// @ts-nocheck
/**
* WordPress dependencies
*/
import { createContext } from '@wordpress/element';
/**
* Internal dependencies
*/
import type { BaseSlotFillContext } from './types';

export const SlotFillContext = createContext( {
const initialValue: BaseSlotFillContext = {
registerSlot: () => {},
unregisterSlot: () => {},
registerFill: () => {},
unregisterFill: () => {},
getSlot: () => {},
getFills: () => {},
subscribe: () => {},
} );
subscribe: () => () => {},
};
export const SlotFillContext = createContext( initialValue );

export default SlotFillContext;
119 changes: 0 additions & 119 deletions packages/components/src/slot-fill/provider.js

This file was deleted.

116 changes: 116 additions & 0 deletions packages/components/src/slot-fill/provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* WordPress dependencies
*/
import type { Component } from '@wordpress/element';

/**
* Internal dependencies
*/
import SlotFillContext from './context';
import type { BaseSlotFillContext } from './types';
import { useState } from '@wordpress/element';
/**
* External dependencies
*/
import type { ReactNode } from 'react';

export function createSlotRegistory(): BaseSlotFillContext {
const slots: Record< string, Component > = {};
const fills: Record< string, unknown[] > = {};
let listeners: Array< () => void > = [];

function registerSlot( name: string, slot: Component ) {
const previousSlot = slots[ name ];
slots[ name ] = slot;
triggerListeners();

// Sometimes the fills are registered after the initial render of slot
// But before the registerSlot call, we need to rerender the slot.
forceUpdateSlot( name );

// If a new instance of a slot is being mounted while another with the
// same name exists, force its update _after_ the new slot has been
// assigned into the instance, such that its own rendering of children
// will be empty (the new Slot will subsume all fills for this name).
if ( previousSlot ) {
previousSlot.forceUpdate();
}
}

function registerFill( name: string, instance: unknown ) {
fills[ name ] = [ ...( fills[ name ] || [] ), instance ];
forceUpdateSlot( name );
}

function unregisterSlot( name: string, instance: Component ) {
// If a previous instance of a Slot by this name unmounts, do nothing,
// as the slot and its fills should only be removed for the current
// known instance.
if ( slots[ name ] !== instance ) {
return;
}

delete slots[ name ];
triggerListeners();
}

function unregisterFill( name: string, instance: unknown ) {
fills[ name ] =
fills[ name ]?.filter( ( fill ) => fill !== instance ) ?? [];
forceUpdateSlot( name );
}

function getSlot( name: string ) {
return slots[ name ];
}

function getFills( name: string, slotInstance: unknown ) {
// Fills should only be returned for the current instance of the slot
// in which they occupy.
if ( slots[ name ] !== slotInstance ) {
return [];
}
return fills[ name ];
}

function forceUpdateSlot( name: string ) {
const slot = getSlot( name );

if ( slot ) {
slot.forceUpdate();
}
}

function triggerListeners() {
listeners.forEach( ( listener ) => listener() );
}

function subscribe( listener: () => void ) {
listeners.push( listener );

return () => {
listeners = listeners.filter( ( l ) => l !== listener );
};
}

return {
registerSlot,
unregisterSlot,
registerFill,
unregisterFill,
getSlot,
getFills,
subscribe,
};
}

export function SlotFillProvider( { children }: { children: ReactNode } ) {
const [ contextValue ] = useState( createSlotRegistory );
return (
<SlotFillContext.Provider value={ contextValue }>
{ children }
</SlotFillContext.Provider>
);
}

export default SlotFillProvider;
15 changes: 15 additions & 0 deletions packages/components/src/slot-fill/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* External dependencies
*/
import type { Component } from 'react';

export type BaseSlotFillContext = {
registerSlot: ( name: string, slot: Component ) => void;
unregisterSlot: ( name: string, slot: Component ) => void;
registerFill: ( name: string, instance: any ) => void;
unregisterFill: ( name: string, instance: any ) => void;
getSlot: ( name: string ) => any;
// TODO: getFill?
getFills: ( name: string, slotInstance: any ) => any;
subscribe: ( listener: () => {} ) => () => void;
};

0 comments on commit 386f9d3

Please sign in to comment.