some runnable variant
This commit is contained in:
parent
0716035f0b
commit
3746efc596
@ -6,7 +6,6 @@ import dev.inmo.jsuikit.elements.*
|
|||||||
import dev.inmo.jsuikit.modifiers.UIKitButton
|
import dev.inmo.jsuikit.modifiers.UIKitButton
|
||||||
import dev.inmo.jsuikit.modifiers.UIKitMargin
|
import dev.inmo.jsuikit.modifiers.UIKitMargin
|
||||||
import dev.inmo.postssystem.client.ui.fsm.*
|
import dev.inmo.postssystem.client.ui.fsm.*
|
||||||
import dev.inmo.postssystem.client.utils.HTMLViewContainer
|
|
||||||
import dev.inmo.postssystem.features.auth.client.ui.*
|
import dev.inmo.postssystem.features.auth.client.ui.*
|
||||||
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
|
||||||
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
||||||
@ -26,7 +25,6 @@ class AuthView(
|
|||||||
|
|
||||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||||
htmlElement: HTMLElement,
|
htmlElement: HTMLElement,
|
||||||
container: HTMLViewContainer,
|
|
||||||
state: AuthUIFSMState
|
state: AuthUIFSMState
|
||||||
): UIFSMState? {
|
): UIFSMState? {
|
||||||
val completion = CompletableDeferred<UIFSMState?>()
|
val completion = CompletableDeferred<UIFSMState?>()
|
||||||
@ -36,12 +34,12 @@ class AuthView(
|
|||||||
val passwordState = mutableStateOf("")
|
val passwordState = mutableStateOf("")
|
||||||
val usernameDisabled = mutableStateOf(true)
|
val usernameDisabled = mutableStateOf(true)
|
||||||
val passwordDisabled = mutableStateOf(true)
|
val passwordDisabled = mutableStateOf(true)
|
||||||
val authBtnDisabled = remember {
|
|
||||||
usernameState.value.isNotBlank() && passwordState.value.isNotBlank()
|
|
||||||
}
|
|
||||||
val errorText = mutableStateOf<String?>(null)
|
val errorText = mutableStateOf<String?>(null)
|
||||||
|
|
||||||
val composition = renderComposableAndLinkToContext(htmlElement) {
|
val composition = renderComposableAndLinkToContext(htmlElement) {
|
||||||
|
val authBtnDisabled = remember {
|
||||||
|
usernameState.value.isNotBlank() && passwordState.value.isNotBlank()
|
||||||
|
}
|
||||||
Form {
|
Form {
|
||||||
TextField(
|
TextField(
|
||||||
InputType.Text,
|
InputType.Text,
|
||||||
|
@ -2,20 +2,17 @@ package dev.inmo.postssystem.client.fsm.ui
|
|||||||
|
|
||||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMHandler
|
import dev.inmo.postssystem.client.ui.fsm.UIFSMHandler
|
||||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
||||||
import dev.inmo.postssystem.client.utils.HTMLViewContainer
|
|
||||||
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
import dev.inmo.micro_utils.fsm.common.StatesMachine
|
||||||
|
import kotlinx.browser.document
|
||||||
import org.w3c.dom.HTMLElement
|
import org.w3c.dom.HTMLElement
|
||||||
|
|
||||||
abstract class JSView<T : UIFSMState> : UIFSMHandler<T> {
|
abstract class JSView<T : UIFSMState> : UIFSMHandler<T> {
|
||||||
open suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
open suspend fun StatesMachine<in UIFSMState>.safeHandleState(
|
||||||
htmlElement: HTMLElement,
|
htmlElement: HTMLElement,
|
||||||
container: HTMLViewContainer,
|
|
||||||
state: T
|
state: T
|
||||||
): UIFSMState? = null
|
): UIFSMState? = null
|
||||||
|
|
||||||
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(state: T): UIFSMState? {
|
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(state: T): UIFSMState? {
|
||||||
return HTMLViewContainer.from(state.context) ?.let {
|
return safeHandleState(document.getElementById(state.context) as? HTMLElement ?: return null, state)
|
||||||
safeHandleState(it.htmlElement ?: return null, it, state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package dev.inmo.postssystem.client.fsm.ui.defaults
|
|
||||||
|
|
||||||
import dev.inmo.postssystem.client.ui.fsm.UIFSMState
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.html.TagConsumer
|
|
||||||
import kotlinx.html.js.button
|
|
||||||
import kotlinx.html.js.onClickFunction
|
|
||||||
import org.w3c.dom.HTMLElement
|
|
||||||
|
|
||||||
fun TagConsumer<HTMLElement>.addBackButton(
|
|
||||||
completableDeferred: CompletableDeferred<UIFSMState>,
|
|
||||||
stateToBack: UIFSMState
|
|
||||||
) {
|
|
||||||
button {
|
|
||||||
+"Назад"
|
|
||||||
onClickFunction = {
|
|
||||||
completableDeferred.complete(stateToBack)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package dev.inmo.postssystem.client.utils
|
|
||||||
|
|
||||||
import com.benasher44.uuid.uuid4
|
|
||||||
import kotlinx.browser.document
|
|
||||||
import kotlinx.html.*
|
|
||||||
import kotlinx.html.dom.append
|
|
||||||
import kotlinx.html.js.*
|
|
||||||
import org.w3c.dom.*
|
|
||||||
|
|
||||||
object DialogHelper {
|
|
||||||
fun createOneFieldDialog(
|
|
||||||
title: String,
|
|
||||||
hint: String,
|
|
||||||
doneButtonText: String,
|
|
||||||
closeButtonText: String,
|
|
||||||
onClose: () -> Unit,
|
|
||||||
onSubmit: (String) -> Unit
|
|
||||||
): HTMLDialogElement {
|
|
||||||
lateinit var dialogElement: HTMLDialogElement
|
|
||||||
(document.getElementsByTagName("body").item(0) as? HTMLBodyElement) ?.append {
|
|
||||||
dialogElement = dialog("mdl-dialog") {
|
|
||||||
h4("mdl-dialog__title") {
|
|
||||||
+title
|
|
||||||
}
|
|
||||||
val id = "form_${uuid4()}_text"
|
|
||||||
div(classes = "mdl-dialog__content") {
|
|
||||||
form("#") {
|
|
||||||
div("mdl-textfield mdl-js-textfield mdl-textfield--floating-label") {
|
|
||||||
input(InputType.text, classes = "mdl-textfield__input") {
|
|
||||||
this.id = id
|
|
||||||
}
|
|
||||||
label(classes = "mdl-textfield__label") {
|
|
||||||
+hint
|
|
||||||
attributes["for"] = id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div(classes = "mdl-dialog__actions mdl-dialog__actions--full-width") {
|
|
||||||
button(classes = "mdl-button", type = ButtonType.button) {
|
|
||||||
+doneButtonText
|
|
||||||
onClickFunction = {
|
|
||||||
it.preventDefault()
|
|
||||||
|
|
||||||
val input = document.getElementById(id) as? HTMLInputElement
|
|
||||||
input ?.value ?.let {
|
|
||||||
onSubmit(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button(classes = "mdl-button", type = ButtonType.button) {
|
|
||||||
+closeButtonText
|
|
||||||
onClickFunction = {
|
|
||||||
it.preventDefault()
|
|
||||||
|
|
||||||
onClose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dialogElement
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@ package dev.inmo.postssystem.client.utils
|
|||||||
|
|
||||||
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
||||||
import dev.inmo.micro_utils.common.toArrayBuffer
|
import dev.inmo.micro_utils.common.toArrayBuffer
|
||||||
|
import io.ktor.utils.io.core.readBytes
|
||||||
import kotlinx.browser.document
|
import kotlinx.browser.document
|
||||||
import org.w3c.dom.HTMLAnchorElement
|
import org.w3c.dom.HTMLAnchorElement
|
||||||
import org.w3c.dom.url.URL
|
import org.w3c.dom.url.URL
|
||||||
@ -10,7 +11,7 @@ import org.w3c.files.Blob
|
|||||||
fun triggerDownloadFile(fullFileInfo: FullFileInfo) {
|
fun triggerDownloadFile(fullFileInfo: FullFileInfo) {
|
||||||
val hiddenElement = document.createElement("a") as HTMLAnchorElement
|
val hiddenElement = document.createElement("a") as HTMLAnchorElement
|
||||||
|
|
||||||
val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().toArrayBuffer())))
|
val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().readBytes().toArrayBuffer())))
|
||||||
hiddenElement.href = url
|
hiddenElement.href = url
|
||||||
hiddenElement.target = "_blank"
|
hiddenElement.target = "_blank"
|
||||||
hiddenElement.download = fullFileInfo.name.name
|
hiddenElement.download = fullFileInfo.name.name
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
package dev.inmo.postssystem.client.utils
|
|
||||||
|
|
||||||
import kotlinx.browser.document
|
|
||||||
import kotlinx.dom.clear
|
|
||||||
import kotlinx.html.dom.append
|
|
||||||
import kotlinx.html.id
|
|
||||||
import kotlinx.html.js.*
|
|
||||||
import org.w3c.dom.HTMLElement
|
|
||||||
import org.w3c.dom.events.Event
|
|
||||||
|
|
||||||
object HTMLViewsConstants {
|
|
||||||
val mainContainer: MainHTMLViewContainer = MainHTMLViewContainer
|
|
||||||
val processesContainer: DrawerHTMLViewContainer = DrawerHTMLViewContainer
|
|
||||||
val toolsContainerId: ToolsHTMLViewContainer = ToolsHTMLViewContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface HTMLViewContainer {
|
|
||||||
val htmlElement: HTMLElement?
|
|
||||||
get() = document.getElementById(id) as? HTMLElement
|
|
||||||
val id: String
|
|
||||||
|
|
||||||
fun setIsLoading() {
|
|
||||||
htmlElement ?.apply {
|
|
||||||
clear()
|
|
||||||
append {
|
|
||||||
div("mdl-spinner mdl-js-spinner is-active")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun from(elementId: String) = when (elementId) {
|
|
||||||
MainHTMLViewContainer.id -> MainHTMLViewContainer
|
|
||||||
DrawerHTMLViewContainer.id -> DrawerHTMLViewContainer
|
|
||||||
ToolsHTMLViewContainer.id -> ToolsHTMLViewContainer
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object MainHTMLViewContainer : HTMLViewContainer {
|
|
||||||
override val id: String
|
|
||||||
get() = "main"
|
|
||||||
}
|
|
||||||
|
|
||||||
object DrawerHTMLViewContainer : HTMLViewContainer {
|
|
||||||
data class DrawerAddButtonInfo(
|
|
||||||
val text: String,
|
|
||||||
val onAddButtonClick: (Event) -> Unit,
|
|
||||||
)
|
|
||||||
|
|
||||||
override val id: String
|
|
||||||
get() = "drawer"
|
|
||||||
|
|
||||||
private val titleElement: HTMLElement?
|
|
||||||
get() = (document.getElementById("drawerTitle") ?:let {
|
|
||||||
htmlElement ?.append {
|
|
||||||
span("mdl-layout-title") {
|
|
||||||
id = "drawerTitle"
|
|
||||||
}
|
|
||||||
} ?.first()
|
|
||||||
}) as? HTMLElement
|
|
||||||
var title: String?
|
|
||||||
get() = titleElement ?.textContent
|
|
||||||
set(value) {
|
|
||||||
if (value == null) {
|
|
||||||
titleElement ?.classList ?.add("gone")
|
|
||||||
} else {
|
|
||||||
val element = titleElement ?: return
|
|
||||||
element.textContent = value
|
|
||||||
element.classList.remove("gone")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val contentElement
|
|
||||||
get() = (document.getElementById("drawerContent") ?:let {
|
|
||||||
htmlElement ?.append {
|
|
||||||
nav("mdl-navigation") {
|
|
||||||
id = "drawerContent"
|
|
||||||
}
|
|
||||||
} ?.first()
|
|
||||||
}) as? HTMLElement
|
|
||||||
|
|
||||||
fun <T> setListContent(
|
|
||||||
title: String?,
|
|
||||||
data: Iterable<T>,
|
|
||||||
getText: (T) -> String?,
|
|
||||||
addButtonInfo: DrawerAddButtonInfo? = null,
|
|
||||||
onClick: (T) -> Unit
|
|
||||||
) {
|
|
||||||
this.title = title
|
|
||||||
val contentElement = contentElement ?: return
|
|
||||||
|
|
||||||
contentElement.clear()
|
|
||||||
contentElement.append {
|
|
||||||
fun hideDrawer() {
|
|
||||||
// Emulate clicking for hiding of drawer
|
|
||||||
(document.getElementsByClassName("mdl-layout__obfuscator").item(0) as? HTMLElement) ?.click()
|
|
||||||
}
|
|
||||||
data.forEach {
|
|
||||||
val elementTitle = getText(it) ?: return@forEach
|
|
||||||
div("mdl-navigation__link") {
|
|
||||||
+elementTitle
|
|
||||||
onClickFunction = { _ ->
|
|
||||||
onClick(it)
|
|
||||||
hideDrawer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (addButtonInfo != null) {
|
|
||||||
button(classes = "mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent") {
|
|
||||||
+addButtonInfo.text
|
|
||||||
onClickFunction = {
|
|
||||||
addButtonInfo.onAddButtonClick(it)
|
|
||||||
hideDrawer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setIsLoading() {
|
|
||||||
contentElement ?.apply {
|
|
||||||
clear()
|
|
||||||
append {
|
|
||||||
div("mdl-spinner mdl-js-spinner is-active")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ToolsHTMLViewContainer : HTMLViewContainer {
|
|
||||||
override val id: String
|
|
||||||
get() = "tools"
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
package dev.inmo.postssystem.client.utils
|
package dev.inmo.postssystem.client.utils
|
||||||
|
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
import dev.inmo.postssystem.features.files.common.FullFileInfo
|
||||||
import dev.inmo.micro_utils.common.*
|
import dev.inmo.micro_utils.common.*
|
||||||
import dev.inmo.micro_utils.mime_types.KnownMimeTypes
|
import dev.inmo.micro_utils.mime_types.KnownMimeTypes
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
|
||||||
<link rel="stylesheet" href="styles/material.min.css" type="text/css">
|
<link rel="stylesheet" href="css/containers.css" type="text/css">
|
||||||
<link rel="stylesheet" href="styles/containers.css" type="text/css">
|
<link rel="stylesheet" href="css/visibility.css" type="text/css">
|
||||||
<link rel="stylesheet" href="styles/visibility.css" type="text/css">
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Always shows a header, even in smaller screens. -->
|
<!-- Always shows a header, even in smaller screens. -->
|
||||||
|
@ -35,14 +35,14 @@ kotlinx_html_version=0.7.3
|
|||||||
android_minSdkVersion=21
|
android_minSdkVersion=21
|
||||||
android_compileSdkVersion=32
|
android_compileSdkVersion=32
|
||||||
android_buildToolsVersion=32.0.0
|
android_buildToolsVersion=32.0.0
|
||||||
dexcount_version=3.0.0
|
dexcount_version=3.0.1
|
||||||
junit_version=4.12
|
junit_version=4.12
|
||||||
test_ext_junit_version=1.1.2
|
test_ext_junit_version=1.1.2
|
||||||
espresso_core=3.3.0
|
espresso_core=3.3.0
|
||||||
|
|
||||||
# Dokka
|
# Dokka
|
||||||
|
|
||||||
dokka_version=1.6.0
|
dokka_version=1.6.10
|
||||||
|
|
||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
|
@ -18,4 +18,4 @@ startTestPostgres:
|
|||||||
sudo docker-compose up
|
sudo docker-compose up
|
||||||
|
|
||||||
addTestUserAndAuth:
|
addTestUserAndAuth:
|
||||||
docker-compose exec test_postgres psql test -U test -c "INSERT INTO Users VALUES (-1, 'test', 'test', 'test');INSERT INTO UsersAuthentications VALUES ('test', -1);"
|
sudo docker-compose exec test_postgres psql test -U test -c "INSERT INTO Users VALUES (-1, 'test', 'test', 'test');INSERT INTO UsersAuthentications VALUES ('test', -1);"
|
||||||
|
Loading…
Reference in New Issue
Block a user