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>
|
||||
15
package.json
15
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() {
|
||||
@ -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
|
||||
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
|
||||
);
|
||||
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++;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
@ -39,11 +40,16 @@ 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) {
|
||||
@ -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,
|
||||
|
||||
@ -193,8 +199,8 @@ 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 () {
|
||||
return this.state.yAxis;
|
||||
@ -211,7 +217,7 @@ export default class AxisChart extends BaseChart {
|
||||
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)
|
||||
@ -299,7 +305,9 @@ 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
|
||||
@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
@ -423,25 +435,23 @@ 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);
|
||||
});
|
||||
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() {
|
||||
|
||||
@ -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,10 +35,11 @@ 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));
|
||||
@ -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);
|
||||
}
|
||||
@ -150,10 +161,12 @@ export default class BaseChart {
|
||||
|
||||
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.setupNavigation(init);
|
||||
}
|
||||
@ -223,13 +236,14 @@ 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) {
|
||||
@ -262,7 +276,26 @@ export default class BaseChart {
|
||||
}
|
||||
}
|
||||
|
||||
renderLegend() {}
|
||||
renderLegend(dataset) {
|
||||
this.legendArea.textContent = '';
|
||||
let count = 0;
|
||||
let y = 0;
|
||||
|
||||
dataset.map((data, index) => {
|
||||
let divisor = Math.floor(this.width / LEGEND_ITEM_WIDTH);
|
||||
if (count > divisor) {
|
||||
count = 0;
|
||||
y += this.config.legendRowHeight;
|
||||
}
|
||||
let x = LEGEND_ITEM_WIDTH * count;
|
||||
let dot = this.makeLegend(data, index, x, y);
|
||||
this.legendArea.appendChild(dot);
|
||||
count++;
|
||||
});
|
||||
}
|
||||
|
||||
makeLegend() { }
|
||||
|
||||
|
||||
setupNavigation(init = false) {
|
||||
if (!this.config.isNavigable) return;
|
||||
|
||||
@ -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
|
||||
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,7 +37,7 @@ export default class DonutChart extends AggregationChart {
|
||||
|
||||
let componentConfigs = [
|
||||
[
|
||||
'donutSlices',
|
||||
this.sliceName,
|
||||
{},
|
||||
function () {
|
||||
return {
|
||||
@ -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;
|
||||
|
||||
@ -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,7 +33,6 @@ export default class PercentageChart extends AggregationChart {
|
||||
'percentageBars',
|
||||
{
|
||||
barHeight: this.barOptions.height,
|
||||
barDepth: this.barOptions.depth,
|
||||
},
|
||||
function () {
|
||||
return {
|
||||
@ -74,11 +75,12 @@ export default class PercentageChart extends AggregationChart {
|
||||
let bars = this.components.get('percentageBars').store;
|
||||
let bar = e.target;
|
||||
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 width = bar.getAttribute('width') || bar.getBoundingClientRect().width;
|
||||
|
||||
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]) + ': ';
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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({
|
||||
@ -112,7 +120,8 @@ export default class PieChart extends AggregationChart {
|
||||
const color = this.colors[i];
|
||||
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;
|
||||
@ -122,11 +131,15 @@ export default class PieChart extends AggregationChart {
|
||||
this.tip.setValues(x, y, { name: title, value: percent + "%" });
|
||||
this.tip.showTip();
|
||||
} else {
|
||||
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);
|
||||
@ -135,12 +148,12 @@ export default class PieChart extends AggregationChart {
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
@ -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 {
|
||||
@ -102,10 +104,14 @@ let componentConfigs = {
|
||||
percentageBars: {
|
||||
layerClass: 'percentage-bars',
|
||||
makeElements(data) {
|
||||
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;
|
||||
});
|
||||
},
|
||||
@ -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
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
@ -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) {
|
||||
@ -379,7 +390,7 @@ let componentConfigs = {
|
||||
}
|
||||
|
||||
this.units = [];
|
||||
if(!c.hideDots) {
|
||||
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) {
|
||||
|
||||
@ -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 || [];
|
||||
@ -31,11 +34,9 @@ export function dataPrep(data, type) {
|
||||
} 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;
|
||||
@ -96,8 +97,8 @@ export function zeroDataPrep(realData) {
|
||||
return zeroData;
|
||||
}
|
||||
|
||||
export function getShortenedLabels(chartWidth, labels=[], isSeries=true) {
|
||||
let allowedSpace = chartWidth / labels.length;
|
||||
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;
|
||||
|
||||
@ -120,8 +121,14 @@ export function getShortenedLabels(chartWidth, labels=[], isSeries=true) {
|
||||
}
|
||||
} else {
|
||||
if (i % seriesMultiple !== 0) {
|
||||
if (i !== (labels.length - 1)) {
|
||||
label = "";
|
||||
}
|
||||
} else {
|
||||
if (i > (labels.length - (seriesMultiple / 2))) {
|
||||
label = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return label;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -2,8 +2,7 @@ export function $(expr, con) {
|
||||
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;
|
||||
|
||||
@ -51,13 +51,15 @@ export function shortenLargeNumber(label) {
|
||||
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 = [];
|
||||
const length = Math.min(xList.length, yList.length);
|
||||
|
||||
for (let i = 0; i < xList.length; i++) {
|
||||
points.push([xList[i], yList[i]]);
|
||||
}
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
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;
|
||||
@ -158,7 +156,7 @@ export function makeGradient(svgDefElem, color, lighter = false) {
|
||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||
let opacities = [1, 0.6, 0.2];
|
||||
if (lighter) {
|
||||
opacities = [0.4, 0.2, 0];
|
||||
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,14 +198,7 @@ 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);
|
||||
@ -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,15 +240,16 @@ 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', {
|
||||
let textValue = null;
|
||||
if (value) {
|
||||
textValue = createSVG('text', {
|
||||
className: 'legend-dataset-value',
|
||||
x: size,
|
||||
y: FONT_SIZE + 10,
|
||||
@ -272,16 +257,19 @@ export function legendDot(x, y, size, radius, fill='none', label, value, truncat
|
||||
dy: (FONT_SIZE / 3) + 'px',
|
||||
'font-size': (FONT_SIZE * 1.2) + 'px',
|
||||
'text-anchor': 'start',
|
||||
fill: FONT_FILL,
|
||||
innerHTML: value
|
||||
});
|
||||
}
|
||||
|
||||
let group = createSVG('g', {
|
||||
transform: `translate(${x}, ${y})`
|
||||
});
|
||||
group.appendChild(createSVG("rect", args));
|
||||
group.appendChild(textLabel);
|
||||
|
||||
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,
|
||||
@ -304,7 +292,6 @@ export function makeText(className, x, y, content, options = {}) {
|
||||
}
|
||||
|
||||
function makeVertLine(x, label, y1, y2, options = {}) {
|
||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||
let l = createSVG('line', {
|
||||
className: 'line-vertical ' + options.className,
|
||||
x1: 0,
|
||||
@ -336,9 +323,14 @@ function makeVertLine(x, label, y1, y2, options={}) {
|
||||
}
|
||||
|
||||
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);
|
||||
if (options.shortenNumbers) {
|
||||
if (options.numberFormatter) {
|
||||
label = options.numberFormatter(label);
|
||||
} else {
|
||||
label = shortenLargeNumber(label);
|
||||
}
|
||||
}
|
||||
|
||||
let className = 'line-horizontal ' + options.className +
|
||||
(options.lineType === "dashed" ? "dashed" : "");
|
||||
@ -400,11 +392,13 @@ 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,
|
||||
});
|
||||
}
|
||||
|
||||
@ -414,7 +408,6 @@ export function xLine(x, label, height, options={}) {
|
||||
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 = '';
|
||||
|
||||
// Draw X axis line in span/tick mode with optional label
|
||||
@ -438,14 +431,16 @@ export function xLine(x, label, height, options={}) {
|
||||
}
|
||||
|
||||
return makeVertLine(x, label, y1, y2, {
|
||||
stroke: options.stroke,
|
||||
className: options.className,
|
||||
lineType: options.lineType
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -477,8 +472,8 @@ export function yRegion(y1, y2, width, label, options={}) {
|
||||
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,
|
||||
@ -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}`,
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user