diff --git a/web/src/components/Markdown/index.tsx b/web/src/components/Markdown/index.tsx index 4f48e655..bd33cf76 100644 --- a/web/src/components/Markdown/index.tsx +++ b/web/src/components/Markdown/index.tsx @@ -22,7 +22,7 @@ * @component */ -import { useState, useRef, useEffect, type FC, createContext, useContext, useCallback } from 'react' +import { useState, useRef, useEffect, type FC, createContext, useContext, useCallback, useMemo } from 'react' import { Image, Input, Select, Form, Checkbox, Radio, ColorPicker, DatePicker, TimePicker, InputNumber, Slider } from 'antd' import ReactMarkdown from 'react-markdown' import RemarkGfm from 'remark-gfm' @@ -44,6 +44,11 @@ const FormContext = createContext<{ onSubmit?: (values: Record) => void; } | null>(null) +/** Stable form wrapper component — state lives in RbMarkdown, survives components object rebuilds */ +const RbForm: FC = ({ children, ...props }) => ( +
{children}
+) + /** Props interface for RbMarkdown component */ interface RbMarkdownProps { /** Markdown content to render */ @@ -60,8 +65,8 @@ interface RbMarkdownProps { onFormSubmit?: (values: Record) => void; } -/** Build components with onFormSubmit callback */ -const buildComponents = (onFormSubmit?: (values: Record) => void) => ({ +/** Build stable components map — form submission handled via FormContext */ +const buildComponents = () => ({ h1: ({ children, ...props }: any) =>

{children}

, h2: ({ children, ...props }: any) =>

{children}

, h3: ({ children, ...props }: any) =>

{children}

, @@ -95,7 +100,7 @@ const buildComponents = (onFormSubmit?: (values: Record) => void) = td: ({ children, ...props }: any) => {children}, button: ({ children, ...props }: any) => { const ctx = useContext(FormContext) - return ctx?.onSubmit?.(ctx.values)}>{[children]} + return ctx?.onSubmit?.(ctx?.values ?? {})}>{[children]} }, input: ({ children, value, ...props }: any) => { const ctx = useContext(FormContext) @@ -125,7 +130,7 @@ const buildComponents = (onFormSubmit?: (values: Record) => void) = return case 'submit': case 'button': - return ctx?.onSubmit?.(ctx.values)}>{[props.value || children]} + return ctx?.onSubmit?.(ctx?.values ?? {})}>{[props.value || children]} case 'checkbox': return handleChange(e.target.checked)}>{children} case 'password': @@ -149,15 +154,7 @@ const buildComponents = (onFormSubmit?: (values: Record) => void) = const ctx = useContext(FormContext) return { if (props.name) ctx?.setValue(props.name, e.target.value) }}>{children} }, - form: ({ children, ...props }: any) => { - const [values, setValues] = useState>({}) - const setValue = useCallback((name: string, value: any) => setValues(prev => ({ ...prev, [name]: value })), []) - return ( - -
{children}
-
- ) - }, + form: RbForm, label: ({ children, ...props }: any) => { return }, @@ -172,7 +169,10 @@ const RbMarkdown: FC = ({ className, onFormSubmit, }) => { - const components = buildComponents(onFormSubmit) + const [formValues, setFormValues] = useState>({}) + const setValue = useCallback((name: string, value: any) => setFormValues(prev => ({ ...prev, [name]: value })), []) + const formCtx = useMemo(() => ({ values: formValues, setValue, onSubmit: onFormSubmit }), [formValues, setValue, onFormSubmit]) + const components = useMemo(() => buildComponents(), []) const [editContent, setEditContent] = useState(content) const textareaRef = useRef(null) @@ -239,6 +239,7 @@ const RbMarkdown: FC = ({ /** Render markdown preview mode */ return ( +