001package react4j; 002 003import java.util.Objects; 004import javax.annotation.Nonnull; 005import javax.annotation.Nullable; 006import jsinterop.annotations.JsFunction; 007import jsinterop.annotations.JsOverlay; 008import jsinterop.annotations.JsPackage; 009import jsinterop.annotations.JsType; 010import jsinterop.base.Js; 011import jsinterop.base.JsPropertyMap; 012 013/** 014 * Context is designed to share data that can be considered "global" for a tree of views. 015 * In a typical React application, data is passed top-down (parent to child) via inputs, but this can 016 * be cumbersome for certain types of inputs (e.g. locale preference, UI theme) that are required by 017 * many views within an application. Context provides a way to share values like this between 018 * views without having to explicitly pass a input through every level of the tree. 019 */ 020@JsType( isNative = true, namespace = JsPackage.GLOBAL, name = "Object" ) 021public final class Context<T> 022{ 023 /** 024 * Create a builder for the Provider component. 025 * 026 * @return a builder for the Provider component. 027 */ 028 @JsOverlay 029 @Nonnull 030 public ProviderBuilder<T> provider() 031 { 032 return new ProviderBuilder<>( this ); 033 } 034 035 /** 036 * Create a provider component that provides specified value to specified children. 037 * 038 * @param value the value to provider to children. 039 * @param children the child elements. 040 * @return the element. 041 */ 042 @JsOverlay 043 @Nonnull 044 public ReactNode provide( @Nullable final T value, final ReactNode... children ) 045 { 046 return provider().value( value ).children( children ); 047 } 048 049 /** 050 * Create a builder for the Consumer component. 051 * 052 * @return a builder for the Consumer component. 053 */ 054 @JsOverlay 055 @Nonnull 056 public ConsumerBuilder<T> consumer() 057 { 058 return new ConsumerBuilder<>( this ); 059 } 060 061 /** 062 * A Builder for the Provider component. 063 * A provider component is a react component that allows Consumers to subscribe to context changes. 064 */ 065 public static final class ProviderBuilder<ST> 066 { 067 @Nonnull 068 private final ReactElement _element; 069 070 private ProviderBuilder( @Nonnull final Context<ST> context ) 071 { 072 _element = ReactElement.createContextElement( Js.<JsPropertyMap<Object>>cast( context ).get( "Provider" ) ); 073 } 074 075 /** 076 * Specify the key for the component if required. 077 * 078 * @param key the key for the component. 079 * @return the builder. 080 */ 081 @Nonnull 082 public ProviderBuilder<ST> key( @Nonnull final String key ) 083 { 084 _element.setKey( Objects.requireNonNull( key ) ); 085 return this; 086 } 087 088 @Nonnull 089 public ProviderBuilder<ST> value( @Nullable final ST value ) 090 { 091 _element.input( "value", value ); 092 return this; 093 } 094 095 @Nonnull 096 public ReactNode children( final ReactNode... children ) 097 { 098 _element.input( "children", children ); 099 return build(); 100 } 101 102 @Nonnull 103 public ReactNode build() 104 { 105 return _element; 106 } 107 } 108 109 /** 110 * Interface used to type the render function input. 111 */ 112 @JsFunction 113 @FunctionalInterface 114 public interface ConsumerRenderFunction<T> 115 { 116 /** 117 * Render the specified tree for context value. 118 * 119 * @param value the context value. 120 * @return the rendered react node tree. 121 */ 122 ReactNode render( T value ); 123 } 124 125 /** 126 * A Builder for the Consumer component. 127 * A Consumer component is a react component that subscribes to context changes. 128 */ 129 public static final class ConsumerBuilder<ST> 130 { 131 @Nonnull 132 private final ReactElement _element; 133 134 private ConsumerBuilder( @Nonnull final Context<ST> context ) 135 { 136 _element = ReactElement.createContextElement( Js.<JsPropertyMap<Object>>cast( context ).get( "Consumer" ) ); 137 } 138 139 /** 140 * Specify the key for component if required. 141 * 142 * @param key the key for the component. 143 * @return the builder. 144 */ 145 @Nonnull 146 public ConsumerBuilder<ST> key( @Nonnull final String key ) 147 { 148 _element.setKey( Objects.requireNonNull( key ) ); 149 return this; 150 } 151 152 /** 153 * Specify the child render function. 154 * 155 * @param render the child render function. 156 * @return the creates react node. 157 */ 158 @Nonnull 159 public ReactNode render( @Nonnull final ConsumerRenderFunction<ST> render ) 160 { 161 _element.input( "children", render ); 162 return _element; 163 } 164 } 165}