2023-01-26 11:39:25 +00:00
|
|
|
import { h, options, createContext } from 'preact';
|
|
|
|
import { useRef } from 'preact/hooks';
|
2023-02-28 16:33:33 +00:00
|
|
|
import { rawStore as store } from './store';
|
2023-02-20 16:48:33 +00:00
|
|
|
import { componentPrefix } from './constants';
|
2023-01-26 11:39:25 +00:00
|
|
|
|
|
|
|
// Main context.
|
|
|
|
const context = createContext( {} );
|
|
|
|
|
|
|
|
// WordPress Directives.
|
2023-02-20 16:48:33 +00:00
|
|
|
const directiveMap = {};
|
2023-01-26 11:39:25 +00:00
|
|
|
export const directive = ( name, cb ) => {
|
2023-02-20 16:48:33 +00:00
|
|
|
directiveMap[ name ] = cb;
|
2023-01-26 11:39:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// WordPress Components.
|
2023-02-20 16:48:33 +00:00
|
|
|
const componentMap = {};
|
2023-01-26 11:39:25 +00:00
|
|
|
export const component = ( name, Comp ) => {
|
2023-02-20 16:48:33 +00:00
|
|
|
componentMap[ name ] = Comp;
|
2023-01-26 11:39:25 +00:00
|
|
|
};
|
|
|
|
|
2023-02-28 16:33:33 +00:00
|
|
|
// Resolve the path to some property of the store object.
|
2023-01-26 11:39:25 +00:00
|
|
|
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.
|
2023-02-20 16:48:33 +00:00
|
|
|
const Directive = ( { type, directives, props: originalProps } ) => {
|
2023-01-26 11:39:25 +00:00
|
|
|
const ref = useRef( null );
|
|
|
|
const element = h( type, { ...originalProps, ref, _wrapped: true } );
|
|
|
|
const props = { ...originalProps, children: element };
|
|
|
|
const evaluate = getEvaluate( { ref: ref.current } );
|
2023-02-20 16:48:33 +00:00
|
|
|
const directiveArgs = { directives, props, element, context, evaluate };
|
2023-01-26 11:39:25 +00:00
|
|
|
|
2023-02-20 16:48:33 +00:00
|
|
|
for ( const d in directives ) {
|
|
|
|
const wrapper = directiveMap[ d ]?.( directiveArgs );
|
2023-01-26 11:39:25 +00:00
|
|
|
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;
|
2023-02-20 16:48:33 +00:00
|
|
|
const { directives } = vnode.props;
|
2023-01-26 11:39:25 +00:00
|
|
|
|
2023-02-20 16:48:33 +00:00
|
|
|
if (
|
|
|
|
typeof type === 'string' &&
|
|
|
|
type.slice( 0, componentPrefix.length ) === componentPrefix
|
|
|
|
) {
|
2023-01-26 11:39:25 +00:00
|
|
|
vnode.props.children = h(
|
2023-02-20 16:48:33 +00:00
|
|
|
componentMap[ type.slice( componentPrefix.length ) ],
|
2023-01-26 11:39:25 +00:00
|
|
|
{ ...vnode.props, context, evaluate: getEvaluate() },
|
|
|
|
vnode.props.children
|
|
|
|
);
|
2023-02-20 16:48:33 +00:00
|
|
|
} else if ( directives ) {
|
2023-01-26 11:39:25 +00:00
|
|
|
const props = vnode.props;
|
2023-02-20 16:48:33 +00:00
|
|
|
delete props.directives;
|
2023-01-26 11:39:25 +00:00
|
|
|
if ( ! props._wrapped ) {
|
2023-02-20 16:48:33 +00:00
|
|
|
vnode.props = { type: vnode.type, directives, props };
|
|
|
|
vnode.type = Directive;
|
2023-01-26 11:39:25 +00:00
|
|
|
} else {
|
|
|
|
delete props._wrapped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( old ) old( vnode );
|
|
|
|
};
|