Skip to content

Commit d81f5cb

Browse files
committed
refactor: Switch to es6 classes
1 parent dc9c56f commit d81f5cb

2 files changed

Lines changed: 56 additions & 62 deletions

File tree

src/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,6 @@ export default function register<P = {}, S = {}>(
5252
tagName?: string,
5353
propNames?: (keyof P)[],
5454
options?: Options
55-
): HTMLElement;
55+
): typeof HTMLElement & {
56+
new (): HTMLElement;
57+
};

src/index.js

Lines changed: 53 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)