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}