woocommerce/plugins/woocommerce-blocks/assets/js/interactivity/hooks.js

89 lines
2.2 KiB
JavaScript

/**
* External dependencies
*/
import { h, options, createContext } from 'preact';
import { useRef } from 'preact/hooks';
/**
* Internal dependencies
*/
import { store } from './wpx';
// Main context.
const context = createContext( {} );
// WordPress Directives.
const directives = {};
export const directive = ( name, cb ) => {
directives[ name ] = cb;
};
// WordPress Components.
const components = {};
export const component = ( name, Comp ) => {
components[ name ] = Comp;
};
// Resolve the path to some property of the wpx object.
const resolve = ( path, context ) => {
let current = { ...store, context };
path.split( '.' ).forEach( ( p ) => ( current = current[ p ] ) );
return current;
};
// Generate the evaluate function.
const getEvaluate =
( { ref } = {} ) =>
( path, extraArgs = {} ) => {
const value = resolve( path, extraArgs.context );
return typeof value === 'function'
? value( {
state: store.state,
...( ref !== undefined ? { ref } : {} ),
...extraArgs,
} )
: value;
};
// Directive wrapper.
const WpDirective = ( { type, wp, props: originalProps } ) => {
const ref = useRef( null );
const element = h( type, { ...originalProps, ref, _wrapped: true } );
const props = { ...originalProps, children: element };
const evaluate = getEvaluate( { ref: ref.current } );
const directiveArgs = { directives: wp, props, element, context, evaluate };
for ( const d in wp ) {
const wrapper = directives[ d ]?.( directiveArgs );
if ( wrapper !== undefined ) props.children = wrapper;
}
return props.children;
};
// Preact Options Hook called each time a vnode is created.
const old = options.vnode;
options.vnode = ( vnode ) => {
const type = vnode.type;
const wp = vnode.props.wp;
if ( typeof type === 'string' && type.startsWith( 'wp-' ) ) {
vnode.props.children = h(
components[ type ],
{ ...vnode.props, context, evaluate: getEvaluate() },
vnode.props.children
);
} else if ( wp ) {
const props = vnode.props;
delete props.wp;
if ( ! props._wrapped ) {
vnode.props = { type: vnode.type, wp, props };
vnode.type = WpDirective;
} else {
delete props._wrapped;
}
}
if ( old ) old( vnode );
};