1
- import { Chain , NamedChain } from '@ephox/agar' ;
2
1
import { Fun , Optional } from '@ephox/katamari' ;
3
2
import { SugarElement , SugarNode } from '@ephox/sugar' ;
4
3
import * as React from 'react' ;
5
4
import * as ReactDOM from 'react-dom' ;
6
- import { Editor , IAllProps , IProps } from '../../../main/ts/components/Editor' ;
5
+ import { Editor , IAllProps , IProps , Version } from '../../../main/ts/components/Editor' ;
7
6
import { Editor as TinyMCEEditor } from 'tinymce' ;
7
+ import { before , context } from '@ephox/bedrock-client' ;
8
+ import { VersionLoader } from '@tinymce/miniature' ;
9
+
10
+ // @ts -expect-error Remove when dispose polyfill is not needed
11
+ Symbol . dispose ??= Symbol ( 'Symbol.dispose' ) ;
12
+ // @ts -expect-error Remove when dispose polyfill is not needed
13
+ Symbol . asyncDispose ??= Symbol ( 'Symbol.asyncDispose' ) ;
8
14
9
15
export interface Context {
10
16
DOMNode : HTMLElement ;
@@ -18,78 +24,78 @@ const getRoot = () => Optional.from(document.getElementById('root')).getOrThunk(
18
24
document . body . appendChild ( root ) ;
19
25
return root ;
20
26
} ) ;
27
+ export interface ReactEditorContext extends Context , Disposable {
28
+ reRender ( props : IAllProps ) : Promise < void > ;
29
+ remove ( ) : void ;
30
+ }
21
31
22
- const cRender = ( props : Partial < IAllProps > ) => Chain . async < unknown , Context > ( ( _ , next , die ) => {
32
+ export const render = async ( props : Partial < IAllProps > = { } , container : HTMLElement = getRoot ( ) ) : Promise < ReactEditorContext > => {
23
33
const originalInit = props . init || { } ;
24
34
const originalSetup = originalInit . setup || Fun . noop ;
25
35
const ref = React . createRef < Editor > ( ) ;
26
36
27
- const init : IProps [ 'init' ] = {
28
- ...originalInit ,
29
- setup : ( editor ) => {
30
- originalSetup ( editor ) ;
37
+ const ctx = await new Promise < Context > ( ( resolve , reject ) => {
38
+ const init : IProps [ 'init' ] = {
39
+ ...originalInit ,
40
+ setup : ( editor ) => {
41
+ originalSetup ( editor ) ;
31
42
32
- editor . on ( 'SkinLoaded' , ( ) => {
33
- setTimeout ( ( ) => {
34
- Optional . from ( ref . current )
35
- . map ( ReactDOM . findDOMNode )
36
- . bind ( Optional . from )
37
- . map ( SugarElement . fromDom )
38
- . filter ( SugarNode . isHTMLElement )
39
- . map ( ( val ) => val . dom )
40
- . fold ( ( ) => die ( 'Could not find DOMNode' ) , ( DOMNode ) => {
41
- next ( {
42
- ref,
43
- editor,
44
- DOMNode
43
+ editor . on ( 'SkinLoaded' , ( ) => {
44
+ setTimeout ( ( ) => {
45
+ Optional . from ( ref . current )
46
+ . map ( ReactDOM . findDOMNode )
47
+ . bind ( Optional . from )
48
+ . map ( SugarElement . fromDom )
49
+ . filter ( SugarNode . isHTMLElement )
50
+ . map ( ( val ) => val . dom )
51
+ . fold ( ( ) => reject ( 'Could not find DOMNode' ) , ( DOMNode ) => {
52
+ resolve ( {
53
+ ref,
54
+ editor,
55
+ DOMNode,
56
+ } ) ;
45
57
} ) ;
46
- } ) ;
47
- } , 0 ) ;
48
- } ) ;
49
- }
50
- } ;
58
+ } , 0 ) ;
59
+ } ) ;
60
+ }
61
+ } ;
51
62
52
- /**
63
+ /**
53
64
* NOTE: TinyMCE will manipulate the DOM directly and this may cause issues with React's virtual DOM getting
54
65
* out of sync. The official fix for this is wrap everything (textarea + editor) in an element. As far as React
55
66
* is concerned, the wrapper always only has a single child, thus ensuring that React doesn’t have a reason to
56
67
* touch the nodes created by TinyMCE. Since this only seems to be an issue when rendering TinyMCE 4 directly
57
68
* into a root and a fix would be a breaking change, let's just wrap the editor in a <div> here for now.
58
69
*/
59
- ReactDOM . render ( < div > < Editor ref = { ref } apiKey = 'no-api-key' { ...props } init = { init } /> </ div > , getRoot ( ) ) ;
60
- } ) ;
61
-
62
- // By rendering the Editor into the same root, React will perform a diff and update.
63
- const cReRender = ( props : Partial < IAllProps > ) => Chain . op < Context > ( ( context ) => {
64
- ReactDOM . render ( < div > < Editor apiKey = 'no-api-key' ref = { context . ref } { ...props } /> </ div > , getRoot ( ) ) ;
65
- } ) ;
70
+ ReactDOM . render ( < div > < Editor ref = { ref } apiKey = 'no-api-key' { ...props } init = { init } /> </ div > , container ) ;
71
+ } ) ;
66
72
67
- const cRemove = Chain . op ( ( _ ) => {
68
- ReactDOM . unmountComponentAtNode ( getRoot ( ) ) ;
69
- } ) ;
73
+ const remove = ( ) => {
74
+ ReactDOM . unmountComponentAtNode ( container ) ;
75
+ } ;
70
76
71
- const cNamedChainDirect = ( name : keyof Context ) => NamedChain . direct (
72
- NamedChain . inputName ( ) ,
73
- Chain . mapper ( ( res : Context ) => res [ name ] ) ,
74
- name
75
- ) ;
77
+ return {
78
+ ...ctx ,
79
+ /** By rendering the Editor into the same root, React will perform a diff and update. */
80
+ reRender : ( newProps : IAllProps ) => new Promise < void > ( ( resolve ) =>
81
+ ReactDOM . render ( < div > < Editor apiKey = 'no-api-key' ref = { ctx . ref } { ...newProps } /> </ div > , container , resolve )
82
+ ) ,
83
+ remove,
84
+ [ Symbol . dispose ] : remove
85
+ } ;
86
+ } ;
76
87
77
- const cDOMNode = ( chain : Chain < Context [ 'DOMNode' ] , unknown > ) : Chain < Context , Context > => NamedChain . asChain < Context > ( [
78
- cNamedChainDirect ( 'DOMNode' ) ,
79
- NamedChain . read ( 'DOMNode' , chain ) ,
80
- NamedChain . outputInput
81
- ] ) ;
88
+ type RenderWithVersion = (
89
+ props : Omit < IAllProps , 'cloudChannel' | 'tinymceScriptSrc' > ,
90
+ container ?: HTMLElement | HTMLDivElement
91
+ ) => Promise < ReactEditorContext > ;
82
92
83
- const cEditor = ( chain : Chain < Context [ 'editor' ] , unknown > ) : Chain < Context , Context > => NamedChain . asChain < Context > ( [
84
- cNamedChainDirect ( 'editor' ) ,
85
- NamedChain . read ( 'editor' , chain ) ,
86
- NamedChain . outputInput
87
- ] ) ;
93
+ export const withVersion = ( version : Version , fn : ( render : RenderWithVersion ) => void ) : void => {
94
+ context ( `TinyMCE ( ${ version } )` , ( ) => {
95
+ before ( async ( ) => {
96
+ await VersionLoader . pLoadVersion ( version ) ;
97
+ } ) ;
88
98
89
- export {
90
- cRender ,
91
- cReRender ,
92
- cRemove ,
93
- cDOMNode ,
94
- cEditor
99
+ fn ( render as RenderWithVersion ) ;
100
+ } ) ;
95
101
} ;
0 commit comments