@cfcs/core
Version:
Write once, create framework components that supports React, Vue, Svelte, and more.
151 lines (136 loc) • 4.09 kB
text/typescript
/**
* cfcs
* Copyright (c) 2022-present NAVER Corp.
* MIT license
*/
import { ComputedObserver } from "./ComputedObserver";
import { Observer } from "./Observer";
import { ExtractNever, isFunction, keys } from "../core";
import { Observe } from "./decorators/Observe";
import { injectReactiveSubscribe, ReactiveSubscribe } from "./decorators/ReactiveSubscribe";
import { defineObservers, isObserver, setObserver } from "./utils";
type ConvertValue<Object extends Record<string, any>> = {
[Key in keyof Object]: Object[Key] extends Observer<infer Type> ? Type : Object[Key];
}
type PickObverser<Object extends Record<string, any>> = ExtractNever<{
[Key in keyof Object]: Object[Key] extends Observer<infer Type> ? Type : never;
}>;
/**
* @typedef
* @category Reactive
*/
export type ParitalReactiveObject<Object extends Record<string, any>>
= ConvertValue<Object> & ReactiveSubscribe<PickObverser<Object>>;
/**
* Result type of `reactive` function call
* @category Reactive
* @see reactive
* @example
* ```js
* import { reactive } from "@cfcs/core";
*
* // define reactive state
* const obj = reactive({
* value1: 1,
* value2: 2,
* });
* ```
*/
export type ReactiveObject<Object extends Record<string, any>>
= Object & ReactiveSubscribe<Object>;
function makeReactiveObject<Object extends Record<string, any>>(
setup: Readonly<Object> | (() => Readonly<Object>),
all?: boolean,
): ParitalReactiveObject<Object> {
const result = isFunction(setup) ? setup() : setup;
const reactiveObject: Record<string, any> = {};
defineObservers(reactiveObject);
keys(result).forEach((name: any) => {
const value = result[name];
if (isObserver(value)) {
setObserver(reactiveObject, name, value);
} else {
setObserver(reactiveObject, name, observe(value));
}
Observe(name)(reactiveObject, name);
});
injectReactiveSubscribe(reactiveObject);
return reactiveObject as ParitalReactiveObject<Object>;
}
/**
* @description Make the return value of the corresponding object or function a reactive object.
* @category Reactive
* @param setup - The target object or function to which reactive is applied
* @returns Makes all values into reactive objects.
* @example
* ```ts
* import { reactive } from "@cfcs/core";
*
* const obj = reactive({
* value1: 1,
* value2: 2,
* });
*
* obj.subscribe("value1", value1 => {
* console.log(value1);
* });
* obj.value1 = 2;
* ```
*/
export function reactive<Object extends Record<string, any>>(
setup: Readonly<Object> | (() => Readonly<Object>),
): ReactiveObject<Object> {
return makeReactiveObject(setup, true) as ReactiveObject<Object>;
}
/**
* @description Make the return value of the corresponding object or function a reactive object.
* @category Reactive
* @param setup - The target object or function to which reactive is applied
* @returns Only the values to which observer is applied are objects to which reactive is applied.
* @example
* ```ts
* import { partialReactive, observe } from "@cfcs/core";
*
* const value1 = observe(1);
* const value2 = observe(2);
* const obj = partialReactive({
* value1,
* value2,
* });
*
* obj.subscribe("value1", value1 => {
* console.log(value1);
* });
* value1.current = 2;
* ```
*/
export function partialReactive<Object extends Record<string, any>>(
setup: Readonly<Object> | (() => Readonly<Object>),
): ParitalReactiveObject<Object> {
return makeReactiveObject(setup);
}
/**
* @description Creates a mutable ref object. You can access the `.current` value and detect the value change through `.subscribe`.
* @category Reactive
* @example
* ```ts
* import { observe } from "@cfcs/core";
*
* const ob1 = observe(1);
*
* ob1.subscribe(nextValue => {
* console.log(nextValue);
* });
*
* ob1.current = 2;
* ```
*/
export function observe<Type>(defaultValue?: Type): Observer<Type> {
return new Observer<Type>(defaultValue);
}
/**
* @hidden
*/
export function computed<Type>(computedCallback: () => Type) {
return new ComputedObserver(computedCallback);
}