diff --git a/demo/ts/app.tsx b/demo/ts/app.tsx deleted file mode 100644 index 5fca81a73..000000000 --- a/demo/ts/app.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from "react"; -import * as ReactDOM from "react-dom"; - -import ThemeBuilder from "./components/theme-builder"; - -interface AppState { - route: string; -} - -const containerStyle: React.CSSProperties = { - display: "flex", - flexDirection: "column", - height: "100vh", - fontFamily: "sans-serif", -}; - -const contentStyle: React.CSSProperties = { - display: "flex", - gap: "20px", - overflow: "hidden", - flex: 1, -}; - -class App extends React.Component { - render() { - return ( -
-
- -
-
- ); - } -} - -// eslint-disable-next-line react/no-deprecated -ReactDOM.render(, document.getElementById("content")); diff --git a/demo/ts/components/theme-builder/button.tsx b/demo/ts/components/theme-builder/button.tsx deleted file mode 100644 index bf35a5fd8..000000000 --- a/demo/ts/components/theme-builder/button.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; - -type ButtonProps = { - onClick: () => void; - children: React.ReactNode; - className?: string; - ariaLabel?: string; - disabled?: boolean; -}; - -const Button = ({ - onClick, - children, - className = "", - ariaLabel = "", - disabled = false, - ...props -}: ButtonProps) => { - const baseClasses = - "py-2 px-5 border-0 rounded-md cursor-pointer text-sm bg-primary text-white hover:bg-secondary disabled:bg-gray-200 disabled:cursor-not-allowed"; - - return ( - - ); -}; - -export default Button; diff --git a/demo/ts/components/theme-builder/color-picker.tsx b/demo/ts/components/theme-builder/color-picker.tsx deleted file mode 100644 index 39dd8c58d..000000000 --- a/demo/ts/components/theme-builder/color-picker.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import { TiPencil } from "react-icons/ti"; -import clsx from "clsx"; - -type ColorPickerProps = { - label?: string; - color: string; - id: string; - onColorChange: (color: string) => void; - showColorName?: boolean; - className?: string; -}; - -const ColorPicker = ({ - label, - color, - id, - onColorChange, - showColorName = false, - className, -}: ColorPickerProps) => { - const [isPickerOpen, setIsPickerOpen] = React.useState(false); - - const handleChange = (event: React.ChangeEvent) => { - if (onColorChange) { - onColorChange(event.target.value); - } - }; - - return ( -
- {label && ( - - )} -
-
-
-
- {!showColorName && ( -
- -
- )} -
- {showColorName && ( - - {color} - - )} -
- {showColorName && ( -
- -
- )} - setIsPickerOpen(true)} - onBlur={() => setIsPickerOpen(false)} - /> -
-
- ); -}; - -export default ColorPicker; diff --git a/demo/ts/components/theme-builder/color-scale-options.tsx b/demo/ts/components/theme-builder/color-scale-options.tsx deleted file mode 100644 index f9e6c3eef..000000000 --- a/demo/ts/components/theme-builder/color-scale-options.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from "react"; -import { ColorScalePropType, VictoryThemeDefinition } from "victory-core"; -import Select from "./select"; -import ColorPicker from "./color-picker"; - -export type ColorChangeArgs = { - newColor: string; - index: number; - colorScale: string; -}; - -type ColorScaleOptionsProps = { - palette?: VictoryThemeDefinition["palette"]; - activeColorScale?: ColorScalePropType; - onColorChange: (args: ColorChangeArgs) => void; - onColorScaleChange: (colorScale: string) => void; -}; - -const colorScales = [ - { - label: "Qualitative", - value: "qualitative", - }, - { - label: "Heatmap", - value: "heatmap", - }, - { - label: "Warm", - value: "warm", - }, - { - label: "Cool", - value: "cool", - }, - { - label: "Red", - value: "red", - }, - { - label: "Green", - value: "green", - }, -]; - -const ColorScaleOptions = ({ - activeColorScale, - palette, - onColorChange, - onColorScaleChange, -}: ColorScaleOptionsProps) => { - return ( -
- - ); - case "colorPicker": - return ( - - ); - default: - return null; - } -}; - -const ConfigMapper = ({ - themeConfig, - activeColorScale, - updateThemeConfig, - handleColorScaleChange, -}) => { - return ( - <> - {optionsConfig.map((section, index) => ( - - {section.fields.map((field, i) => { - return ( - - ); - })} - - ))} - - ); -}; - -export default ConfigMapper; diff --git a/demo/ts/components/theme-builder/config-preview.tsx b/demo/ts/components/theme-builder/config-preview.tsx deleted file mode 100644 index 05e73d9ea..000000000 --- a/demo/ts/components/theme-builder/config-preview.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from "react"; -import { VictoryThemeDefinition } from "victory-core"; -import Button from "./button"; -import { Prism, SyntaxHighlighterProps } from "react-syntax-highlighter"; - -const SyntaxHighlighter = Prism as any as React.FC; - -type ConfigPreviewProps = { - config: VictoryThemeDefinition; - onClose: () => void; -}; - -const ConfigPreview = ({ config, onClose }: ConfigPreviewProps) => { - const [copyStatus, setCopyStatus] = React.useState(null); - - const handleCopyThemeConfig = () => { - navigator.clipboard - .writeText(JSON.stringify(config, null, 2)) - .then(() => { - setCopyStatus("Copied successfully."); - return "Theme config copied to clipboard"; - }) - .catch(() => { - setCopyStatus("Failed to copy."); - }); - }; - - const handleClose = () => { - onClose(); - }; - - return ( -
- -

Theme Config Preview

- - {JSON.stringify(config, null, 2)} - -
- {copyStatus && ( - {copyStatus} - )} - -
-
- ); -}; -export default ConfigPreview; diff --git a/demo/ts/components/theme-builder/index.tsx b/demo/ts/components/theme-builder/index.tsx deleted file mode 100644 index 51ea9517a..000000000 --- a/demo/ts/components/theme-builder/index.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import React from "react"; -import "./tailwind.css"; - -import { - ColorScalePropType, - VictoryTheme, - VictoryThemeDefinition, -} from "victory-core"; -import { VictoryChart } from "victory-chart"; -import { VictoryAxis } from "victory-axis"; -import { VictoryStack } from "victory-stack"; -import { VictoryBar } from "victory-bar"; -import { VictoryArea } from "victory-area"; -import { VictoryTooltip } from "victory-tooltip"; -import Select from "./select"; -import ConfigPreview from "./config-preview"; -import Button from "./button"; -import ConfigMapper from "./config-mapper"; -import { setNestedConfigValue } from "./utils"; -import optionsConfig from "./options-config"; - -export type ThemeOption = { - name: string; - config?: VictoryThemeDefinition; -}; - -const themes: ThemeOption[] = [ - { name: "Clean", config: VictoryTheme.clean }, - { name: "Material", config: VictoryTheme.material }, - { name: "Grayscale", config: VictoryTheme.grayscale }, -]; - -const themeOptions = [ - { label: "Select a theme", value: undefined }, - ...themes.map((theme) => ({ - label: theme.name, - value: theme.name, - })), -]; - -const sampleStackData = [ - { - x: 1, - y: 2, - }, - { - x: 2, - y: 3, - }, - { - x: 3, - y: 5, - }, - { - x: 4, - y: 4, - }, - { - x: 5, - y: 7, - }, -]; - -const chartStyle: { [key: string]: React.CSSProperties } = { - parent: { - border: "1px solid #ccc", - width: "100%", - height: 400, - display: "flex", - justifyContent: "center", - alignItems: "center", - }, -}; - -const ThemeBuilder = () => { - const [baseTheme, setBaseTheme] = React.useState( - undefined, - ); - const [customThemeConfig, setCustomThemeConfig] = React.useState< - VictoryThemeDefinition | undefined - >(undefined); - const [activeColorScale, setActiveColorScale] = - React.useState("qualitative"); - const [showThemeConfigPreview, setShowThemeConfigPreview] = - React.useState(false); - const [showTooltips, setShowTooltips] = React.useState(false); - - const handleThemeSelect = (themeName: string) => { - const theme = themes.find((t) => t.name === themeName); - if (!theme) { - setBaseTheme(undefined); - setCustomThemeConfig(undefined); - return; - } - setBaseTheme(theme); - setCustomThemeConfig({ ...theme?.config }); - }; - - const updateCustomThemeConfig = ( - path: string | string[], - newValue: unknown, - ) => { - if (!customThemeConfig) return; - const updatedConfig = setNestedConfigValue( - customThemeConfig, - path, - newValue, - ); - setCustomThemeConfig(updatedConfig); - }; - - const handleColorScaleChange = (colorScale: string) => { - setActiveColorScale(colorScale as ColorScalePropType); - }; - - const handleThemeConfigPreviewOpen = () => { - setShowThemeConfigPreview(true); - }; - - const handleThemeConfigPreviewClose = () => { - setShowThemeConfigPreview(false); - }; - - return ( -
-
- )} - - {showThemeConfigPreview && customThemeConfig && ( - - )} - - ); -}; -export default ThemeBuilder; diff --git a/demo/ts/components/theme-builder/options-config.tsx b/demo/ts/components/theme-builder/options-config.tsx deleted file mode 100644 index 64334ce61..000000000 --- a/demo/ts/components/theme-builder/options-config.tsx +++ /dev/null @@ -1,1037 +0,0 @@ -import React from "react"; -import { VictoryArea } from "victory-area"; -import { VictoryAxis } from "victory-axis"; -import { VictoryBar } from "victory-bar"; -import { VictoryCandlestick } from "victory-candlestick"; -import { VictoryErrorBar } from "victory-errorbar"; -import { VictoryHistogram } from "victory-histogram"; -import { VictoryLegend } from "victory-legend"; -import { VictoryLine } from "victory-line"; -import { VictoryPie } from "victory-pie"; -import { VictoryScatter } from "victory-scatter"; -import { VictoryVoronoi } from "victory-voronoi"; -import { VictoryPolarAxis } from "victory-polar-axis"; - -type ThemeBuilderFieldConfig = - | { - type: "section" | "colorScale"; - label: string; - fields?: ThemeBuilderFieldConfig[]; - } - | { - type: "slider" | "select" | "colorPicker"; - label: string; - path: string | string[]; - min?: number; - max?: number; - step?: number; - unit?: string; - default: number | string; - options?: { label: string; value: string }[]; - }; - -enum StrokeProps { - STROKE = "Stroke", - STROKE_WIDTH = "Stroke Width", - STROKE_DASH_ARRAY = "Stroke Dash Array", - STROKE_LINE_CAP = "Stroke Line Cap", - STROKE_LINE_JOIN = "Stroke Line Join", -} - -type ThemeBuilderOptionsConfig = { - type: "section"; - title: string; - hasVictoryChart?: boolean; - content?: (props: any) => React.ReactNode; - fields: ThemeBuilderFieldConfig[]; -}[]; - -const defaultFill = "#000"; - -const getPath = (basePath: string | string[], key: string) => { - if (Array.isArray(basePath)) { - return basePath.map((p) => `${p}.${key}`); - } - return `${basePath}.${key}`; -}; - -const getBaseStrokeConfig = ( - basePath: string | string[], - includedStrokeProps: StrokeProps[] = [], -): ThemeBuilderFieldConfig[] => { - const config = [ - { - type: "colorPicker", - label: StrokeProps.STROKE, - default: defaultFill, - path: getPath(basePath, "stroke"), - }, - { - type: "slider", - label: StrokeProps.STROKE_WIDTH, - min: 0, - max: 5, - unit: "px", - default: 1, - path: getPath(basePath, "strokeWidth"), - }, - { - type: "slider", - label: StrokeProps.STROKE_DASH_ARRAY, - min: 0, - max: 10, - default: 0, - path: getPath(basePath, "strokeDasharray"), - }, - { - type: "select", - label: StrokeProps.STROKE_LINE_CAP, - options: [ - { label: "Round", value: "round" }, - { label: "Square", value: "square" }, - { label: "Butt", value: "butt" }, - ], - default: "round", - path: getPath(basePath, "strokeLinecap"), - }, - { - type: "select", - label: StrokeProps.STROKE_LINE_JOIN, - options: [ - { label: "Round", value: "round" }, - { label: "Bevel", value: "bevel" }, - { label: "Miter", value: "miter" }, - ], - default: "round", - path: getPath(basePath, "strokeLinejoin"), - }, - ] as ThemeBuilderFieldConfig[]; - return includedStrokeProps.length - ? config.filter((field) => - includedStrokeProps.includes(field.label as StrokeProps), - ) - : config; -}; - -const getBaseLabelsConfig = ( - basePath: string | string[], -): ThemeBuilderFieldConfig[] => [ - { - type: "slider", - label: "Font Size", - min: 10, - max: 24, - unit: "px", - path: getPath(basePath, "fontSize"), - default: 12, - }, - { - type: "slider", - label: "Padding", - min: 0, - max: 50, - unit: "px", - path: getPath(basePath, "padding"), - default: 8, - }, - { - type: "colorPicker", - label: "Fill", - path: getPath(basePath, "fill"), - default: defaultFill, - }, -]; - -const optionsConfig: ThemeBuilderOptionsConfig = [ - { - type: "section", - title: "Palette", - fields: [ - { - type: "colorScale", - label: "Color Scale", - }, - ], - }, - { - type: "section", - title: "Global Settings", - fields: [ - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig([ - "axis.style.axisLabel", - "polarAxis.style.tickLabels", - "polarDependentAxis.style.tickLabels", - "tooltip.style", - "area.style.labels", - "bar.style.labels", - "candlestick.style.labels", - "errorbar.style.labels", - "histogram.style.labels", - "legend.style.labels", - "line.style.labels", - "pie.style.labels", - "scatter.style.labels", - "voronoi.style.labels", - ]), - }, - { - type: "section", - label: "Data", - fields: getBaseStrokeConfig([ - "area.style.data", - "bar.style.data", - "candlestick.style.data", - "errorbar.style.data", - "histogram.style.data", - "line.style.data", - "pie.style.data", - "scatter.style.data", - "voronoi.style.data", - ]), - }, - ], - }, - { - type: "section", - title: "Axis", - fields: [ - { - type: "section", - label: "General", - fields: [ - { - type: "colorPicker", - label: "Fill", - default: defaultFill, - path: "axis.style.axis.fill", - }, - ...getBaseStrokeConfig("axis.style.axis", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - StrokeProps.STROKE_LINE_CAP, - StrokeProps.STROKE_LINE_JOIN, - ]), - ], - }, - { - type: "section", - label: "Grid", - fields: [ - { - type: "colorPicker", - label: "Fill", - default: defaultFill, - path: "axis.style.grid.fill", - }, - ...getBaseStrokeConfig("axis.style.grid"), - ], - }, - { - type: "section", - label: "Ticks", - fields: [ - { - type: "slider", - label: "Size", - min: 0, - max: 50, - unit: "px", - default: 5, - path: "axis.style.ticks.size", - }, - ...getBaseStrokeConfig("axis.style.ticks", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("axis.style.axisLabel"), - }, - ], - }, - { - type: "section", - title: "Polar Axis", - content: (props) => [ - , - , - ], - fields: [ - { - type: "section", - label: "General", - fields: getBaseStrokeConfig("polarAxis.style.axis", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - }, - { - type: "section", - label: "Grid", - fields: getBaseStrokeConfig("polarAxis.style.grid"), - }, - { - type: "section", - label: "Ticks", - fields: [ - { - type: "slider", - label: "Size", - min: 0, - max: 50, - unit: "px", - default: 5, - path: "polarAxis.style.ticks.size", - }, - ...getBaseStrokeConfig("polarAxis.style.ticks", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - StrokeProps.STROKE_LINE_CAP, - StrokeProps.STROKE_LINE_JOIN, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("polarAxis.style.tickLabels"), - }, - ], - }, - { - type: "section", - title: "Polar Dependent Axis", - fields: [ - { - type: "section", - label: "General", - fields: getBaseStrokeConfig("polarDependentAxis.style.axis", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - }, - { - type: "section", - label: "Grid", - fields: getBaseStrokeConfig("polarDependentAxis.style.grid"), - }, - { - type: "section", - label: "Ticks", - fields: [ - { - type: "slider", - label: "Size", - min: 0, - max: 50, - unit: "px", - default: 5, - path: "polarDependentAxis.style.ticks.size", - }, - ...getBaseStrokeConfig("polarDependentAxis.style.ticks", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - StrokeProps.STROKE_LINE_CAP, - StrokeProps.STROKE_LINE_JOIN, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("polarDependentAxis.style.tickLabels"), - }, - ], - }, - { - type: "section", - title: "Area Chart", - content: (props) => [ - , - , - , - ], - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "slider", - label: "Fill Opacity", - min: 0, - max: 1, - step: 0.1, - path: "area.style.data.fillOpacity", - default: 1, - }, - ...getBaseStrokeConfig("area.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - { - type: "colorPicker", - label: "Fill", - path: "area.style.data.fill", - default: defaultFill, - }, - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("area.style.labels"), - }, - ], - }, - { - type: "section", - title: "Bar Chart", - content: (props) => [ - , - , - , - ], - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "colorPicker", - label: "Fill", - path: "bar.style.data.fill", - default: defaultFill, - }, - ...getBaseStrokeConfig("bar.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - { - type: "slider", - label: "Fill Opacity", - min: 0, - max: 1, - step: 0.1, - path: "bar.style.data.fillOpacity", - default: 1, - }, - { - type: "slider", - label: "Top Corner Radius", - path: "bar.cornerRadius.top", - max: 2, - step: 0.5, - default: 0, - }, - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("bar.style.labels"), - }, - ], - }, - { - type: "section", - title: "Candlestick Chart", - content: (props) => ( - - ), - fields: [ - { - type: "section", - label: "Data", - fields: [ - ...getBaseStrokeConfig("candlestick.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - { - type: "slider", - label: "Border Radius", - max: 2, - step: 0.5, - default: 0, - path: "candlestick.style.data.rx", - }, - { - type: "slider", - label: "Wick Width", - min: 0, - max: 5, - unit: "px", - path: "candlestick.wickStrokeWidth", - default: 2, - }, - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("candlestick.style.labels"), - }, - { - type: "section", - label: "Colors", - fields: [ - { - type: "colorPicker", - label: "Positive Color", - path: "candlestick.candleColors.positive", - default: "#ffffff", - }, - { - type: "colorPicker", - label: "Negative Color", - path: "candlestick.candleColors.negative", - default: defaultFill, - }, - ], - }, - ], - }, - { - type: "section", - title: "Error Bar", - content: (props) => ( - - ), - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "slider", - label: "Border Width", - min: 0, - max: 10, - unit: "px", - path: "errorbar.borderWidth", - default: 8, - }, - ...getBaseStrokeConfig("errorbar.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - StrokeProps.STROKE_LINE_CAP, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("errorbar.style.labels"), - }, - ], - }, - { - type: "section", - title: "Histogram", - content: (props) => ( - `Bin count:\n ${datum.x}`} - /> - ), - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "colorPicker", - label: "Fill", - path: "histogram.style.data.fill", - default: defaultFill, - }, - { - type: "slider", - label: "Fill Opacity", - min: 0, - max: 1, - step: 0.1, - path: "histogram.style.data.fillOpacity", - default: 1, - }, - { - type: "slider", - label: "Top Corner Radius", - path: "histogram.cornerRadius.top", - max: 10, - step: 0.5, - default: 0, - }, - { - type: "slider", - label: "Bin Spacing", - min: 0, - max: 10, - unit: "px", - path: "histogram.binSpacing", - default: 4, - }, - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("histogram.style.labels"), - }, - ], - }, - { - type: "section", - title: "Legend", - content: (props) => [ - , - datum.fill, - }, - }} - />, - ], - fields: [ - { - type: "section", - label: "General", - fields: [ - { - type: "slider", - label: "Gutter", - min: 0, - max: 50, - unit: "px", - path: "legend.gutter", - default: 20, - }, - { - type: "slider", - label: "Border Padding", - min: 0, - max: 50, - unit: "px", - path: "legend.borderPadding", - default: 10, - }, - { - type: "select", - label: "Orientation", - options: [ - { label: "Horizontal", value: "horizontal" }, - { label: "Vertical", value: "vertical" }, - ], - path: "legend.orientation", - default: "horizontal", - }, - { - type: "select", - label: "Title Orientation", - options: [ - { label: "Top", value: "top" }, - { label: "Bottom", value: "bottom" }, - { label: "Left", value: "left" }, - { label: "Right", value: "right" }, - ], - path: "legend.titleOrientation", - default: "top", - }, - { - type: "select", - label: "Data Type", - options: [ - { label: "Circle", value: "circle" }, - { label: "Square", value: "square" }, - { label: "Diamond", value: "diamond" }, - { label: "Star", value: "star" }, - ], - path: "legend.style.data.type", - default: "circle", - }, - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("legend.style.labels"), - }, - { - type: "section", - label: "Title", - fields: getBaseLabelsConfig("legend.style.title"), - }, - { - type: "section", - label: "Border", - fields: getBaseStrokeConfig("legend.style.border", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - }, - ], - }, - { - type: "section", - title: "Line Chart", - content: (props) => [ - , - , - datum.y} - />, - ], - fields: [ - { - type: "section", - label: "Data", - fields: getBaseStrokeConfig("line.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - StrokeProps.STROKE_LINE_CAP, - StrokeProps.STROKE_LINE_JOIN, - ]), - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("line.style.labels"), - }, - ], - }, - { - type: "section", - title: "Pie Chart", - hasVictoryChart: false, - content: (props) => ( - - ), - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "slider", - label: "Padding", - min: 0, - max: 50, - unit: "px", - path: "pie.style.data.padding", - default: 0, - }, - ...getBaseStrokeConfig("pie.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("pie.style.labels"), - }, - ], - }, - { - type: "section", - title: "Scatter Chart", - content: (props) => ( - - ), - fields: [ - { - type: "section", - label: "Data", - fields: [ - { - type: "colorPicker", - label: "Fill", - default: defaultFill, - path: "scatter.style.data.fill", - }, - { - type: "slider", - label: "Opacity", - min: 0, - max: 1, - step: 0.1, - default: 1, - path: "scatter.style.data.opacity", - }, - ...getBaseStrokeConfig("scatter.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("scatter.style.labels"), - }, - ], - }, - { - type: "section", - title: "Tooltip", - fields: [ - { - type: "section", - label: "General", - fields: [ - ...getBaseLabelsConfig("tooltip.style"), - { - type: "slider", - label: "Corner Radius", - min: 0, - max: 10, - default: 0, - path: "tooltip.cornerRadius", - }, - { - type: "slider", - label: "Pointer Length", - min: 0, - max: 20, - default: 10, - path: "tooltip.pointerLength", - }, - ], - }, - { - type: "section", - label: "Flyout", - fields: [ - { - type: "colorPicker", - label: "Fill", - default: "#FFFFFF", - path: "tooltip.flyoutStyle.fill", - }, - ...getBaseStrokeConfig("tooltip.flyoutStyle", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - { - type: "select", - label: "Pointer Events", - options: [ - { label: "None", value: "none" }, - { label: "All", value: "all" }, - ], - default: "none", - path: "tooltip.flyoutStyle.pointerEvents", - }, - ], - }, - ], - }, - { - type: "section", - title: "Voronoi", - content: (props) => ( - - ), - fields: [ - { - type: "section", - label: "General", - fields: [ - { - type: "colorPicker", - label: "Fill", - default: "#FFFFFF", - path: "voronoi.style.data.fill", - }, - ...getBaseStrokeConfig("voronoi.style.data", [ - StrokeProps.STROKE, - StrokeProps.STROKE_WIDTH, - ]), - ], - }, - { - type: "section", - label: "Labels", - fields: getBaseLabelsConfig("voronoi.style.labels"), - }, - ], - }, -]; - -export default optionsConfig; diff --git a/demo/ts/components/theme-builder/slider.tsx b/demo/ts/components/theme-builder/slider.tsx deleted file mode 100644 index 5949df6e1..000000000 --- a/demo/ts/components/theme-builder/slider.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from "react"; - -type SliderProps = { - label: string; - id: string; - value?: number; - unit?: string; - onChange?: (value: number) => void; - min?: number; - max?: number; - step?: number; - className?: string; -}; - -const Slider = ({ - label, - id, - value, - unit, - onChange, - min, - max, - step = 1, - className, -}: SliderProps) => { - const handleChange = (event) => { - const newValue = event.target.valueAsNumber; - if (onChange) { - onChange(newValue); - } - }; - - return ( -
- - -
- ); -}; -export default Slider; diff --git a/demo/ts/components/theme-builder/tailwind.css b/demo/ts/components/theme-builder/tailwind.css deleted file mode 100644 index 87a75ca86..000000000 --- a/demo/ts/components/theme-builder/tailwind.css +++ /dev/null @@ -1,9 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - html { - font-family: "Roboto", system-ui, sans-serif; - } -} diff --git a/demo/ts/components/theme-builder/utils.ts b/demo/ts/components/theme-builder/utils.ts deleted file mode 100644 index 44fd0449e..000000000 --- a/demo/ts/components/theme-builder/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { VictoryThemeDefinition } from "victory-core"; - -export const setNestedConfigValue = ( - config: VictoryThemeDefinition, - paths: string | string[], - value: unknown, -) => { - const updatedConfig = { ...config }; - const pathsArray = Array.isArray(paths) ? paths : [paths]; - - pathsArray.forEach((path) => { - const pathArray = path.split("."); - pathArray.reduce((acc, key, i) => { - if (i === pathArray.length - 1) { - acc[key] = value; - } else { - acc[key] = { ...acc[key] }; - } - return acc[key]; - }, updatedConfig); - }); - - return updatedConfig; -}; - -export const getConfigValue = ( - config: VictoryThemeDefinition, - path: string | string[], - defaultValue?: unknown, -) => { - const pathString = Array.isArray(path) ? path[0] : path; - if (!pathString) return undefined; - const pathArray = pathString.split("."); - return pathArray.reduce((acc, key) => { - return acc && acc[key] ? acc[key] : defaultValue || undefined; - }, config); -}; diff --git a/demo/ts/index.html b/demo/ts/index.html deleted file mode 100644 index bc28f1d23..000000000 --- a/demo/ts/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - Demo (Typescript) - - - - - - - - - -
-

Loading...

-
- - - diff --git a/demo/ts/tsconfig.json b/demo/ts/tsconfig.json deleted file mode 100644 index aedc2136d..000000000 --- a/demo/ts/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "paths": { - "victory*": ["../../packages/victory*"], - } - } -} diff --git a/eslint.config.mjs b/eslint.config.mjs index f72dedd3d..709f828b1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -137,17 +137,6 @@ export default tseslint.config( }, }, - // Overrides for Demos - { - files: ["**/demo/**/*.{ts,tsx}"], - rules: { - "no-magic-numbers": "off", - "no-restricted-imports": "off", - "react/no-multi-comp": "off", - "@typescript-eslint/no-empty-object-type": "off", - }, - }, - // Overrides for Storybook { files: ["**/stories/**/*.ts", "**/stories/**/*.stories.tsx"], diff --git a/package.json b/package.json index 057de6152..1996951d7 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,6 @@ "@types/prop-types": "^15.7.5", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", - "autoprefixer": "^10.0.1", "babel-jest": "29.7.0", "babel-loader": "9.1.3", "babel-plugin-module-resolver": "5.0.0", @@ -59,7 +58,6 @@ "concurrently": "^9.0.1", "cpx2": "^4.2.0", "cross-env": "^7.0.3", - "css-loader": "^7.1.2", "eslint": "^9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.9.0", @@ -80,24 +78,18 @@ "metro-react-native-babel-preset": "0.77.0", "nps": "^5.9.0", "octokit": "^3.1.1", - "postcss": "^8", - "postcss-loader": "^8.1.1", "prettier": "^3.3.3", "prop-types": "^15.8.1", "react": "^18.1.0", "react-dom": "^18.1.0", "react-hot-loader": "4.13.0", "react-icons": "^5.3.0", - "react-syntax-highlighter": "^15.6.1", "react-test-renderer": "^18.1.0", "remark-parse": "^7.0.1", "remark-stringify": "^7.0.3", "rimraf": "^3.0.2", - "sass": "^1.80.3", "seedrandom": "^3.0.5", "storybook": "^8.4.1", - "style-loader": "^4.0.0", - "tailwindcss": "^3.3.0", "ts-jest": "^29.2.5", "ts-loader": "^9.3.0", "ts-node": "^10.9.1", @@ -280,12 +272,11 @@ ] }, "lint:root": { - "command": "eslint --color *.js scripts config demo stories test website", + "command": "eslint --color *.js scripts config stories test website", "files": [ "*.js", "scripts", "config", - "demo", "stories", "test", "website", @@ -297,12 +288,11 @@ ] }, "lint:root:fix": { - "command": "eslint --color --fix *.js scripts config demo stories test website", + "command": "eslint --color --fix *.js scripts config stories test website", "files": [ "*.js", "scripts", "config", - "demo", "stories", "test", "website", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c4db7557..960c8e366 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,9 +98,6 @@ importers: '@types/react-dom': specifier: ^18.0.6 version: 18.0.6 - autoprefixer: - specifier: ^10.0.1 - version: 10.4.17(postcss@8.4.33) babel-jest: specifier: 29.7.0 version: 29.7.0(@babel/core@7.23.9) @@ -125,9 +122,6 @@ importers: cross-env: specifier: ^7.0.3 version: 7.0.3 - css-loader: - specifier: ^7.1.2 - version: 7.1.2(webpack@5.74.0) eslint: specifier: ^9.14.0 version: 9.14.0(jiti@1.21.0) @@ -188,12 +182,6 @@ importers: octokit: specifier: ^3.1.1 version: 3.1.1 - postcss: - specifier: ^8 - version: 8.4.33 - postcss-loader: - specifier: ^8.1.1 - version: 8.1.1(postcss@8.4.33)(typescript@5.7.2)(webpack@5.74.0) prettier: specifier: ^3.3.3 version: 3.3.3 @@ -212,9 +200,6 @@ importers: react-icons: specifier: ^5.3.0 version: 5.3.0(react@18.2.0) - react-syntax-highlighter: - specifier: ^15.6.1 - version: 15.6.1(react@18.2.0) react-test-renderer: specifier: ^18.1.0 version: 18.2.0(react@18.2.0) @@ -227,21 +212,12 @@ importers: rimraf: specifier: ^3.0.2 version: 3.0.2 - sass: - specifier: ^1.80.3 - version: 1.80.3 seedrandom: specifier: ^3.0.5 version: 3.0.5 storybook: specifier: ^8.4.1 version: 8.4.1(prettier@3.3.3) - style-loader: - specifier: ^4.0.0 - version: 4.0.0(webpack@5.74.0) - tailwindcss: - specifier: ^3.3.0 - version: 3.4.1(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2)) ts-jest: specifier: ^29.2.5 version: 29.2.5(@babel/core@7.23.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.18.20)(jest@29.7.0(@types/node@18.6.1)(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2)))(typescript@5.7.2) @@ -1181,6 +1157,9 @@ importers: '@mdx-js/react': specifier: ^3.0.0 version: 3.1.0(@types/react@18.0.15)(react@18.2.0) + '@monaco-editor/react': + specifier: ^4.6.0 + version: 4.6.0(monaco-editor@0.52.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) axios: specifier: ^1.7.7 version: 1.7.7 @@ -1224,6 +1203,9 @@ importers: '@docusaurus/types': specifier: ^3.5.2 version: 3.6.0(@swc/core@1.8.0)(acorn@6.4.2)(esbuild@0.18.20)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(webpack-cli@4.10.0) + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@18.2.0) '@types/react': specifier: ^18.0.0 version: 18.0.15 @@ -3345,6 +3327,11 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -3508,6 +3495,18 @@ packages: '@types/react': '>=16' react: '>=16' + '@monaco-editor/loader@1.4.0': + resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==} + peerDependencies: + monaco-editor: '>= 0.21.0 < 1' + + '@monaco-editor/react@4.6.0': + resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@napi-rs/wasm-runtime@0.2.5': resolution: {integrity: sha512-kwUxR7J9WLutBbulqg1dfOrMTwhMdXLdcGUhcbCcGwnPLt3gz19uHVdwH1syKVDbE022ZS2vZxOWflFLS0YTjw==} @@ -3734,82 +3733,6 @@ packages: resolution: {integrity: sha512-8iG+/yza7hwz1RrQ7i7uGpK2/tuItZxZq1aTmeg2TNp2xTUB8F8lZF/FcZvyyAxT8tpDMF74TjFGCDACkf1kAQ==} engines: {node: '>= 18'} - '@parcel/watcher-android-arm64@2.4.1': - resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [android] - - '@parcel/watcher-darwin-arm64@2.4.1': - resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [darwin] - - '@parcel/watcher-darwin-x64@2.4.1': - resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [darwin] - - '@parcel/watcher-freebsd-x64@2.4.1': - resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [freebsd] - - '@parcel/watcher-linux-arm-glibc@2.4.1': - resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==} - engines: {node: '>= 10.0.0'} - cpu: [arm] - os: [linux] - - '@parcel/watcher-linux-arm64-glibc@2.4.1': - resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - - '@parcel/watcher-linux-arm64-musl@2.4.1': - resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [linux] - - '@parcel/watcher-linux-x64-glibc@2.4.1': - resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - - '@parcel/watcher-linux-x64-musl@2.4.1': - resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [linux] - - '@parcel/watcher-win32-arm64@2.4.1': - resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} - engines: {node: '>= 10.0.0'} - cpu: [arm64] - os: [win32] - - '@parcel/watcher-win32-ia32@2.4.1': - resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==} - engines: {node: '>= 10.0.0'} - cpu: [ia32] - os: [win32] - - '@parcel/watcher-win32-x64@2.4.1': - resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==} - engines: {node: '>= 10.0.0'} - cpu: [x64] - os: [win32] - - '@parcel/watcher@2.4.1': - resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==} - engines: {node: '>= 10.0.0'} - '@philpl/buble@0.19.7': resolution: {integrity: sha512-wKTA2DxAGEW+QffRQvOhRQ0VBiYU2h2p8Yc1oBNlqSKws48/8faxqKNIuub0q4iuyTuLwtB8EkwiKwhlfV1PBA==} hasBin: true @@ -4533,9 +4456,6 @@ packages: '@types/hammerjs@2.0.41': resolution: {integrity: sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==} - '@types/hast@2.3.10': - resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} - '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -5158,13 +5078,6 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} - autoprefixer@10.4.17: - resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - autoprefixer@10.4.20: resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} engines: {node: ^10 || ^12 || >=14} @@ -5730,10 +5643,6 @@ packages: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.1: - resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} - engines: {node: '>= 14.16.0'} - chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -5872,9 +5781,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - comma-separated-tokens@1.0.8: - resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} - comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} @@ -6034,15 +5940,6 @@ packages: typescript: optional: true - cosmiconfig@9.0.0: - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - cpx2@4.2.3: resolution: {integrity: sha512-UM7Iza+OM8FZ2ntTml/mdb3RmSLK5I2DqFqDdMihlGyKZCAAnDP++H973Oyc/2TQpEMtg5JHeRNfewclE330EA==} engines: {node: '>=14'} @@ -6102,18 +5999,6 @@ packages: peerDependencies: webpack: ^5.0.0 - css-loader@7.1.2: - resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - webpack: ^5.27.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - css-minimizer-webpack-plugin@5.0.1: resolution: {integrity: sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==} engines: {node: '>= 14.15.0'} @@ -6671,10 +6556,6 @@ packages: resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==} engines: {node: '>=8'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - envinfo@7.13.0: resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} engines: {node: '>=4'} @@ -7055,9 +6936,6 @@ packages: fastq@1.13.0: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} - fault@1.0.4: - resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -7556,9 +7434,6 @@ packages: hast-util-from-parse5@8.0.1: resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==} - hast-util-parse-selector@2.2.5: - resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} - hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -7580,9 +7455,6 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hastscript@6.0.0: - resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} - hastscript@8.0.0: resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} @@ -7612,12 +7484,6 @@ packages: resolution: {integrity: sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==} engines: {node: '>=8'} - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - highlightjs-vue@1.0.0: - resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} - history@4.10.1: resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==} @@ -7771,9 +7637,6 @@ packages: resolution: {integrity: sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==} engines: {node: '>=0.10.0'} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - import-fresh@2.0.0: resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} engines: {node: '>=4'} @@ -8704,9 +8567,6 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lowlight@1.20.0: - resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - lru-cache@10.0.1: resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} @@ -9236,6 +9096,9 @@ packages: engines: {node: '>=10'} hasBin: true + monaco-editor@0.52.2: + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} @@ -9295,9 +9158,6 @@ packages: node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-dir@0.1.17: resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} engines: {node: '>= 0.10.5'} @@ -9584,9 +9444,6 @@ packages: parse-entities@1.2.2: resolution: {integrity: sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==} - parse-entities@2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - parse-entities@4.0.1: resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==} @@ -9818,19 +9675,6 @@ packages: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - postcss-loader@8.1.1: - resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - postcss: ^7.0.0 || ^8.0.1 - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - postcss-merge-idents@6.0.3: resolution: {integrity: sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==} engines: {node: ^14 || ^16 || >=18.0} @@ -10016,10 +9860,6 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.4.33: - resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.4.47: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} @@ -10077,10 +9917,6 @@ packages: peerDependencies: react: '>=16.0.0' - prismjs@1.27.0: - resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} - engines: {node: '>=6'} - prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -10124,9 +9960,6 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - property-information@5.6.0: - resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} - property-information@6.4.1: resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==} @@ -10403,11 +10236,6 @@ packages: peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-syntax-highlighter@15.6.1: - resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} - peerDependencies: - react: '>= 0.14.0' - react-test-renderer@18.2.0: resolution: {integrity: sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==} peerDependencies: @@ -10446,10 +10274,6 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.0.2: - resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} - engines: {node: '>= 14.16.0'} - reading-time@1.5.0: resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==} @@ -10500,9 +10324,6 @@ packages: resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} engines: {node: '>= 0.4'} - refractor@3.6.0: - resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} - regenerate-unicode-properties@10.1.1: resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} engines: {node: '>=4'} @@ -10731,6 +10552,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rtl-detect@1.1.2: @@ -10767,11 +10589,6 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.80.3: - resolution: {integrity: sha512-ptDWyVmDMVielpz/oWy3YP3nfs7LpJTHIJZboMVs8GEC9eUmtZTZhMHlTW98wY4aEorDfjN38+Wr/XjskFWcfA==} - engines: {node: '>=14.0.0'} - hasBin: true - sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -11037,9 +10854,6 @@ packages: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead - space-separated-tokens@1.1.5: - resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} - space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -11106,6 +10920,9 @@ packages: starts-with@1.0.2: resolution: {integrity: sha512-QUw5X+IMTGDm1nrdowEdDaA0MNiUmRlQFwpTTXmhuPKQc+7b0h8fOHtlt1zZqcEK5x1Fsitrobo7KEusc+d1rg==} + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + state-toggle@1.0.3: resolution: {integrity: sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==} @@ -11261,12 +11078,6 @@ packages: peerDependencies: webpack: ^5.0.0 - style-loader@4.0.0: - resolution: {integrity: sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - webpack: ^5.27.0 - style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} @@ -11346,11 +11157,6 @@ packages: resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} engines: {node: ^14.18.0 || >=16.0.0} - tailwindcss@3.4.1: - resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==} - engines: {node: '>=14.0.0'} - hasBin: true - tailwindcss@3.4.14: resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} @@ -16432,6 +16238,10 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 + '@heroicons/react@2.2.0(react@18.2.0)': + dependencies: + react: 18.2.0 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -16737,6 +16547,18 @@ snapshots: '@types/react': 18.0.15 react: 18.2.0 + '@monaco-editor/loader@1.4.0(monaco-editor@0.52.2)': + dependencies: + monaco-editor: 0.52.2 + state-local: 1.0.7 + + '@monaco-editor/react@4.6.0(monaco-editor@0.52.2)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@monaco-editor/loader': 1.4.0(monaco-editor@0.52.2) + monaco-editor: 0.52.2 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + '@napi-rs/wasm-runtime@0.2.5': dependencies: '@emnapi/core': 1.3.1 @@ -16992,62 +16814,6 @@ snapshots: '@octokit/webhooks-types': 7.1.0 aggregate-error: 3.1.0 - '@parcel/watcher-android-arm64@2.4.1': - optional: true - - '@parcel/watcher-darwin-arm64@2.4.1': - optional: true - - '@parcel/watcher-darwin-x64@2.4.1': - optional: true - - '@parcel/watcher-freebsd-x64@2.4.1': - optional: true - - '@parcel/watcher-linux-arm-glibc@2.4.1': - optional: true - - '@parcel/watcher-linux-arm64-glibc@2.4.1': - optional: true - - '@parcel/watcher-linux-arm64-musl@2.4.1': - optional: true - - '@parcel/watcher-linux-x64-glibc@2.4.1': - optional: true - - '@parcel/watcher-linux-x64-musl@2.4.1': - optional: true - - '@parcel/watcher-win32-arm64@2.4.1': - optional: true - - '@parcel/watcher-win32-ia32@2.4.1': - optional: true - - '@parcel/watcher-win32-x64@2.4.1': - optional: true - - '@parcel/watcher@2.4.1': - dependencies: - detect-libc: 1.0.3 - is-glob: 4.0.3 - micromatch: 4.0.5 - node-addon-api: 7.1.1 - optionalDependencies: - '@parcel/watcher-android-arm64': 2.4.1 - '@parcel/watcher-darwin-arm64': 2.4.1 - '@parcel/watcher-darwin-x64': 2.4.1 - '@parcel/watcher-freebsd-x64': 2.4.1 - '@parcel/watcher-linux-arm-glibc': 2.4.1 - '@parcel/watcher-linux-arm64-glibc': 2.4.1 - '@parcel/watcher-linux-arm64-musl': 2.4.1 - '@parcel/watcher-linux-x64-glibc': 2.4.1 - '@parcel/watcher-linux-x64-musl': 2.4.1 - '@parcel/watcher-win32-arm64': 2.4.1 - '@parcel/watcher-win32-ia32': 2.4.1 - '@parcel/watcher-win32-x64': 2.4.1 - '@philpl/buble@0.19.7': dependencies: acorn: 6.4.2 @@ -18255,10 +18021,6 @@ snapshots: '@types/hammerjs@2.0.41': {} - '@types/hast@2.3.10': - dependencies: - '@types/unist': 2.0.6 - '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.2 @@ -18979,16 +18741,6 @@ snapshots: at-least-node@1.0.0: {} - autoprefixer@10.4.17(postcss@8.4.33): - dependencies: - browserslist: 4.22.2 - caniuse-lite: 1.0.30001580 - fraction.js: 4.3.7 - normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.33 - postcss-value-parser: 4.2.0 - autoprefixer@10.4.20(postcss@8.4.47): dependencies: browserslist: 4.24.2 @@ -19925,10 +19677,6 @@ snapshots: optionalDependencies: fsevents: 2.3.2 - chokidar@4.0.1: - dependencies: - readdirp: 4.0.2 - chownr@2.0.0: {} chromatic@11.16.4: {} @@ -20049,8 +19797,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - comma-separated-tokens@1.0.8: {} - comma-separated-tokens@2.0.3: {} command-exists@1.2.9: {} @@ -20211,15 +19957,6 @@ snapshots: optionalDependencies: typescript: 5.2.2 - cosmiconfig@9.0.0(typescript@5.7.2): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.7.2 - cpx2@4.2.3: dependencies: debounce: 1.2.1 @@ -20323,19 +20060,6 @@ snapshots: semver: 7.6.3 webpack: 5.96.1(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0) - css-loader@7.1.2(webpack@5.74.0): - dependencies: - icss-utils: 5.1.0(postcss@8.4.47) - postcss: 8.4.47 - postcss-modules-extract-imports: 3.1.0(postcss@8.4.47) - postcss-modules-local-by-default: 4.0.5(postcss@8.4.47) - postcss-modules-scope: 3.2.0(postcss@8.4.47) - postcss-modules-values: 4.0.0(postcss@8.4.47) - postcss-value-parser: 4.2.0 - semver: 7.5.4 - optionalDependencies: - webpack: 5.74.0(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0) - css-minimizer-webpack-plugin@5.0.1(clean-css@5.3.3)(esbuild@0.18.20)(webpack@5.96.1(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0)): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -20873,8 +20597,6 @@ snapshots: env-editor@0.4.2: {} - env-paths@2.2.1: {} - envinfo@7.13.0: {} eol@0.9.1: {} @@ -21465,10 +21187,6 @@ snapshots: dependencies: reusify: 1.0.4 - fault@1.0.4: - dependencies: - format: 0.2.2 - fault@2.0.1: dependencies: format: 0.2.2 @@ -22024,8 +21742,6 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 - hast-util-parse-selector@2.2.5: {} - hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 @@ -22115,14 +21831,6 @@ snapshots: dependencies: '@types/hast': 3.0.4 - hastscript@6.0.0: - dependencies: - '@types/hast': 2.3.10 - comma-separated-tokens: 1.0.8 - hast-util-parse-selector: 2.2.5 - property-information: 5.6.0 - space-separated-tokens: 1.1.5 - hastscript@8.0.0: dependencies: '@types/hast': 3.0.4 @@ -22155,10 +21863,6 @@ snapshots: dependencies: source-map: 0.7.4 - highlight.js@10.7.3: {} - - highlightjs-vue@1.0.0: {} - history@4.10.1: dependencies: '@babel/runtime': 7.26.0 @@ -22351,8 +22055,6 @@ snapshots: immutable@3.8.2: {} - immutable@4.3.7: {} - import-fresh@2.0.0: dependencies: caller-path: 2.0.0 @@ -23445,11 +23147,6 @@ snapshots: lowercase-keys@3.0.0: {} - lowlight@1.20.0: - dependencies: - fault: 1.0.4 - highlight.js: 10.7.3 - lru-cache@10.0.1: {} lru-cache@4.1.5: @@ -24396,6 +24093,8 @@ snapshots: mkdirp@1.0.4: {} + monaco-editor@0.52.2: {} + mrmime@2.0.0: {} ms@2.0.0: {} @@ -24446,8 +24145,6 @@ snapshots: node-abort-controller@3.1.1: {} - node-addon-api@7.1.1: {} - node-dir@0.1.17: dependencies: minimatch: 3.1.2 @@ -24761,15 +24458,6 @@ snapshots: is-decimal: 1.0.4 is-hexadecimal: 1.0.4 - parse-entities@2.0.0: - dependencies: - character-entities: 1.2.4 - character-entities-legacy: 1.1.4 - character-reference-invalid: 1.1.4 - is-alphanumerical: 1.0.4 - is-decimal: 1.0.4 - is-hexadecimal: 1.0.4 - parse-entities@4.0.1: dependencies: '@types/unist': 2.0.6 @@ -24959,14 +24647,6 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.47 - postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2)): - dependencies: - lilconfig: 3.1.1 - yaml: 2.4.0 - optionalDependencies: - postcss: 8.4.47 - ts-node: 10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2) - postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@22.8.7)(typescript@5.2.2)): dependencies: lilconfig: 3.1.1 @@ -24985,17 +24665,6 @@ snapshots: transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.4.33)(typescript@5.7.2)(webpack@5.74.0): - dependencies: - cosmiconfig: 9.0.0(typescript@5.7.2) - jiti: 1.21.0 - postcss: 8.4.33 - semver: 7.5.4 - optionalDependencies: - webpack: 5.74.0(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0) - transitivePeerDependencies: - - typescript - postcss-merge-idents@6.0.3(postcss@8.4.47): dependencies: cssnano-utils: 4.0.2(postcss@8.4.47) @@ -25165,12 +24834,6 @@ snapshots: dependencies: postcss: 8.4.47 - postcss@8.4.33: - dependencies: - nanoid: 3.3.7 - picocolors: 1.0.0 - source-map-js: 1.0.2 - postcss@8.4.47: dependencies: nanoid: 3.3.7 @@ -25233,8 +24896,6 @@ snapshots: clsx: 2.1.1 react: 18.2.0 - prismjs@1.27.0: {} - prismjs@1.29.0: {} private@0.1.8: {} @@ -25272,10 +24933,6 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - property-information@5.6.0: - dependencies: - xtend: 4.0.2 - property-information@6.4.1: {} proto-list@1.2.4: {} @@ -25698,16 +25355,6 @@ snapshots: react: 18.2.0 react-is: 18.2.0 - react-syntax-highlighter@15.6.1(react@18.2.0): - dependencies: - '@babel/runtime': 7.24.0 - highlight.js: 10.7.3 - highlightjs-vue: 1.0.0 - lowlight: 1.20.0 - prismjs: 1.29.0 - react: 18.2.0 - refractor: 3.6.0 - react-test-renderer@18.2.0(react@18.2.0): dependencies: react: 18.2.0 @@ -25768,8 +25415,6 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.0.2: {} - reading-time@1.5.0: {} readline-sync@1.4.10: {} @@ -25848,12 +25493,6 @@ snapshots: globalthis: 1.0.4 which-builtin-type: 1.1.4 - refractor@3.6.0: - dependencies: - hastscript: 6.0.0 - parse-entities: 2.0.0 - prismjs: 1.27.0 - regenerate-unicode-properties@10.1.1: dependencies: regenerate: 1.4.2 @@ -26200,13 +25839,6 @@ snapshots: safer-buffer@2.1.2: {} - sass@1.80.3: - dependencies: - '@parcel/watcher': 2.4.1 - chokidar: 4.0.1 - immutable: 4.3.7 - source-map-js: 1.0.2 - sax@1.2.4: {} saxes@6.0.0: @@ -26509,8 +26141,6 @@ snapshots: sourcemap-codec@1.4.8: {} - space-separated-tokens@1.1.5: {} - space-separated-tokens@2.0.2: {} spawn-command-with-kill@1.0.2: @@ -26590,6 +26220,8 @@ snapshots: starts-with@1.0.2: {} + state-local@1.0.7: {} + state-toggle@1.0.3: {} statuses@1.5.0: {} @@ -26757,10 +26389,6 @@ snapshots: dependencies: webpack: 5.74.0(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0) - style-loader@4.0.0(webpack@5.74.0): - dependencies: - webpack: 5.74.0(@swc/core@1.8.0)(esbuild@0.18.20)(webpack-cli@4.10.0) - style-to-object@0.4.4: dependencies: inline-style-parser: 0.1.1 @@ -26850,33 +26478,6 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.1 - tailwindcss@3.4.1(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2)): - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.5.3 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.2 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.0 - lilconfig: 2.1.0 - micromatch: 4.0.5 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.47 - postcss-import: 15.1.0(postcss@8.4.47) - postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@18.6.1)(typescript@5.7.2)) - postcss-nested: 6.0.1(postcss@8.4.47) - postcss-selector-parser: 6.0.15 - resolve: 1.22.8 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node - tailwindcss@3.4.14(ts-node@10.9.1(@swc/core@1.8.0)(@types/node@22.8.7)(typescript@5.2.2)): dependencies: '@alloc/quick-lru': 5.2.0 diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 12a703d90..000000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index 02c3005b4..000000000 --- a/tailwind.config.js +++ /dev/null @@ -1,25 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ["./demo/ts/components/theme-builder/**/*.{ts,tsx}"], - theme: { - colors: { - currentColor: "currentColor", - transparent: "transparent", - primary: "#007bff", - secondary: "#0056b3", - white: "#fff", - gray: { - 100: "#f9f9f9", - 200: "#ccc", - 300: "#666", - }, - }, - extend: { - backgroundImage: { - "select-chevron": - 'url(\'data:image/svg+xml;utf8,\')', - }, - }, - }, - plugins: [], -}; diff --git a/website/docs/api/victory-area.mdx b/website/docs/api/victory-area.mdx index 53fea2eca..5f1c8cd13 100644 --- a/website/docs/api/victory-area.mdx +++ b/website/docs/api/victory-area.mdx @@ -129,7 +129,7 @@ See the [Events Guide](/docs/guides/events) for more information on defining eve `VictoryArea` uses the standard `groupComponent` prop. [Read about it in detail](/docs/api/victory-common-theme-props#groupcomponent) :::note -`VictoryArea` uses [`VictoryClipContainer`](/docs/api/victory-clip-container) as its default `groupComponent` `VictoryClipContainer` renders a `` tag with a `clipPath` `def`. This allows continuous data components to transition smoothly when new data points enter and exit. +`VictoryArea` uses [`VictoryClipContainer`](/docs/api/victory-clip-container) as its default `groupComponent` `VictoryClipContainer` renders a `` tag with a `clipPath` `def`. This allows continuous data components to transition smoothly when new data points enter and exit. ::: :::warning @@ -241,7 +241,7 @@ To enable tooltips on `VictoryArea`, it is necessary to use [`VictoryVoronoiCont Defines the style of the component using [VictoryStyleInterface](/docs/api/victory-style-interface). :::note -Since `VictoryArea` renders a single element to represent an entire dataset, it is not possible to use functional styles to change the style of the line as a function of an individual `datum`. Instead, try using [gradient fills](/docs/guides/themes#gradient-fills) for styling continuous data. +Since `VictoryArea` renders a single element to represent an entire dataset, it is not possible to use functional styles to change the style of the line as a function of an individual `datum`. Instead, try using [gradient fills](/docs/guides/themes#using-gradient-fills) for styling continuous data. ::: ```jsx live diff --git a/website/docs/api/victory-theme.mdx b/website/docs/api/victory-theme.mdx index f9a9c0d22..d6d9c7cb6 100644 --- a/website/docs/api/victory-theme.mdx +++ b/website/docs/api/victory-theme.mdx @@ -43,6 +43,23 @@ interface VictoryThemeDefinition { } ``` +### `palette` + +The `palette` property defines a collection of colors that can be used across all chart elements. Each palette contains predefined color arrays that you can use directly or customize as needed. + +#### Properties + +| Property | Type | Description | +| ------------- | ---------- | --------------------------------------------------------------------------------------------- | +| `grayscale` | `string[]` | Shades of gray, ideal for minimalist or monochrome chart designs | +| `qualitative` | `string[]` | Used for categorical data, containing distinct colors that are easy to differentiate visually | +| `heatmap` | `string[]` | A gradient-based color scheme often used for heatmaps or data density visualizations | +| `warm` | `string[]` | Warm colors like reds, oranges, and yellows | +| `cool` | `string[]` | Cool colors such as blues, purples, and greens | +| `red` | `string[]` | Various shades of red | +| `blue` | `string[]` | Various shades of blue | +| `green` | `string[]` | Various shades of green | + ## Example ```jsx live diff --git a/website/docs/guides/themes.mdx b/website/docs/guides/themes.mdx index da2038252..76862d6c5 100644 --- a/website/docs/guides/themes.mdx +++ b/website/docs/guides/themes.mdx @@ -4,7 +4,61 @@ title: Themes & Styling ## Themes -Try out the Victory themes and make your own. Check out the [VictoryTheme API documentation](/docs/api/victory-theme) more details on themes. +Victory themes are reusable style configurations that enable you to standardize the appearance of charts across your application. Themes simplify chart development by reducing repetitive styling logic and ensuring visual consistency. + +Themes are applied at the chart level and automatically cascade to all child components, such as axes, bars, lines, and legends. Victory provides built-in themes like `clean`, `material` and `grayscale`, but you can also create custom themes to match your branding. + +### How Themes Work in Victory + +Victory themes are essentially JavaScript objects that define styling properties for various chart elements. These theme configurations enable consistent and reusable styling across charts. Each theme configuration variable corresponds to a specific chart element and takes props that define its styles. + +Theme configuration variables include: + +- [`.palette`](/docs/api/victory-theme#palette) + - Defines color schemes used across the theme. +- [`.chart`](/docs/api/victory-chart) + - Global styles like background and padding. +- [`.area`](/docs/api/victory-area) +- [`.axis`](/docs/api/victory-axis) +- [`.polarAxis`](/docs/api/victory-polar-axis) +- [`.polarDependentAxis`](/docs/api/victory-polar-axis) +- [`.bar`](/docs/api/victory-bar) +- [`.boxplot`](/docs/api/victory-boxplot) +- [`.candlestick`](/docs/api/victory-candlestick) +- [`.errorbar`](/docs/api/victory-error-bar) +- [`.group`](/docs/api/victory-group) +- [`.histogram`](/docs/api/victory-histogram) +- [`.legend`](/docs/api/victory-legend) +- [`.line`](/docs/api/victory-line) +- [`.pie`](/docs/api/victory-pie) +- [`.scatter`](/docs/api/victory-scatter) +- [`.stack`](/docs/api/victory-stack) +- [`.tooltip`](/docs/api/victory-tooltip) +- [`.voronoi`](/docs/api/victory-voronoi) + +Each configuration variable takes props specific to its chart element, allowing customization of styles like colors, padding, labels, and more. For detailed information about each variable, refer to the [VictoryTheme API page](/docs/api/victory-theme). + +When a theme is passed to a chart via the `theme` prop, the styles from the theme object are applied to the corresponding child components unless they are explicitly overridden by inline props. + +```jsx live + + + +``` + +### Predefined Themes + +Victory includes several built-in themes to help you quickly style your charts: + +| Theme | Description | +| ------------------------ | --------------------------------------------------------------------------------------------------- | +| `VictoryTheme.clean` | A minimalist theme with no gridlines or extra styling, perfect for clean and modern visualizations. | +| `VictoryTheme.material` | Inspired by Google's Material Design, this theme includes bold colors and a structured grid. | +| `VictoryTheme.grayscale` | A neutral theme featuring grayscale tones, ideal for muted and professional-looking charts. | + +Each of these themes can be applied to your Victory components by passing it into the `theme` prop. ```jsx live noInline const result = [...Array(10).keys()]; @@ -39,34 +93,34 @@ const DemoComponent = () => { ]; return (
-
+
{ render(); ``` -## Styles +You can also customize these themes or use them as a base to create your own. To build upon a predefined theme, you can extend it using the spread operator: -### How can I change the colors of lines and other elements in Victory? +```jsx +const extendedTheme = { + ...VictoryTheme.material, + axis: { + ...VictoryTheme.material.axis, + style: { + ...VictoryTheme.material.axis + .style, + tickLabels: { + fill: "#444", + fontSize: 10, + fontStyle: "italic", + }, + }, + }, +}; +``` + +### Creating a Custom Theme -Most components in Victory use a standard `style` prop with style namespaces for "data" and "labels". Any styles added to the "data" namespace will be applied to all the svg elements rendered for a given dataset. +To create a completely custom theme, define a JavaScript object that includes styles for the components you want to theme. You can omit components that use default styling. + +```jsx live noInline +const customTheme = { + axis: { + style: { + grid: { stroke: "none" }, + axis: { + stroke: "#333", + strokeWidth: 2, + }, + ticks: { + stroke: "#555", + size: 5, + }, + tickLabels: { + fill: "#222", + fontSize: 12, + padding: 5, + }, + }, + }, + bar: { + style: { + data: { + fill: "#0074d9", + width: 15, + }, + }, + }, +}; + +render( + + + , +); +``` + +To enhance the "Styles" section of the Victory Themes guide, consider incorporating the following detailed explanations and examples: + +## Styling Individual Components + +To customize the appearance of Victory components, you can use the `style` prop, which accepts an object containing styles for various component elements like `data`, `labels`, and `parent`. For a detailed breakdown of the style options available, refer to the [Victory Style Interface](/docs/api/victory-style-interface). + +**Example: Customizing Bar and Line Colors** ```jsx live ``` -### How can I change the color of an individual point or bar? +### Styling Data + +To style individual elements within a dataset, you can include style attributes directly in your data objects and utilize functional styles. -Individual elements in Victory can be styled by adding style attributes directly to your data object and using functional styles and props as in the example below. Functions are called with all the props that correspond to the element they render. +**Note:** Continuous data components like `VictoryLine` and `VictoryArea` render a single SVG element for the entire dataset, so individual styling of data points within these components is not applicable. ```jsx live @@ -284,7 +412,7 @@ Individual elements in Victory can be styled by adding style attributes directly style={{ data: { fill: ({ index }) => - +index % 2 === 0 + index % 2 === 0 ? "blue" : "grey", stroke: ({ datum }) => @@ -307,11 +435,9 @@ Individual elements in Victory can be styled by adding style attributes directly ``` -Note that continuous data types such as `VictoryLine` and `VictoryArea` cannot be styled in this way, as they only render a single element for a given dataset. - -### Gradient Fills +### Using Gradient Fills -Create a gradient def as usual and then reference it by id in your style object. Gradients can be used to give continuous charts (_i.e._ line or area charts) the appearance of discrete data elements and hover states. +To apply gradient fills to your charts, define a gradient in the SVG `defs` section and reference it by id in your component's style. Gradients can be used to give continuous charts (i.e. line or area charts) the appearance of discrete data elements and hover states. ```jsx live
diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index a064531b4..27c8b6501 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -147,6 +147,11 @@ const config: Config = { position: "left", label: "DOCS", }, + { + to: "/themes", + label: "THEMES", + position: "left", + }, { href: "https://github.com/FormidableLabs/victory", "aria-label": "GitHub Repository", @@ -170,6 +175,10 @@ const config: Config = { darkTheme: prismThemes.dracula, additionalLanguages: ["diff", "diff-ts"], }, + colorMode: { + defaultMode: "light", + disableSwitch: true, + }, }, headTags: [ { diff --git a/website/package.json b/website/package.json index fc228e2a1..7d79437ff 100644 --- a/website/package.json +++ b/website/package.json @@ -25,6 +25,7 @@ "@docusaurus/theme-live-codeblock": "^3.5.2", "@easyops-cn/docusaurus-search-local": "^0.44.5", "@mdx-js/react": "^3.0.0", + "@monaco-editor/react": "^4.6.0", "axios": "^1.7.7", "clsx": "^2.0.0", "date-fns": "^3.6.0", @@ -41,6 +42,7 @@ "@docusaurus/module-type-aliases": "^3.5.2", "@docusaurus/tsconfig": "^3.5.2", "@docusaurus/types": "^3.5.2", + "@heroicons/react": "^2.2.0", "@types/react": "^18.0.0", "autoprefixer": "^10.4.20", "d3-array": "^2.4.0", diff --git a/website/src/components/button.tsx b/website/src/components/button.tsx new file mode 100644 index 000000000..f9fb1bd81 --- /dev/null +++ b/website/src/components/button.tsx @@ -0,0 +1,41 @@ +import clsx from "clsx"; +import React from "react"; + +type ButtonProps = { + onClick: () => void; + children: React.ReactNode; + className?: string; + ariaLabel?: string; + disabled?: boolean; + size?: "sm" | "md"; +}; + +export const Button = ({ + onClick, + children, + className = "", + ariaLabel = "", + disabled = false, + size = "md", + ...props +}: ButtonProps) => { + const baseClasses = + "py-2 px-4 font-semibold rounded-md cursor-pointer text-sm border-2 border-solid border-button-border bg-button-bg text-button-fg hover:bg-button-bg-hover hover:text-button-fg-hover disabled:bg-grayscale-300 disabled:text-grayscale-400 disabled:cursor-not-allowed"; + + return ( + + ); +}; diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 026c60696..798ce8e19 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -106,3 +106,20 @@ body { .footer__copyright { color: white; } + +@layer base { + /* Needed for theme builder since we're disabling Tailwind's reset for docs */ + .theme-builder * { + border-width: 0; + border-style: solid; + + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: Inter, Helvetica, Arial, sans-serif; + } + } +} diff --git a/website/src/hooks/useClickOutside.tsx b/website/src/hooks/useClickOutside.tsx new file mode 100644 index 000000000..867de51ea --- /dev/null +++ b/website/src/hooks/useClickOutside.tsx @@ -0,0 +1,29 @@ +import { useEffect, useLayoutEffect, useRef } from "react"; + +export function useClickOutside(cb: (e: Event) => void) { + const ref = useRef(null); + const refCb = useRef(cb); + + useLayoutEffect(() => { + refCb.current = cb; + }); + + useEffect(() => { + const handler = (e: Event) => { + const element = ref.current; + if (element && !element.contains(e.target as Node)) { + refCb.current(e); + } + }; + + document.addEventListener("mousedown", handler); + document.addEventListener("touchstart", handler); + + return () => { + document.removeEventListener("mousedown", handler); + document.removeEventListener("touchstart", handler); + }; + }, []); + + return ref; +} diff --git a/website/src/hooks/useLocalStorage.tsx b/website/src/hooks/useLocalStorage.tsx new file mode 100644 index 000000000..1cd84df26 --- /dev/null +++ b/website/src/hooks/useLocalStorage.tsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; + +export const useLocalStorage = (key: string, initialValue: any) => { + const [storedValue, setStoredValue] = useState(() => { + if (typeof window === "undefined") return initialValue; + + try { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : initialValue; + } catch { + return initialValue; + } + }); + + useEffect(() => { + if (typeof window !== "undefined") { + try { + localStorage.setItem(key, JSON.stringify(storedValue)); + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error saving to localStorage", error); + } + } + }, [storedValue, key]); + + return [storedValue, setStoredValue]; +}; diff --git a/demo/ts/components/theme-builder/accordion.tsx b/website/src/pages/themes/_components/accordion.tsx similarity index 71% rename from demo/ts/components/theme-builder/accordion.tsx rename to website/src/pages/themes/_components/accordion.tsx index 5578b4d5a..a03180b81 100644 --- a/demo/ts/components/theme-builder/accordion.tsx +++ b/website/src/pages/themes/_components/accordion.tsx @@ -7,6 +7,7 @@ type AccordionProps = { title: string; children: React.ReactNode; defaultOpen?: boolean; + className?: string; }; const Accordion = ({ @@ -14,6 +15,7 @@ const Accordion = ({ title, children, defaultOpen = false, + className, }: AccordionProps) => { const [isOpen, setIsOpen] = React.useState(defaultOpen); @@ -22,12 +24,12 @@ const Accordion = ({ }; return ( -
-

+
+

+

+ ); +}; + +export default Alert; diff --git a/website/src/pages/themes/_components/base-theme-panel.tsx b/website/src/pages/themes/_components/base-theme-panel.tsx new file mode 100644 index 000000000..96d621729 --- /dev/null +++ b/website/src/pages/themes/_components/base-theme-panel.tsx @@ -0,0 +1,61 @@ +import React from "react"; +import Select from "./select"; +import { CUSTOM_THEME, themes, useTheme } from "../_providers/themeProvider"; +import { usePreviewOptions } from "../_providers/previewOptionsProvider"; +import PanelHeader from "./panel-header"; +import Card from "./card"; +import { codeItem } from "./sidenav"; +import { TiArrowRight } from "react-icons/ti"; +import { useSideNavContext } from "../_providers/sideNavProvider"; + +const themeOptions = themes.map((theme) => ({ + label: theme.name, + value: theme.name, +})); + +const BaseThemePanel = () => { + const { baseTheme, onBaseThemeSelect } = useTheme(); + const { resetPreviewOptions } = usePreviewOptions(); + const { setActiveSideNavItem } = useSideNavContext(); + + const handleThemeSelect = (themeName?: string) => { + onBaseThemeSelect(themeName); + resetPreviewOptions(); + }; + + const isCustomTheme = baseTheme?.name === CUSTOM_THEME.name; + + return ( + <> + + + + {controls.map((control, i) => { + return ( + + ); + })} + + ); +}; +export default ChartPanel; diff --git a/website/src/pages/themes/_components/checkbox.tsx b/website/src/pages/themes/_components/checkbox.tsx new file mode 100644 index 000000000..ee92cd3b4 --- /dev/null +++ b/website/src/pages/themes/_components/checkbox.tsx @@ -0,0 +1,42 @@ +import clsx from "clsx"; +import React, { useId } from "react"; + +type CheckboxProps = { + label?: string; + isChecked?: boolean; + onChange?: (isChecked: boolean) => void; + className?: string; +}; + +const Checkbox = ({ + label = "Checkbox", + isChecked = false, + onChange, + className, +}: CheckboxProps) => { + const handleChange = (event: React.ChangeEvent) => { + if (onChange) { + onChange(event.target.checked); + } + }; + + const id = useId(); + + return ( + + ); +}; +export default Checkbox; diff --git a/website/src/pages/themes/_components/code-block.tsx b/website/src/pages/themes/_components/code-block.tsx new file mode 100644 index 000000000..16072fb95 --- /dev/null +++ b/website/src/pages/themes/_components/code-block.tsx @@ -0,0 +1,108 @@ +import clsx from "clsx"; +import { Highlight } from "prism-react-renderer"; +import React from "react"; +import { FiCheck, FiCopy } from "react-icons/fi"; + +type CodeBlock = { + title?: string; + code: string; + language: string; +}; + +type CodeBlockProps = { + className?: string; +} & (CodeBlock | { blocks: CodeBlock[] }); + +const CodeBlock = (props: CodeBlockProps) => { + const { className: classes } = props; + const [copyStatus, setCopyStatus] = React.useState(null); + const [activeBlockIndex, setActiveBlockIndex] = React.useState(0); + + const code = + "blocks" in props ? props.blocks[activeBlockIndex].code : props.code; + const language = + "blocks" in props + ? props.blocks[activeBlockIndex].language + : props.language; + + const handleCopyThemeConfig = () => { + navigator.clipboard + .writeText(code) + // eslint-disable-next-line promise/always-return + .then(() => { + setCopyStatus("Copied successfully."); + }) + .catch(() => { + setCopyStatus("Failed to copy."); + }); + }; + + const handleBlockChange = (index: number) => { + setActiveBlockIndex(index); + setCopyStatus(null); + }; + + return ( +
+ {"blocks" in props && ( +
+ {props.blocks.map(({ title }, index) => ( + + ))} +
+
+ {copyStatus && ( + + {copyStatus} + + )} + +
+
+
+ )} +
+ + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+              {tokens.map((line, i) => (
+                
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+
+
+ ); +}; + +export default CodeBlock; diff --git a/website/src/pages/themes/_components/code-panel.tsx b/website/src/pages/themes/_components/code-panel.tsx new file mode 100644 index 000000000..196a9bf3e --- /dev/null +++ b/website/src/pages/themes/_components/code-panel.tsx @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from "react"; +import Editor from "@monaco-editor/react"; +import { CUSTOM_THEME, useTheme } from "../_providers/themeProvider"; +import { stringifyWithoutQuotes } from "../_utils"; +import { Button } from "@site/src/components/button"; +import { useAlert } from "../_providers/alertProvider"; +import { AlertType } from "./alert"; + +const EDITOR_OPTIONS = { + minimap: { enabled: false }, + fontSize: 12, +}; + +const CodePanel = () => { + const { onBaseThemeSelect, customThemeConfig } = useTheme(); + const { addAlert } = useAlert(); + const [customTheme, setCustomTheme] = useState(() => + stringifyWithoutQuotes(customThemeConfig), + ); + + useEffect(() => { + setCustomTheme(stringifyWithoutQuotes(customThemeConfig)); + }, [customThemeConfig]); + + const handleCustomThemeChange = (value: string | undefined) => { + setCustomTheme(value || ""); + }; + + const applyCustomTheme = () => { + try { + const parsedTheme = new Function(`return (${customTheme.trim()});`)(); + if (typeof parsedTheme !== "object" || Array.isArray(parsedTheme)) { + addAlert({ + type: AlertType.ERROR, + title: "Invalid theme structure.", + message: "Must be an object.", + }); + return; + } + addAlert({ + type: AlertType.SUCCESS, + title: "Changes applied successfully.", + }); + onBaseThemeSelect(CUSTOM_THEME.name, parsedTheme); + } catch { + addAlert({ + type: AlertType.ERROR, + title: "Invalid JavaScript object.", + message: "Please check your theme configuration.", + }); + } + }; + + const handleEditorMount = (_, monaco) => { + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: false, + enableSchemaRequest: false, + schemas: [], + }); + }; + + return ( +
+

Theme Code

+

+ You can import your code by pasting your custom theme + here or edit the existing theme configuration. +

+
+

+ + `candlestick.style.labels.padding` + {" "} + and{" "} + + `errorbar.borderWidth` + {" "} + are required for proper theme functionality. +

+ +
+
+ +
+
+ ); +}; +export default CodePanel; diff --git a/website/src/pages/themes/_components/color-palette-selector.tsx b/website/src/pages/themes/_components/color-palette-selector.tsx new file mode 100644 index 000000000..33c5cac27 --- /dev/null +++ b/website/src/pages/themes/_components/color-palette-selector.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { VictoryThemeDefinition } from "victory"; +import clsx from "clsx"; +import { usePreviewOptions } from "../_providers/previewOptionsProvider"; +import ColorPickerList from "./color-picker-list"; + +type ColorPaletteSelectorProps = { + label: string; + value: string; + palette?: VictoryThemeDefinition["palette"]; + className?: string; + onColorsChange: (newColors: string[]) => void; +}; + +const ColorPaletteSelector = ({ + label, + value, + palette, + className, + onColorsChange, +}: ColorPaletteSelectorProps) => { + const { colorScale, updateColorScale } = usePreviewOptions(); + + const handleRadioChange = () => { + updateColorScale(value); + }; + + const handleColorsChange = (newColors) => { + onColorsChange(newColors); + updateColorScale(value); + }; + + const isSelected = colorScale === value; + + return ( + + ); +}; +export default ColorPaletteSelector; diff --git a/website/src/pages/themes/_components/color-picker-list.tsx b/website/src/pages/themes/_components/color-picker-list.tsx new file mode 100644 index 000000000..9228ad6ad --- /dev/null +++ b/website/src/pages/themes/_components/color-picker-list.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import ColorPicker, { PLACEHOLDER_COLOR } from "./color-picker"; +import clsx from "clsx"; +import { TiPlus } from "react-icons/ti"; + +type ColorPickerListProps = { + label?: string; + colors?: string[]; + onColorsChange: (newColors: string[]) => void; + className?: string; +}; + +const ColorPickerList = ({ + label, + colors = [], + onColorsChange, + className, +}: ColorPickerListProps) => { + const handleColorChange = (newColor, i) => { + const updatedColors = [...colors]; + updatedColors[i] = newColor; + onColorsChange(updatedColors); + }; + + const handleRemoveColor = (i) => { + const updatedColors = [...colors]; + updatedColors.splice(i, 1); + onColorsChange(updatedColors); + }; + + const handleAddColor = () => { + const updatedColors = [...colors, PLACEHOLDER_COLOR]; + onColorsChange(updatedColors); + }; + + return ( +
+ {label && ( + + {label} + + )} +
+ {colors.map((color, i) => ( + handleColorChange(newColor, i)} + onColorRemove={() => handleRemoveColor(i)} + /> + ))} + +
+
+ ); +}; +export default ColorPickerList; diff --git a/website/src/pages/themes/_components/color-picker.tsx b/website/src/pages/themes/_components/color-picker.tsx new file mode 100644 index 000000000..51a89f122 --- /dev/null +++ b/website/src/pages/themes/_components/color-picker.tsx @@ -0,0 +1,137 @@ +import React, { useId } from "react"; +import { IoMdClose } from "react-icons/io"; +import clsx from "clsx"; +import Select from "./select"; + +type ColorPickerProps = { + label?: string; + color: string; + onColorChange: (color?: string) => void; + onColorRemove?: () => void; + showSelectOptions?: boolean; + className?: string; +}; + +export const PLACEHOLDER_COLOR = "#000000"; +const DEFAULT_COLOR = undefined; +enum ColorPickerOptions { + NONE = "none", + CUSTOM = "custom", +} + +const ColorPicker = ({ + label, + color, + onColorChange, + onColorRemove, + showSelectOptions = false, + className, +}: ColorPickerProps) => { + const [isPickerOpen, setIsPickerOpen] = React.useState(false); + const [colorOption, setColorOption] = React.useState( + () => { + if (color === ColorPickerOptions.NONE || color === "transparent") { + return ColorPickerOptions.NONE; + } + if (color === DEFAULT_COLOR) { + return DEFAULT_COLOR; + } + return ColorPickerOptions.CUSTOM; + }, + ); + + const handleColorOptionChange = (value?: string) => { + setColorOption(value); + if (value === ColorPickerOptions.NONE) { + onColorChange(ColorPickerOptions.NONE); + } else if (value === ColorPickerOptions.CUSTOM) { + onColorChange(PLACEHOLDER_COLOR); + } else { + onColorChange(DEFAULT_COLOR); + } + }; + + const handleChange = (event: React.ChangeEvent) => { + if (onColorChange) { + onColorChange(event.target.value); + } + }; + + const handleRemoveColor = () => { + if (onColorRemove) onColorRemove(); + }; + + const id = useId(); + + return ( +
+ {label && ( + + )} +
+ {showSelectOptions && ( +
+ setIsPickerOpen(true)} + onBlur={() => setIsPickerOpen(false)} + /> +
+ )} +
+
+ ); +}; + +export default ColorPicker; diff --git a/website/src/pages/themes/_components/color-scale-override-selector.tsx b/website/src/pages/themes/_components/color-scale-override-selector.tsx new file mode 100644 index 000000000..c624bd87f --- /dev/null +++ b/website/src/pages/themes/_components/color-scale-override-selector.tsx @@ -0,0 +1,74 @@ +import React, { useCallback } from "react"; +import clsx from "clsx"; +import Toggle from "./toggle"; +import { + defaultColorScale, + usePreviewOptions, +} from "../_providers/previewOptionsProvider"; +import ColorPickerList from "./color-picker-list"; + +type ColorScaleOverrideSelectorProps = { + id: string; + label?: string; + colors?: string | string[]; + onColorsChange: (colors: string[] | undefined) => void; + hideDefaultToggle?: boolean; + className?: string; +}; + +const ColorScaleOverrideSelector = ({ + id, + label = "Color Scale", + colors, + onColorsChange, + hideDefaultToggle = false, + className, +}: ColorScaleOverrideSelectorProps) => { + const { colorScale, updateColorScale } = usePreviewOptions(); + const [showCustomColors, setShowCustomColors] = React.useState( + () => !!colors && Array.isArray(colors), + ); + const [prevColors, setPrevColors] = React.useState( + Array.isArray(colors) ? colors : [], + ); + + const setColorScaleToDefault = useCallback(() => { + if (colorScale !== defaultColorScale) { + updateColorScale(defaultColorScale); + } + }, [colorScale, updateColorScale]); + + const onCheckboxChange = (isChecked) => { + setShowCustomColors(isChecked); + onColorsChange(!isChecked ? undefined : prevColors); + setColorScaleToDefault(); + }; + + const handleColorsChange = (newColors) => { + onColorsChange(newColors); + setPrevColors(newColors); + setColorScaleToDefault(); + }; + + return ( +
+ + {!hideDefaultToggle && ( + + )} + {showCustomColors && typeof colors !== "string" && ( + + )} +
+ ); +}; +export default ColorScaleOverrideSelector; diff --git a/website/src/pages/themes/_components/control.tsx b/website/src/pages/themes/_components/control.tsx new file mode 100644 index 000000000..c1dd35d35 --- /dev/null +++ b/website/src/pages/themes/_components/control.tsx @@ -0,0 +1,151 @@ +import React, { useId } from "react"; +import Select from "./select"; +import Slider from "./slider"; +import ColorPicker from "./color-picker"; +import ColorPaletteSelector from "./color-palette-selector"; +import { getConfigValue } from "../_utils"; +import { useTheme } from "../_providers/themeProvider"; +import Accordion from "./accordion"; +import ColorScaleOverrideSelector from "./color-scale-override-selector"; +import PreviewColorScaleSelect from "./theme-preview/preview-color-scale-select"; + +export type ColorChangeArgs = { + newColor?: string; + index: number; + colorScale: string; +}; + +type ControlProps = { + type: string; + control: any; + className?: string; +}; + +const Control = ({ type, control, className }: ControlProps) => { + const { customThemeConfig, updateCustomThemeConfig } = useTheme(); + + const handleChange = (newValue) => { + updateCustomThemeConfig(control.path, newValue); + }; + + const configValue = getConfigValue(customThemeConfig, control.path); + const id = useId(); + + switch (type) { + case "accordion": + return ( + + {control.controls?.map((nestedControl, i) => ( + + ))} + + ); + case "colorPalette": + return ( + + ); + case "section": + return ( +
+
+

+ {control.label} +

+ {control.description && ( +

+ {control.description} +

+ )} +
+ {control.controls?.map((nestedControl, i) => ( + + ))} +
+ ); + case "slider": + return ( + + ); + case "select": + return ( + handleCheckboxChange(value)} + className="mr-2" + /> + + + ))} + + +

+ +
  • + Save the Exported Theme File +

    + Save your custom generate theme to a file in your project. Use{" "} + theme.js or theme.ts depending on whether + your project uses JavaScript or TypeScript. +

    + +
  • +
  • + Import the Theme +

    + To use your custom theme in your application, import the file where + you saved the theme configuration. The import path should match the + file's location in your project directory. +

    + +
  • +
  • + Apply the Theme to Victory Components +

    + Once the theme is imported, you can apply it to Victory components + by passing it as the theme prop. +

    + + {/* Add your Victory components here */} +`} + language="jsx" + /> +
  • + +
    + ); +}; + +export default ExportPanel; diff --git a/website/src/pages/themes/_components/main.tsx b/website/src/pages/themes/_components/main.tsx new file mode 100644 index 000000000..7f556d248 --- /dev/null +++ b/website/src/pages/themes/_components/main.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { useSideNavContext } from "../_providers/sideNavProvider"; +import { ThemePreview } from "./theme-preview"; +import BaseThemePanel from "./base-theme-panel"; +import ChartPanel from "./chart-panel"; +import OptionsPanel from "./options-panel"; +import ExportPanel from "./export-panel"; +import CodePanel from "./code-panel"; + +const Main = () => { + const { activeSideNavItem } = useSideNavContext(); + + const isExportPanel = activeSideNavItem.panelType === "export"; + + if (isExportPanel) return ; + + if (activeSideNavItem.panelType === "code") return ; + + return ( + <> + + + + ); +}; +export default Main; diff --git a/website/src/pages/themes/_components/options-panel.tsx b/website/src/pages/themes/_components/options-panel.tsx new file mode 100644 index 000000000..897fd2423 --- /dev/null +++ b/website/src/pages/themes/_components/options-panel.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import Control from "./control"; +import PanelHeader from "./panel-header"; +import { OptionsPanelConfig } from "../_config"; + +type GlobalPanelProps = { + config: OptionsPanelConfig; +}; + +const OptionsPanel = ({ + config: { title, description, controls }, +}: GlobalPanelProps) => { + return ( + <> + + {controls.map((control, i) => { + return ( + + ); + })} + + ); +}; +export default OptionsPanel; diff --git a/website/src/pages/themes/_components/panel-header.tsx b/website/src/pages/themes/_components/panel-header.tsx new file mode 100644 index 000000000..5005f984b --- /dev/null +++ b/website/src/pages/themes/_components/panel-header.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +type PanelHeaderProps = { + title?: string; + description?: string; +}; + +const PanelHeader = ({ title, description }: PanelHeaderProps) => { + return ( +
    + {!!title && ( +

    {title}

    + )} + {!!description && ( +

    {description}

    + )} +
    + ); +}; +export default PanelHeader; diff --git a/demo/ts/components/theme-builder/select.tsx b/website/src/pages/themes/_components/select.tsx similarity index 53% rename from demo/ts/components/theme-builder/select.tsx rename to website/src/pages/themes/_components/select.tsx index dad35cbbd..69f9026e3 100644 --- a/demo/ts/components/theme-builder/select.tsx +++ b/website/src/pages/themes/_components/select.tsx @@ -12,7 +12,9 @@ type SelectProps = { options: SelectOption[]; value?: string; onChange: (value: string) => void; + includeDefault?: boolean; className?: string; + size?: "sm" | "md"; }; const Select = ({ @@ -21,17 +23,30 @@ const Select = ({ options, value = "", onChange, + includeDefault, className, + size = "md", }: SelectProps) => { const handleChange = (event: React.ChangeEvent) => { if (onChange) { onChange(event.target.value); } }; + + const labelSizeClasses = size === "sm" ? "font-medium" : "font-semibold"; + const selectSizeClasses = + size === "sm" ? "text-sm px-2 py-1.5" : "text-base p-2"; + return ( -
    +
    {label && ( -