diff --git a/package-lock.json b/package-lock.json index 82974c7..2210274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,10 @@ "lucide": "^0.379.0", "lucide-react": "^0.379.0", "next": "14.2.3", + "next-themes": "^0.3.0", "react": "^18", "react-dom": "^18", + "sonner": "^1.4.41", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7" }, @@ -4324,6 +4326,15 @@ } } }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5331,6 +5342,15 @@ "node": ">=8" } }, + "node_modules/sonner": { + "version": "1.4.41", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.41.tgz", + "integrity": "sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==", + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", diff --git a/package.json b/package.json index 9cea5aa..67f0e5e 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "lucide": "^0.379.0", "lucide-react": "^0.379.0", "next": "14.2.3", + "next-themes": "^0.3.0", "react": "^18", "react-dom": "^18", + "sonner": "^1.4.41", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7" }, diff --git a/src/app/embed/page.tsx b/src/app/embed/page.tsx index 7d70463..769172f 100644 --- a/src/app/embed/page.tsx +++ b/src/app/embed/page.tsx @@ -7,7 +7,7 @@ import { allThemes, Theme } from '@/lib/theme'; export default function EmbedPage() { const searchParams = useSearchParams(); const padding = searchParams?.get('padding') || '64'; - const theme = searchParams?.get('theme') || JSON.stringify(allThemes['sunset']); + const theme = searchParams?.get('theme') || JSON.stringify(allThemes['firecrawl']); const background = searchParams?.get('background') || 'false'; const [parsedPadding, setParsedPadding] = useState(0); @@ -41,6 +41,7 @@ export default function EmbedPage() { padding={parsedPadding} theme={parsedTheme} background={parsedBackground} + darkMode={false} chartRef={chartRef} /> ); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3314e47..abaa984 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import { Toaster } from "sonner"; const inter = Inter({ subsets: ["latin"] }); @@ -17,6 +18,7 @@ export default function RootLayout({ return ( {children} + ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index 6a71106..9a8fac1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,19 +9,21 @@ import { Theme, allThemes } from "@/lib/theme"; import html2canvas from "html2canvas"; /** - * The issue in the original code is that the `allThemes["sunset"]` object does not match the expected `Theme` type. - * To fix this, we need to ensure that the `allThemes["sunset"]` object conforms to the `Theme` type. + * The issue in the original code is that the `allThemes["firecrawl"]` object does not match the expected `Theme` type. + * To fix this, we need to ensure that the `allThemes["firecrawl"]` object conforms to the `Theme` type. */ export default function Home() { const [padding, setPadding] = useState(64); - const [theme, setTheme] = useState(() => allThemes["sunset"] as Theme); + const [theme, setTheme] = useState(() => allThemes["firecrawl"] as Theme); const [background, setBackground] = useState(true); + const [darkMode, setDarkMode] = useState(false); + const [copyAsImage, setCopyAsImage] = useState(false); const chartRef = useRef(null); - const handleExport = async () => { + const handleExport = async (copyAsImage: boolean = false) => { if (chartRef.current) { const canvas = await html2canvas(chartRef.current, { useCORS: true, @@ -41,10 +43,22 @@ export default function Home() { }); }, }); + console.log("coco"); const link = document.createElement("a"); link.href = canvas.toDataURL("image/png"); link.download = "chart.png"; + document.body.appendChild(link); // Append the link to the body link.click(); + document.body.removeChild(link); // Remove the link after download + if (copyAsImage) { + console.log("copying as image"); + canvas.toBlob((blob) => { + if (blob) { + const item = new ClipboardItem({ "image/png": blob }); + navigator.clipboard.write([item]); + } + }); + } } }; @@ -58,7 +72,7 @@ export default function Home() { }} >
- + handleExport(true)} />
Made by{" "} diff --git a/src/components/graph.tsx b/src/components/graph.tsx index 81aa304..ed5e438 100644 --- a/src/components/graph.tsx +++ b/src/components/graph.tsx @@ -2,18 +2,20 @@ import { Theme } from "@/lib/theme"; import { AreaChart, Color } from "@tremor/react"; -import { useState, useRef } from "react"; +import { useState, useRef, useEffect } from "react"; import html2canvas from "html2canvas"; export default function Graph({ padding, theme, background, + darkMode, chartRef, }: { padding: number; theme: Theme; background: boolean; + darkMode: boolean; chartRef: React.RefObject; }) { const mockchartdata = [ @@ -87,8 +89,6 @@ export default function Graph({ const maxStars = Math.max(...mockchartdata.map((data) => data.Stars)); const [maxValue, setMaxValue] = useState(maxStars); - - return (
-
-
-
-
-
+
+
+
+
+
+
+
+
+
Your awesome graph
+
void; background: boolean; setBackground: (background: boolean) => void; + darkMode: boolean; + setDarkMode: (darkMode: boolean) => void; + handleExportCopyAsImage: () => void; handleExport: () => void; }) { const generateEmbedCode = () => { const embedCode = ` `; navigator.clipboard.writeText(embedCode).then(() => { - alert("Embed code copied to clipboard!"); + toast("Embed code copied to clipboard!"); }); }; return ( @@ -93,6 +115,14 @@ export default function Menu({ onCheckedChange={(checked) => setBackground(checked)} />
+ {/*
+ Dark Mode + setDarkMode(checked)} + /> +
*/}
Padding
- - +
+ + +
{/*
diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx index 816d36b..610c5de 100644 --- a/src/components/ui/select.tsx +++ b/src/components/ui/select.tsx @@ -14,8 +14,8 @@ const SelectValue = SelectPrimitive.Value const SelectTrigger = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( + Omit, 'noIcon'> & { noIcon?: boolean } +>(({ className, children, noIcon, ...props }, ref) => ( {children} - + {noIcon ? <> : } )) diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx new file mode 100644 index 0000000..9f71edf --- /dev/null +++ b/src/components/ui/sonner.tsx @@ -0,0 +1,31 @@ +"use client" + +import { useTheme } from "next-themes" +import { Toaster as Sonner } from "sonner" + +type ToasterProps = React.ComponentProps + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + + ) +} + +export { Toaster } diff --git a/src/lib/theme.ts b/src/lib/theme.ts index 1a87d38..ecf12a6 100644 --- a/src/lib/theme.ts +++ b/src/lib/theme.ts @@ -5,39 +5,70 @@ export interface Theme { } export const allThemes = { - "sunset":{ - "name": "sunset", + "firecrawl": { + "name": "firecrawl", "gradient": "linear-gradient(135deg, #fdba74, #f97316)", "color": "orange", }, - "ocean":{ + "ocean": { "name": "ocean", "gradient": "linear-gradient(135deg, #61a5c2, #1a2980)", "color": "blue", }, - "forest":{ + "forest": { "name": "forest", "gradient": "linear-gradient(135deg, #a8d8ea, #021c1e)", "color": "teal", }, - "fire":{ - "name": "fire", + "sunset": { + "name": "sunset", "gradient": "linear-gradient(135deg, #f9d423, #ff4e50)", "color": "red", }, - "space":{ + "space": { "name": "space", "gradient": "linear-gradient(135deg, #000428, #004e92)", "color": "indigo", }, - "sky":{ + "sky": { "name": "sky", "gradient": "linear-gradient(135deg, #a1c4fd, #c2e9fb)", "color": "sky", }, - "night":{ + "night": { "name": "night", "gradient": "linear-gradient(135deg, #000000, #434343)", "color": "gray", }, + "dawn": { + "name": "dawn", + "gradient": "linear-gradient(135deg, #ff9a9e, #fad0c4)", + "color": "pink", + }, + "aurora": { + "name": "aurora", + "gradient": "linear-gradient(135deg, #00c6ff, #0072ff)", + "color": "cyan", + }, + "meadow": { + "name": "meadow", + "gradient": "linear-gradient(135deg, #a8e063, #56ab2f)", + "color": "green", + }, + "galaxy": { + "name": "galaxy", + "gradient": "linear-gradient(135deg, #3a1c71, #d76d77, #ffaf7b)", + "color": "purple", + }, + "sunrise": { + "name": "sunrise", + "gradient": "linear-gradient(135deg, #ff512f, #dd2476)", + "color": "rose", + }, + "twilight": { + "name": "twilight", + "gradient": "linear-gradient(135deg, #0f2027, #203a43, #2c5364)", + "color": "slate", + }, } +