forked from lit/lit
/
lit-element-renderer.ts
114 lines (102 loc) · 3.62 KB
/
lit-element-renderer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
import {ElementRenderer} from './element-renderer.js';
import {LitElement, CSSResult, ReactiveElement} from 'lit';
import {_$LE} from 'lit-element/private-ssr-support.js';
import {
ariaMixinEnum,
HYDRATE_INTERNALS_ATTR_PREFIX,
} from '@lit-labs/ssr-dom-shim';
import {renderValue} from './render-value.js';
import type {RenderInfo} from './render-value.js';
import type {RenderResult} from './render-result.js';
export type Constructor<T> = {new (): T};
const {attributeToProperty, changedProperties} = _$LE;
/**
* ElementRenderer implementation for LitElements
*/
export class LitElementRenderer extends ElementRenderer {
override element: LitElement;
static override matchesClass(ctor: typeof HTMLElement) {
// This property needs to remain unminified.
return (ctor as unknown as typeof LitElement)['_$litElement$'];
}
constructor(tagName: string) {
super(tagName);
this.element = new (customElements.get(this.tagName)!)() as LitElement;
// Reflect internals AOM attributes back to the DOM prior to hydration to
// ensure search bots can accurately parse element semantics prior to
// hydration. This is called whenever an instance of ElementInternals is
// created on an element to wire up the getters/setters for the ARIAMixin
// properties.
const internals = (
this.element as object as {__internals: ElementInternals}
).__internals;
if (internals) {
for (const [key, value] of Object.entries(internals)) {
const ariaAttribute = ariaMixinEnum[key as keyof ARIAMixin];
if (
ariaAttribute &&
value &&
!this.element.hasAttribute(ariaAttribute)
) {
this.element.setAttribute(ariaAttribute, value);
this.element.setAttribute(
`${HYDRATE_INTERNALS_ATTR_PREFIX}${ariaAttribute}`,
value
);
}
}
}
}
override get shadowRootOptions() {
return (
(this.element.constructor as typeof LitElement).shadowRootOptions ??
super.shadowRootOptions
);
}
override connectedCallback() {
// Call LitElement's `willUpdate` method.
// Note, this method is required not to use DOM APIs.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.element as any)?.willUpdate(changedProperties(this.element as any));
// Reflect properties to attributes by calling into ReactiveElement's
// update, which _only_ reflects attributes
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(ReactiveElement.prototype as any).update.call(this.element);
}
override attributeChangedCallback(
name: string,
_old: string | null,
value: string | null
) {
attributeToProperty(this.element as LitElement, name, value);
}
override *renderShadow(renderInfo: RenderInfo): RenderResult {
// Render styles.
const styles = (this.element.constructor as typeof LitElement)
.elementStyles;
if (styles !== undefined && styles.length > 0) {
yield '<style>';
for (const style of styles) {
yield (style as CSSResult).cssText;
}
yield '</style>';
}
// Render template
// eslint-disable-next-line @typescript-eslint/no-explicit-any
yield* renderValue((this.element as any).render(), renderInfo);
}
override *renderLight(renderInfo: RenderInfo): RenderResult {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = (this.element as any)?.renderLight();
if (value) {
yield* renderValue(value, renderInfo);
} else {
yield '';
}
}
}