Dojo NG is being built in the open, one section at a time — follow along on Heptapod.

API REFERENCE · UTILITIES

@dojo-ng/dojo-element

The base class for every Dojo NG component, plus shared helpers for building your own.

Try it in the playground

@dojo-ng/dojo-element · v0.1.3 · No custom element

npm install @dojo-ng/dojo-element

Extend the base class, emit events with emit(), and register the tag with define().

import DojoElement from "@dojo-ng/dojo-element";
import { html } from "lit";

export class MyGreeting extends DojoElement {
  static properties = { name: {} };

  render() {
    return html`<button @click=${() => this.emit("my-greet", { detail: { name: this.name } })}>
      Hello, ${this.name}
    </button>`;
  }
}
MyGreeting.define("my-greeting");

DojoElement

  • It extends LitElement. Import it as the default export.
  • emit(name, options) dispatches a CustomEvent that bubbles and crosses shadow boundaries (composed) by default.
  • static define(tag) registers the element. Registering the same tag again does nothing. If the versions differ, it logs a warning instead of throwing an error.
  • static dependencies lists child elements to register when the element is created.

Form controls

  • FormControl(DojoElement) is a mixin for form-associated controls.
  • A control inside a disabled <fieldset> or form is disabled too.
  • State is restored after back and forward navigation and after autofill.
  • Validity is mirrored onto the host as data-dj-required, data-dj-valid, data-dj-invalid, data-dj-user-valid, and data-dj-user-invalid. The user- states turn on only after the user has interacted with the control.

Helpers

  • Focus: trapTabKey, collectFocusables, firstFocusable, isFocusable, deepActiveElement, isFocusWithin, and dismissOnFocusOut.
  • lockBodyScroll() stops the page from scrolling behind a modal and returns a function that unlocks it. Nested locks are counted.
  • TokenFlagController reads a true or false flag from a --dj-* custom property. The value 1 means true.
  • baseStyles and reducedMotion are shared styles for component shadow roots.

Examples

Style invalid fields

Form controls mirror their validity onto the host, so page CSS can react to it. Here a hint turns red after the user leaves the field invalid.

<style>
  .field:has(dj-text-input[data-dj-user-invalid]) .hint {
    color: var(--dj-color-danger-600);
  }
</style>
<div class="field">
  <dj-text-input label="Email" type="email" required></dj-text-input>
  <p class="hint">Enter an address like name@example.com.</p>
</div>

Functions

collectFocusables

function collectFocusables(shadowRoot: ShadowRoot, host: HTMLElement): HTMLElement[];

deepActiveElement

function deepActiveElement(): Element | null;

dismissOnFocusOut

function dismissOnFocusOut(host: HTMLElement, onLeave: () => void): () => void;

firstFocusable

function firstFocusable(root: ParentNode): HTMLElement | null;

FormControl

function FormControl<T extends Constructor<DojoElement>>(Base: T): T & Constructor<FormControlMixinInterface>;

isFocusable

function isFocusable(el: Element): boolean;

isFocusWithin

function isFocusWithin(host: Element): boolean;

lockBodyScroll

function lockBodyScroll(): () => void;

trapTabKey

function trapTabKey(event: KeyboardEvent, focusables: HTMLElement[]): void;

Classes

TokenFlagController

class TokenFlagController implements ReactiveController {
  value: boolean;
  constructor(host: ReactiveControllerHost & HTMLElement, property: string);
  hostConnected(): void;
  hostDisconnected(): void;
  refresh(): void;
}

Values

FOCUSABLE_SELECTOR

const FOCUSABLE_SELECTOR = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])";

NATIVE_FOCUSABLE_SELECTOR

const NATIVE_FOCUSABLE_SELECTOR = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex=\"-1\"])";

reducedMotion

const reducedMotion: import("lit").CSSResult;

Types

DojoFormControl

interface DojoFormControl extends DojoElement {
  name: string;
  value: unknown;
  disabled?: boolean;
  defaultValue?: unknown;
  defaultChecked?: boolean;
  form?: string;
  pattern?: string;
  min?: number | string | Date;
  max?: number | string | Date;
  step?: number | string | "any";
  required?: boolean;
  minlength?: number;
  maxlength?: number;
  readonly validity: ValidityState;
  readonly validationMessage: string;
  checkValidity: () => boolean;
  getForm: () => HTMLFormElement | null;
  reportValidity: () => boolean;
  setCustomValidity: (message: string) => void;
}

FormControlMixinInterface

interface FormControlMixinInterface {
  formDisabled: boolean;
  readonly isDisabled: boolean;
  formDisabledCallback(disabled: boolean): void;
  formStateRestoreCallback(state: FormRestoreState, mode: string): void;
  restoreFormState(state: FormRestoreState): void;
  readonly validity: ValidityState | undefined;
  readonly validationMessage: string;
  checkValidity(): boolean;
  reportValidity(): boolean;
}

FormRestoreState

type FormRestoreState = File | string | FormData | null;