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

lucuma.ui.table.hooks.UseReactTableWithStateStore.scala Maven / Gradle / Ivy

// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.ui.table.hooks

import cats.effect.IO
import cats.syntax.all.*
import crystal.react.*
import japgolly.scalajs.react.*
import japgolly.scalajs.react.hooks.CustomHook
import japgolly.scalajs.react.util.DefaultEffects.Async as DefaultA
import lucuma.core.util.NewType
import lucuma.react.table.*
import lucuma.ui.reusability.given

case class TableOptionsWithStateStore[F[_], T, M](
  tableOptions: TableOptions[T, M],
  stateStore:   TableStateStore[F]
)

private object UseReactTableWithStateStore:
  private object PrefsLoaded extends NewType[Boolean]

  private object CanSave extends NewType[Boolean]

  def useReactTableWithStateStore[T, M](
    options: TableOptionsWithStateStore[DefaultA, T, M]
  ): HookResult[Table[T, M]] =
    for
      table       <- useReactTable(options.tableOptions)
      prefsLoadad <- useState(PrefsLoaded(false))
      canSave     <- useRef(CanSave(false))
      _           <- useEffectOnMount:
                       (options.stateStore.load() >>=
                         (mod =>
                           val newState: TableState = mod(table.getState())

                           // We apply partial state changes in case there are partial state overrides.
                           table.setColumnVisibility(newState.columnVisibility).to[DefaultA] >>
                             // table.setColumnOrder(newState.columnOrder).to[DefaultA] >> // Not implemented yet in lucuma-react
                             table.setColumnPinning(newState.columnPinning).to[DefaultA] >>
                             table.setRowPinning(newState.rowPinning).to[DefaultA] >>
                             table.setSorting(newState.sorting).to[DefaultA] >>
                             table.setExpanded(newState.expanded).to[DefaultA] >>
                             table.setColumnSizing(newState.columnSizing).to[DefaultA] >>
                             table.setColumnSizingInfo(newState.columnSizingInfo).to[DefaultA] >>
                             table.setRowSelection(newState.rowSelection).to[DefaultA] >>
                             prefsLoadad.setStateAsync(PrefsLoaded(true))
                         ))
                         .guaranteeCase(outcome => canSave.setAsync(CanSave(true)).unlessA(outcome.isSuccess))
      _           <- useEffectWithDeps(table.getState()): state =>
                       // Don't save prefs while we are still attempting to load them or if we just loaded them.
                       options.stateStore
                         .save(state)
                         .whenA(canSave.value.value)
                         >>
                           canSave
                             .setAsync(CanSave(true))
                             .whenA(prefsLoadad.value.value && !canSave.value.value)
    yield table

  private def hook[T, M]: CustomHook[TableOptionsWithStateStore[DefaultA, T, M], Table[T, M]] =
    CustomHook.fromHookResult(useReactTableWithStateStore(_))

  object HooksApiExt:
    sealed class Primary[Ctx, Step <: HooksApi.AbstractStep](api: HooksApi.Primary[Ctx, Step]):
      final def useReactTableWithStateStore[T, M](
        options: TableOptionsWithStateStore[DefaultA, T, M]
      )(using
        step:    Step
      ): step.Next[Table[T, M]] =
        useReactTableWithStateStoreBy(_ => options)

      final def useReactTableWithStateStoreBy[T, M](
        options: Ctx => TableOptionsWithStateStore[DefaultA, T, M]
      )(using
        step:    Step
      ): step.Next[Table[T, M]] =
        api.customBy(ctx => hook(options(ctx)))

    final class Secondary[Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]](
      api: HooksApi.Secondary[Ctx, CtxFn, Step]
    ) extends Primary[Ctx, Step](api):
      def useReactTableWithStateStoreBy[T, M](
        tableDefWithOptions: CtxFn[TableOptionsWithStateStore[DefaultA, T, M]]
      )(using
        step:                Step
      ): step.Next[Table[T, M]] =
        super.useReactTableWithStateStoreBy(step.squash(tableDefWithOptions)(_))

  trait HooksApiExt:
    import HooksApiExt.*

    implicit def hooksExtReactTableWithStateStore1[Ctx, Step <: HooksApi.AbstractStep](
      api: HooksApi.Primary[Ctx, Step]
    ): Primary[Ctx, Step] =
      new Primary(api)

    implicit def hooksExtReactTableWithStateStore2[
      Ctx,
      CtxFn[_],
      Step <: HooksApi.SubsequentStep[Ctx, CtxFn]
    ](
      api: HooksApi.Secondary[Ctx, CtxFn, Step]
    ): Secondary[Ctx, CtxFn, Step] =
      new Secondary(api)

  object syntax extends HooksApiExt




© 2015 - 2025 Weber Informatics LLC | Privacy Policy