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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ interface IArrow {
export default function Arrow({ director }: IArrow) { export default function Arrow({ director }: IArrow) {
let d: string; let d: string;
if (director === 'right') { if (director === 'right') {
d = 'm2 0l12 8l-12 8 z' d = 'm2 0l12 8l-12 8 z';
} else if (director === 'down') { } else if (director === 'down') {
d = 'm0 2h16 l-8 12 z'; 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'> <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='8px' height='8px'>
<path d={d} fill='currentColor' /> <path d={d} fill='currentColor' />
</svg> </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'> <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' /> <path d='M0 0 H16 V16 H0 z M2 2 H8 V8 H2 V2z' fill='currentColor' fill-rule='evenodd' />
</svg> </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'> <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'/> <path d='m2 0l12 8l-12 8 z' fill='#000'/>
</svg> </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="4" fill="rgb(255, 255, 255)" />
<circle cx="8" cy="8" r="2" fill="#000000" /> <circle cx="8" cy="8" r="2" fill="#000000" />
</svg> </svg>
) );
} }

View File

@ -1,9 +1,8 @@
export default function Select() { export default function Select() {
return ( return (
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='1rem' height='1rem'> <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' /> <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> </svg>
) );
} }

View File

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