total rework

This commit is contained in:
2022-11-16 00:56:24 +06:00
parent 53a76c7a73
commit 8430e68167
69 changed files with 904 additions and 1586 deletions

View File

@@ -7,6 +7,9 @@ plugins {
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
js (IR) {
binaries.executable()
}
sourceSets {
commonMain {
dependencies {
@@ -17,10 +20,27 @@ kotlin {
api(compose.runtime)
}
}
jsMain {
dependencies {
implementation(compose.web.core)
api libs.ktor.client.js
api libs.jsuikit
}
}
jvmMain {
dependencies {
implementation(compose.desktop.currentOs)
api libs.ktor.client.cio
}
}
}
}
compose {
desktop {
application {
mainClass = "dev.inmo.kmppscriptbuilder.desktop.BuilderKt"
}
}
}

View File

@@ -5,18 +5,26 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultDivider
expect object BuilderViewDrawer : Drawer<BuilderView>
@Composable
expect fun TopAppBar(
config: Config,
saveAvailable: Boolean,
onSaveAvailable: (Boolean) -> Unit,
onNewConfig: (Config) -> Unit
)
class BuilderView : View() {
internal val projectTypeView = ProjectTypeView()
internal val mavenInfoView = MavenInfoView()
internal val licensesView = LicensesView()
internal val projectTypeView by mutableStateOf(ProjectTypeView())
internal val mavenInfoView by mutableStateOf(MavenInfoView())
internal val licensesView by mutableStateOf(LicensesView())
internal var saveAvailableState by mutableStateOf(false)
var config: Config
get() = Config(licensesView.licenses, mavenInfoView.mavenConfig, projectTypeView.projectType)
get() {
return Config(licensesView.licenses, mavenInfoView.mavenConfig, projectTypeView.projectType)
}
set(value) {
licensesView.licenses = value.licenses
mavenInfoView.mavenConfig = value.mavenConfig
@@ -26,6 +34,20 @@ class BuilderView : View() {
@Composable
override fun build() {
with(BuilderViewDrawer) { draw() }
TopAppBar(
config,
saveAvailableState,
{
saveAvailableState = it
}
) {
config = it
}
projectTypeView.build()
DefaultDivider()
licensesView.build()
DefaultDivider()
mavenInfoView.build()
}
}

View File

@@ -1,10 +1,12 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import dev.inmo.kmppscriptbuilder.core.models.Developer
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
class DeveloperState(
id: String = "",
@@ -21,11 +23,32 @@ class DeveloperState(
private fun Developer.toDeveloperState() = DeveloperState(id, name, eMail)
class DevelopersView : ListView<DeveloperState>("Developers info") {
var developers = mutableStateListOf<Developer>()
var developers: List<Developer>
get() = itemsList.map { it.toDeveloper() }
set(value) {
itemsList.apply {
clear()
addAll(value.map { it.toDeveloperState() })
}
}
override val addItemText: String = "Add developer"
override val removeItemText: String = "Remove developer"
override fun createItem(): DeveloperState = DeveloperState()
@Composable
override fun buildView(item: DeveloperState) {
CommonTextField(
item.id,
"Developer username",
) { item.id = it }
CommonTextField(
item.name,
"Developer name",
) { item.name = it }
CommonTextField(
item.eMail,
"Developer E-Mail",
) { item.eMail = it }
}
}

View File

@@ -1,12 +1,15 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import dev.inmo.kmppscriptbuilder.core.models.License
import dev.inmo.kmppscriptbuilder.core.models.getLicenses
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import io.ktor.client.HttpClient
import kotlinx.coroutines.CoroutineScope
@@ -38,8 +41,14 @@ class LicensesView: VerticalView("Licenses") {
licensesListState.addAll(value.map { it.toLicenseState() })
}
internal val availableLicensesState = mutableStateListOf<License>()
internal val licensesOffersToShow = mutableStateListOf<License>()
internal var licenseSearchFilter by mutableStateOf("")
internal val searchFieldFocused = mutableStateOf(false)
internal val licensesOffersToShow = derivedStateOf {
val query = licenseSearchFilter.lowercase()
availableLicensesState.filter {
it.title.lowercase().contains(query)
}
}
init {
CoroutineScope(Dispatchers.Default).launch {
@@ -50,6 +59,16 @@ class LicensesView: VerticalView("Licenses") {
}
override val content: @Composable () -> Unit = {
CommonTextField(
licenseSearchFilter,
"Search filter",
onFocusChanged = {
searchFieldFocused.value = it
}
) { filterText ->
licenseSearchFilter = filterText
}
with(LicensesDrawer) { draw() }
}
}

View File

@@ -18,7 +18,7 @@ abstract class ListView<T>(title: String) : VerticalView(title) {
protected val drawer = ListViewDrawer<T>()
override val content: () -> Unit = {
override val content: @Composable () -> Unit = {
with(drawer) {
draw()
}

View File

@@ -2,9 +2,17 @@ package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.*
import dev.inmo.kmppscriptbuilder.core.models.GpgSigning
import dev.inmo.kmppscriptbuilder.core.models.MavenConfig
import dev.inmo.kmppscriptbuilder.core.models.SonatypeRepository
import dev.inmo.kmppscriptbuilder.core.models.defaultProjectDescription
import dev.inmo.kmppscriptbuilder.core.models.defaultProjectName
import dev.inmo.kmppscriptbuilder.core.ui.utils.ButtonsPanel
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import dev.inmo.kmppscriptbuilder.core.ui.utils.SwitchWithLabel
expect object MavenInfoDrawer : Drawer<MavenInfoView>
expect class GpgSigningOptionDrawer : Drawer<GpgSigning>
expect fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer
class MavenInfoView : VerticalView("Project information") {
internal var projectNameProperty by mutableStateOf("")
@@ -43,10 +51,46 @@ class MavenInfoView : VerticalView("Project information") {
publishToMavenCentralProperty = value.repositories.any { it == SonatypeRepository }
developersView.developers = value.developers
repositoriesView.repositories = value.repositories.filter { it != SonatypeRepository }
// developersView.developers = value.developers
}
private val gpgSigningDrawer = GpgSigningOptionDrawerWithView(this)
override val content: @Composable () -> Unit = {
with (MavenInfoDrawer) { draw() }
CommonTextField(
projectNameProperty,
"Public project name",
) { projectNameProperty = it }
CommonTextField(
projectDescriptionProperty,
"Public project description",
) { projectDescriptionProperty = it }
CommonTextField(
projectUrlProperty,
"Public project URL",
) { projectUrlProperty = it }
CommonTextField(
projectVcsUrlProperty,
"Public project VCS URL (with .git)",
) { projectVcsUrlProperty = it }
ButtonsPanel(
GpgSigning.Disabled,
GpgSigning.Optional,
GpgSigning.Enabled
) {
with(gpgSigningDrawer) {
with (it) {
draw()
}
}
}
SwitchWithLabel(
"Include publication to MavenCentral",
publishToMavenCentralProperty,
placeSwitchAtTheStart = true
) { publishToMavenCentralProperty = it }
developersView.build()
repositoriesView.build()
}
}

View File

@@ -4,16 +4,31 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import dev.inmo.kmppscriptbuilder.core.models.JSProjectType
import dev.inmo.kmppscriptbuilder.core.models.JVMProjectType
import dev.inmo.kmppscriptbuilder.core.models.MultiplatformProjectType
import dev.inmo.kmppscriptbuilder.core.models.ProjectType
import dev.inmo.kmppscriptbuilder.core.ui.utils.ButtonsPanel
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
expect object ProjectTypeDrawer : Drawer<ProjectTypeView>
expect class ProjectTypeDrawer : Drawer<ProjectType>
expect fun ProjectTypeDrawerWithView(view: ProjectTypeView): ProjectTypeDrawer
class ProjectTypeView : VerticalView("Project type") {
var projectType by mutableStateOf<ProjectType>(MultiplatformProjectType)
private val typeDrawer = ProjectTypeDrawerWithView(this)
override val content: @Composable () -> Unit = {
with(ProjectTypeDrawer) { draw() }
ButtonsPanel(
MultiplatformProjectType,
JVMProjectType,
JSProjectType
) {
with(typeDrawer) {
with (it) {
draw()
}
}
}
}
}

View File

@@ -2,9 +2,11 @@ package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
class RepositoryState(
@@ -19,8 +21,6 @@ class RepositoryState(
private fun MavenPublishingRepository.toRepositoryState() = RepositoryState(name, url)
expect object RepositoryStateDrawer : Drawer<RepositoryState>
class RepositoriesView : ListView<RepositoryState>("Repositories info") {
var repositories: List<MavenPublishingRepository>
get() = itemsList.map { it.toRepository() }
@@ -38,7 +38,14 @@ class RepositoriesView : ListView<RepositoryState>("Repositories info") {
@Composable
override fun buildView(item: RepositoryState) {
with(RepositoryStateDrawer) { with(item) { draw() } }
CommonTextField(
item.name,
"Repository name",
) { item.name = it }
CommonTextField(
item.url,
"Repository url",
) { item.url = it }
}
}

View File

@@ -2,7 +2,7 @@ package dev.inmo.kmppscriptbuilder.core.ui.utils
import androidx.compose.runtime.Composable
interface Drawer<T> {
fun interface Drawer<T> {
@Composable
fun T.draw()
}

View File

@@ -9,7 +9,12 @@ expect fun TitleText(text: String)
expect fun CommonText(text: String, onClick: (() -> Unit)? = null)
@Composable
expect fun CommonTextField(presetText: String, hint: String, onChange: (String) -> Unit)
expect fun CommonTextField(
presetText: String,
hint: String,
onFocusChanged: (Boolean) -> Unit = {},
onChange: (String) -> Unit
)
@Composable
expect fun SwitchWithLabel(
@@ -19,3 +24,18 @@ expect fun SwitchWithLabel(
switchEnabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit
)
@Composable
expect fun <T> ButtonsPanel(
data: Iterable<T>,
itemDrawer: @Composable (T) -> Unit
)
@Composable
fun <T> ButtonsPanel(
vararg data: T,
itemDrawer: @Composable (T) -> Unit
) = ButtonsPanel(data.toList(), itemDrawer)
@Composable
expect fun DefaultDivider()

View File

@@ -5,16 +5,7 @@ import dev.inmo.micro_utils.common.MPPFile
internal const val appExtension = "kpsb"
private var lastFile: MPPFile? = null
fun loadConfigFile(file: MPPFile): Config {
lastFile = file
return serialFormat.decodeFromString(Config.serializer(), file.text())
}
expect fun MPPFile.text(): String
expect fun loadConfig(): Config?
expect fun openNewConfig(onParsed: (Config) -> Unit)
expect fun saveConfig(config: Config): Boolean

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2M18 20H6V4H13V9H18V20M16 11V18.1L13.9 16L11.1 18.8L8.3 16L11.1 13.2L8.9 11H16Z" /></svg>

After

Width:  |  Height:  |  Size: 463 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M6.1,10L4,18V8H21A2,2 0 0,0 19,6H12L10,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H19C19.9,20 20.7,19.4 20.9,18.5L23.2,10H6.1M19,18H6L7.6,12H20.6L19,18Z" /></svg>

After

Width:  |  Height:  |  Size: 452 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M4 19H10V21H4C2.89 21 2 20.1 2 19V5C2 3.9 2.89 3 4 3H16L20 7V9.12L18 11.12V7.83L15.17 5H4V19M14 10V6H5V10H14M20.42 12.3C20.31 12.19 20.18 12.13 20.04 12.13C19.9 12.13 19.76 12.19 19.65 12.3L18.65 13.3L20.7 15.35L21.7 14.35C21.92 14.14 21.92 13.79 21.7 13.58L20.42 12.3M12 19.94V22H14.06L20.12 15.93L18.07 13.88L12 19.94M14 15C14 13.34 12.66 12 11 12S8 13.34 8 15 9.34 18 11 18C11.04 18 11.08 18 11.13 18L14 15.13C14 15.09 14 15.05 14 15" /></svg>

After

Width:  |  Height:  |  Size: 744 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M17 3H5C3.89 3 3 3.9 3 5V19C3 20.1 3.89 21 5 21H19C20.1 21 21 20.1 21 19V7L17 3M19 19H5V5H16.17L19 7.83V19M12 12C10.34 12 9 13.34 9 15S10.34 18 12 18 15 16.66 15 15 13.66 12 12 12M6 6H15V10H6V6Z" /></svg>

After

Width:  |  Height:  |  Size: 502 B

View File

@@ -0,0 +1,14 @@
package dev.inmo.kmppscriptbuilder.core
import dev.inmo.kmppscriptbuilder.core.ui.BuilderView
import kotlinx.browser.window
import org.jetbrains.compose.web.renderComposableInBody
fun main() {
window.addEventListener("load", {
val builder = BuilderView()
renderComposableInBody {
builder.build()
}
})
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.DefaultButton
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.kmppscriptbuilder.core.models.GpgSigning
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual class GpgSigningOptionDrawer(
private val mavenInfoView: MavenInfoView
) : Drawer<GpgSigning> {
@Composable
override fun GpgSigning.draw() {
if (mavenInfoView.gpgSignProperty == this) {
DefaultButton(name, UIKitButton.Type.Primary)
} else {
DefaultButton(name, UIKitButton.Type.Default) {
mavenInfoView.gpgSignProperty = this
}
}
}
}
actual fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer = GpgSigningOptionDrawer(mavenInfoView = view)

View File

@@ -0,0 +1,59 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.DefaultButton
import dev.inmo.jsuikit.elements.Divider
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.jsuikit.modifiers.builder
import dev.inmo.jsuikit.utils.Attrs
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import org.jetbrains.compose.web.dom.Div
actual object LicensesDrawer : Drawer<LicensesView> {
@Composable
override fun LicensesView.draw() {
dev.inmo.jsuikit.elements.List(
licensesOffersToShow.value,
Attrs {
if (!searchFieldFocused.value) {
hidden()
}
}
) {
DefaultButton(
it.title,
UIKitButton.Type.Text
) { _ ->
licensesListState.add(it.toLicenseState())
licenseSearchFilter = ""
}
Divider.Common()
}
DefaultButton("Add empty license", UIKitButton.Type.Primary, UIKitMargin.Small) {
licensesListState.add(LicenseState())
}
licensesListState.forEach { license ->
Div(UIKitMargin.Small.builder()) {
CommonTextField(
license.id,
"License ID",
) { license.id = it }
CommonTextField(
license.title,
"License title",
) { license.title = it }
CommonTextField(
license.url ?: "",
"License URL",
) { license.url = it }
DefaultButton("Remove", UIKitButton.Type.Danger, UIKitMargin.Small) {
licensesListState.remove(license)
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.DefaultButton
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.jsuikit.modifiers.builder
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import org.jetbrains.compose.web.dom.Div
actual class ListViewDrawer<T> : Drawer<ListView<T>> {
@Composable
override fun ListView<T>.draw() {
DefaultButton(addItemText, UIKitButton.Type.Primary ) { itemsList.add(createItem()) }
itemsList.forEach { item ->
Div(UIKitMargin.Small.builder()) {
buildView(item)
DefaultButton(removeItemText, UIKitButton.Type.Danger, UIKitMargin.Small) {
itemsList.remove(item)
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.DefaultButton
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.kmppscriptbuilder.core.models.ProjectType
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual class ProjectTypeDrawer(
private val projectTypeView: ProjectTypeView
) : Drawer<ProjectType> {
@Composable
override fun ProjectType.draw() {
if (projectTypeView.projectType == this) {
DefaultButton(name, UIKitButton.Type.Primary)
} else {
DefaultButton(name, UIKitButton.Type.Default) {
projectTypeView.projectType = this
}
}
}
}
actual fun ProjectTypeDrawerWithView(view: ProjectTypeView): ProjectTypeDrawer = ProjectTypeDrawer(projectTypeView = view)

View File

@@ -0,0 +1,82 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.Icon
import dev.inmo.jsuikit.elements.NavItemElement
import dev.inmo.jsuikit.elements.Navbar
import dev.inmo.jsuikit.elements.NavbarNav
import dev.inmo.jsuikit.elements.drawAsLink
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.jsuikit.modifiers.UIKitPadding
import dev.inmo.jsuikit.modifiers.UIKitText
import dev.inmo.jsuikit.modifiers.UIKitTooltipModifier
import dev.inmo.jsuikit.modifiers.builder
import dev.inmo.jsuikit.modifiers.include
import dev.inmo.jsuikit.utils.AttrsWithContentBuilder
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.core.utils.exportGradle
import dev.inmo.kmppscriptbuilder.core.utils.openNewConfig
import dev.inmo.kmppscriptbuilder.core.utils.saveConfig
import org.jetbrains.compose.web.dom.A
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.Img
import org.jetbrains.compose.web.dom.Text
@Composable
actual fun TopAppBar(
config: Config,
saveAvailable: Boolean,
onSaveAvailable: (Boolean) -> Unit,
onNewConfig: (Config) -> Unit
) {
Navbar(
leftBuilder = AttrsWithContentBuilder {
Div(
{
onClick {
console.log(config)
}
include(UIKitPadding.Size.Small, UIKitText.Style.Lead)
}
) {
Text("Kotlin publication scripts builder")
}
Div(UIKitMargin.Small.builder()) {
A("https://github.com/InsanusMokrassar/KotlinPublicationScriptsBuilder") {
Img("https://img.shields.io/github/stars/InsanusMokrassar/KotlinPublicationScriptsBuilder?label=Github&style=plastic")
}
}
},
rightBuilder = AttrsWithContentBuilder {
NavbarNav(
AttrsWithContentBuilder {
NavItemElement(
UIKitTooltipModifier("Open file")
) {
Icon.Storage.Pull.drawAsLink {
openNewConfig(onNewConfig)
}
}
},
AttrsWithContentBuilder {
NavItemElement(
UIKitTooltipModifier("Save config")
) {
Icon.Storage.Push.drawAsLink {
saveConfig(config)
}
}
},
AttrsWithContentBuilder {
NavItemElement(
UIKitTooltipModifier("Export gradle script")
) {
Icon.Storage.Upload.drawAsLink {
exportGradle(config)
}
}
},
)
}
)
}

View File

@@ -1,6 +1,10 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitForm
import dev.inmo.jsuikit.modifiers.builder
import org.jetbrains.compose.web.dom.Legend
import org.jetbrains.compose.web.dom.Text
actual abstract class View {
@Composable
@@ -9,4 +13,8 @@ actual abstract class View {
@Composable
actual fun View.DrawVertically(title: String, block: @Composable () -> Unit) {
Legend(UIKitForm.Legend.builder()) {
Text(title)
}
block()
}

View File

@@ -0,0 +1,107 @@
package dev.inmo.kmppscriptbuilder.core.ui.utils
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.elements.DefaultButton
import dev.inmo.jsuikit.elements.DefaultInput
import dev.inmo.jsuikit.elements.Divider
import dev.inmo.jsuikit.elements.Flex
import dev.inmo.jsuikit.elements.Icon
import dev.inmo.jsuikit.elements.Label
import dev.inmo.jsuikit.elements.drawAsFormInputPart
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitFlex
import dev.inmo.jsuikit.modifiers.UIKitForm
import dev.inmo.jsuikit.modifiers.UIKitInverse
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.jsuikit.modifiers.UIKitText
import dev.inmo.jsuikit.modifiers.UIKitUtility
import dev.inmo.jsuikit.modifiers.builder
import dev.inmo.jsuikit.modifiers.include
import dev.inmo.jsuikit.utils.Attrs
import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.Legend
import org.jetbrains.compose.web.dom.Text
@Composable
actual fun TitleText(text: String) {
Legend(UIKitForm.Legend.builder()) {
Text(text)
}
}
@Composable
actual fun CommonText(text: String, onClick: (() -> Unit)?) {
Div(
{
onClick ?.let {
this.onClick { _ ->
it()
}
}
}
) {
Text(text)
}
}
@Composable
actual fun CommonTextField(
presetText: String,
hint: String,
onFocusChanged: (Boolean) -> Unit,
onChange: (String) -> Unit
) {
DefaultInput(
InputType.Text,
presetText,
false,
placeholder = hint,
attributesCustomizer = {
onFocusIn { onFocusChanged(true) }
onFocusOut { onFocusChanged(false) }
},
onChange = onChange
)
}
@Composable
actual fun SwitchWithLabel(
label: String,
checked: Boolean,
placeSwitchAtTheStart: Boolean,
switchEnabled: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
DefaultButton(
if (checked) {
UIKitButton.Type.Primary
} else {
UIKitButton.Type.Default
},
disabled = !switchEnabled,
onClick = {
onCheckedChange(!checked)
},
attributesCustomizer = {
include(UIKitUtility.Inline)
}
) {
if (checked) {
Icon.App.Check.drawAsFormInputPart(UIKitInverse.Light)
}
Text(label)
}
}
@Composable
actual fun <T> ButtonsPanel(data: Iterable<T>, itemDrawer: @Composable (T) -> Unit) {
Flex(UIKitFlex.Alignment.Vertical.Middle, UIKitMargin.Small) {
data.forEach { itemDrawer(it) }
}
}
@Composable
actual fun DefaultDivider() {
Divider.Common()
}

View File

@@ -0,0 +1,83 @@
package dev.inmo.kmppscriptbuilder.core.utils
import dev.inmo.kmppscriptbuilder.core.models.Config
import kotlinx.browser.document
import kotlinx.dom.appendElement
import org.w3c.dom.HTMLAnchorElement
import org.w3c.dom.HTMLInputElement
import org.w3c.dom.url.URL
import org.w3c.files.Blob
import org.w3c.files.BlobPropertyBag
import org.w3c.files.FileReader
import org.w3c.files.get
fun saveFile(content: String, filename: String) {
val a = document.body!!.appendElement("a") {
setAttribute("style", "visibility:hidden; display: none")
} as HTMLAnchorElement
val blob = Blob(arrayOf(content), BlobPropertyBag(
"application/*;charset=utf-8"
))
val url = URL.createObjectURL(blob)
a.href = url
a.download = filename
a.click()
URL.revokeObjectURL(url)
a.remove()
}
actual fun openNewConfig(onParsed: (Config) -> Unit) {
val targetInput = document.body!!.appendElement("input") {
setAttribute("style", "visibility:hidden; display: none")
} as HTMLInputElement
targetInput.type = "file"
targetInput.onchange = {
targetInput.files ?.also { files ->
for (i in (0 until files.length) ) {
files[i] ?.also { file ->
val reader = FileReader()
reader.onload = {
val content = it.target.asDynamic().result as String
onParsed(serialFormat.decodeFromString(Config.serializer(), content))
false
}
reader.readAsText(file)
}
}
}
}
targetInput.click()
targetInput.remove()
}
actual fun saveConfig(config: Config): Boolean {
saveFile(
serialFormat.encodeToString(Config.serializer(), config),
"publish.kpsb"
)
return true
}
actual fun exportGradle(config: Config): Boolean {
val filename = "publish.gradle"
val content = config.run {
type.buildMavenGradleConfig(
mavenConfig,
licenses
)
}
saveFile(content, filename)
return true
}
actual fun saveAs(config: Config): Boolean {
saveFile(
serialFormat.encodeToString(Config.serializer(), config),
"publish.kpsb"
)
return true
}

View File

@@ -0,0 +1,6 @@
package dev.inmo.kmppscriptbuilder.core.utils
actual fun openLink(link: String): Boolean {
dev.inmo.micro_utils.common.openLink(link)
return true
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kotlin Publication Scripts Builder</title>
<!-- UIkit CSS -->
<link rel="stylesheet" href="./css/uikit.min.css" />
</head>
<body>
<!-- <form class="uk-padding-small">-->
<!-- <fieldset class="uk-fieldset">-->
<!-- <legend class="uk-legend">Project type</legend>-->
<!-- <div class="uk-padding-small">-->
<!-- <ul class="uk-subnav uk-subnav-pill">-->
<!-- <li id="mppProjectType" class="uk-active"><a href="#">Multiplatform</a></li>-->
<!-- <li id="jvmProjectType"><a href="#">JVM</a></li>-->
<!-- <li id="jsProjectType"><a href="#">JS</a></li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- <legend class="uk-legend">Licenses</legend>-->
<!-- <div id="licensesListDiv" class="uk-padding-small"></div>-->
<!-- <legend class="uk-legend">Project information</legend>-->
<!-- <div class="uk-padding-small">-->
<!-- <div class="uk-margin uk-width-1-1">-->
<!-- <label class="uk-form-label" for="projectNameInput">Public project name</label>-->
<!-- <input id="projectNameInput" class="uk-input uk-width-expand" type="text" placeholder="${project.name}">-->
<!-- </div>-->
<!-- <div class="uk-margin uk-width-1-1">-->
<!-- <label class="uk-form-label" for="projectDescriptionInput">Public project description</label>-->
<!-- <input id="projectDescriptionInput" class="uk-input uk-width-expand" type="text" placeholder="${project.name}">-->
<!-- </div>-->
<!-- <div class="uk-margin uk-width-1-1">-->
<!-- <label class="uk-form-label" for="projectUrlInput">Public project URL</label>-->
<!-- <input id="projectUrlInput" class="uk-input uk-width-expand" type="text" placeholder="Type url to github or other source with readme">-->
<!-- </div>-->
<!-- <div class="uk-margin uk-width-1-1">-->
<!-- <label class="uk-form-label" for="projectVCSUrlInput">Public project VCS URL (with .git)</label>-->
<!-- <input id="projectVCSUrlInput" class="uk-input uk-width-expand" type="text" placeholder="Type url to github .git file">-->
<!-- </div>-->
<!-- <div class="uk-margin">-->
<!-- <label>GPG Signing</label>-->
<!-- <div class="uk-padding-small">-->
<!-- <ul class="uk-subnav uk-subnav-pill">-->
<!-- <li id="disableGpgSigning" class="uk-active" uk-tooltip="title: Signing will not be added"><a href="#">Disabled</a></li>-->
<!-- <li id="optionalGpgSigning" uk-tooltip="title: Signing will be added, but disabled in case of absence 'signatory.keyId'"><a href="#">Optional</a></li>-->
<!-- <li id="enableGpgSigning" uk-tooltip="title: Signing will be always enabled"><a href="#">Enabled</a></li>-->
<!-- </ul>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="uk-margin">-->
<!-- <label><input id="includeMavenCentralTargetRepoToggle" class="uk-checkbox" type="checkbox"> Include publication to MavenCentral</label>-->
<!-- </div>-->
<!-- </div>-->
<!-- <legend class="uk-legend">Developers info</legend>-->
<!-- <div id="developersListDiv" class="uk-padding-small"></div>-->
<!-- <legend class="uk-legend">Repositories info</legend>-->
<!-- <div id="repositoriesListDiv" class="uk-padding-small"></div>-->
<!-- </fieldset>-->
<!-- </form>-->
<!-- UIkit JS -->
<script src="./js/uikit.min.js"></script>
<script src="./js/uikit-icons.min.js"></script>
<!-- Internal JS -->
<script src="kmppscriptbuilder.core.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,68 @@
package dev.inmo.kmppscriptbuilder.core
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.Colors
import androidx.compose.material.MaterialTheme
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import dev.inmo.kmppscriptbuilder.core.ui.BuilderView
import dev.inmo.kmppscriptbuilder.core.utils.loadConfigFile
import java.io.File
//private val uncaughtExceptionsBC = BroadcastChannel<DefaultErrorHandler.ErrorEvent>(Channel.CONFLATED)
//val uncaughtExceptionsFlow: Flow<DefaultErrorHandler.ErrorEvent> = uncaughtExceptionsBC.asFlow()
fun main(args: Array<String>) = application {
Window(onCloseRequest = ::exitApplication, title = "Kotlin Multiplatform Publishing Builder") {
val builder = BuilderView()
MaterialTheme(
Colors(
primary = Color(0x01, 0x57, 0x9b),
primaryVariant = Color(0x00, 0x2f, 0x6c),
secondary = Color(0xb2, 0xeb, 0xf2),
secondaryVariant = Color(0x81, 0xb9, 0xbf),
background = Color(0xe1, 0xe2, 0xe1),
surface = Color(0xf5, 0xf5, 0xf6),
error = Color(0xb7, 0x1c, 0x1c),
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
onError = Color.White,
isLight = MaterialTheme.colors.isLight,
)
) {
Box(
Modifier.fillMaxSize()
.background(color = Color(245, 245, 245))
) {
val stateVertical = rememberScrollState(0)
Box(
modifier = Modifier
.fillMaxSize()
.verticalScroll(stateVertical)
) {
Column {
builder.build()
}
}
VerticalScrollbar(
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
adapter = rememberScrollbarAdapter(stateVertical)
)
}
}
if (args.isNotEmpty()) {
val config = loadConfigFile(File(args.first()))
builder.config = config
}
}
}

View File

@@ -1,65 +0,0 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import dev.inmo.kmppscriptbuilder.core.utils.exportGradle
import dev.inmo.kmppscriptbuilder.core.utils.loadConfig
import dev.inmo.kmppscriptbuilder.core.utils.saveAs
import dev.inmo.kmppscriptbuilder.core.utils.saveConfig
actual object BuilderViewDrawer : Drawer<BuilderView> {
override fun BuilderView.draw() {
Box(Modifier.fillMaxSize()) {
Column() {
TopAppBar(
@Composable {
Text("Kotlin publication scripts builder", Modifier.clickable { println(config) })
},
actions = {
createIcon("Open file", "images/open_file.svg") {
loadConfig()?.also {
config = it
}
}
if (saveAvailableState) {
createIcon("Save", "images/save_file.svg") {
saveConfig(config)
}
}
if (saveAvailableState) {
createIcon("Export Gradle script", "images/export_gradle.svg") {
exportGradle(config)
}
}
createIcon("Save as", "images/save_as.svg") {
if (saveAs(config)) {
saveAvailableState = true
}
}
}
)
Column(Modifier.padding(8.dp)) {
projectTypeView.init()
Divider()
licensesView.init()
Divider()
mavenInfoView.init()
}
}
}
}
}

View File

@@ -0,0 +1,39 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.models.GpgSigning
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import dev.inmo.kmppscriptbuilder.core.ui.utils.SwitchWithLabel
actual class GpgSigningOptionDrawer(
private val mavenInfoView: MavenInfoView
) : Drawer<GpgSigning> {
@Composable
override fun GpgSigning.draw() {
if (mavenInfoView.gpgSignProperty == this) {
Button({}, Modifier.padding(8.dp)) {
Text(name)
}
} else {
OutlinedButton(
{
mavenInfoView.gpgSignProperty = this
},
Modifier.padding(8.dp)
) {
Text(name)
}
}
}
}
actual fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer = GpgSigningOptionDrawer(mavenInfoView = view)

View File

@@ -4,6 +4,9 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText
@@ -11,44 +14,39 @@ import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual object LicensesDrawer : Drawer<LicensesView> {
@Composable
override fun LicensesView.draw() {
CommonTextField(licenseSearchFilter, "Search filter") { filterText ->
licenseSearchFilter = filterText
licensesOffersToShow.clear()
if (licenseSearchFilter.isNotEmpty()) {
licensesOffersToShow.addAll(
availableLicensesState.filter { filterText.all { symbol -> symbol.lowercaseChar() in it.title } }
)
}
}
Column {
licensesOffersToShow.forEach {
Column(Modifier.padding(16.dp, 8.dp, 8.dp, 8.dp)) {
CommonText(it.title) {
licensesListState.add(it.toLicenseState())
licenseSearchFilter = ""
licensesOffersToShow.clear()
if (searchFieldFocused.value) {
Column {
licensesOffersToShow.value.forEach {
Column(Modifier.padding(16.dp, 8.dp, 8.dp, 8.dp)) {
CommonText(it.title) {
licensesListState.add(it.toLicenseState())
licenseSearchFilter = ""
}
Divider()
}
Divider()
}
}
}
Button({ licensesListState.add(LicenseState()) }, Modifier.padding(8.dp)) {
CommonText("Add empty license",)
CommonText("Add empty license")
}
licensesListState.forEach { license ->
Column(Modifier.padding(8.dp)) {
CommonTextField(
license.id,
"License ID"
"License ID",
) { license.id = it }
CommonTextField(
license.title,
"License title"
"License title",
) { license.title = it }
CommonTextField(
license.url ?: "",
"License URL"
"License URL",
) { license.url = it }
Button({ licensesListState.remove(license) }, Modifier.padding(8.dp)) {
CommonText("Remove",)

View File

@@ -3,12 +3,14 @@ package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual class ListViewDrawer<T> : Drawer<ListView<T>> {
@Composable
override fun ListView<T>.draw() {
Button({ itemsList.add(createItem()) }) {
CommonText(addItemText,)

View File

@@ -1,68 +0,0 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.models.GpgSigning
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
import dev.inmo.kmppscriptbuilder.core.ui.utils.SwitchWithLabel
actual object MavenInfoDrawer : Drawer<MavenInfoView> {
@Composable
private fun MavenInfoView.addGpgSigningButton(gpgSigning: GpgSigning) {
if (gpgSignProperty == gpgSigning) {
Button({}, Modifier.padding(8.dp)) {
Text(gpgSigning.name)
}
} else {
OutlinedButton(
{
gpgSignProperty = gpgSigning
},
Modifier.padding(8.dp)
) {
Text(gpgSigning.name)
}
}
}
override fun MavenInfoView.draw() {
CommonTextField(
projectNameProperty,
"Public project name"
) { projectNameProperty = it }
CommonTextField(
projectDescriptionProperty,
"Public project description"
) { projectDescriptionProperty = it }
CommonTextField(
projectUrlProperty,
"Public project URL"
) { projectUrlProperty = it }
CommonTextField(
projectVcsUrlProperty,
"Public project VCS URL (with .git)"
) { projectVcsUrlProperty = it }
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Gpg Signing: ")
addGpgSigningButton(GpgSigning.Disabled)
addGpgSigningButton(GpgSigning.Optional)
addGpgSigningButton(GpgSigning.Enabled)
}
SwitchWithLabel(
"Include publication to MavenCentral",
publishToMavenCentralProperty,
placeSwitchAtTheStart = true
) { publishToMavenCentralProperty = it }
developersView.init()
repositoriesView.init()
}
}

View File

@@ -15,30 +15,26 @@ import dev.inmo.kmppscriptbuilder.core.models.MultiplatformProjectType
import dev.inmo.kmppscriptbuilder.core.models.ProjectType
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual object ProjectTypeDrawer : Drawer<ProjectTypeView> {
actual class ProjectTypeDrawer(
private val projectTypeView: ProjectTypeView
) : Drawer<ProjectType> {
@Composable
private fun ProjectTypeView.addProjectTypeButton(newProjectType: ProjectType) {
if (projectType == newProjectType) {
override fun ProjectType.draw() {
if (projectTypeView.projectType == this) {
Button({}, Modifier.padding(8.dp)) {
Text(newProjectType.name)
Text(name)
}
} else {
OutlinedButton(
{
projectType = newProjectType
projectTypeView.projectType = this
},
Modifier.padding(8.dp)
) {
Text(newProjectType.name)
Text(name)
}
}
}
override fun ProjectTypeView.draw() {
Row(verticalAlignment = Alignment.CenterVertically) {
addProjectTypeButton(MultiplatformProjectType)
addProjectTypeButton(JVMProjectType)
addProjectTypeButton(JSProjectType)
}
}
}
actual fun ProjectTypeDrawerWithView(view: ProjectTypeView): ProjectTypeDrawer = ProjectTypeDrawer(projectTypeView = view)

View File

@@ -1,18 +0,0 @@
package dev.inmo.kmppscriptbuilder.core.ui
import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField
import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer
actual object RepositoryStateDrawer : Drawer<RepositoriesView> {
override fun RepositoriesView.draw() {
CommonTextField(
item.name,
"Repository name"
) { item.name = it }
CommonTextField(
item.url,
"Repository url"
) { item.url = it }
}
}

View File

@@ -0,0 +1,88 @@
package dev.inmo.kmppscriptbuilder.core.ui
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.TooltipArea
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.primarySurface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.core.utils.exportGradle
import dev.inmo.kmppscriptbuilder.core.utils.openNewConfig
import dev.inmo.kmppscriptbuilder.core.utils.saveAs
import dev.inmo.kmppscriptbuilder.core.utils.saveConfig
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun createIcon(
tooltip: String,
resource: String,
onClick: () -> Unit
) {
TooltipArea(
tooltip = {
Surface(
modifier = Modifier.shadow(4.dp),
color = MaterialTheme.colors.primarySurface,
shape = RoundedCornerShape(4.dp)
) {
Text(tooltip, modifier = Modifier.padding(10.dp), color = MaterialTheme.colors.onPrimary)
}
}
) {
IconButton(onClick) {
Image(
painter = painterResource(resource),
contentDescription = tooltip
)
}
}
}
@Composable
actual fun TopAppBar(
config: Config,
saveAvailable: Boolean,
onSaveAvailable: (Boolean) -> Unit,
onNewConfig: (Config) -> Unit
) {
TopAppBar(
@Composable {
Text("Kotlin publication scripts builder", Modifier.clickable { println(config) })
},
actions = {
createIcon("Open file", "images/open_file.svg") {
openNewConfig(onNewConfig)
}
if (saveAvailable) {
createIcon("Save", "images/save_file.svg") {
saveConfig(config)
}
}
if (saveAvailable) {
createIcon("Export Gradle script", "images/export_gradle.svg") {
exportGradle(config)
}
}
createIcon("Save as", "images/save_as.svg") {
if (saveAs(config)) {
onSaveAvailable(true)
}
}
}
)
}

View File

@@ -6,12 +6,14 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -29,15 +31,15 @@ actual fun SwitchWithLabel(
switchEnabled: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(Modifier.padding(0.dp, 8.dp), Arrangement.Start, Alignment.Top) {
Row(Modifier.padding(0.dp, 8.dp).clickable { onCheckedChange(!checked) }, Arrangement.Start, Alignment.Top) {
val switchCreator = @Composable {
Switch(checked, onCheckedChange, Modifier.padding(8.dp, 0.dp), enabled = switchEnabled)
Switch(checked, null, Modifier.padding(8.dp, 0.dp), enabled = switchEnabled)
}
if (placeSwitchAtTheStart) {
switchCreator()
}
Box(Modifier.fillMaxWidth().align(Alignment.CenterVertically).clickable { }) {
CommonText(label,)
Box(Modifier.fillMaxWidth().align(Alignment.CenterVertically)) {
CommonText(label)
}
if (!placeSwitchAtTheStart) {
switchCreator()
@@ -46,11 +48,18 @@ actual fun SwitchWithLabel(
}
@Composable
actual fun CommonTextField(presetText: String, hint: String, onChange: (String) -> Unit) {
actual fun CommonTextField(
presetText: String,
hint: String,
onFocusChanged: (Boolean) -> Unit,
onChange: (String) -> Unit
) {
OutlinedTextField(
presetText,
onChange,
Modifier.fillMaxWidth(),
Modifier.fillMaxWidth().onFocusChanged {
onFocusChanged(it.isFocused)
},
singleLine = true,
label = {
CommonText(hint,)
@@ -69,3 +78,15 @@ actual fun TitleText(text: String) {
text, Modifier.padding(0.dp, 8.dp), fontSize = 18.sp
)
}
@Composable
actual fun <T> ButtonsPanel(data: Iterable<T>, itemDrawer: @Composable (T) -> Unit) {
Row {
data.forEach { itemDrawer(it) }
}
}
@Composable
actual fun DefaultDivider() {
Divider()
}

View File

@@ -2,33 +2,29 @@ package dev.inmo.kmppscriptbuilder.core.utils
import dev.inmo.kmppscriptbuilder.core.models.Config
import dev.inmo.kmppscriptbuilder.core.ui.utils.FileFilter
import dev.inmo.kmppscriptbuilder.core.utils.serialFormat
import dev.inmo.micro_utils.common.MPPFile
import java.io.File
import javax.swing.JFileChooser
private const val appExtension = "kpsb"
fun MPPFile.text() = readText()
private var lastFile: File? = null
internal var lastFile: MPPFile? = null
fun loadConfigFile(file: File): Config {
fun loadConfigFile(file: MPPFile): Config {
lastFile = file
return serialFormat.decodeFromString(Config.serializer(), file.readText())
return serialFormat.decodeFromString(Config.serializer(), file.text())
}
actual fun MPPFile.text() = readText()
actual fun loadConfig(): Config? {
actual fun openNewConfig(onParsed: (Config) -> Unit) {
val fc = JFileChooser(lastFile ?.parent)
fc.addChoosableFileFilter(FileFilter("Kotlin Publication Scripts Builder", Regex(".*\\.$appExtension")))
fc.addChoosableFileFilter(FileFilter("JSON", Regex(".*\\.json")))
return when (fc.showOpenDialog(null)) {
when (fc.showOpenDialog(null)) {
JFileChooser.APPROVE_OPTION -> {
val file = fc.selectedFile
lastFile = file
return serialFormat.decodeFromString(Config.serializer(), fc.selectedFile.readText())
onParsed(serialFormat.decodeFromString(Config.serializer(), fc.selectedFile.readText()))
}
else -> null
}
}