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

lucuma.ui.table.TableHooks.scala Maven / Gradle / Ivy

// Copyright (c) 2016-2022 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

import cats.effect.IO
import cats.effect.Sync
import cats.syntax.all.*
import crystal.react.implicits.*
import japgolly.scalajs.react.Reusability
import japgolly.scalajs.react.*
import japgolly.scalajs.react.hooks.CustomHook
import japgolly.scalajs.react.util.DefaultEffects.{Async => DefaultA}
import lucuma.core.util.NewType
import lucuma.react.table.*
import reactST.{tanstackTableCore => raw}

import scalajs.js.JSConverters.*
import scalajs.js

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

private object TableHooks:
  private given Reusability[ColumnId]                  = Reusability.by(_.value)
  private given Reusability[Visibility]                = Reusability.by(_.value)
  private given Reusability[Map[ColumnId, Visibility]] = Reusability.map
  private given Reusability[ColumnVisibility]          = Reusability.by(_.value)
  private given Reusability[SortDirection]             = Reusability.by(_.toDescending)
  private given Reusability[ColumnSort]                = Reusability.derive
  private given Reusability[Sorting]                   = Reusability.by(_.value)
  private given Reusability[TableState]                =
    Reusability.by(state => (state.columnVisibility, state.sorting))

  private object PrefsLoaded extends NewType[Boolean]
  private type PrefsLoaded = PrefsLoaded.Type

  private object CanSave extends NewType[Boolean]
  private type CanSave = CanSave.Type

  private def hook[T] =
    CustomHook[TableOptionsWithStateStore[DefaultA, T]]
      .useReactTableBy(_.tableOptions)
      .useState(PrefsLoaded(false))
      .useRef(CanSave(false))
      .useEffectOnMountBy((props, table, prefsLoadad, canSave) =>
        (props.stateStore.load() >>=
          (mod => table.modState(mod).to[DefaultA]))
          .guarantee( // This also forces a rerender, which react-table isn't doing by just changing the state.
            prefsLoadad.setStateAsync(PrefsLoaded(true))
          )
      )
      .useEffectWithDepsBy((_, table, _, _) => table.getState())((props, _, prefsLoadad, canSave) =>
        state =>
          // Don't save prefs while we are still attempting to load them or if we just loaded them.
          props.stateStore
            .save(state)
            .whenA(prefsLoadad.value.value && canSave.value.value)
            >> canSave
              .setAsync(CanSave(true))
              .whenA(prefsLoadad.value.value && !canSave.value.value)
      )
      .buildReturning((_, table, _, _) => table)

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

    final def useReactTableWithStateStoreBy[T](
      options: Ctx => TableOptionsWithStateStore[DefaultA, T]
    )(using
      step:    Step
    ): step.Next[Table[T]] =
      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](
      tableDefWithOptions: CtxFn[TableOptionsWithStateStore[DefaultA, T]]
    )(implicit
      step:                Step
    ): step.Next[Table[T]] =
      super.useReactTableWithStateStoreBy(step.squash(tableDefWithOptions)(_))

trait TableHooks:
  import TableHooks._

  given [Ctx, Step <: HooksApi.AbstractStep]
    : Conversion[HooksApi.Primary[Ctx, Step], Primary[Ctx, Step]] =
    api => new Primary(api)

  given [Ctx, CtxFn[_], Step <: HooksApi.SubsequentStep[Ctx, CtxFn]]
    : Conversion[HooksApi.Secondary[Ctx, CtxFn, Step], Secondary[Ctx, CtxFn, Step]] =
    api => new Secondary(api)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy