This repository has been archived by the owner on Jul 28, 2023. It is now read-only.
/
hooks.js
85 lines (74 loc) · 2.21 KB
/
hooks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { h, options, createContext } from 'preact';
import { useRef } from 'preact/hooks';
import { rawStore as store } from './store';
import { componentPrefix } from './constants';
// Main context.
const context = createContext({});
// WordPress Directives.
const directiveMap = {};
export const directive = (name, cb) => {
directiveMap[name] = cb;
};
// WordPress Components.
const componentMap = {};
export const component = (name, Comp) => {
componentMap[name] = Comp;
};
// Resolve the path to some property of the store 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 Directive = ({ type, directives, 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, props, element, context, evaluate };
for (const d in directives) {
const wrapper = directiveMap[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 { directives } = vnode.props;
if (
typeof type === 'string' &&
type.slice(0, componentPrefix.length) === componentPrefix
) {
vnode.props.children = h(
componentMap[type.slice(componentPrefix.length)],
{ ...vnode.props, context, evaluate: getEvaluate() },
vnode.props.children
);
} else if (directives) {
const props = vnode.props;
delete props.directives;
if (!props._wrapped) {
vnode.props = { type: vnode.type, directives, props };
vnode.type = Directive;
} else {
delete props._wrapped;
}
}
if (old) old(vnode);
};