2023-01-10 18:13:02 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2023-01-19 09:52:45 +00:00
|
|
|
import { isValidElement, Fragment } from 'react';
|
2023-01-10 18:13:02 +00:00
|
|
|
import { Slot, Fill } from '@wordpress/components';
|
|
|
|
import { cloneElement, createElement } from '@wordpress/element';
|
|
|
|
|
2023-02-13 08:42:26 +00:00
|
|
|
type ChildrenProps = {
|
|
|
|
order: number;
|
|
|
|
};
|
|
|
|
|
2023-01-10 18:13:02 +00:00
|
|
|
/**
|
2023-02-13 08:42:26 +00:00
|
|
|
* Returns an object with the children and props that will be used by `cloneElement`. They will change depending on the
|
|
|
|
* type of children passed in.
|
2023-01-10 18:13:02 +00:00
|
|
|
*
|
2023-02-13 08:42:26 +00:00
|
|
|
* @param {Node} children - Node children.
|
|
|
|
* @param {number} order - Node order.
|
|
|
|
* @param {Array} props - Fill props.
|
|
|
|
* @param {Object} injectProps - Props to inject.
|
|
|
|
* @return {Object} Object with the keys: children and props.
|
2023-01-10 18:13:02 +00:00
|
|
|
*/
|
2023-02-13 08:42:26 +00:00
|
|
|
function getChildrenAndProps< T = Fill.Props, S = Record< string, unknown > >(
|
2023-01-10 18:13:02 +00:00
|
|
|
children: React.ReactNode,
|
|
|
|
order: number,
|
2023-01-19 09:52:45 +00:00
|
|
|
props: T,
|
|
|
|
injectProps?: S
|
2023-01-10 18:13:02 +00:00
|
|
|
) {
|
|
|
|
if ( typeof children === 'function' ) {
|
2023-02-13 08:42:26 +00:00
|
|
|
return {
|
|
|
|
children: children( { ...props, order, ...injectProps } ),
|
|
|
|
props: { order, ...injectProps },
|
|
|
|
};
|
2023-01-10 18:13:02 +00:00
|
|
|
} else if ( isValidElement( children ) ) {
|
2023-02-13 08:42:26 +00:00
|
|
|
// This checks whether 'children' is a react element or a standard HTML element.
|
|
|
|
if ( typeof children?.type === 'function' ) {
|
|
|
|
return {
|
|
|
|
children,
|
|
|
|
props: {
|
|
|
|
...props,
|
|
|
|
order,
|
|
|
|
...injectProps,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
children: children as React.ReactElement< ChildrenProps >,
|
2023-02-16 14:42:41 +00:00
|
|
|
props: { order, ...injectProps },
|
2023-02-13 08:42:26 +00:00
|
|
|
};
|
2023-01-10 18:13:02 +00:00
|
|
|
}
|
|
|
|
throw Error( 'Invalid children type' );
|
|
|
|
}
|
2023-02-13 08:42:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Ordered fill item.
|
|
|
|
*
|
|
|
|
* @param {Node} children - Node children.
|
|
|
|
* @param {number} order - Node order.
|
|
|
|
* @param {Array} props - Fill props.
|
|
|
|
* @param {Object} injectProps - Props to inject.
|
|
|
|
* @return {Node} Node.
|
|
|
|
*/
|
|
|
|
function createOrderedChildren< T = Fill.Props, S = Record< string, unknown > >(
|
|
|
|
children: React.ReactNode,
|
|
|
|
order: number,
|
|
|
|
props: T,
|
|
|
|
injectProps?: S
|
2023-03-24 01:02:20 +00:00
|
|
|
): React.ReactElement {
|
2023-02-13 08:42:26 +00:00
|
|
|
const { children: childrenToRender, props: propsToRender } =
|
|
|
|
getChildrenAndProps( children, order, props, injectProps );
|
|
|
|
return cloneElement( childrenToRender, propsToRender );
|
|
|
|
}
|
2023-01-10 18:13:02 +00:00
|
|
|
export { createOrderedChildren };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sort fills by order for slot children.
|
|
|
|
*
|
|
|
|
* @param {Array} fills - slot's `Fill`s.
|
|
|
|
* @return {Node} Node.
|
|
|
|
*/
|
|
|
|
export const sortFillsByOrder: Slot.Props[ 'children' ] = ( fills ) => {
|
|
|
|
// Copy fills array here because its type is readonly array that doesn't have .sort method in Typescript definition.
|
|
|
|
const sortedFills = [ ...fills ].sort( ( a, b ) => {
|
|
|
|
return a[ 0 ].props.order - b[ 0 ].props.order;
|
|
|
|
} );
|
|
|
|
|
|
|
|
return <Fragment>{ sortedFills }</Fragment>;
|
|
|
|
};
|