-
-
Notifications
You must be signed in to change notification settings - Fork 364
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
767 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package build | ||
|
||
import mill._, kotlinlib._, kotlinlib.js._ | ||
|
||
object `package` extends RootModule with KotlinModule { | ||
|
||
def kotlinVersion = "1.9.24" | ||
def ktorVersion = "2.3.12" | ||
def kotlinHtmlVersion = "0.11.0" | ||
|
||
def mainClass = Some("webapp.WebApp") | ||
|
||
def ivyDeps = Agg( | ||
ivy"io.ktor:ktor-server-core-jvm:$ktorVersion", | ||
ivy"io.ktor:ktor-server-netty-jvm:$ktorVersion", | ||
ivy"io.ktor:ktor-server-html-builder-jvm:$ktorVersion", | ||
ivy"org.jetbrains.kotlinx:kotlinx-html-js:$kotlinHtmlVersion", | ||
) | ||
|
||
def resources = Task { | ||
os.makeDir(Task.dest / "webapp") | ||
val jsPath = client.linkBinary().classes.path | ||
// Move root.js[.map]into the proper filesystem position | ||
// in the resource folder for the web server code to pick up | ||
os.copy(jsPath / "client.js", Task.dest / "webapp/client.js") | ||
os.copy(jsPath / "client.js.map", Task.dest / "webapp/client.js.map") | ||
super.resources() ++ Seq(PathRef(Task.dest)) | ||
} | ||
|
||
object test extends KotlinTests with TestModule.Junit5 { | ||
def ivyDeps = super.ivyDeps() ++ Agg( | ||
ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1", | ||
ivy"io.ktor:ktor-server-test-host-jvm:$ktorVersion" | ||
) | ||
} | ||
|
||
object client extends KotlinJSModule { | ||
def kotlinVersion = "1.9.24" | ||
|
||
override def splitPerModule = false | ||
|
||
def ivyDeps = Agg( | ||
ivy"org.jetbrains.kotlinx:kotlinx-html-js:$kotlinHtmlVersion", | ||
) | ||
} | ||
} | ||
|
||
// A minimal example of a Kotlin backend server wired up with a Kotlin/JS | ||
// front-end. The backend code is identical to the <<_todomvc_web_app>> example, but | ||
// we replace the `main.js` client side code with the Javascript output of | ||
// `ClientApp.kt`. | ||
// | ||
// Note that the client-side Kotlin code is the simplest 1-to-1 translation of | ||
// the original Javascript, using `kotlinx.browser`, as this example is intended to | ||
// demonstrate the `build.mill` config in Mill. A real codebase is likely to use | ||
// Javascript or Kotlin/JS UI frameworks to manage the UI, but those are beyond the | ||
// scope of this example. | ||
|
||
/** Usage | ||
|
||
> ./mill test | ||
...webapp.WebAppTestssimpleRequest ... | ||
|
||
> ./mill runBackground | ||
|
||
> curl http://localhost:8082 | ||
...What needs to be done... | ||
... | ||
|
||
> curl http://localhost:8082/static/client.js | ||
...bindEvent(this, 'todo-all', '/list/all', 'all')... | ||
... | ||
|
||
> ./mill clean runBackground | ||
|
||
*/ |
80 changes: 80 additions & 0 deletions
80
example/kotlinlib/web/4-webapp-kotlinjs/client/src/ClientApp.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package client | ||
|
||
import kotlinx.browser.document | ||
import kotlinx.browser.window | ||
import org.w3c.dom.Element | ||
import org.w3c.dom.HTMLInputElement | ||
import org.w3c.dom.asList | ||
import org.w3c.dom.events.KeyboardEvent | ||
import org.w3c.dom.get | ||
import org.w3c.fetch.RequestInit | ||
|
||
object ClientApp { | ||
|
||
private var state = "all" | ||
|
||
private val todoApp: Element | ||
get() = checkNotNull(document.getElementsByClassName("todoApp")[0]) | ||
|
||
private fun postFetchUpdate(url: String) { | ||
window | ||
.fetch(url, RequestInit(method = "POST")) | ||
.then { it.text() } | ||
.then { text -> | ||
todoApp.innerHTML = text | ||
initListeners() | ||
} | ||
} | ||
|
||
private fun bindEvent(cls: String, url: String, endState: String? = null) { | ||
document.getElementsByClassName(cls)[0] | ||
?.addEventListener("mousedown", { | ||
postFetchUpdate(url) | ||
if (endState != null) state = endState | ||
} | ||
) | ||
} | ||
|
||
private fun bindIndexedEvent(cls: String, block: (String) -> String) { | ||
for (elem in document.getElementsByClassName(cls).asList()) { | ||
elem.addEventListener( | ||
"mousedown", | ||
{ postFetchUpdate(block(elem.getAttribute("data-todo-index")!!)) } | ||
) | ||
} | ||
} | ||
|
||
fun initListeners() { | ||
bindIndexedEvent("destroy") { | ||
"/delete/$state/$it" | ||
} | ||
bindIndexedEvent("toggle") { | ||
"/toggle/$state/$it" | ||
} | ||
bindEvent("toggle-all", "/toggle-all/$state") | ||
bindEvent("todo-all", "/list/all", "all") | ||
bindEvent("todo-active", "/list/active", "active") | ||
bindEvent("todo-completed", "/list/completed", "completed") | ||
bindEvent("clear-completed", "/clear-completed/$state") | ||
|
||
val newTodoInput = document.getElementsByClassName("new-todo")[0] as HTMLInputElement | ||
newTodoInput.addEventListener( | ||
"keydown", | ||
{ | ||
check(it is KeyboardEvent) | ||
if (it.keyCode == 13) { | ||
window | ||
.fetch("/add/$state", RequestInit(method = "POST", body = newTodoInput.value)) | ||
.then { it.text() } | ||
.then { text -> | ||
newTodoInput.value = "" | ||
todoApp.innerHTML = text | ||
initListeners() | ||
} | ||
} | ||
} | ||
) | ||
} | ||
} | ||
|
||
fun main(args: Array<String>) = ClientApp.initListeners() |
Oops, something went wrong.