parent
843a64ebd9
commit
727db59da0
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
import { watch } from 'inula-reactive';
|
import { watch } from 'inula-reactive';
|
||||||
import { isReactiveObj } from 'inula-reactive';
|
import { isReactiveObj } from 'inula-reactive';
|
||||||
|
import { RefCallback, RefObject } from './type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a function that returns a Node created from the provided HTML string.
|
* Creates a function that returns a Node created from the provided HTML string.
|
||||||
|
@ -93,7 +94,7 @@ function insertExpression(parent: Node, value: any, prevValue: any, marker?: Nod
|
||||||
result = cleanChildren(parent, prevValue, marker);
|
result = cleanChildren(parent, prevValue, marker);
|
||||||
} else if (t === 'function') {
|
} else if (t === 'function') {
|
||||||
// 在watch里面执行
|
// 在watch里面执行
|
||||||
watchRender((prev) => {
|
watchRender(prev => {
|
||||||
let v = value();
|
let v = value();
|
||||||
while (isReactiveObj(v)) {
|
while (isReactiveObj(v)) {
|
||||||
v = v.get();
|
v = v.get();
|
||||||
|
@ -108,7 +109,7 @@ function insertExpression(parent: Node, value: any, prevValue: any, marker?: Nod
|
||||||
const array: any[] = [];
|
const array: any[] = [];
|
||||||
const isPrevArray: boolean = prevValue && Array.isArray(prevValue);
|
const isPrevArray: boolean = prevValue && Array.isArray(prevValue);
|
||||||
if (flattenArray(array, value)) {
|
if (flattenArray(array, value)) {
|
||||||
watchRender((prev) => {
|
watchRender(prev => {
|
||||||
result = insertExpression(parent, array, prev, marker);
|
result = insertExpression(parent, array, prev, marker);
|
||||||
return result;
|
return result;
|
||||||
}, prevValue);
|
}, prevValue);
|
||||||
|
@ -116,7 +117,8 @@ function insertExpression(parent: Node, value: any, prevValue: any, marker?: Nod
|
||||||
return () => result;
|
return () => result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array.length === 0) { // 当前没有节点
|
if (array.length === 0) {
|
||||||
|
// 当前没有节点
|
||||||
result = cleanChildren(parent, prevValue, marker);
|
result = cleanChildren(parent, prevValue, marker);
|
||||||
if (multi) {
|
if (multi) {
|
||||||
return result;
|
return result;
|
||||||
|
@ -135,7 +137,8 @@ function insertExpression(parent: Node, value: any, prevValue: any, marker?: Nod
|
||||||
appendNodes(parent, array);
|
appendNodes(parent, array);
|
||||||
}
|
}
|
||||||
result = array;
|
result = array;
|
||||||
} else if (value.nodeType) { // 是Node节点
|
} else if (value.nodeType) {
|
||||||
|
// 是Node节点
|
||||||
if (Array.isArray(prevValue)) {
|
if (Array.isArray(prevValue)) {
|
||||||
if (multi) {
|
if (multi) {
|
||||||
return cleanChildren(parent, prevValue, marker, value);
|
return cleanChildren(parent, prevValue, marker, value);
|
||||||
|
@ -367,3 +370,18 @@ export function style(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a ref to a node.
|
||||||
|
* @param node
|
||||||
|
* @param ref
|
||||||
|
*/
|
||||||
|
export function bindRef<T>(node: T, ref: RefObject<T> | RefCallback<T>): void {
|
||||||
|
if (typeof ref === 'function') {
|
||||||
|
ref(node);
|
||||||
|
} else if (ref && 'current' in ref) {
|
||||||
|
(ref as RefObject<T>).current = node;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid ref');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,3 +20,7 @@ export type AppDisposer = () => void;
|
||||||
export type Props = Record<string, unknown>;
|
export type Props = Record<string, unknown>;
|
||||||
export type Context = Props;
|
export type Context = Props;
|
||||||
export type WithChildren<T = Props> = T & { children?: JSXElement };
|
export type WithChildren<T = Props> = T & { children?: JSXElement };
|
||||||
|
// --- ref ---
|
||||||
|
export type RefObject<T> = { current: T | null };
|
||||||
|
export type RefCallback<T> = (instance: T | null) => void;
|
||||||
|
export type Ref<T> = RefObject<T> | RefCallback<T> | T;
|
||||||
|
|
|
@ -406,7 +406,7 @@ describe('env', () => {
|
||||||
|
|
||||||
render(() => $$runComponent(App, {}), container);
|
render(() => $$runComponent(App, {}), container);
|
||||||
expect(container.innerHTML).toMatchInlineSnapshot(
|
expect(container.innerHTML).toMatchInlineSnapshot(
|
||||||
'<ul><li>root</li><ul><li>sub1</li><li>root-sub1-1</li><li>root-sub1-2</li></ul><ul><li>sub2</li><li>root-sub2-3</li><ul><li>sub3</li><li>root-sub2-sub3-4</li></ul><li>root-sub2-5</li></ul></ul>',
|
'"<ul><li>root</li><ul><li>sub1</li><li>root-sub1-1</li><li>root-sub1-2</li></ul><ul><li>sub2</li><li>root-sub2-3</li><ul><li>sub3</li><li>root-sub2-sub3-4</li></ul><li>root-sub2-5</li></ul></ul>"',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* 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, bindRef as $$bindRef } from '../src/dom';
|
||||||
|
import { runComponent as $$runComponent, render } from '../src/core';
|
||||||
|
|
||||||
|
describe('ref', () => {
|
||||||
|
it('should reference to dom.', ({ container }) => {
|
||||||
|
/*
|
||||||
|
* 源码:
|
||||||
|
* function App(props) {
|
||||||
|
* let ref;
|
||||||
|
* return <canvas ref={ref} width="256" height="256" />;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const $tmpl$ = $$template(`<canvas width="256" height="256"></canvas>`);
|
||||||
|
let ref: Node;
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (() => {
|
||||||
|
const $div = $tmpl$();
|
||||||
|
const $ref = ref;
|
||||||
|
typeof $ref === 'function' ? $$bindRef($ref, $div) : (ref = $div);
|
||||||
|
return $div;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => $$runComponent(App, {}), container);
|
||||||
|
expect(ref).toBe(container.firstChild);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reference to component.', ({ container }) => {
|
||||||
|
/**
|
||||||
|
* 源码:
|
||||||
|
* // App.tsx
|
||||||
|
* import Canvas from "./canvas";
|
||||||
|
*
|
||||||
|
* function App() {
|
||||||
|
* let canvas;
|
||||||
|
* return <Canvas ref={canvas} />;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* render(() => <App />, document.getElementById("app"));
|
||||||
|
*
|
||||||
|
* // canvas.tsx
|
||||||
|
* export default function Canvas(props) {
|
||||||
|
* return <canvas ref={props.ref} width="256" height="256" />;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const $tmpl$ = $$template('<canvas width="256" height="256"></canvas>');
|
||||||
|
|
||||||
|
function Canvas() {
|
||||||
|
return (() => {
|
||||||
|
const $div = $tmpl$();
|
||||||
|
const $ref = canvas;
|
||||||
|
typeof $ref === 'function' ? $$bindRef($ref, $div) : (canvas = $div);
|
||||||
|
return $div;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvas: Node;
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return $$runComponent(Canvas, {
|
||||||
|
ref($r: Node) {
|
||||||
|
const $ref = canvas;
|
||||||
|
typeof $ref === 'function' ? $ref($r) : (canvas = $r);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render(() => $$runComponent(App, {}), container);
|
||||||
|
expect(canvas).toBe(container.firstChild);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue