diff --git a/demo/ts/components/theme-builder/accordion.tsx b/demo/ts/components/theme-builder/accordion.tsx new file mode 100644 index 000000000..5578b4d5a --- /dev/null +++ b/demo/ts/components/theme-builder/accordion.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import clsx from "clsx"; +import { FaChevronDown } from "react-icons/fa"; + +type AccordionProps = { + id: string; + title: string; + children: React.ReactNode; + defaultOpen?: boolean; +}; + +const Accordion = ({ + id, + title, + children, + defaultOpen = false, +}: AccordionProps) => { + const [isOpen, setIsOpen] = React.useState(defaultOpen); + + const toggleAccordion = () => { + setIsOpen(!isOpen); + }; + + return ( +
+

+ +

+
+
+ {children} +
+
+
+ ); +}; +export default Accordion; diff --git a/demo/ts/components/theme-builder/color-picker.tsx b/demo/ts/components/theme-builder/color-picker.tsx index 2759c2af0..ba1609469 100644 --- a/demo/ts/components/theme-builder/color-picker.tsx +++ b/demo/ts/components/theme-builder/color-picker.tsx @@ -6,7 +6,7 @@ type ColorPickerProps = { label?: string; color: string; id: string; - onColorChange: (event: React.ChangeEvent) => void; + onColorChange: (color: string) => void; showColorName?: boolean; }; @@ -21,7 +21,7 @@ const ColorPicker = ({ const handleChange = (event: React.ChangeEvent) => { if (onColorChange) { - onColorChange(event); + onColorChange(event.target.value); } }; @@ -33,7 +33,7 @@ const ColorPicker = ({ )}
{!showColorName && (
@@ -82,7 +82,7 @@ const ColorPicker = ({ )} ; + newColor: string; index: number; colorScale: string; }; @@ -13,7 +13,7 @@ type ColorScaleOptionsProps = { palette?: VictoryThemeDefinition["palette"]; activeColorScale?: ColorScalePropType; onColorChange: (args: ColorChangeArgs) => void; - onColorScaleChange: (event: React.ChangeEvent) => void; + onColorScaleChange: (colorScale: string) => void; }; const colorScales = [ @@ -57,6 +57,7 @@ const ColorScaleOptions = ({ onChange={onColorScaleChange} options={colorScales} label="Color Scale" + className="mb-5" />
{palette?.[activeColorScale as string]?.map((color, i) => ( @@ -64,9 +65,9 @@ const ColorScaleOptions = ({ key={i} color={color} id={`color-${i}`} - onColorChange={(event) => + onColorChange={(newColor) => onColorChange({ - event, + newColor, index: i, colorScale: activeColorScale as string, }) diff --git a/demo/ts/components/theme-builder/config-mapper.tsx b/demo/ts/components/theme-builder/config-mapper.tsx new file mode 100644 index 000000000..41f12a845 --- /dev/null +++ b/demo/ts/components/theme-builder/config-mapper.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import optionsConfig from "./options-config"; +import Accordion from "./accordion"; +import Select from "./select"; +import Slider from "./slider"; +import ColorPicker from "./color-picker"; +import ColorScaleOptions from "./color-scale-options"; +import { getConfigValue } from "./utils"; + +const ConfigMapper = ({ + themeConfig, + activeColorScale, + updateThemeConfig, + handleColorScaleChange, +}) => { + const handleColorChange = ({ newColor, index, colorScale }) => { + const updatedColors = themeConfig?.palette?.[colorScale]?.map((color, i) => + i === index ? newColor : color, + ); + updateThemeConfig(`palette.${colorScale}`, updatedColors); + }; + + return ( + <> + {optionsConfig.map((section, index) => ( + + {section.fields.map((field) => { + if (field.type === "colorScale") { + return ( + + ); + } + const configValue = getConfigValue(themeConfig, field.path); + if (field.type === "slider") { + return ( + + updateThemeConfig(field.path, newValue) + } + /> + ); + } + if (field.type === "select") { + return ( + - {options.map((option) => ( - ))} diff --git a/demo/ts/components/theme-builder/slider.tsx b/demo/ts/components/theme-builder/slider.tsx index 2b4934ccd..c6cb870c6 100644 --- a/demo/ts/components/theme-builder/slider.tsx +++ b/demo/ts/components/theme-builder/slider.tsx @@ -1,19 +1,31 @@ -import React, { useEffect } from "react"; +import React from "react"; -const Slider = ({ label, id, defaultValue, unit = "px", onChange }) => { - const [value, setValue] = React.useState(defaultValue); +type SliderProps = { + label: string; + id: string; + value: number; + unit?: string; + onChange?: (value: number) => void; + min?: number; + max?: number; +}; +const Slider = ({ + label, + id, + value, + unit = "px", + onChange, + min, + max, +}: SliderProps) => { const handleChange = (event) => { - setValue(event.target.value); + const newValue = event.target.value; if (onChange) { - onChange(event); + onChange(newValue); } }; - useEffect(() => { - setValue(defaultValue); - }, [defaultValue]); - return (
); diff --git a/demo/ts/components/theme-builder/utils.ts b/demo/ts/components/theme-builder/utils.ts new file mode 100644 index 000000000..5dd43c36b --- /dev/null +++ b/demo/ts/components/theme-builder/utils.ts @@ -0,0 +1,28 @@ +import { VictoryThemeDefinition } from "victory-core"; + +export const setNestedConfigValue = ( + config: VictoryThemeDefinition, + path: string, + value: unknown, +) => { + const pathArray = path.split("."); + const updatedConfig = { ...config }; + 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, +) => { + const pathArray = path.split("."); + return pathArray.reduce((acc, key) => acc[key], config); +};