diff --git a/build.sbt b/build.sbt index f256626..2690619 100644 --- a/build.sbt +++ b/build.sbt @@ -1,9 +1,6 @@ import java.nio.charset.StandardCharsets import org.scalajs.linker.interface.ModuleSplitStyle -lazy val currentYear: String = - java.util.Calendar.getInstance().get(java.util.Calendar.YEAR).toString - val scala33 = "3.3.1" inThisBuild( @@ -37,7 +34,6 @@ inThisBuild( scalacOptions ++= Seq( "-deprecation", "-feature", - "-Wunused:all", "-Xfatal-warnings" ) ) @@ -102,7 +98,8 @@ val usedScalacOptions = Seq( "-unchecked", "-language:higherKinds", "-language:implicitConversions", - "-Xmax-inlines:64" + "-Xmax-inlines:64", + "-Wunused:all" ) lazy val core = scalajsProject("core", false) .settings( @@ -222,36 +219,3 @@ Global / onLoad := { (Global / onLoad).value } -enablePlugins( - SiteScaladocPlugin, - SitePreviewPlugin, - ScalaUnidocPlugin, - GhpagesPlugin -) - -ScalaUnidoc / siteSubdirName := "" -addMappingsToSiteDir( - ScalaUnidoc / packageDoc / mappings, - ScalaUnidoc / siteSubdirName -) -git.remoteRepo := "git@github.com:cheleb/laminar-form-derivation.git" -ghpagesNoJekyll := true -Compile / doc / scalacOptions ++= Seq( - "-siteroot", - "docs", - "-project", - "Laminar Form Derivation", - "-groups", - "-project-version", - version.value, - "-revision", - version.value, - "-default-templates", - "static-site-main", - "-project-footer", - s"Copyright (c) 2022-$currentYear, Olivier NOUGUIER", - "-Ygenerate-inkuire", - "-skip-by-regex:demo\\..*", - "-skip-by-regex:samples\\..*", - "-snippet-compiler:compile" -) diff --git a/docs/_docs/getting-started.md b/docs/_docs/getting-started.md index 0aa6e08..1bfe92f 100644 --- a/docs/_docs/getting-started.md +++ b/docs/_docs/getting-started.md @@ -3,7 +3,7 @@ ## Installation ```sbt -libraryDependencies += "dev.cheleb" %% "laminar-form-derivation" % "0.1.0" +libraryDependencies += "dev.cheleb" %%% "laminar-form-derivation" % "{{ version }}" ``` ### Requirements diff --git a/docs/_docs/test.md b/docs/_docs/test.md new file mode 100644 index 0000000..875f284 --- /dev/null +++ b/docs/_docs/test.md @@ -0,0 +1,8 @@ +```scala mdoc +import java.time.Instant + +def now() = Instant.now() +object Foo extends App{ + println(now()) +} +``` \ No newline at end of file diff --git a/examples/client/src/main/scala/HelloWorld.scala b/examples/client/src/main/scala/HelloWorld.scala index f57caaa..e1696a2 100644 --- a/examples/client/src/main/scala/HelloWorld.scala +++ b/examples/client/src/main/scala/HelloWorld.scala @@ -1,13 +1,29 @@ -package demo +package samples import org.scalajs.dom import com.raquo.laminar.api.L.* import be.doeraene.webcomponents.ui5.* -import samples.tree + +case class Sample(name: String, component: HtmlElement) object App extends App { - val sample = Var(samples.enums) + val sample = Var(samples.enums.component) + + private def item(name: String) = SideNavigation.item( + _.text := name, + dataAttr("component-name") := name + ) + + private val demos = Seq( + samples.simple, + samples.either, + samples.enums, + samples.person, + samples.validation, + samples.list, + samples.tree + ) val myApp = div( @@ -23,51 +39,14 @@ object App extends App { _.events.onSelectionChange .map(_.detail.item.dataset.get("componentName")) --> Observer[ Option[String] - ] { - case e @ Some("Either") => - sample.set(samples.either) - case e @ Some("Enums") => - sample.set(samples.enums) - case v @ Some("Person") => - sample.set(samples.person) - case v @ Some("Validation") => - sample.set(samples.validation) - case v @ Some("Lists") => - sample.set( - samples.component - ) - case v @ Some("Tree") => - sample.set(tree.component) - case _ => - sample.set(div("????")) + ] { name => + val el = name + .flatMap(n => demos.find(_.name == n)) + .getOrElse(samples.simple) + sample.set(el.component) }, - Seq( - SideNavigation.item( - _.text := "Either", - dataAttr("component-name") := "Either" - ), - SideNavigation.item( - _.text := "Enums", - dataAttr("component-name") := "Enums" - ), - SideNavigation.item( - _.text := "Person", - dataAttr("component-name") := "Person" - ), - SideNavigation.item( - _.text := "Validation", - dataAttr("component-name") := "Validation" - ), - SideNavigation.item( - _.text := "List", - dataAttr("component-name") := "Lists" - ), - SideNavigation.item( - _.text := "Tree", - dataAttr("component-name") := "Tree" - ) - ) + demos.map(_.name).map(item) ) ), div( diff --git a/examples/client/src/main/scala/WebSocketDemo.scala b/examples/client/src/main/scala/WebSocketDemo.scala index 252373a..9983790 100644 --- a/examples/client/src/main/scala/WebSocketDemo.scala +++ b/examples/client/src/main/scala/WebSocketDemo.scala @@ -1,6 +1,6 @@ -package polyui +package samples -import com.raquo.laminar.api.L.{*, given} +import com.raquo.laminar.api.L.* import be.doeraene.webcomponents.ui5.* import be.doeraene.webcomponents.ui5.configkeys.* import io.laminext.websocket._ diff --git a/examples/client/src/main/scala/samples/EitherSample.scala b/examples/client/src/main/scala/samples/EitherSample.scala index cde9d89..be34723 100644 --- a/examples/client/src/main/scala/samples/EitherSample.scala +++ b/examples/client/src/main/scala/samples/EitherSample.scala @@ -6,21 +6,23 @@ import com.raquo.laminar.api.L.* import com.raquo.airstream.state.Var -val either = { +val either = Sample( + "Either", { - case class EitherSample( - either: Either[Cat, Dog], - optionalInt: Option[Int] - ) + case class EitherSample( + either: Either[Cat, Dog], + optionalInt: Option[Int] + ) - val eitherVar = Var(EitherSample(Left(Cat("Scala le chat", 6)), Some(1))) + val eitherVar = Var(EitherSample(Left(Cat("Scala le chat", 6)), Some(1))) - div( - child <-- eitherVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(eitherVar) - ) -} + div( + child <-- eitherVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(eitherVar) + ) + } +) diff --git a/examples/client/src/main/scala/samples/EnumSample.scala b/examples/client/src/main/scala/samples/EnumSample.scala index 3543360..be2b0bf 100644 --- a/examples/client/src/main/scala/samples/EnumSample.scala +++ b/examples/client/src/main/scala/samples/EnumSample.scala @@ -6,32 +6,34 @@ import com.raquo.laminar.api.L.* import com.raquo.airstream.state.Var -val enums = { - - enum Color(code: String): - case Black extends Color("000") - case White extends Color("FFF") - case Isabelle extends Color("???") - - case class Basket(@EnumValues(Color.values) color: Color, cat: Cat) - - case class Cat( - name: String, - age: Int, - @EnumValues(Color.values) - color: Color - ) - - val eitherVar = Var( - Basket(Color.Black, Cat("Scala", 10, Color.White)) - ) - - div( - child <-- eitherVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(eitherVar) - ) -} +val enums = Sample( + "Enums", { + + enum Color(val code: String): + case Black extends Color("000") + case White extends Color("FFF") + case Isabelle extends Color("???") + + case class Basket(@EnumValues(Color.values) color: Color, cat: Cat) + + case class Cat( + name: String, + age: Int, + @EnumValues(Color.values) + color: Color + ) + + val eitherVar = Var( + Basket(Color.Black, Cat("Scala", 10, Color.White)) + ) + + div( + child <-- eitherVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(eitherVar) + ) + } +) diff --git a/examples/client/src/main/scala/samples/ListElement.scala b/examples/client/src/main/scala/samples/ListElement.scala index 8440502..6f89a4a 100644 --- a/examples/client/src/main/scala/samples/ListElement.scala +++ b/examples/client/src/main/scala/samples/ListElement.scala @@ -4,9 +4,6 @@ import dev.cheleb.scalamigen.{*, given} import com.raquo.laminar.api.L.* -import io.github.iltotore.iron.* -import io.github.iltotore.iron.constraint.all.Positive - case class Person2(name: String, age: Int) case class ListElement( @@ -16,17 +13,20 @@ case class ListElement( val listPersonVar = Var(ListElement(List(1, 2, 3).map(Person2("Vlad", _)))) val listIntVar = Var(List(1, 2, 3)) -val component = div( - child <-- listPersonVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(listPersonVar), - child <-- listIntVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(listIntVar) +val list = Sample( + "List", + div( + child <-- listPersonVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(listPersonVar), + child <-- listIntVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(listIntVar) + ) ) diff --git a/examples/client/src/main/scala/samples/Persons.scala b/examples/client/src/main/scala/samples/Persons.scala index 06328f1..96c809e 100644 --- a/examples/client/src/main/scala/samples/Persons.scala +++ b/examples/client/src/main/scala/samples/Persons.scala @@ -2,10 +2,8 @@ package samples import dev.cheleb.scalamigen.{*, given} -import org.scalajs.dom import com.raquo.laminar.api.L.* import magnolia1.* -import be.doeraene.webcomponents.ui5.* import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* @@ -45,11 +43,14 @@ val vlad = val personVar = Var(vlad) -def person = div( - child <-- personVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(personVar) +val person = Sample( + "Person", + div( + child <-- personVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(personVar) + ) ) diff --git a/examples/client/src/main/scala/samples/SimpleSample.scala b/examples/client/src/main/scala/samples/SimpleSample.scala new file mode 100644 index 0000000..ee04458 --- /dev/null +++ b/examples/client/src/main/scala/samples/SimpleSample.scala @@ -0,0 +1,23 @@ +package samples + +import dev.cheleb.scalamigen.{*, given} + +import com.raquo.laminar.api.L.* + +import com.raquo.airstream.state.Var + +val simple = Sample( + "Simple", { + + val eitherVar = Var(Cat("Scala le chat", 6)) + + div( + child <-- eitherVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(eitherVar) + ) + } +) diff --git a/examples/client/src/main/scala/samples/Tree.scala b/examples/client/src/main/scala/samples/Tree.scala index d96ec10..c6687a8 100644 --- a/examples/client/src/main/scala/samples/Tree.scala +++ b/examples/client/src/main/scala/samples/Tree.scala @@ -6,85 +6,75 @@ import com.raquo.laminar.api.L.* import com.raquo.airstream.state.Var -object tree { - enum Tree[+T]: - case Empty extends Tree[Nothing] - case Node(value: T, left: Tree[T] = Empty, right: Tree[T] = Empty) - - object Tree: - def homomorphism[A, B](f: A => B)(tree: Tree[A]): Tree[B] = - tree match - case Empty => Empty - case Node(value, left, right) => - Node(f(value), homomorphism(f)(left), homomorphism(f)(right)) - - def isomorphic[A, B](f: A => B, g: B => A)(tree: Tree[A]): Tree[B] = - homomorphism(f)(tree) - - def isSameStructure(tree1: Tree[_], tree2: Tree[_]): Boolean = - (tree1, tree2) match - case (Empty, Empty) => true - case (Node(_, _, _), Empty) => false - case (Empty, Node(_, _, _)) => false - case (Node(_, left1, right1), Node(_, left2, right2)) => - isSameStructure(left1, left2) && isSameStructure(right1, right2) - - implicit def treeInstance[A](using - default: Defaultable[A] - )(using Form[A]): Form[Tree[A]] = - new Form[Tree[A]] { - override def isAnyRef = true - - override def render( - variable: Var[Tree[A]], - syncParent: () => Unit, - values: List[Tree[A]] - )(using WidgetFactory): HtmlElement = - variable.now() match - case Tree.Empty => - button( - "Add me", - onClick.mapTo( - Tree.Node(default.default, Tree.Empty, Tree.Empty) - ) --> variable.writer - ) - - case Tree.Node(value, left, right) => - div( - button("drop", onClick.mapTo(Tree.Empty) --> variable.writer), - summon[Form[Tree.Node[A]]].render( - variable.asInstanceOf[Var[Tree.Node[A]]] - ) +enum Tree[+T]: + case Empty extends Tree[Nothing] + case Node(value: T, left: Tree[T] = Empty, right: Tree[T] = Empty) +object Tree: + def homomorphism[A, B](f: A => B)(tree: Tree[A]): Tree[B] = + tree match + case Empty => Empty + case Node(value, left, right) => + Node(f(value), homomorphism(f)(left), homomorphism(f)(right)) + def isomorphic[A, B](f: A => B, g: B => A)(tree: Tree[A]): Tree[B] = + homomorphism(f)(tree) + def isSameStructure(tree1: Tree[_], tree2: Tree[_]): Boolean = + (tree1, tree2) match + case (Empty, Empty) => true + case (Node(_, _, _), Empty) => false + case (Empty, Node(_, _, _)) => false + case (Node(_, left1, right1), Node(_, left2, right2)) => + isSameStructure(left1, left2) && isSameStructure(right1, right2) +implicit def treeInstance[A](using + default: Defaultable[A] +)(using Form[A]): Form[Tree[A]] = + new Form[Tree[A]] { + override def isAnyRef = true + override def render( + variable: Var[Tree[A]], + syncParent: () => Unit, + values: List[Tree[A]] + )(using WidgetFactory): HtmlElement = + variable.now() match + case Tree.Empty => + button( + "Add me", + onClick.mapTo( + Tree.Node(default.default, Tree.Empty, Tree.Empty) + ) --> variable.writer + ) + case Tree.Node(value, left, right) => + div( + button("drop", onClick.mapTo(Tree.Empty) --> variable.writer), + summon[Form[Tree.Node[A]]].render( + variable.asInstanceOf[Var[Tree.Node[A]]] ) - } - - import Tree.* + ) + } - val treeVar = Var( - Node(1, Node(2, Node(3, Empty, Empty), Empty), Node(4, Empty, Empty)) - ) +val tree = Sample( + "Tree", { - case class Person(name: String, age: Int) + import Tree.* - object Person { - given Defaultable[Person] with - def default = Person("--", 0) + case class Person(name: String, age: Int) + object Person { + given Defaultable[Person] with + def default = Person("--", 0) + } + val treeVar2 = Var( + Node(Person("agnes", 40), Empty, Empty) + ) + div( + child <-- treeVar2.signal.map { item => + div( + s"$item zozo" + ) + }, + child <-- treeVar2.signal + .distinctByFn(Tree.isSameStructure) + .map { item => + Form.renderVar(treeVar2) + } + ) } - - val treeVar2 = Var( - Node(Person("agnes", 40), Empty, Empty) - ) - - val component = div( - child <-- treeVar2.signal.map { item => - div( - s"$item zozo" - ) - }, - child <-- treeVar2.signal - .distinctByFn(Tree.isSameStructure) - .map { item => - Form.renderVar(treeVar2) - } - ) -} +) diff --git a/examples/client/src/main/scala/samples/Validation.scala b/examples/client/src/main/scala/samples/Validation.scala index d0185f2..ac1b557 100644 --- a/examples/client/src/main/scala/samples/Validation.scala +++ b/examples/client/src/main/scala/samples/Validation.scala @@ -8,9 +8,8 @@ import io.github.iltotore.iron.* import io.github.iltotore.iron.constraint.all.* import com.raquo.airstream.state.Var -import be.doeraene.webcomponents.ui5.Input -import model.CurrencyCode +import samples.model.CurrencyCode given Form[CurrencyCode] = stringForm(CurrencyCode(_)) @@ -18,7 +17,7 @@ case class IronSample( curenncyCode: CurrencyCode, optional: Option[String], optionalInt: Option[Int], - doublePositive: Double :| GreaterEqual[8.0], + doubleGreaterThanEight: Double :| GreaterEqual[8.0], optionalDoublePositive: Option[Double :| Positive] ) @@ -31,11 +30,14 @@ val ironSampleVar = Var( IronSample(CurrencyCode("Eur"), Some("name"), Some(1), 9.1, Some(1)) ) -val validation = div( - child <-- ironSampleVar.signal.map { item => - div( - s"$item" - ) - }, - Form.renderVar(ironSampleVar) +val validation = Sample( + "Validation", + div( + child <-- ironSampleVar.signal.map { item => + div( + s"$item" + ) + }, + Form.renderVar(ironSampleVar) + ) ) diff --git a/examples/client/src/main/scala/model/CurrencyCode.scala b/examples/client/src/main/scala/samples/model/CurrencyCode.scala similarity index 82% rename from examples/client/src/main/scala/model/CurrencyCode.scala rename to examples/client/src/main/scala/samples/model/CurrencyCode.scala index eaa61c3..e049f52 100644 --- a/examples/client/src/main/scala/model/CurrencyCode.scala +++ b/examples/client/src/main/scala/samples/model/CurrencyCode.scala @@ -1,4 +1,4 @@ -package model +package samples.model opaque type CurrencyCode = String diff --git a/examples/client/src/main/scala/samples/package.scala b/examples/client/src/main/scala/samples/package.scala index 206d027..6a4febe 100644 --- a/examples/client/src/main/scala/samples/package.scala +++ b/examples/client/src/main/scala/samples/package.scala @@ -14,4 +14,4 @@ given Defaultable[Cat] with given Defaultable[Dog] with def default = Dog("", 0) -given WidgetFactory = UI5WidgetFactory +given WidgetFactory = if true then UI5WidgetFactory else LaminarWidgetFactory diff --git a/examples/server/src/main/scala/sampleserver/SampleServer.scala b/examples/server/src/main/scala/samples/server/SampleServer.scala similarity index 98% rename from examples/server/src/main/scala/sampleserver/SampleServer.scala rename to examples/server/src/main/scala/samples/server/SampleServer.scala index e1d8367..355e91e 100644 --- a/examples/server/src/main/scala/sampleserver/SampleServer.scala +++ b/examples/server/src/main/scala/samples/server/SampleServer.scala @@ -1,4 +1,4 @@ -package sampleserver +package samples.server import zio.* import zio.http.* diff --git a/modules/core/src/main/scala/dev/cheleb/scalamigen/Form.scala b/modules/core/src/main/scala/dev/cheleb/scalamigen/Form.scala index a33664c..c86c37a 100644 --- a/modules/core/src/main/scala/dev/cheleb/scalamigen/Form.scala +++ b/modules/core/src/main/scala/dev/cheleb/scalamigen/Form.scala @@ -2,19 +2,11 @@ package dev.cheleb.scalamigen import com.raquo.laminar.api.L.* import magnolia1.* -import scala.CanEqual.derived -import java.util.UUID -import scala.util.Random -import io.github.iltotore.iron.* -import io.github.iltotore.iron.constraint.all.* import scala.util.Try import com.raquo.airstream.state.Var -import com.raquo.airstream.core.Source -import com.raquo.laminar.nodes.ReactiveElement import org.scalajs.dom.HTMLDivElement import magnolia1.SealedTrait.SubtypeValue -import com.raquo.laminar.modifiers.EventListener trait Form[A] { self => diff --git a/modules/core/src/main/scala/dev/cheleb/scalamigen/Forms.scala b/modules/core/src/main/scala/dev/cheleb/scalamigen/Forms.scala index 667603c..d5e08cd 100644 --- a/modules/core/src/main/scala/dev/cheleb/scalamigen/Forms.scala +++ b/modules/core/src/main/scala/dev/cheleb/scalamigen/Forms.scala @@ -5,7 +5,6 @@ import io.github.iltotore.iron.constraint.all.* import com.raquo.laminar.api.L.* -import scala.quoted.* import scala.util.Try import com.raquo.airstream.state.Var @@ -52,7 +51,6 @@ given Form[String] with onInput.mapToValue --> { v => variable.set(v) syncParent() - } ) diff --git a/modules/core/src/main/scala/dev/cheleb/scalamigen/LaminarWidgetFactory.scala b/modules/core/src/main/scala/dev/cheleb/scalamigen/LaminarWidgetFactory.scala index b1df279..13c2549 100644 --- a/modules/core/src/main/scala/dev/cheleb/scalamigen/LaminarWidgetFactory.scala +++ b/modules/core/src/main/scala/dev/cheleb/scalamigen/LaminarWidgetFactory.scala @@ -4,6 +4,8 @@ import com.raquo.laminar.api.L.* import com.raquo.laminar.modifiers.EventListener import org.scalajs.dom.HTMLSelectElement +/** This is raw laminar implementation of the widget factory. + */ object LaminarWidgetFactory extends WidgetFactory: def renderText: HtmlElement = input( tpe := "text" diff --git a/modules/core/src/main/scala/dev/cheleb/scalamigen/WidgetFactory.scala b/modules/core/src/main/scala/dev/cheleb/scalamigen/WidgetFactory.scala index d1c28ba..2f18dae 100644 --- a/modules/core/src/main/scala/dev/cheleb/scalamigen/WidgetFactory.scala +++ b/modules/core/src/main/scala/dev/cheleb/scalamigen/WidgetFactory.scala @@ -3,6 +3,8 @@ package dev.cheleb.scalamigen import com.raquo.laminar.api.L.HtmlElement import com.raquo.laminar.modifiers.EventListener +/** This is a trait that defines the interface for the widget factory. + */ trait WidgetFactory: def renderText: HtmlElement def renderLabel(required: Boolean, name: String): HtmlElement diff --git a/modules/ui5/src/main/scala/dev/cheleb/scalamigen/ui5/UI5WidgetFactory.scala b/modules/ui5/src/main/scala/dev/cheleb/scalamigen/ui5/UI5WidgetFactory.scala index a6e3d57..2ffaa1c 100644 --- a/modules/ui5/src/main/scala/dev/cheleb/scalamigen/ui5/UI5WidgetFactory.scala +++ b/modules/ui5/src/main/scala/dev/cheleb/scalamigen/ui5/UI5WidgetFactory.scala @@ -1,6 +1,6 @@ package dev.cheleb.scalamigen.ui5 -import com.raquo.laminar.api.L.{button as lbutton, ul as lul, *} +import com.raquo.laminar.api.L.* import be.doeraene.webcomponents.ui5.* import be.doeraene.webcomponents.ui5.configkeys.InputType import com.raquo.laminar.modifiers.EventListener diff --git a/website.sbt b/website.sbt new file mode 100644 index 0000000..1132b81 --- /dev/null +++ b/website.sbt @@ -0,0 +1,35 @@ +lazy val currentYear: String = + java.util.Calendar.getInstance().get(java.util.Calendar.YEAR).toString + +enablePlugins( + SiteScaladocPlugin, + SitePreviewPlugin, + ScalaUnidocPlugin, + GhpagesPlugin +) + +ScalaUnidoc / siteSubdirName := "" +addMappingsToSiteDir( + ScalaUnidoc / packageDoc / mappings, + ScalaUnidoc / siteSubdirName +) +git.remoteRepo := "git@github.com:cheleb/laminar-form-derivation.git" +ghpagesNoJekyll := true +Compile / doc / scalacOptions ++= Seq( + "-siteroot", + "docs", + "-project", + "Laminar Form Derivation", + "-groups", + "-project-version", + version.value, + "-revision", + version.value, + "-default-templates", + "static-site-main", + "-project-footer", + s"Copyright (c) 2022-$currentYear, Olivier NOUGUIER", + "-Ygenerate-inkuire", + "-skip-by-regex:samples\\..*", + "-snippet-compiler:compile" +)