feat: use rescala by default for reactivity

downscale operations, supporting multiple reactive backends is too complex
This commit is contained in:
DavidOnTop 2024-10-26 21:30:59 +02:00
parent 07e4e4006a
commit ec478533c3
No known key found for this signature in database
GPG key ID: 5D05538A45D5149F
11 changed files with 35 additions and 57 deletions

View file

@ -36,7 +36,10 @@ lazy val dom = crossProject(JSPlatform, JVMPlatform)
.in(file("./dom")) .in(file("./dom"))
.settings( .settings(
name := "sfs-dom", name := "sfs-dom",
version := "0.1.0-alpha" version := "0.1.0-alpha",
libraryDependencies ++= Seq(
"de.tu-darmstadt.stg" %% "rescala" % "0.35.1"
)
) )
.jvmSettings( .jvmSettings(
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
@ -57,26 +60,3 @@ lazy val sfs = crossProject(JSPlatform, JVMPlatform)
version := "0.1.0-alpha" version := "0.1.0-alpha"
) )
.dependsOn(dom) .dependsOn(dom)
lazy val sfsRouter = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.in(file("./router"))
.settings(
name := "sfs-router",
version := "0.1.0-alpha"
)
lazy val sfsReScala = crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.in(file("./reactive/rescala"))
.settings(
name := "sfs-rescala",
version := "0.1.0-alpha"
)
lazy val sfsZio = project
.in(file("./integrations/zio"))
.settings(
name := "sfs-zio",
version := "0.1.0-alpha"
)

View file

@ -1,5 +1,6 @@
package top.davidon.sfs.dom package top.davidon.sfs.dom
import rescala.default.Signal
import top.davidon.sfs.dom.codecs.* import top.davidon.sfs.dom.codecs.*
import top.davidon.sfs.dom.defs.attrs.{AriaAttrs, HtmlAttrs, SvgAttrs} import top.davidon.sfs.dom.defs.attrs.{AriaAttrs, HtmlAttrs, SvgAttrs}
import top.davidon.sfs.dom.defs.complex.{ComplexHtmlKeys, ComplexSvgKeys} import top.davidon.sfs.dom.defs.complex.{ComplexHtmlKeys, ComplexSvgKeys}
@ -7,6 +8,7 @@ import top.davidon.sfs.dom.defs.eventProps.GlobalEventProps
import top.davidon.sfs.dom.defs.props.HtmlProps import top.davidon.sfs.dom.defs.props.HtmlProps
import top.davidon.sfs.dom.defs.tags.{HtmlTags, SvgTags} import top.davidon.sfs.dom.defs.tags.{HtmlTags, SvgTags}
import top.davidon.sfs.dom.plain.PlainValue import top.davidon.sfs.dom.plain.PlainValue
import top.davidon.sfs.dom.reactive.ReactiveValue
trait ScalaFullStackDOM trait ScalaFullStackDOM
extends HtmlTags extends HtmlTags
@ -42,4 +44,11 @@ trait ScalaFullStackDOM
def apply(from: Iterable[String]): PlainValue[Iterable[String], String] = def apply(from: Iterable[String]): PlainValue[Iterable[String], String] =
PlainValue(from, IterableAsSpaceSeparatedStringCodec) PlainValue(from, IterableAsSpaceSeparatedStringCodec)
} }
given signalToVal: Conversion[Signal[String], ReactiveValue[String, String]]
with {
def apply(from: Signal[String]): ReactiveValue[String, String] = {
ReactiveValue(from, StringAsIsCodec)
}
}
} }

View file

@ -1,12 +1,9 @@
package top.davidon.sfs.dom.keys package top.davidon.sfs.dom.keys
import rescala.default.Signal
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.mods.Modifier import top.davidon.sfs.dom.mods.Modifier
import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue} import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue}
import top.davidon.sfs.dom.reactive.{ import top.davidon.sfs.dom.reactive.{ReactiveModifier, ReactiveValue}
Observable,
ReactiveModifier,
ReactiveValue
}
class AriaAttr[V]( class AriaAttr[V](
suffix: String, suffix: String,
@ -14,11 +11,11 @@ class AriaAttr[V](
) extends Key { ) extends Key {
override val name: String = "aria-" + suffix override val name: String = "aria-" + suffix
@inline def apply(value: V | Observable[V]): Modifier[V, String] = { @inline def apply(value: V | Signal[V]): Modifier[V, String] = {
this := value this := value
} }
def :=(value: V | Observable[V]): Modifier[V, String] = { def :=(value: V | Signal[V]): Modifier[V, String] = {
Modifier.fromVorObservableV(this, value, codec) Modifier.fromVorObservableV(this, value, codec)
} }
} }

View file

@ -5,7 +5,6 @@ import top.davidon.sfs.dom.Value
import top.davidon.sfs.dom.codecs.{EmptyCodec, StringAsIsCodec} import top.davidon.sfs.dom.codecs.{EmptyCodec, StringAsIsCodec}
import top.davidon.sfs.dom.mods.{EventMod, Modifier} import top.davidon.sfs.dom.mods.{EventMod, Modifier}
import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue} import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue}
import top.davidon.sfs.dom.reactive.Observable
class EventProp[E <: dom.Event](override val name: String) extends Key { class EventProp[E <: dom.Event](override val name: String) extends Key {

View file

@ -1,11 +1,11 @@
package top.davidon.sfs.dom.keys package top.davidon.sfs.dom.keys
import rescala.default.Signal
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.Value import top.davidon.sfs.dom.Value
import top.davidon.sfs.dom.mods.Modifier import top.davidon.sfs.dom.mods.Modifier
import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue} import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue}
import top.davidon.sfs.dom.reactive.{ import top.davidon.sfs.dom.reactive.{
Observable,
ReactiveModifier, ReactiveModifier,
ReactiveValue ReactiveValue
} }
@ -14,11 +14,11 @@ class HtmlAttr[V](
override val name: String, override val name: String,
val codec: Codec[V, String] val codec: Codec[V, String]
) extends Key { ) extends Key {
@inline def apply(value: V | Observable[V]): Modifier[V, String] = { @inline def apply(value: V | Signal[V]): Modifier[V, String] = {
this := value this := value
} }
def :=(value: V | Observable[V]): Modifier[V, String] = { def :=(value: V | Signal[V]): Modifier[V, String] = {
Modifier.fromVorObservableV(this, value, codec) Modifier.fromVorObservableV(this, value, codec)
} }
} }

View file

@ -1,19 +1,19 @@
package top.davidon.sfs.dom.keys package top.davidon.sfs.dom.keys
import rescala.default.Signal
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.Value import top.davidon.sfs.dom.Value
import top.davidon.sfs.dom.mods.Modifier import top.davidon.sfs.dom.mods.Modifier
import top.davidon.sfs.dom.plain.PlainValue import top.davidon.sfs.dom.plain.PlainValue
import top.davidon.sfs.dom.reactive.Observable
class HtmlProp[V, DomV]( class HtmlProp[V, DomV](
override val name: String, override val name: String,
val codec: Codec[V, DomV] val codec: Codec[V, DomV]
) extends Key { ) extends Key {
@inline def apply(value: V | Observable[V]): Modifier[V, DomV] = { @inline def apply(value: V | Signal[V]): Modifier[V, DomV] = {
this := value this := value
} }
def :=(value: V | Observable[V]): Modifier[V, DomV] = { def :=(value: V | Signal[V]): Modifier[V, DomV] = {
Modifier.fromVorObservableV(this, value, codec) Modifier.fromVorObservableV(this, value, codec)
} }
} }

View file

@ -1,7 +1,7 @@
package top.davidon.sfs.dom.keys package top.davidon.sfs.dom.keys
import rescala.default.Signal
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.mods.Modifier import top.davidon.sfs.dom.mods.Modifier
import top.davidon.sfs.dom.reactive.Observable
class SvgAttr[V]( class SvgAttr[V](
val localName: String, val localName: String,
@ -13,11 +13,11 @@ class SvgAttr[V](
val namespaceUri: Option[String] = namespacePrefix.map(SvgAttr.namespaceUri) val namespaceUri: Option[String] = namespacePrefix.map(SvgAttr.namespaceUri)
@inline def apply(value: V | Observable[V]): Modifier[V, String] = { @inline def apply(value: V | Signal[V]): Modifier[V, String] = {
this := value this := value
} }
def :=(value: V | Observable[V]): Modifier[V, String] = { def :=(value: V | Signal[V]): Modifier[V, String] = {
Modifier.fromVorObservableV(this, value, codec) Modifier.fromVorObservableV(this, value, codec)
} }
} }

View file

@ -1,11 +1,11 @@
package top.davidon.sfs.dom.mods package top.davidon.sfs.dom.mods
import rescala.default.Signal
import top.davidon.sfs.dom.Value import top.davidon.sfs.dom.Value
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.keys.Key import top.davidon.sfs.dom.keys.Key
import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue} import top.davidon.sfs.dom.plain.{PlainModifier, PlainValue}
import top.davidon.sfs.dom.reactive.{ import top.davidon.sfs.dom.reactive.{
Observable,
ReactiveModifier, ReactiveModifier,
ReactiveValue ReactiveValue
} }
@ -18,16 +18,16 @@ class Modifier[F, T] protected (
object Modifier { object Modifier {
def fromVorObservableV[F, T]( def fromVorObservableV[F, T](
key: Key, key: Key,
value: F | Observable[F], value: F | Signal[F],
codec: Codec[F, T] codec: Codec[F, T]
): Modifier[F, T] = { ): Modifier[F, T] = {
value match { value match {
case _: F => case _: F =>
PlainModifier(key, PlainValue(value.asInstanceOf[F], codec)) PlainModifier(key, PlainValue(value.asInstanceOf[F], codec))
case _: Observable[F] => case _: Signal[F] =>
ReactiveModifier( ReactiveModifier(
key, key,
ReactiveValue(value.asInstanceOf[Observable[F]], codec) ReactiveValue(value.asInstanceOf[Signal[F]], codec)
) )
} }
} }

View file

@ -1,8 +0,0 @@
package top.davidon.sfs.dom.reactive
abstract class Observable[T](var value: T) {
def subscribe(observer: T => Unit): Unit
def unsubscribe(observer: T => Unit): Unit
def update(value: T): Unit
def now(): T
}

View file

@ -1,11 +1,12 @@
package top.davidon.sfs.dom.reactive package top.davidon.sfs.dom.reactive
import rescala.default.Signal
import top.davidon.sfs.dom.Value import top.davidon.sfs.dom.Value
import top.davidon.sfs.dom.codecs.Codec import top.davidon.sfs.dom.codecs.Codec
import top.davidon.sfs.dom.plain.PlainValue import top.davidon.sfs.dom.plain.PlainValue
class ReactiveValue[F, T]( class ReactiveValue[F, T](
val reactiveValue: Observable[F], val reactiveValue: Signal[F],
codec: Codec[F, T] codec: Codec[F, T]
) extends Value[F, T](reactiveValue.now(), codec) { ) extends Value[F, T](reactiveValue.now(), codec) {
override def apply(): T = { override def apply(): T = {

View file

@ -55,7 +55,7 @@ class ClientSideRenderer(val root: dom.Element) extends Renderer[Unit] {
m.value m.value
.asInstanceOf[ReactiveValue[?, ?]] .asInstanceOf[ReactiveValue[?, ?]]
.reactiveValue .reactiveValue
.subscribe(value => { modifierFunc(el, m) }) .observe({ value => modifierFunc(el, m) })
} }
case _: EventProp[?] => case _: EventProp[?] =>
el.addEventListener( el.addEventListener(
@ -67,14 +67,14 @@ class ClientSideRenderer(val root: dom.Element) extends Renderer[Unit] {
m.value m.value
.asInstanceOf[ReactiveValue[?, ?]] .asInstanceOf[ReactiveValue[?, ?]]
.reactiveValue .reactiveValue
.subscribe(value => { modifierFunc(el, m) }) .observe({ value => modifierFunc(el, m) })
} }
} }
) )
valueFunc(el, element.children) valueFunc(el, element.children)
element.children.foreach { case r: ReactiveValue[?, String] => element.children.foreach { case r: ReactiveValue[?, String] =>
r.reactiveValue.subscribe(value => { valueFunc(el, element.children) }) r.reactiveValue.observe({ value => valueFunc(el, element.children) })
} }
parent.append(el) parent.append(el)