Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
eae1bd1
feat(charts): add D3-based AreaChart component (ChartsV2)
ankit-thesys Mar 6, 2026
7109407
refactor(ChartsV2): extract hooks, improve layout, and fix fixed-dime…
ankit-thesys Mar 6, 2026
4eb2c7d
css fix
ankit-thesys Mar 6, 2026
6d1c4f4
chore: update eslint config and remove unused tooltip files
ankit-thesys Mar 7, 2026
59da036
refactor(ChartsV2): extract shared chart infrastructure
ankit-thesys Mar 7, 2026
b845ce5
refactor(ChartsV2): decompose useChartOrchestration into focused hooks
ankit-thesys Mar 7, 2026
c08e2fe
refactor(ChartsV2): update D3AreaChart to use shared infrastructure
ankit-thesys Mar 7, 2026
98981ef
feat(ChartsV2): add D3BarChart component
ankit-thesys Mar 7, 2026
9aca022
feat(ChartsV2): add D3LineChart component
ankit-thesys Mar 7, 2026
f8c8824
feat(ChartsV2): export D3BarChart and D3LineChart from package
ankit-thesys Mar 7, 2026
3f5897b
fix(ChartsV2): resolve rendering issues in D3AreaChart
ankit-thesys Mar 7, 2026
2fbfbe1
refactor(ChartsV2): add condensed orchestrator and supporting hooks
ankit-thesys Mar 7, 2026
008f7e9
feat(ChartsV2): add shared utils and AngledXAxis for condensed mode
ankit-thesys Mar 7, 2026
e288bd0
refactor(ChartsV2): split charts into Scrollable and Condensed variants
ankit-thesys Mar 7, 2026
8268139
docs(ChartsV2): update stories and docs for condensed variant support
ankit-thesys Mar 7, 2026
b097a20
refactor(ChartsV2): group orchestrator returns and add density prop
ankit-thesys Mar 7, 2026
9ae2e74
refactor(ChartsV2): extract shared layout components and add empty st…
ankit-thesys Mar 7, 2026
01b3a8e
refactor(ChartsV2): reorganize hooks into core/cartesian/polar layers
ankit-thesys Mar 8, 2026
32f7c97
docs(ChartsV2): update README for hooks reorganization and polar charts
ankit-thesys Mar 8, 2026
3c15e9e
feat(ChartsV2): add D3RadarChart component
ankit-thesys Mar 8, 2026
5954240
feat(ChartsV2): add D3ScatterChart component
ankit-thesys Mar 8, 2026
9cac228
Merge branch 'main' of https://github.com/ankit-thesys/openui-comp in…
ankit-thesys Mar 17, 2026
45fdd65
refactor(ChartsV2): fix SSR, align scatter chart, restructure shared …
ankit-thesys Mar 19, 2026
bb069c3
chore: update gitignore for AI-generated docs and planning files
ankit-thesys Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
Claude.md
.claude/
CLAUDE.local.md
ARCHITECTURE.md
TECH-DEBT.md

# Dependencies
node_modules
Expand Down
3 changes: 3 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
/build
*.tsbuildinfo

# Ai Planning
/adr

# misc
.DS_Store
*.pem
Expand Down
1 change: 1 addition & 0 deletions eslint.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ module.exports = [
allow: ["error", "warn", "info"],
},
],
"no-restricted-imports": "off",
...eslintPluginPrettier.configs.recommended.rules,
"react-hooks/exhaustive-deps": "warn",
},
Expand Down
8 changes: 8 additions & 0 deletions packages/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
"@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.2.7",
"clsx": "^2.1.1",
"d3": "^7.9.0",
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-shape": "^3.2.0",
"date-fns": "^4.1.0",
"lodash-es": "^4.17.21",
"lucide-react": "^0.562.0",
Expand Down Expand Up @@ -113,6 +117,10 @@
"@storybook/react-vite": "^8.5.3",
"@storybook/test": "^8.5.3",
"@storybook/theming": "^8.5.3",
"@types/d3": "^7.4.3",
"@types/d3-scale": "^4.0.9",
"@types/d3-selection": "^3.0.11",
"@types/d3-shape": "^3.1.8",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.12.0",
"@types/node-fetch": "2.6.11",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import clsx from "clsx";

import { D3AreaChartCondensed } from "./D3AreaChartCondensed";
import { D3AreaChartScrollable } from "./D3AreaChartScrollable";

import type { D3AreaChartData, D3AreaChartProps } from "./types";

export function D3AreaChart<T extends D3AreaChartData>(props: D3AreaChartProps<T>) {
if (!props.data || props.data.length === 0) {
return (
<div
className={clsx("openui-d3-area-chart-container openui-d3-chart-empty", props.className)}
>
<span className="openui-d3-chart-empty-text">No data available</span>
</div>
);
}
if (props.condensed) {
return <D3AreaChartCondensed {...props} />;
}
return <D3AreaChartScrollable {...props} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { useCallback } from "react";

import {
useChartCondensedOrchestrator,
usePrintContext,
useStackedData,
useXScale,
useYScale,
} from "../hooks";
import { AngledXAxis } from "../shared/cartesian/axes/AngledXAxis";
import { CondensedChartLayout } from "../shared/cartesian/layouts/CondensedChartLayout";
import { LineDotCrosshair } from "../shared/cartesian/LineDotCrosshair";
import { findNearestDataIndex } from "../utils/mouseUtils";
import { AreaSeries } from "./parts/AreaSeries";
import { GradientDefs } from "./parts/GradientDefs";

import type { D3AreaChartData, D3AreaChartProps } from "./types";

export function D3AreaChartCondensed<T extends D3AreaChartData>({
data,
categoryKey,
theme: chartThemeName = "ocean",
customPalette,
variant = "natural",
stacked = true,
grid = true,
legend: showLegend = true,
icons,
isAnimationActive = false,
showYAxis = true,
xAxisLabel,
yAxisLabel,
className,
height,
width: fixedWidth,
fitLegendInHeight,
onClick,
}: D3AreaChartProps<T>) {
const isPrinting = usePrintContext();

const orch = useChartCondensedOrchestrator({
data,
categoryKey,
chartThemeName,
customPalette,
showLegend,
showYAxis,
height,
fixedWidth,
fitLegendInHeight,
chartIdPrefix: "d3ac",
icons,
onClick,
});

const xScale = useXScale(
data,
orch.data.catKey,
orch.dimensions.chartAreaWidth,
orch.dimensions.widthPerDataPoint,
);
const stackedData = useStackedData(data, orch.data.dataKeys, stacked);
const yScale = useYScale(data, orch.data.dataKeys, orch.dimensions.chartInnerHeight, stackedData);

const getYValue = useCallback(
(_row: Record<string, string | number>, key: string, seriesIndex: number) => {
if (stackedData && orch.hover.hoveredIndex !== null) {
const series = stackedData[seriesIndex];
const point = series?.[orch.hover.hoveredIndex];
return point ? point[1] : 0;
}
return Number(_row[key]) || 0;
},
[stackedData, orch.hover.hoveredIndex],
);

const findIndex = useCallback((mouseX: number) => findNearestDataIndex(xScale, mouseX), [xScale]);
const mouseHandlers = orch.hover.createMouseHandlers(findIndex);

return (
<CondensedChartLayout
orch={orch}
yScale={yScale}
mouseHandlers={mouseHandlers}
classPrefix="area-chart"
chartType="area"
ariaLabel="Area chart"
showYAxis={showYAxis}
grid={grid}
showLegend={showLegend}
xAxisLabel={xAxisLabel}
yAxisLabel={yAxisLabel}
className={className}
defs={
<GradientDefs
dataKeys={orch.data.dataKeys}
transformedKeys={orch.data.transformedKeys}
colors={orch.data.colorMap}
chartId={orch.identity.chartId}
chartWidth={orch.dimensions.chartAreaWidth}
chartHeight={orch.dimensions.chartInnerHeight}
/>
}
series={
<>
<AreaSeries
data={data}
dataKeys={orch.data.dataKeys}
xScale={xScale}
yScale={yScale}
variant={variant}
stackedData={stackedData}
categoryKey={orch.data.catKey}
transformedKeys={orch.data.transformedKeys}
colors={orch.data.colorMap}
chartId={orch.identity.chartId}
isAnimationActive={isAnimationActive && !isPrinting}
/>
<LineDotCrosshair
hoveredIndex={orch.hover.hoveredIndex}
xScale={xScale}
yScale={yScale}
data={data}
dataKeys={orch.data.dataKeys}
categoryKey={orch.data.catKey}
colors={orch.data.colorMap}
chartHeight={orch.dimensions.chartInnerHeight}
getYValue={getYValue}
classPrefix="openui-d3-area-chart"
/>
</>
}
xAxis={
<AngledXAxis
scale={xScale}
angle={orch.xAxis.angle}
xAxisHeight={orch.xAxis.xAxisHeight}
classPrefix="openui-d3-area-chart"
/>
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useCallback } from "react";

import {
useChartScrollableOrchestrator,
usePrintContext,
useStackedData,
useXScale,
useYScale,
} from "../hooks";
import { LineDotCrosshair } from "../shared/cartesian/LineDotCrosshair";
import { XAxis } from "../shared/cartesian/axes/XAxis";
import { ScrollableChartLayout } from "../shared/cartesian/layouts/ScrollableChartLayout";
import { findNearestDataIndex } from "../utils/mouseUtils";
import { AreaSeries } from "./parts/AreaSeries";
import { GradientDefs } from "./parts/GradientDefs";

import type { D3AreaChartData, D3AreaChartProps } from "./types";

export function D3AreaChartScrollable<T extends D3AreaChartData>({
data,
categoryKey,
theme: chartThemeName = "ocean",
customPalette,
variant = "natural",
tickVariant: tickVariantProp = "multiLine",
stacked = true,
grid = true,
legend: showLegend = true,
icons,
isAnimationActive = false,
showYAxis = true,
xAxisLabel,
yAxisLabel,
className,
height,
width: fixedWidth,
fitLegendInHeight,
condensed = false,
density,
onClick,
}: D3AreaChartProps<T>) {
const isPrinting = usePrintContext();

const orch = useChartScrollableOrchestrator({
data,
categoryKey,
chartThemeName,
customPalette,
showLegend,
showYAxis,
height,
fixedWidth,
fitLegendInHeight,
tickVariantProp,
chartIdPrefix: "d3ac",
icons,
onClick,
condensed,
density,
});

const xScale = useXScale(
data,
orch.data.catKey,
orch.dimensions.svgWidth,
orch.dimensions.widthOfGroup,
);
const stackedData = useStackedData(data, orch.data.dataKeys, stacked);
const yScale = useYScale(data, orch.data.dataKeys, orch.dimensions.chartInnerHeight, stackedData);

const getYValue = useCallback(
(_row: Record<string, string | number>, key: string, seriesIndex: number) => {
if (stackedData && orch.hover.hoveredIndex !== null) {
const series = stackedData[seriesIndex];
const point = series?.[orch.hover.hoveredIndex];
return point ? point[1] : 0;
}
return Number(_row[key]) || 0;
},
[stackedData, orch.hover.hoveredIndex],
);

const findIndex = useCallback((mouseX: number) => findNearestDataIndex(xScale, mouseX), [xScale]);
const mouseHandlers = orch.hover.createMouseHandlers(findIndex);

return (
<ScrollableChartLayout
orch={orch}
yScale={yScale}
mouseHandlers={mouseHandlers}
classPrefix="area-chart"
chartType="area"
ariaLabel="Area chart"
showYAxis={showYAxis}
grid={grid}
showLegend={showLegend}
xAxisLabel={xAxisLabel}
yAxisLabel={yAxisLabel}
className={className}
defs={
<GradientDefs
dataKeys={orch.data.dataKeys}
transformedKeys={orch.data.transformedKeys}
colors={orch.data.colorMap}
chartId={orch.identity.chartId}
chartWidth={orch.dimensions.svgWidth}
chartHeight={orch.dimensions.chartInnerHeight}
/>
}
series={
<>
<AreaSeries
data={data}
dataKeys={orch.data.dataKeys}
xScale={xScale}
yScale={yScale}
variant={variant}
stackedData={stackedData}
categoryKey={orch.data.catKey}
transformedKeys={orch.data.transformedKeys}
colors={orch.data.colorMap}
chartId={orch.identity.chartId}
isAnimationActive={isAnimationActive && !isPrinting}
/>
<LineDotCrosshair
hoveredIndex={orch.hover.hoveredIndex}
xScale={xScale}
yScale={yScale}
data={data}
dataKeys={orch.data.dataKeys}
categoryKey={orch.data.catKey}
colors={orch.data.colorMap}
chartHeight={orch.dimensions.chartInnerHeight}
getYValue={getYValue}
classPrefix="openui-d3-area-chart"
/>
</>
}
xAxis={
<XAxis
scale={xScale}
data={data}
categoryKey={orch.data.catKey}
tickVariant={orch.dimensions.tickVariant}
widthOfGroup={orch.dimensions.widthOfGroup}
labelHeight={orch.dimensions.xAxisHeight}
labelInterval={orch.dimensions.labelInterval}
classPrefix="openui-d3-area-chart"
/>
}
/>
);
}
Loading