From 85efe4c6b24da355d488f957f6fcb9c06547a712 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 00:22:20 +0000 Subject: [PATCH 01/40] - --- .../terminal21/client/ConnectedSession.scala | 12 ++- .../client/components/StdElement.scala | 3 +- .../client/components/UiComponent.scala | 3 +- .../client/components/UiElement.scala | 7 +- .../components/chakra/ChakraElement.scala | 85 +++++++++++++------ .../terminal21/client/json/ServerJson.scala | 35 ++++++++ 6 files changed, 109 insertions(+), 36 deletions(-) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index b5a73373..93a54fcb 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} import org.terminal21.client.components.UiElementEncoding +import org.terminal21.client.json.ServerJson import org.terminal21.model.* import org.terminal21.ui.std.SessionsService @@ -92,14 +93,11 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se def render(): Unit = val j = toJson - sessionsService.setSessionJsonState(session, j.toJson.noSpaces) + println(s"Sending: ${j.noSpaces}") + sessionsService.setSessionJsonState(session, j.noSpaces) def allElements: Seq[UiElement] = synchronized(elements) - private def toJson: JsonObject = + private def toJson: Json = import encoding.given - val elementsCopy = allElements - val json = - for e <- elementsCopy - yield e.asJson.deepDropNullValues - JsonObject(("elements", json.asJson)) + ServerJson.from(allElements).asJson.deepDropNullValues diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala index 1c4d316b..93fcdada 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala @@ -17,7 +17,8 @@ case class Paragraph( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends StdElement - with HasChildren[Paragraph] + with HasChildren[Paragraph]: + override def copyNoChildren: Paragraph = copy(children = Nil) case class Input( key: String = Keys.nextKey, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index bf121453..925f98c9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -4,4 +4,5 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ -trait UiComponent extends UiElement with HasChildren[UiComponent] +trait UiComponent extends UiElement with HasChildren[UiComponent]: + override def copyNoChildren: UiComponent = ??? diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index eca8cb17..a6fb0d28 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -4,6 +4,7 @@ import org.terminal21.client.{ConnectedSession, EventHandler} trait UiElement: def key: String + def flat: Seq[UiElement] = Seq(this) def render()(using session: ConnectedSession): Unit = session.add(this) @@ -16,10 +17,12 @@ object UiElement: case hc: HasChildren[_] => allDeep(hc.children) .flatten - trait HasChildren[A]: + trait HasChildren[A <: UiElement]: this: A => var children: Seq[UiElement] + override def flat: Seq[UiElement] = Seq(this) ++ children.flatMap(_.flat) + def withChildren(cn: UiElement*): A = children = cn this @@ -28,6 +31,8 @@ object UiElement: children = children ++ e this + def copyNoChildren: A + trait HasEventHandler: def defaultEventHandler: EventHandler diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 54be2d48..0ff6f264 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -43,7 +43,8 @@ case class ButtonGroup( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[ButtonGroup] + with HasChildren[ButtonGroup]: + override def copyNoChildren: ButtonGroup = copy(children = Nil) /** https://chakra-ui.com/docs/components/box */ @@ -58,7 +59,8 @@ case class Box( @volatile var as: Option[String] = None, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[Box] + with HasChildren[Box]: + override def copyNoChildren: Box = copy(children = Nil) /** https://chakra-ui.com/docs/components/stack */ @@ -69,7 +71,8 @@ case class HStack( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[HStack] + with HasChildren[HStack]: + override def copyNoChildren: HStack = copy(children = Nil) case class VStack( key: String = Keys.nextKey, @volatile var spacing: Option[String] = None, @@ -77,7 +80,8 @@ case class VStack( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[VStack] + with HasChildren[VStack]: + override def copyNoChildren: VStack = copy(children = Nil) case class SimpleGrid( key: String = Keys.nextKey, @@ -88,7 +92,8 @@ case class SimpleGrid( @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[SimpleGrid] + with HasChildren[SimpleGrid]: + override def copyNoChildren: SimpleGrid = copy(children = Nil) /** https://chakra-ui.com/docs/components/editable */ @@ -104,6 +109,7 @@ case class Editable( with OnChangeEventHandler.CanHandleOnChangeEvent[Editable]: if value == "" then value = defaultValue override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def copyNoChildren: Editable = copy(children = Nil) case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement @@ -117,7 +123,8 @@ case class FormControl( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[FormControl] + with HasChildren[FormControl]: + override def copyNoChildren: FormControl = copy(children = Nil) /** https://chakra-ui.com/docs/components/form-control */ @@ -127,7 +134,8 @@ case class FormLabel( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[FormLabel] + with HasChildren[FormLabel]: + override def copyNoChildren: FormLabel = copy(children = Nil) /** https://chakra-ui.com/docs/components/form-control */ @@ -137,7 +145,8 @@ case class FormHelperText( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[FormHelperText] + with HasChildren[FormHelperText]: + override def copyNoChildren: FormHelperText = copy(children = Nil) /** https://chakra-ui.com/docs/components/input */ @@ -160,7 +169,8 @@ case class InputGroup( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[InputGroup] + with HasChildren[InputGroup]: + override def copyNoChildren: InputGroup = copy(children = Nil) case class InputLeftAddon( key: String = Keys.nextKey, @@ -168,7 +178,8 @@ case class InputLeftAddon( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[InputLeftAddon] + with HasChildren[InputLeftAddon]: + override def copyNoChildren: InputLeftAddon = copy(children = Nil) case class InputRightAddon( key: String = Keys.nextKey, @@ -176,7 +187,8 @@ case class InputRightAddon( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[InputRightAddon] + with HasChildren[InputRightAddon]: + override def copyNoChildren: InputRightAddon = copy(children = Nil) /** https://chakra-ui.com/docs/components/checkbox */ @@ -216,6 +228,8 @@ case class RadioGroup( override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def copyNoChildren: RadioGroup = copy(children = Nil) + case class Center( key: String = Keys.nextKey, @volatile var text: String = "", @@ -226,7 +240,8 @@ case class Center( @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Center] + with HasChildren[Center]: + override def copyNoChildren: Center = copy(children = Nil) case class Circle( key: String = Keys.nextKey, @@ -238,7 +253,8 @@ case class Circle( @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Circle] + with HasChildren[Circle]: + override def copyNoChildren: Circle = copy(children = Nil) case class Square( key: String = Keys.nextKey, @@ -250,7 +266,8 @@ case class Square( @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Square] + with HasChildren[Square]: + override def copyNoChildren: Square = copy(children = Nil) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -937,6 +954,8 @@ case class Select( with OnChangeEventHandler.CanHandleOnChangeEvent[Select]: override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def copyNoChildren: Select = copy(children = Nil) + case class Option_( key: String = Keys.nextKey, value: String, @@ -965,6 +984,8 @@ case class TableContainer(key: String = Keys.nextKey, @volatile var children: Se for b <- tableBodies do b.withChildren(newTrs: _*) this + override def copyNoChildren: TableContainer = copy(children = Nil) + case class Table( key: String = Keys.nextKey, @volatile var variant: String = "simple", @@ -973,23 +994,28 @@ case class Table( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[Table] + with HasChildren[Table]: + override def copyNoChildren: Table = copy(children = Nil) case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement - with HasChildren[Thead] + with HasChildren[Thead]: + override def copyNoChildren: Thead = copy(children = Nil) case class Tbody(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement - with HasChildren[Tbody] + with HasChildren[Tbody]: + override def copyNoChildren: Tbody = copy(children = Nil) case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement - with HasChildren[Tfoot] + with HasChildren[Tfoot]: + override def copyNoChildren: Tfoot = copy(children = Nil) case class Tr( key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Tr] + with HasChildren[Tr]: + override def copyNoChildren: Tr = copy(children = Nil) case class Th( key: String = Keys.nextKey, @volatile var text: String = "", @@ -997,7 +1023,8 @@ case class Th( @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Th] + with HasChildren[Th]: + override def copyNoChildren: Th = copy(children = Nil) case class Td( key: String = Keys.nextKey, @volatile var text: String = "", @@ -1005,13 +1032,15 @@ case class Td( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[Td] + with HasChildren[Td]: + override def copyNoChildren: Td = copy(children = Nil) /** https://chakra-ui.com/docs/components/menu/usage */ case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement - with HasChildren[Menu] + with HasChildren[Menu]: + override def copyNoChildren: Menu = copy(children = Nil) case class MenuButton( key: String = Keys.nextKey, @volatile var text: String = "", @@ -1020,10 +1049,12 @@ case class MenuButton( @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement - with HasChildren[MenuButton] + with HasChildren[MenuButton]: + override def copyNoChildren: MenuButton = copy(children = Nil) case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement - with HasChildren[MenuList] + with HasChildren[MenuList]: + override def copyNoChildren: MenuList = copy(children = Nil) case class MenuItem( key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @@ -1031,7 +1062,8 @@ case class MenuItem( @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[MenuItem] - with OnClickEventHandler.CanHandleOnClickEvent[MenuItem] + with OnClickEventHandler.CanHandleOnClickEvent[MenuItem]: + override def copyNoChildren: MenuItem = copy(children = Nil) case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement @@ -1044,7 +1076,8 @@ case class Badge( @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement - with HasChildren[Badge] + with HasChildren[Badge]: + override def copyNoChildren: Badge = copy(children = Nil) /** https://chakra-ui.com/docs/components/image/usage * diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala new file mode 100644 index 00000000..6649a687 --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala @@ -0,0 +1,35 @@ +package org.terminal21.client.json + +import org.terminal21.client.components.UiElement +import org.terminal21.client.components.UiElement.HasChildren + +case class ServerJson( + rootKeys: Seq[String], + elements: Map[String, UiElement], + keyTree: Map[String, Seq[String]] +) + +object ServerJson: + def from(elements: Seq[UiElement]): ServerJson = + val flat = elements.flatMap(_.flat) + ServerJson( + elements.map(_.key), + flat + .map: el => + ( + el.key, + el match + case e: HasChildren[_] => e.copyNoChildren + case e => e + ) + .toMap, + flat + .map: e => + ( + e.key, + e match + case e: HasChildren[_] => e.children.map(_.key) + case _ => Nil + ) + .toMap + ) From d721cb1c5c5adf36030d7f0e7fa2a5e0686c4ad9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 00:28:59 +0000 Subject: [PATCH 02/40] - --- .../src/main/scala/org/terminal21/client/ConnectedSession.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 93a54fcb..900e2223 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -93,7 +93,6 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se def render(): Unit = val j = toJson - println(s"Sending: ${j.noSpaces}") sessionsService.setSessionJsonState(session, j.noSpaces) def allElements: Seq[UiElement] = synchronized(elements) From a30906035cbd9a9bef3cd729e7652ba65c02a8bb Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 13:40:14 +0000 Subject: [PATCH 03/40] - --- build.sbt | 2 +- .../terminal21/server/json/WsResponse.scala | 4 +-- .../server/model/SessionState.scala | 7 ++-- .../service/ServerSessionsService.scala | 6 ++-- .../server/ui/SessionsWebSocket.scala | 2 +- .../org/terminal21/ui/std/ServerJson.scala | 12 +++++++ .../terminal21/ui/std/SessionsService.scala | 2 +- .../terminal21/client/ConnectedSession.scala | 32 +++++++++++++---- .../terminal21/client/json/ServerJson.scala | 35 ------------------- 9 files changed, 50 insertions(+), 52 deletions(-) create mode 100644 terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala delete mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala diff --git a/build.sbt b/build.sbt index f9bf9122..fe86f287 100644 --- a/build.sbt +++ b/build.sbt @@ -100,7 +100,7 @@ lazy val `terminal21-server` = project lazy val `terminal21-ui-std-exports` = project .settings( commonSettings, - libraryDependencies ++= Seq(ScalaTest), + libraryDependencies ++= Seq(ScalaTest) ++ Circe, // make sure exportedArtifact points to the full artifact name of the receiver. buildInfoKeys := Seq[BuildInfoKey](organization, name, version, scalaVersion, "exportedArtifact" -> "none"), buildInfoPackage := "org.terminal21.ui.std" diff --git a/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala b/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala index c10797c1..7214d2d8 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala @@ -1,10 +1,10 @@ package org.terminal21.server.json -import io.circe.Json import org.terminal21.model.Session +import org.terminal21.ui.std.ServerJson sealed trait WsResponse case class SessionsWsResponse(sessions: Seq[Session]) extends WsResponse -case class StateWsResponse(session: Session, sessionState: Json) extends WsResponse +case class StateWsResponse(session: Session, sessionState: ServerJson) extends WsResponse diff --git a/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala b/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala index ac308494..f74a44f1 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala @@ -2,10 +2,11 @@ package org.terminal21.server.model import org.terminal21.model.CommandEvent import org.terminal21.server.utils.NotificationRegistry +import org.terminal21.ui.std.ServerJson case class SessionState( - json: String, + json: ServerJson, eventsNotificationRegistry: NotificationRegistry[CommandEvent] ): - def withNewState(newJson: String): SessionState = copy(json = newJson) - def close: SessionState = copy(eventsNotificationRegistry = new NotificationRegistry[CommandEvent]) + def withNewState(newJson: ServerJson): SessionState = copy(json = newJson) + def close: SessionState = copy(eventsNotificationRegistry = new NotificationRegistry[CommandEvent]) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala index f3ae206a..06568f1d 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala @@ -5,7 +5,7 @@ import org.terminal21.model.* import org.terminal21.server.json.UiEvent import org.terminal21.server.model.SessionState import org.terminal21.server.utils.{ListenerFunction, NotificationRegistry} -import org.terminal21.ui.std.SessionsService +import org.terminal21.ui.std.{ServerJson, SessionsService} import java.util.UUID @@ -38,7 +38,7 @@ class ServerSessionsService extends SessionsService: val s = Session(id, name, UUID.randomUUID().toString, true) logger.info(s"Creating session $s") sessions.keys.toList.foreach(s => if s.id == id then sessions.remove(s)) - val state = SessionState("""{ "elements" : [] }""", new NotificationRegistry) + val state = SessionState(ServerJson.Empty, new NotificationRegistry) sessions += s -> state sessionChangeNotificationRegistry.notifyAll(allSessions) s @@ -49,7 +49,7 @@ class ServerSessionsService extends SessionsService: sessionStateChangeNotificationRegistry.add(f) for (session, state) <- sessions do f(session, state) - override def setSessionJsonState(session: Session, newStateJson: String): Unit = + override def setSessionJsonState(session: Session, newStateJson: ServerJson): Unit = val oldV = sessions(session) val newV = oldV.withNewState(newStateJson) sessions += session -> newV diff --git a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala index 60cc5e93..bb2fb9f8 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala @@ -26,7 +26,7 @@ class SessionsWebSocket(sessionsService: ServerSessionsService) extends WsListen sendSessionState(wsSession, session, sessionState) private def sendSessionState(wsSession: WsSession, session: Session, sessionState: SessionState): Unit = - val response = StateWsResponse(session.hideSecret, sessionState.json.asJson).asJson.noSpaces + val response = StateWsResponse(session.hideSecret, sessionState.json).asJson.noSpaces logger.info(s"$wsSession: Sending session state response $response") wsSession.send(response, true) diff --git a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala new file mode 100644 index 00000000..7ea60a3e --- /dev/null +++ b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala @@ -0,0 +1,12 @@ +package org.terminal21.ui.std + +import io.circe.Json + +case class ServerJson( + rootKeys: Seq[String], + elements: Map[String, Json], + keyTree: Map[String, Seq[String]] +) + +object ServerJson: + val Empty = ServerJson(Nil, Map.empty, Map.empty) diff --git a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala index c724ddfc..b7b9b345 100644 --- a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala +++ b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala @@ -8,4 +8,4 @@ trait SessionsService: def createSession(id: String, name: String): Session def terminateSession(session: Session): Unit - def setSessionJsonState(session: Session, state: String): Unit + def setSessionJsonState(session: Session, state: ServerJson): Unit diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 900e2223..f73bdb5b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -5,11 +5,10 @@ import io.circe.generic.auto.* import io.circe.syntax.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement -import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} +import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} import org.terminal21.client.components.UiElementEncoding -import org.terminal21.client.json.ServerJson import org.terminal21.model.* -import org.terminal21.ui.std.SessionsService +import org.terminal21.ui.std.{ServerJson, SessionsService} import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.{CountDownLatch, TimeUnit} @@ -93,10 +92,31 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se def render(): Unit = val j = toJson - sessionsService.setSessionJsonState(session, j.noSpaces) + sessionsService.setSessionJsonState(session, j) def allElements: Seq[UiElement] = synchronized(elements) - private def toJson: Json = + private def toJson = import encoding.given - ServerJson.from(allElements).asJson.deepDropNullValues + val flat = elements.flatMap(_.flat) + ServerJson( + elements.map(_.key), + flat + .map: el => + ( + el.key, + el match + case e: HasChildren[_] => encoding.uiElementEncoder(e.copyNoChildren) + case e => encoding.uiElementEncoder(e) + ) + .toMap, + flat + .map: e => + ( + e.key, + e match + case e: HasChildren[_] => e.children.map(_.key) + case _ => Nil + ) + .toMap + ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala deleted file mode 100644 index 6649a687..00000000 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/json/ServerJson.scala +++ /dev/null @@ -1,35 +0,0 @@ -package org.terminal21.client.json - -import org.terminal21.client.components.UiElement -import org.terminal21.client.components.UiElement.HasChildren - -case class ServerJson( - rootKeys: Seq[String], - elements: Map[String, UiElement], - keyTree: Map[String, Seq[String]] -) - -object ServerJson: - def from(elements: Seq[UiElement]): ServerJson = - val flat = elements.flatMap(_.flat) - ServerJson( - elements.map(_.key), - flat - .map: el => - ( - el.key, - el match - case e: HasChildren[_] => e.copyNoChildren - case e => e - ) - .toMap, - flat - .map: e => - ( - e.key, - e match - case e: HasChildren[_] => e.children.map(_.key) - case _ => Nil - ) - .toMap - ) From 596f1b04a1e7db04a08836b1c6b1e37edfd7e354 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 14:03:43 +0000 Subject: [PATCH 04/40] - --- .../client/ConnectedSessionMock.scala | 9 ++++++-- .../client/ConnectedSessionTest.scala | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala index ca667af7..6323b0c7 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala @@ -6,6 +6,11 @@ import org.terminal21.model.CommonModelBuilders.session import org.terminal21.ui.std.SessionsService object ConnectedSessionMock: - def newConnectedSessionMock: ConnectedSession = + val encoding = new UiElementEncoding(Seq(StdElementEncoding)) + val encoder = ConnectedSessionMock.encoding.uiElementEncoder + + def newConnectedSessionAndSessionServiceMock: (SessionsService, ConnectedSession) = val sessionsService = mock(classOf[SessionsService]) - new ConnectedSession(session(), new UiElementEncoding(Seq(StdElementEncoding)), "test", sessionsService, () => ()) + (sessionsService, new ConnectedSession(session(), encoding, "test", sessionsService, () => ())) + + def newConnectedSessionMock: ConnectedSession = newConnectedSessionAndSessionServiceMock._2 diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 42706244..547de78a 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -1,11 +1,17 @@ package org.terminal21.client +import org.mockito.Mockito +import org.mockito.Mockito.verify import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* +import org.terminal21.client.ConnectedSessionMock.encoder +import org.terminal21.client.components.{Paragraph, Span} import org.terminal21.client.components.chakra.Editable import org.terminal21.model.OnChange +import org.terminal21.ui.std.ServerJson class ConnectedSessionTest extends AnyFunSuiteLike: + test("default event handlers are invoked before user handlers"): given connectedSession: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock val editable = Editable() @@ -14,3 +20,19 @@ class ConnectedSessionTest extends AnyFunSuiteLike: connectedSession.add(editable) connectedSession.fireEvent(OnChange(editable.key, "new value")) + + test("to server json"): + val (sessionService, connectedSession) = ConnectedSessionMock.newConnectedSessionAndSessionServiceMock + + val p1 = Paragraph(text = "p1") + val span1 = Span(text = "span1") + connectedSession.add(p1.withChildren(span1)) + connectedSession.render() + verify(sessionService).setSessionJsonState( + connectedSession.session, + ServerJson( + Seq(p1.key), + Map(p1.key -> encoder(p1.copyNoChildren), span1.key -> encoder(span1)), + Map(p1.key -> Seq(span1.key), span1.key -> Nil) + ) + ) From 4f99a5a9e94cdc0e91c8c028db92146cd5f0a9d5 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 16:06:28 +0000 Subject: [PATCH 05/40] - --- .../src/main/scala/tests/chakra/Overlay.scala | 8 ++--- .../terminal21/server/json/WsResponse.scala | 2 ++ .../service/ServerSessionsService.scala | 15 +++++--- .../server/ui/SessionsWebSocket.scala | 12 +++++-- .../org/terminal21/ui/std/ServerJson.scala | 8 ++++- .../terminal21/ui/std/SessionsService.scala | 1 + .../terminal21/ui/std/ServerJsonTest.scala | 17 ++++++++++ .../client/ClientEventsWsListener.scala | 3 +- .../terminal21/client/ConnectedSession.scala | 34 ++++++++++++------- .../client/components/UiElement.scala | 5 +++ .../client/ConnectedSessionTest.scala | 16 +++++++++ 11 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/ServerJsonTest.scala diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala b/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala index 75e672cb..ccddf740 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala @@ -19,20 +19,20 @@ object Overlay: MenuItem(text = "Download menu-download") .onClick: () => box1.text = "'Download' clicked" - session.render() + box1.renderChanges() , MenuItem(text = "Copy").onClick: () => box1.text = "'Copy' clicked" - session.render() + box1.renderChanges() , MenuItem(text = "Paste").onClick: () => box1.text = "'Paste' clicked" - session.render() + box1.renderChanges() , MenuDivider(), MenuItem(text = "Exit").onClick: () => box1.text = "'Exit' clicked" - session.render() + box1.renderChanges() ) ), box1 diff --git a/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala b/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala index 7214d2d8..e6be4591 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/json/WsResponse.scala @@ -8,3 +8,5 @@ sealed trait WsResponse case class SessionsWsResponse(sessions: Seq[Session]) extends WsResponse case class StateWsResponse(session: Session, sessionState: ServerJson) extends WsResponse + +case class StateChangeWsResponse(session: Session, sessionStateChange: ServerJson) extends WsResponse diff --git a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala index 06568f1d..13873dfe 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala @@ -15,7 +15,7 @@ class ServerSessionsService extends SessionsService: private val sessions = collection.concurrent.TrieMap.empty[Session, SessionState] private val sessionChangeNotificationRegistry = new NotificationRegistry[Seq[Session]] - private val sessionStateChangeNotificationRegistry = new NotificationRegistry[(Session, SessionState)] + private val sessionStateChangeNotificationRegistry = new NotificationRegistry[(Session, SessionState, Option[ServerJson])] def sessionById(sessionId: String): Session = sessions.keys.find(_.id == sessionId).getOrElse(throw new IllegalArgumentException(s"Invalid session id = $sessionId")) @@ -45,17 +45,24 @@ class ServerSessionsService extends SessionsService: def allSessions: Seq[Session] = sessions.keySet.toList - def notifyMeWhenSessionChanges(f: ListenerFunction[(Session, SessionState)]): Unit = + def notifyMeWhenSessionChanges(f: ListenerFunction[(Session, SessionState, Option[ServerJson])]): Unit = sessionStateChangeNotificationRegistry.add(f) - for (session, state) <- sessions do f(session, state) + for (session, state) <- sessions do f(session, state, None) override def setSessionJsonState(session: Session, newStateJson: ServerJson): Unit = val oldV = sessions(session) val newV = oldV.withNewState(newStateJson) sessions += session -> newV - sessionStateChangeNotificationRegistry.notifyAll((session, newV)) + sessionStateChangeNotificationRegistry.notifyAll((session, newV, None)) logger.info(s"Session $session new state $newStateJson") + override def changeSessionJsonState(session: Session, change: ServerJson): Unit = + val oldV = sessions(session) + val newV = oldV.withNewState(oldV.json.include(change)) + sessions += session -> newV + sessionStateChangeNotificationRegistry.notifyAll((session, newV, Some(change))) + logger.info(s"Session $session change $change") + def addEvent(event: UiEvent): Unit = val e = event match case org.terminal21.server.json.OnClick(_, key) => OnClick(key) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala index bb2fb9f8..7338a6d1 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala @@ -9,6 +9,7 @@ import org.terminal21.model.Session import org.terminal21.server.json.* import org.terminal21.server.model.SessionState import org.terminal21.server.service.ServerSessionsService +import org.terminal21.ui.std.ServerJson import org.terminal21.utils.ErrorLogger // websocket: https://helidon.io/docs/v4/#/se/websocket @@ -21,15 +22,22 @@ class SessionsWebSocket(sessionsService: ServerSessionsService) extends WsListen WsSessionOps.returnTrueIfSessionIsNotClosed: sendSessions(wsSession, allSessions) - sessionsService.notifyMeWhenSessionChanges: (session, sessionState) => + sessionsService.notifyMeWhenSessionChanges: (session, sessionState, changeOpt) => WsSessionOps.returnTrueIfSessionIsNotClosed: - sendSessionState(wsSession, session, sessionState) + changeOpt match + case None => sendSessionState(wsSession, session, sessionState) + case Some(change) => sendSessionStateChange(wsSession, session, change) private def sendSessionState(wsSession: WsSession, session: Session, sessionState: SessionState): Unit = val response = StateWsResponse(session.hideSecret, sessionState.json).asJson.noSpaces logger.info(s"$wsSession: Sending session state response $response") wsSession.send(response, true) + private def sendSessionStateChange(wsSession: WsSession, session: Session, change: ServerJson): Unit = + val response = StateChangeWsResponse(session.hideSecret, change).asJson.noSpaces + logger.info(s"$wsSession: Sending session change state response $response") + wsSession.send(response, true) + private def sendSessions(wsSession: WsSession, allSessions: Seq[Session]): Unit = val sessions = allSessions.map(_.hideSecret).sortBy(_.name) val json = SessionsWsResponse(sessions).asJson.noSpaces diff --git a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala index 7ea60a3e..5ff4371b 100644 --- a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala +++ b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/ServerJson.scala @@ -6,7 +6,13 @@ case class ServerJson( rootKeys: Seq[String], elements: Map[String, Json], keyTree: Map[String, Seq[String]] -) +): + def include(j: ServerJson): ServerJson = + ServerJson( + rootKeys, + elements ++ j.elements, + keyTree ++ j.keyTree + ) object ServerJson: val Empty = ServerJson(Nil, Map.empty, Map.empty) diff --git a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala index b7b9b345..8809d7da 100644 --- a/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala +++ b/terminal21-ui-std-exports/src/main/scala/org/terminal21/ui/std/SessionsService.scala @@ -9,3 +9,4 @@ trait SessionsService: def terminateSession(session: Session): Unit def setSessionJsonState(session: Session, state: ServerJson): Unit + def changeSessionJsonState(session: Session, state: ServerJson): Unit diff --git a/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/ServerJsonTest.scala b/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/ServerJsonTest.scala new file mode 100644 index 00000000..4a2f7dbf --- /dev/null +++ b/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/ServerJsonTest.scala @@ -0,0 +1,17 @@ +package org.terminal21.ui.std + +import io.circe.Json +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers.* + +class ServerJsonTest extends AnyFunSuiteLike: + test("include"): + val j1 = ServerJson(Seq("k1"), Map("k1" -> Json.fromInt(1), "k2" -> Json.fromInt(2)), Map("k1" -> Seq("k2", "k3"))) + val j2 = ServerJson(Nil, Map("k2" -> Json.fromInt(3)), Map("k2" -> Seq("k4"))) + j1.include(j2) should be( + ServerJson( + Seq("k1"), + Map("k1" -> Json.fromInt(1), "k2" -> Json.fromInt(3)), + Map("k1" -> Seq("k2", "k3"), "k2" -> Seq("k4")) + ) + ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ClientEventsWsListener.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ClientEventsWsListener.scala index ae26915c..3753d9f2 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ClientEventsWsListener.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ClientEventsWsListener.scala @@ -34,7 +34,8 @@ class ClientEventsWsListener(wsClient: WsClient, session: ConnectedSession, exec logger.error(s"An invalid json was received as an event. error = $e") case Right(event) => executor.submit: - session.fireEvent(event) + try session.fireEvent(event) + catch case t: Throwable => logger.error("An error occurred while an event was fired", t) def close(): Unit = eventsListener.close() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index f73bdb5b..2137c036 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -2,11 +2,9 @@ package org.terminal21.client import io.circe.* import io.circe.generic.auto.* -import io.circe.syntax.* import org.slf4j.LoggerFactory -import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} -import org.terminal21.client.components.UiElementEncoding +import org.terminal21.client.components.{UiElement, UiElementEncoding} import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} @@ -15,21 +13,25 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): - private val logger = LoggerFactory.getLogger(getClass) - private var elements = List.empty[UiElement] + private val logger = LoggerFactory.getLogger(getClass) + private var elements = List.empty[UiElement] + private var containedKeys = Set.empty[String] def uiUrl: String = serverUrl + "/ui" def clear(): Unit = synchronized: elements = Nil + eventHandlers.clear() + containedKeys = Set.empty - def add(es: UiElement*): Unit = - val withEvents = allDeep(es).collect: - case h: HasEventHandler => h + def add(es: UiElement*): Unit = synchronized: + for e <- es do if containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Key ${e.key} already added. Component: $e") + val all = allDeep(es) + containedKeys = containedKeys ++ all.map(_.key) + val withEvents = all.collect: + case h: HasEventHandler => h for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) - - synchronized: - elements = elements ::: es.toList + elements = elements ::: es.toList private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] @@ -94,10 +96,16 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se val j = toJson sessionsService.setSessionJsonState(session, j) + def renderChanges(e: UiElement): Unit = + if !containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Element $e is not added to the session") + val j = toJson(Seq(e)) + sessionsService.changeSessionJsonState(session, j) + def allElements: Seq[UiElement] = synchronized(elements) - private def toJson = - import encoding.given + private def toJson: ServerJson = toJson(allElements) + + private def toJson(elements: Seq[UiElement]): ServerJson = val flat = elements.flatMap(_.flat) ServerJson( elements.map(_.key), diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index a6fb0d28..2e406a85 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -10,6 +10,11 @@ trait UiElement: session.add(this) session.render() + /** Renders any changes for this element and it's children (if any). The element must previously have been added to the session. + */ + def renderChanges()(using session: ConnectedSession): Unit = + session.renderChanges(this) + object UiElement: def allDeep(elements: Seq[UiElement]): Seq[UiElement] = elements ++ elements diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 547de78a..038e7f2b 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -36,3 +36,19 @@ class ConnectedSessionTest extends AnyFunSuiteLike: Map(p1.key -> Seq(span1.key), span1.key -> Nil) ) ) + + test("renderChanges"): + val (sessionService, connectedSession) = ConnectedSessionMock.newConnectedSessionAndSessionServiceMock + + val p1 = Paragraph(text = "p1") + val span1 = Span(text = "span1") + connectedSession.add(p1) + connectedSession.renderChanges(p1.withChildren(span1)) + verify(sessionService).changeSessionJsonState( + connectedSession.session, + ServerJson( + Seq(p1.key), + Map(p1.key -> encoder(p1.copyNoChildren), span1.key -> encoder(span1)), + Map(p1.key -> Seq(span1.key), span1.key -> Nil) + ) + ) From 4bcc9aaa9764ba68790f5f46fa6c14ade1a461ed Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 16:33:59 +0000 Subject: [PATCH 06/40] - --- build.sbt | 2 +- example-scripts/bouncing-ball.sc | 6 ++---- example-scripts/csv-editor.sc | 2 +- example-scripts/csv-viewer.sc | 3 ++- example-scripts/hello-world.sc | 2 +- example-scripts/mathjax.sc | 2 +- example-scripts/nivo-bar-chart.sc | 2 +- example-scripts/nivo-line-chart.sc | 2 +- example-scripts/postit.sc | 2 +- example-scripts/project.scala | 7 ++++--- example-scripts/server.sc | 2 +- example-scripts/textedit.sc | 7 ++++--- example-spark/project.scala | 8 ++++---- 13 files changed, 24 insertions(+), 23 deletions(-) diff --git a/build.sbt b/build.sbt index fe86f287..3972ac88 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ */ val scala3Version = "3.3.1" -ThisBuild / version := "0.11" +ThisBuild / version := "0.12" ThisBuild / organization := "io.github.kostaskougios" name := "rest-api" ThisBuild / scalaVersion := scala3Version diff --git a/example-scripts/bouncing-ball.sc b/example-scripts/bouncing-ball.sc index 108e3364..41bb1c81 100755 --- a/example-scripts/bouncing-ball.sc +++ b/example-scripts/bouncing-ball.sc @@ -20,13 +20,11 @@ Sessions.withNewSession("bouncing-ball", "C64 bouncing ball"): session => // Files under ~/.terminal21/web will be served under /web . Please place a ball.png file under ~/.terminal21/web/images on the box where the server runs. val ball = Image(src = "/web/images/ball.png") - Seq( - ball - ).render() + ball.render() @tailrec def animateBall(x: Int, y: Int, dx: Int, dy: Int): Unit = ball.style = Map("position" -> "fixed", "left" -> (x + "px"), "top" -> (y + "px")) - session.render() + ball.renderChanges() Thread.sleep(1000 / 120) val newDx = if x < 0 || x > 600 then -dx else dx val newDy = if y < 0 || y > 500 then -dy else dy diff --git a/example-scripts/csv-editor.sc b/example-scripts/csv-editor.sc index 7e7c3deb..f3333ec0 100755 --- a/example-scripts/csv-editor.sc +++ b/example-scripts/csv-editor.sc @@ -81,7 +81,7 @@ Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session .onChange: newValue => csvMap((x, y)) = newValue status.text = s"($x,$y) value changed to $newValue" - session.render() + status.renderChanges() Seq( TableContainer().withChildren( diff --git a/example-scripts/csv-viewer.sc b/example-scripts/csv-viewer.sc index 2ecd5a2d..8c861968 100755 --- a/example-scripts/csv-viewer.sc +++ b/example-scripts/csv-viewer.sc @@ -48,4 +48,5 @@ Sessions.withNewSession(s"csv-viewer-$fileName", s"CsvView: $fileName"): session ) ).render() println(s"Now open ${session.uiUrl} to view the UI.") - session.waitTillUserClosesSession() + // since this is a read-only UI, we can exit the app but leave the session open on the UI + session.leaveSessionOpenAfterExiting() diff --git a/example-scripts/hello-world.sc b/example-scripts/hello-world.sc index b6b7aa34..fe37bf5d 100755 --- a/example-scripts/hello-world.sc +++ b/example-scripts/hello-world.sc @@ -14,4 +14,4 @@ Sessions.withNewSession("hello-world", "Hello World Example"): session => Seq( Paragraph(text = "Hello World!") ).render() - session.waitTillUserClosesSession() + session.leaveSessionOpenAfterExiting() diff --git a/example-scripts/mathjax.sc b/example-scripts/mathjax.sc index 902ff2e2..30c2ea72 100755 --- a/example-scripts/mathjax.sc +++ b/example-scripts/mathjax.sc @@ -23,4 +23,4 @@ Sessions.withNewSession("mathjax", "MathJax Example", MathJaxLib /* note we need |""".stripMargin ) ).render() - session.waitTillUserClosesSession() + session.leaveSessionOpenAfterExiting() diff --git a/example-scripts/nivo-bar-chart.sc b/example-scripts/nivo-bar-chart.sc index 47ee7837..3ac982ad 100755 --- a/example-scripts/nivo-bar-chart.sc +++ b/example-scripts/nivo-bar-chart.sc @@ -47,7 +47,7 @@ Sessions.withNewSession("nivo-bar-chart", "Nivo Bar Chart", NivoLib /* note we n while !session.isClosed do Thread.sleep(2000) chart.data = createRandomData - session.render() + chart.renderChanges() session.waitTillUserClosesSession() diff --git a/example-scripts/nivo-line-chart.sc b/example-scripts/nivo-line-chart.sc index 664f5698..a70beab6 100755 --- a/example-scripts/nivo-line-chart.sc +++ b/example-scripts/nivo-line-chart.sc @@ -28,7 +28,7 @@ Sessions.withNewSession("nivo-line-chart", "Nivo Line Chart", NivoLib /* note we while !session.isClosed do Thread.sleep(2000) chart.data = createRandomData - session.render() + chart.renderChanges() session.waitTillUserClosesSession() diff --git a/example-scripts/postit.sc b/example-scripts/postit.sc index cca81ef6..3061c723 100755 --- a/example-scripts/postit.sc +++ b/example-scripts/postit.sc @@ -29,7 +29,7 @@ Sessions.withNewSession("postit", "Post-It"): session => ) ) // always render after adding/modifying something - session.render() + messages.renderChanges() Seq( Paragraph(text = "Please type your note below and click 'Post It' to post it so that everyone can view it."), diff --git a/example-scripts/project.scala b/example-scripts/project.scala index dec52579..b8bcff17 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -1,7 +1,8 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-ui-std:0.11 -//> using dep io.github.kostaskougios::terminal21-nivo:0.11 -//> using dep io.github.kostaskougios::terminal21-mathjax:0.11 +//> using dep io.github.kostaskougios::terminal21-ui-std:0.12 +//> using dep io.github.kostaskougios::terminal21-nivo:0.12 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.12 + //> using dep commons-io:commons-io:2.15.1 diff --git a/example-scripts/server.sc b/example-scripts/server.sc index b93acb6e..b08e946f 100755 --- a/example-scripts/server.sc +++ b/example-scripts/server.sc @@ -2,7 +2,7 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-server:0.11 +//> using dep io.github.kostaskougios::terminal21-server:0.12 import org.terminal21.server.Terminal21Server diff --git a/example-scripts/textedit.sc b/example-scripts/textedit.sc index dc894124..04730c28 100755 --- a/example-scripts/textedit.sc +++ b/example-scripts/textedit.sc @@ -44,7 +44,7 @@ Sessions.withNewSession(s"textedit-$fileName", s"Edit: $fileName"): session => // when the user changes the textarea, we get the new text and we can compare it with the loaded value. editor.onChange: newValue => modified.text = if newValue != contents then "*" else "" - session.render() + modified.renderChanges() Seq( HStack().withChildren( @@ -57,11 +57,12 @@ Sessions.withNewSession(s"textedit-$fileName", s"Edit: $fileName"): session => // we'll display a "Saved" badge for 1 second. status.text = "Saved" modified.text = "" - session.render() + status.renderChanges() + modified.renderChanges() // each event handler runs on a new fibler, it is ok to sleep here Thread.sleep(1000) status.text = "" - session.render() + status.renderChanges() , MenuItem(text = "Exit") .onClick: () => diff --git a/example-spark/project.scala b/example-spark/project.scala index f921305a..bdf43ace 100644 --- a/example-spark/project.scala +++ b/example-spark/project.scala @@ -8,10 +8,10 @@ //> using javaOpt -Dlogback.configurationFile=file:etc/logback.xml // terminal21 dependencies -//> using dep io.github.kostaskougios::terminal21-ui-std:0.11 -//> using dep io.github.kostaskougios::terminal21-spark:0.11 -//> using dep io.github.kostaskougios::terminal21-nivo:0.11 -//> using dep io.github.kostaskougios::terminal21-mathjax:0.11 +//> using dep io.github.kostaskougios::terminal21-ui-std:0.12 +//> using dep io.github.kostaskougios::terminal21-spark:0.12 +//> using dep io.github.kostaskougios::terminal21-nivo:0.12 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.12 //> using dep ch.qos.logback:logback-classic:1.4.14 From 2aaeb361b1d3953e4259590307fc4a399e66b9cf Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 17:59:51 +0000 Subject: [PATCH 07/40] - --- .../terminal21/sparklib/endtoend/SparkBasics.scala | 1 - .../org/terminal21/client/ConnectedSession.scala | 12 ++++++------ .../client/components/StdUiCalculation.scala | 6 +++--- .../terminal21/client/components/UiComponent.scala | 2 +- .../client/components/UiElementEncoding.scala | 2 +- .../client/components/chakra/QuickTable.scala | 2 ++ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 1b391b16..c2a0a6e8 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -58,7 +58,6 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val sourceFileChart = sortedSourceFilesDS.visualize("Biggest Code Files", chart): results => val data = results.take(10).map(cf => Datum(StringUtils.substringBeforeLast(cf.name, ".scala"), cf.numOfLines)).toList chart.data = Seq(Serie("Scala", data = data)) - session.render() Seq( codeFilesCalculation, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 2137c036..1350765e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -4,7 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} -import org.terminal21.client.components.{UiElement, UiElementEncoding} +import org.terminal21.client.components.{UiComponent, UiElement, UiElementEncoding} import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} @@ -96,9 +96,9 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se val j = toJson sessionsService.setSessionJsonState(session, j) - def renderChanges(e: UiElement): Unit = - if !containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Element $e is not added to the session") - val j = toJson(Seq(e)) + def renderChanges(es: UiElement*): Unit = + for e <- es do if !containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Element $es is not added to the session") + val j = toJson(es) sessionsService.changeSessionJsonState(session, j) def allElements: Seq[UiElement] = synchronized(elements) @@ -114,8 +114,8 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se ( el.key, el match - case e: HasChildren[_] => encoding.uiElementEncoder(e.copyNoChildren) - case e => encoding.uiElementEncoder(e) + case e: HasChildren[_] => encoding.uiElementEncoder(e.copyNoChildren).deepDropNullValues + case e => encoding.uiElementEncoder(e).deepDropNullValues ) .toMap, flat diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index c46bec4e..c95f5ff8 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -42,7 +42,7 @@ trait StdUiCalculation[OUT]( badge.text = s"Error: ${t.getMessage}" badge.colorScheme = Some("red") recalc.isDisabled = None - session.render() + session.renderChanges(badge, dataUi, recalc) super.onError(t) override protected def whenResultsNotReady(): Unit = @@ -50,7 +50,7 @@ trait StdUiCalculation[OUT]( badge.colorScheme = Some("purple") recalc.isDisabled = Some(true) dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") - session.render() + session.renderChanges(badge, dataUi, recalc) super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = @@ -58,4 +58,4 @@ trait StdUiCalculation[OUT]( badge.colorScheme = None recalc.isDisabled = Some(false) dataUi.style = dataUi.style - "filter" - session.render() + session.renderChanges(badge, dataUi, recalc) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index 925f98c9..400d6eca 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -5,4 +5,4 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ trait UiComponent extends UiElement with HasChildren[UiComponent]: - override def copyNoChildren: UiComponent = ??? + override def copyNoChildren: UiComponent = this diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index 1d312f19..5a0a27d9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -32,5 +32,5 @@ object StdElementEncoding extends ComponentLib: case std: StdElement => std.asJson.mapObject(o => o.add("type", "Std".asJson)) case c: ChakraElement => c.asJson.mapObject(o => o.add("type", "Chakra".asJson)) case c: UiComponent => - val b: ChakraElement = Box(children = c.children) + val b: ChakraElement = Box(key = c.key, text = "") b.asJson.mapObject(o => o.add("type", "Chakra".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index fb7dd571..0a1d870c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -39,3 +39,5 @@ case class QuickTable( def caption(text: String): QuickTable = table.addChildren(TableCaption(text = text)) this + + override def copyNoChildren: UiComponent = copy() From 36b004629ba0dd9a846e4ac0f4bfa99595fd8fc6 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 18:02:25 +0000 Subject: [PATCH 08/40] - --- .../org/terminal21/client/components/chakra/QuickTable.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 0a1d870c..484bd30e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -23,7 +23,7 @@ case class QuickTable( def headers(headers: String*): QuickTable = headersElements(headers.map(h => Text(text = h)): _*) def headersElements(headers: UiElement*): QuickTable = - head.children = headers.map(h => Th(children = Seq(h))) + head.children = Seq(Tr(children = headers.map(h => Th(children = Seq(h))))) this def rows(data: Seq[Seq[Any]]): QuickTable = rowsElements(data.map(_.map(c => Text(text = c.toString)))) From f9db1cda8cc4eac7b9878d0a2d8be2dc998aeeca Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 19:48:28 +0000 Subject: [PATCH 09/40] - --- Readme.md | 5 +++++ .../main/scala/org/terminal21/client/ConnectedSession.scala | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 6a483b66..a8b96e09 100644 --- a/Readme.md +++ b/Readme.md @@ -170,6 +170,11 @@ Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi # Changelog +## Version 0.12 + +- ability to render only 1 element and it's children +- refactoring internal way of storing elements on lib - server - ui + ## Version 0.11 - Quick* classes to simplify UI creation diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 1350765e..58cf5968 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -4,7 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} -import org.terminal21.client.components.{UiComponent, UiElement, UiElementEncoding} +import org.terminal21.client.components.{UiElement, UiElementEncoding} import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} From 48b36e775d94e7f08cf67fcab0f005fbe6b34630 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 20:16:29 +0000 Subject: [PATCH 10/40] - --- .../terminal21/client/ConnectedSession.scala | 38 +++++------------- .../client/internal/ElementTree.scala | 39 +++++++++++++++++++ 2 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 58cf5968..6af8a9a8 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -5,6 +5,7 @@ import io.circe.generic.auto.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} import org.terminal21.client.components.{UiElement, UiElementEncoding} +import org.terminal21.client.internal.ElementTree import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} @@ -13,35 +14,16 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): - private val logger = LoggerFactory.getLogger(getClass) - private var elements = List.empty[UiElement] - private var containedKeys = Set.empty[String] + private val logger = LoggerFactory.getLogger(getClass) + private val elementTree = new ElementTree def uiUrl: String = serverUrl + "/ui" - def clear(): Unit = synchronized: - elements = Nil - eventHandlers.clear() - containedKeys = Set.empty + def clear(): Unit = elementTree.clear() - def add(es: UiElement*): Unit = synchronized: - for e <- es do if containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Key ${e.key} already added. Component: $e") + def add(es: UiElement*): Unit = + elementTree.add(es) - val all = allDeep(es) - containedKeys = containedKeys ++ all.map(_.key) - val withEvents = all.collect: - case h: HasEventHandler => h - for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) - elements = elements ::: es.toList - - private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] - - private def addEventHandlerAtTheTop(key: String, handler: EventHandler): Unit = - val handlers = eventHandlers.getOrElse(key, Nil) - eventHandlers += key -> (handler :: handlers) - - def addEventHandler(key: String, handler: EventHandler): Unit = - val handlers = eventHandlers.getOrElse(key, Nil) - eventHandlers += key -> (handlers :+ handler) + def addEventHandler(key: String, handler: EventHandler): Unit = elementTree.addEventHandler(key, handler) private val exitLatch = new CountDownLatch(1) @@ -81,7 +63,7 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se exitLatch.countDown() onCloseHandler() case _ => - eventHandlers.get(event.key) match + elementTree.getEventHandler(event.key) match case Some(handlers) => for handler <- handlers do (event, handler) match @@ -97,11 +79,11 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se sessionsService.setSessionJsonState(session, j) def renderChanges(es: UiElement*): Unit = - for e <- es do if !containedKeys.contains(e.key) then throw new IllegalArgumentException(s"Element $es is not added to the session") + for e <- es do if !elementTree.containsKey(e.key) then throw new IllegalArgumentException(s"Element $es is not added to the session") val j = toJson(es) sessionsService.changeSessionJsonState(session, j) - def allElements: Seq[UiElement] = synchronized(elements) + def allElements: Seq[UiElement] = elementTree.allElements private def toJson: ServerJson = toJson(allElements) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala new file mode 100644 index 00000000..e6915313 --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala @@ -0,0 +1,39 @@ +package org.terminal21.client.internal + +import org.terminal21.client.EventHandler +import org.terminal21.client.components.UiElement +import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} + +class ElementTree: + private var elements = List.empty[UiElement] + private var containedKeys = Set.empty[String] + private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] + + def allElements: Seq[UiElement] = synchronized(elements) + + def containsKey(key: String): Boolean = containedKeys.contains(key) + + def add(es: Seq[UiElement]): Unit = synchronized: + for e <- es do if containsKey(e.key) then throw new IllegalArgumentException(s"Key ${e.key} already added. Component: $e") + val all = allDeep(es) + containedKeys = containedKeys ++ all.map(_.key) + val withEvents = all.collect: + case h: HasEventHandler => h + + for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) + elements = elements ::: es.toList + + def addEventHandler(key: String, handler: EventHandler): Unit = + val handlers = eventHandlers.getOrElse(key, Nil) + eventHandlers += key -> (handlers :+ handler) + + def getEventHandler(key: String): Option[List[EventHandler]] = eventHandlers.get(key) + + private def addEventHandlerAtTheTop(key: String, handler: EventHandler): Unit = + val handlers = eventHandlers.getOrElse(key, Nil) + eventHandlers += key -> (handler :: handlers) + + def clear(): Unit = synchronized: + elements = Nil + eventHandlers.clear() + containedKeys = Set.empty From bb5c0fa29bd61c95eb975e9f5bbaad8b37598517 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 16 Jan 2024 20:23:50 +0000 Subject: [PATCH 11/40] - --- .../client/internal/ElementTreeTest.scala | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala new file mode 100644 index 00000000..67e7a919 --- /dev/null +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala @@ -0,0 +1,42 @@ +package org.terminal21.client.internal + +import org.scalatest.funsuite.AnyFunSuiteLike +import org.terminal21.client.components.Paragraph +import org.terminal21.client.components.chakra.Box +import org.scalatest.matchers.should.Matchers.* + +class ElementTreeTest extends AnyFunSuiteLike: + test("all elements"): + val t = new ElementTree + val box = Box() + val p = Paragraph() + t.add(Seq(box, p)) + t.allElements should be(Seq(box, p)) + + test("all elements after clear"): + val t = new ElementTree + val box = Box() + val p = Paragraph() + t.add(Seq(box, p)) + t.clear() + t.allElements should be(Nil) + + test("containsKey"): + val t = new ElementTree + val box = Box() + t.add(Seq(box)) + t.containsKey(box.key) should be(true) + + test("containsKey deep"): + val t = new ElementTree + val p = Paragraph() + val box = Box().withChildren(p) + t.add(Seq(box)) + t.containsKey(p.key) should be(true) + + test("containsKey after clear"): + val t = new ElementTree + val box = Box() + t.add(Seq(box)) + t.clear() + t.containsKey(box.key) should be(false) From c3eaa936ab492e21dd8d5079e06445ad487fee19 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 12:15:28 +0000 Subject: [PATCH 12/40] - --- .../main/scala/tests/ChakraComponents.scala | 3 +- .../main/scala/tests/MathJaxComponents.scala | 2 +- .../src/main/scala/tests/StdComponents.scala | 3 +- .../main/scala/tests/chakra/Editables.scala | 4 +- .../src/main/scala/tests/chakra/Forms.scala | 24 +++++------ example-scripts/csv-editor.sc | 16 +++---- example-scripts/hello-world.sc | 1 + example-scripts/nivo-bar-chart.sc | 3 +- example-scripts/nivo-line-chart.sc | 1 + example-scripts/postit.sc | 1 + example-scripts/textedit.sc | 1 + .../terminal21/client/ConnectedSession.scala | 25 +++++------ .../client/components/UiElement.scala | 3 +- .../client/components/UiElementEncoding.scala | 1 + .../client/components/extensions.scala | 6 ++- .../components/{ => std}/StdElement.scala | 9 ++-- ...{ElementTree.scala => EventHandlers.scala} | 15 +------ .../client/ConnectedSessionTest.scala | 8 ++-- .../client/internal/ElementTreeTest.scala | 42 ------------------- 19 files changed, 61 insertions(+), 107 deletions(-) rename terminal21-ui-std/src/main/scala/org/terminal21/client/components/{ => std}/StdElement.scala (87%) rename terminal21-ui-std/src/main/scala/org/terminal21/client/internal/{ElementTree.scala => EventHandlers.scala} (63%) delete mode 100644 terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala diff --git a/end-to-end-tests/src/main/scala/tests/ChakraComponents.scala b/end-to-end-tests/src/main/scala/tests/ChakraComponents.scala index 5940e29e..6bdebe53 100644 --- a/end-to-end-tests/src/main/scala/tests/ChakraComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/ChakraComponents.scala @@ -2,7 +2,8 @@ package tests import org.terminal21.client.* import org.terminal21.client.components.chakra.* -import org.terminal21.client.components.{Paragraph, render} +import org.terminal21.client.components.render +import org.terminal21.client.components.std.Paragraph import tests.chakra.* import java.util.concurrent.CountDownLatch diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala index c34fe57d..731e437a 100644 --- a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -19,4 +19,4 @@ import org.terminal21.client.components.mathjax.* style = Map("backgroundColor" -> "gray") ) ).render() - session.waitTillUserClosesSession() + session.leaveSessionOpenAfterExiting() diff --git a/end-to-end-tests/src/main/scala/tests/StdComponents.scala b/end-to-end-tests/src/main/scala/tests/StdComponents.scala index 7f633f7b..6a952b6e 100644 --- a/end-to-end-tests/src/main/scala/tests/StdComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/StdComponents.scala @@ -2,12 +2,13 @@ package tests import org.terminal21.client.* import org.terminal21.client.components.* +import org.terminal21.client.components.std.{Em, Header1, Input, NewLine, Paragraph, Span} @main def stdComponents(): Unit = Sessions.withNewSession("std-components", "Std Components"): session => given ConnectedSession = session - val input = Input(defaultValue = "Please enter your name") + val input = Input(defaultValue = Some("Please enter your name")) val output = Paragraph(text = "This will reflect what you type in the input") input.onChange: newValue => output.text = newValue diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala index c9dd0a5a..fe5652ff 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala @@ -16,7 +16,7 @@ object Editables: editable1.onChange: newValue => status.text = s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}" - session.render() + status.renderChanges() val editable2 = Editable(defaultValue = "For longer maybe-editable texts\nUse an EditableTextarea\nIt uses a textarea control.").withChildren( EditablePreview(), @@ -24,7 +24,7 @@ object Editables: ) editable2.onChange: newValue => status.text = s"editable2 newValue = $newValue, verify editable2.value = ${editable2.value}" - session.render() + status.renderChanges() Seq( commonBox(text = "Editables"), diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala index 21e6ce13..3f2f4889 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala @@ -1,7 +1,7 @@ package tests.chakra import org.terminal21.client.ConnectedSession -import org.terminal21.client.components.UiElement +import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* import tests.chakra.Common.* @@ -17,12 +17,12 @@ object Forms: email.onChange: newValue => status.text = s"email input new value = $newValue, verify email.value = ${email.value}" if newValue.contains("@") then emailRightAddOn.children = Seq(okIcon) else emailRightAddOn.children = Seq(notOkIcon) - session.render() + Seq(status, emailRightAddOn).renderChanges() val description = Textarea(placeholder = "Please enter a few things about you") description.onChange: newValue => status.text = s"description input new value = $newValue, verify description.value = ${description.value}" - session.render() + status.renderChanges() val select1 = Select(placeholder = "Please choose").withChildren( Option_(text = "Male", value = "male"), @@ -31,7 +31,7 @@ object Forms: select1.onChange: newValue => status.text = s"select1 input new value = $newValue, verify select1.value = ${select1.value}" - session.render() + status.renderChanges() val select2 = Select(value = "1", bg = Some("tomato"), color = Some("black"), borderColor = Some("yellow")).withChildren( Option_(text = "First", value = "1"), @@ -42,24 +42,24 @@ object Forms: val dob = Input(`type` = "datetime-local") dob.onChange: newValue => status.text = s"dob = $newValue , verify dob.value = ${dob.value}" - session.render() + status.renderChanges() val color = Input(`type` = "color") color.onChange: newValue => status.text = s"color = $newValue , verify color.value = ${color.value}" - session.render() + status.renderChanges() val checkbox2 = Checkbox(text = "Check 2", defaultChecked = true) checkbox2.onChange: newValue => status.text = s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.checked}" - session.render() + status.renderChanges() val checkbox1 = Checkbox(text = "Check 1") checkbox1.onChange: newValue => checkbox2.isDisabled = newValue status.text = s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.checked}" - session.render() + Seq(status, checkbox2).renderChanges() val switch1 = Switch(text = "Switch 1") val switch2 = Switch(text = "Switch 2") @@ -67,7 +67,7 @@ object Forms: switch1.onChange: newValue => switch2.isDisabled = newValue status.text = s"switch1 checked is $newValue , verify switch1.checked = ${switch1.checked}" - session.render() + Seq(status, switch2).renderChanges() val radioGroup = RadioGroup(defaultValue = "2").withChildren( HStack().withChildren( @@ -79,7 +79,7 @@ object Forms: radioGroup.onChange: newValue => status.text = s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.value}" - session.render() + status.renderChanges() Seq( commonBox(text = "Forms"), @@ -139,12 +139,12 @@ object Forms: .onClick: () => status.text = s"Saved clicked. Email = ${email.value}, password = ${password.value}, dob = ${dob.value}, check1 = ${checkbox1.checked}, check2 = ${checkbox2.checked}, radio = ${radioGroup.value}" - session.render() + status.renderChanges() , Button(text = "Cancel") .onClick: () => status.text = "Cancel clicked" - session.render() + status.renderChanges() ), radioGroup, status diff --git a/example-scripts/csv-editor.sc b/example-scripts/csv-editor.sc index f3333ec0..6683f70c 100755 --- a/example-scripts/csv-editor.sc +++ b/example-scripts/csv-editor.sc @@ -6,6 +6,8 @@ // always import these import org.terminal21.client.* + +import java.util.concurrent.atomic.AtomicBoolean // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala import org.terminal21.client.components.* // use the chakra components for menus, forms etc, https://chakra-ui.com/docs/components @@ -14,7 +16,6 @@ import org.terminal21.client.components.chakra.* import org.apache.commons.io.FileUtils import java.io.File -import java.util.concurrent.CountDownLatch import scala.collection.concurrent.TrieMap if args.length != 1 then @@ -55,7 +56,7 @@ def saveCsvMap() = FileUtils.writeStringToFile(file, s, "UTF-8") // this will be countDown to 0 when we have to exit -val exitLatch = new CountDownLatch(1) +val exitFlag = new AtomicBoolean(false) Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session => given ConnectedSession = session @@ -64,13 +65,13 @@ Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session val saveAndExit = Button(text = "Save & Exit") .onClick: () => saveCsvMap() - session.add(Paragraph(text = "Csv file saved, exiting.")) - session.render() - exitLatch.countDown() + status.text="Csv file saved, exiting." + status.renderChanges() + exitFlag.set(true) val exit = Button(text = "Exit Without Saving") .onClick: () => - exitLatch.countDown() + exitFlag.set(true) def newEditable(x: Int, y: Int, value: String) = Editable(defaultValue = value) @@ -107,4 +108,5 @@ Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session println(s"Now open ${session.uiUrl} to view the UI") // wait for one of the save/exit buttons to be pressed. - exitLatch.await() + session.waitTillUserClosesSessionOr(exitFlag.get()) + diff --git a/example-scripts/hello-world.sc b/example-scripts/hello-world.sc index fe37bf5d..74857e29 100755 --- a/example-scripts/hello-world.sc +++ b/example-scripts/hello-world.sc @@ -7,6 +7,7 @@ import org.terminal21.client.* // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala import org.terminal21.client.components.* +import org.terminal21.client.components.std.* Sessions.withNewSession("hello-world", "Hello World Example"): session => given ConnectedSession = session diff --git a/example-scripts/nivo-bar-chart.sc b/example-scripts/nivo-bar-chart.sc index 3ac982ad..7f7fbad5 100755 --- a/example-scripts/nivo-bar-chart.sc +++ b/example-scripts/nivo-bar-chart.sc @@ -3,6 +3,7 @@ import org.terminal21.client.* import org.terminal21.client.fiberExecutor import org.terminal21.client.components.* +import org.terminal21.client.components.std.* import org.terminal21.client.components.nivo.* import scala.util.Random @@ -39,7 +40,7 @@ Sessions.withNewSession("nivo-bar-chart", "Nivo Bar Chart", NivoLib /* note we n ) Seq( - Paragraph(text = "Means of transportation for various countries", style = Map("margin" -> 20)), + Paragraph(text = "Various foods.", style = Map("margin" -> 20)), chart ).render() diff --git a/example-scripts/nivo-line-chart.sc b/example-scripts/nivo-line-chart.sc index a70beab6..1c88eec0 100755 --- a/example-scripts/nivo-line-chart.sc +++ b/example-scripts/nivo-line-chart.sc @@ -3,6 +3,7 @@ import org.terminal21.client.* import org.terminal21.client.fiberExecutor import org.terminal21.client.components.* +import org.terminal21.client.components.std.* import org.terminal21.client.components.nivo.* import scala.util.Random diff --git a/example-scripts/postit.sc b/example-scripts/postit.sc index 3061c723..e5ad1123 100755 --- a/example-scripts/postit.sc +++ b/example-scripts/postit.sc @@ -7,6 +7,7 @@ import org.terminal21.client.* // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala import org.terminal21.client.components.* +import org.terminal21.client.components.std.* // use the chakra components for menus, forms etc, https://chakra-ui.com/docs/components // The scala case classes : https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala import org.terminal21.client.components.chakra.* diff --git a/example-scripts/textedit.sc b/example-scripts/textedit.sc index 04730c28..d9fc50f5 100755 --- a/example-scripts/textedit.sc +++ b/example-scripts/textedit.sc @@ -12,6 +12,7 @@ import java.io.File import org.terminal21.client.* // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala import org.terminal21.client.components.* +import org.terminal21.client.components.std.* // use the chakra components for menus, forms etc, https://chakra-ui.com/docs/components // The scala case classes : https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala import org.terminal21.client.components.chakra.* diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 6af8a9a8..22849d8c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -5,7 +5,7 @@ import io.circe.generic.auto.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} import org.terminal21.client.components.{UiElement, UiElementEncoding} -import org.terminal21.client.internal.ElementTree +import org.terminal21.client.internal.EventHandlers import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} @@ -15,15 +15,14 @@ import scala.annotation.tailrec class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): private val logger = LoggerFactory.getLogger(getClass) - private val elementTree = new ElementTree + private val handlers = new EventHandlers def uiUrl: String = serverUrl + "/ui" - def clear(): Unit = elementTree.clear() + def clear(): Unit = + render() + handlers.clear() - def add(es: UiElement*): Unit = - elementTree.add(es) - - def addEventHandler(key: String, handler: EventHandler): Unit = elementTree.addEventHandler(key, handler) + def addEventHandler(key: String, handler: EventHandler): Unit = handlers.addEventHandler(key, handler) private val exitLatch = new CountDownLatch(1) @@ -63,7 +62,7 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se exitLatch.countDown() onCloseHandler() case _ => - elementTree.getEventHandler(event.key) match + handlers.getEventHandler(event.key) match case Some(handlers) => for handler <- handlers do (event, handler) match @@ -74,19 +73,15 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se case None => logger.warn(s"There is no event handler for event $event") - def render(): Unit = - val j = toJson + def render(es: UiElement*): Unit = + handlers.registerEventHandlers(es) + val j = toJson(es) sessionsService.setSessionJsonState(session, j) def renderChanges(es: UiElement*): Unit = - for e <- es do if !elementTree.containsKey(e.key) then throw new IllegalArgumentException(s"Element $es is not added to the session") val j = toJson(es) sessionsService.changeSessionJsonState(session, j) - def allElements: Seq[UiElement] = elementTree.allElements - - private def toJson: ServerJson = toJson(allElements) - private def toJson(elements: Seq[UiElement]): ServerJson = val flat = elements.flatMap(_.flat) ServerJson( diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 2e406a85..649fd51f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -7,8 +7,7 @@ trait UiElement: def flat: Seq[UiElement] = Seq(this) def render()(using session: ConnectedSession): Unit = - session.add(this) - session.render() + session.render(this) /** Renders any changes for this element and it's children (if any). The element must previously have been added to the session. */ diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index 5a0a27d9..e07ee465 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -4,6 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* import org.terminal21.client.components.chakra.{Box, ChakraElement} +import org.terminal21.client.components.std.StdElement class UiElementEncoding(libs: Seq[ComponentLib]): given uiElementEncoder: Encoder[UiElement] = diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/extensions.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/extensions.scala index 72585946..c0159356 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/extensions.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/extensions.scala @@ -4,5 +4,7 @@ import org.terminal21.client.ConnectedSession extension (s: Seq[UiElement]) def render()(using session: ConnectedSession): Unit = - session.add(s: _*) - session.render() + session.render(s: _*) + + def renderChanges()(using session: ConnectedSession): Unit = + session.renderChanges(s: _*) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala similarity index 87% rename from terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala rename to terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala index 93fcdada..94638d68 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala @@ -1,6 +1,7 @@ -package org.terminal21.client.components +package org.terminal21.client.components.std import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} +import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{ConnectedSession, OnChangeEventHandler} sealed trait StdElement extends UiElement with HasStyle @@ -23,12 +24,12 @@ case class Paragraph( case class Input( key: String = Keys.nextKey, `type`: String = "text", - defaultValue: String = "", + defaultValue: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, - @volatile var value: String = "" + @volatile var value: Option[String] = None ) extends StdElement with HasEventHandler: - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def defaultEventHandler: OnChangeEventHandler = newValue => value = Some(newValue) def onChange(h: OnChangeEventHandler)(using session: ConnectedSession): Input = session.addEventHandler(key, h) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala similarity index 63% rename from terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala rename to terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala index e6915313..08024d0f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/ElementTree.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala @@ -4,24 +4,15 @@ import org.terminal21.client.EventHandler import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} -class ElementTree: - private var elements = List.empty[UiElement] - private var containedKeys = Set.empty[String] +class EventHandlers: private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] - def allElements: Seq[UiElement] = synchronized(elements) - - def containsKey(key: String): Boolean = containedKeys.contains(key) - - def add(es: Seq[UiElement]): Unit = synchronized: - for e <- es do if containsKey(e.key) then throw new IllegalArgumentException(s"Key ${e.key} already added. Component: $e") + def registerEventHandlers(es: Seq[UiElement]): Unit = synchronized: val all = allDeep(es) - containedKeys = containedKeys ++ all.map(_.key) val withEvents = all.collect: case h: HasEventHandler => h for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) - elements = elements ::: es.toList def addEventHandler(key: String, handler: EventHandler): Unit = val handlers = eventHandlers.getOrElse(key, Nil) @@ -34,6 +25,4 @@ class ElementTree: eventHandlers += key -> (handler :: handlers) def clear(): Unit = synchronized: - elements = Nil eventHandlers.clear() - containedKeys = Set.empty diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 038e7f2b..2dd40b88 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -5,8 +5,8 @@ import org.mockito.Mockito.verify import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* import org.terminal21.client.ConnectedSessionMock.encoder -import org.terminal21.client.components.{Paragraph, Span} import org.terminal21.client.components.chakra.Editable +import org.terminal21.client.components.std.{Paragraph, Span} import org.terminal21.model.OnChange import org.terminal21.ui.std.ServerJson @@ -18,7 +18,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: editable.onChange: newValue => editable.value should be(newValue) - connectedSession.add(editable) + connectedSession.render(editable) connectedSession.fireEvent(OnChange(editable.key, "new value")) test("to server json"): @@ -26,7 +26,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: val p1 = Paragraph(text = "p1") val span1 = Span(text = "span1") - connectedSession.add(p1.withChildren(span1)) + connectedSession.render(p1.withChildren(span1)) connectedSession.render() verify(sessionService).setSessionJsonState( connectedSession.session, @@ -42,7 +42,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: val p1 = Paragraph(text = "p1") val span1 = Span(text = "span1") - connectedSession.add(p1) + connectedSession.render(p1) connectedSession.renderChanges(p1.withChildren(span1)) verify(sessionService).changeSessionJsonState( connectedSession.session, diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala deleted file mode 100644 index 67e7a919..00000000 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/internal/ElementTreeTest.scala +++ /dev/null @@ -1,42 +0,0 @@ -package org.terminal21.client.internal - -import org.scalatest.funsuite.AnyFunSuiteLike -import org.terminal21.client.components.Paragraph -import org.terminal21.client.components.chakra.Box -import org.scalatest.matchers.should.Matchers.* - -class ElementTreeTest extends AnyFunSuiteLike: - test("all elements"): - val t = new ElementTree - val box = Box() - val p = Paragraph() - t.add(Seq(box, p)) - t.allElements should be(Seq(box, p)) - - test("all elements after clear"): - val t = new ElementTree - val box = Box() - val p = Paragraph() - t.add(Seq(box, p)) - t.clear() - t.allElements should be(Nil) - - test("containsKey"): - val t = new ElementTree - val box = Box() - t.add(Seq(box)) - t.containsKey(box.key) should be(true) - - test("containsKey deep"): - val t = new ElementTree - val p = Paragraph() - val box = Box().withChildren(p) - t.add(Seq(box)) - t.containsKey(p.key) should be(true) - - test("containsKey after clear"): - val t = new ElementTree - val box = Box() - t.add(Seq(box)) - t.clear() - t.containsKey(box.key) should be(false) From 703aa5a69ea9b43d11f51f120d718e604d1456a2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 13:49:49 +0000 Subject: [PATCH 13/40] - --- build.sbt | 14 ++- .../tastyextractor/BetterErrors.scala | 6 + .../tastyextractor/StructureExtractor.scala | 114 ++++++++++++++++++ .../tastyextractor/model/EImport.scala | 3 + .../tastyextractor/model/EMethod.scala | 3 + .../tastyextractor/model/EPackage.scala | 4 + .../tastyextractor/model/EParam.scala | 3 + .../tastyextractor/model/EType.scala | 8 ++ .../PropertiesExtensionGenerator.scala | 22 ++++ 9 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/BetterErrors.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EImport.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EMethod.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EPackage.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EParam.scala create mode 100644 terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala create mode 100644 terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala diff --git a/build.sbt b/build.sbt index 3972ac88..bd7f8974 100644 --- a/build.sbt +++ b/build.sbt @@ -25,8 +25,9 @@ val FunctionsHelidonClient = "io.github.kostaskougios" %% "helidon-client" val FunctionsHelidonWsClient = "io.github.kostaskougios" %% "helidon-ws-client" % FunctionsVersion val FunctionsFibers = "io.github.kostaskougios" %% "fibers" % FunctionsVersion -val ScalaTest = "org.scalatest" %% "scalatest" % "3.2.15" % Test -val Mockito = "org.mockito" % "mockito-all" % "2.0.2-beta" % Test +val ScalaTest = "org.scalatest" %% "scalatest" % "3.2.15" % Test +val Mockito = "org.mockito" % "mockito-all" % "2.0.2-beta" % Test +val Scala3Tasty = "org.scala-lang" %% "scala3-tasty-inspector" % scala3Version val CirceVersion = "0.14.6" val Circe = Seq( @@ -176,3 +177,12 @@ lazy val `terminal21-mathjax` = project ) ) .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") + +lazy val `terminal21-code-generation` = project + .settings( + commonSettings, + libraryDependencies ++= Seq( + ScalaTest, + Scala3Tasty + ) + ) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/BetterErrors.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/BetterErrors.scala new file mode 100644 index 00000000..2696d725 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/BetterErrors.scala @@ -0,0 +1,6 @@ +package functions.tastyextractor + +object BetterErrors: + def betterError[R](name: String)(f: => R): R = + try f + catch case t: Throwable => throw new IllegalStateException(name, t) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala new file mode 100644 index 00000000..2489125b --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala @@ -0,0 +1,114 @@ +package functions.tastyextractor + +import dotty.tools.dotc.ast.untpd.ImportSelector +import dotty.tools.dotc.core.Symbols.ClassSymbol +import functions.tastyextractor.model.* + +import scala.collection.mutable +import scala.quoted.* +import scala.tasty.inspector.* +import functions.tastyextractor.model.{EImport, EMethod, EPackage, EParam, EType} + +private class StructureExtractorInspector extends Inspector: + val packages = mutable.ListBuffer.empty[EPackage] + + override def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit = + import quotes.reflect.* + + def eTypeOf(tpe: TypeRepr): EType = tpe match + case t: TypeRef => + val name = t.name + val code = t.show + EType.code(name, code) + case at: AppliedType => + val args = at.args.map(eTypeOf) + val tycon = at.tycon + EType(tycon.typeSymbol.name, tpe.show, args, Nil, None, Nil) + case c => + // not sure if this works: + EType(c.typeSymbol.name, tpe.show, Nil, Nil, None, Nil) + + object MethodTraverser extends TreeAccumulator[List[EMethod]]: + def foldTree(existing: List[EMethod], tree: Tree)(owner: Symbol): List[EMethod] = + def paramsCode(param: Any) = + param match + case v: ValDef @unchecked => + val tpe = eTypeOf(v.tpt.tpe) + EParam(v.name, tpe, s"${v.name} : ${v.tpt.show}") + + val r = tree match + case d: DefDef if !d.name.contains("$") && d.name != "" => + BetterErrors.betterError(s"Error while parsing. Owner: $owner method: ${d.show}"): + val m = EMethod(d.name, d.paramss.map(pc => pc.params.map(paramsCode)), eTypeOf(d.returnTpt.tpe), d.symbol.docstring) + List(m) + case _ => + Nil + foldOverTree(existing ++ r, tree)(owner) + end MethodTraverser + + object ValTraverser extends TreeAccumulator[List[EParam]]: + def foldTree(existing: List[EParam], tree: Tree)(owner: Symbol): List[EParam] = + val r = tree match + case v: ValDef => + val tpe = eTypeOf(v.tpt.tpe) + owner match + case cc: ClassSymbol => + List(EParam(v.name, tpe, s"${v.name} : ${v.tpt.show}")) + case _ => Nil + case _ => + Nil + foldOverTree(existing ++ r, tree)(owner) + end ValTraverser + + object TypeTraverser extends TreeAccumulator[List[EType]]: + + def foldTree(existing: List[EType], tree: Tree)(owner: Symbol): List[EType] = + val r = tree match + case c: ClassDef => + val methods = MethodTraverser.foldTree(Nil, c)(owner) + val vals = ValTraverser.foldTree(Nil, c)(owner) + val t = EType(c.name, c.name, Nil, vals, c.symbol.docstring, methods) + List(t) + case _ => + Nil + foldOverTree(existing ++ r, tree)(owner) + end TypeTraverser + + object ImportTraverser extends TreeAccumulator[List[EImport]]: + def foldTree(existing: List[EImport], tree: Tree)(owner: Symbol): List[EImport] = + val r = tree match + case Import(module, paths) => + for case ImportSelector(ident, _, _) <- paths + yield EImport(module.show + "." + ident.name.toString) + + case _ => Nil + foldOverTree(existing ++ r, tree)(owner) + end ImportTraverser + + object PackageTraverser extends TreeAccumulator[List[EPackage]]: + def foldTree(existing: List[EPackage], tree: Tree)(owner: Symbol): List[EPackage] = + val r = tree match + case p: PackageClause => + val types = TypeTraverser.foldTree(Nil, p)(owner) + val imports = ImportTraverser.foldTree(Nil, p)(owner) + val t = EPackage(p.pid.show, imports, types) + List(t) + case _ => + Nil + foldOverTree(existing ++ r, tree)(owner) + end PackageTraverser + + for tasty <- tastys do + val tree = tasty.ast + packages ++= PackageTraverser.foldTree(Nil, tree)(tree.symbol) + +/** Converts tasty files to an easier to digest domain model + */ +class StructureExtractor: + def fromFiles(files: List[String]): Seq[EPackage] = + val inspector = new StructureExtractorInspector + TastyInspector.inspectAllTastyFiles(files, Nil, Nil)(inspector) + inspector.packages.toSeq + +object StructureExtractor: + def apply() = new StructureExtractor diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EImport.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EImport.scala new file mode 100644 index 00000000..c0913ba8 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EImport.scala @@ -0,0 +1,3 @@ +package functions.tastyextractor.model + +case class EImport(fullName: String) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EMethod.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EMethod.scala new file mode 100644 index 00000000..6a9cf310 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EMethod.scala @@ -0,0 +1,3 @@ +package functions.tastyextractor.model + +case class EMethod(name: String, paramss: List[List[EParam]], returnType: EType, scalaDocs: Option[String]) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EPackage.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EPackage.scala new file mode 100644 index 00000000..0f280962 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EPackage.scala @@ -0,0 +1,4 @@ +package functions.tastyextractor.model + +case class EPackage(name: String, imports: Seq[EImport], types: Seq[EType]): + def toPath: String = name.replace('.', '/') diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EParam.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EParam.scala new file mode 100644 index 00000000..49f7be5e --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EParam.scala @@ -0,0 +1,3 @@ +package functions.tastyextractor.model + +case class EParam(name: String, `type`: EType, code: String) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala new file mode 100644 index 00000000..d15bb378 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala @@ -0,0 +1,8 @@ +package functions.tastyextractor.model + +case class EType(name: String, code: String, typeArgs: Seq[EType], vals: List[EParam], scalaDocs: Option[String], methods: Seq[EMethod]): + def isUnit: Boolean = code == "scala.Unit" + def simplifiedCode: String = if typeArgs.isEmpty then name else s"$name[${typeArgs.map(_.simplifiedCode).mkString(", ")}]" + +object EType: + def code(name: String, code: String) = EType(name, code, Nil, Nil, None, Nil) diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala new file mode 100644 index 00000000..b9852d06 --- /dev/null +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -0,0 +1,22 @@ +package org.terminal21.codegen + +import functions.tastyextractor.StructureExtractor +import org.terminal21.codegen.PropertiesExtensionGenerator.extract + +import java.io.File +object PropertiesExtensionGenerator: + private val e = StructureExtractor() + + def extract(tasty: String) = + val p = e.fromFiles(List(tasty)) + val types = p.flatMap(_.types).filterNot(_.name.contains("$")) + types + +@main def propertiesExtensionGeneratorApp(): Unit = + val targetDir = new File("../terminal21-ui-std/target/") + val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get + val classesDir = new File(scala3Dir, "classes") + val types = extract(classesDir.getAbsolutePath + "/org/terminal21/client/components/std/Paragraph.tasty") + for t <- types do + println(t.name) + println(t.vals.mkString("\n")) From 7818e6325fda8a23499b53653d95f6986c14df44 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 14:11:09 +0000 Subject: [PATCH 14/40] - --- build.sbt | 12 ++++--- .../tastyextractor/model/EType.scala | 5 ++- .../scala/org/terminal21/codegen/Code.scala | 10 ++++++ .../PropertiesExtensionGenerator.scala | 34 ++++++++++++++----- 4 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 terminal21-code-generation/src/main/scala/org/terminal21/codegen/Code.scala diff --git a/build.sbt b/build.sbt index bd7f8974..b4019f88 100644 --- a/build.sbt +++ b/build.sbt @@ -25,9 +25,11 @@ val FunctionsHelidonClient = "io.github.kostaskougios" %% "helidon-client" val FunctionsHelidonWsClient = "io.github.kostaskougios" %% "helidon-ws-client" % FunctionsVersion val FunctionsFibers = "io.github.kostaskougios" %% "fibers" % FunctionsVersion -val ScalaTest = "org.scalatest" %% "scalatest" % "3.2.15" % Test -val Mockito = "org.mockito" % "mockito-all" % "2.0.2-beta" % Test -val Scala3Tasty = "org.scala-lang" %% "scala3-tasty-inspector" % scala3Version +val ScalaTest = "org.scalatest" %% "scalatest" % "3.2.15" % Test +val Mockito = "org.mockito" % "mockito-all" % "2.0.2-beta" % Test +val Scala3Tasty = "org.scala-lang" %% "scala3-tasty-inspector" % scala3Version +val CommonsText = "org.apache.commons" % "commons-text" % "1.10.0" +val CommonsIO = "commons-io" % "commons-io" % "2.11.0" val CirceVersion = "0.14.6" val Circe = Seq( @@ -183,6 +185,8 @@ lazy val `terminal21-code-generation` = project commonSettings, libraryDependencies ++= Seq( ScalaTest, - Scala3Tasty + Scala3Tasty, + CommonsText, + CommonsIO ) ) diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala index d15bb378..9b820658 100644 --- a/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/model/EType.scala @@ -1,7 +1,10 @@ package functions.tastyextractor.model +import org.apache.commons.lang3.StringUtils + case class EType(name: String, code: String, typeArgs: Seq[EType], vals: List[EParam], scalaDocs: Option[String], methods: Seq[EMethod]): - def isUnit: Boolean = code == "scala.Unit" + def isUnit: Boolean = code == "scala.Unit" + def simplifiedCode: String = if typeArgs.isEmpty then name else s"$name[${typeArgs.map(_.simplifiedCode).mkString(", ")}]" object EType: diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/Code.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/Code.scala new file mode 100644 index 00000000..c8b0b5ef --- /dev/null +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/Code.scala @@ -0,0 +1,10 @@ +package org.terminal21.codegen + +import org.apache.commons.io.FileUtils + +import java.io.File + +case class Code(file: String, code: String): + def writeTo(srcRootFolder: String): Unit = + val f = new File(srcRootFolder, file) + FileUtils.writeStringToFile(f, code, "UTF-8") diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index b9852d06..c5049f37 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -1,22 +1,40 @@ package org.terminal21.codegen import functions.tastyextractor.StructureExtractor +import functions.tastyextractor.model.EType import org.terminal21.codegen.PropertiesExtensionGenerator.extract import java.io.File object PropertiesExtensionGenerator: private val e = StructureExtractor() - def extract(tasty: String) = - val p = e.fromFiles(List(tasty)) - val types = p.flatMap(_.types).filterNot(_.name.contains("$")) - types + def extract(tasty: String): Seq[Code] = + val packages = e.fromFiles(List(tasty)) + packages.map: p => + val ext = p.types + .filterNot(_.name.contains("$")) + .map: t => + createExtension(t) + + Code( + "-", + s""" + |package ${p.name} + |${ext.mkString("\n")}""".stripMargin + ) + + def createExtension(t: EType): String = + val methods = t.vals.map: vl => + s"def ${vl.name}(v: ${vl.`type`.simplifiedCode}) = e.copy(${vl.name} = v)" + + s""" + |extension (e: Paragraph) + | ${methods.mkString("\n ")} + |""".stripMargin @main def propertiesExtensionGeneratorApp(): Unit = val targetDir = new File("../terminal21-ui-std/target/") val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get val classesDir = new File(scala3Dir, "classes") - val types = extract(classesDir.getAbsolutePath + "/org/terminal21/client/components/std/Paragraph.tasty") - for t <- types do - println(t.name) - println(t.vals.mkString("\n")) + val codes = extract(classesDir.getAbsolutePath + "/org/terminal21/client/components/std/Paragraph.tasty") + println(codes.mkString("\n")) From 4ff02626a508b71961d575dedaab08f77b9c4d0a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 14:27:21 +0000 Subject: [PATCH 15/40] - --- .../PropertiesExtensionGenerator.scala | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index c5049f37..be366384 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -8,33 +8,37 @@ import java.io.File object PropertiesExtensionGenerator: private val e = StructureExtractor() - def extract(tasty: String): Seq[Code] = - val packages = e.fromFiles(List(tasty)) - packages.map: p => - val ext = p.types + def extract(tastys: List[String]): Code = + val packages = e.fromFiles(tastys) + val ext = packages.map: p => + val extCode = p.types .filterNot(_.name.contains("$")) .map: t => createExtension(t) - Code( - "-", - s""" + extCode.mkString("\n") + + val p = packages.head + Code( + s"${p.name.replace('.', '/')}/extensions.scala", + s""" |package ${p.name} - |${ext.mkString("\n")}""".stripMargin - ) + |${ext.mkString("\n")} + |""".stripMargin + ) def createExtension(t: EType): String = val methods = t.vals.map: vl => s"def ${vl.name}(v: ${vl.`type`.simplifiedCode}) = e.copy(${vl.name} = v)" s""" - |extension (e: Paragraph) + |extension (e: ${t.name}) | ${methods.mkString("\n ")} |""".stripMargin @main def propertiesExtensionGeneratorApp(): Unit = val targetDir = new File("../terminal21-ui-std/target/") val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get - val classesDir = new File(scala3Dir, "classes") - val codes = extract(classesDir.getAbsolutePath + "/org/terminal21/client/components/std/Paragraph.tasty") - println(codes.mkString("\n")) + val classesDir = new File(scala3Dir, "classes/org/terminal21/client/components/std/") + val code = extract(classesDir.listFiles().filter(_.getName.endsWith(".tasty")).filterNot(_.getName.contains("$")).map(_.getAbsolutePath).toList) + println(code) From d2d686a9f5fafe4432ef89ab8c667d16852ce466 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 15:10:10 +0000 Subject: [PATCH 16/40] - --- build.sbt | 3 +++ .../PropertiesExtensionGenerator.scala | 23 +++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index b4019f88..4e079055 100644 --- a/build.sbt +++ b/build.sbt @@ -121,6 +121,9 @@ lazy val `terminal21-client-common` = project lazy val `terminal21-ui-std` = project .settings( commonSettings, + Compile / unmanagedSourceDirectories ++= { + Seq(baseDirectory.value / "src" / "main" / "ui-generated") + }, callerExports := Seq(s"io.github.kostaskougios:terminal21-ui-std-exports_3:${version.value}"), callerJsonSerialization := true, callerHelidonClientTransport := true, diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index be366384..ba95f53b 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -2,7 +2,7 @@ package org.terminal21.codegen import functions.tastyextractor.StructureExtractor import functions.tastyextractor.model.EType -import org.terminal21.codegen.PropertiesExtensionGenerator.extract +import org.terminal21.codegen.PropertiesExtensionGenerator.{extract, generate} import java.io.File object PropertiesExtensionGenerator: @@ -13,6 +13,7 @@ object PropertiesExtensionGenerator: val ext = packages.map: p => val extCode = p.types .filterNot(_.name.contains("$")) + .filterNot(_.vals.isEmpty) .map: t => createExtension(t) @@ -23,22 +24,30 @@ object PropertiesExtensionGenerator: s"${p.name.replace('.', '/')}/extensions.scala", s""" |package ${p.name} + |${p.imports.map(_.fullName).mkString("import ", "\nimport ", "")} |${ext.mkString("\n")} |""".stripMargin ) + def fix(n: String) = n match + case "type" => "`type`" + case _ => n + def createExtension(t: EType): String = val methods = t.vals.map: vl => - s"def ${vl.name}(v: ${vl.`type`.simplifiedCode}) = e.copy(${vl.name} = v)" + s"def ${fix(vl.name)}(v: ${vl.`type`.simplifiedCode}) = e.copy(${fix(vl.name)} = v)" s""" |extension (e: ${t.name}) | ${methods.mkString("\n ")} |""".stripMargin + def generate(moduleDir: File, pckg: String): Unit = + val targetDir = new File(moduleDir, "target") + val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get + val classesDir = new File(scala3Dir, s"classes/${pckg.replace('.', '/')}") + val code = extract(classesDir.listFiles().filter(_.getName.endsWith(".tasty")).filterNot(_.getName.contains("$")).map(_.getAbsolutePath).toList) + code.writeTo(s"${moduleDir.getAbsolutePath}/src/main/ui-generated") + @main def propertiesExtensionGeneratorApp(): Unit = - val targetDir = new File("../terminal21-ui-std/target/") - val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get - val classesDir = new File(scala3Dir, "classes/org/terminal21/client/components/std/") - val code = extract(classesDir.listFiles().filter(_.getName.endsWith(".tasty")).filterNot(_.getName.contains("$")).map(_.getAbsolutePath).toList) - println(code) + generate(new File("../terminal21-ui-std"), "org.terminal21.client.components.std") From 3ba2fdba4c72d4a850211d567a13278dc52c87ce Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 17:25:49 +0000 Subject: [PATCH 17/40] - --- .../src/main/scala/tests/StdComponents.scala | 5 +- .../PropertiesExtensionGenerator.scala | 5 +- .../client/components/mathjax/MathJax.scala | 3 +- .../client/components/nivo/NivoElement.scala | 11 +- .../client/components/nivo/NivoLib.scala | 2 +- .../sparklib/CalculationsExtensions.scala | 2 +- .../calculations/SparkCalculation.scala | 2 +- .../terminal21/client/ConnectedSession.scala | 4 +- .../client/components/StdUiCalculation.scala | 30 +- .../client/components/UiComponent.scala | 4 +- .../client/components/UiElement.scala | 17 +- .../client/components/UiElementEncoding.scala | 12 +- .../components/chakra/ChakraElement.scala | 389 ++++++++++++------ .../client/components/chakra/QuickTable.scala | 54 ++- .../client/components/std/StdElement.scala | 37 +- .../client/ConnectedSessionTest.scala | 4 +- 16 files changed, 349 insertions(+), 232 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/StdComponents.scala b/end-to-end-tests/src/main/scala/tests/StdComponents.scala index 6a952b6e..67c870ea 100644 --- a/end-to-end-tests/src/main/scala/tests/StdComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/StdComponents.scala @@ -2,7 +2,7 @@ package tests import org.terminal21.client.* import org.terminal21.client.components.* -import org.terminal21.client.components.std.{Em, Header1, Input, NewLine, Paragraph, Span} +import org.terminal21.client.components.std.* @main def stdComponents(): Unit = Sessions.withNewSession("std-components", "Std Components"): session => @@ -11,8 +11,7 @@ import org.terminal21.client.components.std.{Em, Header1, Input, NewLine, Paragr val input = Input(defaultValue = Some("Please enter your name")) val output = Paragraph(text = "This will reflect what you type in the input") input.onChange: newValue => - output.text = newValue - session.render() + output.withText(newValue).renderChanges() Seq( Header1(text = "Welcome to the std components demo/test"), diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index ba95f53b..68f80c44 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -24,6 +24,9 @@ object PropertiesExtensionGenerator: s"${p.name.replace('.', '/')}/extensions.scala", s""" |package ${p.name} + | + |// GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT + | |${p.imports.map(_.fullName).mkString("import ", "\nimport ", "")} |${ext.mkString("\n")} |""".stripMargin @@ -35,7 +38,7 @@ object PropertiesExtensionGenerator: def createExtension(t: EType): String = val methods = t.vals.map: vl => - s"def ${fix(vl.name)}(v: ${vl.`type`.simplifiedCode}) = e.copy(${fix(vl.name)} = v)" + s"def with${vl.name.capitalize}(v: ${vl.`type`.simplifiedCode}) = e.copy(${fix(vl.name)} = v)" s""" |extension (e: ${t.name}) diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index 10fad50f..adce7ce2 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -14,4 +14,5 @@ case class MathJax( @volatile var expression: String = """fill in the expression as per https://asciimath.org/""", @volatile var style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib ) extends MathJaxElement - with HasStyle + with HasStyle[MathJax]: + override def style(v: Map[String, Any]): MathJax = copy(style = v) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 7200fc36..210aed7e 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -3,7 +3,8 @@ package org.terminal21.client.components.nivo import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.{Keys, UiElement} -sealed trait NivoElement extends UiElement +sealed trait NEJson extends UiElement +sealed trait NivoElement[A <: UiElement] extends NEJson with HasStyle[A] /** https://nivo.rocks/line/ */ @@ -28,8 +29,8 @@ case class ResponsiveLine( @volatile var pointLabelYOffset: Int = -12, @volatile var useMesh: Boolean = true, @volatile var legends: Seq[Legend] = Nil -) extends NivoElement - with HasStyle +) extends NivoElement[ResponsiveLine]: + override def style(v: Map[String, Any]): ResponsiveLine = copy(style = v) /** https://nivo.rocks/bar/ */ @@ -54,5 +55,5 @@ case class ResponsiveBar( @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), @volatile var legends: Seq[Legend] = Nil, @volatile var ariaLabel: String = "Chart Label" -) extends NivoElement - with HasStyle +) extends NivoElement[ResponsiveBar]: + override def style(v: Map[String, Any]): ResponsiveBar = copy(style = v) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala index d4030b54..a7594003 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala @@ -9,4 +9,4 @@ import io.circe.syntax.* object NivoLib extends ComponentLib: import org.terminal21.client.components.StdElementEncoding.given override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = - case n: NivoElement => n.asJson.mapObject(o => o.add("type", "Nivo".asJson)) + case n: NEJson => n.asJson.mapObject(o => o.add("type", "Nivo".asJson)) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala index 55e63694..718c3917 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala @@ -8,7 +8,7 @@ import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.sparklib.calculations.{ReadWriter, StdUiSparkCalculation} extension [OUT: ReadWriter](ds: OUT) - def visualize(name: String, dataUi: UiElement with HasStyle)( + def visualize(name: String, dataUi: UiElement with HasStyle[_])( toUi: OUT => Unit )(using session: ConnectedSession, diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 10b20321..fc7ed440 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -48,7 +48,7 @@ trait SparkCalculation[OUT: ReadWriter](name: String)(using executor: FiberExecu abstract class StdUiSparkCalculation[OUT: ReadWriter]( val key: String, name: String, - dataUi: UiElement with HasStyle + dataUi: UiElement with HasStyle[_] )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) extends SparkCalculation[OUT](name) with StdUiCalculation[OUT](name, dataUi) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 22849d8c..60659d6d 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -14,7 +14,7 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): - private val logger = LoggerFactory.getLogger(getClass) + private val logger = LoggerFactory.getLogger(getClass) private val handlers = new EventHandlers def uiUrl: String = serverUrl + "/ui" @@ -91,7 +91,7 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se ( el.key, el match - case e: HasChildren[_] => encoding.uiElementEncoder(e.copyNoChildren).deepDropNullValues + case e: HasChildren[_] => encoding.uiElementEncoder(e.withChildren()).deepDropNullValues case e => encoding.uiElementEncoder(e).deepDropNullValues ) .toMap, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index c95f5ff8..af343db9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -14,29 +14,27 @@ import java.util.concurrent.atomic.AtomicBoolean */ trait StdUiCalculation[OUT]( name: String, - dataUi: UiElement with HasStyle + dataUi: UiElement with HasStyle[_] )(using session: ConnectedSession, executor: FiberExecutor) extends Calculation[OUT] with UiComponent: - val badge = Badge() private val running = new AtomicBoolean(false) + val badge = Badge() val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => if running.compareAndSet(false, true) then try reCalculate() finally running.set(false) - val header = Box(bg = "green", p = 4).withChildren( - HStack().withChildren( - Text(text = name), - badge, - recalc + override def rendered: Seq[UiElement] = + val header = Box( + bg = "green", + p = 4, + children = Seq( + HStack(children = Seq(Text(text = name), badge, recalc)) + ) ) - ) - @volatile var children: Seq[UiElement] = Seq( - header, - dataUi - ) + Seq(header, dataUi) override def onError(t: Throwable): Unit = badge.text = s"Error: ${t.getMessage}" @@ -49,13 +47,13 @@ trait StdUiCalculation[OUT]( badge.text = "Calculating" badge.colorScheme = Some("purple") recalc.isDisabled = Some(true) - dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") - session.renderChanges(badge, dataUi, recalc) + val newDataUi = dataUi.style(dataUi.style + ("filter" -> "grayscale(100%)")) + session.renderChanges(badge, newDataUi, recalc) super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" badge.colorScheme = None recalc.isDisabled = Some(false) - dataUi.style = dataUi.style - "filter" - session.renderChanges(badge, dataUi, recalc) + val newDataUi = dataUi.style(dataUi.style - "filter") + session.renderChanges(badge, newDataUi, recalc) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index 400d6eca..82e011ee 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -4,5 +4,5 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ -trait UiComponent extends UiElement with HasChildren[UiComponent]: - override def copyNoChildren: UiComponent = this +trait UiComponent extends UiElement: + def rendered: Seq[UiElement] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 649fd51f..65b0596f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -23,22 +23,15 @@ object UiElement: trait HasChildren[A <: UiElement]: this: A => - var children: Seq[UiElement] + def children: Seq[UiElement] override def flat: Seq[UiElement] = Seq(this) ++ children.flatMap(_.flat) - def withChildren(cn: UiElement*): A = - children = cn - this - - def addChildren(e: UiElement*): A = - children = children ++ e - this - - def copyNoChildren: A + def withChildren(cn: UiElement*): A trait HasEventHandler: def defaultEventHandler: EventHandler - trait HasStyle: - var style: Map[String, Any] + trait HasStyle[A <: UiElement]: + def style: Map[String, Any] + def style(v: Map[String, Any]): A diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index e07ee465..04993b10 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -3,8 +3,8 @@ package org.terminal21.client.components import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* -import org.terminal21.client.components.chakra.{Box, ChakraElement} -import org.terminal21.client.components.std.StdElement +import org.terminal21.client.components.chakra.{Box, CEJson, ChakraElement} +import org.terminal21.client.components.std.{StdEJson, StdElement} class UiElementEncoding(libs: Seq[ComponentLib]): given uiElementEncoder: Encoder[UiElement] = @@ -30,8 +30,8 @@ object StdElementEncoding extends ComponentLib: Json.obj(vs: _*) override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = - case std: StdElement => std.asJson.mapObject(o => o.add("type", "Std".asJson)) - case c: ChakraElement => c.asJson.mapObject(o => o.add("type", "Chakra".asJson)) - case c: UiComponent => - val b: ChakraElement = Box(key = c.key, text = "") + case std: StdEJson => std.asJson.mapObject(o => o.add("type", "Std".asJson)) + case c: CEJson => c.asJson.mapObject(o => o.add("type", "Chakra".asJson)) + case c: UiComponent => + val b: ChakraElement[Box] = Box(key = c.key, text = "") b.asJson.mapObject(o => o.add("type", "Chakra".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 0ff6f264..25b96b22 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -4,11 +4,13 @@ import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} +sealed trait CEJson extends UiElement + /** The chakra-react based components, for a complete (though bit rough) example please see * https://github.com/kostaskougios/terminal21-restapi/blob/main/examples/src/main/scala/tests/ChakraComponents.scala and it's related scala files under * https://github.com/kostaskougios/terminal21-restapi/tree/main/examples/src/main/scala/tests/chakra */ -sealed trait ChakraElement extends UiElement with HasStyle +sealed trait ChakraElement[A <: ChakraElement[A]] extends CEJson with HasStyle[A] /** https://chakra-ui.com/docs/components/button */ @@ -26,8 +28,9 @@ case class Button( @volatile var isLoading: Option[Boolean] = None, @volatile var isAttached: Option[Boolean] = None, @volatile var spacing: Option[String] = None -) extends ChakraElement - with OnClickEventHandler.CanHandleOnClickEvent[Button] +) extends ChakraElement[Button] + with OnClickEventHandler.CanHandleOnClickEvent[Button]: + override def style(v: Map[String, Any]): Button = copy(style = v) /** https://chakra-ui.com/docs/components/button */ @@ -42,9 +45,10 @@ case class ButtonGroup( @volatile var borderColor: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[ButtonGroup] with HasChildren[ButtonGroup]: - override def copyNoChildren: ButtonGroup = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]): ButtonGroup = copy(style = v) /** https://chakra-ui.com/docs/components/box */ @@ -58,9 +62,10 @@ case class Box( @volatile var style: Map[String, Any] = Map.empty, @volatile var as: Option[String] = None, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[Box] with HasChildren[Box]: - override def copyNoChildren: Box = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/stack */ @@ -70,18 +75,21 @@ case class HStack( @volatile var align: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[HStack] with HasChildren[HStack]: - override def copyNoChildren: HStack = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class VStack( key: String = Keys.nextKey, @volatile var spacing: Option[String] = None, @volatile var align: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[VStack] with HasChildren[VStack]: - override def copyNoChildren: VStack = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class SimpleGrid( key: String = Keys.nextKey, @@ -91,9 +99,10 @@ case class SimpleGrid( @volatile var columns: Int = 2, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SimpleGrid] with HasChildren[SimpleGrid]: - override def copyNoChildren: SimpleGrid = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/editable */ @@ -103,17 +112,23 @@ case class Editable( @volatile var value: String = "", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[Editable] with HasEventHandler with HasChildren[Editable] with OnChangeEventHandler.CanHandleOnChangeEvent[Editable]: if value == "" then value = defaultValue override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def copyNoChildren: Editable = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + +case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditablePreview]: + override def style(v: Map[String, Any]) = copy(style = v) -case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement -case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement -case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement +case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditableInput]: + override def style(v: Map[String, Any]) = copy(style = v) + +case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditableTextarea]: + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -122,9 +137,10 @@ case class FormControl( as: String = "", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[FormControl] with HasChildren[FormControl]: - override def copyNoChildren: FormControl = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -133,9 +149,10 @@ case class FormLabel( @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[FormLabel] with HasChildren[FormLabel]: - override def copyNoChildren: FormLabel = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -144,9 +161,10 @@ case class FormHelperText( @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[FormHelperText] with HasChildren[FormHelperText]: - override def copyNoChildren: FormHelperText = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/input */ @@ -158,37 +176,41 @@ case class Input( @volatile var variant: Option[String] = None, @volatile var value: String = "", @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Input] with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Input]: override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def style(v: Map[String, Any]) = copy(style = v) case class InputGroup( key: String = Keys.nextKey, @volatile var size: String = "md", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[InputGroup] with HasChildren[InputGroup]: - override def copyNoChildren: InputGroup = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class InputLeftAddon( key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[InputLeftAddon] with HasChildren[InputLeftAddon]: - override def copyNoChildren: InputLeftAddon = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class InputRightAddon( key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[InputRightAddon] with HasChildren[InputRightAddon]: - override def copyNoChildren: InputRightAddon = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/checkbox */ @@ -198,12 +220,13 @@ case class Checkbox( defaultChecked: Boolean = false, @volatile var isDisabled: Boolean = false, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Checkbox] with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Checkbox]: @volatile private var checkedV: Option[Boolean] = None def checked: Boolean = checkedV.getOrElse(defaultChecked) override def defaultEventHandler: OnChangeEventHandler = newValue => checkedV = Some(newValue.toBoolean) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/radio */ @@ -213,22 +236,24 @@ case class Radio( @volatile var text: String = "", @volatile var colorScheme: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Radio]: + override def style(v: Map[String, Any]) = copy(style = v) + case class RadioGroup( key: String = Keys.nextKey, defaultValue: String = "", @volatile var value: String = "", @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[RadioGroup] with HasEventHandler with HasChildren[RadioGroup] with OnChangeEventHandler.CanHandleOnChangeEvent[RadioGroup]: if value == "" then value = defaultValue override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - - override def copyNoChildren: RadioGroup = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class Center( key: String = Keys.nextKey, @@ -239,9 +264,10 @@ case class Center( @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Center] with HasChildren[Center]: - override def copyNoChildren: Center = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class Circle( key: String = Keys.nextKey, @@ -252,9 +278,10 @@ case class Circle( @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Circle] with HasChildren[Circle]: - override def copyNoChildren: Circle = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class Square( key: String = Keys.nextKey, @@ -265,9 +292,10 @@ case class Square( @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Square] with HasChildren[Square]: - override def copyNoChildren: Square = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -278,7 +306,8 @@ case class AddIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[AddIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -289,7 +318,8 @@ case class ArrowBackIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowBackIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -300,7 +330,8 @@ case class ArrowDownIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowDownIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -311,7 +342,8 @@ case class ArrowForwardIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowForwardIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -322,7 +354,8 @@ case class ArrowLeftIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowLeftIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -333,7 +366,8 @@ case class ArrowRightIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowRightIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -344,7 +378,8 @@ case class ArrowUpIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowUpIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -355,7 +390,8 @@ case class ArrowUpDownIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ArrowUpDownIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -366,7 +402,8 @@ case class AtSignIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[AtSignIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -377,7 +414,8 @@ case class AttachmentIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[AttachmentIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -388,7 +426,8 @@ case class BellIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[BellIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -399,7 +438,8 @@ case class CalendarIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[CalendarIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -410,7 +450,8 @@ case class ChatIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ChatIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -421,7 +462,8 @@ case class CheckIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[CheckIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -432,7 +474,8 @@ case class CheckCircleIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[CheckCircleIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -443,7 +486,8 @@ case class ChevronDownIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ChevronDownIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -454,7 +498,8 @@ case class ChevronLeftIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ChevronLeftIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -465,7 +510,8 @@ case class ChevronRightIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ChevronRightIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -476,7 +522,8 @@ case class ChevronUpIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ChevronUpIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -487,7 +534,8 @@ case class CloseIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[CloseIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -498,7 +546,8 @@ case class CopyIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[CopyIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -509,7 +558,8 @@ case class DeleteIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[DeleteIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -520,7 +570,8 @@ case class DownloadIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[DownloadIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -531,7 +582,8 @@ case class DragHandleIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[DragHandleIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -542,7 +594,8 @@ case class EditIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[EditIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -553,7 +606,8 @@ case class EmailIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[EmailIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -564,7 +618,8 @@ case class ExternalLinkIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ExternalLinkIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -575,7 +630,8 @@ case class HamburgerIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[HamburgerIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -586,7 +642,8 @@ case class InfoIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[InfoIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -597,7 +654,8 @@ case class InfoOutlineIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[InfoOutlineIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -608,7 +666,8 @@ case class LinkIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[LinkIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -619,7 +678,8 @@ case class LockIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[LockIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -630,7 +690,8 @@ case class MinusIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[MinusIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -641,7 +702,8 @@ case class MoonIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[MoonIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -652,7 +714,8 @@ case class NotAllowedIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[NotAllowedIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -663,7 +726,8 @@ case class PhoneIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[PhoneIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -674,7 +738,8 @@ case class PlusSquareIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[PlusSquareIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -685,7 +750,8 @@ case class QuestionIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[QuestionIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -696,7 +762,8 @@ case class QuestionOutlineIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[QuestionOutlineIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -707,7 +774,8 @@ case class RepeatIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[RepeatIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -718,7 +786,8 @@ case class RepeatClockIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[RepeatClockIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -729,7 +798,8 @@ case class SearchIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SearchIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -740,7 +810,8 @@ case class Search2Icon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Search2Icon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -751,7 +822,8 @@ case class SettingsIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SettingsIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -762,7 +834,8 @@ case class SmallAddIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SmallAddIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -773,7 +846,8 @@ case class SmallCloseIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SmallCloseIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -784,7 +858,8 @@ case class SpinnerIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SpinnerIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -795,7 +870,8 @@ case class StarIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[StarIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -806,7 +882,8 @@ case class SunIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[SunIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -817,7 +894,8 @@ case class TimeIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[TimeIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -828,7 +906,8 @@ case class TriangleDownIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[TriangleDownIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -839,7 +918,8 @@ case class TriangleUpIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[TriangleUpIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -850,7 +930,8 @@ case class UnlockIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[UnlockIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -861,7 +942,8 @@ case class UpDownIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[UpDownIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -872,7 +954,8 @@ case class ViewIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ViewIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -883,7 +966,8 @@ case class ViewOffIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[ViewOffIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -894,7 +978,8 @@ case class WarningIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[WarningIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -905,7 +990,8 @@ case class WarningTwoIcon( @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[WarningTwoIcon]: + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/textarea */ @@ -917,10 +1003,11 @@ case class Textarea( @volatile var variant: Option[String] = None, @volatile var value: String = "", @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Textarea] with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Textarea]: override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/switch */ @@ -930,12 +1017,13 @@ case class Switch( defaultChecked: Boolean = false, @volatile var isDisabled: Boolean = false, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Switch] with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Switch]: @volatile private var checkedV: Option[Boolean] = None def checked: Boolean = checkedV.getOrElse(defaultChecked) override def defaultEventHandler: OnChangeEventHandler = newValue => checkedV = Some(newValue.toBoolean) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/select */ @@ -948,26 +1036,28 @@ case class Select( @volatile var borderColor: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[Select] with HasEventHandler with HasChildren[Select] with OnChangeEventHandler.CanHandleOnChangeEvent[Select]: override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - - override def copyNoChildren: Select = copy(children = Nil) + override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) case class Option_( key: String = Keys.nextKey, value: String, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Option_]: + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/table/usage */ case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) - extends ChakraElement + extends ChakraElement[TableContainer] with HasChildren[TableContainer]: + override def style(v: Map[String, Any]) = copy(style = v) def withRowStringData(data: Seq[Seq[String]]): TableContainer = withRowData(data.map(_.map(c => Text(text = c)))) def withRowData(data: Seq[Seq[UiElement]]): TableContainer = val tableBodies = children @@ -984,7 +1074,7 @@ case class TableContainer(key: String = Keys.nextKey, @volatile var children: Se for b <- tableBodies do b.withChildren(newTrs: _*) this - override def copyNoChildren: TableContainer = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) case class Table( key: String = Keys.nextKey, @@ -993,54 +1083,72 @@ case class Table( @volatile var colorScheme: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[Table] with HasChildren[Table]: - override def copyNoChildren: Table = copy(children = Nil) -case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + +case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty) + extends ChakraElement[TableCaption]: + override def style(v: Map[String, Any]) = copy(style = v) + case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) - extends ChakraElement + extends ChakraElement[Thead] with HasChildren[Thead]: - override def copyNoChildren: Thead = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class Tbody(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) - extends ChakraElement + extends ChakraElement[Tbody] with HasChildren[Tbody]: - override def copyNoChildren: Tbody = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) - extends ChakraElement + extends ChakraElement[Tfoot] with HasChildren[Tfoot]: - override def copyNoChildren: Tfoot = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class Tr( key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Tr] with HasChildren[Tr]: - override def copyNoChildren: Tr = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class Th( key: String = Keys.nextKey, @volatile var text: String = "", isNumeric: Boolean = false, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Th] with HasChildren[Th]: - override def copyNoChildren: Th = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class Td( key: String = Keys.nextKey, @volatile var text: String = "", isNumeric: Boolean = false, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[Td] with HasChildren[Td]: - override def copyNoChildren: Td = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/menu/usage */ case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) - extends ChakraElement + extends ChakraElement[Menu] with HasChildren[Menu]: - override def copyNoChildren: Menu = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class MenuButton( key: String = Keys.nextKey, @volatile var text: String = "", @@ -1048,24 +1156,30 @@ case class MenuButton( @volatile var colorScheme: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[MenuButton] with HasChildren[MenuButton]: - override def copyNoChildren: MenuButton = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) - extends ChakraElement + extends ChakraElement[MenuList] with HasChildren[MenuList]: - override def copyNoChildren: MenuList = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) + case class MenuItem( key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var text: String = "", @volatile var children: Seq[UiElement] = Nil -) extends ChakraElement +) extends ChakraElement[MenuItem] with HasChildren[MenuItem] with OnClickEventHandler.CanHandleOnClickEvent[MenuItem]: - override def copyNoChildren: MenuItem = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) -case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement +case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[MenuDivider]: + override def style(v: Map[String, Any]) = copy(style = v) case class Badge( key: String = Keys.nextKey, @@ -1075,9 +1189,10 @@ case class Badge( @volatile var size: String = "md", @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Badge] with HasChildren[Badge]: - override def copyNoChildren: Badge = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/image/usage * @@ -1092,7 +1207,8 @@ case class Image( @volatile var boxSize: Option[String] = None, @volatile var borderRadius: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Image]: + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/text */ @@ -1107,4 +1223,5 @@ case class Text( @volatile var casing: Option[String] = None, @volatile var decoration: Option[String] = None, @volatile var style: Map[String, Any] = Map.empty -) extends ChakraElement +) extends ChakraElement[Text]: + override def style(v: Map[String, Any]) = copy(style = v) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 484bd30e..70f20756 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -7,37 +7,33 @@ case class QuickTable( key: String = Keys.nextKey, variant: String = "striped", colorScheme: String = "teal", - size: String = "mg" + size: String = "mg", + style: Map[String, Any] = Map.empty, + caption: Option[String] = None, + headers: Seq[UiElement] = Nil, + rows: Seq[Seq[UiElement]] = Nil ) extends UiComponent - with HasStyle: - val head = Thead() - val body = Tbody() + with HasStyle[QuickTable]: - val table = Table(variant = variant, colorScheme = Some(colorScheme), size = size) - .withChildren( - head, - body + override def rendered: Seq[UiElement] = + val head = Thead(children = Seq(Tr(children = headers.map(h => Th(children = Seq(h)))))) + val body = Tbody( + children = rows.map: row => + Tr(children = row.map(c => Td().withChildren(c))) ) - @volatile var tableContainer = TableContainer().withChildren(table) - @volatile var children: Seq[UiElement] = Seq(tableContainer) - - def headers(headers: String*): QuickTable = headersElements(headers.map(h => Text(text = h)): _*) - def headersElements(headers: UiElement*): QuickTable = - head.children = Seq(Tr(children = headers.map(h => Th(children = Seq(h))))) - this - - def rows(data: Seq[Seq[Any]]): QuickTable = rowsElements(data.map(_.map(c => Text(text = c.toString)))) - - def rowsElements(data: Seq[Seq[UiElement]]): QuickTable = - body.children = data.map: row => - Tr(children = row.map(c => Td().withChildren(c))) - this - - def style: Map[String, Any] = tableContainer.style - def style_=(s: Map[String, Any]): Unit = tableContainer.style = s + val table = Table( + variant = variant, + colorScheme = Some(colorScheme), + size = size, + children = caption.map(text => TableCaption(text = text)).toSeq ++ Seq(head, body) + ) + val tableContainer = TableContainer().withChildren(table) + Seq(tableContainer) - def caption(text: String): QuickTable = - table.addChildren(TableCaption(text = text)) - this + def headers(headers: String*): QuickTable = copy(headers = headers.map(h => Text(text = h))) + def headersElements(headers: UiElement*): QuickTable = copy(headers = headers) + def rows(data: Seq[Seq[Any]]): QuickTable = copy(rows = data.map(_.map(c => Text(text = c.toString)))) + def rowsElements(data: Seq[Seq[UiElement]]): QuickTable = copy(rows = data) - override def copyNoChildren: UiComponent = copy() + def caption(text: String): QuickTable = copy(caption = Some(text)) + override def style(v: Map[String, Any]): QuickTable = copy(style = v) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala index 94638d68..69809def 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala @@ -4,32 +4,41 @@ import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{ConnectedSession, OnChangeEventHandler} -sealed trait StdElement extends UiElement with HasStyle +sealed trait StdEJson extends UiElement +sealed trait StdElement[A <: UiElement] extends StdEJson with HasStyle[A] -case class Span(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement -case class NewLine(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends StdElement -case class Em(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement +case class Span(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Span]: + override def style(v: Map[String, Any]) = copy(style = v) -case class Header1(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement +case class NewLine(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends StdElement[NewLine]: + override def style(v: Map[String, Any]) = copy(style = v) + +case class Em(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Em]: + override def style(v: Map[String, Any]) = copy(style = v) + +case class Header1(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Header1]: + override def style(v: Map[String, Any]) = copy(style = v) case class Paragraph( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil -) extends StdElement + text: String = "", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil +) extends StdElement[Paragraph] with HasChildren[Paragraph]: - override def copyNoChildren: Paragraph = copy(children = Nil) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class Input( key: String = Keys.nextKey, `type`: String = "text", defaultValue: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var value: Option[String] = None -) extends StdElement + style: Map[String, Any] = Map.empty, + value: Option[String] = None +) extends StdElement[Input] with HasEventHandler: - override def defaultEventHandler: OnChangeEventHandler = newValue => value = Some(newValue) + override def defaultEventHandler: OnChangeEventHandler = newValue => copy(value = Some(newValue)) + override def style(v: Map[String, Any]) = copy(style = v) def onChange(h: OnChangeEventHandler)(using session: ConnectedSession): Input = session.addEventHandler(key, h) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 2dd40b88..d07042cc 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -32,7 +32,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: connectedSession.session, ServerJson( Seq(p1.key), - Map(p1.key -> encoder(p1.copyNoChildren), span1.key -> encoder(span1)), + Map(p1.key -> encoder(p1.withChildren()), span1.key -> encoder(span1)), Map(p1.key -> Seq(span1.key), span1.key -> Nil) ) ) @@ -48,7 +48,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: connectedSession.session, ServerJson( Seq(p1.key), - Map(p1.key -> encoder(p1.copyNoChildren), span1.key -> encoder(span1)), + Map(p1.key -> encoder(p1.withChildren()), span1.key -> encoder(span1)), Map(p1.key -> Seq(span1.key), span1.key -> Nil) ) ) From 18a7d92841cbc76eb29c62f2d531cdb34e5897a3 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 17:54:45 +0000 Subject: [PATCH 18/40] - --- .../src/main/scala/tests/chakra/Buttons.scala | 12 +- .../main/scala/tests/chakra/Editables.scala | 6 +- .../src/main/scala/tests/chakra/Forms.scala | 51 +- .../src/main/scala/tests/chakra/Overlay.scala | 15 +- .../PropertiesExtensionGenerator.scala | 1 + .../terminal21/client/ConnectedSession.scala | 6 +- .../client/components/StdUiCalculation.scala | 30 +- .../client/components/UiElement.scala | 2 +- .../components/chakra/ChakraElement.scala | 956 +++++++++--------- .../client/components/std/StdElement.scala | 4 +- .../client/internal/EventHandlers.scala | 6 +- .../client/components/chakra/extensions.scala | 804 +++++++++++++++ .../client/components/std/extensions.scala | 52 + 13 files changed, 1397 insertions(+), 548 deletions(-) create mode 100644 terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala create mode 100644 terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala b/end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala index 8c112cea..7e3674e1 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala @@ -1,8 +1,8 @@ package tests.chakra import org.terminal21.client.ConnectedSession -import org.terminal21.client.components.UiElement -import org.terminal21.client.components.chakra.{Box, Button} +import org.terminal21.client.components.* +import org.terminal21.client.components.chakra.* import tests.chakra.Common.* import java.util.concurrent.CountDownLatch @@ -14,10 +14,10 @@ object Buttons: Seq( box1, exitButton.onClick: () => - box1.text = "Exit Clicked!" - exitButton.text = "Stopping..." - exitButton.colorScheme = Some("green") - session.render() + Seq( + box1.withText("Exit Clicked!"), + exitButton.withText("Stopping...").withColorScheme(Some("green")) + ).renderChanges() Thread.sleep(1000) latch.countDown() ) diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala index fe5652ff..14693f61 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala @@ -15,16 +15,14 @@ object Editables: ) editable1.onChange: newValue => - status.text = s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}" - status.renderChanges() + status.withText(s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}").renderChanges() val editable2 = Editable(defaultValue = "For longer maybe-editable texts\nUse an EditableTextarea\nIt uses a textarea control.").withChildren( EditablePreview(), EditableTextarea() ) editable2.onChange: newValue => - status.text = s"editable2 newValue = $newValue, verify editable2.value = ${editable2.value}" - status.renderChanges() + status.withText(s"editable2 newValue = $newValue, verify editable2.value = ${editable2.value}").renderChanges() Seq( commonBox(text = "Editables"), diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala index 3f2f4889..555fc84e 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala @@ -15,14 +15,14 @@ object Forms: val email = Input(`type` = "email", value = "my@email.com") email.onChange: newValue => - status.text = s"email input new value = $newValue, verify email.value = ${email.value}" - if newValue.contains("@") then emailRightAddOn.children = Seq(okIcon) else emailRightAddOn.children = Seq(notOkIcon) - Seq(status, emailRightAddOn).renderChanges() + Seq( + status.withText(s"email input new value = $newValue, verify email.value = ${email.value}"), + if newValue.contains("@") then emailRightAddOn.withChildren(okIcon) else emailRightAddOn.withChildren(notOkIcon) + ).renderChanges() val description = Textarea(placeholder = "Please enter a few things about you") description.onChange: newValue => - status.text = s"description input new value = $newValue, verify description.value = ${description.value}" - status.renderChanges() + status.withText(s"description input new value = $newValue, verify description.value = ${description.value}").renderChanges() val select1 = Select(placeholder = "Please choose").withChildren( Option_(text = "Male", value = "male"), @@ -30,8 +30,7 @@ object Forms: ) select1.onChange: newValue => - status.text = s"select1 input new value = $newValue, verify select1.value = ${select1.value}" - status.renderChanges() + status.withText(s"select1 input new value = $newValue, verify select1.value = ${select1.value}").renderChanges() val select2 = Select(value = "1", bg = Some("tomato"), color = Some("black"), borderColor = Some("yellow")).withChildren( Option_(text = "First", value = "1"), @@ -41,33 +40,32 @@ object Forms: val password = Input(`type` = "password", value = "mysecret") val dob = Input(`type` = "datetime-local") dob.onChange: newValue => - status.text = s"dob = $newValue , verify dob.value = ${dob.value}" - status.renderChanges() + status.withText(s"dob = $newValue , verify dob.value = ${dob.value}").renderChanges() val color = Input(`type` = "color") color.onChange: newValue => - status.text = s"color = $newValue , verify color.value = ${color.value}" - status.renderChanges() + status.withText(s"color = $newValue , verify color.value = ${color.value}").renderChanges() val checkbox2 = Checkbox(text = "Check 2", defaultChecked = true) checkbox2.onChange: newValue => - status.text = s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.checked}" - status.renderChanges() + status.withText(s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.checked}").renderChanges() val checkbox1 = Checkbox(text = "Check 1") checkbox1.onChange: newValue => - checkbox2.isDisabled = newValue - status.text = s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.checked}" - Seq(status, checkbox2).renderChanges() + Seq( + status.withText(s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.checked}"), + checkbox2.withIsDisabled(newValue) + ).renderChanges() val switch1 = Switch(text = "Switch 1") val switch2 = Switch(text = "Switch 2") switch1.onChange: newValue => - switch2.isDisabled = newValue - status.text = s"switch1 checked is $newValue , verify switch1.checked = ${switch1.checked}" - Seq(status, switch2).renderChanges() + Seq( + status.withText(s"switch1 checked is $newValue , verify switch1.checked = ${switch1.checked}"), + switch2.withIsDisabled(newValue) + ).renderChanges() val radioGroup = RadioGroup(defaultValue = "2").withChildren( HStack().withChildren( @@ -78,8 +76,7 @@ object Forms: ) radioGroup.onChange: newValue => - status.text = s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.value}" - status.renderChanges() + status.withText(s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.value}").renderChanges() Seq( commonBox(text = "Forms"), @@ -137,14 +134,14 @@ object Forms: ButtonGroup(variant = Some("outline"), spacing = Some("24")).withChildren( Button(text = "Save", colorScheme = Some("red")) .onClick: () => - status.text = - s"Saved clicked. Email = ${email.value}, password = ${password.value}, dob = ${dob.value}, check1 = ${checkbox1.checked}, check2 = ${checkbox2.checked}, radio = ${radioGroup.value}" - status.renderChanges() - , + status + .withText( + s"Saved clicked. Email = ${email.value}, password = ${password.value}, dob = ${dob.value}, check1 = ${checkbox1.checked}, check2 = ${checkbox2.checked}, radio = ${radioGroup.value}" + ) + .renderChanges(), Button(text = "Cancel") .onClick: () => - status.text = "Cancel clicked" - status.renderChanges() + status.withText("Cancel clicked").renderChanges() ), radioGroup, status diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala b/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala index ccddf740..bed13a35 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala @@ -18,21 +18,14 @@ object Overlay: MenuList().withChildren( MenuItem(text = "Download menu-download") .onClick: () => - box1.text = "'Download' clicked" - box1.renderChanges() - , + box1.withText("'Download' clicked").renderChanges(), MenuItem(text = "Copy").onClick: () => - box1.text = "'Copy' clicked" - box1.renderChanges() - , + box1.withText("'Copy' clicked").renderChanges(), MenuItem(text = "Paste").onClick: () => - box1.text = "'Paste' clicked" - box1.renderChanges() - , + box1.withText("'Paste' clicked").renderChanges(), MenuDivider(), MenuItem(text = "Exit").onClick: () => - box1.text = "'Exit' clicked" - box1.renderChanges() + box1.withText("'Exit' clicked").renderChanges() ) ), box1 diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index 68f80c44..c1fde596 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -54,3 +54,4 @@ object PropertiesExtensionGenerator: @main def propertiesExtensionGeneratorApp(): Unit = generate(new File("../terminal21-ui-std"), "org.terminal21.client.components.std") + generate(new File("../terminal21-ui-std"), "org.terminal21.client.components.chakra") diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 60659d6d..b5ee331d 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -12,10 +12,11 @@ import org.terminal21.ui.std.{ServerJson, SessionsService} import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec +import scala.collection.concurrent.TrieMap class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): private val logger = LoggerFactory.getLogger(getClass) - private val handlers = new EventHandlers + private val handlers = new EventHandlers(this) def uiUrl: String = serverUrl + "/ui" def clear(): Unit = @@ -105,3 +106,6 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se ) .toMap ) + private val modifiedElements = TrieMap.empty[String, UiElement] + def modified(e: UiElement): Unit = + modifiedElements += e.key -> e diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index af343db9..88523f8e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -3,7 +3,7 @@ package org.terminal21.client.components import functions.fibers.FiberExecutor import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.components.chakra.* +import org.terminal21.client.components.chakra.{withColorScheme, *} import java.util.concurrent.atomic.AtomicBoolean @@ -37,23 +37,25 @@ trait StdUiCalculation[OUT]( Seq(header, dataUi) override def onError(t: Throwable): Unit = - badge.text = s"Error: ${t.getMessage}" - badge.colorScheme = Some("red") - recalc.isDisabled = None - session.renderChanges(badge, dataUi, recalc) + session.renderChanges( + badge.withText(s"Error: ${t.getMessage}").withColorScheme(Some("red")), + dataUi, + recalc.withIsDisabled(None) + ) super.onError(t) override protected def whenResultsNotReady(): Unit = - badge.text = "Calculating" - badge.colorScheme = Some("purple") - recalc.isDisabled = Some(true) - val newDataUi = dataUi.style(dataUi.style + ("filter" -> "grayscale(100%)")) - session.renderChanges(badge, newDataUi, recalc) + session.renderChanges( + badge.withText("Calculating").withColorScheme(Some("purple")), + dataUi.style(dataUi.style + ("filter" -> "grayscale(100%)")), + recalc.withIsDisabled(Some(true)) + ) super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = - badge.text = "Ready" - badge.colorScheme = None - recalc.isDisabled = Some(false) val newDataUi = dataUi.style(dataUi.style - "filter") - session.renderChanges(badge, newDataUi, recalc) + session.renderChanges( + badge.withText("Ready").withColorScheme(None), + newDataUi, + recalc.withIsDisabled(Some(false)) + ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 65b0596f..2631ff00 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -30,7 +30,7 @@ object UiElement: def withChildren(cn: UiElement*): A trait HasEventHandler: - def defaultEventHandler: EventHandler + def defaultEventHandler(session: ConnectedSession): EventHandler trait HasStyle[A <: UiElement]: def style: Map[String, Any] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 25b96b22..66d51c89 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -2,7 +2,7 @@ package org.terminal21.client.components.chakra import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} import org.terminal21.client.components.{Keys, UiElement} -import org.terminal21.client.{OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} +import org.terminal21.client.{ConnectedSession, OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} sealed trait CEJson extends UiElement @@ -16,18 +16,18 @@ sealed trait ChakraElement[A <: ChakraElement[A]] extends CEJson with HasStyle[A */ case class Button( key: String = Keys.nextKey, - @volatile var text: String = "Ok", - @volatile var size: Option[String] = None, - @volatile var variant: Option[String] = None, - @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var leftIcon: Option[UiElement] = None, - @volatile var rightIcon: Option[UiElement] = None, - @volatile var isActive: Option[Boolean] = None, - @volatile var isDisabled: Option[Boolean] = None, - @volatile var isLoading: Option[Boolean] = None, - @volatile var isAttached: Option[Boolean] = None, - @volatile var spacing: Option[String] = None + text: String = "Ok", + size: Option[String] = None, + variant: Option[String] = None, + colorScheme: Option[String] = None, + style: Map[String, Any] = Map.empty, + leftIcon: Option[UiElement] = None, + rightIcon: Option[UiElement] = None, + isActive: Option[Boolean] = None, + isDisabled: Option[Boolean] = None, + isLoading: Option[Boolean] = None, + isAttached: Option[Boolean] = None, + spacing: Option[String] = None ) extends ChakraElement[Button] with OnClickEventHandler.CanHandleOnClickEvent[Button]: override def style(v: Map[String, Any]): Button = copy(style = v) @@ -36,15 +36,15 @@ case class Button( */ case class ButtonGroup( key: String = Keys.nextKey, - @volatile var variant: Option[String] = None, - @volatile var spacing: Option[String] = None, - @volatile var size: Option[String] = None, - @volatile var width: Option[String] = None, - @volatile var height: Option[String] = None, - @volatile var border: Option[String] = None, - @volatile var borderColor: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + variant: Option[String] = None, + spacing: Option[String] = None, + size: Option[String] = None, + width: Option[String] = None, + height: Option[String] = None, + border: Option[String] = None, + borderColor: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[ButtonGroup] with HasChildren[ButtonGroup]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -54,14 +54,14 @@ case class ButtonGroup( */ case class Box( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var bg: String = "", - @volatile var w: String = "", - @volatile var p: Int = 0, - @volatile var color: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var as: Option[String] = None, - @volatile var children: Seq[UiElement] = Nil + text: String = "", + bg: String = "", + w: String = "", + p: Int = 0, + color: String = "", + style: Map[String, Any] = Map.empty, + as: Option[String] = None, + children: Seq[UiElement] = Nil ) extends ChakraElement[Box] with HasChildren[Box]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -71,10 +71,10 @@ case class Box( */ case class HStack( key: String = Keys.nextKey, - @volatile var spacing: Option[String] = None, - @volatile var align: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + spacing: Option[String] = None, + align: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[HStack] with HasChildren[HStack]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -82,10 +82,10 @@ case class HStack( case class VStack( key: String = Keys.nextKey, - @volatile var spacing: Option[String] = None, - @volatile var align: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + spacing: Option[String] = None, + align: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[VStack] with HasChildren[VStack]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -93,12 +93,12 @@ case class VStack( case class SimpleGrid( key: String = Keys.nextKey, - @volatile var spacing: Option[String] = None, - @volatile var spacingX: Option[String] = None, - @volatile var spacingY: Option[String] = None, - @volatile var columns: Int = 2, - @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, Any] = Map.empty + spacing: Option[String] = None, + spacingX: Option[String] = None, + spacingY: Option[String] = None, + columns: Int = 2, + children: Seq[UiElement] = Nil, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SimpleGrid] with HasChildren[SimpleGrid]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -109,25 +109,25 @@ case class SimpleGrid( case class Editable( key: String = Keys.nextKey, defaultValue: String = "", - @volatile var value: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + value: String = "", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[Editable] with HasEventHandler with HasChildren[Editable] with OnChangeEventHandler.CanHandleOnChangeEvent[Editable]: - if value == "" then value = defaultValue - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = + newValue => session.modified(copy(value = newValue)) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) -case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditablePreview]: +case class EditablePreview(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditablePreview]: override def style(v: Map[String, Any]) = copy(style = v) -case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditableInput]: +case class EditableInput(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditableInput]: override def style(v: Map[String, Any]) = copy(style = v) -case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[EditableTextarea]: +case class EditableTextarea(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditableTextarea]: override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/form-control @@ -135,8 +135,8 @@ case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map case class FormControl( key: String = Keys.nextKey, as: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[FormControl] with HasChildren[FormControl]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -146,9 +146,9 @@ case class FormControl( */ case class FormLabel( key: String = Keys.nextKey, - @volatile var text: String, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + text: String, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[FormLabel] with HasChildren[FormLabel]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -158,9 +158,9 @@ case class FormLabel( */ case class FormHelperText( key: String = Keys.nextKey, - @volatile var text: String, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + text: String, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[FormHelperText] with HasChildren[FormHelperText]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -172,21 +172,21 @@ case class Input( key: String = Keys.nextKey, `type`: String = "text", placeholder: String = "", - @volatile var size: String = "md", - @volatile var variant: Option[String] = None, - @volatile var value: String = "", - @volatile var style: Map[String, Any] = Map.empty + size: String = "md", + variant: Option[String] = None, + value: String = "", + style: Map[String, Any] = Map.empty ) extends ChakraElement[Input] with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Input]: - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def style(v: Map[String, Any]) = copy(style = v) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) + override def style(v: Map[String, Any]) = copy(style = v) case class InputGroup( key: String = Keys.nextKey, - @volatile var size: String = "md", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + size: String = "md", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[InputGroup] with HasChildren[InputGroup]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -194,9 +194,9 @@ case class InputGroup( case class InputLeftAddon( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + text: String = "", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[InputLeftAddon] with HasChildren[InputLeftAddon]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -204,9 +204,9 @@ case class InputLeftAddon( case class InputRightAddon( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + text: String = "", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[InputRightAddon] with HasChildren[InputRightAddon]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -216,54 +216,53 @@ case class InputRightAddon( */ case class Checkbox( key: String = Keys.nextKey, - @volatile var text: String = "", + text: String = "", defaultChecked: Boolean = false, - @volatile var isDisabled: Boolean = false, - @volatile var style: Map[String, Any] = Map.empty + isDisabled: Boolean = false, + style: Map[String, Any] = Map.empty, + checkedV: Option[Boolean] = None ) extends ChakraElement[Checkbox] with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Checkbox]: - @volatile private var checkedV: Option[Boolean] = None - def checked: Boolean = checkedV.getOrElse(defaultChecked) - override def defaultEventHandler: OnChangeEventHandler = newValue => checkedV = Some(newValue.toBoolean) - override def style(v: Map[String, Any]) = copy(style = v) + def checked: Boolean = checkedV.getOrElse(defaultChecked) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(checkedV = Some(newValue.toBoolean))) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/radio */ case class Radio( key: String = Keys.nextKey, value: String, - @volatile var text: String = "", - @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + colorScheme: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Radio]: override def style(v: Map[String, Any]) = copy(style = v) case class RadioGroup( key: String = Keys.nextKey, defaultValue: String = "", - @volatile var value: String = "", - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + value: String = "", + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[RadioGroup] with HasEventHandler with HasChildren[RadioGroup] with OnChangeEventHandler.CanHandleOnChangeEvent[RadioGroup]: - if value == "" then value = defaultValue - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def style(v: Map[String, Any]) = copy(style = v) case class Center( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var children: Seq[UiElement] = Nil, - @volatile var bg: Option[String] = None, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + children: Seq[UiElement] = Nil, + bg: Option[String] = None, + w: Option[String] = None, + h: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Center] with HasChildren[Center]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -271,13 +270,13 @@ case class Center( case class Circle( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var children: Seq[UiElement] = Nil, - @volatile var bg: Option[String] = None, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + children: Seq[UiElement] = Nil, + bg: Option[String] = None, + w: Option[String] = None, + h: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Circle] with HasChildren[Circle]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -285,13 +284,13 @@ case class Circle( case class Square( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var children: Seq[UiElement] = Nil, - @volatile var bg: Option[String] = None, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + children: Seq[UiElement] = Nil, + bg: Option[String] = None, + w: Option[String] = None, + h: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Square] with HasChildren[Square]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -301,11 +300,11 @@ case class Square( */ case class AddIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[AddIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -313,11 +312,11 @@ case class AddIcon( */ case class ArrowBackIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowBackIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -325,11 +324,11 @@ case class ArrowBackIcon( */ case class ArrowDownIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowDownIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -337,11 +336,11 @@ case class ArrowDownIcon( */ case class ArrowForwardIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowForwardIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -349,11 +348,11 @@ case class ArrowForwardIcon( */ case class ArrowLeftIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowLeftIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -361,11 +360,11 @@ case class ArrowLeftIcon( */ case class ArrowRightIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowRightIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -373,11 +372,11 @@ case class ArrowRightIcon( */ case class ArrowUpIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowUpIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -385,11 +384,11 @@ case class ArrowUpIcon( */ case class ArrowUpDownIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowUpDownIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -397,11 +396,11 @@ case class ArrowUpDownIcon( */ case class AtSignIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[AtSignIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -409,11 +408,11 @@ case class AtSignIcon( */ case class AttachmentIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[AttachmentIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -421,11 +420,11 @@ case class AttachmentIcon( */ case class BellIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[BellIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -433,11 +432,11 @@ case class BellIcon( */ case class CalendarIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[CalendarIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -445,11 +444,11 @@ case class CalendarIcon( */ case class ChatIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ChatIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -457,11 +456,11 @@ case class ChatIcon( */ case class CheckIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[CheckIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -469,11 +468,11 @@ case class CheckIcon( */ case class CheckCircleIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[CheckCircleIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -481,11 +480,11 @@ case class CheckCircleIcon( */ case class ChevronDownIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronDownIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -493,11 +492,11 @@ case class ChevronDownIcon( */ case class ChevronLeftIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronLeftIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -505,11 +504,11 @@ case class ChevronLeftIcon( */ case class ChevronRightIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronRightIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -517,11 +516,11 @@ case class ChevronRightIcon( */ case class ChevronUpIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronUpIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -529,11 +528,11 @@ case class ChevronUpIcon( */ case class CloseIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[CloseIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -541,11 +540,11 @@ case class CloseIcon( */ case class CopyIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[CopyIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -553,11 +552,11 @@ case class CopyIcon( */ case class DeleteIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[DeleteIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -565,11 +564,11 @@ case class DeleteIcon( */ case class DownloadIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[DownloadIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -577,11 +576,11 @@ case class DownloadIcon( */ case class DragHandleIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[DragHandleIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -589,11 +588,11 @@ case class DragHandleIcon( */ case class EditIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[EditIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -601,11 +600,11 @@ case class EditIcon( */ case class EmailIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[EmailIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -613,11 +612,11 @@ case class EmailIcon( */ case class ExternalLinkIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ExternalLinkIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -625,11 +624,11 @@ case class ExternalLinkIcon( */ case class HamburgerIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[HamburgerIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -637,11 +636,11 @@ case class HamburgerIcon( */ case class InfoIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[InfoIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -649,11 +648,11 @@ case class InfoIcon( */ case class InfoOutlineIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[InfoOutlineIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -661,11 +660,11 @@ case class InfoOutlineIcon( */ case class LinkIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[LinkIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -673,11 +672,11 @@ case class LinkIcon( */ case class LockIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[LockIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -685,11 +684,11 @@ case class LockIcon( */ case class MinusIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[MinusIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -697,11 +696,11 @@ case class MinusIcon( */ case class MoonIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[MoonIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -709,11 +708,11 @@ case class MoonIcon( */ case class NotAllowedIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[NotAllowedIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -721,11 +720,11 @@ case class NotAllowedIcon( */ case class PhoneIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[PhoneIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -733,11 +732,11 @@ case class PhoneIcon( */ case class PlusSquareIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[PlusSquareIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -745,11 +744,11 @@ case class PlusSquareIcon( */ case class QuestionIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[QuestionIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -757,11 +756,11 @@ case class QuestionIcon( */ case class QuestionOutlineIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[QuestionOutlineIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -769,11 +768,11 @@ case class QuestionOutlineIcon( */ case class RepeatIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[RepeatIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -781,11 +780,11 @@ case class RepeatIcon( */ case class RepeatClockIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[RepeatClockIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -793,11 +792,11 @@ case class RepeatClockIcon( */ case class SearchIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SearchIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -805,11 +804,11 @@ case class SearchIcon( */ case class Search2Icon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Search2Icon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -817,11 +816,11 @@ case class Search2Icon( */ case class SettingsIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SettingsIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -829,11 +828,11 @@ case class SettingsIcon( */ case class SmallAddIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SmallAddIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -841,11 +840,11 @@ case class SmallAddIcon( */ case class SmallCloseIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SmallCloseIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -853,11 +852,11 @@ case class SmallCloseIcon( */ case class SpinnerIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SpinnerIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -865,11 +864,11 @@ case class SpinnerIcon( */ case class StarIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[StarIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -877,11 +876,11 @@ case class StarIcon( */ case class SunIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[SunIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -889,11 +888,11 @@ case class SunIcon( */ case class TimeIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[TimeIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -901,11 +900,11 @@ case class TimeIcon( */ case class TriangleDownIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[TriangleDownIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -913,11 +912,11 @@ case class TriangleDownIcon( */ case class TriangleUpIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[TriangleUpIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -925,11 +924,11 @@ case class TriangleUpIcon( */ case class UnlockIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[UnlockIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -937,11 +936,11 @@ case class UnlockIcon( */ case class UpDownIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[UpDownIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -949,11 +948,11 @@ case class UpDownIcon( */ case class ViewIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ViewIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -961,11 +960,11 @@ case class ViewIcon( */ case class ViewOffIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[ViewOffIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -973,11 +972,11 @@ case class ViewOffIcon( */ case class WarningIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[WarningIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -985,11 +984,11 @@ case class WarningIcon( */ case class WarningTwoIcon( key: String = Keys.nextKey, - @volatile var w: Option[String] = None, - @volatile var h: Option[String] = None, - @volatile var boxSize: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + w: Option[String] = None, + h: Option[String] = None, + boxSize: Option[String] = None, + color: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[WarningTwoIcon]: override def style(v: Map[String, Any]) = copy(style = v) @@ -999,62 +998,62 @@ case class Textarea( key: String = Keys.nextKey, `type`: String = "text", placeholder: String = "", - @volatile var size: String = "md", - @volatile var variant: Option[String] = None, - @volatile var value: String = "", - @volatile var style: Map[String, Any] = Map.empty + size: String = "md", + variant: Option[String] = None, + value: String = "", + style: Map[String, Any] = Map.empty ) extends ChakraElement[Textarea] with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Textarea]: - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def style(v: Map[String, Any]) = copy(style = v) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/switch */ case class Switch( key: String = Keys.nextKey, - @volatile var text: String = "", + text: String = "", defaultChecked: Boolean = false, - @volatile var isDisabled: Boolean = false, - @volatile var style: Map[String, Any] = Map.empty + isDisabled: Boolean = false, + style: Map[String, Any] = Map.empty, + checkedV: Option[Boolean] = None ) extends ChakraElement[Switch] with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Switch]: - @volatile private var checkedV: Option[Boolean] = None - def checked: Boolean = checkedV.getOrElse(defaultChecked) - override def defaultEventHandler: OnChangeEventHandler = newValue => checkedV = Some(newValue.toBoolean) - override def style(v: Map[String, Any]) = copy(style = v) + def checked: Boolean = checkedV.getOrElse(defaultChecked) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(checkedV = Some(newValue.toBoolean))) + override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/select */ case class Select( key: String = Keys.nextKey, placeholder: String = "", - @volatile var value: String = "", - @volatile var bg: Option[String] = None, - @volatile var color: Option[String] = None, - @volatile var borderColor: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + value: String = "", + bg: Option[String] = None, + color: Option[String] = None, + borderColor: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[Select] with HasEventHandler with HasChildren[Select] with OnChangeEventHandler.CanHandleOnChangeEvent[Select]: - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue - override def style(v: Map[String, Any]) = copy(style = v) - override def withChildren(cn: UiElement*) = copy(children = cn) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) + override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) case class Option_( key: String = Keys.nextKey, value: String, - @volatile var text: String = "", - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + style: Map[String, Any] = Map.empty ) extends ChakraElement[Option_]: override def style(v: Map[String, Any]) = copy(style = v) /** https://chakra-ui.com/docs/components/table/usage */ -case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) +case class TableContainer(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[TableContainer] with HasChildren[TableContainer]: override def style(v: Map[String, Any]) = copy(style = v) @@ -1078,33 +1077,32 @@ case class TableContainer(key: String = Keys.nextKey, @volatile var children: Se case class Table( key: String = Keys.nextKey, - @volatile var variant: String = "simple", - @volatile var size: String = "md", - @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + variant: String = "simple", + size: String = "md", + colorScheme: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[Table] with HasChildren[Table]: override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) -case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty) - extends ChakraElement[TableCaption]: +case class TableCaption(key: String = Keys.nextKey, text: String = "", style: Map[String, Any] = Map.empty) extends ChakraElement[TableCaption]: override def style(v: Map[String, Any]) = copy(style = v) -case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) +case class Thead(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Thead] with HasChildren[Thead]: override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) -case class Tbody(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) +case class Tbody(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Tbody] with HasChildren[Tbody]: override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) -case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) +case class Tfoot(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Tfoot] with HasChildren[Tfoot]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1112,8 +1110,8 @@ case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiEleme case class Tr( key: String = Keys.nextKey, - @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, Any] = Map.empty + children: Seq[UiElement] = Nil, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Tr] with HasChildren[Tr]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1121,10 +1119,10 @@ case class Tr( case class Th( key: String = Keys.nextKey, - @volatile var text: String = "", + text: String = "", isNumeric: Boolean = false, - @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, Any] = Map.empty + children: Seq[UiElement] = Nil, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Th] with HasChildren[Th]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1132,10 +1130,10 @@ case class Th( case class Td( key: String = Keys.nextKey, - @volatile var text: String = "", + text: String = "", isNumeric: Boolean = false, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[Td] with HasChildren[Td]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1143,7 +1141,7 @@ case class Td( /** https://chakra-ui.com/docs/components/menu/usage */ -case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) +case class Menu(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty, children: Seq[UiElement] = Nil) extends ChakraElement[Menu] with HasChildren[Menu]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1151,17 +1149,17 @@ case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, Any case class MenuButton( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var size: Option[String] = None, - @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var children: Seq[UiElement] = Nil + text: String = "", + size: Option[String] = None, + colorScheme: Option[String] = None, + style: Map[String, Any] = Map.empty, + children: Seq[UiElement] = Nil ) extends ChakraElement[MenuButton] with HasChildren[MenuButton]: override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) -case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) +case class MenuList(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty, children: Seq[UiElement] = Nil) extends ChakraElement[MenuList] with HasChildren[MenuList]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1169,26 +1167,26 @@ case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, case class MenuItem( key: String = Keys.nextKey, - @volatile var style: Map[String, Any] = Map.empty, - @volatile var text: String = "", - @volatile var children: Seq[UiElement] = Nil + style: Map[String, Any] = Map.empty, + text: String = "", + children: Seq[UiElement] = Nil ) extends ChakraElement[MenuItem] with HasChildren[MenuItem] with OnClickEventHandler.CanHandleOnClickEvent[MenuItem]: override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) -case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement[MenuDivider]: +case class MenuDivider(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[MenuDivider]: override def style(v: Map[String, Any]) = copy(style = v) case class Badge( key: String = Keys.nextKey, - @volatile var text: String = "", - @volatile var colorScheme: Option[String] = None, - @volatile var variant: Option[String] = None, - @volatile var size: String = "md", - @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, Any] = Map.empty + text: String = "", + colorScheme: Option[String] = None, + variant: Option[String] = None, + size: String = "md", + children: Seq[UiElement] = Nil, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Badge] with HasChildren[Badge]: override def withChildren(cn: UiElement*) = copy(children = cn) @@ -1202,11 +1200,11 @@ case class Badge( */ case class Image( key: String = Keys.nextKey, - @volatile var src: String = "", - @volatile var alt: String = "", - @volatile var boxSize: Option[String] = None, - @volatile var borderRadius: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + src: String = "", + alt: String = "", + boxSize: Option[String] = None, + borderRadius: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Image]: override def style(v: Map[String, Any]) = copy(style = v) @@ -1214,14 +1212,14 @@ case class Image( */ case class Text( key: String = Keys.nextKey, - @volatile var text: String = "text.text is empty. Did you accidentally assigned the text to the `key` field?", - @volatile var fontSize: Option[String] = None, - @volatile var noOfLines: Option[Int] = None, - @volatile var color: Option[String] = None, - @volatile var as: Option[String] = None, - @volatile var align: Option[String] = None, - @volatile var casing: Option[String] = None, - @volatile var decoration: Option[String] = None, - @volatile var style: Map[String, Any] = Map.empty + text: String = "text.text is empty. Did you accidentally assigned the text to the `key` field?", + fontSize: Option[String] = None, + noOfLines: Option[Int] = None, + color: Option[String] = None, + as: Option[String] = None, + align: Option[String] = None, + casing: Option[String] = None, + decoration: Option[String] = None, + style: Map[String, Any] = Map.empty ) extends ChakraElement[Text]: override def style(v: Map[String, Any]) = copy(style = v) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala index 69809def..8c3a8ce3 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala @@ -37,8 +37,8 @@ case class Input( value: Option[String] = None ) extends StdElement[Input] with HasEventHandler: - override def defaultEventHandler: OnChangeEventHandler = newValue => copy(value = Some(newValue)) - override def style(v: Map[String, Any]) = copy(style = v) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = Some(newValue))) + override def style(v: Map[String, Any]) = copy(style = v) def onChange(h: OnChangeEventHandler)(using session: ConnectedSession): Input = session.addEventHandler(key, h) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala index 08024d0f..39ba715e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/internal/EventHandlers.scala @@ -1,10 +1,10 @@ package org.terminal21.client.internal -import org.terminal21.client.EventHandler +import org.terminal21.client.{ConnectedSession, EventHandler} import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} -class EventHandlers: +class EventHandlers(session: ConnectedSession): private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] def registerEventHandlers(es: Seq[UiElement]): Unit = synchronized: @@ -12,7 +12,7 @@ class EventHandlers: val withEvents = all.collect: case h: HasEventHandler => h - for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) + for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler(session)) def addEventHandler(key: String, handler: EventHandler): Unit = val handlers = eventHandlers.getOrElse(key, Nil) diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala new file mode 100644 index 00000000..b2790bf8 --- /dev/null +++ b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala @@ -0,0 +1,804 @@ +package org.terminal21.client.components.chakra + +// GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT + +import org.terminal21.client.components.UiElement.HasChildren +import org.terminal21.client.components.UiElement.HasEventHandler +import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.client.components.Keys +import org.terminal21.client.components.UiElement +import org.terminal21.client.OnChangeBooleanEventHandler +import org.terminal21.client.OnChangeEventHandler +import org.terminal21.client.OnClickEventHandler + +extension (e: PlusSquareIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: FormControl) + def withKey(v: String) = e.copy(key = v) + def withAs(v: String) = e.copy(as = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: ArrowLeftIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Center) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ViewIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Square) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Image) + def withKey(v: String) = e.copy(key = v) + def withSrc(v: String) = e.copy(src = v) + def withAlt(v: String) = e.copy(alt = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withBorderRadius(v: Option[String]) = e.copy(borderRadius = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: SpinnerIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: EditableInput) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ArrowRightIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Table) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: String) = e.copy(variant = v) + def withSize(v: String) = e.copy(size = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: SmallCloseIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: InputLeftAddon) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: MenuList) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: TriangleDownIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Editable) + def withKey(v: String) = e.copy(key = v) + def withDefaultValue(v: String) = e.copy(defaultValue = v) + def withValue(v: String) = e.copy(value = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: InfoIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: AttachmentIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: RepeatClockIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: QuestionOutlineIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: SmallAddIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ChevronRightIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Tr) + def withKey(v: String) = e.copy(key = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: UpDownIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: MenuButton) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withSize(v: Option[String]) = e.copy(size = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: InputGroup) + def withKey(v: String) = e.copy(key = v) + def withSize(v: String) = e.copy(size = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: ArrowDownIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: CopyIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ExternalLinkIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ArrowUpIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: BellIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ChevronUpIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: CloseIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: SettingsIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: QuestionIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ArrowUpDownIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: AddIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: StarIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: RadioGroup) + def withKey(v: String) = e.copy(key = v) + def withDefaultValue(v: String) = e.copy(defaultValue = v) + def withValue(v: String) = e.copy(value = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: WarningIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Tbody) + def withKey(v: String) = e.copy(key = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Textarea) + def withKey(v: String) = e.copy(key = v) + def withType(v: String) = e.copy(`type` = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withSize(v: String) = e.copy(size = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withValue(v: String) = e.copy(value = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: MenuDivider) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: WarningTwoIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: EmailIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: MinusIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: TriangleUpIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: HamburgerIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ArrowBackIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Th) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: SearchIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: DeleteIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Td) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: SimpleGrid) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withSpacingX(v: Option[String]) = e.copy(spacingX = v) + def withSpacingY(v: Option[String]) = e.copy(spacingY = v) + def withColumns(v: Int) = e.copy(columns = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Text) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withFontSize(v: Option[String]) = e.copy(fontSize = v) + def withNoOfLines(v: Option[Int]) = e.copy(noOfLines = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withAs(v: Option[String]) = e.copy(as = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withCasing(v: Option[String]) = e.copy(casing = v) + def withDecoration(v: Option[String]) = e.copy(decoration = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ChatIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Search2Icon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Button) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withSize(v: Option[String]) = e.copy(size = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withLeftIcon(v: Option[UiElement]) = e.copy(leftIcon = v) + def withRightIcon(v: Option[UiElement]) = e.copy(rightIcon = v) + def withIsActive(v: Option[Boolean]) = e.copy(isActive = v) + def withIsDisabled(v: Option[Boolean]) = e.copy(isDisabled = v) + def withIsLoading(v: Option[Boolean]) = e.copy(isLoading = v) + def withIsAttached(v: Option[Boolean]) = e.copy(isAttached = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + +extension (e: Option_) + def withKey(v: String) = e.copy(key = v) + def withValue(v: String) = e.copy(value = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: LinkIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ChevronLeftIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ArrowForwardIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ButtonGroup) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withSize(v: Option[String]) = e.copy(size = v) + def withWidth(v: Option[String]) = e.copy(width = v) + def withHeight(v: Option[String]) = e.copy(height = v) + def withBorder(v: Option[String]) = e.copy(border = v) + def withBorderColor(v: Option[String]) = e.copy(borderColor = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: NotAllowedIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: PhoneIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ChevronDownIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: HStack) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: VStack) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: TimeIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: InfoOutlineIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: CalendarIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: DragHandleIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: EditIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: QuickTable) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: String) = e.copy(variant = v) + def withColorScheme(v: String) = e.copy(colorScheme = v) + def withSize(v: String) = e.copy(size = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withCaption(v: Option[String]) = e.copy(caption = v) + def withHeaders(v: Seq[UiElement]) = e.copy(headers = v) + def withRows(v: Seq[Seq[UiElement]]) = e.copy(rows = v) + +extension (e: MoonIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: DownloadIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Menu) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: Circle) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: RepeatIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: UnlockIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Checkbox) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) + def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withCheckedV(v: Option[Boolean]) = e.copy(checkedV = v) + +extension (e: Radio) + def withKey(v: String) = e.copy(key = v) + def withValue(v: String) = e.copy(value = v) + def withText(v: String) = e.copy(text = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Badge) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withSize(v: String) = e.copy(size = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: CheckCircleIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: MenuItem) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withText(v: String) = e.copy(text = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: EditableTextarea) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: CheckIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: FormLabel) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: LockIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: TableContainer) + def withKey(v: String) = e.copy(key = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Tfoot) + def withKey(v: String) = e.copy(key = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Box) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withBg(v: String) = e.copy(bg = v) + def withW(v: String) = e.copy(w = v) + def withP(v: Int) = e.copy(p = v) + def withColor(v: String) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withAs(v: Option[String]) = e.copy(as = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: Thead) + def withKey(v: String) = e.copy(key = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: AtSignIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: SunIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: ViewOffIcon) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: TableCaption) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Select) + def withKey(v: String) = e.copy(key = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withValue(v: String) = e.copy(value = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withBorderColor(v: Option[String]) = e.copy(borderColor = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: FormHelperText) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: InputRightAddon) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + +extension (e: Switch) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) + def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: Input) + def withKey(v: String) = e.copy(key = v) + def withType(v: String) = e.copy(`type` = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withSize(v: String) = e.copy(size = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withValue(v: String) = e.copy(value = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + +extension (e: EditablePreview) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala new file mode 100644 index 00000000..fc9c38d1 --- /dev/null +++ b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala @@ -0,0 +1,52 @@ + +package org.terminal21.client.components.std + +// GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT + +import org.terminal21.client.components.UiElement.HasChildren +import org.terminal21.client.components.UiElement.HasEventHandler +import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.client.components.Keys +import org.terminal21.client.components.UiElement +import org.terminal21.client.ConnectedSession +import org.terminal21.client.OnChangeEventHandler + +extension (e: NewLine) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + + +extension (e: Header1) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + + +extension (e: Span) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + + +extension (e: Paragraph) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + + + + +extension (e: Em) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + + +extension (e: Input) + def withKey(v: String) = e.copy(key = v) + def withType(v: String) = e.copy(`type` = v) + def withDefaultValue(v: Option[String]) = e.copy(defaultValue = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withValue(v: Option[String]) = e.copy(value = v) + From 4b7353450261ef7640a26935bec5ec26309bafa0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 18:07:04 +0000 Subject: [PATCH 19/40] - --- .../main/scala/tests/chakra/Editables.scala | 4 ++-- .../src/main/scala/tests/chakra/Forms.scala | 20 +++++++++---------- .../terminal21/client/ConnectedSession.scala | 2 ++ .../client/components/UiElement.scala | 4 ++++ .../components/chakra/ChakraElement.scala | 4 ++-- .../client/components/std/StdElement.scala | 4 ++-- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala index 14693f61..505f5edf 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala @@ -15,14 +15,14 @@ object Editables: ) editable1.onChange: newValue => - status.withText(s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}").renderChanges() + status.withText(s"editable1 newValue = $newValue, verify editable1.value = ${editable1.current.value}").renderChanges() val editable2 = Editable(defaultValue = "For longer maybe-editable texts\nUse an EditableTextarea\nIt uses a textarea control.").withChildren( EditablePreview(), EditableTextarea() ) editable2.onChange: newValue => - status.withText(s"editable2 newValue = $newValue, verify editable2.value = ${editable2.value}").renderChanges() + status.withText(s"editable2 newValue = $newValue, verify editable2.value = ${editable2.current.value}").renderChanges() Seq( commonBox(text = "Editables"), diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala index 555fc84e..c352f2ae 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala @@ -16,13 +16,13 @@ object Forms: val email = Input(`type` = "email", value = "my@email.com") email.onChange: newValue => Seq( - status.withText(s"email input new value = $newValue, verify email.value = ${email.value}"), + status.withText(s"email input new value = $newValue, verify email.value = ${email.current.value}"), if newValue.contains("@") then emailRightAddOn.withChildren(okIcon) else emailRightAddOn.withChildren(notOkIcon) ).renderChanges() val description = Textarea(placeholder = "Please enter a few things about you") description.onChange: newValue => - status.withText(s"description input new value = $newValue, verify description.value = ${description.value}").renderChanges() + status.withText(s"description input new value = $newValue, verify description.value = ${description.current.value}").renderChanges() val select1 = Select(placeholder = "Please choose").withChildren( Option_(text = "Male", value = "male"), @@ -30,7 +30,7 @@ object Forms: ) select1.onChange: newValue => - status.withText(s"select1 input new value = $newValue, verify select1.value = ${select1.value}").renderChanges() + status.withText(s"select1 input new value = $newValue, verify select1.value = ${select1.current.value}").renderChanges() val select2 = Select(value = "1", bg = Some("tomato"), color = Some("black"), borderColor = Some("yellow")).withChildren( Option_(text = "First", value = "1"), @@ -40,21 +40,21 @@ object Forms: val password = Input(`type` = "password", value = "mysecret") val dob = Input(`type` = "datetime-local") dob.onChange: newValue => - status.withText(s"dob = $newValue , verify dob.value = ${dob.value}").renderChanges() + status.withText(s"dob = $newValue , verify dob.value = ${dob.current.value}").renderChanges() val color = Input(`type` = "color") color.onChange: newValue => - status.withText(s"color = $newValue , verify color.value = ${color.value}").renderChanges() + status.withText(s"color = $newValue , verify color.value = ${color.current.value}").renderChanges() val checkbox2 = Checkbox(text = "Check 2", defaultChecked = true) checkbox2.onChange: newValue => - status.withText(s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.checked}").renderChanges() + status.withText(s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.current.checked}").renderChanges() val checkbox1 = Checkbox(text = "Check 1") checkbox1.onChange: newValue => Seq( - status.withText(s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.checked}"), + status.withText(s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.current.checked}"), checkbox2.withIsDisabled(newValue) ).renderChanges() @@ -63,7 +63,7 @@ object Forms: switch1.onChange: newValue => Seq( - status.withText(s"switch1 checked is $newValue , verify switch1.checked = ${switch1.checked}"), + status.withText(s"switch1 checked is $newValue , verify switch1.checked = ${switch1.current.checked}"), switch2.withIsDisabled(newValue) ).renderChanges() @@ -76,7 +76,7 @@ object Forms: ) radioGroup.onChange: newValue => - status.withText(s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.value}").renderChanges() + status.withText(s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.current.value}").renderChanges() Seq( commonBox(text = "Forms"), @@ -136,7 +136,7 @@ object Forms: .onClick: () => status .withText( - s"Saved clicked. Email = ${email.value}, password = ${password.value}, dob = ${dob.value}, check1 = ${checkbox1.checked}, check2 = ${checkbox2.checked}, radio = ${radioGroup.value}" + s"Saved clicked. Email = ${email.current.value}, password = ${password.current.value}, dob = ${dob.current.value}, check1 = ${checkbox1.current.checked}, check2 = ${checkbox2.current.checked}, radio = ${radioGroup.current.value}" ) .renderChanges(), Button(text = "Cancel") diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index b5ee331d..02afcb7d 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -22,6 +22,7 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se def clear(): Unit = render() handlers.clear() + modifiedElements.clear() def addEventHandler(key: String, handler: EventHandler): Unit = handlers.addEventHandler(key, handler) @@ -109,3 +110,4 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se private val modifiedElements = TrieMap.empty[String, UiElement] def modified(e: UiElement): Unit = modifiedElements += e.key -> e + def currentState[A <: UiElement](e: A): A = modifiedElements.getOrElse(e.key, e).asInstanceOf[A] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 2631ff00..ab04223e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -21,6 +21,10 @@ object UiElement: case hc: HasChildren[_] => allDeep(hc.children) .flatten + trait Current[A <: UiElement]: + this: UiElement => + def current(using session: ConnectedSession): A = session.currentState(this.asInstanceOf[A]) + trait HasChildren[A <: UiElement]: this: A => def children: Seq[UiElement] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 66d51c89..65c5e3a8 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -1,6 +1,6 @@ package org.terminal21.client.components.chakra -import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} +import org.terminal21.client.components.UiElement.{Current, HasChildren, HasEventHandler, HasStyle} import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{ConnectedSession, OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} @@ -10,7 +10,7 @@ sealed trait CEJson extends UiElement * https://github.com/kostaskougios/terminal21-restapi/blob/main/examples/src/main/scala/tests/ChakraComponents.scala and it's related scala files under * https://github.com/kostaskougios/terminal21-restapi/tree/main/examples/src/main/scala/tests/chakra */ -sealed trait ChakraElement[A <: ChakraElement[A]] extends CEJson with HasStyle[A] +sealed trait ChakraElement[A <: ChakraElement[A]] extends CEJson with HasStyle[A] with Current[A] /** https://chakra-ui.com/docs/components/button */ diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala index 8c3a8ce3..750537cb 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala @@ -1,11 +1,11 @@ package org.terminal21.client.components.std -import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} +import org.terminal21.client.components.UiElement.{Current, HasChildren, HasEventHandler, HasStyle} import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{ConnectedSession, OnChangeEventHandler} sealed trait StdEJson extends UiElement -sealed trait StdElement[A <: UiElement] extends StdEJson with HasStyle[A] +sealed trait StdElement[A <: UiElement] extends StdEJson with HasStyle[A] with Current[A] case class Span(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Span]: override def style(v: Map[String, Any]) = copy(style = v) From 5f37723dc29113b2c50553b2e4a2dd51b19cef27 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 18:26:44 +0000 Subject: [PATCH 20/40] - --- .../src/main/scala/tests/chakra/Forms.scala | 2 +- .../components/chakra/ChakraElement.scala | 6 +- .../client/components/chakra/extensions.scala | 949 ++++++++++-------- .../client/components/std/extensions.scala | 1 + 4 files changed, 534 insertions(+), 424 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala index c352f2ae..ce591841 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala @@ -59,7 +59,7 @@ object Forms: ).renderChanges() val switch1 = Switch(text = "Switch 1") - val switch2 = Switch(text = "Switch 2") + val switch2 = Switch(text = "Switch 2", defaultChecked = true) switch1.onChange: newValue => Seq( diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 65c5e3a8..e3ed9c8b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -242,17 +242,17 @@ case class Radio( case class RadioGroup( key: String = Keys.nextKey, defaultValue: String = "", - value: String = "", + valueV: Option[String] = None, style: Map[String, Any] = Map.empty, children: Seq[UiElement] = Nil ) extends ChakraElement[RadioGroup] with HasEventHandler with HasChildren[RadioGroup] with OnChangeEventHandler.CanHandleOnChangeEvent[RadioGroup]: - - override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) + override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(valueV = Some(newValue))) override def withChildren(cn: UiElement*) = copy(children = cn) override def style(v: Map[String, Any]) = copy(style = v) + def value: String = valueV.getOrElse(defaultValue) case class Center( key: String = Keys.nextKey, diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala index b2790bf8..296fa6fc 100644 --- a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala +++ b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala @@ -1,804 +1,913 @@ + package org.terminal21.client.components.chakra // GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT +import org.terminal21.client.components.UiElement.Current import org.terminal21.client.components.UiElement.HasChildren import org.terminal21.client.components.UiElement.HasEventHandler import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.Keys import org.terminal21.client.components.UiElement +import org.terminal21.client.ConnectedSession import org.terminal21.client.OnChangeBooleanEventHandler import org.terminal21.client.OnChangeEventHandler import org.terminal21.client.OnClickEventHandler extension (e: PlusSquareIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: FormControl) - def withKey(v: String) = e.copy(key = v) - def withAs(v: String) = e.copy(as = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withAs(v: String) = e.copy(as = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: ArrowLeftIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Center) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ViewIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Square) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Image) - def withKey(v: String) = e.copy(key = v) - def withSrc(v: String) = e.copy(src = v) - def withAlt(v: String) = e.copy(alt = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) + def withKey(v: String) = e.copy(key = v) + def withSrc(v: String) = e.copy(src = v) + def withAlt(v: String) = e.copy(alt = v) + def withBoxSize(v: Option[String]) = e.copy(boxSize = v) def withBorderRadius(v: Option[String]) = e.copy(borderRadius = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: SpinnerIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: EditableInput) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ArrowRightIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Table) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: String) = e.copy(variant = v) - def withSize(v: String) = e.copy(size = v) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: String) = e.copy(variant = v) + def withSize(v: String) = e.copy(size = v) def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: SmallCloseIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: InputLeftAddon) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: MenuList) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: TriangleDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Editable) - def withKey(v: String) = e.copy(key = v) - def withDefaultValue(v: String) = e.copy(defaultValue = v) - def withValue(v: String) = e.copy(value = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withDefaultValue(v: String) = e.copy(defaultValue = v) + def withValue(v: String) = e.copy(value = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: InfoIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: AttachmentIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: RepeatClockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: QuestionOutlineIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: SmallAddIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ChevronRightIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Tr) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: UpDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: MenuButton) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withSize(v: Option[String]) = e.copy(size = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withSize(v: Option[String]) = e.copy(size = v) def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: InputGroup) - def withKey(v: String) = e.copy(key = v) - def withSize(v: String) = e.copy(size = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withSize(v: String) = e.copy(size = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: ArrowDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: CopyIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ExternalLinkIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ArrowUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: BellIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ChevronUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: CloseIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: SettingsIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: QuestionIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ArrowUpDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: AddIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: StarIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: RadioGroup) - def withKey(v: String) = e.copy(key = v) - def withDefaultValue(v: String) = e.copy(defaultValue = v) - def withValue(v: String) = e.copy(value = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withDefaultValue(v: String) = e.copy(defaultValue = v) + def withValueV(v: Option[String]) = e.copy(valueV = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: WarningIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Tbody) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Textarea) - def withKey(v: String) = e.copy(key = v) - def withType(v: String) = e.copy(`type` = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withSize(v: String) = e.copy(size = v) + def withKey(v: String) = e.copy(key = v) + def withType(v: String) = e.copy(`type` = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withSize(v: String) = e.copy(size = v) def withVariant(v: Option[String]) = e.copy(variant = v) - def withValue(v: String) = e.copy(value = v) + def withValue(v: String) = e.copy(value = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + + extension (e: MenuDivider) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: WarningTwoIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: EmailIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: MinusIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: TriangleUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: HamburgerIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ArrowBackIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Th) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: SearchIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: DeleteIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Td) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: SimpleGrid) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) def withSpacingX(v: Option[String]) = e.copy(spacingX = v) def withSpacingY(v: Option[String]) = e.copy(spacingY = v) - def withColumns(v: Int) = e.copy(columns = v) + def withColumns(v: Int) = e.copy(columns = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Text) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withFontSize(v: Option[String]) = e.copy(fontSize = v) - def withNoOfLines(v: Option[Int]) = e.copy(noOfLines = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withAs(v: Option[String]) = e.copy(as = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withCasing(v: Option[String]) = e.copy(casing = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withFontSize(v: Option[String]) = e.copy(fontSize = v) + def withNoOfLines(v: Option[Int]) = e.copy(noOfLines = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withAs(v: Option[String]) = e.copy(as = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withCasing(v: Option[String]) = e.copy(casing = v) def withDecoration(v: Option[String]) = e.copy(decoration = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ChatIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Search2Icon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Button) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withSize(v: Option[String]) = e.copy(size = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withLeftIcon(v: Option[UiElement]) = e.copy(leftIcon = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withSize(v: Option[String]) = e.copy(size = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withLeftIcon(v: Option[UiElement]) = e.copy(leftIcon = v) def withRightIcon(v: Option[UiElement]) = e.copy(rightIcon = v) - def withIsActive(v: Option[Boolean]) = e.copy(isActive = v) - def withIsDisabled(v: Option[Boolean]) = e.copy(isDisabled = v) - def withIsLoading(v: Option[Boolean]) = e.copy(isLoading = v) - def withIsAttached(v: Option[Boolean]) = e.copy(isAttached = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withIsActive(v: Option[Boolean]) = e.copy(isActive = v) + def withIsDisabled(v: Option[Boolean]) = e.copy(isDisabled = v) + def withIsLoading(v: Option[Boolean]) = e.copy(isLoading = v) + def withIsAttached(v: Option[Boolean]) = e.copy(isAttached = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + extension (e: Option_) - def withKey(v: String) = e.copy(key = v) - def withValue(v: String) = e.copy(value = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withValue(v: String) = e.copy(value = v) + def withText(v: String) = e.copy(text = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: LinkIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ChevronLeftIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ArrowForwardIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ButtonGroup) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withSize(v: Option[String]) = e.copy(size = v) - def withWidth(v: Option[String]) = e.copy(width = v) - def withHeight(v: Option[String]) = e.copy(height = v) - def withBorder(v: Option[String]) = e.copy(border = v) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withSize(v: Option[String]) = e.copy(size = v) + def withWidth(v: Option[String]) = e.copy(width = v) + def withHeight(v: Option[String]) = e.copy(height = v) + def withBorder(v: Option[String]) = e.copy(border = v) def withBorderColor(v: Option[String]) = e.copy(borderColor = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: NotAllowedIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: PhoneIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ChevronDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: HStack) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: VStack) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withSpacing(v: Option[String]) = e.copy(spacing = v) + def withAlign(v: Option[String]) = e.copy(align = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: TimeIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: InfoOutlineIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: CalendarIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: DragHandleIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: EditIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: QuickTable) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: String) = e.copy(variant = v) - def withColorScheme(v: String) = e.copy(colorScheme = v) - def withSize(v: String) = e.copy(size = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withCaption(v: Option[String]) = e.copy(caption = v) - def withHeaders(v: Seq[UiElement]) = e.copy(headers = v) + def withKey(v: String) = e.copy(key = v) + def withVariant(v: String) = e.copy(variant = v) + def withColorScheme(v: String) = e.copy(colorScheme = v) + def withSize(v: String) = e.copy(size = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withCaption(v: Option[String]) = e.copy(caption = v) + def withHeaders(v: Seq[UiElement]) = e.copy(headers = v) def withRows(v: Seq[Seq[UiElement]]) = e.copy(rows = v) + extension (e: MoonIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: DownloadIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Menu) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: Circle) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) + def withColor(v: Option[String]) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: RepeatIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: UnlockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Checkbox) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) - def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) + def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withCheckedV(v: Option[Boolean]) = e.copy(checkedV = v) + extension (e: Radio) - def withKey(v: String) = e.copy(key = v) - def withValue(v: String) = e.copy(value = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withValue(v: String) = e.copy(value = v) + def withText(v: String) = e.copy(text = v) def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Badge) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withSize(v: String) = e.copy(size = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withVariant(v: Option[String]) = e.copy(variant = v) + def withSize(v: String) = e.copy(size = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: CheckCircleIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + + extension (e: MenuItem) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withText(v: String) = e.copy(text = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: EditableTextarea) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: CheckIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: FormLabel) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: LockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: TableContainer) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Tfoot) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Box) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withBg(v: String) = e.copy(bg = v) - def withW(v: String) = e.copy(w = v) - def withP(v: Int) = e.copy(p = v) - def withColor(v: String) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withAs(v: Option[String]) = e.copy(as = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withBg(v: String) = e.copy(bg = v) + def withW(v: String) = e.copy(w = v) + def withP(v: Int) = e.copy(p = v) + def withColor(v: String) = e.copy(color = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withAs(v: Option[String]) = e.copy(as = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: Thead) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: AtSignIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: SunIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: ViewOffIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) + def withKey(v: String) = e.copy(key = v) + def withW(v: Option[String]) = e.copy(w = v) + def withH(v: Option[String]) = e.copy(h = v) def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withColor(v: Option[String]) = e.copy(color = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: TableCaption) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: Select) - def withKey(v: String) = e.copy(key = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withValue(v: String) = e.copy(value = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withColor(v: Option[String]) = e.copy(color = v) + def withKey(v: String) = e.copy(key = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withValue(v: String) = e.copy(value = v) + def withBg(v: Option[String]) = e.copy(bg = v) + def withColor(v: Option[String]) = e.copy(color = v) def withBorderColor(v: Option[String]) = e.copy(borderColor = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: FormHelperText) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: InputRightAddon) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) + def withStyle(v: Map[String, Any]) = e.copy(style = v) def withChildren(v: Seq[UiElement]) = e.copy(children = v) + extension (e: Switch) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) + def withKey(v: String) = e.copy(key = v) + def withText(v: String) = e.copy(text = v) def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) - def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) + def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + def withCheckedV(v: Option[Boolean]) = e.copy(checkedV = v) + extension (e: Input) - def withKey(v: String) = e.copy(key = v) - def withType(v: String) = e.copy(`type` = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withSize(v: String) = e.copy(size = v) + def withKey(v: String) = e.copy(key = v) + def withType(v: String) = e.copy(`type` = v) + def withPlaceholder(v: String) = e.copy(placeholder = v) + def withSize(v: String) = e.copy(size = v) def withVariant(v: Option[String]) = e.copy(variant = v) - def withValue(v: String) = e.copy(value = v) + def withValue(v: String) = e.copy(value = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + extension (e: EditablePreview) - def withKey(v: String) = e.copy(key = v) + def withKey(v: String) = e.copy(key = v) def withStyle(v: Map[String, Any]) = e.copy(style = v) + diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala index fc9c38d1..bc0b4e4d 100644 --- a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala +++ b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala @@ -3,6 +3,7 @@ package org.terminal21.client.components.std // GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT +import org.terminal21.client.components.UiElement.Current import org.terminal21.client.components.UiElement.HasChildren import org.terminal21.client.components.UiElement.HasEventHandler import org.terminal21.client.components.UiElement.HasStyle From 35ba77d377e7c8991fefaed62a2590aa50f2ab64 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 18:40:51 +0000 Subject: [PATCH 21/40] - --- end-to-end-tests/src/main/scala/tests/chakra/Forms.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala index ce591841..f693b29c 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala @@ -136,7 +136,7 @@ object Forms: .onClick: () => status .withText( - s"Saved clicked. Email = ${email.current.value}, password = ${password.current.value}, dob = ${dob.current.value}, check1 = ${checkbox1.current.checked}, check2 = ${checkbox2.current.checked}, radio = ${radioGroup.current.value}" + s"Saved clicked. Email = ${email.current.value}, password = ${password.current.value}, dob = ${dob.current.value}, check1 = ${checkbox1.current.checked}, check2 = ${checkbox2.current.checked}, radio = ${radioGroup.current.value}, switch1 = ${switch1.current.checked}, switch2 = ${switch2.current.checked}" ) .renderChanges(), Button(text = "Cancel") From 26758c1f225edeaf7a2fbac4457fc4e0c13b7fde Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 19:10:37 +0000 Subject: [PATCH 22/40] - --- .../src/main/scala/tests/chakra/DataDisplay.scala | 11 +++++++++++ .../org/terminal21/client/ConnectedSession.scala | 7 +++++-- .../terminal21/client/components/UiComponent.scala | 2 ++ .../org/terminal21/client/components/UiElement.scala | 2 -- .../client/components/chakra/QuickTable.scala | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala b/end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala index 4265da5a..320e493d 100644 --- a/end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala +++ b/end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala @@ -12,6 +12,15 @@ object DataDisplay: Th(text = "into"), Th(text = "multiply by", isNumeric = true) ) + val quickTable1 = QuickTable() + .headers("id", "name") + .caption("Quick Table Caption") + .rows( + Seq( + Seq(1, "Kostas"), + Seq(2, "Andreas") + ) + ) Seq( commonBox(text = "Badges"), HStack().withChildren( @@ -23,6 +32,8 @@ object DataDisplay: Button(text = "test") ) ), + commonBox(text = "Quick Tables"), + quickTable1, commonBox(text = "Tables"), TableContainer().withChildren( Table(variant = "striped", colorScheme = Some("teal"), size = "lg").withChildren( diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 02afcb7d..6490bb34 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -4,7 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, allDeep} -import org.terminal21.client.components.{UiElement, UiElementEncoding} +import org.terminal21.client.components.{UiComponent, UiElement, UiElementEncoding} import org.terminal21.client.internal.EventHandlers import org.terminal21.model.* import org.terminal21.ui.std.{ServerJson, SessionsService} @@ -86,13 +86,14 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se private def toJson(elements: Seq[UiElement]): ServerJson = val flat = elements.flatMap(_.flat) - ServerJson( + val sj = ServerJson( elements.map(_.key), flat .map: el => ( el.key, el match + case e: UiComponent => encoding.uiElementEncoder(e).deepDropNullValues case e: HasChildren[_] => encoding.uiElementEncoder(e.withChildren()).deepDropNullValues case e => encoding.uiElementEncoder(e).deepDropNullValues ) @@ -102,11 +103,13 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se ( e.key, e match + case e: UiComponent => e.rendered.map(_.key) case e: HasChildren[_] => e.children.map(_.key) case _ => Nil ) .toMap ) + sj private val modifiedElements = TrieMap.empty[String, UiElement] def modified(e: UiElement): Unit = modifiedElements += e.key -> e diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index 82e011ee..a67d36c3 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -5,4 +5,6 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ trait UiComponent extends UiElement: + // Note: impl as a val to avoid UiElements getting a random key def rendered: Seq[UiElement] + override def flat = Seq(this) ++ rendered.flatMap(_.flat) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index ab04223e..eae2b0c2 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -28,9 +28,7 @@ object UiElement: trait HasChildren[A <: UiElement]: this: A => def children: Seq[UiElement] - override def flat: Seq[UiElement] = Seq(this) ++ children.flatMap(_.flat) - def withChildren(cn: UiElement*): A trait HasEventHandler: diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 70f20756..133e743c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -15,7 +15,7 @@ case class QuickTable( ) extends UiComponent with HasStyle[QuickTable]: - override def rendered: Seq[UiElement] = + override lazy val rendered: Seq[UiElement] = val head = Thead(children = Seq(Tr(children = headers.map(h => Th(children = Seq(h)))))) val body = Tbody( children = rows.map: row => From 62de2d799d1dd743b98e905f2d6b1734e8847ba2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 17 Jan 2024 19:11:59 +0000 Subject: [PATCH 23/40] - --- .../scala/org/terminal21/client/components/UiComponent.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index a67d36c3..bae57edb 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -5,6 +5,6 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ trait UiComponent extends UiElement: - // Note: impl as a val to avoid UiElements getting a random key + // Note: impl as a lazy val to avoid UiElements getting a random key def rendered: Seq[UiElement] override def flat = Seq(this) ++ rendered.flatMap(_.flat) From 739ef0b24b66b3eb61fb2788be5cbe5392abe27e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 18 Jan 2024 14:19:28 +0000 Subject: [PATCH 24/40] - --- build.sbt | 7 +- .../tastyextractor/StructureExtractor.scala | 4 +- .../PropertiesExtensionGenerator.scala | 23 +- .../client/components/mathjax/MathJax.scala | 2 +- .../components/{nivo => }/NivoLib.scala | 8 +- .../client/components/nivo/NivoElement.scala | 75 +- .../sparklib/CalculationsExtensions.scala | 4 +- .../sparklib/endtoend/SparkBasics.scala | 2 +- .../client/components/StdUiCalculation.scala | 12 +- .../client/components/UiElement.scala | 2 +- .../components/chakra/ChakraElement.scala | 700 +++++++++++--- .../client/components/chakra/QuickTable.scala | 11 +- .../client/components/std/StdElement.scala | 27 +- .../client/components/chakra/extensions.scala | 913 ------------------ .../client/components/std/extensions.scala | 53 - 15 files changed, 677 insertions(+), 1166 deletions(-) rename terminal21-nivo/src/main/scala/org/terminal21/client/components/{nivo => }/NivoLib.scala (81%) delete mode 100644 terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala delete mode 100644 terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala diff --git a/build.sbt b/build.sbt index 4e079055..f79a1821 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import sbt.librarymanagement.ModuleFilter + /** This build has different sections for each integration. I.e. an http4s section and a kafka section. These sections are not related to each other, please * examine the section you're interested in. */ @@ -121,9 +123,6 @@ lazy val `terminal21-client-common` = project lazy val `terminal21-ui-std` = project .settings( commonSettings, - Compile / unmanagedSourceDirectories ++= { - Seq(baseDirectory.value / "src" / "main" / "ui-generated") - }, callerExports := Seq(s"io.github.kostaskougios:terminal21-ui-std-exports_3:${version.value}"), callerJsonSerialization := true, callerHelidonClientTransport := true, @@ -183,7 +182,7 @@ lazy val `terminal21-mathjax` = project ) .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") -lazy val `terminal21-code-generation` = project +lazy val `terminal21-code-generation`: Project = project .settings( commonSettings, libraryDependencies ++= Seq( diff --git a/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala b/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala index 2489125b..7cbef960 100644 --- a/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala +++ b/terminal21-code-generation/src/main/scala/functions/tastyextractor/StructureExtractor.scala @@ -105,9 +105,9 @@ private class StructureExtractorInspector extends Inspector: /** Converts tasty files to an easier to digest domain model */ class StructureExtractor: - def fromFiles(files: List[String]): Seq[EPackage] = + def fromFiles(files: List[String], dependencyCp: List[String]): Seq[EPackage] = val inspector = new StructureExtractorInspector - TastyInspector.inspectAllTastyFiles(files, Nil, Nil)(inspector) + TastyInspector.inspectAllTastyFiles(files, Nil, dependencyCp)(inspector) inspector.packages.toSeq object StructureExtractor: diff --git a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala index c1fde596..0f2b6dbf 100644 --- a/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala +++ b/terminal21-code-generation/src/main/scala/org/terminal21/codegen/PropertiesExtensionGenerator.scala @@ -9,7 +9,8 @@ object PropertiesExtensionGenerator: private val e = StructureExtractor() def extract(tastys: List[String]): Code = - val packages = e.fromFiles(tastys) + val mainCp = detectClasspath(new File("../terminal21-ui-std")) + val packages = e.fromFiles(tastys, List(mainCp.getAbsolutePath)) val ext = packages.map: p => val extCode = p.types .filterNot(_.name.contains("$")) @@ -37,21 +38,31 @@ object PropertiesExtensionGenerator: case _ => n def createExtension(t: EType): String = - val methods = t.vals.map: vl => - s"def with${vl.name.capitalize}(v: ${vl.`type`.simplifiedCode}) = e.copy(${fix(vl.name)} = v)" + val skipVals = Set("children", "rendered", "style") + val methods = t.vals + .filterNot(v => skipVals(v.name)) + .map: vl => + s"def with${vl.name.capitalize}(v: ${vl.`type`.simplifiedCode}) = copy(${fix(vl.name)} = v)" s""" |extension (e: ${t.name}) | ${methods.mkString("\n ")} |""".stripMargin - def generate(moduleDir: File, pckg: String): Unit = + def detectClasspath(moduleDir: File) = val targetDir = new File(moduleDir, "target") val scala3Dir = targetDir.listFiles().find(_.getName.startsWith("scala-3")).get - val classesDir = new File(scala3Dir, s"classes/${pckg.replace('.', '/')}") - val code = extract(classesDir.listFiles().filter(_.getName.endsWith(".tasty")).filterNot(_.getName.contains("$")).map(_.getAbsolutePath).toList) + val classesDir = new File(scala3Dir, "classes") + classesDir + + def generate(moduleDir: File, pckg: String): Unit = + println(s"Generating for $pckg") + val classesRootDir = detectClasspath(moduleDir) + val classesDir = new File(classesRootDir, pckg.replace('.', '/')) + val code = extract(classesDir.listFiles().filter(_.getName.endsWith(".tasty")).filterNot(_.getName.contains("$")).map(_.getAbsolutePath).toList) code.writeTo(s"${moduleDir.getAbsolutePath}/src/main/ui-generated") @main def propertiesExtensionGeneratorApp(): Unit = generate(new File("../terminal21-ui-std"), "org.terminal21.client.components.std") generate(new File("../terminal21-ui-std"), "org.terminal21.client.components.chakra") +// generate(new File("../terminal21-nivo"), "org.terminal21.client.components.nivo") diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index adce7ce2..b7fcec05 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -15,4 +15,4 @@ case class MathJax( @volatile var style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib ) extends MathJaxElement with HasStyle[MathJax]: - override def style(v: Map[String, Any]): MathJax = copy(style = v) + override def withStyle(v: Map[String, Any]): MathJax = copy(style = v) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/NivoLib.scala similarity index 81% rename from terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala rename to terminal21-nivo/src/main/scala/org/terminal21/client/components/NivoLib.scala index a7594003..4a68b032 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/NivoLib.scala @@ -1,10 +1,10 @@ -package org.terminal21.client.components.nivo +package org.terminal21.client.components -import io.circe.{Encoder, Json} -import org.terminal21.client.components.{ComponentLib, UiElement} -import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* +import io.circe.* +import org.terminal21.client.components.nivo.NEJson +import org.terminal21.client.components.{ComponentLib, UiElement} object NivoLib extends ComponentLib: import org.terminal21.client.components.StdElementEncoding.given diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 210aed7e..4ce70ded 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -12,25 +12,27 @@ case class ResponsiveLine( key: String = Keys.nextKey, // to give width and height, we wrap the component in a wrapper element. Height must be provided // for nivo components to be visible - @volatile var style: Map[String, Any] = Map("height" -> "400px"), - @volatile var data: Seq[Serie] = Nil, - @volatile var margin: Margin = Margin(right = 110), - @volatile var xScale: Scale = Scale.Point, - @volatile var yScale: Scale = Scale(), - @volatile var yFormat: String = " >-.2f", - @volatile var axisTop: Option[Axis] = None, - @volatile var axisRight: Option[Axis] = None, - @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), - @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), - @volatile var pointSize: Int = 10, - @volatile var pointColor: Map[String, String] = Map("theme" -> "background"), - @volatile var pointBorderWidth: Int = 2, - @volatile var pointBorderColor: Map[String, String] = Map("from" -> "serieColor"), - @volatile var pointLabelYOffset: Int = -12, - @volatile var useMesh: Boolean = true, - @volatile var legends: Seq[Legend] = Nil + style: Map[String, Any] = Map("height" -> "400px"), + data: Seq[Serie] = Nil, + margin: Margin = Margin(right = 110), + xScale: Scale = Scale.Point, + yScale: Scale = Scale(), + yFormat: String = " >-.2f", + axisTop: Option[Axis] = None, + axisRight: Option[Axis] = None, + axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + pointSize: Int = 10, + pointColor: Map[String, String] = Map("theme" -> "background"), + pointBorderWidth: Int = 2, + pointBorderColor: Map[String, String] = Map("from" -> "serieColor"), + pointLabelYOffset: Int = -12, + useMesh: Boolean = true, + legends: Seq[Legend] = Nil ) extends NivoElement[ResponsiveLine]: - override def style(v: Map[String, Any]): ResponsiveLine = copy(style = v) + override def withStyle(v: Map[String, Any]): ResponsiveLine = copy(style = v) + def withKey(v: String) = copy(key = v) + def withData(data: Seq[Serie]) = copy(data = data) /** https://nivo.rocks/bar/ */ @@ -38,22 +40,23 @@ case class ResponsiveBar( key: String = Keys.nextKey, // to give width and height, we wrap the component in a wrapper element. Height must be provided // for nivo components to be visible - @volatile var style: Map[String, Any] = Map("height" -> "400px"), - @volatile var data: Seq[Seq[BarDatum]] = Nil, - @volatile var keys: Seq[String] = Nil, - @volatile var indexBy: String = "", - @volatile var margin: Margin = Margin(right = 110), - @volatile var padding: Float = 0, - @volatile var valueScale: Scale = Scale(), - @volatile var indexScale: Scale = Scale(), - @volatile var colors: Map[String, String] = Map("scheme" -> "nivo"), - @volatile var defs: Seq[Defs] = Nil, - @volatile var fill: Seq[Fill] = Nil, - @volatile var axisTop: Option[Axis] = None, - @volatile var axisRight: Option[Axis] = None, - @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), - @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), - @volatile var legends: Seq[Legend] = Nil, - @volatile var ariaLabel: String = "Chart Label" + style: Map[String, Any] = Map("height" -> "400px"), + data: Seq[Seq[BarDatum]] = Nil, + keys: Seq[String] = Nil, + indexBy: String = "", + margin: Margin = Margin(right = 110), + padding: Float = 0, + valueScale: Scale = Scale(), + indexScale: Scale = Scale(), + colors: Map[String, String] = Map("scheme" -> "nivo"), + defs: Seq[Defs] = Nil, + fill: Seq[Fill] = Nil, + axisTop: Option[Axis] = None, + axisRight: Option[Axis] = None, + axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + legends: Seq[Legend] = Nil, + ariaLabel: String = "Chart Label" ) extends NivoElement[ResponsiveBar]: - override def style(v: Map[String, Any]): ResponsiveBar = copy(style = v) + override def withStyle(v: Map[String, Any]): ResponsiveBar = copy(style = v) + def withData(data: Seq[Seq[BarDatum]]) = copy(data = data) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala index 718c3917..309a81ca 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala @@ -9,7 +9,7 @@ import org.terminal21.sparklib.calculations.{ReadWriter, StdUiSparkCalculation} extension [OUT: ReadWriter](ds: OUT) def visualize(name: String, dataUi: UiElement with HasStyle[_])( - toUi: OUT => Unit + toUi: OUT => UiElement )(using session: ConnectedSession, executor: FiberExecutor, @@ -17,7 +17,7 @@ extension [OUT: ReadWriter](ds: OUT) ) = val ui = new StdUiSparkCalculation[OUT](Keys.nextKey, name, dataUi): override protected def whenResultsReady(results: OUT): Unit = - try toUi(results) + try toUi(results).renderChanges() catch case t: Throwable => t.printStackTrace() super.whenResultsReady(results) override def nonCachedCalculation: OUT = ds diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index c2a0a6e8..ec6d1a2a 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -57,7 +57,7 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val sourceFileChart = sortedSourceFilesDS.visualize("Biggest Code Files", chart): results => val data = results.take(10).map(cf => Datum(StringUtils.substringBeforeLast(cf.name, ".scala"), cf.numOfLines)).toList - chart.data = Seq(Serie("Scala", data = data)) + chart.withData(Seq(Serie("Scala", data = data))) Seq( codeFilesCalculation, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index 88523f8e..1493f219 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -3,7 +3,7 @@ package org.terminal21.client.components import functions.fibers.FiberExecutor import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.components.chakra.{withColorScheme, *} +import org.terminal21.client.components.chakra.* import java.util.concurrent.atomic.AtomicBoolean @@ -19,14 +19,14 @@ trait StdUiCalculation[OUT]( extends Calculation[OUT] with UiComponent: private val running = new AtomicBoolean(false) - val badge = Badge() - val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + lazy val badge = Badge() + lazy val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => if running.compareAndSet(false, true) then try reCalculate() finally running.set(false) - override def rendered: Seq[UiElement] = + override lazy val rendered: Seq[UiElement] = val header = Box( bg = "green", p = 4, @@ -47,13 +47,13 @@ trait StdUiCalculation[OUT]( override protected def whenResultsNotReady(): Unit = session.renderChanges( badge.withText("Calculating").withColorScheme(Some("purple")), - dataUi.style(dataUi.style + ("filter" -> "grayscale(100%)")), + dataUi.withStyle(dataUi.style + ("filter" -> "grayscale(100%)")), recalc.withIsDisabled(Some(true)) ) super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = - val newDataUi = dataUi.style(dataUi.style - "filter") + val newDataUi = dataUi.withStyle(dataUi.style - "filter") session.renderChanges( badge.withText("Ready").withColorScheme(None), newDataUi, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index eae2b0c2..8658ceb3 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -36,4 +36,4 @@ object UiElement: trait HasStyle[A <: UiElement]: def style: Map[String, Any] - def style(v: Map[String, Any]): A + def withStyle(v: Map[String, Any]): A diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index e3ed9c8b..128deac6 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -30,7 +30,19 @@ case class Button( spacing: Option[String] = None ) extends ChakraElement[Button] with OnClickEventHandler.CanHandleOnClickEvent[Button]: - override def style(v: Map[String, Any]): Button = copy(style = v) + override def withStyle(v: Map[String, Any]): Button = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withSize(v: Option[String]) = copy(size = v) + def withVariant(v: Option[String]) = copy(variant = v) + def withColorScheme(v: Option[String]) = copy(colorScheme = v) + def withLeftIcon(v: Option[UiElement]) = copy(leftIcon = v) + def withRightIcon(v: Option[UiElement]) = copy(rightIcon = v) + def withIsActive(v: Option[Boolean]) = copy(isActive = v) + def withIsDisabled(v: Option[Boolean]) = copy(isDisabled = v) + def withIsLoading(v: Option[Boolean]) = copy(isLoading = v) + def withIsAttached(v: Option[Boolean]) = copy(isAttached = v) + def withSpacing(v: Option[String]) = copy(spacing = v) /** https://chakra-ui.com/docs/components/button */ @@ -47,8 +59,16 @@ case class ButtonGroup( children: Seq[UiElement] = Nil ) extends ChakraElement[ButtonGroup] with HasChildren[ButtonGroup]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]): ButtonGroup = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]): ButtonGroup = copy(style = v) + def withKey(v: String) = copy(key = v) + def withVariant(v: Option[String]) = copy(variant = v) + def withSpacing(v: Option[String]) = copy(spacing = v) + def withSize(v: Option[String]) = copy(size = v) + def withWidth(v: Option[String]) = copy(width = v) + def withHeight(v: Option[String]) = copy(height = v) + def withBorder(v: Option[String]) = copy(border = v) + def withBorderColor(v: Option[String]) = copy(borderColor = v) /** https://chakra-ui.com/docs/components/box */ @@ -64,8 +84,15 @@ case class Box( children: Seq[UiElement] = Nil ) extends ChakraElement[Box] with HasChildren[Box]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withBg(v: String) = copy(bg = v) + def withW(v: String) = copy(w = v) + def withP(v: Int) = copy(p = v) + def withColor(v: String) = copy(color = v) + def withAs(v: Option[String]) = copy(as = v) /** https://chakra-ui.com/docs/components/stack */ @@ -77,8 +104,11 @@ case class HStack( children: Seq[UiElement] = Nil ) extends ChakraElement[HStack] with HasChildren[HStack]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withSpacing(v: Option[String]) = copy(spacing = v) + def withAlign(v: Option[String]) = copy(align = v) case class VStack( key: String = Keys.nextKey, @@ -88,8 +118,11 @@ case class VStack( children: Seq[UiElement] = Nil ) extends ChakraElement[VStack] with HasChildren[VStack]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withSpacing(v: Option[String]) = copy(spacing = v) + def withAlign(v: Option[String]) = copy(align = v) case class SimpleGrid( key: String = Keys.nextKey, @@ -101,8 +134,13 @@ case class SimpleGrid( style: Map[String, Any] = Map.empty ) extends ChakraElement[SimpleGrid] with HasChildren[SimpleGrid]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withSpacing(v: Option[String]) = copy(spacing = v) + def withSpacingX(v: Option[String]) = copy(spacingX = v) + def withSpacingY(v: Option[String]) = copy(spacingY = v) + def withColumns(v: Int) = copy(columns = v) /** https://chakra-ui.com/docs/components/editable */ @@ -119,16 +157,22 @@ case class Editable( override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withDefaultValue(v: String) = copy(defaultValue = v) + def withValue(v: String) = copy(value = v) case class EditablePreview(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditablePreview]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class EditableInput(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditableInput]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class EditableTextarea(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[EditableTextarea]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -139,8 +183,10 @@ case class FormControl( children: Seq[UiElement] = Nil ) extends ChakraElement[FormControl] with HasChildren[FormControl]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withAs(v: String) = copy(as = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -151,8 +197,10 @@ case class FormLabel( children: Seq[UiElement] = Nil ) extends ChakraElement[FormLabel] with HasChildren[FormLabel]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) /** https://chakra-ui.com/docs/components/form-control */ @@ -163,8 +211,10 @@ case class FormHelperText( children: Seq[UiElement] = Nil ) extends ChakraElement[FormHelperText] with HasChildren[FormHelperText]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) /** https://chakra-ui.com/docs/components/input */ @@ -180,7 +230,13 @@ case class Input( with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Input]: override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withType(v: String) = copy(`type` = v) + def withPlaceholder(v: String) = copy(placeholder = v) + def withSize(v: String) = copy(size = v) + def withVariant(v: Option[String]) = copy(variant = v) + def withValue(v: String) = copy(value = v) case class InputGroup( key: String = Keys.nextKey, @@ -189,8 +245,10 @@ case class InputGroup( children: Seq[UiElement] = Nil ) extends ChakraElement[InputGroup] with HasChildren[InputGroup]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withSize(v: String) = copy(size = v) case class InputLeftAddon( key: String = Keys.nextKey, @@ -199,8 +257,10 @@ case class InputLeftAddon( children: Seq[UiElement] = Nil ) extends ChakraElement[InputLeftAddon] with HasChildren[InputLeftAddon]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class InputRightAddon( key: String = Keys.nextKey, @@ -209,8 +269,10 @@ case class InputRightAddon( children: Seq[UiElement] = Nil ) extends ChakraElement[InputRightAddon] with HasChildren[InputRightAddon]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) /** https://chakra-ui.com/docs/components/checkbox */ @@ -226,7 +288,11 @@ case class Checkbox( with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Checkbox]: def checked: Boolean = checkedV.getOrElse(defaultChecked) override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(checkedV = Some(newValue.toBoolean))) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withDefaultChecked(v: Boolean) = copy(defaultChecked = v) + def withIsDisabled(v: Boolean) = copy(isDisabled = v) /** https://chakra-ui.com/docs/components/radio */ @@ -237,7 +303,11 @@ case class Radio( colorScheme: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[Radio]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withValue(v: String) = copy(value = v) + def withText(v: String) = copy(text = v) + def withColorScheme(v: Option[String]) = copy(colorScheme = v) case class RadioGroup( key: String = Keys.nextKey, @@ -251,8 +321,10 @@ case class RadioGroup( with OnChangeEventHandler.CanHandleOnChangeEvent[RadioGroup]: override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(valueV = Some(newValue))) override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) def value: String = valueV.getOrElse(defaultValue) + def withKey(v: String) = copy(key = v) + def withDefaultValue(v: String) = copy(defaultValue = v) case class Center( key: String = Keys.nextKey, @@ -265,8 +337,14 @@ case class Center( style: Map[String, Any] = Map.empty ) extends ChakraElement[Center] with HasChildren[Center]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withBg(v: Option[String]) = copy(bg = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withColor(v: Option[String]) = copy(color = v) case class Circle( key: String = Keys.nextKey, @@ -279,8 +357,14 @@ case class Circle( style: Map[String, Any] = Map.empty ) extends ChakraElement[Circle] with HasChildren[Circle]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withBg(v: Option[String]) = copy(bg = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withColor(v: Option[String]) = copy(color = v) case class Square( key: String = Keys.nextKey, @@ -293,8 +377,14 @@ case class Square( style: Map[String, Any] = Map.empty ) extends ChakraElement[Square] with HasChildren[Square]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withBg(v: Option[String]) = copy(bg = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -306,7 +396,12 @@ case class AddIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[AddIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -318,7 +413,12 @@ case class ArrowBackIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowBackIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -330,7 +430,12 @@ case class ArrowDownIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowDownIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -342,7 +447,12 @@ case class ArrowForwardIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowForwardIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -354,7 +464,12 @@ case class ArrowLeftIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowLeftIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -366,7 +481,12 @@ case class ArrowRightIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowRightIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -378,7 +498,12 @@ case class ArrowUpIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowUpIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -390,7 +515,12 @@ case class ArrowUpDownIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ArrowUpDownIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -402,7 +532,12 @@ case class AtSignIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[AtSignIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -414,7 +549,12 @@ case class AttachmentIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[AttachmentIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -426,7 +566,12 @@ case class BellIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[BellIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -438,7 +583,12 @@ case class CalendarIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[CalendarIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -450,7 +600,12 @@ case class ChatIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ChatIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -462,7 +617,12 @@ case class CheckIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[CheckIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -474,7 +634,12 @@ case class CheckCircleIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[CheckCircleIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -486,7 +651,12 @@ case class ChevronDownIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronDownIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -498,7 +668,12 @@ case class ChevronLeftIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronLeftIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -510,7 +685,12 @@ case class ChevronRightIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronRightIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -522,7 +702,12 @@ case class ChevronUpIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ChevronUpIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -534,7 +719,12 @@ case class CloseIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[CloseIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -546,7 +736,12 @@ case class CopyIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[CopyIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -558,7 +753,12 @@ case class DeleteIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[DeleteIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -570,7 +770,12 @@ case class DownloadIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[DownloadIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -582,7 +787,12 @@ case class DragHandleIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[DragHandleIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -594,7 +804,12 @@ case class EditIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[EditIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -606,7 +821,12 @@ case class EmailIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[EmailIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -618,7 +838,12 @@ case class ExternalLinkIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ExternalLinkIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -630,7 +855,12 @@ case class HamburgerIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[HamburgerIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -642,7 +872,12 @@ case class InfoIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[InfoIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -654,7 +889,12 @@ case class InfoOutlineIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[InfoOutlineIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -666,7 +906,12 @@ case class LinkIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[LinkIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -678,7 +923,12 @@ case class LockIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[LockIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -690,7 +940,12 @@ case class MinusIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[MinusIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -702,7 +957,12 @@ case class MoonIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[MoonIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -714,7 +974,12 @@ case class NotAllowedIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[NotAllowedIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -726,7 +991,12 @@ case class PhoneIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[PhoneIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -738,7 +1008,12 @@ case class PlusSquareIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[PlusSquareIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -750,7 +1025,12 @@ case class QuestionIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[QuestionIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -762,7 +1042,12 @@ case class QuestionOutlineIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[QuestionOutlineIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -774,7 +1059,12 @@ case class RepeatIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[RepeatIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -786,7 +1076,12 @@ case class RepeatClockIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[RepeatClockIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -798,7 +1093,12 @@ case class SearchIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SearchIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -810,7 +1110,12 @@ case class Search2Icon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[Search2Icon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -822,7 +1127,12 @@ case class SettingsIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SettingsIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -834,7 +1144,12 @@ case class SmallAddIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SmallAddIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -846,7 +1161,12 @@ case class SmallCloseIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SmallCloseIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -858,7 +1178,12 @@ case class SpinnerIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SpinnerIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -870,7 +1195,12 @@ case class StarIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[StarIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -882,7 +1212,12 @@ case class SunIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[SunIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -894,7 +1229,12 @@ case class TimeIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[TimeIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -906,7 +1246,12 @@ case class TriangleDownIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[TriangleDownIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -918,7 +1263,12 @@ case class TriangleUpIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[TriangleUpIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -930,7 +1280,12 @@ case class UnlockIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[UnlockIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -942,7 +1297,12 @@ case class UpDownIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[UpDownIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -954,7 +1314,12 @@ case class ViewIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ViewIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -966,7 +1331,12 @@ case class ViewOffIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[ViewOffIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -978,7 +1348,12 @@ case class WarningIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[WarningIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon */ @@ -990,7 +1365,12 @@ case class WarningTwoIcon( color: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[WarningTwoIcon]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withW(v: Option[String]) = copy(w = v) + def withH(v: Option[String]) = copy(h = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withColor(v: Option[String]) = copy(color = v) /** https://chakra-ui.com/docs/components/textarea */ @@ -1006,7 +1386,13 @@ case class Textarea( with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Textarea]: override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withType(v: String) = copy(`type` = v) + def withPlaceholder(v: String) = copy(placeholder = v) + def withSize(v: String) = copy(size = v) + def withVariant(v: Option[String]) = copy(variant = v) + def withValue(v: String) = copy(value = v) /** https://chakra-ui.com/docs/components/switch */ @@ -1022,7 +1408,11 @@ case class Switch( with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Switch]: def checked: Boolean = checkedV.getOrElse(defaultChecked) override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(checkedV = Some(newValue.toBoolean))) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withDefaultChecked(v: Boolean) = copy(defaultChecked = v) + def withIsDisabled(v: Boolean) = copy(isDisabled = v) /** https://chakra-ui.com/docs/components/select */ @@ -1040,8 +1430,14 @@ case class Select( with HasChildren[Select] with OnChangeEventHandler.CanHandleOnChangeEvent[Select]: override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = newValue)) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) override def withChildren(cn: UiElement*) = copy(children = cn) + def withKey(v: String) = copy(key = v) + def withPlaceholder(v: String) = copy(placeholder = v) + def withValue(v: String) = copy(value = v) + def withBg(v: Option[String]) = copy(bg = v) + def withColor(v: Option[String]) = copy(color = v) + def withBorderColor(v: Option[String]) = copy(borderColor = v) case class Option_( key: String = Keys.nextKey, @@ -1049,14 +1445,18 @@ case class Option_( text: String = "", style: Map[String, Any] = Map.empty ) extends ChakraElement[Option_]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withValue(v: String) = copy(value = v) + def withText(v: String) = copy(text = v) /** https://chakra-ui.com/docs/components/table/usage */ case class TableContainer(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[TableContainer] with HasChildren[TableContainer]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) def withRowStringData(data: Seq[Seq[String]]): TableContainer = withRowData(data.map(_.map(c => Text(text = c)))) def withRowData(data: Seq[Seq[UiElement]]): TableContainer = val tableBodies = children @@ -1084,29 +1484,38 @@ case class Table( children: Seq[UiElement] = Nil ) extends ChakraElement[Table] with HasChildren[Table]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withVariant(v: String) = copy(variant = v) + def withSize(v: String) = copy(size = v) + def withColorScheme(v: Option[String]) = copy(colorScheme = v) case class TableCaption(key: String = Keys.nextKey, text: String = "", style: Map[String, Any] = Map.empty) extends ChakraElement[TableCaption]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class Thead(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Thead] with HasChildren[Thead]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Tbody(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Tbody] with HasChildren[Tbody]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Tfoot(key: String = Keys.nextKey, children: Seq[UiElement] = Nil, style: Map[String, Any] = Map.empty) extends ChakraElement[Tfoot] with HasChildren[Tfoot]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Tr( key: String = Keys.nextKey, @@ -1114,8 +1523,9 @@ case class Tr( style: Map[String, Any] = Map.empty ) extends ChakraElement[Tr] with HasChildren[Tr]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Th( key: String = Keys.nextKey, @@ -1125,8 +1535,11 @@ case class Th( style: Map[String, Any] = Map.empty ) extends ChakraElement[Th] with HasChildren[Th]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withIsNumeric(v: Boolean) = copy(isNumeric = v) case class Td( key: String = Keys.nextKey, @@ -1136,16 +1549,20 @@ case class Td( children: Seq[UiElement] = Nil ) extends ChakraElement[Td] with HasChildren[Td]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withIsNumeric(v: Boolean) = copy(isNumeric = v) /** https://chakra-ui.com/docs/components/menu/usage */ case class Menu(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty, children: Seq[UiElement] = Nil) extends ChakraElement[Menu] with HasChildren[Menu]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class MenuButton( key: String = Keys.nextKey, @@ -1156,14 +1573,19 @@ case class MenuButton( children: Seq[UiElement] = Nil ) extends ChakraElement[MenuButton] with HasChildren[MenuButton]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withSize(v: Option[String]) = copy(size = v) + def withColorScheme(v: Option[String]) = copy(colorScheme = v) case class MenuList(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty, children: Seq[UiElement] = Nil) extends ChakraElement[MenuList] with HasChildren[MenuList]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class MenuItem( key: String = Keys.nextKey, @@ -1173,11 +1595,14 @@ case class MenuItem( ) extends ChakraElement[MenuItem] with HasChildren[MenuItem] with OnClickEventHandler.CanHandleOnClickEvent[MenuItem]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class MenuDivider(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends ChakraElement[MenuDivider]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Badge( key: String = Keys.nextKey, @@ -1189,8 +1614,13 @@ case class Badge( style: Map[String, Any] = Map.empty ) extends ChakraElement[Badge] with HasChildren[Badge]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withColorScheme(v: Option[String]) = copy(colorScheme = v) + def withVariant(v: Option[String]) = copy(variant = v) + def withSize(v: String) = copy(size = v) /** https://chakra-ui.com/docs/components/image/usage * @@ -1206,7 +1636,12 @@ case class Image( borderRadius: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[Image]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withSrc(v: String) = copy(src = v) + def withAlt(v: String) = copy(alt = v) + def withBoxSize(v: Option[String]) = copy(boxSize = v) + def withBorderRadius(v: Option[String]) = copy(borderRadius = v) /** https://chakra-ui.com/docs/components/text */ @@ -1222,4 +1657,13 @@ case class Text( decoration: Option[String] = None, style: Map[String, Any] = Map.empty ) extends ChakraElement[Text]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) + def withFontSize(v: Option[String]) = copy(fontSize = v) + def withNoOfLines(v: Option[Int]) = copy(noOfLines = v) + def withColor(v: Option[String]) = copy(color = v) + def withAs(v: Option[String]) = copy(as = v) + def withAlign(v: Option[String]) = copy(align = v) + def withCasing(v: Option[String]) = copy(casing = v) + def withDecoration(v: Option[String]) = copy(decoration = v) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 133e743c..70f56c12 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -14,6 +14,13 @@ case class QuickTable( rows: Seq[Seq[UiElement]] = Nil ) extends UiComponent with HasStyle[QuickTable]: + def withKey(v: String) = copy(key = v) + def withVariant(v: String) = copy(variant = v) + def withColorScheme(v: String) = copy(colorScheme = v) + def withSize(v: String) = copy(size = v) + def withCaption(v: Option[String]) = copy(caption = v) + def withHeaders(v: Seq[UiElement]) = copy(headers = v) + def withRows(v: Seq[Seq[UiElement]]) = copy(rows = v) override lazy val rendered: Seq[UiElement] = val head = Thead(children = Seq(Tr(children = headers.map(h => Th(children = Seq(h)))))) @@ -35,5 +42,5 @@ case class QuickTable( def rows(data: Seq[Seq[Any]]): QuickTable = copy(rows = data.map(_.map(c => Text(text = c.toString)))) def rowsElements(data: Seq[Seq[UiElement]]): QuickTable = copy(rows = data) - def caption(text: String): QuickTable = copy(caption = Some(text)) - override def style(v: Map[String, Any]): QuickTable = copy(style = v) + def caption(text: String): QuickTable = copy(caption = Some(text)) + override def withStyle(v: Map[String, Any]): QuickTable = copy(style = v) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala index 750537cb..b1384c30 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/std/StdElement.scala @@ -8,16 +8,23 @@ sealed trait StdEJson extends UiElement sealed trait StdElement[A <: UiElement] extends StdEJson with HasStyle[A] with Current[A] case class Span(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Span]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class NewLine(key: String = Keys.nextKey, style: Map[String, Any] = Map.empty) extends StdElement[NewLine]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) case class Em(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Em]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class Header1(key: String = Keys.nextKey, text: String, style: Map[String, Any] = Map.empty) extends StdElement[Header1]: - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class Paragraph( key: String = Keys.nextKey, @@ -26,8 +33,10 @@ case class Paragraph( children: Seq[UiElement] = Nil ) extends StdElement[Paragraph] with HasChildren[Paragraph]: - override def withChildren(cn: UiElement*) = copy(children = cn) - override def style(v: Map[String, Any]) = copy(style = v) + override def withChildren(cn: UiElement*) = copy(children = cn) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withText(v: String) = copy(text = v) case class Input( key: String = Keys.nextKey, @@ -38,7 +47,11 @@ case class Input( ) extends StdElement[Input] with HasEventHandler: override def defaultEventHandler(session: ConnectedSession): OnChangeEventHandler = newValue => session.modified(copy(value = Some(newValue))) - override def style(v: Map[String, Any]) = copy(style = v) + override def withStyle(v: Map[String, Any]) = copy(style = v) + def withKey(v: String) = copy(key = v) + def withType(v: String) = copy(`type` = v) + def withDefaultValue(v: Option[String]) = copy(defaultValue = v) + def withValue(v: Option[String]) = copy(value = v) def onChange(h: OnChangeEventHandler)(using session: ConnectedSession): Input = session.addEventHandler(key, h) diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala deleted file mode 100644 index 296fa6fc..00000000 --- a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/chakra/extensions.scala +++ /dev/null @@ -1,913 +0,0 @@ - -package org.terminal21.client.components.chakra - -// GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT - -import org.terminal21.client.components.UiElement.Current -import org.terminal21.client.components.UiElement.HasChildren -import org.terminal21.client.components.UiElement.HasEventHandler -import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.components.Keys -import org.terminal21.client.components.UiElement -import org.terminal21.client.ConnectedSession -import org.terminal21.client.OnChangeBooleanEventHandler -import org.terminal21.client.OnChangeEventHandler -import org.terminal21.client.OnClickEventHandler - -extension (e: PlusSquareIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: FormControl) - def withKey(v: String) = e.copy(key = v) - def withAs(v: String) = e.copy(as = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: ArrowLeftIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Center) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ViewIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Square) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Image) - def withKey(v: String) = e.copy(key = v) - def withSrc(v: String) = e.copy(src = v) - def withAlt(v: String) = e.copy(alt = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withBorderRadius(v: Option[String]) = e.copy(borderRadius = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: SpinnerIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: EditableInput) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ArrowRightIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Table) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: String) = e.copy(variant = v) - def withSize(v: String) = e.copy(size = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: SmallCloseIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: InputLeftAddon) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: MenuList) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: TriangleDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Editable) - def withKey(v: String) = e.copy(key = v) - def withDefaultValue(v: String) = e.copy(defaultValue = v) - def withValue(v: String) = e.copy(value = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: InfoIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: AttachmentIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: RepeatClockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: QuestionOutlineIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: SmallAddIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ChevronRightIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Tr) - def withKey(v: String) = e.copy(key = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: UpDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: MenuButton) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withSize(v: Option[String]) = e.copy(size = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: InputGroup) - def withKey(v: String) = e.copy(key = v) - def withSize(v: String) = e.copy(size = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: ArrowDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: CopyIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ExternalLinkIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ArrowUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: BellIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ChevronUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: CloseIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: SettingsIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: QuestionIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ArrowUpDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: AddIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: StarIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: RadioGroup) - def withKey(v: String) = e.copy(key = v) - def withDefaultValue(v: String) = e.copy(defaultValue = v) - def withValueV(v: Option[String]) = e.copy(valueV = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: WarningIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Tbody) - def withKey(v: String) = e.copy(key = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Textarea) - def withKey(v: String) = e.copy(key = v) - def withType(v: String) = e.copy(`type` = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withSize(v: String) = e.copy(size = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withValue(v: String) = e.copy(value = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - - -extension (e: MenuDivider) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: WarningTwoIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: EmailIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: MinusIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: TriangleUpIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: HamburgerIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ArrowBackIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Th) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: SearchIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: DeleteIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Td) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withIsNumeric(v: Boolean) = e.copy(isNumeric = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: SimpleGrid) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withSpacingX(v: Option[String]) = e.copy(spacingX = v) - def withSpacingY(v: Option[String]) = e.copy(spacingY = v) - def withColumns(v: Int) = e.copy(columns = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Text) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withFontSize(v: Option[String]) = e.copy(fontSize = v) - def withNoOfLines(v: Option[Int]) = e.copy(noOfLines = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withAs(v: Option[String]) = e.copy(as = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withCasing(v: Option[String]) = e.copy(casing = v) - def withDecoration(v: Option[String]) = e.copy(decoration = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ChatIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Search2Icon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Button) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withSize(v: Option[String]) = e.copy(size = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withLeftIcon(v: Option[UiElement]) = e.copy(leftIcon = v) - def withRightIcon(v: Option[UiElement]) = e.copy(rightIcon = v) - def withIsActive(v: Option[Boolean]) = e.copy(isActive = v) - def withIsDisabled(v: Option[Boolean]) = e.copy(isDisabled = v) - def withIsLoading(v: Option[Boolean]) = e.copy(isLoading = v) - def withIsAttached(v: Option[Boolean]) = e.copy(isAttached = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - - -extension (e: Option_) - def withKey(v: String) = e.copy(key = v) - def withValue(v: String) = e.copy(value = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: LinkIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ChevronLeftIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ArrowForwardIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ButtonGroup) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withSize(v: Option[String]) = e.copy(size = v) - def withWidth(v: Option[String]) = e.copy(width = v) - def withHeight(v: Option[String]) = e.copy(height = v) - def withBorder(v: Option[String]) = e.copy(border = v) - def withBorderColor(v: Option[String]) = e.copy(borderColor = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: NotAllowedIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: PhoneIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ChevronDownIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: HStack) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: VStack) - def withKey(v: String) = e.copy(key = v) - def withSpacing(v: Option[String]) = e.copy(spacing = v) - def withAlign(v: Option[String]) = e.copy(align = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: TimeIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: InfoOutlineIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: CalendarIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: DragHandleIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: EditIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: QuickTable) - def withKey(v: String) = e.copy(key = v) - def withVariant(v: String) = e.copy(variant = v) - def withColorScheme(v: String) = e.copy(colorScheme = v) - def withSize(v: String) = e.copy(size = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withCaption(v: Option[String]) = e.copy(caption = v) - def withHeaders(v: Seq[UiElement]) = e.copy(headers = v) - def withRows(v: Seq[Seq[UiElement]]) = e.copy(rows = v) - - -extension (e: MoonIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: DownloadIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Menu) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: Circle) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: RepeatIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: UnlockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Checkbox) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) - def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withCheckedV(v: Option[Boolean]) = e.copy(checkedV = v) - - -extension (e: Radio) - def withKey(v: String) = e.copy(key = v) - def withValue(v: String) = e.copy(value = v) - def withText(v: String) = e.copy(text = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Badge) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withColorScheme(v: Option[String]) = e.copy(colorScheme = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withSize(v: String) = e.copy(size = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: CheckCircleIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - - -extension (e: MenuItem) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withText(v: String) = e.copy(text = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: EditableTextarea) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: CheckIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: FormLabel) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: LockIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: TableContainer) - def withKey(v: String) = e.copy(key = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Tfoot) - def withKey(v: String) = e.copy(key = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Box) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withBg(v: String) = e.copy(bg = v) - def withW(v: String) = e.copy(w = v) - def withP(v: Int) = e.copy(p = v) - def withColor(v: String) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withAs(v: Option[String]) = e.copy(as = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: Thead) - def withKey(v: String) = e.copy(key = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: AtSignIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: SunIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: ViewOffIcon) - def withKey(v: String) = e.copy(key = v) - def withW(v: Option[String]) = e.copy(w = v) - def withH(v: Option[String]) = e.copy(h = v) - def withBoxSize(v: Option[String]) = e.copy(boxSize = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: TableCaption) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Select) - def withKey(v: String) = e.copy(key = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withValue(v: String) = e.copy(value = v) - def withBg(v: Option[String]) = e.copy(bg = v) - def withColor(v: Option[String]) = e.copy(color = v) - def withBorderColor(v: Option[String]) = e.copy(borderColor = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: FormHelperText) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: InputRightAddon) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - -extension (e: Switch) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withDefaultChecked(v: Boolean) = e.copy(defaultChecked = v) - def withIsDisabled(v: Boolean) = e.copy(isDisabled = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withCheckedV(v: Option[Boolean]) = e.copy(checkedV = v) - - -extension (e: Input) - def withKey(v: String) = e.copy(key = v) - def withType(v: String) = e.copy(`type` = v) - def withPlaceholder(v: String) = e.copy(placeholder = v) - def withSize(v: String) = e.copy(size = v) - def withVariant(v: Option[String]) = e.copy(variant = v) - def withValue(v: String) = e.copy(value = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: EditablePreview) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - diff --git a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala b/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala deleted file mode 100644 index bc0b4e4d..00000000 --- a/terminal21-ui-std/src/main/ui-generated/org/terminal21/client/components/std/extensions.scala +++ /dev/null @@ -1,53 +0,0 @@ - -package org.terminal21.client.components.std - -// GENERATED WITH PropertiesExtensionGenerator, DON'T EDIT - -import org.terminal21.client.components.UiElement.Current -import org.terminal21.client.components.UiElement.HasChildren -import org.terminal21.client.components.UiElement.HasEventHandler -import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.components.Keys -import org.terminal21.client.components.UiElement -import org.terminal21.client.ConnectedSession -import org.terminal21.client.OnChangeEventHandler - -extension (e: NewLine) - def withKey(v: String) = e.copy(key = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Header1) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Span) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Paragraph) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withChildren(v: Seq[UiElement]) = e.copy(children = v) - - - - -extension (e: Em) - def withKey(v: String) = e.copy(key = v) - def withText(v: String) = e.copy(text = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - - -extension (e: Input) - def withKey(v: String) = e.copy(key = v) - def withType(v: String) = e.copy(`type` = v) - def withDefaultValue(v: Option[String]) = e.copy(defaultValue = v) - def withStyle(v: Map[String, Any]) = e.copy(style = v) - def withValue(v: Option[String]) = e.copy(value = v) - From e815c8588f81a7beeff3dead85391cafde8de913 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 18 Jan 2024 14:21:46 +0000 Subject: [PATCH 25/40] - --- .../terminal21/client/components/mathjax/MathJax.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index b7fcec05..4deef557 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -9,10 +9,11 @@ sealed trait MathJaxElement extends UiElement */ case class MathJax( key: String = Keys.nextKey, - /** expression should be like """ text \( asciimath \) text""", i.e. """When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\)""" - */ - @volatile var expression: String = """fill in the expression as per https://asciimath.org/""", - @volatile var style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib + // expression should be like """ text \( asciimath \) text""", i.e. """When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\)""" + expression: String = """fill in the expression as per https://asciimath.org/""", + style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib ) extends MathJaxElement with HasStyle[MathJax]: override def withStyle(v: Map[String, Any]): MathJax = copy(style = v) + def withKey(k: String) = copy(key = k) + def withExpression(e: String) = copy(expression = e) From e066247c75a845423212f68eaffb3fa1b1811ec2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 18 Jan 2024 19:53:15 +0000 Subject: [PATCH 26/40] - --- .../sparklib/CalculationsExtensions.scala | 4 ++-- .../client/components/StdUiCalculation.scala | 16 ++++++++++------ .../client/components/UiComponent.scala | 2 ++ .../client/components/chakra/QuickTable.scala | 8 +++++--- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala index 309a81ca..2ee3e933 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala @@ -9,7 +9,7 @@ import org.terminal21.sparklib.calculations.{ReadWriter, StdUiSparkCalculation} extension [OUT: ReadWriter](ds: OUT) def visualize(name: String, dataUi: UiElement with HasStyle[_])( - toUi: OUT => UiElement + toUi: OUT => UiElement & HasStyle[_] )(using session: ConnectedSession, executor: FiberExecutor, @@ -17,7 +17,7 @@ extension [OUT: ReadWriter](ds: OUT) ) = val ui = new StdUiSparkCalculation[OUT](Keys.nextKey, name, dataUi): override protected def whenResultsReady(results: OUT): Unit = - try toUi(results).renderChanges() + try updateUi(toUi(results)) catch case t: Throwable => t.printStackTrace() super.whenResultsReady(results) override def nonCachedCalculation: OUT = ds diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index 1493f219..44c7b279 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -5,7 +5,7 @@ import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.chakra.* -import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.{AtomicBoolean, AtomicReference} /** Creates a standard UI for a calculation which may take time. While the calculation runs, the UI is grayed out, including the dataUi component. When the * calculation completes, it allows for updating the dataUi component. @@ -18,9 +18,13 @@ trait StdUiCalculation[OUT]( )(using session: ConnectedSession, executor: FiberExecutor) extends Calculation[OUT] with UiComponent: - private val running = new AtomicBoolean(false) - lazy val badge = Badge() - lazy val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + private val running = new AtomicBoolean(false) + private val currentUi = new AtomicReference(dataUi) + + protected def updateUi(dataUi: UiElement & HasStyle[_]) = currentUi.set(dataUi) + + lazy val badge = Badge() + lazy val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => if running.compareAndSet(false, true) then try reCalculate() @@ -47,13 +51,13 @@ trait StdUiCalculation[OUT]( override protected def whenResultsNotReady(): Unit = session.renderChanges( badge.withText("Calculating").withColorScheme(Some("purple")), - dataUi.withStyle(dataUi.style + ("filter" -> "grayscale(100%)")), + currentUi.get().withStyle(dataUi.style + ("filter" -> "grayscale(100%)")), recalc.withIsDisabled(Some(true)) ) super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = - val newDataUi = dataUi.withStyle(dataUi.style - "filter") + val newDataUi = currentUi.get().withStyle(dataUi.style - "filter") session.renderChanges( badge.withText("Ready").withColorScheme(None), newDataUi, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index bae57edb..fec3b90a 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -8,3 +8,5 @@ trait UiComponent extends UiElement: // Note: impl as a lazy val to avoid UiElements getting a random key def rendered: Seq[UiElement] override def flat = Seq(this) ++ rendered.flatMap(_.flat) + +// protected def componentKey(i: Int): String = s"$key-$i" diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 70f56c12..a3394b89 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -23,18 +23,20 @@ case class QuickTable( def withRows(v: Seq[Seq[UiElement]]) = copy(rows = v) override lazy val rendered: Seq[UiElement] = - val head = Thead(children = Seq(Tr(children = headers.map(h => Th(children = Seq(h)))))) + val head = Thead(key = key + "-th", children = Seq(Tr(children = headers.map(h => Th(children = Seq(h)))))) val body = Tbody( + key = key + "-tb", children = rows.map: row => - Tr(children = row.map(c => Td().withChildren(c))) + Tr(children = row.map(c => Td(children = Seq(c)))) ) val table = Table( + key = key + "-t", variant = variant, colorScheme = Some(colorScheme), size = size, children = caption.map(text => TableCaption(text = text)).toSeq ++ Seq(head, body) ) - val tableContainer = TableContainer().withChildren(table) + val tableContainer = TableContainer(key = key + "-tc", style = style, children = Seq(table)) Seq(tableContainer) def headers(headers: String*): QuickTable = copy(headers = headers.map(h => Text(text = h))) From b1da80c6b5fa28f1900ad78ab80e6d749ab09550 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 18 Jan 2024 20:02:34 +0000 Subject: [PATCH 27/40] - --- .../scala/org/terminal21/client/components/UiComponent.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index fec3b90a..5a16e5cc 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -5,7 +5,8 @@ import org.terminal21.client.components.UiElement.HasChildren /** A UiComponent is a UI element that is composed of a seq of other ui elements */ trait UiComponent extends UiElement: - // Note: impl as a lazy val to avoid UiElements getting a random key + // Note: impl as a lazy val to avoid UiElements getting a random key and try to fix the + // keys of any sub-elements the component has. def rendered: Seq[UiElement] override def flat = Seq(this) ++ rendered.flatMap(_.flat) From 5fcd6731ec8ddfca83c0f6c2748303ba51694506 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 18 Jan 2024 20:04:15 +0000 Subject: [PATCH 28/40] - --- .../org/terminal21/client/components/nivo/NivoElement.scala | 1 + .../scala/org/terminal21/client/components/UiComponent.scala | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 4ce70ded..8816f500 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -59,4 +59,5 @@ case class ResponsiveBar( ariaLabel: String = "Chart Label" ) extends NivoElement[ResponsiveBar]: override def withStyle(v: Map[String, Any]): ResponsiveBar = copy(style = v) + def withKey(v: String) = copy(key = v) def withData(data: Seq[Seq[BarDatum]]) = copy(data = data) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index 5a16e5cc..c1213662 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -9,5 +9,3 @@ trait UiComponent extends UiElement: // keys of any sub-elements the component has. def rendered: Seq[UiElement] override def flat = Seq(this) ++ rendered.flatMap(_.flat) - -// protected def componentKey(i: Int): String = s"$key-$i" From 2f4cb51c180909546e81bd276608451df9fb2816 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 10:46:43 +0000 Subject: [PATCH 29/40] - --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index e8a1e246..abbbce5d 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.7 +sbt.version=1.9.8 From 3414fa2febfa0c12c0d7edaeee297e40583a7910 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 10:57:31 +0000 Subject: [PATCH 30/40] - --- build.sbt | 2 +- example-spark/project.scala | 8 ++++---- example-spark/spark-notebook.sc | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index f79a1821..3533b52d 100644 --- a/build.sbt +++ b/build.sbt @@ -5,7 +5,7 @@ import sbt.librarymanagement.ModuleFilter */ val scala3Version = "3.3.1" -ThisBuild / version := "0.12" +ThisBuild / version := "0.20" ThisBuild / organization := "io.github.kostaskougios" name := "rest-api" ThisBuild / scalaVersion := scala3Version diff --git a/example-spark/project.scala b/example-spark/project.scala index bdf43ace..a3ec4846 100644 --- a/example-spark/project.scala +++ b/example-spark/project.scala @@ -8,10 +8,10 @@ //> using javaOpt -Dlogback.configurationFile=file:etc/logback.xml // terminal21 dependencies -//> using dep io.github.kostaskougios::terminal21-ui-std:0.12 -//> using dep io.github.kostaskougios::terminal21-spark:0.12 -//> using dep io.github.kostaskougios::terminal21-nivo:0.12 -//> using dep io.github.kostaskougios::terminal21-mathjax:0.12 +//> using dep io.github.kostaskougios::terminal21-ui-std:0.20 +//> using dep io.github.kostaskougios::terminal21-spark:0.20 +//> using dep io.github.kostaskougios::terminal21-nivo:0.20 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.20 //> using dep ch.qos.logback:logback-classic:1.4.14 diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index c0cce5e2..02a6a39c 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -54,12 +54,12 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(/* con val oldestPeopleChartCalc = peopleDS .orderBy($"age".desc) .visualize("Oldest people", oldestPeopleChart): data => - oldestPeopleChart.data = Seq( + oldestPeopleChart.withData(Seq( Serie( "Person", data = data.take(5).map(person => Datum(person.name, person.age)) ) - ) + )) Seq( // just make it look a bit more like a proper notebook by adding some fake maths From 849b9d81a0d4e3be844e669e26f5c29d1a1d3402 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 11:51:51 +0000 Subject: [PATCH 31/40] - --- Readme.md | 23 ++++++----------- example-scripts/bouncing-ball.sc | 3 +-- example-scripts/csv-editor.sc | 6 ++--- example-scripts/csv-viewer.sc | 12 ++++----- example-scripts/hello-world.sc | 4 +-- example-scripts/nivo-bar-chart.sc | 3 +-- example-scripts/nivo-line-chart.sc | 3 +-- example-scripts/postit.sc | 25 ++++++++++--------- example-scripts/project.scala | 6 ++--- example-scripts/server.sc | 2 +- example-scripts/textedit.sc | 25 +++++++++---------- .../terminal21/client/ConnectedSession.scala | 1 + .../client/components/UiElement.scala | 4 ++- 13 files changed, 52 insertions(+), 65 deletions(-) diff --git a/Readme.md b/Readme.md index a8b96e09..c02dc337 100644 --- a/Readme.md +++ b/Readme.md @@ -147,28 +147,21 @@ the state in the client scripts. # Mutability -terminal21 ui components are mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. -changing the text of a paragraph on an event handler is as simple as : +terminal21 ui components are immutable from v0.20. Use `component.withX(...).renderChanges()` to modify a component +and render it. Note that the original `component` is not changed. -```scala - p.text = "new text" -``` - -The equivalent immutable code would be (at least) -```scala - p.copy(text= "new text") -``` - -Also by default some component values (like input boxes) are changed by the user. These changes are reflected in the component graph, something that -would be a lot harder if the graph was immutable. - -If there is a reasonable way to refactor to have immutability without compromising simplicity, it will be done. +Also when getting a value of i.e. an Input, use `myInput.current.value`. `current` makes sure we read the component with +all changes that may have occurred at the browser. # Need help? Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi/discussions) of the project to post any questions, comments or ideas. # Changelog +## Version 0.20 + +- immutable components +- option to render only changed components ## Version 0.12 diff --git a/example-scripts/bouncing-ball.sc b/example-scripts/bouncing-ball.sc index 41bb1c81..abd85ce1 100755 --- a/example-scripts/bouncing-ball.sc +++ b/example-scripts/bouncing-ball.sc @@ -23,8 +23,7 @@ Sessions.withNewSession("bouncing-ball", "C64 bouncing ball"): session => ball.render() @tailrec def animateBall(x: Int, y: Int, dx: Int, dy: Int): Unit = - ball.style = Map("position" -> "fixed", "left" -> (x + "px"), "top" -> (y + "px")) - ball.renderChanges() + ball.withStyle("position" -> "fixed", "left" -> (x + "px"), "top" -> (y + "px")).renderChanges() Thread.sleep(1000 / 120) val newDx = if x < 0 || x > 600 then -dx else dx val newDy = if y < 0 || y > 500 then -dy else dy diff --git a/example-scripts/csv-editor.sc b/example-scripts/csv-editor.sc index 6683f70c..79a01e0e 100755 --- a/example-scripts/csv-editor.sc +++ b/example-scripts/csv-editor.sc @@ -65,8 +65,7 @@ Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session val saveAndExit = Button(text = "Save & Exit") .onClick: () => saveCsvMap() - status.text="Csv file saved, exiting." - status.renderChanges() + status.withText("Csv file saved, exiting.").renderChanges() exitFlag.set(true) val exit = Button(text = "Exit Without Saving") @@ -81,8 +80,7 @@ Sessions.withNewSession(s"csv-editor-$fileName", s"CsvEdit: $fileName"): session ) .onChange: newValue => csvMap((x, y)) = newValue - status.text = s"($x,$y) value changed to $newValue" - status.renderChanges() + status.withText(s"($x,$y) value changed to $newValue").renderChanges() Seq( TableContainer().withChildren( diff --git a/example-scripts/csv-viewer.sc b/example-scripts/csv-viewer.sc index 8c861968..85fbcfcc 100755 --- a/example-scripts/csv-viewer.sc +++ b/example-scripts/csv-viewer.sc @@ -23,7 +23,7 @@ if args.length != 1 then ) val fileName = args(0) -val file = new File(fileName) +val file = new File(fileName) val contents = FileUtils.readFileToString(file, "UTF-8") val csv = contents.split("\n").map(_.split(",")) @@ -31,12 +31,11 @@ val csv = contents.split("\n").map(_.split(",")) Sessions.withNewSession(s"csv-viewer-$fileName", s"CsvView: $fileName"): session => given ConnectedSession = session - Seq( - TableContainer().withChildren( + TableContainer() + .withChildren( Table(variant = "striped", colorScheme = Some("teal"), size = "mg") .withChildren( TableCaption(text = "Csv file contents"), - Thead(), Tbody( children = csv.map: row => Tr( @@ -45,8 +44,7 @@ Sessions.withNewSession(s"csv-viewer-$fileName", s"CsvView: $fileName"): session ) ) ) - ) - ).render() + ).render() println(s"Now open ${session.uiUrl} to view the UI.") - // since this is a read-only UI, we can exit the app but leave the session open on the UI + // since this is a read-only UI, we can exit the app but leave the session open on the UI for the user to examine the data. session.leaveSessionOpenAfterExiting() diff --git a/example-scripts/hello-world.sc b/example-scripts/hello-world.sc index 74857e29..e314267f 100755 --- a/example-scripts/hello-world.sc +++ b/example-scripts/hello-world.sc @@ -12,7 +12,5 @@ import org.terminal21.client.components.std.* Sessions.withNewSession("hello-world", "Hello World Example"): session => given ConnectedSession = session - Seq( - Paragraph(text = "Hello World!") - ).render() + Paragraph(text = "Hello World!").render() session.leaveSessionOpenAfterExiting() diff --git a/example-scripts/nivo-bar-chart.sc b/example-scripts/nivo-bar-chart.sc index 7f7fbad5..546d1205 100755 --- a/example-scripts/nivo-bar-chart.sc +++ b/example-scripts/nivo-bar-chart.sc @@ -47,8 +47,7 @@ Sessions.withNewSession("nivo-bar-chart", "Nivo Bar Chart", NivoLib /* note we n fiberExecutor.submit: while !session.isClosed do Thread.sleep(2000) - chart.data = createRandomData - chart.renderChanges() + chart.withData(createRandomData).renderChanges() session.waitTillUserClosesSession() diff --git a/example-scripts/nivo-line-chart.sc b/example-scripts/nivo-line-chart.sc index 1c88eec0..6f5843ad 100755 --- a/example-scripts/nivo-line-chart.sc +++ b/example-scripts/nivo-line-chart.sc @@ -28,8 +28,7 @@ Sessions.withNewSession("nivo-line-chart", "Nivo Line Chart", NivoLib /* note we fiberExecutor.submit: while !session.isClosed do Thread.sleep(2000) - chart.data = createRandomData - chart.renderChanges() + chart.withData(createRandomData).renderChanges() session.waitTillUserClosesSession() diff --git a/example-scripts/postit.sc b/example-scripts/postit.sc index e5ad1123..b64c48a6 100755 --- a/example-scripts/postit.sc +++ b/example-scripts/postit.sc @@ -15,22 +15,23 @@ import org.terminal21.client.components.chakra.* Sessions.withNewSession("postit", "Post-It"): session => given ConnectedSession = session - val editor = Textarea(placeholder = "Please post your note by clicking here and editing the content") + val editor = Textarea(placeholder = "Please post your note by clicking here and editing the content") val messages = VStack(align = Some("stretch")) - val add = Button(text = "Post It.").onClick: () => + val add = Button(text = "Post It.").onClick: () => // add the new msg. // note: editor.value is automatically updated by terminal-ui - messages.addChildren( - HStack().withChildren( - Image( - src = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_Notes_icon.svg/2048px-Apple_Notes_icon.svg.png", - boxSize = Some("32px") - ), - Box(text = editor.value) + val currentMessages = messages.current + currentMessages + .addChildren( + HStack().withChildren( + Image( + src = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_Notes_icon.svg/2048px-Apple_Notes_icon.svg.png", + boxSize = Some("32px") + ), + Box(text = editor.current.value) + ) ) - ) - // always render after adding/modifying something - messages.renderChanges() + .renderChanges() Seq( Paragraph(text = "Please type your note below and click 'Post It' to post it so that everyone can view it."), diff --git a/example-scripts/project.scala b/example-scripts/project.scala index b8bcff17..320278ba 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -1,8 +1,8 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-ui-std:0.12 -//> using dep io.github.kostaskougios::terminal21-nivo:0.12 -//> using dep io.github.kostaskougios::terminal21-mathjax:0.12 +//> using dep io.github.kostaskougios::terminal21-ui-std:0.20 +//> using dep io.github.kostaskougios::terminal21-nivo:0.20 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.20 //> using dep commons-io:commons-io:2.15.1 diff --git a/example-scripts/server.sc b/example-scripts/server.sc index b08e946f..be0e534a 100755 --- a/example-scripts/server.sc +++ b/example-scripts/server.sc @@ -2,7 +2,7 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-server:0.12 +//> using dep io.github.kostaskougios::terminal21-server:0.20 import org.terminal21.server.Terminal21Server diff --git a/example-scripts/textedit.sc b/example-scripts/textedit.sc index d9fc50f5..f054089f 100755 --- a/example-scripts/textedit.sc +++ b/example-scripts/textedit.sc @@ -1,4 +1,4 @@ -#!/usr/bin/env -S scala-cli project.scala +//#!/usr/bin/env -S scala-cli project.scala // ------------------------------------------------------------------------------ // A text file editor for small files. @@ -25,7 +25,7 @@ if args.length != 1 then ) val fileName = args(0) -val file = new File(fileName) +val file = new File(fileName) val contents = if file.exists() then FileUtils.readFileToString(file, "UTF-8") else "" @@ -34,18 +34,17 @@ def saveFile(content: String) = FileUtils.writeStringToFile(file, content, "UTF- Sessions.withNewSession(s"textedit-$fileName", s"Edit: $fileName"): session => given ConnectedSession = session // we will wait till the user clicks the "Exit" menu, this latch makes sure the main thread of the app waits. - val exitLatch = new CountDownLatch(1) + val exitLatch = new CountDownLatch(1) // the main editor area. - val editor = Textarea(value = contents) + val editor = Textarea(value = contents) // This will display a "saved" badge for a second when the user saves the file - val status = Badge() + val status = Badge() // This will display an asterisk when the contents of the file are changed in the editor - val modified = Badge(colorScheme = Some("red")) + val modified = Badge(colorScheme = Some("red")) // when the user changes the textarea, we get the new text and we can compare it with the loaded value. editor.onChange: newValue => - modified.text = if newValue != contents then "*" else "" - modified.renderChanges() + modified.withText(if newValue != contents then "*" else "").renderChanges() Seq( HStack().withChildren( @@ -56,14 +55,14 @@ Sessions.withNewSession(s"textedit-$fileName", s"Edit: $fileName"): session => .onClick: () => saveFile(editor.value) // we'll display a "Saved" badge for 1 second. - status.text = "Saved" - modified.text = "" - status.renderChanges() + Seq( + status.withText("Saved"), + modified.withText("") + ).renderChanges() modified.renderChanges() // each event handler runs on a new fibler, it is ok to sleep here Thread.sleep(1000) - status.text = "" - status.renderChanges() + status.withText("").renderChanges() , MenuItem(text = "Exit") .onClick: () => diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 6490bb34..dbeb26d0 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -81,6 +81,7 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se sessionsService.setSessionJsonState(session, j) def renderChanges(es: UiElement*): Unit = + for e <- es do modified(e) val j = toJson(es) sessionsService.changeSessionJsonState(session, j) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 8658ceb3..049acba6 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -28,8 +28,9 @@ object UiElement: trait HasChildren[A <: UiElement]: this: A => def children: Seq[UiElement] - override def flat: Seq[UiElement] = Seq(this) ++ children.flatMap(_.flat) + override def flat: Seq[UiElement] = Seq(this) ++ children.flatMap(_.flat) def withChildren(cn: UiElement*): A + def addChildren(cn: UiElement*): A = withChildren(children ++ cn: _*) trait HasEventHandler: def defaultEventHandler(session: ConnectedSession): EventHandler @@ -37,3 +38,4 @@ object UiElement: trait HasStyle[A <: UiElement]: def style: Map[String, Any] def withStyle(v: Map[String, Any]): A + def withStyle(vs: (String, Any)*): A = withStyle(vs.toMap) From 672a9476ff8bae9a216635e495ede9334c04eb67 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 11:59:38 +0000 Subject: [PATCH 32/40] - --- example-scripts/textedit.sc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/example-scripts/textedit.sc b/example-scripts/textedit.sc index f054089f..e0561f3b 100755 --- a/example-scripts/textedit.sc +++ b/example-scripts/textedit.sc @@ -1,4 +1,4 @@ -//#!/usr/bin/env -S scala-cli project.scala +#!/usr/bin/env -S scala-cli project.scala // ------------------------------------------------------------------------------ // A text file editor for small files. @@ -53,14 +53,13 @@ Sessions.withNewSession(s"textedit-$fileName", s"Edit: $fileName"): session => MenuList().withChildren( MenuItem(text = "Save") .onClick: () => - saveFile(editor.value) + saveFile(editor.current.value) // we'll display a "Saved" badge for 1 second. Seq( status.withText("Saved"), modified.withText("") ).renderChanges() - modified.renderChanges() - // each event handler runs on a new fibler, it is ok to sleep here + // each event handler runs on a new fiber, it is ok to sleep here Thread.sleep(1000) status.withText("").renderChanges() , From e6d30f74c978fe78995fd4d9d0b61073ad66b1b0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 12:03:15 +0000 Subject: [PATCH 33/40] - --- .../terminal21/client/ConnectedSessionTest.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index d07042cc..5b26595c 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -16,7 +16,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: given connectedSession: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock val editable = Editable() editable.onChange: newValue => - editable.value should be(newValue) + editable.current.value should be(newValue) connectedSession.render(editable) connectedSession.fireEvent(OnChange(editable.key, "new value")) @@ -37,7 +37,7 @@ class ConnectedSessionTest extends AnyFunSuiteLike: ) ) - test("renderChanges"): + test("renderChanges changes state on server"): val (sessionService, connectedSession) = ConnectedSessionMock.newConnectedSessionAndSessionServiceMock val p1 = Paragraph(text = "p1") @@ -52,3 +52,12 @@ class ConnectedSessionTest extends AnyFunSuiteLike: Map(p1.key -> Seq(span1.key), span1.key -> Nil) ) ) + + test("renderChanges updates current version of component"): + given connectedSession: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + + val p1 = Paragraph(text = "p1") + val span1 = Span(text = "span1") + connectedSession.render(p1) + connectedSession.renderChanges(p1.withChildren(span1)) + p1.current.children should be(Seq(span1)) From 831310070473c42238b2b90b0ee2ea8b8067a903 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 14:19:53 +0000 Subject: [PATCH 34/40] - --- .../org/terminal21/server/json/WsRequest.scala | 2 ++ .../server/service/ServerSessionsService.scala | 2 ++ .../server/ui/SessionsWebSocket.scala | 17 +++++++++++------ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/json/WsRequest.scala b/terminal21-server/src/main/scala/org/terminal21/server/json/WsRequest.scala index 35f0406d..96164b3d 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/json/WsRequest.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/json/WsRequest.scala @@ -8,6 +8,8 @@ case class WsRequest(operation: String, body: Option[Body]) sealed trait Body +case class SessionFullRefresh(sessionId: String) extends Body + sealed trait UiEvent extends Body: def sessionId: String diff --git a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala index 13873dfe..5fbfce28 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala @@ -20,6 +20,8 @@ class ServerSessionsService extends SessionsService: def sessionById(sessionId: String): Session = sessions.keys.find(_.id == sessionId).getOrElse(throw new IllegalArgumentException(s"Invalid session id = $sessionId")) + def sessionStateOf(session: Session): SessionState = sessions(session) + def notifyMeWhenSessionsChange(listener: ListenerFunction[Seq[Session]]): Unit = sessionChangeNotificationRegistry.addAndNotify(allSessions)(listener) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala index 7338a6d1..a6f7c202 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala @@ -48,21 +48,26 @@ class SessionsWebSocket(sessionsService: ServerSessionsService) extends WsListen logger.info(s"$wsSession: Received json: $text , last = $last") errorLogger.logErrors: WsRequest.decoder(text) match - case Right(WsRequest("sessions", None)) => + case Right(WsRequest("sessions", None)) => continuouslyRespond(wsSession) logger.info(s"$wsSession: sessions processed successfully") - case Right(WsRequest(eventName, Some(event: UiEvent))) => + case Right(WsRequest("session-full-refresh", Some(SessionFullRefresh(sessionId)))) => + logger.info(s"$wsSession: session-full-refresh requested, sending full session data for $sessionId") + val session = sessionsService.sessionById(sessionId) + val sessionState = sessionsService.sessionStateOf(session) + sendSessionState(wsSession, session, sessionState) + case Right(WsRequest(eventName, Some(event: UiEvent))) => logger.info(s"$wsSession: Received event $eventName = $event") sessionsService.addEvent(event) - case Right(WsRequest("ping", None)) => + case Right(WsRequest("ping", None)) => logger.info(s"$wsSession: ping received") - case Right(WsRequest("close-session", Some(CloseSession(sessionId)))) => + case Right(WsRequest("close-session", Some(CloseSession(sessionId)))) => val session = sessionsService.sessionById(sessionId) sessionsService.terminateSession(session) - case Right(WsRequest("remove-session", Some(RemoveSession(sessionId)))) => + case Right(WsRequest("remove-session", Some(RemoveSession(sessionId)))) => val session = sessionsService.sessionById(sessionId) sessionsService.removeSession(session) - case x => + case x => logger.error(s"Invalid request : $x") override def onOpen(wsSession: WsSession): Unit = From dfb3f398b03dc5deba9579be6204ab5f09e55493 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 15:07:42 +0000 Subject: [PATCH 35/40] - --- build.sbt | 2 +- .../server/model/SessionState.scala | 4 +- .../service/ServerSessionsService.scala | 2 +- .../server/ui/SessionsWebSocket.scala | 2 +- .../service/ServerSessionsServiceTest.scala | 65 +++++++++++++++++++ .../src/test/scala/scripts/ExamineJson.scala | 9 --- .../ui/std/StdExportsBuilders.scala | 14 ++++ 7 files changed, 84 insertions(+), 14 deletions(-) create mode 100644 terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala delete mode 100644 terminal21-server/src/test/scala/scripts/ExamineJson.scala create mode 100644 terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/StdExportsBuilders.scala diff --git a/build.sbt b/build.sbt index 3533b52d..03eee7fb 100644 --- a/build.sbt +++ b/build.sbt @@ -99,7 +99,7 @@ lazy val `terminal21-server` = project LogBack ) ++ Circe ) - .dependsOn(`terminal21-ui-std-exports`, `terminal21-server-client-common`) + .dependsOn(`terminal21-ui-std-exports` % "compile->compile;test->test", `terminal21-server-client-common`) .enablePlugins(FunctionsRemotePlugin) lazy val `terminal21-ui-std-exports` = project diff --git a/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala b/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala index f74a44f1..2189ed89 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/model/SessionState.scala @@ -5,8 +5,8 @@ import org.terminal21.server.utils.NotificationRegistry import org.terminal21.ui.std.ServerJson case class SessionState( - json: ServerJson, + serverJson: ServerJson, eventsNotificationRegistry: NotificationRegistry[CommandEvent] ): - def withNewState(newJson: ServerJson): SessionState = copy(json = newJson) + def withNewState(newJson: ServerJson): SessionState = copy(serverJson = newJson) def close: SessionState = copy(eventsNotificationRegistry = new NotificationRegistry[CommandEvent]) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala index 5fbfce28..b937260f 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala @@ -60,7 +60,7 @@ class ServerSessionsService extends SessionsService: override def changeSessionJsonState(session: Session, change: ServerJson): Unit = val oldV = sessions(session) - val newV = oldV.withNewState(oldV.json.include(change)) + val newV = oldV.withNewState(oldV.serverJson.include(change)) sessions += session -> newV sessionStateChangeNotificationRegistry.notifyAll((session, newV, Some(change))) logger.info(s"Session $session change $change") diff --git a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala index a6f7c202..b24faca6 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala @@ -29,7 +29,7 @@ class SessionsWebSocket(sessionsService: ServerSessionsService) extends WsListen case Some(change) => sendSessionStateChange(wsSession, session, change) private def sendSessionState(wsSession: WsSession, session: Session, sessionState: SessionState): Unit = - val response = StateWsResponse(session.hideSecret, sessionState.json).asJson.noSpaces + val response = StateWsResponse(session.hideSecret, sessionState.serverJson).asJson.noSpaces logger.info(s"$wsSession: Sending session state response $response") wsSession.send(response, true) diff --git a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala new file mode 100644 index 00000000..f47b0877 --- /dev/null +++ b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala @@ -0,0 +1,65 @@ +package org.terminal21.server.service + +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers.* +import org.terminal21.model.SessionClosed +import org.terminal21.ui.std.StdExportsBuilders.serverJson + +class ServerSessionsServiceTest extends AnyFunSuiteLike: + test("sessionById"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + serverSessionsService.sessionById(session.id) should be(session) + + test("sessionStateOf"): + new App: + val session = createSession + val sj = serverJson() + serverSessionsService.setSessionJsonState(session, sj) + serverSessionsService.sessionStateOf(session).serverJson should be(sj) + + test("removeSession"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + serverSessionsService.removeSession(session) + an[IllegalArgumentException] should be thrownBy: + serverSessionsService.sessionById(session.id) + + test("removeSession notifies listeners"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + var listenerCalled = 0 + serverSessionsService.notifyMeWhenSessionsChange: sessions => + listenerCalled match + case 0 => sessions should be(Seq(session)) + case 1 => sessions should be(Nil) + listenerCalled += 1 + true + serverSessionsService.removeSession(session) + listenerCalled should be(2) + + test("terminateSession marks session as closed"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + serverSessionsService.terminateSession(session) + serverSessionsService.sessionById(session.id).isOpen should be(false) + + test("terminateSession notifies listeners"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + var eventCalled = false + serverSessionsService.notifyMeOnSessionEvents(session): event => + event should be(SessionClosed("-")) + eventCalled = true + true + serverSessionsService.terminateSession(session) + eventCalled should be(true) + + class App: + val serverSessionsService = new ServerSessionsService + def createSession = serverSessionsService.createSession("test", "Test") diff --git a/terminal21-server/src/test/scala/scripts/ExamineJson.scala b/terminal21-server/src/test/scala/scripts/ExamineJson.scala deleted file mode 100644 index bf91d08b..00000000 --- a/terminal21-server/src/test/scala/scripts/ExamineJson.scala +++ /dev/null @@ -1,9 +0,0 @@ -//package scripts -// -//import io.circe.* -//import io.circe.generic.auto.* -//import io.circe.syntax.* -//import org.terminal21.server.json.{OnClick, WsRequest} -// -//@main def examineJson(): Unit = -// println(WsRequest("init", Some(OnClick("123"))).asJson.noSpaces) diff --git a/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/StdExportsBuilders.scala b/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/StdExportsBuilders.scala new file mode 100644 index 00000000..68f693e6 --- /dev/null +++ b/terminal21-ui-std-exports/src/test/scala/org/terminal21/ui/std/StdExportsBuilders.scala @@ -0,0 +1,14 @@ +package org.terminal21.ui.std + +import io.circe.Json + +object StdExportsBuilders: + def serverJson( + rootKeys: Seq[String] = Nil, + elements: Map[String, Json] = Map.empty, + keyTree: Map[String, Seq[String]] = Map.empty + ) = ServerJson( + rootKeys, + elements, + keyTree + ) From 8349fdd5ebefbe0970ff01b2c0373a5440cba97b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 15:09:11 +0000 Subject: [PATCH 36/40] - --- .../service/ServerSessionsServiceTest.scala | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala index f47b0877..9dc53fe0 100644 --- a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala +++ b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala @@ -48,7 +48,7 @@ class ServerSessionsServiceTest extends AnyFunSuiteLike: serverSessionsService.terminateSession(session) serverSessionsService.sessionById(session.id).isOpen should be(false) - test("terminateSession notifies listeners"): + test("terminateSession notifies session listeners"): new App: val session = createSession serverSessionsService.setSessionJsonState(session, serverJson()) @@ -60,6 +60,20 @@ class ServerSessionsServiceTest extends AnyFunSuiteLike: serverSessionsService.terminateSession(session) eventCalled should be(true) + test("terminateSession notifies sessions listeners"): + new App: + val session = createSession + serverSessionsService.setSessionJsonState(session, serverJson()) + var listenerCalled = 0 + serverSessionsService.notifyMeWhenSessionsChange: sessions => + listenerCalled match + case 0 => sessions should be(Seq(session)) + case 1 => sessions should be(Seq(session.close)) + listenerCalled += 1 + true + serverSessionsService.terminateSession(session) + listenerCalled should be(2) + class App: val serverSessionsService = new ServerSessionsService def createSession = serverSessionsService.createSession("test", "Test") From 81f530d60718445d1e1e28fc626dfe5a3bb0cc9b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 15:15:10 +0000 Subject: [PATCH 37/40] - --- .../server/utils/NotificationRegistry.scala | 7 ++++++- .../server/service/ServerSessionsServiceTest.scala | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala b/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala index e95c5c69..6efa05e2 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala @@ -17,5 +17,10 @@ class NotificationRegistry[A]: def notifyAll(a: A): Int = synchronized: - ns = ns.filter(f => Try(f(a)).getOrElse(false)) + ns = ns.filter: f => + Try(f(a)) + .recover: e => + e.printStackTrace() + false + .get ns.size diff --git a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala index 9dc53fe0..f694cb40 100644 --- a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala +++ b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala @@ -74,6 +74,19 @@ class ServerSessionsServiceTest extends AnyFunSuiteLike: serverSessionsService.terminateSession(session) listenerCalled should be(2) + test("createSession notifies listeners"): + new App: + var listenerCalled = 0 + serverSessionsService.notifyMeWhenSessionsChange: sessions => + listenerCalled match + case 0 => sessions should be(Nil) + case 1 => sessions.size should be(1) + listenerCalled += 1 + true + + val session = createSession + listenerCalled should be(2) + class App: val serverSessionsService = new ServerSessionsService def createSession = serverSessionsService.createSession("test", "Test") From b5919ec00948821a50dd6c50b5286a689406d447 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 15:42:35 +0000 Subject: [PATCH 38/40] - --- .../server/utils/NotificationRegistry.scala | 7 ++-- .../service/ServerSessionsServiceTest.scala | 35 ++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala b/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala index 6efa05e2..1b8c2c36 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/utils/NotificationRegistry.scala @@ -1,12 +1,15 @@ package org.terminal21.server.utils +import org.slf4j.LoggerFactory + import scala.util.Try // make sure this doesn't throw any exceptions type ListenerFunction[A] = A => Boolean class NotificationRegistry[A]: - private var ns = List.empty[ListenerFunction[A]] + private val logger = LoggerFactory.getLogger(getClass) + private var ns = List.empty[ListenerFunction[A]] def add(listener: ListenerFunction[A]): Unit = synchronized: @@ -20,7 +23,7 @@ class NotificationRegistry[A]: ns = ns.filter: f => Try(f(a)) .recover: e => - e.printStackTrace() + logger.error("an error occurred during a notification", e) false .get ns.size diff --git a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala index f694cb40..b8b11b1b 100644 --- a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala +++ b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala @@ -1,5 +1,6 @@ package org.terminal21.server.service +import io.circe.Json import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* import org.terminal21.model.SessionClosed @@ -84,9 +85,41 @@ class ServerSessionsServiceTest extends AnyFunSuiteLike: listenerCalled += 1 true - val session = createSession + createSession listenerCalled should be(2) + test("changeSessionJsonState changes session's state"): + new App: + val session = createSession + val sj1 = serverJson(elements = Map("e1" -> Json.fromString("e1v"))) + serverSessionsService.setSessionJsonState(session, sj1) + val sj2 = serverJson(elements = Map("e2" -> Json.fromString("e2v"))) + serverSessionsService.changeSessionJsonState(session, sj2) + serverSessionsService.sessionStateOf(session).serverJson should be( + sj1.include(sj2) + ) + + test("changeSessionJsonState notifies listeners"): + new App: + val session = createSession + val sj1 = serverJson(elements = Map("e1" -> Json.fromString("e1v"))) + serverSessionsService.setSessionJsonState(session, sj1) + val sj2 = serverJson(elements = Map("e2" -> Json.fromString("e2v"))) + var called = 0 + serverSessionsService.notifyMeWhenSessionChanges: (s, sessionState, sjOption) => + called match + case 0 => + s should be(session) + sjOption should be(None) + case 1 => + s should be(session) + sjOption should be(Some(sj2)) + + called += 1 + true + serverSessionsService.changeSessionJsonState(session, sj2) + called should be(2) + class App: val serverSessionsService = new ServerSessionsService def createSession = serverSessionsService.createSession("test", "Test") From b2d7979c4d2d8c2253a93ba6949c46320dffa950 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 19 Jan 2024 15:51:34 +0000 Subject: [PATCH 39/40] - --- .../service/ServerSessionsService.scala | 2 +- .../server/ui/SessionsWebSocket.scala | 2 +- .../service/ServerSessionsServiceTest.scala | 27 ++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala index b937260f..d8749662 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/service/ServerSessionsService.scala @@ -65,7 +65,7 @@ class ServerSessionsService extends SessionsService: sessionStateChangeNotificationRegistry.notifyAll((session, newV, Some(change))) logger.info(s"Session $session change $change") - def addEvent(event: UiEvent): Unit = + def triggerUiEvent(event: UiEvent): Unit = val e = event match case org.terminal21.server.json.OnClick(_, key) => OnClick(key) case org.terminal21.server.json.OnChange(_, key, value) => OnChange(key, value) diff --git a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala index b24faca6..e78a3544 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/ui/SessionsWebSocket.scala @@ -58,7 +58,7 @@ class SessionsWebSocket(sessionsService: ServerSessionsService) extends WsListen sendSessionState(wsSession, session, sessionState) case Right(WsRequest(eventName, Some(event: UiEvent))) => logger.info(s"$wsSession: Received event $eventName = $event") - sessionsService.addEvent(event) + sessionsService.triggerUiEvent(event) case Right(WsRequest("ping", None)) => logger.info(s"$wsSession: ping received") case Right(WsRequest("close-session", Some(CloseSession(sessionId)))) => diff --git a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala index b8b11b1b..ddd96db5 100644 --- a/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala +++ b/terminal21-server/src/test/scala/org/terminal21/server/service/ServerSessionsServiceTest.scala @@ -3,7 +3,8 @@ package org.terminal21.server.service import io.circe.Json import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* -import org.terminal21.model.SessionClosed +import org.terminal21.model.{OnChange, OnClick, SessionClosed} +import org.terminal21.server.json import org.terminal21.ui.std.StdExportsBuilders.serverJson class ServerSessionsServiceTest extends AnyFunSuiteLike: @@ -120,6 +121,30 @@ class ServerSessionsServiceTest extends AnyFunSuiteLike: serverSessionsService.changeSessionJsonState(session, sj2) called should be(2) + test("triggerUiEvent notifies listeners for clicks"): + new App: + val session = createSession + var called = false + serverSessionsService.notifyMeOnSessionEvents(session): e => + called = true + e should be(OnClick("key1")) + true + + serverSessionsService.triggerUiEvent(json.OnClick(session.id, "key1")) + called should be(true) + + test("triggerUiEvent notifies listeners for change"): + new App: + val session = createSession + var called = false + serverSessionsService.notifyMeOnSessionEvents(session): e => + called = true + e should be(OnChange("key1", "newvalue")) + true + + serverSessionsService.triggerUiEvent(json.OnChange(session.id, "key1", "newvalue")) + called should be(true) + class App: val serverSessionsService = new ServerSessionsService def createSession = serverSessionsService.createSession("test", "Test") From cbd170129b8886613edaade3981531f494c7718a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 22 Jan 2024 11:42:15 +0000 Subject: [PATCH 40/40] - --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index c02dc337..bf3371d0 100644 --- a/Readme.md +++ b/Readme.md @@ -151,7 +151,7 @@ terminal21 ui components are immutable from v0.20. Use `component.withX(...).ren and render it. Note that the original `component` is not changed. Also when getting a value of i.e. an Input, use `myInput.current.value`. `current` makes sure we read the component with -all changes that may have occurred at the browser. +all changes that may have occurred at the browser and all the changes we did on our script. # Need help?