All Downloads are FREE. Search and download functionalities are using the official Maven repository.

japgolly.scalajs.react.extra.router.package.scala Maven / Gradle / Ivy

The newest version!
package japgolly.scalajs.react.extra

import japgolly.scalajs.react.extra.router.RouterConfig.{Logger, Parsed}
import japgolly.scalajs.react.internal.CoreGeneral._
import japgolly.scalajs.react.util.DefaultEffects.Sync
import japgolly.scalajs.react.vdom.VdomElement
import japgolly.scalajs.react.{CtorType, ScalaComponent}

package object router {

  type Action     [Page, -Props] = ActionF[Sync, Page, Props]
  type Renderer   [Page, -Props] = RendererF[Sync, Page, Props]
  type RouterCtl  [A]            = RouterCtlF[Sync, A]
  type RouterLogic[Page, Props]  = RouterLogicF[Sync, Page, Props]

  type RouterWithPropsF [F[_], P, Props] = ScalaComponent              [Props, ResolutionWithProps[P, Props], OnUnmountF[F], CtorType.Props]
  type RouterWithPropsFU[F[_], P, Props] = ScalaComponent.Unmounted    [Props, ResolutionWithProps[P, Props], OnUnmountF[F]]
  type RouterWithPropsFM[F[_], P, Props] = ScalaComponent.MountedImpure[Props, ResolutionWithProps[P, Props], OnUnmountF[F]]

  type RouterWithProps [P, Props] = RouterWithPropsF [Sync, P, Props]
  type RouterWithPropsU[P, Props] = RouterWithPropsFU[Sync, P, Props]
  type RouterWithPropsM[P, Props] = RouterWithPropsFM[Sync, P, Props]

  // START: Compatibility with no-props Router API
  type RouterConfig[P] = RouterWithPropsConfig[P, Unit]
  type Resolution[P]   = ResolutionWithProps[P, Unit]

  type RouterF [F[_], P] = ScalaComponent              [Unit, Resolution[P], OnUnmountF[F], CtorType.Nullary]
  type RouterFU[F[_], P] = ScalaComponent.Unmounted    [Unit, Resolution[P], OnUnmountF[F]]
  type RouterFM[F[_], P] = ScalaComponent.MountedImpure[Unit, Resolution[P], OnUnmountF[F]]

  type Router [P] = RouterF [Sync, P]
  type RouterU[P] = RouterFU[Sync, P]
  type RouterM[P] = RouterFM[Sync, P]
  // END

  private[router] implicit class OptionFnExt[A, B](private val f: A => Option[B]) extends AnyVal {
    def ||(g: A => Option[B]): A => Option[B] = a => f(a) orElse g(a)
    def | (g: A => B)        : A => B         = a => f(a) getOrElse g(a)
  }

  private[router] implicit class CallbackToOptionFnExt[A, B](private val f: A => Sync[Option[B]]) extends AnyVal {
    def ||(g: A => Sync[Option[B]]): A => Sync[Option[B]] =
      a => Sync.flatMap(f(a)) {
        case s@ Some(_) => Sync.pure(s)
        case None       => g(a)
      }
  }

  private[router] implicit class OptionFn2Ext[A, B, C](private val f: (A, B) => Option[C]) extends AnyVal {
    def ||(g: (A, B) => Option[C]): (A, B) => Option[C] = (a, b) => f(a, b) orElse g(a, b)
    def | (g: (A, B) => C)        : (A, B) => C         = (a, b) => f(a, b) getOrElse g(a, b)
  }

  private[router] implicit class CallbackToOptionFn2Ext[A, B, C](private val f: (A, B) => Sync[Option[C]]) extends AnyVal {
    def ||(g: (A, B) => Sync[Option[C]]): (A, B) => Sync[Option[C]] =
      (a, b) => Sync.flatMap(f(a, b)) {
        case s@ Some(_) => Sync.pure(s)
        case None       => g(a, b)
      }
  }

  private[router] implicit class SaneEitherMethods[A, B](private val e: Either[A, B]) extends AnyVal {
    def map[C](f: B => C): Either[A, C] =
      e match {
        case Right(b) => Right(f(b))
        case l: Left[A, B] => l.asInstanceOf[Left[A, Nothing]]
      }

    def bimap[C, D](f: A => C, g: B => D): Either[C, D] =
      e match {
        case Right(b) => Right(g(b))
        case Left(a)  => Left(f(a))
      }
  }

  // =====================================================================================================================

  /** A complete set of routing rules that allow the router to handle every all routes without further input.
    *
    * @tparam Page The type of legal pages. Most commonly, a sealed trait that you've created, where all subclasses
    *              represent a page in your SPA.
    */
  type RoutingRules[Page, Props] = RoutingRulesF[Sync, Page, Props]

  object RoutingRules {

    type Exception = RoutingRulesF.Exception

    @inline def fromRule[Page, Props](rule          : RoutingRule[Page, Props],
                                      fallbackPath  : Page => Path,
                                      fallbackAction: (Path, Page) => Action[Page, Props],
                                      whenNotFound  : Path => Sync[Parsed[Page]],
                                     ): RoutingRules[Page, Props] =
      RoutingRulesF.fromRule(rule, fallbackPath, fallbackAction, whenNotFound)

    /** Create routing rules all at once, with compiler proof that all `Page`s will have a `Path` and `Action`
      * associated.
      *
      * The trade-off here is that care will need to be taken to ensure that path-parsing aligns with paths
      * generated for pages. It is recommended that you call [[RouterConfig.verify]] as a sanity-check.
      */
    @inline def bulk[Page, Props](toPage  : Path => Option[Parsed[Page]],
                                  fromPage: Page => (Path, Action[Page, Props]),
                                  notFound: Path => Parsed[Page],
                                 ): RoutingRules[Page, Props] =
      RoutingRulesF.bulk(toPage, fromPage, notFound)

    /** Create routing rules all at once, with compiler proof that all `Page`s will have a `Path` and `Action`
      * associated.
      *
      * The trade-off here is that care will need to be taken to ensure that path-parsing aligns with paths
      * generated for pages. It is recommended that you call [[RouterConfig.verify]] as a sanity-check.
      */
    @inline def bulkDynamic[Page, Props](toPage  : Path => Sync[Option[Parsed[Page]]],
                                         fromPage: Page => (Path, Sync[Action[Page, Props]]),
                                         notFound: Path => Parsed[Page],
                                        ): RoutingRules[Page, Props] =
      RoutingRulesF.bulkDynamic(toPage, fromPage, notFound)
  }

  // ===================================================================================================================

  type RouterWithPropsConfig[Page, Props] = RouterWithPropsConfigF[Sync, Page, Props]

  @inline def RouterWithPropsConfig[Page, Props](
      rules       : RoutingRules[Page, Props],
      renderFn    : (RouterCtl[Page], ResolutionWithProps[Page, Props]) => Props => VdomElement,
      postRenderFn: (Option[Page], Page, Props) => Sync[Unit],
      logger      : Logger,
     ): RouterWithPropsConfig[Page, Props] =
   RouterWithPropsConfigF(rules, renderFn, postRenderFn, logger)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy