parent
cf87ab2183
commit
a7a57426b0
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
import { JSXElement } from '../type';
|
||||||
|
import { computed, isReactiveObj, untrack } from 'inula-reactive';
|
||||||
|
|
||||||
|
// It's boolean when the condition is static of default branch
|
||||||
|
// Otherwise it's a function that return boolean
|
||||||
|
type CondExpression = boolean | (() => boolean);
|
||||||
|
// When branch only include static JSXElement the branch can be a JSXElement
|
||||||
|
// Otherwise, the branch should be a function that return JSXElement
|
||||||
|
type Branch = JSXElement | (() => JSXElement);
|
||||||
|
|
||||||
|
export interface CondProps {
|
||||||
|
// Array of tuples, first item is the condition, second is the branch to render
|
||||||
|
branches: [CondExpression, Branch];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Cond(props: CondProps) {
|
||||||
|
// Find the first branch that matches the condition
|
||||||
|
// Any signal that used in condition expression, will trigger the condition to recompute
|
||||||
|
const currentBranch = computed(() => {
|
||||||
|
// clean up the previous branch
|
||||||
|
|
||||||
|
for (let i = 0; i < props.branches.length; i++) {
|
||||||
|
const [condition, branch] = props.branches[i];
|
||||||
|
if (typeof condition === 'function' ? condition() : condition) {
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
// Compute the current branch, when condition changes or when the branch changes
|
||||||
|
return computed(() => {
|
||||||
|
let result = currentBranch.get();
|
||||||
|
if (typeof result === 'function') {
|
||||||
|
// untrack the result to avoid reactivity.
|
||||||
|
untrack(() => {
|
||||||
|
result = result();
|
||||||
|
// Nested condition will return a reactive object, we need to get the value
|
||||||
|
// to avoid create a new render effect in insert function.
|
||||||
|
while (isReactiveObj(result)) {
|
||||||
|
result = result.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, false);
|
||||||
|
}
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
||||||
*
|
|
||||||
* openGauss is licensed under Mulan PSL v2.
|
|
||||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
||||||
* You may obtain a copy of Mulan PSL v2 at:
|
|
||||||
*
|
|
||||||
* http://license.coscl.org.cn/MulanPSL2
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
||||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
||||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
||||||
* See the Mulan PSL v2 for more details.
|
|
||||||
*/
|
|
||||||
import { isReactiveObj } from 'inula-reactive';
|
|
||||||
|
|
||||||
export function Show<T>({
|
|
||||||
if: rIf,
|
|
||||||
else: rElse,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
if: any | (() => T);
|
|
||||||
else?: any;
|
|
||||||
children: any;
|
|
||||||
}): any {
|
|
||||||
return () => {
|
|
||||||
const ifValue: any = calculateReactive(rIf);
|
|
||||||
|
|
||||||
let child: any = null;
|
|
||||||
if (ifValue) {
|
|
||||||
child = typeof children === 'function' ? children() : children;
|
|
||||||
} else {
|
|
||||||
child = typeof rElse === 'function' ? rElse() : rElse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return child;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 如果是函数就执行,如果是reactive就调用get()
|
|
||||||
* @param val 值/reactive对象/函数
|
|
||||||
* @return 返回真实值
|
|
||||||
*/
|
|
||||||
export function calculateReactive(val: any | (() => any)): any {
|
|
||||||
let ret = val;
|
|
||||||
if (typeof val === 'function') {
|
|
||||||
ret = val();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isReactiveObj(ret)) {
|
|
||||||
ret = ret.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
|
@ -13,13 +13,14 @@
|
||||||
* See the Mulan PSL v2 for more details.
|
* See the Mulan PSL v2 for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { insert } from './dom';
|
import {insert} from './dom';
|
||||||
|
import { untrack } from 'inula-reactive/dist';
|
||||||
|
|
||||||
type ComponentConstructor<T> = (props: T) => any;
|
type ComponentConstructor<T> = (props: T) => any;
|
||||||
type CodeFunction = () => any;
|
type CodeFunction = () => any;
|
||||||
|
|
||||||
export function runComponent<T>(Comp: ComponentConstructor<T>, props: T = {} as T): any {
|
export function runComponent<T>(Comp: ComponentConstructor<T>, props: T = {} as T): any {
|
||||||
return Comp(props);
|
return untrack(() => Comp(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function render(codeFn: CodeFunction, element: HTMLElement): () => void {
|
export function render(codeFn: CodeFunction, element: HTMLElement): () => void {
|
||||||
|
|
|
@ -290,7 +290,7 @@ export function setAttribute(node: Element, name: string, value: string | null):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function className(node: Element, value: string | string[] | Record<string, boolean> | null): void {
|
export function className(node: Element, value: string | Record<string, boolean> | null): void {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
node.removeAttribute('class');
|
node.removeAttribute('class');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,5 +14,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: JSX type
|
// TODO: JSX type
|
||||||
|
export type JSXElement = any;
|
||||||
export type FunctionComponent<Props = Record<string, unknown>> = (props: Props) => unknown;
|
export type FunctionComponent<Props = Record<string, unknown>> = (props: Props) => unknown;
|
||||||
export type AppDisposer = () => void;
|
export type AppDisposer = () => void;
|
||||||
|
|
|
@ -152,8 +152,8 @@ describe('For', () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
$$on(_el$8, 'click', add);
|
$$on(_el$8, 'click', add, true);
|
||||||
$$on(_el$10, 'click', push);
|
$$on(_el$10, 'click', push, true);
|
||||||
return _el$5;
|
return _el$5;
|
||||||
})();
|
})();
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,408 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-nocheck For the compiled code.
|
||||||
|
|
||||||
|
import { describe, expect } from 'vitest';
|
||||||
|
import { domTest as it } from './utils';
|
||||||
|
import { template as _$template, insert as _$insert } from '../src/dom';
|
||||||
|
import { Cond } from '../src/components/Cond';
|
||||||
|
import { runComponent as _$runComponent, render } from '../src/core';
|
||||||
|
import { reactive } from 'inula-reactive';
|
||||||
|
|
||||||
|
describe('conditions', () => {
|
||||||
|
it('should render the first branch that matches the condition.', ({ container }) => {
|
||||||
|
/**
|
||||||
|
* 源码:
|
||||||
|
* const fn = vi.fn();
|
||||||
|
* function App() {
|
||||||
|
* const x = reactive(7);
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <div>
|
||||||
|
* <h1>if</h1>
|
||||||
|
* <if cond={x.get() > 10}>
|
||||||
|
* <p>{x.get()} is greater than 10</p>
|
||||||
|
* </if>
|
||||||
|
* <else-if cond={5 > x.get()}>
|
||||||
|
* <p>{x.get()} is less than 5</p>
|
||||||
|
* </else-if>
|
||||||
|
* <else>
|
||||||
|
* <p>{x.get()} is between 5 and 10</p>
|
||||||
|
* </else>
|
||||||
|
* </div>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* render(() => <App />, container);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 编译后:
|
||||||
|
|
||||||
|
const _tmpl$ = /*#__PURE__*/ _$template(`<p> is greater than 10`),
|
||||||
|
_tmpl$2 = /*#__PURE__*/ _$template(`<p> is less than 5`),
|
||||||
|
_tmpl$3 = /*#__PURE__*/ _$template(`<div><h1>xxx`),
|
||||||
|
_tmpl$4 = /*#__PURE__*/ _$template(`<p> is between 5 and 10`);
|
||||||
|
let change;
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const x = reactive(7);
|
||||||
|
change = v => x.set(v);
|
||||||
|
return (() => {
|
||||||
|
const _el$ = _tmpl$3(),
|
||||||
|
_el$2 = _el$.firstChild;
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => x.get() > 10,
|
||||||
|
() => {
|
||||||
|
const _el$3 = _tmpl$(),
|
||||||
|
_el$4 = _el$3.firstChild;
|
||||||
|
_$insert(_el$3, x, _el$4);
|
||||||
|
return _el$3;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
() => 5 > x.get(),
|
||||||
|
() => {
|
||||||
|
const _el$5 = _tmpl$2(),
|
||||||
|
_el$6 = _el$5.firstChild;
|
||||||
|
_$insert(_el$5, x, _el$6);
|
||||||
|
return _el$5;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
() => {
|
||||||
|
const _el$7 = _tmpl$4(),
|
||||||
|
_el$8 = _el$7.firstChild;
|
||||||
|
_$insert(_el$7, x, _el$8);
|
||||||
|
return _el$7;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return _el$;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => _$runComponent(App, {}), container);
|
||||||
|
|
||||||
|
expect(container.innerHTML).toBe('<div><h1>xxx</h1><p>7 is between 5 and 10</p></div>');
|
||||||
|
change(11);
|
||||||
|
expect(container.innerHTML).toBe('<div><h1>xxx</h1><p>11 is greater than 10</p></div>');
|
||||||
|
change(4);
|
||||||
|
expect(container.innerHTML).toBe('<div><h1>xxx</h1><p>4 is less than 5</p></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render any branch if all conditions failed', ({ container }) => {
|
||||||
|
/**
|
||||||
|
* 源码:
|
||||||
|
* const fn = vi.fn();
|
||||||
|
* function App() {
|
||||||
|
* const x = reactive(7);
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <div>
|
||||||
|
* <h1>xxx</h1>
|
||||||
|
* <if cond={x.get() > 10}>
|
||||||
|
* <p>{x.get()} is greater than 10</p>
|
||||||
|
* </if>
|
||||||
|
* <h1>xxx</h1>
|
||||||
|
* </div>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* render(() => <App />, container);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 编译后:
|
||||||
|
|
||||||
|
const _tmpl$ = /*#__PURE__*/ _$template(`<p> is greater than 10`),
|
||||||
|
_tmpl$3 = /*#__PURE__*/ _$template(`<div><h1>xxx`);
|
||||||
|
let change;
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const x = reactive(7);
|
||||||
|
change = v => x.set(v);
|
||||||
|
return (() => {
|
||||||
|
const _el$ = _tmpl$3(),
|
||||||
|
_el$2 = _el$.firstChild;
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => x.get() > 10,
|
||||||
|
() => {
|
||||||
|
const _el$3 = _tmpl$(),
|
||||||
|
_el$4 = _el$3.firstChild;
|
||||||
|
_$insert(_el$3, x, _el$4);
|
||||||
|
return _el$3;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return _el$;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => _$runComponent(App, {}), container);
|
||||||
|
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1></div>"');
|
||||||
|
change(11);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>11 is greater than 10</p></div>"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render nested conditions', ({ container }) => {
|
||||||
|
/**
|
||||||
|
* 源码:
|
||||||
|
* const fn = vi.fn();
|
||||||
|
* function App() {
|
||||||
|
* const x = reactive(7);
|
||||||
|
*
|
||||||
|
* return (
|
||||||
|
* <div>
|
||||||
|
* <h1>xxx</h1>
|
||||||
|
* <if cond={x.get() > 10}>
|
||||||
|
* <p>{x.get()} is greater than 10</p>
|
||||||
|
* </if>
|
||||||
|
* <else-if cond={5 > x.get()}>
|
||||||
|
* <p>{x.get()} is less than 5</p>
|
||||||
|
* </else-if>
|
||||||
|
* <else>
|
||||||
|
* <if cond={x.get() > 7}>
|
||||||
|
* <p>{x.get()} is greater than 7</p>
|
||||||
|
* </if>
|
||||||
|
* <else>
|
||||||
|
* <p>{x.get()} is 7 or less</p>
|
||||||
|
* </else>
|
||||||
|
* </else>
|
||||||
|
* </div>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* render(() => <App />, container);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 编译后:
|
||||||
|
const _tmpl$ = /*#__PURE__*/ _$template(`<p> is greater than 10`),
|
||||||
|
_tmpl$2 = /*#__PURE__*/ _$template(`<p> is less than 5`),
|
||||||
|
_tmpl$3 = /*#__PURE__*/ _$template(`<p> is greater than 7`),
|
||||||
|
_tmpl$4 = /*#__PURE__*/ _$template(`<p> is 7 or less`),
|
||||||
|
_tmpl$5 = /*#__PURE__*/ _$template(`<div><h1>xxx`);
|
||||||
|
let change;
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const x = reactive(7);
|
||||||
|
change = v => x.set(v);
|
||||||
|
return (() => {
|
||||||
|
const _el$ = _tmpl$5(),
|
||||||
|
_el$2 = _el$.firstChild;
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => x.get() > 10,
|
||||||
|
() => {
|
||||||
|
const _el$3 = _tmpl$(),
|
||||||
|
_el$4 = _el$3.firstChild;
|
||||||
|
_$insert(_el$3, x, _el$4);
|
||||||
|
return _el$3;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
() => 5 > x.get(),
|
||||||
|
() => {
|
||||||
|
const _el$5 = _tmpl$2(),
|
||||||
|
_el$6 = _el$5.firstChild;
|
||||||
|
_$insert(_el$5, x, _el$6);
|
||||||
|
return _el$5;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
() => {
|
||||||
|
return _$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => x.get() > 7,
|
||||||
|
() => {
|
||||||
|
const _el$8 = _tmpl$3(),
|
||||||
|
_el$9 = _el$8.firstChild;
|
||||||
|
_$insert(_el$8, x, _el$9);
|
||||||
|
return _el$8;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
true,
|
||||||
|
() => {
|
||||||
|
const _el$10 = _tmpl$4(),
|
||||||
|
_el$11 = _el$10.firstChild;
|
||||||
|
_$insert(_el$10, x, _el$11);
|
||||||
|
return _el$10;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
return _el$;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => _$runComponent(App, {}), container);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>7 is 7 or less</p></div>"');
|
||||||
|
change(11);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>11 is greater than 10</p></div>"');
|
||||||
|
change(4);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>4 is less than 5</p></div>"');
|
||||||
|
change(8);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>8 is greater than 7</p></div>"');
|
||||||
|
change(6);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>xxx</h1><p>6 is 7 or less</p></div>"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render parallel conditions', ({ container }) => {
|
||||||
|
/**
|
||||||
|
* 源码:
|
||||||
|
* const fn = vi.fn();
|
||||||
|
* function App() {
|
||||||
|
* const showX = reactive(true);
|
||||||
|
* const showY = reactive(true);
|
||||||
|
* const showZ = reactive(true);
|
||||||
|
* return (
|
||||||
|
* <div>
|
||||||
|
* <h1>parallel</h1>
|
||||||
|
* <if cond={showX.get()}>
|
||||||
|
* <p>XXX</p>
|
||||||
|
* </if>
|
||||||
|
* <if cond={showY.get()}>
|
||||||
|
* <p>YYY</p>
|
||||||
|
* </if>
|
||||||
|
* <if cond={showZ.get()}>
|
||||||
|
* <p>ZZZ</p>
|
||||||
|
* </if>
|
||||||
|
* </div>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* render(() => <App />, container);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 编译后:
|
||||||
|
|
||||||
|
const _tmpl$ = /*#__PURE__*/ _$template(`<p>XXX`),
|
||||||
|
_tmpl2$ = /*#__PURE__*/ _$template(`<p>YYY`),
|
||||||
|
_tmpl3$ = /*#__PURE__*/ _$template(`<p>ZZZ`),
|
||||||
|
_tmpl$4 = /*#__PURE__*/ _$template(`<div><h1>parallel`);
|
||||||
|
|
||||||
|
const showX = reactive(true);
|
||||||
|
const showY = reactive(true);
|
||||||
|
const showZ = reactive(true);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (() => {
|
||||||
|
const _el$ = _tmpl$4(),
|
||||||
|
_el$2 = _el$.firstChild;
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => showX.get(),
|
||||||
|
() => {
|
||||||
|
return _tmpl$();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => showY.get(),
|
||||||
|
() => {
|
||||||
|
return _tmpl2$();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
_$insert(
|
||||||
|
_el$,
|
||||||
|
_$runComponent(Cond, {
|
||||||
|
get branches() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
() => showZ.get(),
|
||||||
|
() => {
|
||||||
|
return _tmpl3$();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return _el$;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => _$runComponent(App, {}), container);
|
||||||
|
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>XXX</p><p>YYY</p><p>ZZZ</p></div>"');
|
||||||
|
// hide X, Y, Z randomly
|
||||||
|
showY.set(false);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>XXX</p><p>ZZZ</p></div>"');
|
||||||
|
showX.set(false);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>ZZZ</p></div>"');
|
||||||
|
showZ.set(false);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1></div>"');
|
||||||
|
// show X, Y, Z randomly
|
||||||
|
showY.set(true);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>YYY</p></div>"');
|
||||||
|
showZ.set(true);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>YYY</p><p>ZZZ</p></div>"');
|
||||||
|
showX.set(true);
|
||||||
|
expect(container.innerHTML).toMatchInlineSnapshot('"<div><h1>parallel</h1><p>XXX</p><p>YYY</p><p>ZZZ</p></div>"');
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,11 +14,10 @@
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-nocheck For the compiled code.
|
// @ts-nocheck For the compiled code.
|
||||||
import { computed, reactive, watch } from 'inula-reactive';
|
import { reactive } from 'inula-reactive';
|
||||||
import {
|
import {
|
||||||
template as $$template,
|
template as $$template,
|
||||||
insert as $$insert,
|
insert as $$insert,
|
||||||
setAttribute as $$setAttribute,
|
|
||||||
effect as $$effect,
|
effect as $$effect,
|
||||||
style as $$style,
|
style as $$style,
|
||||||
className as $$className,
|
className as $$className,
|
||||||
|
@ -27,8 +26,6 @@ import { runComponent as $$runComponent, render } from '../src/core';
|
||||||
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
|
import { delegateEvents as $$delegateEvents, addEventListener as $$on } from '../src/event';
|
||||||
import { describe, expect } from 'vitest';
|
import { describe, expect } from 'vitest';
|
||||||
import { domTest as it } from './utils';
|
import { domTest as it } from './utils';
|
||||||
import { Show } from '../src/components/Show';
|
|
||||||
import { For } from '../src/components/For';
|
|
||||||
|
|
||||||
describe('render', () => {
|
describe('render', () => {
|
||||||
it('should render plain jsx', ({ container }) => {
|
it('should render plain jsx', ({ container }) => {
|
||||||
|
@ -553,445 +550,4 @@ describe('render', () => {
|
||||||
render(() => $$runComponent(Comp, {}), container);
|
render(() => $$runComponent(Comp, {}), container);
|
||||||
expect(container.querySelector('div').className).toEqual('red green');
|
expect(container.querySelector('div').className).toEqual('red green');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('使用Show组件', ({ container }) => {
|
|
||||||
/**
|
|
||||||
* 源码:
|
|
||||||
* const CountValue = (props) => {
|
|
||||||
* return <div id="count">Count value is {props.count()}.</div>;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const CountingComponent = () => {
|
|
||||||
* const [count, setCount] = createSignal(0);
|
|
||||||
* const add = () => {
|
|
||||||
* setCount((c) => c + 1);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* return <div>
|
|
||||||
* <Show when={count() > 0} fallback={<CountValue count={999} />}>
|
|
||||||
* <CountValue count={count} />
|
|
||||||
* </Show>
|
|
||||||
* <div><button id="btn" onClick={add}>add</button></div>
|
|
||||||
* </div>;
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* render(() => <CountingComponent />, document.getElementById("app"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 编译后:
|
|
||||||
const $tmpl = /*#__PURE__*/ $$template('<div id="count">Count value is <!>.'),
|
|
||||||
$tmpl_2 = /*#__PURE__*/ $$template('<div><div><button id="btn">add');
|
|
||||||
const CountValue = props => {
|
|
||||||
return (() => {
|
|
||||||
const _el$ = $tmpl(),
|
|
||||||
_el$2 = _el$.firstChild,
|
|
||||||
_el$4 = _el$2.nextSibling;
|
|
||||||
$$insert(_el$, () => props.count, _el$4);
|
|
||||||
return _el$;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
const CountingComponent = () => {
|
|
||||||
const count = reactive(0);
|
|
||||||
const add = () => {
|
|
||||||
count.set(c => c + 1);
|
|
||||||
};
|
|
||||||
return (() => {
|
|
||||||
const _el$5 = $tmpl_2(),
|
|
||||||
_el$6 = _el$5.firstChild,
|
|
||||||
_el$7 = _el$6.firstChild;
|
|
||||||
$$insert(
|
|
||||||
_el$5,
|
|
||||||
$$runComponent(Show, {
|
|
||||||
get if() {
|
|
||||||
return computed(() => count.get() > 0);
|
|
||||||
},
|
|
||||||
get else() {
|
|
||||||
return $$runComponent(CountValue, {
|
|
||||||
count: 999,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
get children() {
|
|
||||||
return $$runComponent(CountValue, {
|
|
||||||
count: count,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
_el$6
|
|
||||||
);
|
|
||||||
$$on(_el$7, 'click', add, true);
|
|
||||||
return _el$5;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
render(() => $$runComponent(CountingComponent, {}), container);
|
|
||||||
$$delegateEvents(['click']);
|
|
||||||
|
|
||||||
expect(container.querySelector('#count').innerHTML).toEqual('Count value is 999<!---->.');
|
|
||||||
|
|
||||||
container.querySelector('#btn').click();
|
|
||||||
|
|
||||||
expect(container.querySelector('#count').innerHTML).toEqual('Count value is 1<!---->.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('使用For组件', ({ container }) => {
|
|
||||||
/**
|
|
||||||
* 源码:
|
|
||||||
* const Todo = (props) => {
|
|
||||||
* return <div>Count value is {props.todo.title}.</div>;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const CountingComponent = () => {
|
|
||||||
* const [state, setState] = createStore({
|
|
||||||
* counter: 2,
|
|
||||||
* todoList: [
|
|
||||||
* { id: 23, title: 'Birds' },
|
|
||||||
* { id: 27, title: 'Fish' }
|
|
||||||
* ]
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* const add = () => {
|
|
||||||
* setState('todoList', () => {
|
|
||||||
* return [
|
|
||||||
* { id: 23, title: 'Birds' },
|
|
||||||
* { id: 27, title: 'Fish' },
|
|
||||||
* { id: 27, title: 'Cat' }
|
|
||||||
* ];
|
|
||||||
* });
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const push = () => {
|
|
||||||
* state.todoList.push({
|
|
||||||
* id: 27,
|
|
||||||
* title: 'Pig',
|
|
||||||
* },);
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* return <div>
|
|
||||||
* <div id="todos">
|
|
||||||
* <For each={state.todoList}>
|
|
||||||
* {todo => <><Todo todo={todo} /><Todo todo={todo} /></>}
|
|
||||||
* </For>
|
|
||||||
* </div>
|
|
||||||
* <div><button id="btn" onClick={add}>add</button></div>
|
|
||||||
* <div><button id="btn-push" onClick={push}>push</button></div>
|
|
||||||
* </div>;
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* render(() => <CountingComponent />, document.getElementById("app"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 编译后:
|
|
||||||
const $tmpl = /*#__PURE__*/ $$template('<div>Count value is <!>.'),
|
|
||||||
$tmpl_2 = /*#__PURE__*/ $$template(
|
|
||||||
'<div><div id="todos"></div><div><button id="btn">add</button></div><div><button id="btn-push">push'
|
|
||||||
);
|
|
||||||
const Todo = props => {
|
|
||||||
return (() => {
|
|
||||||
const _el$ = $tmpl(),
|
|
||||||
_el$2 = _el$.firstChild,
|
|
||||||
_el$4 = _el$2.nextSibling,
|
|
||||||
_el$3 = _el$4.nextSibling;
|
|
||||||
$$insert(_el$, () => props.todo.title, _el$4);
|
|
||||||
return _el$;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
const CountingComponent = () => {
|
|
||||||
const state = reactive({
|
|
||||||
counter: 2,
|
|
||||||
todoList: [
|
|
||||||
{
|
|
||||||
id: 23,
|
|
||||||
title: 'Birds',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 27,
|
|
||||||
title: 'Fish',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
const add = () => {
|
|
||||||
state.todoList.set(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
id: 23,
|
|
||||||
title: 'Birds',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 27,
|
|
||||||
title: 'Fish',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 27,
|
|
||||||
title: 'Cat',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const push = () => {
|
|
||||||
state.todoList.push({
|
|
||||||
id: 27,
|
|
||||||
title: 'Pig',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
return (() => {
|
|
||||||
const _el$5 = $tmpl_2(),
|
|
||||||
_el$6 = _el$5.firstChild,
|
|
||||||
_el$7 = _el$6.nextSibling,
|
|
||||||
_el$8 = _el$7.firstChild,
|
|
||||||
_el$9 = _el$7.nextSibling,
|
|
||||||
_el$10 = _el$9.firstChild;
|
|
||||||
$$insert(
|
|
||||||
_el$6,
|
|
||||||
$$runComponent(For, {
|
|
||||||
get each() {
|
|
||||||
return state.todoList;
|
|
||||||
},
|
|
||||||
children: todo => [
|
|
||||||
$$runComponent(Todo, {
|
|
||||||
todo: todo,
|
|
||||||
}),
|
|
||||||
$$runComponent(Todo, {
|
|
||||||
todo: todo,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
$$on(_el$8, 'click', add, true);
|
|
||||||
$$on(_el$10, 'click', push, true);
|
|
||||||
return _el$5;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
render(() => $$runComponent(CountingComponent, {}), container);
|
|
||||||
$$delegateEvents(['click']);
|
|
||||||
|
|
||||||
expect(container.querySelector('#todos').innerHTML).toEqual(
|
|
||||||
'<div>Count value is Birds<!---->.</div><div>Count value is Birds<!---->.</div><div>Count value is Fish<!---->.</div><div>Count value is Fish<!---->.</div>'
|
|
||||||
);
|
|
||||||
|
|
||||||
container.querySelector('#btn').click();
|
|
||||||
|
|
||||||
expect(container.querySelector('#todos').innerHTML).toEqual(
|
|
||||||
'<div>Count value is Birds<!---->.</div><div>Count value is Birds<!---->.</div><div>Count value is Fish<!---->.</div><div>Count value is Fish<!---->.</div><div>Count value is Cat<!---->.</div><div>Count value is Cat<!---->.</div>'
|
|
||||||
);
|
|
||||||
|
|
||||||
container.querySelector('#btn-push').click();
|
|
||||||
|
|
||||||
expect(container.querySelector('#todos').innerHTML).toEqual(
|
|
||||||
'<div>Count value is Birds<!---->.</div><div>Count value is Birds<!---->.</div><div>Count value is Fish<!---->.</div><div>Count value is Fish<!---->.</div><div>Count value is Cat<!---->.</div><div>Count value is Cat<!---->.</div><div>Count value is Pig<!---->.</div><div>Count value is Pig<!---->.</div>'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('使用effect, setAttribute, addEventListener', ({ container }) => {
|
|
||||||
/**
|
|
||||||
* 源码:
|
|
||||||
* const A = ['pretty', 'large', 'big', 'small', 'tall', 'short', 'long', 'handsome', 'plain', 'quaint', 'clean',
|
|
||||||
* 'elegant', 'easy', 'angry', 'crazy', 'helpful', 'mushy', 'odd', 'unsightly', 'adorable', 'important', 'inexpensive',
|
|
||||||
* 'cheap', 'expensive', 'fancy'];
|
|
||||||
*
|
|
||||||
* const random = (max: any) => Math.round(Math.random() * 1000) % max;
|
|
||||||
*
|
|
||||||
* let nextId = 1;
|
|
||||||
*
|
|
||||||
* function buildData(count: number) {
|
|
||||||
* let data = new Array(count);
|
|
||||||
*
|
|
||||||
* for (let i = 0; i < count; i++) {
|
|
||||||
* data[i] = {
|
|
||||||
* id: nextId++,
|
|
||||||
* label: `${A[random(A.length)]}`,
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* return data;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const Row = (props) => {
|
|
||||||
* const selected = createMemo(() => {
|
|
||||||
* return props.item.selected ? 'danger' : '';
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
* return (
|
|
||||||
* <tr class={selected()}>
|
|
||||||
* <td class="col-md-1">{props.item.label}</td>
|
|
||||||
* </tr>
|
|
||||||
* )
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* const RowList = (props) => {
|
|
||||||
* return <For each={props.list}>
|
|
||||||
* {(item) => <Row item={item}/>}
|
|
||||||
* </For>;
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* const Button = (props) => (
|
|
||||||
* <div class="col-sm-6">
|
|
||||||
* <button type="button" id={props.id} onClick={props.cb}>{props.title}</button>
|
|
||||||
* </div>
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* const Main = () => {
|
|
||||||
* const [state, setState] = createStore({data: [{id: 1, label: '111', selected: false}, {id: 2, label: '222', selected: false}], num: 2});
|
|
||||||
*
|
|
||||||
* function run() {
|
|
||||||
* setState('data', buildData(5));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* return (
|
|
||||||
* <div>
|
|
||||||
* <div>
|
|
||||||
* <div>
|
|
||||||
* <div><h1>Horizon-reactive-novnode</h1></div>
|
|
||||||
* <div>
|
|
||||||
* <div>
|
|
||||||
* <Button id="run" title="Create 1,000 rows" cb={run}/>
|
|
||||||
* </div>
|
|
||||||
* </div>
|
|
||||||
* </div>
|
|
||||||
* </div>
|
|
||||||
* <table>
|
|
||||||
* <tbody id="tbody"><RowList list={state.data}/></tbody>
|
|
||||||
* </table>
|
|
||||||
* </div>
|
|
||||||
* );
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* render(() => <Main />, document.getElementById("app"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 编译后:
|
|
||||||
const $tmpl = /*#__PURE__*/ $$template('<tr><td class="col-md-1">'),
|
|
||||||
$tmpl_2 = /*#__PURE__*/ $$template('<div class="col-sm-6"><button type="button">'),
|
|
||||||
$tmpl_3 = /*#__PURE__*/ $$template(
|
|
||||||
'<div><div><div><div><h1>Horizon-reactive-novnode</h1></div><div><div></div></div></div></div><table><tbody id="tbody">'
|
|
||||||
);
|
|
||||||
const A = [
|
|
||||||
'pretty',
|
|
||||||
'large',
|
|
||||||
'big',
|
|
||||||
'small',
|
|
||||||
'tall',
|
|
||||||
'short',
|
|
||||||
'long',
|
|
||||||
'handsome',
|
|
||||||
'plain',
|
|
||||||
'quaint',
|
|
||||||
'clean',
|
|
||||||
'elegant',
|
|
||||||
'easy',
|
|
||||||
'angry',
|
|
||||||
'crazy',
|
|
||||||
'helpful',
|
|
||||||
'mushy',
|
|
||||||
'odd',
|
|
||||||
'unsightly',
|
|
||||||
'adorable',
|
|
||||||
'important',
|
|
||||||
'inexpensive',
|
|
||||||
'cheap',
|
|
||||||
'expensive',
|
|
||||||
'fancy',
|
|
||||||
];
|
|
||||||
const random = max => Math.round(Math.random() * 1000) % max;
|
|
||||||
let nextId = 1;
|
|
||||||
|
|
||||||
function buildData(count) {
|
|
||||||
const data = new Array(count);
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
data[i] = {
|
|
||||||
id: nextId++,
|
|
||||||
label: `${A[random(A.length)]}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Row = props => {
|
|
||||||
const selected = computed(() => {
|
|
||||||
return props.item.selected.get() ? 'danger' : '';
|
|
||||||
});
|
|
||||||
|
|
||||||
return (() => {
|
|
||||||
const _el$ = $tmpl(),
|
|
||||||
_el$2 = _el$.firstChild;
|
|
||||||
$$insert(_el$2, () => props.item.label);
|
|
||||||
return _el$;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
const RowList = props => {
|
|
||||||
return $$runComponent(For, {
|
|
||||||
get each() {
|
|
||||||
return props.list;
|
|
||||||
},
|
|
||||||
children: item =>
|
|
||||||
$$runComponent(Row, {
|
|
||||||
item: item,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const Button = props =>
|
|
||||||
(() => {
|
|
||||||
const _el$3 = $tmpl_2(),
|
|
||||||
_el$4 = _el$3.firstChild;
|
|
||||||
$$on(_el$4, 'click', props.cb, true);
|
|
||||||
$$insert(_el$4, () => props.title);
|
|
||||||
watch(() => $$setAttribute(_el$4, 'id', props.id));
|
|
||||||
return _el$3;
|
|
||||||
})();
|
|
||||||
const Main = () => {
|
|
||||||
const state = reactive({
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
label: '111',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
label: '222',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
num: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
state.list.set(buildData(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (() => {
|
|
||||||
const _el$5 = $tmpl_3(),
|
|
||||||
_el$6 = _el$5.firstChild,
|
|
||||||
_el$7 = _el$6.firstChild,
|
|
||||||
_el$8 = _el$7.firstChild,
|
|
||||||
_el$9 = _el$8.nextSibling,
|
|
||||||
_el$10 = _el$9.firstChild,
|
|
||||||
_el$11 = _el$6.nextSibling,
|
|
||||||
_el$12 = _el$11.firstChild;
|
|
||||||
$$insert(
|
|
||||||
_el$10,
|
|
||||||
$$runComponent(Button, {
|
|
||||||
id: 'run',
|
|
||||||
title: 'Create 1,000 rows',
|
|
||||||
cb: run,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
$$insert(
|
|
||||||
_el$12,
|
|
||||||
$$runComponent(RowList, {
|
|
||||||
get list() {
|
|
||||||
return state.list;
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return _el$5;
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
render(() => $$runComponent(Main, {}), container);
|
|
||||||
$$delegateEvents(['click']);
|
|
||||||
|
|
||||||
expect(container.querySelector('#tbody').innerHTML).toEqual(
|
|
||||||
'<tr><td class="col-md-1">111</td></tr><tr><td class="col-md-1">222</td></tr>'
|
|
||||||
);
|
|
||||||
|
|
||||||
container.querySelector('#run').click();
|
|
||||||
|
|
||||||
expect(container.querySelector('#tbody').children.length).toEqual(5);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,9 +24,9 @@ export function createReactive<T extends number>(raw?: T): Signal<number>;
|
||||||
export function createReactive<T extends symbol>(raw?: T): Signal<symbol>;
|
export function createReactive<T extends symbol>(raw?: T): Signal<symbol>;
|
||||||
export function createReactive<T extends number | string | symbol>(raw?: T): Signal<T>;
|
export function createReactive<T extends number | string | symbol>(raw?: T): Signal<T>;
|
||||||
export function createReactive<T extends Record<any, any> | Array<any> | symbol>(raw?: T): DeepReactive<T>;
|
export function createReactive<T extends Record<any, any> | Array<any> | symbol>(raw?: T): DeepReactive<T>;
|
||||||
export function createReactive<T extends NonFunctionType>(raw: T): DeepReactive<T> | Signal<T> {
|
export function createReactive<T extends NonFunctionType>(raw: T, deep = true): DeepReactive<T> | Signal<T> {
|
||||||
// Function, Date, RegExp, null, undefined are simple signals
|
// Function, Date, RegExp, null, undefined are simple signals
|
||||||
if (isPrimitive(raw) || raw === null || raw === undefined || raw instanceof Date || raw instanceof RegExp || typeof raw === 'function') {
|
if (!deep || isPrimitive(raw) || raw === null || raw === undefined || raw instanceof Date || raw instanceof RegExp || typeof raw === 'function') {
|
||||||
return new RNode(raw, {isSignal: true});
|
return new RNode(raw, {isSignal: true});
|
||||||
} else {
|
} else {
|
||||||
const node = new RProxyNode(raw);
|
const node = new RProxyNode(raw);
|
||||||
|
@ -34,7 +34,10 @@ export function createReactive<T extends NonFunctionType>(raw: T): DeepReactive<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createComputed<T extends NoArgFn>(fn: T) {
|
export function createComputed<T extends NoArgFn>(fn: T, deep = true) {
|
||||||
|
if (!deep) {
|
||||||
|
return new RNode(fn, {isComputed: true});
|
||||||
|
}
|
||||||
const rNode = new RProxyNode<T>(fn, {isComputed: true});
|
const rNode = new RProxyNode<T>(fn, {isComputed: true});
|
||||||
return rNode.proxy;
|
return rNode.proxy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
|
||||||
|
*
|
||||||
|
* openInula is licensed under Mulan PSL v2.
|
||||||
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||||
|
* You may obtain a copy of Mulan PSL v2 at:
|
||||||
|
*
|
||||||
|
* http://license.coscl.org.cn/MulanPSL2
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the Mulan PSL v2 for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { reactive, computed, untrack, watch } from '../src';
|
||||||
|
|
||||||
|
describe('untrack', () => {
|
||||||
|
it('should work in computed', () => {
|
||||||
|
const a = reactive(7);
|
||||||
|
const b = reactive(1);
|
||||||
|
|
||||||
|
const product = computed(() => {
|
||||||
|
return a.get() + untrack(() => b.get());
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
expect(product.get()).toBe(8);
|
||||||
|
b.set(2);
|
||||||
|
expect(product.get()).toBe(8);
|
||||||
|
a.set(8);
|
||||||
|
expect(product.get()).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work in watch', () => {
|
||||||
|
const a = reactive(7);
|
||||||
|
const b = reactive(1);
|
||||||
|
const fn = jest.fn();
|
||||||
|
watch(() => {
|
||||||
|
fn(a.get() + untrack(() => b.get()));
|
||||||
|
});
|
||||||
|
expect(fn).toBeCalledTimes(1);
|
||||||
|
b.set(2);
|
||||||
|
expect(fn).toBeCalledTimes(1);
|
||||||
|
a.set(8);
|
||||||
|
expect(fn).toBeCalledTimes(2);
|
||||||
|
expect(fn).toBeCalledWith(10);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue