001package react4j;
002
003import java.util.Objects;
004import javaemul.internal.annotations.DoNotAutobox;
005import javax.annotation.Nonnull;
006import javax.annotation.Nullable;
007import jsinterop.annotations.JsOverlay;
008import jsinterop.annotations.JsPackage;
009import jsinterop.annotations.JsProperty;
010import jsinterop.annotations.JsType;
011import jsinterop.base.JsPropertyMap;
012import org.jetbrains.annotations.Contract;
013import react4j.internal.ViewConstructorFunction;
014
015/**
016 * Element represents either a view or a host component.
017 */
018@SuppressWarnings( "unused" )
019@JsType( isNative = true, name = "Object", namespace = JsPackage.GLOBAL )
020public class ReactElement
021  implements ReactNode
022{
023  @JsProperty( name = "$$typeof" )
024  private Object typeof;
025  private Object type;
026  private String key;
027  private Object ref;
028  @JsProperty( name = "props" )
029  private JsPropertyMap<Object> inputs;
030  // The view responsible for creating this element.
031  // can be null if create happens outside of a render method (i.e. at the top level).
032  @Nullable
033  private Object _owner;
034
035  @JsOverlay
036  @Contract( pure = true )
037  @Nonnull
038  public final ReactElement dup()
039  {
040    final ReactElement element = createRawNode( typeof, type );
041    element.key = key;
042    element.ref = ref;
043    element.inputs = JsPropertyMap.of();
044    inputs.forEach( key -> element.inputs.set( key, inputs.get( key ) ) );
045    return element;
046  }
047
048  @JsOverlay
049  @Nonnull
050  @Contract( pure = true )
051  public static ReactElement createViewElement( @Nonnull final ViewConstructorFunction type )
052  {
053    @SuppressWarnings( { "JavaExistingMethodCanBeUsed", "RedundantSuppression" } )
054    final ReactElement element = create( type );
055    element.inputs = JsPropertyMap.of();
056    element.key = null;
057    element.ref = null;
058    return element;
059  }
060
061  @JsOverlay
062  @Nonnull
063  @Contract( pure = true )
064  static ReactElement createContextElement( @Nonnull final Object type )
065  {
066    final ReactElement element = create( type );
067    element.inputs = JsPropertyMap.of();
068    element.key = null;
069    element.ref = null;
070    return element;
071  }
072
073  @JsOverlay
074  @Nonnull
075  @Contract( pure = true )
076  private static ReactElement create( @Nonnull final Object type )
077  {
078    return createRawNode( React.Element, type );
079  }
080
081  @JsOverlay
082  @Nonnull
083  @Contract( pure = true )
084  private static ReactElement createRawNode( @Nonnull final Object typeof, @Nonnull final Object type )
085  {
086    final ReactElement element = new ReactElement();
087    element.typeof = Objects.requireNonNull( typeof );
088    element.type = Objects.requireNonNull( type );
089    element._owner = React.currentOwner();
090    return element;
091  }
092
093  @JsOverlay
094  @Nonnull
095  @Contract( pure = true )
096  private static ReactElement createRawElement( @Nonnull final Object type,
097                                                @Nullable final String key,
098                                                @Nullable final Object ref,
099                                                @Nonnull final JsPropertyMap<Object> inputs )
100  {
101    final ReactElement element = create( type );
102    element.key = key;
103    element.ref = ref;
104    element.inputs = Objects.requireNonNull( inputs );
105    return element;
106  }
107
108  @JsOverlay
109  @Nonnull
110  @Contract( pure = true )
111  public static ReactElement createFragment( @Nullable final String key, @Nonnull final ReactNode... children )
112  {
113    final ReactElement element = createRawNode( React.Element, React.Fragment );
114    element.key = key;
115    element.ref = null;
116    element.inputs = JsPropertyMap.of( "children", Objects.requireNonNull( children ) );
117    return element;
118  }
119
120  /**
121   * Create a StrictMode component with the specified children.
122   *
123   * @param children the child nodes.
124   * @return a new React.StrictMode component.
125   */
126  @JsOverlay
127  @Nonnull
128  @Contract( pure = true )
129  public static ReactNode createStrictMode( @Nonnull final ReactNode... children )
130  {
131    return ReactElement.createRawElement( React.StrictMode,
132                                          null,
133                                          null,
134                                          JsPropertyMap.of( "children", Objects.requireNonNull( children ) ) );
135  }
136
137  @JsOverlay
138  @Nonnull
139  @Contract( pure = true )
140  public static ReactElement createSuspense( @Nullable final String key,
141                                             @Nullable final ReactNode fallback,
142                                             final int maxTimeToFallback,
143                                             @Nonnull final ReactNode... children )
144  {
145    final ReactElement element = createRawNode( React.Element, React.Suspense );
146    element.key = key;
147    element.ref = null;
148    element.inputs = JsPropertyMap.of( "children", Objects.requireNonNull( children ),
149                                       "fallback", fallback,
150                                       "ms", maxTimeToFallback );
151    return element;
152  }
153
154  @JsOverlay
155  @Nonnull
156  @Contract( pure = true )
157  public static ReactElement createHostElement( @Nonnull final String type,
158                                                @Nullable final String key,
159                                                @Nullable final Object ref,
160                                                @Nonnull final JsPropertyMap<Object> inputs )
161  {
162    return createRawElement( type, key, ref, inputs );
163  }
164
165  @JsOverlay
166  @Nullable
167  @Contract( pure = true )
168  public final String key()
169  {
170    return key;
171  }
172
173  @JsOverlay
174  public final void setKey( @Nullable final String key )
175  {
176    this.key = key;
177  }
178
179  @JsOverlay
180  @Nonnull
181  @Contract( pure = true )
182  public final JsPropertyMap<Object> inputs()
183  {
184    return inputs;
185  }
186
187  @JsOverlay
188  @Nonnull
189  public final ReactElement input( @Nonnull final String key, @DoNotAutobox final Object value )
190  {
191    inputs.set( key, value );
192    return this;
193  }
194
195  @JsOverlay
196  protected final void setInputs( @Nonnull final JsPropertyMap<Object> inputs )
197  {
198    this.inputs = inputs;
199  }
200}