diff --git a/libs/extension/src/components/ComponentInfo.tsx b/libs/extension/src/components/ComponentInfo.tsx index 7e13bb42..9c76b503 100644 --- a/libs/extension/src/components/ComponentInfo.tsx +++ b/libs/extension/src/components/ComponentInfo.tsx @@ -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} - ) + ); } export default function ComponentInfo({ name, attrs }: IComponentInfo) { @@ -104,5 +104,5 @@ export default function ComponentInfo({ name, attrs }: IComponentInfo) { - ) + ); } \ No newline at end of file diff --git a/libs/extension/src/components/ComponentsInfo.less b/libs/extension/src/components/ComponentsInfo.less index 173bec22..3622363e 100644 --- a/libs/extension/src/components/ComponentsInfo.less +++ b/libs/extension/src/components/ComponentsInfo.less @@ -68,6 +68,7 @@ .attrDetail { padding-bottom: 0.5rem; + .attrArrow { color: @arrow-color; width: 12px; diff --git a/libs/extension/src/components/Search.less b/libs/extension/src/components/Search.less index 617a9d26..003f8577 100644 --- a/libs/extension/src/components/Search.less +++ b/libs/extension/src/components/Search.less @@ -1,3 +1,3 @@ .search { width: 100%; -} \ No newline at end of file +} diff --git a/libs/extension/src/components/Search.tsx b/libs/extension/src/components/Search.tsx index 7418bc71..ce644325 100644 --- a/libs/extension/src/components/Search.tsx +++ b/libs/extension/src/components/Search.tsx @@ -8,12 +8,12 @@ export default function Search(props: SearchProps) { const { onChange } = props; const handleChange = (event) => { onChange(event.target.value); - } + }; return ( - ) + ); } \ No newline at end of file diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less index e83125e3..1460ce0f 100644 --- a/libs/extension/src/components/VTree.less +++ b/libs/extension/src/components/VTree.less @@ -5,10 +5,12 @@ width: 100%; height: 100%; overflow-y: auto; + .treeItem { width: 100%; position: absolute; line-height: 18px; + &:hover { background-color: @select-color; } @@ -19,15 +21,15 @@ width: 12px; padding-left: 0.5rem; } - + .componentName { color: @component-name-color; } - + .componentKeyName { color: @component-key-color; } - + .componentKeyValue { color: @componentKeyValue-color; } @@ -37,4 +39,3 @@ background-color: rgb(141 199 248 / 60%); } } - diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 91a7c42e..86f1daf7 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -26,14 +26,14 @@ const lineHeight = 18; const indentationLength = 20; function Item(props: IItem) { - const { - name, - style, + const { + name, + style, userKey, - hasChild, - onCollapse, - isCollapsed, - id, + hasChild, + onCollapse, + isCollapsed, + id, indentation, onClick, isSelect, @@ -43,14 +43,14 @@ function Item(props: IItem) { const showIcon = hasChild ? : ''; const handleClickCollapse = () => { onCollapse(id); - } + }; const handleClick = () => { onClick(id); - } - const itemAttr: any = {style, className: styles.treeItem, onClick: handleClick}; + }; + 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({char}); @@ -90,7 +90,7 @@ function Item(props: IItem) { )} - ) + ); } 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( - ) + ); } 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 ( -
+
{showList} {/* 确保有足够的高度 */}
- ) + ); } export default VTree; diff --git a/libs/extension/src/components/assets.less b/libs/extension/src/components/assets.less index a00d8ade..7e74bacf 100644 --- a/libs/extension/src/components/assets.less +++ b/libs/extension/src/components/assets.less @@ -1,5 +1,5 @@ -@arrow-color:rgb(95, 99, 104); -@divider-color:rgb(202, 205, 209); +@arrow-color: rgb(95, 99, 104); +@divider-color: rgb(202, 205, 209); @attr-name-color: rgb(200, 0, 0); @component-name-color: rgb(136, 18, 128); @component-key-color: rgb(153, 69, 0); @@ -11,4 +11,4 @@ @divider-width: 0.2px; @common-font-size: 12px; -@divider-style: @divider-color solid @divider-width; \ No newline at end of file +@divider-style: @divider-color solid @divider-width; diff --git a/libs/extension/src/devtools/mock.ts b/libs/extension/src/devtools/mock.ts index 0c40d06b..3a3eef88 100644 --- a/libs/extension/src/devtools/mock.ts +++ b/libs/extension/src/devtools/mock.ts @@ -1,3 +1,7 @@ +/** + * 用一个纯数据类型的对象 tree 去表示树的结构是非常清晰的,但是它不能准确的模拟 VNode 中存在的引用 + * 关系,需要进行转换 getMockVNodeTree + */ import { parseAttr } from '../parser/parseAttr'; import parseTreeRoot from '../parser/parseVNode'; @@ -9,7 +13,7 @@ const mockComponentNames = ['Apple', 'Pear', 'Banana', 'Orange', 'Jenny', 'Kiwi' function MockVNode(tag: string, props = {}, key = null, realNode = {}) { const vNode = new VNode(tag, props, key, realNode); const name = mockComponentNames.shift() || 'MockComponent'; - vNode.type = {name}; + vNode.type = { name }; return vNode; } @@ -18,41 +22,49 @@ interface IMockTree { children?: IMockTree[], } +// 模拟树 const tree: IMockTree = { tag: ClassComponent, children: [ - {tag: FunctionComponent}, - {tag: ClassComponent}, - {tag: FunctionComponent}, + { tag: FunctionComponent }, + { tag: ClassComponent }, + { tag: FunctionComponent }, { tag: FunctionComponent, children: [ - {tag: ClassComponent} + { tag: ClassComponent } ] } ] -} +}; function addOneThousandNode(node: IMockTree) { const nodes = []; - for(let i = 0; i < 1000; i++) { - nodes.push({tag: FunctionComponent}); + for (let i = 0; i < 1000; i++) { + nodes.push({ tag: FunctionComponent }); } - node?.children.push({tag: ClassComponent,children: nodes}); -}; + 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++) { + for (let i = 1; i < children.length; i++) { const nextNode = children[i]; const nextVNode = MockVNode(nextNode.tag); nextVNode.key = String(i); @@ -68,20 +80,19 @@ 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')]), - arr: [1,2,3,4], + arr: [1, 2, 3, 4], obj: { - niko: {jenny: 'jenny'} + niko: { jenny: 'jenny' } } }; -export const parsedMockState = parseAttr(mockState); \ No newline at end of file +export const parsedMockState = parseAttr(mockState); diff --git a/libs/extension/src/panel/App.less b/libs/extension/src/panel/App.less index 0186eec1..f48e225a 100644 --- a/libs/extension/src/panel/App.less +++ b/libs/extension/src/panel/App.less @@ -1,48 +1,53 @@ @import '../components/assets.less'; -.app{ +.app { display: flex; flex-direction: row; height: 100%; font-size: @common-font-size; } -.left{ +.left { 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; + flex: 1; height: 0; } } -.right{ +.right { flex: 3; border-left: @divider-style; } -input{ +input { outline: none; border-width: 0; padding: 0; -} \ No newline at end of file +} diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx index 871ad0ad..2b9ef6bc 100644 --- a/libs/extension/src/panel/App.tsx +++ b/libs/extension/src/panel/App.tsx @@ -1,4 +1,4 @@ -import {useState, useEffect} from 'horizon'; +import { useState, useEffect } from 'horizon'; import VTree, { IData } from '../components/VTree'; import Search from '../components/Search'; import ComponentInfo from '../components/ComponentInfo'; @@ -8,7 +8,7 @@ import { mockParsedVNodeData, parsedMockState } from '../devtools/mock'; function App() { const [parsedVNodeData, setParsedVNodeData] = useState([]); - const [componentInfo, setComponentInfo] = useState({name: null, attrs: {}}); + const [componentInfo, setComponentInfo] = useState({ name: null, attrs: {} }); const [filterValue, setFilterValue] = useState(''); useEffect(() => { if (isDev) { @@ -19,7 +19,7 @@ function App() { state: parsedMockState, props: parsedMockState, }, - }) + }); } }, []); const idIndentationMap: { @@ -27,7 +27,7 @@ function App() { } = {}; const data: IData[] = []; let i = 0; - while(i < parsedVNodeData.length) { + while (i < parsedVNodeData.length) { const id = parsedVNodeData[i] as string; i++; const name = parsedVNodeData[i] as string; @@ -46,7 +46,7 @@ function App() { const handleSearchChange = (str: string) => { setFilterValue(str); - } + }; return (
@@ -57,15 +57,15 @@ function App() {
- +
- +
- +
); diff --git a/libs/extension/src/panel/index.tsx b/libs/extension/src/panel/index.tsx index 9e50ba58..a6174e37 100644 --- a/libs/extension/src/panel/index.tsx +++ b/libs/extension/src/panel/index.tsx @@ -1,7 +1,7 @@ -import {render} from 'horizon'; +import { render } from 'horizon'; import App from './App'; render( - , + , document.getElementById('root') ); \ No newline at end of file diff --git a/libs/extension/src/panel/panel.html b/libs/extension/src/panel/panel.html index 70f60f3b..ae944454 100644 --- a/libs/extension/src/panel/panel.html +++ b/libs/extension/src/panel/panel.html @@ -1,27 +1,34 @@ - - - - - - -
- + + + Horizon + + + + + + +
+ + diff --git a/libs/extension/src/parser/parseAttr.ts b/libs/extension/src/parser/parseAttr.ts index 0c5335d1..c52b1891 100644 --- a/libs/extension/src/parser/parseAttr.ts +++ b/libs/extension/src/parser/parseAttr.ts @@ -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; @@ -20,7 +27,7 @@ export function parseAttr(rootAttr: any) { } else if (stateType === 'object') { if (attr === null) { showType = 'null'; - }else if (attr instanceof Map) { + } else if (attr instanceof Map) { showType = 'map'; const size = attr.size; value = `Map(${size})`; @@ -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 = '{...}'; diff --git a/libs/extension/src/parser/parseVNode.ts b/libs/extension/src/parser/parseVNode.ts index bb8ad91d..91104058 100644 --- a/libs/extension/src/parser/parseVNode.ts +++ b/libs/extension/src/parser/parseVNode.ts @@ -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(); const IdToVNodeMap = new Map(); @@ -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) { diff --git a/libs/extension/src/svgs/Arrow.tsx b/libs/extension/src/svgs/Arrow.tsx index c315bf19..0cb49d0d 100644 --- a/libs/extension/src/svgs/Arrow.tsx +++ b/libs/extension/src/svgs/Arrow.tsx @@ -2,16 +2,16 @@ interface IArrow { director: 'right' | 'down' } -export default function Arrow({director}: 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'; } return ( - + - ) + ); } \ No newline at end of file diff --git a/libs/extension/src/svgs/Copy.tsx b/libs/extension/src/svgs/Copy.tsx index 865222fe..483d9ece 100644 --- a/libs/extension/src/svgs/Copy.tsx +++ b/libs/extension/src/svgs/Copy.tsx @@ -2,7 +2,7 @@ export default function Copy() { return ( - + - ) + ); } diff --git a/libs/extension/src/svgs/Debug.tsx b/libs/extension/src/svgs/Debug.tsx index 6ac25393..41e037fb 100644 --- a/libs/extension/src/svgs/Debug.tsx +++ b/libs/extension/src/svgs/Debug.tsx @@ -4,5 +4,5 @@ export default function Debug() { - ) + ); } diff --git a/libs/extension/src/svgs/Eye.tsx b/libs/extension/src/svgs/Eye.tsx index f17f1fd2..bd34c2ac 100644 --- a/libs/extension/src/svgs/Eye.tsx +++ b/libs/extension/src/svgs/Eye.tsx @@ -3,8 +3,8 @@ export default function Eye() { return ( - - + + - ) + ); } diff --git a/libs/extension/src/svgs/Select.tsx b/libs/extension/src/svgs/Select.tsx index 08aef9bb..463805e5 100644 --- a/libs/extension/src/svgs/Select.tsx +++ b/libs/extension/src/svgs/Select.tsx @@ -1,9 +1,8 @@ - export default function Select() { return ( - ) + ); } \ No newline at end of file diff --git a/libs/extension/src/utils.ts b/libs/extension/src/utils.ts index 727c672d..ae2ee2a0 100644 --- a/libs/extension/src/utils.ts +++ b/libs/extension/src/utils.ts @@ -1,5 +1,5 @@ -export function createRegExp(expression: string){ +export function createRegExp(expression: string) { let str = expression; if (str[0] === '/') { str = str.slice(1); @@ -9,7 +9,7 @@ export function createRegExp(expression: string){ } try { return new RegExp(str, 'i'); - }catch(err) { + } catch (err) { return null; } } \ No newline at end of file diff --git a/libs/extension/webpack.dev.js b/libs/extension/webpack.dev.js index 37cbf13c..1c468b0c 100644 --- a/libs/extension/webpack.dev.js +++ b/libs/extension/webpack.dev.js @@ -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,14 +41,14 @@ module.exports = { { test: /\.less/i, use: [ - "style-loader", - { - loader: "css-loader", - options: { + 'style-loader', + { + loader: 'css-loader', + options: { modules: true, - - } - }, + + } + }, 'less-loader'], }] },