Skip to content

Instantly share code, notes, and snippets.

@au5ton
Last active March 25, 2023 22:03
Show Gist options
  • Save au5ton/91801be50a6b81c80355558e3bb85c0c to your computer and use it in GitHub Desktop.
Save au5ton/91801be50a6b81c80355558e3bb85c0c to your computer and use it in GitHub Desktop.
LitElement my-context-provider sample
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Vite + Lit + TS</title>
<script type="module" src="/src/index.ts"></script>
</head>
<body>
<my-context-provider>
<my-inner-element>AAA</my-inner-element>
<my-inner-element>BBB</my-inner-element>
</my-context-provider>
</body>
</html>
import { provide, createContext } from '@lit-labs/context'
import { LitElement, html, css } from 'lit'
import { customElement, property } from 'lit/decorators.js'
/**
* Update these when reusing the file
*/
export const CONTEXT_PROVIDER_NAME = 'my-context-provider'
export type CONTEXT_DATA_TYPE = MyContextualData;
export const CONTEXT_DEFAULT_VALUE: CONTEXT_DATA_TYPE = { name: '' };
export interface MyContextualData {
name: string;
}
declare global {
interface HTMLElementTagNameMap {
'my-context-provider': ContextProvider
}
}
///
///
///
/// BOILERPLATE CODE BELOW
/// BOILERPLATE CODE BELOW
/// BOILERPLATE CODE BELOW
///
///
///
// #region Generally, nothing here changes. To reuse this Typescript file, you just copy/paste and change the parts above.
export const context = createContext<CONTEXT_DATA_TYPE>(Symbol(CONTEXT_PROVIDER_NAME));
/**
* Used to send updates from the child to the parent
* @param self When called from inside a LitElement, `self` should be `this`
* @param detail
*/
export function dispatchContextStateChange(self: LitElement, detail: CONTEXT_DATA_TYPE) {
const event = new CustomEvent(`${CONTEXT_PROVIDER_NAME}-state-change`, {
bubbles: true,
composed: true,
detail,
});
self.dispatchEvent(event);
}
@customElement(CONTEXT_PROVIDER_NAME)
export class ContextProvider extends LitElement {
@provide({context: context })
@property({attribute: false})
providedData: CONTEXT_DATA_TYPE = CONTEXT_DEFAULT_VALUE;
connectedCallback() {
super.connectedCallback()
this.shadowRoot?.addEventListener(`${CONTEXT_PROVIDER_NAME}-state-change`, ((event: CustomEvent<CONTEXT_DATA_TYPE>) => {
this.providedData = {
...this.providedData,
...event.detail
}
}) as EventListener)
}
render() {
return html`
<slot></slot>
`
}
static styles = css`
:host {
display: contents
}
`
}
// #endregion
import { consume } from '@lit-labs/context'
import { LitElement, css, html } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import { context, dispatchContextStateChange, MyContextualData } from './my-context-provider'
/**
* An example element.
*
* @slot - This element has a slot
* @csspart button - The button
*/
@customElement('my-inner-element')
export class MyInnerElement extends LitElement {
@consume({ context: context, subscribe: true })
@property({attribute: false})
public myConsumedData?: MyContextualData;
private handleClick() {
console.log('myConsumedData:',this.myConsumedData);
}
private handleChange(event: Event) {
const target = event.target as HTMLInputElement;
console.log(target.value)
dispatchContextStateChange(this, {
...this.myConsumedData,
name: target.value,
})
}
render() {
return html`
<div class="wrap">
<h3><slot></slot></h3>
<p>Hello</p>
<p>
<button @click=${this.handleClick}>print consumed data</button>
</p>
<p>
Consumed data:
</p>
<p>
<input type="text" style="width: 90%" .value=${this.myConsumedData?.name ?? '(undefined)'} @input=${this.handleChange} />
</p>
<p>
<code>${JSON.stringify(this.myConsumedData)}</code>
</p>
</div>
`
}
static styles = css`
:host {
display: block;
}
.wrap {
border: 1px solid blue;
}
`
}
declare global {
interface HTMLElementTagNameMap {
'my-inner-element': MyInnerElement
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment