Porównaj commity

...

97 Commity

Autor SHA1 Wiadomość Data
  Suraj Shetty ea95aece5a chore: Bump version to fix faulty publish 2 lat temu
  Suraj Shetty f2d2eb1e9e chore: bump version 2 lat temu
  Ankush Menat 2b455e95a6
Merge pull request #388 from frappe/custom_number_shortner 2 lat temu
  Ankush Menat 624cf70f03 feat: custom number formatter function 2 lat temu
  Ankush Menat 5ea1d33fdf chore: add editorconfig 2 lat temu
  Shivam Mishra 46191e7a53 chore: upgrade can I use 3 lat temu
  Shivam Mishra 95a19f09a3 chore: bump version 3 lat temu
  Shivam Mishra 8bc8231b21 fix: imports 3 lat temu
  Shivam Mishra c18c076adc chore: bump to rc19 4 lat temu
  Shivam Mishra a765508bbd chore: bump to rc18 4 lat temu
  Shivam Mishra cdf5fbe33b feat: better regionFill gradients 4 lat temu
  Shivam Mishra a26a81c866 fix: style issue with dots 4 lat temu
  Shivam Mishra f19d15f112 chore: bump version 4 lat temu
  Shivam Mishra 07597ebe87 feat: add trailingDot option 4 lat temu
  Shivam Mishra 9c0ecc89be chore: bump to rc15 4 lat temu
  Shivam Mishra 3d6a349425 fix: range clipping logic 4 lat temu
  Shivam Mishra ce4126b3b1 chore: bump to rc14 4 lat temu
  Shivam Mishra f145d6bbe9 fix: allow option to disable entry animation 4 lat temu
  Shivam Mishra b7f20aab15 feat: allow clipping yAxis range 4 lat temu
  Shivam Mishra 7c9cf65385 refactor: remove space before shortened number 4 lat temu
  T3cH_W1z4rD 61717aee95 Fixed XSS (#339) 4 lat temu
  Shivam Mishra 304d23502a chore: bump to rc13 4 lat temu
  Shivam Mishra db12dcf27c fix: path building error 4 lat temu
  Shivam Mishra 1b4e206a1e fix: chart labels spacing issue 4 lat temu
  Shivam Mishra 8d8096869f chore: bump to rc12 4 lat temu
  Shivam Mishra fa559cc8bd fix: donut chart, single data point bug 4 lat temu
  Shivam Mishra ed8f97efd5 feat: add resize observer 4 lat temu
  Suraj Shetty 8d68d9933e chore: Bump version to v2.0.0-rc11 4 lat temu
  prssanna 72d5507c6f chore: bump to rc10 4 lat temu
  prssanna d6ab5b952e fix: name.replace exception 4 lat temu
  Shivam Mishra 4854591a89 chore: bump to rc9 4 lat temu
  Shivam Mishra a0265677a2 fix: clone in update 4 lat temu
  Shivam Mishra ba0d3703f5 chore: bump to rc8 4 lat temu
  Shivam Mishra bbe91840f7 chore: update export 4 lat temu
  Shivam Mishra 94a7e02d75 chore: bump to rc7 4 lat temu
  Shivam Mishra d8984ec5ae feat: allow setting lineType for yMarker 4 lat temu
  Shivam Mishra 067f5ea7f8 feat: allow hiding legends 4 lat temu
  Shivam Mishra 263f38c9ec chore: bump to rc6 4 lat temu
  Shivam Mishra 7e13f81063 feat: clone options before building 4 lat temu
  Suraj Shetty 9cc7bde398 chore: Bump version to rc5 4 lat temu
  prssanna ce64f01a8b fix: import round function 4 lat temu
  Shivam Mishra 583d82a96e chore: build rc3 4 lat temu
  Shivam Mishra 8308b9fd90 feat: default to css vars in makeText 4 lat temu
  Shivam Mishra f413c75932 chore: build rc2 for v2 4 lat temu
  Shivam Mishra 86bbb6d2aa chore: remove base font-fill 4 lat temu
  Shivam Mishra d386d21e2f chore: build rc1 for v2 4 lat temu
  Shivam Mishra 8101b1822e fix: remove duplicate function 4 lat temu
  Shivam Mishra cff7ce4c27 feat: add deepsource 4 lat temu
  Shivam Mishra 690a7f4467 refactor: validate y 4 lat temu
  Shivam Mishra 03f6436be4 feat: update variables 4 lat temu
  Shivam Mishra 8524e6cbd6 feat: allow custom yRegion colors 4 lat temu
  Shivam Mishra 1181660ed1 chore: remove console.log 4 lat temu
  Shivam Mishra 26671b143b chore: format codebase 4 lat temu
  Shivam Mishra 5d2c141f14 fix: floating point rounding fixes 4 lat temu
  Shivam Mishra e2d7ce8b21 feat: allow stroke color for yMarker 4 lat temu
  Shivam Mishra 73f1d9b1e3 feat: DonutChart extends PieChart 4 lat temu
  Shivam Mishra dc00b46a7a chore: format donut chart and pie chart 4 lat temu
  Shivam Mishra fd28107795 chore: remove unused import 4 lat temu
  Shivam Mishra 7cfa35a418 feat: new legends 4 lat temu
  Shivam Mishra d84614cb65 feat: update legendBar 4 lat temu
  Shivam Mishra 610cfb1f6d feat: update main in package.json 4 lat temu
  Shivam Mishra 767a905967 feat: remove deprecated MultiAxis 4 lat temu
  Shivam Mishra 56a7374277 chore: formatting improvements 4 lat temu
  Shivam Mishra 562a9cba55 chore: update tooltip styles 4 lat temu
  Shivam Mishra 4f4dd4d36f chore: remove dist 4 lat temu
  Shivam Mishra 0257bc370e feat: ignore dist 4 lat temu
  Shivam Mishra 7dde276477 feat: generate es 4 lat temu
  Shivam Mishra b12b916584 chore: update build 4 lat temu
  Shivam Mishra a7b06bb027 feat: change axis line colors 4 lat temu
  Shivam Mishra 9dd24aa7a7 feat: don't override axis line colors 4 lat temu
  Shivam Mishra 4d8321e9be feat: always show last label when xIsSeries is toggled 4 lat temu
  Shivam Mishra 399ff37b6a feat: add space ratio option for x-axis series labels 4 lat temu
  Shivam Mishra dc49b29d39 feat: [breaking] hide dots by default 4 lat temu
  Shivam Mishra 51a6ed33a5 feat: update opacity for light gradient 4 lat temu
  Shivam Mishra f8c3f9d7b4 chore: reset space ratio 4 lat temu
  Shivam Mishra d3909d49c1 feat: added function for rounded top rect 4 lat temu
  Shivam Mishra 255e806533 chore: cleanup rounded bar utility 4 lat temu
  Shivam Mishra 6cb9bf6bd3 chore: update build 4 lat temu
  Shivam Mishra 5e17dea6de feat: tune spacing for legends 4 lat temu
  Shivam Mishra 89fc842330 feat: update build 4 lat temu
  Shivam Mishra 63e0a0bdf4 feat: generate sourcemaps inn build 4 lat temu
  Shivam Mishra d2dd27dfab feat: added helper function for rounded edges 4 lat temu
  Shivam Mishra 846a21ae6a feat: remove depth for percentage chart 4 lat temu
  Shivam Mishra 5b00da4974 chore: remove docs folder 4 lat temu
  Shivam Mishra 069a5f7451 feat: show bundle size 4 lat temu
  Shivam Mishra 96095ec579 feat: update legend dots 4 lat temu
  Shivam Mishra fbe314d9f9 feat: update default space ratio, narrower bars 4 lat temu
  Shivam Mishra 39a7a8ffef feat: use css variables for styling 4 lat temu
  Shivam Mishra 0e7fa08b99 feat: rewrite build 4 lat temu
  Shivam Mishra 2c560b9bcc feat: remove svg pointer border 5 lat temu
  Shivam Mishra f7ec6fc779 feat: update tooltip colors 5 lat temu
  Shivam Mishra e54427004c feat: update text and line colors 5 lat temu
  Shivam Mishra 23bdf8a719 feat: truncate tooltip 5 lat temu
  Shivam Mishra 79157c318f feat: update colors 5 lat temu
  Shivam Mishra 09c3186813 feat: new colors 5 lat temu
  Shivam Mishra 3ec31560e9 feat: show 4 data points per row in tooltip 5 lat temu
  Shivam Mishra 479752c05f feat: update tooltip 5 lat temu
64 zmienionych plików z 2356 dodań i 21750 usunięć
  1. +0
    -14
      .babelrc
  2. +9
    -0
      .deepsource.toml
  3. +14
    -0
      .editorconfig
  4. +1
    -1
      .eslintrc.json
  5. +3
    -0
      .gitignore
  6. +0
    -4072
      dist/frappe-charts.esm.js
  7. +0
    -2
      dist/frappe-charts.min.cjs.js
  8. +0
    -1
      dist/frappe-charts.min.cjs.js.map
  9. +0
    -1
      dist/frappe-charts.min.css
  10. +0
    -2
      dist/frappe-charts.min.esm.js
  11. +0
    -1
      dist/frappe-charts.min.esm.js.map
  12. +0
    -2
      dist/frappe-charts.min.iife.js
  13. +0
    -1
      dist/frappe-charts.min.iife.js.map
  14. +0
    -2
      docs/_config.yml
  15. +0
    -7
      docs/assets/css/bootstrap.min.css
  16. +0
    -99
      docs/assets/css/hljs.css
  17. +0
    -110
      docs/assets/css/index.css
  18. +0
    -353
      docs/assets/css/reset.css
  19. BIN
      docs/assets/img/callisto.jpg
  20. BIN
      docs/assets/img/europa.jpg
  21. BIN
      docs/assets/img/frappe-bird.png
  22. BIN
      docs/assets/img/ganymede.jpg
  23. BIN
      docs/assets/img/io.jpg
  24. +0
    -278
      docs/assets/js/data.js
  25. +0
    -55
      docs/assets/js/demoConfig.js
  26. +0
    -2
      docs/assets/js/frappe-charts.min.js
  27. +0
    -1
      docs/assets/js/frappe-charts.min.js.map
  28. +0
    -2
      docs/assets/js/highlight.pack.js
  29. +0
    -375
      docs/assets/js/index.js
  30. +0
    -648
      docs/assets/js/index.min.js
  31. +0
    -1
      docs/assets/js/index.min.js.map
  32. +0
    -0
      docs/docs.html
  33. +0
    -323
      docs/index.html
  34. +0
    -10646
      package-lock.json
  35. +18
    -36
      package.json
  36. +27
    -174
      rollup.config.js
  37. +10
    -0
      src/.babelrc
  38. +102
    -30
      src/css/charts.scss
  39. +1
    -1
      src/css/chartsCss.js
  40. +0
    -2
      src/js/chart.js
  41. +22
    -35
      src/js/charts/AggregationChart.js
  42. +75
    -70
      src/js/charts/AxisChart.js
  43. +82
    -49
      src/js/charts/BaseChart.js
  44. +19
    -122
      src/js/charts/DonutChart.js
  45. +31
    -27
      src/js/charts/Heatmap.js
  46. +0
    -173
      src/js/charts/MultiAxisChart.js
  47. +14
    -12
      src/js/charts/PercentageChart.js
  48. +41
    -28
      src/js/charts/PieChart.js
  49. +1
    -1
      src/js/index.js
  50. +54
    -27
      src/js/objects/ChartComponents.js
  51. +18
    -11
      src/js/objects/SvgTip.js
  52. +12
    -12
      src/js/utils/animate.js
  53. +9
    -9
      src/js/utils/animation.js
  54. +32
    -25
      src/js/utils/axis-chart-utils.js
  55. +23
    -17
      src/js/utils/colors.js
  56. +5
    -7
      src/js/utils/constants.js
  57. +7
    -7
      src/js/utils/date-utils.js
  58. +14
    -15
      src/js/utils/dom.js
  59. +12
    -10
      src/js/utils/draw-utils.js
  60. +164
    -133
      src/js/utils/draw.js
  61. +2
    -2
      src/js/utils/export.js
  62. +26
    -0
      src/js/utils/helpers.js
  63. +38
    -30
      src/js/utils/intervals.js
  64. +1470
    -3686
      yarn.lock

+ 0
- 14
.babelrc Wyświetl plik

@@ -1,14 +0,0 @@
{
"presets": [
["latest", {
"es2015": {
"modules": false
}
}]
],
"env": {
"test": {
"presets": ["env"]
}
}
}

+ 9
- 0
.deepsource.toml Wyświetl plik

@@ -0,0 +1,9 @@
version = 1

[[analyzers]]
name = "javascript"
enabled = true

[analyzers.meta]
environment = ["browser"]
style_guide = "standard"

+ 14
- 0
.editorconfig Wyświetl plik

@@ -0,0 +1,14 @@
# Root editor config file
root = true

# Common settings
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8

# indentation settings
[{*.js,*.css,*.html}]
indent_style = tab
indent_size = 4

+ 1
- 1
.eslintrc.json Wyświetl plik

@@ -30,4 +30,4 @@
"globals": { "globals": {
"ENV": true "ENV": true
} }
}
}

+ 3
- 0
.gitignore Wyświetl plik

@@ -60,4 +60,7 @@ typings/
# next.js build output # next.js build output
.next .next


# npm build output
dist

.DS_Store .DS_Store

+ 0
- 4072
dist/frappe-charts.esm.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 2
dist/frappe-charts.min.cjs.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 1
dist/frappe-charts.min.cjs.js.map
Plik diff jest za duży
Wyświetl plik


+ 0
- 1
dist/frappe-charts.min.css Wyświetl plik

@@ -1 +0,0 @@
.chart-container{position:relative;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.chart-container .axis,.chart-container .chart-label{fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .dataset-units circle{stroke:#fff;stroke-width:2}.chart-container .dataset-units path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container .dataset-path{stroke-width:2px}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .axis-line .specific-value{text-anchor:start}.chart-container .axis-line .y-line{text-anchor:end}.chart-container .axis-line .x-line{text-anchor:middle}.chart-container .legend-dataset-text{fill:#6c7680;font-weight:600}.graph-svg-tip{position:absolute;z-index:1;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.graph-svg-tip ol,.graph-svg-tip ul{padding-left:0;display:-webkit-box;display:-ms-flexbox;display:flex}.graph-svg-tip ul.data-point-list li{min-width:90px;-webkit-box-flex:1;-ms-flex:1;flex:1;font-weight:600}.graph-svg-tip strong{color:#dfe2e5;font-weight:600}.graph-svg-tip .svg-pointer{position:absolute;height:5px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}

+ 0
- 2
dist/frappe-charts.min.esm.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 1
dist/frappe-charts.min.esm.js.map
Plik diff jest za duży
Wyświetl plik


+ 0
- 2
dist/frappe-charts.min.iife.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 1
dist/frappe-charts.min.iife.js.map
Plik diff jest za duży
Wyświetl plik


+ 0
- 2
docs/_config.yml Wyświetl plik

@@ -1,2 +0,0 @@
plugins:
- jekyll-redirect-from

+ 0
- 7
docs/assets/css/bootstrap.min.css
Plik diff jest za duży
Wyświetl plik


+ 0
- 99
docs/assets/css/hljs.css Wyświetl plik

@@ -1,99 +0,0 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/

.hljs {
display: block;
color: #36414c;
overflow-x: auto;
padding: 0.5em;
background: #F8F8F9;
border-radius: 3px;
}

.hljs-comment,
.hljs-quote {
color: #998;
font-style: italic;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-subst {
color: #333;
font-weight: bold;
}

.hljs-number,
.hljs-literal,
.hljs-variable,
.hljs-template-variable,
.hljs-tag .hljs-attr {
color: #008080;
}

.hljs-string,
.hljs-doctag {
color: #d14;
}

.hljs-title,
.hljs-section,
.hljs-selector-id {
color: #900;
font-weight: bold;
}

.hljs-subst {
font-weight: normal;
}

.hljs-type,
.hljs-class .hljs-title {
color: #458;
font-weight: bold;
}

.hljs-tag,
.hljs-name,
.hljs-attribute {
color: #000080;
font-weight: normal;
}

.hljs-regexp,
.hljs-link {
color: #009926;
}

.hljs-symbol,

.hljs-bullet {
color: #990073;
}

.hljs-built_in,
.hljs-builtin-name {
color: #0086b3;
}

.hljs-meta {
color: #999;
font-weight: bold;
}

.hljs-deletion {
background: #fdd;
}

.hljs-addition {
background: #dfd;
}

.hljs-emphasis {
font-style: italic;
}

.hljs-strong {
font-weight: bold;
}

+ 0
- 110
docs/assets/css/index.css Wyświetl plik

@@ -1,110 +0,0 @@
body {
/* container styles */
max-width: 720px;
margin: auto;

font-family: "proxima-nova", sans-serif;
font-size: 15px;
color: #6c7680;
text-rendering: optimizeLegibility !important;
line-height: 1.5em;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
h5,
h6,
.lead,
.page-sidebar,
.breadcrumb,
.label,
.h6,
.sans,
blockquote {
font-family: "proxima-nova", sans-serif;
color: #36414C;
}

header {
margin: 4rem 0; /* SAME 1 */
font-size: 1.6em;
font-weight: 300;
text-align: center;
}
header .lead-text {
line-height: 3rem;
margin: 2rem 0;
}
.demo-tip {
margin-top: 1rem; /* SAME 2 */
font-size: 1rem;
}
section {
margin: 4em 0; /* SAME 1 */
}
h1 {
font-size: 3.5rem;
margin-bottom: 1.5rem;
}
h1, h6 {
font-weight: 700;
}
.btn {
outline: none !important;
}
.blue.button {
color: #fff;
background: #7575ff;
border: 0px;
border-bottom: 3px solid rgba(0, 0, 0, 0.2);
}
.blue.button:hover {
background: #5b5be5;
}
.large.button {
font-size: 1.33em;
padding: 12px 24px 10px;
border-bottom: 3px solid rgba(0, 0, 0, 0.2);
}
a {
color: #5E64FF;
}
a, a:focus, a:hover {
transition: color 0.3s, border 0.3s, background-color 0.3s;
}


/* BaseCSS */
.margin-top {
margin-top: 1rem; /* SAME 2 */
}
.mv1 {
margin: 2em 0 1em 0;
}
.border {
border: 1px solid #ddd;
border-radius: 3px;
}


/* Moon images */
.image-container {
padding: 3px;
}
.image-container img{
display: block;
width: 100%;
}
.content-data p {
margin-bottom: 5px;
font-size: 12px;
}


.text-center {
text-align: center;
}

+ 0
- 353
docs/assets/css/reset.css Wyświetl plik

@@ -1,353 +0,0 @@
/*!
*this reset is a copy of bootstrap's reboot.css which is inturn a fork of normalise*
* Bootstrap Reboot v4.0.0-beta.3 (https://getbootstrap.com)
* Copyright 2011-2017 The Bootstrap Authors
* Copyright 2011-2017 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/

*,
*::before,
*::after {
box-sizing: border-box;
}

html {
font-family: sans-serif;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: transparent;
--line-height: 3;
line-height: calc(((var(--line-height) * var(--capital-height)) - var(--valign)) * 1px);
}

@-ms-viewport {
width: device-width;
}

article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}

body {
margin: 0;
font-size: 1em;
font-weight: 400;
/* line-height: 1.5; */
text-align: left;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, Noto, Oxygen-Sans, "Noto Sans", Ubuntu,Cantarell, sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
color: #36414c;
font-weight:normal;
-webkit-text-size-adjust: 100%;
-webkit-font-feature-settings: "kern" 1;
-moz-font-feature-settings: "kern" 1;
-o-font-feature-settings: "kern" 1;
font-feature-settings: "kern" 1;
font-kerning: normal;
text-rendering: optimizeLegibility;
}

[tabindex="-1"]:focus {
outline: 0 !important;
}

hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}

h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 1.6rem;
}

p {
margin-top: 0;
margin-bottom: 1rem;
}

abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}

address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}

ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}

ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}

dt {
font-weight: 700;
}

dd {
margin-bottom: .5rem;
margin-left: 0;
}

blockquote {
margin: 0 0 1rem;
}

dfn {
font-style: italic;
}

b,
strong {
font-weight: bolder;
}

small {
font-size: 80%;
}

sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}

sub {
bottom: -.25em;
}

sup {
top: -.5em;
}

a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}

a:hover {
color: #0056b3;
text-decoration: underline;
}

a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):focus, a:not([href]):not([tabindex]):hover {
color: inherit;
text-decoration: none;
}

a:not([href]):not([tabindex]):focus {
outline: 0;
}

pre,
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}

pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}

figure {
margin: 0 0 1rem;
}

img {
vertical-align: middle;
border-style: none;
}

svg:not(:root) {
overflow: hidden;
}

a,
area,
button,
[role="button"],
input:not([type="range"]),
label,
select,
summary,
textarea {
-ms-touch-action: manipulation;
touch-action: manipulation;
}

table {
border-collapse: collapse;
}

caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}

th {
text-align: inherit;
}

label {
display: inline-block;
margin-bottom: .5rem;
}

button {
border-radius: 0;
}

button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}

input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}

button,
input {
overflow: visible;
}

button,
select {
text-transform: none;
}

button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}

button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}

input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}

input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}

textarea {
overflow: auto;
resize: vertical;
}

fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}

legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}

progress {
vertical-align: baseline;
}

[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}

[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}

[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}

::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}

output {
display: inline-block;
}

summary {
display: list-item;
cursor: pointer;
}

template {
display: none;
}

[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

BIN
docs/assets/img/callisto.jpg Wyświetl plik

Przed Po
Szerokość: 236  |  Wysokość: 236  |  Rozmiar: 15 KiB

BIN
docs/assets/img/europa.jpg Wyświetl plik

Przed Po
Szerokość: 240  |  Wysokość: 240  |  Rozmiar: 20 KiB

BIN
docs/assets/img/frappe-bird.png Wyświetl plik

Przed Po
Szerokość: 299  |  Wysokość: 237  |  Rozmiar: 7.5 KiB

BIN
docs/assets/img/ganymede.jpg Wyświetl plik

Przed Po
Szerokość: 240  |  Wysokość: 240  |  Rozmiar: 16 KiB

BIN
docs/assets/img/io.jpg Wyświetl plik

Przed Po
Szerokość: 240  |  Wysokość: 240  |  Rozmiar: 17 KiB

+ 0
- 278
docs/assets/js/data.js Wyświetl plik

@@ -1,278 +0,0 @@
import { MONTH_NAMES_SHORT } from "../../../src/js/utils/date-utils";

// Composite Chart
// ================================================================================
const reportCountList = [
152,
222,
199,
287,
534,
709,
1179,
1256,
1632,
1856,
1850,
];

export const lineCompositeData = {
labels: [
"2007",
"2008",
"2009",
"2010",
"2011",
"2012",
"2013",
"2014",
"2015",
"2016",
"2017",
],

yMarkers: [
{
label: "Average 100 reports/month",
value: 1200,
options: { labelPos: "left" },
},
],

datasets: [
{
name: "Events",
values: reportCountList,
},
],
};

export const fireball_5_25 = [
[4, 0, 3, 1, 1, 2, 1, 1, 1, 0, 1, 1],
[2, 3, 3, 2, 1, 3, 0, 1, 2, 7, 10, 4],
[5, 6, 2, 4, 0, 1, 4, 3, 0, 2, 0, 1],
[0, 2, 6, 2, 1, 1, 2, 3, 6, 3, 7, 8],
[6, 8, 7, 7, 4, 5, 6, 5, 22, 12, 10, 11],
[7, 10, 11, 7, 3, 2, 7, 7, 11, 15, 22, 20],
[13, 16, 21, 18, 19, 17, 12, 17, 31, 28, 25, 29],
[24, 14, 21, 14, 11, 15, 19, 21, 41, 22, 32, 18],
[31, 20, 30, 22, 14, 17, 21, 35, 27, 50, 117, 24],
[32, 24, 21, 27, 11, 27, 43, 37, 44, 40, 48, 32],
[31, 38, 36, 26, 23, 23, 25, 29, 26, 47, 61, 50],
];
export const fireball_2_5 = [
[22, 6, 6, 9, 7, 8, 6, 14, 19, 10, 8, 20],
[11, 13, 12, 8, 9, 11, 9, 13, 10, 22, 40, 24],
[20, 13, 13, 19, 13, 10, 14, 13, 20, 18, 5, 9],
[7, 13, 16, 19, 12, 11, 21, 27, 27, 24, 33, 33],
[38, 25, 28, 22, 31, 21, 35, 42, 37, 32, 46, 53],
[50, 33, 36, 34, 35, 28, 27, 52, 58, 59, 75, 69],
[54, 67, 67, 45, 66, 51, 38, 64, 90, 113, 116, 87],
[84, 52, 56, 51, 55, 46, 50, 87, 114, 83, 152, 93],
[73, 58, 59, 63, 56, 51, 83, 140, 103, 115, 265, 89],
[106, 95, 94, 71, 77, 75, 99, 136, 129, 154, 168, 156],
[81, 102, 95, 72, 58, 91, 89, 122, 124, 135, 183, 171],
];
export const fireballOver25 = [
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
[1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2],
[3, 2, 1, 3, 2, 0, 2, 2, 2, 3, 0, 1],
[2, 3, 5, 2, 1, 3, 0, 2, 3, 5, 1, 4],
[7, 4, 6, 1, 9, 2, 2, 2, 20, 9, 4, 9],
[5, 6, 1, 2, 5, 4, 5, 5, 16, 9, 14, 9],
[5, 4, 7, 5, 1, 5, 3, 3, 5, 7, 22, 2],
[5, 13, 11, 6, 1, 7, 9, 8, 14, 17, 16, 3],
[8, 9, 8, 6, 4, 8, 5, 6, 14, 11, 21, 12],
];

export const barCompositeData = {
labels: MONTH_NAMES_SHORT,
datasets: [
{
name: "Over 25 reports",
values: fireballOver25[9],
},
{
name: "5 to 25 reports",
values: fireball_5_25[9],
},
{
name: "2 to 5 reports",
values: fireball_2_5[9],
},
],
};

// Demo Chart multitype Chart
// ================================================================================
export const typeData = {
labels: [
"12am-3am",
"3am-6am",
"6am-9am",
"9am-12pm",
"12pm-3pm",
"3pm-6pm",
"6pm-9pm",
"9pm-12am",
],

yMarkers: [
{
label: "Marker",
value: 43,
options: { labelPos: "left" },
// type: 'dashed'
},
],

yRegions: [
{
label: "Region",
start: -10,
end: 50,
options: { labelPos: "right" },
},
],

datasets: [
{
name: "Some Data",
values: [18, 40, 30, 35, 8, 52, 17, -4],
axisPosition: "right",
chartType: "bar",
},
{
name: "Another Set",
values: [30, 50, -10, 15, 18, 32, 27, 14],
axisPosition: "right",
chartType: "bar",
},
{
name: "Yet Another",
values: [15, 20, -3, -15, 58, 12, -17, 37],
chartType: "line",
},
],
};

export const trendsData = {
labels: [
1967,
1968,
1969,
1970,
1971,
1972,
1973,
1974,
1975,
1976,
1977,
1978,
1979,
1980,
1981,
1982,
1983,
1984,
1985,
1986,
1987,
1988,
1989,
1990,
1991,
1992,
1993,
1994,
1995,
1996,
1997,
1998,
1999,
2000,
2001,
2002,
2003,
2004,
2005,
2006,
2007,
2008,
2009,
2010,
2011,
2012,
2013,
2014,
2015,
2016,
],
datasets: [
{
values: [
132.9,
150.0,
149.4,
148.0,
94.4,
97.6,
54.1,
49.2,
22.5,
18.4,
39.3,
131.0,
220.1,
218.9,
198.9,
162.4,
91.0,
60.5,
20.6,
14.8,
33.9,
123.0,
211.1,
191.8,
203.3,
133.0,
76.1,
44.9,
25.1,
11.6,
28.9,
88.3,
136.3,
173.9,
170.4,
163.6,
99.3,
65.3,
45.8,
24.7,
12.6,
4.2,
4.8,
24.9,
80.8,
84.5,
94.0,
113.3,
69.8,
39.8,
],
},
],
};

export const moonData = {
names: ["Ganymede", "Callisto", "Io", "Europa"],
masses: [14819000, 10759000, 8931900, 4800000],
distances: [1070.412, 1882.709, 421.7, 671.034],
diameters: [5262.4, 4820.6, 3637.4, 3121.6],
};

+ 0
- 55
docs/assets/js/demoConfig.js Wyświetl plik

@@ -1,55 +0,0 @@
import { lineCompositeData, barCompositeData } from './data';

export default {
lineComposite: {
elementID: "#chart-composite-1",
options: {
title: "Fireball/Bolide Events - Yearly (reported)",
data: lineCompositeData,
type: "line",
height: 190,
colors: ["green"],
isNavigable: 1,
valuesOverPoints: 1,

lineOptions: {
dotSize: 8
}
}
},

barComposite: {
elementID: "#chart-composite-2",
options: {
data: barCompositeData,
type: "bar",
height: 210,
colors: ["violet", "light-blue", "#46a9f9"],
valuesOverPoints: 1,
axisOptions: {
xAxisMode: "tick",
shortenYAxisNumbers: true
},
barOptions: {
stacked: 1
}
}
},

demoMain: {
elementID: "",
options: {
title: "My Awesome Chart",
data: "typeData",
type: "axis-mixed",
height: 300,
colors: ["purple", "magenta", "light-blue"],
maxSlices: 10,

tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}
}
}
};

+ 0
- 2
docs/assets/js/frappe-charts.min.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 1
docs/assets/js/frappe-charts.min.js.map
Plik diff jest za duży
Wyświetl plik


+ 0
- 2
docs/assets/js/highlight.pack.js
Plik diff jest za duży
Wyświetl plik


+ 0
- 375
docs/assets/js/index.js Wyświetl plik

@@ -1,375 +0,0 @@
import { shuffle, getRandomBias } from '../../../src/js/utils/helpers';
import { HEATMAP_COLORS_YELLOW, HEATMAP_COLORS_BLUE } from '../../../src/js/utils/constants';
import { SEC_IN_DAY, clone, timestampToMidnight, timestampSec, addDays } from '../../../src/js/utils/date-utils';
/* eslint-disable no-unused-vars */
import { fireballOver25, fireball_2_5, fireball_5_25, lineCompositeData,
barCompositeData, typeData, trendsData, moonData } from './data';
/* eslint-enable no-unused-vars */
import demoConfig from './demoConfig';
// import { lineComposite, barComposite } from './demoConfig';
// ================================================================================

let Chart = frappe.Chart; // eslint-disable-line no-undef

let lc = demoConfig.lineComposite;
let lineCompositeChart = new Chart (lc.elementID, lc.options);

let bc = demoConfig.barComposite;
let barCompositeChart = new Chart (bc.elementID, bc.options);

lineCompositeChart.parent.addEventListener('data-select', (e) => {
let i = e.index;
barCompositeChart.updateDatasets([
fireballOver25[i], fireball_5_25[i], fireball_2_5[i]
]);
});

// ================================================================================

let customColors = ['purple', 'magenta', 'light-blue'];
let typeChartArgs = {
title: "My Awesome Chart",
data: typeData,
type: 'axis-mixed',
height: 300,
colors: customColors,

// maxLegendPoints: 6,
maxSlices: 10,

tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}
};

let aggrChart = new Chart("#chart-aggr", typeChartArgs);

Array.prototype.slice.call(
document.querySelectorAll('.aggr-type-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let type = btn.getAttribute('data-type');
typeChartArgs.type = type;
if(type !== 'axis-mixed') {
typeChartArgs.colors = undefined;
} else {
typeChartArgs.colors = customColors;
}

if(type !== 'percentage') {
typeChartArgs.height = 300;
} else {
typeChartArgs.height = undefined;
}

let newChart = new Chart("#chart-aggr", typeChartArgs);
if(newChart){
aggrChart = newChart;
}
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-aggr').addEventListener('click', () => {
aggrChart.export();
});

// Update values chart
// ================================================================================
let updateDataAllLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue",
"Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon"];

let getRandom = () => Math.floor(getRandomBias(-40, 60, 0.8, 1));
let updateDataAllValues = Array.from({length: 30}, getRandom);

// We're gonna be shuffling this
let updateDataAllIndices = updateDataAllLabels.map((d,i) => i);

let getUpdateData = (source_array, length=10) => {
let indices = updateDataAllIndices.slice(0, length);
return indices.map((index) => source_array[index]);
};

let updateData = {
labels: getUpdateData(updateDataAllLabels),
datasets: [{
"values": getUpdateData(updateDataAllValues)
}],
yMarkers: [
{
label: "Altitude",
value: 25,
type: 'dashed'
}
],
yRegions: [
{
label: "Range",
start: 10,
end: 45
},
],
};

let updateChart = new Chart("#chart-update", {
data: updateData,
type: 'line',
height: 300,
colors: ['#ff6c03'],
lineOptions: {
// hideLine: 1,
regionFill: 1
},
});

let chartUpdateButtons = document.querySelector('.chart-update-buttons');

chartUpdateButtons.querySelector('[data-update="random"]').addEventListener("click", () => {
shuffle(updateDataAllIndices);
let value = getRandom();
let start = getRandom();
let end = getRandom();
let data = {
labels: updateDataAllLabels.slice(0, 10),
datasets: [{values: getUpdateData(updateDataAllValues)}],
yMarkers: [
{
label: "Altitude",
value: value,
type: 'dashed'
}
],
yRegions: [
{
label: "Range",
start: start,
end: end
},
],
};
updateChart.update(data);
});

chartUpdateButtons.querySelector('[data-update="add"]').addEventListener("click", () => {
let index = updateChart.state.datasetLength; // last index to add
if(index >= updateDataAllIndices.length) return;
updateChart.addDataPoint(
updateDataAllLabels[index], [updateDataAllValues[index]]
);
});

chartUpdateButtons.querySelector('[data-update="remove"]').addEventListener("click", () => {
updateChart.removeDataPoint();
});

document.querySelector('.export-update').addEventListener('click', () => {
updateChart.export();
});

// Trends Chart
// ================================================================================

let plotChartArgs = {
title: "Mean Total Sunspot Count - Yearly",
data: trendsData,
type: 'line',
height: 300,
colors: ['#238e38'],
lineOptions: {
hideDots: 1,
heatline: 1,
},
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
};

let trendsChart = new Chart("#chart-trends", plotChartArgs);

Array.prototype.slice.call(
document.querySelectorAll('.chart-plot-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let type = btn.getAttribute('data-type');
let config = {};
config[type] = 1;

if(['regionFill', 'heatline'].includes(type)) {
config.hideDots = 1;
}

// plotChartArgs.init = false;
plotChartArgs.lineOptions = config;

new Chart("#chart-trends", plotChartArgs);

Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-trends').addEventListener('click', () => {
trendsChart.export();
});


// Event chart
// ================================================================================



let eventsData = {
labels: ["Ganymede", "Callisto", "Io", "Europa"],
datasets: [
{
"values": moonData.distances,
"formatted": moonData.distances.map(d => d*1000 + " km")
}
]
};

let eventsChart = new Chart("#chart-events", {
title: "Jupiter's Moons: Semi-major Axis (1000 km)",
data: eventsData,
type: 'bar',
height: 330,
colors: ['grey'],
isNavigable: 1,
});

let dataDiv = document.querySelector('.chart-events-data');

eventsChart.parent.addEventListener('data-select', (e) => {
let name = moonData.names[e.index];
dataDiv.querySelector('.moon-name').innerHTML = name;
dataDiv.querySelector('.semi-major-axis').innerHTML = moonData.distances[e.index] * 1000;
dataDiv.querySelector('.mass').innerHTML = moonData.masses[e.index];
dataDiv.querySelector('.diameter').innerHTML = moonData.diameters[e.index];
dataDiv.querySelector('img').src = "./assets/img/" + name.toLowerCase() + ".jpg";
});

// Heatmap
// ================================================================================

let today = new Date();
let start = clone(today);
addDays(start, 4);
let end = clone(start);
start.setFullYear( start.getFullYear() - 2 );
end.setFullYear( end.getFullYear() - 1 );

let dataPoints = {};

let startTs = timestampSec(start);
let endTs = timestampSec(end);

startTs = timestampToMidnight(startTs);
endTs = timestampToMidnight(endTs, true);

while (startTs < endTs) {
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
startTs += SEC_IN_DAY;
}

const heatmapData = {
dataPoints: dataPoints,
start: start,
end: end
};

let heatmapArgs = {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
};
let heatmapChart = new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(
document.querySelectorAll('.heatmap-mode-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let mode = btn.getAttribute('data-mode');
let discreteDomains = 0;

if(mode === 'discrete') {
discreteDomains = 1;
}

let colors = [];
let colors_mode = document
.querySelector('.heatmap-color-buttons .active')
.getAttribute('data-color');
if(colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}

heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

Array.prototype.slice.call(
document.querySelectorAll('.heatmap-color-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let colors_mode = btn.getAttribute('data-color');
let colors = [];

if(colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}

let discreteDomains = 1;

let view_mode = document
.querySelector('.heatmap-mode-buttons .active')
.getAttribute('data-mode');
if(view_mode === 'continuous') {
discreteDomains = 0;
}

heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-heatmap').addEventListener('click', () => {
heatmapChart.export();
});

+ 0
- 648
docs/assets/js/index.min.js Wyświetl plik

@@ -1,648 +0,0 @@
(function () {
'use strict';

// Fixed 5-color theme,
// More colors are difficult to parse visually









var HEATMAP_COLORS_BLUE = ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'];
var HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];



// Universal constants

/**
* Returns the value of a number upto 2 decimal places.
* @param {Number} d Any number
*/


/**
* Returns whether or not two given arrays are equal.
* @param {Array} arr1 First array
* @param {Array} arr2 Second array
*/


/**
* Shuffles array in place. ES6 version
* @param {Array} array An array containing the items.
*/
function shuffle(array) {
// Awesomeness: https://bost.ocks.org/mike/shuffle/
// https://stackoverflow.com/a/2450976/6495043
// https://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array?noredirect=1&lq=1

for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var _ref = [array[j], array[i]];
array[i] = _ref[0];
array[j] = _ref[1];
}

return array;
}

/**
* Fill an array with extra points
* @param {Array} array Array
* @param {Number} count number of filler elements
* @param {Object} element element to fill with
* @param {Boolean} start fill at start?
*/


/**
* Returns pixel width of string.
* @param {String} string
* @param {Number} charWidth Width of single char in pixels
*/




// https://stackoverflow.com/a/29325222
function getRandomBias(min, max, bias, influence) {
var range = max - min;
var biasValue = range * bias + min;
var rnd = Math.random() * range + min,
// random in range
mix = Math.random() * influence; // random mixer
return rnd * (1 - mix) + biasValue * mix; // mix full range and bias
}



/**
* Check if a number is valid for svg attributes
* @param {object} candidate Candidate to test
* @param {Boolean} nonNegative flag to treat negative number as invalid
*/


/**
* Round a number to the closes precision, max max precision 4
* @param {Number} d Any Number
*/

// Playing around with dates




var NO_OF_MILLIS = 1000;
var SEC_IN_DAY = 86400;


var MONTH_NAMES_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];






function clone(date) {
return new Date(date.getTime());
}

function timestampSec(date) {
return date.getTime() / NO_OF_MILLIS;
}

function timestampToMidnight(timestamp) {
var roundAhead = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;

var midnightTs = Math.floor(timestamp - timestamp % SEC_IN_DAY);
if (roundAhead) {
return midnightTs + SEC_IN_DAY;
}
return midnightTs;
}

// export function getMonthsBetween(startDate, endDate) {}











// mutates


// mutates
function addDays(date, numberOfDays) {
date.setDate(date.getDate() + numberOfDays);
}

// Composite Chart
// ================================================================================
var reportCountList = [152, 222, 199, 287, 534, 709, 1179, 1256, 1632, 1856, 1850];

var lineCompositeData = {
labels: ["2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017"],

yMarkers: [{
label: "Average 100 reports/month",
value: 1200,
options: { labelPos: "left" }
}],

datasets: [{
name: "Events",
values: reportCountList
}]
};

var fireball_5_25 = [[4, 0, 3, 1, 1, 2, 1, 1, 1, 0, 1, 1], [2, 3, 3, 2, 1, 3, 0, 1, 2, 7, 10, 4], [5, 6, 2, 4, 0, 1, 4, 3, 0, 2, 0, 1], [0, 2, 6, 2, 1, 1, 2, 3, 6, 3, 7, 8], [6, 8, 7, 7, 4, 5, 6, 5, 22, 12, 10, 11], [7, 10, 11, 7, 3, 2, 7, 7, 11, 15, 22, 20], [13, 16, 21, 18, 19, 17, 12, 17, 31, 28, 25, 29], [24, 14, 21, 14, 11, 15, 19, 21, 41, 22, 32, 18], [31, 20, 30, 22, 14, 17, 21, 35, 27, 50, 117, 24], [32, 24, 21, 27, 11, 27, 43, 37, 44, 40, 48, 32], [31, 38, 36, 26, 23, 23, 25, 29, 26, 47, 61, 50]];
var fireball_2_5 = [[22, 6, 6, 9, 7, 8, 6, 14, 19, 10, 8, 20], [11, 13, 12, 8, 9, 11, 9, 13, 10, 22, 40, 24], [20, 13, 13, 19, 13, 10, 14, 13, 20, 18, 5, 9], [7, 13, 16, 19, 12, 11, 21, 27, 27, 24, 33, 33], [38, 25, 28, 22, 31, 21, 35, 42, 37, 32, 46, 53], [50, 33, 36, 34, 35, 28, 27, 52, 58, 59, 75, 69], [54, 67, 67, 45, 66, 51, 38, 64, 90, 113, 116, 87], [84, 52, 56, 51, 55, 46, 50, 87, 114, 83, 152, 93], [73, 58, 59, 63, 56, 51, 83, 140, 103, 115, 265, 89], [106, 95, 94, 71, 77, 75, 99, 136, 129, 154, 168, 156], [81, 102, 95, 72, 58, 91, 89, 122, 124, 135, 183, 171]];
var fireballOver25 = [
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2], [3, 2, 1, 3, 2, 0, 2, 2, 2, 3, 0, 1], [2, 3, 5, 2, 1, 3, 0, 2, 3, 5, 1, 4], [7, 4, 6, 1, 9, 2, 2, 2, 20, 9, 4, 9], [5, 6, 1, 2, 5, 4, 5, 5, 16, 9, 14, 9], [5, 4, 7, 5, 1, 5, 3, 3, 5, 7, 22, 2], [5, 13, 11, 6, 1, 7, 9, 8, 14, 17, 16, 3], [8, 9, 8, 6, 4, 8, 5, 6, 14, 11, 21, 12]];

var barCompositeData = {
labels: MONTH_NAMES_SHORT,
datasets: [{
name: "Over 25 reports",
values: fireballOver25[9]
}, {
name: "5 to 25 reports",
values: fireball_5_25[9]
}, {
name: "2 to 5 reports",
values: fireball_2_5[9]
}]
};

// Demo Chart multitype Chart
// ================================================================================
var typeData = {
labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm", "12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],

yMarkers: [{
label: "Marker",
value: 43,
options: { labelPos: "left" }
// type: 'dashed'
}],

yRegions: [{
label: "Region",
start: -10,
end: 50,
options: { labelPos: "right" }
}],

datasets: [{
name: "Some Data",
values: [18, 40, 30, 35, 8, 52, 17, -4],
axisPosition: "right",
chartType: "bar"
}, {
name: "Another Set",
values: [30, 50, -10, 15, 18, 32, 27, 14],
axisPosition: "right",
chartType: "bar"
}, {
name: "Yet Another",
values: [15, 20, -3, -15, 58, 12, -17, 37],
chartType: "line"
}]
};

var trendsData = {
labels: [1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016],
datasets: [{
values: [132.9, 150.0, 149.4, 148.0, 94.4, 97.6, 54.1, 49.2, 22.5, 18.4, 39.3, 131.0, 220.1, 218.9, 198.9, 162.4, 91.0, 60.5, 20.6, 14.8, 33.9, 123.0, 211.1, 191.8, 203.3, 133.0, 76.1, 44.9, 25.1, 11.6, 28.9, 88.3, 136.3, 173.9, 170.4, 163.6, 99.3, 65.3, 45.8, 24.7, 12.6, 4.2, 4.8, 24.9, 80.8, 84.5, 94.0, 113.3, 69.8, 39.8]
}]
};

var moonData = {
names: ["Ganymede", "Callisto", "Io", "Europa"],
masses: [14819000, 10759000, 8931900, 4800000],
distances: [1070.412, 1882.709, 421.7, 671.034],
diameters: [5262.4, 4820.6, 3637.4, 3121.6]
};

var demoConfig = {
lineComposite: {
elementID: "#chart-composite-1",
options: {
title: "Fireball/Bolide Events - Yearly (reported)",
data: lineCompositeData,
type: "line",
height: 190,
colors: ["green"],
isNavigable: 1,
valuesOverPoints: 1,

lineOptions: {
dotSize: 8
}
}
},

barComposite: {
elementID: "#chart-composite-2",
options: {
data: barCompositeData,
type: "bar",
height: 210,
colors: ["violet", "light-blue", "#46a9f9"],
valuesOverPoints: 1,
axisOptions: {
xAxisMode: "tick",
shortenYAxisNumbers: true
},
barOptions: {
stacked: 1
}
}
},

demoMain: {
elementID: "",
options: {
title: "My Awesome Chart",
data: "typeData",
type: "axis-mixed",
height: 300,
colors: ["purple", "magenta", "light-blue"],
maxSlices: 10,

tooltipOptions: {
formatTooltipX: function formatTooltipX(d) {
return (d + '').toUpperCase();
},
formatTooltipY: function formatTooltipY(d) {
return d + ' pts';
}
}
}
}
};

/* eslint-disable no-unused-vars */
/* eslint-enable no-unused-vars */
// import { lineComposite, barComposite } from './demoConfig';
// ================================================================================

var Chart = frappe.Chart; // eslint-disable-line no-undef

var lc = demoConfig.lineComposite;
var lineCompositeChart = new Chart(lc.elementID, lc.options);

var bc = demoConfig.barComposite;
var barCompositeChart = new Chart(bc.elementID, bc.options);

lineCompositeChart.parent.addEventListener('data-select', function (e) {
var i = e.index;
barCompositeChart.updateDatasets([fireballOver25[i], fireball_5_25[i], fireball_2_5[i]]);
});

// ================================================================================

var customColors = ['purple', 'magenta', 'light-blue'];
var typeChartArgs = {
title: "My Awesome Chart",
data: typeData,
type: 'axis-mixed',
height: 300,
colors: customColors,

// maxLegendPoints: 6,
maxSlices: 10,

tooltipOptions: {
formatTooltipX: function formatTooltipX(d) {
return (d + '').toUpperCase();
},
formatTooltipY: function formatTooltipY(d) {
return d + ' pts';
}
}
};

var aggrChart = new Chart("#chart-aggr", typeChartArgs);

Array.prototype.slice.call(document.querySelectorAll('.aggr-type-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var type = btn.getAttribute('data-type');
typeChartArgs.type = type;
if (type !== 'axis-mixed') {
typeChartArgs.colors = undefined;
} else {
typeChartArgs.colors = customColors;
}

if (type !== 'percentage') {
typeChartArgs.height = 300;
} else {
typeChartArgs.height = undefined;
}

var newChart = new Chart("#chart-aggr", typeChartArgs);
if (newChart) {
aggrChart = newChart;
}
Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-aggr').addEventListener('click', function () {
aggrChart.export();
});

// Update values chart
// ================================================================================
var updateDataAllLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Mon"];

var getRandom = function getRandom() {
return Math.floor(getRandomBias(-40, 60, 0.8, 1));
};
var updateDataAllValues = Array.from({ length: 30 }, getRandom);

// We're gonna be shuffling this
var updateDataAllIndices = updateDataAllLabels.map(function (d, i) {
return i;
});

var getUpdateData = function getUpdateData(source_array) {
var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;

var indices = updateDataAllIndices.slice(0, length);
return indices.map(function (index) {
return source_array[index];
});
};

var updateData = {
labels: getUpdateData(updateDataAllLabels),
datasets: [{
"values": getUpdateData(updateDataAllValues)
}],
yMarkers: [{
label: "Altitude",
value: 25,
type: 'dashed'
}],
yRegions: [{
label: "Range",
start: 10,
end: 45
}]
};

var updateChart = new Chart("#chart-update", {
data: updateData,
type: 'line',
height: 300,
colors: ['#ff6c03'],
lineOptions: {
// hideLine: 1,
regionFill: 1
}
});

var chartUpdateButtons = document.querySelector('.chart-update-buttons');

chartUpdateButtons.querySelector('[data-update="random"]').addEventListener("click", function () {
shuffle(updateDataAllIndices);
var value = getRandom();
var start = getRandom();
var end = getRandom();
var data = {
labels: updateDataAllLabels.slice(0, 10),
datasets: [{ values: getUpdateData(updateDataAllValues) }],
yMarkers: [{
label: "Altitude",
value: value,
type: 'dashed'
}],
yRegions: [{
label: "Range",
start: start,
end: end
}]
};
updateChart.update(data);
});

chartUpdateButtons.querySelector('[data-update="add"]').addEventListener("click", function () {
var index = updateChart.state.datasetLength; // last index to add
if (index >= updateDataAllIndices.length) return;
updateChart.addDataPoint(updateDataAllLabels[index], [updateDataAllValues[index]]);
});

chartUpdateButtons.querySelector('[data-update="remove"]').addEventListener("click", function () {
updateChart.removeDataPoint();
});

document.querySelector('.export-update').addEventListener('click', function () {
updateChart.export();
});

// Trends Chart
// ================================================================================

var plotChartArgs = {
title: "Mean Total Sunspot Count - Yearly",
data: trendsData,
type: 'line',
height: 300,
colors: ['#238e38'],
lineOptions: {
hideDots: 1,
heatline: 1
},
axisOptions: {
xAxisMode: 'tick',
yAxisMode: 'span',
xIsSeries: 1
}
};

var trendsChart = new Chart("#chart-trends", plotChartArgs);

Array.prototype.slice.call(document.querySelectorAll('.chart-plot-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var type = btn.getAttribute('data-type');
var config = {};
config[type] = 1;

if (['regionFill', 'heatline'].includes(type)) {
config.hideDots = 1;
}

// plotChartArgs.init = false;
plotChartArgs.lineOptions = config;

new Chart("#chart-trends", plotChartArgs);

Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-trends').addEventListener('click', function () {
trendsChart.export();
});

// Event chart
// ================================================================================


var eventsData = {
labels: ["Ganymede", "Callisto", "Io", "Europa"],
datasets: [{
"values": moonData.distances,
"formatted": moonData.distances.map(function (d) {
return d * 1000 + " km";
})
}]
};

var eventsChart = new Chart("#chart-events", {
title: "Jupiter's Moons: Semi-major Axis (1000 km)",
data: eventsData,
type: 'bar',
height: 330,
colors: ['grey'],
isNavigable: 1
});

var dataDiv = document.querySelector('.chart-events-data');

eventsChart.parent.addEventListener('data-select', function (e) {
var name = moonData.names[e.index];
dataDiv.querySelector('.moon-name').innerHTML = name;
dataDiv.querySelector('.semi-major-axis').innerHTML = moonData.distances[e.index] * 1000;
dataDiv.querySelector('.mass').innerHTML = moonData.masses[e.index];
dataDiv.querySelector('.diameter').innerHTML = moonData.diameters[e.index];
dataDiv.querySelector('img').src = "./assets/img/" + name.toLowerCase() + ".jpg";
});

// Heatmap
// ================================================================================

var today = new Date();
var start = clone(today);
addDays(start, 4);
var end = clone(start);
start.setFullYear(start.getFullYear() - 2);
end.setFullYear(end.getFullYear() - 1);

var dataPoints = {};

var startTs = timestampSec(start);
var endTs = timestampSec(end);

startTs = timestampToMidnight(startTs);
endTs = timestampToMidnight(endTs, true);

while (startTs < endTs) {
dataPoints[parseInt(startTs)] = Math.floor(getRandomBias(0, 5, 0.2, 1));
startTs += SEC_IN_DAY;
}

var heatmapData = {
dataPoints: dataPoints,
start: start,
end: end
};

var heatmapArgs = {
title: "Monthly Distribution",
data: heatmapData,
type: 'heatmap',
discreteDomains: 1,
countLabel: 'Level',
colors: HEATMAP_COLORS_BLUE,
legendScale: [0, 1, 2, 4, 5]
};
var heatmapChart = new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(document.querySelectorAll('.heatmap-mode-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var mode = btn.getAttribute('data-mode');
var discreteDomains = 0;

if (mode === 'discrete') {
discreteDomains = 1;
}

var colors = [];
var colors_mode = document.querySelector('.heatmap-color-buttons .active').getAttribute('data-color');
if (colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}

heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

Array.prototype.slice.call(document.querySelectorAll('.heatmap-color-buttons button')).map(function (el) {
el.addEventListener('click', function (e) {
var btn = e.target;
var colors_mode = btn.getAttribute('data-color');
var colors = [];

if (colors_mode === 'halloween') {
colors = HEATMAP_COLORS_YELLOW;
} else if (colors_mode === 'blue') {
colors = HEATMAP_COLORS_BLUE;
}

var discreteDomains = 1;

var view_mode = document.querySelector('.heatmap-mode-buttons .active').getAttribute('data-mode');
if (view_mode === 'continuous') {
discreteDomains = 0;
}

heatmapArgs.discreteDomains = discreteDomains;
heatmapArgs.colors = colors;
new Chart("#chart-heatmap", heatmapArgs);

Array.prototype.slice.call(btn.parentNode.querySelectorAll('button')).map(function (el) {
el.classList.remove('active');
});
btn.classList.add('active');
});
});

document.querySelector('.export-heatmap').addEventListener('click', function () {
heatmapChart.export();
});

}());
//# sourceMappingURL=index.min.js.map

+ 0
- 1
docs/assets/js/index.min.js.map
Plik diff jest za duży
Wyświetl plik


+ 0
- 0
docs/docs.html Wyświetl plik


+ 0
- 323
docs/index.html Wyświetl plik

@@ -1,323 +0,0 @@
---
redirect_to: "https://frappe.io/charts"
---
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Frappe Charts</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="open source javascript js charts library svg zero-dependency interactive data visualization beautiful drag resize">
<meta name="description" content="A simple, responsive, modern charts library for the web.">

<link rel="stylesheet" type="text/css" href="assets/css/reset.css" media="screen">
<link rel="stylesheet" type="text/css" href="assets/css/bootstrap.min.css" media="screen">
<link rel="stylesheet" type="text/css" href="assets/css/index.css" media="screen">
<link rel="stylesheet" type="text/css" href="assets/css/hljs.css" media="screen">

<script src="assets/js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>

<link rel="shortcut icon" href="https://frappe.github.io/frappe/assets/img/favicon.png" type="image/x-icon">
<link rel="icon" href="https://frappe.github.io/frappe/assets/img/favicon.png" type="image/x-icon">

<script async defer src="https://buttons.github.io/buttons.js"></script>
</head>

<body>
<header>
<h1>Frappe Charts</h1>
<p class="lead-text">GitHub-inspired simple and modern SVG charts for the web<br>with zero dependencies.</p>
<div id="chart-composite-1" class="border"></div>
<p class="demo-tip">Click or use arrow keys to navigate data points</p>
<div id="chart-composite-2" class="border"></div>
</header>

<section>
<h6>Create a chart</h6>
<pre><code class="hljs html"> &lt!--HTML--&gt;
&lt;div id="chart"&gt;&lt;/div&gt;</code></pre>
<pre><code class="hljs javascript"> // Javascript
let chart = new frappe.Chart( "#chart", { // or DOM element
data: {
labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm",
"12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],

datasets: [
{
name: "Some Data", chartType: 'bar',
values: [25, 40, 30, 35, 8, 52, 17, -4]
},
{
name: "Another Set", chartType: 'bar',
values: [25, 50, -10, 15, 18, 32, 27, 14]
},
{
name: "Yet Another", chartType: 'line',
values: [15, 20, -3, -15, 58, 12, -17, 37]
}
],

yMarkers: [{ label: "Marker", value: 70,
options: { labelPos: 'left' }}],
yRegions: [{ label: "Region", start: -10, end: 50,
options: { labelPos: 'right' }}]
},

title: "My Awesome Chart",
type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage', 'donut'
height: 300,
colors: ['purple', '#ffa3ef', 'light-blue'],

tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}
});

chart.export();
</code></pre>

<div id="chart-aggr" class="border"></div>
<div class="btn-group aggr-type-buttons margin-top mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary active" data-type='axis-mixed'>Mixed</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='pie'>Pie Chart</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='donut'>Donut Chart</button>
<button type="button" class="btn btn-sm btn-secondary" data-type='percentage'>Percentage Chart</button>
</div>
<div class="btn-group export-buttons margin-top mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary export-aggr">Export ...</button>
</div>
</section>

<section>
<h6>Update Values</h6>
<div id="chart-update" class="border"></div>

<div class="chart-update-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary" data-update="random">Random Data</button>
<button type="button" class="btn btn-sm btn-secondary" data-update="add">Add Value</button>
<button type="button" class="btn btn-sm btn-secondary" data-update="remove">Remove Value</button>
<button type="button" class="btn btn-sm btn-secondary export-update">Export ...</button>
</div>
</section>

<section>
<h6>Plot Trends</h6>
<div id="chart-trends" class="border"></div>

<div class="btn-group chart-plot-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary" data-type="hideDots">Line</button>
<button type="button" class="btn btn-sm btn-secondary" data-type="hideLine">Dots</button>
<button type="button" class="btn btn-sm btn-secondary active" data-type="heatline">HeatLine</button>
<button type="button" class="btn btn-sm btn-secondary" data-type="regionFill">Region</button>
</div>
<div class="btn-group export-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary export-trends">Export ...</button>
</div>
</section>

<section>
<h6>Listen to state change</h6>
<div class="row">
<div class="col-sm-8">
<div id="chart-events" class="border"></div>
</div>
<div class="col-sm-4 chart-events-data">
<div class="image-container border">
<img class="moon-image" src="./assets/img/europa.jpg">
</div>
<div class="content-data margin-top">
<h6 class="moon-name">Europa</h6>
<p>Semi-major-axis: <span class="semi-major-axis">671034</span> km</p>
<p>Mass: <span class="mass">4800000</span> x 10^16 kg</p>
<p>Diameter: <span class="diameter">3121.6</span> km</p>
</div>
</div>
</div>
<pre><code class="hljs javascript margin-top"> ...
isNavigable: 1, // Navigate across data points; default 0
...

chart.parent.addEventListener('data-select', (e) => {
update_moon_data(e.index); // e contains index and value of current datapoint
});</code></pre>
</section>

<section>
<h6>
And a Month-wise Heatmap
</h6>
<div id="chart-heatmap" class="border"
style="overflow: scroll;"></div>
<div class="heatmap-mode-buttons btn-group mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary active" data-mode="discrete">Discrete</button>
<button type="button" class="btn btn-sm btn-secondary" data-mode="continuous">Continuous</button>
</div>
<div class="heatmap-color-buttons btn-group mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary" data-color="default">Green (Default)</button>
<button type="button" class="btn btn-sm btn-secondary active" data-color="blue">Blue</button>
<button type="button" class="btn btn-sm btn-secondary" data-color="halloween">GitHub's Halloween</button>
</div>
<div class="btn-group export-buttons mt-1 mx-auto" role="group">
<button type="button" class="btn btn-sm btn-secondary export-heatmap">Export ...</button>
</div>
<pre><code class="hljs javascript margin-top"> let heatmap = new frappe.Chart("#heatmap", {
type: 'heatmap',
title: "Monthly Distribution",
data: {
dataPoints: {'1524064033': 8, /* ... */},
// object with timestamp-value pairs
start: startDate
end: endDate // Date objects
},
countLabel: 'Level',
discreteDomains: 0 // default: 1
colors: ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'],
// Set of five incremental colors,
// preferably with a low-saturation color for zero data;
// def: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
});</code></pre>
</section>

<section>
<h6>Demo</h6>
<p data-height="299" data-theme-id="light" data-slug-hash="wjKBoq" data-default-tab="js,result"
data-user="pratu16x7" data-embed-version="2" data-pen-title="Frappe Charts Demo" class="codepen">
See the Pen <a href="https://codepen.io/pratu16x7/pen/wjKBoq/">Frappe Charts Demo</a>
by Prateeksha Singh (<a href="https://codepen.io/pratu16x7">@pratu16x7</a>) on
<a href="https://codepen.io">CodePen</a>.
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
</section>

<section>
<h6>Available options</h6>
<pre><code class="hljs javascript">
...
{
data: {
labels: [],
datasets: [],
yRegions: [],
yMarkers: []
}
title: '',
colors: [],
height: 200,

tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' pts',
}

// Axis charts
isNavigable: 1, // default: 0
valuesOverPoints: 1, // default: 0
barOptions: {
spaceRatio: 1 // default: 0.5
stacked: 1 // default: 0
}

lineOptions: {
dotSize: 6, // default: 4
hideLine: 0, // default: 0
hideDots: 1, // default: 0
heatline: 1, // default: 0
regionFill: 1 // default: 0
}

axisOptions: {
yAxisMode: 'span', // Axis lines, default
xAxisMode: 'tick', // No axis lines, only short ticks
xIsSeries: 1 // Allow skipping x values for space
// default: 0
},

// Pie/Percentage/Donut charts
maxLegendPoints: 6, // default: 20
maxSlices: 10, // default: 20

// Percentage chart
barOptions: {
height: 15 // default: 20
depth: 5 // default: 2
}

// Heatmap
discreteDomains: 1, // default: 1
}
...

// Updating values
chart.update(data);

// Axis charts:
chart.addDataPoint(label, valueFromEachDataset, index)
chart.removeDataPoint(index)
chart.updateDataset(datasetValues, index)

// Exporting
chart.export();

// Unbind window-resize events
chart.destroy();

</code></pre>
</section>

<section>
<h6>Install</h6>
<p>Install via npm</p>
<pre><code class="hljs console"> npm install frappe-charts</code></pre>
<p>And include it in your project</p>
<pre><code class="hljs javascript"> import { Chart } from "frappe-charts"</code></pre>
<p>(for ES6+ import the ESModule from the dist folder)</p>
<pre><code class="hljs javascript"> import { Chart } from "frappe-charts/dist/frappe-charts.esm.js"</code></pre>
<p>... or include it directly in your HTML</p>
<pre><code class="hljs html"> &lt;script src="https://unpkg.com/frappe-charts@1.1.0"&gt;&lt;/script&gt;</code></pre>
<p>Use as:</p>
<pre><code class="hljs javascript"> new Chart(); // ES6 module
// or
new frappe.Chart(); // Browser</code></pre>
</section>

<section class="text-center">
<!-- Closing -->
<a href="https://github.com/frappe/charts/archive/master.zip"><button class="large blue button btn">Download</button></a>
<p style="margin-top: 3rem;margin-bottom: 1.5rem;">
<!-- <a href="docs.html" style="margin-right: 1rem;" target="_blank">Documentation</a> -->
<a href="https://github.com/frappe/charts" target="_blank">View on GitHub</a>
</p>
<p style="margin-top: 1rem;">
<a class="github-button" href="https://github.com/frappe/charts" data-icon="octicon-star" data-show-count="true" aria-label="Star frappe/charts on GitHub">Star</a>
</p>
<p>License: MIT</p>
</section>

<footer class="built-with-frappe text-center">
<img style="padding: 5px; width: 40px; background: #fff" class="frappe-bird" src="./assets/img/frappe-bird.png">
<p style="margin: 24px 0 0px 0; font-size: 15px">
Project maintained by <a href="https://frappe.io" target="_blank">Frappe</a>.
Used in <a href="https://erpnext.com" target="_blank">ERPNext</a>.
Read the <a href="https://medium.com/@pratu16x7/so-we-decided-to-create-our-own-charts-a95cb5032c97" target="_blank">blog post</a>.
</p>
<p style="margin: 24px 0 80px 0; font-size: 12px">
Data from the <a href="https://www.amsmeteors.org" target="_blank">American Meteor Society</a>,
<a href="http://www.sidc.be/silso" target="_blank">SILSO</a> and
<a href="https://api.nasa.gov/index.html" target="_blank">NASA Open APIs</a>
</p>
</footer>

<a href="https://github.com/frappe/charts" target="_blank" class="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#9a9a9a; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
</svg>
</a>

<script src="assets/js/frappe-charts.min.js"></script>
<script src="assets/js/index.min.js"></script>
</body>
</html>

+ 0
- 10646
package-lock.json
Plik diff jest za duży
Wyświetl plik


+ 18
- 36
package.json Wyświetl plik

@@ -1,11 +1,13 @@
{ {
"name": "frappe-charts", "name": "frappe-charts",
"version": "1.5.5",
"version": "2.0.0-rc22",
"main": "dist/frappe-charts.esm.js",
"module": "dist/frappe-charts.esm.js",
"browser": "dist/frappe-charts.umd.js",
"common": "dist/frappe-charts.cjs.js",
"unnpkg": "dist/frappe-charts.umd.js",
"type": "module",
"description": "https://frappe.github.io/charts", "description": "https://frappe.github.io/charts",
"main": "dist/frappe-charts.min.cjs.js",
"module": "dist/frappe-charts.min.esm.js",
"src": "dist/frappe-charts.esm.js",
"browser": "dist/frappe-charts.min.iife.js",
"directories": { "directories": {
"doc": "docs" "doc": "docs"
}, },
@@ -34,35 +36,15 @@
}, },
"homepage": "https://github.com/frappe/charts#readme", "homepage": "https://github.com/frappe/charts#readme",
"devDependencies": { "devDependencies": {
"autoprefixer": "^8.2.0",
"babel-core": "^6.26.3",
"babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-istanbul": "^5.1.4",
"babel-preset-env": "^1.7.0",
"babel-preset-latest": "^6.24.1",
"babel-register": "^6.26.0",
"clean-css": "^4.1.11",
"coveralls": "^3.0.0",
"cross-env": "^5.1.4",
"cssnano": "^4.1.10",
"eslint": "^4.18.2",
"mocha": "^5.0.5",
"node-sass": "^4.12.0",
"npm-run-all": "^4.1.1",
"nyc": "^14.1.1",
"postcss": "^6.0.21",
"postcss-cssnext": "^3.0.2",
"postcss-nested": "^2.1.2",
"precss": "^3.1.2",
"rollup": "^0.50.0",
"rollup-plugin-babel": "^3.0.2",
"rollup-plugin-eslint": "^6.0.0",
"rollup-plugin-node-resolve": "^3.0.0",
"rollup-plugin-postcss": "^2.0.3",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-uglify": "^2.0.1",
"rollup-plugin-uglify-es": "0.0.1",
"rollup-watch": "^4.3.1"
},
"dependencies": {}
"@babel/core": "^7.10.5",
"@babel/preset-env": "^7.10.4",
"rollup": "^2.21.0",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-bundle-size": "^1.0.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-eslint": "^7.0.0",
"rollup-plugin-postcss": "^3.1.3",
"rollup-plugin-scss": "^2.5.0",
"rollup-plugin-terser": "^6.1.0"
}
} }

+ 27
- 174
rollup.config.js Wyświetl plik

@@ -1,195 +1,48 @@
import pkg from './package.json'; import pkg from './package.json';


// Rollup plugins
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel'; import babel from 'rollup-plugin-babel';
import { eslint } from 'rollup-plugin-eslint';
import replace from 'rollup-plugin-replace';
import uglify from 'rollup-plugin-uglify-es';
import sass from 'node-sass';
import postcss from 'rollup-plugin-postcss';
import scss from 'rollup-plugin-scss';
import bundleSize from 'rollup-plugin-bundle-size';
import { terser } from 'rollup-plugin-terser';


// PostCSS plugins
import postcssPlugin from 'rollup-plugin-postcss';
import nested from 'postcss-nested';
import cssnext from 'postcss-cssnext';
import cssnano from 'cssnano';

import postcss from 'postcss';
import precss from 'precss';
import CleanCSS from 'clean-css';
import autoprefixer from 'autoprefixer';
import fs from 'fs';

fs.readFile('src/css/charts.scss', (err, css) => {
postcss([precss, autoprefixer])
.process(css, { from: 'src/css/charts.scss', to: 'src/css/charts.css' })
.then(result => {
let options = {
level: {
1: {
removeQuotes: false,
}
}
}
let output = new CleanCSS(options).minify(result.css);
let res = JSON.stringify(output.styles).replace(/"/g, "'");
let js = `export const CSSTEXT = "${res.slice(1, -1)}";`;
fs.writeFile('src/css/chartsCss.js', js);
});
});


export default [ export default [
// browser-friendly UMD build
{ {
input: 'src/js/index.js', input: 'src/js/index.js',
sourcemap: true,
output: [
{
file: 'docs/assets/js/frappe-charts.min.js',
format: 'iife',
},
{
file: pkg.browser,
format: 'iife',
}
],
name: 'frappe',
plugins: [
postcssPlugin({
preprocessor: (content, id) => new Promise((resolve, reject) => {
const result = sass.renderSync({ file: id })
resolve({ code: result.css.toString() })
}),
extensions: [ '.scss' ],
plugins: [
nested(),
cssnext({ warnForDuplicates: false }),
cssnano()
]
}),
eslint({
exclude: [
'src/css/**'
]
}),
babel({
exclude: 'node_modules/**',
plugins: ['external-helpers']
}),
replace({
exclude: 'node_modules/**',
ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
}),
uglify()
]
},
{
input: 'docs/assets/js/index.js',
sourcemap: true,
output: [
{
file: 'docs/assets/js/index.min.js',
format: 'iife',
}
],
name: 'frappe',
output: {
sourcemap: true,
name: 'frappe-charts',
file: pkg.browser,
format: 'umd'
},
plugins: [ plugins: [
postcssPlugin({
preprocessor: (content, id) => new Promise((resolve, reject) => {
const result = sass.renderSync({ file: id })
resolve({ code: result.css.toString() })
}),
extensions: [ '.scss' ],
plugins: [
nested(),
cssnext({ warnForDuplicates: false }),
cssnano()
]
}),
eslint({
exclude: [
'src/css/**'
]
}),
commonjs(),
babel({ babel({
exclude: 'node_modules/**'
exclude: ['node_modules/**']
}), }),
replace({
exclude: 'node_modules/**',
ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
})
terser(),
scss({ output: 'dist/frappe-charts.min.css' }),
bundleSize()
] ]
}, },

// CommonJS (for Node) and ES module (for bundlers) build.
{ {
input: 'src/js/chart.js', input: 'src/js/chart.js',
sourcemap: true,
output: [ output: [
{
file: pkg.main,
format: 'cjs',
},
{
file: pkg.module,
format: 'es',
}
{ file: pkg.common, format: 'cjs', sourcemap: true },
{ file: pkg.module, format: 'es', sourcemap: true },
], ],
plugins: [ plugins: [
postcssPlugin({
preprocessor: (content, id) => new Promise((resolve, reject) => {
const result = sass.renderSync({ file: id })
resolve({ code: result.css.toString() })
}),
extensions: [ '.scss' ],
plugins: [
nested(),
cssnext({ warnForDuplicates: false }),
cssnano()
]
}),
eslint({
exclude: [
'src/css/**',
]
}),
babel({ babel({
exclude: 'node_modules/**',
}),
replace({
exclude: 'node_modules/**',
ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
}),
uglify()
],
},
{
input: 'src/js/chart.js',
output: [
{
file: pkg.src,
format: 'es',
}
],
plugins: [
postcssPlugin({
preprocessor: (content, id) => new Promise((resolve, reject) => {
const result = sass.renderSync({ file: id })
resolve({ code: result.css.toString() })
}),
extensions: [ '.scss' ],
extract: 'dist/frappe-charts.min.css',
plugins: [
nested(),
cssnext({ warnForDuplicates: false }),
cssnano()
]
}),
eslint({
exclude: [
'src/css/**',
]
exclude: ['node_modules/**']
}), }),
replace({
exclude: 'node_modules/**',
ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
})
],
terser(),
postcss(),
bundleSize()
]
} }
];
];

+ 10
- 0
src/.babelrc Wyświetl plik

@@ -0,0 +1,10 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
},
"modules": false
}]
]
}

+ 102
- 30
src/css/charts.scss Wyświetl plik

@@ -1,43 +1,65 @@
:root {
--charts-label-color: #313b44;
--charts-axis-line-color: #f4f5f6;

--charts-tooltip-title: var(--charts-label-color);
--charts-tooltip-label: var(--charts-label-color);
--charts-tooltip-value: #192734;
--charts-tooltip-bg: #ffffff;

--charts-stroke-width: 2px;
--charts-dataset-circle-stroke: #ffffff;
--charts-dataset-circle-stroke-width: var(--charts-stroke-width);

--charts-legend-label: var(--charts-label-color);
--charts-legend-value: var(--charts-label-color);
}

.chart-container { .chart-container {
position: relative; /* for absolutely positioned tooltip */ position: relative; /* for absolutely positioned tooltip */


/* https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/ */
font-family: -apple-system, BlinkMacSystemFont,
'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell',
'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;


.axis, .chart-label {
fill: #555b51;
.axis,
.chart-label {
fill: var(--charts-label-color);


line { line {
stroke: #dadada;
stroke: var(--charts-axis-line-color);
} }
} }

.dataset-units { .dataset-units {
circle { circle {
stroke: #fff;
stroke-width: 2;
stroke: var(--charts-dataset-circle-stroke);
stroke-width: var(--charts-dataset-circle-stroke-width);
} }


path { path {
fill: none; fill: none;
stroke-opacity: 1; stroke-opacity: 1;
stroke-width: 2px;
stroke-width: var(--charts-stroke-width);
} }
} }

.dataset-path { .dataset-path {
stroke-width: 2px;
stroke-width: var(--charts-stroke-width);
} }

.path-group { .path-group {
path { path {
fill: none; fill: none;
stroke-opacity: 1; stroke-opacity: 1;
stroke-width: 2px;
stroke-width: var(--charts-stroke-width);
} }
} }

line.dashed { line.dashed {
stroke-dasharray: 5, 3; stroke-dasharray: 5, 3;
} }

.axis-line { .axis-line {
.specific-value { .specific-value {
text-anchor: start; text-anchor: start;
@@ -49,10 +71,15 @@
text-anchor: middle; text-anchor: middle;
} }
} }
.legend-dataset-text {
fill: #6c7680;

.legend-dataset-label {
fill: var(--charts-legend-label);
font-weight: 600; font-weight: 600;
} }

.legend-dataset-value {
fill: var(--charts-legend-value);
}
} }


.graph-svg-tip { .graph-svg-tip {
@@ -60,57 +87,102 @@
z-index: 99999; z-index: 99999;
padding: 10px; padding: 10px;
font-size: 12px; font-size: 12px;
color: #959da5;
text-align: center; text-align: center;
background: rgba(0, 0, 0, 0.8);
border-radius: 3px;
background: var(--charts-tooltip-bg);
box-shadow: 0px 1px 4px rgba(17, 43, 66, 0.1),
0px 2px 6px rgba(17, 43, 66, 0.08),
0px 40px 30px -30px rgba(17, 43, 66, 0.1);
border-radius: 6px;

ul { ul {
padding-left: 0; padding-left: 0;
display: flex; display: flex;
} }

ol { ol {
padding-left: 0; padding-left: 0;
display: flex; display: flex;
} }

ul.data-point-list { ul.data-point-list {
li { li {
min-width: 90px; min-width: 90px;
flex: 1;
font-weight: 600; font-weight: 600;
} }
} }
strong {
color: #dfe2e5;
font-weight: 600;
}

.svg-pointer { .svg-pointer {
position: absolute; position: absolute;
height: 5px;
margin: 0 0 0 -5px;
content: ' ';
border: 5px solid transparent;
border-top-color: rgba(0, 0, 0, 0.8);
height: 12px;
width: 12px;
border-radius: 2px;
background: var(--charts-tooltip-bg);
transform: rotate(45deg);
margin-top: -7px;
margin-left: -6px;
} }

&.comparison { &.comparison {
padding: 0;
text-align: left; text-align: left;
padding: 0px;
pointer-events: none; pointer-events: none;

.title { .title {
display: block; display: block;
padding: 10px;
padding: 16px;
margin: 0; margin: 0;
color: var(--charts-tooltip-title);
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
pointer-events: none; pointer-events: none;
text-transform: uppercase;

strong {
color: var(--charts-tooltip-value);
}
} }

ul { ul {
margin: 0; margin: 0;
white-space: nowrap; white-space: nowrap;
list-style: none; list-style: none;

&.tooltip-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 5px;
}
} }

li { li {
display: inline-block; display: inline-block;
padding: 5px 10px;
display: flex;
flex-direction: row;
font-weight: 600;
line-height: 1;

padding: 5px 15px 15px 15px;

.tooltip-legend {
height: 12px;
width: 12px;
margin-right: 8px;
border-radius: 2px;
}

.tooltip-label {
margin-top: 4px;
font-size: 11px;
line-height: 1.25;
max-width: 150px;
white-space: normal;

color: var(--charts-tooltip-label);
}

.tooltip-value {
color: var(--charts-tooltip-value);
}
} }
} }
} }

+ 1
- 1
src/css/chartsCss.js Wyświetl plik

@@ -1 +1 @@
export const CSSTEXT = ".chart-container{position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif}.chart-container .axis,.chart-container .chart-label{fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .dataset-units circle{stroke:#fff;stroke-width:2}.chart-container .dataset-units path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container .dataset-path{stroke-width:2px}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .axis-line .specific-value{text-anchor:start}.chart-container .axis-line .y-line{text-anchor:end}.chart-container .axis-line .x-line{text-anchor:middle}.chart-container .legend-dataset-text{fill:#6c7680;font-weight:600}.graph-svg-tip{position:absolute;z-index:99999;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.graph-svg-tip ul{padding-left:0;display:flex}.graph-svg-tip ol{padding-left:0;display:flex}.graph-svg-tip ul.data-point-list li{min-width:90px;flex:1;font-weight:600}.graph-svg-tip strong{color:#dfe2e5;font-weight:600}.graph-svg-tip .svg-pointer{position:absolute;height:5px;margin:0 0 0 -5px;content:' ';border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}";
export const CSSTEXT = ".chart-container{position:relative;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif}.chart-container .axis,.chart-container .chart-label{fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .dataset-units circle{stroke:#fff;stroke-width:2}.chart-container .dataset-units path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container .dataset-path{stroke-width:2px}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .axis-line .specific-value{text-anchor:start}.chart-container .axis-line .y-line{text-anchor:end}.chart-container .axis-line .x-line{text-anchor:middle}.chart-container .legend-dataset-text{fill:#6c7680;font-weight:600}.graph-svg-tip{position:absolute;z-index:99999;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.graph-svg-tip ul{padding-left:0;display:flex}.graph-svg-tip ol{padding-left:0;display:flex}.graph-svg-tip ul.data-point-list li{min-width:90px;flex:1;font-weight:600}.graph-svg-tip strong{color:#dfe2e5;font-weight:600}.graph-svg-tip .svg-pointer{position:absolute;height:5px;margin:0 0 0 -5px;content:' ';border:5px solid transparent;}.graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}";

+ 0
- 2
src/js/chart.js Wyświetl plik

@@ -1,6 +1,5 @@
import '../css/charts.scss'; import '../css/charts.scss';


// import MultiAxisChart from './charts/MultiAxisChart';
import PercentageChart from './charts/PercentageChart'; import PercentageChart from './charts/PercentageChart';
import PieChart from './charts/PieChart'; import PieChart from './charts/PieChart';
import Heatmap from './charts/Heatmap'; import Heatmap from './charts/Heatmap';
@@ -10,7 +9,6 @@ import DonutChart from './charts/DonutChart';
const chartTypes = { const chartTypes = {
bar: AxisChart, bar: AxisChart,
line: AxisChart, line: AxisChart,
// multiaxis: MultiAxisChart,
percentage: PercentageChart, percentage: PercentageChart,
heatmap: Heatmap, heatmap: Heatmap,
pie: PieChart, pie: PieChart,


+ 22
- 35
src/js/charts/AggregationChart.js Wyświetl plik

@@ -1,8 +1,6 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { truncateString } from '../utils/draw-utils';
import { legendDot } from '../utils/draw'; import { legendDot } from '../utils/draw';
import { round } from '../utils/helpers'; import { round } from '../utils/helpers';
import { getExtraWidth } from '../utils/constants';


export default class AggregationChart extends BaseChart { export default class AggregationChart extends BaseChart {
constructor(parent, args) { constructor(parent, args) {
@@ -15,6 +13,7 @@ export default class AggregationChart extends BaseChart {
this.config.formatTooltipY = (args.tooltipOptions || {}).formatTooltipY; this.config.formatTooltipY = (args.tooltipOptions || {}).formatTooltipY;
this.config.maxSlices = args.maxSlices || 20; this.config.maxSlices = args.maxSlices || 20;
this.config.maxLegendPoints = args.maxLegendPoints || 20; this.config.maxLegendPoints = args.maxLegendPoints || 20;
this.config.legendRowHeight = 60;
} }


calc() { calc() {
@@ -31,17 +30,17 @@ export default class AggregationChart extends BaseChart {
}).filter(d => { return d[0] >= 0; }); // keep only positive results }).filter(d => { return d[0] >= 0; }); // keep only positive results


let totals = allTotals; let totals = allTotals;
if(allTotals.length > maxSlices) {
if (allTotals.length > maxSlices) {
// Prune and keep a grey area for rest as per maxSlices // Prune and keep a grey area for rest as per maxSlices
allTotals.sort((a, b) => { return b[0] - a[0]; }); allTotals.sort((a, b) => { return b[0] - a[0]; });


totals = allTotals.slice(0, maxSlices-1);
let remaining = allTotals.slice(maxSlices-1);
totals = allTotals.slice(0, maxSlices - 1);
let remaining = allTotals.slice(maxSlices - 1);


let sumOfRemaining = 0; let sumOfRemaining = 0;
remaining.map(d => {sumOfRemaining += d[0];});
remaining.map(d => { sumOfRemaining += d[0]; });
totals.push([sumOfRemaining, 'Rest']); totals.push([sumOfRemaining, 'Rest']);
this.colors[maxSlices-1] = 'grey';
this.colors[maxSlices - 1] = 'grey';
} }


s.labels = []; s.labels = [];
@@ -62,34 +61,22 @@ export default class AggregationChart extends BaseChart {
let s = this.state; let s = this.state;
this.legendArea.textContent = ''; this.legendArea.textContent = '';
this.legendTotals = s.sliceTotals.slice(0, this.config.maxLegendPoints); this.legendTotals = s.sliceTotals.slice(0, this.config.maxLegendPoints);
super.renderLegend(this.legendTotals);
}


let count = 0;
let y = 0;
this.legendTotals.map((d, i) => {
let barWidth = 150;
let divisor = Math.floor(
(this.width - getExtraWidth(this.measures))/barWidth
);
if (this.legendTotals.length < divisor) {
barWidth = this.width/this.legendTotals.length;
}
if(count > divisor) {
count = 0;
y += 20;
}
let x = barWidth * count + 5;
let label = this.config.truncateLegends ? truncateString(s.labels[i], barWidth/10) : s.labels[i];
let formatted = this.config.formatTooltipY ? this.config.formatTooltipY(d) : d;
let dot = legendDot(
x,
y,
5,
this.colors[i],
`${label}: ${formatted}`,
false
);
this.legendArea.appendChild(dot);
count++;
});
makeLegend(data, index, x_pos, y_pos) {
let formatted = this.config.formatTooltipY ? this.config.formatTooltipY(data) : data;

return legendDot(
x_pos,
y_pos,
12, // size
3, // dot radius
this.colors[index], // fill
this.state.labels[index], // label
formatted, // value
null, // base_font_size
this.config.truncateLegends // truncate_legends
);
} }
} }

+ 75
- 70
src/js/charts/AxisChart.js Wyświetl plik

@@ -1,13 +1,14 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { dataPrep, zeroDataPrep, getShortenedLabels } from '../utils/axis-chart-utils'; import { dataPrep, zeroDataPrep, getShortenedLabels } from '../utils/axis-chart-utils';
import { AXIS_LEGEND_BAR_SIZE } from '../utils/constants';
import { getComponent } from '../objects/ChartComponents'; import { getComponent } from '../objects/ChartComponents';
import { getOffset, fire } from '../utils/dom'; import { getOffset, fire } from '../utils/dom';
import { calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex, scale, getClosestInArray } from '../utils/intervals'; import { calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex, scale, getClosestInArray } from '../utils/intervals';
import { floatTwo } from '../utils/helpers'; import { floatTwo } from '../utils/helpers';
import { makeOverlay, updateOverlay, legendBar } from '../utils/draw';
import { getTopOffset, getLeftOffset, MIN_BAR_PERCENT_HEIGHT, BAR_CHART_SPACE_RATIO,
LINE_CHART_DOT_SIZE } from '../utils/constants';
import { makeOverlay, updateOverlay, legendDot } from '../utils/draw';
import {
getTopOffset, getLeftOffset, MIN_BAR_PERCENT_HEIGHT, BAR_CHART_SPACE_RATIO,
LINE_CHART_DOT_SIZE
} from '../utils/constants';


export default class AxisChart extends BaseChart { export default class AxisChart extends BaseChart {
constructor(parent, args) { constructor(parent, args) {
@@ -23,7 +24,7 @@ export default class AxisChart extends BaseChart {
} }


setMeasures() { setMeasures() {
if(this.data.datasets.length <= 1) {
if (this.data.datasets.length <= 1) {
this.config.showLegend = 0; this.config.showLegend = 0;
this.measures.paddings.bottom = 30; this.measures.paddings.bottom = 30;
} }
@@ -39,24 +40,28 @@ export default class AxisChart extends BaseChart {
this.config.yAxisMode = options.axisOptions.yAxisMode || 'span'; this.config.yAxisMode = options.axisOptions.yAxisMode || 'span';
this.config.xIsSeries = options.axisOptions.xIsSeries || 0; this.config.xIsSeries = options.axisOptions.xIsSeries || 0;
this.config.shortenYAxisNumbers = options.axisOptions.shortenYAxisNumbers || 0; this.config.shortenYAxisNumbers = options.axisOptions.shortenYAxisNumbers || 0;
this.config.numberFormatter = options.axisOptions.numberFormatter;

this.config.yAxisRange = options.axisOptions.yAxisRange || {},


this.config.formatTooltipX = options.tooltipOptions.formatTooltipX; this.config.formatTooltipX = options.tooltipOptions.formatTooltipX;
this.config.formatTooltipY = options.tooltipOptions.formatTooltipY; this.config.formatTooltipY = options.tooltipOptions.formatTooltipY;


this.config.valuesOverPoints = options.valuesOverPoints; this.config.valuesOverPoints = options.valuesOverPoints;
this.config.legendRowHeight = 30;
} }


prepareData(data=this.data) {
prepareData(data = this.data) {
return dataPrep(data, this.type); return dataPrep(data, this.type);
} }


prepareFirstData(data=this.data) {
prepareFirstData(data = this.data) {
return zeroDataPrep(data); return zeroDataPrep(data);
} }


calc(onlyWidthChange = false) { calc(onlyWidthChange = false) {
this.calcXPositions(); this.calcXPositions();
if(!onlyWidthChange) {
if (!onlyWidthChange) {
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line'); this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
} }
this.makeDataByIndex(); this.makeDataByIndex();
@@ -67,9 +72,9 @@ export default class AxisChart extends BaseChart {
let labels = this.data.labels; let labels = this.data.labels;
s.datasetLength = labels.length; s.datasetLength = labels.length;


s.unitWidth = this.width/(s.datasetLength);
s.unitWidth = this.width / (s.datasetLength);
// Default, as per bar, and mixed. Only line will be a special case // Default, as per bar, and mixed. Only line will be a special case
s.xOffset = s.unitWidth/2;
s.xOffset = s.unitWidth / 2;


// // For a pure Line Chart // // For a pure Line Chart
// s.unitWidth = this.width/(s.datasetLength - 1); // s.unitWidth = this.width/(s.datasetLength - 1);
@@ -84,7 +89,7 @@ export default class AxisChart extends BaseChart {
} }


calcYAxisParameters(dataValues, withMinimum = 'false') { calcYAxisParameters(dataValues, withMinimum = 'false') {
const yPts = calcChartIntervals(dataValues, withMinimum);
const yPts = calcChartIntervals(dataValues, withMinimum, this.config.yAxisRange);
const scaleMultiplier = this.height / getValueRange(yPts); const scaleMultiplier = this.height / getValueRange(yPts);
const intervalHeight = getIntervalSize(yPts) * scaleMultiplier; const intervalHeight = getIntervalSize(yPts) * scaleMultiplier;
const zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight); const zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
@@ -110,7 +115,7 @@ export default class AxisChart extends BaseChart {
let values = d.values; let values = d.values;
let cumulativeYs = d.cumulativeYs || []; let cumulativeYs = d.cumulativeYs || [];
return { return {
name: d.name.replace(/<|>|&/g, (char) => char == '&' ? '&amp;' : char == '<' ? '&lt;' : '&gt;'),
name: d.name && d.name.replace(/<|>|&/g, (char) => char == '&' ? '&amp;' : char == '<' ? '&lt;' : '&gt;'),
index: i, index: i,
chartType: d.chartType, chartType: d.chartType,


@@ -125,14 +130,14 @@ export default class AxisChart extends BaseChart {


calcYExtremes() { calcYExtremes() {
let s = this.state; let s = this.state;
if(this.barOptions.stacked) {
if (this.barOptions.stacked) {
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativeYPos; s.yExtremes = s.datasets[s.datasets.length - 1].cumulativeYPos;
return; return;
} }
s.yExtremes = new Array(s.datasetLength).fill(9999); s.yExtremes = new Array(s.datasetLength).fill(9999);
s.datasets.map(d => { s.datasets.map(d => {
d.yPositions.map((pos, j) => { d.yPositions.map((pos, j) => {
if(pos < s.yExtremes[j]) {
if (pos < s.yExtremes[j]) {
s.yExtremes[j] = pos; s.yExtremes[j] = pos;
} }
}); });
@@ -141,21 +146,21 @@ export default class AxisChart extends BaseChart {


calcYRegions() { calcYRegions() {
let s = this.state; let s = this.state;
if(this.data.yMarkers) {
if (this.data.yMarkers) {
this.state.yMarkers = this.data.yMarkers.map(d => { this.state.yMarkers = this.data.yMarkers.map(d => {
d.position = scale(d.value, s.yAxis); d.position = scale(d.value, s.yAxis);
if(!d.options) d.options = {};
if (!d.options) d.options = {};
// if(!d.label.includes(':')) { // if(!d.label.includes(':')) {
// d.label += ': ' + d.value; // d.label += ': ' + d.value;
// } // }
return d; return d;
}); });
} }
if(this.data.yRegions) {
if (this.data.yRegions) {
this.state.yRegions = this.data.yRegions.map(d => { this.state.yRegions = this.data.yRegions.map(d => {
d.startPos = scale(d.start, s.yAxis); d.startPos = scale(d.start, s.yAxis);
d.endPos = scale(d.end, s.yAxis); d.endPos = scale(d.end, s.yAxis);
if(!d.options) d.options = {};
if (!d.options) d.options = {};
return d; return d;
}); });
} }
@@ -164,7 +169,7 @@ export default class AxisChart extends BaseChart {
getAllYValues() { getAllYValues() {
let key = 'values'; let key = 'values';


if(this.barOptions.stacked) {
if (this.barOptions.stacked) {
key = 'cumulativeYs'; key = 'cumulativeYs';
let cumulative = new Array(this.state.datasetLength).fill(0); let cumulative = new Array(this.state.datasetLength).fill(0);
this.data.datasets.map((d, i) => { this.data.datasets.map((d, i) => {
@@ -174,10 +179,10 @@ export default class AxisChart extends BaseChart {
} }


let allValueLists = this.data.datasets.map(d => d[key]); let allValueLists = this.data.datasets.map(d => d[key]);
if(this.data.yMarkers) {
if (this.data.yMarkers) {
allValueLists.push(this.data.yMarkers.map(d => d.value)); allValueLists.push(this.data.yMarkers.map(d => d.value));
} }
if(this.data.yRegions) {
if (this.data.yRegions) {
this.data.yRegions.map(d => { this.data.yRegions.map(d => {
allValueLists.push([d.end, d.start]); allValueLists.push([d.end, d.start]);
}); });
@@ -193,10 +198,10 @@ export default class AxisChart extends BaseChart {
{ {
mode: this.config.yAxisMode, mode: this.config.yAxisMode,
width: this.width, width: this.width,
shortenNumbers: this.config.shortenYAxisNumbers
// pos: 'right'
shortenNumbers: this.config.shortenYAxisNumbers,
numberFormatter: this.config.numberFormatter,
}, },
function() {
function () {
return this.state.yAxis; return this.state.yAxis;
}.bind(this) }.bind(this)
], ],
@@ -208,7 +213,7 @@ export default class AxisChart extends BaseChart {
height: this.height, height: this.height,
// pos: 'right' // pos: 'right'
}, },
function() {
function () {
let s = this.state; let s = this.state;
s.xAxis.calcLabels = getShortenedLabels(this.width, s.xAxis.calcLabels = getShortenedLabels(this.width,
s.xAxis.labels, this.config.xIsSeries); s.xAxis.labels, this.config.xIsSeries);
@@ -223,7 +228,7 @@ export default class AxisChart extends BaseChart {
width: this.width, width: this.width,
pos: 'right' pos: 'right'
}, },
function() {
function () {
return this.state.yRegions; return this.state.yRegions;
}.bind(this) }.bind(this)
], ],
@@ -245,23 +250,23 @@ export default class AxisChart extends BaseChart {
valuesOverPoints: this.config.valuesOverPoints, valuesOverPoints: this.config.valuesOverPoints,
minHeight: this.height * MIN_BAR_PERCENT_HEIGHT, minHeight: this.height * MIN_BAR_PERCENT_HEIGHT,
}, },
function() {
function () {
let s = this.state; let s = this.state;
let d = s.datasets[index]; let d = s.datasets[index];
let stacked = this.barOptions.stacked; let stacked = this.barOptions.stacked;


let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO; let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
let barsWidth = s.unitWidth * (1 - spaceRatio); let barsWidth = s.unitWidth * (1 - spaceRatio);
let barWidth = barsWidth/(stacked ? 1 : barDatasets.length);
let barWidth = barsWidth / (stacked ? 1 : barDatasets.length);


let xPositions = s.xAxis.positions.map(x => x - barsWidth/2);
if(!stacked) {
let xPositions = s.xAxis.positions.map(x => x - barsWidth / 2);
if (!stacked) {
xPositions = xPositions.map(p => p + barWidth * index); xPositions = xPositions.map(p => p + barWidth * index);
} }


let labels = new Array(s.datasetLength).fill(''); let labels = new Array(s.datasetLength).fill('');
if(this.config.valuesOverPoints) {
if(stacked && d.index === s.datasets.length - 1) {
if (this.config.valuesOverPoints) {
if (stacked && d.index === s.datasets.length - 1) {
labels = d.cumulativeYs; labels = d.cumulativeYs;
} else { } else {
labels = d.values; labels = d.values;
@@ -269,7 +274,7 @@ export default class AxisChart extends BaseChart {
} }


let offsets = new Array(s.datasetLength).fill(0); let offsets = new Array(s.datasetLength).fill(0);
if(stacked) {
if (stacked) {
offsets = d.yPositions.map((y, j) => y - d.cumulativeYPos[j]); offsets = d.yPositions.map((y, j) => y - d.cumulativeYPos[j]);
} }


@@ -299,13 +304,15 @@ export default class AxisChart extends BaseChart {
heatline: this.lineOptions.heatline, heatline: this.lineOptions.heatline,
regionFill: this.lineOptions.regionFill, regionFill: this.lineOptions.regionFill,
spline: this.lineOptions.spline, spline: this.lineOptions.spline,
hideDots: this.lineOptions.hideDots,
showDots: this.lineOptions.showDots,
trailingDot: this.lineOptions.trailingDot,
hideDotBorder: this.lineOptions.hideDotBorder,
hideLine: this.lineOptions.hideLine, hideLine: this.lineOptions.hideLine,


// same for all datasets // same for all datasets
valuesOverPoints: this.config.valuesOverPoints, valuesOverPoints: this.config.valuesOverPoints,
}, },
function() {
function () {
let s = this.state; let s = this.state;
let d = s.datasets[index]; let d = s.datasets[index];
let minLine = s.yAxis.positions[0] < s.yAxis.zeroLine let minLine = s.yAxis.positions[0] < s.yAxis.zeroLine
@@ -331,7 +338,7 @@ export default class AxisChart extends BaseChart {
width: this.width, width: this.width,
pos: 'right' pos: 'right'
}, },
function() {
function () {
return this.state.yMarkers; return this.state.yMarkers;
}.bind(this) }.bind(this)
] ]
@@ -346,7 +353,7 @@ export default class AxisChart extends BaseChart {
.filter(args => !optionals.includes(args[0]) || this.state[args[0]]) .filter(args => !optionals.includes(args[0]) || this.state[args[0]])
.map(args => { .map(args => {
let component = getComponent(...args); let component = getComponent(...args);
if(args[0].includes('lineGraph') || args[0].includes('barGraph')) {
if (args[0].includes('lineGraph') || args[0].includes('barGraph')) {
this.dataUnitComponents.push(component); this.dataUnitComponents.push(component);
} }
return [args[0], component]; return [args[0], component];
@@ -391,8 +398,8 @@ export default class AxisChart extends BaseChart {
let relX = e.pageX - o.left - getLeftOffset(m); let relX = e.pageX - o.left - getLeftOffset(m);
let relY = e.pageY - o.top; let relY = e.pageY - o.top;


if(relY < this.height + getTopOffset(m)
&& relY > getTopOffset(m)) {
if (relY < this.height + getTopOffset(m)
&& relY > getTopOffset(m)) {
this.mapTooltipXPosition(relX); this.mapTooltipXPosition(relX);
} else { } else {
this.tip.hideTip(); this.tip.hideTip();
@@ -402,7 +409,7 @@ export default class AxisChart extends BaseChart {


mapTooltipXPosition(relX) { mapTooltipXPosition(relX) {
let s = this.state; let s = this.state;
if(!s.yExtremes) return;
if (!s.yExtremes) return;


let index = getClosestInArray(relX, s.xAxis.positions, true); let index = getClosestInArray(relX, s.xAxis.positions, true);
if (index >= 0) { if (index >= 0) {
@@ -411,7 +418,7 @@ export default class AxisChart extends BaseChart {
this.tip.setValues( this.tip.setValues(
dbi.xPos + this.tip.offset.x, dbi.xPos + this.tip.offset.x,
dbi.yExtreme + this.tip.offset.y, dbi.yExtreme + this.tip.offset.y,
{name: dbi.formattedLabel, value: ''},
{ name: dbi.formattedLabel, value: '' },
dbi.values, dbi.values,
index index
); );
@@ -422,34 +429,32 @@ export default class AxisChart extends BaseChart {


renderLegend() { renderLegend() {
let s = this.data; let s = this.data;
if(s.datasets.length > 1) {
this.legendArea.textContent = '';
s.datasets.map((d, i) => {
let barWidth = AXIS_LEGEND_BAR_SIZE;
// let rightEndPoint = this.baseWidth - this.measures.margins.left - this.measures.margins.right;
// let multiplier = s.datasets.length - i;
let rect = legendBar(
// rightEndPoint - multiplier * barWidth, // To right align
barWidth * i,
'0',
barWidth,
this.colors[i],
d.name,
this.config.truncateLegends);
this.legendArea.appendChild(rect);
});
if (s.datasets.length > 1) {
super.renderLegend(s.datasets);
} }
} }



makeLegend(data, index, x_pos, y_pos) {
return legendDot(
x_pos,
y_pos + 5, // Extra offset
12, // size
3, // dot radius
this.colors[index], // fill
data.name, //label
null, // value
8.75, // base_font_size
this.config.truncateLegends // truncate legends
);
}


// Overlay // Overlay
makeOverlay() { makeOverlay() {
if(this.init) {
if (this.init) {
this.init = 0; this.init = 0;
return; return;
} }
if(this.overlayGuides) {
if (this.overlayGuides) {
this.overlayGuides.forEach(g => { this.overlayGuides.forEach(g => {
let o = g.overlay; let o = g.overlay;
o.parentNode.removeChild(o); o.parentNode.removeChild(o);
@@ -464,7 +469,7 @@ export default class AxisChart extends BaseChart {
}; };
}); });


if(this.state.currentIndex === undefined) {
if (this.state.currentIndex === undefined) {
this.state.currentIndex = this.state.datasetLength - 1; this.state.currentIndex = this.state.datasetLength - 1;
} }


@@ -478,7 +483,7 @@ export default class AxisChart extends BaseChart {
} }


updateOverlayGuides() { updateOverlayGuides() {
if(this.overlayGuides) {
if (this.overlayGuides) {
this.overlayGuides.forEach(g => { this.overlayGuides.forEach(g => {
let o = g.overlay; let o = g.overlay;
o.parentNode.removeChild(o); o.parentNode.removeChild(o);
@@ -524,7 +529,7 @@ export default class AxisChart extends BaseChart {
this.setCurrentDataPoint(this.state.currentIndex + 1); this.setCurrentDataPoint(this.state.currentIndex + 1);
} }


getDataPoint(index=this.state.currentIndex) {
getDataPoint(index = this.state.currentIndex) {
let s = this.state; let s = this.state;
let data_point = { let data_point = {
index: index, index: index,
@@ -537,9 +542,9 @@ export default class AxisChart extends BaseChart {
setCurrentDataPoint(index) { setCurrentDataPoint(index) {
let s = this.state; let s = this.state;
index = parseInt(index); index = parseInt(index);
if(index < 0) index = 0;
if(index >= s.xAxis.labels.length) index = s.xAxis.labels.length - 1;
if(index === s.currentIndex) return;
if (index < 0) index = 0;
if (index >= s.xAxis.labels.length) index = s.xAxis.labels.length - 1;
if (index === s.currentIndex) return;
s.currentIndex = index; s.currentIndex = index;
fire(this.parent, "data-select", this.getDataPoint()); fire(this.parent, "data-select", this.getDataPoint());
} }
@@ -547,7 +552,7 @@ export default class AxisChart extends BaseChart {




// API // API
addDataPoint(label, datasetValues, index=this.state.datasetLength) {
addDataPoint(label, datasetValues, index = this.state.datasetLength) {
super.addDataPoint(label, datasetValues, index); super.addDataPoint(label, datasetValues, index);
this.data.labels.splice(index, 0, label); this.data.labels.splice(index, 0, label);
this.data.datasets.map((d, i) => { this.data.datasets.map((d, i) => {
@@ -556,7 +561,7 @@ export default class AxisChart extends BaseChart {
this.update(this.data); this.update(this.data);
} }


removeDataPoint(index = this.state.datasetLength-1) {
removeDataPoint(index = this.state.datasetLength - 1) {
if (this.data.labels.length <= 1) { if (this.data.labels.length <= 1) {
return; return;
} }
@@ -568,7 +573,7 @@ export default class AxisChart extends BaseChart {
this.update(this.data); this.update(this.data);
} }


updateDataset(datasetValues, index=0) {
updateDataset(datasetValues, index = 0) {
this.data.datasets[index].values = datasetValues; this.data.datasets[index].values = datasetValues;
this.update(this.data); this.update(this.data);
} }
@@ -577,7 +582,7 @@ export default class AxisChart extends BaseChart {


updateDatasets(datasets) { updateDatasets(datasets) {
this.data.datasets.map((d, i) => { this.data.datasets.map((d, i) => {
if(datasets[i]) {
if (datasets[i]) {
d.values = datasets[i]; d.values = datasets[i];
} }
}); });


+ 82
- 49
src/js/charts/BaseChart.js Wyświetl plik

@@ -1,14 +1,19 @@
import SvgTip from '../objects/SvgTip'; import SvgTip from '../objects/SvgTip';
import { $, isElementInViewport, getElementContentWidth, isHidden } from '../utils/dom'; import { $, isElementInViewport, getElementContentWidth, isHidden } from '../utils/dom';
import { makeSVGContainer, makeSVGDefs, makeSVGGroup, makeText } from '../utils/draw'; import { makeSVGContainer, makeSVGDefs, makeSVGGroup, makeText } from '../utils/draw';
import { BASE_MEASURES, getExtraHeight, getExtraWidth, getTopOffset, getLeftOffset,
INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT, DEFAULT_COLORS} from '../utils/constants';
import { LEGEND_ITEM_WIDTH } from '../utils/constants';
import {
BASE_MEASURES, getExtraHeight, getExtraWidth, getTopOffset, getLeftOffset,
INIT_CHART_UPDATE_TIMEOUT, CHART_POST_ANIMATE_TIMEOUT, DEFAULT_COLORS
} from '../utils/constants';
import { getColor, isValidColor } from '../utils/colors'; import { getColor, isValidColor } from '../utils/colors';
import { runSMILAnimation } from '../utils/animation'; import { runSMILAnimation } from '../utils/animation';
import { downloadFile, prepareForExport } from '../utils/export'; import { downloadFile, prepareForExport } from '../utils/export';
import { deepClone } from '../utils/helpers';


export default class BaseChart { export default class BaseChart {
constructor(parent, options) { constructor(parent, options) {
options = deepClone(options);


this.parent = typeof parent === 'string' this.parent = typeof parent === 'string'
? document.querySelector(parent) ? document.querySelector(parent)
@@ -30,17 +35,18 @@ export default class BaseChart {


this.config = { this.config = {
showTooltip: 1, // calculate showTooltip: 1, // calculate
showLegend: 1, // calculate
showLegend: (typeof options.showLegend !== 'undefined') ? options.showLegend : 1,
isNavigable: options.isNavigable || 0, isNavigable: options.isNavigable || 0,
animate: (typeof options.animate !== 'undefined') ? options.animate : 1, animate: (typeof options.animate !== 'undefined') ? options.animate : 1,
disableEntryAnimation: options.disableEntryAnimation || 0,
truncateLegends: options.truncateLegends || 1 truncateLegends: options.truncateLegends || 1
}; };


this.measures = JSON.parse(JSON.stringify(BASE_MEASURES)); this.measures = JSON.parse(JSON.stringify(BASE_MEASURES));
let m = this.measures; let m = this.measures;
this.setMeasures(options); this.setMeasures(options);
if(!this.title.length) { m.titleHeight = 0; }
if(!this.config.showLegend) m.legendHeight = 0;
if (!this.title.length) { m.titleHeight = 0; }
if (!this.config.showLegend) m.legendHeight = 0;
this.argHeight = options.height || m.baseHeight; this.argHeight = options.height || m.baseHeight;


this.state = {}; this.state = {};
@@ -48,7 +54,7 @@ export default class BaseChart {


this.initTimeout = INIT_CHART_UPDATE_TIMEOUT; this.initTimeout = INIT_CHART_UPDATE_TIMEOUT;


if(this.config.isNavigable) {
if (this.config.isNavigable) {
this.overlays = []; this.overlays = [];
} }


@@ -68,7 +74,7 @@ export default class BaseChart {
colors = (colors || []).concat(DEFAULT_COLORS[type]); colors = (colors || []).concat(DEFAULT_COLORS[type]);
colors.forEach((string) => { colors.forEach((string) => {
const color = getColor(string); const color = getColor(string);
if(!isValidColor(color)) {
if (!isValidColor(color)) {
console.warn('"' + string + '" is not a valid color.'); console.warn('"' + string + '" is not a valid color.');
} else { } else {
validColors.push(color); validColors.push(color);
@@ -89,11 +95,16 @@ export default class BaseChart {


// Bind window events // Bind window events
this.boundDrawFn = () => this.draw(true); this.boundDrawFn = () => this.draw(true);
if (ResizeObserver) {
this.resizeObserver = new ResizeObserver(this.boundDrawFn);
this.resizeObserver.observe(this.parent);
}
window.addEventListener('resize', this.boundDrawFn); window.addEventListener('resize', this.boundDrawFn);
window.addEventListener('orientationchange', this.boundDrawFn); window.addEventListener('orientationchange', this.boundDrawFn);
} }


destroy() { destroy() {
if (this.resizeObserver) this.resizeObserver.disconnect();
window.removeEventListener('resize', this.boundDrawFn); window.removeEventListener('resize', this.boundDrawFn);
window.removeEventListener('orientationchange', this.boundDrawFn); window.removeEventListener('orientationchange', this.boundDrawFn);
} }
@@ -116,7 +127,7 @@ export default class BaseChart {
className: 'chart-container' className: 'chart-container'
}; };


if(this.independentWidth) {
if (this.independentWidth) {
args.styles = { width: this.independentWidth + 'px' }; args.styles = { width: this.independentWidth + 'px' };
} }


@@ -131,9 +142,9 @@ export default class BaseChart {
this.bindTooltip(); this.bindTooltip();
} }


bindTooltip() {}
bindTooltip() { }


draw(onlyWidthChange=false, init=false) {
draw(onlyWidthChange = false, init = false) {
if (onlyWidthChange && isHidden(this.parent)) { if (onlyWidthChange && isHidden(this.parent)) {
// Don't update anything if the chart is hidden // Don't update anything if the chart is hidden
return; return;
@@ -148,17 +159,19 @@ export default class BaseChart {
// this.components.forEach(c => c.make()); // this.components.forEach(c => c.make());
this.render(this.components, false); this.render(this.components, false);


if(init) {
if (init) {
this.data = this.realData; this.data = this.realData;
setTimeout(() => {this.update(this.data);}, this.initTimeout);
setTimeout(() => { this.update(this.data, true); }, this.initTimeout);
}
if (this.config.showLegend) {
this.renderLegend();
} }

this.renderLegend();


this.setupNavigation(init); this.setupNavigation(init);
} }


calc() {} // builds state
calc() { } // builds state


updateWidth() { updateWidth() {
this.baseWidth = getElementContentWidth(this.parent); this.baseWidth = getElementContentWidth(this.parent);
@@ -166,7 +179,7 @@ export default class BaseChart {
} }


makeChartArea() { makeChartArea() {
if(this.svg) {
if (this.svg) {
this.container.removeChild(this.svg); this.container.removeChild(this.svg);
} }
let m = this.measures; let m = this.measures;
@@ -179,7 +192,7 @@ export default class BaseChart {
); );
this.svgDefs = makeSVGDefs(this.svg); this.svgDefs = makeSVGDefs(this.svg);


if(this.title.length) {
if (this.title.length) {
this.titleEL = makeText( this.titleEL = makeText(
'title', 'title',
m.margins.left, m.margins.left,
@@ -199,7 +212,7 @@ export default class BaseChart {
`translate(${getLeftOffset(m)}, ${top})` `translate(${getLeftOffset(m)}, ${top})`
); );


if(this.config.showLegend) {
if (this.config.showLegend) {
top += this.height + m.paddings.bottom; top += this.height + m.paddings.bottom;
this.legendArea = makeSVGGroup( this.legendArea = makeSVGGroup(
'chart-legend', 'chart-legend',
@@ -207,9 +220,9 @@ export default class BaseChart {
); );
} }


if(this.title.length) { this.svg.appendChild(this.titleEL); }
if (this.title.length) { this.svg.appendChild(this.titleEL); }
this.svg.appendChild(this.drawArea); this.svg.appendChild(this.drawArea);
if(this.config.showLegend) { this.svg.appendChild(this.legendArea); }
if (this.config.showLegend) { this.svg.appendChild(this.legendArea); }


this.updateTipOffset(getLeftOffset(m), getTopOffset(m)); this.updateTipOffset(getLeftOffset(m), getTopOffset(m));
} }
@@ -223,17 +236,18 @@ export default class BaseChart {


setupComponents() { this.components = new Map(); } setupComponents() { this.components = new Map(); }


update(data) {
if(!data) {
console.error('No data to update.');
}
update(data, drawing = false) {
if (!data) console.error('No data to update.');
if (!drawing) data = deepClone(data);
const animate = drawing ? !this.config.disableEntryAnimation : this.config.animate;
this.data = this.prepareData(data); this.data = this.prepareData(data);
this.calc(); // builds state this.calc(); // builds state
this.render(this.components, this.config.animate);
this.render(this.components, animate);
} }


render(components=this.components, animate=true) {
if(this.config.isNavigable) {
render(components = this.components, animate = true) {
if (this.config.isNavigable) {
// Remove all existing overlays // Remove all existing overlays
this.overlays.map(o => o.parentNode.removeChild(o)); this.overlays.map(o => o.parentNode.removeChild(o));
// ref.parentNode.insertBefore(element, ref); // ref.parentNode.insertBefore(element, ref);
@@ -243,7 +257,7 @@ export default class BaseChart {
components.forEach(c => { components.forEach(c => {
elementsToAnimate = elementsToAnimate.concat(c.update(animate)); elementsToAnimate = elementsToAnimate.concat(c.update(animate));
}); });
if(elementsToAnimate.length > 0) {
if (elementsToAnimate.length > 0) {
runSMILAnimation(this.container, this.svg, elementsToAnimate); runSMILAnimation(this.container, this.svg, elementsToAnimate);
setTimeout(() => { setTimeout(() => {
components.forEach(c => c.make()); components.forEach(c => c.make());
@@ -256,18 +270,37 @@ export default class BaseChart {
} }


updateNav() { updateNav() {
if(this.config.isNavigable) {
if (this.config.isNavigable) {
this.makeOverlay(); this.makeOverlay();
this.bindUnits(); this.bindUnits();
} }
} }


renderLegend() {}
renderLegend(dataset) {
this.legendArea.textContent = '';
let count = 0;
let y = 0;

dataset.map((data, index) => {
let divisor = Math.floor(this.width / LEGEND_ITEM_WIDTH);
if (count > divisor) {
count = 0;
y += this.config.legendRowHeight;
}
let x = LEGEND_ITEM_WIDTH * count;
let dot = this.makeLegend(data, index, x, y);
this.legendArea.appendChild(dot);
count++;
});
}

makeLegend() { }



setupNavigation(init=false) {
if(!this.config.isNavigable) return;
setupNavigation(init = false) {
if (!this.config.isNavigable) return;


if(init) {
if (init) {
this.bindOverlay(); this.bindOverlay();


this.keyActions = { this.keyActions = {
@@ -279,9 +312,9 @@ export default class BaseChart {
}; };


document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if(isElementInViewport(this.container)) {
if (isElementInViewport(this.container)) {
e = e || window.event; e = e || window.event;
if(this.keyActions[e.keyCode]) {
if (this.keyActions[e.keyCode]) {
this.keyActions[e.keyCode](); this.keyActions[e.keyCode]();
} }
} }
@@ -289,24 +322,24 @@ export default class BaseChart {
} }
} }


makeOverlay() {}
updateOverlay() {}
bindOverlay() {}
bindUnits() {}
makeOverlay() { }
updateOverlay() { }
bindOverlay() { }
bindUnits() { }


onLeftArrow() {}
onRightArrow() {}
onUpArrow() {}
onDownArrow() {}
onEnterKey() {}
onLeftArrow() { }
onRightArrow() { }
onUpArrow() { }
onDownArrow() { }
onEnterKey() { }


addDataPoint() {}
removeDataPoint() {}
addDataPoint() { }
removeDataPoint() { }


getDataPoint() {}
setCurrentDataPoint() {}
getDataPoint() { }
setCurrentDataPoint() { }


updateDataset() {}
updateDataset() { }


export() { export() {
let chartSvg = prepareForExport(this.svg); let chartSvg = prepareForExport(this.svg);


+ 19
- 122
src/js/charts/DonutChart.js Wyświetl plik

@@ -1,86 +1,35 @@
import AggregationChart from './AggregationChart';
import PieChart from './PieChart';
import { getComponent } from '../objects/ChartComponents'; import { getComponent } from '../objects/ChartComponents';
import { getOffset } from '../utils/dom';
import { getPositionByAngle } from '../utils/helpers';
import { makeArcStrokePathStr, makeStrokeCircleStr } from '../utils/draw'; import { makeArcStrokePathStr, makeStrokeCircleStr } from '../utils/draw';
import { lightenDarkenColor } from '../utils/colors';
import { transform } from '../utils/animation'; import { transform } from '../utils/animation';
import { FULL_ANGLE } from '../utils/constants';


export default class DonutChart extends AggregationChart {
export default class DonutChart extends PieChart {
constructor(parent, args) { constructor(parent, args) {
super(parent, args); super(parent, args);
this.type = 'donut';
this.initTimeout = 0;
this.init = 1;

this.setup();
} }


configure(args) { configure(args) {
super.configure(args); super.configure(args);
this.mouseMove = this.mouseMove.bind(this);
this.mouseLeave = this.mouseLeave.bind(this);


this.hoverRadio = args.hoverRadio || 0.1;
this.config.startAngle = args.startAngle || 0;
this.type = 'donut';
this.sliceName = 'donutSlices';

this.arcFunc = makeArcStrokePathStr;
this.shapeFunc = makeStrokeCircleStr;


this.clockWise = args.clockWise || false;
this.strokeWidth = args.strokeWidth || 30; this.strokeWidth = args.strokeWidth || 30;
} }


calc() {
super.calc();
let s = this.state;
this.radius =
this.height > this.width
? this.center.x - this.strokeWidth / 2
: this.center.y - this.strokeWidth / 2;

const { radius, clockWise } = this;

const prevSlicesProperties = s.slicesProperties || [];
s.sliceStrings = [];
s.slicesProperties = [];
let curAngle = 180 - this.config.startAngle;

s.sliceTotals.map((total, i) => {
const startAngle = curAngle;
const originDiffAngle = (total / s.grandTotal) * FULL_ANGLE;
const largeArc = originDiffAngle > 180 ? 1: 0;
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
const endAngle = curAngle = curAngle + diffAngle;
const startPosition = getPositionByAngle(startAngle, radius);
const endPosition = getPositionByAngle(endAngle, radius);

const prevProperty = this.init && prevSlicesProperties[i];

let curStart,curEnd;
if(this.init) {
curStart = prevProperty ? prevProperty.startPosition : startPosition;
curEnd = prevProperty ? prevProperty.endPosition : startPosition;
} else {
curStart = startPosition;
curEnd = endPosition;
}
const curPath =
originDiffAngle === 360
? makeStrokeCircleStr(curStart, curEnd, this.center, this.radius, this.clockWise, largeArc)
: makeArcStrokePathStr(curStart, curEnd, this.center, this.radius, this.clockWise, largeArc);

s.sliceStrings.push(curPath);
s.slicesProperties.push({
startPosition,
endPosition,
value: total,
total: s.grandTotal,
startAngle,
endAngle,
angle: diffAngle
});
getRadius() {
return this.height > this.width
? this.center.x - this.strokeWidth / 2
: this.center.y - this.strokeWidth / 2;
}


});
this.init = 0;
resetHover(path, color) {
transform(path,'translate3d(0,0,0)');
this.tip.hideTip();
path.style.stroke = color;
} }


setupComponents() { setupComponents() {
@@ -88,9 +37,9 @@ export default class DonutChart extends AggregationChart {


let componentConfigs = [ let componentConfigs = [
[ [
'donutSlices',
{ },
function() {
this.sliceName,
{},
function () {
return { return {
sliceStrings: s.sliceStrings, sliceStrings: s.sliceStrings,
colors: this.colors, colors: this.colors,
@@ -106,56 +55,4 @@ export default class DonutChart extends AggregationChart {
return [args[0], component]; return [args[0], component];
})); }));
} }

calTranslateByAngle(property){
const{ radius, hoverRadio } = this;
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
}

hoverSlice(path,i,flag,e){
if(!path) return;
const color = this.colors[i];
if(flag) {
transform(path, this.calTranslateByAngle(this.state.slicesProperties[i]));
path.style.stroke = lightenDarkenColor(color, 50);
let g_off = getOffset(this.svg);
let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10;
let title = (this.formatted_labels && this.formatted_labels.length > 0
? this.formatted_labels[i] : this.state.labels[i]) + ': ';
let percent = (this.state.sliceTotals[i] * 100 / this.state.grandTotal).toFixed(1);
this.tip.setValues(x, y, {name: title, value: percent + "%"});
this.tip.showTip();
} else {
transform(path,'translate3d(0,0,0)');
this.tip.hideTip();
path.style.stroke = color;
}
}

bindTooltip() {
this.container.addEventListener('mousemove', this.mouseMove);
this.container.addEventListener('mouseleave', this.mouseLeave);
}

mouseMove(e){
const target = e.target;
let slices = this.components.get('donutSlices').store;
let prevIndex = this.curActiveSliceIndex;
let prevAcitve = this.curActiveSlice;
if(slices.includes(target)) {
let i = slices.indexOf(target);
this.hoverSlice(prevAcitve, prevIndex,false);
this.curActiveSlice = target;
this.curActiveSliceIndex = i;
this.hoverSlice(target, i, true, e);
} else {
this.mouseLeave();
}
}

mouseLeave(){
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
}
} }

+ 31
- 27
src/js/charts/Heatmap.js Wyświetl plik

@@ -1,11 +1,15 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { getComponent } from '../objects/ChartComponents'; import { getComponent } from '../objects/ChartComponents';
import { makeText, heatSquare } from '../utils/draw'; import { makeText, heatSquare } from '../utils/draw';
import { DAY_NAMES_SHORT, addDays, areInSameMonth, getLastDateInMonth, setDayToSunday, getYyyyMmDd, getWeeksBetween, getMonthName, clone,
NO_OF_MILLIS, NO_OF_YEAR_MONTHS, NO_OF_DAYS_IN_WEEK } from '../utils/date-utils';
import {
DAY_NAMES_SHORT, addDays, areInSameMonth, getLastDateInMonth, setDayToSunday, getYyyyMmDd, getWeeksBetween, getMonthName, clone,
NO_OF_MILLIS, NO_OF_YEAR_MONTHS, NO_OF_DAYS_IN_WEEK
} from '../utils/date-utils';
import { calcDistribution, getMaxCheckpoint } from '../utils/intervals'; import { calcDistribution, getMaxCheckpoint } from '../utils/intervals';
import { getExtraHeight, getExtraWidth, HEATMAP_DISTRIBUTION_SIZE, HEATMAP_SQUARE_SIZE,
HEATMAP_GUTTER_SIZE } from '../utils/constants';
import {
getExtraHeight, getExtraWidth, HEATMAP_DISTRIBUTION_SIZE, HEATMAP_SQUARE_SIZE,
HEATMAP_GUTTER_SIZE
} from '../utils/constants';


const COL_WIDTH = HEATMAP_SQUARE_SIZE + HEATMAP_GUTTER_SIZE; const COL_WIDTH = HEATMAP_SQUARE_SIZE + HEATMAP_GUTTER_SIZE;
const ROW_HEIGHT = COL_WIDTH; const ROW_HEIGHT = COL_WIDTH;
@@ -49,19 +53,19 @@ export default class Heatmap extends BaseChart {
+ getExtraWidth(this.measures); + getExtraWidth(this.measures);
} }


prepareData(data=this.data) {
if(data.start && data.end && data.start > data.end) {
prepareData(data = this.data) {
if (data.start && data.end && data.start > data.end) {
throw new Error('Start date cannot be greater than end date.'); throw new Error('Start date cannot be greater than end date.');
} }


if(!data.start) {
if (!data.start) {
data.start = new Date(); data.start = new Date();
data.start.setFullYear( data.start.getFullYear() - 1 );
data.start.setFullYear(data.start.getFullYear() - 1);
} }
if(!data.end) { data.end = new Date(); }
if (!data.end) { data.end = new Date(); }
data.dataPoints = data.dataPoints || {}; data.dataPoints = data.dataPoints || {};


if(parseInt(Object.keys(data.dataPoints)[0]) > 100000) {
if (parseInt(Object.keys(data.dataPoints)[0]) > 100000) {
let points = {}; let points = {};
Object.keys(data.dataPoints).forEach(timestampSec => { Object.keys(data.dataPoints).forEach(timestampSec => {
let date = new Date(timestampSec * NO_OF_MILLIS); let date = new Date(timestampSec * NO_OF_MILLIS);
@@ -105,7 +109,7 @@ export default class Heatmap extends BaseChart {
.reduce((a, b) => a + b, 0) .reduce((a, b) => a + b, 0)
* COL_WIDTH * COL_WIDTH
}, },
function() {
function () {
return s.domainConfigs[i]; return s.domainConfigs[i];
}.bind(this) }.bind(this)


@@ -120,8 +124,8 @@ export default class Heatmap extends BaseChart {


let y = 0; let y = 0;
DAY_NAMES_SHORT.forEach((dayName, i) => { DAY_NAMES_SHORT.forEach((dayName, i) => {
if([1, 3, 5].includes(i)) {
let dayText = makeText('subdomain-name', -COL_WIDTH/2, y, dayName,
if ([1, 3, 5].includes(i)) {
let dayText = makeText('subdomain-name', -COL_WIDTH / 2, y, dayName,
{ {
fontSize: HEATMAP_SQUARE_SIZE, fontSize: HEATMAP_SQUARE_SIZE,
dy: 8, dy: 8,
@@ -135,7 +139,7 @@ export default class Heatmap extends BaseChart {
} }


update(data) { update(data) {
if(!data) {
if (!data) {
console.error('No data to update.'); console.error('No data to update.');
} }


@@ -149,22 +153,22 @@ export default class Heatmap extends BaseChart {
this.components.forEach(comp => { this.components.forEach(comp => {
let daySquares = comp.store; let daySquares = comp.store;
let daySquare = e.target; let daySquare = e.target;
if(daySquares.includes(daySquare)) {
if (daySquares.includes(daySquare)) {


let count = daySquare.getAttribute('data-value'); let count = daySquare.getAttribute('data-value');
let dateParts = daySquare.getAttribute('data-date').split('-'); let dateParts = daySquare.getAttribute('data-date').split('-');


let month = getMonthName(parseInt(dateParts[1])-1, true);
let month = getMonthName(parseInt(dateParts[1]) - 1, true);


let gOff = this.container.getBoundingClientRect(), pOff = daySquare.getBoundingClientRect(); let gOff = this.container.getBoundingClientRect(), pOff = daySquare.getBoundingClientRect();


let width = parseInt(e.target.getAttribute('width')); let width = parseInt(e.target.getAttribute('width'));
let x = pOff.left - gOff.left + width/2;
let x = pOff.left - gOff.left + width / 2;
let y = pOff.top - gOff.top; let y = pOff.top - gOff.top;
let value = count + ' ' + this.countLabel; let value = count + ' ' + this.countLabel;
let name = ' on ' + month + ' ' + dateParts[0] + ', ' + dateParts[2]; let name = ' on ' + month + ' ' + dateParts[0] + ', ' + dateParts[2];


this.tip.setValues(x, y, {name: name, value: value, valueFirst: 1}, []);
this.tip.setValues(x, y, { name: name, value: value, valueFirst: 1 }, []);
this.tip.showTip(); this.tip.showTip();
} }
}); });
@@ -183,7 +187,7 @@ export default class Heatmap extends BaseChart {
dy: 9 dy: 9
} }
); );
x = (COL_WIDTH * 2) + COL_WIDTH/2;
x = (COL_WIDTH * 2) + COL_WIDTH / 2;
this.legendArea.appendChild(lessText); this.legendArea.appendChild(lessText);


this.colors.slice(0, HEATMAP_DISTRIBUTION_SIZE).map((color, i) => { this.colors.slice(0, HEATMAP_DISTRIBUTION_SIZE).map((color, i) => {
@@ -192,7 +196,7 @@ export default class Heatmap extends BaseChart {
this.legendArea.appendChild(square); this.legendArea.appendChild(square);
}); });


let moreTextX = x + HEATMAP_DISTRIBUTION_SIZE * (COL_WIDTH + 3) + COL_WIDTH/4;
let moreTextX = x + HEATMAP_DISTRIBUTION_SIZE * (COL_WIDTH + 3) + COL_WIDTH / 4;
let moreText = makeText('subdomain-name', moreTextX, y, 'More', let moreText = makeText('subdomain-name', moreTextX, y, 'More',
{ {
fontSize: HEATMAP_SQUARE_SIZE + 1, fontSize: HEATMAP_SQUARE_SIZE + 1,
@@ -212,9 +216,9 @@ export default class Heatmap extends BaseChart {
let domainConfigs = []; let domainConfigs = [];


let startOfMonth = clone(s.start); let startOfMonth = clone(s.start);
for(var i = 0; i < noOfMonths; i++) {
for (var i = 0; i < noOfMonths; i++) {
let endDate = s.end; let endDate = s.end;
if(!areInSameMonth(startOfMonth, s.end)) {
if (!areInSameMonth(startOfMonth, s.end)) {
let [month, year] = [startOfMonth.getMonth(), startOfMonth.getFullYear()]; let [month, year] = [startOfMonth.getMonth(), startOfMonth.getFullYear()];
endDate = getLastDateInMonth(month, year); endDate = getLastDateInMonth(month, year);
} }
@@ -227,7 +231,7 @@ export default class Heatmap extends BaseChart {
return domainConfigs; return domainConfigs;
} }


getDomainConfig(startDate, endDate='') {
getDomainConfig(startDate, endDate = '') {
let [month, year] = [startDate.getMonth(), startDate.getFullYear()]; let [month, year] = [startDate.getMonth(), startDate.getFullYear()];
let startOfWeek = setDayToSunday(startDate); // TODO: Monday as well let startOfWeek = setDayToSunday(startDate); // TODO: Monday as well
endDate = clone(endDate) || getLastDateInMonth(month, year); endDate = clone(endDate) || getLastDateInMonth(month, year);
@@ -241,7 +245,7 @@ export default class Heatmap extends BaseChart {
let noOfMonthWeeks = getWeeksBetween(startOfWeek, endDate); let noOfMonthWeeks = getWeeksBetween(startOfWeek, endDate);


let cols = [], col; let cols = [], col;
for(var i = 0; i < noOfMonthWeeks; i++) {
for (var i = 0; i < noOfMonthWeeks; i++) {
col = this.getCol(startOfWeek, month); col = this.getCol(startOfWeek, month);
cols.push(col); cols.push(col);


@@ -249,7 +253,7 @@ export default class Heatmap extends BaseChart {
addDays(startOfWeek, 1); addDays(startOfWeek, 1);
} }


if(col[NO_OF_DAYS_IN_WEEK - 1].dataValue !== undefined) {
if (col[NO_OF_DAYS_IN_WEEK - 1].dataValue !== undefined) {
addDays(startOfWeek, 1); addDays(startOfWeek, 1);
cols.push(this.getCol(startOfWeek, month, true)); cols.push(this.getCol(startOfWeek, month, true));
} }
@@ -266,13 +270,13 @@ export default class Heatmap extends BaseChart {
let currentDate = clone(startDate); let currentDate = clone(startDate);
let col = []; let col = [];


for(var i = 0; i < NO_OF_DAYS_IN_WEEK; i++, addDays(currentDate, 1)) {
for (var i = 0; i < NO_OF_DAYS_IN_WEEK; i++, addDays(currentDate, 1)) {
let config = {}; let config = {};


// Non-generic adjustment for entire heatmap, needs state // Non-generic adjustment for entire heatmap, needs state
let currentDateWithinData = currentDate >= s.start && currentDate <= s.end; let currentDateWithinData = currentDate >= s.start && currentDate <= s.end;


if(empty || currentDate.getMonth() !== month || !currentDateWithinData) {
if (empty || currentDate.getMonth() !== month || !currentDateWithinData) {
config.yyyyMmDd = getYyyyMmDd(currentDate); config.yyyyMmDd = getYyyyMmDd(currentDate);
} else { } else {
config = this.getSubDomainConfig(currentDate); config = this.getSubDomainConfig(currentDate);


+ 0
- 173
src/js/charts/MultiAxisChart.js Wyświetl plik

@@ -1,173 +0,0 @@
import AxisChart from './AxisChart';
import { Y_AXIS_MARGIN } from '../utils/constants';
// import { ChartComponent } from '../objects/ChartComponents';
import { floatTwo } from '../utils/helpers';

export default class MultiAxisChart extends AxisChart {
constructor(args) {
super(args);
// this.unitType = args.unitType || 'line';
// this.setup();
}

preSetup() {
this.type = 'multiaxis';
}

setMeasures() {
super.setMeasures();
let noOfLeftAxes = this.data.datasets.filter(d => d.axisPosition === 'left').length;
this.measures.margins.left = (noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
this.measures.margins.right = (this.data.datasets.length - noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
}

prepareYAxis() { }

prepareData(data) {
super.prepareData(data);
let sets = this.state.datasets;
// let axesLeft = sets.filter(d => d.axisPosition === 'left');
// let axesRight = sets.filter(d => d.axisPosition === 'right');
// let axesNone = sets.filter(d => !d.axisPosition ||
// !['left', 'right'].includes(d.axisPosition));

let leftCount = 0, rightCount = 0;

sets.forEach((d, i) => {
d.yAxis = {
position: d.axisPosition,
index: d.axisPosition === 'left' ? leftCount++ : rightCount++
};
});
}

configure(args) {
super.configure(args);
this.config.xAxisMode = args.xAxisMode || 'tick';
this.config.yAxisMode = args.yAxisMode || 'span';
}

// setUnitWidthAndXOffset() {
// this.state.unitWidth = this.width/(this.state.datasetLength);
// this.state.xOffset = this.state.unitWidth/2;
// }

configUnits() {
this.unitArgs = {
type: 'bar',
args: {
spaceWidth: this.state.unitWidth/2,
}
};
}

setYAxis() {
this.state.datasets.map(d => {
this.calcYAxisParameters(d.yAxis, d.values, this.unitType === 'line');
});
}

calcYUnits() {
this.state.datasets.map(d => {
d.positions = d.values.map(val => floatTwo(d.yAxis.zeroLine - val * d.yAxis.scaleMultiplier));
});
}

// TODO: function doesn't exist, handle with components
renderConstants() {
this.state.datasets.map(d => {
let guidePos = d.yAxis.position === 'left'
? -1 * d.yAxis.index * Y_AXIS_MARGIN
: this.width + d.yAxis.index * Y_AXIS_MARGIN;
this.renderer.xLine(guidePos, '', {
pos:'top',
mode: 'span',
stroke: this.colors[i],
className: 'y-axis-guide'
})
});
}

getYAxesComponents() {
return this.data.datasets.map((e, i) => {
return new ChartComponent({
layerClass: 'y axis y-axis-' + i,
make: () => {
let yAxis = this.state.datasets[i].yAxis;
this.renderer.setZeroline(yAxis.zeroline);
let options = {
pos: yAxis.position,
mode: 'tick',
offset: yAxis.index * Y_AXIS_MARGIN,
stroke: this.colors[i]
};

return yAxis.positions.map((position, j) =>
this.renderer.yLine(position, yAxis.labels[j], options)
);
},
animate: () => {}
});
});
}

// TODO remove renderer zeroline from above and below
getChartComponents() {
return this.data.datasets.map((d, index) => {
return new ChartComponent({
layerClass: 'dataset-units dataset-' + index,
make: () => {
let d = this.state.datasets[index];
let unitType = this.unitArgs;

// the only difference, should be tied to datasets or default
this.renderer.setZeroline(d.yAxis.zeroLine);

return d.positions.map((y, j) => {
return this.renderer[unitType.type](
this.state.xAxisPositions[j],
y,
unitType.args,
this.colors[index],
j,
index,
this.state.datasetLength
);
});
},
animate: (svgUnits) => {
let d = this.state.datasets[index];
let unitType = this.unitArgs.type;

// have been updated in axis render;
let newX = this.state.xAxisPositions;
let newY = this.state.datasets[index].positions;

let lastUnit = svgUnits[svgUnits.length - 1];
let parentNode = lastUnit.parentNode;

if(this.oldState.xExtra > 0) {
for(var i = 0; i<this.oldState.xExtra; i++) {
let unit = lastUnit.cloneNode(true);
parentNode.appendChild(unit);
svgUnits.push(unit);
}
}

this.renderer.setZeroline(d.yAxis.zeroLine);

svgUnits.map((unit, i) => {
if(newX[i] === undefined || newY[i] === undefined) return;
this.elementsToAnimate.push(this.renderer['animate' + unitType](
unit, // unit, with info to replace where it came from in the data
newX[i],
newY[i],
index,
this.state.noOfDatasets
));
});
}
});
});
}
}

+ 14
- 12
src/js/charts/PercentageChart.js Wyświetl plik

@@ -1,7 +1,7 @@
import AggregationChart from './AggregationChart'; import AggregationChart from './AggregationChart';
import { getOffset } from '../utils/dom'; import { getOffset } from '../utils/dom';
import { getComponent } from '../objects/ChartComponents'; import { getComponent } from '../objects/ChartComponents';
import { PERCENTAGE_BAR_DEFAULT_HEIGHT, PERCENTAGE_BAR_DEFAULT_DEPTH } from '../utils/constants';
import { PERCENTAGE_BAR_DEFAULT_HEIGHT, getExtraHeight } from '../utils/constants';


export default class PercentageChart extends AggregationChart { export default class PercentageChart extends AggregationChart {
constructor(parent, args) { constructor(parent, args) {
@@ -16,11 +16,13 @@ export default class PercentageChart extends AggregationChart {


let b = this.barOptions; let b = this.barOptions;
b.height = b.height || PERCENTAGE_BAR_DEFAULT_HEIGHT; b.height = b.height || PERCENTAGE_BAR_DEFAULT_HEIGHT;
b.depth = b.depth || PERCENTAGE_BAR_DEFAULT_DEPTH;


m.paddings.right = 30; m.paddings.right = 30;
m.legendHeight = 60;
m.baseHeight = (b.height + b.depth * 0.5) * 8;
m.paddings.top = 60;
m.paddings.bottom = 0;

m.legendHeight = 80;
m.baseHeight = (b.height) * 8 + getExtraHeight(m);
} }


setupComponents() { setupComponents() {
@@ -31,9 +33,8 @@ export default class PercentageChart extends AggregationChart {
'percentageBars', 'percentageBars',
{ {
barHeight: this.barOptions.height, barHeight: this.barOptions.height,
barDepth: this.barOptions.depth,
}, },
function() {
function () {
return { return {
xPositions: s.xPositions, xPositions: s.xPositions,
widths: s.widths, widths: s.widths,
@@ -73,18 +74,19 @@ export default class PercentageChart extends AggregationChart {
this.container.addEventListener('mousemove', (e) => { this.container.addEventListener('mousemove', (e) => {
let bars = this.components.get('percentageBars').store; let bars = this.components.get('percentageBars').store;
let bar = e.target; let bar = e.target;
if(bars.includes(bar)) {

if (bars.includes(bar)) {
let i = bars.indexOf(bar); let i = bars.indexOf(bar);
let gOff = getOffset(this.container), pOff = getOffset(bar); let gOff = getOffset(this.container), pOff = getOffset(bar);


let x = pOff.left - gOff.left + parseInt(bar.getAttribute('width'))/2;
let width = bar.getAttribute('width') || bar.getBoundingClientRect().width;

let x = pOff.left - gOff.left + parseInt(width) / 2;
let y = pOff.top - gOff.top; let y = pOff.top - gOff.top;
let title = (this.formattedLabels && this.formattedLabels.length>0
let title = (this.formattedLabels && this.formattedLabels.length > 0
? this.formattedLabels[i] : this.state.labels[i]) + ': '; ? this.formattedLabels[i] : this.state.labels[i]) + ': ';
let fraction = s.sliceTotals[i]/s.grandTotal;
let fraction = s.sliceTotals[i] / s.grandTotal;


this.tip.setValues(x, y, {name: title, value: (fraction*100).toFixed(1) + "%"});
this.tip.setValues(x, y, { name: title, value: (fraction * 100).toFixed(1) + "%" });
this.tip.showTip(); this.tip.showTip();
} }
}); });


+ 41
- 28
src/js/charts/PieChart.js Wyświetl plik

@@ -3,14 +3,12 @@ import { getComponent } from '../objects/ChartComponents';
import { getOffset } from '../utils/dom'; import { getOffset } from '../utils/dom';
import { getPositionByAngle } from '../utils/helpers'; import { getPositionByAngle } from '../utils/helpers';
import { makeArcPathStr, makeCircleStr } from '../utils/draw'; import { makeArcPathStr, makeCircleStr } from '../utils/draw';
import { lightenDarkenColor } from '../utils/colors';
import { transform } from '../utils/animation'; import { transform } from '../utils/animation';
import { FULL_ANGLE } from '../utils/constants'; import { FULL_ANGLE } from '../utils/constants';


export default class PieChart extends AggregationChart { export default class PieChart extends AggregationChart {
constructor(parent, args) { constructor(parent, args) {
super(parent, args); super(parent, args);
this.type = 'pie';
this.initTimeout = 0; this.initTimeout = 0;
this.init = 1; this.init = 1;


@@ -25,13 +23,23 @@ export default class PieChart extends AggregationChart {
this.hoverRadio = args.hoverRadio || 0.1; this.hoverRadio = args.hoverRadio || 0.1;
this.config.startAngle = args.startAngle || 0; this.config.startAngle = args.startAngle || 0;


this.type = 'pie';
this.sliceName = 'pieSlices';

this.arcFunc = makeArcPathStr;
this.shapeFunc = makeCircleStr;

this.clockWise = args.clockWise || false; this.clockWise = args.clockWise || false;
} }


getRadius() {
return this.height > this.width ? this.center.x : this.center.y;
}

calc() { calc() {
super.calc(); super.calc();
let s = this.state; let s = this.state;
this.radius = (this.height > this.width ? this.center.x : this.center.y);
this.radius = this.getRadius();


const { radius, clockWise } = this; const { radius, clockWise } = this;


@@ -42,7 +50,7 @@ export default class PieChart extends AggregationChart {
s.sliceTotals.map((total, i) => { s.sliceTotals.map((total, i) => {
const startAngle = curAngle; const startAngle = curAngle;
const originDiffAngle = (total / s.grandTotal) * FULL_ANGLE; const originDiffAngle = (total / s.grandTotal) * FULL_ANGLE;
const largeArc = originDiffAngle > 180 ? 1: 0;
const largeArc = originDiffAngle > 180 ? 1 : 0;
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle; const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
const endAngle = curAngle = curAngle + diffAngle; const endAngle = curAngle = curAngle + diffAngle;
const startPosition = getPositionByAngle(startAngle, radius); const startPosition = getPositionByAngle(startAngle, radius);
@@ -50,8 +58,8 @@ export default class PieChart extends AggregationChart {


const prevProperty = this.init && prevSlicesProperties[i]; const prevProperty = this.init && prevSlicesProperties[i];


let curStart,curEnd;
if(this.init) {
let curStart, curEnd;
if (this.init) {
curStart = prevProperty ? prevProperty.startPosition : startPosition; curStart = prevProperty ? prevProperty.startPosition : startPosition;
curEnd = prevProperty ? prevProperty.endPosition : startPosition; curEnd = prevProperty ? prevProperty.endPosition : startPosition;
} else { } else {
@@ -60,8 +68,8 @@ export default class PieChart extends AggregationChart {
} }
const curPath = const curPath =
originDiffAngle === 360 originDiffAngle === 360
? makeCircleStr(curStart, curEnd, this.center, this.radius, clockWise, largeArc)
: makeArcPathStr(curStart, curEnd, this.center, this.radius, clockWise, largeArc);
? this.shapeFunc(curStart, curEnd, this.center, this.radius, clockWise, largeArc)
: this.arcFunc(curStart, curEnd, this.center, this.radius, clockWise, largeArc);


s.sliceStrings.push(curPath); s.sliceStrings.push(curPath);
s.slicesProperties.push({ s.slicesProperties.push({
@@ -84,8 +92,8 @@ export default class PieChart extends AggregationChart {
let componentConfigs = [ let componentConfigs = [
[ [
'pieSlices', 'pieSlices',
{ },
function() {
{},
function () {
return { return {
sliceStrings: s.sliceStrings, sliceStrings: s.sliceStrings,
colors: this.colors colors: this.colors
@@ -101,46 +109,51 @@ export default class PieChart extends AggregationChart {
})); }));
} }


calTranslateByAngle(property){
const{radius,hoverRadio} = this;
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
calTranslateByAngle(property) {
const { radius, hoverRadio } = this;
const position = getPositionByAngle(property.startAngle + (property.angle / 2), radius);
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`; return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
} }


hoverSlice(path,i,flag,e){
if(!path) return;
hoverSlice(path, i, flag, e) {
if (!path) return;
const color = this.colors[i]; const color = this.colors[i];
if(flag) {
if (flag) {
transform(path, this.calTranslateByAngle(this.state.slicesProperties[i])); transform(path, this.calTranslateByAngle(this.state.slicesProperties[i]));
path.style.fill = lightenDarkenColor(color, 50);
// path.style.fill = lightenDarkenColor(color, 50);
// path.style.stroke = lightenDarkenColor(color, 50);
let g_off = getOffset(this.svg); let g_off = getOffset(this.svg);
let x = e.pageX - g_off.left + 10; let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10; let y = e.pageY - g_off.top - 10;
let title = (this.formatted_labels && this.formatted_labels.length > 0 let title = (this.formatted_labels && this.formatted_labels.length > 0
? this.formatted_labels[i] : this.state.labels[i]) + ': '; ? this.formatted_labels[i] : this.state.labels[i]) + ': ';
let percent = (this.state.sliceTotals[i] * 100 / this.state.grandTotal).toFixed(1); let percent = (this.state.sliceTotals[i] * 100 / this.state.grandTotal).toFixed(1);
this.tip.setValues(x, y, {name: title, value: percent + "%"});
this.tip.setValues(x, y, { name: title, value: percent + "%" });
this.tip.showTip(); this.tip.showTip();
} else { } else {
transform(path,'translate3d(0,0,0)');
this.tip.hideTip();
path.style.fill = color;
this.resetHover(path, color)
} }
} }


resetHover(path, color) {
transform(path, 'translate3d(0,0,0)');
this.tip.hideTip();
path.style.fill = color;
}

bindTooltip() { bindTooltip() {
this.container.addEventListener('mousemove', this.mouseMove); this.container.addEventListener('mousemove', this.mouseMove);
this.container.addEventListener('mouseleave', this.mouseLeave); this.container.addEventListener('mouseleave', this.mouseLeave);
} }


mouseMove(e){
mouseMove(e) {
const target = e.target; const target = e.target;
let slices = this.components.get('pieSlices').store;
let slices = this.components.get(this.sliceName).store;
let prevIndex = this.curActiveSliceIndex; let prevIndex = this.curActiveSliceIndex;
let prevAcitve = this.curActiveSlice;
if(slices.includes(target)) {
let prevActive = this.curActiveSlice;
if (slices.includes(target)) {
let i = slices.indexOf(target); let i = slices.indexOf(target);
this.hoverSlice(prevAcitve, prevIndex,false);
this.hoverSlice(prevActive, prevIndex, false);
this.curActiveSlice = target; this.curActiveSlice = target;
this.curActiveSliceIndex = i; this.curActiveSliceIndex = i;
this.hoverSlice(target, i, true, e); this.hoverSlice(target, i, true, e);
@@ -149,7 +162,7 @@ export default class PieChart extends AggregationChart {
} }
} }


mouseLeave(){
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
mouseLeave() {
this.hoverSlice(this.curActiveSlice, this.curActiveSliceIndex, false);
} }
} }

+ 1
- 1
src/js/index.js Wyświetl plik

@@ -3,7 +3,7 @@ import * as Charts from './chart';
let frappe = { }; let frappe = { };


frappe.NAME = 'Frappe Charts'; frappe.NAME = 'Frappe Charts';
frappe.VERSION = '1.5.5';
frappe.VERSION = '2.0.0-rc22';


frappe = Object.assign({ }, frappe, Charts); frappe = Object.assign({ }, frappe, Charts);



+ 54
- 27
src/js/objects/ChartComponents.js Wyświetl plik

@@ -1,8 +1,10 @@
import { makeSVGGroup } from '../utils/draw'; import { makeSVGGroup } from '../utils/draw';
import { makeText, makePath, xLine, yLine, yMarker, yRegion, datasetBar, datasetDot, percentageBar, getPaths, heatSquare } from '../utils/draw'; import { makeText, makePath, xLine, yLine, yMarker, yRegion, datasetBar, datasetDot, percentageBar, getPaths, heatSquare } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils'; import { equilizeNoOfElements } from '../utils/draw-utils';
import { translateHoriLine, translateVertLine, animateRegion, animateBar,
animateDot, animatePath, animatePathStr } from '../utils/animate';
import {
translateHoriLine, translateVertLine, animateRegion, animateBar,
animateDot, animatePath, animatePathStr
} from '../utils/animate';
import { getMonthName } from '../utils/date-utils'; import { getMonthName } from '../utils/date-utils';


class ChartComponent { class ChartComponent {
@@ -27,7 +29,7 @@ class ChartComponent {
this.labels = []; this.labels = [];


this.layerClass = layerClass; this.layerClass = layerClass;
this.layerClass = typeof(this.layerClass) === 'function'
this.layerClass = typeof (this.layerClass) === 'function'
? this.layerClass() : this.layerClass; ? this.layerClass() : this.layerClass;


this.refresh(); this.refresh();
@@ -61,7 +63,7 @@ class ChartComponent {
update(animate = true) { update(animate = true) {
this.refresh(); this.refresh();
let animateElements = []; let animateElements = [];
if(animate) {
if (animate) {
animateElements = this.animateElements(this.data) || []; animateElements = this.animateElements(this.data) || [];
} }
return animateElements; return animateElements;
@@ -86,7 +88,7 @@ let componentConfigs = {
pieSlices: { pieSlices: {
layerClass: 'pie-slices', layerClass: 'pie-slices',
makeElements(data) { makeElements(data) {
return data.sliceStrings.map((s, i) =>{
return data.sliceStrings.map((s, i) => {
let slice = makePath(s, 'pie-path', 'none', data.colors[i]); let slice = makePath(s, 'pie-path', 'none', data.colors[i]);
slice.style.transition = 'transform .3s;'; slice.style.transition = 'transform .3s;';
return slice; return slice;
@@ -102,16 +104,20 @@ let componentConfigs = {
percentageBars: { percentageBars: {
layerClass: 'percentage-bars', layerClass: 'percentage-bars',
makeElements(data) { makeElements(data) {
return data.xPositions.map((x, i) =>{
const numberOfPoints = data.xPositions.length;
return data.xPositions.map((x, i) => {
let y = 0; let y = 0;
let bar = percentageBar(x, y, data.widths[i],
this.constants.barHeight, this.constants.barDepth, data.colors[i]);

let isLast = i == numberOfPoints - 1;
let isFirst = i == 0;

let bar = percentageBar(x, y, data.widths[i], this.constants.barHeight, isFirst, isLast, data.colors[i]);
return bar; return bar;
}); });
}, },


animateElements(newData) { animateElements(newData) {
if(newData) return [];
if (newData) return [];
} }
}, },
yAxis: { yAxis: {
@@ -119,7 +125,12 @@ let componentConfigs = {
makeElements(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
yLine(position, data.labels[i], this.constants.width, yLine(position, data.labels[i], this.constants.width,
{mode: this.constants.mode, pos: this.constants.pos, shortenNumbers: this.constants.shortenNumbers})
{
mode: this.constants.mode,
pos: this.constants.pos,
shortenNumbers: this.constants.shortenNumbers,
numberFormatter: this.constants.numberFormatter
})
); );
}, },


@@ -150,7 +161,7 @@ let componentConfigs = {
makeElements(data) { makeElements(data) {
return data.positions.map((position, i) => return data.positions.map((position, i) =>
xLine(position, data.calcLabels[i], this.constants.height, xLine(position, data.calcLabels[i], this.constants.height,
{mode: this.constants.mode, pos: this.constants.pos})
{ mode: this.constants.mode, pos: this.constants.pos })
); );
}, },


@@ -181,7 +192,7 @@ let componentConfigs = {
makeElements(data) { makeElements(data) {
return data.map(m => return data.map(m =>
yMarker(m.position, m.label, this.constants.width, yMarker(m.position, m.label, this.constants.width,
{labelPos: m.options.labelPos, mode: 'span', lineType: 'dashed'})
{ labelPos: m.options.labelPos, stroke: m.options.stroke, mode: 'span', lineType: m.options.lineType })
); );
}, },
animateElements(newData) { animateElements(newData) {
@@ -214,7 +225,7 @@ let componentConfigs = {
makeElements(data) { makeElements(data) {
return data.map(r => return data.map(r =>
yRegion(r.startPos, r.endPos, this.constants.width, yRegion(r.startPos, r.endPos, this.constants.width,
r.label, {labelPos: r.options.labelPos})
r.label, { labelPos: r.options.labelPos, stroke: r.options.stroke, fill: r.options.fill })
); );
}, },
animateElements(newData) { animateElements(newData) {
@@ -250,16 +261,16 @@ let componentConfigs = {
}, },


heatDomain: { heatDomain: {
layerClass: function() { return 'heat-domain domain-' + this.constants.index; },
layerClass: function () { return 'heat-domain domain-' + this.constants.index; },
makeElements(data) { makeElements(data) {
let {index, colWidth, rowHeight, squareSize, radius, xTranslate} = this.constants;
let { index, colWidth, rowHeight, squareSize, radius, xTranslate } = this.constants;
let monthNameHeight = -12; let monthNameHeight = -12;
let x = xTranslate, y = 0; let x = xTranslate, y = 0;


this.serializedSubDomains = []; this.serializedSubDomains = [];


data.cols.map((week, weekNo) => { data.cols.map((week, weekNo) => {
if(weekNo === 1) {
if (weekNo === 1) {
this.labels.push( this.labels.push(
makeText('domain-name', x, monthNameHeight, getMonthName(index, true).toUpperCase(), makeText('domain-name', x, monthNameHeight, getMonthName(index, true).toUpperCase(),
{ {
@@ -269,7 +280,7 @@ let componentConfigs = {
); );
} }
week.map((day, i) => { week.map((day, i) => {
if(day.fill) {
if (day.fill) {
let data = { let data = {
'data-date': day.yyyyMmDd, 'data-date': day.yyyyMmDd,
'data-value': day.dataValue, 'data-value': day.dataValue,
@@ -288,12 +299,12 @@ let componentConfigs = {
}, },


animateElements(newData) { animateElements(newData) {
if(newData) return [];
if (newData) return [];
} }
}, },


barGraph: { barGraph: {
layerClass: function() { return 'dataset-units dataset-bars dataset-' + this.constants.index; },
layerClass: function () { return 'dataset-units dataset-bars dataset-' + this.constants.index; },
makeElements(data) { makeElements(data) {
let c = this.constants; let c = this.constants;
this.unitType = 'bar'; this.unitType = 'bar';
@@ -347,7 +358,7 @@ let componentConfigs = {
this.store.map((bar, i) => { this.store.map((bar, i) => {
animateElements = animateElements.concat(animateBar( animateElements = animateElements.concat(animateBar(
bar, newXPos[i], newYPos[i], newData.barWidth, newOffsets[i], bar, newXPos[i], newYPos[i], newData.barWidth, newOffsets[i],
{zeroLine: newData.zeroLine}
{ zeroLine: newData.zeroLine }
)); ));
}); });


@@ -356,12 +367,12 @@ let componentConfigs = {
}, },


lineGraph: { lineGraph: {
layerClass: function() { return 'dataset-units dataset-line dataset-' + this.constants.index; },
layerClass: function () { return 'dataset-units dataset-line dataset-' + this.constants.index; },
makeElements(data) { makeElements(data) {
let c = this.constants; let c = this.constants;
this.unitType = 'dot'; this.unitType = 'dot';
this.paths = {}; this.paths = {};
if(!c.hideLine) {
if (!c.hideLine) {
this.paths = getPaths( this.paths = getPaths(
data.xPositions, data.xPositions,
data.yPositions, data.yPositions,
@@ -378,8 +389,8 @@ let componentConfigs = {
); );
} }


this.units = [];
if(!c.hideDots) {
this.units = [];
if (c.showDots) {
this.units = data.yPositions.map((y, j) => { this.units = data.yPositions.map((y, j) => {
return datasetDot( return datasetDot(
data.xPositions[j], data.xPositions[j],
@@ -387,11 +398,27 @@ let componentConfigs = {
data.radius, data.radius,
c.color, c.color,
(c.valuesOverPoints ? data.values[j] : ''), (c.valuesOverPoints ? data.values[j] : ''),
j
j,
c.hideDotBorder
); );
}); });
} }


if (c.trailingDot && !c.showDots) {
const lastIndex = data.yPositions.length - 1;
const dot = datasetDot(
data.xPositions[lastIndex],
data.yPositions[lastIndex],
data.radius,
c.color,
(c.valuesOverPoints ? data.values[lastIndex] : ''),
lastIndex,
c.hideDotBorder
);

this.units.push(dot);
}

return Object.values(this.paths).concat(this.units); return Object.values(this.paths).concat(this.units);
}, },
animateElements(newData) { animateElements(newData) {
@@ -418,12 +445,12 @@ let componentConfigs = {


let animateElements = []; let animateElements = [];


if(Object.keys(this.paths).length) {
if (Object.keys(this.paths).length) {
animateElements = animateElements.concat(animatePath( animateElements = animateElements.concat(animatePath(
this.paths, newXPos, newYPos, newData.zeroLine, this.constants.spline)); this.paths, newXPos, newYPos, newData.zeroLine, this.constants.spline));
} }


if(this.units.length) {
if (this.units.length) {
this.units.map((dot, i) => { this.units.map((dot, i) => {
animateElements = animateElements.concat(animateDot( animateElements = animateElements.concat(animateDot(
dot, newXPos[i], newYPos[i])); dot, newXPos[i], newYPos[i]));


+ 18
- 11
src/js/objects/SvgTip.js Wyświetl plik

@@ -42,6 +42,7 @@ export default class SvgTip {
this.hideTip(); this.hideTip();


this.title = this.container.querySelector('.title'); this.title = this.container.querySelector('.title');
this.list = this.container.querySelector('.data-point-list');
this.dataPointList = this.container.querySelector('.data-point-list'); this.dataPointList = this.container.querySelector('.data-point-list');


this.parent.addEventListener('mouseleave', () => { this.parent.addEventListener('mouseleave', () => {
@@ -51,27 +52,33 @@ export default class SvgTip {


fill() { fill() {
let title; let title;
if(this.index) {
if (this.index) {
this.container.setAttribute('data-point-index', this.index); this.container.setAttribute('data-point-index', this.index);
} }
if(this.titleValueFirst) {
if (this.titleValueFirst) {
title = `<strong>${this.titleValue}</strong>${this.titleName}`; title = `<strong>${this.titleValue}</strong>${this.titleName}`;
} else { } else {
title = `${this.titleName}<strong>${this.titleValue}</strong>`; title = `${this.titleName}<strong>${this.titleValue}</strong>`;
} }

if (this.listValues.length > 4) {
this.list.classList.add('tooltip-grid');
} else {
this.list.classList.remove('tooltip-grid');
}

this.title.innerHTML = title; this.title.innerHTML = title;
this.dataPointList.innerHTML = ''; this.dataPointList.innerHTML = '';


this.listValues.map((set, i) => { this.listValues.map((set, i) => {
const color = this.colors[i] || 'black'; const color = this.colors[i] || 'black';
let value = set.formatted === 0 || set.formatted ? set.formatted : set.value; let value = set.formatted === 0 || set.formatted ? set.formatted : set.value;

let li = $.create('li', { let li = $.create('li', {
styles: {
'border-top': `3px solid ${color}`
},
innerHTML: `<strong style="display: block;">${ value === 0 || value ? value : '' }</strong>
${set.title ? set.title : '' }`
innerHTML: `<div class="tooltip-legend" style="background: ${color};"></div>
<div>
<div class="tooltip-value">${value === 0 || value ? value : ''}</div>
<div class="tooltip-label">${set.title ? set.title : ''}</div>
</div>`
}); });


this.dataPointList.appendChild(li); this.dataPointList.appendChild(li);
@@ -83,15 +90,15 @@ export default class SvgTip {


this.top = this.y - this.container.offsetHeight this.top = this.y - this.container.offsetHeight
- TOOLTIP_POINTER_TRIANGLE_HEIGHT; - TOOLTIP_POINTER_TRIANGLE_HEIGHT;
this.left = this.x - width/2;
this.left = this.x - width / 2;
let maxLeft = this.parent.offsetWidth - width; let maxLeft = this.parent.offsetWidth - width;


let pointer = this.container.querySelector('.svg-pointer'); let pointer = this.container.querySelector('.svg-pointer');


if(this.left < 0) {
if (this.left < 0) {
pointer.style.left = `calc(50% - ${-1 * this.left}px)`; pointer.style.left = `calc(50% - ${-1 * this.left}px)`;
this.left = 0; this.left = 0;
} else if(this.left > maxLeft) {
} else if (this.left > maxLeft) {
let delta = this.left - maxLeft; let delta = this.left - maxLeft;
let pointerOffset = `calc(50% + ${delta}px)`; let pointerOffset = `calc(50% + ${delta}px)`;
pointer.style.left = pointerOffset; pointer.style.left = pointerOffset;


+ 12
- 12
src/js/utils/animate.js Wyświetl plik

@@ -11,11 +11,11 @@ export function translate(unit, oldCoord, newCoord, duration) {
let old = typeof oldCoord === 'string' ? oldCoord : oldCoord.join(', '); let old = typeof oldCoord === 'string' ? oldCoord : oldCoord.join(', ');
return [ return [
unit, unit,
{transform: newCoord.join(', ')},
{ transform: newCoord.join(', ') },
duration, duration,
STD_EASING, STD_EASING,
"translate", "translate",
{transform: old}
{ transform: old }
]; ];
} }


@@ -42,14 +42,14 @@ export function animateRegion(rectGroup, newY1, newY2, oldY2) {
return [rectAnim, groupAnim]; return [rectAnim, groupAnim];
} }


export function animateBar(bar, x, yTop, width, offset=0, meta={}) {
export function animateBar(bar, x, yTop, width, offset = 0, meta = {}) {
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine); let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
y -= offset; y -= offset;
if(bar.nodeName !== 'rect') {
if (bar.nodeName !== 'rect') {
let rect = bar.childNodes[0]; let rect = bar.childNodes[0];
let rectAnim = [ let rectAnim = [
rect, rect,
{width: width, height: height},
{ width: width, height: height },
UNIT_ANIM_DUR, UNIT_ANIM_DUR,
STD_EASING STD_EASING
]; ];
@@ -58,18 +58,18 @@ export function animateBar(bar, x, yTop, width, offset=0, meta={}) {
let groupAnim = translate(bar, oldCoordStr, [x, y], MARKER_LINE_ANIM_DUR); let groupAnim = translate(bar, oldCoordStr, [x, y], MARKER_LINE_ANIM_DUR);
return [rectAnim, groupAnim]; return [rectAnim, groupAnim];
} else { } else {
return [[bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING]];
return [[bar, { width: width, height: height, x: x, y: y }, UNIT_ANIM_DUR, STD_EASING]];
} }
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein); // bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
} }


export function animateDot(dot, x, y) { export function animateDot(dot, x, y) {
if(dot.nodeName !== 'circle') {
if (dot.nodeName !== 'circle') {
let oldCoordStr = dot.getAttribute("transform").split("(")[1].slice(0, -1); let oldCoordStr = dot.getAttribute("transform").split("(")[1].slice(0, -1);
let groupAnim = translate(dot, oldCoordStr, [x, y], MARKER_LINE_ANIM_DUR); let groupAnim = translate(dot, oldCoordStr, [x, y], MARKER_LINE_ANIM_DUR);
return [groupAnim]; return [groupAnim];
} else { } else {
return [[dot, {cx: x, cy: y}, UNIT_ANIM_DUR, STD_EASING]];
return [[dot, { cx: x, cy: y }, UNIT_ANIM_DUR, STD_EASING]];
} }
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein); // dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
} }
@@ -81,16 +81,16 @@ export function animatePath(paths, newXList, newYList, zeroLine, spline) {
if (spline) if (spline)
pointsStr = getSplineCurvePointsStr(newXList, newYList); pointsStr = getSplineCurvePointsStr(newXList, newYList);


const animPath = [paths.path, {d:"M" + pointsStr}, PATH_ANIM_DUR, STD_EASING];
const animPath = [paths.path, { d: "M" + pointsStr }, PATH_ANIM_DUR, STD_EASING];
pathComponents.push(animPath); pathComponents.push(animPath);


if(paths.region) {
if (paths.region) {
let regStartPt = `${newXList[0]},${zeroLine}L`; let regStartPt = `${newXList[0]},${zeroLine}L`;
let regEndPt = `L${newXList.slice(-1)[0]}, ${zeroLine}`; let regEndPt = `L${newXList.slice(-1)[0]}, ${zeroLine}`;


const animRegion = [ const animRegion = [
paths.region, paths.region,
{d:"M" + regStartPt + pointsStr + regEndPt},
{ d: "M" + regStartPt + pointsStr + regEndPt },
PATH_ANIM_DUR, PATH_ANIM_DUR,
STD_EASING STD_EASING
]; ];
@@ -101,5 +101,5 @@ export function animatePath(paths, newXList, newYList, zeroLine, spline) {
} }


export function animatePathStr(oldPath, pathStr) { export function animatePathStr(oldPath, pathStr) {
return [oldPath, {d: pathStr}, UNIT_ANIM_DUR, STD_EASING];
return [oldPath, { d: pathStr }, UNIT_ANIM_DUR, STD_EASING];
} }

+ 9
- 9
src/js/utils/animation.js Wyświetl plik

@@ -11,14 +11,14 @@ const EASING = {
easeinout: "0.42 0 0.58 1" easeinout: "0.42 0 0.58 1"
}; };


function animateSVGElement(element, props, dur, easingType="linear", type=undefined, oldValues={}) {
function animateSVGElement(element, props, dur, easingType = "linear", type = undefined, oldValues = {}) {


let animElement = element.cloneNode(true); let animElement = element.cloneNode(true);
let newElement = element.cloneNode(true); let newElement = element.cloneNode(true);


for(var attributeName in props) {
for (var attributeName in props) {
let animateElement; let animateElement;
if(attributeName === 'transform') {
if (attributeName === 'transform') {
animateElement = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform"); animateElement = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
} else { } else {
animateElement = document.createElementNS("http://www.w3.org/2000/svg", "animate"); animateElement = document.createElementNS("http://www.w3.org/2000/svg", "animate");
@@ -31,7 +31,7 @@ function animateSVGElement(element, props, dur, easingType="linear", type=undefi
from: currentValue, from: currentValue,
to: value, to: value,
begin: "0s", begin: "0s",
dur: dur/1000 + "s",
dur: dur / 1000 + "s",
values: currentValue + ";" + value, values: currentValue + ";" + value,
keySplines: EASING[easingType], keySplines: EASING[easingType],
keyTimes: "0;1", keyTimes: "0;1",
@@ -39,7 +39,7 @@ function animateSVGElement(element, props, dur, easingType="linear", type=undefi
fill: 'freeze' fill: 'freeze'
}; };


if(type) {
if (type) {
animAttr["type"] = type; animAttr["type"] = type;
} }


@@ -49,7 +49,7 @@ function animateSVGElement(element, props, dur, easingType="linear", type=undefi


animElement.appendChild(animateElement); animElement.appendChild(animateElement);


if(type) {
if (type) {
newElement.setAttribute(attributeName, `translate(${value})`); newElement.setAttribute(attributeName, `translate(${value})`);
} else { } else {
newElement.setAttribute(attributeName, value); newElement.setAttribute(attributeName, value);
@@ -97,10 +97,10 @@ function animateSVG(svgContainer, elements) {
} }


export function runSMILAnimation(parent, svgElement, elementsToAnimate) { export function runSMILAnimation(parent, svgElement, elementsToAnimate) {
if(elementsToAnimate.length === 0) return;
if (elementsToAnimate.length === 0) return;


let animSvgElement = animateSVG(svgElement, elementsToAnimate); let animSvgElement = animateSVG(svgElement, elementsToAnimate);
if(svgElement.parentNode == parent) {
if (svgElement.parentNode == parent) {
parent.removeChild(svgElement); parent.removeChild(svgElement);
parent.appendChild(animSvgElement); parent.appendChild(animSvgElement);


@@ -108,7 +108,7 @@ export function runSMILAnimation(parent, svgElement, elementsToAnimate) {


// Replace the new svgElement (data has already been replaced) // Replace the new svgElement (data has already been replaced)
setTimeout(() => { setTimeout(() => {
if(animSvgElement.parentNode == parent) {
if (animSvgElement.parentNode == parent) {
parent.removeChild(animSvgElement); parent.removeChild(animSvgElement);
parent.appendChild(svgElement); parent.appendChild(svgElement);
} }


+ 32
- 25
src/js/utils/axis-chart-utils.js Wyświetl plik

@@ -1,5 +1,8 @@
import { fillArray } from '../utils/helpers'; import { fillArray } from '../utils/helpers';
import { DEFAULT_AXIS_CHART_TYPE, AXIS_DATASET_CHART_TYPES, DEFAULT_CHAR_WIDTH } from '../utils/constants';
import {
DEFAULT_AXIS_CHART_TYPE, AXIS_DATASET_CHART_TYPES, DEFAULT_CHAR_WIDTH,
SERIES_LABEL_SPACE_RATIO
} from '../utils/constants';


export function dataPrep(data, type) { export function dataPrep(data, type) {
data.labels = data.labels || []; data.labels = data.labels || [];
@@ -9,16 +12,16 @@ export function dataPrep(data, type) {
// Datasets // Datasets
let datasets = data.datasets; let datasets = data.datasets;
let zeroArray = new Array(datasetLength).fill(0); let zeroArray = new Array(datasetLength).fill(0);
if(!datasets) {
if (!datasets) {
// default // default
datasets = [{ datasets = [{
values: zeroArray values: zeroArray
}]; }];
} }


datasets.map(d=> {
datasets.map(d => {
// Set values // Set values
if(!d.values) {
if (!d.values) {
d.values = zeroArray; d.values = zeroArray;
} else { } else {
// Check for non values // Check for non values
@@ -26,19 +29,17 @@ export function dataPrep(data, type) {
vals = vals.map(val => (!isNaN(val) ? val : 0)); vals = vals.map(val => (!isNaN(val) ? val : 0));


// Trim or extend // Trim or extend
if(vals.length > datasetLength) {
if (vals.length > datasetLength) {
vals = vals.slice(0, datasetLength); vals = vals.slice(0, datasetLength);
} else { } else {
vals = fillArray(vals, datasetLength - vals.length, 0); vals = fillArray(vals, datasetLength - vals.length, 0);
} }
d.values = vals;
} }


// Set labels
//

// Set type // Set type
if(!d.chartType ) {
if(!AXIS_DATASET_CHART_TYPES.includes(type)) type === DEFAULT_AXIS_CHART_TYPE;
if (!d.chartType) {
if (!AXIS_DATASET_CHART_TYPES.includes(type)) type === DEFAULT_AXIS_CHART_TYPE;
d.chartType = type; d.chartType = type;
} }


@@ -48,9 +49,9 @@ export function dataPrep(data, type) {


// Regions // Regions
// data.yRegions = data.yRegions || []; // data.yRegions = data.yRegions || [];
if(data.yRegions) {
if (data.yRegions) {
data.yRegions.map(d => { data.yRegions.map(d => {
if(d.end < d.start) {
if (d.end < d.start) {
[d.start, d.end] = [d.end, d.start]; [d.start, d.end] = [d.end, d.start];
} }
}); });
@@ -74,7 +75,7 @@ export function zeroDataPrep(realData) {
}), }),
}; };


if(realData.yMarkers) {
if (realData.yMarkers) {
zeroData.yMarkers = [ zeroData.yMarkers = [
{ {
value: 0, value: 0,
@@ -83,7 +84,7 @@ export function zeroDataPrep(realData) {
]; ];
} }


if(realData.yRegions) {
if (realData.yRegions) {
zeroData.yRegions = [ zeroData.yRegions = [
{ {
start: 0, start: 0,
@@ -96,31 +97,37 @@ export function zeroDataPrep(realData) {
return zeroData; return zeroData;
} }


export function getShortenedLabels(chartWidth, labels=[], isSeries=true) {
let allowedSpace = chartWidth / labels.length;
if(allowedSpace <= 0) allowedSpace = 1;
export function getShortenedLabels(chartWidth, labels = [], isSeries = true) {
let allowedSpace = (chartWidth / labels.length) * SERIES_LABEL_SPACE_RATIO;
if (allowedSpace <= 0) allowedSpace = 1;
let allowedLetters = allowedSpace / DEFAULT_CHAR_WIDTH; let allowedLetters = allowedSpace / DEFAULT_CHAR_WIDTH;


let seriesMultiple; let seriesMultiple;
if(isSeries) {
if (isSeries) {
// Find the maximum label length for spacing calculations // Find the maximum label length for spacing calculations
let maxLabelLength = Math.max(...labels.map(label => label.length)); let maxLabelLength = Math.max(...labels.map(label => label.length));
seriesMultiple = Math.ceil(maxLabelLength/allowedLetters);
seriesMultiple = Math.ceil(maxLabelLength / allowedLetters);
} }


let calcLabels = labels.map((label, i) => { let calcLabels = labels.map((label, i) => {
label += ""; label += "";
if(label.length > allowedLetters) {
if (label.length > allowedLetters) {


if(!isSeries) {
if(allowedLetters-3 > 0) {
label = label.slice(0, allowedLetters-3) + " ...";
if (!isSeries) {
if (allowedLetters - 3 > 0) {
label = label.slice(0, allowedLetters - 3) + " ...";
} else { } else {
label = label.slice(0, allowedLetters) + '..'; label = label.slice(0, allowedLetters) + '..';
} }
} else { } else {
if(i % seriesMultiple !== 0) {
label = "";
if (i % seriesMultiple !== 0) {
if (i !== (labels.length - 1)) {
label = "";
}
} else {
if (i > (labels.length - (seriesMultiple / 2))) {
label = "";
}
} }
} }
} }


+ 23
- 17
src/js/utils/colors.js Wyświetl plik

@@ -1,21 +1,27 @@
const PRESET_COLOR_MAP = { const PRESET_COLOR_MAP = {
'light-blue': '#7cd6fd',
'blue': '#5e64ff',
'violet': '#743ee2',
'red': '#ff5858',
'orange': '#ffa00a',
'yellow': '#feef72',
'green': '#28a745',
'light-green': '#98d85b',
'purple': '#b554ff',
'magenta': '#ffa3ef',
'black': '#36114C',
'grey': '#bdd3e6',
'light-grey': '#f0f4f7',
'dark-grey': '#b8c2cc'
'pink': '#F683AE',
'blue': '#318AD8',
'green': '#48BB74',
'grey': '#A6B1B9',
'red': '#F56B6B',
'yellow': '#FACF7A',
'purple': '#44427B',
'teal': '#5FD8C4',
'cyan': '#15CCEF',
'orange': '#F8814F',
'light-pink': '#FED7E5',
'light-blue': '#BFDDF7',
'light-green': '#48BB74',
'light-grey': '#F4F5F6',
'light-red': '#F6DFDF',
'light-yellow': '#FEE9BF',
'light-purple': '#E8E8F7',
'light-teal': '#D3FDF6',
'light-cyan': '#DDF8FD',
'light-orange': '#FECDB8'
}; };


function limitColor(r){
function limitColor(r) {
if (r > 255) return 255; if (r > 255) return 255;
else if (r < 0) return 0; else if (r < 0) return 0;
return r; return r;
@@ -28,11 +34,11 @@ export function lightenDarkenColor(color, amt) {
col = col.slice(1); col = col.slice(1);
usePound = true; usePound = true;
} }
let num = parseInt(col,16);
let num = parseInt(col, 16);
let r = limitColor((num >> 16) + amt); let r = limitColor((num >> 16) + amt);
let b = limitColor(((num >> 8) & 0x00FF) + amt); let b = limitColor(((num >> 8) & 0x00FF) + amt);
let g = limitColor((num & 0x0000FF) + amt); let g = limitColor((num & 0x0000FF) + amt);
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16);
} }


export function isValidColor(string) { export function isValidColor(string) {


+ 5
- 7
src/js/utils/constants.js Wyświetl plik

@@ -65,7 +65,8 @@ export const CHART_POST_ANIMATE_TIMEOUT = 400;
export const DEFAULT_AXIS_CHART_TYPE = 'line'; export const DEFAULT_AXIS_CHART_TYPE = 'line';
export const AXIS_DATASET_CHART_TYPES = ['line', 'bar']; export const AXIS_DATASET_CHART_TYPES = ['line', 'bar'];


export const AXIS_LEGEND_BAR_SIZE = 100;
export const LEGEND_ITEM_WIDTH = 150;
export const SERIES_LABEL_SPACE_RATIO = 0.6;


export const BAR_CHART_SPACE_RATIO = 0.5; export const BAR_CHART_SPACE_RATIO = 0.5;
export const MIN_BAR_PERCENT_HEIGHT = 0.00; export const MIN_BAR_PERCENT_HEIGHT = 0.00;
@@ -73,8 +74,7 @@ export const MIN_BAR_PERCENT_HEIGHT = 0.00;
export const LINE_CHART_DOT_SIZE = 4; export const LINE_CHART_DOT_SIZE = 4;
export const DOT_OVERLAY_SIZE_INCR = 4; export const DOT_OVERLAY_SIZE_INCR = 4;


export const PERCENTAGE_BAR_DEFAULT_HEIGHT = 20;
export const PERCENTAGE_BAR_DEFAULT_DEPTH = 2;
export const PERCENTAGE_BAR_DEFAULT_HEIGHT = 16;


// Fixed 5-color theme, // Fixed 5-color theme,
// More colors are difficult to parse visually // More colors are difficult to parse visually
@@ -85,10 +85,8 @@ export const HEATMAP_GUTTER_SIZE = 2;


export const DEFAULT_CHAR_WIDTH = 7; export const DEFAULT_CHAR_WIDTH = 7;


export const TOOLTIP_POINTER_TRIANGLE_HEIGHT = 5;

const DEFAULT_CHART_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
export const TOOLTIP_POINTER_TRIANGLE_HEIGHT = 7.48;
const DEFAULT_CHART_COLORS = ['pink', 'blue', 'green', 'grey', 'red', 'yellow', 'purple', 'teal', 'cyan', 'orange'];
const HEATMAP_COLORS_GREEN = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']; const HEATMAP_COLORS_GREEN = ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'];
export const HEATMAP_COLORS_BLUE = ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e']; export const HEATMAP_COLORS_BLUE = ['#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e'];
export const HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c']; export const HEATMAP_COLORS_YELLOW = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];


+ 7
- 7
src/js/utils/date-utils.js Wyświetl plik

@@ -27,8 +27,8 @@ export function getYyyyMmDd(date) {
let mm = date.getMonth() + 1; // getMonth() is zero-based let mm = date.getMonth() + 1; // getMonth() is zero-based
return [ return [
date.getFullYear(), date.getFullYear(),
(mm>9 ? '' : '0') + mm,
(dd>9 ? '' : '0') + dd
(mm > 9 ? '' : '0') + mm,
(dd > 9 ? '' : '0') + dd
].join('-'); ].join('-');
} }


@@ -37,12 +37,12 @@ export function clone(date) {
} }


export function timestampSec(date) { export function timestampSec(date) {
return date.getTime()/NO_OF_MILLIS;
return date.getTime() / NO_OF_MILLIS;
} }


export function timestampToMidnight(timestamp, roundAhead = false) { export function timestampToMidnight(timestamp, roundAhead = false) {
let midnightTs = Math.floor(timestamp - (timestamp % SEC_IN_DAY)); let midnightTs = Math.floor(timestamp - (timestamp % SEC_IN_DAY));
if(roundAhead) {
if (roundAhead) {
return midnightTs + SEC_IN_DAY; return midnightTs + SEC_IN_DAY;
} }
return midnightTs; return midnightTs;
@@ -65,12 +65,12 @@ export function areInSameMonth(startDate, endDate) {
&& startDate.getFullYear() === endDate.getFullYear(); && startDate.getFullYear() === endDate.getFullYear();
} }


export function getMonthName(i, short=false) {
export function getMonthName(i, short = false) {
let monthName = MONTH_NAMES[i]; let monthName = MONTH_NAMES[i];
return short ? monthName.slice(0, 3) : monthName; return short ? monthName.slice(0, 3) : monthName;
} }


export function getLastDateInMonth (month, year) {
export function getLastDateInMonth(month, year) {
return new Date(year, month + 1, 0); // 0: last day in previous month return new Date(year, month + 1, 0); // 0: last day in previous month
} }


@@ -78,7 +78,7 @@ export function getLastDateInMonth (month, year) {
export function setDayToSunday(date) { export function setDayToSunday(date) {
let newDate = clone(date); let newDate = clone(date);
const day = newDate.getDay(); const day = newDate.getDay();
if(day !== 0) {
if (day !== 0) {
addDays(newDate, (-1) * day); addDays(newDate, (-1) * day);
} }
return newDate; return newDate;


+ 14
- 15
src/js/utils/dom.js Wyświetl plik

@@ -1,9 +1,8 @@
export function $(expr, con) { export function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
return typeof expr === "string" ? (con || document).querySelector(expr) : expr || null;
} }


export function findNodeIndex(node)
{
export function findNodeIndex(node) {
var i = 0; var i = 0;
while (node.previousSibling) { while (node.previousSibling) {
node = node.previousSibling; node = node.previousSibling;
@@ -27,12 +26,12 @@ $.create = (tag, o) => {
element.appendChild(ref); element.appendChild(ref);


} else if (i === "styles") { } else if (i === "styles") {
if(typeof val === "object") {
if (typeof val === "object") {
Object.keys(val).map(prop => { Object.keys(val).map(prop => {
element.style[prop] = val[prop]; element.style[prop] = val[prop];
}); });
} }
} else if (i in element ) {
} else if (i in element) {
element[i] = val; element[i] = val;
} }
else { else {
@@ -67,9 +66,9 @@ export function isElementInViewport(el) {


return ( return (
rect.top >= 0 && rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
); );
} }


@@ -81,7 +80,7 @@ export function getElementContentWidth(element) {
return element.clientWidth - padding; return element.clientWidth - padding;
} }


export function bind(element, o){
export function bind(element, o) {
if (element) { if (element) {
for (var event in o) { for (var event in o) {
var callback = o[event]; var callback = o[event];
@@ -93,12 +92,12 @@ export function bind(element, o){
} }
} }


export function unbind(element, o){
export function unbind(element, o) {
if (element) { if (element) {
for (var event in o) { for (var event in o) {
var callback = o[event]; var callback = o[event];


event.split(/\s+/).forEach(function(event) {
event.split(/\s+/).forEach(function (event) {
element.removeEventListener(event, callback); element.removeEventListener(event, callback);
}); });
} }
@@ -108,7 +107,7 @@ export function unbind(element, o){
export function fire(target, type, properties) { export function fire(target, type, properties) {
var evt = document.createEvent("HTMLEvents"); var evt = document.createEvent("HTMLEvents");


evt.initEvent(type, true, true );
evt.initEvent(type, true, true);


for (var j in properties) { for (var j in properties) {
evt[j] = properties[j]; evt[j] = properties[j];
@@ -119,17 +118,17 @@ export function fire(target, type, properties) {


// https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/ // https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
export function forEachNode(nodeList, callback, scope) { export function forEachNode(nodeList, callback, scope) {
if(!nodeList) return;
if (!nodeList) return;
for (var i = 0; i < nodeList.length; i++) { for (var i = 0; i < nodeList.length; i++) {
callback.call(scope, nodeList[i], i); callback.call(scope, nodeList[i], i);
} }
} }


export function activate($parent, $child, commonClass, activeClass='active', index = -1) {
export function activate($parent, $child, commonClass, activeClass = 'active', index = -1) {
let $children = $parent.querySelectorAll(`.${commonClass}.${activeClass}`); let $children = $parent.querySelectorAll(`.${commonClass}.${activeClass}`);


forEachNode($children, (node, i) => { forEachNode($children, (node, i) => {
if(index >= 0 && i <= index) return;
if (index >= 0 && i <= index) return;
node.classList.remove(activeClass); node.classList.remove(activeClass);
}); });




+ 12
- 10
src/js/utils/draw-utils.js Wyświetl plik

@@ -17,7 +17,7 @@ export function equilizeNoOfElements(array1, array2,
extraCount = array2.length - array1.length) { extraCount = array2.length - array1.length) {


// Doesn't work if either has zero elements. // Doesn't work if either has zero elements.
if(extraCount > 0) {
if (extraCount > 0) {
array1 = fillArray(array1, extraCount); array1 = fillArray(array1, extraCount);
} else { } else {
array2 = fillArray(array2, extraCount); array2 = fillArray(array2, extraCount);
@@ -30,7 +30,7 @@ export function truncateString(txt, len) {
return; return;
} }
if (txt.length > len) { if (txt.length > len) {
return txt.slice(0, len-3) + '...';
return txt.slice(0, len - 3) + '...';
} else { } else {
return txt; return txt;
} }
@@ -47,18 +47,20 @@ export function shortenLargeNumber(label) {
// Using absolute since log wont work for negative numbers // Using absolute since log wont work for negative numbers
let p = Math.floor(Math.log10(Math.abs(number))); let p = Math.floor(Math.log10(Math.abs(number)));
if (p <= 2) return number; // Return as is for a 3 digit number of less if (p <= 2) return number; // Return as is for a 3 digit number of less
let l = Math.floor(p / 3);
let l = Math.floor(p / 3);
let shortened = (Math.pow(10, p - l * 3) * +(number / Math.pow(10, p)).toFixed(1)); let shortened = (Math.pow(10, p - l * 3) * +(number / Math.pow(10, p)).toFixed(1));


// Correct for floating point error upto 2 decimal places // Correct for floating point error upto 2 decimal places
return Math.round(shortened*100)/100 + ' ' + ['', 'K', 'M', 'B', 'T'][l];
return Math.round(shortened * 100) / 100 + ['', 'K', 'M', 'B', 'T'][l];
} }


// cubic bezier curve calculation (from example by François Romain) // cubic bezier curve calculation (from example by François Romain)
export function getSplineCurvePointsStr(xList, yList) { export function getSplineCurvePointsStr(xList, yList) {


let points=[];
for(let i=0;i<xList.length;i++){
let points = [];
const length = Math.min(xList.length, yList.length);
for (let i = 0; i < xList.length; i++) {
points.push([xList[i], yList[i]]); points.push([xList[i], yList[i]]);
} }


@@ -71,7 +73,7 @@ export function getSplineCurvePointsStr(xList, yList) {
angle: Math.atan2(lengthY, lengthX) angle: Math.atan2(lengthY, lengthX)
}; };
}; };
let controlPoint = (current, previous, next, reverse) => { let controlPoint = (current, previous, next, reverse) => {
let p = previous || current; let p = previous || current;
let n = next || current; let n = next || current;
@@ -82,18 +84,18 @@ export function getSplineCurvePointsStr(xList, yList) {
let y = current[1] + Math.sin(angle) * length; let y = current[1] + Math.sin(angle) * length;
return [x, y]; return [x, y];
}; };
let bezierCommand = (point, i, a) => { let bezierCommand = (point, i, a) => {
let cps = controlPoint(a[i - 1], a[i - 2], point); let cps = controlPoint(a[i - 1], a[i - 2], point);
let cpe = controlPoint(point, a[i - 1], a[i + 1], true); let cpe = controlPoint(point, a[i - 1], a[i + 1], true);
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`; return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
}; };
let pointStr = (points, command) => { let pointStr = (points, command) => {
return points.reduce((acc, point, i, a) => i === 0 return points.reduce((acc, point, i, a) => i === 0
? `${point[0]},${point[1]}` ? `${point[0]},${point[1]}`
: `${acc} ${command(point, i, a)}`, ''); : `${acc} ${command(point, i, a)}`, '');
}; };
return pointStr(points, bezierCommand); return pointStr(points, bezierCommand);
} }

+ 164
- 133
src/js/utils/draw.js Wyświetl plik

@@ -1,17 +1,15 @@
import { getBarHeightAndYAttr, truncateString, shortenLargeNumber, getSplineCurvePointsStr } from './draw-utils'; import { getBarHeightAndYAttr, truncateString, shortenLargeNumber, getSplineCurvePointsStr } from './draw-utils';
import { getStringWidth, isValidNumber } from './helpers';
import { DOT_OVERLAY_SIZE_INCR, PERCENTAGE_BAR_DEFAULT_DEPTH } from './constants';
import { lightenDarkenColor } from './colors';
import { getStringWidth, isValidNumber, round } from './helpers';
import { DOT_OVERLAY_SIZE_INCR } from './constants';


export const AXIS_TICK_LENGTH = 6; export const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4; const LABEL_MARGIN = 4;
const LABEL_MAX_CHARS = 15;
const LABEL_MAX_CHARS = 18;
export const FONT_SIZE = 10; export const FONT_SIZE = 10;
const BASE_LINE_COLOR = '#dadada';
const FONT_FILL = '#555b51';
const BASE_LINE_COLOR = '#E2E6E9';


function $(expr, con) { function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
return typeof expr === "string" ? (con || document).querySelector(expr) : expr || null;
} }


export function createSVG(tag, o) { export function createSVG(tag, o) {
@@ -29,14 +27,14 @@ export function createSVG(tag, o) {
element.appendChild(ref); element.appendChild(ref);


} else if (i === "styles") { } else if (i === "styles") {
if(typeof val === "object") {
if (typeof val === "object") {
Object.keys(val).map(prop => { Object.keys(val).map(prop => {
element.style[prop] = val[prop]; element.style[prop] = val[prop];
}); });
} }
} else { } else {
if(i === "className") { i = "class"; }
if(i === "innerHTML") {
if (i === "className") { i = "class"; }
if (i === "innerHTML") {
element['textContent'] = val; element['textContent'] = val;
} else { } else {
element.setAttribute(i, val); element.setAttribute(i, val);
@@ -82,16 +80,16 @@ export function makeSVGDefs(svgContainer) {
}); });
} }


export function makeSVGGroup(className, transform='', parent=undefined) {
export function makeSVGGroup(className, transform = '', parent = undefined) {
let args = { let args = {
className: className, className: className,
transform: transform transform: transform
}; };
if(parent) args.inside = parent;
if (parent) args.inside = parent;
return createSVG('g', args); return createSVG('g', args);
} }


export function wrapInSVGGroup(elements, className='') {
export function wrapInSVGGroup(elements, className = '') {
let g = createSVG('g', { let g = createSVG('g', {
className: className className: className
}); });
@@ -99,7 +97,7 @@ export function wrapInSVGGroup(elements, className='') {
return g; return g;
} }


export function makePath(pathStr, className='', stroke='none', fill='none', strokeWidth=2) {
export function makePath(pathStr, className = '', stroke = 'none', fill = 'none', strokeWidth = 2) {
return createSVG('path', { return createSVG('path', {
className: className, className: className,
d: pathStr, d: pathStr,
@@ -111,7 +109,7 @@ export function makePath(pathStr, className='', stroke='none', fill='none', stro
}); });
} }


export function makeArcPathStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
export function makeArcPathStr(startPosition, endPosition, center, radius, clockWise = 1, largeArc = 0) {
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y]; let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y]; let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
return `M${center.x} ${center.y} return `M${center.x} ${center.y}
@@ -120,7 +118,7 @@ export function makeArcPathStr(startPosition, endPosition, center, radius, clock
${arcEndX} ${arcEndY} z`; ${arcEndX} ${arcEndY} z`;
} }


export function makeCircleStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
export function makeCircleStr(startPosition, endPosition, center, radius, clockWise = 1, largeArc = 0) {
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y]; let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, center.y * 2, center.y + endPosition.y]; let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, center.y * 2, center.y + endPosition.y];
return `M${center.x} ${center.y} return `M${center.x} ${center.y}
@@ -132,7 +130,7 @@ export function makeCircleStr(startPosition, endPosition, center, radius, clockW
${arcEndX} ${arcEndY} z`; ${arcEndX} ${arcEndY} z`;
} }


export function makeArcStrokePathStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
export function makeArcStrokePathStr(startPosition, endPosition, center, radius, clockWise = 1, largeArc = 0) {
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y]; let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y]; let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];


@@ -141,7 +139,7 @@ export function makeArcStrokePathStr(startPosition, endPosition, center, radius,
${arcEndX} ${arcEndY}`; ${arcEndX} ${arcEndY}`;
} }


export function makeStrokeCircleStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
export function makeStrokeCircleStr(startPosition, endPosition, center, radius, clockWise = 1, largeArc = 0) {
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y]; let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, radius * 2 + arcStartY, center.y + startPosition.y]; let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, radius * 2 + arcStartY, center.y + startPosition.y];


@@ -154,11 +152,11 @@ export function makeStrokeCircleStr(startPosition, endPosition, center, radius,
} }


export function makeGradient(svgDefElem, color, lighter = false) { export function makeGradient(svgDefElem, color, lighter = false) {
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
let gradientId = 'path-fill-gradient' + '-' + color + '-' + (lighter ? 'lighter' : 'default');
let gradientDef = renderVerticalGradient(svgDefElem, gradientId); let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
let opacities = [1, 0.6, 0.2]; let opacities = [1, 0.6, 0.2];
if(lighter) {
opacities = [0.4, 0.2, 0];
if (lighter) {
opacities = [0.15, 0.05, 0];
} }


setGradientStop(gradientDef, "0%", color, opacities[0]); setGradientStop(gradientDef, "0%", color, opacities[0]);
@@ -168,8 +166,31 @@ export function makeGradient(svgDefElem, color, lighter = false) {
return gradientId; return gradientId;
} }


export function percentageBar(x, y, width, height,
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
export function rightRoundedBar(x, width, height) {
// https://medium.com/@dennismphil/one-side-rounded-rectangle-using-svg-fb31cf318d90
let radius = height / 2;
let xOffset = width - radius;

return `M${x},0 h${xOffset} q${radius},0 ${radius},${radius} q0,${radius} -${radius},${radius} h-${xOffset} v${height}z`;
}

export function leftRoundedBar(x, width, height) {
let radius = height / 2;
let xOffset = width - radius;

return `M${x + radius},0 h${xOffset} v${height} h-${xOffset} q-${radius}, 0 -${radius},-${radius} q0,-${radius} ${radius},-${radius}z`;
}

export function percentageBar(x, y, width, height, isFirst, isLast, fill = 'none') {
if (isLast) {
let pathStr = rightRoundedBar(x, width, height);
return makePath(pathStr, 'percentage-bar', null, fill);
}

if (isFirst) {
let pathStr = leftRoundedBar(x, width, height);
return makePath(pathStr, 'percentage-bar', null, fill);
}


let args = { let args = {
className: 'percentage-bar', className: 'percentage-bar',
@@ -177,20 +198,13 @@ export function percentageBar(x, y, width, height,
y: y, y: y,
width: width, width: width,
height: height, height: height,
fill: fill,
styles: {
'stroke': lightenDarkenColor(fill, -25),
// Diabolically good: https://stackoverflow.com/a/9000859
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
'stroke-dasharray': `0, ${height + width}, ${width}, ${height}`,
'stroke-width': depth
},
fill: fill
}; };


return createSVG("rect", args); return createSVG("rect", args);
} }


export function heatSquare(className, x, y, size, radius, fill='none', data={}) {
export function heatSquare(className, x, y, size, radius, fill = 'none', data = {}) {
let args = { let args = {
className: className, className: className,
x: x, x: x,
@@ -208,64 +222,54 @@ export function heatSquare(className, x, y, size, radius, fill='none', data={})
return createSVG("rect", args); return createSVG("rect", args);
} }


export function legendBar(x, y, size, fill='none', label, truncate=false) {
export function legendDot(x, y, size, radius, fill = 'none', label, value, font_size = null, truncate = false) {
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label; label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
if (!font_size) font_size = FONT_SIZE;


let args = { let args = {
className: 'legend-bar',
className: 'legend-dot',
x: 0, x: 0,
y: 0,
y: 4 - size,
height: size,
width: size, width: size,
height: '2px',
rx: radius,
fill: fill fill: fill
}; };
let text = createSVG('text', {
className: 'legend-dataset-text',
x: 0,

let textLabel = createSVG('text', {
className: 'legend-dataset-label',
x: size,
y: 0, y: 0,
dy: (FONT_SIZE * 2) + 'px',
'font-size': (FONT_SIZE * 1.2) + 'px',
dx: (font_size) + 'px',
dy: (font_size / 3) + 'px',
'font-size': (font_size * 1.6) + 'px',
'text-anchor': 'start', 'text-anchor': 'start',
fill: FONT_FILL,
innerHTML: label innerHTML: label
}); });


let textValue = null;
if (value) {
textValue = createSVG('text', {
className: 'legend-dataset-value',
x: size,
y: FONT_SIZE + 10,
dx: (FONT_SIZE) + 'px',
dy: (FONT_SIZE / 3) + 'px',
'font-size': (FONT_SIZE * 1.2) + 'px',
'text-anchor': 'start',
innerHTML: value
});
}

let group = createSVG('g', { let group = createSVG('g', {
transform: `translate(${x}, ${y})` transform: `translate(${x}, ${y})`
}); });
group.appendChild(createSVG("rect", args)); group.appendChild(createSVG("rect", args));
group.appendChild(text);

return group;
}
group.appendChild(textLabel);


export function legendDot(x, y, size, fill='none', label, truncate=false) {
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;

let args = {
className: 'legend-dot',
cx: 0,
cy: 0,
r: size,
fill: fill
};
let text = createSVG('text', {
className: 'legend-dataset-text',
x: 0,
y: 0,
dx: (FONT_SIZE) + 'px',
dy: (FONT_SIZE/3) + 'px',
'font-size': (FONT_SIZE * 1.2) + 'px',
'text-anchor': 'start',
fill: FONT_FILL,
innerHTML: label
});

let group = createSVG('g', {
transform: `translate(${x}, ${y})`
});
group.appendChild(createSVG("circle", args));
group.appendChild(text);
if (value && textValue) {
group.appendChild(textValue);
}


return group; return group;
} }
@@ -273,7 +277,7 @@ export function legendDot(x, y, size, fill='none', label, truncate=false) {
export function makeText(className, x, y, content, options = {}) { export function makeText(className, x, y, content, options = {}) {
let fontSize = options.fontSize || FONT_SIZE; let fontSize = options.fontSize || FONT_SIZE;
let dy = options.dy !== undefined ? options.dy : (fontSize / 2); let dy = options.dy !== undefined ? options.dy : (fontSize / 2);
let fill = options.fill || FONT_FILL;
let fill = options.fill || "var(--charts-label-color)";
let textAnchor = options.textAnchor || 'start'; let textAnchor = options.textAnchor || 'start';
return createSVG('text', { return createSVG('text', {
className: className, className: className,
@@ -287,8 +291,7 @@ export function makeText(className, x, y, content, options = {}) {
}); });
} }


function makeVertLine(x, label, y1, y2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
function makeVertLine(x, label, y1, y2, options = {}) {
let l = createSVG('line', { let l = createSVG('line', {
className: 'line-vertical ' + options.className, className: 'line-vertical ' + options.className,
x1: 0, x1: 0,
@@ -310,7 +313,7 @@ function makeVertLine(x, label, y1, y2, options={}) {
}); });


let line = createSVG('g', { let line = createSVG('g', {
transform: `translate(${ x }, 0)`
transform: `translate(${x}, 0)`
}); });


line.appendChild(l); line.appendChild(l);
@@ -319,13 +322,18 @@ function makeVertLine(x, label, y1, y2, options={}) {
return line; return line;
} }


function makeHoriLine(y, label, x1, x2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.lineType) options.lineType = '';
if (options.shortenNumbers) label = shortenLargeNumber(label);
function makeHoriLine(y, label, x1, x2, options = {}) {
if (!options.lineType) options.lineType = '';
if (options.shortenNumbers) {
if (options.numberFormatter) {
label = options.numberFormatter(label);
} else {
label = shortenLargeNumber(label);
}
}


let className = 'line-horizontal ' + options.className + let className = 'line-horizontal ' + options.className +
(options.lineType === "dashed" ? "dashed": "");
(options.lineType === "dashed" ? "dashed" : "");


let l = createSVG('line', { let l = createSVG('line', {
className: className, className: className,
@@ -344,7 +352,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
dy: (FONT_SIZE / 2 - 2) + 'px', dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
'text-anchor': x1 < x2 ? 'end' : 'start', 'text-anchor': x1 < x2 ? 'end' : 'start',
innerHTML: label+""
innerHTML: label + ""
}); });


let line = createSVG('g', { let line = createSVG('g', {
@@ -352,7 +360,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
'stroke-opacity': 1 'stroke-opacity': 1
}); });


if(text === 0 || text === '0') {
if (text === 0 || text === '0') {
line.style.stroke = "rgba(27, 31, 35, 0.6)"; line.style.stroke = "rgba(27, 31, 35, 0.6)";
} }


@@ -362,19 +370,19 @@ function makeHoriLine(y, label, x1, x2, options={}) {
return line; return line;
} }


export function yLine(y, label, width, options={}) {
export function yLine(y, label, width, options = {}) {
if (!isValidNumber(y)) y = 0; if (!isValidNumber(y)) y = 0;


if(!options.pos) options.pos = 'left';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
if (!options.pos) options.pos = 'left';
if (!options.offset) options.offset = 0;
if (!options.mode) options.mode = 'span';
if (!options.stroke) options.stroke = BASE_LINE_COLOR;
if (!options.className) options.className = '';


let x1 = -1 * AXIS_TICK_LENGTH; let x1 = -1 * AXIS_TICK_LENGTH;
let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0; let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0;


if(options.mode === 'tick' && options.pos === 'right') {
if (options.mode === 'tick' && options.pos === 'right') {
x1 = width + AXIS_TICK_LENGTH; x1 = width + AXIS_TICK_LENGTH;
x2 = width; x2 = width;
} }
@@ -384,22 +392,23 @@ export function yLine(y, label, width, options={}) {
x1 += options.offset; x1 += options.offset;
x2 += options.offset; x2 += options.offset;


if (typeof label === "number") label = round(label);

return makeHoriLine(y, label, x1, x2, { return makeHoriLine(y, label, x1, x2, {
stroke: options.stroke,
className: options.className, className: options.className,
lineType: options.lineType, lineType: options.lineType,
shortenNumbers: options.shortenNumbers
shortenNumbers: options.shortenNumbers,
numberFormatter: options.numberFormatter,
}); });
} }


export function xLine(x, label, height, options={}) {
export function xLine(x, label, height, options = {}) {
if (!isValidNumber(x)) x = 0; if (!isValidNumber(x)) x = 0;


if(!options.pos) options.pos = 'bottom';
if(!options.offset) options.offset = 0;
if(!options.mode) options.mode = 'span';
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.className) options.className = '';
if (!options.pos) options.pos = 'bottom';
if (!options.offset) options.offset = 0;
if (!options.mode) options.mode = 'span';
if (!options.className) options.className = '';


// Draw X axis line in span/tick mode with optional label // Draw X axis line in span/tick mode with optional label
// y2(span) // y2(span)
@@ -415,21 +424,23 @@ export function xLine(x, label, height, options={}) {
let y1 = height + AXIS_TICK_LENGTH; let y1 = height + AXIS_TICK_LENGTH;
let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : height; let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : height;


if(options.mode === 'tick' && options.pos === 'top') {
if (options.mode === 'tick' && options.pos === 'top') {
// top axis ticks // top axis ticks
y1 = -1 * AXIS_TICK_LENGTH; y1 = -1 * AXIS_TICK_LENGTH;
y2 = 0; y2 = 0;
} }


return makeVertLine(x, label, y1, y2, { return makeVertLine(x, label, y1, y2, {
stroke: options.stroke,
className: options.className, className: options.className,
lineType: options.lineType lineType: options.lineType
}); });
} }


export function yMarker(y, label, width, options={}) {
if(!options.labelPos) options.labelPos = 'right';
export function yMarker(y, label, width, options = {}) {
if (!isValidNumber(y)) y = 0;

if (!options.labelPos) options.labelPos = 'right';
if (!options.lineType) options.lineType = 'dashed';
let x = options.labelPos === 'left' ? LABEL_MARGIN let x = options.labelPos === 'left' ? LABEL_MARGIN
: width - getStringWidth(label, 5) - LABEL_MARGIN; : width - getStringWidth(label, 5) - LABEL_MARGIN;


@@ -440,7 +451,7 @@ export function yMarker(y, label, width, options={}) {
dy: (FONT_SIZE / -2) + 'px', dy: (FONT_SIZE / -2) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
'text-anchor': 'start', 'text-anchor': 'start',
innerHTML: label+""
innerHTML: label + ""
}); });


let line = makeHoriLine(y, '', 0, width, { let line = makeHoriLine(y, '', 0, width, {
@@ -454,15 +465,15 @@ export function yMarker(y, label, width, options={}) {
return line; return line;
} }


export function yRegion(y1, y2, width, label, options={}) {
export function yRegion(y1, y2, width, label, options = {}) {
// return a group // return a group
let height = y1 - y2; let height = y1 - y2;


let rect = createSVG('rect', { let rect = createSVG('rect', {
className: `bar mini`, // remove class className: `bar mini`, // remove class
styles: { styles: {
fill: `rgba(228, 234, 239, 0.49)`,
stroke: BASE_LINE_COLOR,
fill: options.fill || `rgba(228, 234, 239, 0.49)`,
stroke: options.stroke || BASE_LINE_COLOR,
'stroke-dasharray': `${width}, ${height}` 'stroke-dasharray': `${width}, ${height}`
}, },
// 'data-point-index': index, // 'data-point-index': index,
@@ -472,9 +483,9 @@ export function yRegion(y1, y2, width, label, options={}) {
height: height height: height
}); });


if(!options.labelPos) options.labelPos = 'right';
if (!options.labelPos) options.labelPos = 'right';
let x = options.labelPos === 'left' ? LABEL_MARGIN let x = options.labelPos === 'left' ? LABEL_MARGIN
: width - getStringWidth(label+"", 4.5) - LABEL_MARGIN;
: width - getStringWidth(label + "", 4.5) - LABEL_MARGIN;


let labelSvg = createSVG('text', { let labelSvg = createSVG('text', {
className: 'chart-label', className: 'chart-label',
@@ -483,7 +494,7 @@ export function yRegion(y1, y2, width, label, options={}) {
dy: (FONT_SIZE / -2) + 'px', dy: (FONT_SIZE / -2) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
'text-anchor': 'start', 'text-anchor': 'start',
innerHTML: label+""
innerHTML: label + ""
}); });


let region = createSVG('g', { let region = createSVG('g', {
@@ -496,11 +507,11 @@ export function yRegion(y1, y2, width, label, options={}) {
return region; return region;
} }


export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, meta={}) {
export function datasetBar(x, yTop, width, color, label = '', index = 0, offset = 0, meta = {}) {
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine); let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
y -= offset; y -= offset;


if(height === 0) {
if (height === 0) {
height = meta.minHeight; height = meta.minHeight;
y -= meta.minHeight; y -= meta.minHeight;
} }
@@ -511,6 +522,26 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
if (!isValidNumber(height, true)) height = 0; if (!isValidNumber(height, true)) height = 0;
if (!isValidNumber(width, true)) width = 0; if (!isValidNumber(width, true)) width = 0;


// x y h w

// M{x},{y+r}
// q0,-{r} {r},-{r}
// q{r},0 {r},{r}
// v{h-r}
// h-{w}z

// let radius = width/2;
// let pathStr = `M${x},${y+radius} q0,-${radius} ${radius},-${radius} q${radius},0 ${radius},${radius} v${height-radius} h-${width}z`

// let rect = createSVG('path', {
// className: 'bar mini',
// d: pathStr,
// styles: { fill: color },
// x: x,
// y: y,
// 'data-point-index': index,
// });

let rect = createSVG('rect', { let rect = createSVG('rect', {
className: `bar mini`, className: `bar mini`,
style: `fill: ${color}`, style: `fill: ${color}`,
@@ -523,14 +554,14 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m


label += ""; label += "";


if(!label && !label.length) {
if (!label && !label.length) {
return rect; return rect;
} else { } else {
rect.setAttribute('y', 0); rect.setAttribute('y', 0);
rect.setAttribute('x', 0); rect.setAttribute('x', 0);
let text = createSVG('text', { let text = createSVG('text', {
className: 'data-point-value', className: 'data-point-value',
x: width/2,
x: width / 2,
y: 0, y: 0,
dy: (FONT_SIZE / 2 * -1) + 'px', dy: (FONT_SIZE / 2 * -1) + 'px',
'font-size': FONT_SIZE + 'px', 'font-size': FONT_SIZE + 'px',
@@ -549,9 +580,9 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
} }
} }


export function datasetDot(x, y, radius, color, label='', index=0) {
export function datasetDot(x, y, radius, color, label = '', index = 0, hideDotBorder = false) {
let dot = createSVG('circle', { let dot = createSVG('circle', {
style: `fill: ${color}`,
style: `fill: ${color}; ${hideDotBorder ? `stroke: ${color}`: ''}`,
'data-point-index': index, 'data-point-index': index,
cx: x, cx: x,
cy: y, cy: y,
@@ -560,7 +591,7 @@ export function datasetDot(x, y, radius, color, label='', index=0) {


label += ""; label += "";


if(!label && !label.length) {
if (!label && !label.length) {
return dot; return dot;
} else { } else {
dot.setAttribute('cy', 0); dot.setAttribute('cy', 0);
@@ -587,7 +618,7 @@ export function datasetDot(x, y, radius, color, label='', index=0) {
} }
} }


export function getPaths(xList, yList, color, options={}, meta={}) {
export function getPaths(xList, yList, color, options = {}, meta = {}) {
let pointsList = yList.map((y, i) => (xList[i] + ',' + y)); let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
let pointsStr = pointsList.join("L"); let pointsStr = pointsList.join("L");


@@ -595,10 +626,10 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
if (options.spline) if (options.spline)
pointsStr = getSplineCurvePointsStr(xList, yList); pointsStr = getSplineCurvePointsStr(xList, yList);


let path = makePath("M"+pointsStr, 'line-graph-path', color);
let path = makePath("M" + pointsStr, 'line-graph-path', color);


// HeatLine // HeatLine
if(options.heatline) {
if (options.heatline) {
let gradient_id = makeGradient(meta.svgDefs, color); let gradient_id = makeGradient(meta.svgDefs, color);
path.style.stroke = `url(#${gradient_id})`; path.style.stroke = `url(#${gradient_id})`;
} }
@@ -608,7 +639,7 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
}; };


// Region // Region
if(options.regionFill) {
if (options.regionFill) {
let gradient_id_region = makeGradient(meta.svgDefs, color, true); let gradient_id_region = makeGradient(meta.svgDefs, color, true);


let pathStr = "M" + `${xList[0]},${meta.zeroLine}L` + pointsStr + `L${xList.slice(-1)[0]},${meta.zeroLine}`; let pathStr = "M" + `${xList[0]},${meta.zeroLine}L` + pointsStr + `L${xList.slice(-1)[0]},${meta.zeroLine}`;
@@ -621,7 +652,7 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
export let makeOverlay = { export let makeOverlay = {
'bar': (unit) => { 'bar': (unit) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'rect') {
if (unit.nodeName !== 'rect') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -629,7 +660,7 @@ export let makeOverlay = {
overlay.style.fill = '#000000'; overlay.style.fill = '#000000';
overlay.style.opacity = '0.4'; overlay.style.opacity = '0.4';


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
return overlay; return overlay;
@@ -637,7 +668,7 @@ export let makeOverlay = {


'dot': (unit) => { 'dot': (unit) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'circle') {
if (unit.nodeName !== 'circle') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -648,7 +679,7 @@ export let makeOverlay = {
overlay.setAttribute('fill', fill); overlay.setAttribute('fill', fill);
overlay.style.opacity = '0.6'; overlay.style.opacity = '0.6';


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
return overlay; return overlay;
@@ -656,7 +687,7 @@ export let makeOverlay = {


'heat_square': (unit) => { 'heat_square': (unit) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'circle') {
if (unit.nodeName !== 'circle') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -667,7 +698,7 @@ export let makeOverlay = {
overlay.setAttribute('fill', fill); overlay.setAttribute('fill', fill);
overlay.style.opacity = '0.6'; overlay.style.opacity = '0.6';


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
return overlay; return overlay;
@@ -677,7 +708,7 @@ export let makeOverlay = {
export let updateOverlay = { export let updateOverlay = {
'bar': (unit, overlay) => { 'bar': (unit, overlay) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'rect') {
if (unit.nodeName !== 'rect') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -688,14 +719,14 @@ export let updateOverlay = {
overlay.setAttribute(attr.name, attr.nodeValue); overlay.setAttribute(attr.name, attr.nodeValue);
}); });


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
}, },


'dot': (unit, overlay) => { 'dot': (unit, overlay) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'circle') {
if (unit.nodeName !== 'circle') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -706,14 +737,14 @@ export let updateOverlay = {
overlay.setAttribute(attr.name, attr.nodeValue); overlay.setAttribute(attr.name, attr.nodeValue);
}); });


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
}, },


'heat_square': (unit, overlay) => { 'heat_square': (unit, overlay) => {
let transformValue; let transformValue;
if(unit.nodeName !== 'circle') {
if (unit.nodeName !== 'circle') {
transformValue = unit.getAttribute('transform'); transformValue = unit.getAttribute('transform');
unit = unit.childNodes[0]; unit = unit.childNodes[0];
} }
@@ -724,7 +755,7 @@ export let updateOverlay = {
overlay.setAttribute(attr.name, attr.nodeValue); overlay.setAttribute(attr.name, attr.nodeValue);
}); });


if(transformValue) {
if (transformValue) {
overlay.setAttribute('transform', transformValue); overlay.setAttribute('transform', transformValue);
} }
}, },


+ 2
- 2
src/js/utils/export.js Wyświetl plik

@@ -4,13 +4,13 @@ import { CSSTEXT } from '../../css/chartsCss';
export function downloadFile(filename, data) { export function downloadFile(filename, data) {
var a = document.createElement('a'); var a = document.createElement('a');
a.style = "display: none"; a.style = "display: none";
var blob = new Blob(data, {type: "image/svg+xml; charset=utf-8"});
var blob = new Blob(data, { type: "image/svg+xml; charset=utf-8" });
var url = window.URL.createObjectURL(blob); var url = window.URL.createObjectURL(blob);
a.href = url; a.href = url;
a.download = filename; a.download = filename;
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
setTimeout(function(){
setTimeout(function () {
document.body.removeChild(a); document.body.removeChild(a);
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
}, 300); }, 300);


+ 26
- 0
src/js/utils/helpers.js Wyświetl plik

@@ -115,3 +115,29 @@ export function round(d) {
// https://www.jacklmoore.com/notes/rounding-in-javascript/ // https://www.jacklmoore.com/notes/rounding-in-javascript/
return Number(Math.round(d + 'e4') + 'e-4'); return Number(Math.round(d + 'e4') + 'e-4');
} }

/**
* Creates a deep clone of an object
* @param {Object} candidate Any Object
*/
export function deepClone(candidate) {
let cloned, value, key;
if (candidate instanceof Date) {
return new Date(candidate.getTime());
}
if (typeof candidate !== "object" || candidate === null) {
return candidate;
}
cloned = Array.isArray(candidate) ? [] : {};
for (key in candidate) {
value = candidate[key];
cloned[key] = deepClone(value);
}
return cloned;
}

+ 38
- 30
src/js/utils/intervals.js Wyświetl plik

@@ -5,25 +5,25 @@ function normalize(x) {
// Returns normalized number and exponent // Returns normalized number and exponent
// https://stackoverflow.com/q/9383593/6495043 // https://stackoverflow.com/q/9383593/6495043


if(x===0) {
if (x === 0) {
return [0, 0]; return [0, 0];
} }
if(isNaN(x)) {
return {mantissa: -6755399441055744, exponent: 972};
if (isNaN(x)) {
return { mantissa: -6755399441055744, exponent: 972 };
} }
var sig = x > 0 ? 1 : -1; var sig = x > 0 ? 1 : -1;
if(!isFinite(x)) {
return {mantissa: sig * 4503599627370496, exponent: 972};
if (!isFinite(x)) {
return { mantissa: sig * 4503599627370496, exponent: 972 };
} }


x = Math.abs(x); x = Math.abs(x);
var exp = Math.floor(Math.log10(x)); var exp = Math.floor(Math.log10(x));
var man = x/Math.pow(10, exp);
var man = x / Math.pow(10, exp);


return [sig * man, exp]; return [sig * man, exp];
} }


function getChartRangeIntervals(max, min=0) {
function getChartRangeIntervals(max, min = 0) {
let upperBound = Math.ceil(max); let upperBound = Math.ceil(max);
let lowerBound = Math.floor(min); let lowerBound = Math.floor(min);
let range = upperBound - lowerBound; let range = upperBound - lowerBound;
@@ -32,38 +32,38 @@ function getChartRangeIntervals(max, min=0) {
let partSize = 1; let partSize = 1;


// To avoid too many partitions // To avoid too many partitions
if(range > 5) {
if(range % 2 !== 0) {
if (range > 5) {
if (range % 2 !== 0) {
upperBound++; upperBound++;
// Recalc range // Recalc range
range = upperBound - lowerBound; range = upperBound - lowerBound;
} }
noOfParts = range/2;
noOfParts = range / 2;
partSize = 2; partSize = 2;
} }


// Special case: 1 and 2 // Special case: 1 and 2
if(range <= 2) {
if (range <= 2) {
noOfParts = 4; noOfParts = 4;
partSize = range/noOfParts;
partSize = range / noOfParts;
} }


// Special case: 0 // Special case: 0
if(range === 0) {
if (range === 0) {
noOfParts = 5; noOfParts = 5;
partSize = 1; partSize = 1;
} }


let intervals = []; let intervals = [];
for(var i = 0; i <= noOfParts; i++){
for (var i = 0; i <= noOfParts; i++) {
intervals.push(lowerBound + partSize * i); intervals.push(lowerBound + partSize * i);
} }
return intervals; return intervals;
} }


function getChartIntervals(maxValue, minValue=0) {
function getChartIntervals(maxValue, minValue = 0) {
let [normalMaxValue, exponent] = normalize(maxValue); let [normalMaxValue, exponent] = normalize(maxValue);
let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0;
let normalMinValue = minValue ? minValue / Math.pow(10, exponent) : 0;


// Allow only 7 significant digits // Allow only 7 significant digits
normalMaxValue = normalMaxValue.toFixed(6); normalMaxValue = normalMaxValue.toFixed(6);
@@ -73,7 +73,7 @@ function getChartIntervals(maxValue, minValue=0) {
return intervals; return intervals;
} }


export function calcChartIntervals(values, withMinimum=false) {
export function calcChartIntervals(values, withMinimum = false, range = {}) {
//*** Where the magic happens *** //*** Where the magic happens ***


// Calculates best-fit y intervals from given values // Calculates best-fit y intervals from given values
@@ -82,6 +82,14 @@ export function calcChartIntervals(values, withMinimum=false) {
let maxValue = Math.max(...values); let maxValue = Math.max(...values);
let minValue = Math.min(...values); let minValue = Math.min(...values);


if (range.max !== undefined) {
maxValue = maxValue > range.max ? maxValue : range.max;
}
if (range.min !== undefined) {
minValue = minValue < range.min ? minValue : range.min;
}

// Exponent to be used for pretty print // Exponent to be used for pretty print
let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars


@@ -92,7 +100,7 @@ export function calcChartIntervals(values, withMinimum=false) {


// Then unshift the negative values // Then unshift the negative values
let value = 0; let value = 0;
for(var i = 1; value < absMinValue; i++) {
for (var i = 1; value < absMinValue; i++) {
value += intervalSize; value += intervalSize;
intervals.unshift((-1) * value); intervals.unshift((-1) * value);
} }
@@ -101,9 +109,9 @@ export function calcChartIntervals(values, withMinimum=false) {


// CASE I: Both non-negative // CASE I: Both non-negative


if(maxValue >= 0 && minValue >= 0) {
if (maxValue >= 0 && minValue >= 0) {
exponent = normalize(maxValue)[1]; exponent = normalize(maxValue)[1];
if(!withMinimum) {
if (!withMinimum) {
intervals = getChartIntervals(maxValue); intervals = getChartIntervals(maxValue);
} else { } else {
intervals = getChartIntervals(maxValue, minValue); intervals = getChartIntervals(maxValue, minValue);
@@ -112,7 +120,7 @@ export function calcChartIntervals(values, withMinimum=false) {


// CASE II: Only minValue negative // CASE II: Only minValue negative


else if(maxValue > 0 && minValue < 0) {
else if (maxValue > 0 && minValue < 0) {
// `withMinimum` irrelevant in this case, // `withMinimum` irrelevant in this case,
// We'll be handling both sides of zero separately // We'll be handling both sides of zero separately
// (both starting from zero) // (both starting from zero)
@@ -121,7 +129,7 @@ export function calcChartIntervals(values, withMinimum=false) {


let absMinValue = Math.abs(minValue); let absMinValue = Math.abs(minValue);


if(maxValue >= absMinValue) {
if (maxValue >= absMinValue) {
exponent = normalize(maxValue)[1]; exponent = normalize(maxValue)[1];
intervals = getPositiveFirstIntervals(maxValue, absMinValue); intervals = getPositiveFirstIntervals(maxValue, absMinValue);
} else { } else {
@@ -135,7 +143,7 @@ export function calcChartIntervals(values, withMinimum=false) {


// CASE III: Both non-positive // CASE III: Both non-positive


else if(maxValue <= 0 && minValue <= 0) {
else if (maxValue <= 0 && minValue <= 0) {
// Mirrored Case I: // Mirrored Case I:
// Work with positives, then reverse the sign and array // Work with positives, then reverse the sign and array


@@ -143,7 +151,7 @@ export function calcChartIntervals(values, withMinimum=false) {
let pseudoMinValue = Math.abs(maxValue); let pseudoMinValue = Math.abs(maxValue);


exponent = normalize(pseudoMaxValue)[1]; exponent = normalize(pseudoMaxValue)[1];
if(!withMinimum) {
if (!withMinimum) {
intervals = getChartIntervals(pseudoMaxValue); intervals = getChartIntervals(pseudoMaxValue);
} else { } else {
intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue); intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue);
@@ -158,11 +166,11 @@ export function calcChartIntervals(values, withMinimum=false) {
export function getZeroIndex(yPts) { export function getZeroIndex(yPts) {
let zeroIndex; let zeroIndex;
let interval = getIntervalSize(yPts); let interval = getIntervalSize(yPts);
if(yPts.indexOf(0) >= 0) {
if (yPts.indexOf(0) >= 0) {
// the range has a given zero // the range has a given zero
// zero-line on the chart // zero-line on the chart
zeroIndex = yPts.indexOf(0); zeroIndex = yPts.indexOf(0);
} else if(yPts[0] > 0) {
} else if (yPts[0] > 0) {
// Minimum value is positive // Minimum value is positive
// zero-line is off the chart: below // zero-line is off the chart: below
let min = yPts[0]; let min = yPts[0];
@@ -181,7 +189,7 @@ export function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
let part = range * 1.0 / noOfIntervals; let part = range * 1.0 / noOfIntervals;
let intervals = []; let intervals = [];


for(var i = 0; i <= noOfIntervals; i++) {
for (var i = 0; i <= noOfIntervals; i++) {
intervals.push(min + part * i); intervals.push(min + part * i);
} }


@@ -193,7 +201,7 @@ export function getIntervalSize(orderedArray) {
} }


export function getValueRange(orderedArray) { export function getValueRange(orderedArray) {
return orderedArray[orderedArray.length-1] - orderedArray[0];
return orderedArray[orderedArray.length - 1] - orderedArray[0];
} }


export function scale(val, yAxis) { export function scale(val, yAxis) {
@@ -210,7 +218,7 @@ export function isInRange2D(coord, minCoord, maxCoord) {
} }


export function getClosestInArray(goal, arr, index = false) { export function getClosestInArray(goal, arr, index = false) {
let closest = arr.reduce(function(prev, curr) {
let closest = arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
}, []); }, []);


@@ -226,7 +234,7 @@ export function calcDistribution(values, distributionSize) {
let distributionStep = 1 / (distributionSize - 1); let distributionStep = 1 / (distributionSize - 1);
let distribution = []; let distribution = [];


for(var i = 0; i < distributionSize; i++) {
for (var i = 0; i < distributionSize; i++) {
let checkpoint = dataMaxValue * (distributionStep * i); let checkpoint = dataMaxValue * (distributionStep * i);
distribution.push(checkpoint); distribution.push(checkpoint);
} }


+ 1470
- 3686
yarn.lock
Plik diff jest za duży
Wyświetl plik


Ładowanie…
Anuluj
Zapisz