-
Notifications
You must be signed in to change notification settings - Fork 1
/
bhe-frontend.tsx
162 lines (148 loc) · 4.48 KB
/
bhe-frontend.tsx
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* External dependencies
*/
import { ReactElement } from 'react';
/**
* Internal dependencies
*/
import { matcherFromSource, pickKeys } from './utils';
import { hydrate } from './bhe-element';
declare global {
interface Window {
blockTypes: Map< string, ReactElement >;
}
}
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace, @typescript-eslint/no-unused-vars
namespace JSX {
interface IntrinsicElements {
// eslint-disable-next-line @typescript-eslint/naming-convention
'wp-inner-blocks': React.DetailedHTMLProps<
React.HTMLAttributes< HTMLElement >,
HTMLElement
>;
}
}
}
// We assign `blockTypes` to window to make sure it's a global singleton.
//
// Have to do this because of the way we are currently bundling the code
// in this repo, each block gets its own copy of this file.
//
// We COULD fix this by doing some webpack magic to spit out the code in
// `gutenberg-packages` to a shared chunk but assigning `blockTypes` to window
// is a cheap hack for now that will be fixed once we can merge this code into Gutenberg.
if ( typeof window.blockTypes === 'undefined' ) {
window.blockTypes = new Map();
}
export const registerBlockType = ( name: string, Comp: ReactElement ) => {
window.blockTypes.set( name, Comp );
};
const Children = ( { value, providedContext } ) => {
if ( ! value ) {
return null;
}
return (
<wp-inner-blocks
ref={ ( el ) => {
if ( el !== null ) {
// listen for the ping from the child
el.addEventListener( 'wp-block-context', ( event ) => {
// We have to also destructure `event.detail.context` because there can
// already exist a property in the context with the same name.
event.detail.context = {
...providedContext,
...event?.detail?.context,
};
} );
}
} }
suppressHydrationWarning={ true }
dangerouslySetInnerHTML={ { __html: value } }
/>
);
};
Children.shouldComponentUpdate = () => false;
class WpBlock extends HTMLElement {
connectedCallback() {
setTimeout( () => {
// ping the parent for the context
const event = new CustomEvent( 'wp-block-context', {
detail: {},
bubbles: true,
cancelable: true,
} );
this.dispatchEvent( event );
const usesContext = JSON.parse(
this.getAttribute(
'data-wp-block-uses-block-context'
) as string
);
const providesContext = JSON.parse(
this.getAttribute(
'data-wp-block-provides-block-context'
) as string
);
const attributes = JSON.parse(
this.getAttribute( 'data-wp-block-attributes' ) as string
);
const sourcedAttributes = JSON.parse(
this.getAttribute(
'data-wp-block-sourced-attributes'
) as string
);
for ( const attr in sourcedAttributes ) {
attributes[ attr ] = matcherFromSource(
sourcedAttributes[ attr ]
)( this );
}
// pass the context to children if needed
const providedContext =
providesContext &&
pickKeys( attributes, Object.keys( providesContext ) );
// select only the parts of the context that the block declared in
// the `usesContext` of its block.json
const context = pickKeys( event.detail.context, usesContext );
const blockType = this.getAttribute( 'data-wp-block-type' );
const blockProps = {
className: this.children[ 0 ].className,
style: this.children[ 0 ].style,
};
const innerBlocks = this.querySelector( 'wp-inner-blocks' );
const Comp = window.blockTypes.get( blockType );
const technique = this.getAttribute( 'data-wp-block-hydrate' );
const media = this.getAttribute( 'data-wp-block-media' );
const hydrationOptions = { technique, media };
hydrate(
<>
<Comp
attributes={ attributes }
blockProps={ blockProps }
suppressHydrationWarning={ true }
context={ context }
>
<Children
value={ innerBlocks && innerBlocks.innerHTML }
suppressHydrationWarning={ true }
providedContext={ providedContext }
/>
</Comp>
<template
className="wp-inner-blocks"
suppressHydrationWarning={ true }
/>
</>,
this,
hydrationOptions
);
} );
}
}
// We need to wrap the element registration code in a conditional for the same
// reason we assing `blockTypes` to window (see top of the file).
//
// We need to ensure that the component registration code is only run once
// because it throws if you try to register an element with the same name twice.
if ( customElements.get( 'wp-block' ) === undefined ) {
customElements.define( 'wp-block', WpBlock );
}