001package react4j.dom;
002
003import akasha.Element;
004import arez.Arez;
005import arez.ArezContext;
006import javax.annotation.Nonnull;
007import javax.annotation.Nullable;
008import jsinterop.annotations.JsFunction;
009import jsinterop.annotations.JsMethod;
010import jsinterop.annotations.JsOverlay;
011import jsinterop.annotations.JsPackage;
012import jsinterop.annotations.JsType;
013import react4j.React;
014import react4j.ReactElement;
015import react4j.ReactNode;
016
017/**
018 * Core interface into React DOM library.
019 */
020@JsType( isNative = true, namespace = JsPackage.GLOBAL )
021public class ReactDOM
022{
023  private ReactDOM()
024  {
025  }
026
027  /**
028   * Interface for performing an action inside batch.
029   */
030  @FunctionalInterface
031  @JsFunction
032  public interface BatchedUpdatesFn
033  {
034    /**
035     * Perform action while batching react changes.
036     *
037     * @throws Throwable if an error occurred.
038     */
039    void call()
040      throws Throwable;
041  }
042
043  /**
044   * Interface for performing an action on render complete.
045   */
046  @FunctionalInterface
047  @JsFunction
048  public interface RenderCallbackFn
049  {
050    /**
051     * Perform action on render complete.
052     */
053    void call();
054  }
055
056  @JsOverlay
057  @Nonnull
058  public static ReactRoot createRoot( @Nonnull final Element container )
059  {
060    return unstable_createRoot( container );
061  }
062
063  @Nonnull
064  private static native ReactRoot unstable_createRoot( @Nonnull Element container );
065
066  /**
067   * Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy
068   * of the parent component.
069   *
070   * <p>Even though a portal can be anywhere in the DOM tree, it behaves like a normal React child in every
071   * other way. Features like context work exactly the same regardless of whether the child is a portal, as
072   * the portal still exists in the React tree regardless of position in the DOM tree.</p>
073   *
074   * <p>This includes event bubbling. An event fired from inside a portal will propagate to ancestors in
075   * the containing React tree, even if those elements are not ancestors in the DOM tree.</p>
076   *
077   * @param children  the react node to render.
078   * @param container the DOM element to render into.
079   * @return the new portal.
080   */
081  public static native ReactPortal createPortal( @Nonnull ReactNode children, @Nonnull Element container );
082
083  /**
084   * Render a React element into the DOM in the supplied container.
085   *
086   * <p>If the React element was previously rendered into container, this will perform an update on it and only
087   * mutate the DOM as necessary to reflect the latest React element.</p>
088   *
089   * <p>If the optional callback is provided, it will be executed after the component is rendered or updated.</p>
090   *
091   * @param node      the react node to render.
092   * @param container the DOM element to render into.
093   * @param onUpdate  the callback invoked when rendering is complete.
094   * @return a reference to the created React Component, DOM Node, Portal or null (stateless components).
095   */
096  @Nullable
097  @JsOverlay
098  public static Object render( @Nonnull final ReactNode node,
099                               @Nonnull final Element container,
100                               @Nullable final RenderCallbackFn onUpdate )
101  {
102    return _render( React.shouldCheckInvariants() ? ReactElement.createStrictMode( node ) : node, container, onUpdate );
103  }
104
105  @Nullable
106  @JsMethod( name = "render" )
107  private static native Object _render( @Nonnull ReactNode node,
108                                        @Nonnull Element container,
109                                        @Nullable RenderCallbackFn onUpdate );
110
111  /**
112   * Render a React element into the DOM in the supplied container.
113   *
114   * <p>If the React element was previously rendered into container, this will perform an update on it and only
115   * mutate the DOM as necessary to reflect the latest React element.</p>
116   *
117   * @param node      the react node to render.
118   * @param container the DOM element to render into.
119   * @return a reference to the created React Component, DOM Node, Portal or null (stateless components).
120   * @see #render(ReactNode, Element, RenderCallbackFn)
121   */
122  @Nullable
123  @JsOverlay
124  public static Object render( @Nonnull ReactNode node, @Nonnull Element container )
125  {
126    return render( node, container, null );
127  }
128
129  /**
130   * Remove a mounted React component from the DOM and clean up its event handlers and state. If
131   * no component was mounted in the container, calling this function does nothing.
132   *
133   * @param container the DOM container containing the react component to unmount
134   * @return true if a component was unmounted and false if there was no component to unmount.
135   */
136  public static native boolean unmountComponentAtNode( @Nonnull Element container );
137
138  /**
139   * Batch all state updates within the action.
140   * This is currently an unstable API within the React 16, mostly because it is only useful when called
141   * outside an event handler (i.e. from network code) and because it is likely to be enabled by default
142   * in a later version of React.
143   *
144   * @param action the action where all state updates are batched.
145   */
146  @JsOverlay
147  public static void batchedUpdates( @Nonnull final BatchedUpdatesFn action )
148  {
149    unstable_batchedUpdates( action );
150  }
151
152  /**
153   * Register an task interceptor on the current Arez context that ensures any view updates are batched.
154   */
155  @JsOverlay
156  public static void registerBatchedArezTaskInterceptor()
157  {
158    registerBatchedArezTaskInterceptor( Arez.context() );
159  }
160
161  /**
162   * Register an task interceptor that ensures any view updates are batched.
163   *
164   * @param context the context to add interceptor to.
165   */
166  @JsOverlay
167  public static void registerBatchedArezTaskInterceptor( @Nonnull final ArezContext context )
168  {
169    context.setTaskInterceptor( new BatchingTaskInterceptor() );
170  }
171
172  /**
173   * The native method with the unstable prefix.
174   *
175   * @param action the action where all state updates are batched.
176   */
177  @JsMethod
178  private static native void unstable_batchedUpdates( @Nonnull BatchedUpdatesFn action );
179}