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