myd3g
Version:
just simple D3js graoh plugin
340 lines (331 loc) • 18 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
Barchart: () => Barchart_default,
BrushableScatterplot: () => BrushableScatterplot_default,
LineChart: () => Linechart_default,
MultiIndexChart: () => MultiIndexChart_default,
PannableChart: () => PannableChart_default,
ScatterplotMatrix: () => ScatterplotMatrix_default,
SunburstChart: () => SunburstChart_default
});
module.exports = __toCommonJS(index_exports);
// src/graphs/Barchart.tsx
var import_react = require("react");
var d3 = __toESM(require("d3"));
var import_jsx_runtime = require("react/jsx-runtime");
function Barchart({ data, width = 928, height = 500, marginTop = 20, marginRight = 0, marginBottom = 30, marginLeft = 40, className }) {
const chartRef = (0, import_react.useRef)(null);
(0, import_react.useEffect)(() => {
if (!data || data.length === 0) return;
d3.select(chartRef.current).selectAll("*").remove();
const svg = d3.select(chartRef.current).attr("viewBox", [0, 0, width, height]).attr("width", width).attr("height", height).attr("style", "max-width: 100%; height: auto;");
const x = d3.scaleBand().domain(data.map((d) => d.x)).range([marginLeft, width - marginRight]).padding(0.1);
const y = d3.scaleLinear().domain([0, d3.max(data, (d) => d.y) || 0]).nice().range([height - marginBottom, marginTop]);
const xAxis = d3.axisBottom(x).tickSizeOuter(0);
const yAxis = d3.axisLeft(y);
svg.append("g").attr("fill", "steelblue").selectAll("rect").data(data).join("rect").attr("x", (d) => x(d.x) ?? 0).attr("y", (d) => y(d.y)).attr("height", (d) => {
const height2 = y(0) - y(d.y);
return isNaN(height2) ? 0 : height2;
}).attr("width", x.bandwidth());
svg.append("g").attr("transform", `translate(0,${height - marginBottom})`).call(xAxis);
svg.append("g").attr("transform", `translate(${marginLeft},0)`).call(yAxis).call((g) => g.select(".domain").remove());
function zoom3(svg2) {
const extent4 = [[marginLeft, marginTop], [width - marginRight, height - marginTop]];
svg2.call(d3.zoom().scaleExtent([1, 8]).translateExtent(extent4).extent(extent4).on("zoom", zoomed));
function zoomed(event) {
x.range([marginLeft, width - marginRight].map((d) => event.transform.applyX(d)));
svg2.selectAll("rect").attr("x", (d) => x(d.x)).attr("width", x.bandwidth());
svg2.selectAll("g.x-axis").call(xAxis);
}
}
zoom3(svg);
}, [data]);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, ref: chartRef }) });
}
var Barchart_default = Barchart;
// src/graphs/BrushableScatterplot.tsx
var import_react2 = require("react");
var d32 = __toESM(require("d3"));
var import_jsx_runtime2 = require("react/jsx-runtime");
var BrushableScatterplot = ({
data,
width = 600,
height = 400,
className,
style,
xDomainStart,
xDomainEnd,
yDomainStart,
yDomainEnd,
speciesValues,
colors
}) => {
const svgRef = (0, import_react2.useRef)(null);
const [selectedPoints, setSelectedPoints] = (0, import_react2.useState)([]);
const [speciesValue, setSpeciesValue] = (0, import_react2.useState)([]);
const [colorScaleState, setColorScaleState] = (0, import_react2.useState)([]);
(0, import_react2.useCallback)(() => {
setSpeciesValue(speciesValues);
setColorScaleState(colors);
}, []);
(0, import_react2.useEffect)(() => {
if (!svgRef.current) return;
const svg = d32.select(svgRef.current);
svg.selectAll("*").remove();
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
const plotWidth = width - margin.left - margin.right;
const plotHeight = height - margin.top - margin.bottom;
const xScale = d32.scaleLinear().domain([xDomainStart, xDomainEnd]).range([0, plotWidth]);
const yScale = d32.scaleLinear().domain([yDomainStart, yDomainEnd]).range([plotHeight, 0]);
const chart = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
chart.append("g").attr("transform", `translate(0,${plotHeight})`).call(d32.axisBottom(xScale));
chart.append("g").call(d32.axisLeft(yScale));
const colorScale = d32.scaleOrdinal().domain(speciesValue).range(colorScaleState);
const points = chart.selectAll("circle").data(data).enter().append("circle").attr("cx", (d) => xScale(d.x)).attr("cy", (d) => yScale(d.y)).attr("r", 6).attr("fill", (d) => colorScale(d == null ? void 0 : d.species)).attr("stroke", "black").attr("class", "dot");
const brush3 = d32.brush().extent([
[0, 0],
[plotWidth, plotHeight]
]).on("end", (event) => {
if (!event.selection) return;
const [[x0, y0], [x1, y1]] = event.selection;
const brushedData = data.filter(
(d) => xScale(d.x) >= x0 && xScale(d.x) <= x1 && yScale(d.y) >= y0 && yScale(d.y) <= y1 && d.species !== void 0
);
setSelectedPoints(brushedData);
points.attr(
"fill",
(d) => brushedData.some((point) => point.x === d.x && point.y === d.y && (point == null ? void 0 : point.species) === (d == null ? void 0 : d.species)) ? "orange" : colorScale(d == null ? void 0 : d.species)
);
});
chart.append("g").attr("class", "brush").call(brush3);
}, [width, height]);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className, ref: svgRef, width, height, style: { border: "1px solid black", ...style } }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { children: [
"Selected Points: ",
selectedPoints.length
] }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: selectedPoints.map((point, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: `Sepal Length: ${point.x}, Sepal Width: ${point.y}, Species: ${point == null ? void 0 : point.species}` }, index)) })
] });
};
var BrushableScatterplot_default = BrushableScatterplot;
// src/graphs/MultiIndexChart.tsx
var import_react3 = require("react");
var d33 = __toESM(require("d3"));
var import_jsx_runtime3 = require("react/jsx-runtime");
function MultiIndexChart({ data, width = 700, height = 400, margin, className, style }) {
const svgRef = (0, import_react3.useRef)(null);
(0, import_react3.useEffect)(() => {
if (!data || Object.keys(data).length === 0) return;
margin = { top: 30, right: 60, bottom: 40, left: 50 };
const svg = d33.select(svgRef.current);
svg.selectAll("*").remove();
const stocks = Object.keys(data);
const indexedData = stocks.map((stock) => ({
name: stock,
values: data[stock].map((d) => ({
x: new Date(d.x),
y: d.y / data[stock][0].y
// Normalize values
}))
}));
const xScale = d33.scaleUtc().domain(d33.extent(indexedData[0].values, (d) => d.x).map((d) => d ?? /* @__PURE__ */ new Date())).range([margin.left, width - margin.right]).clamp(true);
const yScale = d33.scaleLinear().domain([
d33.min(indexedData.flatMap((stock) => stock.values.map((d) => d.y))) - 0.2,
d33.max(indexedData.flatMap((stock) => stock.values.map((d) => d.y))) + 0.2
]).rangeRound([height - margin.bottom, margin.top]);
const colorScale = d33.scaleOrdinal(d33.schemeCategory10).domain(stocks);
const line3 = d33.line().x((d) => xScale(d.x)).y((d) => yScale(d.y)).curve(d33.curveMonotoneX);
svg.append("g").attr("transform", `translate(0,${height - margin.bottom})`).call(d33.axisBottom(xScale).ticks(6));
svg.append("g").attr("transform", `translate(${margin.left},0)`).call(d33.axisLeft(yScale));
indexedData.forEach((stock) => {
svg.append("path").datum(stock.values).attr("fill", "none").attr("stroke", colorScale(stock.name)).attr("stroke-width", 2).attr("d", line3);
svg.append("text").attr("x", xScale(stock.values[stock.values.length - 1].x) + 5).attr("y", yScale(stock.values[stock.values.length - 1].y)).attr("fill", String(colorScale(stock.name))).attr("font-size", "12px").text(stock.name);
});
}, [data]);
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className, ref: svgRef, width, height, style: { ...style } });
}
var MultiIndexChart_default = MultiIndexChart;
// src/graphs/PannableChart.tsx
var import_react4 = require("react");
var d34 = __toESM(require("d3"));
var import_jsx_runtime4 = require("react/jsx-runtime");
function PannableChart({ data, margin, width, height, className, style }) {
const svgRef = (0, import_react4.useRef)(null);
(0, import_react4.useEffect)(() => {
margin = { top: 20, right: 30, bottom: 30, left: 40 };
width = 600 - margin.left - margin.right;
height = 300 - margin.top - margin.bottom;
if (!svgRef.current) return;
const svg = d34.select(svgRef.current);
svg.selectAll("*").remove();
const chart = svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", `translate(${margin.left},${margin.top})`);
const xScale = d34.scaleUtc().domain([0, d34.max(data, (d) => d.x)]).range([0, width]);
const yScale = d34.scaleLinear().domain([0, d34.max(data, (d) => d.y)]).range([height, 0]);
const line3 = d34.area().x((d) => xScale(d.x)).y1((d) => yScale(d.y)).y0(yScale(0)).curve(d34.curveStep);
const path = chart.append("path").datum(data).attr("fill", "steelblue").attr("stroke", "steelblue").attr("stroke-width", 2).attr("d", line3);
const xAxis = d34.axisBottom(xScale);
const xAxisGroup = chart.append("g").attr("transform", `translate(0,${height})`).call(xAxis);
const yAxis = d34.axisLeft(yScale);
chart.append("g").call(yAxis);
const zoom3 = d34.zoom().scaleExtent([1, 1]).translateExtent([
[-width, 0],
[2 * width, height]
]).on("zoom", (event) => {
const { transform } = event;
chart.attr("transform", transform);
});
svg.call(zoom3);
}, []);
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { className, ref: svgRef, style: { border: "1px solid #ccc", ...style } });
}
var PannableChart_default = PannableChart;
// src/graphs/ScatterplotMatrix.tsx
var import_react5 = require("react");
var d35 = __toESM(require("d3"));
var import_jsx_runtime5 = require("react/jsx-runtime");
function ScatterplotMatrix({ data, margin, size, padding, className, style }) {
const ref = (0, import_react5.useRef)(null);
(0, import_react5.useEffect)(() => {
if (!data || data.length === 0) return;
margin = { top: 20, right: 20, bottom: 20, left: 20 };
size = 150;
padding = 20;
const columns = Object.keys(data[0]);
const numCols = columns.length;
const color = d35.scaleOrdinal().domain(data.map((_, i) => i.toString())).range(d35.schemeCategory10);
const svg = d35.select(ref.current).attr("width", size * numCols + margin.left + margin.right).attr("height", size * numCols + margin.top + margin.bottom).append("g").attr("transform", `translate(${margin.left},${margin.top})`);
const scales = {};
columns.forEach((col) => {
scales[col] = d35.scaleLinear().domain(d35.extent(data, (d) => d[col])).range([padding, size - padding]);
});
function brushed(event) {
const selection = event.selection;
if (!selection) return;
const [[x0, y0], [x1, y1]] = selection;
d35.selectAll("circle").classed("selected", (d) => {
const x = scales[d.xVar](d[d.xVar]);
const y = scales[d.yVar](d[d.yVar]);
return x >= x0 && x <= x1 && y >= y0 && y <= y1;
});
}
columns.forEach((xVar, i) => {
columns.forEach((yVar, j) => {
const g = svg.append("g").attr("transform", `translate(${i * size},${j * size})`);
g.append("rect").attr("fill", "none").attr("stroke", "#ddd").attr("width", size).attr("height", size);
const brush3 = d35.brush().extent([[0, 0], [size, size]]).on("brush", brushed);
g.append("g").attr("class", "brush").call(brush3);
g.selectAll("circle").data(data).enter().append("circle").attr("class", "circle").attr("cx", (d) => scales[xVar](d[xVar])).attr("cy", (d) => scales[yVar](d[yVar])).attr("r", 3).attr("fill", "steelblue").attr("opacity", 0.7).attr("fill", (d) => color(d.C));
});
});
}, []);
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { className, style: { ...style }, ref });
}
var ScatterplotMatrix_default = ScatterplotMatrix;
// src/graphs/SunburstChart.tsx
var import_react6 = require("react");
var d36 = __toESM(require("d3"));
var import_jsx_runtime6 = require("react/jsx-runtime");
function SunburstChart({ data, width = 600, height = 600, className, style, text }) {
const ref = (0, import_react6.useRef)(null);
const [hoveredData, setHoveredData] = (0, import_react6.useState)({ sequence: [], percentage: 0 });
text = "Hover on chart";
(0, import_react6.useEffect)(() => {
if (!data) return;
const radius = width / 2;
const partition2 = (data2) => d36.partition().size([2 * Math.PI, radius])(
d36.hierarchy(data2).sum((d) => d.value).sort((a, b) => (b.value || 0) - (a.value || 0))
);
const root = partition2(data);
const color = d36.scaleOrdinal(d36.quantize(d36.interpolateRainbow, data.children.length + 1));
const arc2 = d36.arc().startAngle((d) => d.x0).endAngle((d) => d.x1).innerRadius((d) => d.y0).outerRadius((d) => d.y1);
const svg = d36.select(ref.current).attr("viewBox", [-width / 2, -height / 2, width, height]).style("font", "12px sans-serif");
svg.selectAll("*").remove();
svg.node();
const g = svg.append("g");
g.selectAll("path").data(root.descendants().filter((d) => d.depth)).enter().append("path").attr("fill", (d) => color(d.ancestors().map((d2) => d2.data.name).join("/"))).attr("d", arc2).style("cursor", "pointer").on("mouseenter", function(e, d) {
d36.select(this).attr("opacity", 0.7);
setHoveredData({
sequence: d.ancestors().map((d2) => d2.data.name),
percentage: (d.value || 0) / (root.value || 0) * 100
});
}).on("mouseleave", function() {
d36.select(this).attr("opacity", 1);
setHoveredData({ sequence: [], percentage: 0 });
});
svg.append("text").attr("text-anchor", "middle").attr("dy", "-0.5em").attr("font-size", "16px").attr("font-weight", "bold").text(`Sunburst Chart`);
}, [width, height]);
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative flex flex-col items-center mt-4", children: [
hoveredData.percentage > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-black rounded", children: [
hoveredData.percentage.toFixed(2),
"% of visits begin with this sequence."
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: text }),
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className, style: { ...style }, ref, width, height })
] });
}
var SunburstChart_default = SunburstChart;
// src/graphs/Linechart.tsx
var import_react7 = require("react");
var d37 = __toESM(require("d3"));
var import_jsx_runtime7 = require("react/jsx-runtime");
var LineChart = ({
data,
margin,
width,
height,
className,
style
}) => {
const svgRef = (0, import_react7.useRef)(null);
(0, import_react7.useEffect)(() => {
if (!data || data.length === 0) return;
margin = { top: 20, right: 30, bottom: 30, left: 40 }, width = 600 - margin.left - margin.right, height = 400 - margin.top - margin.bottom;
const svg = d37.select(svgRef.current).attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", `translate(${margin.left},${margin.top})`);
const x = d37.scaleLinear().domain(d37.extent(data, (d) => d.x)).range([0, width]);
const y = d37.scaleLinear().domain([0, d37.max(data, (d) => d.y)]).range([height, 0]);
const line3 = d37.line().x((d) => x(d.x)).y((d) => y(d.y)).curve(d37.curveMonotoneX);
svg.append("g").attr("transform", `translate(0,${height})`).call(d37.axisBottom(x));
svg.append("g").call(d37.axisLeft(y));
svg.append("path").datum(data).attr("fill", "none").attr("stroke", "steelblue").attr("stroke-width", 2).attr("d", line3);
}, [data]);
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("svg", { className, style: { ...style }, ref: svgRef });
};
var Linechart_default = LineChart;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Barchart,
BrushableScatterplot,
LineChart,
MultiIndexChart,
PannableChart,
ScatterplotMatrix,
SunburstChart
});