import React, { useContext, useEffect, useState } from "react";
//type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
//type KeysNotOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? never : K }[keyof T];
export class Property {
    _value;
    watchers = [];
    constructor(value) {
        this._value = value;
    }
    get value() {
        return this._value;
    }
    set value(value) {
        if (value !== this._value) {
            this._value = value;
            for (const watcher of this.watchers) {
                watcher(value);
            }
        }
    }
    watch(watcher) {
        this.watchers.push(watcher);
        return () => {
            this.watchers = this.watchers.filter(w => w !== watcher);
        };
    }
}
export function createCompositeStateProvider(StateClass) {
    const context = React.createContext(undefined);
    StateClass.Context = context;
    return context.Provider;
}
export class Slot {
    consume = undefined;
    _current;
    constructor(content) {
        this._current = content;
    }
    set current(content) {
        if (content !== this._current) {
            this._current = content;
            this.consume?.(content);
        }
    }
    get current() {
        return this._current;
    }
    withConsumer(consume) {
        this.consume = consume;
        consume && consume(this.current);
        return this;
    }
}
export function useCompositeState(StateClass) {
    const context = StateClass.Context;
    if (!context) {
        throw new Error("Context not defined for " + StateClass.name);
    }
    return useContext(context);
}
export function useGetCompositeStateProperty(property) {
    const [value, setValue] = useState(property.value);
    useEffect(() => {
        return property.watch((value) => {
            if (typeof value === 'function') {
                setValue(() => value); // cannot directly store functions
            }
            else {
                setValue(value);
            }
        });
    }, [property]);
    return value;
}
// use memo on value to avoid re-computations if needed
export function useSetCompositeStateProperty(property, value) {
    useEffect(() => {
        property.value = value;
        return () => {
            property.value = undefined;
        };
    }, [property, value]);
    return value;
}
// use memo on watcher to avoid re-computations if needed
export function useWatchCompositeStateProperty(property, watcher) {
    useEffect(() => {
        return property.watch(watcher);
    }, [property, watcher]);
}
export function useSlot(slot) {
    const [value, setValue] = useState(slot.current);
    useEffect(() => {
        slot.withConsumer(setValue);
        return () => {
            slot.withConsumer(undefined);
        };
    }, [slot]);
    return value;
}
export function useWatchSlot(slot, watcher) {
    useEffect(() => {
        slot.withConsumer(watcher);
        return () => {
            slot.withConsumer(undefined);
        };
    }, [slot, watcher]);
}
// use memo for value if needed
export function useDefineSlot(slot, value) {
    useEffect(() => {
        slot.current = value;
        return () => {
            slot.current = undefined;
        };
    }, [slot, value]);
}
//# sourceMappingURL=CompositeState.js.map