MatthiasKainer/lit-element-state-decoupler

View on GitHub
decorator.ts

Summary

Maintainability
C
7 hrs
Test Coverage
import { LitLikeElement, DecoratedLitLikeElement, InjectableState, Reduce, UpdateableLitLikeElement, StateOptions, Workflow } from "./types";

export function asUpdateableLitElement(element: LitLikeElement) {
    if (!element.dispatchEvent || !element.requestUpdate) throw new Error("Element missing required functions (dispatchEvent/requestUpdate)")
    return element as unknown as UpdateableLitLikeElement
}

const reservedField = "__registered_states";

export function decorate(litElement: LitLikeElement) {
    const decoratedLitElement = (litElement as DecoratedLitLikeElement)
    if (decoratedLitElement[reservedField]) return decoratedLitElement

    const updateableLitLikeElement = asUpdateableLitElement(litElement)

    const oldUpdated = updateableLitLikeElement.updated
    decoratedLitElement[reservedField] = {
        index: 0,
        count: 0,
        states: [],
        reducers: [],
        workflows: []
    }
    updateableLitLikeElement.updated = (args) => {
        decoratedLitElement[reservedField].index = 0
        return oldUpdated(args)
    }
    return decoratedLitElement
}

export function withState(litElement: LitLikeElement, state: InjectableState<any>, options: StateOptions = {}) {
    const decoratedLitElement = decorate(litElement)
    const {index, count} = decoratedLitElement[reservedField]
    if (index === count) {
        decoratedLitElement[reservedField].index++
        decoratedLitElement[reservedField].count++
        decoratedLitElement[reservedField].states.push(state)
        return state
    }

    decoratedLitElement[reservedField].index++
    if (options.updateDefault) decoratedLitElement[reservedField].states[index].inject(state.get())
    return decoratedLitElement[reservedField].states[index]
}

export function withReducer(litElement: LitLikeElement, reduce: Reduce<any>) {
    const decoratedLitElement = decorate(litElement)
    const {index, count, reducers} = decoratedLitElement[reservedField]
    if (index === count && !reducers[index-1]) {
        decoratedLitElement[reservedField].reducers[index-1] = reduce
        return reduce
    }

    return decoratedLitElement[reservedField].reducers[index-1]
}

export function withWorkflow(litElement: LitLikeElement, workflow: Workflow) {
    const decoratedLitElement = decorate(litElement)
    const {index, count, workflows} = decoratedLitElement[reservedField]
    if (index === count && !workflows[index-1]) {
        decoratedLitElement[reservedField].workflows[index-1] = workflow
        return workflow
    }

    return decoratedLitElement[reservedField].workflows[index-1]
}