You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by fj...@apache.org on 2019/07/31 05:35:43 UTC
[incubator-druid] branch master updated: Web console: segment
timeline (#8202)
This is an automated email from the ASF dual-hosted git repository.
fjy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-druid.git
The following commit(s) were added to refs/heads/master by this push:
new 645fca5 Web console: segment timeline (#8202)
645fca5 is described below
commit 645fca53d8f76e12d6e0fe390c89046d86d9a7e6
Author: Vadim Ogievetsky <va...@gmail.com>
AuthorDate: Tue Jul 30 22:35:30 2019 -0700
Web console: segment timeline (#8202)
* Add segment timeline chart
* fix file
* Fix bugs: no data handling & scaling problems
* resolve conflict
* changed package-lock
* do not show by default
* trust the interop
* stricter type fixes
* fix sasslint
---
web-console/package-lock.json | 768 +++++++++++++++++++--
web-console/package.json | 2 +
web-console/src/components/index.ts | 1 +
.../segment-timeline/segment-timeline.scss} | 43 +-
.../segment-timeline/segment-timeline.tsx | 496 +++++++++++++
.../__snapshots__/datasource-view.spec.tsx.snap | 7 +-
.../src/views/datasource-view/datasource-view.scss | 12 +-
.../src/views/datasource-view/datasource-view.tsx | 48 +-
web-console/src/visualization/bar-group.tsx | 90 +++
.../bar-unit.scss} | 18 +-
web-console/src/visualization/bar-unit.tsx | 55 ++
.../chart-axis.tsx} | 33 +-
.../stacked-bar-chart.scss} | 24 +-
.../src/visualization/stacked-bar-chart.tsx | 172 +++++
14 files changed, 1647 insertions(+), 122 deletions(-)
diff --git a/web-console/package-lock.json b/web-console/package-lock.json
index a93be6d..689c610 100644
--- a/web-console/package-lock.json
+++ b/web-console/package-lock.json
@@ -1439,12 +1439,274 @@
"integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==",
"dev": true
},
+ "@types/d3": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-5.7.2.tgz",
+ "integrity": "sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "^1",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-collection": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@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-voronoi": "*",
+ "@types/d3-zoom": "*"
+ },
+ "dependencies": {
+ "@types/d3-array": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.7.tgz",
+ "integrity": "sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA==",
+ "dev": true
+ }
+ }
+ },
"@types/d3-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.0.0.tgz",
"integrity": "sha512-rGqfPVowNDTszSFvwoZIXvrPG7s/qKzm9piCRIH6xwTTRu7pPZ3ootULFnPkTt74B6i5lN0FpLQL24qGOw1uZA==",
"dev": true
},
+ "@types/d3-axis": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.12.tgz",
+ "integrity": "sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-brush": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.10.tgz",
+ "integrity": "sha512-J8jREATIrfJaAfhJivqaEKPnJsRlwwrOPje+ABqZFgamADjll+q9zaDXnYyjiGPPsiJEU+Qq9jQi5rECxIOfhg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-chord": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.9.tgz",
+ "integrity": "sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw==",
+ "dev": true
+ },
+ "@types/d3-collection": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.8.tgz",
+ "integrity": "sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ==",
+ "dev": true
+ },
+ "@types/d3-color": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz",
+ "integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==",
+ "dev": true
+ },
+ "@types/d3-contour": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-1.3.0.tgz",
+ "integrity": "sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-dispatch": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.7.tgz",
+ "integrity": "sha512-M+z84G7UKwK6hEPnGCSccOg8zJ3Nk2hgDQ9sCstHXgsFU0sMxlIZVKqKB5oxUDbALqQG6ucg0G9e8cmOSlishg==",
+ "dev": true
+ },
+ "@types/d3-drag": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.3.tgz",
+ "integrity": "sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-dsv": {
+ "version": "1.0.36",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz",
+ "integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA==",
+ "dev": true
+ },
+ "@types/d3-ease": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.8.tgz",
+ "integrity": "sha512-VRf8czVWHSJPoUWxMunzpePK02//wHDAswknU8QWzcyrQn6pqe46bHRYi2smSpw5VjsT2CG8k/QeWIdWPS3Bmg==",
+ "dev": true
+ },
+ "@types/d3-fetch": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-1.1.5.tgz",
+ "integrity": "sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A==",
+ "dev": true,
+ "requires": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "@types/d3-force": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz",
+ "integrity": "sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA==",
+ "dev": true
+ },
+ "@types/d3-format": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz",
+ "integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A==",
+ "dev": true
+ },
+ "@types/d3-geo": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.11.1.tgz",
+ "integrity": "sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ==",
+ "dev": true,
+ "requires": {
+ "@types/geojson": "*"
+ }
+ },
+ "@types/d3-hierarchy": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz",
+ "integrity": "sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg==",
+ "dev": true
+ },
+ "@types/d3-interpolate": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz",
+ "integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==",
+ "dev": true,
+ "requires": {
+ "@types/d3-color": "*"
+ }
+ },
+ "@types/d3-path": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz",
+ "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==",
+ "dev": true
+ },
+ "@types/d3-polygon": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.7.tgz",
+ "integrity": "sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q==",
+ "dev": true
+ },
+ "@types/d3-quadtree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
+ "integrity": "sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q==",
+ "dev": true
+ },
+ "@types/d3-random": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.2.tgz",
+ "integrity": "sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA==",
+ "dev": true
+ },
+ "@types/d3-scale": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.1.1.tgz",
+ "integrity": "sha512-kNTkbZQ+N/Ip8oX9PByXfDLoCSaZYm+VUOasbmsa6KD850/ziMdYepg/8kLg2plHzoLANdMqPoYQbvExevLUHg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-time": "*"
+ }
+ },
+ "@types/d3-scale-chromatic": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.3.1.tgz",
+ "integrity": "sha512-Ny3rLbV5tnmqgW7w/poCcef4kXP8mHPo/p8EjTS5d9OUk8MlqAeRaM8eF7Vyv7QMLiIXNE94Pa1cMLSPkXQBoQ==",
+ "dev": true
+ },
+ "@types/d3-selection": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.1.tgz",
+ "integrity": "sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA==",
+ "dev": true
+ },
+ "@types/d3-shape": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.1.tgz",
+ "integrity": "sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg==",
+ "dev": true,
+ "requires": {
+ "@types/d3-path": "*"
+ }
+ },
+ "@types/d3-time": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz",
+ "integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw==",
+ "dev": true
+ },
+ "@types/d3-time-format": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz",
+ "integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g==",
+ "dev": true
+ },
+ "@types/d3-timer": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.9.tgz",
+ "integrity": "sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ==",
+ "dev": true
+ },
+ "@types/d3-transition": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.4.tgz",
+ "integrity": "sha512-/vsmKVUIXEyCcIXYAlw7bnYkIs9/J/nZbptRJFKUN3FdXq/dF6j9z9xXzerkyU6TDHLrMrwx9eGwdKyTIy/j9w==",
+ "dev": true,
+ "requires": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "@types/d3-voronoi": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz",
+ "integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==",
+ "dev": true
+ },
+ "@types/d3-zoom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.4.tgz",
+ "integrity": "sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA==",
+ "dev": true,
+ "requires": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
"@types/dom4": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/dom4/-/dom4-2.0.1.tgz",
@@ -1481,6 +1743,12 @@
"integrity": "sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==",
"dev": true
},
+ "@types/geojson": {
+ "version": "7946.0.7",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz",
+ "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==",
+ "dev": true
+ },
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
@@ -3237,8 +3505,7 @@
"commander": {
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
- "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
- "dev": true
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
},
"commondir": {
"version": "1.0.1",
@@ -3711,11 +3978,305 @@
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"dev": true
},
+ "d3": {
+ "version": "5.9.7",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-5.9.7.tgz",
+ "integrity": "sha512-jENytrmdXtGPw7HuSK2S4gxRM1eUGjKvWQkQ6ct4yK+DB8SG3VcnVrwesfnsv8rIcxMUg18TafT4Q8mOZUMP4Q==",
+ "requires": {
+ "d3-array": "1",
+ "d3-axis": "1",
+ "d3-brush": "1",
+ "d3-chord": "1",
+ "d3-collection": "1",
+ "d3-color": "1",
+ "d3-contour": "1",
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-dsv": "1",
+ "d3-ease": "1",
+ "d3-fetch": "1",
+ "d3-force": "1",
+ "d3-format": "1",
+ "d3-geo": "1",
+ "d3-hierarchy": "1",
+ "d3-interpolate": "1",
+ "d3-path": "1",
+ "d3-polygon": "1",
+ "d3-quadtree": "1",
+ "d3-random": "1",
+ "d3-scale": "2",
+ "d3-scale-chromatic": "1",
+ "d3-selection": "1",
+ "d3-shape": "1",
+ "d3-time": "1",
+ "d3-time-format": "2",
+ "d3-timer": "1",
+ "d3-transition": "1",
+ "d3-voronoi": "1",
+ "d3-zoom": "1"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ }
+ }
+ },
"d3-array": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.2.0.tgz",
"integrity": "sha512-eE0QmSh6xToqM3sxHiJYg/QFdNn52ZEgmFE8A8abU8GsHvsIOolqH8B70/8+VGAKm5MlwaExhqR3DLIjOJMLPA=="
},
+ "d3-axis": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz",
+ "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ=="
+ },
+ "d3-brush": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz",
+ "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
+ },
+ "d3-chord": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz",
+ "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==",
+ "requires": {
+ "d3-array": "1",
+ "d3-path": "1"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ }
+ }
+ },
+ "d3-collection": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+ },
+ "d3-color": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.8.tgz",
+ "integrity": "sha512-yeANXzP37PHk0DbSTMNPhnJD+Nn4G//O5E825bR6fAfHH43hobSBpgB9G9oWVl9+XgUaQ4yCnsX1H+l8DoaL9A=="
+ },
+ "d3-contour": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz",
+ "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==",
+ "requires": {
+ "d3-array": "^1.1.1"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ }
+ }
+ },
+ "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-dsv": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz",
+ "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==",
+ "requires": {
+ "commander": "2",
+ "iconv-lite": "0.4",
+ "rw": "1"
+ }
+ },
+ "d3-ease": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz",
+ "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ=="
+ },
+ "d3-fetch": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz",
+ "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==",
+ "requires": {
+ "d3-dsv": "1"
+ }
+ },
+ "d3-force": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
+ "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
+ "requires": {
+ "d3-collection": "1",
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
+ "d3-format": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz",
+ "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ=="
+ },
+ "d3-geo": {
+ "version": "1.11.6",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz",
+ "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==",
+ "requires": {
+ "d3-array": "1"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ }
+ }
+ },
+ "d3-hierarchy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz",
+ "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w=="
+ },
+ "d3-interpolate": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz",
+ "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==",
+ "requires": {
+ "d3-color": "1"
+ }
+ },
+ "d3-path": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz",
+ "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA=="
+ },
+ "d3-polygon": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz",
+ "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w=="
+ },
+ "d3-quadtree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz",
+ "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA=="
+ },
+ "d3-random": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
+ "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
+ },
+ "d3-scale": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz",
+ "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==",
+ "requires": {
+ "d3-array": "^1.2.0",
+ "d3-collection": "1",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ },
+ "dependencies": {
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ }
+ }
+ },
+ "d3-scale-chromatic": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz",
+ "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==",
+ "requires": {
+ "d3-color": "1",
+ "d3-interpolate": "1"
+ }
+ },
+ "d3-selection": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz",
+ "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg=="
+ },
+ "d3-shape": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz",
+ "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==",
+ "requires": {
+ "d3-path": "1"
+ }
+ },
+ "d3-time": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz",
+ "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw=="
+ },
+ "d3-time-format": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz",
+ "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==",
+ "requires": {
+ "d3-time": "1"
+ }
+ },
+ "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=="
+ },
+ "d3-transition": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz",
+ "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==",
+ "requires": {
+ "d3-color": "1",
+ "d3-dispatch": "1",
+ "d3-ease": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "^1.1.0",
+ "d3-timer": "1"
+ }
+ },
+ "d3-voronoi": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz",
+ "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg=="
+ },
+ "d3-zoom": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz",
+ "integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==",
+ "requires": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
+ },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5182,25 +5743,29 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"dev": true,
"optional": true,
"requires": {
@@ -5210,13 +5775,15 @@
},
"balanced-match": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
"requires": {
@@ -5226,37 +5793,43 @@
},
"chownr": {
"version": "1.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "4.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"optional": true,
"requires": {
@@ -5265,25 +5838,29 @@
},
"deep-extend": {
"version": "0.6.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz",
+ "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"dev": true,
"optional": true,
"requires": {
@@ -5292,13 +5869,15 @@
},
"fs.realpath": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
"requires": {
@@ -5314,7 +5893,8 @@
},
"glob": {
"version": "7.1.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"optional": true,
"requires": {
@@ -5328,13 +5908,15 @@
},
"has-unicode": {
"version": "2.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"optional": true,
"requires": {
@@ -5343,7 +5925,8 @@
},
"ignore-walk": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"dev": true,
"optional": true,
"requires": {
@@ -5352,7 +5935,8 @@
},
"inflight": {
"version": "1.0.6",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
"requires": {
@@ -5362,19 +5946,22 @@
},
"inherits": {
"version": "2.0.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
"requires": {
@@ -5383,13 +5970,15 @@
},
"isarray": {
"version": "1.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
"requires": {
@@ -5398,13 +5987,15 @@
},
"minimist": {
"version": "0.0.8",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+ "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true,
"optional": true,
"requires": {
@@ -5414,7 +6005,8 @@
},
"minizlib": {
"version": "1.2.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz",
+ "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"dev": true,
"optional": true,
"requires": {
@@ -5423,7 +6015,8 @@
},
"mkdirp": {
"version": "0.5.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"optional": true,
"requires": {
@@ -5432,13 +6025,15 @@
},
"ms": {
"version": "2.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true,
"optional": true
},
"needle": {
"version": "2.3.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz",
+ "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"dev": true,
"optional": true,
"requires": {
@@ -5449,7 +6044,8 @@
},
"node-pre-gyp": {
"version": "0.12.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz",
+ "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"dev": true,
"optional": true,
"requires": {
@@ -5467,7 +6063,8 @@
},
"nopt": {
"version": "4.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"dev": true,
"optional": true,
"requires": {
@@ -5477,13 +6074,15 @@
},
"npm-bundled": {
"version": "1.0.6",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+ "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.4.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz",
+ "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"dev": true,
"optional": true,
"requires": {
@@ -5493,7 +6092,8 @@
},
"npmlog": {
"version": "4.1.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
"requires": {
@@ -5505,19 +6105,22 @@
},
"number-is-nan": {
"version": "1.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
"requires": {
@@ -5526,19 +6129,22 @@
},
"os-homedir": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
"requires": {
@@ -5548,19 +6154,22 @@
},
"path-is-absolute": {
"version": "1.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.8",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"optional": true,
"requires": {
@@ -5572,7 +6181,8 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true,
"optional": true
}
@@ -5580,7 +6190,8 @@
},
"readable-stream": {
"version": "2.3.6",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
"requires": {
@@ -5595,7 +6206,8 @@
},
"rimraf": {
"version": "2.6.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"optional": true,
"requires": {
@@ -5604,43 +6216,50 @@
},
"safe-buffer": {
"version": "5.1.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.7.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz",
+ "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
"requires": {
@@ -5651,7 +6270,8 @@
},
"string_decoder": {
"version": "1.1.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
"requires": {
@@ -5660,7 +6280,8 @@
},
"strip-ansi": {
"version": "3.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
@@ -5669,13 +6290,15 @@
},
"strip-json-comments": {
"version": "2.0.1",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.8",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
+ "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"optional": true,
"requires": {
@@ -5690,13 +6313,15 @@
},
"util-deprecate": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"optional": true,
"requires": {
@@ -5705,13 +6330,15 @@
},
"wrappy": {
"version": "1.0.2",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
- "bundled": true,
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true,
"optional": true
}
@@ -11063,6 +11690,11 @@
"aproba": "^1.1.1"
}
},
+ "rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+ },
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
diff --git a/web-console/package.json b/web-console/package.json
index ea100bc..4f62c4d 100644
--- a/web-console/package.json
+++ b/web-console/package.json
@@ -58,6 +58,7 @@
"classnames": "^2.2.6",
"copy-to-clipboard": "^3.2.0",
"core-js": "^3.1.4",
+ "d3": "^5.9.7",
"d3-array": "^2.2.0",
"druid-console": "^0.0.2",
"file-saver": "^2.0.2",
@@ -82,6 +83,7 @@
"@babel/preset-env": "^7.5.5",
"@testing-library/react": "^8.0.7",
"@types/classnames": "^2.2.9",
+ "@types/d3": "^5.7.2",
"@types/d3-array": "^2.0.0",
"@types/enzyme": "^3.10.3",
"@types/enzyme-adapter-react-16": "^1.0.5",
diff --git a/web-console/src/components/index.ts b/web-console/src/components/index.ts
index fb9475d..3280dd8 100644
--- a/web-console/src/components/index.ts
+++ b/web-console/src/components/index.ts
@@ -35,3 +35,4 @@ export * from './view-control-bar/view-control-bar';
export * from './clearable-input/clearable-input';
export * from './refresh-button/refresh-button';
export * from './timed-button/timed-button';
+export * from './segment-timeline/segment-timeline';
diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/components/segment-timeline/segment-timeline.scss
similarity index 60%
copy from web-console/src/views/datasource-view/datasource-view.scss
copy to web-console/src/components/segment-timeline/segment-timeline.scss
index 7c483ce..7c8682c 100644
--- a/web-console/src/views/datasource-view/datasource-view.scss
+++ b/web-console/src/components/segment-timeline/segment-timeline.scss
@@ -16,20 +16,43 @@
* limitations under the License.
*/
-@import '../../variables';
-
-.data-sources-view {
+.segment-timeline {
+ display: grid;
+ grid-template-columns: 85% 15%;
height: 100%;
- width: 100%;
+ margin-top: 10px;
+
+ .loader {
+ width: 85%;
+ }
+
+ .loading-error {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ -webkit-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+ }
+
+ .bar-chart-tooltip {
+ margin-top: 15px;
+ text-align: center;
- .clickable-cell {
- cursor: pointer;
+ div {
+ display: inline-block;
+ width: 200px;
+ }
}
- .ReactTable {
+ .no-data-text {
position: absolute;
- top: $view-control-bar-height + $standard-padding;
- bottom: 0;
- width: 100%;
+ left: 30vw;
+ top: 15vh;
+ font-size: 20px;
+ }
+
+ .side-control {
+ padding-left: 1vw;
+ padding-top: 5vh;
}
}
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx
new file mode 100644
index 0000000..34d3a88
--- /dev/null
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { FormGroup, HTMLSelect, Radio, RadioGroup } from '@blueprintjs/core';
+import * as d3 from 'd3';
+import { AxisScale } from 'd3';
+import React from 'react';
+
+import { formatBytes, queryDruidSql, QueryManager } from '../../utils/index';
+import { StackedBarChart } from '../../visualization/stacked-bar-chart';
+import { Loader } from '../loader/loader';
+
+import './segment-timeline.scss';
+
+interface SegmentTimelineProps extends React.Props<any> {
+ chartHeight: number;
+ chartWidth: number;
+}
+
+interface SegmentTimelineState {
+ data?: Record<string, any>;
+ datasources: string[];
+ stackedData?: Record<string, BarUnitData[]>;
+ singleDatasourceData?: Record<string, Record<string, BarUnitData[]>>;
+ activeDatasource: string | null;
+ activeDataType: string; // "countData" || "sizeData"
+ dataToRender: BarUnitData[];
+ timeSpan: number; // by months
+ loading: boolean;
+ error?: string;
+ xScale: AxisScale<Date> | null;
+ yScale: AxisScale<number> | null;
+ dStart: Date;
+ dEnd: Date;
+}
+
+interface BarChartScales {
+ xScale: AxisScale<Date>;
+ yScale: AxisScale<number>;
+}
+
+export interface BarUnitData {
+ x: number;
+ y: number;
+ y0?: number;
+ width: number;
+ datasource: string;
+ color: string;
+}
+
+export interface BarChartMargin {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+}
+
+export class SegmentTimeline extends React.Component<SegmentTimelineProps, SegmentTimelineState> {
+ private dataQueryManager: QueryManager<null, any>;
+ private datasourceQueryManager: QueryManager<null, any>;
+ private colors = [
+ '#b33040',
+ '#d25c4d',
+ '#f2b447',
+ '#d9d574',
+ '#4FAA7E',
+ '#57ceff',
+ '#789113',
+ '#098777',
+ '#b33040',
+ '#d2757b',
+ '#f29063',
+ '#d9a241',
+ '#80aa61',
+ '#c4ff9e',
+ '#915412',
+ '#87606c',
+ ];
+ private chartMargin = { top: 20, right: 10, bottom: 20, left: 10 };
+
+ constructor(props: SegmentTimelineProps) {
+ super(props);
+ const dStart = new Date();
+ const dEnd = new Date();
+ dStart.setMonth(dStart.getMonth() - 3);
+ this.state = {
+ data: {},
+ datasources: [],
+ stackedData: {},
+ singleDatasourceData: {},
+ dataToRender: [],
+ activeDatasource: null,
+ activeDataType: 'countData',
+ timeSpan: 3,
+ loading: true,
+ xScale: null,
+ yScale: null,
+ dEnd: dEnd,
+ dStart: dStart,
+ };
+
+ this.dataQueryManager = new QueryManager({
+ processQuery: async () => {
+ const { timeSpan } = this.state;
+ const query = `SELECT "start", "end", "datasource", COUNT(*) AS "count", sum(size) as "total_size"
+ FROM sys.segments
+ WHERE "start" > time_format(TIMESTAMPADD(MONTH, -${timeSpan}, current_timestamp), 'yyyy-MM-dd''T''hh:mm:ss.SSS')
+ GROUP BY 1, 2, 3
+ ORDER BY "start" DESC`;
+ const resp: any[] = await queryDruidSql({ query });
+ const data = this.processRawData(resp);
+ const stackedData = this.calculateStackedData(data);
+ const singleDatasourceData = this.calculateSingleDatasourceData(data);
+ return { data, stackedData, singleDatasourceData };
+ },
+ onStateChange: ({ result, loading, error }) => {
+ this.setState({
+ data: result ? result.data : undefined,
+ stackedData: result ? result.stackedData : undefined,
+ singleDatasourceData: result ? result.singleDatasourceData : undefined,
+ loading,
+ error,
+ });
+ },
+ });
+
+ this.datasourceQueryManager = new QueryManager({
+ processQuery: async () => {
+ const query = `SELECT DISTINCT "datasource" FROM sys.segments`;
+ const resp: any[] = await queryDruidSql({ query });
+ const data = resp.map((r: any) => r.datasource);
+ return data;
+ },
+ onStateChange: ({ result }) => {
+ if (result == null) result = [];
+ this.setState({
+ datasources: result,
+ });
+ },
+ });
+ }
+
+ componentDidMount(): void {
+ this.dataQueryManager.runQuery(null);
+ this.datasourceQueryManager.runQuery(null);
+ }
+
+ componentWillUnmount(): void {
+ this.dataQueryManager.terminate();
+ this.datasourceQueryManager.terminate();
+ }
+
+ componentDidUpdate(prevProps: SegmentTimelineProps, prevState: SegmentTimelineState): void {
+ const { activeDatasource, activeDataType, singleDatasourceData, stackedData } = this.state;
+ if (
+ prevState.data !== this.state.data ||
+ prevState.activeDataType !== this.state.activeDataType ||
+ prevState.activeDatasource !== this.state.activeDatasource ||
+ prevProps.chartWidth !== this.props.chartWidth ||
+ prevProps.chartHeight !== this.props.chartHeight
+ ) {
+ const scales: BarChartScales | undefined = this.calculateScales();
+ let dataToRender: BarUnitData[] | undefined;
+ dataToRender = activeDatasource
+ ? singleDatasourceData
+ ? singleDatasourceData[activeDataType][activeDatasource]
+ : undefined
+ : stackedData
+ ? stackedData[activeDataType]
+ : undefined;
+
+ if (scales && dataToRender) {
+ this.setState({
+ dataToRender,
+ xScale: scales.xScale,
+ yScale: scales.yScale,
+ });
+ }
+ }
+ }
+
+ private processRawData(data: any) {
+ if (data === null) return [];
+ const countData: Record<string, any> = {};
+ const sizeData: Record<string, any> = {};
+ data.forEach((entry: any) => {
+ const start = entry.start;
+ const day = start.split('T')[0];
+ const datasource = entry.datasource;
+ const count = entry.count;
+ const segmentSize = entry['total_size'];
+ if (countData[day] === undefined) {
+ countData[day] = {
+ day,
+ [datasource]: count,
+ total: count,
+ };
+ sizeData[day] = {
+ day,
+ [datasource]: segmentSize,
+ total: segmentSize,
+ };
+ } else {
+ const countDataEntry = countData[day][datasource];
+ countData[day][datasource] = count + (countDataEntry === undefined ? 0 : countDataEntry);
+ const sizeDataEntry = sizeData[day][datasource];
+ sizeData[day][datasource] = segmentSize + (sizeDataEntry === undefined ? 0 : sizeDataEntry);
+ countData[day].total += count;
+ sizeData[day].total += segmentSize;
+ }
+ });
+ const countDataArray = Object.keys(countData)
+ .reverse()
+ .map((time: any) => {
+ return countData[time];
+ });
+ const sizeDataArray = Object.keys(sizeData)
+ .reverse()
+ .map((time: any) => {
+ return sizeData[time];
+ });
+ return { countData: countDataArray, sizeData: sizeDataArray };
+ }
+
+ private calculateStackedData(data: Record<string, any>): Record<string, BarUnitData[]> {
+ const { datasources } = this.state;
+ const newStackedData: Record<string, BarUnitData[]> = {};
+ Object.keys(data).forEach((type: any) => {
+ const stackedData: any = data[type].map((d: any) => {
+ let y0 = 0;
+ return datasources.map((datasource: string, i) => {
+ const barUnitData = {
+ x: d.day,
+ y: d[datasource] === undefined ? 0 : d[datasource],
+ y0,
+ datasource,
+ color: this.colors[i],
+ };
+ y0 += d[datasource] === undefined ? 0 : d[datasource];
+ return barUnitData;
+ });
+ });
+ newStackedData[type] = stackedData.flat();
+ });
+ return newStackedData;
+ }
+
+ private calculateSingleDatasourceData(
+ data: Record<string, any>,
+ ): Record<string, Record<string, BarUnitData[]>> {
+ const { datasources } = this.state;
+ const singleDatasourceData: Record<string, Record<string, BarUnitData[]>> = {};
+ Object.keys(data).forEach(dataType => {
+ singleDatasourceData[dataType] = {};
+ datasources.forEach((datasource, i) => {
+ const currentData = data[dataType];
+ if (currentData.length === 0) return;
+ const dataResult = currentData.map((d: any) => {
+ let y = 0;
+ if (d[datasource] !== undefined) {
+ y = d[datasource];
+ }
+ return {
+ x: d.day,
+ y,
+ datasource,
+ color: this.colors[i],
+ };
+ });
+ if (!dataResult.every((d: any) => d.y === 0)) {
+ singleDatasourceData[dataType][datasource] = dataResult;
+ }
+ });
+ });
+ return singleDatasourceData;
+ }
+
+ private calculateScales(): BarChartScales | undefined {
+ const { chartWidth, chartHeight } = this.props;
+ const {
+ data,
+ activeDataType,
+ activeDatasource,
+ singleDatasourceData,
+ dStart,
+ dEnd,
+ } = this.state;
+ if (!data || !Object.keys(data).length) return;
+ const activeData = data[activeDataType];
+ const xDomain: Date[] = [dStart, dEnd];
+ let yDomain: number[] = [
+ 0,
+ activeData.length === 0
+ ? 0
+ : activeData.reduce((max: any, d: any) => (max.total > d.total ? max : d)).total,
+ ];
+
+ if (
+ activeDatasource !== null &&
+ singleDatasourceData![activeDataType][activeDatasource] !== undefined
+ ) {
+ yDomain = [
+ 0,
+ singleDatasourceData![activeDataType][activeDatasource].reduce((max: any, d: any) =>
+ max.y > d.y ? max : d,
+ ).y,
+ ];
+ }
+
+ const xScale: AxisScale<Date> = d3
+ .scaleTime()
+ .domain(xDomain)
+ .range([0, chartWidth - this.chartMargin.left - this.chartMargin.right]);
+
+ const yScale: AxisScale<number> = d3
+ .scaleLinear()
+ .rangeRound([chartHeight - this.chartMargin.top - this.chartMargin.bottom, 0])
+ .domain(yDomain);
+
+ return {
+ xScale,
+ yScale,
+ };
+ }
+
+ onTimeSpanChange = (e: any) => {
+ const dStart = new Date();
+ const dEnd = new Date();
+ dStart.setMonth(dStart.getMonth() - e);
+ this.setState({
+ timeSpan: e,
+ loading: true,
+ dStart,
+ dEnd,
+ });
+ this.dataQueryManager.rerunLastQuery();
+ };
+
+ formatTick = (n: number) => {
+ const { activeDataType } = this.state;
+ if (activeDataType === 'countData') {
+ return n.toString();
+ } else {
+ return formatBytes(n);
+ }
+ };
+
+ renderStackedBarChart() {
+ const { chartWidth, chartHeight } = this.props;
+ const {
+ loading,
+ dataToRender,
+ activeDataType,
+ error,
+ xScale,
+ yScale,
+ data,
+ activeDatasource,
+ dStart,
+ dEnd,
+ } = this.state;
+ if (loading) {
+ return (
+ <div>
+ <Loader loading={loading} />
+ </div>
+ );
+ }
+
+ if (error) {
+ return (
+ <div>
+ <span className={'no-data-text'}>Error when loading data: {error}</span>
+ </div>
+ );
+ }
+
+ if (xScale === null || yScale === null) {
+ return (
+ <div>
+ <span className={'no-data-text'}>Error when calculating scales</span>
+ </div>
+ );
+ }
+
+ if (data![activeDataType].length === 0) {
+ return (
+ <div>
+ <span className={'no-data-text'}>No data available for the time span selected</span>
+ </div>
+ );
+ }
+
+ if (
+ activeDatasource !== null &&
+ data![activeDataType].every((d: any) => d[activeDatasource] === undefined)
+ ) {
+ return (
+ <div>
+ <span className={'no-data-text'}>
+ No data available for <i>{activeDatasource}</i>
+ </span>
+ </div>
+ );
+ }
+
+ const millisecondsPerDay = 24 * 60 * 60 * 1000;
+ const barCounts = (dEnd.getTime() - dStart.getTime()) / millisecondsPerDay;
+ const barWidth = (chartWidth - this.chartMargin.left - this.chartMargin.right) / barCounts;
+ return (
+ <StackedBarChart
+ dataToRender={dataToRender}
+ svgHeight={chartHeight}
+ svgWidth={chartWidth}
+ margin={this.chartMargin}
+ changeActiveDatasource={(e: string) => this.setState({ activeDatasource: e })}
+ activeDataType={activeDataType}
+ formatTick={(n: number) => this.formatTick(n)}
+ xScale={xScale}
+ yScale={yScale}
+ barWidth={barWidth}
+ />
+ );
+ }
+
+ render(): JSX.Element {
+ const { datasources, activeDataType, activeDatasource, timeSpan } = this.state;
+
+ return (
+ <div className={'segment-timeline app-view'}>
+ {this.renderStackedBarChart()}
+ <div className={'side-control'}>
+ <FormGroup>
+ <RadioGroup
+ onChange={(e: any) => this.setState({ activeDataType: e.target.value })}
+ selectedValue={activeDataType}
+ >
+ <Radio label={'Segment count'} value={'countData'} />
+ <Radio label={'Total size'} value={'sizeData'} />
+ </RadioGroup>
+ </FormGroup>
+
+ <FormGroup label={'Datasource:'}>
+ <HTMLSelect
+ onChange={(e: any) =>
+ this.setState({
+ activeDatasource: e.target.value === 'all' ? null : e.target.value,
+ })
+ }
+ value={activeDatasource == null ? 'all' : activeDatasource}
+ fill
+ >
+ <option value={'all'}>Show all</option>
+ {datasources.map(d => {
+ return (
+ <option key={d} value={d}>
+ {d}
+ </option>
+ );
+ })}
+ </HTMLSelect>
+ </FormGroup>
+
+ <FormGroup label={'Period:'}>
+ <HTMLSelect
+ onChange={(e: any) => this.onTimeSpanChange(e.target.value)}
+ value={timeSpan}
+ fill
+ >
+ <option value={1}> 1 months</option>
+ <option value={3}> 3 months</option>
+ <option value={6}> 6 months</option>
+ <option value={9}> 9 months</option>
+ <option value={12}> 1 year</option>
+ </HTMLSelect>
+ </FormGroup>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
index b619181..9162516 100755
--- a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
+++ b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap
@@ -18,6 +18,11 @@ exports[`data source view matches snapshot 1`] = `
/>
<Blueprint3.Switch
checked={false}
+ label="Show segment timeline"
+ onChange={[Function]}
+ />
+ <Blueprint3.Switch
+ checked={false}
label="Show disabled"
onChange={[Function]}
/>
@@ -58,7 +63,7 @@ exports[`data source view matches snapshot 1`] = `
TrComponent={[Function]}
TrGroupComponent={[Function]}
aggregatedKey="_aggregated"
- className=""
+ className="-striped -highlight full-height"
collapseOnDataChange={true}
collapseOnPageChange={true}
collapseOnSortingChange={true}
diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/views/datasource-view/datasource-view.scss
index 7c483ce..6891a82 100644
--- a/web-console/src/views/datasource-view/datasource-view.scss
+++ b/web-console/src/views/datasource-view/datasource-view.scss
@@ -21,15 +21,25 @@
.data-sources-view {
height: 100%;
width: 100%;
+ overflow: auto;
.clickable-cell {
cursor: pointer;
}
+ .chart-container {
+ height: 40vh;
+ margin-bottom: 10px;
+ }
+
.ReactTable {
position: absolute;
- top: $view-control-bar-height + $standard-padding;
+ top: 50%; //$view-control-bar-height + $standard-padding;
bottom: 0;
width: 100%;
+
+ &.full-height {
+ top: 50px;
+ }
}
}
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index 6580af6..9390984 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -19,6 +19,7 @@
import { Button, FormGroup, InputGroup, Intent, Switch } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import axios from 'axios';
+import classNames from 'classnames';
import React from 'react';
import ReactTable, { Filter } from 'react-table';
@@ -30,6 +31,7 @@ import {
ViewControlBar,
} from '../../components';
import { ActionIcon } from '../../components/action-icon/action-icon';
+import { SegmentTimeline } from '../../components/segment-timeline/segment-timeline';
import { AsyncActionDialog, CompactionDialog, RetentionDialog } from '../../dialogs';
import { AppToaster } from '../../singletons/toaster';
import {
@@ -123,6 +125,9 @@ export interface DatasourcesViewState {
dropReloadAction: 'drop' | 'reload';
dropReloadInterval: string;
hiddenColumns: LocalStorageBackedArray<string>;
+ showChart: boolean;
+ chartWidth: number;
+ chartHeight: number;
}
export class DatasourcesView extends React.PureComponent<
@@ -175,6 +180,9 @@ GROUP BY 1`;
hiddenColumns: new LocalStorageBackedArray<string>(
LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION,
),
+ showChart: false,
+ chartWidth: window.innerWidth * 0.85,
+ chartHeight: window.innerHeight * 0.4,
};
this.datasourceQueryManager = new QueryManager({
@@ -252,9 +260,31 @@ GROUP BY 1`;
});
}
+ private handleResize = () => {
+ this.setState({
+ chartWidth: window.innerWidth * 0.85,
+ chartHeight: window.innerHeight * 0.4,
+ });
+ };
+
+ private refresh = (auto: any): void => {
+ this.datasourceQueryManager.rerunLastQuery(auto);
+ // this looks ugly, but it forces the chart to re-render when refresh is clicked
+ this.setState(
+ {
+ showChart: !this.state.showChart,
+ },
+ () =>
+ this.setState({
+ showChart: !this.state.showChart,
+ }),
+ );
+ };
+
componentDidMount(): void {
const { noSqlMode } = this.props;
this.datasourceQueryManager.runQuery(noSqlMode);
+ window.addEventListener('resize', this.handleResize);
}
componentWillUnmount(): void {
@@ -572,6 +602,7 @@ GROUP BY 1`;
datasourcesFilter,
showDisabled,
hiddenColumns,
+ showChart,
} = this.state;
let data = datasources || [];
if (!showDisabled) {
@@ -790,6 +821,7 @@ GROUP BY 1`;
},
]}
defaultPageSize={50}
+ className={classNames(`-striped -highlight`, showChart ? '' : 'full-height')}
/>
{this.renderDropDataAction()}
{this.renderEnableAction()}
@@ -803,13 +835,15 @@ GROUP BY 1`;
render(): JSX.Element {
const { goToQuery, noSqlMode } = this.props;
- const { showDisabled, hiddenColumns } = this.state;
+ const { showDisabled, hiddenColumns, showChart, chartHeight, chartWidth } = this.state;
return (
<div className="data-sources-view app-view">
<ViewControlBar label="Datasources">
<RefreshButton
- onRefresh={auto => this.datasourceQueryManager.rerunLastQuery(auto)}
+ onRefresh={auto => {
+ this.refresh(auto);
+ }}
localStorageKey={LocalStorageKeys.DATASOURCES_REFRESH_RATE}
/>
{!noSqlMode && (
@@ -820,6 +854,11 @@ GROUP BY 1`;
/>
)}
<Switch
+ checked={showChart}
+ label="Show segment timeline"
+ onChange={() => this.setState({ showChart: !showChart })}
+ />
+ <Switch
checked={showDisabled}
label="Show disabled"
onChange={() => this.toggleDisabled(showDisabled)}
@@ -830,6 +869,11 @@ GROUP BY 1`;
tableColumnsHidden={hiddenColumns.storedArray}
/>
</ViewControlBar>
+ {showChart && (
+ <div className={'chart-container'}>
+ <SegmentTimeline chartHeight={chartHeight} chartWidth={chartWidth} />
+ </div>
+ )}
{this.renderDatasourceTable()}
</div>
);
diff --git a/web-console/src/visualization/bar-group.tsx b/web-console/src/visualization/bar-group.tsx
new file mode 100644
index 0000000..0856905
--- /dev/null
+++ b/web-console/src/visualization/bar-group.tsx
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { AxisScale } from 'd3-axis';
+import React from 'react';
+
+import { BarUnitData } from '../components/segment-timeline/segment-timeline';
+
+import { BarUnit } from './bar-unit';
+import { HoveredBarInfo } from './stacked-bar-chart';
+
+interface BarGroupProps extends React.Props<any> {
+ dataToRender: BarUnitData[];
+ changeActiveDatasource: (e: string) => void;
+ formatTick: (e: number) => string;
+ xScale: AxisScale<Date>;
+ yScale: AxisScale<number>;
+ barWidth: number;
+ onHoverBar?: (e: any) => void;
+ offHoverBar?: () => void;
+ hoverOn?: HoveredBarInfo | null;
+}
+
+interface BarGroupState {}
+
+export class BarGroup extends React.Component<BarGroupProps, BarGroupState> {
+ constructor(props: BarGroupProps) {
+ super(props);
+ this.state = {};
+ }
+
+ shouldComponentUpdate(nextProps: BarGroupProps, nextState: BarGroupState): boolean {
+ if (nextState === null) console.log(nextState);
+ return nextProps.hoverOn === this.props.hoverOn;
+ }
+
+ render(): JSX.Element[] | null {
+ const {
+ dataToRender,
+ changeActiveDatasource,
+ xScale,
+ yScale,
+ onHoverBar,
+ barWidth,
+ } = this.props;
+ if (dataToRender === undefined) return null;
+
+ return dataToRender.map((entry: BarUnitData, i: number) => {
+ const y0 = yScale(entry.y0 || 0) || 0;
+ const x = xScale(new Date(entry.x));
+ const y = yScale((entry.y0 || 0) + entry.y) || 0;
+ const height = y0 - y;
+ const barInfo: HoveredBarInfo = {
+ xCoordinate: x,
+ yCoordinate: y,
+ height,
+ datasource: entry.datasource,
+ xValue: entry.x,
+ yValue: entry.y,
+ };
+ return (
+ <BarUnit
+ key={i}
+ x={x}
+ y={y}
+ width={barWidth}
+ height={height}
+ style={{ fill: entry.color }}
+ onClick={() => changeActiveDatasource(entry.datasource)}
+ onHover={() => onHoverBar && onHoverBar(barInfo)}
+ />
+ );
+ });
+ }
+}
diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/visualization/bar-unit.scss
similarity index 75%
copy from web-console/src/views/datasource-view/datasource-view.scss
copy to web-console/src/visualization/bar-unit.scss
index 7c483ce..5767d68 100644
--- a/web-console/src/views/datasource-view/datasource-view.scss
+++ b/web-console/src/visualization/bar-unit.scss
@@ -16,20 +16,6 @@
* limitations under the License.
*/
-@import '../../variables';
-
-.data-sources-view {
- height: 100%;
- width: 100%;
-
- .clickable-cell {
- cursor: pointer;
- }
-
- .ReactTable {
- position: absolute;
- top: $view-control-bar-height + $standard-padding;
- bottom: 0;
- width: 100%;
- }
+.bar-chart-unit {
+ transform: translateX(65px);
}
diff --git a/web-console/src/visualization/bar-unit.tsx b/web-console/src/visualization/bar-unit.tsx
new file mode 100644
index 0000000..6c43b1c
--- /dev/null
+++ b/web-console/src/visualization/bar-unit.tsx
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+
+import './bar-unit.scss';
+
+interface BarChartUnitProps extends React.Props<any> {
+ x: number | undefined;
+ y: number;
+ width: number;
+ height: number;
+ style?: any;
+ onClick?: () => void;
+ onHover?: () => void;
+ offHover?: () => void;
+}
+
+interface BarChartUnitState {}
+
+export class BarUnit extends React.Component<BarChartUnitProps, BarChartUnitState> {
+ constructor(props: BarChartUnitProps) {
+ super(props);
+ this.state = {};
+ }
+
+ render(): JSX.Element {
+ const { x, y, width, height, style, onClick, onHover, offHover } = this.props;
+ return (
+ <g
+ className={`bar-chart-unit`}
+ onClick={onClick}
+ onMouseOver={onHover}
+ onMouseLeave={offHover}
+ >
+ <rect x={x} y={y} width={width} height={height} style={style} />
+ </g>
+ );
+ }
+}
diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/visualization/chart-axis.tsx
similarity index 58%
copy from web-console/src/views/datasource-view/datasource-view.scss
copy to web-console/src/visualization/chart-axis.tsx
index 7c483ce..294a64b 100644
--- a/web-console/src/views/datasource-view/datasource-view.scss
+++ b/web-console/src/visualization/chart-axis.tsx
@@ -16,20 +16,31 @@
* limitations under the License.
*/
-@import '../../variables';
+import * as d3 from 'd3';
+import React from 'react';
-.data-sources-view {
- height: 100%;
- width: 100%;
+interface ChartAxisProps extends React.Props<any> {
+ transform: string;
+ scale: any;
+ className?: string;
+}
+
+interface ChartAxisState {}
- .clickable-cell {
- cursor: pointer;
+export class ChartAxis extends React.Component<ChartAxisProps, ChartAxisState> {
+ constructor(props: ChartAxisProps) {
+ super(props);
+ this.state = {};
}
- .ReactTable {
- position: absolute;
- top: $view-control-bar-height + $standard-padding;
- bottom: 0;
- width: 100%;
+ render(): JSX.Element {
+ const { transform, scale, className } = this.props;
+ return (
+ <g
+ className={`axis ${className}`}
+ transform={transform}
+ ref={node => d3.select(node).call(scale)}
+ />
+ );
}
}
diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/visualization/stacked-bar-chart.scss
similarity index 76%
copy from web-console/src/views/datasource-view/datasource-view.scss
copy to web-console/src/visualization/stacked-bar-chart.scss
index 7c483ce..fed00ae 100644
--- a/web-console/src/views/datasource-view/datasource-view.scss
+++ b/web-console/src/visualization/stacked-bar-chart.scss
@@ -16,20 +16,18 @@
* limitations under the License.
*/
-@import '../../variables';
-
-.data-sources-view {
- height: 100%;
- width: 100%;
-
- .clickable-cell {
- cursor: pointer;
+.bar-chart {
+ .hovered-bar {
+ fill: transparent;
+ stroke: #ffffff;
+ stroke-width: 1.5px;
+ transform: translateX(65px);
}
- .ReactTable {
- position: absolute;
- top: $view-control-bar-height + $standard-padding;
- bottom: 0;
- width: 100%;
+ .gridline-x {
+ line {
+ stroke-dasharray: 5, 5;
+ opacity: 0.5;
+ }
}
}
diff --git a/web-console/src/visualization/stacked-bar-chart.tsx b/web-console/src/visualization/stacked-bar-chart.tsx
new file mode 100644
index 0000000..9fdab75
--- /dev/null
+++ b/web-console/src/visualization/stacked-bar-chart.tsx
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as d3 from 'd3';
+import { AxisScale } from 'd3';
+import React from 'react';
+
+import { BarChartMargin, BarUnitData } from '../components/segment-timeline/segment-timeline';
+
+import { BarGroup } from './bar-group';
+import { ChartAxis } from './chart-axis';
+
+import './stacked-bar-chart.scss';
+
+interface StackedBarChartProps extends React.Props<any> {
+ svgWidth: number;
+ svgHeight: number;
+ margin: BarChartMargin;
+ activeDataType?: string;
+ dataToRender: BarUnitData[];
+ changeActiveDatasource: (e: string) => void;
+ formatTick: (e: number) => string;
+ xScale: AxisScale<Date>;
+ yScale: AxisScale<number>;
+ barWidth: number;
+}
+
+interface StackedBarChartState {
+ width: number;
+ height: number;
+ hoverOn?: HoveredBarInfo | null;
+}
+
+export interface HoveredBarInfo {
+ xCoordinate?: number;
+ yCoordinate?: number;
+ height?: number;
+ width?: number;
+ datasource?: string;
+ xValue?: number;
+ yValue?: number;
+}
+
+export class StackedBarChart extends React.Component<StackedBarChartProps, StackedBarChartState> {
+ constructor(props: StackedBarChartProps) {
+ super(props);
+ this.state = {
+ width: this.props.svgWidth - this.props.margin.left - this.props.margin.right,
+ height: this.props.svgHeight - this.props.margin.bottom - this.props.margin.top,
+ };
+ }
+
+ componentWillReceiveProps(nextProps: StackedBarChartProps): void {
+ if (nextProps !== this.props) {
+ this.setState({
+ width: nextProps.svgWidth - this.props.margin.left - this.props.margin.right,
+ height: nextProps.svgHeight - this.props.margin.bottom - this.props.margin.top,
+ });
+ }
+ }
+
+ renderBarChart() {
+ const {
+ svgWidth,
+ svgHeight,
+ formatTick,
+ xScale,
+ yScale,
+ dataToRender,
+ changeActiveDatasource,
+ barWidth,
+ } = this.props;
+ const { width, height, hoverOn } = this.state;
+ return (
+ <div className={'bar-chart-container'}>
+ <svg
+ width={width}
+ height={height}
+ viewBox={`0 0 ${svgWidth} ${svgHeight}`}
+ preserveAspectRatio={'xMinYMin meet'}
+ style={{ marginTop: '20px' }}
+ >
+ <ChartAxis
+ className={'gridline-x'}
+ transform={'translate(60, 0)'}
+ scale={d3
+ .axisLeft(yScale)
+ .ticks(5)
+ .tickSize(-width)
+ .tickFormat(() => '')
+ .tickSizeOuter(0)}
+ />
+ <ChartAxis
+ className={'axis--x'}
+ transform={`translate(65, ${height})`}
+ scale={d3.axisBottom(xScale)}
+ />
+ <ChartAxis
+ className={'axis--y'}
+ transform={'translate(60, 0)'}
+ scale={d3
+ .axisLeft(yScale)
+ .ticks(5)
+ .tickFormat((e: number) => formatTick(e))}
+ />
+ <g className="bars-group" onMouseLeave={() => this.setState({ hoverOn: null })}>
+ <BarGroup
+ dataToRender={dataToRender}
+ changeActiveDatasource={changeActiveDatasource}
+ formatTick={formatTick}
+ xScale={xScale}
+ yScale={yScale}
+ onHoverBar={(e: HoveredBarInfo) => this.setState({ hoverOn: e })}
+ hoverOn={hoverOn}
+ barWidth={barWidth}
+ />
+ {hoverOn && (
+ <g
+ className={'hovered-bar'}
+ onClick={() => {
+ this.setState({ hoverOn: null });
+ changeActiveDatasource(hoverOn.datasource as string);
+ }}
+ >
+ <rect
+ x={hoverOn.xCoordinate}
+ y={hoverOn.yCoordinate}
+ width={barWidth}
+ height={hoverOn.height}
+ />
+ </g>
+ )}
+ </g>
+ </svg>
+ </div>
+ );
+ }
+
+ render(): JSX.Element {
+ const { activeDataType, formatTick } = this.props;
+ const { hoverOn } = this.state;
+ return (
+ <div className={'bar-chart'}>
+ <div className={'bar-chart-tooltip'}>
+ <div>Datasource: {hoverOn ? hoverOn.datasource : ''}</div>
+ <div>Time: {hoverOn ? hoverOn.xValue : ''}</div>
+ <div>
+ {`${activeDataType === 'countData' ? 'Count:' : 'Size:'} ${
+ hoverOn ? formatTick(hoverOn.yValue as number) : ''
+ }`}
+ </div>
+ </div>
+ {this.renderBarChart()}
+ </div>
+ );
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org