packages/guides/src/GuidesManager.tsx

import { ref, Properties } from "framework-utils";
import * as React from "react";
import { PROPERTIES, METHODS, EVENTS } from "./consts";
import { GuidesInterface, GuidesEvents, GuidesOptions } from "@scena/react-guides/declaration/types";
import InnerGuides from "./InnerGuides";
import EventEmitter from "@scena/event-emitter";
import { camelize } from "@daybrush/utils";
import { ContainerProvider, renderSelf } from "croact";

@Properties(METHODS as any, (prototype, property) => {
    if (prototype[property]) {
        return;
    }
    prototype[property] = function(...args) {
        const self = this.getInnerGuides();

        if (!self || !self[property]) {
            return;
        }
        return self[property](...args);
    };
})
@Properties(PROPERTIES, (prototype, property) => {
    Object.defineProperty(prototype, property, {
        get() {
            return this.getInnerGuides().props[property];
        },
        set(value) {
            this.innerGuides.setState({
                [property]: value,
            });
        },
        enumerable: true,
        configurable: true,
    });
})
/**
 * @sort 1
 * @extends EventEmitter
 */
class Guides extends EventEmitter<GuidesEvents> {
    private containerProvider: ContainerProvider | null = null;
    private selfElement: HTMLElement | null = null;
    private _warp = false;
    private innerGuides!: InnerGuides;
    /**
     * @sort 1
     * @param - guides' container
     * @param {$ts:Partial<Guides.GuidesOptions>} - guides' options
     */
    constructor(container: HTMLElement, options: Partial<GuidesOptions> = {}) {
        super();
        const events: any = {};

        EVENTS.forEach(name => {
            events[camelize(`on ${name}`)] = (e: any) => this.trigger(name as any, e);
        });
        let selfElement!: HTMLElement;

        if (options.warpSelf) {
            delete options.warpSelf;
            this._warp = true;
            selfElement = container;
        } else {
            selfElement = document.createElement("div");
            container.appendChild(selfElement);
        }
        this.containerProvider = renderSelf(
            <InnerGuides ref={ref(this, "innerGuides")}
                {...events}
                {...options} />,
            selfElement,
        );
    }
    /**
     * @param state
     * @param callback
     */
    public setState(state: Partial<GuidesOptions>, callback?: () => void) {
        this.innerGuides.setState(state, callback);
    }
    /**
     * @param callback
     */
    public forceUpdate(callback?: () => void) {
        this.innerGuides.forceUpdate(callback);
    }
    /**
     * destroy guides
     */
    public destroy() {
        const selfElement = this.selfElement!;

        renderSelf(
            null,
            selfElement!,
            this.containerProvider,
        );
        if (!this._warp) {
            selfElement?.parentElement?.removeChild(selfElement);
        }
        this.selfElement = null;
        this.innerGuides = null;
    }
    private getInnerGuides() {
        return this.innerGuides.guides;
    }
}
interface Guides extends GuidesInterface {}

export default Guides;