chartist 2.0.0-rc.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/chartist.min.css +1 -1
- package/dist/chartist.min.js +2 -1
- package/dist/compat.cjs +7 -0
- package/dist/compat.cjs.map +1 -1
- package/dist/compat.js +7 -0
- package/dist/compat.js.map +1 -1
- package/dist/compat.min.js +1 -1
- package/dist/compat.min.js.map +1 -1
- package/dist/compat.umd.js +1 -1
- package/dist/compat.umd.js.map +1 -1
- package/dist/index.cjs +1185 -202
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +192 -52
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +3442 -2701
- package/dist/index.d.ts +3442 -2701
- package/dist/index.js +1179 -203
- package/dist/index.js.map +1 -1
- package/dist/index.min.css +1 -1
- package/dist/index.min.css.map +1 -1
- package/dist/index.min.js +2 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.umd.js +2 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/live.cjs +770 -0
- package/dist/live.cjs.map +1 -0
- package/dist/live.d.cts +203 -0
- package/dist/live.d.ts +202 -0
- package/dist/live.js +767 -0
- package/dist/live.js.map +1 -0
- package/dist/live.min.js +4 -0
- package/dist/live.min.js.map +1 -0
- package/dist/live.umd.js +4 -0
- package/dist/live.umd.js.map +1 -0
- package/dist/scss/_settings.scss +22 -0
- package/dist/scss/index.scss +107 -6
- package/package.json +12 -1
package/dist/index.js
CHANGED
|
@@ -533,6 +533,21 @@ function deserialize(data) {
|
|
|
533
533
|
return parsedData;
|
|
534
534
|
}
|
|
535
535
|
//#endregion
|
|
536
|
+
//#region src/render/style.ts
|
|
537
|
+
/**
|
|
538
|
+
* Applies a style declaration list through the CSSOM. A Content Security
|
|
539
|
+
* Policy without `'unsafe-inline'` for styles blocks a `style` attribute set
|
|
540
|
+
* by a script, but not `element.style`; the browser still reflects the
|
|
541
|
+
* declarations into the `style` attribute, so the markup stays the same.
|
|
542
|
+
* Elements without a `style` property (none in HTML or SVG documents) get
|
|
543
|
+
* the attribute.
|
|
544
|
+
*/
|
|
545
|
+
function setStyle(element, value) {
|
|
546
|
+
const { style } = element;
|
|
547
|
+
if (style) style.cssText = value;
|
|
548
|
+
else element.setAttribute("style", value);
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
536
551
|
//#region src/svg/SvgList.ts
|
|
537
552
|
/**
|
|
538
553
|
* A list of {@link Svg} wrappers that calls an `Svg` method on all of them
|
|
@@ -781,6 +796,160 @@ const easings = {
|
|
|
781
796
|
1.55
|
|
782
797
|
]
|
|
783
798
|
};
|
|
799
|
+
/** The CSS easing keywords as cubic Bézier curves. */
|
|
800
|
+
const CSS_EASINGS = {
|
|
801
|
+
linear: [
|
|
802
|
+
0,
|
|
803
|
+
0,
|
|
804
|
+
1,
|
|
805
|
+
1
|
|
806
|
+
],
|
|
807
|
+
ease: [
|
|
808
|
+
.25,
|
|
809
|
+
.1,
|
|
810
|
+
.25,
|
|
811
|
+
1
|
|
812
|
+
],
|
|
813
|
+
"ease-in": [
|
|
814
|
+
.42,
|
|
815
|
+
0,
|
|
816
|
+
1,
|
|
817
|
+
1
|
|
818
|
+
],
|
|
819
|
+
"ease-out": [
|
|
820
|
+
0,
|
|
821
|
+
0,
|
|
822
|
+
.58,
|
|
823
|
+
1
|
|
824
|
+
],
|
|
825
|
+
"ease-in-out": [
|
|
826
|
+
.42,
|
|
827
|
+
0,
|
|
828
|
+
.58,
|
|
829
|
+
1
|
|
830
|
+
]
|
|
831
|
+
};
|
|
832
|
+
const NUMBER = /-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi;
|
|
833
|
+
/** The four numbers of a cubic Bézier easing, or nothing for other easings. */
|
|
834
|
+
function curveOf(easing) {
|
|
835
|
+
const css = easing.trim();
|
|
836
|
+
if (CSS_EASINGS[css]) return CSS_EASINGS[css];
|
|
837
|
+
if (easings[css]) return easings[css];
|
|
838
|
+
const numbers = css.startsWith("cubic-bezier(") ? css.match(NUMBER) : null;
|
|
839
|
+
return numbers?.length === 4 ? numbers.map(Number) : void 0;
|
|
840
|
+
}
|
|
841
|
+
/** A cubic Bézier easing as a function of time, solved for x by bisection. */
|
|
842
|
+
function bezier([x1, y1, x2, y2]) {
|
|
843
|
+
const at = (a, b, t) => 3 * a * t * (1 - t) ** 2 + 3 * b * t ** 2 * (1 - t) + t ** 3;
|
|
844
|
+
return (time) => {
|
|
845
|
+
let low = 0;
|
|
846
|
+
let high = 1;
|
|
847
|
+
let t = time;
|
|
848
|
+
for (let i = 0; i < 30; i++) {
|
|
849
|
+
const x = at(x1, x2, t);
|
|
850
|
+
if (Math.abs(x - time) < 1e-6) break;
|
|
851
|
+
if (x < time) low = t;
|
|
852
|
+
else high = t;
|
|
853
|
+
t = (low + high) / 2;
|
|
854
|
+
}
|
|
855
|
+
return at(y1, y2, t);
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
/** `steps(n, position)` as a function of time. */
|
|
859
|
+
function steps(css) {
|
|
860
|
+
const count = Math.max(1, Math.round(Number(css.match(NUMBER)?.[0] ?? 1)));
|
|
861
|
+
const position = css.match(/(jump-)?(start|end|none|both)\s*\)/)?.[2] ?? "end";
|
|
862
|
+
const jumps = position === "none" ? count - 1 : position === "both" ? count + 1 : count;
|
|
863
|
+
const offset = position === "start" || position === "both" ? 1 : 0;
|
|
864
|
+
return (time) => Math.min(1, (Math.min(Math.floor(time * count), count - 1) + offset) / Math.max(1, jumps));
|
|
865
|
+
}
|
|
866
|
+
/** `linear(…)` as a function of time: its stops, evenly spread where not given. */
|
|
867
|
+
function linearStops(css) {
|
|
868
|
+
const stops = css.slice(css.indexOf("(") + 1, css.lastIndexOf(")")).split(",").map((stop) => stop.trim().split(/\s+/).map(parseFloat));
|
|
869
|
+
const ys = stops.map(([value]) => value);
|
|
870
|
+
const xs = stops.map(([, at], index) => at === void 0 || Number.isNaN(at) ? index / Math.max(1, stops.length - 1) : at / 100);
|
|
871
|
+
return (time) => {
|
|
872
|
+
let i = 1;
|
|
873
|
+
while (i < xs.length - 1 && time > xs[i]) i++;
|
|
874
|
+
const span = xs[i] - xs[i - 1] || 1;
|
|
875
|
+
return ys[i - 1] + (ys[i] - ys[i - 1]) * (time - xs[i - 1]) / span;
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
/** An easing as a function of time, `ease` for easings it doesn't know. */
|
|
879
|
+
function easingFunction(easing) {
|
|
880
|
+
if (typeof easing !== "string") return bezier(easing);
|
|
881
|
+
const css = easing.trim();
|
|
882
|
+
if (css.startsWith("steps(")) return steps(css);
|
|
883
|
+
if (css.startsWith("linear(")) return linearStops(css);
|
|
884
|
+
return bezier(curveOf(css) ?? CSS_EASINGS.ease);
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* The SMIL `keySplines` of an easing SMIL can express: a cubic Bézier
|
|
888
|
+
* curve whose four numbers lie within 0 and 1 (the CSS keywords, many
|
|
889
|
+
* `cubic-bezier()` curves, most names of {@link easings}). Browsers ignore
|
|
890
|
+
* an animation whose `keySplines` leave that range, so for other easings
|
|
891
|
+
* it returns nothing: sample them with {@link sampleEasing} instead.
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* import { keySplinesOf } from 'chartist';
|
|
896
|
+
*
|
|
897
|
+
* keySplinesOf('ease-out'); // '0 0 0.58 1'
|
|
898
|
+
* keySplinesOf('easeOutBack'); // undefined: it overshoots
|
|
899
|
+
* ```
|
|
900
|
+
* @param easing A CSS easing, a name of {@link easings}, or four numbers
|
|
901
|
+
* @returns The `keySplines` value, or nothing
|
|
902
|
+
*/
|
|
903
|
+
function keySplinesOf(easing) {
|
|
904
|
+
const curve = typeof easing === "string" ? curveOf(easing) : easing;
|
|
905
|
+
return curve?.length === 4 && curve.every((value) => value >= 0 && value <= 1) ? curve.join(" ") : void 0;
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* The eased progress of an animation at evenly spaced times, for SMIL
|
|
909
|
+
* `values` with even `keyTimes`: 12 points, and one per 50 ms for
|
|
910
|
+
* animations longer than 600 ms, at most 40. It takes the CSS easings
|
|
911
|
+
* (the keywords, `cubic-bezier()` curves, also ones that overshoot,
|
|
912
|
+
* `linear()` and `steps()`), the names of {@link easings} and four
|
|
913
|
+
* numbers of a cubic Bézier curve; anything else is sampled as `ease`.
|
|
914
|
+
* Points lie below 0 or above 1 where the easing overshoots.
|
|
915
|
+
*
|
|
916
|
+
* @example
|
|
917
|
+
* ```ts
|
|
918
|
+
* import { sampleEasing } from 'chartist';
|
|
919
|
+
*
|
|
920
|
+
* // A value that grows from 10 to 20 and overshoots a little on the way.
|
|
921
|
+
* const progress = sampleEasing('cubic-bezier(0.34, 1.56, 0.64, 1)', 400);
|
|
922
|
+
* const values = progress.map(p => 10 + (20 - 10) * p).join(';');
|
|
923
|
+
* ```
|
|
924
|
+
* @param easing The easing
|
|
925
|
+
* @param duration The animation's duration in ms
|
|
926
|
+
* @returns The progress at each point, from 0 to 1
|
|
927
|
+
*/
|
|
928
|
+
function sampleEasing(easing, duration) {
|
|
929
|
+
const count = duration > 600 ? Math.min(40, Math.max(12, Math.round(duration / 50))) : 12;
|
|
930
|
+
const at = easingFunction(easing);
|
|
931
|
+
return Array.from({ length: count }, (_, index) => {
|
|
932
|
+
const time = index / (count - 1);
|
|
933
|
+
return index === 0 ? 0 : index === count - 1 ? 1 : at(time);
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* The value between `from` and `to` at `progress`: the numbers in `to`
|
|
938
|
+
* moved from their counterparts in `from`, when both have the same shape
|
|
939
|
+
* (`10px` and `40px`, two paths with the same commands), else `from`
|
|
940
|
+
* until halfway and `to` after.
|
|
941
|
+
*/
|
|
942
|
+
function between(from, to, progress) {
|
|
943
|
+
const fromNumbers = from.match(NUMBER) ?? [];
|
|
944
|
+
const toNumbers = to.match(NUMBER) ?? [];
|
|
945
|
+
if (fromNumbers.length !== toNumbers.length || from.replace(NUMBER, "#") !== to.replace(NUMBER, "#")) return progress < .5 ? from : to;
|
|
946
|
+
let index = 0;
|
|
947
|
+
return to.replace(NUMBER, () => {
|
|
948
|
+
const start = Number(fromNumbers[index]);
|
|
949
|
+
const end = Number(toNumbers[index++]);
|
|
950
|
+
return String(+(start + (end - start) * progress).toFixed(4));
|
|
951
|
+
});
|
|
952
|
+
}
|
|
784
953
|
/**
|
|
785
954
|
* The property of an element that holds the emitter its descendants'
|
|
786
955
|
* animations report to when `animate()` gets none. The compat plugin
|
|
@@ -800,13 +969,22 @@ function createAnimation(element, attribute, animationDefinition, createGuided =
|
|
|
800
969
|
const attributeProperties = {};
|
|
801
970
|
let animationEasing;
|
|
802
971
|
let timeout;
|
|
803
|
-
if (easing) animationEasing = Array.isArray(easing) ? easing : easings[easing];
|
|
972
|
+
if (easing) animationEasing = Array.isArray(easing) ? easing : easings[easing] ?? easing;
|
|
804
973
|
def.begin = ensureUnit(def.begin, "ms");
|
|
805
974
|
def.dur = ensureUnit(def.dur, "ms");
|
|
806
975
|
if (animationEasing) {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
976
|
+
const keySplines = keySplinesOf(animationEasing);
|
|
977
|
+
if (keySplines) {
|
|
978
|
+
def.calcMode = "spline";
|
|
979
|
+
def.keySplines = keySplines;
|
|
980
|
+
def.keyTimes = "0;1";
|
|
981
|
+
} else if (def.from !== void 0 && def.to !== void 0) {
|
|
982
|
+
const duration = quantity(def.dur);
|
|
983
|
+
const progress = sampleEasing(animationEasing, duration.unit === "s" ? duration.value * 1e3 : duration.value);
|
|
984
|
+
def.calcMode = String(animationEasing).trim().startsWith("steps(") ? "discrete" : "linear";
|
|
985
|
+
def.values = progress.map((at) => between(String(def.from), String(def.to), at)).join(";");
|
|
986
|
+
def.keyTimes = progress.map((_, index) => +(index / (progress.length - 1)).toFixed(4)).join(";");
|
|
987
|
+
}
|
|
810
988
|
}
|
|
811
989
|
if (createGuided) {
|
|
812
990
|
def.fill = "freeze";
|
|
@@ -920,7 +1098,8 @@ var Svg = class Svg {
|
|
|
920
1098
|
}
|
|
921
1099
|
Object.keys(attributes).forEach((key) => {
|
|
922
1100
|
if (attributes[key] === void 0) return;
|
|
923
|
-
if (key
|
|
1101
|
+
if (key === "style") setStyle(this._node, String(attributes[key]));
|
|
1102
|
+
else if (key.indexOf(":") !== -1) {
|
|
924
1103
|
const namespacedAttribute = key.split(":");
|
|
925
1104
|
this._node.setAttributeNS(namespaces[namespacedAttribute[0]], key, String(attributes[key]));
|
|
926
1105
|
} else this._node.setAttribute(key, String(attributes[key]));
|
|
@@ -1505,6 +1684,65 @@ var BaseChart = class {
|
|
|
1505
1684
|
}
|
|
1506
1685
|
};
|
|
1507
1686
|
//#endregion
|
|
1687
|
+
//#region src/scene/node.ts
|
|
1688
|
+
const splitClasses = (names) => names.split(/\s+/).filter(Boolean);
|
|
1689
|
+
const builder = {
|
|
1690
|
+
attr(attributes) {
|
|
1691
|
+
const attrs = { ...this.attrs };
|
|
1692
|
+
for (const [name, value] of Object.entries(attributes)) if (value === void 0 || value === null) delete attrs[name];
|
|
1693
|
+
else attrs[name] = value;
|
|
1694
|
+
return withFields(this, { attrs });
|
|
1695
|
+
},
|
|
1696
|
+
addClass(names) {
|
|
1697
|
+
const added = splitClasses(names).filter((name, i, all) => !this.classes.includes(name) && all.indexOf(name) === i);
|
|
1698
|
+
return added.length ? withFields(this, { classes: [...this.classes, ...added] }) : this;
|
|
1699
|
+
},
|
|
1700
|
+
removeClass(names) {
|
|
1701
|
+
const removed = splitClasses(names);
|
|
1702
|
+
return withFields(this, { classes: this.classes.filter((name) => !removed.includes(name)) });
|
|
1703
|
+
},
|
|
1704
|
+
append(child, insertFirst = false) {
|
|
1705
|
+
return withFields(this, { children: insertFirst ? [child, ...this.children] : [...this.children, child] });
|
|
1706
|
+
},
|
|
1707
|
+
remove(child) {
|
|
1708
|
+
return withFields(this, { children: this.children.filter((existing) => existing !== child) });
|
|
1709
|
+
},
|
|
1710
|
+
replace(child, next) {
|
|
1711
|
+
return withFields(this, { children: this.children.map((existing) => existing === child ? next : existing) });
|
|
1712
|
+
},
|
|
1713
|
+
text(content) {
|
|
1714
|
+
return withFields(this, { content });
|
|
1715
|
+
},
|
|
1716
|
+
empty() {
|
|
1717
|
+
return withFields(this, {
|
|
1718
|
+
children: [],
|
|
1719
|
+
content: void 0
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
function create(fields) {
|
|
1724
|
+
const created = Object.create(builder);
|
|
1725
|
+
for (const [field, value] of Object.entries(fields)) if (value !== void 0) created[field] = value;
|
|
1726
|
+
return created;
|
|
1727
|
+
}
|
|
1728
|
+
function withFields(source, changes) {
|
|
1729
|
+
return create({
|
|
1730
|
+
...source,
|
|
1731
|
+
...changes
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
function node(name, attributes = {}, className = "", meta = {}) {
|
|
1735
|
+
const attrs = {};
|
|
1736
|
+
for (const [attr, value] of Object.entries(attributes)) if (value !== void 0 && value !== null) attrs[attr] = value;
|
|
1737
|
+
return create({
|
|
1738
|
+
name,
|
|
1739
|
+
attrs,
|
|
1740
|
+
classes: [...new Set(splitClasses(className))],
|
|
1741
|
+
children: [],
|
|
1742
|
+
...meta
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
//#endregion
|
|
1508
1746
|
//#region src/svg/SvgPath.ts
|
|
1509
1747
|
/**
|
|
1510
1748
|
* The parameters of each SVG path command, in the order the `d` attribute
|
|
@@ -2031,63 +2269,57 @@ function monotoneCubic(options) {
|
|
|
2031
2269
|
};
|
|
2032
2270
|
}
|
|
2033
2271
|
//#endregion
|
|
2034
|
-
//#region src/
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
const attrs = { ...this.attrs };
|
|
2039
|
-
for (const [name, value] of Object.entries(attributes)) if (value === void 0 || value === null) delete attrs[name];
|
|
2040
|
-
else attrs[name] = value;
|
|
2041
|
-
return withFields(this, { attrs });
|
|
2042
|
-
},
|
|
2043
|
-
addClass(names) {
|
|
2044
|
-
const added = splitClasses(names).filter((name, i, all) => !this.classes.includes(name) && all.indexOf(name) === i);
|
|
2045
|
-
return added.length ? withFields(this, { classes: [...this.classes, ...added] }) : this;
|
|
2046
|
-
},
|
|
2047
|
-
removeClass(names) {
|
|
2048
|
-
const removed = splitClasses(names);
|
|
2049
|
-
return withFields(this, { classes: this.classes.filter((name) => !removed.includes(name)) });
|
|
2050
|
-
},
|
|
2051
|
-
append(child, insertFirst = false) {
|
|
2052
|
-
return withFields(this, { children: insertFirst ? [child, ...this.children] : [...this.children, child] });
|
|
2053
|
-
},
|
|
2054
|
-
remove(child) {
|
|
2055
|
-
return withFields(this, { children: this.children.filter((existing) => existing !== child) });
|
|
2056
|
-
},
|
|
2057
|
-
replace(child, next) {
|
|
2058
|
-
return withFields(this, { children: this.children.map((existing) => existing === child ? next : existing) });
|
|
2059
|
-
},
|
|
2060
|
-
text(content) {
|
|
2061
|
-
return withFields(this, { content });
|
|
2062
|
-
},
|
|
2063
|
-
empty() {
|
|
2064
|
-
return withFields(this, {
|
|
2065
|
-
children: [],
|
|
2066
|
-
content: void 0
|
|
2067
|
-
});
|
|
2068
|
-
}
|
|
2069
|
-
};
|
|
2070
|
-
function create(fields) {
|
|
2071
|
-
const created = Object.create(builder);
|
|
2072
|
-
for (const [field, value] of Object.entries(fields)) if (value !== void 0) created[field] = value;
|
|
2073
|
-
return created;
|
|
2272
|
+
//#region src/layers/path.ts
|
|
2273
|
+
/** The interpolation for a `lineSmooth` option: smooth, straight or given. */
|
|
2274
|
+
function interpolationOf(lineSmooth) {
|
|
2275
|
+
return typeof lineSmooth === "function" ? lineSmooth : lineSmooth ? monotoneCubic() : none();
|
|
2074
2276
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2277
|
+
/**
|
|
2278
|
+
* The px shift that puts points in the middle of each step of a step axis,
|
|
2279
|
+
* where bars are centered, for `align: 'center'`.
|
|
2280
|
+
*/
|
|
2281
|
+
function centerShift(axisX, align) {
|
|
2282
|
+
return align === "center" && axisX.kind === "step" && !axisX.stretch ? (axisX.step ?? 0) / 2 : 0;
|
|
2080
2283
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2284
|
+
/**
|
|
2285
|
+
* Projects a series' values into the chart area and interpolates a path
|
|
2286
|
+
* through them. Holes break the path unless the interpolation fills them.
|
|
2287
|
+
* @param meta The meta of a value, attached to its path element.
|
|
2288
|
+
*/
|
|
2289
|
+
function seriesPath(values, axes, area, shift, interpolation, meta = () => void 0) {
|
|
2290
|
+
const pathCoordinates = [];
|
|
2291
|
+
const pathData = [];
|
|
2292
|
+
values.forEach((value, valueIndex) => {
|
|
2293
|
+
pathCoordinates.push(area.x1 + shift + axes.x.project(value, valueIndex, values), area.y1 - axes.y.project(value, valueIndex, values));
|
|
2294
|
+
pathData.push({
|
|
2295
|
+
value,
|
|
2296
|
+
valueIndex,
|
|
2297
|
+
meta: meta(valueIndex)
|
|
2298
|
+
});
|
|
2090
2299
|
});
|
|
2300
|
+
return interpolation(pathCoordinates, pathData);
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* The same path traced backward: it starts where the path ends, and each
|
|
2304
|
+
* segment runs to the previous point, with a curve's control points
|
|
2305
|
+
* swapped, so it draws the same shape. Holes (`M` commands within the
|
|
2306
|
+
* path) are kept between the same points. Arcs aren't supported.
|
|
2307
|
+
*/
|
|
2308
|
+
function reversePath(path) {
|
|
2309
|
+
const reversed = new SvgPath();
|
|
2310
|
+
const elements = path.pathElements;
|
|
2311
|
+
for (let i = elements.length - 1; i >= 0; i--) {
|
|
2312
|
+
const element = elements[i];
|
|
2313
|
+
const target = elements[i - 1];
|
|
2314
|
+
if (i === elements.length - 1 || elements[i + 1].command === "M") reversed.move(element.x, element.y, false, element.data);
|
|
2315
|
+
if (element.command === "M" || !target) continue;
|
|
2316
|
+
if (element.command === "C") {
|
|
2317
|
+
const curve = element;
|
|
2318
|
+
reversed.curve(curve.x2, curve.y2, curve.x1, curve.y1, target.x, target.y, false, target.data);
|
|
2319
|
+
} else if (element.command === "L") reversed.line(target.x, target.y, false, target.data);
|
|
2320
|
+
else throw new Error(`Chartist: can't reverse a path with "${element.command}" commands`);
|
|
2321
|
+
}
|
|
2322
|
+
return reversed;
|
|
2091
2323
|
}
|
|
2092
2324
|
//#endregion
|
|
2093
2325
|
//#region src/layers/line.ts
|
|
@@ -2102,7 +2334,7 @@ const DEFAULTS = {
|
|
|
2102
2334
|
showArea: false,
|
|
2103
2335
|
areaBase: 0
|
|
2104
2336
|
};
|
|
2105
|
-
const CLASS_NAMES$
|
|
2337
|
+
const CLASS_NAMES$5 = {
|
|
2106
2338
|
series: "ct-series",
|
|
2107
2339
|
line: "ct-line",
|
|
2108
2340
|
point: "ct-point",
|
|
@@ -2164,11 +2396,10 @@ function seriesOption(series, options, key) {
|
|
|
2164
2396
|
* ]
|
|
2165
2397
|
* });
|
|
2166
2398
|
* ```
|
|
2167
|
-
* @experimental
|
|
2168
2399
|
*/
|
|
2169
2400
|
function line(options = {}) {
|
|
2170
2401
|
const classNames = {
|
|
2171
|
-
...CLASS_NAMES$
|
|
2402
|
+
...CLASS_NAMES$5,
|
|
2172
2403
|
...options.classNames
|
|
2173
2404
|
};
|
|
2174
2405
|
return {
|
|
@@ -2183,7 +2414,7 @@ function line(options = {}) {
|
|
|
2183
2414
|
const { x: axisX, y: axisY } = axes;
|
|
2184
2415
|
if (!axisX || !axisY || !normalized) throw new Error("Layer \"line\" needs an x and a y axis");
|
|
2185
2416
|
if (seriesIndexes && options.reverseData) throw new Error("Layer \"line\": a series selection can't be combined with reverseData");
|
|
2186
|
-
const shift = options.align
|
|
2417
|
+
const shift = centerShift(axisX, options.align);
|
|
2187
2418
|
const order = data.series.map((_, i) => i);
|
|
2188
2419
|
if (options.reverseData) order.reverse();
|
|
2189
2420
|
return order.map((dataIndex, localIndex) => {
|
|
@@ -2193,18 +2424,11 @@ function line(options = {}) {
|
|
|
2193
2424
|
const seriesName = safeHasProperty(series, "name") && series.name;
|
|
2194
2425
|
const seriesClassName = safeHasProperty(series, "className") && series.className;
|
|
2195
2426
|
const seriesMeta = safeHasProperty(series, "meta") ? series.meta : void 0;
|
|
2196
|
-
const
|
|
2197
|
-
const
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
value,
|
|
2202
|
-
valueIndex,
|
|
2203
|
-
meta: getMetaData(series, valueIndex)
|
|
2204
|
-
});
|
|
2205
|
-
});
|
|
2206
|
-
const lineSmooth = seriesOption(series, options, "lineSmooth");
|
|
2207
|
-
const path = (typeof lineSmooth === "function" ? lineSmooth : lineSmooth ? monotoneCubic() : none())(pathCoordinates, pathData);
|
|
2427
|
+
const smoothing = interpolationOf(seriesOption(series, options, "lineSmooth"));
|
|
2428
|
+
const path = seriesPath(values, {
|
|
2429
|
+
x: axisX,
|
|
2430
|
+
y: axisY
|
|
2431
|
+
}, area, shift, smoothing, (valueIndex) => getMetaData(series, valueIndex));
|
|
2208
2432
|
const seriesContext = {
|
|
2209
2433
|
series,
|
|
2210
2434
|
seriesIndex,
|
|
@@ -2242,7 +2466,8 @@ function line(options = {}) {
|
|
|
2242
2466
|
ctx: {
|
|
2243
2467
|
...seriesContext,
|
|
2244
2468
|
values,
|
|
2245
|
-
path: path.clone()
|
|
2469
|
+
path: path.clone(),
|
|
2470
|
+
smoothing
|
|
2246
2471
|
}
|
|
2247
2472
|
}), ...children];
|
|
2248
2473
|
if (seriesOption(series, options, "showArea") && axisY.range) {
|
|
@@ -2251,14 +2476,15 @@ function line(options = {}) {
|
|
|
2251
2476
|
path.splitByCommand("M").filter((segment) => segment.pathElements.length > 1).map((segment) => {
|
|
2252
2477
|
const first = segment.pathElements[0];
|
|
2253
2478
|
const last = segment.pathElements[segment.pathElements.length - 1];
|
|
2254
|
-
return segment.clone(true).position(0).remove(1).move(first.x, areaBaseProjected).line(first.x, first.y).position(segment.pathElements.length + 1).line(last.x, areaBaseProjected);
|
|
2479
|
+
return segment.clone(true).position(0).remove(1).move(first.x, areaBaseProjected).line(first.x, first.y, false, first.data).position(segment.pathElements.length + 1).line(last.x, areaBaseProjected);
|
|
2255
2480
|
}).forEach((areaPath) => {
|
|
2256
2481
|
children = [node("path", { d: areaPath.stringify() }, classNames.area, {
|
|
2257
2482
|
role: "area",
|
|
2258
2483
|
ctx: {
|
|
2259
2484
|
...seriesContext,
|
|
2260
2485
|
values,
|
|
2261
|
-
path: areaPath.clone()
|
|
2486
|
+
path: areaPath.clone(),
|
|
2487
|
+
smoothing
|
|
2262
2488
|
}
|
|
2263
2489
|
}), ...children];
|
|
2264
2490
|
});
|
|
@@ -2667,7 +2893,6 @@ function presetAxes(config, boundOf) {
|
|
|
2667
2893
|
* `{ x, y }` form.
|
|
2668
2894
|
* @param extra `reverseData` and `normalize`, for the list form (the
|
|
2669
2895
|
* `{ x, y }` form takes them inline).
|
|
2670
|
-
* @experimental
|
|
2671
2896
|
*/
|
|
2672
2897
|
function cartesianAxes(input, extra = {}) {
|
|
2673
2898
|
const withOffset = (setup, dimension) => ({
|
|
@@ -2734,7 +2959,6 @@ const valueOf = (value, dimension) => Number(getMultiValue(value, dimension));
|
|
|
2734
2959
|
* layers: [axisGrid(), line(), axisLabels()]
|
|
2735
2960
|
* });
|
|
2736
2961
|
* ```
|
|
2737
|
-
* @experimental
|
|
2738
2962
|
*/
|
|
2739
2963
|
function stepAxis() {
|
|
2740
2964
|
return {
|
|
@@ -2793,7 +3017,6 @@ function stepAxis() {
|
|
|
2793
3017
|
* layers: [axisGrid(), line(), axisLabels()]
|
|
2794
3018
|
* });
|
|
2795
3019
|
* ```
|
|
2796
|
-
* @experimental
|
|
2797
3020
|
*/
|
|
2798
3021
|
function autoScaleAxis() {
|
|
2799
3022
|
return {
|
|
@@ -2853,7 +3076,6 @@ function autoScaleAxis() {
|
|
|
2853
3076
|
* layers: [axisGrid(), line({ showLine: false }), axisLabels()]
|
|
2854
3077
|
* });
|
|
2855
3078
|
* ```
|
|
2856
|
-
* @experimental
|
|
2857
3079
|
*/
|
|
2858
3080
|
function fixedScaleAxis() {
|
|
2859
3081
|
return {
|
|
@@ -2987,8 +3209,9 @@ function createPhaseEmitter() {
|
|
|
2987
3209
|
return current;
|
|
2988
3210
|
},
|
|
2989
3211
|
notify(name, value) {
|
|
2990
|
-
(listeners.get(name) ?? []).
|
|
3212
|
+
const results = (listeners.get(name) ?? []).map(({ listener }) => listener(value));
|
|
2991
3213
|
observe(name, value);
|
|
3214
|
+
return results;
|
|
2992
3215
|
},
|
|
2993
3216
|
clear() {
|
|
2994
3217
|
listeners.clear();
|
|
@@ -3055,7 +3278,6 @@ function findAll(scene, query = {}) {
|
|
|
3055
3278
|
* });
|
|
3056
3279
|
* const markup = mapped ? renderToString(mapped) : '';
|
|
3057
3280
|
* ```
|
|
3058
|
-
* @experimental
|
|
3059
3281
|
*/
|
|
3060
3282
|
function mapNodes(scene, mapper) {
|
|
3061
3283
|
let children = scene.children;
|
|
@@ -3200,10 +3422,9 @@ function layout(emitter, config, width, height) {
|
|
|
3200
3422
|
validate(result);
|
|
3201
3423
|
return result;
|
|
3202
3424
|
}
|
|
3203
|
-
function compose(emitter, config, data, area, measureText, axes = {}, normalized, padding = config.padding, bindings = [], labels = "html", measureStyle) {
|
|
3204
|
-
const composed = [];
|
|
3425
|
+
function compose(emitter, config, data, area, measureText, axes = {}, normalized, padding = config.padding, bindings = [], labels = "html", measureStyle, drawn = []) {
|
|
3205
3426
|
const byId = new Map((axes.all ?? []).map((axis) => [axis.id, axis]));
|
|
3206
|
-
config.layers.
|
|
3427
|
+
const layers = config.layers.map((layer, layerIndex) => {
|
|
3207
3428
|
const binding = bindings[layerIndex] ?? {};
|
|
3208
3429
|
const selection = data.flat ? void 0 : binding.series;
|
|
3209
3430
|
const layerData = selection ? {
|
|
@@ -3214,7 +3435,7 @@ function compose(emitter, config, data, area, measureText, axes = {}, normalized
|
|
|
3214
3435
|
...normalized,
|
|
3215
3436
|
series: selection.map((i) => normalized.series[i])
|
|
3216
3437
|
} : normalized;
|
|
3217
|
-
const
|
|
3438
|
+
const context = {
|
|
3218
3439
|
data: layerData,
|
|
3219
3440
|
area,
|
|
3220
3441
|
padding,
|
|
@@ -3230,12 +3451,38 @@ function compose(emitter, config, data, area, measureText, axes = {}, normalized
|
|
|
3230
3451
|
seriesIndexes: selection,
|
|
3231
3452
|
labels,
|
|
3232
3453
|
measureStyle
|
|
3454
|
+
};
|
|
3455
|
+
const nodes = layer.compose(context).map((top) => assignKeys(top, layerIndex));
|
|
3456
|
+
drawn.push({
|
|
3457
|
+
nodes: [],
|
|
3458
|
+
context
|
|
3233
3459
|
});
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3460
|
+
return {
|
|
3461
|
+
layer,
|
|
3462
|
+
context,
|
|
3463
|
+
nodes,
|
|
3464
|
+
layerIndex
|
|
3465
|
+
};
|
|
3238
3466
|
});
|
|
3467
|
+
const refiners = layers.flatMap(({ layer, context }) => layer.refine ? [{
|
|
3468
|
+
refine: layer.refine.bind(layer),
|
|
3469
|
+
context
|
|
3470
|
+
}] : []);
|
|
3471
|
+
const refine = (current) => refiners.reduce((refined, { refine: refineNode, context }) => {
|
|
3472
|
+
const result = refineNode(refined, context);
|
|
3473
|
+
return result ? withFields(result, {
|
|
3474
|
+
key: refined.key,
|
|
3475
|
+
children: refined.children
|
|
3476
|
+
}) : refined;
|
|
3477
|
+
}, current);
|
|
3478
|
+
const composed = [];
|
|
3479
|
+
for (const { nodes, layerIndex } of layers) for (const top of nodes) {
|
|
3480
|
+
const result = mapNodes(top, (current) => current.role === void 0 ? void 0 : emitter.transform("compose", refiners.length > 0 ? refine(current) : current));
|
|
3481
|
+
if (result) {
|
|
3482
|
+
drawn[layerIndex].nodes.push(result);
|
|
3483
|
+
composed.push(result);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3239
3486
|
return composed;
|
|
3240
3487
|
}
|
|
3241
3488
|
function scene(emitter, config, children, size) {
|
|
@@ -3263,6 +3510,7 @@ function runPipeline(emitter, options, environment, previous, from = "resolve")
|
|
|
3263
3510
|
rootClass: className,
|
|
3264
3511
|
...measureOptions
|
|
3265
3512
|
}) : measure;
|
|
3513
|
+
const drawn = [];
|
|
3266
3514
|
const measureText = withRootClass(environment.measureText);
|
|
3267
3515
|
const measureStyle = withRootClass(environment.measureStyle);
|
|
3268
3516
|
return {
|
|
@@ -3271,10 +3519,11 @@ function runPipeline(emitter, options, environment, previous, from = "resolve")
|
|
|
3271
3519
|
layout: laidOut,
|
|
3272
3520
|
axes,
|
|
3273
3521
|
normalized,
|
|
3274
|
-
scene: scene(emitter, config, compose(emitter, config, data, laidOut.area, measureText, axes, normalized, laidOut.padding, bindings, config.labels ?? environment.labels ?? "html", measureStyle), environment.rootSize ?? {
|
|
3522
|
+
scene: scene(emitter, config, compose(emitter, config, data, laidOut.area, measureText, axes, normalized, laidOut.padding, bindings, config.labels ?? environment.labels ?? "html", measureStyle, drawn), environment.rootSize ?? {
|
|
3275
3523
|
width: config.width ?? laidOut.width,
|
|
3276
3524
|
height: config.height ?? laidOut.height
|
|
3277
|
-
})
|
|
3525
|
+
}),
|
|
3526
|
+
drawn
|
|
3278
3527
|
};
|
|
3279
3528
|
}
|
|
3280
3529
|
/**
|
|
@@ -3282,7 +3531,6 @@ function runPipeline(emitter, options, environment, previous, from = "resolve")
|
|
|
3282
3531
|
* below the top of the line (a typical font's ascent). Lines are one font
|
|
3283
3532
|
* size apart, so a renderer finds a line's vertical center at
|
|
3284
3533
|
* `y - (textAscent - 0.5) * fontSize` from its `y`.
|
|
3285
|
-
* @experimental
|
|
3286
3534
|
*/
|
|
3287
3535
|
const textAscent = .85;
|
|
3288
3536
|
/**
|
|
@@ -3300,7 +3548,6 @@ const textAscent = .85;
|
|
|
3300
3548
|
* return { ...size, width: size.width * 1.1 };
|
|
3301
3549
|
* };
|
|
3302
3550
|
* ```
|
|
3303
|
-
* @experimental
|
|
3304
3551
|
*/
|
|
3305
3552
|
const estimateText = (text, options = {}) => {
|
|
3306
3553
|
const fontSize = options.fontSize ?? 12;
|
|
@@ -3312,24 +3559,62 @@ const estimateText = (text, options = {}) => {
|
|
|
3312
3559
|
};
|
|
3313
3560
|
//#endregion
|
|
3314
3561
|
//#region src/render/dom.ts
|
|
3315
|
-
|
|
3562
|
+
/**
|
|
3563
|
+
* Sets one scene attribute on an element as the DOM renderer does: names
|
|
3564
|
+
* with a known prefix (`ct:value`) in their namespace, and `style` through
|
|
3565
|
+
* the CSSOM, which a Content Security Policy allows. For renderers that
|
|
3566
|
+
* change elements they keep.
|
|
3567
|
+
*
|
|
3568
|
+
* @example
|
|
3569
|
+
* ```ts
|
|
3570
|
+
* import { applyAttribute } from 'chartist';
|
|
3571
|
+
*
|
|
3572
|
+
* const point = document.querySelector('.ct-point');
|
|
3573
|
+
* if (point) {
|
|
3574
|
+
* applyAttribute(point, 'ct:value', 12);
|
|
3575
|
+
* applyAttribute(point, 'style', 'stroke-width: 12px;');
|
|
3576
|
+
* }
|
|
3577
|
+
* ```
|
|
3578
|
+
* @param element The element to change
|
|
3579
|
+
* @param name The attribute's name, as in a scene node's `attrs`
|
|
3580
|
+
* @param value The attribute's value
|
|
3581
|
+
*/
|
|
3582
|
+
function applyAttribute(element, name, value) {
|
|
3316
3583
|
const separator = name.indexOf(":");
|
|
3317
3584
|
const prefix = separator > 0 ? name.slice(0, separator) : "";
|
|
3318
|
-
if (
|
|
3585
|
+
if (name === "style") setStyle(element, String(value));
|
|
3586
|
+
else if (prefix && prefix !== "xmlns" && namespaces[prefix]) element.setAttributeNS(namespaces[prefix], name, String(value));
|
|
3319
3587
|
else element.setAttribute(name, String(value));
|
|
3320
3588
|
}
|
|
3321
3589
|
/**
|
|
3322
|
-
* Creates the element tree
|
|
3323
|
-
*
|
|
3590
|
+
* Creates the element tree of a scene node as the DOM renderer does: SVG
|
|
3591
|
+
* elements, HTML elements inside a `foreignObject`, attributes set with
|
|
3592
|
+
* {@link applyAttribute}. For renderers that create elements.
|
|
3593
|
+
*
|
|
3594
|
+
* @example
|
|
3595
|
+
* ```ts
|
|
3596
|
+
* import { node, renderElement } from 'chartist';
|
|
3597
|
+
*
|
|
3598
|
+
* const dot = renderElement(node('circle', { cx: 5, cy: 5, r: 4 }, 'ct-dot'), document);
|
|
3599
|
+
* document.querySelector('svg')?.append(dot);
|
|
3600
|
+
* ```
|
|
3601
|
+
* @param current The node to create
|
|
3602
|
+
* @param document The document to create the elements in
|
|
3603
|
+
* @param options The namespace, and a callback for each created element
|
|
3604
|
+
* @returns The node's element, with its children
|
|
3324
3605
|
*/
|
|
3325
|
-
function
|
|
3606
|
+
function renderElement(current, document, options = {}) {
|
|
3607
|
+
const { html = false, created } = options;
|
|
3326
3608
|
const element = document.createElementNS(html ? namespaces.xhtml : namespaces.svg, current.name);
|
|
3327
3609
|
if (current.classes.length) element.setAttribute("class", current.classes.join(" "));
|
|
3328
|
-
for (const [name, value] of Object.entries(current.attrs))
|
|
3610
|
+
for (const [name, value] of Object.entries(current.attrs)) applyAttribute(element, name, value);
|
|
3329
3611
|
if (current.content) element.appendChild(document.createTextNode(current.content));
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
|
|
3612
|
+
const childOptions = html || current.name !== "foreignObject" ? options : {
|
|
3613
|
+
...options,
|
|
3614
|
+
html: true
|
|
3615
|
+
};
|
|
3616
|
+
for (const child of current.children) element.appendChild(renderElement(child, document, childOptions));
|
|
3617
|
+
created?.(current, element);
|
|
3333
3618
|
return element;
|
|
3334
3619
|
}
|
|
3335
3620
|
/** Removes Chartist SVGs (the ones with the ct namespace) from a container. */
|
|
@@ -3343,7 +3628,7 @@ function removePrevious(container) {
|
|
|
3343
3628
|
*/
|
|
3344
3629
|
function probe(document, name, options, read) {
|
|
3345
3630
|
const svg = document.createElementNS(namespaces.svg, "svg");
|
|
3346
|
-
svg
|
|
3631
|
+
setStyle(svg, "position: absolute; visibility: hidden; width: 0; height: 0;");
|
|
3347
3632
|
if (options.rootClass) svg.setAttribute("class", options.rootClass);
|
|
3348
3633
|
const node = document.createElementNS(namespaces.svg, name);
|
|
3349
3634
|
if (options.classes?.length) node.setAttribute("class", options.classes.join(" "));
|
|
@@ -3377,8 +3662,10 @@ function measureInDocument(document) {
|
|
|
3377
3662
|
/**
|
|
3378
3663
|
* Creates the renderer `chart()` uses by default: it renders scenes as
|
|
3379
3664
|
* inline SVG into the container, replacing an earlier Chartist SVG there
|
|
3380
|
-
* (as 1.x does), and measures text and reads styles for
|
|
3381
|
-
* container, so its CSS applies.
|
|
3665
|
+
* (as 1.x does) on every render, and measures text and reads styles for
|
|
3666
|
+
* layout in the container, so its CSS applies. It ignores the previous
|
|
3667
|
+
* render it gets from a chart's second render on; `liveDomRenderer()`
|
|
3668
|
+
* from `chartist/live` keeps its SVG and changes it instead.
|
|
3382
3669
|
* Wrap it to change what every render puts on the page.
|
|
3383
3670
|
*
|
|
3384
3671
|
* @example
|
|
@@ -3404,7 +3691,6 @@ function measureInDocument(document) {
|
|
|
3404
3691
|
* renderer
|
|
3405
3692
|
* });
|
|
3406
3693
|
* ```
|
|
3407
|
-
* @experimental
|
|
3408
3694
|
*/
|
|
3409
3695
|
function domRenderer() {
|
|
3410
3696
|
return {
|
|
@@ -3412,7 +3698,7 @@ function domRenderer() {
|
|
|
3412
3698
|
render(scene, container) {
|
|
3413
3699
|
const document = container.ownerDocument;
|
|
3414
3700
|
const elements = /* @__PURE__ */ new WeakMap();
|
|
3415
|
-
const svg =
|
|
3701
|
+
const svg = renderElement(scene, document, { created: (node, element) => elements.set(node, element) });
|
|
3416
3702
|
svg.setAttributeNS(namespaces.xmlns, "xmlns:ct", namespaces.ct);
|
|
3417
3703
|
removePrevious(container);
|
|
3418
3704
|
container.appendChild(svg);
|
|
@@ -3434,6 +3720,35 @@ function domRenderer() {
|
|
|
3434
3720
|
};
|
|
3435
3721
|
}
|
|
3436
3722
|
//#endregion
|
|
3723
|
+
//#region src/chart/changes.ts
|
|
3724
|
+
/** Adds an entry for `current` and each of its descendants, in document order. */
|
|
3725
|
+
function each$1(current, op, into) {
|
|
3726
|
+
into.push({
|
|
3727
|
+
op,
|
|
3728
|
+
node: current
|
|
3729
|
+
});
|
|
3730
|
+
current.children.forEach((child) => each$1(child, op, into));
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* The changes of a render that replaced its output: every node of the
|
|
3734
|
+
* previous scene removed, every node of the new one added.
|
|
3735
|
+
*/
|
|
3736
|
+
function replacementChanges(previous, next) {
|
|
3737
|
+
const changes = [];
|
|
3738
|
+
if (previous) each$1(previous, "remove", changes);
|
|
3739
|
+
each$1(next, "add", changes);
|
|
3740
|
+
return changes;
|
|
3741
|
+
}
|
|
3742
|
+
/**
|
|
3743
|
+
* Set on a chart handle by the 1.x classes and the compat plugin: the
|
|
3744
|
+
* chart never hands its renderer the previous render, so every render
|
|
3745
|
+
* draws new elements, as 1.x code expects. A global symbol, so the compat
|
|
3746
|
+
* build sets the same one as the core.
|
|
3747
|
+
*/
|
|
3748
|
+
const REPLACES_OUTPUT = Symbol.for("chartist.replacesOutput");
|
|
3749
|
+
/** Whether the chart must replace its output on every render. */
|
|
3750
|
+
const replacesOutput = (handle) => handle[REPLACES_OUTPUT] === true;
|
|
3751
|
+
//#endregion
|
|
3437
3752
|
//#region src/chart/className.ts
|
|
3438
3753
|
/**
|
|
3439
3754
|
* The chart type class for a chart's layers, so the stylesheet's chart
|
|
@@ -3486,7 +3801,6 @@ function withChartTypeClass(config) {
|
|
|
3486
3801
|
* { plugins: [withOptions(lastPoint, { className: 'ct-last' })] }
|
|
3487
3802
|
* );
|
|
3488
3803
|
* ```
|
|
3489
|
-
* @experimental
|
|
3490
3804
|
*/
|
|
3491
3805
|
function withOptions(plugin, options) {
|
|
3492
3806
|
return [plugin, options];
|
|
@@ -3552,16 +3866,37 @@ function asUserListeners(chart, add) {
|
|
|
3552
3866
|
else add();
|
|
3553
3867
|
}
|
|
3554
3868
|
/**
|
|
3555
|
-
* Installs v2 plugins in order and
|
|
3556
|
-
* Provided names are known before any plugin installs, so a
|
|
3557
|
-
* listen to events another plugin provides. Listeners added
|
|
3558
|
-
* plugin installs belong to plugins.
|
|
3869
|
+
* Installs v2 plugins in order and adds the event names they provide to
|
|
3870
|
+
* `provided`. Provided names are known before any plugin installs, so a
|
|
3871
|
+
* plugin may listen to events another plugin provides. Listeners added
|
|
3872
|
+
* while a plugin installs belong to plugins.
|
|
3559
3873
|
*/
|
|
3560
3874
|
function installPlugins(chart, plugins = [], provided, scope) {
|
|
3561
3875
|
scopes.set(chart, scope);
|
|
3562
3876
|
const entries = normalize(plugins);
|
|
3563
3877
|
for (const [plugin] of entries) plugin.provides?.forEach((name) => provided.add(name));
|
|
3564
|
-
|
|
3878
|
+
const cleanups = [];
|
|
3879
|
+
for (const [plugin, options] of entries) addingAs(scope, "plugin", () => {
|
|
3880
|
+
const cleanup = plugin.install(chart, options);
|
|
3881
|
+
if (typeof cleanup === "function") cleanups.push(cleanup);
|
|
3882
|
+
});
|
|
3883
|
+
return {
|
|
3884
|
+
plugins: entries.map(([plugin]) => plugin),
|
|
3885
|
+
cleanups
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3888
|
+
/**
|
|
3889
|
+
* Calls every function, also when one throws, and throws the first error
|
|
3890
|
+
* afterwards.
|
|
3891
|
+
*/
|
|
3892
|
+
function runAll(functions) {
|
|
3893
|
+
const errors = [];
|
|
3894
|
+
for (const run of functions) try {
|
|
3895
|
+
run();
|
|
3896
|
+
} catch (error) {
|
|
3897
|
+
errors.push(error);
|
|
3898
|
+
}
|
|
3899
|
+
if (errors.length) throw errors[0];
|
|
3565
3900
|
}
|
|
3566
3901
|
/** Registers `config.on` listeners. */
|
|
3567
3902
|
function registerListeners(chart, listeners = {}) {
|
|
@@ -3662,7 +3997,6 @@ function resolveTarget(target) {
|
|
|
3662
3997
|
* @param target The container element, or a CSS selector for it.
|
|
3663
3998
|
* @param config The data, axes, layers and the rest of the chart.
|
|
3664
3999
|
* @returns A handle to update the chart, listen to it or remove it.
|
|
3665
|
-
* @experimental
|
|
3666
4000
|
*/
|
|
3667
4001
|
function chart(target, config) {
|
|
3668
4002
|
const container = resolveTarget(target);
|
|
@@ -3688,6 +4022,12 @@ function chart(target, config) {
|
|
|
3688
4022
|
let watcher;
|
|
3689
4023
|
let state;
|
|
3690
4024
|
let output;
|
|
4025
|
+
let cleanups = [];
|
|
4026
|
+
let installed = {
|
|
4027
|
+
plugins: [],
|
|
4028
|
+
cleanups: []
|
|
4029
|
+
};
|
|
4030
|
+
let layerOf;
|
|
3691
4031
|
let observer;
|
|
3692
4032
|
let destroyed = false;
|
|
3693
4033
|
let attached = true;
|
|
@@ -3695,7 +4035,7 @@ function chart(target, config) {
|
|
|
3695
4035
|
const view = container.ownerDocument.defaultView;
|
|
3696
4036
|
const fonts = container.ownerDocument.fonts;
|
|
3697
4037
|
const onFontsLoaded = () => {
|
|
3698
|
-
if (!destroyed && state && measuredText) render("layout");
|
|
4038
|
+
if (!destroyed && state && measuredText) render("layout", "fonts");
|
|
3699
4039
|
};
|
|
3700
4040
|
let printed;
|
|
3701
4041
|
let printing = false;
|
|
@@ -3725,7 +4065,7 @@ function chart(target, config) {
|
|
|
3725
4065
|
probe.setAttribute("width", width);
|
|
3726
4066
|
probe.setAttribute("height", height);
|
|
3727
4067
|
const px = (value) => typeof value === "number" ? "px" : "";
|
|
3728
|
-
probe
|
|
4068
|
+
setStyle(probe, `width: ${width}${px(effective.width)}; height: ${height}${px(effective.height)};`);
|
|
3729
4069
|
container.appendChild(probe);
|
|
3730
4070
|
const measured = {
|
|
3731
4071
|
width: probe.clientWidth,
|
|
@@ -3741,9 +4081,33 @@ function chart(target, config) {
|
|
|
3741
4081
|
height: effective.viewBox?.height || svg.height || (fixedSize(effective.height) ?? container.clientHeight)
|
|
3742
4082
|
};
|
|
3743
4083
|
}
|
|
3744
|
-
|
|
4084
|
+
/**
|
|
4085
|
+
* Asks the plugins with `keyframes`, in order, then the layer that drew
|
|
4086
|
+
* the node, for the keyframes of a node's update animation.
|
|
4087
|
+
*/
|
|
4088
|
+
function keyframes(previous, next, progress) {
|
|
4089
|
+
for (const plugin of installed.plugins) {
|
|
4090
|
+
const frames = plugin.keyframes?.(previous, next, progress);
|
|
4091
|
+
if (frames) return frames;
|
|
4092
|
+
}
|
|
4093
|
+
if (!state) return;
|
|
4094
|
+
if (!layerOf) {
|
|
4095
|
+
const map = /* @__PURE__ */ new WeakMap();
|
|
4096
|
+
const mark = (drawnNode, index) => {
|
|
4097
|
+
map.set(drawnNode, index);
|
|
4098
|
+
drawnNode.children.forEach((child) => mark(child, index));
|
|
4099
|
+
};
|
|
4100
|
+
state.drawn.forEach(({ nodes }, index) => nodes.forEach((top) => mark(top, index)));
|
|
4101
|
+
layerOf = map;
|
|
4102
|
+
}
|
|
4103
|
+
const index = layerOf.get(next);
|
|
4104
|
+
return index === void 0 ? void 0 : state.config.layers[index].keyframes?.(previous, next, state.drawn[index].context, progress);
|
|
4105
|
+
}
|
|
4106
|
+
function render(from, cause) {
|
|
3745
4107
|
const { width, height } = size();
|
|
3746
4108
|
measuredText = false;
|
|
4109
|
+
const previous = state;
|
|
4110
|
+
const previousOutput = output;
|
|
3747
4111
|
state = runPipeline(emitter, withChartTypeClass(effective), {
|
|
3748
4112
|
width,
|
|
3749
4113
|
height,
|
|
@@ -3756,18 +4120,35 @@ function chart(target, config) {
|
|
|
3756
4120
|
}
|
|
3757
4121
|
}, state, from);
|
|
3758
4122
|
const { scene } = state;
|
|
3759
|
-
|
|
4123
|
+
layerOf = void 0;
|
|
4124
|
+
const pending = cleanups;
|
|
4125
|
+
cleanups = [];
|
|
4126
|
+
runAll(pending);
|
|
4127
|
+
const update = previous && previousOutput && !replacesOutput(handle) ? {
|
|
4128
|
+
previous: previousOutput,
|
|
4129
|
+
previousScene: previous.scene,
|
|
4130
|
+
cause,
|
|
4131
|
+
labels: state.data.labels,
|
|
4132
|
+
previousLabels: previous.data.labels,
|
|
4133
|
+
keyframes
|
|
4134
|
+
} : void 0;
|
|
4135
|
+
const rendered = renderer.render(scene, container, update);
|
|
4136
|
+
output = rendered;
|
|
3760
4137
|
if (printing) applyPrintViewBox();
|
|
3761
|
-
|
|
3762
|
-
scene,
|
|
3763
|
-
output
|
|
3764
|
-
});
|
|
3765
|
-
const elementOf = output.elementOf ?? (() => void 0);
|
|
3766
|
-
emitter.notify("mount", {
|
|
4138
|
+
const event = {
|
|
3767
4139
|
scene,
|
|
3768
|
-
output,
|
|
3769
|
-
|
|
3770
|
-
|
|
4140
|
+
output: rendered,
|
|
4141
|
+
cause,
|
|
4142
|
+
changes: update && rendered.patched && rendered.changes ? rendered.changes : replacementChanges(previous?.scene, scene),
|
|
4143
|
+
previous: previous?.scene
|
|
4144
|
+
};
|
|
4145
|
+
emitter.notify("render", event);
|
|
4146
|
+
const elementOf = (node) => rendered.elementOf?.(node) ?? previousOutput?.elementOf?.(node);
|
|
4147
|
+
cleanups = emitter.notify("mount", {
|
|
4148
|
+
...event,
|
|
4149
|
+
elementOf,
|
|
4150
|
+
animation: rendered.animation
|
|
4151
|
+
}).filter((result) => typeof result === "function");
|
|
3771
4152
|
}
|
|
3772
4153
|
function resolveResponsive() {
|
|
3773
4154
|
const entries = current.responsive?.entries;
|
|
@@ -3783,7 +4164,7 @@ function chart(target, config) {
|
|
|
3783
4164
|
previousConfig,
|
|
3784
4165
|
currentConfig: effective
|
|
3785
4166
|
});
|
|
3786
|
-
render("resolve");
|
|
4167
|
+
render("resolve", "responsive");
|
|
3787
4168
|
}
|
|
3788
4169
|
/** (Re)creates the watcher of the responsive entries. */
|
|
3789
4170
|
function watch() {
|
|
@@ -3802,7 +4183,7 @@ function chart(target, config) {
|
|
|
3802
4183
|
};
|
|
3803
4184
|
if ("responsive" in changes) watch();
|
|
3804
4185
|
else resolveResponsive();
|
|
3805
|
-
render("resolve");
|
|
4186
|
+
render("resolve", "update");
|
|
3806
4187
|
return handle;
|
|
3807
4188
|
},
|
|
3808
4189
|
detach() {
|
|
@@ -3821,8 +4202,15 @@ function chart(target, config) {
|
|
|
3821
4202
|
if (destroyed) return;
|
|
3822
4203
|
destroyed = true;
|
|
3823
4204
|
handle.detach();
|
|
3824
|
-
|
|
3825
|
-
|
|
4205
|
+
const pending = cleanups;
|
|
4206
|
+
cleanups = [];
|
|
4207
|
+
try {
|
|
4208
|
+
runAll(pending);
|
|
4209
|
+
} finally {
|
|
4210
|
+
output?.remove?.();
|
|
4211
|
+
emitter.clear();
|
|
4212
|
+
runAll([...installed.cleanups].reverse());
|
|
4213
|
+
}
|
|
3826
4214
|
},
|
|
3827
4215
|
...eventMethods(emitter, () => handle, provided, scope)
|
|
3828
4216
|
};
|
|
@@ -3838,15 +4226,15 @@ function chart(target, config) {
|
|
|
3838
4226
|
return;
|
|
3839
4227
|
}
|
|
3840
4228
|
const { width, height } = size();
|
|
3841
|
-
if (width !== state.layout.width || height !== state.layout.height) render("layout");
|
|
4229
|
+
if (width !== state.layout.width || height !== state.layout.height) render("layout", "resize");
|
|
3842
4230
|
});
|
|
3843
4231
|
observer.observe(container);
|
|
3844
4232
|
}
|
|
3845
|
-
installPlugins(handle, config.plugins, provided, scope);
|
|
4233
|
+
installed = installPlugins(handle, config.plugins, provided, scope);
|
|
3846
4234
|
registerListeners(handle, config.on);
|
|
3847
4235
|
if (attached) watch();
|
|
3848
4236
|
else resolveResponsive();
|
|
3849
|
-
render("resolve");
|
|
4237
|
+
render("resolve", "create");
|
|
3850
4238
|
return handle;
|
|
3851
4239
|
}
|
|
3852
4240
|
//#endregion
|
|
@@ -3874,7 +4262,7 @@ function axisNodes(axis, frame) {
|
|
|
3874
4262
|
const options = frame.options ?? (axis.dimension === "x" ? frame.axisX : frame.axisY);
|
|
3875
4263
|
const place = frame.place;
|
|
3876
4264
|
const projected = axis.ticks.map((tick, i) => axis.project(tick, i));
|
|
3877
|
-
const labelValues = axis.ticks.map(options.
|
|
4265
|
+
const labelValues = axis.ticks.map(options.labelTransform);
|
|
3878
4266
|
const gridOffset = axis.dimension === "x" ? area.y2 : area.x1;
|
|
3879
4267
|
const gridLength = axis.dimension === "x" ? area.height : area.width;
|
|
3880
4268
|
const grids = [];
|
|
@@ -3969,6 +4357,12 @@ function gridBackground(area, className) {
|
|
|
3969
4357
|
}, className, { role: "gridBackground" });
|
|
3970
4358
|
}
|
|
3971
4359
|
//#endregion
|
|
4360
|
+
//#region src/layers/label.ts
|
|
4361
|
+
/** The label function to use; `labelTransform` wins when both are set. */
|
|
4362
|
+
function labelTransformOf(options) {
|
|
4363
|
+
return options.labelTransform ?? options.labelInterpolationFnc ?? ((value) => value);
|
|
4364
|
+
}
|
|
4365
|
+
//#endregion
|
|
3972
4366
|
//#region src/scales/svgLabels.ts
|
|
3973
4367
|
/**
|
|
3974
4368
|
* SVG text labels: the label text wrapped into `tspan` lines and aligned
|
|
@@ -4067,13 +4461,15 @@ function svgLabels(measureText, kind, override = {}) {
|
|
|
4067
4461
|
* container('(width < 400px)'),
|
|
4068
4462
|
* {
|
|
4069
4463
|
* showPoint: false,
|
|
4070
|
-
* axisX: {
|
|
4464
|
+
* axisX: { labelTransform: value => String(value)[0] }
|
|
4071
4465
|
* }
|
|
4072
4466
|
* ]
|
|
4073
4467
|
* ]
|
|
4074
4468
|
* );
|
|
4075
4469
|
* ```
|
|
4076
|
-
* @experimental
|
|
4470
|
+
* @experimental It rests on `Element.matchContainer()`, a draft standard
|
|
4471
|
+
* (w3c/csswg-drafts#6205), through a polyfill: it may still change in a
|
|
4472
|
+
* 2.x minor.
|
|
4077
4473
|
*/
|
|
4078
4474
|
function container(query) {
|
|
4079
4475
|
return Object.freeze({
|
|
@@ -4190,7 +4586,6 @@ function watchResponsive(entries, host, onChange, observeResize = false) {
|
|
|
4190
4586
|
* ])
|
|
4191
4587
|
* });
|
|
4192
4588
|
* ```
|
|
4193
|
-
* @experimental
|
|
4194
4589
|
*/
|
|
4195
4590
|
function responsive(entries) {
|
|
4196
4591
|
return {
|
|
@@ -4209,13 +4604,18 @@ function applyResponsive(base, entries, matching, merge) {
|
|
|
4209
4604
|
* option merge, window responsive options, the preset state that plugins
|
|
4210
4605
|
* (compat) read, and the grid and label layers.
|
|
4211
4606
|
*/
|
|
4607
|
+
/** An axis's options with its label function resolved from either name. */
|
|
4608
|
+
const drawn = (axis) => ({
|
|
4609
|
+
...axis,
|
|
4610
|
+
labelTransform: labelTransformOf(axis)
|
|
4611
|
+
});
|
|
4212
4612
|
function frame(options, context, svgLabel) {
|
|
4213
4613
|
return {
|
|
4214
4614
|
svgLabel,
|
|
4215
4615
|
area: context.area,
|
|
4216
4616
|
padding: context.padding,
|
|
4217
|
-
axisX: options.axisX,
|
|
4218
|
-
axisY: options.axisY,
|
|
4617
|
+
axisX: drawn(options.axisX),
|
|
4618
|
+
axisY: drawn(options.axisY),
|
|
4219
4619
|
classNames: options.classNames
|
|
4220
4620
|
};
|
|
4221
4621
|
}
|
|
@@ -4243,10 +4643,10 @@ function labelLayer(options, order = ["x", "y"]) {
|
|
|
4243
4643
|
type: "labels",
|
|
4244
4644
|
coordinates: ["cartesian"],
|
|
4245
4645
|
compose(context) {
|
|
4246
|
-
const svgLabel = context.labels === "svg" ? svgLabels(context.measureText, options.labelKind ?? "line") : void 0;
|
|
4247
4646
|
return [group(options.classNames.labelGroup, order.flatMap((dimension) => {
|
|
4248
4647
|
const axis = context.axes[dimension];
|
|
4249
|
-
|
|
4648
|
+
if (!axis) return [];
|
|
4649
|
+
return axisNodes(axis, frame(options, context, context.labels === "svg" ? svgLabels(context.measureText, options.labelKind ?? "line", options[dimension === "x" ? "axisX" : "axisY"].labelAlign) : void 0)).labels;
|
|
4250
4650
|
}))];
|
|
4251
4651
|
}
|
|
4252
4652
|
};
|
|
@@ -4293,7 +4693,7 @@ function rootStyle(getOptions, state) {
|
|
|
4293
4693
|
*/
|
|
4294
4694
|
function presetConfig(spec, data, options) {
|
|
4295
4695
|
const resolved = extend({}, spec.defaults, options);
|
|
4296
|
-
const { plugins, on } = options ?? {};
|
|
4696
|
+
const { plugins, on, renderer } = options ?? {};
|
|
4297
4697
|
return {
|
|
4298
4698
|
...spec.config({
|
|
4299
4699
|
...data,
|
|
@@ -4301,7 +4701,8 @@ function presetConfig(spec, data, options) {
|
|
|
4301
4701
|
series: data.series || []
|
|
4302
4702
|
}, resolved),
|
|
4303
4703
|
plugins,
|
|
4304
|
-
on
|
|
4704
|
+
on,
|
|
4705
|
+
...renderer && { renderer }
|
|
4305
4706
|
};
|
|
4306
4707
|
}
|
|
4307
4708
|
/** Internal: creates a preset chart; used by the preset functions and classes. */
|
|
@@ -4357,7 +4758,8 @@ function createPreset(spec, target, initialData, options = {}, responsiveOptions
|
|
|
4357
4758
|
const inner = chart(target, {
|
|
4358
4759
|
...withState(spec.config(data, current)),
|
|
4359
4760
|
plugins: [rootStyle(() => current, state), ...plugins ?? []],
|
|
4360
|
-
on: options.on
|
|
4761
|
+
on: options.on,
|
|
4762
|
+
renderer: options.renderer
|
|
4361
4763
|
});
|
|
4362
4764
|
function onResponsiveChange() {
|
|
4363
4765
|
const previousOptions = current;
|
|
@@ -4446,6 +4848,7 @@ const lineSpec = {
|
|
|
4446
4848
|
config(data, preset) {
|
|
4447
4849
|
const options = preset;
|
|
4448
4850
|
const axisOptions = options;
|
|
4851
|
+
const { below = [], above = [] } = options.layers ?? {};
|
|
4449
4852
|
return {
|
|
4450
4853
|
data,
|
|
4451
4854
|
axes: presetAxes(axesConfig$1(options)),
|
|
@@ -4457,6 +4860,7 @@ const lineSpec = {
|
|
|
4457
4860
|
labels: options.labels,
|
|
4458
4861
|
layers: [
|
|
4459
4862
|
gridLayer(axisOptions),
|
|
4863
|
+
...below,
|
|
4460
4864
|
seriesGroup(line({
|
|
4461
4865
|
lineSmooth: options.lineSmooth,
|
|
4462
4866
|
showPoint: options.showPoint,
|
|
@@ -4467,6 +4871,7 @@ const lineSpec = {
|
|
|
4467
4871
|
reverseData: options.reverseData,
|
|
4468
4872
|
classNames: options.classNames
|
|
4469
4873
|
})),
|
|
4874
|
+
...above,
|
|
4470
4875
|
labelLayer(axisOptions)
|
|
4471
4876
|
]
|
|
4472
4877
|
};
|
|
@@ -4489,7 +4894,6 @@ const lineSpec = {
|
|
|
4489
4894
|
* toScene(config, { width: 600, height: 300 })
|
|
4490
4895
|
* );
|
|
4491
4896
|
* ```
|
|
4492
|
-
* @experimental
|
|
4493
4897
|
*/
|
|
4494
4898
|
function lineChartConfig(data, options = {}) {
|
|
4495
4899
|
return presetConfig(lineSpec, data, options);
|
|
@@ -4502,7 +4906,6 @@ function createLineChart(target, data, options = {}, responsiveOptions, plugins)
|
|
|
4502
4906
|
* Creates a line chart with the 1.x options and output, rendered
|
|
4503
4907
|
* synchronously. Plugins must be v2 plugins; convert 1.x plugins with
|
|
4504
4908
|
* `fromLegacyPlugin` from `chartist/compat`.
|
|
4505
|
-
* @experimental
|
|
4506
4909
|
*/
|
|
4507
4910
|
function lineChart(target, data, options = {}, responsiveOptions) {
|
|
4508
4911
|
return createLineChart(target, data, options, responsiveOptions).handle;
|
|
@@ -4574,6 +4977,7 @@ var PresetChart = class extends BaseChart {
|
|
|
4574
4977
|
name: "chart-listeners",
|
|
4575
4978
|
install: (handle) => {
|
|
4576
4979
|
this.installing = handle;
|
|
4980
|
+
handle[REPLACES_OUTPUT] = true;
|
|
4577
4981
|
asUserListeners(handle, () => queued.forEach(([event, listener]) => this.on(event, listener)));
|
|
4578
4982
|
}
|
|
4579
4983
|
};
|
|
@@ -4767,7 +5171,6 @@ function rawSeries(series) {
|
|
|
4767
5171
|
* className: 'ct-chart-bar'
|
|
4768
5172
|
* });
|
|
4769
5173
|
* ```
|
|
4770
|
-
* @experimental
|
|
4771
5174
|
*/
|
|
4772
5175
|
function bar(options = {}) {
|
|
4773
5176
|
const classNames = {
|
|
@@ -4777,6 +5180,7 @@ function bar(options = {}) {
|
|
|
4777
5180
|
};
|
|
4778
5181
|
const horizontal = Boolean(options.horizontalBars);
|
|
4779
5182
|
const seriesBarDistance = options.seriesBarDistance ?? 15;
|
|
5183
|
+
const offset = options.offset ?? 0;
|
|
4780
5184
|
const stackMode = options.stackMode ?? "accumulate";
|
|
4781
5185
|
const stacks = options.stackBars && stackMode !== "overlap" && stackMode !== true;
|
|
4782
5186
|
return {
|
|
@@ -4837,6 +5241,7 @@ function bar(options = {}) {
|
|
|
4837
5241
|
if (!labelAxis.stretch) projected[labelPos] += periodHalfLength * (horizontal ? -1 : 1);
|
|
4838
5242
|
projected[labelPos] += options.stackBars || options.distributeSeries ? 0 : biPol * seriesBarDistance * (horizontal ? -1 : 1);
|
|
4839
5243
|
}
|
|
5244
|
+
projected[labelPos] += offset;
|
|
4840
5245
|
if (isAccumulateRelative) stack = Number(horizontal ? valueX : valueY) < 0 ? negStack : posStack;
|
|
4841
5246
|
const previousStack = stack[valueIndex] || zeroPoint;
|
|
4842
5247
|
stack[valueIndex] = previousStack - (zeroPoint - projected[counterPos]);
|
|
@@ -5037,6 +5442,7 @@ const barSpec = {
|
|
|
5037
5442
|
...seriesGroup(layer),
|
|
5038
5443
|
extent: extentOf.extent
|
|
5039
5444
|
};
|
|
5445
|
+
const { below = [], above = [] } = options.layers ?? {};
|
|
5040
5446
|
return {
|
|
5041
5447
|
data,
|
|
5042
5448
|
axes: presetAxes(axesConfig(options)),
|
|
@@ -5048,7 +5454,9 @@ const barSpec = {
|
|
|
5048
5454
|
labels: options.labels,
|
|
5049
5455
|
layers: [
|
|
5050
5456
|
gridLayer(axisOptions, order),
|
|
5457
|
+
...below,
|
|
5051
5458
|
bars,
|
|
5459
|
+
...above,
|
|
5052
5460
|
labelLayer({
|
|
5053
5461
|
...axisOptions,
|
|
5054
5462
|
labelKind: options.horizontalBars ? "horizontalBar" : "bar"
|
|
@@ -5062,7 +5470,6 @@ const barSpec = {
|
|
|
5062
5470
|
* options, without a container: the bar layer with its axes, grid and labels, horizontal or stacked as the options say. Pass it to `toScene()` to render
|
|
5063
5471
|
* the preset chart with another renderer. Responsive options and the
|
|
5064
5472
|
* inline size style of the root svg are left out; both need a container.
|
|
5065
|
-
* @experimental
|
|
5066
5473
|
*/
|
|
5067
5474
|
function barChartConfig(data, options = {}) {
|
|
5068
5475
|
return presetConfig(barSpec, data, options);
|
|
@@ -5075,7 +5482,6 @@ function createBarChart(target, data, options = {}, responsiveOptions, plugins)
|
|
|
5075
5482
|
* Creates a bar chart with the 1.x options and output, rendered
|
|
5076
5483
|
* synchronously. Plugins must be v2 plugins; convert 1.x plugins with
|
|
5077
5484
|
* `fromLegacyPlugin` from `chartist/compat`.
|
|
5078
|
-
* @experimental
|
|
5079
5485
|
*/
|
|
5080
5486
|
function barChart(target, data, options = {}, responsiveOptions) {
|
|
5081
5487
|
return createBarChart(target, data, options, responsiveOptions).handle;
|
|
@@ -5136,12 +5542,40 @@ function cssWidth(measureStyle, sliceClass) {
|
|
|
5136
5542
|
const width = measureStyle?.("stroke-width", { classes: sliceClass.split(" ") });
|
|
5137
5543
|
return width && /^[\d.]+px$/.test(width) ? parseFloat(width) : void 0;
|
|
5138
5544
|
}
|
|
5139
|
-
const CLASS_NAMES$
|
|
5545
|
+
const CLASS_NAMES$4 = {
|
|
5140
5546
|
series: "ct-series",
|
|
5141
5547
|
slicePie: "ct-slice-pie",
|
|
5142
5548
|
sliceDonut: "ct-slice-donut",
|
|
5143
5549
|
label: "ct-label"
|
|
5144
5550
|
};
|
|
5551
|
+
/** The line height of a pie label with line breaks, in `em`. */
|
|
5552
|
+
const LINE_HEIGHT = 1.2;
|
|
5553
|
+
/**
|
|
5554
|
+
* A pie label's `text` at its anchor. One line is placed with `dx`/`dy`,
|
|
5555
|
+
* as in 1.x. A label with line breaks gets one `tspan` per line, one line
|
|
5556
|
+
* height apart, the block centered on the anchor (see #546).
|
|
5557
|
+
*/
|
|
5558
|
+
function labelText(text, position, anchor, className, ctx) {
|
|
5559
|
+
const lines = text.split("\n");
|
|
5560
|
+
const meta = {
|
|
5561
|
+
role: "label",
|
|
5562
|
+
ctx
|
|
5563
|
+
};
|
|
5564
|
+
if (lines.length === 1) return node("text", {
|
|
5565
|
+
dx: position.x,
|
|
5566
|
+
dy: position.y,
|
|
5567
|
+
"text-anchor": anchor
|
|
5568
|
+
}, className, meta).text(text);
|
|
5569
|
+
const shift = Math.round(-(lines.length - 1) * LINE_HEIGHT * 50) / 100;
|
|
5570
|
+
return withFields(node("text", {
|
|
5571
|
+
x: position.x,
|
|
5572
|
+
y: position.y,
|
|
5573
|
+
"text-anchor": anchor
|
|
5574
|
+
}, className, meta), { children: lines.map((line, index) => node("tspan", {
|
|
5575
|
+
x: position.x,
|
|
5576
|
+
dy: `${index ? LINE_HEIGHT : shift}em`
|
|
5577
|
+
}).text(line)) });
|
|
5578
|
+
}
|
|
5145
5579
|
/**
|
|
5146
5580
|
* Creates a layer that draws a flat series as pie or donut slices with
|
|
5147
5581
|
* labels, in the markup of the 1.x pie chart (a `g.ct-series` per slice,
|
|
@@ -5163,14 +5597,13 @@ const CLASS_NAMES$1 = {
|
|
|
5163
5597
|
* className: 'ct-chart-donut'
|
|
5164
5598
|
* });
|
|
5165
5599
|
* ```
|
|
5166
|
-
* @experimental
|
|
5167
5600
|
*/
|
|
5168
5601
|
function pie(options = {}) {
|
|
5169
5602
|
const classNames = {
|
|
5170
|
-
...CLASS_NAMES$
|
|
5603
|
+
...CLASS_NAMES$4,
|
|
5171
5604
|
...options.classNames
|
|
5172
5605
|
};
|
|
5173
|
-
const
|
|
5606
|
+
const transform = labelTransformOf(options);
|
|
5174
5607
|
return {
|
|
5175
5608
|
type: "pie",
|
|
5176
5609
|
chartType: options.donut ? "donut" : "pie",
|
|
@@ -5266,7 +5699,7 @@ function pie(options = {}) {
|
|
|
5266
5699
|
y: center.y
|
|
5267
5700
|
} : polarToCartesian(center.x, center.y, labelRadius, startAngle + (endAngle - startAngle) / 2);
|
|
5268
5701
|
const rawValue = labels && !isFalseyButZero(labels[index]) ? labels[index] : values[index];
|
|
5269
|
-
const interpolated =
|
|
5702
|
+
const interpolated = transform(rawValue, index);
|
|
5270
5703
|
if (interpolated || interpolated === 0) {
|
|
5271
5704
|
const text = String(interpolated);
|
|
5272
5705
|
if (options.preventOverlappingLabelOffset) {
|
|
@@ -5274,24 +5707,17 @@ function pie(options = {}) {
|
|
|
5274
5707
|
labelPositions.forEach((item) => moveLabel(labelPosition, item, options.preventOverlappingLabelOffset, length));
|
|
5275
5708
|
labelPositions.push(labelPosition);
|
|
5276
5709
|
}
|
|
5277
|
-
labelNodes.push(
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
text,
|
|
5289
|
-
series,
|
|
5290
|
-
meta: seriesMeta,
|
|
5291
|
-
x: labelPosition.x,
|
|
5292
|
-
y: labelPosition.y
|
|
5293
|
-
}
|
|
5294
|
-
}).text(text));
|
|
5710
|
+
labelNodes.push(labelText(text, labelPosition, determineAnchorPosition(center, labelPosition, options.labelDirection ?? "neutral"), classNames.label, {
|
|
5711
|
+
kind: "pie",
|
|
5712
|
+
index,
|
|
5713
|
+
seriesIndex: index,
|
|
5714
|
+
valueIndex: 0,
|
|
5715
|
+
text,
|
|
5716
|
+
series,
|
|
5717
|
+
meta: seriesMeta,
|
|
5718
|
+
x: labelPosition.x,
|
|
5719
|
+
y: labelPosition.y
|
|
5720
|
+
}));
|
|
5295
5721
|
}
|
|
5296
5722
|
}
|
|
5297
5723
|
startAngle = endAngle;
|
|
@@ -5338,6 +5764,7 @@ const pieSpec = {
|
|
|
5338
5764
|
defaults: pieDefaults,
|
|
5339
5765
|
config(data, preset) {
|
|
5340
5766
|
const options = preset;
|
|
5767
|
+
const { below = [], above = [] } = preset.layers ?? {};
|
|
5341
5768
|
return {
|
|
5342
5769
|
data,
|
|
5343
5770
|
coordinates: "polar",
|
|
@@ -5346,20 +5773,25 @@ const pieSpec = {
|
|
|
5346
5773
|
height: options.height,
|
|
5347
5774
|
viewBox: options.viewBox,
|
|
5348
5775
|
className: options.donut ? options.classNames.chartDonut : options.classNames.chartPie,
|
|
5349
|
-
layers: [
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5776
|
+
layers: [
|
|
5777
|
+
...below,
|
|
5778
|
+
pie({
|
|
5779
|
+
startAngle: options.startAngle,
|
|
5780
|
+
total: options.total,
|
|
5781
|
+
donut: options.donut,
|
|
5782
|
+
donutWidth: options.donutWidth,
|
|
5783
|
+
showLabel: options.showLabel,
|
|
5784
|
+
labelOffset: options.labelOffset,
|
|
5785
|
+
labelPosition: options.labelPosition,
|
|
5786
|
+
labelTransform: options.labelTransform,
|
|
5787
|
+
labelInterpolationFnc: options.labelInterpolationFnc,
|
|
5788
|
+
labelDirection: options.labelDirection,
|
|
5789
|
+
ignoreEmptyValues: options.ignoreEmptyValues,
|
|
5790
|
+
preventOverlappingLabelOffset: options.preventOverlappingLabelOffset,
|
|
5791
|
+
classNames: options.classNames
|
|
5792
|
+
}),
|
|
5793
|
+
...above
|
|
5794
|
+
]
|
|
5363
5795
|
};
|
|
5364
5796
|
}
|
|
5365
5797
|
};
|
|
@@ -5368,7 +5800,6 @@ const pieSpec = {
|
|
|
5368
5800
|
* options, without a container: the pie layer with its labels. Pass it to `toScene()` to render
|
|
5369
5801
|
* the preset chart with another renderer. Responsive options and the
|
|
5370
5802
|
* inline size style of the root svg are left out; both need a container.
|
|
5371
|
-
* @experimental
|
|
5372
5803
|
*/
|
|
5373
5804
|
function pieChartConfig(data, options = {}) {
|
|
5374
5805
|
return presetConfig(pieSpec, data, options);
|
|
@@ -5381,7 +5812,6 @@ function createPieChart(target, data, options = {}, responsiveOptions, plugins)
|
|
|
5381
5812
|
* Creates a pie or donut chart with the 1.x options and output, rendered
|
|
5382
5813
|
* synchronously. Plugins must be v2 plugins; convert 1.x plugins with
|
|
5383
5814
|
* `fromLegacyPlugin` from `chartist/compat`.
|
|
5384
|
-
* @experimental
|
|
5385
5815
|
*/
|
|
5386
5816
|
function pieChart(target, data, options = {}, responsiveOptions) {
|
|
5387
5817
|
return createPieChart(target, data, options, responsiveOptions).handle;
|
|
@@ -5693,7 +6123,6 @@ var interpolation_exports = /* @__PURE__ */ __exportAll({
|
|
|
5693
6123
|
* // The color of series `c`, the third.
|
|
5694
6124
|
* export const third = seriesColors[2];
|
|
5695
6125
|
* ```
|
|
5696
|
-
* @experimental
|
|
5697
6126
|
*/
|
|
5698
6127
|
const seriesColors = [
|
|
5699
6128
|
"#d70206",
|
|
@@ -5715,16 +6144,22 @@ const seriesColors = [
|
|
|
5715
6144
|
//#endregion
|
|
5716
6145
|
//#region src/render/string.ts
|
|
5717
6146
|
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => escapingMap[char]);
|
|
6147
|
+
const nameStart = "A-Z_:a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|
6148
|
+
const xmlName = new RegExp(`^[${nameStart}][${nameStart}\\-.0-9\\u00B7\\u0300-\\u036F\\u203F\\u2040]*$`);
|
|
6149
|
+
function checked(name, kind) {
|
|
6150
|
+
if (!xmlName.test(name)) throw new Error(`Chartist: "${name}" is not a valid ${kind} name`);
|
|
6151
|
+
return name;
|
|
6152
|
+
}
|
|
5718
6153
|
function attributes(current, position) {
|
|
5719
6154
|
const parts = [];
|
|
5720
6155
|
if (position === "root") parts.push(`xmlns="${namespaces.svg}"`, `xmlns:ct="${namespaces.ct}"`);
|
|
5721
6156
|
else if (position === "html-root") parts.push(`xmlns="${namespaces.xhtml}"`);
|
|
5722
6157
|
if (current.classes.length) parts.push(`class="${escape(current.classes.join(" "))}"`);
|
|
5723
|
-
for (const [name, value] of Object.entries(current.attrs)) if (name !== "xmlns") parts.push(`${name}="${escape(value)}"`);
|
|
6158
|
+
for (const [name, value] of Object.entries(current.attrs)) if (name !== "xmlns") parts.push(`${checked(name, "attribute")}="${escape(value)}"`);
|
|
5724
6159
|
return parts.length ? ` ${parts.join(" ")}` : "";
|
|
5725
6160
|
}
|
|
5726
6161
|
function serialize$1(current, position) {
|
|
5727
|
-
const open = `<${current.name}${attributes(current, position)}`;
|
|
6162
|
+
const open = `<${checked(current.name, "element")}${attributes(current, position)}`;
|
|
5728
6163
|
const childPosition = current.name === "foreignObject" ? "html-root" : "other";
|
|
5729
6164
|
const content = (current.content === void 0 ? "" : escape(current.content)) + current.children.map((child) => serialize$1(child, childPosition)).join("");
|
|
5730
6165
|
return content ? `${open}>${content}</${current.name}>` : `${open}/>`;
|
|
@@ -5770,7 +6205,6 @@ function serialize$1(current, position) {
|
|
|
5770
6205
|
* return renderToString(scene);
|
|
5771
6206
|
* }
|
|
5772
6207
|
* ```
|
|
5773
|
-
* @experimental
|
|
5774
6208
|
*/
|
|
5775
6209
|
function renderToString(scene) {
|
|
5776
6210
|
return serialize$1(scene, "root");
|
|
@@ -5833,7 +6267,6 @@ function unavailable(method) {
|
|
|
5833
6267
|
*
|
|
5834
6268
|
* export const markup = renderToString(scene);
|
|
5835
6269
|
* ```
|
|
5836
|
-
* @experimental
|
|
5837
6270
|
*/
|
|
5838
6271
|
function toScene(config, size) {
|
|
5839
6272
|
const emitter = createPhaseEmitter();
|
|
@@ -5855,6 +6288,548 @@ function toScene(config, size) {
|
|
|
5855
6288
|
}).scene;
|
|
5856
6289
|
}
|
|
5857
6290
|
//#endregion
|
|
6291
|
+
//#region src/layers/band.ts
|
|
6292
|
+
const CLASS_NAMES$3 = {
|
|
6293
|
+
band: "ct-band",
|
|
6294
|
+
area: "ct-band-area",
|
|
6295
|
+
inBand: "ct-in-band",
|
|
6296
|
+
outBand: "ct-out-band"
|
|
6297
|
+
};
|
|
6298
|
+
/** The series an edge follows, if it's a series. */
|
|
6299
|
+
function selectorOf(edge) {
|
|
6300
|
+
return typeof edge === "number" || isValues(edge) ? void 0 : typeof edge === "string" ? edge : edge.series;
|
|
6301
|
+
}
|
|
6302
|
+
function isValues(edge) {
|
|
6303
|
+
return Array.isArray(edge);
|
|
6304
|
+
}
|
|
6305
|
+
/** An edge's own values, normalized like a series' values. */
|
|
6306
|
+
function normalizedValues(values) {
|
|
6307
|
+
return values.map((value) => isNumeric(value) ? { y: Number(value) } : value && typeof value === "object" ? value : void 0);
|
|
6308
|
+
}
|
|
6309
|
+
/** The value of a normalized value along Y, or `undefined` for a hole. */
|
|
6310
|
+
function yOf(value) {
|
|
6311
|
+
const y = value !== null && typeof value === "object" ? value.y : value;
|
|
6312
|
+
return isNumeric(y) ? Number(y) : void 0;
|
|
6313
|
+
}
|
|
6314
|
+
function xOf(value) {
|
|
6315
|
+
return value !== null && typeof value === "object" ? value.x : void 0;
|
|
6316
|
+
}
|
|
6317
|
+
/**
|
|
6318
|
+
* The position of a series in the layer's data: the layer gets only the
|
|
6319
|
+
* series it selects, with their chart-wide indexes in `seriesIndexes`.
|
|
6320
|
+
*/
|
|
6321
|
+
function localIndex(selector, context) {
|
|
6322
|
+
const { data, seriesIndexes } = context;
|
|
6323
|
+
if (typeof selector === "string") return data.series.findIndex((series) => series.name === selector);
|
|
6324
|
+
return seriesIndexes ? seriesIndexes.indexOf(selector) : selector;
|
|
6325
|
+
}
|
|
6326
|
+
function chartIndex(selector, context) {
|
|
6327
|
+
const index = localIndex(selector, context);
|
|
6328
|
+
return context.seriesIndexes?.[index] ?? index;
|
|
6329
|
+
}
|
|
6330
|
+
/**
|
|
6331
|
+
* Works out the band's edges from the data in the context.
|
|
6332
|
+
* @param constant Changes a constant edge before it's used, e.g. to keep it
|
|
6333
|
+
* within the axis range for drawing.
|
|
6334
|
+
*/
|
|
6335
|
+
function bandEdges(options, context, constant = (value) => value) {
|
|
6336
|
+
const all = context.normalized?.series ?? [];
|
|
6337
|
+
const seriesOf = (selector) => all[localIndex(selector, context)] ?? [];
|
|
6338
|
+
if (options.series) {
|
|
6339
|
+
const group = options.series.map(seriesOf);
|
|
6340
|
+
return {
|
|
6341
|
+
constant: false,
|
|
6342
|
+
length: Math.max(0, ...group.map((values) => values.length)),
|
|
6343
|
+
at(valueIndex) {
|
|
6344
|
+
const defined = group.map((values) => values[valueIndex]).filter((value) => yOf(value) !== void 0);
|
|
6345
|
+
if (defined.length === 0) return;
|
|
6346
|
+
const ys = defined.map((value) => yOf(value));
|
|
6347
|
+
return {
|
|
6348
|
+
x: xOf(defined[0]),
|
|
6349
|
+
low: Math.min(...ys),
|
|
6350
|
+
high: Math.max(...ys)
|
|
6351
|
+
};
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
const edge = (option) => {
|
|
6356
|
+
if (option !== void 0 && isValues(option)) return { values: normalizedValues(option) };
|
|
6357
|
+
const selector = option === void 0 ? void 0 : selectorOf(option);
|
|
6358
|
+
return selector === void 0 ? { value: constant(option) } : { values: seriesOf(selector) };
|
|
6359
|
+
};
|
|
6360
|
+
const low = edge(options.low);
|
|
6361
|
+
const high = edge(options.high);
|
|
6362
|
+
const lengths = [low, high].flatMap((e) => "values" in e ? [e.values.length] : []);
|
|
6363
|
+
return {
|
|
6364
|
+
constant: lengths.length === 0,
|
|
6365
|
+
length: Math.max(0, ...lengths),
|
|
6366
|
+
at(valueIndex) {
|
|
6367
|
+
const lowValue = "values" in low ? low.values[valueIndex] : void 0;
|
|
6368
|
+
const highValue = "values" in high ? high.values[valueIndex] : void 0;
|
|
6369
|
+
const lowY = "value" in low ? low.value : yOf(lowValue);
|
|
6370
|
+
const highY = "value" in high ? high.value : yOf(highValue);
|
|
6371
|
+
if (lowY === void 0 || highY === void 0) return;
|
|
6372
|
+
return {
|
|
6373
|
+
x: xOf(lowValue) ?? xOf(highValue),
|
|
6374
|
+
low: lowY,
|
|
6375
|
+
high: highY
|
|
6376
|
+
};
|
|
6377
|
+
}
|
|
6378
|
+
};
|
|
6379
|
+
}
|
|
6380
|
+
/** The value a mark compares with the band: `y`, else `x`. */
|
|
6381
|
+
function markValue(value) {
|
|
6382
|
+
if (value !== null && typeof value === "object") {
|
|
6383
|
+
const { x, y } = value;
|
|
6384
|
+
return isNumeric(y) ? Number(y) : isNumeric(x) ? Number(x) : void 0;
|
|
6385
|
+
}
|
|
6386
|
+
return isNumeric(value) ? Number(value) : void 0;
|
|
6387
|
+
}
|
|
6388
|
+
function edgeContext(option, context) {
|
|
6389
|
+
if (option === void 0) return;
|
|
6390
|
+
if (isValues(option)) return { values: normalizedValues(option).map(yOf) };
|
|
6391
|
+
const selector = selectorOf(option);
|
|
6392
|
+
return selector === void 0 ? { value: option } : { seriesIndex: chartIndex(selector, context) };
|
|
6393
|
+
}
|
|
6394
|
+
/**
|
|
6395
|
+
* Creates a layer that fills the area between a low and a high edge
|
|
6396
|
+
* (`g.ct-band` with a `path.ct-band-area` per piece; the band breaks at
|
|
6397
|
+
* holes). Each edge is a constant value, values of its own (one per
|
|
6398
|
+
* label) or a series, or `series` names a group and the band covers their
|
|
6399
|
+
* minimum to their maximum at each value index. Constants and own values
|
|
6400
|
+
* widen the Y axis to include them. List the band
|
|
6401
|
+
* before the series layers to draw it behind them. It needs an X and a Y
|
|
6402
|
+
* axis, from {@link cartesianAxes}.
|
|
6403
|
+
*
|
|
6404
|
+
* @example
|
|
6405
|
+
* ```ts
|
|
6406
|
+
* import {
|
|
6407
|
+
* autoScaleAxis,
|
|
6408
|
+
* axisGrid,
|
|
6409
|
+
* axisLabels,
|
|
6410
|
+
* band,
|
|
6411
|
+
* cartesianAxes,
|
|
6412
|
+
* chart,
|
|
6413
|
+
* line,
|
|
6414
|
+
* stepAxis
|
|
6415
|
+
* } from 'chartist';
|
|
6416
|
+
*
|
|
6417
|
+
* const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
|
|
6418
|
+
*
|
|
6419
|
+
* chart('#chart', {
|
|
6420
|
+
* data: {
|
|
6421
|
+
* labels: days,
|
|
6422
|
+
* series: [{ name: 'visits', data: [12, 9, 7, 8, 5] }]
|
|
6423
|
+
* },
|
|
6424
|
+
* axes: cartesianAxes([
|
|
6425
|
+
* { id: 'day', side: 'bottom', type: stepAxis(), ticks: days },
|
|
6426
|
+
* { id: 'count', side: 'left', type: autoScaleAxis(), low: 0 }
|
|
6427
|
+
* ]),
|
|
6428
|
+
* layers: [
|
|
6429
|
+
* axisGrid(),
|
|
6430
|
+
* band({ low: 6, high: 10, mark: true }),
|
|
6431
|
+
* line(),
|
|
6432
|
+
* axisLabels()
|
|
6433
|
+
* ]
|
|
6434
|
+
* });
|
|
6435
|
+
* ```
|
|
6436
|
+
*/
|
|
6437
|
+
function band(options) {
|
|
6438
|
+
const hasEdges = options.low !== void 0 || options.high !== void 0;
|
|
6439
|
+
if (options.series && hasEdges) throw new Error("Layer \"band\": series can't be combined with low and high");
|
|
6440
|
+
if (!options.series && (options.low === void 0 || options.high === void 0)) throw new Error("Layer \"band\": the band needs low and high, or series");
|
|
6441
|
+
const classNames = {
|
|
6442
|
+
...CLASS_NAMES$3,
|
|
6443
|
+
...options.classNames
|
|
6444
|
+
};
|
|
6445
|
+
const edgeSelectors = options.series ? void 0 : [options.low, options.high].flatMap((edge) => {
|
|
6446
|
+
const selector = edge === void 0 ? void 0 : selectorOf(edge);
|
|
6447
|
+
return selector === void 0 ? [] : [selector];
|
|
6448
|
+
});
|
|
6449
|
+
const ownValues = options.series ? [] : [options.low, options.high].flatMap((edge) => typeof edge === "number" ? [edge] : edge !== void 0 && isValues(edge) ? normalizedValues(edge).flatMap((value) => {
|
|
6450
|
+
const y = yOf(value);
|
|
6451
|
+
return y === void 0 ? [] : [y];
|
|
6452
|
+
}) : []);
|
|
6453
|
+
const layer = {
|
|
6454
|
+
type: "band",
|
|
6455
|
+
coordinates: ["cartesian"],
|
|
6456
|
+
axes: {
|
|
6457
|
+
x: options.x,
|
|
6458
|
+
y: options.y
|
|
6459
|
+
},
|
|
6460
|
+
series: options.series ?? edgeSelectors,
|
|
6461
|
+
extent({ axes }) {
|
|
6462
|
+
return axes.y !== void 0 && ownValues.length > 0 ? { [axes.y]: {
|
|
6463
|
+
low: Math.min(...ownValues),
|
|
6464
|
+
high: Math.max(...ownValues)
|
|
6465
|
+
} } : {};
|
|
6466
|
+
},
|
|
6467
|
+
compose(context) {
|
|
6468
|
+
const { area, axes } = context;
|
|
6469
|
+
const { x: axisX, y: axisY } = axes;
|
|
6470
|
+
if (!axisX || !axisY || !context.normalized) throw new Error("Layer \"band\" needs an x and a y axis");
|
|
6471
|
+
const range = axisY.range;
|
|
6472
|
+
const clamp = (value) => range ? Math.min(Math.max(value, range.min), range.max) : value;
|
|
6473
|
+
const edges = bandEdges(options, context, clamp);
|
|
6474
|
+
let pieces;
|
|
6475
|
+
const interpolation = interpolationOf(options.lineSmooth ?? true);
|
|
6476
|
+
if (edges.constant) {
|
|
6477
|
+
const { low, high } = edges.at(0);
|
|
6478
|
+
const lowY = area.y1 - axisY.project(low);
|
|
6479
|
+
const highY = area.y1 - axisY.project(high);
|
|
6480
|
+
pieces = [new SvgPath(true).move(area.x1, highY).line(area.x2, highY).line(area.x2, lowY).line(area.x1, lowY)];
|
|
6481
|
+
} else {
|
|
6482
|
+
const upper = [];
|
|
6483
|
+
const lower = [];
|
|
6484
|
+
for (let valueIndex = 0; valueIndex < edges.length; valueIndex++) {
|
|
6485
|
+
const at = edges.at(valueIndex);
|
|
6486
|
+
upper.push(at && {
|
|
6487
|
+
x: at.x,
|
|
6488
|
+
y: at.high
|
|
6489
|
+
});
|
|
6490
|
+
lower.push(at && {
|
|
6491
|
+
x: at.x,
|
|
6492
|
+
y: at.low
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6495
|
+
const shift = centerShift(axisX, options.align);
|
|
6496
|
+
const pathOf = (values) => seriesPath(values, {
|
|
6497
|
+
x: axisX,
|
|
6498
|
+
y: axisY
|
|
6499
|
+
}, area, shift, interpolation).splitByCommand("M").filter((piece) => piece.pathElements.length > 1);
|
|
6500
|
+
const lowerPieces = pathOf(lower);
|
|
6501
|
+
pieces = pathOf(upper).flatMap((upperPiece, index) => {
|
|
6502
|
+
const lowerPiece = lowerPieces[index];
|
|
6503
|
+
if (!lowerPiece || lowerPiece.pathElements.length !== upperPiece.pathElements.length) return [];
|
|
6504
|
+
const back = reversePath(lowerPiece);
|
|
6505
|
+
const start = back.pathElements[0];
|
|
6506
|
+
back.position(0).remove(1).line(start.x, start.y, false, start.data);
|
|
6507
|
+
return [SvgPath.join([upperPiece, back], true)];
|
|
6508
|
+
});
|
|
6509
|
+
}
|
|
6510
|
+
const edgesContext = options.series ? { series: options.series.map((s) => chartIndex(s, context)) } : {
|
|
6511
|
+
low: edgeContext(options.low, context),
|
|
6512
|
+
high: edgeContext(options.high, context)
|
|
6513
|
+
};
|
|
6514
|
+
const children = pieces.map((path) => node("path", { d: path.stringify() }, classNames.area, {
|
|
6515
|
+
role: "band",
|
|
6516
|
+
ctx: {
|
|
6517
|
+
...edgesContext,
|
|
6518
|
+
path: path.clone(),
|
|
6519
|
+
smoothing: interpolation
|
|
6520
|
+
}
|
|
6521
|
+
}));
|
|
6522
|
+
return [withFields(node("g", {}, options.className ? `${classNames.band} ${options.className}` : classNames.band), { children })];
|
|
6523
|
+
}
|
|
6524
|
+
};
|
|
6525
|
+
if (!options.mark) return layer;
|
|
6526
|
+
return {
|
|
6527
|
+
...layer,
|
|
6528
|
+
refine(current, context) {
|
|
6529
|
+
if (current.role !== "point" && current.role !== "bar") return;
|
|
6530
|
+
const value = markValue(current.ctx.value);
|
|
6531
|
+
const at = bandEdges(options, context).at(current.ctx.valueIndex);
|
|
6532
|
+
if (value === void 0 || !at) return;
|
|
6533
|
+
const inBand = value >= Math.min(at.low, at.high) && value <= Math.max(at.low, at.high);
|
|
6534
|
+
return current.addClass(inBand ? classNames.inBand : classNames.outBand);
|
|
6535
|
+
}
|
|
6536
|
+
};
|
|
6537
|
+
}
|
|
6538
|
+
//#endregion
|
|
6539
|
+
//#region src/layers/targetLine.ts
|
|
6540
|
+
/**
|
|
6541
|
+
* Target line layer: a line across the chart area at a constant value on
|
|
6542
|
+
* one axis, such as a goal, a limit or an average, with an optional label.
|
|
6543
|
+
*/
|
|
6544
|
+
const CLASS_NAMES$2 = {
|
|
6545
|
+
line: "ct-target-line",
|
|
6546
|
+
label: "ct-target-label"
|
|
6547
|
+
};
|
|
6548
|
+
/** The distance in px between a label and its line or the area's edge. */
|
|
6549
|
+
const LABEL_INSET = 4;
|
|
6550
|
+
/**
|
|
6551
|
+
* Creates a layer that draws a line across the chart area at a constant
|
|
6552
|
+
* value on one axis, such as a goal or a limit (`line.ct-target-line`).
|
|
6553
|
+
* On a Y axis the line is horizontal, on an X axis vertical. It can have
|
|
6554
|
+
* a label, drawn as SVG text (`text.ct-target-label`) next to it inside
|
|
6555
|
+
* the chart area, and a class, e.g. `ct-series-b` for a series' color.
|
|
6556
|
+
* The axis widens to include the value, unless its own `low` or `high`
|
|
6557
|
+
* leave it out; a line outside the axis isn't drawn. For several lines,
|
|
6558
|
+
* add a layer per line.
|
|
6559
|
+
*
|
|
6560
|
+
* @example
|
|
6561
|
+
* ```ts
|
|
6562
|
+
* import {
|
|
6563
|
+
* autoScaleAxis,
|
|
6564
|
+
* axisGrid,
|
|
6565
|
+
* axisLabels,
|
|
6566
|
+
* cartesianAxes,
|
|
6567
|
+
* chart,
|
|
6568
|
+
* line,
|
|
6569
|
+
* stepAxis,
|
|
6570
|
+
* targetLine
|
|
6571
|
+
* } from 'chartist';
|
|
6572
|
+
*
|
|
6573
|
+
* const months = ['Jan', 'Feb', 'Mar', 'Apr'];
|
|
6574
|
+
*
|
|
6575
|
+
* chart('#chart', {
|
|
6576
|
+
* data: { labels: months, series: [[620, 810, 740, 930]] },
|
|
6577
|
+
* axes: cartesianAxes([
|
|
6578
|
+
* { id: 'month', side: 'bottom', type: stepAxis(), ticks: months },
|
|
6579
|
+
* { id: 'sales', side: 'left', type: autoScaleAxis(), low: 0 }
|
|
6580
|
+
* ]),
|
|
6581
|
+
* layers: [
|
|
6582
|
+
* axisGrid(),
|
|
6583
|
+
* line(),
|
|
6584
|
+
* targetLine({ value: 700, label: 'Min' }),
|
|
6585
|
+
* targetLine({ value: 1000, label: 'Goal', className: 'ct-series-b' }),
|
|
6586
|
+
* axisLabels()
|
|
6587
|
+
* ]
|
|
6588
|
+
* });
|
|
6589
|
+
* ```
|
|
6590
|
+
*/
|
|
6591
|
+
function targetLine(options) {
|
|
6592
|
+
const { value, label, className } = options;
|
|
6593
|
+
if (!Number.isFinite(value)) throw new Error("Layer \"targetLine\" needs a value");
|
|
6594
|
+
const classNames = {
|
|
6595
|
+
...CLASS_NAMES$2,
|
|
6596
|
+
...options.classNames
|
|
6597
|
+
};
|
|
6598
|
+
const atStart = options.labelPosition === "start";
|
|
6599
|
+
return {
|
|
6600
|
+
type: "targetLine",
|
|
6601
|
+
coordinates: ["cartesian"],
|
|
6602
|
+
series: [],
|
|
6603
|
+
extent({ axes }) {
|
|
6604
|
+
const id = options.axis ?? axes.y;
|
|
6605
|
+
return id === void 0 ? {} : { [id]: {
|
|
6606
|
+
low: value,
|
|
6607
|
+
high: value
|
|
6608
|
+
} };
|
|
6609
|
+
},
|
|
6610
|
+
compose({ area, axes, measureText }) {
|
|
6611
|
+
const all = axes.all ?? [];
|
|
6612
|
+
const axis = options.axis === void 0 ? all.find((candidate) => candidate.dimension === "y") : all.find((candidate) => candidate.id === options.axis);
|
|
6613
|
+
if (!axis) throw new Error(options.axis === void 0 ? "Layer \"targetLine\" needs a y axis" : `Chartist: unknown axis "${options.axis}" in layer "targetLine"`);
|
|
6614
|
+
const horizontal = axis.dimension === "y";
|
|
6615
|
+
const relative = axis.project(value, value);
|
|
6616
|
+
if (!Number.isFinite(relative) || relative < -1e-9 || relative > axis.length + 1e-9) return [];
|
|
6617
|
+
const ends = horizontal ? {
|
|
6618
|
+
x1: area.x1,
|
|
6619
|
+
y1: area.y1 - relative,
|
|
6620
|
+
x2: area.x2,
|
|
6621
|
+
y2: area.y1 - relative
|
|
6622
|
+
} : {
|
|
6623
|
+
x1: area.x1 + relative,
|
|
6624
|
+
y1: area.y2,
|
|
6625
|
+
x2: area.x1 + relative,
|
|
6626
|
+
y2: area.y1
|
|
6627
|
+
};
|
|
6628
|
+
const extra = className ? ` ${className}` : "";
|
|
6629
|
+
const lineNode = node("line", ends, `${classNames.line}${extra}`, {
|
|
6630
|
+
role: "targetLine",
|
|
6631
|
+
ctx: {
|
|
6632
|
+
axis,
|
|
6633
|
+
value,
|
|
6634
|
+
...label !== void 0 && { label },
|
|
6635
|
+
...ends
|
|
6636
|
+
}
|
|
6637
|
+
});
|
|
6638
|
+
if (label === void 0 || label === "") return [lineNode];
|
|
6639
|
+
const labelClasses = `${classNames.label}${extra}`;
|
|
6640
|
+
let position;
|
|
6641
|
+
if (horizontal) position = {
|
|
6642
|
+
x: atStart ? area.x1 + LABEL_INSET : area.x2 - LABEL_INSET,
|
|
6643
|
+
y: ends.y1 - LABEL_INSET,
|
|
6644
|
+
anchor: atStart ? "start" : "end"
|
|
6645
|
+
};
|
|
6646
|
+
else {
|
|
6647
|
+
const fontSize = measureText(label, { classes: labelClasses.split(" ") }).fontSize ?? 12;
|
|
6648
|
+
position = {
|
|
6649
|
+
x: ends.x1 + LABEL_INSET,
|
|
6650
|
+
y: atStart ? area.y1 - LABEL_INSET : area.y2 + LABEL_INSET + textAscent * fontSize,
|
|
6651
|
+
anchor: "start"
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
return [lineNode, node("text", {
|
|
6655
|
+
x: position.x,
|
|
6656
|
+
y: position.y,
|
|
6657
|
+
"text-anchor": position.anchor
|
|
6658
|
+
}, labelClasses, {
|
|
6659
|
+
role: "targetLabel",
|
|
6660
|
+
ctx: {
|
|
6661
|
+
axis,
|
|
6662
|
+
value,
|
|
6663
|
+
text: label,
|
|
6664
|
+
x: position.x,
|
|
6665
|
+
y: position.y
|
|
6666
|
+
}
|
|
6667
|
+
}).text(label)];
|
|
6668
|
+
}
|
|
6669
|
+
};
|
|
6670
|
+
}
|
|
6671
|
+
//#endregion
|
|
6672
|
+
//#region src/layers/trendLine.ts
|
|
6673
|
+
/**
|
|
6674
|
+
* Trend line layer: a least-squares straight line through each series'
|
|
6675
|
+
* values, drawn in the series' color.
|
|
6676
|
+
*/
|
|
6677
|
+
const CLASS_NAMES$1 = {
|
|
6678
|
+
series: "ct-series",
|
|
6679
|
+
line: "ct-trend-line"
|
|
6680
|
+
};
|
|
6681
|
+
/**
|
|
6682
|
+
* Fits a straight line through a series' values by least squares: X is
|
|
6683
|
+
* the value's index (`byIndex`, for step axes) or its `x`. Holes and
|
|
6684
|
+
* values without a number for X are skipped. Fewer than two values, or
|
|
6685
|
+
* values that all share one X, fit no line.
|
|
6686
|
+
*/
|
|
6687
|
+
function fit(values, byIndex) {
|
|
6688
|
+
const xs = [];
|
|
6689
|
+
const ys = [];
|
|
6690
|
+
values.forEach((value, index) => {
|
|
6691
|
+
const y = isDataHoleValue(value) ? void 0 : getMultiValue(value, "y");
|
|
6692
|
+
const x = byIndex ? index : getMultiValue(value, "x");
|
|
6693
|
+
if (typeof x === "number" && Number.isFinite(x) && Number.isFinite(y)) {
|
|
6694
|
+
xs.push(x);
|
|
6695
|
+
ys.push(y);
|
|
6696
|
+
}
|
|
6697
|
+
});
|
|
6698
|
+
if (xs.length < 2) return;
|
|
6699
|
+
const mean = (list) => list.reduce((sum, value) => sum + value, 0) / list.length;
|
|
6700
|
+
const meanX = mean(xs);
|
|
6701
|
+
const meanY = mean(ys);
|
|
6702
|
+
let covariance = 0;
|
|
6703
|
+
let variance = 0;
|
|
6704
|
+
xs.forEach((x, i) => {
|
|
6705
|
+
covariance += (x - meanX) * (ys[i] - meanY);
|
|
6706
|
+
variance += (x - meanX) ** 2;
|
|
6707
|
+
});
|
|
6708
|
+
if (variance === 0) return;
|
|
6709
|
+
const slope = covariance / variance;
|
|
6710
|
+
return {
|
|
6711
|
+
slope,
|
|
6712
|
+
intercept: meanY - slope * meanX,
|
|
6713
|
+
x1: Math.min(...xs),
|
|
6714
|
+
x2: Math.max(...xs)
|
|
6715
|
+
};
|
|
6716
|
+
}
|
|
6717
|
+
/** True when every value that isn't a hole has a number for X. */
|
|
6718
|
+
function hasX(values) {
|
|
6719
|
+
const present = values.filter((value) => !isDataHoleValue(value));
|
|
6720
|
+
return present.length > 0 && present.every((value) => typeof getMultiValue(value, "x") === "number");
|
|
6721
|
+
}
|
|
6722
|
+
/**
|
|
6723
|
+
* Creates a layer that draws a trend line for each series: the straight
|
|
6724
|
+
* line that fits its values best (least squares), from its smallest to its
|
|
6725
|
+
* largest X, as a `line.ct-trend-line` in a series group
|
|
6726
|
+
* (`g.ct-series.ct-series-a`), so it takes the series' color. On a step
|
|
6727
|
+
* axis X is the value's index, on a scale axis the value's `x`. Holes are
|
|
6728
|
+
* skipped, and a series with fewer than two values gets no line. The Y
|
|
6729
|
+
* axis widens to include the line's ends. Add it next to the {@link line}
|
|
6730
|
+
* or {@link bar} layer that draws the values.
|
|
6731
|
+
*
|
|
6732
|
+
* @example
|
|
6733
|
+
* ```ts
|
|
6734
|
+
* import {
|
|
6735
|
+
* autoScaleAxis,
|
|
6736
|
+
* axisGrid,
|
|
6737
|
+
* axisLabels,
|
|
6738
|
+
* cartesianAxes,
|
|
6739
|
+
* chart,
|
|
6740
|
+
* line,
|
|
6741
|
+
* stepAxis,
|
|
6742
|
+
* trendLine
|
|
6743
|
+
* } from 'chartist';
|
|
6744
|
+
*
|
|
6745
|
+
* const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
|
|
6746
|
+
*
|
|
6747
|
+
* chart('#chart', {
|
|
6748
|
+
* data: {
|
|
6749
|
+
* labels: months,
|
|
6750
|
+
* series: [{ name: 'sales', data: [620, 810, 740, 930, 880, 1020] }]
|
|
6751
|
+
* },
|
|
6752
|
+
* axes: cartesianAxes([
|
|
6753
|
+
* { id: 'month', side: 'bottom', type: stepAxis(), ticks: months },
|
|
6754
|
+
* { id: 'eur', side: 'left', type: autoScaleAxis() }
|
|
6755
|
+
* ]),
|
|
6756
|
+
* layers: [axisGrid(), line(), trendLine(), axisLabels()]
|
|
6757
|
+
* });
|
|
6758
|
+
* ```
|
|
6759
|
+
*/
|
|
6760
|
+
function trendLine(options = {}) {
|
|
6761
|
+
const classNames = {
|
|
6762
|
+
...CLASS_NAMES$1,
|
|
6763
|
+
...options.classNames
|
|
6764
|
+
};
|
|
6765
|
+
return {
|
|
6766
|
+
type: "trendLine",
|
|
6767
|
+
coordinates: ["cartesian"],
|
|
6768
|
+
axes: {
|
|
6769
|
+
x: options.x,
|
|
6770
|
+
y: options.y
|
|
6771
|
+
},
|
|
6772
|
+
series: options.series,
|
|
6773
|
+
extent({ normalized, axes }) {
|
|
6774
|
+
if (axes.y === void 0) return {};
|
|
6775
|
+
const ends = normalized.series.flatMap((values) => {
|
|
6776
|
+
const fitted = fit(values, !hasX(values));
|
|
6777
|
+
return fitted ? [fitted.slope * fitted.x1 + fitted.intercept, fitted.slope * fitted.x2 + fitted.intercept] : [];
|
|
6778
|
+
});
|
|
6779
|
+
return ends.length > 0 ? { [axes.y]: {
|
|
6780
|
+
low: Math.min(...ends),
|
|
6781
|
+
high: Math.max(...ends)
|
|
6782
|
+
} } : {};
|
|
6783
|
+
},
|
|
6784
|
+
compose({ data, area, axes, normalized, seriesIndexes }) {
|
|
6785
|
+
const { x: axisX, y: axisY } = axes;
|
|
6786
|
+
if (!axisX || !axisY || !normalized) throw new Error("Layer \"trendLine\" needs an x and a y axis");
|
|
6787
|
+
const byIndex = axisX.kind === "step";
|
|
6788
|
+
const shift = centerShift(axisX, options.align);
|
|
6789
|
+
return data.series.flatMap((series, localIndex) => {
|
|
6790
|
+
const fitted = fit(normalized.series[localIndex], byIndex);
|
|
6791
|
+
if (!fitted) return [];
|
|
6792
|
+
const seriesIndex = seriesIndexes?.[localIndex] ?? localIndex;
|
|
6793
|
+
const { slope, intercept } = fitted;
|
|
6794
|
+
const at = (x) => ({
|
|
6795
|
+
x: area.x1 + shift + axisX.project(x, x),
|
|
6796
|
+
y: area.y1 - axisY.project(slope * x + intercept)
|
|
6797
|
+
});
|
|
6798
|
+
const start = at(fitted.x1);
|
|
6799
|
+
const end = at(fitted.x2);
|
|
6800
|
+
const ends = {
|
|
6801
|
+
x1: start.x,
|
|
6802
|
+
y1: start.y,
|
|
6803
|
+
x2: end.x,
|
|
6804
|
+
y2: end.y
|
|
6805
|
+
};
|
|
6806
|
+
const seriesContext = {
|
|
6807
|
+
series: {
|
|
6808
|
+
name: series.name,
|
|
6809
|
+
className: series.className,
|
|
6810
|
+
meta: series.meta,
|
|
6811
|
+
data: series.values
|
|
6812
|
+
},
|
|
6813
|
+
seriesIndex,
|
|
6814
|
+
meta: series.meta
|
|
6815
|
+
};
|
|
6816
|
+
return [withFields(node("g", {}, `${classNames.series} ${series.className || `${classNames.series}-${alphaNumerate(seriesIndex)}`}`, {
|
|
6817
|
+
role: "series",
|
|
6818
|
+
ctx: seriesContext
|
|
6819
|
+
}), { children: [node("line", ends, classNames.line, {
|
|
6820
|
+
role: "trendLine",
|
|
6821
|
+
ctx: {
|
|
6822
|
+
seriesIndex,
|
|
6823
|
+
slope,
|
|
6824
|
+
intercept,
|
|
6825
|
+
...ends
|
|
6826
|
+
}
|
|
6827
|
+
})] })];
|
|
6828
|
+
});
|
|
6829
|
+
}
|
|
6830
|
+
};
|
|
6831
|
+
}
|
|
6832
|
+
//#endregion
|
|
5858
6833
|
//#region src/layers/axes.ts
|
|
5859
6834
|
const CLASS_NAMES = {
|
|
5860
6835
|
grid: "ct-grid",
|
|
@@ -5880,7 +6855,7 @@ function drawnOptions(axis) {
|
|
|
5880
6855
|
},
|
|
5881
6856
|
showLabel: options.showLabel ?? true,
|
|
5882
6857
|
showGrid: options.showGrid ?? true,
|
|
5883
|
-
|
|
6858
|
+
labelTransform: labelTransformOf(options)
|
|
5884
6859
|
};
|
|
5885
6860
|
}
|
|
5886
6861
|
function selectedAxes(context, ids) {
|
|
@@ -5955,7 +6930,6 @@ function nodesOf(axis, context, classNames, svgLabel) {
|
|
|
5955
6930
|
* ```
|
|
5956
6931
|
* @param options The axes to draw and their classes; `background` adds a
|
|
5957
6932
|
* `rect.ct-grid-background` over the chart area, behind the lines.
|
|
5958
|
-
* @experimental
|
|
5959
6933
|
*/
|
|
5960
6934
|
function axisGrid(options = {}) {
|
|
5961
6935
|
const classNames = {
|
|
@@ -6010,7 +6984,6 @@ function axisGrid(options = {}) {
|
|
|
6010
6984
|
* ]
|
|
6011
6985
|
* });
|
|
6012
6986
|
* ```
|
|
6013
|
-
* @experimental
|
|
6014
6987
|
*/
|
|
6015
6988
|
function axisLabels(options = {}) {
|
|
6016
6989
|
const classNames = {
|
|
@@ -6022,12 +6995,15 @@ function axisLabels(options = {}) {
|
|
|
6022
6995
|
coordinates: ["cartesian"],
|
|
6023
6996
|
series: [],
|
|
6024
6997
|
compose(context) {
|
|
6025
|
-
const
|
|
6026
|
-
return [withFields(node("g", {}, classNames.labelGroup), { children: selectedAxes(context, options.axes).flatMap((axis) => nodesOf(axis, context, classNames,
|
|
6998
|
+
const svg = (options.labels ?? context.labels) === "svg";
|
|
6999
|
+
return [withFields(node("g", {}, classNames.labelGroup), { children: selectedAxes(context, options.axes).flatMap((axis) => nodesOf(axis, context, classNames, svg ? svgLabels(context.measureText, "line", {
|
|
7000
|
+
...options.align,
|
|
7001
|
+
...axis.options.labelAlign
|
|
7002
|
+
}) : void 0).labels) })];
|
|
6027
7003
|
}
|
|
6028
7004
|
};
|
|
6029
7005
|
}
|
|
6030
7006
|
//#endregion
|
|
6031
|
-
export { AutoScaleAxis, Axis, BarChart, BaseChart, EPSILON, EventEmitter, FixedScaleAxis, interpolation_exports as Interpolation, LineChart, PieChart, StepAxis, Svg, SvgList, SvgPath, alphaNumerate, autoScaleAxis, axisGrid, axisLabels, axisUnits, bar, barChart, barChartConfig, cartesianAxes, chart, container, createChartRect, createGrid, createGridBackground, createLabel, createSvg, deserialize, determineAnchorPosition, domRenderer, each, easings, ensureUnit, escapingMap, estimateText, extend, findAll, fixedScaleAxis, getBounds, getHighLow, getMetaData, getMultiValue, getNumberOrUndefined, getSeriesOption, isArrayOfArrays, isArrayOfSeries, isDataHoleValue, isFalseyButZero, isMultiValue, isNumeric, line, lineChart, lineChartConfig, mapNodes, namespaces, node, noop, normalizeData, normalizePadding, optionsProvider, orderOfMagnitude, pie, pieChart, pieChartConfig, polarToCartesian, precision, projectLength, quantity, renderToString, responsive, rho, roundWithPrecision, safeHasProperty, serialMap, serialize, seriesColors, splitIntoSegments, stepAxis, sum, textAscent, times, toScene, withOptions };
|
|
7007
|
+
export { AutoScaleAxis, Axis, BarChart, BaseChart, EPSILON, EventEmitter, FixedScaleAxis, interpolation_exports as Interpolation, LineChart, PieChart, StepAxis, Svg, SvgList, SvgPath, alphaNumerate, applyAttribute, autoScaleAxis, axisGrid, axisLabels, axisUnits, band, bar, barChart, barChartConfig, cartesianAxes, chart, container, createChartRect, createGrid, createGridBackground, createLabel, createSvg, deserialize, determineAnchorPosition, domRenderer, each, easings, ensureUnit, escapingMap, estimateText, extend, findAll, fixedScaleAxis, getBounds, getHighLow, getMetaData, getMultiValue, getNumberOrUndefined, getSeriesOption, isArrayOfArrays, isArrayOfSeries, isDataHoleValue, isFalseyButZero, isMultiValue, isNumeric, keySplinesOf, line, lineChart, lineChartConfig, mapNodes, namespaces, node, noop, normalizeData, normalizePadding, optionsProvider, orderOfMagnitude, pie, pieChart, pieChartConfig, polarToCartesian, precision, projectLength, quantity, renderElement, renderToString, responsive, rho, roundWithPrecision, safeHasProperty, sampleEasing, serialMap, serialize, seriesColors, splitIntoSegments, stepAxis, sum, targetLine, textAscent, times, toScene, trendLine, withOptions };
|
|
6032
7008
|
|
|
6033
7009
|
//# sourceMappingURL=index.js.map
|