1- import { forwardRef , ComponentPropsWithoutRef , PropsWithoutRef } from "react"
1+ import { forwardRef , ComponentPropsWithoutRef , PropsWithoutRef , useState } from "react"
22import { useField , UseFieldConfig } from "react-final-form"
33
4+ import ReactMarkdown from "react-markdown"
5+ import remarkGfm from "remark-gfm"
6+ import remarkBreaks from "remark-breaks"
7+
48export interface LabeledTextAreaFieldProps
59 extends PropsWithoutRef < JSX . IntrinsicElements [ "textarea" ] > {
610 /** Field name. */
@@ -27,18 +31,76 @@ export const LabeledTextAreaField = forwardRef<HTMLTextAreaElement, LabeledTextA
2731 } )
2832
2933 const normalizedError = Array . isArray ( error ) ? error . join ( ", " ) : error || submitError
34+ const [ mode , setMode ] = useState < "edit" | "preview" > ( "edit" )
3035
3136 return (
3237 < div { ...outerProps } data-testid = "labeledarea-testid" >
3338 < label { ...labelProps } >
3439 { label }
35- < textarea
36- { ...input }
37- disabled = { submitting }
38- { ...props }
39- ref = { ref }
40- data-testid = "labeledtarget-testid"
41- />
40+
41+ { /* Toolbar */ }
42+ < div className = "flex items-center mt-2" >
43+ < div className = "join" >
44+ < button
45+ type = "button"
46+ className = { `btn btn-sm join-item ${ mode === "edit" ? "btn-primary" : "btn-ghost" } ` }
47+ onClick = { ( ) => setMode ( "edit" ) }
48+ >
49+ Edit
50+ </ button >
51+ < button
52+ type = "button"
53+ className = { `btn btn-sm join-item ${
54+ mode === "preview" ? "btn-primary" : "btn-ghost"
55+ } `}
56+ onClick = { ( ) => setMode ( "preview" ) }
57+ >
58+ Preview
59+ </ button >
60+ </ div >
61+ < span className = "text-sm text-base-content ml-3 italic" >
62+ Supports{ " " }
63+ < a
64+ href = "https://www.markdownguide.org/cheat-sheet/"
65+ target = "_blank"
66+ rel = "noopener noreferrer"
67+ className = "text-primary underline"
68+ >
69+ Markdown
70+ </ a > { " " }
71+ formatting.
72+ </ span >
73+ </ div >
74+
75+ { mode === "edit" ? (
76+ < textarea
77+ { ...input }
78+ disabled = { submitting }
79+ { ...props }
80+ ref = { ref }
81+ data-testid = "labeledtarget-testid"
82+ rows = { 6 }
83+ wrap = "soft"
84+ />
85+ ) : (
86+ < div className = "markdown-preview mb-4" data-testid = "labeledpreview-testid" >
87+ < ReactMarkdown
88+ remarkPlugins = { [ remarkGfm , remarkBreaks ] }
89+ components = { {
90+ a : ( { node, ...props } ) => (
91+ < a
92+ { ...props }
93+ target = "_blank"
94+ rel = "noopener noreferrer"
95+ className = "text-primary"
96+ />
97+ ) ,
98+ } }
99+ >
100+ { input . value || "_Nothing to preview yet…_" }
101+ </ ReactMarkdown >
102+ </ div >
103+ ) }
42104 </ label >
43105
44106 { touched && normalizedError && (
@@ -61,6 +123,11 @@ export const LabeledTextAreaField = forwardRef<HTMLTextAreaElement, LabeledTextA
61123 border-radius: 3px;
62124 appearance: none;
63125 margin-top: 0.5rem;
126+ resize: both;
127+ overflow: auto;
128+ max-width: 100%;
129+ white-space: pre-wrap;
130+ line-height: 1.5;
64131 }
65132
66133 textarea:focus {
0 commit comments