shown
Version:
Statically-generated, responsive charts, without the need for client-side Javascript.
1,742 lines (1,709 loc) • 43.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.js
var src_exports = {};
__export(src_exports, {
area: () => area_default,
axis: () => axis_exports,
bar: () => bar_default,
color: () => color_exports,
legend: () => legend_default,
line: () => line_default,
map: () => map_exports,
pie: () => pie_default,
scatter: () => scatter_default
});
module.exports = __toCommonJS(src_exports);
// src/lib/dom/svg.js
var svg_default = [
"a",
"animate",
"animateMotion",
"animateTransform",
"circle",
"clipPath",
"defs",
"desc",
"discard",
"ellipse",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"filter",
"foreignObject",
"g",
"hatch",
"hatchpath",
"image",
"line",
"linearGradient",
"marker",
"mask",
"metadata",
"mpath",
"path",
"pattern",
"polygon",
"polyline",
"radialGradient",
"rect",
"script",
"set",
"stop",
"style",
"svg",
"switch",
"symbol",
"text",
"textPath",
"title",
"tspan",
"use",
"view"
];
// src/lib/dom/html.js
var html_default = [
"a",
"abbr",
"address",
"area",
"article",
"aside",
"audio",
"b",
"base",
"bdi",
"bdo",
"blockquote",
"body",
"br",
"button",
"canvas",
"caption",
"cite",
"code",
"col",
"colgroup",
"data",
"datalist",
"dd",
"del",
"details",
"dfn",
"dialog",
"div",
"dl",
"dt",
"em",
"embed",
"fieldset",
"figcaption",
"figure",
"footer",
"form",
"head",
"header",
"hr",
"html",
"i",
"iframe",
"img",
"input",
"ins",
"kbd",
"label",
"legend",
"li",
"link",
"main",
"map",
"mark",
"math",
"menu",
"meta",
"meter",
"nav",
"noscript",
"object",
"ol",
"optgroup",
"option",
"output",
"p",
"param",
"picture",
"portal",
"pre",
"progress",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"script",
"section",
"select",
"slot",
"small",
"source",
"span",
"strong",
"style",
"sub",
"summary",
"sup",
"svg",
"table",
"tbody",
"td",
"template",
"textarea",
"tfoot",
"th",
"thead",
"time",
"title",
"tr",
"track",
"u",
"ul",
"var",
"video",
"wbr"
];
// src/lib/dom/join.js
var mapSpecial = ([key, val]) => {
if (key === "class" && Array.isArray(val)) {
val = val.filter((f) => f).join(" ");
}
if (key === "style" && typeof val === "object") {
val = Object.entries(val).map((pair) => pair.join(":")).join(";");
}
return [key, val];
};
var flattenAttrs = (attrs) => {
const result = __spreadValues(__spreadValues({}, attrs), attrs.attrs);
delete result.attrs;
return result;
};
var join = (attrs = false) => attrs ? Object.entries(flattenAttrs(attrs)).filter(([key, val]) => val || val === 0).map(mapSpecial).map(([key, val]) => val === true ? key : `${key}="${val}"`).join(" ") : "";
var join_default = join;
// src/lib/dom/wrap.js
var shouldRender = (arg) => arg === 0 || arg;
var wrap_default = (type) => {
return (attrs) => {
attrs = join_default(attrs);
if (attrs)
attrs = " " + attrs;
return (children = "") => {
if (!Array.isArray(children))
children = [children];
const contents = children.filter(shouldRender).map((child) => typeof child === "function" ? child() : child).join("");
return `<${type}${attrs}>${contents}</${type}>`;
};
};
};
// src/lib/dom/index.js
var dom_default = [...svg_default, ...html_default].reduce((memo, key) => {
memo[key] = wrap_default(key);
return memo;
}, {});
// src/lib/utils/percent.js
var percent_default = (value, units = "%") => value !== 0 ? +(value * 100).toFixed(2) + units : "0";
// src/lib/utils/math.js
var isFinite = (v) => Number.isFinite(v);
var isInteger = (v) => Number.isInteger(v);
var min = (...values) => Math.min(...values);
var max = (...values) => Math.max(...values);
var clamp = (v, vmin, vmax) => min(max(v, vmax), vmin);
var tau = Math.PI * 2;
var cos = Math.cos;
var sin = Math.sin;
var atan2 = Math.atan2;
var abs = Math.abs;
var pow = Math.pow;
var floor = Math.floor;
var ceil = Math.ceil;
var round = Math.round;
var log10 = Math.log10;
// src/lib/utils/constants.js
var EPSILON = Number.EPSILON;
var DEFAULT_PRECISION = floor(abs(log10(EPSILON)));
// src/lib/utils/sum.js
var sum_default = (array, precision = DEFAULT_PRECISION) => {
const f = pow(10, precision);
return array.reduce((m, v = 0) => (f * m + f * (v && v.value || +v || 0)) / f, 0);
};
// src/lib/utils/to-precision.js
var to_precision_default = (value, precision = DEFAULT_PRECISION) => {
const f = pow(10, precision);
const n = precision < 0 ? value : 0.01 / f + value;
return round(n * f) / f;
};
// src/lib/map.js
var map_exports = {};
__export(map_exports, {
default: () => map_default
});
// src/lib/color.js
var color_exports = {};
__export(color_exports, {
default: () => color_default,
get: () => get,
wrap: () => wrap
});
var DEFAULT_COLORS = [
"#0036b0",
"#0236b1",
"#0536b1",
"#0737b2",
"#0a37b2",
"#0c37b3",
"#0f37b3",
"#1137b4",
"#1437b4",
"#1638b5",
"#1938b5",
"#1b38b6",
"#1e38b6",
"#2038b7",
"#2338b7",
"#2539b8",
"#2839b8",
"#2a39b9",
"#2d39b9",
"#2f39ba",
"#323aba",
"#343abb",
"#373abb",
"#393abc",
"#3c3abc",
"#3e3abd",
"#413bbd",
"#433bbe",
"#463bbe",
"#483bbf",
"#4b3bbf",
"#4d3bc0",
"#503cc0",
"#523cc1",
"#553cc1",
"#573cc2",
"#5a3cc2",
"#5c3dc3",
"#5f3dc3",
"#613dc4",
"#643dc4",
"#663dc5",
"#693dc5",
"#6b3ec6",
"#6e3ec6",
"#703ec7",
"#733ec7",
"#753ec8",
"#783fc8",
"#7a3fc9",
"#7d3fc9",
"#7f3fca",
"#823fca",
"#843fcb",
"#8740cb",
"#8940cc",
"#8c40cc",
"#8e40cd",
"#9140cd",
"#9340ce",
"#9641ce",
"#9841cf",
"#9b41cf",
"#9d41d0",
"#a041d0",
"#a242d1",
"#a542d1",
"#a742d2",
"#aa42d2",
"#ac42d3",
"#af42d3",
"#b143d4",
"#b443d4",
"#b643d5",
"#b943d5",
"#bb43d6",
"#be43d6",
"#c044d7",
"#c344d7",
"#c544d8",
"#c844d9",
"#cb44d9",
"#ce44d9",
"#d144d9",
"#d444da",
"#d844da",
"#da44da",
"#db44d7",
"#db44d5",
"#db44d2",
"#db44cf",
"#dc44cd",
"#dc44ca",
"#dc44c7",
"#dc44c5",
"#dd44c2",
"#dd44bf",
"#dd44bd",
"#dd45ba",
"#de45b7",
"#de45b4",
"#de45b2",
"#df45af",
"#df45ac",
"#df45a9",
"#df45a7",
"#e045a4",
"#e045a1",
"#e0459e",
"#e0459b",
"#e14599",
"#e14596",
"#e14593",
"#e14590",
"#e2458d",
"#e2458a",
"#e24688",
"#e24685",
"#e34682",
"#e3467f",
"#e3467c",
"#e34679",
"#e44676",
"#e44673",
"#e44671",
"#e54670",
"#e5456e",
"#e5456d",
"#e6456c",
"#e6446a",
"#e74469",
"#e74467",
"#e84466",
"#e84364",
"#e84363",
"#e94361",
"#e94260",
"#ea425e",
"#ea425c",
"#eb425b",
"#eb4159",
"#eb4158",
"#ec4156",
"#ec4154",
"#ed4053",
"#ed4051",
"#ed404f",
"#ee3f4e",
"#ee3f4c",
"#ef3f4a",
"#ef3f49",
"#ef3e47",
"#f03e45",
"#f03e44",
"#f13e42",
"#f13d40",
"#f23d3e",
"#f23d3d",
"#f23e3d",
"#f3403c",
"#f3413c",
"#f4423c",
"#f4433b",
"#f4453b",
"#f5463b",
"#f5473b",
"#f6493a",
"#f64a3a",
"#f64b3a",
"#f74d3a",
"#f74e39",
"#f85039",
"#f85139",
"#f85239",
"#f95438",
"#f95538",
"#fa5738",
"#fa5838",
"#fa5a37",
"#fb5b37",
"#fb5d37",
"#fc5e37",
"#fc6036",
"#fc6136",
"#fd6336",
"#fd6436",
"#fe6635",
"#fe6835",
"#fe6934",
"#fe6a34",
"#fe6b33",
"#fe6c32",
"#fe6d31",
"#fe6f30",
"#fe7030",
"#fe712f",
"#fe722e",
"#fe732d",
"#fe752c",
"#fe762b",
"#fe772b",
"#fe782a",
"#fe7a29",
"#fe7b28",
"#fe7c27",
"#fe7e27",
"#fe7f26",
"#fe8025",
"#fe8224",
"#fe8323",
"#fe8423",
"#fe8622",
"#fe8721",
"#fe8920",
"#fe8a1f",
"#fe8c1e",
"#fe8d1e",
"#fe8f1d",
"#fe901c",
"#fe921b",
"#fe931a",
"#fe951a",
"#fe9619",
"#fe9818",
"#ff9a17",
"#ff9b16",
"#ff9d15",
"#ff9e15",
"#ffa014",
"#ffa213",
"#ffa312",
"#ffa511",
"#ffa711",
"#ffa910",
"#ffaa0f",
"#ffac0e",
"#ffae0d",
"#ffb00c",
"#ffb10c",
"#ffb30b",
"#ffb50a",
"#ffb709",
"#ffb908",
"#ffbb08",
"#ffbc07",
"#ffbe06",
"#ffc005",
"#ffc204",
"#ffc403",
"#ffc603",
"#ffc802",
"#ffca01"
];
var get = (t) => {
if (!(t >= 0 && t <= 1))
t = 0;
const c = floor(t * (DEFAULT_COLORS.length - 1));
return t < 0.6 ? [DEFAULT_COLORS[c], "#fff"] : DEFAULT_COLORS[c];
};
var wrap = (fn) => (...args) => {
const res = fn(...args);
return Array.isArray(res) ? res : [res, false];
};
var color_default = get;
// src/lib/utils/decimal-places.js
var decimal_places_default = (n, precision = DEFAULT_PRECISION) => {
let count = 0;
n = abs(n);
while (to_precision_default(n % 1, precision) > 0 && count < precision) {
count++;
n *= 10;
}
return count;
};
// src/lib/map.js
var defaults = {
value: (v) => v > 0 ? v : 0,
tally: false
};
var recur = (map, data, depth, indices = []) => {
if (Array.isArray(data[0]) && (!depth || indices.length < depth - 1)) {
return data.map((list, i) => recur(map, list, depth, [...indices, i]));
} else {
return data.map((d, i) => Object.fromEntries(Object.entries(map).map(([k, v]) => [k, v(d, ...indices, i)])));
}
};
var Map = function(options, data = [], { minValue = 0, maxDepth, colors = data.length } = {}) {
const map = Object.assign({}, defaults, options);
if (data.length > 0 && Array.isArray(map.y ? map.y(data[0]) : data[0])) {
console.warn("Data should be flattened when constructing a Map");
}
const toValue = map.y || map.value;
const values = data.map(toValue).filter(isFinite);
const places = min(max(...values.map(decimal_places_default)), 2);
if (map.label === void 0 || map.label === true) {
const maxValue = max(...values);
map.label = (v) => (v = toValue(v)) && isFinite(v) && v / maxValue >= minValue && v.toFixed(places);
}
const convert = (data2) => recur(map, data2, maxDepth);
if (map.tally === true) {
map.tally = (v) => (v = toValue(v)) && isFinite(v) && v.toFixed(places);
}
Object.entries(map).forEach(([k, v]) => {
if (Array.isArray(v)) {
map[k] = (d, i) => v[i];
} else if (typeof v !== "function") {
map[k] = () => v;
}
});
const base = map.color || ((v, i) => get(i / (colors - 1)));
map.color = wrap(base);
Object.entries(map).forEach(([k, v]) => {
convert[k] = v;
});
return convert;
};
var map_default = Map;
// src/templates/wrap.js
var wrapper = dom_default.div({
class: "shown"
});
var wrap_default2 = wrapper;
// src/templates/symbol.js
var symbol = (type) => {
let symbol2 = "";
if (type === "circle") {
symbol2 = dom_default.circle({
r: 4.5
});
}
if (type === "square") {
symbol2 = dom_default.path({
d: "M-4-4h8v8h-8Z"
});
}
if (type === "triangle") {
symbol2 = dom_default.path({
d: "M0-5L5,4H-5Z"
});
}
if (type === "diamond") {
symbol2 = dom_default.path({
d: "M0-5.5L5.5,0L0,5.5L-5.5,0Z"
});
}
if (type === "cross") {
symbol2 = dom_default.path({
d: "M-6,0H6M0,-6V6"
});
}
if (type === "asterisk") {
symbol2 = [
dom_default.circle({
r: 6
})(),
dom_default.path({
d: "M-6,0H6M0,-6V6M-4-4L4,4M-4,4L4,-4"
})()
].join("");
}
const hitArea = dom_default.circle({
class: "touch",
r: 15
});
return dom_default.symbol({
class: "symbol shown",
id: `symbol-${type}`,
viewBox: "0 0 10 10"
})([hitArea, symbol2]);
};
var symbol_default = (data) => {
const keys = [
...new Set(data.flat().filter((d) => d.shape).map((d) => d.shape))
];
if (keys && keys.length) {
return keys.map(symbol);
}
};
// src/templates/legend.js
var shape = (type, color, includeLine) => {
let symbol2 = "";
if (type && type !== "none") {
symbol2 = [
includeLine && dom_default.line({
x2: "100%",
y1: "50%",
y2: "50%"
}),
type !== "line" && dom_default.use({
x: "50%",
y: "50%",
width: "1em",
height: "1em",
href: `#symbol-${type}`,
class: "symbol"
})
];
} else {
symbol2 = dom_default.rect({
width: "100%",
height: "100%"
});
}
return dom_default.svg({
role: "presentation",
class: "legend-marker",
style: { color }
})(symbol2);
};
var legend_default = ({
data,
line: includeLine = false,
wrap: includeWrap = false,
defs: includeDefs
} = {}) => {
if (!data)
return;
includeDefs = includeDefs != null ? includeDefs : includeWrap;
const keys = Object.values(data.flat().reduce((m, d) => {
if (d.key && !m[d.key])
m[d.key] = d;
return m;
}, {}));
if (keys && keys.length > 1) {
const res = dom_default.ul({
class: "legend"
})(keys.map((d) => dom_default.li()([
shape(d.shape, Array.isArray(d.color) ? d.color[0] : d.color, includeLine),
dom_default.span()(d.key)
])));
if (includeWrap) {
const defs = includeDefs && symbol_default(data);
return wrap_default2([res, defs && dom_default.svg({ display: "none" })(dom_default.defs()(defs))]);
} else {
return res;
}
} else {
return "";
}
};
// src/templates/pie.js
var arc = (t, r) => percent_default(t % 1 * tau * r);
var getBounds = (t0, t1) => {
const ts = [t0, t1];
if (t0 < -0.5 && t1 > -0.5)
ts.push(-0.5);
if (t0 < -0.25 && t1 > -0.25)
ts.push(-0.25);
if (t0 < 0 && t1 > 0)
ts.push(0);
if (t0 < 0.25 && t1 > 0.25)
ts.push(0.25);
if (t0 < 0.5 && t1 > 0.5)
ts.push(0.5);
const xs = ts.map((t) => to_precision_default(cos(t * tau) / 2));
const ys = ts.map((t) => to_precision_default(sin(t * tau) / 2));
const maxX = max(...xs);
const minX = min(...xs);
const maxY = max(...ys);
const minY = min(...ys);
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
};
var pie_default = ({
data,
title,
description,
sorted = true,
map,
startAngle = 0,
endAngle = 1
}) => {
if (!data || data.length === 0)
return;
map = new map_default(__spreadValues({ width: () => 1 }, map), data, { minValue: 0.05 });
data = map(data);
if (sorted) {
data.sort((a, b) => a.value === b.value ? 0 : a.value < b.value ? 1 : -1);
}
startAngle = (startAngle - 0.25) % 1;
endAngle = (endAngle - 0.25) % 1;
if (startAngle > endAngle)
endAngle += 1;
const bounds = getBounds(startAngle, endAngle);
const total = sum_default(data);
const scale = endAngle - startAngle;
const segments = data.map((d, i) => {
const t = d.value / total * scale;
const o = startAngle + sum_default(data.slice(0, i)) / total * scale;
const radius = (1 - d.width / 2) / 2;
const dashoffset = arc(-o, radius);
const dasharray = [arc(t, radius), arc(1 - t, radius)].join(" ");
const theta = tau * (o + t / 2);
const shift = d.width === 1 && t < 0.25 ? 1.2 : 1;
const x = cos(theta) * shift * radius;
const y = sin(theta) * shift * radius;
return dom_default.g({
"class": `segment segment-${i}`,
"aria-label": `${d.label} (${percent_default(t)})`,
"attrs": d.attrs,
"dominant-baseline": "central"
})([
dom_default.svg({
viewBox: "0 0 100 100"
})(dom_default.circle({
"class": "segment-arc",
"role": "presentation",
"r": percent_default(radius),
"stroke": d.color[0],
"stroke-dasharray": dasharray,
"stroke-dashoffset": dashoffset,
"stroke-width": percent_default(d.width / 2),
"fill": "none"
})),
d.label && dom_default.text({
class: "segment-label",
x: percent_default(x),
y: percent_default(y),
role: "presentation",
color: d.color[1]
})(d.label)
]);
});
return wrap_default2(dom_default.div({
class: "chart chart-pie"
})([
dom_default.div({
class: "chart-pie-wrap",
style: {
"aspect-ratio": +(bounds.w / bounds.h).toFixed(3)
}
})(dom_default.svg({
class: "chart-pie-svg",
xmlns: "http://www.w3.org/2000/svg",
width: +bounds.w.toFixed(3),
height: +bounds.h.toFixed(3)
})([
title && dom_default.title()(title),
description && dom_default.desc()(description),
dom_default.svg({
"x": percent_default(0.5 - (bounds.x + bounds.w / 2) / bounds.w),
"y": percent_default(0.5 - (bounds.y + bounds.h / 2) / bounds.h),
"width": percent_default(1 / bounds.w),
"height": percent_default(1 / bounds.h),
"role": "presentation",
"text-anchor": "middle"
})(segments)
])),
legend_default({ data })
]));
};
// src/templates/axis.js
var axis_exports = {};
__export(axis_exports, {
default: () => axis_default,
setup: () => setup
});
// src/lib/utils/magnitude.js
var magnitude_default = (n) => floor(log10(abs(n)));
// src/templates/axis.js
var TICKCOUNT_ORDER = [5, 6, 7, 8, 4, 9, 3, 10, 11, 12, 13];
var MAX_PRECISION = 7;
var LOW_PRIMES = [7, 5, 3, 2];
var pad = (t, inset = 0) => inset + (1 - inset * 2) * t;
var getScale = (vmin, vmax) => {
let m = max(magnitude_default(vmin), magnitude_default(vmax));
m = min(m, magnitude_default(vmax - vmin));
let scale = pow(10, m);
if (isInteger(vmax) && isInteger(vmin) && vmin > 0 && scale === 1) {
return 1;
}
vmin /= scale;
vmax /= scale;
let d = vmax - vmin;
if (d < 2) {
d *= 10;
scale /= 10;
} else if (d <= 3) {
d *= 5;
scale /= 5;
}
LOW_PRIMES.forEach((p) => {
if (vmin > 0 || vmax < 0 ? d % p === 0 : vmin % p === 0 && vmax % p === 0) {
scale /= p;
}
});
return scale;
};
var getBounds2 = (values) => {
if (values.length === 0)
return [0, 1];
let vmin = to_precision_default(min(...values), MAX_PRECISION);
let vmax = to_precision_default(max(...values), MAX_PRECISION);
if (vmin === vmax) {
if (vmax === 0)
return [0, 1];
if (vmin < 0)
vmax = 0;
else
vmin = 0;
}
const f = getScale(vmin, vmax);
vmin = floor(vmin / f);
vmax = ceil(vmax / f);
const d = vmax - vmin;
if (d % 2 > 0 && !LOW_PRIMES.some((p) => d % p === 0)) {
if (abs(vmax) % 2 === 1) {
vmax += 1;
} else {
vmin -= 1;
}
}
return [
to_precision_default(vmin * f, MAX_PRECISION),
to_precision_default(vmax * f, MAX_PRECISION)
];
};
var getTicks = (vmin, vmax) => {
if (!isFinite(vmin) || !isFinite(vmax))
return 0;
if (vmin === vmax)
return 0;
const f = getScale(vmin, vmax);
vmin = to_precision_default(vmin / f, MAX_PRECISION);
vmax = to_precision_default(vmax / f, MAX_PRECISION);
let d = vmax - vmin;
if (vmin >= 0 || vmax <= 0) {
LOW_PRIMES.forEach((p) => {
if (d > max(10, 10 * f) && d % p === 0) {
d /= p;
}
});
}
const maxDecimals = decimal_places_default(to_precision_default(d, MAX_PRECISION));
return TICKCOUNT_ORDER.find((n) => {
const mod = to_precision_default(d / (n - 1), MAX_PRECISION);
const dec = to_precision_default(mod, MAX_PRECISION);
return decimal_places_default(dec) <= maxDecimals && (vmin > 0 || vmax < 0 || vmin % mod === 0 && vmax % mod === 0);
}) || 2;
};
var setup = (axis = {}, data, guessBounds = true) => {
let { min: vmin, max: vmax, ticks, label, line, spine, inset } = axis;
let bmin, bmax, hasOverflow, width;
const showLabel = !!data || isFinite(axis.min) || isFinite(axis.max);
if (data) {
if (guessBounds) {
const values = [vmin, vmax, ...data].filter(isFinite);
if (values.some((n) => !isInteger(n))) {
values.push(0);
}
;
[bmin, bmax] = getBounds2(values);
} else {
bmin = min(...data.filter(isFinite));
bmax = max(...data.filter(isFinite));
}
} else {
bmin = 0;
bmax = ticks - 1;
}
hasOverflow = bmin < vmin || bmax > vmax;
vmin = vmin != null ? vmin : bmin;
vmax = vmax != null ? vmax : bmax;
ticks = ticks != null ? ticks : getTicks(vmin, vmax);
inset = inset != null ? inset : 0;
if (Array.isArray(label)) {
const arr = label;
label = (v, i) => arr[i];
} else if (label !== void 0 && typeof label !== "function") {
const val = label;
label = () => val;
} else if (showLabel && !label) {
const length = max(abs(vmax), abs(vmin)).toString().length;
label = (v, i) => (ticks < 8 || i % 2 === 0) && abs(v).toString().length <= length && to_precision_default(v, MAX_PRECISION);
}
if (Array.isArray(line)) {
const arr = line;
line = (v, i) => arr[i];
} else if (line !== void 0 && typeof line !== "function") {
const val = line;
line = () => val;
} else if (!line) {
line = () => true;
}
let grid = Array.from({ length: ticks }, (n, i) => i / (ticks - 1));
if (axis.group)
inset = (0.5 + inset) / ticks;
if (vmax === vmin) {
grid = [0.5];
inset = 0.5;
}
const scale = (v) => vmax === vmin ? 0.5 : pad((v - vmin) / (vmax - vmin), inset);
if (label) {
width = max(...grid.map((t) => vmin + t * (vmax - vmin)).map(label).filter((t) => t.length || isFinite(t)).map((t) => t.toString().length));
}
return __spreadProps(__spreadValues({}, axis), {
min: vmin,
max: vmax,
grid,
ticks,
label,
line,
inset,
scale,
spine,
hasOverflow,
width
});
};
var axis_default = (type, axis) => {
const svgProps = type === "x" ? { "width": "100%", "text-anchor": "middle" } : {
"height": "100%",
"text-anchor": "end",
"dominant-baseline": "central"
};
const txtProps = type === "x" ? { y: "100%", dy: "1.5em" } : { x: "-0.5em" };
const line = (t, className, d) => {
const props = { class: className };
const v = t !== 0 && percent_default(t);
if (type === "x") {
props.x1 = v;
props.x2 = v;
props.y2 = percent_default(1);
} else {
props.y1 = v;
props.y2 = v;
props.x2 = percent_default(1);
}
if (d) {
const offset = (type === "x" ? [d, 0] : [0, -d]).join(" ");
props.transform = `translate(${offset})`;
}
return dom_default.line(props);
};
if (axis.hasSeries && type === "x")
txtProps.dy = "3em";
const children = axis.grid.map((t, i) => {
const v = to_precision_default(axis.min + (axis.max - axis.min) * t, MAX_PRECISION);
const lines = [];
if (axis.label) {
const label = axis.label(v, i, axis);
if (label || label === 0)
lines.push(dom_default.text(__spreadValues({ class: "axis-label" }, txtProps))(label));
}
if (axis.line(v, i, axis)) {
if (axis.group) {
if (axis.ticks !== 1) {
const altOffset = (0.5 - axis.inset) / (axis.ticks - 1);
lines.push((axis.inset || t > 0) && line(-altOffset, "axis-line", t === 0 && 0.5), t === 1 && line(altOffset, "axis-line", -0.5));
}
} else {
lines.push(line(0, v == 0 ? "axis-base" : "axis-line", t === 0 ? 0.5 : t === 1 ? -0.5 : 0));
}
}
return dom_default.svg(type === "x" ? { x: percent_default(pad(t, axis.inset)) } : { y: percent_default(pad(1 - t, axis.inset)) })(lines);
});
if (axis.spine) {
if (!axis.line(axis.min, 0, axis) || pad(axis.grid[0], axis.inset) > 0) {
children.unshift(line(0, "axis-spine", 0.5));
}
if (!axis.line(axis.max, axis.ticks - 1, axis) || pad(axis.grid[axis.grid.length - 1], axis.inset) < 1) {
children.push(line(1, "axis-spine", -0.5));
}
}
return dom_default.svg(__spreadValues({
class: ["axis", "axis-" + type]
}, svgProps))(children);
};
// src/templates/bar.js
var bar_default = ({
data,
title,
description,
map,
stack = false,
xAxis,
yAxis
}) => {
data = data.map((v) => Array.isArray(v) ? v : [v]);
if (data.length && !Array.isArray(data[0][0])) {
data = stack ? data.map((d) => [d]) : data.map((d) => d.map((d2) => [d2]));
} else {
stack = true;
}
const maxStack = max(...data.flat(1).map((d) => d.length));
const maxSeries = max(...data.map((d) => d.length));
if (maxStack === 1 && (!map || !map.label)) {
map = __spreadValues({
label: false,
tally: true
}, map);
}
if (Array.isArray(map == null ? void 0 : map.key)) {
const arr = map.key;
map.key = (v, k, j, i) => arr[maxStack > 1 ? i : j];
}
if (Array.isArray(map == null ? void 0 : map.series)) {
const arr = map.series;
map.series = (v, k, j) => arr[j];
}
if (Array.isArray(map == null ? void 0 : map.color)) {
const arr = map.color;
map.color = (v, k, j, i) => arr[maxStack > 1 ? i : j];
}
map = new map_default(__spreadValues({
color: (v, k, j, i) => {
return get(maxStack === 1 ? j / (maxSeries - 1) : i / (maxStack - 1));
},
width: 0.6
}, map), stack ? data.flat().map((d) => sum_default(d)) : data.flat(2), { minValue: 0.05 });
data = map(data);
const maxWidth = max(...data.flat(2).map((d) => d.width));
const values = data.flat().map((d) => sum_default(d));
xAxis = __spreadValues({
ticks: data.length,
hasSeries: data.flat(2).map((d) => d.series).some((f) => f),
group: true,
line: maxSeries > 1
}, xAxis);
yAxis = __spreadValues({
min: 0
}, yAxis);
const axes = {
x: setup(xAxis),
y: setup(yAxis, values)
};
const axisX = axis_default("x", axes.x);
const axisY = axis_default("y", axes.y);
const bars = dom_default.svg({ class: "values" })(data.map((series, k) => dom_default.svg({
x: data.length > 1 && percent_default(axes.x.scale(k - 0.5)),
width: percent_default(data.length > 1 ? axes.x.scale(1) - axes.x.scale(0) : 1),
class: ["group", "group-" + k]
})(series.map((stack2, j) => {
const g = (1 - maxWidth) / (maxSeries + 2);
const w = maxWidth / maxSeries;
const x = g * (j + 1.5) + w * (j + 0.5);
const tally = map.tally(sum_default(stack2));
return dom_default.svg({
class: ["series", "series-" + j],
x: percent_default(x),
width: percent_default(w)
})([
...stack2.map((d, i) => {
if (!d.value)
return;
const w2 = d.width / maxWidth;
const h = axes.y.scale(d.value);
const y = axes.y.scale(axes.y.max - sum_default(stack2.slice(0, i + 1)));
const rect = dom_default.rect({
x: percent_default(-w2 / 2),
y: percent_default(y),
height: percent_default(h),
width: percent_default(w2),
fill: d.color[0]
});
const text = d.label && dom_default.text({
y: percent_default(y + h / 2),
color: d.color[1]
})(d.label);
return dom_default.svg({
"class": ["value", "value-" + i],
"attrs": d.attrs,
"dominant-baseline": "central"
})([rect, text]);
}),
tally && dom_default.text({
y: percent_default(axes.y.scale(axes.y.max - sum_default(stack2))),
dy: "-0.5em"
})(tally),
stack2[0] && stack2[0].series && dom_default.text({
class: "series-label",
y: "100%",
dy: "1.5em"
})(stack2[0].series)
]);
}))));
return wrap_default2(dom_default.div({
class: [
"chart",
"chart-bar",
axes.x.label && "has-xaxis xaxis-w" + axes.x.width,
axes.y.label && "has-yaxis yaxis-w" + axes.y.width,
axes.x.hasSeries && "has-series"
]
})([
dom_default.div({
style: {
"flex-grow": 1,
"height": 0,
"text-anchor": "middle",
"box-sizing": "border-box"
}
})(dom_default.svg({
xmlns: "http://www.w3.org/2000/svg",
width: "100%",
height: "100%"
})([
title && dom_default.title()(title),
description && dom_default.desc()(description),
axisX,
axisY,
bars
])),
legend_default({ data: data.flat() })
]));
};
// src/lib/utils/interpolate.js
var interpolate_default = (values) => {
let count = 0;
return values.reduce((stack, next, i) => {
var _a;
if (isFinite(next) || i === values.length - 1) {
next = next != null ? next : stack.slice(-1)[0];
if (count > 0) {
const prev = (_a = stack.slice(-1)[0]) != null ? _a : next;
const delta = 1 / (count + 1);
const index = stack.length;
while (count > 0) {
const t = delta * count;
const v = prev + (next - prev) * t;
stack.splice(index, 0, v);
count--;
}
}
stack.push(next);
} else {
count++;
}
return stack;
}, []);
};
// src/lib/curve/monotone.js
var TENSION = 1 / 3;
var sign = (v) => v < 0 ? -1 : 1;
var slope = (x0, y0, x1, y1, x2, y2) => {
const h0 = x1 - x0;
const h1 = x2 - x1;
const s0 = (y1 - y0) / (h0 || h1 < 0 && -0);
const s1 = (y2 - y1) / (h1 || h0 < 0 && -0);
const p = (s0 * h1 + s1 * h0) / (h0 + h1);
return (sign(s0) + sign(s1)) * min(abs(s0), abs(s1), abs(p) / 2) || 0;
};
var curve = (x0, y0, x1, y1, s0, s1, alpha) => {
const ratio = abs((y1 - y0) / (x1 - x0));
const scale = clamp(ratio, 1, 1.5);
const delta = (x1 - x0) * alpha * scale;
return ["C", x0 + delta, y0 + delta * s0, x1 - delta, y1 - delta * s1, x1, y1];
};
var monotone_default = (points, alpha = TENSION) => {
const m = [];
let s0, s1;
let p0, p1, p2;
for (let i = 0; i < points.length; i++) {
p0 = p1 || points[i - 1];
p1 = p2 || points[i + 0];
p2 = points[i + 1];
if (!p0 && !p2) {
return m;
}
if (!p0) {
p0 = [p1[0] - (p2[0] - p1[0]) / 2, p1[1] - (p2[1] - p1[1]) / 2];
}
if (!p2) {
p2 = [p1[0] + (p1[0] - p0[0]) / 2, p1[1] + (p1[1] - p0[1]) / 2];
}
const [x0, y0] = p0;
const [x1, y1] = p1;
const [x2, y2] = p2;
s1 = slope(x0, y0, x1, y1, x2, y2);
if (i === 0) {
m.push("M", x1, y1);
} else if (x0 !== x1 || y0 !== y1) {
m.push(...curve(x0, y0, x1, y1, s0, s1, alpha));
}
s0 = s1;
}
return m;
};
// src/lib/curve/bump.js
var TENSION2 = 0.4;
var bump_default = (points, alpha = TENSION2) => points.reduce((m, p1, i) => {
if (i === 0)
return m.concat("M", p1);
else {
const p0 = points[i - 1];
const dx = p1[0] - p0[0];
const dy = p1[1] - p0[1];
if (dx === 0 && dy === 0)
return m;
return m.concat(["c", dx * alpha, 0, dx * (1 - alpha), dy, dx, dy]);
}
}, []);
// src/lib/curve/linear.js
var linear_default = (points) => points.reduce((m, p1, i) => {
if (i > 0) {
const p0 = points[i - 1];
if (p0[0] === p1[0] && p0[1] === p1[1])
return m;
}
return m.concat(i === 0 ? "M" : "L", p1);
}, []);
// src/lib/curve/step.js
var stepX = (points) => points.reduce((m, p1, i) => {
if (i === 0)
return m.concat("M", p1);
else {
const p0 = points[i - 1];
const dx = p1[0] - p0[0];
const dy = p1[1] - p0[1];
if (dx !== 0)
m.push("h", dx);
if (dy !== 0)
m.push("v", dy);
return m;
}
}, []);
var stepY = (points) => points.reduce((m, p1, i) => {
if (i === 0)
return m.concat("M", p1);
else {
const p0 = points[i - 1];
const dx = p1[0] - p0[0];
const dy = p1[1] - p0[1];
if (dy !== 0)
m.push("v", dy);
if (dx !== 0)
m.push("h", dx);
return m;
}
}, []);
var stepMidX = (points) => points.reduce((m, p1, i) => {
if (i === 0)
return m.concat("M", p1);
else {
const p0 = points[i - 1];
const dx = (p1[0] - p0[0]) / 2;
const dy = p1[1] - p0[1];
if (dx !== 0)
m.push("h", dx);
if (dy !== 0)
m.push("v", dy);
if (dx !== 0)
m.push("h", dx);
return m;
}
}, []);
var stepMidY = (points) => points.reduce((m, p1, i) => {
if (i === 0)
return m.concat("M", p1);
else {
const p0 = points[i - 1];
const dx = p1[0] - p0[0];
const dy = (p1[1] - p0[1]) / 2;
if (dy !== 0)
m.push("v", dy);
if (dx !== 0)
m.push("h", dx);
if (dy !== 0)
m.push("v", dy);
return m;
}
}, []);
// src/lib/curve.js
var FIXED = 2;
var finite = (line) => line.filter(([x, y]) => isFinite(x) && isFinite(y));
var toPath = (path, d) => path + (d >= 0 && isFinite(+path.slice(-1)[0]) ? " " : "") + (isFinite(d) ? +d.toFixed(FIXED) : d);
var wrap2 = (curve2) => (line, ...args) => curve2(finite(line), ...args).reduce(toPath, "");
var curve_default = {
linear: wrap2(linear_default),
stepX: wrap2(stepX),
stepY: wrap2(stepY),
stepMidX: wrap2(stepMidX),
stepMidY: wrap2(stepMidY),
monotone: wrap2(monotone_default),
bump: wrap2(bump_default)
};
// src/templates/line.js
var SVGLINE_VIEWPORT_W = 100;
var SVGLINE_VIEWPORT_H = SVGLINE_VIEWPORT_W;
var labelAnchorThreshold = 0.3;
var linePath = (points, xAxis, yAxis, showGaps) => {
let path = points.reduce((m, d) => {
const l = m[m.length - 1];
if (!(isFinite(d.x) && isFinite(d.y))) {
if (showGaps) {
return [...m, { curve: d.curve, points: [] }];
} else {
return m;
}
}
const p = [
SVGLINE_VIEWPORT_W * xAxis.scale(d.x),
SVGLINE_VIEWPORT_H * (1 - yAxis.scale(d.y))
];
if (l)
l.points.push(p);
if (!l || l.curve !== d.curve) {
return [...m, { curve: d.curve, points: [p] }];
}
return m;
}, []).map((l) => {
let args = [];
let path2;
let type = l.curve;
if (Array.isArray(type)) {
;
[type, ...args] = type;
}
path2 = curve_default[type](l.points, ...args);
return path2;
}).join("");
return path;
};
var areaPath = (line1, line2, xAxis, yAxis) => {
const path1 = linePath(line1, xAxis, yAxis, false);
if (!line2) {
return path1 + `L${SVGLINE_VIEWPORT_W},${SVGLINE_VIEWPORT_H}L0,${SVGLINE_VIEWPORT_H}Z`;
}
const mirror = {
stepX: "stepY",
stepY: "stepX"
};
line2 = line2.map((d) => __spreadProps(__spreadValues({}, d), {
curve: mirror[d.curve] || d.curve
})).reverse();
const path2 = linePath(line2, xAxis, yAxis, false);
return path1 + "L" + path2.slice(1) + "Z";
};
var line_default = ({
data,
title,
description,
map,
showGaps = true,
xAxis,
yAxis,
area = false,
scatter = false,
sorted = false,
smartLabels = true
}) => {
data = Array.isArray(data[0]) ? data : [data];
const maxLength = Math.max(...data.map((d) => d.length));
map = new map_default(__spreadValues({
curve: "linear",
shape: false,
label: false,
x: (d, i, j) => {
var _a, _b;
const min2 = (_a = (xAxis || {}).min) != null ? _a : 0;
const max2 = (_b = (xAxis || {}).max) != null ? _b : min2 + (maxLength - 1);
return min2 + j / (maxLength - 1) * (max2 - min2);
},
y: (d) => d
}, map), data.flat(), { minValue: -Infinity, colors: data.length, maxDepth: 2 });
data = map(data);
const hasLines = !scatter && !!data.find((line) => line.find((d) => d.curve));
if (sorted) {
data.sort((al, bl) => {
const a = sum_default(al);
const b = sum_default(bl);
return a === b ? 0 : a > b ? 1 : -1;
});
}
if (area) {
const longest = data.find((line) => line.length === maxLength);
data.forEach((line) => {
var _a, _b;
const curve2 = (_a = line.slice(-1)[0]) == null ? void 0 : _a.curve;
while (line.length < maxLength) {
line.push({
x: (_b = longest[line.length]) == null ? void 0 : _b.x,
y: 0,
curve: curve2
});
}
});
data.slice(1).forEach((next, j) => {
const prev = data[j];
const prevBase = interpolate_default(prev.map((d) => d.y));
const nextBase = interpolate_default(next.map((d) => d.y));
next.forEach((item, i) => {
var _a;
if (isFinite((_a = prev[i]) == null ? void 0 : _a.y) && !isFinite(item.y)) {
item.y = nextBase[i];
}
if (isFinite(item.y)) {
item.y += prevBase[i];
}
});
});
}
const axes = {
x: setup(xAxis, data.flat().map((d) => d.x), scatter),
y: setup(yAxis, data.flat().map((d) => d.y))
};
const axisX = axis_default("x", axes.x);
const axisY = axis_default("y", axes.y);
const viewBox = `0 0 ${SVGLINE_VIEWPORT_W} ${SVGLINE_VIEWPORT_H}`;
const areas = area && dom_default.svg({
class: "areas",
viewBox,
preserveAspectRatio: "none",
style: (axes.x.hasOverflow || axes.y.hasOverflow) && "overflow: hidden;"
})(data.filter((line) => line.length > 0).reverse().map((line, i, arr) => dom_default.path({
"class": ["series", "series-" + i],
"vector-effect": "non-scaling-stroke",
"fill": line[0].color[0],
"d": areaPath(line, arr[i + 1], axes.x, axes.y)
})));
const lines = hasLines && dom_default.svg({
class: "lines",
viewBox,
preserveAspectRatio: "none",
style: (axes.x.hasOverflow || axes.y.hasOverflow) && "overflow: hidden;"
})(data.filter((line) => line.length > 0 && !!line.find((d) => d.curve)).map((line, i) => dom_default.path({
"class": ["series", "series-" + i],
"vector-effect": "non-scaling-stroke",
"stroke": line[0].color[0],
"fill": "none",
"d": linePath(line, axes.x, axes.y, showGaps)
})));
const points = dom_default.svg({
class: "points"
})(data.map((data2, j) => {
var _a;
return dom_default.svg({
"class": ["series", "series-" + j],
"color": (_a = data2[0]) == null ? void 0 : _a.color[0],
"text-anchor": "middle",
"dominant-baseline": "central"
})(data2.map((d, i) => {
if (!isFinite(d.x) || !isFinite(d.y))
return;
let shape2;
let label;
if (d.shape) {
shape2 = dom_default.use({
href: `#symbol-${d.shape}`,
width: "1em",
height: "1em",
class: "symbol",
color: data2[0].color[0] !== d.color[0] && d.color[0]
});
}
if (d.label || d.label === 0) {
let x = axes.x.scale(d.x);
let y = 1 - axes.y.scale(d.y);
let dx = 0;
let dy = -1;
let textAnchor;
const prev = data2[i - 1];
const next = data2[i + 1];
if (smartLabels && (prev || next)) {
let nx = !!next && isFinite(next.x) && axes.x.scale(next.x);
let px = !!prev && isFinite(prev.x) && axes.x.scale(prev.x);
let ny = !!next && isFinite(next.y) && 1 - axes.y.scale(next.y);
let py = !!prev && isFinite(prev.y) && 1 - axes.y.scale(prev.y);
if (nx === false || ny === false) {
nx = x + (x - px);
ny = y + (y - py);
}
if (px === false || py === false) {
px = x - (nx - x);
py = y - (ny - y);
}
const pdx = x - px;
const pdy = y - py;
const ndx = nx - x;
const ndy = ny - y;
const nt = atan2(ndy, ndx);
const pt = atan2(pdy, pdx);
const theta = (nt + pt) / 2 - Math.PI / 2;
dx = Math.cos(theta);
dy = -0.75;
if (Math.abs(dx) > labelAnchorThreshold) {
textAnchor = dx > 0 ? "start" : "end";
dx = labelAnchorThreshold * (dx > 0 ? -1 : 1);
}
}
if (d.r > 1)
dy = dy * d.r;
if (d.shape)
dy -= 0.25;
label = dom_default.text({
"class": "label",
"dx": +dx.toFixed(2) + "em",
"dy": +dy.toFixed(2) + "em",
"text-anchor": textAnchor
})(d.label);
}
if (shape2 || label) {
return dom_default.svg({
class: "point",
x: percent_default(axes.x.scale(d.x)),
y: percent_default(1 - axes.y.scale(d.y)),
attrs: d.attrs
})([shape2, label]);
}
}));
}));
const defs = symbol_default(data);
return wrap_default2(dom_default.div({
class: [
"chart",
area ? "chart-area" : scatter ? "chart-scatter" : "chart-line",
axes.x.label && "has-xaxis xaxis-w" + axes.x.width,
axes.y.label && "has-yaxis yaxis-w" + axes.y.width
]
})([
dom_default.div({
style: {
"flex-grow": 1,
"height": 0,
"box-sizing": "border-box"
}
})(dom_default.svg({
xmlns: "http://www.w3.org/2000/svg",
width: "100%",
height: "100%"
})([
defs && dom_default.defs()(defs),
title && dom_default.title()(title),
description && dom_default.desc()(description),
axisY,
axisX,
areas,
lines,
points
])),
legend_default({ data, line: true })
]));
};
// src/templates/area.js
var area_default = ({
data,
title,
description,
map,
xAxis,
yAxis,
sorted,
smartLabels
}) => {
return line_default({
data,
title,
description,
map,
xAxis,
yAxis,
sorted,
smartLabels,
showGaps: true,
area: true
});
};
// src/templates/scatter.js
var scatter_default = ({
data,
title,
description,
map,
xAxis,
yAxis,
sorted,
smartLabels = false
}) => {
data = Array.isArray(data[0][0]) ? data : [data];
map = __spreadValues({
shape: "circle",
curve: false,
x: (d) => d[0],
y: (d) => d[1],
r: 1
}, map);
return line_default({
data,
title,
description,
map,
xAxis,
yAxis,
sorted,
smartLabels,
scatter: true
});
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
area,
axis,
bar,
color,
legend,
line,
map,
pie,
scatter
});