aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package-lock.json720
-rw-r--r--package.json11
-rw-r--r--src/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--src/client/views/nodes/DataViz.tsx20
-rw-r--r--src/client/views/nodes/DataVizBox/ChartBox.tsx144
-rw-r--r--src/client/views/nodes/DataVizBox/ChartInterface.ts36
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx166
-rw-r--r--src/client/views/nodes/DataVizBox/DrawHelper.ts247
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBox.scss18
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBox.tsx159
-rw-r--r--src/client/views/nodes/DataVizBox/components/Chart.scss10
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx290
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.scss (renamed from src/client/views/nodes/DataVizBox/TableBox.scss)0
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx (renamed from src/client/views/nodes/DataVizBox/TableBox.tsx)0
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts67
-rw-r--r--src/server/ApiManagers/DataVizManager.ts26
-rw-r--r--src/server/DataVizUtils.ts6
-rw-r--r--src/server/index.ts15
18 files changed, 1416 insertions, 519 deletions
diff --git a/package-lock.json b/package-lock.json
index 3b9cda8bb..0a340b73a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1173,6 +1173,50 @@
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
},
+ "@types/d3": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
+ "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "@types/d3-array": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz",
+ "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==",
+ "dev": true
+ },
"@types/d3-axis": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.1.3.tgz",
@@ -1181,11 +1225,138 @@
"@types/d3-selection": "^2"
}
},
+ "@types/d3-brush": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.1.tgz",
+ "integrity": "sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw==",
+ "dev": true
+ },
"@types/d3-color": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz",
"integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w=="
},
+ "@types/d3-contour": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.1.tgz",
+ "integrity": "sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-delaunay": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
+ "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
+ "dev": true
+ },
+ "@types/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw==",
+ "dev": true
+ },
+ "@types/d3-drag": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.1.tgz",
+ "integrity": "sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-dsv": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.0.tgz",
+ "integrity": "sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A==",
+ "dev": true
+ },
+ "@types/d3-ease": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
+ "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==",
+ "dev": true
+ },
+ "@types/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "@types/d3-force": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz",
+ "integrity": "sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA==",
+ "dev": true
+ },
+ "@types/d3-format": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
+ "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
+ "dev": true
+ },
+ "@types/d3-geo": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.2.tgz",
+ "integrity": "sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ==",
+ "dev": true,
+ "requires": {
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-hierarchy": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz",
+ "integrity": "sha512-g+sey7qrCa3UbsQlMZZBOHROkFqx7KZKvUpRzI/tAp/8erZWpYq7FgNKvYwebi2LaEiVs1klhUfd3WCThxmmWQ==",
+ "dev": true
+ },
+ "@types/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-color": "*"
+ }
+ },
+ "@types/d3-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
+ "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==",
+ "dev": true
+ },
+ "@types/d3-polygon": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz",
+ "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==",
+ "dev": true
+ },
+ "@types/d3-quadtree": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz",
+ "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==",
+ "dev": true
+ },
+ "@types/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==",
+ "dev": true
+ },
"@types/d3-scale": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
@@ -1194,16 +1365,62 @@
"@types/d3-time": "^2"
}
},
+ "@types/d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==",
+ "dev": true
+ },
"@types/d3-selection": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.1.tgz",
"integrity": "sha512-3mhtPnGE+c71rl/T5HMy+ykg7migAZ4T6gzU0HxpgBFKcasBrSnwRbYV1/UZR6o5fkpySxhWxAhd7yhjj8jL7g=="
},
+ "@types/d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-path": "*"
+ }
+ },
"@types/d3-time": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
"integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
},
+ "@types/d3-time-format": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
+ "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==",
+ "dev": true
+ },
+ "@types/d3-timer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
+ "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==",
+ "dev": true
+ },
+ "@types/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-Sv4qEI9uq3bnZwlOANvYK853zvpdKEm1yz9rcc8ZTsxvRklcs9Fx4YFuGA3gXoQN/c/1T6QkVNjhaRO/cWj94g==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-zoom": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.1.tgz",
+ "integrity": "sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
"@types/dom-speech-recognition": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz",
@@ -2455,7 +2672,7 @@
"textarea-caret": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
- "integrity": "sha512-gRzeti2YS4did7UJnPQ47wrjD+vp+CJIe9zbsu0bJ987d8QVLvLNG9757rqiQTIy4hGIeFauTTJt5Xkn51UkXg=="
+ "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
}
}
},
@@ -2554,7 +2771,7 @@
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
- "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"agent-base": {
"version": "6.0.2",
@@ -3346,7 +3563,7 @@
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
- "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"balanced-match": {
"version": "1.0.2",
@@ -3411,7 +3628,7 @@
"base64-arraybuffer": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
- "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
+ "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
},
"base64-js": {
"version": "1.5.1",
@@ -4000,6 +4217,11 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
+ "chart.js": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.8.0.tgz",
+ "integrity": "sha512-cr8xhrXjLIXVLOBZPkBZVF6NDeiVIrPLHcMhnON7UufudL+CNeRrD+wpYanswlm8NpudMdrt3CHoLMQMxJhHRg=="
+ },
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -4333,7 +4555,7 @@
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
- "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.3.0",
@@ -4343,7 +4565,7 @@
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
- "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"compress-brotli": {
"version": "1.3.8",
@@ -4992,6 +5214,11 @@
"postcss-value-parser": "^3.3.0"
}
},
+ "css-unit-converter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz",
+ "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA=="
+ },
"css-vendor": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
@@ -5051,7 +5278,7 @@
"custom-event": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
- "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
},
"cyclist": {
"version": "1.0.1",
@@ -5059,6 +5286,180 @@
"integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
"dev": true
},
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
+ "d3": {
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
+ "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==",
+ "requires": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz",
+ "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==",
+ "requires": {
+ "internmap": "1 - 2"
+ }
+ },
+ "d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="
+ },
+ "d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
+ },
+ "d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="
+ },
+ "d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ }
+ },
+ "d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="
+ },
+ "d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="
+ },
+ "d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "requires": {
+ "d3-color": "1 - 3"
+ }
+ },
+ "d3-path": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz",
+ "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w=="
+ },
+ "d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "requires": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ }
+ },
+ "d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
+ },
+ "d3-shape": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz",
+ "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==",
+ "requires": {
+ "d3-path": "1 - 3"
+ }
+ },
+ "d3-time": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz",
+ "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==",
+ "requires": {
+ "d3-array": "2 - 3"
+ }
+ },
+ "d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "requires": {
+ "d3-time": "1 - 3"
+ }
+ },
+ "d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="
+ },
+ "d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "requires": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ },
+ "d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ }
+ }
+ }
+ },
"d3-array": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -5072,11 +5473,76 @@
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-2.1.0.tgz",
"integrity": "sha512-z/G2TQMyuf0X3qP+Mh+2PimoJD41VOCjViJzT0BHeL/+JQAofkiWZbWxlwFGb1N8EN+Cl/CW+MUKbVzr1689Cw=="
},
+ "d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "dependencies": {
+ "d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="
+ },
+ "d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "requires": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ }
+ }
+ },
+ "d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "requires": {
+ "d3-path": "1 - 3"
+ }
+ },
"d3-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
"integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
},
+ "d3-contour": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz",
+ "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==",
+ "requires": {
+ "d3-array": "^3.2.0"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz",
+ "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==",
+ "requires": {
+ "internmap": "1 - 2"
+ }
+ }
+ }
+ },
+ "d3-delaunay": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz",
+ "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==",
+ "requires": {
+ "delaunator": "5"
+ }
+ },
"d3-dispatch": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
@@ -5091,16 +5557,64 @@
"d3-selection": "2"
}
},
+ "d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "requires": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ }
+ }
+ },
"d3-ease": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
"integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
},
+ "d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "requires": {
+ "d3-dsv": "1 - 3"
+ }
+ },
+ "d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "requires": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ }
+ },
"d3-format": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
"integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
},
+ "d3-geo": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz",
+ "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==",
+ "requires": {
+ "d3-array": "2.5.0 - 3"
+ }
+ },
+ "d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="
+ },
"d3-interpolate": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
@@ -5114,6 +5628,21 @@
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
"integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
},
+ "d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="
+ },
+ "d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="
+ },
+ "d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="
+ },
"d3-scale": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
@@ -5126,6 +5655,15 @@
"d3-time-format": "2 - 3"
}
},
+ "d3-scale-chromatic": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
+ "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
+ "requires": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ }
+ },
"d3-selection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
@@ -5265,6 +5803,11 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
+ "decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
+ },
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
@@ -5468,6 +6011,14 @@
}
}
},
+ "delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "requires": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -6069,7 +6620,7 @@
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"ws": {
"version": "7.4.6",
@@ -6208,6 +6759,28 @@
"is-symbol": "^1.0.2"
}
},
+ "es5-ext": {
+ "version": "0.10.61",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
+ "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.3",
+ "next-tick": "^1.1.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
"es6-promise": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6219,6 +6792,7 @@
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
"dev": true,
"requires": {
+ "d": "^1.0.1",
"ext": "^1.1.2"
}
},
@@ -7471,8 +8045,7 @@
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
- "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
- "dev": true
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"events": {
"version": "3.3.0",
@@ -7923,6 +8496,11 @@
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
+ "fast-equals": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz",
+ "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w=="
+ },
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -8416,7 +8994,7 @@
"fs-extra": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
- "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+ "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^2.1.0",
@@ -9054,14 +9632,14 @@
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
}
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
- "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"has-flag": {
"version": "3.0.0",
@@ -9557,7 +10135,7 @@
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inflight": {
"version": "1.0.6",
@@ -10600,7 +11178,7 @@
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
"requires": {
"graceful-fs": "^4.1.6"
}
@@ -11019,7 +11597,7 @@
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
- "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lodash.isplainobject": {
"version": "4.0.6",
@@ -11270,7 +11848,7 @@
"mathquill": {
"version": "0.10.1-a",
"resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
- "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
+ "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
"requires": {
"jquery": "^1.12.3"
},
@@ -11278,7 +11856,7 @@
"jquery": {
"version": "1.12.4",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
- "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
+ "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
}
}
},
@@ -12788,7 +13366,7 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.1",
- "resolved": false,
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
},
"is-fullwidth-code-point": {
@@ -14287,7 +14865,7 @@
},
"minimist": {
"version": "1.2.6",
- "resolved": false,
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"minizlib": {
@@ -14336,7 +14914,7 @@
"dependencies": {
"minimist": {
"version": "1.2.6",
- "resolved": false,
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}
}
@@ -15709,7 +16287,7 @@
"dependencies": {
"ansi-regex": {
"version": "4.1.1",
- "resolved": false,
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
},
"is-fullwidth-code-point": {
@@ -17506,6 +18084,11 @@
"use-memo-one": "^1.1.1"
}
},
+ "react-chartjs-2": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.0.tgz",
+ "integrity": "sha512-zWnW3ReDlSC8PNiZKNqu8J/CHBOsVvx1AB4PicFbEAMUf/rpgO0/8ykRSPqS00kBUjP9d8lzfq1gFdLwYFWC8w=="
+ },
"react-color": {
"version": "2.19.3",
"resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
@@ -17814,6 +18397,14 @@
"resolved": "https://registry.npmjs.org/react-resizable-rotatable-draggable/-/react-resizable-rotatable-draggable-0.2.0.tgz",
"integrity": "sha512-F8TPx3z7/AcmRViySbYV3LpUWXFpHlGAmKmNcYMgPlS+h1eYFazRG3xYS8Z6e48hWY1EcCny/YNrwRNUrap8CQ=="
},
+ "react-resize-detector": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-7.1.2.tgz",
+ "integrity": "sha512-zXnPJ2m8+6oq9Nn8zsep/orts9vQv3elrpA+R8XTcW7DVVUJ9vwDwMXaBtykAYjMnkCIaOoK9vObyR7ZgFNlOw==",
+ "requires": {
+ "lodash": "^4.17.21"
+ }
+ },
"react-reveal": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/react-reveal/-/react-reveal-1.2.2.tgz",
@@ -17874,6 +18465,28 @@
}
}
},
+ "react-smooth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.1.tgz",
+ "integrity": "sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA==",
+ "requires": {
+ "fast-equals": "^2.0.0",
+ "react-transition-group": "2.9.0"
+ },
+ "dependencies": {
+ "react-transition-group": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+ "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+ "requires": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ }
+ }
+ },
"react-table": {
"version": "6.11.5",
"resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz",
@@ -18048,6 +18661,32 @@
"resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
"integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
},
+ "recharts": {
+ "version": "2.1.12",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.1.12.tgz",
+ "integrity": "sha512-dAzEuc9AjM+IF0A33QzEdBEUnyGKJcGUPa0MYm0vd38P3WouQjrj2egBrCNInE7ZcQwN+z3MoT7Rw03u8nP9HA==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "d3-interpolate": "^2.0.0",
+ "d3-scale": "^3.0.0",
+ "d3-shape": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.19",
+ "react-is": "^16.10.2",
+ "react-resize-detector": "^7.1.2",
+ "react-smooth": "^2.0.1",
+ "recharts-scale": "^0.4.4",
+ "reduce-css-calc": "^2.1.8"
+ }
+ },
+ "recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "requires": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
@@ -18083,6 +18722,15 @@
"strip-indent": "^1.0.1"
}
},
+ "reduce-css-calc": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz",
+ "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==",
+ "requires": {
+ "css-unit-converter": "^1.1.1",
+ "postcss-value-parser": "^3.3.0"
+ }
+ },
"reduce-flatten": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
@@ -18492,6 +19140,11 @@
"glob": "^7.1.3"
}
},
+ "robust-predicates": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz",
+ "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
+ },
"rope-sequence": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz",
@@ -18528,6 +19181,11 @@
}
}
},
+ "rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
+ },
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
@@ -19338,7 +19996,7 @@
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA=="
},
"debug": {
"version": "4.1.1",
@@ -19351,7 +20009,7 @@
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
- "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
}
}
},
@@ -20203,7 +20861,7 @@
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
- "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"to-fast-properties": {
"version": "2.0.0",
@@ -20715,6 +21373,12 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -21650,7 +22314,7 @@
"resolve-cwd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
- "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
"dev": true,
"requires": {
"resolve-from": "^3.0.0"
@@ -21659,7 +22323,7 @@
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
- "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
"dev": true
},
"semver": {
@@ -22200,7 +22864,7 @@
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
- "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
},
"yn": {
"version": "2.0.0",
diff --git a/package.json b/package.json
index 2e3ef0a07..4a61f09a9 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"@types/connect-flash": "0.0.34",
"@types/cookie-parser": "^1.4.2",
"@types/cookie-session": "^2.0.44",
+ "@types/d3": "^7.4.0",
"@types/dotenv": "^6.1.1",
"@types/exif": "^0.6.3",
"@types/express": "^4.17.13",
@@ -66,17 +67,17 @@
"@types/prosemirror-view": "^1.23.1",
"@types/rc-switch": "^1.9.2",
"@types/react": "^18.0.15",
- "@types/react-icons": "^3.0.0",
- "@types/react-reconciler": "^0.26.4",
- "@types/react-transition-group": "^4.4.5",
"@types/react-autosuggest": "^9.3.14",
"@types/react-color": "^2.17.6",
"@types/react-datepicker": "^3.1.8",
"@types/react-dom": "^18.0.6",
"@types/react-grid-layout": "^1.3.2",
+ "@types/react-icons": "^3.0.0",
"@types/react-measure": "^2.0.8",
+ "@types/react-reconciler": "^0.26.4",
"@types/react-select": "^3.1.2",
"@types/react-table": "^6.8.9",
+ "@types/react-transition-group": "^4.4.5",
"@types/request": "^2.48.8",
"@types/request-promise": "^4.1.48",
"@types/rimraf": "^2.0.5",
@@ -169,6 +170,7 @@
"browser-assert": "^1.2.1",
"bson": "^4.6.1",
"canvas": "^2.9.3",
+ "chart.js": "^3.8.0",
"child_process": "^1.0.2",
"chrome": "^0.1.0",
"class-transformer": "^0.2.0",
@@ -179,6 +181,7 @@
"cookie-parser": "^1.4.6",
"cookie-session": "^2.0.0",
"cors": "^2.8.5",
+ "d3": "^7.6.1",
"depcheck": "^0.9.2",
"equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
"exif": "^0.6.0",
@@ -264,6 +267,7 @@
"react-audio-waveform": "0.0.5",
"react-autosuggest": "^9.4.3",
"react-beautiful-dnd": "^13.1.0",
+ "react-chartjs-2": "^4.3.0",
"react-color": "^2.19.3",
"react-compound-slider": "^2.5.0",
"react-datepicker": "^3.8.0",
@@ -281,6 +285,7 @@
"react-table": "^6.11.5",
"react-transition-group": "^4.4.2",
"readline": "^1.3.0",
+ "recharts": "^2.1.12",
"request": "^2.88.2",
"request-promise": "^4.2.6",
"reveal.js": "^4.3.0",
diff --git a/src/.DS_Store b/src/.DS_Store
index 4751acf44..69407d6c2 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/views/nodes/DataViz.tsx b/src/client/views/nodes/DataViz.tsx
deleted file mode 100644
index df4c8f937..000000000
--- a/src/client/views/nodes/DataViz.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { ViewBoxBaseComponent } from '../DocComponent';
-import './DataViz.scss';
-import { FieldView, FieldViewProps } from './FieldView';
-
-@observer
-export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string) {
- return FieldView.LayoutString(DataVizBox, fieldKey);
- }
-
- render() {
- return (
- <div>
- <div>Hi</div>
- </div>
- );
- }
-}
diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx
new file mode 100644
index 000000000..c229e5471
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx
@@ -0,0 +1,144 @@
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { action, computed, observable } from 'mobx';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { LineChart } from './components/LineChart';
+import { Chart, ChartData } from './ChartInterface';
+
+export interface ChartBoxProps {
+ rootDoc: Doc;
+ dataDoc: Doc;
+ pairs: { x: number; y: number }[];
+ setChartBox: (chartBox: ChartBox) => void;
+ getAnchor: () => Doc;
+}
+
+export interface DataPoint {
+ x: number;
+ y: number;
+}
+
+@observer
+export class ChartBox extends React.Component<ChartBoxProps> {
+ @observable private _chartData: ChartData | undefined = undefined;
+ private currChart: Chart | undefined;
+
+ @computed get currView() {
+ if (this.props.rootDoc._dataVizView) {
+ return StrCast(this.props.rootDoc._currChartView);
+ } else {
+ return 'table';
+ }
+ }
+
+ constructor(props: any) {
+ super(props);
+ if (!this.props.rootDoc._currChartView) {
+ this.props.rootDoc._currChartView = 'bar';
+ }
+ }
+
+ setCurrChart = (chart: Chart) => {
+ this.currChart = chart;
+ };
+
+ @action
+ generateChartData() {
+ if (this.props.rootDoc._chartData) {
+ this._chartData = JSON.parse(StrCast(this.props.rootDoc._chartData));
+ return;
+ }
+
+ const data: ChartData = {
+ xLabel: '',
+ yLabel: '',
+ data: [[]],
+ };
+
+ if (this.props.pairs && this.props.pairs.length > 0) {
+ data.xLabel = 'x';
+ data.yLabel = 'y';
+ this.props.pairs.forEach(pair => {
+ // TODO: nda - add actual support for multiple sets of data
+ data.data[0].push({ x: pair.x, y: pair.y });
+ });
+ }
+
+ this._chartData = data;
+ this.props.rootDoc._chartData = JSON.stringify(data);
+ }
+
+ componentDidMount = () => {
+ this.generateChartData();
+ // setting timeout to allow rerender cycle of the actual chart tof inish
+ // setTimeout(() => {
+ this.props.setChartBox(this);
+ // });
+ // this.props.setChartBox(this);
+ };
+
+ @action
+ onClickChangeChart = (e: React.MouseEvent<HTMLButtonElement>) => {
+ e.preventDefault();
+ e.stopPropagation();
+ console.log(e.currentTarget.value);
+ this.props.rootDoc._currChartView = e.currentTarget.value.toLowerCase();
+ };
+
+ scrollFocus(doc: Doc, smooth: boolean): number | undefined {
+ // if x and y exist on this
+ // then highlight/focus the x and y position (datapoint)
+ // highlight for a few seconds and then remove (set a timer to unhighlight after a period of time,
+ // to be consistent, use 5 seconds and highlight color is orange)
+ // if x and y don't exist => link to whole doc => dash will take care of focusing on the entire doc
+ // return value is how long this action takes
+ // undefined => immediately, 0 => introduces a timeout
+ // number in ms => won't do any of the focus or call the automated highlighting thing until this timeout expires
+ // in this case probably number in ms doesn't matter
+
+ // for now could just update the currSelected in linechart to be what doc.x and doc.y
+
+ if (this.currChart && doc.x && doc.y) {
+ // update curr selected
+ this.currChart.setCurrSelected(Number(doc.x), Number(doc.y));
+ }
+ return 0;
+ }
+
+ _getAnchor() {
+ return this.currChart?._getAnchor();
+ }
+
+ render() {
+ if (this.props.pairs && this._chartData) {
+ let width = NumCast(this.props.rootDoc._width);
+ width = width * 0.7;
+ let height = NumCast(this.props.rootDoc._height);
+ height = height * 0.7;
+ console.log(width, height);
+ const margin = { top: 50, right: 50, bottom: 50, left: 50 };
+ return (
+ <div>
+ <div>
+ {/*{this.props.rootDoc._currChartView == 'line' ? (
+ <Line ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={e => this.onClick(e)} />
+ ) : (
+ <Bar ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={e => this.onClick(e)} />
+ )} */}
+ {/* {this.reLineChart} */}
+ <LineChart margin={margin} width={width} height={height} chartData={this._chartData} setCurrChart={this.setCurrChart} dataDoc={this.props.dataDoc} fieldKey={'data'} getAnchor={this.props.getAnchor} />
+ </div>
+ <button onClick={e => this.onClickChangeChart(e)} value="line">
+ Line
+ </button>
+ <button onClick={e => this.onClickChangeChart(e)} value="bar">
+ Bar
+ </button>
+ </div>
+ );
+ } else {
+ return <div></div>;
+ }
+ }
+}
diff --git a/src/client/views/nodes/DataVizBox/ChartInterface.ts b/src/client/views/nodes/DataVizBox/ChartInterface.ts
new file mode 100644
index 000000000..494242ac5
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/ChartInterface.ts
@@ -0,0 +1,36 @@
+import { Doc } from '../../../../fields/Doc';
+import { DataPoint } from './ChartBox';
+import { LineChart } from './components/LineChart';
+
+export interface Chart {
+ tooltipContent: (data: DataPoint) => string;
+ drawChart: () => void;
+ height: number;
+ width: number;
+ // TODO: nda - probably want to get rid of this to keep charts more generic
+ _getAnchor: () => Doc;
+ setCurrSelected: (x: number, y: number) => void;
+}
+
+export interface ChartProps {
+ chartData: ChartData;
+ width: number;
+ height: number;
+ dataDoc: Doc;
+ fieldKey: string;
+ // returns linechart component but should be generic chart
+ setCurrChart: (chart: Chart) => void;
+ getAnchor: () => Doc;
+ margin: {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+ };
+}
+
+export interface ChartData {
+ xLabel: string;
+ yLabel: string;
+ data: DataPoint[][];
+}
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 592723ee9..db677ff61 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,90 +1,170 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { StrCast } from "../../../../fields/Types";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { FieldViewProps, FieldView } from "../FieldView";
-import "./DataVizBox.scss";
-import { HistogramBox } from "./HistogramBox";
-import { TableBox } from "./TableBox";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
+import { Cast, StrCast } from '../../../../fields/Types';
+import { CsvField } from '../../../../fields/URLField';
+import { Docs } from '../../../documents/Documents';
+import { ViewBoxAnnotatableComponent } from '../../DocComponent';
+import { FieldViewProps, FieldView } from '../FieldView';
+import { ChartBox } from './ChartBox';
+import './DataVizBox.scss';
+import { TableBox } from './components/TableBox';
enum DataVizView {
- TABLE = "table",
- HISTOGRAM= "histogram"
+ TABLE = 'table',
+ HISTOGRAM = 'histogram',
}
-
@observer
-export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() {
- @observable private pairs: {x: number, y:number}[] = [{x: 1, y:2}];
+export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
+ // says we have an object and any string
+ // 2 ways of doing it
+ // @observable private pairs: { [key: string]: number | string | undefined }[] = [];
+ // @observable private pairs: { [key: string]: FieldResult }[] = [];
+ @observable private pairs: { x: number; y: number }[] = [];
+ private _chartBox: ChartBox | undefined;
+ // // another way would be store a schema that defines the type of data we are expecting from an imported doc
+
+ // method1() {
+ // this.pairs[0].x = 3;
+ // }
+
+ // method() {
+ // // this.pairs[0].x = 3;
+ // // go through the pairs
+ // const x = this.pairs[0].x;
+ // if (typeof x == 'number') {
+ // let x1 = Number(x);
+ // // let x1 = NumCast(x);
+ // }
+ // }
+
+ // could use field result
+ // [key: string]: FieldResult;
+ // instead of numeric x,y in there,
// TODO: nda - make this use enum values instead
// @observable private currView: DataVizView = DataVizView.TABLE;
+
+ // TODO: nda - use onmousedown and onmouseup when dragging and changing height and width to update the height and width props only when dragging stops
+
+ setChartBox = (chartBox: ChartBox) => {
+ this._chartBox = chartBox;
+ };
+
@computed get currView() {
if (this.rootDoc._dataVizView) {
return StrCast(this.rootDoc._dataVizView);
} else {
- return "table";
+ return 'table';
}
}
+ scrollFocus = (doc: Doc, smooth: boolean) => {
+ // reconstruct the view based on the anchor ->
+ // keep track of the datavizbox view and the type of chart we're rendering
+
+ // use dataview to restore part of it and then pass on the rest to the chartbox
+
+ // pseudocode
+ return this._chartBox?.scrollFocus(doc, smooth);
+ };
+
+ getAnchor = () => {
+ // TODO: nda - look into DocumentButtonBar and DocumentLinksButton
+
+ /*
+ if nothing selected:
+ return this.rootDoc
+ this part does not specify view type (doesn't change type when following the link)
+
+ // this slightly diff than the stuff below:
+ below part returns an anchor that specifies the viewtype for the whole doc and nothing else
+
+ */
+
+ // store whatever information would allow me to reselect the same thing (store parameters I would pass to get the exact same element)
+ const anchor =
+ // this._COMPONENTNAME._getAnchor() ??
+ this._chartBox?._getAnchor() ??
+ Docs.Create.TextanchorDocument({
+ // when we clear selection -> we should have it so chartBox getAnchor returns undefined
+ // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
+ /*put in some options*/
+ });
+
+ anchor.dataView = this.currView;
+
+ this.addDocument(anchor);
+ return anchor;
+ // have some other function in code that
+ };
constructor(props: any) {
super(props);
if (!this.rootDoc._dataVizView) {
// TODO: nda - this might not always want to default to "table"
- this.rootDoc._dataVizView = "table";
+ this.rootDoc._dataVizView = 'table';
}
}
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); }
-
- @action
- private createPairs() {
- const xVals: number[] = [0, 1, 2, 3, 4, 5];
- // const yVals: number[] = [10, 20, 30, 40, 50, 60];
- const yVals: number[] = [1, 2, 3, 4, 5, 6];
- let pairs: {
- x: number,
- y:number
- }[] = [];
- if (xVals.length != yVals.length) return pairs;
- for (let i = 0; i < xVals.length; i++) {
- pairs.push({x: xVals[i], y: yVals[i]});
- }
- this.pairs = pairs;
- return pairs;
+ public static LayoutString(fieldKey: string) {
+ return FieldView.LayoutString(DataVizBox, fieldKey);
}
@computed get selectView() {
- switch(this.currView) {
- case "table":
- return (<TableBox pairs={this.pairs} />)
- case "histogram":
- return (<HistogramBox rootDoc={this.rootDoc} pairs={this.pairs}/>)
+ switch (this.currView) {
+ case 'table':
+ return <TableBox pairs={this.pairs} />;
+ case 'histogram':
+ return <ChartBox getAnchor={this.getAnchor} setChartBox={this.setChartBox} rootDoc={this.rootDoc} pairs={this.pairs} dataDoc={this.dataDoc} />;
+ // case "histogram":
+ // return (<HistogramBox rootDoc={this.rootDoc} pairs={this.pairs}/>)
}
}
@computed get pairVals() {
- return this.createPairs();
+ return fetch('/csvData?uri=' + this.dataUrl?.url.href).then(res => res.json());
+ }
+
+ @computed get dataUrl() {
+ return Cast(this.dataDoc[this.fieldKey], CsvField);
}
componentDidMount() {
- this.createPairs();
+ this.props.setContentView?.(this);
+ // this.createPairs();
+ this.fetchData();
+ }
+
+ fetchData() {
+ const uri = this.dataUrl?.url.href;
+ fetch('/csvData?uri=' + uri).then(res =>
+ res.json().then(
+ action(res => {
+ this.pairs = res;
+ })
+ )
+ );
}
// handle changing the view using a button
@action changeViewHandler(e: React.MouseEvent<HTMLButtonElement>) {
e.preventDefault();
e.stopPropagation();
- this.rootDoc._dataVizView = this.currView == "table" ? "histogram" : "table";
+ this.rootDoc._dataVizView = this.currView == 'table' ? 'histogram' : 'table';
}
render() {
+ if (this.pairs.length == 0) {
+ return <div>Loading...</div>;
+ }
+
return (
<div className="dataViz">
- <button onClick={(e) => this.changeViewHandler(e)}>Change View</button>
+ <button onClick={e => this.changeViewHandler(e)}>Change View</button>
{this.selectView}
</div>
);
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/DrawHelper.ts b/src/client/views/nodes/DataVizBox/DrawHelper.ts
deleted file mode 100644
index 595cecebf..000000000
--- a/src/client/views/nodes/DataVizBox/DrawHelper.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-export class PIXIPoint {
- public get x() { return this.coords[0]; }
- public get y() { return this.coords[1]; }
- public set x(value: number) { this.coords[0] = value; }
- public set y(value: number) { this.coords[1] = value; }
- public coords: number[] = [0, 0];
- constructor(x: number, y: number) {
- this.coords[0] = x;
- this.coords[1] = y;
- }
-}
-
-export class PIXIRectangle {
- public x: number;
- public y: number;
- public width: number;
- public height: number;
- public get left() { return this.x; }
- public get right() { return this.x + this.width; }
- public get top() { return this.y; }
- public get bottom() { return this.top + this.height; }
- public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); }
- constructor(x: number, y: number, width: number, height: number) {
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- }
-}
-
-export class MathUtil {
-
- public static EPSILON: number = 0.001;
-
- public static Sign(value: number): number {
- return value >= 0 ? 1 : -1;
- }
-
- public static AddPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x += p2.x;
- p1.y += p2.y;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x + p2.x, p1.y + p2.y);
- }
- }
-
- public static Perp(p1: PIXIPoint): PIXIPoint {
- return new PIXIPoint(-p1.y, p1.x);
- }
-
- public static DividePoint(p1: PIXIPoint, by: number, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x /= by;
- p1.y /= by;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x / by, p1.y / by);
- }
- }
-
- public static MultiplyConstant(p1: PIXIPoint, by: number, inline: boolean = false) {
- if (inline) {
- p1.x *= by;
- p1.y *= by;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x * by, p1.y * by);
- }
- }
-
- public static SubtractPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x -= p2.x;
- p1.y -= p2.y;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x - p2.x, p1.y - p2.y);
- }
- }
-
- public static Area(rect: PIXIRectangle): number {
- return rect.width * rect.height;
- }
-
- public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) {
- // Return minimum distance between line segment vw and point p
- var l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
- if (l2 === 0.0) return MathUtil.Dist(p, v); // v === w case
- // Consider the line extending the segment, parameterized as v + t (w - v).
- // We find projection of point p onto the line.
- // It falls where t = [(p-v) . (w-v)] / |w-v|^2
- // We clamp t from [0,1] to handle points outside the segment vw.
- var dot = MathUtil.Dot(
- MathUtil.SubtractPoint(p, v),
- MathUtil.SubtractPoint(w, v)) / l2;
- var t = Math.max(0, Math.min(1, dot));
- // Projection falls on the segment
- var projection = MathUtil.AddPoint(v,
- MathUtil.MultiplyConstant(
- MathUtil.SubtractPoint(w, v), t));
- return MathUtil.Dist(p, projection);
- }
-
- public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined {
- var a1 = pe1.y - ps1.y;
- var b1 = ps1.x - pe1.x;
-
- var a2 = pe2.y - ps2.y;
- var b2 = ps2.x - pe2.x;
-
- var delta = a1 * b2 - a2 * b1;
- if (delta === 0) {
- return undefined;
- }
- var c2 = a2 * ps2.x + b2 * ps2.y;
- var c1 = a1 * ps1.x + b1 * ps1.y;
- var invdelta = 1 / delta;
- return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta);
- }
-
- public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean {
- if (p.x < rect.left - this.EPSILON) {
- return false;
- }
- if (p.x > rect.right + this.EPSILON) {
- return false;
- }
- if (p.y < rect.top - this.EPSILON) {
- return false;
- }
- if (p.y > rect.bottom + this.EPSILON) {
- return false;
- }
-
- return true;
- }
-
- public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> {
- var r1 = new PIXIPoint(rect.left, rect.top);
- var r2 = new PIXIPoint(rect.right, rect.top);
- var r3 = new PIXIPoint(rect.right, rect.bottom);
- var r4 = new PIXIPoint(rect.left, rect.bottom);
- var ret = new Array<PIXIPoint>();
- var dist = this.Dist(lineFrom, lineTo);
- var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- return ret;
- }
-
- public static Intersection(rect1: PIXIRectangle, rect2: PIXIRectangle): PIXIRectangle {
- const left = Math.max(rect1.x, rect2.x);
- const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width);
- const top = Math.max(rect1.y, rect2.y);
- const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height);
- return new PIXIRectangle(left, top, right - left, bottom - top);
- }
-
- public static Dist(p1: PIXIPoint, p2: PIXIPoint): number {
- return Math.sqrt(MathUtil.DistSquared(p1, p2));
- }
-
- public static Dot(p1: PIXIPoint, p2: PIXIPoint): number {
- return p1.x * p2.x + p1.y * p2.y;
- }
-
- public static Normalize(p1: PIXIPoint) {
- var d = this.Length(p1);
- return new PIXIPoint(p1.x / d, p1.y / d);
- }
-
- public static Length(p1: PIXIPoint): number {
- return Math.sqrt(p1.x * p1.x + p1.y * p1.y);
- }
-
- public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number {
- const a = p1.x - p2.x;
- const b = p1.y - p2.y;
- return (a * a + b * b);
- }
-
- public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean {
- return !(r2.x > r1.x + r1.width ||
- r2.x + r2.width < r1.x ||
- r2.y > r1.y + r1.height ||
- r2.y + r2.height < r1.y);
- }
-
- public static ArgMin(temp: number[]): number {
- let index = 0;
- let value = temp[0];
- for (let i = 1; i < temp.length; i++) {
- if (temp[i] < value) {
- value = temp[i];
- index = i;
- }
- }
- return index;
- }
-
- public static ArgMax(temp: number[]): number {
- let index = 0;
- let value = temp[0];
- for (let i = 1; i < temp.length; i++) {
- if (temp[i] > value) {
- value = temp[i];
- index = i;
- }
- }
- return index;
- }
-
- public static Combinations<T>(chars: T[]) {
- let result = new Array<T>();
- let f = (prefix: any, chars: any) => {
- for (let i = 0; i < chars.length; i++) {
- result.push(prefix.concat(chars[i]));
- f(prefix.concat(chars[i]), chars.slice(i + 1));
- }
- };
- f([], chars);
- return result;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.scss b/src/client/views/nodes/DataVizBox/HistogramBox.scss
deleted file mode 100644
index 5aac9dc77..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBox.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-// change the stroke color of line-svg class
-.svgLine {
- position: absolute;
- background: darkGray;
- stroke: #000;
- stroke-width: 1px;
- width:100%;
- height:100%;
- opacity: 0.4;
-}
-
-.svgContainer {
- position: absolute;
- top:0;
- left:0;
- width:100%;
- height: 100%;
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.tsx b/src/client/views/nodes/DataVizBox/HistogramBox.tsx
deleted file mode 100644
index 00dc2ef46..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBox.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast } from "../../../../fields/Types";
-import "./HistogramBox.scss";
-
-interface HistogramBoxProps {
- rootDoc: Doc;
- pairs: {
- x: number,
- y: number
- }[]
-}
-
-
-export class HistogramBox extends React.Component<HistogramBoxProps> {
-
- private origin = {x: 0.1 * this.width, y: 0.9 * this.height};
-
- @computed get width() {
- return NumCast(this.props.rootDoc.width);
- }
-
- @computed get height() {
- return NumCast(this.props.rootDoc.height);
- }
-
- @computed get x() {
- return NumCast(this.props.rootDoc.x);
- }
-
- @computed get y() {
- return NumCast(this.props.rootDoc.y);
- }
-
- @computed get generatePoints() {
- // evenly distribute points along the x axis
- const xVals: number[] = this.props.pairs.map(p => p.x);
- const yVals: number[] = this.props.pairs.map(p => p.y);
-
- const xMin = Math.min(...xVals);
- const xMax = Math.max(...xVals);
- const yMin = Math.min(...yVals);
- const yMax = Math.max(...yVals);
-
- const xRange = xMax - xMin;
- const yRange = yMax - yMin;
-
- const xScale = this.width / xRange;
- const yScale = this.height / yRange;
-
- const xOffset = (this.x + (0.1 * this.width)) - xMin * xScale;
- const yOffset = (this.y + (0.25 * this.height)) - yMin * yScale;
-
- const points: {
- x: number,
- y: number
- }[] = this.props.pairs.map(p => {
- return {
- x: (p.x * xScale + xOffset) + this.origin.x,
- y: (p.y * yScale + yOffset)
- }
- });
-
- return points;
- }
-
- @computed get generateGraphLine() {
- const points = this.generatePoints;
- // loop through points and create a line from each point to the next
- let lines: {
- x1: number,
- y1: number,
- x2: number,
- y2: number
- }[] = [];
- for (let i = 0; i < points.length - 1; i++) {
- lines.push({
- x1: points[i].x,
- y1: points[i].y,
- x2: points[i + 1].x,
- y2: points[i + 1].y
- });
- }
- // generate array of svg with lines
- let svgLines: JSX.Element[] = [];
- for (let i = 0; i < lines.length; i++) {
- svgLines.push(
- <line
- className="svgLine"
- key={i}
- x1={lines[i].x1}
- y1={lines[i].y1}
- x2={lines[i].x2}
- y2={lines[i].y2}
- stroke="black"
- strokeWidth={2}
- />
- );
- }
-
- let res = [];
- for (let i = 0; i < svgLines.length; i++) {
- res.push(<svg className="svgContainer">{svgLines[i]}</svg>)
- }
- return res;
- }
-
- @computed get generateAxes() {
-
- const xAxis = {
- x1: 0.1 * this.width,
- x2: 0.9 * this.width,
- y1: 0.9 * this.height,
- y2: 0.9 * this.height,
- };
-
- const yAxis = {
- x1: 0.1 * this.width,
- x2: 0.1 * this.width,
- y1: 0.25 * this.height,
- y2: 0.9 * this.height,
- };
-
-
- return (
- [
- (<svg className="svgContainer">
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={this.width - (0.1 * this.width)} y2={xAxis} /> */}
- <line className="svgLine" x1={xAxis.x1} y1={xAxis.y1} x2={xAxis.x2} y2={xAxis.y2}/>
-
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */}
- </svg>),
- (
- <svg className="svgContainer">
- <line className="svgLine" x1={yAxis.x1} y1={yAxis.y1} x2={yAxis.x2} y2={yAxis.y2} />
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */}
- </svg>)
- ]
- )
- }
-
-
- render() {
- return (
- <div>histogram box
- {/* <svg className="svgContainer">
- {this.generateSVGLine}
- </svg> */}
- {this.generateAxes[0]}
- {this.generateAxes[1]}
- {this.generateGraphLine.map(line => line)}
- </div>
- )
-
- }
-
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss
new file mode 100644
index 000000000..7792a2758
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/components/Chart.scss
@@ -0,0 +1,10 @@
+.tooltip {
+ // make the height width bigger
+ width: 50px;
+ height: 50px;
+}
+
+.highlight {
+ // change the color of the circle element to be red
+ fill: red;
+}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
new file mode 100644
index 000000000..d893b3e12
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -0,0 +1,290 @@
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { DataPoint } from '../ChartBox';
+// import d3
+import * as d3 from 'd3';
+import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, xAxisCreator, yAxisCreator, scaleCreatorNumerical, scaleCreatorCategorical } from '../utils/D3Utils';
+import { Docs } from '../../../../documents/Documents';
+import { Doc, DocListCast } from '../../../../../fields/Doc';
+import { Chart, ChartProps } from '../ChartInterface';
+import './Chart.scss';
+
+type minMaxRange = {
+ xMin: number | undefined;
+ xMax: number | undefined;
+ yMin: number | undefined;
+ yMax: number | undefined;
+};
+
+interface SelectedDataPoint {
+ x: number;
+ y: number;
+ elem?: d3.Selection<d3.BaseType, unknown, SVGGElement, unknown>;
+}
+
+@observer
+export class LineChart extends React.Component<ChartProps> implements Chart {
+ private _dataReactionDisposer: IReactionDisposer | undefined = undefined;
+ private _heightReactionDisposer: IReactionDisposer | undefined = undefined;
+ private _widthReactionDisposer: IReactionDisposer | undefined;
+ @observable private _x: number = 0;
+ @observable private _y: number = 0;
+ @observable private _currSelected: SelectedDataPoint | undefined = undefined;
+ // create ref for the div
+ private _chartRef: React.RefObject<HTMLDivElement> = React.createRef();
+ private _rangeVals: minMaxRange = {
+ xMin: undefined,
+ xMax: undefined,
+ yMin: undefined,
+ yMax: undefined,
+ };
+ // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
+
+ private _chartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
+
+ // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that
+
+ // write the getanchor function that gets whatever I want as the link anchor
+
+ componentDidMount = () => {
+ this._dataReactionDisposer = reaction(
+ () => this.props.chartData,
+ chartData => {
+ this._rangeVals = minMaxRange(chartData.data);
+ this.drawChart();
+ },
+ { fireImmediately: true }
+ );
+ // DocumentDecorations.Instance.Interacting
+ this._heightReactionDisposer = reaction(() => this.props.height, this.drawChart.bind(this));
+ this._widthReactionDisposer = reaction(() => this.props.width, this.drawChart.bind(this));
+ reaction(
+ () => DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']),
+ annotations => {
+ // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
+ // could be blue colored to make it look like anchor
+ console.log(annotations);
+ // this.drawAnnotations()
+ // loop through annotations and draw them
+ annotations.forEach(a => {
+ this.drawAnnotations(Number(a.x), Number(a.y));
+ });
+ // this.drawAnnotations(annotations.x, annotations.y);
+ },
+ { fireImmediately: true }
+ );
+
+ this.props.setCurrChart(this);
+ };
+
+ // gets called whenever the "data-annotations" fields gets updated
+ drawAnnotations(dataX: number, dataY: number) {
+ // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements
+ // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY
+ // if it exists, then highlight it
+ // if it doesn't exist, then remove the highlight
+ const elements = document.querySelectorAll('.datapoint');
+ for (let i = 0; i < elements.length; i++) {
+ const element = elements[i];
+ const x = element.getAttribute('data-x');
+ const y = element.getAttribute('data-y');
+ if (x === dataX.toString() && y === dataY.toString()) {
+ element.classList.add('highlight');
+ }
+ // TODO: nda - this remove highlight code should go where we remove the links
+ // } else {
+ // element.classList.remove('highlight');
+ // }
+ }
+ }
+
+ removeAnnotations(dataX: number, dataY: number) {
+ // loop through and remove any annotations that no longer exist
+ }
+
+ _getAnchor() {
+ // store whatever information would allow me to reselect the same thing (store parameters I would pass to get the exact same element)
+
+ // TODO: nda - look at pdfviewer get anchor for args
+ const doc = Docs.Create.TextanchorDocument({
+ /*put in some options*/
+ title: 'line doc selection' + this._currSelected?.x,
+ });
+ // access curr selected from the charts
+ doc.x = this._currSelected?.x;
+ doc.y = this._currSelected?.y;
+ doc.chartType = 'line';
+ return doc;
+ // have some other function in code that
+ }
+
+ componentWillUnmount() {
+ if (this._dataReactionDisposer) {
+ this._dataReactionDisposer();
+ }
+ if (this._heightReactionDisposer) {
+ this._heightReactionDisposer();
+ }
+ if (this._widthReactionDisposer) {
+ this._widthReactionDisposer();
+ }
+ }
+
+ tooltipContent(data: DataPoint) {
+ return `<b>x: ${data.x} y: ${data.y}</b>`;
+ }
+
+ @computed get height(): number {
+ return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ }
+
+ @computed get width(): number {
+ return this.props.width - this.props.margin.left - this.props.margin.right;
+ }
+
+ setupTooltip() {
+ const tooltip = d3
+ .select(this._chartRef.current)
+ .append('div')
+ .attr('class', 'tooltip')
+ .style('opacity', 0)
+ .style('background', '#fff')
+ .style('border', '1px solid #ccc')
+ .style('padding', '5px')
+ .style('position', 'absolute')
+ .style('font-size', '12px');
+ return tooltip;
+ }
+
+ // TODO: nda - use this everyewhere we update currSelected?
+ @action
+ setCurrSelected(x: number, y: number) {
+ // TODO: nda - get rid of svg element in the list?
+ this._currSelected = { x: x, y: y };
+ }
+
+ drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
+ if (this._chartSvg) {
+ const circleClass = '.circle-' + idx;
+ this._chartSvg
+ .selectAll(circleClass)
+ .data(data)
+ .join('circle') // enter append
+ .attr('class', `${circleClass} datapoint`)
+ .attr('r', '3') // radius
+ .attr('cx', d => xScale(d.x))
+ .attr('cy', d => yScale(d.y))
+ .attr('data-x', d => d.x)
+ .attr('data-y', d => d.y);
+ }
+ }
+
+ // TODO: nda - can use d3.create() to create html element instead of appending
+ drawChart() {
+ const { chartData, margin } = this.props;
+ const data = chartData.data[0];
+ // clearing tooltip and the current chart
+ d3.select(this._chartRef.current).select('svg').remove();
+ d3.select(this._chartRef.current).select('.tooltip').remove();
+
+ // TODO: nda - refactor code so that it only recalculates min max and things related to data on data update
+
+ const { xMin, xMax, yMin, yMax } = this._rangeVals;
+ // const svg = d3.select(this._chartRef.current).append(this.svgContainer.html());
+ // adding svg
+ this._chartSvg = d3
+ .select(this._chartRef.current)
+ .append('svg')
+ .attr('width', `${this.width + margin.right + margin.left}`)
+ .attr('height', `${this.height + margin.top + margin.bottom}`)
+ .append('g')
+ .attr('transform', `translate(${margin.left}, ${margin.top})`);
+
+ const svg = this._chartSvg;
+
+ if (xMin == undefined || xMax == undefined || yMin == undefined || yMax == undefined) {
+ // TODO: nda - error handle
+ return;
+ }
+
+ // creating the x and y scales
+ const xScale = scaleCreatorNumerical(xMin, xMax, 0, this.width);
+ const yScale = scaleCreatorNumerical(0, yMax, this.height, 0);
+
+ // create a line function that takes in the data.data.x and data.data.y
+ // TODO: nda - fix the types for the d here
+ const lineGen = createLineGenerator(xScale, yScale);
+
+ // create x and y grids
+ xGrid(svg.append('g'), this.height, xScale);
+ yGrid(svg.append('g'), this.width, yScale);
+ xAxisCreator(svg.append('g'), this.height, xScale);
+ yAxisCreator(svg.append('g'), this.width, yScale);
+
+ // draw the line
+ drawLine(svg.append('path'), data, lineGen);
+
+ // draw the datapoint circle
+ this.drawDataPoints(data, 0, xScale, yScale);
+
+ const focus = svg.append('g').attr('class', 'focus').style('display', 'none');
+ focus.append('circle').attr('r', 5).attr('class', 'circle');
+ const tooltip = this.setupTooltip();
+ // add all the tooltipContent to the tooltip
+ const mousemove = action((e: any) => {
+ const bisect = d3.bisector((d: DataPoint) => d.x).left;
+ const xPos = d3.pointer(e)[0];
+ const x0 = bisect(data, xScale.invert(xPos));
+ const d0 = data[x0];
+ this._x = d0.x;
+ this._y = d0.y;
+ focus.attr('transform', `translate(${xScale(d0.x)},${yScale(d0.y)})`);
+ // TODO: nda - implement tooltips
+ tooltip.transition().duration(300).style('opacity', 0.9);
+ // TODO: nda - updating the inner html could be deadly cause injection attacks!
+ tooltip.html(() => this.tooltipContent(d0)).style('transform', `translate(${xScale(d0.x) - (this.width + margin.left + margin.right) + 30}px,${yScale(d0.y) + 30}px)`);
+ });
+
+ const onPointClick = action((e: any) => {
+ const bisect = d3.bisector((d: DataPoint) => d.x).left;
+ const xPos = d3.pointer(e)[0];
+ const x0 = bisect(data, xScale.invert(xPos));
+ const d0 = data[x0];
+ this._x = d0.x;
+ this._y = d0.y;
+ // find .circle-d1 with data-x = d0.x and data-y = d0.y
+ const selected = svg.selectAll('.datapoint').filter((d: any) => d['data-x'] === d0.x && d['data-y'] === d0.y);
+ this._currSelected = { x: d0.x, y: d0.y, elem: selected };
+ console.log('Getting here');
+ // this.drawAnnotations(this._x, this._y);
+ // this.props.getAnchor();
+ console.log(this._currSelected);
+ });
+
+ this._chartSvg
+ .append('rect')
+ .attr('class', 'overlay')
+ .attr('width', this.width)
+ .attr('height', this.height + margin.top + margin.bottom)
+ .attr('fill', 'none')
+ .attr('translate', `translate(${margin.left}, ${-(margin.top + margin.bottom)})`)
+ .style('opacity', 0)
+ .on('mouseover', () => {
+ focus.style('display', null);
+ })
+ .on('mouseout', () => {
+ tooltip.transition().duration(300).style('opacity', 0);
+ })
+ .on('mousemove', mousemove)
+ .on('click', onPointClick);
+ }
+
+ render() {
+ return (
+ <div ref={this._chartRef} className="chart-container">
+ <span>Curr Selected: {this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'}</span>
+ </div>
+ );
+ }
+}
diff --git a/src/client/views/nodes/DataVizBox/TableBox.scss b/src/client/views/nodes/DataVizBox/components/TableBox.scss
index 1264d6a46..1264d6a46 100644
--- a/src/client/views/nodes/DataVizBox/TableBox.scss
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.scss
diff --git a/src/client/views/nodes/DataVizBox/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index dfa8262d8..dfa8262d8 100644
--- a/src/client/views/nodes/DataVizBox/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
new file mode 100644
index 000000000..2bb091999
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -0,0 +1,67 @@
+import * as d3 from 'd3';
+import { DataPoint } from '../ChartBox';
+
+// TODO: nda - implement function that can handle range for strings
+
+export const minMaxRange = (dataPts: DataPoint[][]) => {
+ // find the max and min of all the data points
+ const yMin = d3.min(dataPts, d => d3.min(d, d => Number(d.y)));
+ const yMax = d3.max(dataPts, d => d3.max(d, d => Number(d.y)));
+
+ const xMin = d3.min(dataPts, d => d3.min(d, d => Number(d.x)));
+ const xMax = d3.max(dataPts, d => d3.max(d, d => Number(d.x)));
+
+ return { xMin, xMax, yMin, yMax };
+};
+
+export const scaleCreatorCategorical = (labels: string[], range: number[]) => {
+ const scale = d3.scaleBand().domain(labels).range(range);
+
+ return scale;
+};
+
+export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => {
+ return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]);
+};
+
+export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) => {
+ // TODO: nda - look into the different types of curves
+ return d3
+ .line<DataPoint>()
+ .x(d => xScale(d.x))
+ .y(d => yScale(d.y))
+ .curve(d3.curveMonotoneX);
+};
+
+export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
+ console.log('x axis creator being called');
+ g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
+};
+
+export const yAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, marginLeft: number, yScale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'y-axis').call(d3.axisLeft(yScale));
+};
+
+export const xGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, scale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'xGrid')
+ .attr('transform', `translate(0,${height})`)
+ .call(
+ d3
+ .axisBottom(scale)
+ .tickSize(-height)
+ .tickFormat((a, b) => '')
+ );
+};
+
+export const yGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, width: number, scale: d3.ScaleLinear<number, number, never>) => {
+ g.attr('class', 'yGrid').call(
+ d3
+ .axisLeft(scale)
+ .tickSize(-width)
+ .tickFormat((a, b) => '')
+ );
+};
+
+export const drawLine = (p: d3.Selection<SVGPathElement, unknown, null, undefined>, dataPts: DataPoint[], lineGen: d3.Line<DataPoint>) => {
+ p.datum(dataPts).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('class', 'line').attr('d', lineGen);
+};
diff --git a/src/server/ApiManagers/DataVizManager.ts b/src/server/ApiManagers/DataVizManager.ts
new file mode 100644
index 000000000..0d43130d1
--- /dev/null
+++ b/src/server/ApiManagers/DataVizManager.ts
@@ -0,0 +1,26 @@
+import { csvParser, csvToString } from "../DataVizUtils";
+import { Method, _success } from "../RouteManager";
+import ApiManager, { Registration } from "./ApiManager";
+import { Directory, serverPathToFile } from "./UploadManager";
+import * as path from 'path';
+
+export default class DataVizManager extends ApiManager {
+ protected initialize(register: Registration): void {
+ register({
+ method: Method.GET,
+ subscription: "/csvData",
+ secureHandler: async ({ req, res }) => {
+ const uri = req.query.uri as string;
+
+ return new Promise<void>(resolve => {
+ const name = path.basename(uri);
+ const sPath = serverPathToFile(Directory.csv, name);
+ const parsedCsv = csvParser(csvToString(sPath));
+ _success(res, parsedCsv);
+ resolve();
+ });
+ }
+ });
+ }
+
+} \ No newline at end of file
diff --git a/src/server/DataVizUtils.ts b/src/server/DataVizUtils.ts
index 4fd0ca6ff..2528fb1ab 100644
--- a/src/server/DataVizUtils.ts
+++ b/src/server/DataVizUtils.ts
@@ -1,3 +1,5 @@
+import { readFileSync } from "fs";
+
export function csvParser(csv: string) {
const lines = csv.split("\n");
const headers = lines[0].split(",");
@@ -10,4 +12,8 @@ export function csvParser(csv: string) {
return obj;
});
return data;
+}
+
+export function csvToString(path: string) {
+ return readFileSync(path, 'utf8');
} \ No newline at end of file
diff --git a/src/server/index.ts b/src/server/index.ts
index 6e6bde3cb..6562860fe 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -4,6 +4,7 @@ import * as mobileDetect from 'mobile-detect';
import * as path from 'path';
import * as qs from 'query-string';
import { log_execution } from './ActionUtilities';
+import DataVizManager from './ApiManagers/DataVizManager';
import DeleteManager from './ApiManagers/DeleteManager';
import DownloadManager from './ApiManagers/DownloadManager';
import GeneralGoogleManager from './ApiManagers/GeneralGoogleManager';
@@ -62,7 +63,19 @@ async function preliminaryFunctions() {
* with the server
*/
function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) {
- const managers = [new SessionManager(), new UserManager(), new UploadManager(), new DownloadManager(), new SearchManager(), new PDFManager(), new DeleteManager(), new UtilManager(), new GeneralGoogleManager(), new GooglePhotosManager()];
+ const managers = [
+ new SessionManager(),
+ new UserManager(),
+ new UploadManager(),
+ new DownloadManager(),
+ new SearchManager(),
+ new PDFManager(),
+ new DeleteManager(),
+ new UtilManager(),
+ new GeneralGoogleManager(),
+ new GooglePhotosManager(),
+ new DataVizManager(),
+ ];
// initialize API Managers
console.log(yellow('\nregistering server routes...'));