import { ReactiveAdapter, ReactiveObject, computed, reactive, observe, Observer, isObserver, partialReactive } from "@cfcs/core"; import { isFunction } from "@daybrush/utils"; import Frame from "../Frame"; import { isFrame } from "../utils"; import { ANIMATOR_METHODS, getMethodNames, ReactiveMethods } from "./reactive"; export const FRAME_METHODS = [ ...ANIMATOR_METHODS, ...getMethodNames(Frame), ]; /** * @typedef * @memberof Reactive */ export type FrameReactiveData = Observer | Frame | string | Record | (() => (Observer | Frame | string | Record)); export type FrameReactiveMethods = ReactiveMethods; /** * @typedef * @memberof Reactive */ export interface FrameReactiveState { /** * Returns the frame's cssText. */ cssText: string; /** * Returns the frame's css object (kebab-case). */ cssObject: Record; /** * Returns an object in camel case type of frame. It can be used in React. */ camelCasedCSSObject: Record; } export type FrameReactiveInstance = ReactiveObject & FrameReactiveMethods & { getFrameObserver(): Observer; onUpdate(): void; }; export const FRAME_REACTIVE: ReactiveAdapter< FrameReactiveInstance, FrameReactiveState, keyof FrameReactiveMethods, FrameReactiveData, {} > = { methods: FRAME_METHODS as Array, created(data: FrameReactiveData) { const nextObject = isFunction(data) ? data() : data; const updateCount = observe(0); let frame: Observer; if (isObserver(nextObject)) { frame = nextObject; } else { frame = observe(isFrame(nextObject) ? nextObject : new Frame(nextObject)); } const cssText = computed(() => { frame.current; updateCount.current; return frame.current.toCSSText(); }); const cssObject = computed(() => { frame.current; cssText.current; return frame.current.toCSSObject(); }); const camelCasedCSSObject = computed(() => { frame.current; cssText.current; return frame.current.toCSSObject(true); }); const onUpdate = () => { ++updateCount.current; }; frame.subscribe((currentFrame, prevFrame) => { prevFrame.off("update", onUpdate); currentFrame.on("update", onUpdate); }); const nextReactiveObject = partialReactive({ cssText, cssObject, camelCasedCSSObject, onUpdate, ...FRAME_METHODS.reduce((obj, cur) => { obj[cur] = (...args) => { const currentFrame = frame.current; return currentFrame?.[cur].call(currentFrame, ...args); }; return obj; }, {}), }) as FrameReactiveInstance; return nextReactiveObject; }, destroy(inst) { inst.off("update", inst.onUpdate); }, };