diff --git a/js/package.json b/js/package.json index c7e2f013..c8344302 100644 --- a/js/package.json +++ b/js/package.json @@ -6,7 +6,7 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "devDependencies": { + "devDependencies": { "typescript": "^5.5.4", "@typescript-eslint/eslint-plugin": "^8.21.0", "eslint": "^9.19.0", @@ -21,5 +21,6 @@ }, "keywords": [], "author": "engineering@crayon.ai", - "license": "MIT" + "license": "MIT", + "packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0" } diff --git a/js/packages/react-ui/package.json b/js/packages/react-ui/package.json index d667fdba..95221b0f 100644 --- a/js/packages/react-ui/package.json +++ b/js/packages/react-ui/package.json @@ -73,6 +73,7 @@ "@radix-ui/react-tooltip": "^1.2.7", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "html-to-image": "1.11.11", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "react-day-picker": "^9.5.1", @@ -99,6 +100,7 @@ "@storybook/react-vite": "^8.5.3", "@storybook/test": "^8.5.3", "@storybook/theming": "^8.5.3", + "@tailwindcss/postcss": "^4.1.11", "@types/lodash-es": "^4.17.12", "@types/node": "^22.12.0", "@types/node-fetch": "2.6.11", @@ -106,6 +108,7 @@ "@types/react-dom": ">=17.0.0", "@types/react-syntax-highlighter": "^15.5.13", "@typescript-eslint/eslint-plugin": "^8.18.0", + "autoprefixer": "^10.4.21", "concurrently": "^9.2.0", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx index 5730196a..48395373 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx +++ b/js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { Area, AreaChart as RechartsAreaChart, XAxis, YAxis } from "recharts"; import { useId } from "../../../polyfills"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks"; import { @@ -15,6 +16,8 @@ import { XAxisTick, YAxisTick, } from "../shared"; +import { ChartExportFooter } from "../shared/ChartExportFooter"; +import { ExportButton } from "../shared/ExportButton"; import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { LegendItem, XAxisTickVariant } from "../types"; import { @@ -24,6 +27,7 @@ import { getWidthOfGroup, } from "../utils/AreaAndLine/AreaAndLineUtils"; import { PaletteName, useChartPalette } from "../utils/PalletUtils"; +import { useExportChart } from "../utils/chartExportUtils"; import { get2dChartConfig, getColorForDataKey, @@ -54,6 +58,7 @@ export interface AreaChartProps { className?: string; height?: number; width?: number; + exportRef?: React.RefObject; } const X_AXIS_PADDING = 36; @@ -76,6 +81,7 @@ const AreaChartComponent = ({ className, height, width, + exportRef, }: AreaChartProps) => { const dataKeys = useMemo(() => { return getDataKeys(data, categoryKey as string); @@ -113,6 +119,11 @@ const AreaChartComponent = ({ title: "", values: [], }); + const [isChartHovered, setIsChartHovered] = useState(false); + const exportContext = useExportContext(); + + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef, "crayon-area-chart-main-container"); // Use provided width or observed width const effectiveWidth = useMemo(() => { @@ -295,11 +306,43 @@ const AreaChartComponent = ({ data={sideBarTooltipData} setData={setSideBarTooltipData} > + {!exportContext && ( + + + + )}
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + ref={exportRef} >
{/* Y-axis of the chart */} @@ -408,10 +451,12 @@ const AreaChartComponent = ({ yAxisLabel={yAxisLabel} xAxisLabel={xAxisLabel} containerWidth={effectiveWidth} - isExpanded={isLegendExpanded} + isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode setIsExpanded={setIsLegendExpanded} /> )} + {isChartHovered && } + {exportContext && }
diff --git a/js/packages/react-ui/src/components/Charts/AreaChart/areaChart.scss b/js/packages/react-ui/src/components/Charts/AreaChart/areaChart.scss index 2ced964e..a4bafb59 100644 --- a/js/packages/react-ui/src/components/Charts/AreaChart/areaChart.scss +++ b/js/packages/react-ui/src/components/Charts/AreaChart/areaChart.scss @@ -1,5 +1,9 @@ @use "../../../cssUtils" as cssUtils; +.crayon-area-chart-container { + position: relative; +} + .crayon-area-chart-container-inner { display: flex; width: 100%; diff --git a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx index e65a5466..46810eae 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx @@ -5,6 +5,7 @@ import { useId } from "../../../polyfills"; import { useTheme } from "../../ThemeProvider"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks"; import { cartesianGrid, @@ -16,24 +17,25 @@ import { XAxisTickProps, YAxisTick, } from "../shared"; - +import { ChartExportFooter } from "../shared/ChartExportFooter"; +import { ExportButton } from "../shared/ExportButton"; +import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { ScrollButtonsHorizontal } from "../shared/ScrollButtonsHorizontal/ScrollButtonsHorizontal"; import { XAxisTickVariant } from "../types"; import { type LegendItem } from "../types/Legend"; -import { useChartPalette, type PaletteName } from "../utils/PalletUtils"; - -import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { findNearestSnapPosition, getBarStackInfo, getRadiusArray, } from "../utils/BarCharts/BarChartsUtils"; +import { useExportChart } from "../utils/chartExportUtils"; import { get2dChartConfig, getColorForDataKey, getDataKeys, getLegendItems, } from "../utils/dataUtils"; +import { useChartPalette, type PaletteName } from "../utils/PalletUtils"; import { BarChartData, BarChartVariant } from "./types"; import { BAR_WIDTH, @@ -65,6 +67,7 @@ export interface BarChartProps { className?: string; height?: number; width?: number; + exportRef?: React.RefObject; } const BAR_GAP = 10; // Gap between bars @@ -91,8 +94,10 @@ const BarChartComponent = ({ className, height, width, + exportRef, }: BarChartProps) => { const widthOfGroup = getWidthOfGroup(data, categoryKey as string, variant); + const exportContext = useExportContext(); const maxLabelHeight = useMaxLabelHeight(data, categoryKey as string, tickVariant, widthOfGroup); @@ -122,11 +127,14 @@ const BarChartComponent = ({ const [canScrollRight, setCanScrollRight] = useState(false); const [hoveredCategory, setHoveredCategory] = useState(null); const [isLegendExpanded, setIsLegendExpanded] = useState(false); + const [isChartHovered, setIsChartHovered] = useState(false); const [isSideBarTooltipOpen, setIsSideBarTooltipOpen] = useState(false); const [sideBarTooltipData, setSideBarTooltipData] = useState({ title: "", values: [], }); + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef ?? null, "crayon-bar-chart-main-container"); // Use provided width or observed width const effectiveWidth = useMemo(() => { @@ -407,11 +415,45 @@ const BarChartComponent = ({ data={sideBarTooltipData} setData={setSideBarTooltipData} > + {!exportContext && ( + + + + )} +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + ref={exportRef as React.RefObject} >
{/* Y-axis of the chart */} @@ -497,10 +539,12 @@ const BarChartComponent = ({ yAxisLabel={yAxisLabel} xAxisLabel={xAxisLabel} containerWidth={effectiveWidth} - isExpanded={isLegendExpanded} + isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode setIsExpanded={setIsLegendExpanded} /> )} + {isChartHovered && } + {exportContext && }
diff --git a/js/packages/react-ui/src/components/Charts/BarChart/barChart.scss b/js/packages/react-ui/src/components/Charts/BarChart/barChart.scss index 75c49e89..03823dfe 100644 --- a/js/packages/react-ui/src/components/Charts/BarChart/barChart.scss +++ b/js/packages/react-ui/src/components/Charts/BarChart/barChart.scss @@ -1,5 +1,9 @@ @use "../../../cssUtils" as cssUtils; +.crayon-bar-chart-container { + position: relative; +} + .crayon-bar-chart-container-inner { display: flex; width: 100%; diff --git a/js/packages/react-ui/src/components/Charts/ExportContext/index.ts b/js/packages/react-ui/src/components/Charts/ExportContext/index.ts new file mode 100644 index 00000000..02017f6c --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/ExportContext/index.ts @@ -0,0 +1,12 @@ +import { createContext, useContext } from "react"; + +interface ExportContext { + format: "image"; +} + +export const ExportContext = createContext(null); +export const ExportContextProvider = ExportContext.Provider; + +export const useExportContext = () => { + return useContext(ExportContext); +}; diff --git a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/HorizontalBarChart.tsx b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/HorizontalBarChart.tsx index 8b4ab236..f09fcbce 100644 --- a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/HorizontalBarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/HorizontalBarChart.tsx @@ -5,6 +5,7 @@ import { useId } from "../../../polyfills"; import { useTheme } from "../../ThemeProvider"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { useTransformedKeys } from "../hooks"; import { useHorizontalBarLabelHeight } from "../hooks/useMaxLabelHeight"; import { @@ -14,6 +15,8 @@ import { verticalCartesianGrid, YAxisTick, } from "../shared"; +import { ChartExportFooter } from "../shared/ChartExportFooter"; +import { ExportButton } from "../shared/ExportButton"; import { ScrollButtonsVertical } from "../shared/ScrollButtonsVertical"; import { type LegendItem } from "../types/Legend"; @@ -25,6 +28,7 @@ import { getBarStackInfo, getRadiusArray, } from "../utils/BarCharts/BarChartsUtils"; +import { useExportChart } from "../utils/chartExportUtils"; import { get2dChartConfig, getColorForDataKey, @@ -64,6 +68,7 @@ export interface HorizontalBarChartProps { className?: string; height?: number; width?: number; + exportRef?: React.RefObject; } const X_AXIS_HEIGHT = 40; // Height of X-axis chart when shown @@ -88,8 +93,10 @@ const HorizontalBarChartComponent = ({ className, height, width, + exportRef, }: HorizontalBarChartProps) => { const maxCategoryLabelWidth = useMaxCategoryLabelWidth(data, categoryKey as string); + const exportContext = useExportContext(); const chartContainerRef = useRef(null); const mainContainerRef = useRef(null); @@ -98,11 +105,17 @@ const HorizontalBarChartComponent = ({ const [canScrollDown, setCanScrollDown] = useState(false); const [hoveredCategory, setHoveredCategory] = useState(null); const [isLegendExpanded, setIsLegendExpanded] = useState(false); + const [isChartHovered, setIsChartHovered] = useState(false); const [isSideBarTooltipOpen, setIsSideBarTooltipOpen] = useState(false); const [sideBarTooltipData, setSideBarTooltipData] = useState({ title: "", values: [], }); + const exportChartRef = useRef(null); + const { exportChart } = useExportChart( + exportChartRef ?? null, + "crayon-horizontal-bar-chart-container-inner-wrapper", + ); // Calculate chart width for internal calculations (legend, xAxis, etc.) const effectiveWidth = useMemo(() => { @@ -342,11 +355,50 @@ const HorizontalBarChartComponent = ({ data={sideBarTooltipData} setData={setSideBarTooltipData} > -
+ {!exportContext && ( + + + + )} + +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + ref={exportRef as React.RefObject} + >
@@ -475,10 +527,14 @@ const HorizontalBarChartComponent = ({ yAxisLabel={yAxisLabel} xAxisLabel={xAxisLabel} containerWidth={effectiveWidth} - isExpanded={isLegendExpanded} + isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode setIsExpanded={setIsLegendExpanded} /> )} + {isChartHovered && } + {exportContext && ( + + )}
diff --git a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/components/CustomBarShape.tsx b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/components/CustomBarShape.tsx index 2607c1e5..999f0acc 100644 --- a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/components/CustomBarShape.tsx +++ b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/components/CustomBarShape.tsx @@ -58,7 +58,6 @@ const CustomBarShapeComponent = (props: CustomBarShapeProps) => { width={labelWidth} height={labelHeight} style={{ pointerEvents: "none" }} - xmlns="http://www.w3.org/1999/xhtml" >
{payload[categoryKey]}
diff --git a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/horizontalBarChart.scss b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/horizontalBarChart.scss index b5f96492..11a45ddf 100644 --- a/js/packages/react-ui/src/components/Charts/HorizontalBarChart/horizontalBarChart.scss +++ b/js/packages/react-ui/src/components/Charts/HorizontalBarChart/horizontalBarChart.scss @@ -60,3 +60,7 @@ box-sizing: border-box; } } + +.crayon-horizontal-bar-chart-export-footer { + margin-top: 8px; +} diff --git a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx index 509f9992..4154aba5 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx +++ b/js/packages/react-ui/src/components/Charts/LineChart/LineChart.tsx @@ -4,6 +4,7 @@ import { Line, LineChart as RechartsLineChart, XAxis, YAxis } from "recharts"; import { useId } from "../../../polyfills"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks"; import { ActiveDot, @@ -15,6 +16,8 @@ import { XAxisTick, YAxisTick, } from "../shared"; +import { ChartExportFooter } from "../shared/ChartExportFooter"; +import { ExportButton } from "../shared/ExportButton"; import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip"; import { LegendItem, XAxisTickVariant } from "../types"; import { @@ -23,13 +26,14 @@ import { getWidthOfData, getWidthOfGroup, } from "../utils/AreaAndLine/AreaAndLineUtils"; -import { PaletteName, useChartPalette } from "../utils/PalletUtils"; +import { useExportChart } from "../utils/chartExportUtils"; import { get2dChartConfig, getColorForDataKey, getDataKeys, getLegendItems, } from "../utils/dataUtils"; +import { PaletteName, useChartPalette } from "../utils/PalletUtils"; import { LineChartData, LineChartVariant } from "./types"; type LineChartOnClick = React.ComponentProps["onClick"]; @@ -53,12 +57,13 @@ export interface LineChartProps { height?: number; width?: number; strokeWidth?: number; + exportRef?: React.RefObject; } const X_AXIS_PADDING = 36; const CHART_CONTAINER_BOTTOM_MARGIN = 10; -export const LineChart = ({ +const LineChartComponent = ({ data, categoryKey, theme = "ocean", @@ -76,6 +81,7 @@ export const LineChart = ({ height, width, strokeWidth = 2, + exportRef, }: LineChartProps) => { const dataKeys = useMemo(() => { return getDataKeys(data, categoryKey as string); @@ -113,6 +119,11 @@ export const LineChart = ({ title: "", values: [], }); + const [isChartHovered, setIsChartHovered] = useState(false); + const exportContext = useExportContext(); + + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef, "crayon-line-chart-main-container"); // Use provided width or observed width const effectiveWidth = useMemo(() => { @@ -305,11 +316,45 @@ export const LineChart = ({ data={sideBarTooltipData} setData={setSideBarTooltipData} > + {!exportContext && ( + + + + )} +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + ref={exportRef} >
{/* Y-axis of the chart */} @@ -397,12 +442,17 @@ export const LineChart = ({ yAxisLabel={yAxisLabel} xAxisLabel={xAxisLabel} containerWidth={effectiveWidth} - isExpanded={isLegendExpanded} + isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode setIsExpanded={setIsLegendExpanded} /> )} + {isChartHovered && } + {exportContext && }
); }; + +// Added React.memo for performance optimization to avoid unnecessary re-renders +export const LineChart = React.memo(LineChartComponent) as typeof LineChartComponent; diff --git a/js/packages/react-ui/src/components/Charts/LineChart/lineChart.scss b/js/packages/react-ui/src/components/Charts/LineChart/lineChart.scss index d62a61e5..c082a152 100644 --- a/js/packages/react-ui/src/components/Charts/LineChart/lineChart.scss +++ b/js/packages/react-ui/src/components/Charts/LineChart/lineChart.scss @@ -1,5 +1,9 @@ @use "../../../cssUtils" as cssUtils; +.crayon-line-chart-container { + position: relative; +} + .crayon-line-chart-container-inner { display: flex; width: 100%; diff --git a/js/packages/react-ui/src/components/Charts/PieChart/PieChart.tsx b/js/packages/react-ui/src/components/Charts/PieChart/PieChart.tsx index eb474c3a..42181a93 100644 --- a/js/packages/react-ui/src/components/Charts/PieChart/PieChart.tsx +++ b/js/packages/react-ui/src/components/Charts/PieChart/PieChart.tsx @@ -2,10 +2,14 @@ import clsx from "clsx"; import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Cell, Pie, PieChart as RechartsPieChart } from "recharts"; import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../Charts.js"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { useTransformedKeys } from "../hooks/index.js"; +import { ChartExportFooter } from "../shared/ChartExportFooter/index.js"; import { DefaultLegend } from "../shared/DefaultLegend/DefaultLegend.js"; +import { ExportButton } from "../shared/ExportButton/index.js"; import { StackedLegend } from "../shared/StackedLegend/StackedLegend.js"; import { LegendItem } from "../types/Legend.js"; +import { useExportChart } from "../utils/chartExportUtils.js"; import { getCategoricalChartConfig } from "../utils/dataUtils.js"; import { PaletteName, useChartPalette } from "../utils/PalletUtils.js"; import { PieChartData } from "./types/index.js"; @@ -39,6 +43,7 @@ export interface PieChartProps { className?: string; maxChartSize?: number; minChartSize?: number; + exportRef?: React.RefObject; } const STACKED_LEGEND_BREAKPOINT = 400; @@ -65,12 +70,18 @@ const PieChartComponent = ({ className, maxChartSize = MAX_CHART_SIZE, minChartSize = MIN_CHART_SIZE, + exportRef, }: PieChartProps) => { const wrapperRef = useRef(null); const [wrapperRect, setWrapperRect] = useState({ width: 0, height: 0 }); const [hoveredLegendKey, setHoveredLegendKey] = useState(null); const [isLegendExpanded, setIsLegendExpanded] = useState(false); + const [isChartHovered, setIsChartHovered] = useState(false); const { activeIndex, handleMouseEnter, handleMouseLeave } = useChartHover(); + const exportContext = useExportContext(); + + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef); // Determine layout mode based on container width const isRowLayout = @@ -360,7 +371,7 @@ const PieChartComponent = ({ ); @@ -374,6 +385,7 @@ const PieChartComponent = ({ isRowLayout, defaultLegendItems, isLegendExpanded, + exportContext, ]); const wrapperClassName = useMemo( @@ -387,8 +399,13 @@ const PieChartComponent = ({ [className, legend, legendVariant, isRowLayout], ); - return ( -
+ const pieChartJsx = ( +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + >
@@ -406,10 +423,53 @@ const PieChartComponent = ({
+ {isChartHovered && }
{renderLegend()}
); + + return ( + <> + {!exportContext && ( + + + + )} + {exportContext ? ( +
+ {pieChartJsx} + {} +
+ ) : ( + pieChartJsx + )} + + ); }; export const PieChart = memo(PieChartComponent); diff --git a/js/packages/react-ui/src/components/Charts/PieChart/pieChart.scss b/js/packages/react-ui/src/components/Charts/PieChart/pieChart.scss index 9e9a51c6..4d60de53 100644 --- a/js/packages/react-ui/src/components/Charts/PieChart/pieChart.scss +++ b/js/packages/react-ui/src/components/Charts/PieChart/pieChart.scss @@ -80,3 +80,10 @@ align-items: center; } } + +.crayon-pie-chart-export-container { + display: flex; + flex-direction: column; + padding: 8px; + gap: 8px; +} diff --git a/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx b/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx index 6fa5db04..254abfa2 100644 --- a/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx +++ b/js/packages/react-ui/src/components/Charts/RadarChart/RadarChart.tsx @@ -2,11 +2,15 @@ import clsx from "clsx"; import React, { memo, useEffect, useMemo, useRef, useState } from "react"; import { PolarAngleAxis, PolarGrid, Radar, RadarChart as RechartsRadarChart } from "recharts"; import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { SideBarTooltipProvider } from "../context/SideBarTooltipContext"; import { useTransformedKeys } from "../hooks/useTransformKey"; import { ActiveDot, CustomTooltipContent, DefaultLegend } from "../shared"; +import { ChartExportFooter } from "../shared/ChartExportFooter"; +import { ExportButton } from "../shared/ExportButton"; import { LegendItem } from "../types"; import { useChartPalette } from "../utils/PalletUtils"; +import { useExportChart } from "../utils/chartExportUtils"; import { get2dChartConfig, getDataKeys, getLegendItems } from "../utils/dataUtils"; import { AxisLabel } from "./components/AxisLabel"; import { RadarChartData } from "./types"; @@ -26,6 +30,7 @@ export interface RadarChartProps { areaOpacity?: number; icons?: Partial>; isAnimationActive?: boolean; + exportRef?: React.RefObject; } const RadarChartComponent = ({ @@ -40,6 +45,7 @@ const RadarChartComponent = ({ areaOpacity = 0.2, icons = {}, isAnimationActive = false, + exportRef, }: RadarChartProps) => { const dataKeys = useMemo(() => { return getDataKeys(data, categoryKey as string); @@ -64,8 +70,13 @@ const RadarChartComponent = ({ }, [dataKeys, colors, icons]); const [isLegendExpanded, setIsLegendExpanded] = useState(false); + const [isChartHovered, setIsChartHovered] = useState(false); const wrapperRef = useRef(null); const [wrapperRect, setWrapperRect] = useState({ width: 0, height: 0 }); + const exportContext = useExportContext(); + + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef); useEffect(() => { const wrapper = wrapperRef.current; @@ -137,6 +148,60 @@ const RadarChartComponent = ({ [], ); + const radarChartJsx = ( +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + > +
+
+
+ + + {grid && } + } + /> + + } + /> + {/* rendering the radars here */} + {radars} + + +
+
+
+ {legend && ( + + )} + {isChartHovered && } +
+ ); + return ( ({ data={undefined} setData={() => {}} > -
-
-
-
- - - {grid && } - } - /> - - } - /> - {/* rendering the radars here */} - {radars} - - -
-
-
- {legend && ( - + - )} -
+ + )} + + {!!exportContext ? ( +
+ {radarChartJsx} + {} +
+ ) : ( + radarChartJsx + )}
); }; diff --git a/js/packages/react-ui/src/components/Charts/RadarChart/components/AxisLabel.tsx b/js/packages/react-ui/src/components/Charts/RadarChart/components/AxisLabel.tsx index d03e3fa3..a19c9d16 100644 --- a/js/packages/react-ui/src/components/Charts/RadarChart/components/AxisLabel.tsx +++ b/js/packages/react-ui/src/components/Charts/RadarChart/components/AxisLabel.tsx @@ -1,5 +1,6 @@ import clsx from "clsx"; import React, { useLayoutEffect, useMemo, useRef } from "react"; +import { useExportContext } from "../../ExportContext"; import { calculateAvailableWidth, truncateText } from "../utils"; // This is the props that are passed by recharts to the custom tick component @@ -19,6 +20,7 @@ interface AxisLabelProps { export const AxisLabel: React.FC = (props) => { const { x, y, payload, textAnchor, portalContainerRef, className } = props; const anchorRef = useRef(null); + const exportContext = useExportContext(); /** * Memoizes the calculation of truncated text for axis labels @@ -106,7 +108,9 @@ export const AxisLabel: React.FC = (props) => { textAnchor ?? "middle", 0, ); - const newTruncatedText = truncateText(payload?.value ?? "", availableWidth, 10); + const newTruncatedText = !!exportContext + ? (payload?.value ?? "") + : truncateText(payload?.value ?? "", availableWidth, 10); if (labelEl.textContent !== newTruncatedText) { labelEl.textContent = newTruncatedText; } diff --git a/js/packages/react-ui/src/components/Charts/RadarChart/radarChart.scss b/js/packages/react-ui/src/components/Charts/RadarChart/radarChart.scss index a1a5bfe7..9c834a1b 100644 --- a/js/packages/react-ui/src/components/Charts/RadarChart/radarChart.scss +++ b/js/packages/react-ui/src/components/Charts/RadarChart/radarChart.scss @@ -46,3 +46,10 @@ @include cssUtils.typography(label, 2-extra-small); color: cssUtils.$secondary-text; } + +.crayon-radar-chart-export-container { + display: flex; + flex-direction: column; + padding: 8px; + gap: 8px; +} diff --git a/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx b/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx index 02bb8fea..ea225b11 100644 --- a/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx +++ b/js/packages/react-ui/src/components/Charts/RadialChart/RadialChart.tsx @@ -1,11 +1,15 @@ import clsx from "clsx"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Cell, PolarGrid, RadialBar, RadialBarChart } from "recharts"; import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../Charts"; +import { ExportContextProvider, useExportContext } from "../ExportContext"; import { useTransformedKeys } from "../hooks"; +import { ChartExportFooter } from "../shared/ChartExportFooter"; import { DefaultLegend } from "../shared/DefaultLegend/DefaultLegend"; +import { ExportButton } from "../shared/ExportButton"; import { StackedLegend } from "../shared/StackedLegend/StackedLegend"; import { LegendItem } from "../types/Legend"; +import { useExportChart } from "../utils/chartExportUtils"; import { getCategoricalChartConfig } from "../utils/dataUtils"; import { PaletteName, useChartPalette } from "../utils/PalletUtils"; import { RadialChartData } from "./types"; @@ -37,13 +41,14 @@ export interface RadialChartProps { className?: string; maxChartSize?: number; minChartSize?: number; + exportRef?: React.RefObject; } const STACKED_LEGEND_BREAKPOINT = 400; const MIN_CHART_SIZE = 150; const MAX_CHART_SIZE = 500; -export const RadialChart = ({ +const RadialChartComponent = ({ data, categoryKey, dataKey, @@ -62,12 +67,18 @@ export const RadialChart = ({ className, maxChartSize = MAX_CHART_SIZE, minChartSize = MIN_CHART_SIZE, + exportRef, }: RadialChartProps) => { const wrapperRef = useRef(null); const [wrapperRect, setWrapperRect] = useState({ width: 0, height: 0 }); const [hoveredLegendKey, setHoveredLegendKey] = useState(null); const [isLegendExpanded, setIsLegendExpanded] = useState(false); + const [isChartHovered, setIsChartHovered] = useState(false); const { activeIndex, handleMouseEnter, handleMouseLeave } = useRadialChartHover(); + const exportContext = useExportContext(); + + const exportChartRef = useRef(null); + const { exportChart } = useExportChart(exportChartRef); // Determine layout mode based on container width const isRowLayout = @@ -268,7 +279,7 @@ export const RadialChart = ({ ); @@ -282,21 +293,31 @@ export const RadialChart = ({ isRowLayout, defaultLegendItems, isLegendExpanded, + exportContext, ]); - const wrapperClassName = clsx("crayon-radial-chart-container-wrapper", className, { - "layout-row": isRowLayout, - "layout-column": !isRowLayout, - "legend-default": legend && legendVariant === "default", - "legend-stacked": legend && legendVariant === "stacked", - }); + const wrapperClassName = useMemo( + () => + clsx("crayon-radial-chart-container-wrapper", className, { + "layout-row": isRowLayout, + "layout-column": !isRowLayout, + "legend-default": legend && legendVariant === "default", + "legend-stacked": legend && legendVariant === "stacked", + }), + [className, legend, legendVariant, isRowLayout], + ); // Correct angles for semicircle (top half) const startAngle = variant === "semicircle" ? 180 : 0; const endAngle = variant === "semicircle" ? 0 : 360; - return ( -
+ const radialChartJsx = ( +
setIsChartHovered(true)} + onMouseLeave={() => setIsChartHovered(false)} + >
@@ -346,8 +367,54 @@ export const RadialChart = ({
+ {isChartHovered && }
{renderLegend()}
); + + return ( + <> + {!exportContext && ( + + + + )} + {exportContext ? ( +
+ {radialChartJsx} + {} +
+ ) : ( + radialChartJsx + )} + + ); }; + +export const RadialChart = memo(RadialChartComponent); + +RadialChart.displayName = "RadialChart"; diff --git a/js/packages/react-ui/src/components/Charts/RadialChart/radialChart.scss b/js/packages/react-ui/src/components/Charts/RadialChart/radialChart.scss index a89a478a..ef5ab455 100644 --- a/js/packages/react-ui/src/components/Charts/RadialChart/radialChart.scss +++ b/js/packages/react-ui/src/components/Charts/RadialChart/radialChart.scss @@ -80,3 +80,10 @@ align-items: center; // Vertically center legend content } } + +.crayon-radial-chart-export-container { + display: flex; + flex-direction: column; + padding: 8px; + gap: 8px; +} diff --git a/js/packages/react-ui/src/components/Charts/charts.scss b/js/packages/react-ui/src/components/Charts/charts.scss index faf37fbf..d589ccd1 100644 --- a/js/packages/react-ui/src/components/Charts/charts.scss +++ b/js/packages/react-ui/src/components/Charts/charts.scss @@ -30,6 +30,8 @@ @forward "./shared/ScrollButtonsHorizontal/scrollButtonsHorizontal.scss"; @forward "./shared/ScrollButtonsVertical/scrollButtonsVertical.scss"; @forward "./shared/LabelTooltip/labelTooltip.scss"; +@forward "./shared/ExportButton/exportButton.scss"; +@forward "./shared/ChartExportFooter/chartExportFooter.scss"; // portal tooltip css @forward "./shared/PortalTooltip/portalTooltip.scss"; @@ -88,3 +90,9 @@ .crayon-chart-label-list { fill: cssUtils.$secondary-text; } + +.crayon-chart-export-container { + opacity: 0; + position: fixed; + z-index: -1; +} diff --git a/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx b/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx index bec9aa8d..5755a75f 100644 --- a/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx +++ b/js/packages/react-ui/src/components/Charts/hooks/useMaxLabelHeight.tsx @@ -1,6 +1,7 @@ import { useMemo } from "react"; import { useTheme } from "../../ThemeProvider"; +import { useExportContext } from "../ExportContext"; import { XAxisTickVariant } from "../types"; const DEFAULT_HEIGHT = 30; @@ -12,6 +13,7 @@ export const useMaxLabelHeight = ( widthOfGroup = 70, ) => { const { theme: userTheme } = useTheme(); + const allowFullHeight = !!useExportContext(); const maxLabelHeight = useMemo(() => { if (typeof window === "undefined" || !data || data.length === 0) { @@ -49,10 +51,9 @@ export const useMaxLabelHeight = ( document.body.append(div1); - const largestLabelHeight = Math.min( - div2.getBoundingClientRect().height, - div3.getBoundingClientRect().height * 3, - ); + const largestLabelHeight = allowFullHeight + ? div2.getBoundingClientRect().height + : Math.min(div2.getBoundingClientRect().height, div3.getBoundingClientRect().height * 3); div1.remove(); return largestLabelHeight; diff --git a/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/ChartWatermark.tsx b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/ChartWatermark.tsx new file mode 100644 index 00000000..12d34e17 --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/ChartWatermark.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { useTheme } from "../../../ThemeProvider"; + +export const ChartWatermark = (props: React.SVGProps) => { + const { mode } = useTheme(); + + if (mode === "dark") { + return ( + + + + + + + + + ); + } + + return ( + + + + + + + + + ); +}; diff --git a/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/chartExportFooter.scss b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/chartExportFooter.scss new file mode 100644 index 00000000..88ad67fd --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/chartExportFooter.scss @@ -0,0 +1,13 @@ +.crayon-chart-export-footer-container { + width: 100%; + display: flex; + justify-content: flex-end; + padding: min(5%, 8px); +} + +.crayon-chart-export-watermark-container { + display: flex; + align-items: center; + max-height: 24px; + max-width: min(25%, 146px); +} diff --git a/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/index.tsx b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/index.tsx new file mode 100644 index 00000000..c342de7e --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/ChartExportFooter/index.tsx @@ -0,0 +1,16 @@ +import clsx from "clsx"; +import { ChartWatermark } from "./ChartWatermark"; + +interface ChartExportFooterProps { + className?: string; +} + +export const ChartExportFooter = ({ className }: ChartExportFooterProps) => { + return ( +
+
+ +
+
+ ); +}; diff --git a/js/packages/react-ui/src/components/Charts/shared/ExportButton/exportButton.scss b/js/packages/react-ui/src/components/Charts/shared/ExportButton/exportButton.scss new file mode 100644 index 00000000..450910aa --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/ExportButton/exportButton.scss @@ -0,0 +1,5 @@ +.crayon-chart-export-button-container { + position: absolute; + top: 8px; + right: 8px; +} diff --git a/js/packages/react-ui/src/components/Charts/shared/ExportButton/index.tsx b/js/packages/react-ui/src/components/Charts/shared/ExportButton/index.tsx new file mode 100644 index 00000000..914b924e --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/shared/ExportButton/index.tsx @@ -0,0 +1,27 @@ +import { Download } from "lucide-react"; +import { IconButton } from "../../../IconButton"; +import { useExportContext } from "../../ExportContext"; + +interface ExportButtonProps { + exportChart: () => void; +} + +export const ExportButton = ({ exportChart }: ExportButtonProps) => { + const exportContext = useExportContext(); + + return ( +
+ } + onClick={(e) => { + if (exportContext) { + e.preventDefault(); + return; + } + exportChart(); + }} + /> +
+ ); +}; diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx index 99347585..74e4e068 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/XAxisTick.tsx @@ -1,5 +1,6 @@ import clsx from "clsx"; import React, { useLayoutEffect, useRef, useState } from "react"; +import { useExportContext } from "../../ExportContext"; import { XAxisTickVariant } from "../../types"; import { LabelTooltip } from "../LabelTooltip/LabelTooltip"; @@ -33,6 +34,8 @@ interface XAxisTickProps { } const XAxisTick = React.forwardRef((props, ref) => { + const exportContext = useExportContext(); + const { x, y, @@ -106,7 +109,13 @@ const XAxisTick = React.forwardRef((props, ref) => textAlign: "center", wordBreak: "break-word", }} - className={clsx(spanClassName, className)} + className={clsx( + spanClassName, + { + "crayon-chart-x-axis-tick-export-mode": exportContext, + }, + className, + )} > {value} diff --git a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss index d01cfc89..95fdfa24 100644 --- a/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss +++ b/js/packages/react-ui/src/components/Charts/shared/XAxisTick/xAxisTick.scss @@ -24,3 +24,7 @@ width: 100%; display: block; } + +.crayon-chart-x-axis-tick-export-mode { + overflow: unset; +} diff --git a/js/packages/react-ui/src/components/Charts/utils/chartExportUtils.ts b/js/packages/react-ui/src/components/Charts/utils/chartExportUtils.ts new file mode 100644 index 00000000..f32b7d5f --- /dev/null +++ b/js/packages/react-ui/src/components/Charts/utils/chartExportUtils.ts @@ -0,0 +1,77 @@ +import { toJpeg } from "html-to-image"; +import { useTheme } from "../../ThemeProvider/index.js"; + +export const useExportChart = ( + /** + * The top level container of the chart to screenshot + * + */ + exportChartContainerRef: React.RefObject | null, + /** + * The classname of the container which scrolls. This is used to expand the container before screenshotting to make the entire chart visible + * + */ + scrollableContainerClass?: string, +) => { + const { mode } = useTheme(); + + return { + exportChart: async () => { + if (!exportChartContainerRef) return; + const { current: exportChartContainer } = exportChartContainerRef; + + if (!exportChartContainer) { + return; + } + + const screenshotableNode = exportChartContainer.cloneNode(true) as HTMLDivElement; + // Position the clone off-screen + screenshotableNode.style.position = "fixed"; + screenshotableNode.style.top = "0"; + screenshotableNode.style.left = "0"; + screenshotableNode.style.zIndex = "1000"; + const scrollableContainer = screenshotableNode.querySelector( + `.${scrollableContainerClass}`, + ) as HTMLDivElement | null; + if (scrollableContainer) { + scrollableContainer.style.overflow = "visible"; + } + screenshotableNode.style.opacity = "0"; + // Append to the body to apply styles + document.body.appendChild(screenshotableNode); + + try { + const dataUrl = await toJpeg(screenshotableNode, { + skipFonts: true, + filter(domNode) { + // do not include buttons in the screenshot + const ignoredClasses = ["crayon-button-base", "crayon-icon-button"]; + if (ignoredClasses.some((className) => domNode.classList?.contains(className))) { + return false; + } + return true; + }, + width: Number(getComputedStyle(screenshotableNode).width), + canvasWidth: Number(getComputedStyle(screenshotableNode).width), + height: Number(getComputedStyle(screenshotableNode).height), + canvasHeight: Number(getComputedStyle(screenshotableNode).height), + backgroundColor: mode === "dark" ? "rgba(28,28,28,1)" : "white", + + style: { + overflow: "visible", + opacity: "100%", + }, + }); + const link = document.createElement("a"); + link.download = "chart.jpeg"; + link.href = dataUrl; + link.click(); + } catch (error) { + console.error("Failed to capture chart:", error); + } finally { + // Clean up by removing the clone from the DOM + document.body.removeChild(screenshotableNode); + } + }, + }; +}; diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 86ba67fd..bbb3081c 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -10,28 +10,28 @@ importers: devDependencies: '@typescript-eslint/eslint-plugin': specifier: ^8.21.0 - version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint: specifier: ^9.19.0 - version: 9.29.0(jiti@1.21.7) + version: 9.29.0(jiti@2.5.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.29.0(jiti@1.21.7)) + version: 9.1.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-prettier: specifier: ^5.1.3 - version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)))(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.3) + version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)))(eslint@9.29.0(jiti@2.5.1))(prettier@3.5.3) eslint-plugin-react-hooks: specifier: ^5.1.0 - version: 5.2.0(eslint@9.29.0(jiti@1.21.7)) + version: 5.2.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-react-refresh: specifier: ^0.4.18 - version: 0.4.20(eslint@9.29.0(jiti@1.21.7)) + version: 0.4.20(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-storybook: specifier: ^0.11.2 - version: 0.11.6(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 0.11.6(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint-plugin-unused-imports: specifier: ^3.2.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7)) + version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1)) prettier: specifier: ^3.2.5 version: 3.5.3 @@ -77,25 +77,25 @@ importers: version: 19.1.8 '@typescript-eslint/eslint-plugin': specifier: ^8.18.0 - version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint: specifier: ^9.17.0 - version: 9.29.0(jiti@1.21.7) + version: 9.29.0(jiti@2.5.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.29.0(jiti@1.21.7)) + version: 9.1.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)))(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.3) + version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)))(eslint@9.29.0(jiti@2.5.1))(prettier@3.5.3) eslint-plugin-react-hooks: specifier: ^5.1.0 - version: 5.2.0(eslint@9.29.0(jiti@1.21.7)) + version: 5.2.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-react-refresh: specifier: ^0.4.16 - version: 0.4.20(eslint@9.29.0(jiti@1.21.7)) + version: 0.4.20(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-unused-imports: specifier: ^3.1.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7)) + version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1)) prettier-plugin-organize-imports: specifier: ^3.2.4 version: 3.2.4(prettier@3.5.3)(typescript@5.8.3) @@ -156,6 +156,9 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + html-to-image: + specifier: 1.11.11 + version: 1.11.11 lodash-es: specifier: ^4.17.21 version: 4.17.21 @@ -231,13 +234,16 @@ importers: version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) '@storybook/react-vite': specifier: ^8.5.3 - version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0)) + version: 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0)) '@storybook/test': specifier: ^8.5.3 version: 8.6.14(storybook@8.6.14(prettier@3.5.3)) '@storybook/theming': specifier: ^8.5.3 version: 8.6.14(storybook@8.6.14(prettier@3.5.3)) + '@tailwindcss/postcss': + specifier: ^4.1.11 + version: 4.1.11 '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 @@ -258,31 +264,34 @@ importers: version: 15.5.13 '@typescript-eslint/eslint-plugin': specifier: ^8.18.0 - version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.6) concurrently: specifier: ^9.2.0 version: 9.2.0 eslint: specifier: ^9.17.0 - version: 9.29.0(jiti@1.21.7) + version: 9.29.0(jiti@2.5.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.29.0(jiti@1.21.7)) + version: 9.1.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)))(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.3) + version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)))(eslint@9.29.0(jiti@2.5.1))(prettier@3.5.3) eslint-plugin-react-hooks: specifier: ^5.1.0 - version: 5.2.0(eslint@9.29.0(jiti@1.21.7)) + version: 5.2.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-react-refresh: specifier: ^0.4.16 - version: 0.4.20(eslint@9.29.0(jiti@1.21.7)) + version: 0.4.20(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-storybook: specifier: ^0.11.1 - version: 0.11.6(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 0.11.6(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint-plugin-unused-imports: specifier: ^3.1.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7)) + version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1)) form-data: specifier: ^4.0.0 version: 4.0.3 @@ -315,7 +324,7 @@ importers: version: 4.20.3 vite: specifier: ^5.0.0 - version: 5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0) + version: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0) packages/stream: dependencies: @@ -340,25 +349,25 @@ importers: version: 22.15.32 '@typescript-eslint/eslint-plugin': specifier: ^8.18.0 - version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + version: 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint: specifier: ^9.17.0 - version: 9.29.0(jiti@1.21.7) + version: 9.29.0(jiti@2.5.1) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.29.0(jiti@1.21.7)) + version: 9.1.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)))(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.3) + version: 5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)))(eslint@9.29.0(jiti@2.5.1))(prettier@3.5.3) eslint-plugin-react-hooks: specifier: ^5.1.0 - version: 5.2.0(eslint@9.29.0(jiti@1.21.7)) + version: 5.2.0(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-react-refresh: specifier: ^0.4.16 - version: 0.4.20(eslint@9.29.0(jiti@1.21.7)) + version: 0.4.20(eslint@9.29.0(jiti@2.5.1)) eslint-plugin-unused-imports: specifier: ^3.1.0 - version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7)) + version: 3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1)) prettier-plugin-organize-imports: specifier: ^3.2.4 version: 3.2.4(prettier@3.5.3)(typescript@5.8.3) @@ -821,6 +830,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0': resolution: {integrity: sha512-qYDdL7fPwLRI+bJNurVcis+tNgJmvWjH4YTBGXTA8xMuxFrnAz6E5o35iyzyKbq5J5Lr8mJGfrR5GXl+WGwhgQ==} peerDependencies: @@ -1729,6 +1742,94 @@ packages: peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 + '@tailwindcss/node@4.1.11': + resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==} + + '@tailwindcss/oxide-android-arm64@4.1.11': + resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.11': + resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.11': + resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.11': + resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.11': + resolution: {integrity: sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -2077,6 +2178,13 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2194,6 +2302,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chromatic@11.29.0: resolution: {integrity: sha512-yisBlntp9hHVj19lIQdpTlcYIXuU9H/DbFuu6tyWHmj6hWT2EtukCCcxYXL78XdQt1vm2GfIrtgtKpj/Rzmo4A==} hasBin: true @@ -2365,6 +2477,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} @@ -2667,6 +2783,9 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2792,6 +2911,9 @@ packages: highlightjs-vue@1.0.0: resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} + html-to-image@1.11.11: + resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} @@ -2923,6 +3045,10 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2973,6 +3099,70 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -3220,6 +3410,15 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3251,6 +3450,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -3784,10 +3987,17 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + tailwindcss@4.1.11: + resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==} + tapable@2.2.2: resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} engines: {node: '>=6'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + terser-webpack-plugin@5.3.14: resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} engines: {node: '>= 10.13.0'} @@ -4076,6 +4286,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.8.0: resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} @@ -4389,9 +4603,9 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@2.5.1))': dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -4476,12 +4690,16 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0))': + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0))': dependencies: glob: 10.4.5 magic-string: 0.27.0 react-docgen-typescript: 2.4.0(typescript@5.8.3) - vite: 5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0) + vite: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0) optionalDependencies: typescript: 5.8.3 @@ -5245,13 +5463,13 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) - '@storybook/builder-vite@8.6.14(storybook@8.6.14(prettier@3.5.3))(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0))': + '@storybook/builder-vite@8.6.14(storybook@8.6.14(prettier@3.5.3))(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0))': dependencies: '@storybook/csf-plugin': 8.6.14(storybook@8.6.14(prettier@3.5.3)) browser-assert: 1.2.1 storybook: 8.6.14(prettier@3.5.3) ts-dedent: 2.2.0 - vite: 5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0) + vite: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0) '@storybook/components@8.6.14(storybook@8.6.14(prettier@3.5.3))': dependencies: @@ -5322,11 +5540,11 @@ snapshots: react-dom: 19.1.0(react@19.1.0) storybook: 8.6.14(prettier@3.5.3) - '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0))': + '@storybook/react-vite@8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.43.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.3)(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0)) '@rollup/pluginutils': 5.2.0(rollup@4.43.0) - '@storybook/builder-vite': 8.6.14(storybook@8.6.14(prettier@3.5.3))(vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0)) + '@storybook/builder-vite': 8.6.14(storybook@8.6.14(prettier@3.5.3))(vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0)) '@storybook/react': 8.6.14(@storybook/test@8.6.14(storybook@8.6.14(prettier@3.5.3)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@8.6.14(prettier@3.5.3))(typescript@5.8.3) find-up: 5.0.0 magic-string: 0.30.17 @@ -5336,7 +5554,7 @@ snapshots: resolve: 1.22.10 storybook: 8.6.14(prettier@3.5.3) tsconfig-paths: 4.2.0 - vite: 5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0) + vite: 5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0) optionalDependencies: '@storybook/test': 8.6.14(storybook@8.6.14(prettier@3.5.3)) transitivePeerDependencies: @@ -5374,6 +5592,78 @@ snapshots: dependencies: storybook: 8.6.14(prettier@3.5.3) + '@tailwindcss/node@4.1.11': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.11 + + '@tailwindcss/oxide-android-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide@4.1.11': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-x64': 4.1.11 + '@tailwindcss/oxide-freebsd-x64': 4.1.11 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.11 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-x64-musl': 4.1.11 + '@tailwindcss/oxide-wasm32-wasi': 4.1.11 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.11 + + '@tailwindcss/postcss@4.1.11': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.11 + '@tailwindcss/oxide': 4.1.11 + postcss: 8.5.6 + tailwindcss: 4.1.11 + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.27.1 @@ -5525,15 +5815,15 @@ snapshots: '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.34.1 - '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.34.1 - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -5542,14 +5832,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.34.1 '@typescript-eslint/types': 8.34.1 '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.34.1 debug: 4.4.1 - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -5572,12 +5862,12 @@ snapshots: dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) debug: 4.4.1 - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -5601,13 +5891,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': + '@typescript-eslint/utils@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.5.1)) '@typescript-eslint/scope-manager': 8.34.1 '@typescript-eslint/types': 8.34.1 '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -5801,6 +6091,16 @@ snapshots: asynckit@0.4.0: {} + autoprefixer@10.4.21(postcss@8.5.6): + dependencies: + browserslist: 4.25.0 + caniuse-lite: 1.0.30001723 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -5918,6 +6218,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + chromatic@11.29.0: {} chrome-trace-event@1.0.4: {} @@ -6047,6 +6349,8 @@ snapshots: detect-libc@1.0.3: optional: true + detect-libc@2.0.4: {} + detect-node-es@1.1.0: {} devlop@1.1.0: @@ -6179,44 +6483,44 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)): + eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)): dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) - eslint-plugin-prettier@5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@1.21.7)))(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.3): + eslint-plugin-prettier@5.5.0(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.29.0(jiti@2.5.1)))(eslint@9.29.0(jiti@2.5.1))(prettier@3.5.3): dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) prettier: 3.5.3 prettier-linter-helpers: 1.0.0 synckit: 0.11.8 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 9.1.0(eslint@9.29.0(jiti@1.21.7)) + eslint-config-prettier: 9.1.0(eslint@9.29.0(jiti@2.5.1)) - eslint-plugin-react-hooks@5.2.0(eslint@9.29.0(jiti@1.21.7)): + eslint-plugin-react-hooks@5.2.0(eslint@9.29.0(jiti@2.5.1)): dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) - eslint-plugin-react-refresh@0.4.20(eslint@9.29.0(jiti@1.21.7)): + eslint-plugin-react-refresh@0.4.20(eslint@9.29.0(jiti@2.5.1)): dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) - eslint-plugin-storybook@0.11.6(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3): + eslint-plugin-storybook@0.11.6(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3): dependencies: '@storybook/csf': 0.1.13 - '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) - eslint: 9.29.0(jiti@1.21.7) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) + eslint: 9.29.0(jiti@2.5.1) ts-dedent: 2.2.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7)): + eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1)): dependencies: - eslint: 9.29.0(jiti@1.21.7) + eslint: 9.29.0(jiti@2.5.1) eslint-rule-composer: 0.3.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.29.0(jiti@2.5.1))(typescript@5.8.3) eslint-rule-composer@0.3.0: {} @@ -6234,9 +6538,9 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.29.0(jiti@1.21.7): + eslint@9.29.0(jiti@2.5.1): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.1 '@eslint/config-helpers': 0.2.3 @@ -6272,7 +6576,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 1.21.7 + jiti: 2.5.1 transitivePeerDependencies: - supports-color @@ -6385,6 +6689,8 @@ snapshots: format@0.2.2: {} + fraction.js@4.3.7: {} + fsevents@2.3.3: optional: true @@ -6557,6 +6863,8 @@ snapshots: highlightjs-vue@1.0.0: {} + html-to-image@1.11.11: {} + html-url-attributes@3.0.1: {} ignore@5.3.2: {} @@ -6669,6 +6977,8 @@ snapshots: jiti@1.21.7: {} + jiti@2.5.1: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -6710,6 +7020,51 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -7169,6 +7524,12 @@ snapshots: minipass@7.1.2: {} + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + ms@2.1.3: {} mz@2.7.0: @@ -7197,6 +7558,8 @@ snapshots: normalize-path@3.0.0: {} + normalize-range@0.1.2: {} + object-assign@4.1.1: {} object-hash@3.0.0: {} @@ -7826,8 +8189,19 @@ snapshots: transitivePeerDependencies: - ts-node + tailwindcss@4.1.11: {} + tapable@2.2.2: {} + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + terser-webpack-plugin@5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -8030,7 +8404,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@5.4.19(@types/node@22.15.32)(sass@1.89.2)(terser@5.43.0): + vite@5.4.19(@types/node@22.15.32)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.0): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -8038,6 +8412,7 @@ snapshots: optionalDependencies: '@types/node': 22.15.32 fsevents: 2.3.3 + lightningcss: 1.30.1 sass: 1.89.2 terser: 5.43.0 @@ -8119,6 +8494,8 @@ snapshots: yallist@3.1.1: {} + yallist@5.0.0: {} + yaml@2.8.0: {} yargs-parser@21.1.1: {}