@openelement/element 0.41.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/package.json +30 -0
- package/src/define-element.d.ts +9 -0
- package/src/define-element.js +43 -0
- package/src/error-boundary.d.ts +54 -0
- package/src/error-boundary.js +125 -0
- package/src/index.d.ts +33 -0
- package/src/index.js +29 -0
- package/src/open-element-hydration.js +120 -0
- package/src/open-element-render.js +91 -0
- package/src/open-element.d.ts +176 -0
- package/src/open-element.js +419 -0
- package/src/types.d.ts +8 -0
- package/src/types.js +4 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal rendering helpers for OpenElement.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from open-element.ts to keep the base class focused on its
|
|
5
|
+
* public lifecycle API. These functions are NOT part of the public package
|
|
6
|
+
* surface and should only be consumed by OpenElement itself.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
* @module @openelement/element/open-element-render
|
|
10
|
+
*/ import { renderToDom } from '@openelement/core';
|
|
11
|
+
import { formatError } from '@openelement/core/errors';
|
|
12
|
+
import { createLogger } from '@openelement/core/logger';
|
|
13
|
+
/**
|
|
14
|
+
* Dispose all reactive effects and declarative event listeners created by
|
|
15
|
+
* previous renders or hydration passes.
|
|
16
|
+
*
|
|
17
|
+
* Returns a fresh, empty event-cleanup array so callers can replace their
|
|
18
|
+
* existing bag and avoid retaining stale closures.
|
|
19
|
+
*/ export function disposeRenderBindings(effectDisposers, eventCleanups) {
|
|
20
|
+
for (const d of effectDisposers)d();
|
|
21
|
+
effectDisposers.clear();
|
|
22
|
+
for (const f of eventCleanups)f();
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* CSR render path for light-DOM components.
|
|
27
|
+
*
|
|
28
|
+
* Clears previous bindings, calls render(), caches the result, and mounts the
|
|
29
|
+
* produced DOM into the element itself.
|
|
30
|
+
*/ export function renderIntoLightDom(instance, effectDisposers, eventCleanups, cache) {
|
|
31
|
+
const newEventCleanups = disposeRenderBindings(effectDisposers, eventCleanups);
|
|
32
|
+
const result = instance.render();
|
|
33
|
+
cache.set(result);
|
|
34
|
+
const self = instance;
|
|
35
|
+
while(self.firstChild){
|
|
36
|
+
self.removeChild(self.firstChild);
|
|
37
|
+
}
|
|
38
|
+
if (result != null) {
|
|
39
|
+
self.appendChild(renderToDom(result, undefined, effectDisposers, instance.signalRegistry));
|
|
40
|
+
}
|
|
41
|
+
return newEventCleanups;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* CSR render path for shadow-DOM components.
|
|
45
|
+
*
|
|
46
|
+
* Clears previous bindings, calls render(), caches the result, and mounts the
|
|
47
|
+
* produced DOM into the shadow root.
|
|
48
|
+
*/ export function renderIntoShadowRoot(instance, effectDisposers, eventCleanups, cache) {
|
|
49
|
+
const root = instance.shadowRoot;
|
|
50
|
+
if (!root) return eventCleanups;
|
|
51
|
+
const newEventCleanups = disposeRenderBindings(effectDisposers, eventCleanups);
|
|
52
|
+
const result = instance.render();
|
|
53
|
+
cache.set(result);
|
|
54
|
+
if (result != null) {
|
|
55
|
+
while(root.firstChild){
|
|
56
|
+
root.removeChild(root.firstChild);
|
|
57
|
+
}
|
|
58
|
+
root.appendChild(renderToDom(result, undefined, effectDisposers, instance.signalRegistry));
|
|
59
|
+
}
|
|
60
|
+
return newEventCleanups;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Render a fallback VNode when the unified render/hydrate path throws.
|
|
64
|
+
*
|
|
65
|
+
* Ensures the render root exists, invokes onRenderError(), disposes any
|
|
66
|
+
* previous bindings, and mounts the fallback content.
|
|
67
|
+
*/ export function renderErrorFallback(instance, error, effectDisposers, eventCleanups, onRenderError) {
|
|
68
|
+
const ctor = instance.constructor;
|
|
69
|
+
const isLightDom = ctor.renderMode === 'light';
|
|
70
|
+
if (!instance.shadowRoot && !isLightDom) {
|
|
71
|
+
instance.createRenderRoot();
|
|
72
|
+
}
|
|
73
|
+
const target = isLightDom ? instance : instance.shadowRoot;
|
|
74
|
+
if (!target) return [];
|
|
75
|
+
let fallback;
|
|
76
|
+
try {
|
|
77
|
+
fallback = onRenderError(error);
|
|
78
|
+
} catch (fallbackError) {
|
|
79
|
+
createLogger('dsd').error(`<${instance.tagName.toLowerCase()}> onRenderError failed: ${formatError(fallbackError)}`);
|
|
80
|
+
fallback = null;
|
|
81
|
+
}
|
|
82
|
+
const newEventCleanups = disposeRenderBindings(effectDisposers, eventCleanups);
|
|
83
|
+
if (fallback != null) {
|
|
84
|
+
while(target.firstChild){
|
|
85
|
+
target.removeChild(target.firstChild);
|
|
86
|
+
}
|
|
87
|
+
target.appendChild(renderToDom(fallback, undefined, effectDisposers, instance.signalRegistry));
|
|
88
|
+
}
|
|
89
|
+
return newEventCleanups;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9lbGVtZW50L3NyYy9vcGVuLWVsZW1lbnQtcmVuZGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogSW50ZXJuYWwgcmVuZGVyaW5nIGhlbHBlcnMgZm9yIE9wZW5FbGVtZW50LlxuICpcbiAqIEV4dHJhY3RlZCBmcm9tIG9wZW4tZWxlbWVudC50cyB0byBrZWVwIHRoZSBiYXNlIGNsYXNzIGZvY3VzZWQgb24gaXRzXG4gKiBwdWJsaWMgbGlmZWN5Y2xlIEFQSS4gVGhlc2UgZnVuY3Rpb25zIGFyZSBOT1QgcGFydCBvZiB0aGUgcHVibGljIHBhY2thZ2VcbiAqIHN1cmZhY2UgYW5kIHNob3VsZCBvbmx5IGJlIGNvbnN1bWVkIGJ5IE9wZW5FbGVtZW50IGl0c2VsZi5cbiAqXG4gKiBAaW50ZXJuYWxcbiAqIEBtb2R1bGUgQG9wZW5lbGVtZW50L2VsZW1lbnQvb3Blbi1lbGVtZW50LXJlbmRlclxuICovXG5cbmltcG9ydCB0eXBlIHsgVk5vZGUgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvdm5vZGUnO1xuaW1wb3J0IHsgcmVuZGVyVG9Eb20gfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZSc7XG5pbXBvcnQgeyBmb3JtYXRFcnJvciB9IGZyb20gJ0BvcGVuZWxlbWVudC9jb3JlL2Vycm9ycyc7XG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAb3BlbmVsZW1lbnQvY29yZS9sb2dnZXInO1xuaW1wb3J0IHR5cGUgeyBPcGVuRWxlbWVudCB9IGZyb20gJy4vb3Blbi1lbGVtZW50LmpzJztcblxuLyoqXG4gKiBNaW5pbWFsIGFjY2Vzc29yIGZvciB0aGUgVk5vZGUgY2FjaGUgc3RvcmVkIG9uIGFuIE9wZW5FbGVtZW50IGluc3RhbmNlLlxuICpcbiAqIFRoZSBjYWNoZSBsaXZlcyBhcyB0cnVlIHByaXZhdGUgZmllbGRzIG9uIHRoZSBjbGFzcywgc28gaGVscGVycyBpbnRlcmFjdFxuICogd2l0aCBpdCB0aHJvdWdoIHRoaXMgc21hbGwgZmFjYWRlIGluc3RlYWQgb2YgdHJ5aW5nIHRvIGFjY2VzcyAjcHJpdmF0ZVxuICogc3RhdGUgZnJvbSBvdXRzaWRlIHRoZSBjbGFzcyBib2R5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFZOb2RlQ2FjaGVBY2Nlc3Mge1xuICAvKiogUmVhZCB0aGUgY3VycmVudCBjYWNoZWQgVk5vZGUgYW5kIHdoZXRoZXIgaXQgaXMgdmFsaWQuICovXG4gIGdldCgpOiB7IHZub2RlOiB1bmtub3duOyB2YWxpZDogYm9vbGVhbiB9O1xuICAvKiogU3RvcmUgYSBWTm9kZSBhbmQgbWFyayB0aGUgY2FjaGUgdmFsaWQuICovXG4gIHNldCh2bm9kZTogdW5rbm93bik6IHZvaWQ7XG59XG5cbi8qKlxuICogRGlzcG9zZSBhbGwgcmVhY3RpdmUgZWZmZWN0cyBhbmQgZGVjbGFyYXRpdmUgZXZlbnQgbGlzdGVuZXJzIGNyZWF0ZWQgYnlcbiAqIHByZXZpb3VzIHJlbmRlcnMgb3IgaHlkcmF0aW9uIHBhc3Nlcy5cbiAqXG4gKiBSZXR1cm5zIGEgZnJlc2gsIGVtcHR5IGV2ZW50LWNsZWFudXAgYXJyYXkgc28gY2FsbGVycyBjYW4gcmVwbGFjZSB0aGVpclxuICogZXhpc3RpbmcgYmFnIGFuZCBhdm9pZCByZXRhaW5pbmcgc3RhbGUgY2xvc3VyZXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkaXNwb3NlUmVuZGVyQmluZGluZ3MoXG4gIGVmZmVjdERpc3Bvc2VyczogU2V0PCgpID0+IHZvaWQ+LFxuICBldmVudENsZWFudXBzOiBBcnJheTwoKSA9PiB2b2lkPixcbik6IEFycmF5PCgpID0+IHZvaWQ+IHtcbiAgZm9yIChjb25zdCBkIG9mIGVmZmVjdERpc3Bvc2VycykgZCgpO1xuICBlZmZlY3REaXNwb3NlcnMuY2xlYXIoKTtcbiAgZm9yIChjb25zdCBmIG9mIGV2ZW50Q2xlYW51cHMpIGYoKTtcbiAgcmV0dXJuIFtdO1xufVxuXG4vKipcbiAqIENTUiByZW5kZXIgcGF0aCBmb3IgbGlnaHQtRE9NIGNvbXBvbmVudHMuXG4gKlxuICogQ2xlYXJzIHByZXZpb3VzIGJpbmRpbmdzLCBjYWxscyByZW5kZXIoKSwgY2FjaGVzIHRoZSByZXN1bHQsIGFuZCBtb3VudHMgdGhlXG4gKiBwcm9kdWNlZCBET00gaW50byB0aGUgZWxlbWVudCBpdHNlbGYuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJJbnRvTGlnaHREb20oXG4gIGluc3RhbmNlOiBPcGVuRWxlbWVudCxcbiAgZWZmZWN0RGlzcG9zZXJzOiBTZXQ8KCkgPT4gdm9pZD4sXG4gIGV2ZW50Q2xlYW51cHM6IEFycmF5PCgpID0+IHZvaWQ+LFxuICBjYWNoZTogVk5vZGVDYWNoZUFjY2Vzcyxcbik6IEFycmF5PCgpID0+IHZvaWQ+IHtcbiAgY29uc3QgbmV3RXZlbnRDbGVhbnVwcyA9IGRpc3Bvc2VSZW5kZXJCaW5kaW5ncyhlZmZlY3REaXNwb3NlcnMsIGV2ZW50Q2xlYW51cHMpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IGluc3RhbmNlLnJlbmRlcigpO1xuICBjYWNoZS5zZXQocmVzdWx0KTtcblxuICBjb25zdCBzZWxmID0gaW5zdGFuY2UgYXMgSFRNTEVsZW1lbnQ7XG4gIHdoaWxlIChzZWxmLmZpcnN0Q2hpbGQpIHtcbiAgICBzZWxmLnJlbW92ZUNoaWxkKHNlbGYuZmlyc3RDaGlsZCk7XG4gIH1cblxuICBpZiAocmVzdWx0ICE9IG51bGwpIHtcbiAgICBzZWxmLmFwcGVuZENoaWxkKHJlbmRlclRvRG9tKHJlc3VsdCwgdW5kZWZpbmVkLCBlZmZlY3REaXNwb3NlcnMsIGluc3RhbmNlLnNpZ25hbFJlZ2lzdHJ5KSk7XG4gIH1cblxuICByZXR1cm4gbmV3RXZlbnRDbGVhbnVwcztcbn1cblxuLyoqXG4gKiBDU1IgcmVuZGVyIHBhdGggZm9yIHNoYWRvdy1ET00gY29tcG9uZW50cy5cbiAqXG4gKiBDbGVhcnMgcHJldmlvdXMgYmluZGluZ3MsIGNhbGxzIHJlbmRlcigpLCBjYWNoZXMgdGhlIHJlc3VsdCwgYW5kIG1vdW50cyB0aGVcbiAqIHByb2R1Y2VkIERPTSBpbnRvIHRoZSBzaGFkb3cgcm9vdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlckludG9TaGFkb3dSb290KFxuICBpbnN0YW5jZTogT3BlbkVsZW1lbnQsXG4gIGVmZmVjdERpc3Bvc2VyczogU2V0PCgpID0+IHZvaWQ+LFxuICBldmVudENsZWFudXBzOiBBcnJheTwoKSA9PiB2b2lkPixcbiAgY2FjaGU6IFZOb2RlQ2FjaGVBY2Nlc3MsXG4pOiBBcnJheTwoKSA9PiB2b2lkPiB7XG4gIGNvbnN0IHJvb3QgPSBpbnN0YW5jZS5zaGFkb3dSb290O1xuICBpZiAoIXJvb3QpIHJldHVybiBldmVudENsZWFudXBzO1xuXG4gIGNvbnN0IG5ld0V2ZW50Q2xlYW51cHMgPSBkaXNwb3NlUmVuZGVyQmluZGluZ3MoZWZmZWN0RGlzcG9zZXJzLCBldmVudENsZWFudXBzKTtcblxuICBjb25zdCByZXN1bHQgPSBpbnN0YW5jZS5yZW5kZXIoKTtcbiAgY2FjaGUuc2V0KHJlc3VsdCk7XG5cbiAgaWYgKHJlc3VsdCAhPSBudWxsKSB7XG4gICAgd2hpbGUgKHJvb3QuZmlyc3RDaGlsZCkge1xuICAgICAgcm9vdC5yZW1vdmVDaGlsZChyb290LmZpcnN0Q2hpbGQpO1xuICAgIH1cbiAgICByb290LmFwcGVuZENoaWxkKHJlbmRlclRvRG9tKHJlc3VsdCwgdW5kZWZpbmVkLCBlZmZlY3REaXNwb3NlcnMsIGluc3RhbmNlLnNpZ25hbFJlZ2lzdHJ5KSk7XG4gIH1cblxuICByZXR1cm4gbmV3RXZlbnRDbGVhbnVwcztcbn1cblxuLyoqXG4gKiBSZW5kZXIgYSBmYWxsYmFjayBWTm9kZSB3aGVuIHRoZSB1bmlmaWVkIHJlbmRlci9oeWRyYXRlIHBhdGggdGhyb3dzLlxuICpcbiAqIEVuc3VyZXMgdGhlIHJlbmRlciByb290IGV4aXN0cywgaW52b2tlcyBvblJlbmRlckVycm9yKCksIGRpc3Bvc2VzIGFueVxuICogcHJldmlvdXMgYmluZGluZ3MsIGFuZCBtb3VudHMgdGhlIGZhbGxiYWNrIGNvbnRlbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJFcnJvckZhbGxiYWNrKFxuICBpbnN0YW5jZTogT3BlbkVsZW1lbnQsXG4gIGVycm9yOiB1bmtub3duLFxuICBlZmZlY3REaXNwb3NlcnM6IFNldDwoKSA9PiB2b2lkPixcbiAgZXZlbnRDbGVhbnVwczogQXJyYXk8KCkgPT4gdm9pZD4sXG4gIG9uUmVuZGVyRXJyb3I6IChlcnJvcjogdW5rbm93bikgPT4gVk5vZGUgfCBudWxsLFxuKTogQXJyYXk8KCkgPT4gdm9pZD4ge1xuICBjb25zdCBjdG9yID0gaW5zdGFuY2UuY29uc3RydWN0b3IgYXMgdHlwZW9mIE9wZW5FbGVtZW50O1xuICBjb25zdCBpc0xpZ2h0RG9tID0gY3Rvci5yZW5kZXJNb2RlID09PSAnbGlnaHQnO1xuXG4gIGlmICghaW5zdGFuY2Uuc2hhZG93Um9vdCAmJiAhaXNMaWdodERvbSkge1xuICAgIGluc3RhbmNlLmNyZWF0ZVJlbmRlclJvb3QoKTtcbiAgfVxuXG4gIGNvbnN0IHRhcmdldCA9IGlzTGlnaHREb20gPyAoaW5zdGFuY2UgYXMgSFRNTEVsZW1lbnQpIDogaW5zdGFuY2Uuc2hhZG93Um9vdDtcbiAgaWYgKCF0YXJnZXQpIHJldHVybiBbXTtcblxuICBsZXQgZmFsbGJhY2s6IFZOb2RlIHwgbnVsbDtcbiAgdHJ5IHtcbiAgICBmYWxsYmFjayA9IG9uUmVuZGVyRXJyb3IoZXJyb3IpO1xuICB9IGNhdGNoIChmYWxsYmFja0Vycm9yKSB7XG4gICAgY3JlYXRlTG9nZ2VyKCdkc2QnKS5lcnJvcihcbiAgICAgIGA8JHtpbnN0YW5jZS50YWdOYW1lLnRvTG93ZXJDYXNlKCl9PiBvblJlbmRlckVycm9yIGZhaWxlZDogJHtmb3JtYXRFcnJvcihmYWxsYmFja0Vycm9yKX1gLFxuICAgICk7XG4gICAgZmFsbGJhY2sgPSBudWxsO1xuICB9XG5cbiAgY29uc3QgbmV3RXZlbnRDbGVhbnVwcyA9IGRpc3Bvc2VSZW5kZXJCaW5kaW5ncyhlZmZlY3REaXNwb3NlcnMsIGV2ZW50Q2xlYW51cHMpO1xuXG4gIGlmIChmYWxsYmFjayAhPSBudWxsKSB7XG4gICAgd2hpbGUgKHRhcmdldC5maXJzdENoaWxkKSB7XG4gICAgICB0YXJnZXQucmVtb3ZlQ2hpbGQodGFyZ2V0LmZpcnN0Q2hpbGQpO1xuICAgIH1cbiAgICB0YXJnZXQuYXBwZW5kQ2hpbGQocmVuZGVyVG9Eb20oZmFsbGJhY2ssIHVuZGVmaW5lZCwgZWZmZWN0RGlzcG9zZXJzLCBpbnN0YW5jZS5zaWduYWxSZWdpc3RyeSkpO1xuICB9XG5cbiAgcmV0dXJuIG5ld0V2ZW50Q2xlYW51cHM7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztDQVNDLEdBR0QsU0FBUyxXQUFXLFFBQVEsb0JBQW9CO0FBQ2hELFNBQVMsV0FBVyxRQUFRLDJCQUEyQjtBQUN2RCxTQUFTLFlBQVksUUFBUSwyQkFBMkI7QUFpQnhEOzs7Ozs7Q0FNQyxHQUNELE9BQU8sU0FBUyxzQkFDZCxlQUFnQyxFQUNoQyxhQUFnQztFQUVoQyxLQUFLLE1BQU0sS0FBSyxnQkFBaUI7RUFDakMsZ0JBQWdCLEtBQUs7RUFDckIsS0FBSyxNQUFNLEtBQUssY0FBZTtFQUMvQixPQUFPLEVBQUU7QUFDWDtBQUVBOzs7OztDQUtDLEdBQ0QsT0FBTyxTQUFTLG1CQUNkLFFBQXFCLEVBQ3JCLGVBQWdDLEVBQ2hDLGFBQWdDLEVBQ2hDLEtBQXVCO0VBRXZCLE1BQU0sbUJBQW1CLHNCQUFzQixpQkFBaUI7RUFFaEUsTUFBTSxTQUFTLFNBQVMsTUFBTTtFQUM5QixNQUFNLEdBQUcsQ0FBQztFQUVWLE1BQU0sT0FBTztFQUNiLE1BQU8sS0FBSyxVQUFVLENBQUU7SUFDdEIsS0FBSyxXQUFXLENBQUMsS0FBSyxVQUFVO0VBQ2xDO0VBRUEsSUFBSSxVQUFVLE1BQU07SUFDbEIsS0FBSyxXQUFXLENBQUMsWUFBWSxRQUFRLFdBQVcsaUJBQWlCLFNBQVMsY0FBYztFQUMxRjtFQUVBLE9BQU87QUFDVDtBQUVBOzs7OztDQUtDLEdBQ0QsT0FBTyxTQUFTLHFCQUNkLFFBQXFCLEVBQ3JCLGVBQWdDLEVBQ2hDLGFBQWdDLEVBQ2hDLEtBQXVCO0VBRXZCLE1BQU0sT0FBTyxTQUFTLFVBQVU7RUFDaEMsSUFBSSxDQUFDLE1BQU0sT0FBTztFQUVsQixNQUFNLG1CQUFtQixzQkFBc0IsaUJBQWlCO0VBRWhFLE1BQU0sU0FBUyxTQUFTLE1BQU07RUFDOUIsTUFBTSxHQUFHLENBQUM7RUFFVixJQUFJLFVBQVUsTUFBTTtJQUNsQixNQUFPLEtBQUssVUFBVSxDQUFFO01BQ3RCLEtBQUssV0FBVyxDQUFDLEtBQUssVUFBVTtJQUNsQztJQUNBLEtBQUssV0FBVyxDQUFDLFlBQVksUUFBUSxXQUFXLGlCQUFpQixTQUFTLGNBQWM7RUFDMUY7RUFFQSxPQUFPO0FBQ1Q7QUFFQTs7Ozs7Q0FLQyxHQUNELE9BQU8sU0FBUyxvQkFDZCxRQUFxQixFQUNyQixLQUFjLEVBQ2QsZUFBZ0MsRUFDaEMsYUFBZ0MsRUFDaEMsYUFBK0M7RUFFL0MsTUFBTSxPQUFPLFNBQVMsV0FBVztFQUNqQyxNQUFNLGFBQWEsS0FBSyxVQUFVLEtBQUs7RUFFdkMsSUFBSSxDQUFDLFNBQVMsVUFBVSxJQUFJLENBQUMsWUFBWTtJQUN2QyxTQUFTLGdCQUFnQjtFQUMzQjtFQUVBLE1BQU0sU0FBUyxhQUFjLFdBQTJCLFNBQVMsVUFBVTtFQUMzRSxJQUFJLENBQUMsUUFBUSxPQUFPLEVBQUU7RUFFdEIsSUFBSTtFQUNKLElBQUk7SUFDRixXQUFXLGNBQWM7RUFDM0IsRUFBRSxPQUFPLGVBQWU7SUFDdEIsYUFBYSxPQUFPLEtBQUssQ0FDdkIsQ0FBQyxDQUFDLEVBQUUsU0FBUyxPQUFPLENBQUMsV0FBVyxHQUFHLHdCQUF3QixFQUFFLFlBQVksZ0JBQWdCO0lBRTNGLFdBQVc7RUFDYjtFQUVBLE1BQU0sbUJBQW1CLHNCQUFzQixpQkFBaUI7RUFFaEUsSUFBSSxZQUFZLE1BQU07SUFDcEIsTUFBTyxPQUFPLFVBQVUsQ0FBRTtNQUN4QixPQUFPLFdBQVcsQ0FBQyxPQUFPLFVBQVU7SUFDdEM7SUFDQSxPQUFPLFdBQVcsQ0FBQyxZQUFZLFVBQVUsV0FBVyxpQkFBaUIsU0FBUyxjQUFjO0VBQzlGO0VBRUEsT0FBTztBQUNUIn0=
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { StyleSheetLike } from '@openelement/protocol/style-sheet';
|
|
2
|
+
import type { VNode } from '@openelement/protocol/vnode';
|
|
3
|
+
import type { Signal } from '@openelement/protocol/signal';
|
|
4
|
+
/**
|
|
5
|
+
* SSR-safe base class for OpenElement.
|
|
6
|
+
*
|
|
7
|
+
* In browser: extends HTMLElement directly.
|
|
8
|
+
* In SSR: assigns a minimal stub to globalThis.HTMLElement so the entire
|
|
9
|
+
* dependency graph shares the same base class.
|
|
10
|
+
*/ declare const _Base: any;
|
|
11
|
+
/**
|
|
12
|
+
* Zero-dependency Custom Element base class for DSD rendering.
|
|
13
|
+
*
|
|
14
|
+
* Provides DSD detection, CSR fallback, event hydration, and style management
|
|
15
|
+
* without any framework dependency (no Lit, no reactive-element).
|
|
16
|
+
*
|
|
17
|
+
* Subclasses MUST override `render(): VNode | null`.
|
|
18
|
+
*/ export declare class OpenElement extends _Base {
|
|
19
|
+
/** Component stylesheets (SSR-safe - StyleSheet delegates to native CSSStyleSheet in browser). */ static styles?: StyleSheetLike | StyleSheetLike[];
|
|
20
|
+
/** Rendering mode. Defaults to shadow/DSD; light DOM is explicit opt-in. */ static renderMode?: 'shadow' | 'light';
|
|
21
|
+
/** v0.25.0: Page head metadata. SSG reads this to inject <title> and <meta> tags. */ static head?: {
|
|
22
|
+
title?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
ogImage?: string;
|
|
25
|
+
};
|
|
26
|
+
/** @internal — use openPipeline({ island: { upgradeStrategy } }) instead */ static client?: {
|
|
27
|
+
strategy?: 'load' | 'idle' | 'visible' | 'only';
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Attributes that trigger attributeChangedCallback.
|
|
31
|
+
* Subclasses override this to declare reactive attributes.
|
|
32
|
+
*/ static observedAttributes?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Whether to delegate focus within the shadow root.
|
|
35
|
+
* When true, attachShadow is called with `delegatesFocus: true`.
|
|
36
|
+
*/ static delegatesFocus?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Whether this element participates in form submission.
|
|
39
|
+
* When true, ElementInternals are attached in connectedCallback.
|
|
40
|
+
*/ static formAssociated?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Signal registry for attribute-based hydration (ADR-0065).
|
|
43
|
+
* Maps signal names → signal objects. Built by registerSignal()
|
|
44
|
+
* in component constructors, consumed during hydration.
|
|
45
|
+
*
|
|
46
|
+
* Exposed internally for render helper modules.
|
|
47
|
+
*/ signalRegistry: Map<string, Signal<unknown>>;
|
|
48
|
+
/**
|
|
49
|
+
* Register a signal for hydration by name.
|
|
50
|
+
* Call in constructor: this.registerSignal('count', this.#count);
|
|
51
|
+
*/ protected registerSignal(name: string, sig: Signal<unknown>): void;
|
|
52
|
+
/** Reactive route parameters. Updates automatically on SPA navigation. */ get params(): Record<string, string>;
|
|
53
|
+
set params(value: Record<string, string>);
|
|
54
|
+
/** ElementInternals for form-associated custom elements */ protected _internals?: ElementInternals;
|
|
55
|
+
/**
|
|
56
|
+
* Create or reuse the shadow root.
|
|
57
|
+
*
|
|
58
|
+
* DSD detection: if `this.shadowRoot` already exists and has nodes,
|
|
59
|
+
* the browser pre-populated it from a <template shadowrootmode> tag.
|
|
60
|
+
* In that case we mark `_dsdHydrated = true` and return the existing root.
|
|
61
|
+
*
|
|
62
|
+
* CSR fallback: if no shadow root exists, we call `attachShadow()`. If an
|
|
63
|
+
* empty shadow root already exists, we reuse it and let connectedCallback()
|
|
64
|
+
* populate it from render().
|
|
65
|
+
*
|
|
66
|
+
* @returns The existing or newly created ShadowRoot.
|
|
67
|
+
*/ createRenderRoot(): ShadowRoot | this;
|
|
68
|
+
private _applyStyles: any;
|
|
69
|
+
/**
|
|
70
|
+
* Lifecycle: called when the element is connected to the DOM.
|
|
71
|
+
*
|
|
72
|
+
* DSD path (_dsdHydrated = true):
|
|
73
|
+
* - Calls _hydrateEvents() to bind declarative events on existing DOM.
|
|
74
|
+
*
|
|
75
|
+
* CSR path (_dsdHydrated = false):
|
|
76
|
+
* - Calls createRenderRoot() if no shadow root exists.
|
|
77
|
+
* - Renders this.render() through the VNode DOM renderer.
|
|
78
|
+
*
|
|
79
|
+
* If formAssociated is true, ElementInternals are attached.
|
|
80
|
+
*/ connectedCallback(): void;
|
|
81
|
+
private _renderOrHydrate: any;
|
|
82
|
+
private _hydrateSignals: any;
|
|
83
|
+
private _hydrateExistingDom: any;
|
|
84
|
+
/**
|
|
85
|
+
* v0.23.0: Hook called after DSD hydration completes.
|
|
86
|
+
*
|
|
87
|
+
* Subclasses override this instead of relying on fragile
|
|
88
|
+
* `super.connectedCallback()` call order. At this point the
|
|
89
|
+
* shadow DOM is populated from DSD and declarative events
|
|
90
|
+
* (@click, @keydown) are bound.
|
|
91
|
+
*
|
|
92
|
+
* No-op by default.
|
|
93
|
+
*/ protected onDsdHydrated(): void;
|
|
94
|
+
/**
|
|
95
|
+
* v0.23.0: Hook called after CSR first render completes.
|
|
96
|
+
*
|
|
97
|
+
* Subclasses override this for post-render initialization
|
|
98
|
+
* that depends on the shadow DOM being populated. At this
|
|
99
|
+
* point render() has been called and declarative events
|
|
100
|
+
* are bound.
|
|
101
|
+
*
|
|
102
|
+
* No-op by default.
|
|
103
|
+
*/ protected onCsrRendered(): void;
|
|
104
|
+
/**
|
|
105
|
+
* v0.40.0: Client-side activation hook.
|
|
106
|
+
*
|
|
107
|
+
* Called once after the element is connected, the shadow root
|
|
108
|
+
* is ready, and any DSD hydration or CSR rendering has completed.
|
|
109
|
+
* This is the right place for framework hydration (Preact, React,
|
|
110
|
+
* Vue, Lit) to take over the shadow DOM.
|
|
111
|
+
*
|
|
112
|
+
* Default implementation is a no-op. Subclasses override this to
|
|
113
|
+
* hydrate or render framework components into the shadow root.
|
|
114
|
+
*/ protected clientActivate(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Hook called when the unified client render/hydrate path throws.
|
|
117
|
+
* Subclasses may return a VNode fallback.
|
|
118
|
+
*/ protected onRenderError(error: unknown): VNode | null;
|
|
119
|
+
private _renderErrorFallback: any;
|
|
120
|
+
/**
|
|
121
|
+
* Lifecycle: called when the element is disconnected from the DOM.
|
|
122
|
+
* Aborts all hydration event listeners for cleanup.
|
|
123
|
+
*/ disconnectedCallback(): void;
|
|
124
|
+
/**
|
|
125
|
+
* Lifecycle: called when an observed attribute changes.
|
|
126
|
+
*
|
|
127
|
+
* Base implementation is a no-op. Subclasses override this to react
|
|
128
|
+
* to attribute changes, typically by calling `this.render()` to update
|
|
129
|
+
* the shadow DOM.
|
|
130
|
+
*
|
|
131
|
+
* @param name - Attribute name (lowercase).
|
|
132
|
+
* @param oldValue - Previous value, or null if the attribute was not set.
|
|
133
|
+
* @param newValue - New value, or null if the attribute was removed.
|
|
134
|
+
*/ attributeChangedCallback(_name: string, _oldValue: string | null, _newValue: string | null): void;
|
|
135
|
+
/**
|
|
136
|
+
* Re-render the shadow DOM from `render()` and re-bind declarative events.
|
|
137
|
+
*
|
|
138
|
+
* OpenElement intentionally does not include a reactive scheduler. Components
|
|
139
|
+
* with local state can call this method after state changes instead of
|
|
140
|
+
* duplicating renderToDom() and event hydration.
|
|
141
|
+
*/ update(): void;
|
|
142
|
+
/**
|
|
143
|
+
* ReactiveController-compatible update hook.
|
|
144
|
+
*
|
|
145
|
+
* Async state controllers call this method when state changes. Keeping this
|
|
146
|
+
* tiny alias lets OpenElement host controllers without inheriting Lit or a
|
|
147
|
+
* scheduler.
|
|
148
|
+
*/ requestUpdate(): void;
|
|
149
|
+
private _renderIntoLightDom: any;
|
|
150
|
+
private _renderIntoShadowRoot: any;
|
|
151
|
+
/**
|
|
152
|
+
* Read locale from JS property (set by SSR injectProps) first,
|
|
153
|
+
* then HTML attribute, then fallback to provided default.
|
|
154
|
+
*
|
|
155
|
+
* SSR injectProps() sets camelCase JS properties (e.g. this.locale = 'en')
|
|
156
|
+
* but getAttribute() only reads HTML attributes, which remain null.
|
|
157
|
+
* This method resolves the mismatch by checking JS property first.
|
|
158
|
+
*
|
|
159
|
+
* @param fallback - Default value when neither source has a value. Defaults to 'en'.
|
|
160
|
+
*/ protected _getLocale(fallback?: string): string;
|
|
161
|
+
/**
|
|
162
|
+
* Return Shadow DOM content as a VNode.
|
|
163
|
+
*
|
|
164
|
+
* Subclasses MUST override this method. During SSR, rendered content is
|
|
165
|
+
* wrapped in a <template shadowrootmode="open"> tag. During CSR, VNode values
|
|
166
|
+
* are rendered via renderToDom() with event binding and signal tracking.
|
|
167
|
+
*
|
|
168
|
+
* @returns VNode for the shadow DOM content, or null for empty content.
|
|
169
|
+
*/ render(): VNode | null;
|
|
170
|
+
}
|
|
171
|
+
/** OpenElement constructor with framework-convention static properties. */ export interface OpenElementComponentConstructor extends CustomElementConstructor {
|
|
172
|
+
styles?: StyleSheetLike | StyleSheetLike[];
|
|
173
|
+
tagName?: string;
|
|
174
|
+
renderMode?: 'shadow' | 'light';
|
|
175
|
+
observedAttributes?: string[];
|
|
176
|
+
}
|