Skip to content

Commit

Permalink
Merge pull request #5 from kostaskougios/0.20
Browse files Browse the repository at this point in the history
0.20
  • Loading branch information
kostaskougios authored Jan 22, 2024
2 parents 7dc4fe6 + cbd1701 commit 3c71ae9
Show file tree
Hide file tree
Showing 65 changed files with 2,187 additions and 988 deletions.
28 changes: 13 additions & 15 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,28 +147,26 @@ 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 and all the changes we did on our script.

# 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

- ability to render only 1 element and it's children
- refactoring internal way of storing elements on lib - server - ui

## Version 0.11

Expand Down
26 changes: 21 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
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.
*/
val scala3Version = "3.3.1"

ThisBuild / version := "0.11"
ThisBuild / version := "0.20"
ThisBuild / organization := "io.github.kostaskougios"
name := "rest-api"
ThisBuild / scalaVersion := scala3Version
Expand All @@ -25,8 +27,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 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(
Expand Down Expand Up @@ -94,13 +99,13 @@ 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
.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"
Expand Down Expand Up @@ -176,3 +181,14 @@ lazy val `terminal21-mathjax` = project
)
)
.dependsOn(`terminal21-ui-std` % "compile->compile;test->test")

lazy val `terminal21-code-generation`: Project = project
.settings(
commonSettings,
libraryDependencies ++= Seq(
ScalaTest,
Scala3Tasty,
CommonsText,
CommonsIO
)
)
3 changes: 2 additions & 1 deletion end-to-end-tests/src/main/scala/tests/ChakraComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ import org.terminal21.client.components.mathjax.*
style = Map("backgroundColor" -> "gray")
)
).render()
session.waitTillUserClosesSession()
session.leaveSessionOpenAfterExiting()
6 changes: 3 additions & 3 deletions end-to-end-tests/src/main/scala/tests/StdComponents.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ package tests

import org.terminal21.client.*
import org.terminal21.client.components.*
import org.terminal21.client.components.std.*

@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
session.render()
output.withText(newValue).renderChanges()

Seq(
Header1(text = "Welcome to the std components demo/test"),
Expand Down
12 changes: 6 additions & 6 deletions end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
)
11 changes: 11 additions & 0 deletions end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand Down
6 changes: 2 additions & 4 deletions end-to-end-tests/src/main/scala/tests/chakra/Editables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,14 @@ object Editables:
)

editable1.onChange: newValue =>
status.text = s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}"
session.render()
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.text = s"editable2 newValue = $newValue, verify editable2.value = ${editable2.value}"
session.render()
status.withText(s"editable2 newValue = $newValue, verify editable2.value = ${editable2.current.value}").renderChanges()

Seq(
commonBox(text = "Editables"),
Expand Down
55 changes: 26 additions & 29 deletions end-to-end-tests/src/main/scala/tests/chakra/Forms.scala
Original file line number Diff line number Diff line change
@@ -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.*

Expand All @@ -15,23 +15,22 @@ object Forms:

val email = Input(`type` = "email", value = "[email protected]")
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.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.text = s"description input new value = $newValue, verify description.value = ${description.value}"
session.render()
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"),
Option_(text = "Female", value = "female")
)

select1.onChange: newValue =>
status.text = s"select1 input new value = $newValue, verify select1.value = ${select1.value}"
session.render()
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"),
Expand All @@ -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}"
session.render()
status.withText(s"dob = $newValue , verify dob.value = ${dob.current.value}").renderChanges()

val color = Input(`type` = "color")

color.onChange: newValue =>
status.text = s"color = $newValue , verify color.value = ${color.value}"
session.render()
status.withText(s"color = $newValue , verify color.value = ${color.current.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}"
session.render()
status.withText(s"checkbox2 checked is $newValue , verify checkbox2.checked = ${checkbox2.current.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}"
session.render()
Seq(
status.withText(s"checkbox1 checked is $newValue , verify checkbox1.checked = ${checkbox1.current.checked}"),
checkbox2.withIsDisabled(newValue)
).renderChanges()

val switch1 = Switch(text = "Switch 1")
val switch2 = Switch(text = "Switch 2")
val switch2 = Switch(text = "Switch 2", defaultChecked = true)

switch1.onChange: newValue =>
switch2.isDisabled = newValue
status.text = s"switch1 checked is $newValue , verify switch1.checked = ${switch1.checked}"
session.render()
Seq(
status.withText(s"switch1 checked is $newValue , verify switch1.checked = ${switch1.current.checked}"),
switch2.withIsDisabled(newValue)
).renderChanges()

val radioGroup = RadioGroup(defaultValue = "2").withChildren(
HStack().withChildren(
Expand All @@ -78,8 +76,7 @@ object Forms:
)

radioGroup.onChange: newValue =>
status.text = s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.value}"
session.render()
status.withText(s"radioGroup newValue=$newValue , verify radioGroup.value=${radioGroup.current.value}").renderChanges()

Seq(
commonBox(text = "Forms"),
Expand Down Expand Up @@ -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}"
session.render()
,
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}, switch1 = ${switch1.current.checked}, switch2 = ${switch2.current.checked}"
)
.renderChanges(),
Button(text = "Cancel")
.onClick: () =>
status.text = "Cancel clicked"
session.render()
status.withText("Cancel clicked").renderChanges()
),
radioGroup,
status
Expand Down
15 changes: 4 additions & 11 deletions end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,14 @@ object Overlay:
MenuList().withChildren(
MenuItem(text = "Download menu-download")
.onClick: () =>
box1.text = "'Download' clicked"
session.render()
,
box1.withText("'Download' clicked").renderChanges(),
MenuItem(text = "Copy").onClick: () =>
box1.text = "'Copy' clicked"
session.render()
,
box1.withText("'Copy' clicked").renderChanges(),
MenuItem(text = "Paste").onClick: () =>
box1.text = "'Paste' clicked"
session.render()
,
box1.withText("'Paste' clicked").renderChanges(),
MenuDivider(),
MenuItem(text = "Exit").onClick: () =>
box1.text = "'Exit' clicked"
session.render()
box1.withText("'Exit' clicked").renderChanges()
)
),
box1
Expand Down
7 changes: 2 additions & 5 deletions example-scripts/bouncing-ball.sc
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,10 @@ 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.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
Expand Down
Loading

0 comments on commit 3c71ae9

Please sign in to comment.