From 1085c400a3612d95f0876c351b8b885c99db7297 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 22 Mar 2022 19:54:31 +0800 Subject: [PATCH 01/22] Match-id-f9eaf1cfccee2ac7b3b2f5b071385d1356d11320 --- libs/extension/src/components/VTree.tsx | 103 ++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 libs/extension/src/components/VTree.tsx diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx new file mode 100644 index 00000000..040ea892 --- /dev/null +++ b/libs/extension/src/components/VTree.tsx @@ -0,0 +1,103 @@ +import { useState } from 'horizon'; + +export interface IData { + id: string; + name: string; + indentation: number; + userKey: string; +} + +type IItem = { + style: any, + hasChild: boolean, + onExpand: (id: string) => void, +} & IData + +// TODO: 计算可以展示的最多数量,并且监听显示器高度变化修改数值 +const showNum = 50; +const divHeight = 21; + +function Item({ name, style, userKey, hasChild, onExpand, id, indentation }: IItem) { + const key = userKey === '' ? '' : ` key = '${userKey}'`; + const showIcon = hasChild ? '△' : ''; + const onClickExpand = () => { + onExpand(id); + } + return ( +
+
{showIcon}
+ {name + key} +
+ ) +} + +function VTree({ data }: { data: IData[] }) { + const [scrollTop, setScrollTop] = useState(0); + const [collapseNode, setCollapseNode] = useState(new Set()); + const changeExpandNode = (id: string) => { + const nodes = new Set(); + collapseNode.forEach(value => { + nodes.add(value); + }); + if (nodes.has(id)) { + nodes.delete(id); + } else { + nodes.add(id); + } + setCollapseNode(nodes); + }; + const showList: any[] = []; + + let totalHeight = 0; + let currentCollapseIndentation: null| number = null; + data.forEach((item, index) => { + // 存在未处理完的收起节点 + if (currentCollapseIndentation !== null) { + const indentation = item.indentation; + // 缩进更大,不显示 + if (indentation > currentCollapseIndentation) { + return; + } else { + // 缩进小,说明完成了该收起节点的子节点处理。 + currentCollapseIndentation = null; + } + } + if (totalHeight >= scrollTop && showList.length <= showNum) { + const nextItem = data[index + 1]; + const hasChild = nextItem ? nextItem.indentation > item.indentation : false; + showList.push( + + ) + } + totalHeight = totalHeight + divHeight; + let id = item.id; + if (collapseNode.has(id)) { + // 该节点需要收起子节点 + currentCollapseIndentation = item.indentation; + } + }); + + const scroll = (event: any) => { + const scrollTop = event.target.scrollTop; + setScrollTop(Math.max(scrollTop - 100, 0)); + } + + return ( +
+ {showList} + {/* 确保有足够的高度 */} +
+
+ ) +} + +export default VTree; From 8a86d83faf85eaef64397e5a97d63cf3948ce337 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 22 Mar 2022 19:54:51 +0800 Subject: [PATCH 02/22] Match-id-87622cf11c2fc67c3916642abf1f1eb08b10dae6 --- libs/extension/src/panel/App.tsx | 35 ++++++++++++++++++ libs/extension/src/panel/index.tsx | 7 ++++ libs/extension/src/panel/panel.html | 22 ++++++++++++ libs/extension/webpack.dev.js | 55 +++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 libs/extension/src/panel/App.tsx create mode 100644 libs/extension/src/panel/index.tsx create mode 100644 libs/extension/src/panel/panel.html create mode 100644 libs/extension/webpack.dev.js diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx new file mode 100644 index 00000000..2c9014c5 --- /dev/null +++ b/libs/extension/src/panel/App.tsx @@ -0,0 +1,35 @@ +import VTree, { IData } from '../components/VTree'; + +// 临时开发用数据 +const arr = [9,"Main","","",10,"Jumbotron",9,"",11,"Button",10,"",12,"Button",10,"",13,"Button",10,"",14,"Button",10,"",15,"Button",10,"",16,"Button",10,"",17,"Row",9,"1",18,"Row",9,"2",19,"Row",9,"3",20,"Row",9,"4",21,"Row",9,"5",22,"Row",9,"6",23,"Row",9,"7",24,"Row",9,"8",25,"Row",9,"9",26,"Row",9,"10",27,"Row",9,"11",28,"Row",9,"12",29,"Row",9,"13",30,"Row",9,"14",31,"Row",9,"15",32,"Row",9,"16",33,"Row",9,"17",34,"Row",9,"18",35,"Row",9,"19",36,"Row",9,"20",37,"Row",9,"21",38,"Row",9,"22",39,"Row",9,"23",40,"Row",9,"24",41,"Row",9,"25",42,"Row",9,"26",43,"Row",9,"27",44,"Row",9,"28",45,"Row",9,"29",46,"Row",9,"30",47,"Row",9,"31",48,"Row",9,"32",49,"Row",9,"33",50,"Row",9,"34",51,"Row",9,"35",52,"Row",9,"36",53,"Row",9,"37",54,"Row",9,"38",55,"Row",9,"39",56,"Row",9,"40",57,"Row",9,"41",58,"Row",9,"42",59,"Row",9,"43",60,"Row",9,"44",61,"Row",9,"45",62,"Row",9,"46",63,"Row",9,"47",64,"Row",9,"48",65,"Row",9,"49",66,"Row",9,"50",67,"Row",9,"51",68,"Row",9,"52",69,"Row",9,"53",70,"Row",9,"54",71,"Row",9,"55",72,"Row",9,"56",73,"Row",9,"57",74,"Row",9,"58",75,"Row",9,"59",76,"Row",9,"60",77,"Row",9,"61",78,"Row",9,"62",79,"Row",9,"63",80,"Row",9,"64",81,"Row",9,"65",82,"Row",9,"66",83,"Row",9,"67",84,"Row",9,"68",85,"Row",9,"69",86,"Row",9,"70",87,"Row",9,"71",88,"Row",9,"72",89,"Row",9,"73",90,"Row",9,"74",91,"Row",9,"75",92,"Row",9,"76",93,"Row",9,"77",94,"Row",9,"78",95,"Row",9,"79",96,"Row",9,"80",97,"Row",9,"81",98,"Row",9,"82",99,"Row",9,"83",100,"Row",9,"84",101,"Row",9,"85",102,"Row",9,"86",103,"Row",9,"87",104,"Row",9,"88",105,"Row",9,"89",106,"Row",9,"90",107,"Row",9,"91",108,"Row",9,"92",109,"Row",9,"93",110,"Row",9,"94",111,"Row",9,"95",112,"Row",9,"96",113,"Row",9,"97",114,"Row",9,"98",115,"Row",9,"99",116,"Row",9,"100",117,"Row",9,"101",118,"Row",9,"102",119,"Row",9,"103",120,"Row",9,"104",121,"Row",9,"105",122,"Row",9,"106",123,"Row",9,"107",124,"Row",9,"108",125,"Row",9,"109",126,"Row",9,"110",127,"Row",9,"111",128,"Row",9,"112",129,"Row",9,"113",130,"Row",9,"114",131,"Row",9,"115",132,"Row",9,"116",133,"Row",9,"117",134,"Row",9,"118",135,"Row",9,"119",136,"Row",9,"120",137,"Row",9,"121",138,"Row",9,"122",139,"Row",9,"123",140,"Row",9,"124",141,"Row",9,"125",142,"Row",9,"126",143,"Row",9,"127",144,"Row",9,"128",145,"Row",9,"129",146,"Row",9,"130",147,"Row",9,"131",148,"Row",9,"132",149,"Row",9,"133",150,"Row",9,"134",151,"Row",9,"135",152,"Row",9,"136",153,"Row",9,"137",154,"Row",9,"138",155,"Row",9,"139",156,"Row",9,"140",157,"Row",9,"141",158,"Row",9,"142",159,"Row",9,"143",160,"Row",9,"144",161,"Row",9,"145",162,"Row",9,"146",163,"Row",9,"147",164,"Row",9,"148",165,"Row",9,"149",166,"Row",9,"150",167,"Row",9,"151",168,"Row",9,"152",169,"Row",9,"153",170,"Row",9,"154",171,"Row",9,"155",172,"Row",9,"156",173,"Row",9,"157",174,"Row",9,"158",175,"Row",9,"159",176,"Row",9,"160",177,"Row",9,"161",178,"Row",9,"162",179,"Row",9,"163",180,"Row",9,"164",181,"Row",9,"165",182,"Row",9,"166",183,"Row",9,"167",184,"Row",9,"168",185,"Row",9,"169",186,"Row",9,"170",187,"Row",9,"171",188,"Row",9,"172",189,"Row",9,"173",190,"Row",9,"174",191,"Row",9,"175",192,"Row",9,"176",193,"Row",9,"177",194,"Row",9,"178",195,"Row",9,"179",196,"Row",9,"180",197,"Row",9,"181",198,"Row",9,"182",199,"Row",9,"183",200,"Row",9,"184",201,"Row",9,"185",202,"Row",9,"186",203,"Row",9,"187",204,"Row",9,"188",205,"Row",9,"189",206,"Row",9,"190",207,"Row",9,"191",208,"Row",9,"192",209,"Row",9,"193",210,"Row",9,"194",211,"Row",9,"195",212,"Row",9,"196",213,"Row",9,"197",214,"Row",9,"198",215,"Row",9,"199",216,"Row",9,"200",217,"Row",9,"201",218,"Row",9,"202",219,"Row",9,"203",220,"Row",9,"204",221,"Row",9,"205",222,"Row",9,"206",223,"Row",9,"207",224,"Row",9,"208",225,"Row",9,"209",226,"Row",9,"210",227,"Row",9,"211",228,"Row",9,"212",229,"Row",9,"213",230,"Row",9,"214",231,"Row",9,"215",232,"Row",9,"216",233,"Row",9,"217",234,"Row",9,"218",235,"Row",9,"219",236,"Row",9,"220",237,"Row",9,"221",238,"Row",9,"222",239,"Row",9,"223",240,"Row",9,"224",241,"Row",9,"225",242,"Row",9,"226",243,"Row",9,"227",244,"Row",9,"228",245,"Row",9,"229",246,"Row",9,"230",247,"Row",9,"231",248,"Row",9,"232",249,"Row",9,"233",250,"Row",9,"234",251,"Row",9,"235",252,"Row",9,"236",253,"Row",9,"237",254,"Row",9,"238",255,"Row",9,"239",256,"Row",9,"240",257,"Row",9,"241",258,"Row",9,"242",259,"Row",9,"243",260,"Row",9,"244",261,"Row",9,"245",262,"Row",9,"246",263,"Row",9,"247",264,"Row",9,"248",265,"Row",9,"249",266,"Row",9,"250",267,"Row",9,"251",268,"Row",9,"252",269,"Row",9,"253",270,"Row",9,"254",271,"Row",9,"255",272,"Row",9,"256",273,"Row",9,"257",274,"Row",9,"258",275,"Row",9,"259",276,"Row",9,"260",277,"Row",9,"261",278,"Row",9,"262",279,"Row",9,"263",280,"Row",9,"264",281,"Row",9,"265",282,"Row",9,"266",283,"Row",9,"267",284,"Row",9,"268",285,"Row",9,"269",286,"Row",9,"270",287,"Row",9,"271",288,"Row",9,"272",289,"Row",9,"273",290,"Row",9,"274",291,"Row",9,"275",292,"Row",9,"276",293,"Row",9,"277",294,"Row",9,"278",295,"Row",9,"279",296,"Row",9,"280",297,"Row",9,"281",298,"Row",9,"282",299,"Row",9,"283",300,"Row",9,"284",301,"Row",9,"285",302,"Row",9,"286",303,"Row",9,"287",304,"Row",9,"288",305,"Row",9,"289",306,"Row",9,"290",307,"Row",9,"291",308,"Row",9,"292",309,"Row",9,"293",310,"Row",9,"294",311,"Row",9,"295",312,"Row",9,"296",313,"Row",9,"297",314,"Row",9,"298",315,"Row",9,"299",316,"Row",9,"300",317,"Row",9,"301",318,"Row",9,"302",319,"Row",9,"303",320,"Row",9,"304",321,"Row",9,"305",322,"Row",9,"306",323,"Row",9,"307",324,"Row",9,"308",325,"Row",9,"309",326,"Row",9,"310",327,"Row",9,"311",328,"Row",9,"312",329,"Row",9,"313",330,"Row",9,"314",331,"Row",9,"315",332,"Row",9,"316",333,"Row",9,"317",334,"Row",9,"318",335,"Row",9,"319",336,"Row",9,"320",337,"Row",9,"321",338,"Row",9,"322",339,"Row",9,"323",340,"Row",9,"324",341,"Row",9,"325",342,"Row",9,"326",343,"Row",9,"327",344,"Row",9,"328",345,"Row",9,"329",346,"Row",9,"330",347,"Row",9,"331",348,"Row",9,"332",349,"Row",9,"333",350,"Row",9,"334",351,"Row",9,"335",352,"Row",9,"336",353,"Row",9,"337",354,"Row",9,"338",355,"Row",9,"339",356,"Row",9,"340",357,"Row",9,"341",358,"Row",9,"342",359,"Row",9,"343",360,"Row",9,"344",361,"Row",9,"345",362,"Row",9,"346",363,"Row",9,"347",364,"Row",9,"348",365,"Row",9,"349",366,"Row",9,"350",367,"Row",9,"351",368,"Row",9,"352",369,"Row",9,"353",370,"Row",9,"354",371,"Row",9,"355",372,"Row",9,"356",373,"Row",9,"357",374,"Row",9,"358",375,"Row",9,"359",376,"Row",9,"360",377,"Row",9,"361",378,"Row",9,"362",379,"Row",9,"363",380,"Row",9,"364",381,"Row",9,"365",382,"Row",9,"366",383,"Row",9,"367",384,"Row",9,"368",385,"Row",9,"369",386,"Row",9,"370",387,"Row",9,"371",388,"Row",9,"372",389,"Row",9,"373",390,"Row",9,"374",391,"Row",9,"375",392,"Row",9,"376",393,"Row",9,"377",394,"Row",9,"378",395,"Row",9,"379",396,"Row",9,"380",397,"Row",9,"381",398,"Row",9,"382",399,"Row",9,"383",400,"Row",9,"384",401,"Row",9,"385",402,"Row",9,"386",403,"Row",9,"387",404,"Row",9,"388",405,"Row",9,"389",406,"Row",9,"390",407,"Row",9,"391",408,"Row",9,"392",409,"Row",9,"393",410,"Row",9,"394",411,"Row",9,"395",412,"Row",9,"396",413,"Row",9,"397",414,"Row",9,"398",415,"Row",9,"399",416,"Row",9,"400",417,"Row",9,"401",418,"Row",9,"402",419,"Row",9,"403",420,"Row",9,"404",421,"Row",9,"405",422,"Row",9,"406",423,"Row",9,"407",424,"Row",9,"408",425,"Row",9,"409",426,"Row",9,"410",427,"Row",9,"411",428,"Row",9,"412",429,"Row",9,"413",430,"Row",9,"414",431,"Row",9,"415",432,"Row",9,"416",433,"Row",9,"417",434,"Row",9,"418",435,"Row",9,"419",436,"Row",9,"420",437,"Row",9,"421",438,"Row",9,"422",439,"Row",9,"423",440,"Row",9,"424",441,"Row",9,"425",442,"Row",9,"426",443,"Row",9,"427",444,"Row",9,"428",445,"Row",9,"429",446,"Row",9,"430",447,"Row",9,"431",448,"Row",9,"432",449,"Row",9,"433",450,"Row",9,"434",451,"Row",9,"435",452,"Row",9,"436",453,"Row",9,"437",454,"Row",9,"438",455,"Row",9,"439",456,"Row",9,"440",457,"Row",9,"441",458,"Row",9,"442",459,"Row",9,"443",460,"Row",9,"444",461,"Row",9,"445",462,"Row",9,"446",463,"Row",9,"447",464,"Row",9,"448",465,"Row",9,"449",466,"Row",9,"450",467,"Row",9,"451",468,"Row",9,"452",469,"Row",9,"453",470,"Row",9,"454",471,"Row",9,"455",472,"Row",9,"456",473,"Row",9,"457",474,"Row",9,"458",475,"Row",9,"459",476,"Row",9,"460",477,"Row",9,"461",478,"Row",9,"462",479,"Row",9,"463",480,"Row",9,"464",481,"Row",9,"465",482,"Row",9,"466",483,"Row",9,"467",484,"Row",9,"468",485,"Row",9,"469",486,"Row",9,"470",487,"Row",9,"471",488,"Row",9,"472",489,"Row",9,"473",490,"Row",9,"474",491,"Row",9,"475",492,"Row",9,"476",493,"Row",9,"477",494,"Row",9,"478",495,"Row",9,"479",496,"Row",9,"480",497,"Row",9,"481",498,"Row",9,"482",499,"Row",9,"483",500,"Row",9,"484",501,"Row",9,"485",502,"Row",9,"486",503,"Row",9,"487",504,"Row",9,"488",505,"Row",9,"489",506,"Row",9,"490",507,"Row",9,"491",508,"Row",9,"492",509,"Row",9,"493",510,"Row",9,"494",511,"Row",9,"495",512,"Row",9,"496",513,"Row",9,"497",514,"Row",9,"498",515,"Row",9,"499",516,"Row",9,"500",517,"Row",9,"501",518,"Row",9,"502",519,"Row",9,"503",520,"Row",9,"504",521,"Row",9,"505",522,"Row",9,"506",523,"Row",9,"507",524,"Row",9,"508",525,"Row",9,"509",526,"Row",9,"510",527,"Row",9,"511",528,"Row",9,"512",529,"Row",9,"513",530,"Row",9,"514",531,"Row",9,"515",532,"Row",9,"516",533,"Row",9,"517",534,"Row",9,"518",535,"Row",9,"519",536,"Row",9,"520",537,"Row",9,"521",538,"Row",9,"522",539,"Row",9,"523",540,"Row",9,"524",541,"Row",9,"525",542,"Row",9,"526",543,"Row",9,"527",544,"Row",9,"528",545,"Row",9,"529",546,"Row",9,"530",547,"Row",9,"531",548,"Row",9,"532",549,"Row",9,"533",550,"Row",9,"534",551,"Row",9,"535",552,"Row",9,"536",553,"Row",9,"537",554,"Row",9,"538",555,"Row",9,"539",556,"Row",9,"540",557,"Row",9,"541",558,"Row",9,"542",559,"Row",9,"543",560,"Row",9,"544",561,"Row",9,"545",562,"Row",9,"546",563,"Row",9,"547",564,"Row",9,"548",565,"Row",9,"549",566,"Row",9,"550",567,"Row",9,"551",568,"Row",9,"552",569,"Row",9,"553",570,"Row",9,"554",571,"Row",9,"555",572,"Row",9,"556",573,"Row",9,"557",574,"Row",9,"558",575,"Row",9,"559",576,"Row",9,"560",577,"Row",9,"561",578,"Row",9,"562",579,"Row",9,"563",580,"Row",9,"564",581,"Row",9,"565",582,"Row",9,"566",583,"Row",9,"567",584,"Row",9,"568",585,"Row",9,"569",586,"Row",9,"570",587,"Row",9,"571",588,"Row",9,"572",589,"Row",9,"573",590,"Row",9,"574",591,"Row",9,"575",592,"Row",9,"576",593,"Row",9,"577",594,"Row",9,"578",595,"Row",9,"579",596,"Row",9,"580",597,"Row",9,"581",598,"Row",9,"582",599,"Row",9,"583",600,"Row",9,"584",601,"Row",9,"585",602,"Row",9,"586",603,"Row",9,"587",604,"Row",9,"588",605,"Row",9,"589",606,"Row",9,"590",607,"Row",9,"591",608,"Row",9,"592",609,"Row",9,"593",610,"Row",9,"594",611,"Row",9,"595",612,"Row",9,"596",613,"Row",9,"597",614,"Row",9,"598",615,"Row",9,"599",616,"Row",9,"600",617,"Row",9,"601",618,"Row",9,"602",619,"Row",9,"603",620,"Row",9,"604",621,"Row",9,"605",622,"Row",9,"606",623,"Row",9,"607",624,"Row",9,"608",625,"Row",9,"609",626,"Row",9,"610",627,"Row",9,"611",628,"Row",9,"612",629,"Row",9,"613",630,"Row",9,"614",631,"Row",9,"615",632,"Row",9,"616",633,"Row",9,"617",634,"Row",9,"618",635,"Row",9,"619",636,"Row",9,"620",637,"Row",9,"621",638,"Row",9,"622",639,"Row",9,"623",640,"Row",9,"624",641,"Row",9,"625",642,"Row",9,"626",643,"Row",9,"627",644,"Row",9,"628",645,"Row",9,"629",646,"Row",9,"630",647,"Row",9,"631",648,"Row",9,"632",649,"Row",9,"633",650,"Row",9,"634",651,"Row",9,"635",652,"Row",9,"636",653,"Row",9,"637",654,"Row",9,"638",655,"Row",9,"639",656,"Row",9,"640",657,"Row",9,"641",658,"Row",9,"642",659,"Row",9,"643",660,"Row",9,"644",661,"Row",9,"645",662,"Row",9,"646",663,"Row",9,"647",664,"Row",9,"648",665,"Row",9,"649",666,"Row",9,"650",667,"Row",9,"651",668,"Row",9,"652",669,"Row",9,"653",670,"Row",9,"654",671,"Row",9,"655",672,"Row",9,"656",673,"Row",9,"657",674,"Row",9,"658",675,"Row",9,"659",676,"Row",9,"660",677,"Row",9,"661",678,"Row",9,"662",679,"Row",9,"663",680,"Row",9,"664",681,"Row",9,"665",682,"Row",9,"666",683,"Row",9,"667",684,"Row",9,"668",685,"Row",9,"669",686,"Row",9,"670",687,"Row",9,"671",688,"Row",9,"672",689,"Row",9,"673",690,"Row",9,"674",691,"Row",9,"675",692,"Row",9,"676",693,"Row",9,"677",694,"Row",9,"678",695,"Row",9,"679",696,"Row",9,"680",697,"Row",9,"681",698,"Row",9,"682",699,"Row",9,"683",700,"Row",9,"684",701,"Row",9,"685",702,"Row",9,"686",703,"Row",9,"687",704,"Row",9,"688",705,"Row",9,"689",706,"Row",9,"690",707,"Row",9,"691",708,"Row",9,"692",709,"Row",9,"693",710,"Row",9,"694",711,"Row",9,"695",712,"Row",9,"696",713,"Row",9,"697",714,"Row",9,"698",715,"Row",9,"699",716,"Row",9,"700",717,"Row",9,"701",718,"Row",9,"702",719,"Row",9,"703",720,"Row",9,"704",721,"Row",9,"705",722,"Row",9,"706",723,"Row",9,"707",724,"Row",9,"708",725,"Row",9,"709",726,"Row",9,"710",727,"Row",9,"711",728,"Row",9,"712",729,"Row",9,"713",730,"Row",9,"714",731,"Row",9,"715",732,"Row",9,"716",733,"Row",9,"717",734,"Row",9,"718",735,"Row",9,"719",736,"Row",9,"720",737,"Row",9,"721",738,"Row",9,"722",739,"Row",9,"723",740,"Row",9,"724",741,"Row",9,"725",742,"Row",9,"726",743,"Row",9,"727",744,"Row",9,"728",745,"Row",9,"729",746,"Row",9,"730",747,"Row",9,"731",748,"Row",9,"732",749,"Row",9,"733",750,"Row",9,"734",751,"Row",9,"735",752,"Row",9,"736",753,"Row",9,"737",754,"Row",9,"738",755,"Row",9,"739",756,"Row",9,"740",757,"Row",9,"741",758,"Row",9,"742",759,"Row",9,"743",760,"Row",9,"744",761,"Row",9,"745",762,"Row",9,"746",763,"Row",9,"747",764,"Row",9,"748",765,"Row",9,"749",766,"Row",9,"750",767,"Row",9,"751",768,"Row",9,"752",769,"Row",9,"753",770,"Row",9,"754",771,"Row",9,"755",772,"Row",9,"756",773,"Row",9,"757",774,"Row",9,"758",775,"Row",9,"759",776,"Row",9,"760",777,"Row",9,"761",778,"Row",9,"762",779,"Row",9,"763",780,"Row",9,"764",781,"Row",9,"765",782,"Row",9,"766",783,"Row",9,"767",784,"Row",9,"768",785,"Row",9,"769",786,"Row",9,"770",787,"Row",9,"771",788,"Row",9,"772",789,"Row",9,"773",790,"Row",9,"774",791,"Row",9,"775",792,"Row",9,"776",793,"Row",9,"777",794,"Row",9,"778",795,"Row",9,"779",796,"Row",9,"780",797,"Row",9,"781",798,"Row",9,"782",799,"Row",9,"783",800,"Row",9,"784",801,"Row",9,"785",802,"Row",9,"786",803,"Row",9,"787",804,"Row",9,"788",805,"Row",9,"789",806,"Row",9,"790",807,"Row",9,"791",808,"Row",9,"792",809,"Row",9,"793",810,"Row",9,"794",811,"Row",9,"795",812,"Row",9,"796",813,"Row",9,"797",814,"Row",9,"798",815,"Row",9,"799",816,"Row",9,"800",817,"Row",9,"801",818,"Row",9,"802",819,"Row",9,"803",820,"Row",9,"804",821,"Row",9,"805",822,"Row",9,"806",823,"Row",9,"807",824,"Row",9,"808",825,"Row",9,"809",826,"Row",9,"810",827,"Row",9,"811",828,"Row",9,"812",829,"Row",9,"813",830,"Row",9,"814",831,"Row",9,"815",832,"Row",9,"816",833,"Row",9,"817",834,"Row",9,"818",835,"Row",9,"819",836,"Row",9,"820",837,"Row",9,"821",838,"Row",9,"822",839,"Row",9,"823",840,"Row",9,"824",841,"Row",9,"825",842,"Row",9,"826",843,"Row",9,"827",844,"Row",9,"828",845,"Row",9,"829",846,"Row",9,"830",847,"Row",9,"831",848,"Row",9,"832",849,"Row",9,"833",850,"Row",9,"834",851,"Row",9,"835",852,"Row",9,"836",853,"Row",9,"837",854,"Row",9,"838",855,"Row",9,"839",856,"Row",9,"840",857,"Row",9,"841",858,"Row",9,"842",859,"Row",9,"843",860,"Row",9,"844",861,"Row",9,"845",862,"Row",9,"846",863,"Row",9,"847",864,"Row",9,"848",865,"Row",9,"849",866,"Row",9,"850",867,"Row",9,"851",868,"Row",9,"852",869,"Row",9,"853",870,"Row",9,"854",871,"Row",9,"855",872,"Row",9,"856",873,"Row",9,"857",874,"Row",9,"858",875,"Row",9,"859",876,"Row",9,"860",877,"Row",9,"861",878,"Row",9,"862",879,"Row",9,"863",880,"Row",9,"864",881,"Row",9,"865",882,"Row",9,"866",883,"Row",9,"867",884,"Row",9,"868",885,"Row",9,"869",886,"Row",9,"870",887,"Row",9,"871",888,"Row",9,"872",889,"Row",9,"873",890,"Row",9,"874",891,"Row",9,"875",892,"Row",9,"876",893,"Row",9,"877",894,"Row",9,"878",895,"Row",9,"879",896,"Row",9,"880",897,"Row",9,"881",898,"Row",9,"882",899,"Row",9,"883",900,"Row",9,"884",901,"Row",9,"885",902,"Row",9,"886",903,"Row",9,"887",904,"Row",9,"888",905,"Row",9,"889",906,"Row",9,"890",907,"Row",9,"891",908,"Row",9,"892",909,"Row",9,"893",910,"Row",9,"894",911,"Row",9,"895",912,"Row",9,"896",913,"Row",9,"897",914,"Row",9,"898",915,"Row",9,"899",916,"Row",9,"900",917,"Row",9,"901",918,"Row",9,"902",919,"Row",9,"903",920,"Row",9,"904",921,"Row",9,"905",922,"Row",9,"906",923,"Row",9,"907",924,"Row",9,"908",925,"Row",9,"909",926,"Row",9,"910",927,"Row",9,"911",928,"Row",9,"912",929,"Row",9,"913",930,"Row",9,"914",931,"Row",9,"915",932,"Row",9,"916",933,"Row",9,"917",934,"Row",9,"918",935,"Row",9,"919",936,"Row",9,"920",937,"Row",9,"921",938,"Row",9,"922",939,"Row",9,"923",940,"Row",9,"924",941,"Row",9,"925",942,"Row",9,"926",943,"Row",9,"927",944,"Row",9,"928",945,"Row",9,"929",946,"Row",9,"930",947,"Row",9,"931",948,"Row",9,"932",949,"Row",9,"933",950,"Row",9,"934",951,"Row",9,"935",952,"Row",9,"936",953,"Row",9,"937",954,"Row",9,"938",955,"Row",9,"939",956,"Row",9,"940",957,"Row",9,"941",958,"Row",9,"942",959,"Row",9,"943",960,"Row",9,"944",961,"Row",9,"945",962,"Row",9,"946",963,"Row",9,"947",964,"Row",9,"948",965,"Row",9,"949",966,"Row",9,"950",967,"Row",9,"951",968,"Row",9,"952",969,"Row",9,"953",970,"Row",9,"954",971,"Row",9,"955",972,"Row",9,"956",973,"Row",9,"957",974,"Row",9,"958",975,"Row",9,"959",976,"Row",9,"960",977,"Row",9,"961",978,"Row",9,"962",979,"Row",9,"963",980,"Row",9,"964",981,"Row",9,"965",982,"Row",9,"966",983,"Row",9,"967",984,"Row",9,"968",985,"Row",9,"969",986,"Row",9,"970",987,"Row",9,"971",988,"Row",9,"972",989,"Row",9,"973",990,"Row",9,"974",991,"Row",9,"975",992,"Row",9,"976",993,"Row",9,"977",994,"Row",9,"978",995,"Row",9,"979",996,"Row",9,"980",997,"Row",9,"981",998,"Row",9,"982",999,"Row",9,"983",1000,"Row",9,"984",1001,"Row",9,"985",1002,"Row",9,"986",1003,"Row",9,"987",1004,"Row",9,"988",1005,"Row",9,"989",1006,"Row",9,"990",1007,"Row",9,"991",1008,"Row",9,"992",1009,"Row",9,"993",1010,"Row",9,"994",1011,"Row",9,"995",1012,"Row",9,"996",1013,"Row",9,"997",1014,"Row",9,"998",1015,"Row",9,"999",1016,"Row",9,"1000"]; + +function App() { + const idIndentationMap: { + [id: string]: number; + } = {}; + const data: IData[] = []; + let i = 0; + while(i < arr.length) { + const id = arr[i] as string; + i++; + const name = arr[i] as string; + i++; + const parentId = arr[i] as string; + i++; + const userKey = arr[i] as string; + i++; + const indentation = parentId === '' ? 0 : idIndentationMap[parentId] + 1; + idIndentationMap[id] = indentation; + const item = { + id, name, indentation, userKey + }; + data.push(item); + } + return ( +
+ +
+ ); +} + +export default App; diff --git a/libs/extension/src/panel/index.tsx b/libs/extension/src/panel/index.tsx new file mode 100644 index 00000000..9e50ba58 --- /dev/null +++ b/libs/extension/src/panel/index.tsx @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..5aac97d6 --- /dev/null +++ b/libs/extension/src/panel/panel.html @@ -0,0 +1,22 @@ + + + + + + + + + +
+ + diff --git a/libs/extension/webpack.dev.js b/libs/extension/webpack.dev.js new file mode 100644 index 00000000..57f8730a --- /dev/null +++ b/libs/extension/webpack.dev.js @@ -0,0 +1,55 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); + +// 用于 panel 页面开发 + +module.exports = { + mode: 'development', + entry: { + panel: path.join(__dirname, './src/panel/index.tsx'), + }, + output: { + path: path.join(__dirname, 'dist'), + filename: '[name].js' + }, + resolve: { + extensions: ['.ts', '.tsx', '.js'] + }, + module: { + rules: [{ + test: /\.tsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', + '@babel/preset-typescript', + ['@babel/preset-react', { + runtime: 'classic', + "pragma": "Horizon.createElement", + "pragmaFrag": "Horizon.Fragment", + }]], + } + } + ] + }] + }, + externals: { + 'horizon': 'Horizon', + }, + devServer: { + static: { + directory: path.join(__dirname, 'dist'), + }, + open: 'panel.html', + port: 9000, + magicHtml: true, + }, + plugins: [ + new HtmlWebpackPlugin({ + filename: 'panel.html', + template: './src/panel/panel.html' + }), + ], +}; From dec773daef880378f6a92cf6c1415b0504421aa0 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 22 Mar 2022 19:55:15 +0800 Subject: [PATCH 03/22] Match-id-ccc378b1bded3de0c9f33bad2878073aa2bd86c1 --- libs/extension/readme.md | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 libs/extension/readme.md diff --git a/libs/extension/readme.md b/libs/extension/readme.md new file mode 100644 index 00000000..d7e98884 --- /dev/null +++ b/libs/extension/readme.md @@ -0,0 +1,57 @@ +## 文件清单说明: +devtools_page: devtool主页面 +default_popup: 拓展图标点击时弹窗页面 +content_scripts: 内容脚本,在项目中负责在页面初始化时调用注入全局变量代码和消息传递 +web_accessible_resources: 注入全局变量代码 + +## 打开 panel 页面调试面板的方式 + +1. Open the developer tools. +1. Undock the developer tools if not already done (via the button in the bottom-left corner). +1. Press Ctrl + Shift + J to open the developer tools of the developer tools. +Optional: Feel free to dock the developer tools again if you had undocked it at step 2. +1. Switch from "" to devtoolsBackground.html (or whatever name you have chosen for your devtools). (example) +1. Now you can use the Console tab to play with the chrome.devtools API. + +## 全局变量注入 +通过content_scripts在document初始化时给页面添加script脚本,在新添加的脚本中给window注入全局变量 + +## horizon页面判断 +在页面完成渲染后往全局变量中添加信息,并传递 tabId 给 background 告知这是 horizon 页面 + +## 通信方式: +```mermaid +sequenceDiagram + participant web_page + participant script_content + participant background + participant panel + + Note over web_page: window.postMessage + web_page ->> script_content : {} + Note over script_content: window.addEventListener + Note over script_content: chrome.runtime.sendMessage + script_content ->> background : {} + Note over background: chrome.runtime.onMessage + Note over background: port.postMessage + background ->> panel : {} + Note over panel: connection.onMessage.addListener + Note over panel: connection.postMessage + panel ->> background : {} + Note over background: port.onMessage.addListener + Note over background: chrome.tabs.sendMessage + background ->> script_content : {} + Note over script_content: chrome.runtime.onMessage + Note over script_content: window.postMessage + script_content ->> web_page : {} + Note over web_page: window.addEventListener +``` + +## 数据压缩 +渲染组件树需要知道组件名和层次信息,如果把整个vNode树传递过来,传递对象太大,最好将数据进行压缩然后传递。 +- 相同的组件名可以进行压缩 +- 每个vNode有唯一的 path 属性,可以作为标识使用 +- 通过解析 path 值可以分析出组件树的结构 + +## 滚动动态渲染 Tree +考虑到组件树可能很大,所以并不适合一次性全部渲染出来,可以通过滚动渲染的方式减少页面 dom 的数量。我们可以把树看成有不同缩进长度的列表,动态渲染滚动列表的实现可以参考谷歌的这篇文章:https://developers.google.com/web/updates/2016/07/infinite-scroller 这样,我们需要的组件树数据可以由树结构转变为数组,可以减少动态渲染时对树结构进行解析时的计算工作。 From 5f042d8fa87e901628f2308f9a746c89075185c3 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 23 Mar 2022 11:08:28 +0800 Subject: [PATCH 04/22] Match-id-af5f4471321b0cdce3f5343e1c2f451887b30c17 --- libs/extension/src/components/VTree.less | 10 ++++++ libs/extension/src/components/VTree.tsx | 6 ++-- libs/extension/webpack.dev.js | 39 +++++++++++++----------- 3 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 libs/extension/src/components/VTree.less diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less new file mode 100644 index 00000000..81b716be --- /dev/null +++ b/libs/extension/src/components/VTree.less @@ -0,0 +1,10 @@ +.tree_container { + position: absolute; + width: 100%; + height: 100%; + overflow-y: auto; +} + +.tree_item{ + width: 100%; +} \ No newline at end of file diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 040ea892..59c8ec0c 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -1,4 +1,5 @@ import { useState } from 'horizon'; +import styles from './VTree.less'; export interface IData { id: string; @@ -24,7 +25,7 @@ function Item({ name, style, userKey, hasChild, onExpand, id, indentation }: IIt onExpand(id); } return ( -
+
{showIcon}
{name + key}
@@ -72,7 +73,6 @@ function VTree({ data }: { data: IData[] }) { style={{ position: 'absolute', transform: `translateY(${totalHeight}px)`, - width: '100%' }} onExpand={changeExpandNode} {...item} /> @@ -92,7 +92,7 @@ function VTree({ data }: { data: IData[] }) { } return ( -
+
{showList} {/* 确保有足够的高度 */}
diff --git a/libs/extension/webpack.dev.js b/libs/extension/webpack.dev.js index 57f8730a..0ee87691 100644 --- a/libs/extension/webpack.dev.js +++ b/libs/extension/webpack.dev.js @@ -16,24 +16,29 @@ module.exports = { extensions: ['.ts', '.tsx', '.js'] }, module: { - rules: [{ - test: /\.tsx?$/, - exclude: /node_modules/, - use: [ - { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', - '@babel/preset-typescript', - ['@babel/preset-react', { - runtime: 'classic', - "pragma": "Horizon.createElement", - "pragmaFrag": "Horizon.Fragment", - }]], + rules: [ + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env', + '@babel/preset-typescript', + ['@babel/preset-react', { + runtime: 'classic', + "pragma": "Horizon.createElement", + "pragmaFrag": "Horizon.Fragment", + }]], + } } - } - ] - }] + ] + }, + { + test: /\.less/i, + use: ["style-loader", { loader: "css-loader", options: { modules: true } }, 'less-loader'], + }] }, externals: { 'horizon': 'Horizon', From de7d629d304377eb6d5af1d39d4f2438bf1ec9b9 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 23 Mar 2022 16:19:33 +0800 Subject: [PATCH 05/22] Match-id-ac2594b0aedd973bc420d51b1106d3bfc294541c --- libs/extension/src/components/VTree.less | 15 +++++++++--- libs/extension/src/components/VTree.tsx | 29 ++++++++++++++---------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less index 81b716be..fe24a888 100644 --- a/libs/extension/src/components/VTree.less +++ b/libs/extension/src/components/VTree.less @@ -1,10 +1,19 @@ +@import 'assets.less'; + .tree_container { - position: absolute; + position: relative; width: 100%; height: 100%; overflow-y: auto; } -.tree_item{ +.tree_item { width: 100%; -} \ No newline at end of file + position: absolute; +} + +.tree_icon { + color: @gray; + display: inline-block; + width: 12px +} diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 59c8ec0c..51e8b828 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -1,5 +1,6 @@ import { useState } from 'horizon'; import styles from './VTree.less'; +import Arrow from '../svgs/Arrow'; export interface IData { id: string; @@ -11,22 +12,24 @@ export interface IData { type IItem = { style: any, hasChild: boolean, - onExpand: (id: string) => void, + onCollapse: (id: string) => void, + isCollapsed: boolean, } & IData // TODO: 计算可以展示的最多数量,并且监听显示器高度变化修改数值 const showNum = 50; const divHeight = 21; +const indentationLength = 20; -function Item({ name, style, userKey, hasChild, onExpand, id, indentation }: IItem) { +function Item({ name, style, userKey, hasChild, onCollapse, isCollapsed, id, indentation }: IItem) { const key = userKey === '' ? '' : ` key = '${userKey}'`; - const showIcon = hasChild ? '△' : ''; - const onClickExpand = () => { - onExpand(id); + const showIcon = hasChild ? : ''; + const onClickCollapse = () => { + onCollapse(id); } return (
-
{showIcon}
+
{showIcon}
{name + key}
) @@ -35,7 +38,7 @@ function Item({ name, style, userKey, hasChild, onExpand, id, indentation }: IIt function VTree({ data }: { data: IData[] }) { const [scrollTop, setScrollTop] = useState(0); const [collapseNode, setCollapseNode] = useState(new Set()); - const changeExpandNode = (id: string) => { + const changeCollapseNode = (id: string) => { const nodes = new Set(); collapseNode.forEach(value => { nodes.add(value); @@ -50,7 +53,7 @@ function VTree({ data }: { data: IData[] }) { const showList: any[] = []; let totalHeight = 0; - let currentCollapseIndentation: null| number = null; + let currentCollapseIndentation: null | number = null; data.forEach((item, index) => { // 存在未处理完的收起节点 if (currentCollapseIndentation !== null) { @@ -63,6 +66,8 @@ function VTree({ data }: { data: IData[] }) { currentCollapseIndentation = null; } } + let 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; @@ -71,16 +76,15 @@ function VTree({ data }: { data: IData[] }) { key={item.id} hasChild={hasChild} style={{ - position: 'absolute', transform: `translateY(${totalHeight}px)`, }} - onExpand={changeExpandNode} + onCollapse={changeCollapseNode} + isCollapsed={isCollapsed} {...item} /> ) } totalHeight = totalHeight + divHeight; - let id = item.id; - if (collapseNode.has(id)) { + if (isCollapsed) { // 该节点需要收起子节点 currentCollapseIndentation = item.indentation; } @@ -88,6 +92,7 @@ function VTree({ data }: { data: IData[] }) { const scroll = (event: any) => { const scrollTop = event.target.scrollTop; + // 顶部留 100px 冗余高度 setScrollTop(Math.max(scrollTop - 100, 0)); } From a3c5839a6b4b3d77231f49457200436e3fcd2fa1 Mon Sep 17 00:00:00 2001 From: * <8> Date: Fri, 25 Mar 2022 18:28:35 +0800 Subject: [PATCH 06/22] Match-id-b83bfbcd95720511a7e584ab7beb932978e800e2 --- scripts/__tests__/EventTest/EventMain.test.js | 131 +++++++++++++ .../__tests__/EventTest/FocusEvent.test.js | 46 +++++ .../__tests__/EventTest/KeyboardEvent.test.js | 179 ++++++++++++++++++ .../__tests__/EventTest/MouseEvent.test.js | 160 ++++++++++++++++ .../__tests__/EventTest/WheelEvent.test.js | 52 +++++ scripts/__tests__/jest/testUtils.js | 26 +++ 6 files changed, 594 insertions(+) create mode 100644 scripts/__tests__/EventTest/EventMain.test.js create mode 100644 scripts/__tests__/EventTest/FocusEvent.test.js create mode 100644 scripts/__tests__/EventTest/KeyboardEvent.test.js create mode 100644 scripts/__tests__/EventTest/MouseEvent.test.js create mode 100644 scripts/__tests__/EventTest/WheelEvent.test.js create mode 100644 scripts/__tests__/jest/testUtils.js diff --git a/scripts/__tests__/EventTest/EventMain.test.js b/scripts/__tests__/EventTest/EventMain.test.js new file mode 100644 index 00000000..1a3291c5 --- /dev/null +++ b/scripts/__tests__/EventTest/EventMain.test.js @@ -0,0 +1,131 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; +import * as TestUtils from '../jest/testUtils'; + +describe('事件', () => { + it('根节点挂载全量事件', () => { + const App = () => { + return
; + } + Horizon.render(, container); + console.log(TestUtils.getEventListeners(container)); + }) + + it('事件捕获与冒泡', () => { + const App = () => { + return ( + <> +
LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + const a = container.querySelector('button'); + a.click(); + expect(LogUtils.getAndClear()).toEqual([ + // 从外到内先捕获再冒泡 + 'div capture', + 'p capture', + 'btn capture', + 'btn bubble', + 'p bubble', + 'div bubble' + ]); + }) + + it('returns 0', () => { + let keyCode = null; + const node = Horizon.render( + { + keyCode = e.keyCode; + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + keyCode: 65, + bubbles: true, + cancelable: true, + }), + ); + expect(keyCode).toBe(65); + }); + + it('阻止事件冒泡', () => { + const App = () => { + return ( + <> +
LogUtils.log('div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + container.querySelector('button').click(); + + expect(LogUtils.getAndClear()).toEqual([ + // 到button时停止冒泡 + 'div capture', + 'p capture', + 'btn capture', + 'btn bubble' + ]); + }) + + it('阻止事件捕获', () => { + const App = () => { + return ( + <> +
TestUtils.stopBubbleOrCapture(e, 'div capture')} onClick={() => LogUtils.log('div bubble')}> +

LogUtils.log('p capture')} onClick={() => LogUtils.log('p bubble')}> +

+ + ); + } + Horizon.render(, container); + container.querySelector('button').click(); + + expect(LogUtils.getAndClear()).toEqual([ + // 阻止捕获,不再继续向下执行 + 'div capture' + ]); + }) + + it('阻止原生事件冒泡', () => { + const App = () => { + return ( +
+

+

+ ); + } + Horizon.render(, container); + container.querySelector('div').addEventListener('click', () => { + LogUtils.log('div bubble'); + }, false); + container.querySelector('p').addEventListener('click', () => { + LogUtils.log('p bubble'); + }, false); + container.querySelector('button').addEventListener('click', (e) => { + LogUtils.log('btn bubble'); + e.stopPropagation(); + }, false); + container.querySelector('button').click(); + expect(LogUtils.getAndClear()).toEqual([ + 'btn bubble' + ]); + }) +}) diff --git a/scripts/__tests__/EventTest/FocusEvent.test.js b/scripts/__tests__/EventTest/FocusEvent.test.js new file mode 100644 index 00000000..ee5e3c46 --- /dev/null +++ b/scripts/__tests__/EventTest/FocusEvent.test.js @@ -0,0 +1,46 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; +import { act } from '../jest/customMatcher'; + +describe('合成焦点事件', () => { + + it('onFocus', () => { + const realNode = Horizon.render( + LogUtils.log(`onFocus: ${event.type}`)} + onFocusCapture={event => LogUtils.log(`onFocusCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new FocusEvent('focusin', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onFocusCapture: focus', + 'onFocus: focus', + ]); + }); + + it('onBlur', () => { + const realNode = Horizon.render( + LogUtils.log(`onBlur: ${event.type}`)} + onBlurCapture={event => LogUtils.log(`onBlurCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new FocusEvent('focusout', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onBlurCapture: blur', + 'onBlur: blur', + ]); + }) +}) \ No newline at end of file diff --git a/scripts/__tests__/EventTest/KeyboardEvent.test.js b/scripts/__tests__/EventTest/KeyboardEvent.test.js new file mode 100644 index 00000000..519cc9ce --- /dev/null +++ b/scripts/__tests__/EventTest/KeyboardEvent.test.js @@ -0,0 +1,179 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('Keyboard Event', () => { + + it('keydown,keypress,keyup的keycode,charcode', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyUp: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + onKeyDown={(e) => { + LogUtils.log('onKeyDown: keycode: ' + e.keyCode + ',charcode: ' + e.charCode) + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + node.dispatchEvent( + new KeyboardEvent('keyup', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyDown: keycode: 50,charcode: 0', + 'onKeyUp: keycode: 50,charcode: 0' + ]); + }); + + it('keypress的keycode,charcode', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyPress: keycode: 0,charcode: 50' + ]); + }); + + it('当charcode为13,且不设置keycode的时候', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyPress: keycode: ' + e.keyCode + ',charcode: ' + e.charCode); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 13, + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyPress: keycode: 0,charcode: 13' + ]); + }); + + it('keydown,keypress,keyup的code', () => { + const node = Horizon.render( + { + LogUtils.log('onKeyUp: code: ' + e.code); + }} + onKeyPress={(e) => { + LogUtils.log('onKeyPress: code: ' + e.code); + }} + onKeyDown={(e) => { + LogUtils.log('onKeyDown: code: ' + e.code); + }} + />, + container, + ); + node.dispatchEvent( + new KeyboardEvent('keydown', { + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + node.dispatchEvent( + new KeyboardEvent('keypress', { + keyCode: 50, + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + node.dispatchEvent( + new KeyboardEvent('keyup', { + code: 'Digit2', + bubbles: true, + cancelable: true, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onKeyDown: code: Digit2', + 'onKeyPress: code: Digit2', + 'onKeyUp: code: Digit2' + ]); + }); + + it('可以执行preventDefault和 stopPropagation', () => { + const keyboardProcessing = e => { + expect(e.isDefaultPrevented()).toBe(false); + e.preventDefault(); + expect(e.isDefaultPrevented()).toBe(true); + + expect(e.isPropagationStopped()).toBe(false); + e.stopPropagation(); + expect(e.isPropagationStopped()).toBe(true); + LogUtils.log(e.type + ' handle'); + }; + const div = Horizon.render( +
, + container, + ); + + div.dispatchEvent( + new KeyboardEvent('keydown', { + keyCode: 40, + bubbles: true, + cancelable: true, + }), + ); + div.dispatchEvent( + new KeyboardEvent('keyup', { + keyCode: 40, + bubbles: true, + cancelable: true, + }), + ); + div.dispatchEvent( + new KeyboardEvent('keypress', { + charCode: 40, + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'keydown handle', + 'keyup handle', + 'keypress handle' + ]); + }); +}); diff --git a/scripts/__tests__/EventTest/MouseEvent.test.js b/scripts/__tests__/EventTest/MouseEvent.test.js new file mode 100644 index 00000000..e5ffbae1 --- /dev/null +++ b/scripts/__tests__/EventTest/MouseEvent.test.js @@ -0,0 +1,160 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('MouseEvent Test', () => { + describe('onClick Test', () => { + it('绑定this', () => { + class App extends Horizon.Component { + constructor(props) { + super(props); + this.state = { + num: this.props.num, + price: this.props.price + }; + } + + setNum() { + this.setState( + { + num: this.state.num + 1 + } + ) + } + + setPrice = (e) => { + this.setState( + { + num: this.state.price + 1 + } + ) + } + + render() { + return ( + <> +

{this.state.num}

+

{this.state.price}

+ + + + ); + } + } + Horizon.render(, container); + expect(container.querySelector('p').innerHTML).toBe('0'); + expect(container.querySelector('#p').innerHTML).toBe('100'); + // 点击按钮触发num加1 + container.querySelector('button').click(); + expect(container.querySelector('p').innerHTML).toBe('1'); + + container.querySelector('#btn').click(); + expect(container.querySelector('p').innerHTML).toBe('101'); + }); + + it('点击触发', () => { + const handleClick = jest.fn(); + Horizon.render(, container) + container.querySelector('button').click(); + expect(handleClick).toHaveBeenCalledTimes(1); + for (let i = 0; i < 5; i++) { + container.querySelector('button').click(); + } + expect(handleClick).toHaveBeenCalledTimes(6); + }) + }) + + const test = (name, config) => { + const node = Horizon.render(config, container); + let event = new MouseEvent(name, { + relatedTarget: null, + bubbles: true, + screenX: 1 + }); + node.dispatchEvent(event); + + expect(LogUtils.getAndClear()).toEqual([ + `${name} capture`, + `${name} bubble` + ]); + + event = new MouseEvent(name, { + relatedTarget: null, + bubbles: true, + screenX: 2 + }); + node.dispatchEvent(event); + + // 再次触发新事件 + expect(LogUtils.getAndClear()).toEqual([ + `${name} capture`, + `${name} bubble` + ]); + } + + describe('合成鼠标事件', () => { + it('onMouseMove', () => { + const onMouseMove = () => { + LogUtils.log('mousemove bubble'); + }; + const onMouseMoveCapture = () => { + LogUtils.log('mousemove capture'); + }; + test('mousemove',
) + }); + + it('onMouseDown', () => { + const onMousedown = () => { + LogUtils.log('mousedown bubble'); + }; + const onMousedownCapture = () => { + LogUtils.log('mousedown capture'); + }; + test('mousedown',
) + }); + + it('onMouseUp', () => { + const onMouseUp = () => { + LogUtils.log('mouseup bubble'); + }; + const onMouseUpCapture = () => { + LogUtils.log('mouseup capture'); + }; + test('mouseup',
) + }); + + it('onMouseOut', () => { + const onMouseOut = () => { + LogUtils.log('mouseout bubble'); + }; + const onMouseOutCapture = () => { + LogUtils.log('mouseout capture'); + }; + test('mouseout',
) + }); + + it('onMouseOver', () => { + const onMouseOver = () => { + LogUtils.log('mouseover bubble'); + }; + const onMouseOverCapture = () => { + LogUtils.log('mouseover capture'); + }; + test('mouseover',
) + }); + }) +}) diff --git a/scripts/__tests__/EventTest/WheelEvent.test.js b/scripts/__tests__/EventTest/WheelEvent.test.js new file mode 100644 index 00000000..a0ebf933 --- /dev/null +++ b/scripts/__tests__/EventTest/WheelEvent.test.js @@ -0,0 +1,52 @@ +import * as Horizon from '@cloudsop/horizon/index.ts'; +import * as LogUtils from '../jest/logUtils'; + +describe('合成滚轮事件', () => { + it('onWheel', () => { + const realNode = Horizon.render( +
LogUtils.log(`onWheel: ${event.type}`)} + onWheelCapture={event => LogUtils.log(`onWheelCapture: ${event.type}`)} + />, container); + + realNode.dispatchEvent( + new MouseEvent('wheel', { + bubbles: true, + cancelable: false, + }), + ); + + expect(LogUtils.getAndClear()).toEqual([ + 'onWheelCapture: wheel', + 'onWheel: wheel' + ]); + }); + + it('可以执行preventDefault和 stopPropagation', () => { + const eventHandler = e => { + expect(e.isDefaultPrevented()).toBe(false); + e.preventDefault(); + expect(e.isDefaultPrevented()).toBe(true); + + expect(e.isPropagationStopped()).toBe(false); + e.stopPropagation(); + expect(e.isPropagationStopped()).toBe(true); + LogUtils.log(e.type + ' handle'); + }; + const realNode = Horizon.render( +
, + container + ); + + realNode.dispatchEvent( + new MouseEvent('wheel', { + bubbles: true, + cancelable: true, + }), + ); + expect(LogUtils.getAndClear()).toEqual([ + 'wheel handle' + ]); + }); + +}) \ No newline at end of file diff --git a/scripts/__tests__/jest/testUtils.js b/scripts/__tests__/jest/testUtils.js new file mode 100644 index 00000000..8bab93d9 --- /dev/null +++ b/scripts/__tests__/jest/testUtils.js @@ -0,0 +1,26 @@ +import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection'; +import * as LogUtils from './logUtils'; + +export const stopBubbleOrCapture = (e, value) => { + LogUtils.log(value) + e.stopPropagation(); +}; + +export const getEventListeners = (dom) => { + let ret = true + let keyArray = []; + for (var key in dom) { + keyArray.push(key); + } + try { + allDelegatedNativeEvents.forEach(event => { + if (!keyArray.includes(event)) { + ret = false; + throw new Error('没有挂载全量事件'); + } + }) + } catch (error) { + + } + return ret; +}; \ No newline at end of file From a5f78bf5fb4b64b1cac368825c0f2fd8046a7712 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 10:53:51 +0800 Subject: [PATCH 07/22] Match-id-a9c8af2dec397a83d20a0c78a9d464f4ea450dd6 --- libs/extension/src/components/VTree.less | 17 ++++++++++++---- libs/extension/src/components/VTree.tsx | 25 ++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less index fe24a888..1890647d 100644 --- a/libs/extension/src/components/VTree.less +++ b/libs/extension/src/components/VTree.less @@ -1,19 +1,28 @@ @import 'assets.less'; -.tree_container { +.treeContainer { position: relative; width: 100%; height: 100%; overflow-y: auto; } -.tree_item { +.treeItem { width: 100%; position: absolute; + .componentName { + color: @component-name-color; + } + .componentKeyName { + color: @component-key-color; + } + .componentKeyValue { + color: @componentKeyValue-color; + } } -.tree_icon { - color: @gray; +.treeIcon { + color: @arrow-color; display: inline-block; width: 12px } diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 51e8b828..246a1e08 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -23,14 +23,31 @@ const indentationLength = 20; function Item({ name, style, userKey, hasChild, onCollapse, isCollapsed, id, indentation }: IItem) { const key = userKey === '' ? '' : ` key = '${userKey}'`; + const isShowKey = userKey !== ''; const showIcon = hasChild ? : ''; const onClickCollapse = () => { onCollapse(id); } return ( -
-
{showIcon}
- {name + key} +
+
+ {showIcon} +
+ + {name} + + {isShowKey && ( + <> + + {' '}key + + {'="'} + + {userKey} + + {'"'} + + )}
) } @@ -97,7 +114,7 @@ function VTree({ data }: { data: IData[] }) { } return ( -
+
{showList} {/* 确保有足够的高度 */}
From 23212774898c4ebb06f8f2b0dca9090461dfd73c Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 12:02:43 +0800 Subject: [PATCH 08/22] Match-id-c6f3be2ae5ec4f9fe7002b321e94288f8698854f --- libs/extension/src/components/VTree.less | 12 +++++++----- libs/extension/src/components/VTree.tsx | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less index 1890647d..8c86cf5b 100644 --- a/libs/extension/src/components/VTree.less +++ b/libs/extension/src/components/VTree.less @@ -10,6 +10,13 @@ .treeItem { width: 100%; position: absolute; + +.treeIcon { + color: @arrow-color; + display: inline-block; + width: 12px; + padding-left: 0.2rem; +} .componentName { color: @component-name-color; } @@ -21,8 +28,3 @@ } } -.treeIcon { - color: @arrow-color; - display: inline-block; - width: 12px -} diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 246a1e08..3a2c5360 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -22,7 +22,6 @@ const divHeight = 21; const indentationLength = 20; function Item({ name, style, userKey, hasChild, onCollapse, isCollapsed, id, indentation }: IItem) { - const key = userKey === '' ? '' : ` key = '${userKey}'`; const isShowKey = userKey !== ''; const showIcon = hasChild ? : ''; const onClickCollapse = () => { From a8a2b55de3d289e213ea2f8b8c01e3976ee653c1 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 12:03:27 +0800 Subject: [PATCH 09/22] Match-id-5ab3df85931eea4cab53417f188dce92b348df28 --- libs/extension/src/components/VTree.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/extension/src/components/VTree.less b/libs/extension/src/components/VTree.less index 8c86cf5b..bf2fb88c 100644 --- a/libs/extension/src/components/VTree.less +++ b/libs/extension/src/components/VTree.less @@ -15,7 +15,7 @@ color: @arrow-color; display: inline-block; width: 12px; - padding-left: 0.2rem; + padding-left: 0.5rem; } .componentName { color: @component-name-color; From e78feb8712f4882338aa94a884ef827828b20555 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 17:56:06 +0800 Subject: [PATCH 10/22] Match-id-56a1ac16ba7fc8bbbb7b37d9ff7b62600e952ae7 --- .../src/components/ComponentInfo.tsx | 108 ++++++++++++++++++ .../src/components/ComponentsInfo.less | 91 +++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 libs/extension/src/components/ComponentInfo.tsx create mode 100644 libs/extension/src/components/ComponentsInfo.less diff --git a/libs/extension/src/components/ComponentInfo.tsx b/libs/extension/src/components/ComponentInfo.tsx new file mode 100644 index 00000000..7e13bb42 --- /dev/null +++ b/libs/extension/src/components/ComponentInfo.tsx @@ -0,0 +1,108 @@ +import styles from './ComponentsInfo.less'; +import Eye from '../svgs/Eye'; +import Debug from '../svgs/Debug'; +import Copy from '../svgs/Copy'; +import Arrow from '../svgs/Arrow'; +import { useState } from 'horizon'; + +type IComponentInfo = { + name: string; + attrs: { + props?: IAttr[]; + context?: IAttr[]; + state?: IAttr[]; + hooks?: IAttr[]; + } +}; + +type IAttr = { + name: string; + type: string; + value: string | boolean; + indentation: number; +} + +function ComponentAttr({ name, attr }: { name: string, attr: IAttr[] }) { + const [collapsedNode, setCollapsedNode] = useState(new Set()); + const handleCollapse = (index: number) => { + const newSet = new Set(); + collapsedNode.forEach(value => { + newSet.add(value); + }); + if (newSet.has(index)) { + newSet.delete(index); + } else { + newSet.add(index); + } + setCollapsedNode(newSet); + } + + const showAttr = []; + let currentIndentation = null; + attr.forEach((item, index) => { + const indentation = item.indentation; + if (currentIndentation !== null) { + if (indentation > currentIndentation) { + return; + } else { + currentIndentation = null; + } + } + const nextItem = attr[index + 1]; + const hasChild = nextItem ? nextItem.indentation - item.indentation > 0 : false; + const isCollapsed = collapsedNode.has(index); + showAttr.push( +
(handleCollapse(index))}> + {hasChild && } + {`${item.name}`} + {' :'} + {item.value} +
+ ); + if (isCollapsed) { + currentIndentation = indentation; + } + }); + + return ( +
+
+ {name} + + + +
+
+ {showAttr} +
+
+ ) +} + +export default function ComponentInfo({ name, attrs }: IComponentInfo) { + const { state, props, context, hooks } = attrs; + return ( +
+
+ + {name} + + + + + + + +
+
+ {context && } + {props && } + {state && } + {hooks && } +
+ rendered by +
+
+
+ ) +} \ No newline at end of file diff --git a/libs/extension/src/components/ComponentsInfo.less b/libs/extension/src/components/ComponentsInfo.less new file mode 100644 index 00000000..173bec22 --- /dev/null +++ b/libs/extension/src/components/ComponentsInfo.less @@ -0,0 +1,91 @@ +@import 'assets.less'; + +.infoContainer { + display: flex; + flex-direction: column; + height: 100%; + + + .componentInfoHead { + flex: 0 0 @top-height; + display: flex; + align-items: center; + border-bottom: @divider-style; + + .name { + flex: 1 1 0; + padding: 0 1rem 0 1rem; + } + + .eye { + flex: 0 0 1rem; + padding-right: 1rem; + } + + .debug { + flex: 0 0 1rem; + padding-right: 1rem; + } + } + + + .componentInfoMain { + overflow-y: auto; + + :last-child { + border-bottom: unset; + } + + :first-child { + padding: unset; + } + + >div { + border-bottom: @divider-style; + padding: 0.5rem + } + + .attrContainer { + flex: 0 0; + + .attrHead { + display: flex; + flex-direction: row; + align-items: center; + padding: 0.5rem 0.5rem 0 0.5rem; + + .attrType { + flex: 1 1 0; + } + + + .attrCopy { + flex: 0 0 1rem; + padding-right: 1rem; + } + } + + + .attrDetail { + padding-bottom: 0.5rem; + .attrArrow { + color: @arrow-color; + width: 12px; + display: inline-block; + } + + .attrName { + color: @attr-name-color; + } + + .attrValue { + margin-left: 4px; + } + } + } + + .renderInfo { + flex: 1 1 0; + } + } +} From 4afe0f75cd78e8f668f79df978a0acc4281024d6 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 17:58:24 +0800 Subject: [PATCH 11/22] Match-id-71142987658f65616d22affbe9fbf5d524112007 --- libs/extension/src/parser/parseAttr.ts | 76 ++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 libs/extension/src/parser/parseAttr.ts diff --git a/libs/extension/src/parser/parseAttr.ts b/libs/extension/src/parser/parseAttr.ts new file mode 100644 index 00000000..0c5335d1 --- /dev/null +++ b/libs/extension/src/parser/parseAttr.ts @@ -0,0 +1,76 @@ +export function parseAttr(rootAttr: any) { + const result = []; + let indentation = 0; + const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => { + const stateType = typeof attr; + let value: any; + let showType; + let addSubState; + if (stateType === 'boolean' || + stateType === 'number' || + stateType === 'string' || + stateType === 'undefined') { + value = attr; + showType = stateType; + } else if (stateType === 'function') { + const funName = attr.name; + value = `f() ${funName}{}`; + } else if (stateType === 'symbol') { + value = attr.description; + } else if (stateType === 'object') { + if (attr === null) { + showType = 'null'; + }else if (attr instanceof Map) { + showType = 'map'; + const size = attr.size; + value = `Map(${size})`; + addSubState = () => { + attr.forEach((value, key) => { + parseSubAttr(value, parentIndentation + 2, key); + }); + } + } else if (attr instanceof Set) { + showType = 'set'; + const size = attr.size; + value = `Set(${size})`; + addSubState = () => { + let i = 0; + attr.forEach((value) => { + parseSubAttr(value, parentIndentation + 2, String(i)); + }); + i++; + }; + } else if (Array.isArray(attr)) { + showType = 'array'; + value = `Array(${attr.length})`; + addSubState = () => { + attr.forEach((value, index) => { + parseSubAttr(value, parentIndentation + 2, String(index)); + }) + } + } else { + showType = stateType; + value = '{...}'; + addSubState = () => { + Object.keys(attr).forEach((key) => { + parseSubAttr(attr[key], parentIndentation + 2, key); + }); + }; + } + } + + result.push({ + name: attrName, + type: showType, + value, + indentation: parentIndentation + 1, + }); + if (addSubState) { + addSubState(); + } + }; + Object.keys(rootAttr).forEach(key => { + parseSubAttr(rootAttr[key], indentation, key); + }); + return result; +} From f8fb9c5aef227aab13374ffba39f123b4cf82a68 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 17:58:41 +0800 Subject: [PATCH 12/22] Match-id-14bd1b592a5b4fba4420cfbf2a18d92b33b5c237 --- libs/extension/src/parser/parseVNode.ts | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 libs/extension/src/parser/parseVNode.ts diff --git a/libs/extension/src/parser/parseVNode.ts b/libs/extension/src/parser/parseVNode.ts new file mode 100644 index 00000000..bb8ad91d --- /dev/null +++ b/libs/extension/src/parser/parseVNode.ts @@ -0,0 +1,59 @@ +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"; + +const VNodeToIdMap = new Map(); +const IdToVNodeMap = new Map(); + +let uid = 0; +function generateUid () { + uid++; + return uid; +} + +function isUserComponent(tag: string) { + // TODO: 添加其他组件 + return tag === ClassComponent || tag === FunctionComponent; +} + +function getParentUserComponent(node: VNode) { + let parent = node.parent; + while(parent) { + if (isUserComponent(parent.tag)) { + break; + } + parent = parent.parent; + } + return parent; +} + +function parseTreeRoot(treeRoot: VNode) { + const result: any[] = []; + travelVNodeTree(treeRoot, (node: VNode) => { + const tag = node.tag; + if (isUserComponent(tag)) { + const id = generateUid(); + result.push(id); + const name = (node.type as Function).name; + result.push(name); + const parent = getParentUserComponent(node); + if (parent) { + const parentId = VNodeToIdMap.get(parent); + result.push(parentId); + } else { + result.push(''); + } + const key = node.key; + if (key !== null) { + result.push(key); + } else { + result.push(''); + } + VNodeToIdMap.set(node, id); + IdToVNodeMap.set(id, node); + } + }, null, treeRoot, null); + return result; +} + +export default parseTreeRoot; From ecc99bf97a78210932f28a77a599f68277f8b83b Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 17:59:13 +0800 Subject: [PATCH 13/22] Match-id-8594911a1d814fe13ec8b977d4c31972e295f3cf --- libs/extension/src/components/Search.less | 3 +++ libs/extension/src/components/Search.tsx | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 libs/extension/src/components/Search.less create mode 100644 libs/extension/src/components/Search.tsx diff --git a/libs/extension/src/components/Search.less b/libs/extension/src/components/Search.less new file mode 100644 index 00000000..617a9d26 --- /dev/null +++ b/libs/extension/src/components/Search.less @@ -0,0 +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 new file mode 100644 index 00000000..0686a79b --- /dev/null +++ b/libs/extension/src/components/Search.tsx @@ -0,0 +1,7 @@ +import styles from './Search.less'; + +export default function Search() { + return ( + + ) +} \ No newline at end of file From 3aeaf4969309dc9a99dfff8f3ce017d5bec90539 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 17:59:47 +0800 Subject: [PATCH 14/22] Match-id-7d9916e11a4b9ee889fdb62be87fe24b4d0e5f1c --- libs/extension/src/svgs/Arrow.tsx | 17 +++++++++++++++++ libs/extension/src/svgs/Copy.tsx | 8 ++++++++ libs/extension/src/svgs/Debug.tsx | 8 ++++++++ libs/extension/src/svgs/Eye.tsx | 10 ++++++++++ libs/extension/src/svgs/Select.tsx | 9 +++++++++ 5 files changed, 52 insertions(+) create mode 100644 libs/extension/src/svgs/Arrow.tsx create mode 100644 libs/extension/src/svgs/Copy.tsx create mode 100644 libs/extension/src/svgs/Debug.tsx create mode 100644 libs/extension/src/svgs/Eye.tsx create mode 100644 libs/extension/src/svgs/Select.tsx diff --git a/libs/extension/src/svgs/Arrow.tsx b/libs/extension/src/svgs/Arrow.tsx new file mode 100644 index 00000000..c315bf19 --- /dev/null +++ b/libs/extension/src/svgs/Arrow.tsx @@ -0,0 +1,17 @@ +interface IArrow { + director: 'right' | 'down' +} + +export default function Arrow({director}: IArrow) { + let d: string; + if (director === 'right') { + 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 new file mode 100644 index 00000000..865222fe --- /dev/null +++ b/libs/extension/src/svgs/Copy.tsx @@ -0,0 +1,8 @@ + +export default function Copy() { + return ( + + + + ) +} diff --git a/libs/extension/src/svgs/Debug.tsx b/libs/extension/src/svgs/Debug.tsx new file mode 100644 index 00000000..6ac25393 --- /dev/null +++ b/libs/extension/src/svgs/Debug.tsx @@ -0,0 +1,8 @@ + +export default function Debug() { + return ( + + + + ) +} diff --git a/libs/extension/src/svgs/Eye.tsx b/libs/extension/src/svgs/Eye.tsx new file mode 100644 index 00000000..f17f1fd2 --- /dev/null +++ b/libs/extension/src/svgs/Eye.tsx @@ -0,0 +1,10 @@ + +export default function Eye() { + return ( + + + + + + ) +} diff --git a/libs/extension/src/svgs/Select.tsx b/libs/extension/src/svgs/Select.tsx new file mode 100644 index 00000000..08aef9bb --- /dev/null +++ b/libs/extension/src/svgs/Select.tsx @@ -0,0 +1,9 @@ + + +export default function Select() { + return ( + + + + ) +} \ No newline at end of file From 713c9d2bb80899fa8d1c679f32dc4091a37d0d58 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 18:00:03 +0800 Subject: [PATCH 15/22] Match-id-bb93b31ea0be8b9131ced01cdca40dfd0ec15dcf --- libs/extension/src/devtools/mock.ts | 87 +++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 libs/extension/src/devtools/mock.ts diff --git a/libs/extension/src/devtools/mock.ts b/libs/extension/src/devtools/mock.ts new file mode 100644 index 00000000..0c40d06b --- /dev/null +++ b/libs/extension/src/devtools/mock.ts @@ -0,0 +1,87 @@ + +import { parseAttr } from '../parser/parseAttr'; +import parseTreeRoot from '../parser/parseVNode'; +import { VNode } from './../../../horizon/src/renderer/vnode/VNode'; +import { FunctionComponent, ClassComponent } from './../../../horizon/src/renderer/vnode/VNodeTags'; + +const mockComponentNames = ['Apple', 'Pear', 'Banana', 'Orange', 'Jenny', 'Kiwi', 'Coconut']; + +function MockVNode(tag: string, props = {}, key = null, realNode = {}) { + const vNode = new VNode(tag, props, key, realNode); + const name = mockComponentNames.shift() || 'MockComponent'; + vNode.type = {name}; + return vNode; +} + +interface IMockTree { + tag: string, + children?: IMockTree[], +} + +const tree: IMockTree = { + tag: ClassComponent, + children: [ + {tag: FunctionComponent}, + {tag: ClassComponent}, + {tag: FunctionComponent}, + { + tag: FunctionComponent, + children: [ + {tag: ClassComponent} + ] + } + ] +} + +function addOneThousandNode(node: IMockTree) { + const nodes = []; + for(let i = 0; i < 1000; i++) { + nodes.push({tag: FunctionComponent}); + } + node?.children.push({tag: ClassComponent,children: nodes}); +}; + +addOneThousandNode(tree); + +function getMockVNodeTree(tree: IMockTree, vNode: VNode) { + const children = tree.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++) { + const nextNode = children[i]; + const nextVNode = MockVNode(nextNode.tag); + nextVNode.key = String(i); + nextVNode.parent = vNode; + getMockVNodeTree(nextNode, nextVNode); + childVNode.next = nextVNode; + childVNode = nextVNode; + } + } +} +const rootVNode = MockVNode(tree.tag); +getMockVNodeTree(tree, rootVNode); + +export const mockParsedVNodeData = parseTreeRoot(rootVNode); + + +const mockState = { + str: 'jenny', + num: 3, + boolean: true, + und: undefined, + fun: () => {}, + symbol: Symbol('sym'), + map: new Map([['a', 'a']]), + set: new Set(['a', 1, 2, Symbol('bambi')]), + arr: [1,2,3,4], + obj: { + niko: {jenny: 'jenny'} + } +}; + +export const parsedMockState = parseAttr(mockState); \ No newline at end of file From 9ddccc81fa3b459fcbb0ee6a4d53791ad8279f2b Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 18:00:31 +0800 Subject: [PATCH 16/22] Match-id-a14c72ab119784f339027ab315a7f4d9b801fb80 --- libs/extension/src/components/assets.less | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 libs/extension/src/components/assets.less diff --git a/libs/extension/src/components/assets.less b/libs/extension/src/components/assets.less new file mode 100644 index 00000000..d649d367 --- /dev/null +++ b/libs/extension/src/components/assets.less @@ -0,0 +1,13 @@ +@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); +@componentKeyValue-color: rgb(26, 26, 166); +@component-attr-color: rgb(200, 0, 0); + +@top-height: 2.625rem; +@divider-width: 0.2px; +@common-font-size: 12px; + +@divider-style: @divider-color solid @divider-width; \ No newline at end of file From e6a83346124487d6315679720bc3c2f6706d85e0 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 18:01:12 +0800 Subject: [PATCH 17/22] Match-id-56773f8ccb7db62e98eae918795260754182b697 --- libs/extension/webpack.dev.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/extension/webpack.dev.js b/libs/extension/webpack.dev.js index 0ee87691..37cbf13c 100644 --- a/libs/extension/webpack.dev.js +++ b/libs/extension/webpack.dev.js @@ -1,5 +1,6 @@ const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); +const webpack = require('webpack'); // 用于 panel 页面开发 @@ -12,6 +13,7 @@ module.exports = { path: path.join(__dirname, 'dist'), filename: '[name].js' }, + devtool: 'source-map', resolve: { extensions: ['.ts', '.tsx', '.js'] }, @@ -31,13 +33,23 @@ module.exports = { "pragma": "Horizon.createElement", "pragmaFrag": "Horizon.Fragment", }]], + plugins: ['@babel/plugin-proposal-class-properties'], } } ] }, { test: /\.less/i, - use: ["style-loader", { loader: "css-loader", options: { modules: true } }, 'less-loader'], + use: [ + "style-loader", + { + loader: "css-loader", + options: { + modules: true, + + } + }, + 'less-loader'], }] }, externals: { @@ -56,5 +68,9 @@ module.exports = { filename: 'panel.html', template: './src/panel/panel.html' }), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"development"', + isDev: 'true', + }), ], }; From cb3a9a069220556d3448d9b53eb1aaed2e7c2ab4 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 18:01:36 +0800 Subject: [PATCH 18/22] Match-id-a0dd71dd3bbb10c710c5f95972e5dde0948b11f2 --- libs/extension/src/panel/panel.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/libs/extension/src/panel/panel.html b/libs/extension/src/panel/panel.html index 5aac97d6..70f60f3b 100644 --- a/libs/extension/src/panel/panel.html +++ b/libs/extension/src/panel/panel.html @@ -5,13 +5,18 @@ From 1cef14404241ff668974489de4349b4ef19a6e7d Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 29 Mar 2022 18:02:13 +0800 Subject: [PATCH 19/22] Match-id-87c94fc4d1a59f0d468ba391f244eb3e6783516b --- libs/extension/src/panel/App.less | 48 ++++++++++++++++++++++++++++ libs/extension/src/panel/App.tsx | 53 +++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 libs/extension/src/panel/App.less diff --git a/libs/extension/src/panel/App.less b/libs/extension/src/panel/App.less new file mode 100644 index 00000000..0186eec1 --- /dev/null +++ b/libs/extension/src/panel/App.less @@ -0,0 +1,48 @@ +@import '../components/assets.less'; + +.app{ + display: flex; + flex-direction: row; + height: 100%; + font-size: @common-font-size; +} + +.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; + height: 0; + } +} + +.right{ + flex: 3; + border-left: @divider-style; +} + +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 2c9014c5..73b409ac 100644 --- a/libs/extension/src/panel/App.tsx +++ b/libs/extension/src/panel/App.tsx @@ -1,22 +1,39 @@ +import {useState, useEffect} from 'horizon'; import VTree, { IData } from '../components/VTree'; - -// 临时开发用数据 -const arr = [9,"Main","","",10,"Jumbotron",9,"",11,"Button",10,"",12,"Button",10,"",13,"Button",10,"",14,"Button",10,"",15,"Button",10,"",16,"Button",10,"",17,"Row",9,"1",18,"Row",9,"2",19,"Row",9,"3",20,"Row",9,"4",21,"Row",9,"5",22,"Row",9,"6",23,"Row",9,"7",24,"Row",9,"8",25,"Row",9,"9",26,"Row",9,"10",27,"Row",9,"11",28,"Row",9,"12",29,"Row",9,"13",30,"Row",9,"14",31,"Row",9,"15",32,"Row",9,"16",33,"Row",9,"17",34,"Row",9,"18",35,"Row",9,"19",36,"Row",9,"20",37,"Row",9,"21",38,"Row",9,"22",39,"Row",9,"23",40,"Row",9,"24",41,"Row",9,"25",42,"Row",9,"26",43,"Row",9,"27",44,"Row",9,"28",45,"Row",9,"29",46,"Row",9,"30",47,"Row",9,"31",48,"Row",9,"32",49,"Row",9,"33",50,"Row",9,"34",51,"Row",9,"35",52,"Row",9,"36",53,"Row",9,"37",54,"Row",9,"38",55,"Row",9,"39",56,"Row",9,"40",57,"Row",9,"41",58,"Row",9,"42",59,"Row",9,"43",60,"Row",9,"44",61,"Row",9,"45",62,"Row",9,"46",63,"Row",9,"47",64,"Row",9,"48",65,"Row",9,"49",66,"Row",9,"50",67,"Row",9,"51",68,"Row",9,"52",69,"Row",9,"53",70,"Row",9,"54",71,"Row",9,"55",72,"Row",9,"56",73,"Row",9,"57",74,"Row",9,"58",75,"Row",9,"59",76,"Row",9,"60",77,"Row",9,"61",78,"Row",9,"62",79,"Row",9,"63",80,"Row",9,"64",81,"Row",9,"65",82,"Row",9,"66",83,"Row",9,"67",84,"Row",9,"68",85,"Row",9,"69",86,"Row",9,"70",87,"Row",9,"71",88,"Row",9,"72",89,"Row",9,"73",90,"Row",9,"74",91,"Row",9,"75",92,"Row",9,"76",93,"Row",9,"77",94,"Row",9,"78",95,"Row",9,"79",96,"Row",9,"80",97,"Row",9,"81",98,"Row",9,"82",99,"Row",9,"83",100,"Row",9,"84",101,"Row",9,"85",102,"Row",9,"86",103,"Row",9,"87",104,"Row",9,"88",105,"Row",9,"89",106,"Row",9,"90",107,"Row",9,"91",108,"Row",9,"92",109,"Row",9,"93",110,"Row",9,"94",111,"Row",9,"95",112,"Row",9,"96",113,"Row",9,"97",114,"Row",9,"98",115,"Row",9,"99",116,"Row",9,"100",117,"Row",9,"101",118,"Row",9,"102",119,"Row",9,"103",120,"Row",9,"104",121,"Row",9,"105",122,"Row",9,"106",123,"Row",9,"107",124,"Row",9,"108",125,"Row",9,"109",126,"Row",9,"110",127,"Row",9,"111",128,"Row",9,"112",129,"Row",9,"113",130,"Row",9,"114",131,"Row",9,"115",132,"Row",9,"116",133,"Row",9,"117",134,"Row",9,"118",135,"Row",9,"119",136,"Row",9,"120",137,"Row",9,"121",138,"Row",9,"122",139,"Row",9,"123",140,"Row",9,"124",141,"Row",9,"125",142,"Row",9,"126",143,"Row",9,"127",144,"Row",9,"128",145,"Row",9,"129",146,"Row",9,"130",147,"Row",9,"131",148,"Row",9,"132",149,"Row",9,"133",150,"Row",9,"134",151,"Row",9,"135",152,"Row",9,"136",153,"Row",9,"137",154,"Row",9,"138",155,"Row",9,"139",156,"Row",9,"140",157,"Row",9,"141",158,"Row",9,"142",159,"Row",9,"143",160,"Row",9,"144",161,"Row",9,"145",162,"Row",9,"146",163,"Row",9,"147",164,"Row",9,"148",165,"Row",9,"149",166,"Row",9,"150",167,"Row",9,"151",168,"Row",9,"152",169,"Row",9,"153",170,"Row",9,"154",171,"Row",9,"155",172,"Row",9,"156",173,"Row",9,"157",174,"Row",9,"158",175,"Row",9,"159",176,"Row",9,"160",177,"Row",9,"161",178,"Row",9,"162",179,"Row",9,"163",180,"Row",9,"164",181,"Row",9,"165",182,"Row",9,"166",183,"Row",9,"167",184,"Row",9,"168",185,"Row",9,"169",186,"Row",9,"170",187,"Row",9,"171",188,"Row",9,"172",189,"Row",9,"173",190,"Row",9,"174",191,"Row",9,"175",192,"Row",9,"176",193,"Row",9,"177",194,"Row",9,"178",195,"Row",9,"179",196,"Row",9,"180",197,"Row",9,"181",198,"Row",9,"182",199,"Row",9,"183",200,"Row",9,"184",201,"Row",9,"185",202,"Row",9,"186",203,"Row",9,"187",204,"Row",9,"188",205,"Row",9,"189",206,"Row",9,"190",207,"Row",9,"191",208,"Row",9,"192",209,"Row",9,"193",210,"Row",9,"194",211,"Row",9,"195",212,"Row",9,"196",213,"Row",9,"197",214,"Row",9,"198",215,"Row",9,"199",216,"Row",9,"200",217,"Row",9,"201",218,"Row",9,"202",219,"Row",9,"203",220,"Row",9,"204",221,"Row",9,"205",222,"Row",9,"206",223,"Row",9,"207",224,"Row",9,"208",225,"Row",9,"209",226,"Row",9,"210",227,"Row",9,"211",228,"Row",9,"212",229,"Row",9,"213",230,"Row",9,"214",231,"Row",9,"215",232,"Row",9,"216",233,"Row",9,"217",234,"Row",9,"218",235,"Row",9,"219",236,"Row",9,"220",237,"Row",9,"221",238,"Row",9,"222",239,"Row",9,"223",240,"Row",9,"224",241,"Row",9,"225",242,"Row",9,"226",243,"Row",9,"227",244,"Row",9,"228",245,"Row",9,"229",246,"Row",9,"230",247,"Row",9,"231",248,"Row",9,"232",249,"Row",9,"233",250,"Row",9,"234",251,"Row",9,"235",252,"Row",9,"236",253,"Row",9,"237",254,"Row",9,"238",255,"Row",9,"239",256,"Row",9,"240",257,"Row",9,"241",258,"Row",9,"242",259,"Row",9,"243",260,"Row",9,"244",261,"Row",9,"245",262,"Row",9,"246",263,"Row",9,"247",264,"Row",9,"248",265,"Row",9,"249",266,"Row",9,"250",267,"Row",9,"251",268,"Row",9,"252",269,"Row",9,"253",270,"Row",9,"254",271,"Row",9,"255",272,"Row",9,"256",273,"Row",9,"257",274,"Row",9,"258",275,"Row",9,"259",276,"Row",9,"260",277,"Row",9,"261",278,"Row",9,"262",279,"Row",9,"263",280,"Row",9,"264",281,"Row",9,"265",282,"Row",9,"266",283,"Row",9,"267",284,"Row",9,"268",285,"Row",9,"269",286,"Row",9,"270",287,"Row",9,"271",288,"Row",9,"272",289,"Row",9,"273",290,"Row",9,"274",291,"Row",9,"275",292,"Row",9,"276",293,"Row",9,"277",294,"Row",9,"278",295,"Row",9,"279",296,"Row",9,"280",297,"Row",9,"281",298,"Row",9,"282",299,"Row",9,"283",300,"Row",9,"284",301,"Row",9,"285",302,"Row",9,"286",303,"Row",9,"287",304,"Row",9,"288",305,"Row",9,"289",306,"Row",9,"290",307,"Row",9,"291",308,"Row",9,"292",309,"Row",9,"293",310,"Row",9,"294",311,"Row",9,"295",312,"Row",9,"296",313,"Row",9,"297",314,"Row",9,"298",315,"Row",9,"299",316,"Row",9,"300",317,"Row",9,"301",318,"Row",9,"302",319,"Row",9,"303",320,"Row",9,"304",321,"Row",9,"305",322,"Row",9,"306",323,"Row",9,"307",324,"Row",9,"308",325,"Row",9,"309",326,"Row",9,"310",327,"Row",9,"311",328,"Row",9,"312",329,"Row",9,"313",330,"Row",9,"314",331,"Row",9,"315",332,"Row",9,"316",333,"Row",9,"317",334,"Row",9,"318",335,"Row",9,"319",336,"Row",9,"320",337,"Row",9,"321",338,"Row",9,"322",339,"Row",9,"323",340,"Row",9,"324",341,"Row",9,"325",342,"Row",9,"326",343,"Row",9,"327",344,"Row",9,"328",345,"Row",9,"329",346,"Row",9,"330",347,"Row",9,"331",348,"Row",9,"332",349,"Row",9,"333",350,"Row",9,"334",351,"Row",9,"335",352,"Row",9,"336",353,"Row",9,"337",354,"Row",9,"338",355,"Row",9,"339",356,"Row",9,"340",357,"Row",9,"341",358,"Row",9,"342",359,"Row",9,"343",360,"Row",9,"344",361,"Row",9,"345",362,"Row",9,"346",363,"Row",9,"347",364,"Row",9,"348",365,"Row",9,"349",366,"Row",9,"350",367,"Row",9,"351",368,"Row",9,"352",369,"Row",9,"353",370,"Row",9,"354",371,"Row",9,"355",372,"Row",9,"356",373,"Row",9,"357",374,"Row",9,"358",375,"Row",9,"359",376,"Row",9,"360",377,"Row",9,"361",378,"Row",9,"362",379,"Row",9,"363",380,"Row",9,"364",381,"Row",9,"365",382,"Row",9,"366",383,"Row",9,"367",384,"Row",9,"368",385,"Row",9,"369",386,"Row",9,"370",387,"Row",9,"371",388,"Row",9,"372",389,"Row",9,"373",390,"Row",9,"374",391,"Row",9,"375",392,"Row",9,"376",393,"Row",9,"377",394,"Row",9,"378",395,"Row",9,"379",396,"Row",9,"380",397,"Row",9,"381",398,"Row",9,"382",399,"Row",9,"383",400,"Row",9,"384",401,"Row",9,"385",402,"Row",9,"386",403,"Row",9,"387",404,"Row",9,"388",405,"Row",9,"389",406,"Row",9,"390",407,"Row",9,"391",408,"Row",9,"392",409,"Row",9,"393",410,"Row",9,"394",411,"Row",9,"395",412,"Row",9,"396",413,"Row",9,"397",414,"Row",9,"398",415,"Row",9,"399",416,"Row",9,"400",417,"Row",9,"401",418,"Row",9,"402",419,"Row",9,"403",420,"Row",9,"404",421,"Row",9,"405",422,"Row",9,"406",423,"Row",9,"407",424,"Row",9,"408",425,"Row",9,"409",426,"Row",9,"410",427,"Row",9,"411",428,"Row",9,"412",429,"Row",9,"413",430,"Row",9,"414",431,"Row",9,"415",432,"Row",9,"416",433,"Row",9,"417",434,"Row",9,"418",435,"Row",9,"419",436,"Row",9,"420",437,"Row",9,"421",438,"Row",9,"422",439,"Row",9,"423",440,"Row",9,"424",441,"Row",9,"425",442,"Row",9,"426",443,"Row",9,"427",444,"Row",9,"428",445,"Row",9,"429",446,"Row",9,"430",447,"Row",9,"431",448,"Row",9,"432",449,"Row",9,"433",450,"Row",9,"434",451,"Row",9,"435",452,"Row",9,"436",453,"Row",9,"437",454,"Row",9,"438",455,"Row",9,"439",456,"Row",9,"440",457,"Row",9,"441",458,"Row",9,"442",459,"Row",9,"443",460,"Row",9,"444",461,"Row",9,"445",462,"Row",9,"446",463,"Row",9,"447",464,"Row",9,"448",465,"Row",9,"449",466,"Row",9,"450",467,"Row",9,"451",468,"Row",9,"452",469,"Row",9,"453",470,"Row",9,"454",471,"Row",9,"455",472,"Row",9,"456",473,"Row",9,"457",474,"Row",9,"458",475,"Row",9,"459",476,"Row",9,"460",477,"Row",9,"461",478,"Row",9,"462",479,"Row",9,"463",480,"Row",9,"464",481,"Row",9,"465",482,"Row",9,"466",483,"Row",9,"467",484,"Row",9,"468",485,"Row",9,"469",486,"Row",9,"470",487,"Row",9,"471",488,"Row",9,"472",489,"Row",9,"473",490,"Row",9,"474",491,"Row",9,"475",492,"Row",9,"476",493,"Row",9,"477",494,"Row",9,"478",495,"Row",9,"479",496,"Row",9,"480",497,"Row",9,"481",498,"Row",9,"482",499,"Row",9,"483",500,"Row",9,"484",501,"Row",9,"485",502,"Row",9,"486",503,"Row",9,"487",504,"Row",9,"488",505,"Row",9,"489",506,"Row",9,"490",507,"Row",9,"491",508,"Row",9,"492",509,"Row",9,"493",510,"Row",9,"494",511,"Row",9,"495",512,"Row",9,"496",513,"Row",9,"497",514,"Row",9,"498",515,"Row",9,"499",516,"Row",9,"500",517,"Row",9,"501",518,"Row",9,"502",519,"Row",9,"503",520,"Row",9,"504",521,"Row",9,"505",522,"Row",9,"506",523,"Row",9,"507",524,"Row",9,"508",525,"Row",9,"509",526,"Row",9,"510",527,"Row",9,"511",528,"Row",9,"512",529,"Row",9,"513",530,"Row",9,"514",531,"Row",9,"515",532,"Row",9,"516",533,"Row",9,"517",534,"Row",9,"518",535,"Row",9,"519",536,"Row",9,"520",537,"Row",9,"521",538,"Row",9,"522",539,"Row",9,"523",540,"Row",9,"524",541,"Row",9,"525",542,"Row",9,"526",543,"Row",9,"527",544,"Row",9,"528",545,"Row",9,"529",546,"Row",9,"530",547,"Row",9,"531",548,"Row",9,"532",549,"Row",9,"533",550,"Row",9,"534",551,"Row",9,"535",552,"Row",9,"536",553,"Row",9,"537",554,"Row",9,"538",555,"Row",9,"539",556,"Row",9,"540",557,"Row",9,"541",558,"Row",9,"542",559,"Row",9,"543",560,"Row",9,"544",561,"Row",9,"545",562,"Row",9,"546",563,"Row",9,"547",564,"Row",9,"548",565,"Row",9,"549",566,"Row",9,"550",567,"Row",9,"551",568,"Row",9,"552",569,"Row",9,"553",570,"Row",9,"554",571,"Row",9,"555",572,"Row",9,"556",573,"Row",9,"557",574,"Row",9,"558",575,"Row",9,"559",576,"Row",9,"560",577,"Row",9,"561",578,"Row",9,"562",579,"Row",9,"563",580,"Row",9,"564",581,"Row",9,"565",582,"Row",9,"566",583,"Row",9,"567",584,"Row",9,"568",585,"Row",9,"569",586,"Row",9,"570",587,"Row",9,"571",588,"Row",9,"572",589,"Row",9,"573",590,"Row",9,"574",591,"Row",9,"575",592,"Row",9,"576",593,"Row",9,"577",594,"Row",9,"578",595,"Row",9,"579",596,"Row",9,"580",597,"Row",9,"581",598,"Row",9,"582",599,"Row",9,"583",600,"Row",9,"584",601,"Row",9,"585",602,"Row",9,"586",603,"Row",9,"587",604,"Row",9,"588",605,"Row",9,"589",606,"Row",9,"590",607,"Row",9,"591",608,"Row",9,"592",609,"Row",9,"593",610,"Row",9,"594",611,"Row",9,"595",612,"Row",9,"596",613,"Row",9,"597",614,"Row",9,"598",615,"Row",9,"599",616,"Row",9,"600",617,"Row",9,"601",618,"Row",9,"602",619,"Row",9,"603",620,"Row",9,"604",621,"Row",9,"605",622,"Row",9,"606",623,"Row",9,"607",624,"Row",9,"608",625,"Row",9,"609",626,"Row",9,"610",627,"Row",9,"611",628,"Row",9,"612",629,"Row",9,"613",630,"Row",9,"614",631,"Row",9,"615",632,"Row",9,"616",633,"Row",9,"617",634,"Row",9,"618",635,"Row",9,"619",636,"Row",9,"620",637,"Row",9,"621",638,"Row",9,"622",639,"Row",9,"623",640,"Row",9,"624",641,"Row",9,"625",642,"Row",9,"626",643,"Row",9,"627",644,"Row",9,"628",645,"Row",9,"629",646,"Row",9,"630",647,"Row",9,"631",648,"Row",9,"632",649,"Row",9,"633",650,"Row",9,"634",651,"Row",9,"635",652,"Row",9,"636",653,"Row",9,"637",654,"Row",9,"638",655,"Row",9,"639",656,"Row",9,"640",657,"Row",9,"641",658,"Row",9,"642",659,"Row",9,"643",660,"Row",9,"644",661,"Row",9,"645",662,"Row",9,"646",663,"Row",9,"647",664,"Row",9,"648",665,"Row",9,"649",666,"Row",9,"650",667,"Row",9,"651",668,"Row",9,"652",669,"Row",9,"653",670,"Row",9,"654",671,"Row",9,"655",672,"Row",9,"656",673,"Row",9,"657",674,"Row",9,"658",675,"Row",9,"659",676,"Row",9,"660",677,"Row",9,"661",678,"Row",9,"662",679,"Row",9,"663",680,"Row",9,"664",681,"Row",9,"665",682,"Row",9,"666",683,"Row",9,"667",684,"Row",9,"668",685,"Row",9,"669",686,"Row",9,"670",687,"Row",9,"671",688,"Row",9,"672",689,"Row",9,"673",690,"Row",9,"674",691,"Row",9,"675",692,"Row",9,"676",693,"Row",9,"677",694,"Row",9,"678",695,"Row",9,"679",696,"Row",9,"680",697,"Row",9,"681",698,"Row",9,"682",699,"Row",9,"683",700,"Row",9,"684",701,"Row",9,"685",702,"Row",9,"686",703,"Row",9,"687",704,"Row",9,"688",705,"Row",9,"689",706,"Row",9,"690",707,"Row",9,"691",708,"Row",9,"692",709,"Row",9,"693",710,"Row",9,"694",711,"Row",9,"695",712,"Row",9,"696",713,"Row",9,"697",714,"Row",9,"698",715,"Row",9,"699",716,"Row",9,"700",717,"Row",9,"701",718,"Row",9,"702",719,"Row",9,"703",720,"Row",9,"704",721,"Row",9,"705",722,"Row",9,"706",723,"Row",9,"707",724,"Row",9,"708",725,"Row",9,"709",726,"Row",9,"710",727,"Row",9,"711",728,"Row",9,"712",729,"Row",9,"713",730,"Row",9,"714",731,"Row",9,"715",732,"Row",9,"716",733,"Row",9,"717",734,"Row",9,"718",735,"Row",9,"719",736,"Row",9,"720",737,"Row",9,"721",738,"Row",9,"722",739,"Row",9,"723",740,"Row",9,"724",741,"Row",9,"725",742,"Row",9,"726",743,"Row",9,"727",744,"Row",9,"728",745,"Row",9,"729",746,"Row",9,"730",747,"Row",9,"731",748,"Row",9,"732",749,"Row",9,"733",750,"Row",9,"734",751,"Row",9,"735",752,"Row",9,"736",753,"Row",9,"737",754,"Row",9,"738",755,"Row",9,"739",756,"Row",9,"740",757,"Row",9,"741",758,"Row",9,"742",759,"Row",9,"743",760,"Row",9,"744",761,"Row",9,"745",762,"Row",9,"746",763,"Row",9,"747",764,"Row",9,"748",765,"Row",9,"749",766,"Row",9,"750",767,"Row",9,"751",768,"Row",9,"752",769,"Row",9,"753",770,"Row",9,"754",771,"Row",9,"755",772,"Row",9,"756",773,"Row",9,"757",774,"Row",9,"758",775,"Row",9,"759",776,"Row",9,"760",777,"Row",9,"761",778,"Row",9,"762",779,"Row",9,"763",780,"Row",9,"764",781,"Row",9,"765",782,"Row",9,"766",783,"Row",9,"767",784,"Row",9,"768",785,"Row",9,"769",786,"Row",9,"770",787,"Row",9,"771",788,"Row",9,"772",789,"Row",9,"773",790,"Row",9,"774",791,"Row",9,"775",792,"Row",9,"776",793,"Row",9,"777",794,"Row",9,"778",795,"Row",9,"779",796,"Row",9,"780",797,"Row",9,"781",798,"Row",9,"782",799,"Row",9,"783",800,"Row",9,"784",801,"Row",9,"785",802,"Row",9,"786",803,"Row",9,"787",804,"Row",9,"788",805,"Row",9,"789",806,"Row",9,"790",807,"Row",9,"791",808,"Row",9,"792",809,"Row",9,"793",810,"Row",9,"794",811,"Row",9,"795",812,"Row",9,"796",813,"Row",9,"797",814,"Row",9,"798",815,"Row",9,"799",816,"Row",9,"800",817,"Row",9,"801",818,"Row",9,"802",819,"Row",9,"803",820,"Row",9,"804",821,"Row",9,"805",822,"Row",9,"806",823,"Row",9,"807",824,"Row",9,"808",825,"Row",9,"809",826,"Row",9,"810",827,"Row",9,"811",828,"Row",9,"812",829,"Row",9,"813",830,"Row",9,"814",831,"Row",9,"815",832,"Row",9,"816",833,"Row",9,"817",834,"Row",9,"818",835,"Row",9,"819",836,"Row",9,"820",837,"Row",9,"821",838,"Row",9,"822",839,"Row",9,"823",840,"Row",9,"824",841,"Row",9,"825",842,"Row",9,"826",843,"Row",9,"827",844,"Row",9,"828",845,"Row",9,"829",846,"Row",9,"830",847,"Row",9,"831",848,"Row",9,"832",849,"Row",9,"833",850,"Row",9,"834",851,"Row",9,"835",852,"Row",9,"836",853,"Row",9,"837",854,"Row",9,"838",855,"Row",9,"839",856,"Row",9,"840",857,"Row",9,"841",858,"Row",9,"842",859,"Row",9,"843",860,"Row",9,"844",861,"Row",9,"845",862,"Row",9,"846",863,"Row",9,"847",864,"Row",9,"848",865,"Row",9,"849",866,"Row",9,"850",867,"Row",9,"851",868,"Row",9,"852",869,"Row",9,"853",870,"Row",9,"854",871,"Row",9,"855",872,"Row",9,"856",873,"Row",9,"857",874,"Row",9,"858",875,"Row",9,"859",876,"Row",9,"860",877,"Row",9,"861",878,"Row",9,"862",879,"Row",9,"863",880,"Row",9,"864",881,"Row",9,"865",882,"Row",9,"866",883,"Row",9,"867",884,"Row",9,"868",885,"Row",9,"869",886,"Row",9,"870",887,"Row",9,"871",888,"Row",9,"872",889,"Row",9,"873",890,"Row",9,"874",891,"Row",9,"875",892,"Row",9,"876",893,"Row",9,"877",894,"Row",9,"878",895,"Row",9,"879",896,"Row",9,"880",897,"Row",9,"881",898,"Row",9,"882",899,"Row",9,"883",900,"Row",9,"884",901,"Row",9,"885",902,"Row",9,"886",903,"Row",9,"887",904,"Row",9,"888",905,"Row",9,"889",906,"Row",9,"890",907,"Row",9,"891",908,"Row",9,"892",909,"Row",9,"893",910,"Row",9,"894",911,"Row",9,"895",912,"Row",9,"896",913,"Row",9,"897",914,"Row",9,"898",915,"Row",9,"899",916,"Row",9,"900",917,"Row",9,"901",918,"Row",9,"902",919,"Row",9,"903",920,"Row",9,"904",921,"Row",9,"905",922,"Row",9,"906",923,"Row",9,"907",924,"Row",9,"908",925,"Row",9,"909",926,"Row",9,"910",927,"Row",9,"911",928,"Row",9,"912",929,"Row",9,"913",930,"Row",9,"914",931,"Row",9,"915",932,"Row",9,"916",933,"Row",9,"917",934,"Row",9,"918",935,"Row",9,"919",936,"Row",9,"920",937,"Row",9,"921",938,"Row",9,"922",939,"Row",9,"923",940,"Row",9,"924",941,"Row",9,"925",942,"Row",9,"926",943,"Row",9,"927",944,"Row",9,"928",945,"Row",9,"929",946,"Row",9,"930",947,"Row",9,"931",948,"Row",9,"932",949,"Row",9,"933",950,"Row",9,"934",951,"Row",9,"935",952,"Row",9,"936",953,"Row",9,"937",954,"Row",9,"938",955,"Row",9,"939",956,"Row",9,"940",957,"Row",9,"941",958,"Row",9,"942",959,"Row",9,"943",960,"Row",9,"944",961,"Row",9,"945",962,"Row",9,"946",963,"Row",9,"947",964,"Row",9,"948",965,"Row",9,"949",966,"Row",9,"950",967,"Row",9,"951",968,"Row",9,"952",969,"Row",9,"953",970,"Row",9,"954",971,"Row",9,"955",972,"Row",9,"956",973,"Row",9,"957",974,"Row",9,"958",975,"Row",9,"959",976,"Row",9,"960",977,"Row",9,"961",978,"Row",9,"962",979,"Row",9,"963",980,"Row",9,"964",981,"Row",9,"965",982,"Row",9,"966",983,"Row",9,"967",984,"Row",9,"968",985,"Row",9,"969",986,"Row",9,"970",987,"Row",9,"971",988,"Row",9,"972",989,"Row",9,"973",990,"Row",9,"974",991,"Row",9,"975",992,"Row",9,"976",993,"Row",9,"977",994,"Row",9,"978",995,"Row",9,"979",996,"Row",9,"980",997,"Row",9,"981",998,"Row",9,"982",999,"Row",9,"983",1000,"Row",9,"984",1001,"Row",9,"985",1002,"Row",9,"986",1003,"Row",9,"987",1004,"Row",9,"988",1005,"Row",9,"989",1006,"Row",9,"990",1007,"Row",9,"991",1008,"Row",9,"992",1009,"Row",9,"993",1010,"Row",9,"994",1011,"Row",9,"995",1012,"Row",9,"996",1013,"Row",9,"997",1014,"Row",9,"998",1015,"Row",9,"999",1016,"Row",9,"1000"]; +import Search from '../components/Search'; +import ComponentInfo from '../components/ComponentInfo'; +import styles from './App.less'; +import Select from '../svgs/Select'; +import { mockParsedVNodeData, parsedMockState } from '../devtools/mock'; function App() { + const [parsedVNodeData, setParsedVNodeData] = useState([]); + const [componentInfo, setComponentInfo] = useState({name: null, attrs: {}}); + useEffect(() => { + if (isDev) { + setParsedVNodeData(mockParsedVNodeData); + setComponentInfo({ + name: 'Demo', + attrs: { + state: parsedMockState, + props: parsedMockState, + }, + }) + } + }, []); const idIndentationMap: { [id: string]: number; } = {}; const data: IData[] = []; let i = 0; - while(i < arr.length) { - const id = arr[i] as string; + while(i < parsedVNodeData.length) { + const id = parsedVNodeData[i] as string; i++; - const name = arr[i] as string; + const name = parsedVNodeData[i] as string; i++; - const parentId = arr[i] as string; + const parentId = parsedVNodeData[i] as string; i++; - const userKey = arr[i] as string; + const userKey = parsedVNodeData[i] as string; i++; const indentation = parentId === '' ? 0 : idIndentationMap[parentId] + 1; idIndentationMap[id] = indentation; @@ -26,8 +43,24 @@ function App() { data.push(item); } return ( -
- +
+
+
+
+ + ) } \ No newline at end of file diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index 6e594539..91a7c42e 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -1,6 +1,7 @@ import { useState } from 'horizon'; import styles from './VTree.less'; import Arrow from '../svgs/Arrow'; +import { createRegExp } from './../utils'; export interface IData { id: string; @@ -16,6 +17,7 @@ type IItem = { onClick: (id: string) => void, isCollapsed: boolean, isSelect: boolean, + highlightValue: string, } & IData // TODO: 计算可以展示的最多数量,并且监听显示器高度变化修改数值 @@ -35,6 +37,7 @@ function Item(props: IItem) { indentation, onClick, isSelect, + highlightValue, } = props; const isShowKey = userKey !== ''; const showIcon = hasChild ? : ''; @@ -49,13 +52,30 @@ function Item(props: IItem) { itemAttr.tabIndex = 0; itemAttr.className = styles.treeItem + ' ' + styles.select } + const reg = createRegExp(highlightValue); + const heightCharacters = name.match(reg); + let showName; + if (heightCharacters) { + let cutName = name; + showName = []; + // 高亮第一次匹配即可 + const char = heightCharacters[0]; + let index = name.search(char); + const notHighlightStr = cutName.slice(0, index); + showName.push(notHighlightStr); + showName.push({char}); + cutName = cutName.slice(index + char.length); + showName.push(cutName); + } else { + showName = name; + } return (
{showIcon}
- {name} + {showName} {isShowKey && ( <> @@ -73,7 +93,7 @@ function Item(props: IItem) { ) } -function VTree({ data }: { data: IData[] }) { +function VTree({ data, highlightValue }: { data: IData[], highlightValue: string }) { const [scrollTop, setScrollTop] = useState(0); const [collapseNode, setCollapseNode] = useState(new Set()); const [selectItem, setSelectItem] = useState(); @@ -124,6 +144,7 @@ function VTree({ data }: { data: IData[] }) { onClick={handleClickItem} isCollapsed={isCollapsed} isSelect={id === selectItem} + highlightValue={highlightValue} {...item} /> ) } diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx index 73b409ac..871ad0ad 100644 --- a/libs/extension/src/panel/App.tsx +++ b/libs/extension/src/panel/App.tsx @@ -9,6 +9,7 @@ import { mockParsedVNodeData, parsedMockState } from '../devtools/mock'; function App() { const [parsedVNodeData, setParsedVNodeData] = useState([]); const [componentInfo, setComponentInfo] = useState({name: null, attrs: {}}); + const [filterValue, setFilterValue] = useState(''); useEffect(() => { if (isDev) { setParsedVNodeData(mockParsedVNodeData); @@ -42,6 +43,11 @@ function App() { }; data.push(item); } + + const handleSearchChange = (str: string) => { + setFilterValue(str); + } + return (
@@ -51,11 +57,11 @@ function App() {
- +
- +
diff --git a/libs/extension/src/utils.ts b/libs/extension/src/utils.ts new file mode 100644 index 00000000..727c672d --- /dev/null +++ b/libs/extension/src/utils.ts @@ -0,0 +1,15 @@ + +export function createRegExp(expression: string){ + let str = expression; + if (str[0] === '/') { + str = str.slice(1); + } + if (str[str.length - 1] === '/') { + str = str.slice(0, str.length - 1); + } + try { + return new RegExp(str, 'i'); + }catch(err) { + return null; + } +} \ No newline at end of file From e13f3245bd2258b6e58989e28d5e93c8bf356a3f Mon Sep 17 00:00:00 2001 From: * <8> Date: Thu, 31 Mar 2022 10:25:52 +0800 Subject: [PATCH 22/22] Match-id-7133f2734eacafd1263023fbe3921097f915d497 --- .../src/components/ComponentInfo.tsx | 6 +-- .../src/components/ComponentsInfo.less | 1 + libs/extension/src/components/Search.less | 2 +- libs/extension/src/components/Search.tsx | 4 +- libs/extension/src/components/VTree.less | 9 ++-- libs/extension/src/components/VTree.tsx | 39 +++++++------- libs/extension/src/components/assets.less | 6 +-- libs/extension/src/devtools/mock.ts | 47 +++++++++------- libs/extension/src/panel/App.less | 17 +++--- libs/extension/src/panel/App.tsx | 16 +++--- libs/extension/src/panel/index.tsx | 4 +- libs/extension/src/panel/panel.html | 53 +++++++++++-------- libs/extension/src/parser/parseAttr.ts | 19 ++++--- libs/extension/src/parser/parseVNode.ts | 9 ++-- libs/extension/src/svgs/Arrow.tsx | 8 +-- libs/extension/src/svgs/Copy.tsx | 4 +- libs/extension/src/svgs/Debug.tsx | 2 +- libs/extension/src/svgs/Eye.tsx | 6 +-- libs/extension/src/svgs/Select.tsx | 3 +- libs/extension/src/utils.ts | 4 +- libs/extension/webpack.dev.js | 18 +++---- 21 files changed, 155 insertions(+), 122 deletions(-) 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'], }] },