@@ -2,10 +2,11 @@ import * as React from 'react';
22import * as ReactDOM from 'react-dom' ;
33
44// Internally, the portalNode must be for either HTML or SVG elements
5- const ELEMENT_TYPE_HTML = 'html' ;
5+ const ELEMENT_TYPE_HTML_BLOCK = 'div' ;
6+ const ELEMENT_TYPE_HTML_INLINE = 'span' ;
67const ELEMENT_TYPE_SVG = 'svg' ;
78
8- type ANY_ELEMENT_TYPE = typeof ELEMENT_TYPE_HTML | typeof ELEMENT_TYPE_SVG ;
9+ type ANY_ELEMENT_TYPE = typeof ELEMENT_TYPE_HTML_BLOCK | typeof ELEMENT_TYPE_HTML_INLINE | typeof ELEMENT_TYPE_SVG ;
910
1011type Options = {
1112 attributes : { [ key : string ] : string } ;
@@ -32,29 +33,36 @@ interface PortalNodeBase<C extends Component<any>> {
3233 // latest placeholder we replaced. This avoids some race conditions.
3334 unmount ( expectedPlaceholder ?: Node ) : void ;
3435}
35- export interface HtmlPortalNode < C extends Component < any > = Component < any > > extends PortalNodeBase < C > {
36+ export interface HtmlBlockPortalNode < C extends Component < any > = Component < any > > extends PortalNodeBase < C > {
3637 element : HTMLElement ;
37- elementType : typeof ELEMENT_TYPE_HTML ;
38+ elementType : typeof ELEMENT_TYPE_HTML_BLOCK ;
39+ }
40+ export interface HtmlInlinePortalNode < C extends Component < any > = Component < any > > extends PortalNodeBase < C > {
41+ element : HTMLElement ;
42+ elementType : typeof ELEMENT_TYPE_HTML_INLINE ;
3843}
3944export interface SvgPortalNode < C extends Component < any > = Component < any > > extends PortalNodeBase < C > {
4045 element : SVGElement ;
4146 elementType : typeof ELEMENT_TYPE_SVG ;
4247}
43- type AnyPortalNode < C extends Component < any > = Component < any > > = HtmlPortalNode < C > | SvgPortalNode < C > ;
48+ type AnyPortalNode < C extends Component < any > = Component < any > > = HtmlBlockPortalNode < C > | HtmlInlinePortalNode < C > | SvgPortalNode < C > ;
4449
4550
4651const validateElementType = ( domElement : Element , elementType : ANY_ELEMENT_TYPE ) => {
4752 const ownerDocument = ( domElement . ownerDocument ?? document ) as any ;
4853 // Cast document to `any` because Typescript doesn't know about the legacy `Document.parentWindow` field, and also
4954 // doesn't believe `Window.HTMLElement`/`Window.SVGElement` can be used in instanceof tests.
5055 const ownerWindow = ownerDocument . defaultView ?? ownerDocument . parentWindow ?? window ; // `parentWindow` for IE8 and earlier
51- if ( elementType === ELEMENT_TYPE_HTML ) {
52- return domElement instanceof ownerWindow . HTMLElement ;
53- }
54- if ( elementType === ELEMENT_TYPE_SVG ) {
55- return domElement instanceof ownerWindow . SVGElement ;
56+
57+ switch ( elementType ) {
58+ case ELEMENT_TYPE_HTML_BLOCK :
59+ case ELEMENT_TYPE_HTML_INLINE :
60+ return domElement instanceof ownerWindow . HTMLElement ;
61+ case ELEMENT_TYPE_SVG :
62+ return domElement instanceof ownerWindow . SVGElement ;
63+ default :
64+ throw new Error ( `Unrecognized element type "${ elementType } " for validateElementType.` ) ;
5665 }
57- throw new Error ( `Unrecognized element type "${ elementType } " for validateElementType.` ) ;
5866} ;
5967
6068// This is the internal implementation: the public entry points set elementType to an appropriate value
@@ -68,12 +76,17 @@ const createPortalNode = <C extends Component<any>>(
6876 let lastPlaceholder : Node | undefined ;
6977
7078 let element ;
71- if ( elementType === ELEMENT_TYPE_HTML ) {
72- element = document . createElement ( 'div' ) ;
73- } else if ( elementType === ELEMENT_TYPE_SVG ) {
74- element = document . createElementNS ( SVG_NAMESPACE , 'g' ) ;
75- } else {
76- throw new Error ( `Invalid element type "${ elementType } " for createPortalNode: must be "html" or "svg".` ) ;
79+
80+ switch ( elementType ) {
81+ case ELEMENT_TYPE_HTML_BLOCK :
82+ case ELEMENT_TYPE_HTML_INLINE :
83+ element = document . createElement ( elementType ) ;
84+ break ;
85+ case ELEMENT_TYPE_SVG :
86+ element = document . createElementNS ( SVG_NAMESPACE , 'g' ) ;
87+ break ;
88+ default :
89+ throw new Error ( `Invalid element type "${ elementType } " for createPortalNode: must be "div", "span" or "svg".` ) ;
7790 }
7891
7992 if ( options && typeof options === "object" ) {
@@ -186,7 +199,7 @@ type OutPortalProps<C extends Component<any>> = {
186199
187200class OutPortal < C extends Component < any > > extends React . PureComponent < OutPortalProps < C > > {
188201
189- private placeholderNode = React . createRef < HTMLDivElement > ( ) ;
202+ private placeholderNode = React . createRef < HTMLElement > ( ) ;
190203 private currentPortalNode ?: AnyPortalNode < C > ;
191204
192205 constructor ( props : OutPortalProps < C > ) {
@@ -236,18 +249,23 @@ class OutPortal<C extends Component<any>> extends React.PureComponent<OutPortalP
236249 render ( ) {
237250 // Render a placeholder to the DOM, so we can get a reference into
238251 // our location in the DOM, and swap it out for the portaled node.
239- // A <div> placeholder works fine even for SVG.
240- return < div ref = { this . placeholderNode } /> ;
252+ // A <span> placeholder:
253+ // - prevents invalid HTML (e.g. inside <p>)
254+ // - works fine even for SVG.
255+ return < span ref = { this . placeholderNode } /> ;
241256 }
242257}
243258
244- const createHtmlPortalNode = createPortalNode . bind ( null , ELEMENT_TYPE_HTML ) as
245- < C extends Component < any > = Component < any > > ( options ?: Options ) => HtmlPortalNode < C > ;
259+ const createHtmlPortalNode = createPortalNode . bind ( null , ELEMENT_TYPE_HTML_BLOCK ) as
260+ < C extends Component < any > = Component < any > > ( options ?: Options ) => HtmlBlockPortalNode < C > ;
261+ const createHtmlInlinePortalNode = createPortalNode . bind ( null , ELEMENT_TYPE_HTML_INLINE ) as
262+ < C extends Component < any > = Component < any > > ( options ?: Options ) => HtmlInlinePortalNode < C > ;
246263const createSvgPortalNode = createPortalNode . bind ( null , ELEMENT_TYPE_SVG ) as
247264 < C extends Component < any > = Component < any > > ( options ?: Options ) => SvgPortalNode < C > ;
248265
249266export {
250267 createHtmlPortalNode ,
268+ createHtmlInlinePortalNode ,
251269 createSvgPortalNode ,
252270 InPortal ,
253271 OutPortal ,
0 commit comments