Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tailwind to wave, support shadcn + donut chart as a POC #1775

Merged
merged 25 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
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 .storybook/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ body {
background-color: orange;
}
}

@import "../frontend/tailwindsetup.css";
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@
},
"[md]": {
"editor.wordWrap": "on"
},
"files.associations": {
"*.css": "tailwindcss"
}
}
15 changes: 11 additions & 4 deletions electron.vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react-swc";
import { defineConfig } from "electron-vite";
import flow from "rollup-plugin-flow";
import { ViteImageOptimizer } from "vite-plugin-image-optimizer";
import { viteStaticCopy } from "vite-plugin-static-copy";
import svgr from "vite-plugin-svgr";
Expand All @@ -20,7 +20,7 @@ export default defineConfig({
},
outDir: "dist/main",
},
plugins: [tsconfigPaths(), flow()],
plugins: [tsconfigPaths()],
resolve: {
alias: {
"@": "frontend",
Expand All @@ -46,7 +46,7 @@ export default defineConfig({
},
outDir: "dist/preload",
},
plugins: [tsconfigPaths(), flow()],
plugins: [tsconfigPaths()],
},
renderer: {
root: ".",
Expand All @@ -63,6 +63,13 @@ export default defineConfig({
server: {
open: false,
},
css: {
preprocessorOptions: {
scss: {
silenceDeprecations: ["mixed-decls"],
},
},
},
plugins: [
ViteImageOptimizer(),
tsconfigPaths(),
Expand All @@ -71,7 +78,7 @@ export default defineConfig({
include: "**/*.svg",
}),
react({}),
flow(),
tailwindcss(),
viteStaticCopy({
targets: [{ src: "node_modules/monaco-editor/min/vs/*", dest: "monaco" }],
}),
Expand Down
3 changes: 3 additions & 0 deletions frontend/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ import { NotificationBubbles } from "./notification/notificationbubbles";

import "./app.scss";

// this should come after app.scss (don't remove the newline above otherwise prettier will reorder these imports)
import "../tailwindsetup.css";

const dlog = debug("wave:app");
const focusLog = debug("wave:focus");

Expand Down
73 changes: 73 additions & 0 deletions frontend/app/element/donutchart.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { Meta, StoryObj } from "@storybook/react";
import DonutChart from "./donutchart";

const meta = {
title: "Components/DonutChart",
component: DonutChart,
parameters: {
layout: "centered",
docs: {
description: {
component:
"The `DonutChart` component displays data in a donut-style chart with customizable colors, labels, and tooltip. Useful for visualizing proportions or percentages.",
},
},
},
argTypes: {
data: {
description:
"The data for the chart, where each item includes `label`, `value`, and optional `displayvalue`.",
control: { type: "object" },
},
config: {
description: "config for the chart",
control: { type: "object" },
},
innerLabel: {
description: "The label displayed inside the donut chart (e.g., percentages).",
control: { type: "text" },
},
},
decorators: [
(Story) => (
<div
style={{
width: "200px",
height: "200px",
display: "flex",
justifyContent: "center",
alignItems: "center",
border: "1px solid #ddd",
}}
>
<Story />
</div>
),
],
} satisfies Meta<typeof DonutChart>;

export default meta;
type Story = StoryObj<typeof DonutChart>;

export const Default: Story = {
args: {
config: {
chrome: { label: "Chrome", color: "#8884d8" },
safari: { label: "Safari", color: "#82ca9d" },
firefox: { label: "Firefox", color: "#ffc658" },
edge: { label: "Edge", color: "#ff8042" },
other: { label: "Other", color: "#8dd1e1" },
},
data: [
{ label: "chrome", value: 275, fill: "#8884d8" }, // Purple
{ label: "safari", value: 200, fill: "#82ca9d" }, // Green
{ label: "firefox", value: 287, fill: "#ffc658" }, // Yellow
{ label: "edge", value: 173, fill: "#ff8042" }, // Orange
{ label: "other", value: 190, fill: "#8dd1e1" }, // Light Blue
],
innerLabel: "50%",
innerSubLabel: "50/100",
dataKey: "value",
nameKey: "label",
},
};
101 changes: 101 additions & 0 deletions frontend/app/element/donutchart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "@/app/shadcn/chart";
import { isBlank } from "@/util/util";
import { Label, Pie, PieChart } from "recharts";
import { ViewBox } from "recharts/types/util/types";

const DEFAULT_COLORS = [
"#3498db", // blue
"#2ecc71", // green
"#e74c3c", // red
"#f1c40f", // yellow
"#9b59b6", // purple
"#1abc9c", // turquoise
"#e67e22", // orange
"#34495e", // dark blue
];

const NO_DATA_COLOR = "#E0E0E0";

const PieInnerLabel = ({
innerLabel,
innerSubLabel,
viewBox,
}: {
innerLabel: string;
innerSubLabel: string;
viewBox: ViewBox;
}) => {
if (isBlank(innerLabel)) {
return null;
}
if (!viewBox || !("cx" in viewBox) || !("cy" in viewBox)) {
return null;
}
return (
<text x={viewBox.cx} y={viewBox.cy} textAnchor="middle" dominantBaseline="middle">
<tspan x={viewBox.cx} y={viewBox.cy} fill="white" className="fill-foreground text-2xl font-bold">
{innerLabel}
</tspan>
{innerSubLabel && (
<tspan x={viewBox.cx} y={(viewBox.cy || 0) + 24} className="fill-muted-foreground">
{innerSubLabel}
</tspan>
)}
</text>
);
};

const DonutChart = ({
data,
config,
innerLabel,
innerSubLabel,
dataKey,
nameKey,
}: {
data: any[];
config: ChartConfig;
innerLabel?: string;
innerSubLabel?: string;
dataKey: string;
nameKey: string;
}) => {
Comment on lines +56 to +62
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding proper TypeScript types for data prop.

The data prop is typed as any[] which loses type safety. Consider creating a proper interface for the data structure.

+interface DonutChartData {
+    [key: string]: string | number;
+}
+
 const DonutChart = ({
     data,
     config,
     innerLabel,
     innerSubLabel,
     dataKey,
     nameKey,
 }: {
-    data: any[];
+    data: DonutChartData[];
     config: ChartConfig;
     innerLabel?: string;
     innerSubLabel?: string;
     dataKey: string;
     nameKey: string;
 }) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data: any[];
config: ChartConfig;
innerLabel?: string;
innerSubLabel?: string;
dataKey: string;
nameKey: string;
}) => {
interface DonutChartData {
[key: string]: string | number;
}
const DonutChart = ({
data,
config,
innerLabel,
innerSubLabel,
dataKey,
nameKey,
}: {
data: DonutChartData[];
config: ChartConfig;
innerLabel?: string;
innerSubLabel?: string;
dataKey: string;
nameKey: string;
}) => {
// ... implementation details
}

return (
<div
className="tw"
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
width: "100%",
height: "100%",
}}
>
<ChartContainer config={config} className="mx-auto w-full h-full aspect-square max-h-[250px]">
<PieChart>
<ChartTooltip cursor={false} content={<ChartTooltipContent hideLabel />} />
<Pie
data={data}
dataKey={dataKey}
nameKey={nameKey}
innerRadius={60}
strokeWidth={5}
isAnimationActive={false}
>
<Label
content={({ viewBox }) => (
<PieInnerLabel
innerLabel={innerLabel}
innerSubLabel={innerSubLabel}
viewBox={viewBox}
/>
)}
/>
</Pie>
</PieChart>
</ChartContainer>
</div>
);
};

export default DonutChart;
4 changes: 2 additions & 2 deletions frontend/app/element/markdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@
ul {
list-style-type: disc;
list-style-position: outside;
margin-left: 1.143em;
margin-left: 1em;
}

ol {
list-style-position: outside;
margin-left: 1.357em;
margin-left: 1.2em;
}

blockquote {
Expand Down
Loading
Loading