@@ -7,48 +7,67 @@ import { h, cloneElement, render, hydrate } from 'preact';
77/**
88 * @type {import('./index.d.ts').default }
99 */
10- export default function register ( Component , tagName , propNames , options ) {
11- function PreactElement ( ) {
12- const inst = /** @type {PreactCustomElement } */ (
13- Reflect . construct ( HTMLElement , [ ] , PreactElement )
14- ) ;
15- inst . _vdomComponent = Component ;
16-
17- if ( options && options . shadow ) {
18- inst . _root = inst . attachShadow ( {
19- mode : options . mode || 'open' ,
20- serializable : options . serializable ?? false ,
21- } ) ;
22-
23- if ( options . adoptedStyleSheets ) {
24- inst . _root . adoptedStyleSheets = options . adoptedStyleSheets ;
10+ export default function register (
11+ Component ,
12+ tagName ,
13+ observedAttributes ,
14+ options
15+ ) {
16+ observedAttributes ??= Component . observedAttributes || [ ] ;
17+
18+ class PreactElement extends HTMLElement {
19+ static formAssociated = Component . formAssociated || false ;
20+ static observedAttributes = observedAttributes ;
21+
22+ constructor ( ) {
23+ super ( ) ;
24+
25+ this . _vdomComponent = Component ;
26+ if ( options && options . shadow ) {
27+ this . _root = this . attachShadow ( {
28+ mode : options . mode || 'open' ,
29+ serializable : options . serializable ?? false ,
30+ } ) ;
31+
32+ if ( options . adoptedStyleSheets ) {
33+ this . _root . adoptedStyleSheets = options . adoptedStyleSheets ;
34+ }
35+ } else {
36+ this . _root = this ;
2537 }
26- } else {
27- inst . _root = inst ;
2838 }
2939
30- return inst ;
31- }
32- PreactElement . prototype = Object . create ( HTMLElement . prototype ) ;
33- PreactElement . prototype . constructor = PreactElement ;
34- PreactElement . prototype . connectedCallback = function ( ) {
35- connectedCallback . call ( this , options ) ;
36- } ;
37- PreactElement . prototype . attributeChangedCallback = attributeChangedCallback ;
38- PreactElement . prototype . disconnectedCallback = disconnectedCallback ;
40+ connectedCallback ( ) {
41+ connectedCallback . call ( this , options ) ;
42+ }
3943
40- /**
41- * @type {string[] }
42- */
43- propNames = propNames || Component . observedAttributes || [ ] ;
44- PreactElement . observedAttributes = propNames ;
44+ /**
45+ * Changed whenever an attribute of the HTML element changed
46+ *
47+ * @param {string } name The attribute name
48+ * @param {unknown } oldValue The old value or undefined
49+ * @param {unknown } newValue The new value
50+ */
51+ attributeChangedCallback ( name , oldValue , newValue ) {
52+ if ( ! this . _vdom ) return ;
53+ // Attributes use `null` as an empty value whereas `undefined` is more
54+ // common in pure JS components, especially with default parameters.
55+ // When calling `node.removeAttribute()` we'll receive `null` as the new
56+ // value. See issue #50.
57+ newValue = newValue == null ? undefined : newValue ;
58+ const props = { } ;
59+ props [ name ] = newValue ;
60+ this . _vdom = cloneElement ( this . _vdom , props ) ;
61+ render ( this . _vdom , this . _root ) ;
62+ }
4563
46- if ( Component . formAssociated ) {
47- PreactElement . formAssociated = true ;
64+ disconnectedCallback ( ) {
65+ render ( ( this . _vdom = null ) , this . _root ) ;
66+ }
4867 }
4968
5069 // Keep DOM properties and Preact props in sync
51- propNames . forEach ( ( name ) => {
70+ observedAttributes . forEach ( ( name ) => {
5271 Object . defineProperty ( PreactElement . prototype , name , {
5372 get ( ) {
5473 return this . _vdom ? this . _vdom . props [ name ] : this . _props [ name ] ;
@@ -115,33 +134,6 @@ function connectedCallback(options) {
115134 ( this . hasAttribute ( 'hydrate' ) ? hydrate : render ) ( this . _vdom , this . _root ) ;
116135}
117136
118- /**
119- * Changed whenver an attribute of the HTML element changed
120- * @this {PreactCustomElement}
121- * @param {string } name The attribute name
122- * @param {unknown } oldValue The old value or undefined
123- * @param {unknown } newValue The new value
124- */
125- function attributeChangedCallback ( name , oldValue , newValue ) {
126- if ( ! this . _vdom ) return ;
127- // Attributes use `null` as an empty value whereas `undefined` is more
128- // common in pure JS components, especially with default parameters.
129- // When calling `node.removeAttribute()` we'll receive `null` as the new
130- // value. See issue #50.
131- newValue = newValue == null ? undefined : newValue ;
132- const props = { } ;
133- props [ name ] = newValue ;
134- this . _vdom = cloneElement ( this . _vdom , props ) ;
135- render ( this . _vdom , this . _root ) ;
136- }
137-
138- /**
139- * @this {PreactCustomElement}
140- */
141- function disconnectedCallback ( ) {
142- render ( ( this . _vdom = null ) , this . _root ) ;
143- }
144-
145137/**
146138 * Pass an event listener to each `<slot>` that "forwards" the current
147139 * context value to the rendered child. The child will trigger a custom
0 commit comments