@@ -171,7 +171,8 @@ const toolbarOptions = [
171171] ;
172172
173173const childrenMap = {
174- value : stringExposingStateControl ( "value" ) ,
174+ value : stringExposingStateControl ( "value" ) ,
175+ delta : stringExposingStateControl ( "delta" ) ,
175176 hideToolbar : BoolControl ,
176177 readOnly : BoolControl ,
177178 autoHeight : withDefault ( AutoHeightControl , "fixed" ) ,
@@ -194,7 +195,7 @@ interface IProps {
194195 hideToolbar : boolean ;
195196 readOnly : boolean ;
196197 autoHeight : boolean ;
197- onChange : ( value : string ) => void ;
198+ onChange : ( html : string , deltaJSON : string , text : string ) => void ;
198199 $style : RichTextEditorStyleType ;
199200 contentScrollBar : boolean ;
200201 tabIndex ?: number ;
@@ -207,15 +208,30 @@ function RichTextEditor(props: IProps) {
207208 const [ content , setContent ] = useState ( "" ) ;
208209 const wrapperRef = useRef < HTMLDivElement > ( null ) ;
209210 const editorRef = useRef < ReactQuill > ( null ) ;
211+
212+ const getQuill = ( ) => ( editorRef . current as any ) ?. getEditor ?.( ) ;
213+
214+ const tryParseDelta = ( v : unknown ) => {
215+ if ( ! v ) return null ;
216+ if ( typeof v === "string" ) {
217+ try {
218+ const d = JSON . parse ( v ) ;
219+ return Array . isArray ( d ?. ops ) ? d : null ;
220+ } catch { return null ; }
221+ }
222+ if ( typeof v === "object" && Array . isArray ( ( v as any ) . ops ) ) return v as any ;
223+ return null ;
224+ } ;
225+
210226 const isTypingRef = useRef ( 0 ) ;
211227
212228 const debounce = INPUT_DEFAULT_ONCHANGE_DEBOUNCE ;
213229
214230 const originOnChangeRef = useRef ( props . onChange ) ;
215231 originOnChangeRef . current = props . onChange ;
216232
217- const onChangeRef = useRef (
218- ( v : string ) => originOnChangeRef . current ?.( v )
233+ const onChangeRef = useRef ( ( html : string , deltaJSON : string , text : string ) =>
234+ originOnChangeRef . current ?.( html , deltaJSON , text )
219235 ) ;
220236
221237 // react-quill will not take effect after the placeholder is updated
@@ -235,7 +251,7 @@ function RichTextEditor(props: IProps) {
235251 ( editor . scroll . domNode as HTMLElement ) . tabIndex = props . tabIndex ;
236252 }
237253 }
238- } , [ props . tabIndex , key ] ) ; // Also re-run when key changes due to placeholder update
254+ } , [ props . tabIndex , key ] ) ;
239255
240256 const contains = ( parent : HTMLElement , descendant : HTMLElement ) => {
241257 try {
@@ -248,19 +264,31 @@ function RichTextEditor(props: IProps) {
248264 return parent . contains ( descendant ) ;
249265 } ;
250266
251- const handleChange = ( value : string ) => {
252- setContent ( value ) ;
253- // props.onChange(value);
254- onChangeRef . current ( value ) ;
255- } ;
256267
257268 useEffect ( ( ) => {
258- let finalValue = props . value ;
259- if ( ! / ^ < \w + > .+ < \/ \w + > $ / . test ( props . value ) ) {
260- finalValue = `<p class="">${ props . value } </p>` ;
269+ const q = getQuill ( ) ;
270+
271+ if ( ! q ) {
272+ const v = props . value ?? "" ;
273+ const looksHtml = / < \/ ? [ a - z ] [ \s \S ] * > / i. test ( v ) ;
274+ setContent ( looksHtml ? v : `<p class="">${ v } </p>` ) ;
275+ return ;
261276 }
262- setContent ( finalValue ) ;
277+
278+ const asDelta = tryParseDelta ( props . value ) ;
279+ if ( asDelta ) {
280+ q . setContents ( asDelta , "api" ) ;
281+ const html = q . root ?. innerHTML ?? "" ;
282+ setContent ( html ) ;
283+ return ;
284+ }
285+
286+ const v = props . value ?? "" ;
287+ const looksHtml = / < \/ ? [ a - z ] [ \s \S ] * > / i. test ( v ) ;
288+ const html = looksHtml ? v : `<p class="">${ v } </p>` ;
289+ setContent ( html ) ;
263290 } , [ props . value ] ) ;
291+
264292
265293 const handleClickWrapper = ( e : React . MouseEvent < HTMLDivElement > ) => {
266294 // grid item prevents bubbling, quill can't listen to events on document.body, so it can't close the toolbar drop-down box
@@ -297,7 +325,13 @@ function RichTextEditor(props: IProps) {
297325 value = { content }
298326 placeholder = { props . placeholder }
299327 readOnly = { props . readOnly }
300- onChange = { handleChange }
328+ onChange = { ( html , _delta , source , editor ) => {
329+ setContent ( html ) ;
330+ const quill = editorRef . current ?. getEditor ?.( ) ;
331+ const fullDelta = quill ?. getContents ?.( ) ?? { ops : [ ] } ;
332+ const text = quill ?. getText ?.( ) ?? "" ;
333+ onChangeRef . current ( html , JSON . stringify ( fullDelta ) , text ) ;
334+ } }
301335 />
302336 </ Suspense >
303337 </ Wrapper >
@@ -306,15 +340,16 @@ function RichTextEditor(props: IProps) {
306340
307341const RichTextEditorCompBase = new UICompBuilder ( childrenMap , ( props ) => {
308342 const debouncedOnChangeRef = useRef (
309- debounce ( ( value : string ) => {
310- props . value . onChange ( value ) ;
343+ debounce ( ( html : string , deltaJSON : string , text : string ) => {
344+ props . value . onChange ( html ) ;
345+ props . delta . onChange ( deltaJSON ) ;
311346 props . onEvent ( "change" ) ;
312347 } , 1000 )
313348 ) ;
314349
315- const handleChange = ( value : string ) => {
316- debouncedOnChangeRef . current ?.( value ) ;
317- } ;
350+ const handleChange = ( html : string , deltaJSON : string , text : string ) => {
351+ debouncedOnChangeRef . current ?.( html , deltaJSON , text ) ;
352+ } ;
318353
319354 return (
320355 < RichTextEditor
@@ -379,6 +414,7 @@ class RichTextEditorCompAutoHeight extends RichTextEditorCompBase {
379414
380415export const RichTextEditorComp = withExposingConfigs ( RichTextEditorCompAutoHeight , [
381416 new NameConfig ( "value" , trans ( "export.richTextEditorValueDesc" ) ) ,
417+ new NameConfig ( "delta" , trans ( "export.richTextEditorDeltaDesc" ) ) ,
382418 new NameConfig ( "readOnly" , trans ( "export.richTextEditorReadOnlyDesc" ) ) ,
383419 new NameConfig ( "hideToolbar" , trans ( "export.richTextEditorHideToolBarDesc" ) ) ,
384420 NameConfigHidden ,
0 commit comments