Improve ui & performance

This commit is contained in:
Thomas Nordquist
2019-01-03 11:14:38 +01:00
parent 2b7e9a5ef7
commit 87dafc9c89
18 changed files with 676 additions and 364 deletions

View File

@@ -8,14 +8,21 @@
margin: 0;
padding: 0;
}
@keyframes example {
0% {background-color: inherit;}
25% {background-color: #3f51b5;}
50% {background-color: #3f51b5;}
100% {background-color: inherit;}
}
</style>
<script src="http://localhost:35729/livereload.js"></script>
</head>
<body>
<div id="example"></div>
<!-- Dependencies -->
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<!-- <script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script> -->
<!-- Main -->
<script src="./build/bundle.js"></script>

441
app/package-lock.json generated
View File

@@ -650,6 +650,42 @@
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
},
"body": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz",
"integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=",
"dev": true,
"requires": {
"continuable-cache": "^0.3.1",
"error": "^7.0.0",
"raw-body": "~1.1.0",
"safe-json-parse": "~1.0.1"
},
"dependencies": {
"bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
"integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=",
"dev": true
},
"raw-body": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz",
"integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=",
"dev": true,
"requires": {
"bytes": "1",
"string_decoder": "0.10"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
}
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
@@ -1084,6 +1120,12 @@
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"continuable-cache": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz",
"integrity": "sha1-vXJ6f67XfnH/OYWskzUakSczrQ8=",
"dev": true
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
@@ -1204,36 +1246,6 @@
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
},
"cytoscape": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.3.1.tgz",
"integrity": "sha512-3IIh63Oz/dtiO1ibPb9CWHBPo90JZg/74EI+vDL/hamaP01bQ7Yx1r9oiS4AyTedvlUCio5j7Bu3NtRWxJHryw==",
"requires": {
"heap": "^0.2.6",
"lodash.debounce": "^4.0.8"
}
},
"cytoscape-cola": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/cytoscape-cola/-/cytoscape-cola-2.3.0.tgz",
"integrity": "sha512-xblxlCH8JXGLdH6XMUBJY3xBUJuL1rLy8bLMGvqkvoPHSbBfV+/klMWoqwervVKWOmFHPwUdihtxy8stG4RM5g==",
"requires": {
"webcola": "^3.3.6"
}
},
"cytoscape-dagre": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/cytoscape-dagre/-/cytoscape-dagre-2.2.2.tgz",
"integrity": "sha512-zsg36qNwua/L2stJSWkcbSDcvW3E6VZf6KRe6aLnQJxuXuz89tMqI5EVYVKEcNBgzTEzFMFv0PE3T0nD4m6VDw==",
"requires": {
"dagre": "^0.8.2"
}
},
"cytoscape-expand-collapse": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/cytoscape-expand-collapse/-/cytoscape-expand-collapse-3.1.2.tgz",
"integrity": "sha512-yClfo0z2IqEkhlOipUh85k1kJGSgxaSKs4e30KO1mDIXPKOnvL/iQhVXmNt9+KmuIGg9YtNt09P0j3F4BN3WsA=="
},
"d": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
@@ -1242,39 +1254,6 @@
"es5-ext": "^0.10.9"
}
},
"d3-dispatch": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz",
"integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g=="
},
"d3-drag": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz",
"integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==",
"requires": {
"d3-dispatch": "1",
"d3-selection": "1"
}
},
"d3-selection": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.2.tgz",
"integrity": "sha512-OoXdv1nZ7h2aKMVg3kaUFbLLK5jXUFAMLD/Tu5JA96mjf8f2a9ZUESGY+C36t8R1WFeWk/e55hy54Ml2I62CRQ=="
},
"d3-timer": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz",
"integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg=="
},
"dagre": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.4.tgz",
"integrity": "sha512-Dj0csFDrWYKdavwROb9FccHfTC4fJbyF/oJdL9LNZJ8WUvl968P6PAKEriGqfbdArVJEmmfA+UyumgWEwcHU6A==",
"requires": {
"graphlib": "^2.1.7",
"lodash": "^4.17.4"
}
},
"date-now": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
@@ -1500,11 +1479,6 @@
"minimalistic-crypto-utils": "^1.0.0"
}
},
"emitter-component": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz",
"integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY="
},
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
@@ -1589,6 +1563,16 @@
"prr": "~1.0.1"
}
},
"error": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/error/-/error-7.0.2.tgz",
"integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=",
"dev": true,
"requires": {
"string-template": "~0.2.1",
"xtend": "~4.0.0"
}
},
"es5-ext": {
"version": "0.10.46",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
@@ -2044,21 +2028,25 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
"version": "1.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"optional": true
},
"are-we-there-yet": {
"version": "1.1.4",
"bundled": true,
"resolved": false,
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
"optional": true,
"requires": {
"delegates": "^1.0.0",
@@ -2067,11 +2055,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"resolved": false,
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"resolved": false,
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -2079,29 +2069,35 @@
},
"chownr": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=",
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"resolved": false,
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"resolved": false,
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"core-util-is": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"optional": true
},
"debug": {
"version": "2.6.9",
"bundled": true,
"resolved": false,
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"optional": true,
"requires": {
"ms": "2.0.0"
@@ -2109,22 +2105,26 @@
},
"deep-extend": {
"version": "0.5.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"optional": true,
"requires": {
"minipass": "^2.2.1"
@@ -2132,12 +2132,14 @@
},
"fs.realpath": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"optional": true
},
"gauge": {
"version": "2.7.4",
"bundled": true,
"resolved": false,
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"optional": true,
"requires": {
"aproba": "^1.0.3",
@@ -2152,7 +2154,8 @@
},
"glob": {
"version": "7.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"optional": true,
"requires": {
"fs.realpath": "^1.0.0",
@@ -2165,12 +2168,14 @@
},
"has-unicode": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"optional": true
},
"iconv-lite": {
"version": "0.4.21",
"bundled": true,
"resolved": false,
"integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==",
"optional": true,
"requires": {
"safer-buffer": "^2.1.0"
@@ -2178,7 +2183,8 @@
},
"ignore-walk": {
"version": "3.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"optional": true,
"requires": {
"minimatch": "^3.0.4"
@@ -2186,7 +2192,8 @@
},
"inflight": {
"version": "1.0.6",
"bundled": true,
"resolved": false,
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"optional": true,
"requires": {
"once": "^1.3.0",
@@ -2195,39 +2202,46 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ini": {
"version": "1.3.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"isarray": {
"version": "1.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"optional": true
},
"minimatch": {
"version": "3.0.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -2235,7 +2249,8 @@
},
"minizlib": {
"version": "1.1.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==",
"optional": true,
"requires": {
"minipass": "^2.2.1"
@@ -2243,19 +2258,22 @@
},
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"optional": true
},
"needle": {
"version": "2.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==",
"optional": true,
"requires": {
"debug": "^2.1.2",
@@ -2265,7 +2283,8 @@
},
"node-pre-gyp": {
"version": "0.10.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==",
"optional": true,
"requires": {
"detect-libc": "^1.0.2",
@@ -2282,7 +2301,8 @@
},
"nopt": {
"version": "4.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"optional": true,
"requires": {
"abbrev": "1",
@@ -2291,12 +2311,14 @@
},
"npm-bundled": {
"version": "1.0.3",
"bundled": true,
"resolved": false,
"integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==",
"optional": true
},
"npm-packlist": {
"version": "1.1.10",
"bundled": true,
"resolved": false,
"integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==",
"optional": true,
"requires": {
"ignore-walk": "^3.0.1",
@@ -2305,7 +2327,8 @@
},
"npmlog": {
"version": "4.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"optional": true,
"requires": {
"are-we-there-yet": "~1.1.2",
@@ -2316,33 +2339,39 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"optional": true
},
"once": {
"version": "1.4.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"optional": true
},
"osenv": {
"version": "0.1.5",
"bundled": true,
"resolved": false,
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"optional": true,
"requires": {
"os-homedir": "^1.0.0",
@@ -2351,17 +2380,20 @@
},
"path-is-absolute": {
"version": "1.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"optional": true
},
"rc": {
"version": "1.2.7",
"bundled": true,
"resolved": false,
"integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==",
"optional": true,
"requires": {
"deep-extend": "^0.5.1",
@@ -2372,14 +2404,16 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"optional": true
}
}
},
"readable-stream": {
"version": "2.3.6",
"bundled": true,
"resolved": false,
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"optional": true,
"requires": {
"core-util-is": "~1.0.0",
@@ -2393,7 +2427,8 @@
},
"rimraf": {
"version": "2.6.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"optional": true,
"requires": {
"glob": "^7.0.5"
@@ -2401,36 +2436,43 @@
},
"safe-buffer": {
"version": "5.1.1",
"bundled": true
"resolved": false,
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"safer-buffer": {
"version": "2.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"optional": true
},
"sax": {
"version": "1.2.4",
"bundled": true,
"resolved": false,
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
"semver": {
"version": "5.5.0",
"bundled": true,
"resolved": false,
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"bundled": true,
"resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"optional": true
},
"string-width": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -2439,7 +2481,8 @@
},
"string_decoder": {
"version": "1.1.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -2447,19 +2490,22 @@
},
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
"bundled": true,
"resolved": false,
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"optional": true
},
"tar": {
"version": "4.4.1",
"bundled": true,
"resolved": false,
"integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==",
"optional": true,
"requires": {
"chownr": "^1.0.1",
@@ -2473,12 +2519,14 @@
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true,
"resolved": false,
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"optional": true
},
"wide-align": {
"version": "1.1.2",
"bundled": true,
"resolved": false,
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
"optional": true,
"requires": {
"string-width": "^1.0.2"
@@ -2486,11 +2534,13 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"yallist": {
"version": "3.0.2",
"bundled": true
"resolved": false,
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
}
}
},
@@ -2578,19 +2628,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
},
"graphlib": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.7.tgz",
"integrity": "sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w==",
"requires": {
"lodash": "^4.17.5"
}
},
"hammerjs": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
"integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
},
"handle-thing": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
@@ -2673,11 +2710,6 @@
"minimalistic-assert": "^1.0.1"
}
},
"heap": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz",
"integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw="
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -3139,11 +3171,6 @@
"css-vendor": "^0.3.8"
}
},
"keycharm": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz",
"integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk="
},
"keycode": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
@@ -3167,6 +3194,12 @@
"invert-kv": "^2.0.0"
}
},
"livereload-js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz",
"integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==",
"dev": true
},
"loader-runner": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz",
@@ -3957,14 +3990,6 @@
"object-assign": "^4.1.1"
}
},
"propagating-hammerjs": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.4.6.tgz",
"integrity": "sha1-/tAOmwB2f/1C0U9bUxvEk+tnLjc=",
"requires": {
"hammerjs": "^2.0.6"
}
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
@@ -4107,17 +4132,6 @@
"pure-color": "^1.2.0"
}
},
"react-cytoscape": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/react-cytoscape/-/react-cytoscape-1.0.6.tgz",
"integrity": "sha512-bCBLyctKVDuxe9W2b/TaBGwk+Q1/AX33k+ocD94r0Pq4KQGAJNS91m3Z/czSOkDHcK1VmbImxnH2Q75J5lxeiQ==",
"requires": {
"cytoscape": "^3.2.5",
"cytoscape-cola": "^2.0.0",
"cytoscape-dagre": "^2.1.0",
"fsevents": "*"
}
},
"react-dom": {
"version": "16.3.3",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.3.3.tgz",
@@ -4320,6 +4334,12 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-json-parse": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz",
"integrity": "sha1-PnZyPjjf3aE8mx0poeB//uSzC1c=",
"dev": true
},
"safe-regex": {
"version": "1.1.0",
"resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
@@ -4873,6 +4893,12 @@
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
"integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
},
"string-template": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz",
"integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -4992,6 +5018,37 @@
"setimmediate": "^1.0.4"
}
},
"tiny-lr": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz",
"integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==",
"dev": true,
"requires": {
"body": "^5.1.0",
"debug": "^3.1.0",
"faye-websocket": "~0.10.0",
"livereload-js": "^2.3.0",
"object-assign": "^4.1.0",
"qs": "^6.4.0"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
}
}
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
@@ -5244,18 +5301,6 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"vis": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz",
"integrity": "sha1-3XFji/9/ZJXQC8n0DCU1JhM97Ws=",
"requires": {
"emitter-component": "^1.1.1",
"hammerjs": "^2.0.8",
"keycharm": "^0.2.0",
"moment": "^2.18.1",
"propagating-hammerjs": "^1.4.6"
}
},
"vm-browserify": {
"version": "0.0.4",
"resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
@@ -5290,16 +5335,6 @@
"minimalistic-assert": "^1.0.0"
}
},
"webcola": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/webcola/-/webcola-3.3.8.tgz",
"integrity": "sha512-WVDTdHS1SaqYCUJGPdbOhqj44mchDyTC78tozUdqJllwYeJ2554+BWkJfc5kNphT8foip2StCMw1FWsIvGmv9w==",
"requires": {
"d3-dispatch": "^1.0.3",
"d3-drag": "^1.0.4",
"d3-timer": "^1.0.5"
}
},
"webpack": {
"version": "4.28.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.2.tgz",
@@ -5481,6 +5516,16 @@
}
}
},
"webpack-livereload-plugin": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/webpack-livereload-plugin/-/webpack-livereload-plugin-2.2.0.tgz",
"integrity": "sha512-sx9xA5mHoNOUgLQI0PmXT3KV9ecsVmUaTgr+fsoL69qAOHw/7VzkL1+ZMDQ8n0dPbWounswK6cBRSgMod7Nhgg==",
"dev": true,
"requires": {
"portfinder": "^1.0.17",
"tiny-lr": "^1.1.1"
}
},
"webpack-log": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",

View File

@@ -10,31 +10,27 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@material-ui/icons": "^3.0.1"
"@material-ui/icons": "^3.0.1",
"webpack-livereload-plugin": "^2.2.0"
},
"dependencies": {
"@types/node": "^10.12.18",
"awesome-typescript-loader": "^5.2.1",
"source-map-loader": "^0.2.4",
"typescript": "^3.2.2",
"webpack": "^4.28.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14",
"@material-ui/core": "^3.7.1",
"@types/node": "^10.12.18",
"@types/react": "^16.7.18",
"@types/react-dom": "^16.0.11",
"@types/socket.io-client": "^1.4.32",
"@types/vis": "^4.21.9",
"cytoscape": "^3.3.1",
"cytoscape-dagre": "^2.2.2",
"cytoscape-expand-collapse": "^3.1.2",
"awesome-typescript-loader": "^5.2.1",
"jquery": "^3.3.1",
"lodash.throttle": "^4.1.1",
"react": "^16.3.2",
"react-cytoscape": "^1.0.6",
"react-dom": "^16.3.3",
"react-json-view": "^1.19.1",
"socket.io-client": "^2.2.0",
"vis": "^4.21.0"
"source-map-loader": "^0.2.4",
"typescript": "^3.2.2",
"webpack": "^4.28.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14"
}
}

View File

@@ -3,7 +3,7 @@ import * as q from '../../backend/src/Model'
import { Tree } from './components/Tree'
import TitleBar from './components/TitleBar'
import { Sidebar } from './components/Sidebar'
import Sidebar from './components/Sidebar/Sidebar'
import { withTheme, Theme } from '@material-ui/core/styles'
@@ -12,6 +12,7 @@ class State {
}
interface Props {
name: string
theme: Theme
}
@@ -21,34 +22,47 @@ class App extends React.Component<Props, State> {
this.state = {
selectedNode: undefined,
}
}
console.log('asd', this.props)
this.theme = this.props.theme
this.styles = {
primaryText: {
backgroundColor: this.theme.palette.background.default,
padding: `${this.theme.spacing.unit}px ${this.theme.spacing.unit * 2}px`,
color: this.theme.palette.text.primary,
private getStyles(): {[s: string]: React.CSSProperties} {
const { theme } = this.props
return {
left: {
backgroundColor: theme.palette.background.default,
color: theme.palette.text.primary,
height: 'calc(100vh - 64px)',
float: 'left',
width: '60vw',
overflowY: 'scroll',
overflowX: 'hidden',
display: 'block',
},
primaryColor: {
backgroundColor: this.theme.palette.background.default,
// padding: `${this.theme.spacing.unit}px ${this.theme.spacing.unit * 2}px`,
color: this.theme.palette.common.white,
right: {
height: 'calc(100vh - 64px)',
backgroundColor: theme.palette.background.default,
color: theme.palette.text.primary,
float: 'right', width: '40vw',
overflowY: 'scroll',
overflowX: 'hidden',
display: 'block',
},
}
}
private theme: Theme
private styles: any
public render() {
return <div style={this.styles.primaryColor}>
return <div>
<TitleBar />
<Tree didSelectNode={(node: q.TreeNode) => {
this.setState({ selectedNode: node })
console.log('did select', node)
}} />
</div>
<div>
<div style={this.getStyles().left}>
<Tree didSelectNode={(node: q.TreeNode) => {
this.setState({ selectedNode: node })
}} />
</div>
<div style={this.getStyles().right}>
<Sidebar node={this.state.selectedNode} />
</div>
</div>
</div >
}
}

View File

@@ -1,66 +0,0 @@
import * as React from 'react'
import * as q from '../../../backend/src/Model'
import Drawer from '@material-ui/core/Drawer'
import TextField from '@material-ui/core/TextField'
import Paper from '@material-ui/core/Paper'
import { ValueRenderer } from './ValueRenderer'
interface Props {
node?: q.TreeNode | undefined
}
interface State {
node?: q.TreeNode | undefined
}
export class Sidebar extends React.Component<Props, State> {
private updateNode: (node?: q.TreeNode | undefined) => void
constructor(props: any) {
super(props)
this.state = {}
this.updateNode = (node) => {
if (!node) {
this.setState(this.state)
} else {
this.setState({ node })
}
}
}
public componentWillReceiveProps(nextProps: Props) {
this.props.node && this.props.node.removeListener('update', this.updateNode)
nextProps.node && nextProps.node.on('update', this.updateNode)
nextProps.node && this.updateNode(nextProps.node)
}
private open() {
return this.props.node !== undefined
}
public render() {
return <Drawer open={this.open()} variant="permanent" anchor="right">
{this.renderNode()}
</Drawer>
}
private renderNode() {
const style: React.CSSProperties = { display: 'block', width: '40vw' }
const topicStyle: React.CSSProperties = { width: '100%' }
if (!this.state.node) {
return null
}
return <div style={style}>
<TextField style={topicStyle}
label="Topic"
value={this.state.node.path()}
margin="normal"
variant="outlined"
/>
<Paper>
<ValueRenderer node={this.state.node} />
</Paper>
</div>
}
}

View File

@@ -0,0 +1,41 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
// import Drawer from '@material-ui/core/Drawer'
import { Typography } from '@material-ui/core'
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
interface Props {
node: q.TreeNode,
classes: any,
theme: Theme
}
interface State {
node?: q.TreeNode | undefined
}
class NodeStats extends React.Component<Props, State> {
constructor(props: any) {
super(props)
}
public static styles: StyleRulesCallback<string> = (theme: Theme) => {
return {
}
}
public render() {
const leafes = this.props.node.leafes()
const leafMessages = leafes
.map(leaf => leaf.messages)
.reduce((a, b) => a + b)
return <Typography>
<p>Messages: #{this.props.node.messages}</p>
<p>Subtopics: {leafes.length}</p>
<p>Messages Subtopics: #{leafMessages}</p>
</Typography>
}
}
export default withStyles(NodeStats.styles, { withTheme: true })(NodeStats)

View File

@@ -0,0 +1,105 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
// import Drawer from '@material-ui/core/Drawer'
import ExpansionPanel from '@material-ui/core/ExpansionPanel'
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'
import ExpandMore from '@material-ui/icons/ExpandMore'
import ValueRenderer from './ValueRenderer'
import NodeStats from './NodeStats'
import Topic from './Topic'
import { Typography } from '@material-ui/core'
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
interface Props {
node?: q.TreeNode | undefined,
classes: any,
theme: Theme
}
interface State {
node?: q.TreeNode | undefined
}
class Sidebar extends React.Component<Props, State> {
private updateNode: (node?: q.TreeNode | undefined) => void
constructor(props: any) {
super(props)
this.state = {}
this.updateNode = (node) => {
if (!node) {
this.setState(this.state)
} else {
this.setState({ node })
}
}
}
public static styles: StyleRulesCallback<string> = (theme: Theme) => {
return {
drawer: {
display: 'block',
height: '100%',
},
valuePaper: {
margin: `${theme.spacing.unit}px ${theme.spacing.unit}px ${theme.spacing.unit}px ${theme.spacing.unit}px`,
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular,
},
}
}
public componentWillReceiveProps(nextProps: Props) {
this.props.node && this.props.node.removeListener('update', this.updateNode)
nextProps.node && nextProps.node.on('update', this.updateNode)
nextProps.node && this.updateNode(nextProps.node)
}
private open(): boolean {
return true
}
public render() {
return <div className={this.props.classes.drawer}>
{this.renderNode()}
</div>
}
private renderNode() {
const { classes } = this.props
if (!this.state.node) {
return null
}
return <div>
<ExpansionPanel key="topic" defaultExpanded={true}>
<ExpansionPanelSummary expandIcon={<ExpandMore />}>
<Typography className={classes.heading}>Topic</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Topic node={this.state.node} />
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel key="value" defaultExpanded={true}>
<ExpansionPanelSummary expandIcon={<ExpandMore />}>
<Typography className={classes.heading}>Value</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<ValueRenderer node={this.state.node} />
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel key="stats" defaultExpanded={true}>
<ExpansionPanelSummary expandIcon={<ExpandMore />}>
<Typography className={classes.heading}>Stats</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<NodeStats node={this.state.node} />
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
}
}
export default withStyles(Sidebar.styles, { withTheme: true })(Sidebar)

View File

@@ -0,0 +1,52 @@
import * as React from 'react'
import * as q from '../../../../backend/src/Model'
import { withStyles, Theme, StyleRulesCallback } from '@material-ui/core/styles'
import Button from '@material-ui/core/Button'
interface Props {
classes: any
theme: Theme
node: q.TreeNode
selected?: q.TreeNode
}
class Topic extends React.Component<Props, {}> {
public static styles: StyleRulesCallback<string> = (theme: Theme) => ({
button: {
textTransform: 'none',
padding: '3px 5px 3px 5px',
minWidth: '30px',
},
})
public render() {
const { node } = this.props
let i = 0
const breadCrumps = node.branch()
.map(node => node.sourceEdge)
.filter(edge => Boolean(edge))
.map(edge =>
[<Button
onClick={() => this.setState({ node: edge!.target })}
size="small"
color="secondary"
className={this.props.classes.button}
key={edge!.hash()}
>
{edge!.name}
</Button>],
)
if (breadCrumps.length === 0) {
return null
}
const joinedBreadCrumps = breadCrumps.reduce((prev, current) =>
prev.concat([<span key={i += 1}>/</span>]).concat(current),
)
return <span style={{ lineHeight: '2.2em' }}>{joinedBreadCrumps}</span>
}
}
export default withStyles(Topic.styles, { withTheme: true })(Topic)

View File

@@ -1,16 +1,18 @@
import * as React from 'react'
import * as q from '../../../backend/src/Model'
import * as q from '../../../../backend/src/Model'
import { default as ReactJson } from 'react-json-view'
import { withTheme, Theme } from '@material-ui/core/styles'
interface Props {
node?: q.TreeNode | undefined
theme: Theme
}
interface State {
node?: q.TreeNode | undefined
}
export class ValueRenderer extends React.Component<Props, State> {
class ValueRenderer extends React.Component<Props, State> {
private updateNode: (node?: q.TreeNode | undefined) => void
constructor(props: any) {
super(props)
@@ -30,6 +32,10 @@ export class ValueRenderer extends React.Component<Props, State> {
nextProps.node && this.updateNode(nextProps.node)
}
private style = (theme: Theme) => {
}
public render() {
const node = this.props.node
if (!node || !node.message) {
@@ -48,7 +54,14 @@ export class ValueRenderer extends React.Component<Props, State> {
} else if (typeof json === 'number') {
return this.renderRawValue(node.message.value)
} else {
return <ReactJson src={json} />
const theme = this.props.theme.palette.type === 'dark' ? 'monokai' : 'bright:inverted'
return <ReactJson
style={{ width: '100%' }}
src={json}
theme={theme}
onEdit={(val) => {
console.log(val)
}} />
}
}
@@ -62,6 +75,8 @@ export class ValueRenderer extends React.Component<Props, State> {
padding: '12px 5px 12px 5px',
}
return <pre><code style={style}>{value}</code></pre>
return <pre style={style}><code>{value}</code></pre>
}
}
export default withTheme()(ValueRenderer)

View File

@@ -0,0 +1,3 @@
import Sidebar from './Sidebar'
export { Sidebar }

View File

@@ -1,7 +1,7 @@
import * as React from 'react'
import * as io from 'socket.io-client'
import * as q from '../../../backend/src/Model'
import { TreeNode } from './TreeNode'
import TreeNode from './TreeNode'
import List from '@material-ui/core/List'
const throttle = require('lodash.throttle')
@@ -18,10 +18,14 @@ class TreeState {
export interface TreeNodeProps {
didSelectNode?: (node: q.TreeNode) => void
}
declare const performance: any
export class Tree extends React.Component<TreeNodeProps, TreeState> {
private socket: SocketIOClient.Socket
private renderDuration: number = 300
private renderDuration: number = 200
private updateTimer?: any
private lastUpdate: number = 0
private perf:number = 0
constructor(props: any) {
super(props)
@@ -30,21 +34,36 @@ export class Tree extends React.Component<TreeNodeProps, TreeState> {
this.socket = io('http://localhost:3000')
}
public componentDidMount() {
let updateState = throttle((state: any) => {
this.setState(state)
updateState.cancel()
updateState = throttle(() => {
this.setState(state)
}, Math.max(this.renderDuration * 5, 300), { trailing: true })
}, 1000)
public time(): number {
const time = performance.now() - this.perf
this.perf = performance.now()
return time
}
public throttledStateUpdate(state: any) {
if (this.updateTimer) {
return
}
const updateInterval = Math.max(this.renderDuration * 5, 200)
const timeUntilNextUpdate = updateInterval - (performance.now() - this.lastUpdate)
this.updateTimer = setTimeout(() => {
this.lastUpdate = performance.now()
this.updateTimer && clearTimeout(this.updateTimer)
this.updateTimer = undefined
this.setState(state)
}, Math.max(0, timeUntilNextUpdate))
}
public componentDidMount() {
this.socket.on('message', (msg: any) => {
const edges = msg.topic.split('/')
const node = q.TreeNodeFactory.fromEdgesAndValue(edges, Buffer.from(msg.payload, 'base64').toString())
this.state.tree.updateWithNode(node.firstNode())
updateState({ msg, tree: this.state.tree })
this.throttledStateUpdate({ msg, tree: this.state.tree })
})
}
@@ -56,10 +75,14 @@ export class Tree extends React.Component<TreeNodeProps, TreeState> {
return <div>
<List>
<TreeNode
isRoot={true}
didSelectNode={this.props.didSelectNode}
treeNode={this.state.tree}
name="/" collapsed={false}
performanceCallback={ms => this.renderDuration = ms}
key="rootNode"
performanceCallback={(ms: number) => {
this.renderDuration = ms
}}
/>
</List>
</div>

View File

@@ -1,15 +1,21 @@
import * as React from 'react'
import * as q from '../../../backend/src/Model'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import Collapse from '@material-ui/core/Collapse'
import { List, ListItem, Collapse, Typography } from '@material-ui/core'
import { withTheme, Theme } from '@material-ui/core/styles'
const throttle = require('lodash.throttle')
import Slide from '@material-ui/core/Slide'
import { isElementInViewport } from './helper/isElementInViewport'
const collapseLimit = 3
declare var performance: any
export interface TreeNodeProps {
isRoot?: boolean
treeNode: q.TreeNode
name?: string | undefined
collapsed?: boolean | undefined
performanceCallback?: ((ms: number) => void) | undefined
didSelectNode?: (node: q.TreeNode) => void
theme: Theme
}
interface TreeNodeState {
@@ -19,28 +25,59 @@ interface TreeNodeState {
edgeCount: number
}
const collapseLimit = 0
declare var performance: any
export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
private dirty: boolean = true
private willUpdateTime: number = performance.now()
private titleRef = React.createRef<HTMLElement>()
private markAsDirty = () => {
this.dirty = true
if (!this.props.isRoot) {
this.indicateUpdate()
}
}
constructor(props: TreeNodeProps, state: TreeNodeState) {
super(props, state)
private indicateUpdate = throttle(() => {
const title: any = this.titleRef.current
if (title && isElementInViewport(title)) {
title.style.animation = 'example 0.5s'
setTimeout(() => {
title.style.animation = ''
}, 500)
}
}, 500)
constructor(props: TreeNodeProps) {
super(props)
const edgeCount = Object.keys(props.treeNode.edges).length
const collapsed = edgeCount > collapseLimit
this.props.treeNode.on('update', () => {
this.dirty = true
})
this.state = { collapsed, edgeCount, collapsedOverride: props.collapsed, title: props.name }
}
public componentDidMount() {
this.props.treeNode.on('update', this.markAsDirty)
}
public componentWillUnmount() {
this.props.treeNode.removeListener('update', this.markAsDirty)
}
private getStyles() {
const { theme } = this.props
return {
collapsedSubnodes: {
color: theme.palette.text.secondary,
},
container: {
display: 'block',
},
}
}
public setState(state: any) {
this.dirty = true
this.dirty = this.state.collapsed !== state.collapsed
|| this.state.collapsedOverride !== state.collapsedOverride
|| this.state.edgeCount !== state.edgeCount
super.setState(state)
}
@@ -49,7 +86,6 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
}
public componentDidUpdate() {
this.dirty = false
if (this.props.performanceCallback) {
const renderTime = performance.now() - this.willUpdateTime
this.props.performanceCallback(renderTime)
@@ -73,18 +109,25 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
private renderNodes() {
const edges = Object.values(this.props.treeNode.edges)
const listItemStyle = {
padding: '3px 8px 3px 8px',
padding: '3px 8px 0px 8px',
}
const listStyle = {
padding: '3px 8px 3px 16px',
padding: '3px 8px 0px 16px',
}
if (edges.length > 0) {
const listItems = edges
.map(edge => edge.target)
.map(node => <ListItem style={listItemStyle} button key={node.hash()}>
<TreeNode didSelectNode={this.props.didSelectNode} treeNode={node} />
</ListItem>)
.map(node => (
<ListItem key={node.hash()} style={listItemStyle} button>
<TreeNode
theme={this.props.theme}
didSelectNode={this.props.didSelectNode}
treeNode={node}
/>
</ListItem>
))
return <Collapse in={!this.collapsed()} timeout="auto" unmountOnExit>
<List style={listStyle}>{listItems}</List>
@@ -100,13 +143,10 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
}
const name = this.state.title || (this.props.treeNode.sourceEdge && this.props.treeNode.sourceEdge.name)
return <span style={style} onClick={() => this.toggle()}>{name}</span>
}
private getStyle(): React.CSSProperties {
return {
display: 'block',
}
return <span style={style} onClick={() => {
this.toggle()
this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)
}>{name}</span>
}
public componentWillReceiveProps() {
@@ -128,7 +168,7 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
? <span
style={style}
onMouseOver={() => this.props.didSelectNode && this.props.didSelectNode(this.props.treeNode)}
> = {this.props.treeNode.message.toString()}</span>
> = {this.props.treeNode.message.value.toString()}</span>
: null
}
@@ -137,23 +177,33 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
}
private renderTitleLine() {
const style = {
const style: React.CSSProperties = {
lineHeight: '1em',
whiteSpace: 'nowrap',
width: '15em',
}
return <div style={style}>{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}</div>
return <div
ref={this.titleRef}
>
<Typography style={style}>
{this.renderExpander()} {this.renderSourceEdge()} {this.renderCollapsedSubnodes()} {this.renderValue()}
</Typography>
</div>
}
public render() {
this.dirty = false
const nodeStyle: React.CSSProperties = {
display: 'block',
}
return <div style={this.getStyle()}>
{this.renderTitleLine()}
<div style={nodeStyle}>
{this.clear()}
<div style={this.subnodesStyle()}>
{this.collapsed() ? null : this.renderNodes()}
return <div key={this.props.treeNode.hash()} style={ this.getStyles().container }>
{ this.renderTitleLine() }
<div style = { nodeStyle }>
{ this.clear() }
<div style = { this.subnodesStyle() }>
{ this.collapsed() ? null : this.renderNodes() }
</div>
</div>
</div>
@@ -178,10 +228,8 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
return null
}
const style = {
color: '#333',
}
return <span style={style}>({this.props.treeNode.leafes().length} nodes)</span>
const messages = this.props.treeNode.leafes().map(leaf => leaf.messages).reduce((a, b) => a + b)
return <span style={this.getStyles().collapsedSubnodes}>({this.props.treeNode.leafes().length} nodes, {messages} messages)</span>
}
private subnodesStyle(): React.CSSProperties {
@@ -190,3 +238,5 @@ export class TreeNode extends React.Component<TreeNodeProps, TreeNodeState> {
}
}
}
export default withTheme()(TreeNode)

View File

@@ -0,0 +1,12 @@
declare const window: any
declare const document: any
export function isElementInViewport(el: any) {
const rect = el.getBoundingClientRect()
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
)
}

View File

@@ -1,3 +1,5 @@
var LiveReloadPlugin = require('webpack-livereload-plugin');
module.exports = {
entry: "./src/index.tsx",
output: {
@@ -30,6 +32,10 @@ module.exports = {
]
},
plugins: [
new LiveReloadPlugin({})
],
// When importing a module whose path matches one of the following, just
// assume a corresponding global variable exists and use that instead.
// This is important because it allows us to avoid bundling all of our

View File

@@ -1,10 +1,10 @@
import * as socketIO from 'socket.io'
const http = require('http')
import { TopicProperties, Tree, TreeNodeFactory } from './Model'
import { MqttSource, DataSource } from './DataSource'
import * as socketIO from 'socket.io'
const http = require('http')
const options = { url: 'mqtt://nodered' }
const options = { url: 'mqtt://test.mosquitto.org' }
const dataSource = new MqttSource()
const a: any[] = []
@@ -23,7 +23,9 @@ server.listen(3000)
const state = dataSource.connect(options)
dataSource.onMessage((topic: string, payload: Buffer) => {
let buffer = payload
a.push({ topic, payload: buffer.toString('base64') })
if (a.length < 30) {
a.push({ topic, payload: buffer.toString('base64') })
}
if (buffer.length > 10000) {
buffer = buffer.slice(0, 10000)
}

View File

@@ -7,7 +7,13 @@ let mainWindow
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({width: 1024, height: 600})
mainWindow = new BrowserWindow({
width: 1024,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
// and load the index.html of the app.
mainWindow.loadFile('app/index.html')

BIN
icon.xcf Normal file

Binary file not shown.

View File

@@ -5,6 +5,7 @@
"max-line-length": [true, 200],
"member-access": true,
"no-else-after-return": false,
"align": false
"align": false,
"variable-name": false
}
}