UNPKG

myd3g

Version:

just simple D3js graoh plugin

340 lines (331 loc) 18 kB
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 });