diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala index 2bf8aaf..b90360e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala @@ -18,20 +18,20 @@ def label: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node trait ScenarioExample: def title: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node object ScenarioExample: def apply( t: String, - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = new ScenarioExample: override val title: String = t - override def element(using ComponentContext[_]): HtmlElement = elem + override def element(using ComponentContext[_]): Node = elem trait ScenarioExamples: self: Scenario => @@ -42,11 +42,11 @@ ): List[ScenarioExample] def example(name: String)( - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = ScenarioExample(name, elem) - override def element(using ComponentContext[_]): HtmlElement = + override def element(using ComponentContext[_]): Node = val eventBus: EventBus[Any] = EventBus[Any]() given sc: ScenarioContext = new ScenarioContext: @@ -60,7 +60,7 @@ examples.map(se => renderExample(se.title, se.element)) ) - private def renderExample(t: String, c: HtmlElement): Div = + private def renderExample(t: String, c: Node): Div = div( cls("bg-white overflow-hidden shadow rounded-lg"), div( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala index 2bf8aaf..b90360e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala @@ -18,20 +18,20 @@ def label: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node trait ScenarioExample: def title: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node object ScenarioExample: def apply( t: String, - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = new ScenarioExample: override val title: String = t - override def element(using ComponentContext[_]): HtmlElement = elem + override def element(using ComponentContext[_]): Node = elem trait ScenarioExamples: self: Scenario => @@ -42,11 +42,11 @@ ): List[ScenarioExample] def example(name: String)( - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = ScenarioExample(name, elem) - override def element(using ComponentContext[_]): HtmlElement = + override def element(using ComponentContext[_]): Node = val eventBus: EventBus[Any] = EventBus[Any]() given sc: ScenarioContext = new ScenarioContext: @@ -60,7 +60,7 @@ examples.map(se => renderExample(se.title, se.element)) ) - private def renderExample(t: String, c: HtmlElement): Div = + private def renderExample(t: String, c: Node): Div = div( cls("bg-white overflow-hidden shadow rounded-lg"), div( diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala deleted file mode 100644 index 2815390..0000000 --- a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala +++ /dev/null @@ -1,26 +0,0 @@ -package works.iterative.ui - -import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.model.Computable -import works.iterative.ui.scenarios.Scenario.Id -import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} - -object ComputableScenarioModule extends Scenario with ScenarioExamples: - - override val id: Id = "computable" - - override val label: String = "Computable" - - override protected def examples(using - ScenarioContext, - ComponentContext[?] - ): List[ScenarioExample] = - List(simple) - - private def simple: ScenarioExample = example("ready")( - ComputableComponent(div)( - Val(Computable.Ready(span("Hello World!"))) - ).element - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala index 2bf8aaf..b90360e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala @@ -18,20 +18,20 @@ def label: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node trait ScenarioExample: def title: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node object ScenarioExample: def apply( t: String, - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = new ScenarioExample: override val title: String = t - override def element(using ComponentContext[_]): HtmlElement = elem + override def element(using ComponentContext[_]): Node = elem trait ScenarioExamples: self: Scenario => @@ -42,11 +42,11 @@ ): List[ScenarioExample] def example(name: String)( - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = ScenarioExample(name, elem) - override def element(using ComponentContext[_]): HtmlElement = + override def element(using ComponentContext[_]): Node = val eventBus: EventBus[Any] = EventBus[Any]() given sc: ScenarioContext = new ScenarioContext: @@ -60,7 +60,7 @@ examples.map(se => renderExample(se.title, se.element)) ) - private def renderExample(t: String, c: HtmlElement): Div = + private def renderExample(t: String, c: Node): Div = div( cls("bg-white overflow-hidden shadow rounded-lg"), div( diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala deleted file mode 100644 index 2815390..0000000 --- a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala +++ /dev/null @@ -1,26 +0,0 @@ -package works.iterative.ui - -import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.model.Computable -import works.iterative.ui.scenarios.Scenario.Id -import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} - -object ComputableScenarioModule extends Scenario with ScenarioExamples: - - override val id: Id = "computable" - - override val label: String = "Computable" - - override protected def examples(using - ScenarioContext, - ComponentContext[?] - ): List[ScenarioExample] = - List(simple) - - private def simple: ScenarioExample = example("ready")( - ComputableComponent(div)( - Val(Computable.Ready(span("Hello World!"))) - ).element - ) diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala new file mode 100644 index 0000000..fd458cf --- /dev/null +++ b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala @@ -0,0 +1,22 @@ +package works.iterative.ui + +import com.raquo.laminar.api.L.* +import works.iterative.ui.components.ComponentContext +import works.iterative.ui.scenarios.Scenario.Id +import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} + +object ExampleScenarioModule extends Scenario with ScenarioExamples: + + override val id: Id = "example" + + override val label: String = "Example" + + override protected def examples(using + ScenarioContext, + ComponentContext[?] + ): List[ScenarioExample] = + List(simple) + + private def simple: ScenarioExample = example("ready")( + div("Hello World!") + ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala index 2bf8aaf..b90360e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala @@ -18,20 +18,20 @@ def label: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node trait ScenarioExample: def title: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node object ScenarioExample: def apply( t: String, - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = new ScenarioExample: override val title: String = t - override def element(using ComponentContext[_]): HtmlElement = elem + override def element(using ComponentContext[_]): Node = elem trait ScenarioExamples: self: Scenario => @@ -42,11 +42,11 @@ ): List[ScenarioExample] def example(name: String)( - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = ScenarioExample(name, elem) - override def element(using ComponentContext[_]): HtmlElement = + override def element(using ComponentContext[_]): Node = val eventBus: EventBus[Any] = EventBus[Any]() given sc: ScenarioContext = new ScenarioContext: @@ -60,7 +60,7 @@ examples.map(se => renderExample(se.title, se.element)) ) - private def renderExample(t: String, c: HtmlElement): Div = + private def renderExample(t: String, c: Node): Div = div( cls("bg-white overflow-hidden shadow rounded-lg"), div( diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala deleted file mode 100644 index 2815390..0000000 --- a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala +++ /dev/null @@ -1,26 +0,0 @@ -package works.iterative.ui - -import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.model.Computable -import works.iterative.ui.scenarios.Scenario.Id -import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} - -object ComputableScenarioModule extends Scenario with ScenarioExamples: - - override val id: Id = "computable" - - override val label: String = "Computable" - - override protected def examples(using - ScenarioContext, - ComponentContext[?] - ): List[ScenarioExample] = - List(simple) - - private def simple: ScenarioExample = example("ready")( - ComputableComponent(div)( - Val(Computable.Ready(span("Hello World!"))) - ).element - ) diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala new file mode 100644 index 0000000..fd458cf --- /dev/null +++ b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala @@ -0,0 +1,22 @@ +package works.iterative.ui + +import com.raquo.laminar.api.L.* +import works.iterative.ui.components.ComponentContext +import works.iterative.ui.scenarios.Scenario.Id +import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} + +object ExampleScenarioModule extends Scenario with ScenarioExamples: + + override val id: Id = "example" + + override val label: String = "Example" + + override protected def examples(using + ScenarioContext, + ComponentContext[?] + ): List[ScenarioExample] = + List(simple) + + private def simple: ScenarioExample = example("ready")( + div("Hello World!") + ) diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala b/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala index f7d7534..f1f1061 100644 --- a/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala +++ b/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala @@ -17,7 +17,7 @@ object Main extends ScenarioMain( "ui", - List(ComputableScenarioModule), + List(ExampleScenarioModule), Messages, Css ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala deleted file mode 100644 index f79dfd8..0000000 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponent.scala +++ /dev/null @@ -1,39 +0,0 @@ -package works.iterative.ui.components.laminar - -import com.raquo.laminar.api.L.* -import works.iterative.ui.model.Computable -import works.iterative.ui.model.Computable.* -import com.raquo.laminar.tags.HtmlTag -import com.raquo.laminar.nodes.ReactiveHtmlElement -import com.raquo.laminar.nodes.CommentNode -import org.scalajs.dom -import works.iterative.ui.components.ComponentContext -import works.iterative.core.UserMessage -import LaminarExtensions.* - -class ComputableComponent[Ref <: dom.html.Element]( - as: HtmlTag[Ref], - mods: Mod[ReactiveHtmlElement[Ref]]* -)( - c: Signal[Computable[HtmlElement]] -)(using ComponentContext[?]): - val element: ReactiveHtmlElement[Ref] = as( - mods, - child <-- c.map { - case Uninitialized => CommentNode("Uninitialized") - case Computing(_) => - div( - cls("text-center"), - h3( - cls("mt-2 text-sm font-semibold text-gray-900"), - UserMessage("loading").asElement - ), - UserMessage("loading.description").asOptionalElement.map(dm => - p(cls("mt-1 text-sm text-gray-500"), dm) - ) - ) - case Ready(element) => element - case Failed(_) => CommentNode("Failed") - case Recomputing(_, element) => element - } - ) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala new file mode 100644 index 0000000..e1bdba8 --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/ComputableComponents.scala @@ -0,0 +1,21 @@ +package works.iterative.ui +package components +package laminar + +import com.raquo.laminar.api.L.* +import com.raquo.laminar.nodes.ReactiveHtmlElement +import com.raquo.laminar.tags.HtmlTag +import org.scalajs.dom +import model.Computable + +trait ComputableComponents: + def renderComputable[Ref <: dom.html.Element]( + as: HtmlTag[Ref], + mods: HtmlMod* + )( + c: Signal[Computable[HtmlElement]] + ): ReactiveHtmlElement[Ref] + def renderComputable(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div)(c) + def renderComputable(mods: HtmlMod)(c: Signal[Computable[HtmlElement]]): Div = + renderComputable(div, mods)(c) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala new file mode 100644 index 0000000..aac8faf --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/LaminarUIBuilder.scala @@ -0,0 +1,7 @@ +package works.iterative.ui.components +package laminar + +import com.raquo.laminar.api.L.* +import works.iterative.ui.model.HtmlUIBuilder + +object LaminarUIBuilder extends HtmlUIBuilder[Node, ComponentContext[Any]] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala index c7632a4..c389887 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPage.scala @@ -1,4 +1,8 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui +package components +package laminar +package modules +package formpage import zio.Tag import works.iterative.ui.components.laminar.forms.Form @@ -10,3 +14,4 @@ with FormPageView[T] with FormPageComponent[T] with FormBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala index 6fe35f3..9f4514f 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageComponent.scala @@ -5,14 +5,12 @@ import works.iterative.ui.components.laminar.LaminarComponent import works.iterative.ui.components.laminar.forms.FormBuilderModule import works.iterative.ui.components.laminar.forms.FormBuilderContext -import works.iterative.ui.components.ComponentContext trait FormPageComponent[T]: self: FormPageModel[T] with FormPageView[T] with FormBuilderModule => class Component(effectHandler: EffectHandler[Effect, Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ) extends LaminarComponent[Model, Action, Effect, HtmlElement](effectHandler) with Module: override def render( diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala index 63d6ab8..7555a0e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/formpage/FormPageView.scala @@ -1,27 +1,25 @@ -package works.iterative.ui.components.laminar.modules.formpage +package works.iterative.ui.components +package laminar +package modules +package formpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.forms.FormBuilderModule -import works.iterative.ui.components.laminar.forms.Form -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.components.laminar.forms.FormBuilderContext +import works.iterative.ui.components.laminar.forms.* trait FormPageView[T: Form]: - self: FormPageModel[T] with FormBuilderModule => + self: FormPageModel[T] with FormBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using - fctx: FormBuilderContext, - cctx: ComponentContext[?] + fctx: FormBuilderContext ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map( _.initialValue.map(renderForm).map(div(_)) ) - ).element + ) def renderForm( initialValue: Option[T] diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala index c17d87d..75c9077 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPage.scala @@ -1,7 +1,10 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui +package components +package laminar +package modules +package listpage import zio.Tag -import works.iterative.ui.components.laminar.HtmlTabular import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule trait ListPage[T: Tag: HtmlTabular, Q: Tag] @@ -10,3 +13,4 @@ with ListPageView[T] with ListPageComponent[T] with HtmlTableBuilderModule + with ComputableComponents diff --git a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala index b60fbe7..de91ed3 100644 --- a/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala +++ b/ui/js/src/main/scala/works/iterative/ui/components/laminar/modules/listpage/ListPageView.scala @@ -1,24 +1,24 @@ -package works.iterative.ui.components.laminar.modules.listpage +package works.iterative.ui.components +package laminar +package modules +package listpage import zio.prelude.* import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.laminar.HtmlTabular import io.laminext.syntax.core.* import works.iterative.ui.components.laminar.tables.HtmlTableBuilderModule -import works.iterative.ui.components.ComponentContext trait ListPageView[T: HtmlTabular]: - self: ListPageModel[T, ?] with HtmlTableBuilderModule => + self: ListPageModel[T, ?] with HtmlTableBuilderModule with ComputableComponents => class View(model: Signal[Model], actions: Observer[Action])(using ComponentContext[?] ): val element: HtmlElement = - ComputableComponent(div)( + renderComputable( model.map(_.items.map(renderItem)) - ).element + ) private def renderItem(items: List[T]): HtmlElement = buildTable(items) diff --git a/ui/js/src/main/scala/works/iterative/ui/components/package.scala b/ui/js/src/main/scala/works/iterative/ui/components/package.scala new file mode 100644 index 0000000..c12768f --- /dev/null +++ b/ui/js/src/main/scala/works/iterative/ui/components/package.scala @@ -0,0 +1,4 @@ +package works.iterative.ui + +package object components: + export laminar.LaminarUIBuilder.* diff --git a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala index 2bf8aaf..b90360e 100644 --- a/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala +++ b/ui/js/src/main/scala/works/iterative/ui/scenarios/Scenario.scala @@ -18,20 +18,20 @@ def label: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node trait ScenarioExample: def title: String - def element(using ComponentContext[_]): HtmlElement + def element(using ComponentContext[_]): Node object ScenarioExample: def apply( t: String, - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = new ScenarioExample: override val title: String = t - override def element(using ComponentContext[_]): HtmlElement = elem + override def element(using ComponentContext[_]): Node = elem trait ScenarioExamples: self: Scenario => @@ -42,11 +42,11 @@ ): List[ScenarioExample] def example(name: String)( - elem: ComponentContext[_] ?=> HtmlElement + elem: ComponentContext[_] ?=> Node ): ScenarioExample = ScenarioExample(name, elem) - override def element(using ComponentContext[_]): HtmlElement = + override def element(using ComponentContext[_]): Node = val eventBus: EventBus[Any] = EventBus[Any]() given sc: ScenarioContext = new ScenarioContext: @@ -60,7 +60,7 @@ examples.map(se => renderExample(se.title, se.element)) ) - private def renderExample(t: String, c: HtmlElement): Div = + private def renderExample(t: String, c: Node): Div = div( cls("bg-white overflow-hidden shadow rounded-lg"), div( diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala deleted file mode 100644 index 2815390..0000000 --- a/ui/scenarios/src/main/scala/works/iterative/ui/ComputableScenarioModule.scala +++ /dev/null @@ -1,26 +0,0 @@ -package works.iterative.ui - -import com.raquo.laminar.api.L.* -import works.iterative.ui.components.laminar.ComputableComponent -import works.iterative.ui.components.ComponentContext -import works.iterative.ui.model.Computable -import works.iterative.ui.scenarios.Scenario.Id -import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} - -object ComputableScenarioModule extends Scenario with ScenarioExamples: - - override val id: Id = "computable" - - override val label: String = "Computable" - - override protected def examples(using - ScenarioContext, - ComponentContext[?] - ): List[ScenarioExample] = - List(simple) - - private def simple: ScenarioExample = example("ready")( - ComputableComponent(div)( - Val(Computable.Ready(span("Hello World!"))) - ).element - ) diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala new file mode 100644 index 0000000..fd458cf --- /dev/null +++ b/ui/scenarios/src/main/scala/works/iterative/ui/ExampleScenarioModule.scala @@ -0,0 +1,22 @@ +package works.iterative.ui + +import com.raquo.laminar.api.L.* +import works.iterative.ui.components.ComponentContext +import works.iterative.ui.scenarios.Scenario.Id +import works.iterative.ui.scenarios.{Scenario, ScenarioExample, ScenarioExamples} + +object ExampleScenarioModule extends Scenario with ScenarioExamples: + + override val id: Id = "example" + + override val label: String = "Example" + + override protected def examples(using + ScenarioContext, + ComponentContext[?] + ): List[ScenarioExample] = + List(simple) + + private def simple: ScenarioExample = example("ready")( + div("Hello World!") + ) diff --git a/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala b/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala index f7d7534..f1f1061 100644 --- a/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala +++ b/ui/scenarios/src/main/scala/works/iterative/ui/Main.scala @@ -17,7 +17,7 @@ object Main extends ScenarioMain( "ui", - List(ComputableScenarioModule), + List(ExampleScenarioModule), Messages, Css ) diff --git a/ui/shared/src/main/scala/works/iterative/ui/model/HtmlUIBuilder.scala b/ui/shared/src/main/scala/works/iterative/ui/model/HtmlUIBuilder.scala new file mode 100644 index 0000000..00bb217 --- /dev/null +++ b/ui/shared/src/main/scala/works/iterative/ui/model/HtmlUIBuilder.scala @@ -0,0 +1,31 @@ +package works.iterative.ui.model + +import works.iterative.core.* +import zio.prelude.* + +trait HtmlUIBuilder[Node, Context]: + type Ctx = Context + type Output = Node + type Rendered = Ctx ?=> Output + + sealed trait UIElement + + case class Block( + id: Block.Id, + title: Block.Title, + subtitle: Block.Subtitle, + actions: Block.Actions, + content: Block.Content, + footer: Block.Footer + ) extends UIElement + + object Block: + type Id = String + type Title = Output + type Subtitle = Option[Output] + type Actions = List[Action] + type Content = Reader[Any, Output] + type Footer = Option[Output] + + trait Interpreter: + def render(el: UIElement): Rendered