Match-id-7133f2734eacafd1263023fbe3921097f915d497

This commit is contained in:
* 2022-03-31 10:25:52 +08:00 committed by *
parent 5cde85df95
commit e13f3245bd
21 changed files with 155 additions and 122 deletions

View File

@ -35,7 +35,7 @@ function ComponentAttr({ name, attr }: { name: string, attr: IAttr[] }) {
newSet.add(index);
}
setCollapsedNode(newSet);
}
};
const showAttr = [];
let currentIndentation = null;
@ -76,7 +76,7 @@ function ComponentAttr({ name, attr }: { name: string, attr: IAttr[] }) {
{showAttr}
</div>
</div>
)
);
}
export default function ComponentInfo({ name, attrs }: IComponentInfo) {
@ -104,5 +104,5 @@ export default function ComponentInfo({ name, attrs }: IComponentInfo) {
</div>
</div>
</div>
)
);
}

View File

@ -68,6 +68,7 @@
.attrDetail {
padding-bottom: 0.5rem;
.attrArrow {
color: @arrow-color;
width: 12px;

View File

@ -8,12 +8,12 @@ export default function Search(props: SearchProps) {
const { onChange } = props;
const handleChange = (event) => {
onChange(event.target.value);
}
};
return (
<input
onChange={handleChange}
className={styles.search}
placeholder={'Search (text or /regex/)'}
/>
)
);
}

View File

@ -5,10 +5,12 @@
width: 100%;
height: 100%;
overflow-y: auto;
.treeItem {
width: 100%;
position: absolute;
line-height: 18px;
&:hover {
background-color: @select-color;
}
@ -37,4 +39,3 @@
background-color: rgb(141 199 248 / 60%);
}
}

View File

@ -43,14 +43,14 @@ function Item(props: IItem) {
const showIcon = hasChild ? <Arrow director={isCollapsed ? 'right' : 'down'} /> : '';
const handleClickCollapse = () => {
onCollapse(id);
}
};
const handleClick = () => {
onClick(id);
}
};
const itemAttr: any = { style, className: styles.treeItem, onClick: handleClick };
if (isSelect) {
itemAttr.tabIndex = 0;
itemAttr.className = styles.treeItem + ' ' + styles.select
itemAttr.className = styles.treeItem + ' ' + styles.select;
}
const reg = createRegExp(highlightValue);
const heightCharacters = name.match(reg);
@ -60,7 +60,7 @@ function Item(props: IItem) {
showName = [];
// 高亮第一次匹配即可
const char = heightCharacters[0];
let index = name.search(char);
const index = name.search(char);
const notHighlightStr = cutName.slice(0, index);
showName.push(notHighlightStr);
showName.push(<mark>{char}</mark>);
@ -90,7 +90,7 @@ function Item(props: IItem) {
</>
)}
</div>
)
);
}
function VTree({ data, highlightValue }: { data: IData[], highlightValue: string }) {
@ -128,10 +128,11 @@ function VTree({ data, highlightValue }: { data: IData[], highlightValue: string
currentCollapseIndentation = null;
}
}
let id = item.id;
const id = item.id;
const isCollapsed = collapseNode.has(id);
if (totalHeight >= scrollTop && showList.length <= showNum) {
const nextItem = data[index + 1];
// 如果存在下一个节点,并且节点缩进比自己大,说明下个节点是子节点,节点本身需要显示展开收起图标
const hasChild = nextItem ? nextItem.indentation > item.indentation : false;
showList.push(
<Item
@ -146,7 +147,7 @@ function VTree({ data, highlightValue }: { data: IData[], highlightValue: string
isSelect={id === selectItem}
highlightValue={highlightValue}
{...item} />
)
);
}
totalHeight = totalHeight + lineHeight;
if (isCollapsed) {
@ -155,19 +156,19 @@ function VTree({ data, highlightValue }: { data: IData[], highlightValue: string
}
});
const scroll = (event: any) => {
const handleScroll = (event: any) => {
const scrollTop = event.target.scrollTop;
// 顶部留 100px 冗余高度
setScrollTop(Math.max(scrollTop - 100, 0));
}
};
return (
<div className={styles.treeContainer} onScroll={scroll}>
<div className={styles.treeContainer} onScroll={handleScroll}>
{showList}
{/* 确保有足够的高度 */}
<div style={{ marginTop: totalHeight }} />
</div>
)
);
}
export default VTree;

View File

@ -1,3 +1,7 @@
/**
* tree VNode
* getMockVNodeTree
*/
import { parseAttr } from '../parser/parseAttr';
import parseTreeRoot from '../parser/parseVNode';
@ -18,6 +22,7 @@ interface IMockTree {
children?: IMockTree[],
}
// 模拟树
const tree: IMockTree = {
tag: ClassComponent,
children: [
@ -31,7 +36,7 @@ const tree: IMockTree = {
]
}
]
}
};
function addOneThousandNode(node: IMockTree) {
const nodes = [];
@ -39,17 +44,24 @@ function addOneThousandNode(node: IMockTree) {
nodes.push({ tag: FunctionComponent });
}
node?.children.push({ tag: ClassComponent, children: nodes });
};
}
addOneThousandNode(tree);
function getMockVNodeTree(tree: IMockTree, vNode: VNode) {
const children = tree.children;
/**
* mock数据转变为 VNode
*
* @param node
* @param vNode VNode节点
*/
function getMockVNodeTree(node: IMockTree, vNode: VNode) {
const children = node.children;
if (children && children.length !== 0) {
const childNode = children[0];
let childVNode = MockVNode(childNode.tag);
childVNode.key = '0';
getMockVNodeTree(childNode, childVNode);
// 需要建立双链
vNode.child = childVNode;
childVNode.parent = vNode;
for (let i = 1; i < children.length; i++) {
@ -68,13 +80,12 @@ getMockVNodeTree(tree, rootVNode);
export const mockParsedVNodeData = parseTreeRoot(rootVNode);
const mockState = {
str: 'jenny',
num: 3,
boolean: true,
und: undefined,
fun: () => {},
fun: () => ({}),
symbol: Symbol('sym'),
map: new Map([['a', 'a']]),
set: new Set(['a', 1, 2, Symbol('bambi')]),

View File

@ -11,25 +11,30 @@
flex: 7;
display: flex;
flex-direction: column;
.left_top {
border-bottom: @divider-style;
flex: 0 0 @top-height;
display: flex;
align-items: center;
.select {
padding: 0 0.25rem 0 0.25rem;
flex: 0 0;
}
.divider {
flex: 0 0 1px;
margin: 0 0.25rem 0 0.25rem;
border-left: @divider-style;
height: calc(100% - 1rem);
}
.search {
flex: 1 1 0;
}
}
.left_bottom {
flex: 1;
height: 0;

View File

@ -19,7 +19,7 @@ function App() {
state: parsedMockState,
props: parsedMockState,
},
})
});
}
}, []);
const idIndentationMap: {
@ -46,7 +46,7 @@ function App() {
const handleSearchChange = (str: string) => {
setFilterValue(str);
}
};
return (
<div className={styles.app}>

View File

@ -1,27 +1,34 @@
<!doctype html>
<html>
<head>
<meta charset="utf8">
<title>Horizon</title>
<script src='horizon.production.js'></script>
<style>
html {
width: 100%;
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#root {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@ -1,6 +1,13 @@
// 将状态的值解析成固定格式
export function parseAttr(rootAttr: any) {
const result = [];
let indentation = 0;
const result: {
name: string,
type: string,
value: string,
indentation: number
}[] = [];
const indentation = 0;
const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => {
const stateType = typeof attr;
let value: any;
@ -28,7 +35,7 @@ export function parseAttr(rootAttr: any) {
attr.forEach((value, key) => {
parseSubAttr(value, parentIndentation + 2, key);
});
}
};
} else if (attr instanceof Set) {
showType = 'set';
const size = attr.size;
@ -46,8 +53,8 @@ export function parseAttr(rootAttr: any) {
addSubState = () => {
attr.forEach((value, index) => {
parseSubAttr(value, parentIndentation + 2, String(index));
})
}
});
};
} else {
showType = stateType;
value = '{...}';

View File

@ -1,7 +1,8 @@
import { travelVNodeTree } from "../../../../libs/horizon/src/renderer/vnode/VNodeUtils";
import { VNode } from "../../../../libs/horizon/src/renderer/Types";
import { ClassComponent, FunctionComponent } from "../../../../libs/horizon/src/renderer/vnode/VNodeTags";
import { travelVNodeTree } from '../../../../libs/horizon/src/renderer/vnode/VNodeUtils';
import { VNode } from '../../../../libs/horizon/src/renderer/Types';
import { ClassComponent, FunctionComponent } from '../../../../libs/horizon/src/renderer/vnode/VNodeTags';
// 建立双向映射关系,当用户在修改属性值后,可以找到对应的 VNode
const VNodeToIdMap = new Map<VNode, number>();
const IdToVNodeMap = new Map<number, VNode>();
@ -34,7 +35,7 @@ function parseTreeRoot(treeRoot: VNode) {
if (isUserComponent(tag)) {
const id = generateUid();
result.push(id);
const name = (node.type as Function).name;
const name = node.type.name;
result.push(name);
const parent = getParentUserComponent(node);
if (parent) {

View File

@ -5,7 +5,7 @@ interface IArrow {
export default function Arrow({ director }: IArrow) {
let d: string;
if (director === 'right') {
d = 'm2 0l12 8l-12 8 z'
d = 'm2 0l12 8l-12 8 z';
} else if (director === 'down') {
d = 'm0 2h16 l-8 12 z';
}
@ -13,5 +13,5 @@ export default function Arrow({director}: IArrow) {
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='8px' height='8px'>
<path d={d} fill='currentColor' />
</svg>
)
);
}

View File

@ -4,5 +4,5 @@ export default function Copy() {
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='1rem' height='1rem'>
<path d='M0 0 H16 V16 H0 z M2 2 H8 V8 H2 V2z' fill='currentColor' fill-rule='evenodd' />
</svg>
)
);
}

View File

@ -4,5 +4,5 @@ export default function Debug() {
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='1rem' height='1rem'>
<path d='m2 0l12 8l-12 8 z' fill='#000'/>
</svg>
)
);
}

View File

@ -6,5 +6,5 @@ export default function Eye() {
<circle cx="8" cy="8" r="4" fill="rgb(255, 255, 255)" />
<circle cx="8" cy="8" r="2" fill="#000000" />
</svg>
)
);
}

View File

@ -1,9 +1,8 @@
export default function Select() {
return (
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='1rem' height='1rem'>
<path d='M14 6 V3 C14 2.5 13.5 2 13 2 H3 C2.5 2 2 2.5 2 3 V13 C2 13.5 2.5 14 3 14H6 V13 H3 V3 H13 V6z M7 7 L9 15 L11 12 L14 15 L15 14 L12 11 L15 9z' fill='#000' />
</svg>
)
);
}

View File

@ -30,8 +30,8 @@ module.exports = {
'@babel/preset-typescript',
['@babel/preset-react', {
runtime: 'classic',
"pragma": "Horizon.createElement",
"pragmaFrag": "Horizon.Fragment",
'pragma': 'Horizon.createElement',
'pragmaFrag': 'Horizon.Fragment',
}]],
plugins: ['@babel/plugin-proposal-class-properties'],
}
@ -41,9 +41,9 @@ module.exports = {
{
test: /\.less/i,
use: [
"style-loader",
'style-loader',
{
loader: "css-loader",
loader: 'css-loader',
options: {
modules: true,