diff --git a/apps/lit-override/src/html/markup-light-dom.html b/apps/lit-override/src/html/markup-light-dom.html
index 94b74bb2..559a8620 100644
--- a/apps/lit-override/src/html/markup-light-dom.html
+++ b/apps/lit-override/src/html/markup-light-dom.html
@@ -12,11 +12,11 @@
Markup slotted from the light dom and styles applied by the host app!
-
+
A child component heading from a template in the light dom!
A child component paragraph from a template in the light dom.
-
+
A lit override component heading from a template in the light dom!
A lit override component paragraph from a template in the light dom.
diff --git a/apps/lit-override/src/html/styles-light-dom.html b/apps/lit-override/src/html/styles-light-dom.html
index ba1fe8b3..73792155 100644
--- a/apps/lit-override/src/html/styles-light-dom.html
+++ b/apps/lit-override/src/html/styles-light-dom.html
@@ -25,11 +25,11 @@
Styles slotted from the light dom and markup applied by the host app!
-
+
A child component heading from a Lit template in the host app!
A child component paragraph from a Lit template in the host app.
-
+
A lit override component heading from a Lit template in the host app!
A lit override component paragraph from a Lit template in the host app.
diff --git a/apps/lit-override/src/ts/child-component.ts b/apps/lit-override/src/ts/child-component.ts
index f01bb8c3..2ae342d8 100644
--- a/apps/lit-override/src/ts/child-component.ts
+++ b/apps/lit-override/src/ts/child-component.ts
@@ -1,8 +1,8 @@
import { LitElement, css, html } from 'lit';
-import { property } from 'lit/decorators.js';
import { EmitConnectedCallback } from '@waldronmatt/lit-override/src/mixins/index.js';
import { templateContentWithFallback } from '@waldronmatt/lit-override/src/directives/index.js';
import { AdoptedStyleSheetsConverter } from '@waldronmatt/lit-override/src/controllers/index.js';
+import { queryTemplateById } from '@waldronmatt/lit-override/src/decorators/index.js';
export class ChildComponent extends EmitConnectedCallback(LitElement) {
static styles = css`
@@ -17,12 +17,12 @@ export class ChildComponent extends EmitConnectedCallback(LitElement) {
}
`;
- @property({ reflect: true, type: String })
- templateId!: string;
+ @queryTemplateById()
+ templateId!: HTMLTemplateElement | null;
connectedCallback() {
super.connectedCallback();
- new AdoptedStyleSheetsConverter(this, { id: this.templateId });
+ new AdoptedStyleSheetsConverter(this, { templateEl: this.templateId });
}
markup() {
@@ -34,7 +34,7 @@ export class ChildComponent extends EmitConnectedCallback(LitElement) {
}
protected render() {
- return html`${templateContentWithFallback({ fallback: this.markup(), id: this.templateId })}`;
+ return html`${templateContentWithFallback({ fallback: this.markup(), templateEl: this.templateId })}`;
}
}
diff --git a/apps/lit-override/src/ts/lazy-load.ts b/apps/lit-override/src/ts/lazy-load.ts
index bf02e444..9791d2f3 100644
--- a/apps/lit-override/src/ts/lazy-load.ts
+++ b/apps/lit-override/src/ts/lazy-load.ts
@@ -5,11 +5,11 @@ document.getElementById('load-component')?.addEventListener('click', () => {
const container = document.getElementById('component-container');
const component = document.createElement('host-app');
component.innerHTML = `
-
+
A heading from a template in the light dom!
A paragraph from a template in the light dom.
-
+
A heading from a template in the light dom!
A paragraph from a template in the light dom.
diff --git a/packages/lit-override/README.md b/packages/lit-override/README.md
index 64ca4261..40dcd407 100644
--- a/packages/lit-override/README.md
+++ b/packages/lit-override/README.md
@@ -83,18 +83,27 @@ import { LitElement, html } from 'lit';
import { property } from 'lit/decorators.js';
import { templateContentWithFallback } from '@waldronmatt/lit-override/directives/template-content-with-fallback.js';
import { AdoptedStyleSheetsConverter } from '@waldronmatt/lit-override/controllers/adopted-stylesheets-converter.js';
+import { queryTemplateById } from '@waldronmatt/lit-override/decorators/query-template-by-id.js';
export class ChildComponent extends LitElement {
- @property({ reflect: true, type: String })
- templateId!: string;
+ @queryTemplateById({ fallback: true })
+ templateId!: HTMLTemplateElement | null;
connectedCallback() {
super.connectedCallback();
- new AdoptedStyleSheetsConverter(this, { id: this.templateId });
+ new AdoptedStyleSheetsConverter(this, {
+ clearStyes: true,
+ templateEl: this.templateId,
+ });
}
protected render() {
- return html`${templateContentWithFallback({ fallback: html`Default markup
`, id: this.templateId })}`;
+ return html`
+ ${templateContentWithFallback({
+ fallback: html`Default markup
`,
+ templateEl: this.templateId,
+ })}
+ `;
}
}
```
diff --git a/packages/lit-override/custom-elements.json b/packages/lit-override/custom-elements.json
index 5a4a32eb..48b7cc55 100644
--- a/packages/lit-override/custom-elements.json
+++ b/packages/lit-override/custom-elements.json
@@ -23,6 +23,14 @@
"package": "./controllers/index.js"
}
},
+ {
+ "kind": "js",
+ "name": "*",
+ "declaration": {
+ "name": "*",
+ "package": "./decorators/index.js"
+ }
+ },
{
"kind": "js",
"name": "*",
@@ -101,10 +109,8 @@
"kind": "field",
"name": "templateId",
"type": {
- "text": "string"
- },
- "attribute": "templateId",
- "reflects": true
+ "text": "HTMLTemplateElement | null"
+ }
},
{
"kind": "field",
@@ -149,14 +155,19 @@
"name": "connected-callback"
}
],
- "attributes": [
+ "mixins": [
{
- "name": "templateId",
- "type": {
- "text": "string"
- },
- "fieldName": "templateId"
- },
+ "name": "EmitConnectedCallback",
+ "module": "/src/mixins/emit-connected-callback.js"
+ }
+ ],
+ "superclass": {
+ "name": "LitElement",
+ "package": "lit"
+ },
+ "tagName": "lit-override",
+ "customElement": true,
+ "attributes": [
{
"name": "emitConnectedCallback",
"type": {
@@ -177,19 +188,7 @@
"module": "src/mixins/emit-connected-callback.ts"
}
}
- ],
- "mixins": [
- {
- "name": "EmitConnectedCallback",
- "module": "/src/mixins/emit-connected-callback.js"
- }
- ],
- "superclass": {
- "name": "LitElement",
- "package": "lit"
- },
- "tagName": "lit-override",
- "customElement": true
+ ]
}
],
"exports": [
@@ -253,28 +252,27 @@
},
{
"kind": "field",
- "name": "_template",
+ "name": "clearStyles",
"type": {
- "text": "HTMLTemplateElement | null"
+ "text": "AdoptedStyleSheetsConverterParams['clearStyles']"
},
- "privacy": "private",
- "default": "null"
+ "default": "clearStyles"
},
{
"kind": "field",
- "name": "clearStyles",
+ "name": "templateEl",
"type": {
- "text": "boolean"
+ "text": "AdoptedStyleSheetsConverterParams['templateEl']"
},
- "default": "clearStyles"
+ "default": "templateEl"
},
{
"kind": "field",
- "name": "id",
+ "name": "_shadowRoot",
"type": {
- "text": "string"
+ "text": "ShadowRoot"
},
- "default": "id"
+ "default": "(this.host as LitElement).renderRoot"
},
{
"kind": "method",
@@ -286,22 +284,12 @@
},
{
"kind": "method",
- "name": "getTemplateElement",
- "privacy": "private",
- "return": {
- "type": {
- "text": "HTMLTemplateElement | null"
- }
- }
- },
- {
- "kind": "method",
- "name": "removeComponentStyleTag",
+ "name": "updateStylesheet",
"privacy": "private"
},
{
"kind": "method",
- "name": "handleAdoptedStyleSheets",
+ "name": "setAdoptedStyleSheets",
"privacy": "private",
"parameters": [
{
@@ -314,7 +302,7 @@
},
{
"kind": "method",
- "name": "updateStylesheet",
+ "name": "removeComponentStyleTag",
"privacy": "private"
}
]
@@ -348,7 +336,7 @@
},
{
"kind": "javascript-module",
- "path": "src/directives/index.ts",
+ "path": "src/decorators/index.ts",
"declarations": [],
"exports": [
{
@@ -356,48 +344,69 @@
"name": "*",
"declaration": {
"name": "*",
- "package": "./template-content-with-fallback.js"
+ "package": "./query-template-by-id.js"
}
}
]
},
{
"kind": "javascript-module",
- "path": "src/directives/template-content-with-fallback.ts",
+ "path": "src/decorators/query-template-by-id.ts",
"declarations": [
{
- "kind": "class",
- "description": "",
- "name": "TemplateContentWithFallbackDirective",
- "members": [
+ "kind": "function",
+ "name": "queryTemplateById",
+ "parameters": [
{
- "kind": "field",
- "name": "_template",
+ "name": "{ fallback = false }",
+ "default": "{}",
"type": {
- "text": "HTMLTemplateElement | null"
- },
- "privacy": "private",
- "default": "null"
+ "text": "{ fallback?: boolean }"
+ }
},
{
- "kind": "method",
- "name": "getTemplateElement",
- "privacy": "private",
- "return": {
- "type": {
- "text": "HTMLTemplateElement | null"
- }
- },
- "parameters": [
- {
- "name": "id",
- "type": {
- "text": "string"
- }
- }
- ]
+ "description": "gets a template element if an id is not provided (not cached). Defaults to `false`.",
+ "name": "fallback"
}
],
+ "description": "queryTemplateById\n\nGets a template element by id that is provided to the `templateId` property.\nWill cache the template element on successful query."
+ }
+ ],
+ "exports": [
+ {
+ "kind": "js",
+ "name": "queryTemplateById",
+ "declaration": {
+ "name": "queryTemplateById",
+ "module": "src/decorators/query-template-by-id.ts"
+ }
+ }
+ ]
+ },
+ {
+ "kind": "javascript-module",
+ "path": "src/directives/index.ts",
+ "declarations": [],
+ "exports": [
+ {
+ "kind": "js",
+ "name": "*",
+ "declaration": {
+ "name": "*",
+ "package": "./template-content-with-fallback.js"
+ }
+ }
+ ]
+ },
+ {
+ "kind": "javascript-module",
+ "path": "src/directives/template-content-with-fallback.ts",
+ "declarations": [
+ {
+ "kind": "class",
+ "description": "",
+ "name": "TemplateContentWithFallbackDirective",
+ "members": [],
"superclass": {
"name": "Directive",
"package": "lit/directive.js"
@@ -413,8 +422,8 @@
"name": "fallback"
},
{
- "description": "unique identifier that points to the id of a `template` element. Defaults to empty string.",
- "name": "id"
+ "description": "a `template` element. Defaults to null.",
+ "name": "templateEl"
}
]
}
@@ -583,7 +592,7 @@
"type": {
"text": "TemplateResult"
},
- "description": "TemplateResult\n\n**Note**: Only static markdown is supported."
+ "description": "TemplateResult"
}
],
"description": "Applies the given template to the `shadowRoot` of elements."
diff --git a/packages/lit-override/custom-elements.md b/packages/lit-override/custom-elements.md
index 56e1c224..d0fcd06c 100644
--- a/packages/lit-override/custom-elements.md
+++ b/packages/lit-override/custom-elements.md
@@ -6,6 +6,7 @@
| ---- | ---- | ----------- | ------ | ---------------------- |
| `js` | `*` | \* | | ./components/index.js |
| `js` | `*` | \* | | ./controllers/index.js |
+| `js` | `*` | \* | | ./decorators/index.js |
| `js` | `*` | \* | | ./directives/index.js |
| `js` | `*` | \* | | ./mixins/index.js |
| `js` | `*` | \* | | ./utils/index.js |
@@ -30,12 +31,12 @@
#### Fields
-| Name | Privacy | Type | Default | Description | Inherited From |
-| ----------------------- | ------- | ---------- | ------- | -------------------------------------------------------------------------------------------- | --------------------- |
-| `templateId` | | `string` | | | |
-| `emitConnectedCallback` | | `boolean` | `false` | Set prop to use \`connected-callback\` event. Defaults to \`false\`. | EmitConnectedCallback |
-| `onConnectedCallback` | | `function` | | A callback function called when connected to the DOM. | EmitConnectedCallback |
-| `id` | | `string` | | unique identifier that points to the id of a \`template\` element. Defaults to empty string. | |
+| Name | Privacy | Type | Default | Description | Inherited From |
+| ----------------------- | ------- | ----------------------------- | ------- | -------------------------------------------------------------------------------------------- | --------------------- |
+| `templateId` | | `HTMLTemplateElement \| null` | | | |
+| `emitConnectedCallback` | | `boolean` | `false` | Set prop to use \`connected-callback\` event. Defaults to \`false\`. | EmitConnectedCallback |
+| `onConnectedCallback` | | `function` | | A callback function called when connected to the DOM. | EmitConnectedCallback |
+| `id` | | `string` | | unique identifier that points to the id of a \`template\` element. Defaults to empty string. | |
#### Events
@@ -47,7 +48,6 @@
| Name | Field | Inherited From |
| ----------------------- | --------------------- | --------------------- |
-| `templateId` | templateId | |
| `emitConnectedCallback` | emitConnectedCallback | EmitConnectedCallback |
| `onConnectedCallback` | onConnectedCallback | EmitConnectedCallback |
@@ -81,23 +81,22 @@
#### Fields
-| Name | Privacy | Type | Default | Description | Inherited From |
-| ------------- | ------- | ----------------------------- | ------------- | ----------- | -------------- |
-| `host` | | `ReactiveControllerHost` | `host` | | |
-| `_template` | private | `HTMLTemplateElement \| null` | `null` | | |
-| `clearStyles` | | `boolean` | `clearStyles` | | |
-| `id` | | `string` | `id` | | |
+| Name | Privacy | Type | Default | Description | Inherited From |
+| ------------- | ------- | -------------------------------------------------- | -------------------------------------- | ----------- | -------------- |
+| `host` | | `ReactiveControllerHost` | `host` | | |
+| `clearStyles` | | `AdoptedStyleSheetsConverterParams['clearStyles']` | `clearStyles` | | |
+| `templateEl` | | `AdoptedStyleSheetsConverterParams['templateEl']` | `templateEl` | | |
+| `_shadowRoot` | | `ShadowRoot` | `(this.host as LitElement).renderRoot` | | |
#### Methods
-| Name | Privacy | Description | Parameters | Return | Inherited From |
-| -------------------------- | ------- | ----------- | -------------------------------- | ----------------------------- | -------------- |
-| `hostConnected` | | | | | |
-| `hostUpdated` | | | | | |
-| `getTemplateElement` | private | | | `HTMLTemplateElement \| null` | |
-| `removeComponentStyleTag` | private | | | | |
-| `handleAdoptedStyleSheets` | private | | `styleElement: HTMLStyleElement` | | |
-| `updateStylesheet` | private | | | | |
+| Name | Privacy | Description | Parameters | Return | Inherited From |
+| ------------------------- | ------- | ----------- | -------------------------------- | ------ | -------------- |
+| `hostConnected` | | | | | |
+| `hostUpdated` | | | | | |
+| `updateStylesheet` | private | | | | |
+| `setAdoptedStyleSheets` | private | | `styleElement: HTMLStyleElement` | | |
+| `removeComponentStyleTag` | private | | | | |
@@ -115,6 +114,30 @@
| ---- | ---- | ----------- | ------ | ---------------------------------- |
| `js` | `*` | \* | | ./adopted-stylesheets-converter.js |
+## `src/decorators/index.ts`:
+
+### Exports
+
+| Kind | Name | Declaration | Module | Package |
+| ---- | ---- | ----------- | ------ | ------------------------- |
+| `js` | `*` | \* | | ./query-template-by-id.js |
+
+## `src/decorators/query-template-by-id.ts`:
+
+### Functions
+
+| Name | Description | Parameters | Return |
+| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | ------ |
+| `queryTemplateById` | queryTemplateById
Gets a template element by id that is provided to the \`templateId\` property.
Will cache the template element on successful query. | `{ fallback = false }: { fallback?: boolean }, fallback` | |
+
+
+
+### Exports
+
+| Kind | Name | Declaration | Module | Package |
+| ---- | ------------------- | ----------------- | -------------------------------------- | ------- |
+| `js` | `queryTemplateById` | queryTemplateById | src/decorators/query-template-by-id.ts | |
+
## `src/directives/index.ts`:
### Exports
@@ -127,18 +150,6 @@
### class: `TemplateContentWithFallbackDirective`
-#### Fields
-
-| Name | Privacy | Type | Default | Description | Inherited From |
-| ----------- | ------- | ----------------------------- | ------- | ----------- | -------------- |
-| `_template` | private | `HTMLTemplateElement \| null` | `null` | | |
-
-#### Methods
-
-| Name | Privacy | Description | Parameters | Return | Inherited From |
-| -------------------- | ------- | ----------- | ------------ | ----------------------------- | -------------- |
-| `getTemplateElement` | private | | `id: string` | `HTMLTemplateElement \| null` | |
-
### Exports
diff --git a/packages/lit-override/src/components/lit-override-component.spec.ts b/packages/lit-override/src/components/lit-override-component.spec.ts
index 75e20cbb..ead8f39c 100644
--- a/packages/lit-override/src/components/lit-override-component.spec.ts
+++ b/packages/lit-override/src/components/lit-override-component.spec.ts
@@ -19,21 +19,14 @@ describe('lit-override', () => {
testContainer.remove();
});
- it('renders default slot content when no template is provided', async () => {
- const el = await fixture(html`Default Content
`);
- await expect(el).to.be.accessible();
- expect(el.innerHTML).to.contain('Default Content
');
- expect(el.shadowRoot!.innerHTML).to.contain('');
- });
-
it('renders content from the template when provided', async () => {
const template = document.createElement('template');
- template.id = 'overrideTemplate';
+ template.id = 'markupTemplate';
template.innerHTML = '';
testContainer.appendChild(template);
const el = await fixture(html`
-
+
A paragraph coming from a custom template
`);
@@ -43,9 +36,11 @@ describe('lit-override', () => {
expect(el.shadowRoot!.innerHTML).to.contain('');
});
- it('handles invalid template IDs gracefully', async () => {
- const el = await fixture(html``);
- expect(el.shadowRoot?.innerHTML).to.contain('');
+ it('renders default slot content when no template is provided', async () => {
+ const el = await fixture(html`Default Content
`);
+ await expect(el).to.be.accessible();
+ expect(el.innerHTML).to.contain('Default Content
');
+ expect(el.shadowRoot!.innerHTML).to.contain('');
});
it('applies style tag styles as adoptedStyleSheets', async () => {
@@ -54,37 +49,61 @@ describe('lit-override', () => {
template.innerHTML = '';
testContainer.appendChild(template);
- const el = await fixture(html``);
+ const el = await fixture(html``);
+ await expect(el).to.be.accessible();
const adoptedStyles = el.shadowRoot?.adoptedStyleSheets;
expect(adoptedStyles?.length).to.equal(1);
expect(el.innerHTML).to.not.contain('');
expect(el.shadowRoot?.querySelector('style')).to.not.exist;
});
- it('calls onConnectedCallback appropriately', async () => {
- const callbackSpy = sinon.spy();
+ it('logs an error on invalid template ID', async () => {
+ const consoleErrorSpy = sinon.spy(console, 'error');
+ await fixture(html``);
+ expect(consoleErrorSpy).to.have.been.calledWith('Template id nonexistentTemplateId could not be found');
+ });
+
+ it('gets a template element as a fallback when templateId is not provided', async () => {
+ const template = document.createElement('template');
+ template.id = 'aTemplate';
+ template.innerHTML = '';
+ testContainer.appendChild(template);
+
+ const el = await fixture(html``);
+
+ expect(el.shadowRoot!.innerHTML).to.contain('');
+ });
+
+ it('calls onConnectedCallback on connectedCallback', async () => {
const el = await fixture(html``);
+
+ const callbackSpy = sinon.spy();
// @ts-expect-error - this is already defined on the component
el.onConnectedCallback = callbackSpy;
el.connectedCallback();
await aTimeout(50);
+
expect(callbackSpy).to.have.been.calledOnce;
});
it('does not emit connected-callback event when emitConnectedCallback is false', async () => {
const el = await fixture(html``);
+
const eventSpy = sinon.spy();
el.addEventListener('connected-callback', eventSpy);
setTimeout(() => el.connectedCallback());
await aTimeout(50);
+
expect(eventSpy).not.to.have.been.called;
});
it('emits connected-callback event when emitConnectedCallback is true', async () => {
const el = await fixture(html``);
+
setTimeout(() => el.connectedCallback());
const ev = await oneEvent(el, 'connected-callback');
+
expect(ev).to.exist;
});
});
diff --git a/packages/lit-override/src/components/lit-override-component.ts b/packages/lit-override/src/components/lit-override-component.ts
index 5cb7f090..783703ee 100644
--- a/packages/lit-override/src/components/lit-override-component.ts
+++ b/packages/lit-override/src/components/lit-override-component.ts
@@ -2,7 +2,7 @@ import { LitElement, html } from 'lit';
import { EmitConnectedCallback } from '../mixins/emit-connected-callback.js';
import { templateContentWithFallback } from '../directives/template-content-with-fallback.js';
import { AdoptedStyleSheetsConverter } from '../controllers/adopted-stylesheets-converter.js';
-import { property } from 'lit/decorators.js';
+import { queryTemplateById } from '../decorators/query-template-by-id.js';
/**
* LitOverride - ``
@@ -16,15 +16,15 @@ import { property } from 'lit/decorators.js';
* @slot `` is rendered as fallback if `` element is not found
*/
export class LitOverride extends EmitConnectedCallback(LitElement) {
- @property({ reflect: true, type: String })
- templateId!: string;
+ @queryTemplateById({ fallback: true })
+ templateId!: HTMLTemplateElement | null;
connectedCallback() {
super.connectedCallback();
- new AdoptedStyleSheetsConverter(this, { id: this.templateId });
+ new AdoptedStyleSheetsConverter(this, { templateEl: this.templateId });
}
protected render() {
- return html`${templateContentWithFallback({ id: this.templateId })}`;
+ return html`${templateContentWithFallback({ templateEl: this.templateId })}`;
}
}
diff --git a/packages/lit-override/src/controllers/adopted-stylesheets-converter.ts b/packages/lit-override/src/controllers/adopted-stylesheets-converter.ts
index 68a9b391..238a21f7 100644
--- a/packages/lit-override/src/controllers/adopted-stylesheets-converter.ts
+++ b/packages/lit-override/src/controllers/adopted-stylesheets-converter.ts
@@ -2,7 +2,7 @@ import { LitElement, ReactiveController, ReactiveControllerHost } from 'lit';
export interface AdoptedStyleSheetsConverterParams {
clearStyles?: boolean;
- id?: string;
+ templateEl?: HTMLTemplateElement | null;
}
/**
@@ -12,26 +12,30 @@ export interface AdoptedStyleSheetsConverterParams {
* and adds it to the component's `adoptedStyleSheets`.
*
* @param clearStyles replace or preserve original styles. Defaults to `false`.
- * @param id unique identifier that points to the id of a `template` element. Defaults to empty string.
+ * @param templateEl a `template` element. Defaults to null.
*/
export class AdoptedStyleSheetsConverter implements ReactiveController {
host: ReactiveControllerHost;
- private _template: HTMLTemplateElement | null = null;
+ clearStyles: AdoptedStyleSheetsConverterParams['clearStyles'];
- clearStyles: boolean;
+ templateEl: AdoptedStyleSheetsConverterParams['templateEl'];
- id: string;
+ _shadowRoot: ShadowRoot;
- constructor(host: ReactiveControllerHost, { clearStyles = false, id = '' }: AdoptedStyleSheetsConverterParams = {}) {
+ constructor(
+ host: ReactiveControllerHost,
+ { clearStyles = false, templateEl = null }: AdoptedStyleSheetsConverterParams = {},
+ ) {
this.host = host;
this.clearStyles = clearStyles;
- this.id = id;
+ this.templateEl = templateEl;
+ this._shadowRoot = (this.host as LitElement).renderRoot as ShadowRoot;
+
this.host.addController(this);
}
hostConnected() {
- this._template = this.getTemplateElement();
this.updateStylesheet();
}
@@ -41,43 +45,35 @@ export class AdoptedStyleSheetsConverter implements ReactiveController {
this.removeComponentStyleTag();
}
- private getTemplateElement(): HTMLTemplateElement | null {
- return document.querySelector(`template${this.id ? '#' + this.id : ''}`) || document.querySelector('template');
- }
+ private updateStylesheet() {
+ if (!this.templateEl) {
+ return;
+ }
- private removeComponentStyleTag() {
- const shadowRoot = (this.host as LitElement).renderRoot;
- const styleElement = shadowRoot.querySelector('style');
+ const styleElement = this.templateEl.content.querySelector('style');
if (!styleElement) {
return;
}
- shadowRoot.removeChild(styleElement);
+ this.setAdoptedStyleSheets(styleElement);
}
- private handleAdoptedStyleSheets(styleElement: HTMLStyleElement) {
- const litElement = this.host as LitElement;
- const shadowRoot = litElement.renderRoot as ShadowRoot;
+ private setAdoptedStyleSheets(styleElement: HTMLStyleElement) {
const styleContent = styleElement.textContent || '';
-
const newStyleSheet = new CSSStyleSheet();
newStyleSheet.replaceSync(styleContent);
- shadowRoot.adoptedStyleSheets = this.clearStyles
+ this._shadowRoot.adoptedStyleSheets = this.clearStyles
? [newStyleSheet]
- : [...shadowRoot.adoptedStyleSheets, newStyleSheet];
+ : [...this._shadowRoot.adoptedStyleSheets, newStyleSheet];
}
- private updateStylesheet() {
- if (!this._template) {
- return;
- }
-
- const styleElement = this._template.content.querySelector('style');
+ private removeComponentStyleTag() {
+ const styleElement = this._shadowRoot.querySelector('style');
if (!styleElement) {
return;
}
- this.handleAdoptedStyleSheets(styleElement);
+ this._shadowRoot.removeChild(styleElement);
}
}
diff --git a/packages/lit-override/src/decorators/index.ts b/packages/lit-override/src/decorators/index.ts
new file mode 100644
index 00000000..23c0de5d
--- /dev/null
+++ b/packages/lit-override/src/decorators/index.ts
@@ -0,0 +1 @@
+export * from './query-template-by-id.js';
diff --git a/packages/lit-override/src/decorators/query-template-by-id.ts b/packages/lit-override/src/decorators/query-template-by-id.ts
new file mode 100644
index 00000000..5e7ccb3a
--- /dev/null
+++ b/packages/lit-override/src/decorators/query-template-by-id.ts
@@ -0,0 +1,60 @@
+import { property } from 'lit/decorators.js';
+import { ReactiveElement } from 'lit';
+
+export interface ExtendedElement extends ReactiveElement {
+ [key: symbol]: string | null;
+ _templateCache?: Record;
+}
+
+/**
+ * queryTemplateById
+ *
+ * Gets a template element by id that is provided to the `templateId` property.
+ * Will cache the template element on successful query.
+ *
+ * @param fallback gets a template element if an id is not provided (not cached). Defaults to `false`.
+ */
+export const queryTemplateById = ({ fallback = false }: { fallback?: boolean } = {}) => {
+ return (proto: T, propName: string) => {
+ const internalKey = Symbol(`_${String(propName)}`);
+
+ property({ reflect: true, type: String })(proto, propName);
+
+ Object.defineProperty(proto, 'templateId', {
+ get(this: ExtendedElement): HTMLTemplateElement | null {
+ const id = this[internalKey];
+
+ if (!this._templateCache) {
+ this._templateCache = {};
+ }
+
+ if (id && this._templateCache[id]) {
+ return this._templateCache[id];
+ }
+
+ if (typeof id === 'string' && id) {
+ const templateElement = document.querySelector(`template#${id}`) as HTMLTemplateElement | null;
+
+ if (!templateElement) {
+ console.error(`Template id ${id} could not be found`);
+ return null;
+ }
+
+ this._templateCache[id] = templateElement;
+ return templateElement;
+ }
+
+ return fallback ? (document.querySelector('template') as HTMLTemplateElement | null) : null;
+ },
+ set(this: ExtendedElement, value: string | null) {
+ this[internalKey] = value;
+
+ if (this._templateCache && value) {
+ delete this._templateCache[value];
+ }
+ },
+ enumerable: true,
+ configurable: true,
+ });
+ };
+};
diff --git a/packages/lit-override/src/directives/template-content-with-fallback.ts b/packages/lit-override/src/directives/template-content-with-fallback.ts
index 27cd3597..71c18cfa 100644
--- a/packages/lit-override/src/directives/template-content-with-fallback.ts
+++ b/packages/lit-override/src/directives/template-content-with-fallback.ts
@@ -1,16 +1,14 @@
import { Directive, PartInfo, PartType, directive } from 'lit/directive.js';
import { templateContent } from 'lit/directives/template-content.js';
-import { ChildPart, html, TemplateResult } from 'lit';
+import { html, TemplateResult } from 'lit';
import { when } from 'lit/directives/when.js';
export interface TemplateContentWithFallbackParams {
fallback?: TemplateResult;
- id?: string;
+ templateEl?: HTMLTemplateElement | null;
}
class TemplateContentWithFallbackDirective extends Directive {
- private _template: HTMLTemplateElement | null = null;
-
constructor(partInfo: PartInfo) {
super(partInfo);
@@ -19,21 +17,10 @@ class TemplateContentWithFallbackDirective extends Directive {
}
}
- private getTemplateElement(id: string): HTMLTemplateElement | null {
- return document.querySelector(`template${id ? '#' + id : ''}`) || document.querySelector('template');
- }
-
- // @ts-expect-error - ignore typing error
- override update(part: ChildPart, [params]: [TemplateContentWithFallbackParams?] = []) {
- const { fallback = html``, id = '' } = params || {};
- this._template = this.getTemplateElement(id);
- return this.render(fallback);
- }
-
- render(fallback: TemplateResult) {
+ render({ fallback = html``, templateEl = null }: TemplateContentWithFallbackParams = {}) {
return when(
- this._template,
- () => templateContent(this._template!),
+ templateEl,
+ () => templateContent(templateEl!),
() => fallback,
);
}
@@ -47,8 +34,7 @@ class TemplateContentWithFallbackDirective extends Directive {
*
*
* @param fallback renders markup if a `template` element is not found. Defaults to ``.
- * @param id unique identifier that points to the id of a `template` element. Defaults to empty string.
+ * @param templateEl a `template` element. Defaults to null.
*/
-// @ts-expect-error - ignore typing error
export const templateContentWithFallback = directive(TemplateContentWithFallbackDirective);
export type { TemplateContentWithFallbackDirective };
diff --git a/packages/lit-override/src/index.ts b/packages/lit-override/src/index.ts
index 88bfbb44..7f4ae390 100644
--- a/packages/lit-override/src/index.ts
+++ b/packages/lit-override/src/index.ts
@@ -1,5 +1,6 @@
export * from './components/index.js';
export * from './controllers/index.js';
+export * from './decorators/index.js';
export * from './directives/index.js';
export * from './mixins/index.js';
export * from './utils/index.js';
diff --git a/packages/lit-override/src/mixins/emit-connected-callback.ts b/packages/lit-override/src/mixins/emit-connected-callback.ts
index 6dda3c49..169c30bd 100644
--- a/packages/lit-override/src/mixins/emit-connected-callback.ts
+++ b/packages/lit-override/src/mixins/emit-connected-callback.ts
@@ -47,7 +47,7 @@ export const EmitConnectedCallback = >(superCl
bubbles: true,
composed: true,
cancelable: false,
- detail: { name: this.constructor.name, isConnected: this.isConnected },
+ detail: { ...childInfo },
});
this.dispatchEvent(event);
diff --git a/packages/lit-override/src/utils/markup.ts b/packages/lit-override/src/utils/markup.ts
index 24951686..96be050c 100644
--- a/packages/lit-override/src/utils/markup.ts
+++ b/packages/lit-override/src/utils/markup.ts
@@ -6,8 +6,6 @@ import { render } from 'lit-html';
*
* @param elements iterable of elements to apply markup to
* @param template TemplateResult
- *
- * **Note**: Only static markdown is supported.
*/
export const injectTemplate = (elements: NodeListOf | Array, template: TemplateResult): void => {
if (!elements || !elements.length || !template) {