feat: reactivity 1.
This commit is contained in:
parent
8c9893846c
commit
131f90fcd8
19 changed files with 153 additions and 78 deletions
|
@ -6,6 +6,6 @@ import top.davidon.sfs.dom.tags.Tag
|
||||||
/** tag + modifiers + value */
|
/** tag + modifiers + value */
|
||||||
class Element[+Ref <: org.scalajs.dom.Element](
|
class Element[+Ref <: org.scalajs.dom.Element](
|
||||||
val tag: Tag[Ref],
|
val tag: Tag[Ref],
|
||||||
val mods: Iterable[Modifier[?, ?]],
|
val mods: Iterable[Modifier[?]],
|
||||||
val children: Seq[Element[?] | Value[?, String]]
|
val children: Seq[Element[?] | Value[String]]
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -5,5 +5,5 @@ import top.davidon.sfs.dom.mods.Modifier
|
||||||
|
|
||||||
trait ReactiveRenderer {
|
trait ReactiveRenderer {
|
||||||
def valueFunc[F](element: dom.Element, value: F): Unit
|
def valueFunc[F](element: dom.Element, value: F): Unit
|
||||||
def modifierFunc[F, T](modifier: Modifier[F, T], value: F): Unit
|
def modifierFunc[F, T](modifier: Modifier[T], value: F): Unit
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import top.davidon.sfs.dom.defs.complex.{ComplexHtmlKeys, ComplexSvgKeys}
|
||||||
import top.davidon.sfs.dom.defs.eventProps.GlobalEventProps
|
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
|
||||||
|
|
||||||
trait ScalaFullStackDOM
|
trait ScalaFullStackDOM
|
||||||
extends HtmlTags
|
extends HtmlTags
|
||||||
|
@ -20,23 +21,24 @@ trait ScalaFullStackDOM
|
||||||
|
|
||||||
object svg extends SvgTags with SvgAttrs with ComplexSvgKeys
|
object svg extends SvgTags with SvgAttrs with ComplexSvgKeys
|
||||||
|
|
||||||
given strToVal: Conversion[String, Value[String, String]] with {
|
given strToVal: Conversion[String, Value[String]] with {
|
||||||
def apply(from: String): Value[String, String] =
|
def apply(from: String): PlainValue[String, String] =
|
||||||
Value(from, StringAsIsCodec)
|
PlainValue(from, StringAsIsCodec)
|
||||||
}
|
}
|
||||||
given intToVal: Conversion[Int, Value[Int, String]] with {
|
given intToVal: Conversion[Int, Value[String]] with {
|
||||||
def apply(from: Int): Value[Int, String] = Value(from, IntAsStringCodec)
|
def apply(from: Int): PlainValue[Int, String] =
|
||||||
|
PlainValue(from, IntAsStringCodec)
|
||||||
}
|
}
|
||||||
given doubleToVal: Conversion[Double, Value[Double, String]] with {
|
given doubleToVal: Conversion[Double, Value[String]] with {
|
||||||
def apply(from: Double): Value[Double, String] =
|
def apply(from: Double): PlainValue[Double, String] =
|
||||||
Value(from, DoubleAsStringCodec)
|
PlainValue(from, DoubleAsStringCodec)
|
||||||
}
|
}
|
||||||
given longToVal: Conversion[Long, Value[Long, String]] with {
|
given longToVal: Conversion[Long, Value[String]] with {
|
||||||
def apply(from: Long): Value[Long, String] = Value(from, LongAsStringCodec)
|
def apply(from: Long): PlainValue[Long, String] =
|
||||||
|
PlainValue(from, LongAsStringCodec)
|
||||||
}
|
}
|
||||||
given iterToVal: Conversion[Iterable[String], Value[Iterable[String], String]]
|
given iterToVal: Conversion[Iterable[String], Value[String]] with {
|
||||||
with {
|
def apply(from: Iterable[String]): PlainValue[Iterable[String], String] =
|
||||||
def apply(from: Iterable[String]): Value[Iterable[String], String] =
|
PlainValue(from, IterableAsSpaceSeparatedStringCodec)
|
||||||
Value(from, IterableAsSpaceSeparatedStringCodec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package top.davidon.sfs.dom
|
|
||||||
|
|
||||||
trait TriggerableValue[T] {
|
|
||||||
def trigger[T](value: T): Unit
|
|
||||||
}
|
|
|
@ -1,36 +1,41 @@
|
||||||
package top.davidon.sfs.dom
|
package top.davidon.sfs.dom
|
||||||
|
|
||||||
import top.davidon.sfs.dom.codecs.{Codec, StringAsIsCodec}
|
import top.davidon.sfs.dom.codecs.Codec
|
||||||
|
import top.davidon.sfs.dom.reactive.Observable
|
||||||
|
|
||||||
class Value[F, T](
|
trait Value[T] {
|
||||||
val value: F,
|
def apply(): T
|
||||||
val codec: Codec[F, T]
|
|
||||||
) {
|
|
||||||
def apply(): T = {
|
|
||||||
codec.encode(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def toString: String = {
|
//class Value[F, T](
|
||||||
value match
|
// val value: F,
|
||||||
case v: Int => codecs.IntAsStringCodec.encode(v)
|
// val codec: Codec[F, T]
|
||||||
case v: Long => codecs.LongAsStringCodec.encode(v)
|
//) {
|
||||||
case v: Double => codecs.DoubleAsStringCodec.encode(v)
|
// def apply(): T = {
|
||||||
case v: Boolean => codecs.BooleanAsTrueFalseStringCodec.encode(v)
|
// codec.encode(value)
|
||||||
case v: Iterable[String] =>
|
// }
|
||||||
codecs.IterableAsSpaceSeparatedStringCodec.encode(v)
|
//
|
||||||
case _ =>
|
// override def toString: String = {
|
||||||
throw Exception("Couldn't find codec to convert a value to a string")
|
// value match
|
||||||
}
|
// case v: Int => codecs.IntAsStringCodec.encode(v)
|
||||||
}
|
// case v: Long => codecs.LongAsStringCodec.encode(v)
|
||||||
|
// case v: Double => codecs.DoubleAsStringCodec.encode(v)
|
||||||
object Value {
|
// case v: Boolean => codecs.BooleanAsTrueFalseStringCodec.encode(v)
|
||||||
def join(
|
// case v: Iterable[String] =>
|
||||||
iterator: Iterable[Value[?, String]]
|
// codecs.IterableAsSpaceSeparatedStringCodec.encode(v)
|
||||||
): Value[String, String] = {
|
// case _ =>
|
||||||
Value(
|
// throw Exception("Couldn't find codec to convert a value to a string")
|
||||||
iterator.map(v => v.toString).mkString(""),
|
// }
|
||||||
StringAsIsCodec
|
//}
|
||||||
)
|
//
|
||||||
}
|
//object Value {
|
||||||
|
// def join(
|
||||||
}
|
// iterator: Iterable[Value[?, String]]
|
||||||
|
// ): Value[String, String] = {
|
||||||
|
// Value(
|
||||||
|
// iterator.map(v => v.toString).mkString(""),
|
||||||
|
// StringAsIsCodec
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package top.davidon.sfs.dom.keys
|
||||||
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
||||||
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
|
||||||
|
|
||||||
class AriaAttr[V](
|
class AriaAttr[V](
|
||||||
suffix: String,
|
suffix: String,
|
||||||
|
@ -9,11 +10,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): Modifier[V, String] = {
|
@inline def apply(value: V): Modifier[String] = {
|
||||||
this := value
|
this := value
|
||||||
}
|
}
|
||||||
|
|
||||||
def :=(value: V): Modifier[V, String] = {
|
def :=(value: V): Modifier[String] = {
|
||||||
Modifier(this, Value(value, codec))
|
Modifier(this, PlainValue(value, codec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package top.davidon.sfs.dom.keys
|
package top.davidon.sfs.dom.keys
|
||||||
|
|
||||||
import org.scalajs.dom
|
import org.scalajs.dom
|
||||||
|
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.{TriggerableValue, Value}
|
import top.davidon.sfs.dom.plain.PlainValue
|
||||||
|
import top.davidon.sfs.dom.reactive.TriggerableValue
|
||||||
|
|
||||||
class EventProp[E <: dom.Event](override val name: String) extends Key {
|
class EventProp[E <: dom.Event](override val name: String) extends Key {
|
||||||
|
|
||||||
|
@ -11,23 +13,23 @@ class EventProp[E <: dom.Event](override val name: String) extends Key {
|
||||||
* @param value
|
* @param value
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def :=(value: E => Unit): Modifier[EventMod[E], Unit] = {
|
def :=(value: E => Unit): Modifier[Unit] = {
|
||||||
Modifier(this, Value(EventMod(this, value), EmptyCodec()))
|
Modifier(this, PlainValue(EventMod(this, value), EmptyCodec()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Don't use with StringRenderer and ssr off/false
|
/** Don't use with StringRenderer and ssr off/false
|
||||||
* @param value
|
* @param value
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def :=(value: TriggerableValue[E]): Modifier[EventMod[E], Unit] = {
|
def :=(value: TriggerableValue[E]): Modifier[Unit] = {
|
||||||
Modifier(this, Value(EventMod(this, value.trigger), EmptyCodec()))
|
Modifier(this, PlainValue(EventMod(this, value.trigger), EmptyCodec()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Only use with StringRenderer and ssr off/false
|
/** Only use with StringRenderer and ssr off/false
|
||||||
* @param value
|
* @param value
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def :=(value: String): Modifier[String, String] = {
|
def :=(value: String): Modifier[String] = {
|
||||||
Modifier(this, Value(value, StringAsIsCodec))
|
Modifier(this, PlainValue(value, StringAsIsCodec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,17 @@ package top.davidon.sfs.dom.keys
|
||||||
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
||||||
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
|
||||||
|
|
||||||
class HtmlAttr[V](
|
class HtmlAttr[V](
|
||||||
override val name: String,
|
override val name: String,
|
||||||
val codec: StringCodec[V]
|
val codec: StringCodec[V]
|
||||||
) extends Key {
|
) extends Key {
|
||||||
@inline def apply(value: V): Modifier[V, String] = {
|
@inline def apply(value: V): Modifier[String] = {
|
||||||
this := value
|
this := value
|
||||||
}
|
}
|
||||||
|
|
||||||
def :=(value: V): Modifier[V, String] = {
|
def :=(value: V): Modifier[String] = {
|
||||||
Modifier(this, Value(value, codec))
|
Modifier(this, PlainValue(value, codec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,17 @@ package top.davidon.sfs.dom.keys
|
||||||
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
|
||||||
|
|
||||||
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): Modifier[V, DomV] = {
|
@inline def apply(value: V): Modifier[DomV] = {
|
||||||
this := value
|
this := value
|
||||||
}
|
}
|
||||||
|
|
||||||
def :=(value: V): Modifier[V, DomV] = {
|
def :=(value: V): Modifier[DomV] = {
|
||||||
Modifier(this, Value(value, codec))
|
Modifier(this, PlainValue(value, codec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package top.davidon.sfs.dom.keys
|
||||||
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
import top.davidon.sfs.dom.codecs.{Codec, StringCodec}
|
||||||
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
|
||||||
|
|
||||||
class SvgAttr[V](
|
class SvgAttr[V](
|
||||||
val localName: String,
|
val localName: String,
|
||||||
|
@ -13,12 +14,12 @@ class SvgAttr[V](
|
||||||
|
|
||||||
val namespaceUri: Option[String] = namespacePrefix.map(SvgAttr.namespaceUri)
|
val namespaceUri: Option[String] = namespacePrefix.map(SvgAttr.namespaceUri)
|
||||||
|
|
||||||
@inline def apply(value: V): Modifier[V, String] = {
|
@inline def apply(value: V): Modifier[String] = {
|
||||||
this := value
|
this := value
|
||||||
}
|
}
|
||||||
|
|
||||||
def :=(value: V): Modifier[V, String] = {
|
def :=(value: V): Modifier[String] = {
|
||||||
Modifier(this, Value(value, codec))
|
Modifier(this, PlainValue(value, codec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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
|
||||||
|
|
||||||
class Modifier[F, T](
|
class Modifier[T](
|
||||||
val key: Key,
|
val key: Key,
|
||||||
val value: Value[F, T]
|
val value: Value[T]
|
||||||
) {}
|
) {}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package top.davidon.sfs.dom.plain
|
||||||
|
|
||||||
|
class PlainModifier {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package top.davidon.sfs.dom.plain
|
||||||
|
|
||||||
|
import top.davidon.sfs.dom.{Value, codecs}
|
||||||
|
import top.davidon.sfs.dom.codecs.Codec
|
||||||
|
|
||||||
|
class PlainValue[F, T](
|
||||||
|
val value: F,
|
||||||
|
val codec: Codec[F, T]
|
||||||
|
) extends Value[T] {
|
||||||
|
def apply(): T = {
|
||||||
|
codec.encode(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString: String = {
|
||||||
|
value match
|
||||||
|
case v: Int => codecs.IntAsStringCodec.encode(v)
|
||||||
|
case v: Long => codecs.LongAsStringCodec.encode(v)
|
||||||
|
case v: Double => codecs.DoubleAsStringCodec.encode(v)
|
||||||
|
case v: Boolean => codecs.BooleanAsTrueFalseStringCodec.encode(v)
|
||||||
|
case v: Iterable[String] =>
|
||||||
|
codecs.IterableAsSpaceSeparatedStringCodec.encode(v)
|
||||||
|
case _ =>
|
||||||
|
throw Exception("Couldn't find codec to convert a value to a string")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package top.davidon.sfs.dom.reactive
|
||||||
|
|
||||||
|
trait Observable[T] {
|
||||||
|
def subscribe(observer: T => Unit): Unit
|
||||||
|
def unsubscribe(observer: T => Unit): Unit
|
||||||
|
def notify(value: T): Unit
|
||||||
|
def update(value: T): Unit
|
||||||
|
def now(): T
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package top.davidon.sfs.dom.reactive
|
||||||
|
|
||||||
|
class ReactiveModifier {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package top.davidon.sfs.dom.reactive
|
||||||
|
|
||||||
|
import top.davidon.sfs.dom.Value
|
||||||
|
import top.davidon.sfs.dom.codecs.Codec
|
||||||
|
import top.davidon.sfs.dom.plain.PlainValue
|
||||||
|
|
||||||
|
class ReactiveValue[F, T](
|
||||||
|
val value: Observable[F],
|
||||||
|
val codec: Codec[F, T]
|
||||||
|
) extends Value[T] {
|
||||||
|
def apply(): T = {
|
||||||
|
codec.encode(value.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
def toValueNow: PlainValue[F, T] = {
|
||||||
|
PlainValue(value.now(), codec)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package top.davidon.sfs.dom.reactive
|
||||||
|
|
||||||
|
trait TriggerableValue[T] {
|
||||||
|
def trigger(value: T): Unit
|
||||||
|
}
|
|
@ -9,9 +9,9 @@ trait Tag[+Ref <: dom.Element] {
|
||||||
val void: Boolean
|
val void: Boolean
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
modifiers: Modifier[?, ?]*
|
modifiers: Modifier[?]*
|
||||||
)(
|
)(
|
||||||
values: Element[?] | Value[?, String]*
|
values: Element[?] | Value[String]*
|
||||||
): Element[Ref] = {
|
): Element[Ref] = {
|
||||||
Element[Ref](this, modifiers, values.toSeq)
|
Element[Ref](this, modifiers, values.toSeq)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,10 @@ class StringRenderer(val ssr: Boolean) extends Renderer[String] {
|
||||||
e.mods.map(m => s" ${m.key.name}=\"${m.value.toString}\"").mkString("")
|
e.mods.map(m => s" ${m.key.name}=\"${m.value.toString}\"").mkString("")
|
||||||
val bodyStr = e.children
|
val bodyStr = e.children
|
||||||
.map {
|
.map {
|
||||||
|
case c: Value[String] =>
|
||||||
|
c()
|
||||||
case e: Element[?] =>
|
case e: Element[?] =>
|
||||||
renderElement(e)
|
renderElement(e)
|
||||||
case c: Value[?, String] =>
|
|
||||||
c()
|
|
||||||
}
|
}
|
||||||
.mkString(" ")
|
.mkString(" ")
|
||||||
s"<${e.tag.name}$modsStr>$bodyStr${
|
s"<${e.tag.name}$modsStr>$bodyStr${
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue