1.1.8 • Published 8 months ago

@blockquote-web-components/blockquote-controller-context-meta v1.1.8

Weekly downloads
-
License
MIT
Repository
-
Last release
8 months ago

Lit

BlockquoteControllerContextMeta is a Lit Reactive Controller that encapsulates the controllers provided by @lit/context

Features:

  • It enables a component to serve as both a provider and a consumer.
  • It places the consumer after the first update to reduce the chance of a consumer in LightDOM requesting a context that currently lacks a provider.
  • Create a context object using a global symbol (Symbol.for('my-context')).

Demo

Open in StackBlitz

Usage

import { html, LitElement, css } from 'lit';
import { BlockquoteControllerContextMeta } from '@blockquote-web-components/blockquote-controller-context-meta';

export class ProviderEl extends LitElement {
  static styles = css`
    slot {
      display: block;
      border: dashed 4px grey;
      padding: 0px 10px;
    }

    :host {
      display: block;
      border: solid 4px gainsboro;
      padding: 2px;
    }

    h3 {
      margin-top: 0;
    }
  `;

  static properties = {
    data: {},
  };

  constructor() {
    super();
    this._provider = new BlockquoteControllerContextMeta(this, {
      context: 'contextKey', // String used as key in Symbol.for when creating context with createContext(Symbol.for(context))
    });

    this.data = 'Initial';
  }

   // `data` will be provided to any consumer that is in the DOM tree below it.
  set data(value) {
    this._data = value;
    this._provider.setValue(value);
  }

  get data() {
    return this._data;
  }

  render() {
    return html`
      <h3>Provider's data: <code>${this.data}</code></h3>
      <slot></slot>
    `;
  }
}
customElements.define('provider-el', ProviderEl);

export class ConsumerEl extends LitElement {
  _consumer = new BlockquoteControllerContextMeta(this, {
    context: 'contextKey', // String used as key in Symbol.for when creating context with createContext(Symbol.for(context))
    callback: (v) => {
      this.setAttribute('data-callback', v);
    },
  });


  // `providedData` will be populated by the first ancestor element which
  // provides a value for `context`.

  get providedData() {
    return this._consumer.value;
  }

  render() {
    return html`<h3>Consumer data: <code>${this.providedData}</code></h3>
      <hr />
      <slot></slot>`;
  }
}
customElements.define('consumer-el', ConsumerEl);

src/BlockquoteControllerContextMeta.js:

class: ContextMeta

Fields
NamePrivacyTypeDefaultDescriptionInherited From
value
context
initialValueinitialValue
callbackcallback
hosthost
_contextMetaProvidernew ContextProvider(this.host, { context: this.context, initialValue: this.initialValue, })
Methods
NamePrivacyDescriptionParametersReturnInherited From
setValuev, force
hostConnected

Variables

NameDescriptionType
contextMetaSymbolstring

Exports

KindNameDeclarationModulePackage
jscontextMetaSymbolcontextMetaSymbolsrc/BlockquoteControllerContextMeta.js
jsBlockquoteControllerContextMetaContextMetasrc/BlockquoteControllerContextMeta.js

src/index.js:

Exports

KindNameDeclarationModulePackage
jsBlockquoteControllerContextMetaBlockquoteControllerContextMeta./BlockquoteControllerContextMeta.js
jsBaseContextMetaElementBaseContextMetaElement./BaseContextMetaElement.js

Lit

BaseContextMetaElement is inspired by the concept of 'Customized Built-in Elements', focusing on extending native HTML elements like div using Lit's features and the Context API. This approach simplifies the integration of context providers into a standard elements, enhancing functionality while preserving the core behavior of standard elements. All Structural Roles and Their HTML Equivalents

Is it possible to make normal dom elements context providers?

Demo

Open in StackBlitz

Features

It incorporates functionality to handle context consumption and presentation as a standard block element.

  1. The :host CSS rules ensure the element behaves like a block-level element and respects the hidden attribute to hide itself.

    static styles = [
      css`
        :host {
          display: block;
        }
        :host([hidden]),
        [hidden] {
          display: none !important;
        }
      `,
    ];
  2. The initOrGetContextProvider method allows setting up a context consumer on the element. It creates a new BlockquoteControllerContextMeta if one does not already exist.

      initOrGetContextProvider(contextOrOptions = contextMetaSymbol) {
        const ctx =
          contextOrOptions?.context !== undefined
            ? { ...contextOrOptions }
            : { context: contextOrOptions };
    
        if (!this.#controllerBaseContextMeta) {
          this.#controllerBaseContextMeta = new BlockquoteControllerContextMeta(this, ctx);
        }
        return this.#controllerBaseContextMeta;
      }
  3. Set a default role of 'presentation' to ensure it behaves semantically like a non-interactive container.

    connectedCallback() {
      super.connectedCallback?.();
      Object.assign(this, { role: this.role ?? 'presentation' });
    }
  4. The render method includes a , which allows this element to be a flexible container for any child content, mimicking the behavior of a flow element.

    render() {
      return html`<slot></slot>`;
    }

Usage Example: FlownElement

To demonstrate the utility of BaseContextMetaElement, let's create a derived class called FlownElement:

  1. Define Properties: The surface property is declared with reflection, allowing it to influence rendering and context behavior dynamically.

    static properties = {
      surface: { reflect: true },
    };
  2. Set Context on Construction: The constructor calls initOrGetContextProvider with a specific context, enabling the element to participate in the context API from its inception.

    constructor() {
      super();
      this.surface = undefined;
      this.flowController = this.initOrGetContextProvider(consumerContext);
    }
  3. Update Context Values: The willUpdate lifecycle method updates the context value whenever the surface property changes, ensuring context-sensitive operations react appropriately.

    willUpdate(props) {
      super.willUpdate?.(props);
      if (props.has('surface')) {
        this.flowController?.setValue(this.surface);
      }
    }

Usage Example:

Here's how you might use the FlownElement in your HTML:

<flow-element surface="dim">
  <!-- Child content that can consume context from this provider -->
</flow-element>

With this setup, FlownElement behaves like a flow element but provides the additional benefit of context management via Lit's context API, allowing you to seamlessly integrate context-sensitive behavior without altering the parent element hierarchy.

src/BaseContextMetaElement.js:

class: BaseContextMetaElement

Methods
NamePrivacyDescriptionParametersReturnInherited From
initOrGetContextProviderInitializes or returns the existing context meta controller.contextOrOptions: string \| ObjectBlockquoteControllerContextMeta
Fields
NamePrivacyTypeDefaultDescriptionInherited From
#controllerBaseContextMetaprivateBlockquoteControllerContextMeta \| undefinedundefinedStores the context controller instance.

Exports

KindNameDeclarationModulePackage
jsBaseContextMetaElementBaseContextMetaElementsrc/BaseContextMetaElement.js
1.1.8

8 months ago

1.1.7

8 months ago

1.1.6

9 months ago

1.1.5

10 months ago

1.1.4

10 months ago

1.1.3

10 months ago

1.1.1

12 months ago

1.0.2

1 year ago

1.1.0

12 months ago

1.0.1

1 year ago

1.0.4

12 months ago

1.1.2

11 months ago

1.0.3

1 year ago

1.0.0

1 year ago