CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-lit-element

A simple base class for creating fast, lightweight web components

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

decorators.mddocs/

Decorators

TypeScript decorators for enhanced development experience with declarative property and element configuration. Decorators provide a clean, declarative syntax for configuring reactive properties and DOM queries.

Capabilities

Custom Element Decorator

Registers a custom element with the browser's custom element registry.

/**
 * Class decorator to register a custom element
 * @param tagName The tag name for the custom element (must contain a hyphen)
 * @returns Class decorator function
 */
function customElement(tagName: string): ClassDecorator;

Usage Examples:

import { LitElement, html, customElement } from "lit-element";

@customElement("my-button")
class MyButton extends LitElement {
  render() {
    return html`
      <button>
        <slot></slot>
      </button>
    `;
  }
}

@customElement("user-card")
class UserCard extends LitElement {
  render() {
    return html`
      <div class="card">
        <slot name="avatar"></slot>
        <slot name="name"></slot>
        <slot name="bio"></slot>
      </div>
    `;
  }
}

// Elements are automatically registered and can be used in HTML
// <my-button>Click me</my-button>
// <user-card>
//   <img slot="avatar" src="..." />
//   <h2 slot="name">John Doe</h2>
//   <p slot="bio">Software developer</p>
// </user-card>

Property Decorator

Declares a reactive property that triggers updates when changed.

/**
 * Property decorator for reactive properties
 * @param options Configuration options for the property
 * @returns Property decorator function
 */
function property(options?: PropertyDeclaration): PropertyDecorator;

Usage Examples:

import { LitElement, html, customElement, property } from "lit-element";

@customElement("my-element")
class MyElement extends LitElement {
  @property({ type: String })
  name = "World";

  @property({ type: Number })
  count = 0;

  @property({ type: Boolean })
  disabled = false;

  @property({ type: Array })
  items: string[] = [];

  @property({ type: Object })
  config: any = {};

  render() {
    return html`
      <div>
        <h1>Hello, ${this.name}!</h1>
        <p>Count: ${this.count}</p>
        <button ?disabled=${this.disabled}>
          ${this.disabled ? 'Disabled' : 'Enabled'}
        </button>
        <ul>
          ${this.items.map(item => html`<li>${item}</li>`)}
        </ul>
      </div>
    `;
  }
}

State Decorator

Declares internal reactive state that doesn't reflect to attributes.

/**
 * Property decorator for internal reactive state
 * @param options Configuration options for the state property
 * @returns Property decorator function
 */
function state(options?: StateDeclaration): PropertyDecorator;

Usage Examples:

import { LitElement, html, customElement, property, state } from "lit-element";

@customElement("toggle-element")
class ToggleElement extends LitElement {
  @property({ type: String })
  label = "Toggle";

  @state()
  private _expanded = false;

  @state()
  private _loading = false;

  @state()
  private _data: any[] = [];

  private async _toggle() {
    this._loading = true;
    
    if (!this._expanded) {
      // Simulate loading data
      await new Promise(resolve => setTimeout(resolve, 1000));
      this._data = ['Item 1', 'Item 2', 'Item 3'];
    }
    
    this._expanded = !this._expanded;
    this._loading = false;
  }

  render() {
    return html`
      <div>
        <button @click=${this._toggle} ?disabled=${this._loading}>
          ${this._loading ? 'Loading...' : this.label}
        </button>
        
        ${this._expanded ? html`
          <div class="content">
            <ul>
              ${this._data.map(item => html`<li>${item}</li>`)}
            </ul>
          </div>
        ` : ''}
      </div>
    `;
  }
}

Query Decorator

Queries the render root for an element matching a CSS selector.

/**
 * Property decorator for DOM queries
 * @param selector CSS selector to query for
 * @param cache Whether to cache the query result
 * @returns Property decorator function
 */
function query(selector: string, cache?: boolean): PropertyDecorator;

Usage Examples:

import { LitElement, html, customElement, query } from "lit-element";

@customElement("form-element")
class FormElement extends LitElement {
  @query('#username')
  usernameInput!: HTMLInputElement;

  @query('button[type="submit"]')
  submitButton!: HTMLButtonElement;

  @query('.error-message')
  errorMessage?: HTMLElement;

  @query('form', true) // cached query
  form!: HTMLFormElement;

  private _handleSubmit(e: Event) {
    e.preventDefault();
    
    const username = this.usernameInput.value;
    if (!username.trim()) {
      if (this.errorMessage) {
        this.errorMessage.textContent = 'Username is required';
        this.errorMessage.style.display = 'block';
      }
      return;
    }

    // Hide error message
    if (this.errorMessage) {
      this.errorMessage.style.display = 'none';
    }

    // Disable submit button
    this.submitButton.disabled = true;
    
    console.log('Submitting:', username);
  }

  render() {
    return html`
      <form @submit=${this._handleSubmit}>
        <div>
          <label for="username">Username:</label>
          <input id="username" type="text" required />
        </div>
        <div class="error-message" style="display: none; color: red;"></div>
        <button type="submit">Submit</button>
      </form>
    `;
  }
}

Query All Decorator

Queries the render root for all elements matching a CSS selector.

/**
 * Property decorator for DOM queries returning NodeList
 * @param selector CSS selector to query for
 * @returns Property decorator function
 */
function queryAll(selector: string): PropertyDecorator;

Usage Examples:

import { LitElement, html, customElement, queryAll } from "lit-element";

@customElement("list-element")
class ListElement extends LitElement {
  @queryAll('.item')
  items!: NodeListOf<HTMLElement>;

  @queryAll('input[type="checkbox"]')
  checkboxes!: NodeListOf<HTMLInputElement>;

  @queryAll('.draggable')
  draggableElements!: NodeListOf<HTMLElement>;

  private _selectAll() {
    this.checkboxes.forEach(checkbox => {
      checkbox.checked = true;
    });
  }

  private _clearAll() {
    this.checkboxes.forEach(checkbox => {
      checkbox.checked = false;
    });
  }

  private _highlightAll() {
    this.items.forEach(item => {
      item.classList.add('highlighted');
    });
  }

  render() {
    return html`
      <div>
        <div class="controls">
          <button @click=${this._selectAll}>Select All</button>
          <button @click=${this._clearAll}>Clear All</button>
          <button @click=${this._highlightAll}>Highlight All</button>
        </div>
        
        <div class="item">
          <input type="checkbox" /> Item 1
        </div>
        <div class="item">
          <input type="checkbox" /> Item 2
        </div>
        <div class="item">
          <input type="checkbox" /> Item 3
        </div>
      </div>
    `;
  }
}

Query Async Decorator

Performs an async query that resolves when the element is found.

/**
 * Property decorator for async DOM queries
 * @param selector CSS selector to query for
 * @returns Property decorator function returning Promise<Element>
 */
function queryAsync(selector: string): PropertyDecorator;

Usage Examples:

import { LitElement, html, customElement, queryAsync } from "lit-element";

@customElement("async-element")
class AsyncElement extends LitElement {
  @queryAsync('#dynamic-content')
  dynamicContent!: Promise<HTMLElement>;

  @queryAsync('.lazy-loaded')
  lazyElement!: Promise<HTMLElement>;

  private async _handleDynamicContent() {
    try {
      const element = await this.dynamicContent;
      element.textContent = 'Content loaded!';
      element.style.color = 'green';
    } catch (error) {
      console.error('Failed to find dynamic content:', error);
    }
  }

  private async _loadContent() {
    // Trigger re-render to add the dynamic element
    this.requestUpdate();
    
    // Wait for the element to be available
    await this._handleDynamicContent();
  }

  render() {
    return html`
      <div>
        <button @click=${this._loadContent}>Load Content</button>
        <div id="dynamic-content">Loading...</div>
      </div>
    `;
  }
}

Query Assigned Elements Decorator

Queries for elements assigned to a slot.

/**
 * Property decorator for querying slot assigned elements
 * @param options Query options including slot name and selector
 * @returns Property decorator function
 */
function queryAssignedElements(options?: QueryAssignedElementsOptions): PropertyDecorator;

interface QueryAssignedElementsOptions {
  slot?: string;
  selector?: string;
  flatten?: boolean;
}

Usage Examples:

import { LitElement, html, customElement, queryAssignedElements } from "lit-element";

@customElement("tab-container")
class TabContainer extends LitElement {
  @queryAssignedElements({ slot: 'tab' })
  tabs!: HTMLElement[];

  @queryAssignedElements({ slot: 'panel' })
  panels!: HTMLElement[];

  @queryAssignedElements({ selector: '.special' })
  specialElements!: HTMLElement[];

  private _activeTab = 0;

  private _selectTab(index: number) {
    this._activeTab = index;
    
    // Update tab states
    this.tabs.forEach((tab, i) => {
      tab.classList.toggle('active', i === index);
    });
    
    // Update panel visibility
    this.panels.forEach((panel, i) => {
      panel.style.display = i === index ? 'block' : 'none';
    });
  }

  render() {
    return html`
      <div class="tab-header">
        <slot name="tab" @slotchange=${this._handleTabsChange}></slot>
      </div>
      <div class="tab-content">
        <slot name="panel" @slotchange=${this._handlePanelsChange}></slot>
      </div>
      <div class="special-content">
        <slot @slotchange=${this._handleSpecialChange}></slot>
      </div>
    `;
  }

  private _handleTabsChange() {
    this.tabs.forEach((tab, index) => {
      tab.addEventListener('click', () => this._selectTab(index));
    });
  }

  private _handlePanelsChange() {
    this._selectTab(this._activeTab);
  }

  private _handleSpecialChange() {
    console.log('Special elements:', this.specialElements);
  }
}

// Usage:
// <tab-container>
//   <button slot="tab">Tab 1</button>
//   <button slot="tab">Tab 2</button>
//   <div slot="panel">Panel 1 content</div>
//   <div slot="panel">Panel 2 content</div>
//   <div class="special">Special content</div>
// </tab-container>

Query Assigned Nodes Decorator

Queries for nodes (including text nodes) assigned to a slot.

/**
 * Property decorator for querying slot assigned nodes
 * @param options Query options including slot name and flatten
 * @returns Property decorator function
 */
function queryAssignedNodes(options?: QueryAssignedNodesOptions): PropertyDecorator;

interface QueryAssignedNodesOptions {
  slot?: string;
  flatten?: boolean;
}

Usage Examples:

import { LitElement, html, customElement, queryAssignedNodes } from "lit-element";

@customElement("content-analyzer")
class ContentAnalyzer extends LitElement {
  @queryAssignedNodes()
  allNodes!: Node[];

  @queryAssignedNodes({ slot: 'header' })
  headerNodes!: Node[];

  @queryAssignedNodes({ flatten: true })
  flattenedNodes!: Node[];

  private _analyzeContent() {
    console.log('All nodes:', this.allNodes);
    console.log('Text nodes:', this.allNodes.filter(node => node.nodeType === Node.TEXT_NODE));
    console.log('Element nodes:', this.allNodes.filter(node => node.nodeType === Node.ELEMENT_NODE));
    console.log('Header nodes:', this.headerNodes);
  }

  render() {
    return html`
      <div>
        <button @click=${this._analyzeContent}>Analyze Content</button>
        <div class="header-section">
          <slot name="header" @slotchange=${this._handleSlotChange}></slot>
        </div>
        <div class="main-content">
          <slot @slotchange=${this._handleSlotChange}></slot>
        </div>
      </div>
    `;
  }

  private _handleSlotChange() {
    this._analyzeContent();
  }
}

Event Options Decorator

Configures event listener options for methods.

/**
 * Method decorator for configuring event listener options
 * @param options Event listener options
 * @returns Method decorator function
 */
function eventOptions(options: AddEventListenerOptions): MethodDecorator;

interface AddEventListenerOptions {
  capture?: boolean;
  once?: boolean;
  passive?: boolean;
  signal?: AbortSignal;
}

Usage Examples:

import { LitElement, html, customElement, eventOptions } from "lit-element";

@customElement("event-element")
class EventElement extends LitElement {
  @eventOptions({ passive: true })
  private _handleScroll(e: Event) {
    // Passive scroll handling for better performance
    console.log('Scroll event (passive)');
  }

  @eventOptions({ once: true })
  private _handleFirstClick(e: Event) {
    // This handler will only run once
    console.log('First click only');
  }

  @eventOptions({ capture: true })
  private _handleCaptureClick(e: Event) {
    // Handle in capture phase
    console.log('Capture phase click');
  }

  render() {
    return html`
      <div 
        @scroll=${this._handleScroll}
        @click=${this._handleFirstClick}
        @click=${this._handleCaptureClick}
        style="height: 200px; overflow-y: scroll; border: 1px solid #ccc;"
      >
        <div style="height: 500px; padding: 16px;">
          <p>Scrollable content with event handling</p>
          <button>Click me (once only + capture)</button>
        </div>
      </div>
    `;
  }
}

docs

core-element.md

css-styling.md

decorators.md

directives.md

index.md

polyfill-support.md

property-system.md

template-system.md

tile.json