Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8ae43fac6 | ||
|
|
0ee7f9a30f | ||
|
|
4967fc9e07 | ||
|
|
8cc3b45c18 | ||
|
|
bd77462d01 | ||
|
|
17994abb92 | ||
|
|
77cc7fde6c | ||
|
|
dc6ef83a7b | ||
|
|
f5fc2c1bf7 | ||
|
|
b6403b83c3 | ||
|
|
2e3011c267 | ||
|
|
2aab8dd315 | ||
|
|
ea95aece5a | ||
|
|
f2d2eb1e9e | ||
|
|
2b455e95a6 | ||
|
|
624cf70f03 | ||
|
|
5ea1d33fdf | ||
|
|
46191e7a53 | ||
|
|
95a19f09a3 | ||
|
|
8bc8231b21 | ||
|
|
c18c076adc | ||
|
|
a765508bbd | ||
|
|
cdf5fbe33b | ||
|
|
a26a81c866 | ||
|
|
f19d15f112 | ||
|
|
07597ebe87 | ||
|
|
9c0ecc89be | ||
|
|
3d6a349425 | ||
|
|
ce4126b3b1 | ||
|
|
f145d6bbe9 | ||
|
|
b7f20aab15 | ||
|
|
7c9cf65385 | ||
|
|
61717aee95 | ||
|
|
304d23502a | ||
|
|
db12dcf27c | ||
|
|
1b4e206a1e | ||
|
|
8d8096869f | ||
|
|
fa559cc8bd | ||
|
|
ed8f97efd5 | ||
|
|
8d68d9933e | ||
|
|
72d5507c6f | ||
|
|
d6ab5b952e | ||
|
|
4854591a89 | ||
|
|
a0265677a2 | ||
|
|
ba0d3703f5 | ||
|
|
bbe91840f7 | ||
|
|
94a7e02d75 | ||
|
|
d8984ec5ae | ||
|
|
067f5ea7f8 | ||
|
|
263f38c9ec | ||
|
|
7e13f81063 | ||
|
|
9cc7bde398 | ||
|
|
ce64f01a8b | ||
|
|
583d82a96e | ||
|
|
8308b9fd90 | ||
|
|
f413c75932 | ||
|
|
86bbb6d2aa | ||
|
|
d386d21e2f | ||
|
|
8101b1822e | ||
|
|
cff7ce4c27 | ||
|
|
690a7f4467 | ||
|
|
03f6436be4 | ||
|
|
8524e6cbd6 | ||
|
|
1181660ed1 | ||
|
|
26671b143b | ||
|
|
5d2c141f14 | ||
|
|
e2d7ce8b21 | ||
|
|
73f1d9b1e3 | ||
|
|
dc00b46a7a | ||
|
|
fd28107795 | ||
|
|
7cfa35a418 | ||
|
|
d84614cb65 | ||
|
|
610cfb1f6d | ||
|
|
767a905967 | ||
|
|
56a7374277 | ||
|
|
562a9cba55 | ||
|
|
4f4dd4d36f | ||
|
|
0257bc370e | ||
|
|
7dde276477 | ||
|
|
b12b916584 | ||
|
|
a7b06bb027 | ||
|
|
9dd24aa7a7 | ||
|
|
4d8321e9be | ||
|
|
399ff37b6a | ||
|
|
dc49b29d39 | ||
|
|
51a6ed33a5 | ||
|
|
f8c3f9d7b4 | ||
|
|
d3909d49c1 | ||
|
|
255e806533 | ||
|
|
6cb9bf6bd3 | ||
|
|
5e17dea6de | ||
|
|
89fc842330 | ||
|
|
63e0a0bdf4 | ||
|
|
d2dd27dfab | ||
|
|
846a21ae6a | ||
|
|
5b00da4974 | ||
|
|
069a5f7451 |
9
.deepsource.toml
Normal file
9
.deepsource.toml
Normal file
@ -0,0 +1,9 @@
|
||||
version = 1
|
||||
|
||||
[[analyzers]]
|
||||
name = "javascript"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
environment = ["browser"]
|
||||
style_guide = "standard"
|
||||
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@ -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
|
||||
19
.github/workflows/publish.yml
vendored
Normal file
19
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Publish on NPM
|
||||
on:
|
||||
push:
|
||||
branches: [ v2-beta ]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- uses: JS-DevTools/npm-publish@v3
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
tag: next
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -60,4 +60,7 @@ typings/
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# npm build output
|
||||
dist
|
||||
|
||||
.DS_Store
|
||||
4459
dist/frappe-charts.cjs.js
vendored
4459
dist/frappe-charts.cjs.js
vendored
File diff suppressed because it is too large
Load Diff
4467
dist/frappe-charts.esm.js
vendored
4467
dist/frappe-charts.esm.js
vendored
File diff suppressed because it is too large
Load Diff
104
dist/frappe-charts.min.css
vendored
104
dist/frappe-charts.min.css
vendored
@ -1,104 +0,0 @@
|
||||
.chart-container {
|
||||
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; }
|
||||
.chart-container .axis, .chart-container .chart-label {
|
||||
fill: #313B44; }
|
||||
.chart-container .axis line, .chart-container .chart-label line {
|
||||
stroke: #E2E6E9; }
|
||||
.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;
|
||||
text-align: center;
|
||||
background: #FFFFFF;
|
||||
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; }
|
||||
.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;
|
||||
font-weight: 600; }
|
||||
.graph-svg-tip .svg-pointer {
|
||||
position: absolute;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 2px;
|
||||
background: white;
|
||||
transform: rotate(45deg);
|
||||
margin-top: -7px;
|
||||
margin-left: -6px; }
|
||||
.graph-svg-tip.comparison {
|
||||
text-align: left;
|
||||
padding: 0px;
|
||||
pointer-events: none; }
|
||||
.graph-svg-tip.comparison .title {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
color: #313B44;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
text-transform: uppercase; }
|
||||
.graph-svg-tip.comparison ul {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
list-style: none; }
|
||||
.graph-svg-tip.comparison ul.tooltip-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 5px; }
|
||||
.graph-svg-tip.comparison li {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
padding: 5px 15px 15px 15px; }
|
||||
.graph-svg-tip.comparison li .tooltip-legend {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin-right: 8px;
|
||||
border-radius: 2px; }
|
||||
.graph-svg-tip.comparison li .tooltip-label {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
max-width: 100px;
|
||||
color: #313B44;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap; }
|
||||
.graph-svg-tip.comparison li .tooltip-value {
|
||||
color: #192734; }
|
||||
1
dist/frappe-charts.umd.js
vendored
1
dist/frappe-charts.umd.js
vendored
File diff suppressed because one or more lines are too long
@ -1,2 +0,0 @@
|
||||
plugins:
|
||||
- jekyll-redirect-from
|
||||
7
docs/assets/css/bootstrap.min.css
vendored
7
docs/assets/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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 */
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@ -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],
|
||||
};
|
||||
@ -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',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
2
docs/assets/js/frappe-charts.min.js
vendored
2
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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();
|
||||
});
|
||||
648
docs/assets/js/index.min.js
vendored
648
docs/assets/js/index.min.js
vendored
@ -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
|
||||
File diff suppressed because one or more lines are too long
323
docs/index.html
323
docs/index.html
@ -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"> <!--HTML-->
|
||||
<div id="chart"></div></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"> <script src="https://unpkg.com/frappe-charts@1.1.0"></script></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>
|
||||
17
package.json
17
package.json
@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "frappe-charts",
|
||||
"version": "1.5.2",
|
||||
"main": "dist/frappe-charts.cjs.js",
|
||||
"common": "dist/frappe-charts.cjs.js",
|
||||
"version": "2.0.0-rc26",
|
||||
"main": "dist/frappe-charts.esm.js",
|
||||
"module": "dist/frappe-charts.esm.js",
|
||||
"browser": "dist/frappe-charts.umd.js",
|
||||
"unpkg": "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",
|
||||
"directories": {
|
||||
"doc": "docs"
|
||||
@ -39,10 +40,12 @@
|
||||
"@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"
|
||||
"rollup-plugin-scss": "^4.0.1",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"sass": "^1.86.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import commonjs from 'rollup-plugin-commonjs';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
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';
|
||||
|
||||
|
||||
@ -12,6 +13,7 @@ export default [
|
||||
{
|
||||
input: 'src/js/index.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
name: 'frappe-charts',
|
||||
file: pkg.browser,
|
||||
format: 'umd'
|
||||
@ -22,7 +24,8 @@ export default [
|
||||
exclude: ['node_modules/**']
|
||||
}),
|
||||
terser(),
|
||||
scss({ output: 'dist/frappe-charts.min.css' })
|
||||
scss({ fileName: 'frappe-charts.min.css' }),
|
||||
bundleSize()
|
||||
]
|
||||
},
|
||||
|
||||
@ -30,14 +33,16 @@ export default [
|
||||
{
|
||||
input: 'src/js/chart.js',
|
||||
output: [
|
||||
{ file: pkg.common, format: 'cjs' },
|
||||
{ file: pkg.module, format: 'es' }
|
||||
{ file: pkg.common, format: 'cjs', sourcemap: true },
|
||||
{ file: pkg.module, format: 'es', sourcemap: true },
|
||||
],
|
||||
plugins: [
|
||||
babel({
|
||||
exclude: ['node_modules/**']
|
||||
}),
|
||||
postcss()
|
||||
terser(),
|
||||
postcss(),
|
||||
bundleSize()
|
||||
]
|
||||
}
|
||||
];
|
||||
@ -1,54 +1,58 @@
|
||||
:root {
|
||||
--fr-label-color: #313b44;
|
||||
--fr-axis-line-color: #E2E6E9;
|
||||
--charts-label-color: #313b44;
|
||||
--charts-axis-line-color: #f4f5f6;
|
||||
|
||||
--fr-stroke-width: 2px;
|
||||
--fr-dataset-circle-stroke: #FFFFFF;
|
||||
--fr-dataset-circle-stroke-width: var(--fr-stroke-width);
|
||||
--charts-tooltip-title: var(--charts-label-color);
|
||||
--charts-tooltip-label: var(--charts-label-color);
|
||||
--charts-tooltip-value: #192734;
|
||||
--charts-tooltip-bg: #ffffff;
|
||||
|
||||
--fr-tooltip-title: var(--fr-label-color);
|
||||
--fr-tooltip-label: var(--fr-label-color);
|
||||
--fr-tooltip-value: #192734;
|
||||
--fr-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 {
|
||||
position: relative; /* for absolutely positioned tooltip */
|
||||
|
||||
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: var(--fr-label-color);
|
||||
.axis,
|
||||
.chart-label {
|
||||
fill: var(--charts-label-color);
|
||||
|
||||
line {
|
||||
stroke: var(--fr-axis-line-color);
|
||||
stroke: var(--charts-axis-line-color);
|
||||
}
|
||||
}
|
||||
|
||||
.dataset-units {
|
||||
circle {
|
||||
stroke: var(--fr-dataset-circle-stroke);
|
||||
stroke-width: var(--fr-dataset-circle-stroke-width);
|
||||
stroke: var(--charts-dataset-circle-stroke);
|
||||
stroke-width: var(--charts-dataset-circle-stroke-width);
|
||||
}
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
stroke-opacity: 1;
|
||||
stroke-width: var(--fr-stroke-width);
|
||||
stroke-width: var(--charts-stroke-width);
|
||||
}
|
||||
}
|
||||
|
||||
.dataset-path {
|
||||
stroke-width: var(--fr-stroke-width);
|
||||
stroke-width: var(--charts-stroke-width);
|
||||
}
|
||||
|
||||
.path-group {
|
||||
path {
|
||||
fill: none;
|
||||
stroke-opacity: 1;
|
||||
stroke-width: var(--fr-stroke-width);
|
||||
stroke-width: var(--charts-stroke-width);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,12 +73,12 @@
|
||||
}
|
||||
|
||||
.legend-dataset-label {
|
||||
fill: var(--fr-tooltip-label);
|
||||
fill: var(--charts-legend-label);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.legend-dataset-value {
|
||||
fill: var(--fr-tooltip-value);
|
||||
fill: var(--charts-legend-value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +88,10 @@
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
background: var(--fr-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);
|
||||
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 {
|
||||
@ -110,7 +116,7 @@
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 2px;
|
||||
background: var(--fr-tooltip-bg);
|
||||
background: var(--charts-tooltip-bg);
|
||||
transform: rotate(45deg);
|
||||
margin-top: -7px;
|
||||
margin-left: -6px;
|
||||
@ -125,11 +131,15 @@
|
||||
display: block;
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
color: var(--fr-tooltip-title);
|
||||
color: var(--charts-tooltip-title);
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
strong {
|
||||
color: var(--charts-tooltip-value);
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@ -163,16 +173,15 @@
|
||||
.tooltip-label {
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
max-width: 100px;
|
||||
line-height: 1.25;
|
||||
max-width: 150px;
|
||||
white-space: normal;
|
||||
|
||||
color: var(--fr-tooltip-label);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--charts-tooltip-label);
|
||||
}
|
||||
|
||||
.tooltip-value {
|
||||
color: var(--fr-tooltip-value);
|
||||
color: var(--charts-tooltip-value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import '../css/charts.scss';
|
||||
|
||||
// import MultiAxisChart from './charts/MultiAxisChart';
|
||||
import PercentageChart from './charts/PercentageChart';
|
||||
import PieChart from './charts/PieChart';
|
||||
import Heatmap from './charts/Heatmap';
|
||||
@ -10,7 +9,6 @@ import DonutChart from './charts/DonutChart';
|
||||
const chartTypes = {
|
||||
bar: AxisChart,
|
||||
line: AxisChart,
|
||||
// multiaxis: MultiAxisChart,
|
||||
percentage: PercentageChart,
|
||||
heatmap: Heatmap,
|
||||
pie: PieChart,
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import BaseChart from './BaseChart';
|
||||
import { truncateString } from '../utils/draw-utils';
|
||||
import { legendDot } from '../utils/draw';
|
||||
import { round } from '../utils/helpers';
|
||||
import { getExtraWidth } from '../utils/constants';
|
||||
|
||||
export default class AggregationChart extends BaseChart {
|
||||
constructor(parent, args) {
|
||||
@ -15,6 +13,7 @@ export default class AggregationChart extends BaseChart {
|
||||
this.config.formatTooltipY = (args.tooltipOptions || {}).formatTooltipY;
|
||||
this.config.maxSlices = args.maxSlices || 20;
|
||||
this.config.maxLegendPoints = args.maxLegendPoints || 20;
|
||||
this.config.legendRowHeight = 60;
|
||||
}
|
||||
|
||||
calc() {
|
||||
@ -31,17 +30,17 @@ export default class AggregationChart extends BaseChart {
|
||||
}).filter(d => { return d[0] >= 0; }); // keep only positive results
|
||||
|
||||
let totals = allTotals;
|
||||
if(allTotals.length > maxSlices) {
|
||||
if (allTotals.length > maxSlices) {
|
||||
// Prune and keep a grey area for rest as per maxSlices
|
||||
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;
|
||||
remaining.map(d => {sumOfRemaining += d[0];});
|
||||
remaining.map(d => { sumOfRemaining += d[0]; });
|
||||
totals.push([sumOfRemaining, 'Rest']);
|
||||
this.colors[maxSlices-1] = 'grey';
|
||||
this.colors[maxSlices - 1] = 'grey';
|
||||
}
|
||||
|
||||
s.labels = [];
|
||||
@ -62,36 +61,22 @@ export default class AggregationChart extends BaseChart {
|
||||
let s = this.state;
|
||||
this.legendArea.textContent = '';
|
||||
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 += 60;
|
||||
}
|
||||
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,
|
||||
12,
|
||||
3,
|
||||
this.colors[i],
|
||||
`${label}: ${formatted}`,
|
||||
d,
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import BaseChart from './BaseChart';
|
||||
import { dataPrep, zeroDataPrep, getShortenedLabels } from '../utils/axis-chart-utils';
|
||||
import { AXIS_LEGEND_BAR_SIZE } from '../utils/constants';
|
||||
import { getComponent } from '../objects/ChartComponents';
|
||||
import { getOffset, fire } from '../utils/dom';
|
||||
import { calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex, scale, getClosestInArray } from '../utils/intervals';
|
||||
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 {
|
||||
constructor(parent, args) {
|
||||
@ -23,7 +24,7 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
setMeasures() {
|
||||
if(this.data.datasets.length <= 1) {
|
||||
if (this.data.datasets.length <= 1) {
|
||||
this.config.showLegend = 0;
|
||||
this.measures.paddings.bottom = 30;
|
||||
}
|
||||
@ -39,24 +40,29 @@ export default class AxisChart extends BaseChart {
|
||||
this.config.yAxisMode = options.axisOptions.yAxisMode || 'span';
|
||||
this.config.xIsSeries = options.axisOptions.xIsSeries || 0;
|
||||
this.config.shortenYAxisNumbers = options.axisOptions.shortenYAxisNumbers || 0;
|
||||
this.config.numberFormatter = options.axisOptions.numberFormatter;
|
||||
this.config.seriesLabelSpaceRatio = options.axisOptions.seriesLabelSpaceRatio
|
||||
|
||||
this.config.yAxisRange = options.axisOptions.yAxisRange || {},
|
||||
|
||||
this.config.formatTooltipX = options.tooltipOptions.formatTooltipX;
|
||||
this.config.formatTooltipY = options.tooltipOptions.formatTooltipY;
|
||||
|
||||
this.config.valuesOverPoints = options.valuesOverPoints;
|
||||
this.config.legendRowHeight = 30;
|
||||
}
|
||||
|
||||
prepareData(data=this.data) {
|
||||
prepareData(data = this.data) {
|
||||
return dataPrep(data, this.type);
|
||||
}
|
||||
|
||||
prepareFirstData(data=this.data) {
|
||||
prepareFirstData(data = this.data) {
|
||||
return zeroDataPrep(data);
|
||||
}
|
||||
|
||||
calc(onlyWidthChange = false) {
|
||||
this.calcXPositions();
|
||||
if(!onlyWidthChange) {
|
||||
if (!onlyWidthChange) {
|
||||
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
||||
}
|
||||
this.makeDataByIndex();
|
||||
@ -67,9 +73,9 @@ export default class AxisChart extends BaseChart {
|
||||
let labels = this.data.labels;
|
||||
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
|
||||
s.xOffset = s.unitWidth/2;
|
||||
s.xOffset = s.unitWidth / 2;
|
||||
|
||||
// // For a pure Line Chart
|
||||
// s.unitWidth = this.width/(s.datasetLength - 1);
|
||||
@ -84,7 +90,7 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
calcYAxisParameters(dataValues, withMinimum = 'false') {
|
||||
const yPts = calcChartIntervals(dataValues, withMinimum);
|
||||
const yPts = calcChartIntervals(dataValues, withMinimum, this.config.yAxisRange);
|
||||
const scaleMultiplier = this.height / getValueRange(yPts);
|
||||
const intervalHeight = getIntervalSize(yPts) * scaleMultiplier;
|
||||
const zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
||||
@ -110,7 +116,7 @@ export default class AxisChart extends BaseChart {
|
||||
let values = d.values;
|
||||
let cumulativeYs = d.cumulativeYs || [];
|
||||
return {
|
||||
name: d.name.replace(/<|>|&/g, (char) => char == '&' ? '&' : char == '<' ? '<' : '>'),
|
||||
name: d.name && d.name.replace(/<|>|&/g, (char) => char == '&' ? '&' : char == '<' ? '<' : '>'),
|
||||
index: i,
|
||||
chartType: d.chartType,
|
||||
|
||||
@ -125,14 +131,14 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
calcYExtremes() {
|
||||
let s = this.state;
|
||||
if(this.barOptions.stacked) {
|
||||
if (this.barOptions.stacked) {
|
||||
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativeYPos;
|
||||
return;
|
||||
}
|
||||
s.yExtremes = new Array(s.datasetLength).fill(9999);
|
||||
s.datasets.map(d => {
|
||||
d.yPositions.map((pos, j) => {
|
||||
if(pos < s.yExtremes[j]) {
|
||||
if (pos < s.yExtremes[j]) {
|
||||
s.yExtremes[j] = pos;
|
||||
}
|
||||
});
|
||||
@ -141,21 +147,21 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
calcYRegions() {
|
||||
let s = this.state;
|
||||
if(this.data.yMarkers) {
|
||||
if (this.data.yMarkers) {
|
||||
this.state.yMarkers = this.data.yMarkers.map(d => {
|
||||
d.position = scale(d.value, s.yAxis);
|
||||
if(!d.options) d.options = {};
|
||||
if (!d.options) d.options = {};
|
||||
// if(!d.label.includes(':')) {
|
||||
// d.label += ': ' + d.value;
|
||||
// }
|
||||
return d;
|
||||
});
|
||||
}
|
||||
if(this.data.yRegions) {
|
||||
if (this.data.yRegions) {
|
||||
this.state.yRegions = this.data.yRegions.map(d => {
|
||||
d.startPos = scale(d.start, s.yAxis);
|
||||
d.endPos = scale(d.end, s.yAxis);
|
||||
if(!d.options) d.options = {};
|
||||
if (!d.options) d.options = {};
|
||||
return d;
|
||||
});
|
||||
}
|
||||
@ -164,7 +170,7 @@ export default class AxisChart extends BaseChart {
|
||||
getAllYValues() {
|
||||
let key = 'values';
|
||||
|
||||
if(this.barOptions.stacked) {
|
||||
if (this.barOptions.stacked) {
|
||||
key = 'cumulativeYs';
|
||||
let cumulative = new Array(this.state.datasetLength).fill(0);
|
||||
this.data.datasets.map((d, i) => {
|
||||
@ -174,10 +180,10 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
if(this.data.yRegions) {
|
||||
if (this.data.yRegions) {
|
||||
this.data.yRegions.map(d => {
|
||||
allValueLists.push([d.end, d.start]);
|
||||
});
|
||||
@ -193,10 +199,10 @@ export default class AxisChart extends BaseChart {
|
||||
{
|
||||
mode: this.config.yAxisMode,
|
||||
width: this.width,
|
||||
shortenNumbers: this.config.shortenYAxisNumbers
|
||||
// pos: 'right'
|
||||
shortenNumbers: this.config.shortenYAxisNumbers,
|
||||
numberFormatter: this.config.numberFormatter,
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return this.state.yAxis;
|
||||
}.bind(this)
|
||||
],
|
||||
@ -208,10 +214,10 @@ export default class AxisChart extends BaseChart {
|
||||
height: this.height,
|
||||
// pos: 'right'
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
let s = this.state;
|
||||
s.xAxis.calcLabels = getShortenedLabels(this.width,
|
||||
s.xAxis.labels, this.config.xIsSeries);
|
||||
s.xAxis.labels, this.config.xIsSeries,this.config.seriesLabelSpaceRatio);
|
||||
|
||||
return s.xAxis;
|
||||
}.bind(this)
|
||||
@ -223,7 +229,7 @@ export default class AxisChart extends BaseChart {
|
||||
width: this.width,
|
||||
pos: 'right'
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return this.state.yRegions;
|
||||
}.bind(this)
|
||||
],
|
||||
@ -245,23 +251,23 @@ export default class AxisChart extends BaseChart {
|
||||
valuesOverPoints: this.config.valuesOverPoints,
|
||||
minHeight: this.height * MIN_BAR_PERCENT_HEIGHT,
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
let s = this.state;
|
||||
let d = s.datasets[index];
|
||||
let stacked = this.barOptions.stacked;
|
||||
|
||||
let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
labels = d.values;
|
||||
@ -269,7 +275,7 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
let offsets = new Array(s.datasetLength).fill(0);
|
||||
if(stacked) {
|
||||
if (stacked) {
|
||||
offsets = d.yPositions.map((y, j) => y - d.cumulativeYPos[j]);
|
||||
}
|
||||
|
||||
@ -299,13 +305,15 @@ export default class AxisChart extends BaseChart {
|
||||
heatline: this.lineOptions.heatline,
|
||||
regionFill: this.lineOptions.regionFill,
|
||||
spline: this.lineOptions.spline,
|
||||
hideDots: this.lineOptions.hideDots,
|
||||
showDots: this.lineOptions.showDots,
|
||||
trailingDot: this.lineOptions.trailingDot,
|
||||
hideDotBorder: this.lineOptions.hideDotBorder,
|
||||
hideLine: this.lineOptions.hideLine,
|
||||
|
||||
// same for all datasets
|
||||
valuesOverPoints: this.config.valuesOverPoints,
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
let s = this.state;
|
||||
let d = s.datasets[index];
|
||||
let minLine = s.yAxis.positions[0] < s.yAxis.zeroLine
|
||||
@ -331,7 +339,7 @@ export default class AxisChart extends BaseChart {
|
||||
width: this.width,
|
||||
pos: 'right'
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return this.state.yMarkers;
|
||||
}.bind(this)
|
||||
]
|
||||
@ -346,7 +354,7 @@ export default class AxisChart extends BaseChart {
|
||||
.filter(args => !optionals.includes(args[0]) || this.state[args[0]])
|
||||
.map(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);
|
||||
}
|
||||
return [args[0], component];
|
||||
@ -369,7 +377,11 @@ export default class AxisChart extends BaseChart {
|
||||
value: value,
|
||||
yPos: set.yPositions[index],
|
||||
color: this.colors[i],
|
||||
formatted: formatY ? formatY(value) : value,
|
||||
formatted: formatY ? formatY(value, {
|
||||
name: set.name,
|
||||
index: set.index,
|
||||
values: set.values
|
||||
}) : value,
|
||||
};
|
||||
});
|
||||
|
||||
@ -391,8 +403,8 @@ export default class AxisChart extends BaseChart {
|
||||
let relX = e.pageX - o.left - getLeftOffset(m);
|
||||
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);
|
||||
} else {
|
||||
this.tip.hideTip();
|
||||
@ -402,7 +414,7 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
mapTooltipXPosition(relX) {
|
||||
let s = this.state;
|
||||
if(!s.yExtremes) return;
|
||||
if (!s.yExtremes) return;
|
||||
|
||||
let index = getClosestInArray(relX, s.xAxis.positions, true);
|
||||
if (index >= 0) {
|
||||
@ -411,7 +423,7 @@ export default class AxisChart extends BaseChart {
|
||||
this.tip.setValues(
|
||||
dbi.xPos + this.tip.offset.x,
|
||||
dbi.yExtreme + this.tip.offset.y,
|
||||
{name: dbi.formattedLabel, value: ''},
|
||||
{ name: dbi.formattedLabel, value: '' },
|
||||
dbi.values,
|
||||
index
|
||||
);
|
||||
@ -422,34 +434,32 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
renderLegend() {
|
||||
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
|
||||
makeOverlay() {
|
||||
if(this.init) {
|
||||
if (this.init) {
|
||||
this.init = 0;
|
||||
return;
|
||||
}
|
||||
if(this.overlayGuides) {
|
||||
if (this.overlayGuides) {
|
||||
this.overlayGuides.forEach(g => {
|
||||
let o = g.overlay;
|
||||
o.parentNode.removeChild(o);
|
||||
@ -464,7 +474,7 @@ export default class AxisChart extends BaseChart {
|
||||
};
|
||||
});
|
||||
|
||||
if(this.state.currentIndex === undefined) {
|
||||
if (this.state.currentIndex === undefined) {
|
||||
this.state.currentIndex = this.state.datasetLength - 1;
|
||||
}
|
||||
|
||||
@ -478,7 +488,7 @@ export default class AxisChart extends BaseChart {
|
||||
}
|
||||
|
||||
updateOverlayGuides() {
|
||||
if(this.overlayGuides) {
|
||||
if (this.overlayGuides) {
|
||||
this.overlayGuides.forEach(g => {
|
||||
let o = g.overlay;
|
||||
o.parentNode.removeChild(o);
|
||||
@ -524,7 +534,7 @@ export default class AxisChart extends BaseChart {
|
||||
this.setCurrentDataPoint(this.state.currentIndex + 1);
|
||||
}
|
||||
|
||||
getDataPoint(index=this.state.currentIndex) {
|
||||
getDataPoint(index = this.state.currentIndex) {
|
||||
let s = this.state;
|
||||
let data_point = {
|
||||
index: index,
|
||||
@ -537,9 +547,9 @@ export default class AxisChart extends BaseChart {
|
||||
setCurrentDataPoint(index) {
|
||||
let s = this.state;
|
||||
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;
|
||||
fire(this.parent, "data-select", this.getDataPoint());
|
||||
}
|
||||
@ -547,7 +557,7 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
|
||||
// API
|
||||
addDataPoint(label, datasetValues, index=this.state.datasetLength) {
|
||||
addDataPoint(label, datasetValues, index = this.state.datasetLength) {
|
||||
super.addDataPoint(label, datasetValues, index);
|
||||
this.data.labels.splice(index, 0, label);
|
||||
this.data.datasets.map((d, i) => {
|
||||
@ -556,7 +566,7 @@ export default class AxisChart extends BaseChart {
|
||||
this.update(this.data);
|
||||
}
|
||||
|
||||
removeDataPoint(index = this.state.datasetLength-1) {
|
||||
removeDataPoint(index = this.state.datasetLength - 1) {
|
||||
if (this.data.labels.length <= 1) {
|
||||
return;
|
||||
}
|
||||
@ -568,7 +578,7 @@ export default class AxisChart extends BaseChart {
|
||||
this.update(this.data);
|
||||
}
|
||||
|
||||
updateDataset(datasetValues, index=0) {
|
||||
updateDataset(datasetValues, index = 0) {
|
||||
this.data.datasets[index].values = datasetValues;
|
||||
this.update(this.data);
|
||||
}
|
||||
@ -577,7 +587,7 @@ export default class AxisChart extends BaseChart {
|
||||
|
||||
updateDatasets(datasets) {
|
||||
this.data.datasets.map((d, i) => {
|
||||
if(datasets[i]) {
|
||||
if (datasets[i]) {
|
||||
d.values = datasets[i];
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
import SvgTip from '../objects/SvgTip';
|
||||
import { $, isElementInViewport, getElementContentWidth, isHidden } from '../utils/dom';
|
||||
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 { runSMILAnimation } from '../utils/animation';
|
||||
import { downloadFile, prepareForExport } from '../utils/export';
|
||||
import { deepClone } from '../utils/helpers';
|
||||
|
||||
export default class BaseChart {
|
||||
constructor(parent, options) {
|
||||
options = deepClone(options);
|
||||
|
||||
this.parent = typeof parent === 'string'
|
||||
? document.querySelector(parent)
|
||||
@ -30,17 +35,18 @@ export default class BaseChart {
|
||||
|
||||
this.config = {
|
||||
showTooltip: 1, // calculate
|
||||
showLegend: 1, // calculate
|
||||
showLegend: (typeof options.showLegend !== 'undefined') ? options.showLegend : 1,
|
||||
isNavigable: options.isNavigable || 0,
|
||||
animate: (typeof options.animate !== 'undefined') ? options.animate : 1,
|
||||
truncateLegends: options.truncateLegends || 1
|
||||
disableEntryAnimation: options.disableEntryAnimation || 0,
|
||||
truncateLegends: (typeof options.truncateLegends !== 'undefined') ? options.truncateLegends : 1
|
||||
};
|
||||
|
||||
this.measures = JSON.parse(JSON.stringify(BASE_MEASURES));
|
||||
let m = this.measures;
|
||||
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.state = {};
|
||||
@ -48,7 +54,7 @@ export default class BaseChart {
|
||||
|
||||
this.initTimeout = INIT_CHART_UPDATE_TIMEOUT;
|
||||
|
||||
if(this.config.isNavigable) {
|
||||
if (this.config.isNavigable) {
|
||||
this.overlays = [];
|
||||
}
|
||||
|
||||
@ -68,7 +74,7 @@ export default class BaseChart {
|
||||
colors = (colors || []).concat(DEFAULT_COLORS[type]);
|
||||
colors.forEach((string) => {
|
||||
const color = getColor(string);
|
||||
if(!isValidColor(color)) {
|
||||
if (!isValidColor(color)) {
|
||||
console.warn('"' + string + '" is not a valid color.');
|
||||
} else {
|
||||
validColors.push(color);
|
||||
@ -89,11 +95,16 @@ export default class BaseChart {
|
||||
|
||||
// Bind window events
|
||||
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('orientationchange', this.boundDrawFn);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.resizeObserver) this.resizeObserver.disconnect();
|
||||
window.removeEventListener('resize', this.boundDrawFn);
|
||||
window.removeEventListener('orientationchange', this.boundDrawFn);
|
||||
}
|
||||
@ -116,7 +127,7 @@ export default class BaseChart {
|
||||
className: 'chart-container'
|
||||
};
|
||||
|
||||
if(this.independentWidth) {
|
||||
if (this.independentWidth) {
|
||||
args.styles = { width: this.independentWidth + 'px' };
|
||||
}
|
||||
|
||||
@ -131,9 +142,9 @@ export default class BaseChart {
|
||||
this.bindTooltip();
|
||||
}
|
||||
|
||||
bindTooltip() {}
|
||||
bindTooltip() { }
|
||||
|
||||
draw(onlyWidthChange=false, init=false) {
|
||||
draw(onlyWidthChange = false, init = false) {
|
||||
if (onlyWidthChange && isHidden(this.parent)) {
|
||||
// Don't update anything if the chart is hidden
|
||||
return;
|
||||
@ -148,17 +159,19 @@ export default class BaseChart {
|
||||
// this.components.forEach(c => c.make());
|
||||
this.render(this.components, false);
|
||||
|
||||
if(init) {
|
||||
if (init) {
|
||||
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);
|
||||
}
|
||||
|
||||
calc() {} // builds state
|
||||
calc() { } // builds state
|
||||
|
||||
updateWidth() {
|
||||
this.baseWidth = getElementContentWidth(this.parent);
|
||||
@ -166,7 +179,7 @@ export default class BaseChart {
|
||||
}
|
||||
|
||||
makeChartArea() {
|
||||
if(this.svg) {
|
||||
if (this.svg) {
|
||||
this.container.removeChild(this.svg);
|
||||
}
|
||||
let m = this.measures;
|
||||
@ -179,7 +192,7 @@ export default class BaseChart {
|
||||
);
|
||||
this.svgDefs = makeSVGDefs(this.svg);
|
||||
|
||||
if(this.title.length) {
|
||||
if (this.title.length) {
|
||||
this.titleEL = makeText(
|
||||
'title',
|
||||
m.margins.left,
|
||||
@ -199,7 +212,7 @@ export default class BaseChart {
|
||||
`translate(${getLeftOffset(m)}, ${top})`
|
||||
);
|
||||
|
||||
if(this.config.showLegend) {
|
||||
if (this.config.showLegend) {
|
||||
top += this.height + m.paddings.bottom;
|
||||
this.legendArea = makeSVGGroup(
|
||||
'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);
|
||||
if(this.config.showLegend) { this.svg.appendChild(this.legendArea); }
|
||||
if (this.config.showLegend) { this.svg.appendChild(this.legendArea); }
|
||||
|
||||
this.updateTipOffset(getLeftOffset(m), getTopOffset(m));
|
||||
}
|
||||
@ -223,17 +236,18 @@ export default class BaseChart {
|
||||
|
||||
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.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
|
||||
this.overlays.map(o => o.parentNode.removeChild(o));
|
||||
// ref.parentNode.insertBefore(element, ref);
|
||||
@ -243,7 +257,7 @@ export default class BaseChart {
|
||||
components.forEach(c => {
|
||||
elementsToAnimate = elementsToAnimate.concat(c.update(animate));
|
||||
});
|
||||
if(elementsToAnimate.length > 0) {
|
||||
if (elementsToAnimate.length > 0) {
|
||||
runSMILAnimation(this.container, this.svg, elementsToAnimate);
|
||||
setTimeout(() => {
|
||||
components.forEach(c => c.make());
|
||||
@ -256,18 +270,37 @@ export default class BaseChart {
|
||||
}
|
||||
|
||||
updateNav() {
|
||||
if(this.config.isNavigable) {
|
||||
if (this.config.isNavigable) {
|
||||
this.makeOverlay();
|
||||
this.bindUnits();
|
||||
}
|
||||
}
|
||||
|
||||
renderLegend() {}
|
||||
renderLegend(dataset) {
|
||||
this.legendArea.textContent = '';
|
||||
let count = 0;
|
||||
let y = 0;
|
||||
|
||||
setupNavigation(init=false) {
|
||||
if(!this.config.isNavigable) return;
|
||||
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++;
|
||||
});
|
||||
}
|
||||
|
||||
if(init) {
|
||||
makeLegend() { }
|
||||
|
||||
|
||||
setupNavigation(init = false) {
|
||||
if (!this.config.isNavigable) return;
|
||||
|
||||
if (init) {
|
||||
this.bindOverlay();
|
||||
|
||||
this.keyActions = {
|
||||
@ -279,9 +312,9 @@ export default class BaseChart {
|
||||
};
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if(isElementInViewport(this.container)) {
|
||||
if (isElementInViewport(this.container)) {
|
||||
e = e || window.event;
|
||||
if(this.keyActions[e.keyCode]) {
|
||||
if (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() {
|
||||
let chartSvg = prepareForExport(this.svg);
|
||||
|
||||
@ -1,86 +1,35 @@
|
||||
import AggregationChart from './AggregationChart';
|
||||
import PieChart from './PieChart';
|
||||
import { getComponent } from '../objects/ChartComponents';
|
||||
import { getOffset } from '../utils/dom';
|
||||
import { getPositionByAngle } from '../utils/helpers';
|
||||
import { makeArcStrokePathStr, makeStrokeCircleStr } from '../utils/draw';
|
||||
import { lightenDarkenColor } from '../utils/colors';
|
||||
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) {
|
||||
super(parent, args);
|
||||
this.type = 'donut';
|
||||
this.initTimeout = 0;
|
||||
this.init = 1;
|
||||
|
||||
this.setup();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
getRadius() {
|
||||
return 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
|
||||
});
|
||||
|
||||
});
|
||||
this.init = 0;
|
||||
resetHover(path, color) {
|
||||
transform(path,'translate3d(0,0,0)');
|
||||
this.tip.hideTip();
|
||||
path.style.stroke = color;
|
||||
}
|
||||
|
||||
setupComponents() {
|
||||
@ -88,9 +37,9 @@ export default class DonutChart extends AggregationChart {
|
||||
|
||||
let componentConfigs = [
|
||||
[
|
||||
'donutSlices',
|
||||
{ },
|
||||
function() {
|
||||
this.sliceName,
|
||||
{},
|
||||
function () {
|
||||
return {
|
||||
sliceStrings: s.sliceStrings,
|
||||
colors: this.colors,
|
||||
@ -106,56 +55,4 @@ export default class DonutChart extends AggregationChart {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import BaseChart from './BaseChart';
|
||||
import { getComponent } from '../objects/ChartComponents';
|
||||
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 { 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 ROW_HEIGHT = COL_WIDTH;
|
||||
@ -49,19 +53,19 @@ export default class Heatmap extends BaseChart {
|
||||
+ 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.');
|
||||
}
|
||||
|
||||
if(!data.start) {
|
||||
if (!data.start) {
|
||||
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 || {};
|
||||
|
||||
if(parseInt(Object.keys(data.dataPoints)[0]) > 100000) {
|
||||
if (parseInt(Object.keys(data.dataPoints)[0]) > 100000) {
|
||||
let points = {};
|
||||
Object.keys(data.dataPoints).forEach(timestampSec => {
|
||||
let date = new Date(timestampSec * NO_OF_MILLIS);
|
||||
@ -105,7 +109,7 @@ export default class Heatmap extends BaseChart {
|
||||
.reduce((a, b) => a + b, 0)
|
||||
* COL_WIDTH
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return s.domainConfigs[i];
|
||||
}.bind(this)
|
||||
|
||||
@ -120,8 +124,8 @@ export default class Heatmap extends BaseChart {
|
||||
|
||||
let y = 0;
|
||||
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,
|
||||
dy: 8,
|
||||
@ -135,7 +139,7 @@ export default class Heatmap extends BaseChart {
|
||||
}
|
||||
|
||||
update(data) {
|
||||
if(!data) {
|
||||
if (!data) {
|
||||
console.error('No data to update.');
|
||||
}
|
||||
|
||||
@ -149,22 +153,22 @@ export default class Heatmap extends BaseChart {
|
||||
this.components.forEach(comp => {
|
||||
let daySquares = comp.store;
|
||||
let daySquare = e.target;
|
||||
if(daySquares.includes(daySquare)) {
|
||||
if (daySquares.includes(daySquare)) {
|
||||
|
||||
let count = daySquare.getAttribute('data-value');
|
||||
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 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 value = count + ' ' + this.countLabel;
|
||||
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();
|
||||
}
|
||||
});
|
||||
@ -183,7 +187,7 @@ export default class Heatmap extends BaseChart {
|
||||
dy: 9
|
||||
}
|
||||
);
|
||||
x = (COL_WIDTH * 2) + COL_WIDTH/2;
|
||||
x = (COL_WIDTH * 2) + COL_WIDTH / 2;
|
||||
this.legendArea.appendChild(lessText);
|
||||
|
||||
this.colors.slice(0, HEATMAP_DISTRIBUTION_SIZE).map((color, i) => {
|
||||
@ -192,7 +196,7 @@ export default class Heatmap extends BaseChart {
|
||||
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',
|
||||
{
|
||||
fontSize: HEATMAP_SQUARE_SIZE + 1,
|
||||
@ -212,9 +216,9 @@ export default class Heatmap extends BaseChart {
|
||||
let domainConfigs = [];
|
||||
|
||||
let startOfMonth = clone(s.start);
|
||||
for(var i = 0; i < noOfMonths; i++) {
|
||||
for (var i = 0; i < noOfMonths; i++) {
|
||||
let endDate = s.end;
|
||||
if(!areInSameMonth(startOfMonth, s.end)) {
|
||||
if (!areInSameMonth(startOfMonth, s.end)) {
|
||||
let [month, year] = [startOfMonth.getMonth(), startOfMonth.getFullYear()];
|
||||
endDate = getLastDateInMonth(month, year);
|
||||
}
|
||||
@ -227,7 +231,7 @@ export default class Heatmap extends BaseChart {
|
||||
return domainConfigs;
|
||||
}
|
||||
|
||||
getDomainConfig(startDate, endDate='') {
|
||||
getDomainConfig(startDate, endDate = '') {
|
||||
let [month, year] = [startDate.getMonth(), startDate.getFullYear()];
|
||||
let startOfWeek = setDayToSunday(startDate); // TODO: Monday as well
|
||||
endDate = clone(endDate) || getLastDateInMonth(month, year);
|
||||
@ -241,7 +245,7 @@ export default class Heatmap extends BaseChart {
|
||||
let noOfMonthWeeks = getWeeksBetween(startOfWeek, endDate);
|
||||
|
||||
let cols = [], col;
|
||||
for(var i = 0; i < noOfMonthWeeks; i++) {
|
||||
for (var i = 0; i < noOfMonthWeeks; i++) {
|
||||
col = this.getCol(startOfWeek, month);
|
||||
cols.push(col);
|
||||
|
||||
@ -249,7 +253,7 @@ export default class Heatmap extends BaseChart {
|
||||
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);
|
||||
cols.push(this.getCol(startOfWeek, month, true));
|
||||
}
|
||||
@ -266,13 +270,13 @@ export default class Heatmap extends BaseChart {
|
||||
let currentDate = clone(startDate);
|
||||
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 = {};
|
||||
|
||||
// Non-generic adjustment for entire heatmap, needs state
|
||||
let currentDateWithinData = currentDate >= s.start && currentDate <= s.end;
|
||||
|
||||
if(empty || currentDate.getMonth() !== month || !currentDateWithinData) {
|
||||
if (empty || currentDate.getMonth() !== month || !currentDateWithinData) {
|
||||
config.yyyyMmDd = getYyyyMmDd(currentDate);
|
||||
} else {
|
||||
config = this.getSubDomainConfig(currentDate);
|
||||
|
||||
@ -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
|
||||
));
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import AggregationChart from './AggregationChart';
|
||||
import { getOffset } from '../utils/dom';
|
||||
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 {
|
||||
constructor(parent, args) {
|
||||
@ -16,11 +16,13 @@ export default class PercentageChart extends AggregationChart {
|
||||
|
||||
let b = this.barOptions;
|
||||
b.height = b.height || PERCENTAGE_BAR_DEFAULT_HEIGHT;
|
||||
b.depth = b.depth || PERCENTAGE_BAR_DEFAULT_DEPTH;
|
||||
|
||||
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() {
|
||||
@ -31,9 +33,8 @@ export default class PercentageChart extends AggregationChart {
|
||||
'percentageBars',
|
||||
{
|
||||
barHeight: this.barOptions.height,
|
||||
barDepth: this.barOptions.depth,
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
return {
|
||||
xPositions: s.xPositions,
|
||||
widths: s.widths,
|
||||
@ -73,18 +74,19 @@ export default class PercentageChart extends AggregationChart {
|
||||
this.container.addEventListener('mousemove', (e) => {
|
||||
let bars = this.components.get('percentageBars').store;
|
||||
let bar = e.target;
|
||||
if(bars.includes(bar)) {
|
||||
|
||||
if (bars.includes(bar)) {
|
||||
let i = bars.indexOf(bar);
|
||||
let gOff = getOffset(this.container), pOff = getOffset(bar);
|
||||
|
||||
let x = pOff.left - gOff.left + parseInt(bar.getAttribute('width'))/2;
|
||||
let y = pOff.top - gOff.top;
|
||||
let title = (this.formattedLabels && this.formattedLabels.length>0
|
||||
? this.formattedLabels[i] : this.state.labels[i]) + ': ';
|
||||
let fraction = s.sliceTotals[i]/s.grandTotal;
|
||||
let width = bar.getAttribute('width') || bar.getBoundingClientRect().width;
|
||||
|
||||
this.tip.setValues(x, y, {name: title, value: (fraction*100).toFixed(1) + "%"});
|
||||
let x = pOff.left - gOff.left + parseInt(width) / 2;
|
||||
let y = pOff.top - gOff.top;
|
||||
let title = (this.formattedLabels && this.formattedLabels.length > 0
|
||||
? this.formattedLabels[i] : this.state.labels[i]) + ': ';
|
||||
let fraction = s.sliceTotals[i] / s.grandTotal;
|
||||
|
||||
this.tip.setValues(x, y, { name: title, value: (fraction * 100).toFixed(1) + "%" });
|
||||
this.tip.showTip();
|
||||
}
|
||||
});
|
||||
|
||||
@ -3,14 +3,12 @@ import { getComponent } from '../objects/ChartComponents';
|
||||
import { getOffset } from '../utils/dom';
|
||||
import { getPositionByAngle } from '../utils/helpers';
|
||||
import { makeArcPathStr, makeCircleStr } from '../utils/draw';
|
||||
import { lightenDarkenColor } from '../utils/colors';
|
||||
import { transform } from '../utils/animation';
|
||||
import { FULL_ANGLE } from '../utils/constants';
|
||||
|
||||
export default class PieChart extends AggregationChart {
|
||||
constructor(parent, args) {
|
||||
super(parent, args);
|
||||
this.type = 'pie';
|
||||
this.initTimeout = 0;
|
||||
this.init = 1;
|
||||
|
||||
@ -25,13 +23,23 @@ export default class PieChart extends AggregationChart {
|
||||
this.hoverRadio = args.hoverRadio || 0.1;
|
||||
this.config.startAngle = args.startAngle || 0;
|
||||
|
||||
this.type = 'pie';
|
||||
this.sliceName = 'pieSlices';
|
||||
|
||||
this.arcFunc = makeArcPathStr;
|
||||
this.shapeFunc = makeCircleStr;
|
||||
|
||||
this.clockWise = args.clockWise || false;
|
||||
}
|
||||
|
||||
getRadius() {
|
||||
return this.height > this.width ? this.center.x : this.center.y;
|
||||
}
|
||||
|
||||
calc() {
|
||||
super.calc();
|
||||
let s = this.state;
|
||||
this.radius = (this.height > this.width ? this.center.x : this.center.y);
|
||||
this.radius = this.getRadius();
|
||||
|
||||
const { radius, clockWise } = this;
|
||||
|
||||
@ -42,7 +50,7 @@ export default class PieChart extends AggregationChart {
|
||||
s.sliceTotals.map((total, i) => {
|
||||
const startAngle = curAngle;
|
||||
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 endAngle = curAngle = curAngle + diffAngle;
|
||||
const startPosition = getPositionByAngle(startAngle, radius);
|
||||
@ -50,8 +58,8 @@ export default class PieChart extends AggregationChart {
|
||||
|
||||
const prevProperty = this.init && prevSlicesProperties[i];
|
||||
|
||||
let curStart,curEnd;
|
||||
if(this.init) {
|
||||
let curStart, curEnd;
|
||||
if (this.init) {
|
||||
curStart = prevProperty ? prevProperty.startPosition : startPosition;
|
||||
curEnd = prevProperty ? prevProperty.endPosition : startPosition;
|
||||
} else {
|
||||
@ -60,8 +68,8 @@ export default class PieChart extends AggregationChart {
|
||||
}
|
||||
const curPath =
|
||||
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.slicesProperties.push({
|
||||
@ -84,8 +92,8 @@ export default class PieChart extends AggregationChart {
|
||||
let componentConfigs = [
|
||||
[
|
||||
'pieSlices',
|
||||
{ },
|
||||
function() {
|
||||
{},
|
||||
function () {
|
||||
return {
|
||||
sliceStrings: s.sliceStrings,
|
||||
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)`;
|
||||
}
|
||||
|
||||
hoverSlice(path,i,flag,e){
|
||||
if(!path) return;
|
||||
hoverSlice(path, i, flag, e) {
|
||||
if (!path) return;
|
||||
const color = this.colors[i];
|
||||
if(flag) {
|
||||
if (flag) {
|
||||
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 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.setValues(x, y, { name: title, value: percent + "%" });
|
||||
this.tip.showTip();
|
||||
} 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() {
|
||||
this.container.addEventListener('mousemove', this.mouseMove);
|
||||
this.container.addEventListener('mouseleave', this.mouseLeave);
|
||||
}
|
||||
|
||||
mouseMove(e){
|
||||
mouseMove(e) {
|
||||
const target = e.target;
|
||||
let slices = this.components.get('pieSlices').store;
|
||||
let slices = this.components.get(this.sliceName).store;
|
||||
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);
|
||||
this.hoverSlice(prevAcitve, prevIndex,false);
|
||||
this.hoverSlice(prevActive, prevIndex, false);
|
||||
this.curActiveSlice = target;
|
||||
this.curActiveSliceIndex = i;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import * as Charts from './chart';
|
||||
let frappe = { };
|
||||
|
||||
frappe.NAME = 'Frappe Charts';
|
||||
frappe.VERSION = '1.5.5';
|
||||
frappe.VERSION = '2.0.0-rc22';
|
||||
|
||||
frappe = Object.assign({ }, frappe, Charts);
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { makeSVGGroup } 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 { 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';
|
||||
|
||||
class ChartComponent {
|
||||
@ -27,7 +29,7 @@ class ChartComponent {
|
||||
this.labels = [];
|
||||
|
||||
this.layerClass = layerClass;
|
||||
this.layerClass = typeof(this.layerClass) === 'function'
|
||||
this.layerClass = typeof (this.layerClass) === 'function'
|
||||
? this.layerClass() : this.layerClass;
|
||||
|
||||
this.refresh();
|
||||
@ -61,7 +63,7 @@ class ChartComponent {
|
||||
update(animate = true) {
|
||||
this.refresh();
|
||||
let animateElements = [];
|
||||
if(animate) {
|
||||
if (animate) {
|
||||
animateElements = this.animateElements(this.data) || [];
|
||||
}
|
||||
return animateElements;
|
||||
@ -86,7 +88,7 @@ let componentConfigs = {
|
||||
pieSlices: {
|
||||
layerClass: 'pie-slices',
|
||||
makeElements(data) {
|
||||
return data.sliceStrings.map((s, i) =>{
|
||||
return data.sliceStrings.map((s, i) => {
|
||||
let slice = makePath(s, 'pie-path', 'none', data.colors[i]);
|
||||
slice.style.transition = 'transform .3s;';
|
||||
return slice;
|
||||
@ -102,16 +104,20 @@ let componentConfigs = {
|
||||
percentageBars: {
|
||||
layerClass: 'percentage-bars',
|
||||
makeElements(data) {
|
||||
return data.xPositions.map((x, i) =>{
|
||||
const numberOfPoints = data.xPositions.length;
|
||||
return data.xPositions.map((x, i) => {
|
||||
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;
|
||||
});
|
||||
},
|
||||
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
if (newData) return [];
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
@ -119,7 +125,12 @@ let componentConfigs = {
|
||||
makeElements(data) {
|
||||
return data.positions.map((position, i) =>
|
||||
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) {
|
||||
return data.positions.map((position, i) =>
|
||||
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) {
|
||||
return data.map(m =>
|
||||
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) {
|
||||
@ -214,7 +225,7 @@ let componentConfigs = {
|
||||
makeElements(data) {
|
||||
return data.map(r =>
|
||||
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) {
|
||||
@ -250,16 +261,16 @@ let componentConfigs = {
|
||||
},
|
||||
|
||||
heatDomain: {
|
||||
layerClass: function() { return 'heat-domain domain-' + this.constants.index; },
|
||||
layerClass: function () { return 'heat-domain domain-' + this.constants.index; },
|
||||
makeElements(data) {
|
||||
let {index, colWidth, rowHeight, squareSize, radius, xTranslate} = this.constants;
|
||||
let { index, colWidth, rowHeight, squareSize, radius, xTranslate } = this.constants;
|
||||
let monthNameHeight = -12;
|
||||
let x = xTranslate, y = 0;
|
||||
|
||||
this.serializedSubDomains = [];
|
||||
|
||||
data.cols.map((week, weekNo) => {
|
||||
if(weekNo === 1) {
|
||||
if (weekNo === 1) {
|
||||
this.labels.push(
|
||||
makeText('domain-name', x, monthNameHeight, getMonthName(index, true).toUpperCase(),
|
||||
{
|
||||
@ -269,7 +280,7 @@ let componentConfigs = {
|
||||
);
|
||||
}
|
||||
week.map((day, i) => {
|
||||
if(day.fill) {
|
||||
if (day.fill) {
|
||||
let data = {
|
||||
'data-date': day.yyyyMmDd,
|
||||
'data-value': day.dataValue,
|
||||
@ -288,12 +299,12 @@ let componentConfigs = {
|
||||
},
|
||||
|
||||
animateElements(newData) {
|
||||
if(newData) return [];
|
||||
if (newData) return [];
|
||||
}
|
||||
},
|
||||
|
||||
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) {
|
||||
let c = this.constants;
|
||||
this.unitType = 'bar';
|
||||
@ -347,7 +358,7 @@ let componentConfigs = {
|
||||
this.store.map((bar, i) => {
|
||||
animateElements = animateElements.concat(animateBar(
|
||||
bar, newXPos[i], newYPos[i], newData.barWidth, newOffsets[i],
|
||||
{zeroLine: newData.zeroLine}
|
||||
{ zeroLine: newData.zeroLine }
|
||||
));
|
||||
});
|
||||
|
||||
@ -356,12 +367,12 @@ let componentConfigs = {
|
||||
},
|
||||
|
||||
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) {
|
||||
let c = this.constants;
|
||||
this.unitType = 'dot';
|
||||
this.paths = {};
|
||||
if(!c.hideLine) {
|
||||
if (!c.hideLine) {
|
||||
this.paths = getPaths(
|
||||
data.xPositions,
|
||||
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) => {
|
||||
return datasetDot(
|
||||
data.xPositions[j],
|
||||
@ -387,11 +398,27 @@ let componentConfigs = {
|
||||
data.radius,
|
||||
c.color,
|
||||
(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);
|
||||
},
|
||||
animateElements(newData) {
|
||||
@ -418,12 +445,12 @@ let componentConfigs = {
|
||||
|
||||
let animateElements = [];
|
||||
|
||||
if(Object.keys(this.paths).length) {
|
||||
if (Object.keys(this.paths).length) {
|
||||
animateElements = animateElements.concat(animatePath(
|
||||
this.paths, newXPos, newYPos, newData.zeroLine, this.constants.spline));
|
||||
}
|
||||
|
||||
if(this.units.length) {
|
||||
if (this.units.length) {
|
||||
this.units.map((dot, i) => {
|
||||
animateElements = animateElements.concat(animateDot(
|
||||
dot, newXPos[i], newYPos[i]));
|
||||
|
||||
@ -52,10 +52,10 @@ export default class SvgTip {
|
||||
|
||||
fill() {
|
||||
let title;
|
||||
if(this.index) {
|
||||
if (this.index) {
|
||||
this.container.setAttribute('data-point-index', this.index);
|
||||
}
|
||||
if(this.titleValueFirst) {
|
||||
if (this.titleValueFirst) {
|
||||
title = `<strong>${this.titleValue}</strong>${this.titleName}`;
|
||||
} else {
|
||||
title = `${this.titleName}<strong>${this.titleValue}</strong>`;
|
||||
@ -76,8 +76,8 @@ export default class SvgTip {
|
||||
let li = $.create('li', {
|
||||
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 class="tooltip-value">${value === 0 || value ? value : ''}</div>
|
||||
<div class="tooltip-label">${set.title ? set.title : ''}</div>
|
||||
</div>`
|
||||
});
|
||||
|
||||
@ -90,15 +90,15 @@ export default class SvgTip {
|
||||
|
||||
this.top = this.y - this.container.offsetHeight
|
||||
- TOOLTIP_POINTER_TRIANGLE_HEIGHT;
|
||||
this.left = this.x - width/2;
|
||||
this.left = this.x - width / 2;
|
||||
let maxLeft = this.parent.offsetWidth - width;
|
||||
|
||||
let pointer = this.container.querySelector('.svg-pointer');
|
||||
|
||||
if(this.left < 0) {
|
||||
if (this.left < 0) {
|
||||
pointer.style.left = `calc(50% - ${-1 * this.left}px)`;
|
||||
this.left = 0;
|
||||
} else if(this.left > maxLeft) {
|
||||
} else if (this.left > maxLeft) {
|
||||
let delta = this.left - maxLeft;
|
||||
let pointerOffset = `calc(50% + ${delta}px)`;
|
||||
pointer.style.left = pointerOffset;
|
||||
|
||||
@ -11,11 +11,11 @@ export function translate(unit, oldCoord, newCoord, duration) {
|
||||
let old = typeof oldCoord === 'string' ? oldCoord : oldCoord.join(', ');
|
||||
return [
|
||||
unit,
|
||||
{transform: newCoord.join(', ')},
|
||||
{ transform: newCoord.join(', ') },
|
||||
duration,
|
||||
STD_EASING,
|
||||
"translate",
|
||||
{transform: old}
|
||||
{ transform: old }
|
||||
];
|
||||
}
|
||||
|
||||
@ -42,14 +42,14 @@ export function animateRegion(rectGroup, newY1, newY2, oldY2) {
|
||||
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);
|
||||
y -= offset;
|
||||
if(bar.nodeName !== 'rect') {
|
||||
if (bar.nodeName !== 'rect') {
|
||||
let rect = bar.childNodes[0];
|
||||
let rectAnim = [
|
||||
rect,
|
||||
{width: width, height: height},
|
||||
{ width: width, height: height },
|
||||
UNIT_ANIM_DUR,
|
||||
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);
|
||||
return [rectAnim, groupAnim];
|
||||
} 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);
|
||||
}
|
||||
|
||||
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 groupAnim = translate(dot, oldCoordStr, [x, y], MARKER_LINE_ANIM_DUR);
|
||||
return [groupAnim];
|
||||
} 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);
|
||||
}
|
||||
@ -81,16 +81,16 @@ export function animatePath(paths, newXList, newYList, zeroLine, spline) {
|
||||
if (spline)
|
||||
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);
|
||||
|
||||
if(paths.region) {
|
||||
if (paths.region) {
|
||||
let regStartPt = `${newXList[0]},${zeroLine}L`;
|
||||
let regEndPt = `L${newXList.slice(-1)[0]}, ${zeroLine}`;
|
||||
|
||||
const animRegion = [
|
||||
paths.region,
|
||||
{d:"M" + regStartPt + pointsStr + regEndPt},
|
||||
{ d: "M" + regStartPt + pointsStr + regEndPt },
|
||||
PATH_ANIM_DUR,
|
||||
STD_EASING
|
||||
];
|
||||
@ -101,5 +101,5 @@ export function animatePath(paths, newXList, newYList, zeroLine, spline) {
|
||||
}
|
||||
|
||||
export function animatePathStr(oldPath, pathStr) {
|
||||
return [oldPath, {d: pathStr}, UNIT_ANIM_DUR, STD_EASING];
|
||||
return [oldPath, { d: pathStr }, UNIT_ANIM_DUR, STD_EASING];
|
||||
}
|
||||
|
||||
@ -11,14 +11,14 @@ const EASING = {
|
||||
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 newElement = element.cloneNode(true);
|
||||
|
||||
for(var attributeName in props) {
|
||||
for (var attributeName in props) {
|
||||
let animateElement;
|
||||
if(attributeName === 'transform') {
|
||||
if (attributeName === 'transform') {
|
||||
animateElement = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
|
||||
} else {
|
||||
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,
|
||||
to: value,
|
||||
begin: "0s",
|
||||
dur: dur/1000 + "s",
|
||||
dur: dur / 1000 + "s",
|
||||
values: currentValue + ";" + value,
|
||||
keySplines: EASING[easingType],
|
||||
keyTimes: "0;1",
|
||||
@ -39,7 +39,7 @@ function animateSVGElement(element, props, dur, easingType="linear", type=undefi
|
||||
fill: 'freeze'
|
||||
};
|
||||
|
||||
if(type) {
|
||||
if (type) {
|
||||
animAttr["type"] = type;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ function animateSVGElement(element, props, dur, easingType="linear", type=undefi
|
||||
|
||||
animElement.appendChild(animateElement);
|
||||
|
||||
if(type) {
|
||||
if (type) {
|
||||
newElement.setAttribute(attributeName, `translate(${value})`);
|
||||
} else {
|
||||
newElement.setAttribute(attributeName, value);
|
||||
@ -97,10 +97,10 @@ function animateSVG(svgContainer, elements) {
|
||||
}
|
||||
|
||||
export function runSMILAnimation(parent, svgElement, elementsToAnimate) {
|
||||
if(elementsToAnimate.length === 0) return;
|
||||
if (elementsToAnimate.length === 0) return;
|
||||
|
||||
let animSvgElement = animateSVG(svgElement, elementsToAnimate);
|
||||
if(svgElement.parentNode == parent) {
|
||||
if (svgElement.parentNode == parent) {
|
||||
parent.removeChild(svgElement);
|
||||
parent.appendChild(animSvgElement);
|
||||
|
||||
@ -108,7 +108,7 @@ export function runSMILAnimation(parent, svgElement, elementsToAnimate) {
|
||||
|
||||
// Replace the new svgElement (data has already been replaced)
|
||||
setTimeout(() => {
|
||||
if(animSvgElement.parentNode == parent) {
|
||||
if (animSvgElement.parentNode == parent) {
|
||||
parent.removeChild(animSvgElement);
|
||||
parent.appendChild(svgElement);
|
||||
}
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
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) {
|
||||
data.labels = data.labels || [];
|
||||
@ -9,16 +12,16 @@ export function dataPrep(data, type) {
|
||||
// Datasets
|
||||
let datasets = data.datasets;
|
||||
let zeroArray = new Array(datasetLength).fill(0);
|
||||
if(!datasets) {
|
||||
if (!datasets) {
|
||||
// default
|
||||
datasets = [{
|
||||
values: zeroArray
|
||||
}];
|
||||
}
|
||||
|
||||
datasets.map(d=> {
|
||||
datasets.map(d => {
|
||||
// Set values
|
||||
if(!d.values) {
|
||||
if (!d.values) {
|
||||
d.values = zeroArray;
|
||||
} else {
|
||||
// Check for non values
|
||||
@ -26,19 +29,17 @@ export function dataPrep(data, type) {
|
||||
vals = vals.map(val => (!isNaN(val) ? val : 0));
|
||||
|
||||
// Trim or extend
|
||||
if(vals.length > datasetLength) {
|
||||
if (vals.length > datasetLength) {
|
||||
vals = vals.slice(0, datasetLength);
|
||||
} else {
|
||||
vals = fillArray(vals, datasetLength - vals.length, 0);
|
||||
}
|
||||
d.values = vals;
|
||||
}
|
||||
|
||||
// Set labels
|
||||
//
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -48,9 +49,9 @@ export function dataPrep(data, type) {
|
||||
|
||||
// Regions
|
||||
// data.yRegions = data.yRegions || [];
|
||||
if(data.yRegions) {
|
||||
if (data.yRegions) {
|
||||
data.yRegions.map(d => {
|
||||
if(d.end < d.start) {
|
||||
if (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 = [
|
||||
{
|
||||
value: 0,
|
||||
@ -83,7 +84,7 @@ export function zeroDataPrep(realData) {
|
||||
];
|
||||
}
|
||||
|
||||
if(realData.yRegions) {
|
||||
if (realData.yRegions) {
|
||||
zeroData.yRegions = [
|
||||
{
|
||||
start: 0,
|
||||
@ -96,31 +97,37 @@ export function zeroDataPrep(realData) {
|
||||
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, seriesLabelSpaceRatio) {
|
||||
let allowedSpace = (chartWidth / labels.length) * (seriesLabelSpaceRatio || SERIES_LABEL_SPACE_RATIO);
|
||||
if (allowedSpace <= 0) allowedSpace = 1;
|
||||
let allowedLetters = allowedSpace / DEFAULT_CHAR_WIDTH;
|
||||
|
||||
let seriesMultiple;
|
||||
if(isSeries) {
|
||||
if (isSeries) {
|
||||
// Find the maximum label length for spacing calculations
|
||||
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) => {
|
||||
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 {
|
||||
label = label.slice(0, allowedLetters) + '..';
|
||||
}
|
||||
} else {
|
||||
if(i % seriesMultiple !== 0) {
|
||||
label = "";
|
||||
if (i % seriesMultiple !== 0) {
|
||||
if (i !== (labels.length - 1)) {
|
||||
label = "";
|
||||
}
|
||||
} else {
|
||||
if (i > (labels.length - (seriesMultiple / 2))) {
|
||||
label = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ const PRESET_COLOR_MAP = {
|
||||
'light-orange': '#FECDB8'
|
||||
};
|
||||
|
||||
function limitColor(r){
|
||||
function limitColor(r) {
|
||||
if (r > 255) return 255;
|
||||
else if (r < 0) return 0;
|
||||
return r;
|
||||
@ -34,11 +34,11 @@ export function lightenDarkenColor(color, amt) {
|
||||
col = col.slice(1);
|
||||
usePound = true;
|
||||
}
|
||||
let num = parseInt(col,16);
|
||||
let num = parseInt(col, 16);
|
||||
let r = limitColor((num >> 16) + amt);
|
||||
let b = limitColor(((num >> 8) & 0x00FF) + 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) {
|
||||
|
||||
@ -65,16 +65,16 @@ export const CHART_POST_ANIMATE_TIMEOUT = 400;
|
||||
export const DEFAULT_AXIS_CHART_TYPE = 'line';
|
||||
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.8;
|
||||
export const BAR_CHART_SPACE_RATIO = 0.5;
|
||||
export const MIN_BAR_PERCENT_HEIGHT = 0.00;
|
||||
|
||||
export const LINE_CHART_DOT_SIZE = 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,
|
||||
// More colors are difficult to parse visually
|
||||
|
||||
@ -27,8 +27,8 @@ export function getYyyyMmDd(date) {
|
||||
let mm = date.getMonth() + 1; // getMonth() is zero-based
|
||||
return [
|
||||
date.getFullYear(),
|
||||
(mm>9 ? '' : '0') + mm,
|
||||
(dd>9 ? '' : '0') + dd
|
||||
(mm > 9 ? '' : '0') + mm,
|
||||
(dd > 9 ? '' : '0') + dd
|
||||
].join('-');
|
||||
}
|
||||
|
||||
@ -37,12 +37,12 @@ export function clone(date) {
|
||||
}
|
||||
|
||||
export function timestampSec(date) {
|
||||
return date.getTime()/NO_OF_MILLIS;
|
||||
return date.getTime() / NO_OF_MILLIS;
|
||||
}
|
||||
|
||||
export function timestampToMidnight(timestamp, roundAhead = false) {
|
||||
let midnightTs = Math.floor(timestamp - (timestamp % SEC_IN_DAY));
|
||||
if(roundAhead) {
|
||||
if (roundAhead) {
|
||||
return midnightTs + SEC_IN_DAY;
|
||||
}
|
||||
return midnightTs;
|
||||
@ -65,12 +65,12 @@ export function areInSameMonth(startDate, endDate) {
|
||||
&& startDate.getFullYear() === endDate.getFullYear();
|
||||
}
|
||||
|
||||
export function getMonthName(i, short=false) {
|
||||
export function getMonthName(i, short = false) {
|
||||
let monthName = MONTH_NAMES[i];
|
||||
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
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ export function getLastDateInMonth (month, year) {
|
||||
export function setDayToSunday(date) {
|
||||
let newDate = clone(date);
|
||||
const day = newDate.getDay();
|
||||
if(day !== 0) {
|
||||
if (day !== 0) {
|
||||
addDays(newDate, (-1) * day);
|
||||
}
|
||||
return newDate;
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
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;
|
||||
while (node.previousSibling) {
|
||||
node = node.previousSibling;
|
||||
@ -27,12 +26,12 @@ $.create = (tag, o) => {
|
||||
element.appendChild(ref);
|
||||
|
||||
} else if (i === "styles") {
|
||||
if(typeof val === "object") {
|
||||
if (typeof val === "object") {
|
||||
Object.keys(val).map(prop => {
|
||||
element.style[prop] = val[prop];
|
||||
});
|
||||
}
|
||||
} else if (i in element ) {
|
||||
} else if (i in element) {
|
||||
element[i] = val;
|
||||
}
|
||||
else {
|
||||
@ -67,9 +66,9 @@ export function isElementInViewport(el) {
|
||||
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
|
||||
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;
|
||||
}
|
||||
|
||||
export function bind(element, o){
|
||||
export function bind(element, o) {
|
||||
if (element) {
|
||||
for (var event in o) {
|
||||
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) {
|
||||
for (var event in o) {
|
||||
var callback = o[event];
|
||||
|
||||
event.split(/\s+/).forEach(function(event) {
|
||||
event.split(/\s+/).forEach(function (event) {
|
||||
element.removeEventListener(event, callback);
|
||||
});
|
||||
}
|
||||
@ -108,7 +107,7 @@ export function unbind(element, o){
|
||||
export function fire(target, type, properties) {
|
||||
var evt = document.createEvent("HTMLEvents");
|
||||
|
||||
evt.initEvent(type, true, true );
|
||||
evt.initEvent(type, true, true);
|
||||
|
||||
for (var j in properties) {
|
||||
evt[j] = properties[j];
|
||||
@ -119,17 +118,17 @@ export function fire(target, type, properties) {
|
||||
|
||||
// https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
|
||||
export function forEachNode(nodeList, callback, scope) {
|
||||
if(!nodeList) return;
|
||||
if (!nodeList) return;
|
||||
for (var i = 0; i < nodeList.length; 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}`);
|
||||
|
||||
forEachNode($children, (node, i) => {
|
||||
if(index >= 0 && i <= index) return;
|
||||
if (index >= 0 && i <= index) return;
|
||||
node.classList.remove(activeClass);
|
||||
});
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ export function equilizeNoOfElements(array1, array2,
|
||||
extraCount = array2.length - array1.length) {
|
||||
|
||||
// Doesn't work if either has zero elements.
|
||||
if(extraCount > 0) {
|
||||
if (extraCount > 0) {
|
||||
array1 = fillArray(array1, extraCount);
|
||||
} else {
|
||||
array2 = fillArray(array2, extraCount);
|
||||
@ -30,7 +30,7 @@ export function truncateString(txt, len) {
|
||||
return;
|
||||
}
|
||||
if (txt.length > len) {
|
||||
return txt.slice(0, len-3) + '...';
|
||||
return txt.slice(0, len - 3) + '...';
|
||||
} else {
|
||||
return txt;
|
||||
}
|
||||
@ -47,18 +47,20 @@ export function shortenLargeNumber(label) {
|
||||
// Using absolute since log wont work for negative numbers
|
||||
let p = Math.floor(Math.log10(Math.abs(number)));
|
||||
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));
|
||||
|
||||
// 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)
|
||||
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]]);
|
||||
}
|
||||
|
||||
@ -71,7 +73,7 @@ export function getSplineCurvePointsStr(xList, yList) {
|
||||
angle: Math.atan2(lengthY, lengthX)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
let controlPoint = (current, previous, next, reverse) => {
|
||||
let p = previous || current;
|
||||
let n = next || current;
|
||||
@ -82,18 +84,18 @@ export function getSplineCurvePointsStr(xList, yList) {
|
||||
let y = current[1] + Math.sin(angle) * length;
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
|
||||
let bezierCommand = (point, i, a) => {
|
||||
let cps = controlPoint(a[i - 1], a[i - 2], point);
|
||||
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]}`;
|
||||
};
|
||||
|
||||
|
||||
let pointStr = (points, command) => {
|
||||
return points.reduce((acc, point, i, a) => i === 0
|
||||
? `${point[0]},${point[1]}`
|
||||
: `${acc} ${command(point, i, a)}`, '');
|
||||
};
|
||||
|
||||
|
||||
return pointStr(points, bezierCommand);
|
||||
}
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
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;
|
||||
const LABEL_MARGIN = 4;
|
||||
const LABEL_MAX_CHARS = 15;
|
||||
const LABEL_MAX_CHARS = 18;
|
||||
export const FONT_SIZE = 10;
|
||||
const BASE_LINE_COLOR = '#E2E6E9';
|
||||
const FONT_FILL = '#313B44';
|
||||
|
||||
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) {
|
||||
@ -29,14 +27,14 @@ export function createSVG(tag, o) {
|
||||
element.appendChild(ref);
|
||||
|
||||
} else if (i === "styles") {
|
||||
if(typeof val === "object") {
|
||||
if (typeof val === "object") {
|
||||
Object.keys(val).map(prop => {
|
||||
element.style[prop] = val[prop];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if(i === "className") { i = "class"; }
|
||||
if(i === "innerHTML") {
|
||||
if (i === "className") { i = "class"; }
|
||||
if (i === "innerHTML") {
|
||||
element['textContent'] = val;
|
||||
} else {
|
||||
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 = {
|
||||
className: className,
|
||||
transform: transform
|
||||
};
|
||||
if(parent) args.inside = parent;
|
||||
if (parent) args.inside = parent;
|
||||
return createSVG('g', args);
|
||||
}
|
||||
|
||||
export function wrapInSVGGroup(elements, className='') {
|
||||
export function wrapInSVGGroup(elements, className = '') {
|
||||
let g = createSVG('g', {
|
||||
className: className
|
||||
});
|
||||
@ -99,7 +97,7 @@ export function wrapInSVGGroup(elements, className='') {
|
||||
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', {
|
||||
className: className,
|
||||
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 [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||
return `M${center.x} ${center.y}
|
||||
@ -120,7 +118,7 @@ export function makeArcPathStr(startPosition, endPosition, center, radius, clock
|
||||
${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 [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, center.y * 2, center.y + endPosition.y];
|
||||
return `M${center.x} ${center.y}
|
||||
@ -132,7 +130,7 @@ export function makeCircleStr(startPosition, endPosition, center, radius, clockW
|
||||
${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 [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||
|
||||
@ -141,7 +139,7 @@ export function makeArcStrokePathStr(startPosition, endPosition, center, radius,
|
||||
${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 [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) {
|
||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
||||
let gradientId = 'path-fill-gradient' + '-' + color + '-' + (lighter ? 'lighter' : 'default');
|
||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||
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]);
|
||||
@ -168,8 +166,31 @@ export function makeGradient(svgDefElem, color, lighter = false) {
|
||||
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 = {
|
||||
className: 'percentage-bar',
|
||||
@ -177,20 +198,13 @@ export function percentageBar(x, y, width, height,
|
||||
y: y,
|
||||
width: width,
|
||||
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);
|
||||
}
|
||||
|
||||
export function heatSquare(className, x, y, size, radius, fill='none', data={}) {
|
||||
export function heatSquare(className, x, y, size, radius, fill = 'none', data = {}) {
|
||||
let args = {
|
||||
className: className,
|
||||
x: x,
|
||||
@ -208,39 +222,9 @@ export function heatSquare(className, x, y, size, radius, fill='none', data={})
|
||||
return createSVG("rect", args);
|
||||
}
|
||||
|
||||
export function legendBar(x, y, size, fill='none', label, truncate=false) {
|
||||
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
|
||||
|
||||
let args = {
|
||||
className: 'legend-bar',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: size,
|
||||
height: '2px',
|
||||
fill: fill
|
||||
};
|
||||
let text = createSVG('text', {
|
||||
className: 'legend-dataset-text',
|
||||
x: 0,
|
||||
y: 0,
|
||||
dy: (FONT_SIZE * 2) + '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("rect", args));
|
||||
group.appendChild(text);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
export function legendDot(x, y, size, radius, fill='none', label, value, 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;
|
||||
if (!font_size) font_size = FONT_SIZE;
|
||||
|
||||
let args = {
|
||||
className: 'legend-dot',
|
||||
@ -256,32 +240,36 @@ export function legendDot(x, y, size, radius, fill='none', label, value, truncat
|
||||
className: 'legend-dataset-label',
|
||||
x: size,
|
||||
y: 0,
|
||||
dx: (FONT_SIZE) + 'px',
|
||||
dy: (FONT_SIZE/3) + 'px',
|
||||
'font-size': (FONT_SIZE * 1.6) + 'px',
|
||||
dx: (font_size) + 'px',
|
||||
dy: (font_size / 3) + 'px',
|
||||
'font-size': (font_size * 1.6) + 'px',
|
||||
'text-anchor': 'start',
|
||||
fill: FONT_FILL,
|
||||
innerHTML: label
|
||||
});
|
||||
|
||||
let 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',
|
||||
fill: FONT_FILL,
|
||||
innerHTML: value
|
||||
});
|
||||
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', {
|
||||
transform: `translate(${x}, ${y})`
|
||||
});
|
||||
group.appendChild(createSVG("rect", args));
|
||||
group.appendChild(textLabel);
|
||||
group.appendChild(textValue);
|
||||
|
||||
if (value && textValue) {
|
||||
group.appendChild(textValue);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
@ -289,7 +277,7 @@ export function legendDot(x, y, size, radius, fill='none', label, value, truncat
|
||||
export function makeText(className, x, y, content, options = {}) {
|
||||
let fontSize = options.fontSize || FONT_SIZE;
|
||||
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';
|
||||
return createSVG('text', {
|
||||
className: className,
|
||||
@ -303,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', {
|
||||
className: 'line-vertical ' + options.className,
|
||||
x1: 0,
|
||||
@ -326,7 +313,7 @@ function makeVertLine(x, label, y1, y2, options={}) {
|
||||
});
|
||||
|
||||
let line = createSVG('g', {
|
||||
transform: `translate(${ x }, 0)`
|
||||
transform: `translate(${x}, 0)`
|
||||
});
|
||||
|
||||
line.appendChild(l);
|
||||
@ -335,13 +322,18 @@ function makeVertLine(x, label, y1, y2, options={}) {
|
||||
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 +
|
||||
(options.lineType === "dashed" ? "dashed": "");
|
||||
(options.lineType === "dashed" ? "dashed" : "");
|
||||
|
||||
let l = createSVG('line', {
|
||||
className: className,
|
||||
@ -360,7 +352,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
dy: (FONT_SIZE / 2 - 2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': x1 < x2 ? 'end' : 'start',
|
||||
innerHTML: label+""
|
||||
innerHTML: label + ""
|
||||
});
|
||||
|
||||
let line = createSVG('g', {
|
||||
@ -368,7 +360,7 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
'stroke-opacity': 1
|
||||
});
|
||||
|
||||
if(text === 0 || text === '0') {
|
||||
if (text === 0 || text === '0') {
|
||||
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
||||
}
|
||||
|
||||
@ -378,19 +370,19 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
return line;
|
||||
}
|
||||
|
||||
export function yLine(y, label, width, options={}) {
|
||||
export function yLine(y, label, width, options = {}) {
|
||||
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 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;
|
||||
x2 = width;
|
||||
}
|
||||
@ -400,22 +392,23 @@ export function yLine(y, label, width, options={}) {
|
||||
x1 += options.offset;
|
||||
x2 += options.offset;
|
||||
|
||||
if (typeof label === "number") label = round(label);
|
||||
|
||||
return makeHoriLine(y, label, x1, x2, {
|
||||
stroke: options.stroke,
|
||||
className: options.className,
|
||||
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(!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
|
||||
// y2(span)
|
||||
@ -431,21 +424,23 @@ export function xLine(x, label, height, options={}) {
|
||||
let y1 = height + AXIS_TICK_LENGTH;
|
||||
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
|
||||
y1 = -1 * AXIS_TICK_LENGTH;
|
||||
y2 = 0;
|
||||
}
|
||||
|
||||
return makeVertLine(x, label, y1, y2, {
|
||||
stroke: options.stroke,
|
||||
className: options.className,
|
||||
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
|
||||
: width - getStringWidth(label, 5) - LABEL_MARGIN;
|
||||
|
||||
@ -456,7 +451,7 @@ export function yMarker(y, label, width, options={}) {
|
||||
dy: (FONT_SIZE / -2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': 'start',
|
||||
innerHTML: label+""
|
||||
innerHTML: label + ""
|
||||
});
|
||||
|
||||
let line = makeHoriLine(y, '', 0, width, {
|
||||
@ -470,15 +465,15 @@ export function yMarker(y, label, width, options={}) {
|
||||
return line;
|
||||
}
|
||||
|
||||
export function yRegion(y1, y2, width, label, options={}) {
|
||||
export function yRegion(y1, y2, width, label, options = {}) {
|
||||
// return a group
|
||||
let height = y1 - y2;
|
||||
|
||||
let rect = createSVG('rect', {
|
||||
className: `bar mini`, // remove class
|
||||
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}`
|
||||
},
|
||||
// 'data-point-index': index,
|
||||
@ -488,9 +483,9 @@ export function yRegion(y1, y2, width, label, options={}) {
|
||||
height: height
|
||||
});
|
||||
|
||||
if(!options.labelPos) options.labelPos = 'right';
|
||||
if (!options.labelPos) options.labelPos = 'right';
|
||||
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', {
|
||||
className: 'chart-label',
|
||||
@ -499,7 +494,7 @@ export function yRegion(y1, y2, width, label, options={}) {
|
||||
dy: (FONT_SIZE / -2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': 'start',
|
||||
innerHTML: label+""
|
||||
innerHTML: label + ""
|
||||
});
|
||||
|
||||
let region = createSVG('g', {
|
||||
@ -512,11 +507,11 @@ export function yRegion(y1, y2, width, label, options={}) {
|
||||
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);
|
||||
y -= offset;
|
||||
|
||||
if(height === 0) {
|
||||
if (height === 0) {
|
||||
height = meta.minHeight;
|
||||
y -= meta.minHeight;
|
||||
}
|
||||
@ -527,6 +522,26 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
|
||||
if (!isValidNumber(height, true)) height = 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', {
|
||||
className: `bar mini`,
|
||||
style: `fill: ${color}`,
|
||||
@ -539,14 +554,14 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
|
||||
|
||||
label += "";
|
||||
|
||||
if(!label && !label.length) {
|
||||
if (!label && !label.length) {
|
||||
return rect;
|
||||
} else {
|
||||
rect.setAttribute('y', 0);
|
||||
rect.setAttribute('x', 0);
|
||||
let text = createSVG('text', {
|
||||
className: 'data-point-value',
|
||||
x: width/2,
|
||||
x: width / 2,
|
||||
y: 0,
|
||||
dy: (FONT_SIZE / 2 * -1) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
@ -565,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', {
|
||||
style: `fill: ${color}`,
|
||||
style: `fill: ${color}; ${hideDotBorder ? `stroke: ${color}`: ''}`,
|
||||
'data-point-index': index,
|
||||
cx: x,
|
||||
cy: y,
|
||||
@ -576,7 +591,7 @@ export function datasetDot(x, y, radius, color, label='', index=0) {
|
||||
|
||||
label += "";
|
||||
|
||||
if(!label && !label.length) {
|
||||
if (!label && !label.length) {
|
||||
return dot;
|
||||
} else {
|
||||
dot.setAttribute('cy', 0);
|
||||
@ -603,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 pointsStr = pointsList.join("L");
|
||||
|
||||
@ -611,10 +626,10 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
|
||||
if (options.spline)
|
||||
pointsStr = getSplineCurvePointsStr(xList, yList);
|
||||
|
||||
let path = makePath("M"+pointsStr, 'line-graph-path', color);
|
||||
let path = makePath("M" + pointsStr, 'line-graph-path', color);
|
||||
|
||||
// HeatLine
|
||||
if(options.heatline) {
|
||||
if (options.heatline) {
|
||||
let gradient_id = makeGradient(meta.svgDefs, color);
|
||||
path.style.stroke = `url(#${gradient_id})`;
|
||||
}
|
||||
@ -624,7 +639,7 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
|
||||
};
|
||||
|
||||
// Region
|
||||
if(options.regionFill) {
|
||||
if (options.regionFill) {
|
||||
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}`;
|
||||
@ -637,7 +652,7 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
|
||||
export let makeOverlay = {
|
||||
'bar': (unit) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'rect') {
|
||||
if (unit.nodeName !== 'rect') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -645,7 +660,7 @@ export let makeOverlay = {
|
||||
overlay.style.fill = '#000000';
|
||||
overlay.style.opacity = '0.4';
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
return overlay;
|
||||
@ -653,7 +668,7 @@ export let makeOverlay = {
|
||||
|
||||
'dot': (unit) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'circle') {
|
||||
if (unit.nodeName !== 'circle') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -664,7 +679,7 @@ export let makeOverlay = {
|
||||
overlay.setAttribute('fill', fill);
|
||||
overlay.style.opacity = '0.6';
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
return overlay;
|
||||
@ -672,7 +687,7 @@ export let makeOverlay = {
|
||||
|
||||
'heat_square': (unit) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'circle') {
|
||||
if (unit.nodeName !== 'circle') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -683,7 +698,7 @@ export let makeOverlay = {
|
||||
overlay.setAttribute('fill', fill);
|
||||
overlay.style.opacity = '0.6';
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
return overlay;
|
||||
@ -693,7 +708,7 @@ export let makeOverlay = {
|
||||
export let updateOverlay = {
|
||||
'bar': (unit, overlay) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'rect') {
|
||||
if (unit.nodeName !== 'rect') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -704,14 +719,14 @@ export let updateOverlay = {
|
||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||
});
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
},
|
||||
|
||||
'dot': (unit, overlay) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'circle') {
|
||||
if (unit.nodeName !== 'circle') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -722,14 +737,14 @@ export let updateOverlay = {
|
||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||
});
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
},
|
||||
|
||||
'heat_square': (unit, overlay) => {
|
||||
let transformValue;
|
||||
if(unit.nodeName !== 'circle') {
|
||||
if (unit.nodeName !== 'circle') {
|
||||
transformValue = unit.getAttribute('transform');
|
||||
unit = unit.childNodes[0];
|
||||
}
|
||||
@ -740,7 +755,7 @@ export let updateOverlay = {
|
||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||
});
|
||||
|
||||
if(transformValue) {
|
||||
if (transformValue) {
|
||||
overlay.setAttribute('transform', transformValue);
|
||||
}
|
||||
},
|
||||
|
||||
@ -4,13 +4,13 @@ import { CSSTEXT } from '../../css/chartsCss';
|
||||
export function downloadFile(filename, data) {
|
||||
var a = document.createElement('a');
|
||||
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);
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(function(){
|
||||
setTimeout(function () {
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}, 300);
|
||||
|
||||
@ -115,3 +115,29 @@ export function round(d) {
|
||||
// https://www.jacklmoore.com/notes/rounding-in-javascript/
|
||||
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;
|
||||
}
|
||||
@ -5,25 +5,25 @@ function normalize(x) {
|
||||
// Returns normalized number and exponent
|
||||
// https://stackoverflow.com/q/9383593/6495043
|
||||
|
||||
if(x===0) {
|
||||
if (x === 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;
|
||||
if(!isFinite(x)) {
|
||||
return {mantissa: sig * 4503599627370496, exponent: 972};
|
||||
if (!isFinite(x)) {
|
||||
return { mantissa: sig * 4503599627370496, exponent: 972 };
|
||||
}
|
||||
|
||||
x = Math.abs(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];
|
||||
}
|
||||
|
||||
function getChartRangeIntervals(max, min=0) {
|
||||
function getChartRangeIntervals(max, min = 0) {
|
||||
let upperBound = Math.ceil(max);
|
||||
let lowerBound = Math.floor(min);
|
||||
let range = upperBound - lowerBound;
|
||||
@ -32,38 +32,38 @@ function getChartRangeIntervals(max, min=0) {
|
||||
let partSize = 1;
|
||||
|
||||
// To avoid too many partitions
|
||||
if(range > 5) {
|
||||
if(range % 2 !== 0) {
|
||||
if (range > 5) {
|
||||
if (range % 2 !== 0) {
|
||||
upperBound++;
|
||||
// Recalc range
|
||||
range = upperBound - lowerBound;
|
||||
}
|
||||
noOfParts = range/2;
|
||||
noOfParts = range / 2;
|
||||
partSize = 2;
|
||||
}
|
||||
|
||||
// Special case: 1 and 2
|
||||
if(range <= 2) {
|
||||
if (range <= 2) {
|
||||
noOfParts = 4;
|
||||
partSize = range/noOfParts;
|
||||
partSize = range / noOfParts;
|
||||
}
|
||||
|
||||
// Special case: 0
|
||||
if(range === 0) {
|
||||
if (range === 0) {
|
||||
noOfParts = 5;
|
||||
partSize = 1;
|
||||
}
|
||||
|
||||
let intervals = [];
|
||||
for(var i = 0; i <= noOfParts; i++){
|
||||
for (var i = 0; i <= noOfParts; i++) {
|
||||
intervals.push(lowerBound + partSize * i);
|
||||
}
|
||||
return intervals;
|
||||
}
|
||||
|
||||
function getChartIntervals(maxValue, minValue=0) {
|
||||
function getChartIntervals(maxValue, minValue = 0) {
|
||||
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
|
||||
normalMaxValue = normalMaxValue.toFixed(6);
|
||||
@ -73,7 +73,7 @@ function getChartIntervals(maxValue, minValue=0) {
|
||||
return intervals;
|
||||
}
|
||||
|
||||
export function calcChartIntervals(values, withMinimum=false) {
|
||||
export function calcChartIntervals(values, withMinimum = false, range = {}) {
|
||||
//*** Where the magic happens ***
|
||||
|
||||
// Calculates best-fit y intervals from given values
|
||||
@ -82,6 +82,14 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
let maxValue = Math.max(...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
|
||||
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
|
||||
let value = 0;
|
||||
for(var i = 1; value < absMinValue; i++) {
|
||||
for (var i = 1; value < absMinValue; i++) {
|
||||
value += intervalSize;
|
||||
intervals.unshift((-1) * value);
|
||||
}
|
||||
@ -101,9 +109,9 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
|
||||
// CASE I: Both non-negative
|
||||
|
||||
if(maxValue >= 0 && minValue >= 0) {
|
||||
if (maxValue >= 0 && minValue >= 0) {
|
||||
exponent = normalize(maxValue)[1];
|
||||
if(!withMinimum) {
|
||||
if (!withMinimum) {
|
||||
intervals = getChartIntervals(maxValue);
|
||||
} else {
|
||||
intervals = getChartIntervals(maxValue, minValue);
|
||||
@ -112,7 +120,7 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
|
||||
// CASE II: Only minValue negative
|
||||
|
||||
else if(maxValue > 0 && minValue < 0) {
|
||||
else if (maxValue > 0 && minValue < 0) {
|
||||
// `withMinimum` irrelevant in this case,
|
||||
// We'll be handling both sides of zero separately
|
||||
// (both starting from zero)
|
||||
@ -121,7 +129,7 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
|
||||
let absMinValue = Math.abs(minValue);
|
||||
|
||||
if(maxValue >= absMinValue) {
|
||||
if (maxValue >= absMinValue) {
|
||||
exponent = normalize(maxValue)[1];
|
||||
intervals = getPositiveFirstIntervals(maxValue, absMinValue);
|
||||
} else {
|
||||
@ -135,7 +143,7 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
|
||||
// CASE III: Both non-positive
|
||||
|
||||
else if(maxValue <= 0 && minValue <= 0) {
|
||||
else if (maxValue <= 0 && minValue <= 0) {
|
||||
// Mirrored Case I:
|
||||
// Work with positives, then reverse the sign and array
|
||||
|
||||
@ -143,7 +151,7 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
let pseudoMinValue = Math.abs(maxValue);
|
||||
|
||||
exponent = normalize(pseudoMaxValue)[1];
|
||||
if(!withMinimum) {
|
||||
if (!withMinimum) {
|
||||
intervals = getChartIntervals(pseudoMaxValue);
|
||||
} else {
|
||||
intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue);
|
||||
@ -158,11 +166,11 @@ export function calcChartIntervals(values, withMinimum=false) {
|
||||
export function getZeroIndex(yPts) {
|
||||
let zeroIndex;
|
||||
let interval = getIntervalSize(yPts);
|
||||
if(yPts.indexOf(0) >= 0) {
|
||||
if (yPts.indexOf(0) >= 0) {
|
||||
// the range has a given zero
|
||||
// zero-line on the chart
|
||||
zeroIndex = yPts.indexOf(0);
|
||||
} else if(yPts[0] > 0) {
|
||||
} else if (yPts[0] > 0) {
|
||||
// Minimum value is positive
|
||||
// zero-line is off the chart: below
|
||||
let min = yPts[0];
|
||||
@ -181,7 +189,7 @@ export function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
|
||||
let part = range * 1.0 / noOfIntervals;
|
||||
let intervals = [];
|
||||
|
||||
for(var i = 0; i <= noOfIntervals; i++) {
|
||||
for (var i = 0; i <= noOfIntervals; i++) {
|
||||
intervals.push(min + part * i);
|
||||
}
|
||||
|
||||
@ -193,7 +201,7 @@ export function getIntervalSize(orderedArray) {
|
||||
}
|
||||
|
||||
export function getValueRange(orderedArray) {
|
||||
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
||||
return orderedArray[orderedArray.length - 1] - orderedArray[0];
|
||||
}
|
||||
|
||||
export function scale(val, yAxis) {
|
||||
@ -210,7 +218,7 @@ export function isInRange2D(coord, minCoord, maxCoord) {
|
||||
}
|
||||
|
||||
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);
|
||||
}, []);
|
||||
|
||||
@ -226,7 +234,7 @@ export function calcDistribution(values, distributionSize) {
|
||||
let distributionStep = 1 / (distributionSize - 1);
|
||||
let distribution = [];
|
||||
|
||||
for(var i = 0; i < distributionSize; i++) {
|
||||
for (var i = 0; i < distributionSize; i++) {
|
||||
let checkpoint = dataMaxValue * (distributionStep * i);
|
||||
distribution.push(checkpoint);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user