33
44import * as React from 'react' ;
55import * as ReactDOM from 'react-dom' ;
6+
67import { StringMap } from '../declarations/string-map' ;
78import removeUndefinedProperties from '../utils/object/remove-undefined-properties' ;
89import { CHILDREN_TO_APPEND_PROP } from './react-content' ;
@@ -14,11 +15,15 @@ export function isReactNode(node: any): node is ReactNode {
1415 return ( < ReactNode > node ) . setRenderPendingCallback !== undefined ;
1516}
1617
18+ /**
19+ * Logical representation of everything needed to render a React element in the
20+ * DOM, with the needed methods to do so.
21+ */
1722export class ReactNode {
1823 // Access to these properties are restricted through setters and functions
1924 // so that the dirty "render pending" state of this object can be properly
2025 // tracked and all nodes with "render pending" can be flushed at the end
21- // of render operation.
26+ // of a render operation.
2227 private _props = { } ;
2328 private _comment : string ;
2429 private _text : string ;
@@ -70,28 +75,66 @@ export class ReactNode {
7075 this . _isRenderPending = true ;
7176 }
7277
78+ /**
79+ * Marks the node to be removed from the DOM in the next render cycle.
80+ */
7381 destroyNode ( ) {
7482 this . setRenderPending ( ) ;
7583 this . _isDestroyPending = true ;
7684 }
7785
78- setAttribute ( name : string , value : any ) {
86+ /**
87+ * Sets an attribute on the node.
88+ * @note the value can only be a `string`. See `setProperty` for other use-cases.
89+ * @see `Renderer2#setAttribute`.
90+ *
91+ * @param name The attribute name.
92+ * @param value The new value.
93+ */
94+ setAttribute ( name : string , value : string ) {
7995 this . setAttributes ( { [ name ] : value } ) ;
8096 }
8197
82- setAttributes ( attributes : StringMap ) {
98+ /**
99+ * Set attributes on this node.
100+ * Note that values can only be of type `string`. See `setProperties` for other use-cases.
101+ * @see `Renderer2#setAttribute`.
102+ *
103+ * @param attributes the attributes to set.
104+ */
105+ setAttributes ( attributes : StringMap < string > ) {
83106 this . setProperties ( attributes ) ;
84107 }
85108
109+ /**
110+ * Sets a prop in the underlying React element.
111+ * @see `Renderer2#setProperty`.
112+ *
113+ * @param name The property name.
114+ * @param value The new value.
115+ */
86116 setProperty ( name : string , value : any ) {
87117 this . setProperties ( { [ name ] : value } ) ;
88118 }
89119
120+ /**
121+ * Like `setProperty` but for multiple props at once.
122+ *
123+ * @param properties An object with the props.
124+ */
90125 setProperties ( properties : StringMap ) {
91126 this . setRenderPending ( ) ;
92127 Object . assign ( this . _props , removeUndefinedProperties ( properties ) ) ;
93128 }
94129
130+ /**
131+ * Remove a prop or an attribute from the underlying React element.
132+ * @see `Renderer2#removeAttribute`.
133+ *
134+ * @param name The property name.
135+ * @param childName _Optional_ A property of `name` to remove instead.
136+ * @returns the deleted property
137+ */
95138 removeProperty ( name : string , childName ?: string ) {
96139 this . setRenderPending ( ) ;
97140 if ( childName ) {
@@ -101,23 +144,49 @@ export class ReactNode {
101144 return delete this . _props [ name ] ;
102145 }
103146
147+ /**
148+ * Add a direct child of this node.
149+ * @see `Renderer2#addChild`.
150+ *
151+ * @param node The node to add.
152+ */
104153 addChild ( node : ReactNode ) {
105154 this . setRenderPending ( ) ;
106155 this . _children . push ( node ) ;
107156 }
108157
158+ /**
159+ * Remove a direct child of this node.
160+ * @see `Renderer2#removeChild`.
161+ *
162+ * @param node The node to remove.
163+ */
109164 removeChild ( node : ReactNode ) {
110165 this . setRenderPending ( ) ;
111166 this . _children = this . _children . filter ( child => child !== node ) ;
112167 }
113168
169+ /**
170+ * Cast the node to a comment node.
171+ * @see `Renderer2#createComment`.
172+ *
173+ * @param value the text in the comment to render.
174+ * @returns itself.
175+ */
114176 asComment ( value : string ) {
115177 this . setRenderPending ( ) ;
116178 this . type = undefined ;
117179 this . _comment = value ;
118180 return this ;
119181 }
120182
183+ /**
184+ * Cast the node to a text node.
185+ * @see `Renderer2#createText`.
186+ *
187+ * @param value the text to render.
188+ * @returns itself.
189+ */
121190 asText ( value : string ) {
122191 this . setRenderPending ( ) ;
123192 this . type = undefined ;
@@ -132,6 +201,11 @@ export class ReactNode {
132201 return this ;
133202 }
134203
204+ /**
205+ * Render the node to the DOM, or unmount it, as necessary.
206+ *
207+ * @returns itself.
208+ */
135209 render ( ) : ReactNode {
136210 // Only complete render operations for ReactNodes that are parented by HTMLElements.
137211 // Those that are parented by other ReactNodes will be rendered recursively by their
@@ -159,7 +233,14 @@ export class ReactNode {
159233 return this ;
160234 }
161235
162- // This is called by Angular core when projected content is being added.
236+ /**
237+ * Appends a child.
238+ *
239+ * @see `Renderer2#appendChild`.
240+ * @note This is called by Angular core when projected content is being added.
241+ *
242+ * @param projectedContent the content to project.
243+ */
163244 appendChild ( projectedContent : HTMLElement ) {
164245 if ( DEBUG ) {
165246 console . error (
@@ -172,6 +253,9 @@ export class ReactNode {
172253 this . _childrenToAppend . push ( projectedContent ) ;
173254 }
174255
256+ /**
257+ * @note for easier debugging.
258+ */
175259 toString ( ) : string {
176260 if ( this . _typeName ) {
177261 return `[${ this . _typeName } ReactNode]` ;
@@ -203,6 +287,14 @@ export class ReactNode {
203287 this . _props [ 'key' ] = key ;
204288 }
205289
290+ // Just having some props on a React element can cause it to
291+ // behave undesirably, and since the templates are hard-coded to pass
292+ // all Inputs all the time, they pass `undefined` values too.
293+ // This ensures these are removed.
294+ // Additionally, there are some things that Angular templating forbids,
295+ // and stops at-compile time (with errors), such as `Input`s being prefixed with `on`.
296+ // Since React does not have the notion of `Output`s as Angular (they are just props of type function, essentially callbacks).
297+ // To work around this, we, by convention, prefix any PascalCased prop with `on` here, after the template has already been compiled.
206298 const clearedProps = this . _transformProps ( removeUndefinedProperties ( this . _props ) ) ;
207299
208300 if ( DEBUG ) {
@@ -249,6 +341,8 @@ export class ReactNode {
249341 this . _typeName = this . type as string ;
250342
251343 // Attempt to resolve the type as a React Element class name/type.
344+ // Since Angular templates are just strings, we can't include types in them.
345+ // Therefore, we use the component registry to resolve the type of a component from a string.
252346 if ( typeof this . type === 'string' ) {
253347 this . type = getComponentClass ( this . type ) ;
254348 }
0 commit comments