1
1
import { h , cloneElement , render , hydrate } from 'preact' ;
2
2
3
+ /**
4
+ * @typedef {import('preact').FunctionComponent<any> | import('preact').ComponentClass<any> | import('preact').FunctionalComponent<any> } ComponentDefinition
5
+ * @typedef {{ shadow: false } | { shadow: true, mode: 'open' | 'closed'} } Options
6
+ * @typedef {HTMLElement & { _root: ShadowRoot | HTMLElement, _vdomComponent: ComponentDefinition, _vdom: ReturnType<typeof import("preact").h> | null } } PreactCustomElement
7
+ */
8
+
9
+ /**
10
+ * Register a preact component as web-component.
11
+ * @param {ComponentDefinition } Component The preact component to register
12
+ * @param {string } [tagName] The HTML element tag-name (must contain a hyphen and be lowercase)
13
+ * @param {string[] } [propNames] HTML element attributes to observe
14
+ * @param {Options } [options] Additional element options
15
+ * @example
16
+ * ```ts
17
+ * // use custom web-component class
18
+ * class PreactWebComponent extends Component {
19
+ * static tagName = 'my-web-component';
20
+ * render() {
21
+ * return <p>Hello world!</p>
22
+ * }
23
+ * }
24
+ *
25
+ * register(PreactComponent);
26
+ *
27
+ * // use a preact component
28
+ * function PreactComponent({ prop }) {
29
+ * return <p>Hello {prop}!</p>
30
+ * }
31
+ *
32
+ * register(PreactComponent, 'my-component');
33
+ * register(PreactComponent, 'my-component', ['prop']);
34
+ * register(PreactComponent, 'my-component', ['prop'], {
35
+ * shadow: true,
36
+ * mode: 'closed'
37
+ * });
38
+ * ```
39
+ */
3
40
export default function register ( Component , tagName , propNames , options ) {
4
41
function PreactElement ( ) {
5
- const inst = Reflect . construct ( HTMLElement , [ ] , PreactElement ) ;
42
+ const inst = /** @type {PreactCustomElement } */ (
43
+ Reflect . construct ( HTMLElement , [ ] , PreactElement )
44
+ ) ;
6
45
inst . _vdomComponent = Component ;
7
46
inst . _root =
8
- options && options . shadow ? inst . attachShadow ( { mode : options . mode || 'open' } ) : inst ;
47
+ options && options . shadow
48
+ ? inst . attachShadow ( { mode : options . mode || 'open' } )
49
+ : inst ;
9
50
return inst ;
10
51
}
11
52
PreactElement . prototype = Object . create ( HTMLElement . prototype ) ;
@@ -14,6 +55,9 @@ export default function register(Component, tagName, propNames, options) {
14
55
PreactElement . prototype . attributeChangedCallback = attributeChangedCallback ;
15
56
PreactElement . prototype . disconnectedCallback = disconnectedCallback ;
16
57
58
+ /**
59
+ * @type {string[] }
60
+ */
17
61
propNames =
18
62
propNames ||
19
63
Component . observedAttributes ||
@@ -62,6 +106,9 @@ function ContextProvider(props) {
62
106
return cloneElement ( children , rest ) ;
63
107
}
64
108
109
+ /**
110
+ * @this {PreactCustomElement}
111
+ */
65
112
function connectedCallback ( ) {
66
113
// Obtain a reference to the previous context by pinging the nearest
67
114
// higher up node that was rendered with Preact. If one Preact component
@@ -84,10 +131,22 @@ function connectedCallback() {
84
131
( this . hasAttribute ( 'hydrate' ) ? hydrate : render ) ( this . _vdom , this . _root ) ;
85
132
}
86
133
134
+ /**
135
+ * Camel-cases a string
136
+ * @param {string } str The string to transform to camelCase
137
+ * @returns camel case version of the string
138
+ */
87
139
function toCamelCase ( str ) {
88
140
return str . replace ( / - ( \w ) / g, ( _ , c ) => ( c ? c . toUpperCase ( ) : '' ) ) ;
89
141
}
90
142
143
+ /**
144
+ * Changed whenver an attribute of the HTML element changed
145
+ * @this {PreactCustomElement}
146
+ * @param {string } name The attribute name
147
+ * @param {unknown } oldValue The old value or undefined
148
+ * @param {unknown } newValue The new value
149
+ */
91
150
function attributeChangedCallback ( name , oldValue , newValue ) {
92
151
if ( ! this . _vdom ) return ;
93
152
// Attributes use `null` as an empty value whereas `undefined` is more
@@ -102,6 +161,9 @@ function attributeChangedCallback(name, oldValue, newValue) {
102
161
render ( this . _vdom , this . _root ) ;
103
162
}
104
163
164
+ /**
165
+ * @this {PreactCustomElement}
166
+ */
105
167
function disconnectedCallback ( ) {
106
168
render ( ( this . _vdom = null ) , this . _root ) ;
107
169
}
0 commit comments