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> | Frame | string | Record<string, any>
| (() => (Observer<Frame> | Frame | string | Record<string, any>));
export type FrameReactiveMethods = ReactiveMethods<Frame>;
/**
* @typedef
* @memberof Reactive
*/
export interface FrameReactiveState {
/**
* Returns the frame's cssText.
*/
cssText: string;
/**
* Returns the frame's css object (kebab-case).
*/
cssObject: Record<string, any>;
/**
* Returns an object in camel case type of frame. It can be used in React.
*/
camelCasedCSSObject: Record<string, any>;
}
export type FrameReactiveInstance = ReactiveObject<FrameReactiveState> & FrameReactiveMethods & {
getFrameObserver(): Observer<Frame>;
onUpdate(): void;
};
export const FRAME_REACTIVE: ReactiveAdapter<
FrameReactiveInstance,
FrameReactiveState,
keyof FrameReactiveMethods,
FrameReactiveData,
{}
> = {
methods: FRAME_METHODS as Array<keyof FrameReactiveMethods>,
created(data: FrameReactiveData) {
const nextObject = isFunction(data) ? data() : data;
const updateCount = observe(0);
let frame: Observer<Frame>;
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);
},
};