Skip to content

Instantly share code, notes, and snippets.

@rodydavis
Last active February 14, 2025 18:16
Show Gist options
  • Save rodydavis/ec61c56465191e50095dfeff0706e48d to your computer and use it in GitHub Desktop.
Save rodydavis/ec61c56465191e50095dfeff0706e48d to your computer and use it in GitHub Desktop.
import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'package:web/web.dart';
class WebComponent<T extends HTMLElement> {
late T element;
String get extendsType => 'HTMLElement';
void connectedCallback() {}
void disconnectedCallback() {}
void adoptedCallback() {}
void attributeChangedCallback(
String name,
String? oldValue,
String? newValue,
) {}
Iterable<String> get observedAttributes => [];
R getRoot<R extends JSObject>() {
final hasShadow = element.shadowRoot != null;
return (hasShadow ? element.shadowRoot! : element) as R;
}
bool get isCustom {
return element.getAttribute('is') != null;
}
static void define(
String tag,
WebComponent Function() create, {
String? extendsTag,
}) {
final obj = _factory(create);
if (extendsTag != null) {
window.customElements.define(
tag,
obj,
ElementDefinitionOptions(extends_: extendsTag),
);
} else {
window.customElements.define(tag, obj);
}
}
}
@JS('Reflect.construct')
external JSAny _reflectConstruct(
JSObject target,
JSAny args,
JSFunction constructor,
);
final _instances = <HTMLElement, WebComponent>{};
JSFunction _factory(WebComponent Function() create) {
final base = create();
final elemProto = globalContext[base.extendsType] as JSObject;
late JSAny obj;
JSAny constructor() {
final args = <String>[].jsify()!;
final self = _reflectConstruct(elemProto, args, obj as JSFunction);
final el = self as HTMLElement;
_instances.putIfAbsent(el, () => create()..element = el);
return self;
}
obj = constructor.toJS;
obj = obj as JSObject;
final observedAttributes = base.observedAttributes;
obj['prototype'] = elemProto['prototype'];
obj['observedAttributes'] = observedAttributes.toList().jsify()!;
final prototype = obj['prototype'] as JSObject;
prototype['connectedCallback'] = (HTMLElement instance) {
_instances[instance]?.connectedCallback();
}.toJSCaptureThis;
prototype['disconnectedCallback'] = (HTMLElement instance) {
_instances[instance]?.disconnectedCallback();
_instances.remove(instance);
}.toJSCaptureThis;
prototype['adoptedCallback'] = (HTMLElement instance) {
_instances[instance]?.adoptedCallback();
}.toJSCaptureThis;
prototype['attributeChangedCallback'] = (
HTMLElement instance,
String name,
String? oldName,
String? newName,
) {
_instances[instance]?.attributeChangedCallback(name, oldName, newName);
}.toJSCaptureThis;
return obj as JSFunction;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment