Skip to content

Commit

Permalink
Rethinking Html rendering (SSR)
Browse files Browse the repository at this point in the history
  • Loading branch information
davesmith00000 committed Dec 16, 2023
1 parent a18ede6 commit 81f6655
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 184 deletions.
7 changes: 7 additions & 0 deletions sandbox/src/main/scala/example/Sandbox.scala
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ object Sandbox extends TyrianApp[Msg, Model]:
)
case Msg.FileRead(file) =>
(model.copy(image = Some(file)), Cmd.None)

def view(model: Model): Html[Msg] =
val navItems =
Page.values.toList.map { pg =>
Expand Down Expand Up @@ -391,6 +392,12 @@ object Sandbox extends TyrianApp[Msg, Model]:
if true then p("Showing") else Empty,
if false then p("Showing") else Empty
),
div(
p("From rendered HTML:"),
raw("div")(
"<p>Hello, Bob!</p>" + p("Hello, world!")
)
),
div(
input(id := "fruitName", onInput(s => Msg.UpdateFruitInput(s))),
button(onClick(Msg.AddFruit))(
Expand Down
41 changes: 0 additions & 41 deletions tyrian/js/src/main/scala/tyrian/Tyrian.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package tyrian
import cats.effect.kernel.Async
import org.scalajs.dom.Element
import tyrian.runtime.TyrianRuntime
import tyrian.runtime.TyrianSSR

object Tyrian:

Expand Down Expand Up @@ -43,43 +42,3 @@ object Tyrian:
subscriptions: Model => Sub[F, Msg]
): F[Nothing] =
TyrianRuntime[F, Model, Msg](router, node, init._1, init._2, update, view, subscriptions)

/** Takes a normal Tyrian Model and view function and renders the html to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, model: Model, view: Model => Html[Msg]): String =
TyrianSSR.render(includeDocType, model, view)

/** Takes a normal Tyrian Model and view function and renders the html to a string.
*/
def render[Model, Msg](model: Model, view: Model => Html[Msg]): String =
render(false, model, view)

/** Takes a Tyrian HTML view, and renders it into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, html: Html[Msg]): String =
TyrianSSR.render(includeDocType, html)

/** Takes a Tyrian HTML view, and renders it into to a string.
*/
def render[Model, Msg](html: Html[Msg]): String =
render(false, html)

/** Takes a list of Tyrian elements, and renders the fragment into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, elems: List[Elem[Msg]]): String =
TyrianSSR.render(includeDocType, elems)

/** Takes a list of Tyrian elements, and renders the fragment into to a string.
*/
def render[Model, Msg](elems: List[Elem[Msg]]): String =
render(false, elems)

/** Takes repeatingTyrian elements, and renders the fragment into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, elems: Elem[Msg]*): String =
render(includeDocType, elems.toList)

/** Takes repeating Tyrian elements, and renders the fragment into to a string.
*/
def render[Model, Msg](elems: Elem[Msg]*): String =
render(elems.toList)
42 changes: 0 additions & 42 deletions tyrian/jvm/src/main/scala/tyrian/Tyrian.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package tyrian

import tyrian.runtime.TyrianSSR

object Tyrian:

final case class FakeEvent(name: String, value: Any, target: Any)
Expand All @@ -10,43 +8,3 @@ object Tyrian:
type KeyboardEvent = FakeEvent
type MouseEvent = FakeEvent
type HTMLInputElement = FakeHTMLInputElement

/** Takes a normal Tyrian Model and view function and renders the html to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, model: Model, view: Model => Html[Msg]): String =
TyrianSSR.render(includeDocType, model, view)

/** Takes a normal Tyrian Model and view function and renders the html to a string.
*/
def render[Model, Msg](model: Model, view: Model => Html[Msg]): String =
render(false, model, view)

/** Takes a Tyrian HTML view, and renders it into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, html: Html[Msg]): String =
TyrianSSR.render(includeDocType, html)

/** Takes a Tyrian HTML view, and renders it into to a string.
*/
def render[Model, Msg](html: Html[Msg]): String =
render(false, html)

/** Takes a list of Tyrian elements, and renders the fragment into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, elems: List[Elem[Msg]]): String =
TyrianSSR.render(includeDocType, elems)

/** Takes a list of Tyrian elements, and renders the fragment into to a string.
*/
def render[Model, Msg](elems: List[Elem[Msg]]): String =
render(false, elems)

/** Takes repeatingTyrian elements, and renders the fragment into to a string prefixed with the doctype.
*/
def render[Model, Msg](includeDocType: Boolean, elems: Elem[Msg]*): String =
render(includeDocType, elems.toList)

/** Takes repeating Tyrian elements, and renders the fragment into to a string.
*/
def render[Model, Msg](elems: Elem[Msg]*): String =
render(elems.toList)
9 changes: 0 additions & 9 deletions tyrian/jvm/src/test/scala/tyrian/PlaceholderTests.scala

This file was deleted.

1 change: 1 addition & 0 deletions tyrian/shared/src/main/scala/tyrian/Attr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import scala.util.Try
/** HTML attribute */
sealed trait Attr[+M]:
def map[N](f: M => N): Attr[N]
override def toString(): String = this.render

/** An attribute of an HTML tag that does not exist, used as a "do not render" placeholder
*/
Expand Down
59 changes: 59 additions & 0 deletions tyrian/shared/src/main/scala/tyrian/HTMLRendering.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tyrian

import tyrian.*

val DOCTYPE: String = "<!DOCTYPE HTML>"

private val spacer = (str: String) => if str.isEmpty then str else " " + str

extension [Msg](elem: Elem[Msg])
def render: String =
elem match
case _: Empty.type => ""
case t: Text => t.value
case h: Html[_] => h.render

extension [Msg](html: Html[Msg])
def render: String =
html match
case tag: RawTag[_] =>
val attributes =
spacer(tag.attributes.map(_.render).filterNot(_.isEmpty).mkString(" "))
s"""<${tag.name}$attributes>${tag.innerHTML}</${tag.name}>"""
case tag: Tag[_] =>
val attributes =
spacer(tag.attributes.map(_.render).filterNot(_.isEmpty).mkString(" "))

val children = tag.children.map {
case _: Empty.type => ""
case t: Text => t.value
case h: Html[_] => h.render
}.mkString

s"""<${tag.name}$attributes>$children</${tag.name}>"""

extension (a: Attr[_])
def render: String =
a match
case _: Event[_, _] => ""
case a: Attribute => a.render
case p: Property => p.render
case a: NamedAttribute => a.name
case _: EmptyAttribute.type => ""

extension (a: Attribute)
def render: String =
s"""${a.name}="${a.value}""""

extension (p: Property)
def render: String =
val asStr: String =
p.valueOf match
case x: Boolean => x.toString
case x: String => x

s"""${p.name}="${asStr}""""

extension [Msg](elems: List[Elem[Msg]])
def render: String =
elems.map(_.render).mkString
4 changes: 4 additions & 0 deletions tyrian/shared/src/main/scala/tyrian/Html.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import scala.annotation.targetName
/** An HTML element can be a tag or a text node */
sealed trait Elem[+M]:
def map[N](f: M => N): Elem[N]
override def toString(): String = this.render

/** An Empty Node - renders nothing */
case object Empty extends Elem[Nothing]:
def map[N](f: Nothing => N): Empty.type = this
override def toString(): String = this.render

/** A text node */
final case class Text(value: String) extends Elem[Nothing]:
def map[N](f: Nothing => N): Text = this
override def toString(): String = this.render

/** Base class for HTML tags */
sealed trait Html[+M] extends Elem[M]:
def map[N](f: M => N): Html[N]
def innerHtml(html: String): Html[M]
override def toString(): String = this.render

/** Object used to provide Html syntax `import tyrian.Html.*`
*/
Expand Down
80 changes: 0 additions & 80 deletions tyrian/shared/src/main/scala/tyrian/runtime/TyrianSSR.scala

This file was deleted.

Loading

0 comments on commit 81f6655

Please sign in to comment.