UNPKG

shown

Version:

Statically-generated, responsive charts, without the need for client-side Javascript.

1,742 lines (1,709 loc) 43.2 kB
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 });