2.1.0 • Published 2 months ago

@blockquote-web-components/blockquote-controller-xstate v2.1.0

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

BlockquoteControllerXstate

Lit

Connect XState machines with Lit

The BlockquoteControllerXstate is a Lit Reactive Controller that is specifically designed to facilitate a integration with XState. This controller provides the capability to subscribe to an XState actor. It also provides a callback function to handle the state changes.

Demo

Open in StackBlitz

Open in Stately.ai

Usage

counterMachine.js

import { createMachine, assign } from 'xstate';

const states = {
  enabled: 'enabled',
  disabled: 'disabled',
};

const increment = {
  counter: ({ context }) => context.counter + 1,
  event: ({ event }) => event,
};
const decrement = {
  counter: ({ context }) => context.counter - 1,
  event: ({ event }) => event,
};

const isNotMax = ({ context }) => context.counter < 10;
const isNotMin = ({ context }) => context.counter > 0;

export const counterMachine = createMachine(
  {
    id: 'counter',
    context: { counter: 0, event: undefined },
    initial: 'enabled',
    states: {
      enabled: {
        on: {
          INC: {
            actions: {
              type: 'increment',
            },
            guard: {
              type: 'isNotMax',
            },
          },
          DEC: {
            actions: {
              type: 'decrement',
            },
            guard: {
              type: 'isNotMin',
            },
          },
          TOGGLE: {
            target: states.disabled,
          },
        },
      },
      disabled: {
        on: {
          TOGGLE: {
            target: states.enabled,
          },
        },
      },
    },
  },
  {
    actions: {
      increment: assign(increment),
      decrement: assign(decrement),
    },
    guards: {
      isNotMax,
      isNotMin,
    },
  },
);

new BlockquoteControllerXstate(this, {machine, options?, callback?})

Usage

import { html, LitElement } from 'lit';
import { BlockquoteControllerXstate } from '@blockquote-web-components/blockquote-controller-xstate';
import { counterMachine } from './counterMachine.js';
import { styles } from './styles/xstate-counter-styles.css.js';

export class XstateCounter extends LitElement {
  static properties = {
    _xstate: {
      type: Object,
      state: true,
    },
  };

  static styles = [styles];

  constructor() {
    super();
    this._xstate = {};
    this._inspectEvents = this._inspectEvents.bind(this);
    this._callbackCounterController = this._callbackCounterController.bind(this);

    this.counterController = new BlockquoteControllerXstate(this, {
      machine: counterMachine,
      options: {
        inspect: this._inspectEvents,
      },
      callback: this._callbackCounterController,
    });
  }

  _callbackCounterController(snapshot) {
    this._xstate = snapshot;
  }

  _inspectEvents(inspEvent) {
    if (inspEvent.type === '@xstate.snapshot' && inspEvent.event.type === 'xstate.stop') {
      this._xstate = {};
    }
  }

  updated(props) {
    super.updated && super.updated(props);
    if (props.has('_xstate')) {
      const { context, value } = this._xstate;
      const counterEvent = new CustomEvent('counterchange', {
        bubbles: true,
        detail: { ...context, value },
      });
      this.dispatchEvent(counterEvent);
    }
  }

  get #disabled() {
    return this.counterController.snapshot.matches('disabled');
  }

  render() {
    return html`
      <slot></slot>
      <div aria-disabled="${this.#disabled}">
        <span>
          <button
            ?disabled="${this.#disabled}"
            data-counter="increment"
            \@click=${() => this.counterController.send({ type: 'INC' })}
          >
            Increment
          </button>
          <button
            ?disabled="${this.#disabled}"
            data-counter="decrement"
            \@click=${() => this.counterController.send({ type: 'DEC' })}
          >
            Decrement
          </button>
        </span>
        <p>${this.counterController.snapshot.context.counter}</p>
      </div>
      <div>
        <button \@click=${() => this.counterController.send({ type: 'TOGGLE' })}>
          ${this.#disabled ? 'Enabled counter' : 'Disabled counter'}
        </button>
      </div>
    `;
  }
}

src/BlockquoteControllerXstate.js:

class: UseMachine

Fields
NamePrivacyTypeDefaultDescriptionInherited From
actor
snapshot
machinemachine
optionsoptions
callbackcallback
currentSnapshot
Methods
NamePrivacyDescriptionParametersReturnInherited From
sendev: EventFrom<typeof this.machine>
unsubscribe
onNextsnapshot: SnapshotFrom<typeof this.machine>
startService
stopService
hostConnected
hostDisconnected

Exports

KindNameDeclarationModulePackage
jsBlockquoteControllerXstateUseMachinesrc/BlockquoteControllerXstate.js

src/index.js:

Exports

KindNameDeclarationModulePackage
jsBlockquoteControllerXstateBlockquoteControllerXstate./BlockquoteControllerXstate.js
2.1.0

2 months ago

2.0.0

2 months ago

1.1.6

4 months ago

1.1.5

4 months ago

1.1.4

4 months ago

1.1.3

4 months ago

1.1.2

4 months ago

1.1.1

5 months ago

1.1.0

5 months ago

1.0.1

6 months ago

1.0.0

6 months ago