112 lines
3.2 KiB
JavaScript
112 lines
3.2 KiB
JavaScript
/**
|
|
* External dependencies
|
|
*/
|
|
import deprecated from '@wordpress/deprecated';
|
|
import {
|
|
createSlotFill as baseCreateSlotFill,
|
|
__experimentalUseSlot,
|
|
useSlot as __useSlot,
|
|
} from 'wordpress-components';
|
|
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/block-settings';
|
|
import { Children, cloneElement } from '@wordpress/element';
|
|
|
|
/**
|
|
* Internal dependencies
|
|
*/
|
|
import BlockErrorBoundary from '../error-boundary';
|
|
|
|
/**
|
|
* This function is used in case __experimentalUseSlot is removed and useSlot is not released, it tries to mock
|
|
* the return value of that slot.
|
|
*
|
|
* @return {Object} The hook mocked return, currently:
|
|
* fills, a null array of length 2.
|
|
*/
|
|
const mockedUseSlot = () => {
|
|
/**
|
|
* If we're here, it means useSlot was never graduated and __experimentalUseSlot is removed, so we should change our code.
|
|
*
|
|
*/
|
|
deprecated( '__experimentalUseSlot', {
|
|
plugin: 'woocommerce-gutenberg-products-block',
|
|
} );
|
|
// We're going to mock its value
|
|
return {
|
|
fills: new Array( 2 ),
|
|
};
|
|
};
|
|
|
|
/**
|
|
* A hook that is used inside a slotFillProvider to return information on the a slot.
|
|
*
|
|
* @param {string} slotName The slot name to be hooked into.
|
|
* @return {Object} slot data.
|
|
*/
|
|
let useSlot;
|
|
|
|
if ( typeof __useSlot === 'function' ) {
|
|
useSlot = __useSlot;
|
|
} else if ( typeof __experimentalUseSlot === 'function' ) {
|
|
useSlot = __experimentalUseSlot;
|
|
} else {
|
|
useSlot = mockedUseSlot;
|
|
}
|
|
|
|
export { useSlot };
|
|
|
|
/**
|
|
* Abstracts @wordpress/components createSlotFill, wraps Fill in an error boundary and passes down fillProps.
|
|
*
|
|
* @param {string} slotName The generated slotName, based down to createSlotFill.
|
|
* @param {null|function(Element):Element} [onError] Returns an element to display the error if the current use is an admin.
|
|
*
|
|
* @return {Object} Returns a newly wrapped Fill and Slot.
|
|
*/
|
|
export const createSlotFill = ( slotName, onError = null ) => {
|
|
const { Fill: BaseFill, Slot: BaseSlot } = baseCreateSlotFill( slotName );
|
|
|
|
/**
|
|
* A Fill that will get rendered inside associate slot.
|
|
* If the code inside has a error, it would be caught ad removed.
|
|
* The error is only visible to admins.
|
|
*
|
|
* @param {Object} props Items props.
|
|
* @param {Array} props.children Children to be rendered.
|
|
*/
|
|
const Fill = ( { children } ) => (
|
|
<BaseFill>
|
|
{ ( fillProps ) =>
|
|
Children.map( children, ( fill ) => (
|
|
<BlockErrorBoundary
|
|
/* Returning null would trigger the default error display.
|
|
* Returning () => null would render nothing.
|
|
*/
|
|
renderError={
|
|
CURRENT_USER_IS_ADMIN ? onError : () => null
|
|
}
|
|
>
|
|
{ cloneElement( fill, fillProps ) }
|
|
</BlockErrorBoundary>
|
|
) )
|
|
}
|
|
</BaseFill>
|
|
);
|
|
|
|
/**
|
|
* A Slot that will get rendered inside our tree.
|
|
* This forces Slot to use the Portal implementation that allows events to be bubbled to react tree instead of dom tree.
|
|
*
|
|
* @param {Object} [props] Slot props.
|
|
* @param {string} props.className Class name to be used on slot.
|
|
* @param {Object} props.fillProps Props to be passed to fills.
|
|
* @param {Element|string} props.as Element used to render the slot, defaults to div.
|
|
*
|
|
*/
|
|
const Slot = ( props ) => <BaseSlot { ...props } bubblesVirtually />;
|
|
|
|
return {
|
|
Fill,
|
|
Slot,
|
|
};
|
|
};
|