text: import { html, css } from '@lion/core';
  import { LionLitElement } from '@lion/core/src/LionLitElement.js';
  import { ObserverMixin } from '@lion/core/src/ObserverMixin.js';
  
  /**
   * `LionSteps` is a controller for a multi step system.
   *
   * @customElement
   */
  export class LionSteps extends ObserverMixin(LionLitElement) {
    static get properties() {
      /**
       * Fired when a transition between steps happens.
       *
       * @event transition
       */
  
      return {
        /**
         * Storage for data gathered across different steps.
         * Data is passed into each step condition function as a first argument.
         */
        data: {
          type: Object,
        },
        /**
         * Number of the current entered step.
         */
        current: {
          type: Number,
        },
      };
    }
  
    static get asyncObservers() {
      return {
        _onCurrentChanged: ['current'],
      };
    }
  
    constructor() {
      super();
      this.data = {};
      this._internalCurrentSync = true; // necessary for preventing side effects on initialization
      this.current = 0;
    }
  
    static get styles() {
      return [
        css`
          :host {
            display: block;
          }
        `,
      ];
    }
  
    render() {
      return html`
        <slot></slot>
      `;
    }
  
    firstUpdated() {
      super.firstUpdated();
      this._max = this.steps.length - 1;
    }
  
    next() {
      this._goTo(this.current + 1, this.current);
    }
  
    previous() {
      this._goTo(this.current - 1, this.current);
    }
  
    get steps() {
      const defaultSlot = this.shadowRoot.querySelector('slot:not([name])');
      return defaultSlot.assignedNodes().filter(node => node.nodeType === Node.ELEMENT_NODE);
    }
  
    _goTo(newCurrent, oldCurrent) {
      if (newCurrent < 0 || newCurrent > this._max) {
        throw new Error(`There is no step at index ${newCurrent}.`);
      }
  
      const nextStep = this.steps[newCurrent];
      const back = newCurrent < oldCurrent;
  
      if (nextStep.passesCondition(this.data)) {
        if (back && nextStep.forwardOnly) {
          this._goTo(newCurrent - 1, oldCurrent);
        } else {
          this._changeStep(newCurrent, oldCurrent);
        }
      } else {
        nextStep.skip();
        if (back) {
          this._goTo(newCurrent - 1, oldCurrent);
        } else {
          this._goTo(newCurrent + 1, oldCurrent);
        }
      }
    }
  
    _changeStep(newCurrent, oldCurrent) {
      const oldStepElement = this.steps[oldCurrent];
      const newStepElement = this.steps[newCurrent];
      const fromStep = { number: oldCurrent, element: oldStepElement };
      const toStep = { number: newCurrent, element: newStepElement };
  
      oldStepElement.leave();
      newStepElement.enter();
  
      if (this.current !== newCurrent) {
        this._internalCurrentSync = true;
        this.current = newCurrent;
      }
  
      this._dispatchTransitionEvent(fromStep, toStep);
    }
  
    _dispatchTransitionEvent(fromStep, toStep) {
      this.dispatchEvent(
        new CustomEvent('transition', {
          bubbles: true,
          composed: true,
          detail: { fromStep, toStep },
        }),
      );
    }
  
    _onCurrentChanged(newValues, oldValues) {
      if (this._internalCurrentSync) {
        this._internalCurrentSync = false;
      } else {
        this._goTo(newValues.current, oldValues.current);
      }
    }
  }
  