Compare commits
	
		
			13 Commits
		
	
	
		
			build-9a95
			...
			build-7536
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7536c67589 | |||
| 0770772e7d | |||
| c6b1289f5a | |||
| 788fe49aa4 | |||
| 3be0f24eac | |||
| 9fe7c458e9 | |||
| 8430e68167 | |||
| 53a76c7a73 | |||
| 70baa30127 | |||
| 283bc5acb4 | |||
| 26a5d20e26 | |||
| ca1a91e0f0 | |||
| 26fe225577 | 
							
								
								
									
										2
									
								
								.github/workflows/commit-release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -44,6 +44,6 @@ jobs: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         with: | ||||
|           upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps | ||||
|           asset_path: "./desktop/build/compose/jars/kmppscriptbuilder.desktop-linux-x64-${{ env.version }}${{ env.additional_version }}.jar" | ||||
|           asset_path: "./core/build/compose/jars/kmppscriptbuilder.desktop-linux-x64-${{ env.version }}${{ env.additional_version }}.jar" | ||||
|           asset_name: KotlinPublicationScriptsBuilder-linux-x64.jar | ||||
|           asset_content_type: application/java-archive | ||||
|   | ||||
| @@ -7,11 +7,10 @@ buildscript { | ||||
|     } | ||||
|  | ||||
|     dependencies { | ||||
|         classpath 'com.android.tools.build:gradle:7.0.4' | ||||
|         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||||
|         classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" | ||||
|         classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:$dexcount_version" | ||||
|         classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" | ||||
|         classpath libs.buildscript.kt.gradle | ||||
|         classpath libs.buildscript.kt.serialization | ||||
|         classpath libs.buildscript.jb.dokka | ||||
|         classpath libs.buildscript.gh.release | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,46 @@ | ||||
| plugins { | ||||
|     id "org.jetbrains.kotlin.multiplatform" | ||||
|     id "org.jetbrains.kotlin.plugin.serialization" | ||||
|     id "com.android.library" | ||||
|     alias(libs.plugins.jb.compose) | ||||
| } | ||||
|  | ||||
| apply from: "$mppProjectWithSerializationPresetPath" | ||||
|  | ||||
| kotlin { | ||||
|     js (IR) { | ||||
|         binaries.executable() | ||||
|     } | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
|             dependencies { | ||||
|                 api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" | ||||
|                 api "dev.inmo:micro_utils.coroutines:$micro_utils_version" | ||||
|  | ||||
|                 api "io.ktor:ktor-client-core:$ktor_version" | ||||
|                 api libs.kt.coroutines | ||||
|                 api libs.microutils.common | ||||
|                 api libs.microutils.coroutines | ||||
|                 api libs.ktor.client | ||||
|                 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" | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,14 @@ if (project.hasProperty("signing.gnupg.keyName")) { | ||||
|      | ||||
|         sign publishing.publications | ||||
|     } | ||||
| }""" | ||||
|      | ||||
|     task signAll { | ||||
|         tasks.withType(Sign).forEach { | ||||
|             dependsOn(it) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| """ | ||||
|     GpgSigning.Enabled -> | ||||
| """ | ||||
| apply plugin: 'signing' | ||||
| @@ -23,5 +30,12 @@ signing { | ||||
|     useGpgCmd() | ||||
|  | ||||
|     sign publishing.publications | ||||
| }""" | ||||
| } | ||||
|  | ||||
| task signAll { | ||||
|     tasks.withType(Sign).forEach { | ||||
|         dependsOn(it) | ||||
|     } | ||||
| } | ||||
| """ | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import dev.inmo.kmppscriptbuilder.core.utils.serialFormat | ||||
| import io.ktor.client.HttpClient | ||||
| import io.ktor.client.request.get | ||||
| import io.ktor.client.request.url | ||||
| import io.ktor.client.statement.bodyAsText | ||||
| import kotlinx.serialization.Serializable | ||||
| import kotlinx.serialization.builtins.MapSerializer | ||||
| import kotlinx.serialization.builtins.serializer | ||||
| @@ -20,9 +21,9 @@ private val commonLicensesListDeserializer = MapSerializer(String.serializer(), | ||||
| private var licenses: Map<String, License>? = null | ||||
|  | ||||
| suspend fun HttpClient.getLicenses(): Map<String, License> { | ||||
|     val answer = get<String> { | ||||
|     val answer = get { | ||||
|         url("https://licenses.opendefinition.org/licenses/groups/all.json") | ||||
|     } | ||||
|     }.bodyAsText() | ||||
|     return serialFormat.decodeFromString(commonLicensesListDeserializer, answer).also { gotLicenses -> | ||||
|         licenses = gotLicenses | ||||
|     } | ||||
|   | ||||
| @@ -6,13 +6,13 @@ const val defaultProjectName = "\${project.name}" | ||||
| const val defaultProjectDescription = "\${project.name}" | ||||
|  | ||||
| @Serializable | ||||
| sealed class GpgSigning { | ||||
| sealed class GpgSigning(val name: String) { | ||||
|     @Serializable | ||||
|     object Disabled : GpgSigning() | ||||
|     object Disabled : GpgSigning("Disabled") | ||||
|     @Serializable | ||||
|     object Optional : GpgSigning() | ||||
|     object Optional : GpgSigning("Optional") | ||||
|     @Serializable | ||||
|     object Enabled : GpgSigning() | ||||
|     object Enabled : GpgSigning("Enabled") | ||||
| } | ||||
|  | ||||
| @Serializable | ||||
| @@ -31,23 +31,84 @@ data class MavenConfig( | ||||
| @Serializable | ||||
| data class MavenPublishingRepository( | ||||
|     val name: String, | ||||
|     val url: String | ||||
|     val url: String, | ||||
|     val credsType: CredentialsType = CredentialsType.UsernameAndPassword( | ||||
|         CredentialsType.UsernameAndPassword.defaultUsernameProperty(name), | ||||
|         CredentialsType.UsernameAndPassword.defaultPasswordProperty(name), | ||||
|     ) | ||||
| ) { | ||||
|     private val nameCapitalized by lazy { | ||||
|         name.toUpperCase() | ||||
|     } | ||||
|  | ||||
|     fun build(indent: String): String { | ||||
|         val usernameProperty = "${nameCapitalized}_USER" | ||||
|         val passwordProperty = "${nameCapitalized}_PASSWORD" | ||||
|         return """if ((project.hasProperty('${usernameProperty}') || System.getenv('${usernameProperty}') != null) && (project.hasProperty('${passwordProperty}') || System.getenv('${passwordProperty}') != null)) { | ||||
|     maven { | ||||
|         name = "$name" | ||||
|         url = uri("$url") | ||||
|     @Serializable | ||||
|     sealed interface CredentialsType { | ||||
|         @Serializable | ||||
|         object Nothing: CredentialsType { | ||||
|             override fun buildCheckPart(): String = "true" | ||||
|             override fun buildCredsPart(): String = "" | ||||
|         } | ||||
|         @Serializable | ||||
|         data class UsernameAndPassword( | ||||
|             val usernameProperty: String, | ||||
|             val passwordProperty: String | ||||
|         ): CredentialsType { | ||||
|             constructor(baseParameter: String) : this( | ||||
|                 defaultUsernameProperty(baseParameter), | ||||
|                 defaultPasswordProperty(baseParameter) | ||||
|             ) | ||||
|  | ||||
|             override fun buildCheckPart(): String = "(project.hasProperty('${usernameProperty}') || System.getenv('${usernameProperty}') != null) && (project.hasProperty('${passwordProperty}') || System.getenv('${passwordProperty}') != null)" | ||||
|             override fun buildCredsPart(): String { | ||||
| return """ | ||||
|         credentials { | ||||
|             username = project.hasProperty('${usernameProperty}') ? project.property('${usernameProperty}') : System.getenv('${usernameProperty}') | ||||
|             password = project.hasProperty('${passwordProperty}') ? project.property('${passwordProperty}') : System.getenv('${passwordProperty}') | ||||
|         } | ||||
| """ | ||||
|             } | ||||
|  | ||||
|             companion object { | ||||
|                 fun defaultUsernameProperty(name: String): String { | ||||
|                     return "${name.uppercase()}_USER" | ||||
|                 } | ||||
|                 fun defaultPasswordProperty(name: String): String { | ||||
|                     return "${name.uppercase()}_PASSWORD" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         @Serializable | ||||
|         data class HttpHeaderCredentials( | ||||
|             val headerName: String, | ||||
|             val headerValueProperty: String | ||||
|         ): CredentialsType { | ||||
|             override fun buildCheckPart(): String = "project.hasProperty('${headerValueProperty}') || System.getenv('${headerValueProperty}') != null" | ||||
|             override fun buildCredsPart(): String { | ||||
| return """ | ||||
|         credentials(HttpHeaderCredentials) { | ||||
|             name = "$headerName" | ||||
|             value = project.hasProperty('${headerValueProperty}') ? project.property('${headerValueProperty}') : System.getenv('${headerValueProperty}') | ||||
|         } | ||||
| """ | ||||
|             } | ||||
|  | ||||
|             companion object { | ||||
|                 fun defaultValueProperty(name: String): String { | ||||
|                     return "${name.uppercase()}_TOKEN" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         fun buildCheckPart(): String | ||||
|         fun buildCredsPart(): String | ||||
|     } | ||||
|     private val nameCapitalized by lazy { | ||||
|         name.uppercase() | ||||
|     } | ||||
|  | ||||
|     fun build(indent: String): String { | ||||
|         return """if (${credsType.buildCheckPart()}) { | ||||
|     maven { | ||||
|         name = "$name" | ||||
|         url = uri("$url") | ||||
| ${credsType.buildCredsPart()} | ||||
|     } | ||||
| }""".replace("\n", "\n$indent") | ||||
|     } | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| 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.Config | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultContentColumn | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultDivider | ||||
|  | ||||
| @Composable | ||||
| expect fun TopAppBar( | ||||
|     config: Config, | ||||
|     saveAvailable: Boolean, | ||||
|     onSaveAvailable: (Boolean) -> Unit, | ||||
|     onNewConfig: (Config) -> Unit | ||||
| ) | ||||
|  | ||||
| class BuilderView : View() { | ||||
|     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() { | ||||
|             return Config(licensesView.licenses, mavenInfoView.mavenConfig, projectTypeView.projectType) | ||||
|         } | ||||
|         set(value) { | ||||
|             licensesView.licenses = value.licenses | ||||
|             mavenInfoView.mavenConfig = value.mavenConfig | ||||
|             projectTypeView.projectType = value.type | ||||
|             saveAvailableState = true | ||||
|         } | ||||
|  | ||||
|     @Composable | ||||
|     override fun build() { | ||||
|         TopAppBar( | ||||
|             config, | ||||
|             saveAvailableState, | ||||
|             { | ||||
|                 saveAvailableState = it | ||||
|             } | ||||
|         ) { | ||||
|             config = it | ||||
|         } | ||||
|  | ||||
|         DefaultContentColumn { | ||||
|             projectTypeView.build() | ||||
|             DefaultDivider() | ||||
|             licensesView.build() | ||||
|             DefaultDivider() | ||||
|             mavenInfoView.build() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,13 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
| 
 | ||||
| import androidx.compose.runtime.* | ||||
| 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.Developer | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| 
 | ||||
| class DeveloperState( | ||||
|     id: String = "", | ||||
| @@ -22,10 +27,10 @@ class DevelopersView : ListView<DeveloperState>("Developers info") { | ||||
|     var developers: List<Developer> | ||||
|         get() = itemsList.map { it.toDeveloper() } | ||||
|         set(value) { | ||||
|             itemsList.clear() | ||||
|             itemsList.addAll( | ||||
|                 value.map { it.toDeveloperState() } | ||||
|             ) | ||||
|             itemsList.apply { | ||||
|                 clear() | ||||
|                 addAll(value.map { it.toDeveloperState() }) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     override val addItemText: String = "Add developer" | ||||
| @@ -34,18 +39,19 @@ class DevelopersView : ListView<DeveloperState>("Developers info") { | ||||
|     override fun createItem(): DeveloperState = DeveloperState() | ||||
|     @Composable | ||||
|     override fun buildView(item: DeveloperState) { | ||||
|         CommonText("Developer username") | ||||
|         CommonTextField( | ||||
|             item.id, | ||||
|             "Developer username" | ||||
|         ) { item.id = it } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Developer name") | ||||
|         CommonTextField( | ||||
|             item.name, | ||||
|             "Developer name" | ||||
|         ) { item.name = it } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Developer E-Mail") | ||||
|         CommonTextField( | ||||
|             item.eMail, | ||||
|             "Developer E-Mail" | ||||
|         ) { item.eMail = it } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,103 @@ | ||||
| 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.setValue | ||||
| import dev.inmo.kmppscriptbuilder.core.models.License | ||||
| import dev.inmo.kmppscriptbuilder.core.models.getLicenses | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import io.ktor.client.HttpClient | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.launch | ||||
|  | ||||
| class LicenseState( | ||||
|     id: String = "", | ||||
|     title: String = "", | ||||
|     url: String? = null | ||||
| ) { | ||||
|     var id: String by mutableStateOf(id) | ||||
|     var title: String by mutableStateOf(title) | ||||
|     var url: String? by mutableStateOf(url) | ||||
|  | ||||
|     fun toLicense() = License(id, title, url) | ||||
| } | ||||
|  | ||||
| internal fun License.toLicenseState() = LicenseState(id, title, url) | ||||
|  | ||||
| expect object LicensesDrawer : Drawer<LicensesView> | ||||
|  | ||||
| class LicensesView : ListView<LicenseState>("Licenses") { | ||||
|     var licenses: List<License> | ||||
|         get() = itemsList.map { it.toLicense() } | ||||
|         set(value) { | ||||
|             itemsList.clear() | ||||
|             itemsList.addAll(value.map { it.toLicenseState() }) | ||||
|         } | ||||
|     internal val availableLicensesState = 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) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override val addItemText: String | ||||
|         get() = "Add empty license" | ||||
|  | ||||
|     init { | ||||
|         CoroutineScope(Dispatchers.Default).launch { | ||||
|             val client = HttpClient() | ||||
|             availableLicensesState.addAll(client.getLicenses().values) | ||||
|             client.close() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun createItem(): LicenseState = LicenseState() | ||||
|  | ||||
|     @Composable | ||||
|     override fun buildView(item: LicenseState) { | ||||
|         CommonText("License ID") | ||||
|         CommonTextField( | ||||
|             item.id, | ||||
|             "Short name like \"Apache-2.0\"", | ||||
|         ) { item.id = it } | ||||
|         CommonText("License title") | ||||
|         CommonTextField( | ||||
|             item.title, | ||||
|             "Official title of license (like \"Apache Software License 2.0\")", | ||||
|         ) { item.title = it } | ||||
|         CommonText("License URL") | ||||
|         CommonTextField( | ||||
|             item.url ?: "", | ||||
|             "Link to your LICENSE file OR official license file (like \"https://opensource.org/licenses/Apache-2.0\")", | ||||
|         ) { item.url = it } | ||||
|     } | ||||
|  | ||||
|     override val content: @Composable () -> Unit = { | ||||
|         CommonTextField( | ||||
|             licenseSearchFilter, | ||||
|             "Search filter", | ||||
|             onFocusChanged = { | ||||
|                 searchFieldFocused.value = it | ||||
|             } | ||||
|         ) { filterText -> | ||||
|             licenseSearchFilter = filterText | ||||
|         } | ||||
|         DefaultSmallVerticalMargin() | ||||
|  | ||||
|         with(LicensesDrawer) { draw() } | ||||
|  | ||||
|         DefaultSmallVerticalMargin() | ||||
|  | ||||
|         super.content() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.mutableStateListOf | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| expect class ListViewDrawer<T>() : Drawer<ListView<T>> | ||||
|  | ||||
| abstract class ListView<T>(title: String) : VerticalView(title) { | ||||
|     internal val itemsList = mutableStateListOf<T>() | ||||
|  | ||||
|     internal open val addItemText: String = "Add" | ||||
|     internal open val removeItemText: String = "Remove" | ||||
|  | ||||
|     internal abstract fun createItem(): T | ||||
|     @Composable | ||||
|     internal abstract fun buildView(item: T) | ||||
|  | ||||
|     protected val drawer = ListViewDrawer<T>() | ||||
|  | ||||
|     override val content: @Composable () -> Unit = { | ||||
|         with(drawer) { | ||||
|             draw() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,106 @@ | ||||
| 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.CommonText | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.SwitchWithLabel | ||||
|  | ||||
| expect class GpgSigningOptionDrawer : Drawer<GpgSigning> | ||||
| expect fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer | ||||
|  | ||||
| class MavenInfoView : VerticalView("Project information") { | ||||
|     internal var projectNameProperty by mutableStateOf("") | ||||
|     internal var projectDescriptionProperty by mutableStateOf("") | ||||
|     internal var projectUrlProperty by mutableStateOf("") | ||||
|     internal var projectVcsUrlProperty by mutableStateOf("") | ||||
|     internal var gpgSignProperty by mutableStateOf<GpgSigning>(GpgSigning.Disabled) | ||||
|     internal var publishToMavenCentralProperty by mutableStateOf(false) | ||||
|     internal val developersView = DevelopersView() | ||||
|     internal val repositoriesView = RepositoriesView() | ||||
|  | ||||
|     var mavenConfig: MavenConfig | ||||
|         get() = MavenConfig( | ||||
|             projectNameProperty.ifBlank { defaultProjectName }, | ||||
|             projectDescriptionProperty.ifBlank { defaultProjectDescription }, | ||||
|             projectUrlProperty, | ||||
|             projectVcsUrlProperty, | ||||
|             developersView.developers, | ||||
|             repositoriesView.repositories + if (publishToMavenCentralProperty) { | ||||
|                 listOf(SonatypeRepository) | ||||
|             } else { | ||||
|                 emptyList() | ||||
|             }, | ||||
|             gpgSignProperty | ||||
|         ) | ||||
|         set(value) { | ||||
|             projectNameProperty = value.name | ||||
|             projectDescriptionProperty = value.description | ||||
|             projectUrlProperty = value.url | ||||
|             projectVcsUrlProperty = value.vcsUrl | ||||
|             gpgSignProperty = if (value.includeGpgSigning) { | ||||
|                 GpgSigning.Enabled | ||||
|             } else { | ||||
|                 value.gpgSigning | ||||
|             } | ||||
|             publishToMavenCentralProperty = value.repositories.any { it == SonatypeRepository } | ||||
|             developersView.developers = value.developers | ||||
|             repositoriesView.repositories = value.repositories.filter { it != SonatypeRepository } | ||||
|         } | ||||
|  | ||||
|     private val gpgSigningDrawer = GpgSigningOptionDrawerWithView(this) | ||||
|  | ||||
|     override val content: @Composable () -> Unit = { | ||||
|         CommonText("Public project name") | ||||
|         CommonTextField( | ||||
|             projectNameProperty, | ||||
|             "\${project.name}", | ||||
|         ) { projectNameProperty = it } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Public project description") | ||||
|         CommonTextField( | ||||
|             projectDescriptionProperty, | ||||
|             "\${project.name}", | ||||
|         ) { projectDescriptionProperty = it } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Public project URL") | ||||
|         CommonTextField( | ||||
|             projectUrlProperty, | ||||
|             "Type url to github or other source with readme", | ||||
|         ) { projectUrlProperty = it } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Public project VCS URL (with .git)") | ||||
|         CommonTextField( | ||||
|             projectVcsUrlProperty, | ||||
|             "Type url to github .git file" | ||||
|         ) { projectVcsUrlProperty = it } | ||||
|  | ||||
|         ButtonsPanel( | ||||
|             "Gpg signing", | ||||
|             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() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| 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.DefaultSmallVerticalMargin | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| 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 = { | ||||
|         ButtonsPanel( | ||||
|             "Project type", | ||||
|             MultiplatformProjectType, | ||||
|             JVMProjectType, | ||||
|             JSProjectType | ||||
|         ) { | ||||
|             with(typeDrawer) { | ||||
|                 with (it) { | ||||
|                     draw() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,142 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.remember | ||||
| import androidx.compose.runtime.setValue | ||||
| import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.ButtonsPanel | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonText | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultContentColumn | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| class RepositoryState( | ||||
|     name: String = "", | ||||
|     url: String = "", | ||||
|     credsType: MavenPublishingRepository.CredentialsType = MavenPublishingRepository.CredentialsType.UsernameAndPassword(name) | ||||
| ) { | ||||
|     var name: String by mutableStateOf(name) | ||||
|     var url: String by mutableStateOf(url) | ||||
|     var credsType by mutableStateOf(credsType) | ||||
|  | ||||
|     fun toRepository() = MavenPublishingRepository(name, url, credsType) | ||||
| } | ||||
|  | ||||
| private fun MavenPublishingRepository.toRepositoryState() = RepositoryState(name, url, credsType) | ||||
|  | ||||
| expect class RepositoryCredentialTypeDrawer : Drawer<MavenPublishingRepository.CredentialsType> | ||||
| expect fun RepositoryCredentialTypeDrawerWithState(repositoryState: RepositoryState): RepositoryCredentialTypeDrawer | ||||
|  | ||||
| class RepositoriesView : ListView<RepositoryState>("Repositories info") { | ||||
|     var repositories: List<MavenPublishingRepository> | ||||
|         get() = itemsList.map { it.toRepository() } | ||||
|         set(value) { | ||||
|             itemsList.clear() | ||||
|             itemsList.addAll( | ||||
|                 value.map { it.toRepositoryState() } | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|     override val addItemText: String = "Add repository" | ||||
|     override val removeItemText: String = "Remove repository" | ||||
|  | ||||
|     override fun createItem(): RepositoryState = RepositoryState() | ||||
|  | ||||
|     @Composable | ||||
|     override fun buildView(item: RepositoryState) { | ||||
|         val credsTypesDrawer = remember { | ||||
|             RepositoryCredentialTypeDrawerWithState(item) | ||||
|         } | ||||
|  | ||||
|         CommonText("Repository name") | ||||
|         CommonTextField( | ||||
|             item.name, | ||||
|             "This name will be used to identify repository in gradle" | ||||
|         ) { | ||||
|             val previous = item.name | ||||
|             item.name = it | ||||
|             when (val currentCredsType = item.credsType) { | ||||
|                 is MavenPublishingRepository.CredentialsType.HttpHeaderCredentials -> { | ||||
|                     if (MavenPublishingRepository.CredentialsType.HttpHeaderCredentials.defaultValueProperty(previous) == currentCredsType.headerValueProperty) { | ||||
|                         item.credsType = currentCredsType.copy( | ||||
|                             headerValueProperty = MavenPublishingRepository.CredentialsType.HttpHeaderCredentials.defaultValueProperty(it) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|                 MavenPublishingRepository.CredentialsType.Nothing -> {} | ||||
|                 is MavenPublishingRepository.CredentialsType.UsernameAndPassword -> { | ||||
|                     var current: MavenPublishingRepository.CredentialsType.UsernameAndPassword = currentCredsType | ||||
|                     if (MavenPublishingRepository.CredentialsType.UsernameAndPassword.defaultUsernameProperty(previous) == currentCredsType.usernameProperty) { | ||||
|                         current = current.copy( | ||||
|                             usernameProperty = MavenPublishingRepository.CredentialsType.UsernameAndPassword.defaultUsernameProperty(it) | ||||
|                         ) | ||||
|                     } | ||||
|                     if (MavenPublishingRepository.CredentialsType.UsernameAndPassword.defaultPasswordProperty(previous) == currentCredsType.passwordProperty) { | ||||
|                         current = current.copy( | ||||
|                             passwordProperty = MavenPublishingRepository.CredentialsType.UsernameAndPassword.defaultPasswordProperty(it) | ||||
|                         ) | ||||
|                     } | ||||
|                     item.credsType = current | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         DefaultSmallVerticalMargin() | ||||
|         CommonText("Repository url") | ||||
|         CommonTextField( | ||||
|             item.url, | ||||
|             "For example: https://repo.maven.apache.org/maven2/" | ||||
|         ) { item.url = it } | ||||
|  | ||||
|         ButtonsPanel( | ||||
|             "Credentials type", | ||||
|             MavenPublishingRepository.CredentialsType.Nothing.takeIf { item.credsType != it } ?: item.credsType, | ||||
|             MavenPublishingRepository.CredentialsType.UsernameAndPassword(item.name).takeIf { item.credsType !is MavenPublishingRepository.CredentialsType.UsernameAndPassword } ?: item.credsType, | ||||
|             MavenPublishingRepository.CredentialsType.HttpHeaderCredentials( | ||||
|                 "Authorization", | ||||
|                 MavenPublishingRepository.CredentialsType.HttpHeaderCredentials.defaultValueProperty(item.name) | ||||
|             ).takeIf { item.credsType !is MavenPublishingRepository.CredentialsType.HttpHeaderCredentials } ?: item.credsType, | ||||
|         ) { | ||||
|             with(credsTypesDrawer) { | ||||
|                 with(it) { | ||||
|                     draw() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         DefaultContentColumn { | ||||
|             when (val credsType = item.credsType) { | ||||
|                 is MavenPublishingRepository.CredentialsType.HttpHeaderCredentials -> { | ||||
|                     CommonText("Header name") | ||||
|                     CommonTextField(credsType.headerName) { | ||||
|                         item.credsType = credsType.copy(headerName = it) | ||||
|                     } | ||||
|                     DefaultSmallVerticalMargin() | ||||
|  | ||||
|                     CommonText("Property name") | ||||
|                     CommonTextField(credsType.headerValueProperty) { | ||||
|                         item.credsType = credsType.copy(headerValueProperty = it) | ||||
|                     } | ||||
|                 } | ||||
|                 MavenPublishingRepository.CredentialsType.Nothing -> { | ||||
|                     CommonText("No parameters for absence of credentials") | ||||
|                 } | ||||
|                 is MavenPublishingRepository.CredentialsType.UsernameAndPassword -> { | ||||
|                     CommonText("Username property name") | ||||
|                     CommonTextField(credsType.usernameProperty) { | ||||
|                         item.credsType = credsType.copy(usernameProperty = it) | ||||
|                     } | ||||
|                     DefaultSmallVerticalMargin() | ||||
|  | ||||
|                     CommonText("Password property name") | ||||
|                     CommonTextField(credsType.passwordProperty) { | ||||
|                         item.credsType = credsType.copy(passwordProperty = it) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultContentColumn | ||||
|  | ||||
| abstract class VerticalView(protected val title: String) : View() { | ||||
|     abstract val content: @Composable () -> Unit | ||||
|     @Composable | ||||
|     override fun build() { | ||||
|         DefaultContentColumn { | ||||
|             DrawVertically(title, content) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
|  | ||||
| expect abstract class View() { | ||||
|     @Composable | ||||
|     abstract fun build() | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| expect fun View.DrawVertically(title: String, block: @Composable () -> Unit) | ||||
|  | ||||
| @Composable | ||||
| fun <T : View> T.init(): T = apply { | ||||
|     build() | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui.utils | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
|  | ||||
| fun interface Drawer<T> { | ||||
|     @Composable | ||||
|     fun T.draw() | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui.utils | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
|  | ||||
| @Composable | ||||
| expect fun TitleText(text: String) | ||||
|  | ||||
| @Composable | ||||
| expect fun CommonText(text: String, onClick: (() -> Unit)? = null) | ||||
|  | ||||
| @Composable | ||||
| expect fun CommonTextField( | ||||
|     presetText: String, | ||||
|     hint: String? = null, | ||||
|     onFocusChanged: (Boolean) -> Unit = {}, | ||||
|     onChange: (String) -> Unit | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| expect fun SwitchWithLabel( | ||||
|     label: String, | ||||
|     checked: Boolean, | ||||
|     placeSwitchAtTheStart: Boolean = false, | ||||
|     switchEnabled: Boolean = true, | ||||
|     onCheckedChange: (Boolean) -> Unit | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| expect fun <T> ButtonsPanel( | ||||
|     title: String, | ||||
|     data: Iterable<T>, | ||||
|     itemDrawer: @Composable (T) -> Unit | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| fun <T> ButtonsPanel( | ||||
|     title: String, | ||||
|     vararg data: T, | ||||
|     itemDrawer: @Composable (T) -> Unit | ||||
| ) = ButtonsPanel(title, data.toList(), itemDrawer) | ||||
|  | ||||
| @Composable | ||||
| expect fun DefaultDivider() | ||||
|  | ||||
| @Composable | ||||
| expect fun DefaultSmallVerticalMargin() | ||||
|  | ||||
| @Composable | ||||
| expect fun DefaultContentColumn(block: @Composable () -> Unit) | ||||
| @@ -0,0 +1,14 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.utils | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Config | ||||
| import dev.inmo.micro_utils.common.MPPFile | ||||
|  | ||||
| internal const val appExtension = "kpsb" | ||||
|  | ||||
| expect fun openNewConfig(onParsed: (Config) -> Unit) | ||||
|  | ||||
| expect fun saveConfig(config: Config): Boolean | ||||
|  | ||||
| expect fun exportGradle(config: Config): Boolean | ||||
|  | ||||
| expect fun saveAs(config: Config): Boolean | ||||
| @@ -0,0 +1,3 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.utils | ||||
|  | ||||
| expect fun openLink(link: String): Boolean | ||||
| Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B | 
| Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 452 B | 
| Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B | 
| Before Width: | Height: | Size: 502 B After Width: | Height: | Size: 502 B | 
| @@ -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() | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| 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.UIKitTooltipModifier | ||||
| import dev.inmo.jsuikit.modifiers.UIKitUtility | ||||
| import dev.inmo.kmppscriptbuilder.core.models.GpgSigning | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.NoTransform | ||||
|  | ||||
| actual class GpgSigningOptionDrawer( | ||||
|     private val mavenInfoView: MavenInfoView | ||||
| ) : Drawer<GpgSigning> { | ||||
|     @Composable | ||||
|     override fun GpgSigning.draw() { | ||||
|         val tooltipModifier = UIKitTooltipModifier( | ||||
|             when (this) { | ||||
|                 GpgSigning.Disabled -> "Signing will not be added" | ||||
|                 GpgSigning.Enabled -> "Signing will be always enabled" | ||||
|                 GpgSigning.Optional -> "Signing will be added, but disabled in case of absence 'signatory.keyId'" | ||||
|             } | ||||
|         ) | ||||
|         if (mavenInfoView.gpgSignProperty == this) { | ||||
|             DefaultButton(name, UIKitButton.Type.Primary, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded, tooltipModifier) | ||||
|         } else { | ||||
|             DefaultButton(name, UIKitButton.Type.Default, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded, tooltipModifier) { | ||||
|                 mavenInfoView.gpgSignProperty = this | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer = GpgSigningOptionDrawer(mavenInfoView = view) | ||||
| @@ -0,0 +1,37 @@ | ||||
| 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.UIKitUtility | ||||
| 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 dev.inmo.kmppscriptbuilder.core.ui.utils.NoTransform | ||||
| 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 | ||||
|             ) { _ -> | ||||
|                 itemsList.add(it.toLicenseState()) | ||||
|                 licenseSearchFilter = "" | ||||
|             } | ||||
|             Divider.Common() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| 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.UIKitUtility | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultContentColumn | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.NoTransform | ||||
|  | ||||
| actual class ListViewDrawer<T> : Drawer<ListView<T>> { | ||||
|     @Composable | ||||
|     override fun ListView<T>.draw() { | ||||
|         itemsList.forEach { item -> | ||||
|             DefaultContentColumn { | ||||
|                 buildView(item) | ||||
|                 DefaultButton(removeItemText, UIKitButton.Type.Default, UIKitMargin.Small, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) { | ||||
|                     itemsList.remove(item) | ||||
|                 } | ||||
|             } | ||||
|             DefaultButton(addItemText, UIKitButton.Type.Primary, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded ) { itemsList.add(createItem()) } | ||||
|         } | ||||
|         if (itemsList.isEmpty()) { | ||||
|             DefaultButton(addItemText, UIKitButton.Type.Primary, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded ) { itemsList.add(createItem()) } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| 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.UIKitText | ||||
| import dev.inmo.jsuikit.modifiers.UIKitUtility | ||||
| import dev.inmo.kmppscriptbuilder.core.models.ProjectType | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.NoTransform | ||||
|  | ||||
| actual class ProjectTypeDrawer( | ||||
|     private val projectTypeView: ProjectTypeView | ||||
| ) : Drawer<ProjectType> { | ||||
|     @Composable | ||||
|     override fun ProjectType.draw() { | ||||
|         if (projectTypeView.projectType == this) { | ||||
|             DefaultButton(name, UIKitButton.Type.Primary, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) | ||||
|         } else { | ||||
|             DefaultButton(name, UIKitButton.Type.Default, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) { | ||||
|                 projectTypeView.projectType = this | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun ProjectTypeDrawerWithView(view: ProjectTypeView): ProjectTypeDrawer = ProjectTypeDrawer(projectTypeView = view) | ||||
| @@ -0,0 +1,32 @@ | ||||
| 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.UIKitUtility | ||||
| import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.NoTransform | ||||
|  | ||||
| actual class RepositoryCredentialTypeDrawer( | ||||
|     private val state: RepositoryState | ||||
| ) : Drawer<MavenPublishingRepository.CredentialsType> { | ||||
|     @Composable | ||||
|     override fun MavenPublishingRepository.CredentialsType.draw() { | ||||
|         val name = when (this@draw) { | ||||
|             is MavenPublishingRepository.CredentialsType.HttpHeaderCredentials -> "Headers" | ||||
|             MavenPublishingRepository.CredentialsType.Nothing -> "No" | ||||
|             is MavenPublishingRepository.CredentialsType.UsernameAndPassword -> "Username and password" | ||||
|         } | ||||
|         if (state.credsType == this) { | ||||
|             DefaultButton(name, UIKitButton.Type.Primary, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) | ||||
|         } else { | ||||
|             DefaultButton(name, UIKitButton.Type.Default, UIKitButton.Size.Small, UIKitMargin.Small.Horizontal, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) { | ||||
|                 state.credsType = this | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun RepositoryCredentialTypeDrawerWithState(repositoryState: RepositoryState): RepositoryCredentialTypeDrawer = RepositoryCredentialTypeDrawer(repositoryState) | ||||
| @@ -0,0 +1,92 @@ | ||||
| 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.UIKitBackground | ||||
| import dev.inmo.jsuikit.modifiers.UIKitInverse | ||||
| import dev.inmo.jsuikit.modifiers.UIKitMargin | ||||
| import dev.inmo.jsuikit.modifiers.UIKitModifier | ||||
| import dev.inmo.jsuikit.modifiers.UIKitNavbar | ||||
| import dev.inmo.jsuikit.modifiers.UIKitPadding | ||||
| import dev.inmo.jsuikit.modifiers.UIKitSection | ||||
| import dev.inmo.jsuikit.modifiers.UIKitText | ||||
| import dev.inmo.jsuikit.modifiers.UIKitTooltipModifier | ||||
| import dev.inmo.jsuikit.modifiers.attrsBuilder | ||||
| 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.Section | ||||
| import org.jetbrains.compose.web.dom.Text | ||||
|  | ||||
| @Composable | ||||
| actual fun TopAppBar( | ||||
|     config: Config, | ||||
|     saveAvailable: Boolean, | ||||
|     onSaveAvailable: (Boolean) -> Unit, | ||||
|     onNewConfig: (Config) -> Unit | ||||
| ) { | ||||
|     Section(attrsBuilder(UIKitSection.Style.Primary, UIKitInverse.Light)) { | ||||
|         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) | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                 ) | ||||
|             }, | ||||
|             navModifiers = arrayOf(UIKitNavbar.Transparent) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import dev.inmo.jsuikit.modifiers.UIKitForm | ||||
| import dev.inmo.jsuikit.modifiers.builder | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| import org.jetbrains.compose.web.dom.Legend | ||||
| import org.jetbrains.compose.web.dom.Text | ||||
|  | ||||
| actual abstract class View { | ||||
|     @Composable | ||||
|     actual abstract fun build() | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun View.DrawVertically(title: String, block: @Composable () -> Unit) { | ||||
|     Legend(UIKitForm.Legend.builder()) { | ||||
|         Text(title) | ||||
|     } | ||||
|     DefaultSmallVerticalMargin() | ||||
|     block() | ||||
|     DefaultSmallVerticalMargin() | ||||
| } | ||||
| @@ -0,0 +1,128 @@ | ||||
| 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.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.UIKitUtility | ||||
| import dev.inmo.jsuikit.modifiers.attrsBuilder | ||||
| import dev.inmo.jsuikit.modifiers.builder | ||||
| import dev.inmo.jsuikit.modifiers.include | ||||
| import kotlinx.browser.window | ||||
| 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 { | ||||
|                 window.setTimeout( // avoid immediate hiding of potential interface data with additional delay | ||||
|                     { onFocusChanged(false) }, | ||||
|                     100 | ||||
|                 ) | ||||
|             } | ||||
|         }, | ||||
|         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, UIKitUtility.NoTransform, UIKitUtility.Border.Rounded) | ||||
|         } | ||||
|     ) { | ||||
|         if (checked) { | ||||
|             Icon.App.Check.drawAsFormInputPart(UIKitInverse.Light) | ||||
|         } | ||||
|         Text(label) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun <T> ButtonsPanel( | ||||
|     title: String, | ||||
|     data: Iterable<T>, | ||||
|     itemDrawer: @Composable (T) -> Unit | ||||
| ) { | ||||
|     Flex(UIKitFlex.Alignment.Vertical.Middle, UIKitMargin.Small) { | ||||
|         Div(UIKitMargin.Small.Horizontal.builder()) { Text(title) } | ||||
|         data.forEach { itemDrawer(it) } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultDivider() { | ||||
|     Divider.Common() | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultSmallVerticalMargin() { | ||||
|     Div(UIKitMargin.Small.Top.builder()) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultContentColumn(block: @Composable () -> Unit) { | ||||
|     Div(attrsBuilder(UIKitMargin.Small.Horizontal, UIKitMargin.Small.Vertical)) { | ||||
|         block() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui.utils | ||||
|  | ||||
| import dev.inmo.jsuikit.modifiers.UIKitCustom | ||||
| import dev.inmo.jsuikit.modifiers.UIKitUtility | ||||
|  | ||||
| val ClassNoTransform = UIKitCustom(arrayOf("no-transform")) | ||||
|  | ||||
| val UIKitUtility.Companion.NoTransform | ||||
|     get() = ClassNoTransform | ||||
| @@ -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 | ||||
| } | ||||
| @@ -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 | ||||
| } | ||||
							
								
								
									
										4
									
								
								core/src/jsMain/resources/css/internal.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| .no-transform { | ||||
|     text-transform: none; | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								core/src/jsMain/resources/css/uikit.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										73
									
								
								core/src/jsMain/resources/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,73 @@ | ||||
| <!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" /> | ||||
|     <link rel="stylesheet" href="./css/internal.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> | ||||
							
								
								
									
										1
									
								
								core/src/jsMain/resources/js/uikit-icons.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								core/src/jsMain/resources/js/uikit.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -1,4 +1,4 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop | ||||
| package dev.inmo.kmppscriptbuilder.core | ||||
| 
 | ||||
| import androidx.compose.foundation.* | ||||
| import androidx.compose.foundation.layout.* | ||||
| @@ -9,9 +9,8 @@ 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.desktop.utils.init | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.loadConfigFile | ||||
| import dev.inmo.kmppscriptbuilder.desktop.views.BuilderView | ||||
| 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) | ||||
| @@ -48,7 +47,9 @@ fun main(args: Array<String>) = application { | ||||
|                         .fillMaxSize() | ||||
|                         .verticalScroll(stateVertical) | ||||
|                 ) { | ||||
|                     builder.init() | ||||
|                     Column { | ||||
|                         builder.build() | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
| @@ -0,0 +1,33 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.DefaultSmallVerticalMargin | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.TitleText | ||||
|  | ||||
| actual abstract class View { | ||||
|     internal open val defaultModifier = Modifier.fillMaxWidth() | ||||
|     @Composable | ||||
|     actual abstract fun build() | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun View.DrawVertically( | ||||
|     title: String, | ||||
|     block: @Composable () -> Unit | ||||
| ) { | ||||
|     TitleText(title) | ||||
|     DefaultSmallVerticalMargin() | ||||
|  | ||||
|     Column(defaultModifier) { | ||||
|         block() | ||||
|     } | ||||
|  | ||||
|     DefaultSmallVerticalMargin() | ||||
| } | ||||
| @@ -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, 0.dp)) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } else { | ||||
|             OutlinedButton( | ||||
|                 { | ||||
|                     mavenInfoView.gpgSignProperty = this | ||||
|                 }, | ||||
|                 Modifier.padding(8.dp, 0.dp) | ||||
|             ) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun GpgSigningOptionDrawerWithView(view: MavenInfoView): GpgSigningOptionDrawer = GpgSigningOptionDrawer(mavenInfoView = view) | ||||
| @@ -0,0 +1,31 @@ | ||||
| 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.material.Divider | ||||
| 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.CommonTextField | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| actual object LicensesDrawer : Drawer<LicensesView> { | ||||
|     @Composable | ||||
|     override fun LicensesView.draw() { | ||||
|         if (searchFieldFocused.value) { | ||||
|             Column { | ||||
|                 licensesOffersToShow.value.forEach { | ||||
|                     Column(Modifier.padding(16.dp, 8.dp, 8.dp, 8.dp)) { | ||||
|                         CommonText(it.title) { | ||||
|                             itemsList.add(it.toLicenseState()) | ||||
|                             licenseSearchFilter = "" | ||||
|                         } | ||||
|                         Divider() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,33 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.OutlinedButton | ||||
| 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.DefaultContentColumn | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| actual class ListViewDrawer<T> : Drawer<ListView<T>> { | ||||
|     @Composable | ||||
|     override fun ListView<T>.draw() { | ||||
|         itemsList.forEach { item -> | ||||
|             DefaultContentColumn { | ||||
|                 buildView(item) | ||||
|                 OutlinedButton({ itemsList.remove(item) }, Modifier.padding(8.dp)) { | ||||
|                     CommonText(removeItemText,) | ||||
|                 } | ||||
|             } | ||||
|             Button({ itemsList.add(createItem()) }) { | ||||
|                 CommonText(addItemText,) | ||||
|             } | ||||
|         } | ||||
|         if (itemsList.isEmpty()) { | ||||
|             Button({ itemsList.add(createItem()) }) { | ||||
|                 CommonText(addItemText,) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| 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.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.Drawer | ||||
|  | ||||
| actual class ProjectTypeDrawer( | ||||
|     private val projectTypeView: ProjectTypeView | ||||
| ) : Drawer<ProjectType> { | ||||
|     @Composable | ||||
|     override fun ProjectType.draw() { | ||||
|         if (projectTypeView.projectType == this) { | ||||
|             Button({}, Modifier.padding(8.dp, 0.dp)) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } else { | ||||
|             OutlinedButton( | ||||
|                 { | ||||
|                     projectTypeView.projectType = this | ||||
|                 }, | ||||
|                 Modifier.padding(8.dp, 0.dp) | ||||
|             ) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun ProjectTypeDrawerWithView(view: ProjectTypeView): ProjectTypeDrawer = ProjectTypeDrawer(projectTypeView = view) | ||||
| @@ -0,0 +1,40 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui | ||||
|  | ||||
| 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.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.Drawer | ||||
|  | ||||
| actual class RepositoryCredentialTypeDrawer( | ||||
|     private val state: RepositoryState | ||||
| ) : Drawer<MavenPublishingRepository.CredentialsType> { | ||||
|     @Composable | ||||
|     override fun MavenPublishingRepository.CredentialsType.draw() { | ||||
|         val name = when (this@draw) { | ||||
|             is MavenPublishingRepository.CredentialsType.HttpHeaderCredentials -> "Headers" | ||||
|             MavenPublishingRepository.CredentialsType.Nothing -> "No" | ||||
|             is MavenPublishingRepository.CredentialsType.UsernameAndPassword -> "Username and password" | ||||
|         } | ||||
|         if (state.credsType == this) { | ||||
|             Button({}, Modifier.padding(8.dp, 0.dp)) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } else { | ||||
|             OutlinedButton( | ||||
|                 { | ||||
|                     state.credsType = this | ||||
|                 }, | ||||
|                 Modifier.padding(8.dp, 0.dp) | ||||
|             ) { | ||||
|                 Text(name) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| actual fun RepositoryCredentialTypeDrawerWithState(repositoryState: RepositoryState): RepositoryCredentialTypeDrawer = RepositoryCredentialTypeDrawer(repositoryState) | ||||
| @@ -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) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,113 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.ui.utils | ||||
|  | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| 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 | ||||
|  | ||||
|  | ||||
| val commonTextFieldTextStyle = TextStyle( | ||||
|     fontSize = 12.sp | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| actual fun SwitchWithLabel( | ||||
|     label: String, | ||||
|     checked: Boolean, | ||||
|     placeSwitchAtTheStart: Boolean, | ||||
|     switchEnabled: Boolean, | ||||
|     onCheckedChange: (Boolean) -> Unit | ||||
| ) { | ||||
|     Row(Modifier.padding(0.dp, 8.dp).clickable { onCheckedChange(!checked) }, Arrangement.Start, Alignment.Top) { | ||||
|         val switchCreator = @Composable { | ||||
|             Switch(checked, null, Modifier.padding(8.dp, 0.dp), enabled = switchEnabled) | ||||
|         } | ||||
|         if (placeSwitchAtTheStart) { | ||||
|             switchCreator() | ||||
|         } | ||||
|         Box(Modifier.fillMaxWidth().align(Alignment.CenterVertically)) { | ||||
|             CommonText(label) | ||||
|         } | ||||
|         if (!placeSwitchAtTheStart) { | ||||
|             switchCreator() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun CommonTextField( | ||||
|     presetText: String, | ||||
|     hint: String?, | ||||
|     onFocusChanged: (Boolean) -> Unit, | ||||
|     onChange: (String) -> Unit | ||||
| ) { | ||||
|     OutlinedTextField( | ||||
|         presetText, | ||||
|         onChange, | ||||
|         Modifier.fillMaxWidth().onFocusChanged { | ||||
|             onFocusChanged(it.isFocused) | ||||
|         }, | ||||
|         singleLine = true, | ||||
|         label = hint ?.let { | ||||
|             { | ||||
|                 CommonText(hint) | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun CommonText(text: String, onClick: (() -> Unit)?) { | ||||
|     Text(text, modifier = Modifier.run { onClick ?.let { clickable(onClick = it) } ?: this }) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun TitleText(text: String) { | ||||
|     Text( | ||||
|         text, Modifier.padding(0.dp, 8.dp), fontSize = 18.sp | ||||
|     ) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun <T> ButtonsPanel( | ||||
|     title: String, | ||||
|     data: Iterable<T>, | ||||
|     itemDrawer: @Composable (T) -> Unit | ||||
| ) { | ||||
|     Row { | ||||
|         Text(title, Modifier.padding(8.dp)) | ||||
|         data.forEach { itemDrawer(it) } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultDivider() { | ||||
|     Divider() | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultSmallVerticalMargin() { | ||||
|     Spacer(Modifier.padding(0.dp, 4.dp)) | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| actual fun DefaultContentColumn(block: @Composable () -> Unit) { | ||||
|     Column(Modifier.padding(8.dp)) { | ||||
|         block() | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.utils | ||||
| package dev.inmo.kmppscriptbuilder.core.ui.utils | ||||
| 
 | ||||
| import java.io.File | ||||
| import javax.swing.filechooser.FileFilter | ||||
| @@ -1,40 +1,40 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.utils | ||||
| package dev.inmo.kmppscriptbuilder.core.utils | ||||
| 
 | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Config | ||||
| import dev.inmo.kmppscriptbuilder.core.utils.serialFormat | ||||
| import dev.inmo.kmppscriptbuilder.core.ui.utils.FileFilter | ||||
| 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()) | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun saveConfig(config: Config): Boolean { | ||||
| actual fun saveConfig(config: Config): Boolean { | ||||
|     return lastFile ?.also { | ||||
|         it.writeText(serialFormat.encodeToString(Config.serializer(), config)) | ||||
|     } != null | ||||
| } | ||||
| 
 | ||||
| fun exportGradle(config: Config): Boolean { | ||||
| actual fun exportGradle(config: Config): Boolean { | ||||
|     val fc = JFileChooser(lastFile ?.parent) | ||||
|     fc.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY | ||||
|     return when (fc.showSaveDialog(null)) { | ||||
| @@ -55,7 +55,7 @@ fun exportGradle(config: Config): Boolean { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun saveAs(config: Config): Boolean { | ||||
| actual fun saveAs(config: Config): Boolean { | ||||
|     val fc = JFileChooser(lastFile ?.parent) | ||||
|     fc.addChoosableFileFilter(FileFilter("Kotlin Publication Scripts Builder", Regex(".*\\.$appExtension"))) | ||||
|     fc.addChoosableFileFilter(FileFilter("JSON", Regex(".*\\.json"))) | ||||
| @@ -1,9 +1,9 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.utils | ||||
| package dev.inmo.kmppscriptbuilder.core.utils | ||||
| 
 | ||||
| import java.awt.Desktop | ||||
| import java.net.URI | ||||
| 
 | ||||
| fun openLink(link: String): Boolean { | ||||
| actual fun openLink(link: String): Boolean { | ||||
|     val desktop = if (Desktop.isDesktopSupported()) Desktop.getDesktop() else null | ||||
|     if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { | ||||
|         try { | ||||
| @@ -0,0 +1,2 @@ | ||||
| package dev.inmo.kmppscriptbuilder.core.utils  | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| <manifest package="dev.inmo.KotlinPublicationScriptsBuilder.core"/> | ||||
| @@ -1,40 +0,0 @@ | ||||
| apply plugin: 'com.getkeepsafe.dexcount' | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion "$android_compileSdkVersion".toInteger() | ||||
|     buildToolsVersion "$android_buildToolsVersion" | ||||
|  | ||||
|     defaultConfig { | ||||
|         minSdkVersion "$android_minSdkVersion".toInteger() | ||||
|         targetSdkVersion "$android_compileSdkVersion".toInteger() | ||||
|         versionCode "${android_code_version}".toInteger() | ||||
|         versionName "$version" | ||||
|     } | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled false | ||||
|         } | ||||
|         debug { | ||||
|             debuggable true | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     packagingOptions { | ||||
|         exclude 'META-INF/kotlinx-serialization-runtime.kotlin_module' | ||||
|         exclude 'META-INF/kotlinx-serialization-cbor.kotlin_module' | ||||
|         exclude 'META-INF/kotlinx-serialization-properties.kotlin_module' | ||||
|     } | ||||
|  | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
|         jvmTarget = JavaVersion.VERSION_1_8.toString() | ||||
|     } | ||||
|  | ||||
|     sourceSets { | ||||
|         main.java.srcDirs += 'src/main/kotlin' | ||||
|     } | ||||
| } | ||||
| @@ -1,36 +0,0 @@ | ||||
| plugins { | ||||
|     id "org.jetbrains.kotlin.multiplatform" | ||||
|     id "org.jetbrains.kotlin.plugin.serialization" | ||||
|     id("org.jetbrains.compose") version "$compose_version" | ||||
| } | ||||
|  | ||||
| apply from: "$mppJavaProjectPresetPath" | ||||
|  | ||||
| kotlin { | ||||
|     jvm { | ||||
|         compilations.main.kotlinOptions { | ||||
|             jvmTarget = "11" | ||||
|         } | ||||
|     } | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
|             dependencies { | ||||
|                 implementation project(":kmppscriptbuilder.core") | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         jvmMain { | ||||
|             dependencies { | ||||
|                 implementation(compose.desktop.currentOs) | ||||
|                 api "io.ktor:ktor-client-cio:$ktor_version" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| compose.desktop { | ||||
|     application { | ||||
|         mainClass = "dev.inmo.kmppscriptbuilder.desktop.BuilderKt" | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1,60 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.utils | ||||
|  | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.material.* | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.text.TextStyle | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.compose.ui.unit.sp | ||||
|  | ||||
| val commonTextFieldTextStyle = TextStyle( | ||||
|     fontSize = 12.sp | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| inline fun TitleText(text: String) = Text( | ||||
|     text, Modifier.padding(0.dp, 8.dp), fontSize = 18.sp | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| inline fun CommonText(text: String, modifier: Modifier = Modifier) = Text( | ||||
|     text, modifier = modifier | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| inline fun CommonTextField(presetText: String, hint: String, noinline onChange: (String) -> Unit) = OutlinedTextField( | ||||
|     presetText, | ||||
|     onChange, | ||||
|     Modifier.fillMaxWidth(), | ||||
|     singleLine = true, | ||||
|     label = { | ||||
|         CommonText(hint) | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| inline fun SwitchWithLabel( | ||||
|     label: String, | ||||
|     checked: Boolean, | ||||
|     placeSwitchAtTheStart: Boolean = false, | ||||
|     switchEnabled: Boolean = true, | ||||
|     modifier: Modifier = Modifier.padding(0.dp, 8.dp), | ||||
|     horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, | ||||
|     verticalAlignment: Alignment.Vertical = Alignment.Top, | ||||
|     switchModifier: Modifier = Modifier.padding(8.dp, 0.dp), | ||||
|     noinline onCheckedChange: (Boolean) -> Unit | ||||
| ) = Row(modifier, horizontalArrangement, verticalAlignment) { | ||||
|     val switchCreator = @Composable { | ||||
|         Switch(checked, onCheckedChange, switchModifier, enabled = switchEnabled) | ||||
|     } | ||||
|     if (placeSwitchAtTheStart) { | ||||
|         switchCreator() | ||||
|     } | ||||
|     CommonText(label, Modifier.align(Alignment.CenterVertically).clickable {  }) | ||||
|     if (!placeSwitchAtTheStart) { | ||||
|         switchCreator() | ||||
|     } | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.utils | ||||
|  | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
|  | ||||
| abstract class View { | ||||
|     protected open val defaultModifier = Modifier.fillMaxWidth().padding(8.dp) | ||||
|  | ||||
|     @Composable | ||||
|     abstract fun build() | ||||
| } | ||||
|  | ||||
| abstract class VerticalView(val title: String) : View() { | ||||
|     abstract val content: @Composable ColumnScope.() -> Unit | ||||
|     @Composable | ||||
|     override fun build() { | ||||
|         TitleText(title) | ||||
|  | ||||
|         Column( | ||||
|             defaultModifier | ||||
|         ) { | ||||
|             content() | ||||
|         } | ||||
|  | ||||
|         Spacer(Modifier.fillMaxWidth().height(8.dp)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun <T : View> T.init(): T = apply { | ||||
|     build() | ||||
| } | ||||
| @@ -1,103 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.foundation.Image | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.material.* | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.res.painterResource | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Config | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.* | ||||
|  | ||||
| class BuilderView : View() { | ||||
|     private val projectTypeView = ProjectTypeView() | ||||
|     private val mavenInfoView = MavenInfoView() | ||||
|     private val licensesView = LicensesView() | ||||
|     private var saveAvailableState by mutableStateOf(false) | ||||
|  | ||||
|     override val defaultModifier: Modifier = Modifier.fillMaxSize() | ||||
|  | ||||
|     var config: Config | ||||
|         get() = Config(licensesView.licenses, mavenInfoView.mavenConfig, projectTypeView.projectType) | ||||
|         set(value) { | ||||
|             licensesView.licenses = value.licenses | ||||
|             mavenInfoView.mavenConfig = value.mavenConfig | ||||
|             projectTypeView.projectType = value.type | ||||
|             saveAvailableState = true | ||||
|         } | ||||
|  | ||||
|     @Composable | ||||
|     override fun build() { | ||||
|         Box(Modifier.fillMaxSize()) { | ||||
|             Column() { | ||||
|                 TopAppBar( | ||||
|                     @Composable { | ||||
|                         CommonText("Kotlin publication scripts builder", Modifier.clickable { println(config) }) | ||||
|                     }, | ||||
|                     actions = { | ||||
|                         IconButton( | ||||
|                             { | ||||
|                                 loadConfig()?.also { | ||||
|                                     config = it | ||||
|                                 } | ||||
|                             } | ||||
|                         ) { | ||||
|                             Image( | ||||
|                                 painter = painterResource("images/open_file.svg"), | ||||
|                                 contentDescription = "Open file" | ||||
|                             ) | ||||
|                         } | ||||
|  | ||||
|                         if (saveAvailableState) { | ||||
|                             IconButton( | ||||
|                                 { | ||||
|                                     saveConfig(config) | ||||
|                                 } | ||||
|                             ) { | ||||
|                                 Image( | ||||
|                                     painter = painterResource("images/save_file.svg"), | ||||
|                                     contentDescription = "Save file" | ||||
|                                 ) | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if (saveAvailableState) { | ||||
|                             IconButton( | ||||
|                                 { | ||||
|                                     exportGradle(config) | ||||
|                                 } | ||||
|                             ) { | ||||
|                                 Image( | ||||
|                                     painter = painterResource("images/export_gradle.svg"), | ||||
|                                     contentDescription = "Export Gradle script" | ||||
|                                 ) | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         IconButton( | ||||
|                             { | ||||
|                                 if (saveAs(config)) { | ||||
|                                     saveAvailableState = true | ||||
|                                 } | ||||
|                             } | ||||
|                         ) { | ||||
|                             Image( | ||||
|                                 painter = painterResource("images/save_as.svg"), | ||||
|                                 contentDescription = "Export Gradle script" | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 ) | ||||
|                 Column(Modifier.padding(8.dp)) { | ||||
|                     projectTypeView.init() | ||||
|                     Divider() | ||||
|                     licensesView.init() | ||||
|                     Divider() | ||||
|                     mavenInfoView.init() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,95 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.material.Divider | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.core.models.License | ||||
| import dev.inmo.kmppscriptbuilder.core.models.getLicenses | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.* | ||||
| import io.ktor.client.HttpClient | ||||
| import kotlinx.coroutines.* | ||||
|  | ||||
| private class LicenseState( | ||||
|     id: String = "", | ||||
|     title: String = "", | ||||
|     url: String? = null | ||||
| ) { | ||||
|     var id: String by mutableStateOf(id) | ||||
|     var title: String by mutableStateOf(title) | ||||
|     var url: String? by mutableStateOf(url) | ||||
|  | ||||
|     fun toLicense() = License(id, title, url) | ||||
| } | ||||
|  | ||||
| private fun License.toLicenseState() = LicenseState(id, title, url) | ||||
|  | ||||
| class LicensesView: VerticalView("Licenses") { | ||||
|     private var licensesListState = mutableStateListOf<LicenseState>() | ||||
|     var licenses: List<License> | ||||
|         get() = licensesListState.map { it.toLicense() } | ||||
|         set(value) { | ||||
|             licensesListState.clear() | ||||
|             licensesListState.addAll(value.map { it.toLicenseState() }) | ||||
|         } | ||||
|     private val availableLicensesState = mutableStateListOf<License>() | ||||
|     private val licensesOffersToShow = mutableStateListOf<License>() | ||||
|     private var licenseSearchFilter by mutableStateOf("") | ||||
|  | ||||
|     init { | ||||
|         CoroutineScope(Dispatchers.Default).launch { | ||||
|             val client = HttpClient() | ||||
|             availableLicensesState.addAll(client.getLicenses().values) | ||||
|             client.close() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override val content: @Composable ColumnScope.() -> Unit = { | ||||
|         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, Modifier.clickable { | ||||
|                         licensesListState.add(it.toLicenseState()) | ||||
|                         licenseSearchFilter = "" | ||||
|                         licensesOffersToShow.clear() | ||||
|                     }) | ||||
|                     Divider() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         Button({ licensesListState.add(LicenseState()) }, Modifier.padding(8.dp)) { | ||||
|             CommonText("Add empty license") | ||||
|         } | ||||
|         licensesListState.forEach { license -> | ||||
|             Column(Modifier.padding(8.dp)) { | ||||
|                 CommonTextField( | ||||
|                     license.id, | ||||
|                     "License ID" | ||||
|                 ) { license.id = it } | ||||
|                 CommonTextField( | ||||
|                     license.title, | ||||
|                     "License title" | ||||
|                 ) { license.title = it } | ||||
|                 CommonTextField( | ||||
|                     license.url ?: "", | ||||
|                     "License URL" | ||||
|                 ) { license.url = it } | ||||
|                 Button({ licensesListState.remove(license) }, Modifier.padding(8.dp)) { | ||||
|                     CommonText("Remove") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.material.Button | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.mutableStateListOf | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.CommonText | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.VerticalView | ||||
|  | ||||
| abstract class ListView<T>(title: String) : VerticalView(title) { | ||||
|     protected val itemsList = mutableStateListOf<T>() | ||||
|  | ||||
|     protected open val addItemText: String = "Add" | ||||
|     protected open val removeItemText: String = "Remove" | ||||
|  | ||||
|     protected abstract fun createItem(): T | ||||
|     @Composable | ||||
|     protected abstract fun buildView(item: T) | ||||
|  | ||||
|     override val content: @Composable ColumnScope.() -> Unit = { | ||||
|         Button({ itemsList.add(createItem()) }) { | ||||
|             CommonText(addItemText) | ||||
|         } | ||||
|         itemsList.forEach { item -> | ||||
|             Column(Modifier.padding(8.dp)) { | ||||
|                 buildView(item) | ||||
|                 Button({ itemsList.remove(item) }, Modifier.padding(8.dp)) { | ||||
|                     CommonText(removeItemText) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,81 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.foundation.layout.ColumnScope | ||||
| import androidx.compose.runtime.* | ||||
| import dev.inmo.kmppscriptbuilder.core.models.* | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.* | ||||
|  | ||||
| class MavenInfoView : VerticalView("Project information") { | ||||
|     private var projectNameProperty by mutableStateOf("") | ||||
|     private var projectDescriptionProperty by mutableStateOf("") | ||||
|     private var projectUrlProperty by mutableStateOf("") | ||||
|     private var projectVcsUrlProperty by mutableStateOf("") | ||||
|     private var gpgSignProperty by mutableStateOf<GpgSigning>(GpgSigning.Disabled) | ||||
|     private var publishToMavenCentralProperty by mutableStateOf(false) | ||||
|     private val developersView = DevelopersView() | ||||
|     private val repositoriesView = RepositoriesView() | ||||
|  | ||||
|     var mavenConfig: MavenConfig | ||||
|         get() = MavenConfig( | ||||
|             projectNameProperty.ifBlank { defaultProjectName }, | ||||
|             projectDescriptionProperty.ifBlank { defaultProjectDescription }, | ||||
|             projectUrlProperty, | ||||
|             projectVcsUrlProperty, | ||||
|             developersView.developers, | ||||
|             repositoriesView.repositories + if (publishToMavenCentralProperty) { | ||||
|                 listOf(SonatypeRepository) | ||||
|             } else { | ||||
|                 emptyList() | ||||
|             }, | ||||
|             gpgSignProperty | ||||
|         ) | ||||
|         set(value) { | ||||
|             projectNameProperty = value.name | ||||
|             projectDescriptionProperty = value.description | ||||
|             projectUrlProperty = value.url | ||||
|             projectVcsUrlProperty = value.vcsUrl | ||||
|             gpgSignProperty = if (value.includeGpgSigning) { | ||||
|                 GpgSigning.Enabled | ||||
|             } else { | ||||
|                 value.gpgSigning | ||||
|             } | ||||
|             publishToMavenCentralProperty = value.repositories.any { it == SonatypeRepository } | ||||
|             developersView.developers = value.developers | ||||
|             repositoriesView.repositories = value.repositories.filter { it != SonatypeRepository } | ||||
| //            developersView.developers = value.developers | ||||
|         } | ||||
|  | ||||
|     override val content: @Composable ColumnScope.() -> Unit = { | ||||
|         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 } | ||||
|  | ||||
|  | ||||
| //        SwitchWithLabel( | ||||
| //            "Include GPG Signing", | ||||
| //            includeGpgSignProperty, | ||||
| //            placeSwitchAtTheStart = true | ||||
| //        ) { includeGpgSignProperty = it } | ||||
|  | ||||
|         SwitchWithLabel( | ||||
|             "Include publication to MavenCentral", | ||||
|             publishToMavenCentralProperty, | ||||
|             placeSwitchAtTheStart = true | ||||
|         ) { publishToMavenCentralProperty = it } | ||||
|         developersView.init() | ||||
|         repositoriesView.init() | ||||
|     } | ||||
| } | ||||
| @@ -1,33 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.material.Switch | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.unit.dp | ||||
| import dev.inmo.kmppscriptbuilder.core.models.* | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.VerticalView | ||||
|  | ||||
| class ProjectTypeView : VerticalView("Project type") { | ||||
|     private var projectTypeState by mutableStateOf<Boolean>(false) | ||||
|     private val calculatedProjectType: ProjectType | ||||
|         get() = if (projectTypeState) JVMProjectType else MultiplatformProjectType | ||||
|     var projectType: ProjectType | ||||
|         get() = calculatedProjectType | ||||
|         set(value) { | ||||
|             projectTypeState = value == JVMProjectType | ||||
|         } | ||||
|  | ||||
|     override val content: @Composable ColumnScope.() -> Unit = { | ||||
|         Row(horizontalArrangement = Arrangement.spacedBy(5.dp)) { | ||||
|             Text("Multiplatform", Modifier.alignByBaseline()) | ||||
|             Switch( | ||||
|                 projectTypeState, | ||||
|                 { projectTypeState = it }, | ||||
|                 Modifier.padding(4.dp, 0.dp) | ||||
|             ) | ||||
|             Text("JVM", Modifier.alignByBaseline()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.desktop.views | ||||
|  | ||||
| import androidx.compose.runtime.* | ||||
| import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository | ||||
| import dev.inmo.kmppscriptbuilder.desktop.utils.CommonTextField | ||||
|  | ||||
| class RepositoryState( | ||||
|     name: String = "", | ||||
|     url: String = "" | ||||
| ) { | ||||
|     var name: String by mutableStateOf(name) | ||||
|     var url: String by mutableStateOf(url) | ||||
|  | ||||
|     fun toRepository() = MavenPublishingRepository(name, url) | ||||
| } | ||||
|  | ||||
| private fun MavenPublishingRepository.toRepositoryState() = RepositoryState(name, url) | ||||
|  | ||||
| class RepositoriesView : ListView<RepositoryState>("Repositories info") { | ||||
|     var repositories: List<MavenPublishingRepository> | ||||
|         get() = itemsList.map { it.toRepository() } | ||||
|         set(value) { | ||||
|             itemsList.clear() | ||||
|             itemsList.addAll( | ||||
|                 value.map { it.toRepositoryState() } | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|     override val addItemText: String = "Add repository" | ||||
|     override val removeItemText: String = "Remove repository" | ||||
|  | ||||
|     override fun createItem(): RepositoryState = RepositoryState() | ||||
|     @Composable | ||||
|     override fun buildView(item: RepositoryState) { | ||||
|         CommonTextField( | ||||
|             item.name, | ||||
|             "Repository name" | ||||
|         ) { item.name = it } | ||||
|         CommonTextField( | ||||
|             item.url, | ||||
|             "Repository url" | ||||
|         ) { item.url = it } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -16,7 +16,6 @@ allprojects { | ||||
|         mppProjectWithSerializationPresetPath = "${rootProject.projectDir.absolutePath}/mppProjectWithSerialization.gradle" | ||||
|         mppJavaProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJavaProject.gradle" | ||||
|         mppJsProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppJsProject.gradle" | ||||
|         mppAndroidProjectPresetPath = "${rootProject.projectDir.absolutePath}/mppAndroidProject.gradle" | ||||
|  | ||||
|         defaultAndroidSettingsPresetPath = "${rootProject.projectDir.absolutePath}/defaultAndroidSettings.gradle" | ||||
|  | ||||
|   | ||||
| @@ -3,30 +3,18 @@ org.gradle.parallel=true | ||||
| kotlin.js.generate.externals=true | ||||
| kotlin.incremental=true | ||||
| kotlin.incremental.js=true | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
|  | ||||
| kotlin_version=1.6.10 | ||||
| kotlin_coroutines_version=1.6.0 | ||||
| kotlin_serialisation_core_version=1.3.2 | ||||
| ktor_version=1.6.7 | ||||
| micro_utils_version=0.9.0 | ||||
| kotlin_version=1.7.20 | ||||
| kotlin_coroutines_version=1.6.4 | ||||
| kotlin_serialisation_core_version=1.4.1 | ||||
| ktor_version=2.1.3 | ||||
| micro_utils_version=0.14.2 | ||||
|  | ||||
| compose_version=1.0.1 | ||||
|  | ||||
| # ANDROID | ||||
|  | ||||
| android_minSdkVersion=21 | ||||
| android_compileSdkVersion=32 | ||||
| android_buildToolsVersion=32.0.0 | ||||
| dexcount_version=3.0.1 | ||||
| junit_version=4.12 | ||||
| test_ext_junit_version=1.1.2 | ||||
| espresso_core=3.3.0 | ||||
| compose_version=1.2.1 | ||||
|  | ||||
| # Dokka | ||||
|  | ||||
| dokka_version=1.6.0 | ||||
| dokka_version=1.7.20 | ||||
|  | ||||
| # Project data | ||||
|  | ||||
|   | ||||
							
								
								
									
										45
									
								
								gradle/libs.versions.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,45 @@ | ||||
| [versions] | ||||
|  | ||||
| kt = "1.7.20" | ||||
| kt-serialization = "1.4.1" | ||||
| kt-coroutines = "1.6.4" | ||||
|  | ||||
| jb-compose = "1.2.1" | ||||
| jb-dokka = "1.7.20" | ||||
| microutils = "0.14.2" | ||||
| kjsuikit = "0.4.1" | ||||
|  | ||||
| ktor = "2.1.3" | ||||
|  | ||||
| gh-release = "2.4.1" | ||||
|  | ||||
| [libraries] | ||||
|  | ||||
| kt-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kt" } | ||||
|  | ||||
| kt-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kt-serialization" } | ||||
|  | ||||
| kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" } | ||||
|  | ||||
| ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } | ||||
| ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } | ||||
| ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } | ||||
|  | ||||
| microutils-common = { module = "dev.inmo:micro_utils.common", version.ref = "microutils" } | ||||
| microutils-coroutines = { module = "dev.inmo:micro_utils.coroutines", version.ref = "microutils" } | ||||
|  | ||||
| jsuikit = { module = "dev.inmo:kjsuikit", version.ref = "kjsuikit" } | ||||
|  | ||||
|  | ||||
| kt-test-js = { module = "org.jetbrains.kotlin:kotlin-test-js", version.ref = "kt" } | ||||
| kt-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kt" } | ||||
|  | ||||
|  | ||||
| buildscript-kt-gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kt" } | ||||
| buildscript-kt-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kt" } | ||||
| buildscript-jb-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "jb-dokka" } | ||||
| buildscript-gh-release = { module = "com.github.breadmoirai:github-release", version.ref = "gh-release" } | ||||
|  | ||||
| [plugins] | ||||
|  | ||||
| jb-compose = { id = "org.jetbrains.compose", version.ref = "jb-compose" } | ||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip | ||||
|   | ||||
| @@ -4,9 +4,7 @@ project.group = "$group" | ||||
| // apply from: "$publishGradlePath" | ||||
|  | ||||
| kotlin { | ||||
|     jvm { | ||||
|         compilations.main.kotlinOptions.useIR = true | ||||
|     } | ||||
|     jvm() | ||||
|  | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
|   | ||||
| @@ -5,11 +5,9 @@ project.group = "$group" | ||||
|  | ||||
| kotlin { | ||||
|     js (IR) { | ||||
|         browser { | ||||
|         browser() | ||||
|         binaries.executable() | ||||
|     } | ||||
|         nodejs() | ||||
|     } | ||||
|  | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
|   | ||||
| @@ -4,16 +4,11 @@ project.group = "$group" | ||||
| // apply from: "$publishGradlePath" | ||||
|  | ||||
| kotlin { | ||||
|     jvm { | ||||
|         compilations.main.kotlinOptions.useIR = true | ||||
|     } | ||||
|     jvm() | ||||
|     js (IR) { | ||||
|         browser() | ||||
|         nodejs() | ||||
|     } | ||||
|     android { | ||||
|         publishAllLibraryVariants() | ||||
|     } | ||||
|  | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
| @@ -39,14 +34,5 @@ kotlin { | ||||
|                 implementation kotlin('test-junit') | ||||
|             } | ||||
|         } | ||||
|         androidTest { | ||||
|             dependencies { | ||||
|                 implementation kotlin('test-junit') | ||||
|                 implementation "androidx.test.ext:junit:$test_ext_junit_version" | ||||
|                 implementation "androidx.test.espresso:espresso-core:$espresso_core" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| apply from: "$defaultAndroidSettingsPresetPath" | ||||
|   | ||||
| @@ -9,8 +9,8 @@ rootProject.name = 'kmppscriptbuilder' | ||||
|  | ||||
| String[] includes = [ | ||||
|         ":core", | ||||
|         ":desktop", | ||||
|         ":web" | ||||
| //        ":desktop", | ||||
| //        ":web" | ||||
| ] | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,17 +0,0 @@ | ||||
| plugins { | ||||
|     id "org.jetbrains.kotlin.multiplatform" | ||||
|     id "org.jetbrains.kotlin.plugin.serialization" | ||||
| } | ||||
|  | ||||
| apply from: "$mppJsProjectPresetPath" | ||||
|  | ||||
| kotlin { | ||||
|     sourceSets { | ||||
|         commonMain { | ||||
|             dependencies { | ||||
|                 implementation project(":kmppscriptbuilder.core") | ||||
|                 implementation "dev.inmo:micro_utils.common:$micro_utils_version" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,82 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Config | ||||
| import dev.inmo.kmppscriptbuilder.core.utils.serialFormat | ||||
| import dev.inmo.kmppscriptbuilder.web.views.* | ||||
| import kotlinx.browser.document | ||||
| import kotlinx.dom.appendElement | ||||
| import org.w3c.dom.* | ||||
| import org.w3c.dom.url.URL | ||||
| import org.w3c.files.* | ||||
|  | ||||
| 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() | ||||
| } | ||||
|  | ||||
| fun main() { | ||||
|     document.addEventListener( | ||||
|         "DOMContentLoaded", | ||||
|         { | ||||
|             val builderView = BuilderView() | ||||
|  | ||||
|             (document.getElementById("openConfig") as HTMLElement).onclick = { | ||||
|                 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 | ||||
|                                     builderView.config = serialFormat.decodeFromString(Config.serializer(), content) | ||||
|                                     false | ||||
|                                 } | ||||
|  | ||||
|                                 reader.readAsText(file) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 targetInput.click() | ||||
|                 targetInput.remove() | ||||
|                 false | ||||
|             } | ||||
|             (document.getElementById("saveConfig") as HTMLElement).onclick = { | ||||
|                 val filename = "publish.kpsb" | ||||
|                 val content = serialFormat.encodeToString(Config.serializer(), builderView.config) | ||||
|  | ||||
|                 saveFile(content, filename) | ||||
|  | ||||
|                 false | ||||
|             } | ||||
|             (document.getElementById("exportScript") as HTMLElement).onclick = { | ||||
|                 val filename = "publish.gradle" | ||||
|  | ||||
|                 val content = builderView.config.run { | ||||
|                     type.buildMavenGradleConfig( | ||||
|                         mavenConfig, | ||||
|                         licenses | ||||
|                     ) | ||||
|                 } | ||||
|  | ||||
|                 saveFile(content, filename) | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.utils | ||||
|  | ||||
| import org.w3c.dom.HTMLElement | ||||
|  | ||||
| var HTMLElement.ukActive: Boolean | ||||
|     get() = classList.contains("uk-active") | ||||
|     set(value) { | ||||
|         if (value) { | ||||
|             classList.add("uk-active") | ||||
|         } else { | ||||
|             classList.remove("uk-active") | ||||
|         } | ||||
|     } | ||||
| @@ -1,10 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.utils | ||||
|  | ||||
| import kotlinx.browser.document | ||||
|  | ||||
| inline fun <R> keepScrolling(crossinline block: () -> R): R = document.body ?.let { | ||||
|     val (x, y) = (it.scrollLeft to it.scrollTop) | ||||
|     return block().also { _ -> | ||||
|         it.scrollTo(x, y) | ||||
|     } | ||||
| } ?: block() | ||||
| @@ -1,23 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Config | ||||
| import kotlinx.browser.document | ||||
| import org.w3c.dom.HTMLElement | ||||
|  | ||||
| class BuilderView : View { | ||||
|     private val projectTypeView = ProjectTypeView() | ||||
|     private val licensesView = LicensesView(document.getElementById("licensesListDiv") as HTMLElement) | ||||
|     private val mavenInfoTypeView = MavenProjectInfoView() | ||||
|  | ||||
|     var config: Config | ||||
|         get() = Config( | ||||
|             licensesView.licenses, | ||||
|             mavenInfoTypeView.mavenConfig, | ||||
|             projectTypeView.projectType | ||||
|         ) | ||||
|         set(value) { | ||||
|             licensesView.licenses = value.licenses | ||||
|             mavenInfoTypeView.mavenConfig = value.mavenConfig | ||||
|             projectTypeView.projectType = value.type | ||||
|         } | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.Developer | ||||
| import org.w3c.dom.* | ||||
|  | ||||
| class DevelopersView(rootElement: HTMLElement) : MutableListView<Developer>(rootElement, "Add developer", "Remove developer") { | ||||
|     private val HTMLElement.usernameElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[0] as HTMLInputElement | ||||
|     private val HTMLElement.nameElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[1] as HTMLInputElement | ||||
|     private val HTMLElement.emailElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[2] as HTMLInputElement | ||||
|  | ||||
|     var developers: List<Developer> | ||||
|         get() = elements.map { | ||||
|             Developer(it.usernameElement.value, it.nameElement.value, it.emailElement.value) | ||||
|         } | ||||
|         set(value) { | ||||
|             data = value | ||||
|         } | ||||
|  | ||||
|     override fun createPlainObject(): Developer = Developer("", "", "") | ||||
|  | ||||
|     override fun HTMLElement.addContentBeforeRemoveButton(value: Developer) { | ||||
|         createTextField("Developer ID", "Developer username").value = value.id | ||||
|         createTextField("Developer name", "").value = value.name | ||||
|         createTextField("Developer E-Mail", "").value = value.eMail | ||||
|     } | ||||
|  | ||||
|     override fun HTMLElement.updateElement(from: Developer, to: Developer) { | ||||
|         usernameElement.value = to.id | ||||
|         nameElement.value = to.name | ||||
|         emailElement.value = to.eMail | ||||
|     } | ||||
| } | ||||
| @@ -1,113 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.License | ||||
| import dev.inmo.kmppscriptbuilder.core.models.getLicenses | ||||
| import dev.inmo.micro_utils.coroutines.safeActor | ||||
| import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions | ||||
| import io.ktor.client.HttpClient | ||||
| import kotlinx.coroutines.* | ||||
| import kotlinx.coroutines.channels.Channel | ||||
| import kotlinx.coroutines.channels.SendChannel | ||||
| import kotlinx.coroutines.flow.consumeAsFlow | ||||
| import kotlinx.coroutines.flow.debounce | ||||
| import kotlinx.dom.appendElement | ||||
| import org.w3c.dom.* | ||||
|  | ||||
| class LicensesView( | ||||
|     rootElement: HTMLElement, | ||||
|     client: HttpClient = HttpClient(), | ||||
|     scope: CoroutineScope = CoroutineScope(Dispatchers.Default) | ||||
| ) : MutableListView<License>(rootElement, "Add empty license", "Remove license") { | ||||
|     private val HTMLElement.idElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[0] as HTMLInputElement | ||||
|     private val HTMLElement.titleElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[1] as HTMLInputElement | ||||
|     private val HTMLElement.urlElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[2] as HTMLInputElement | ||||
|  | ||||
|     private class LicenseOfferList( | ||||
|         rootElement: HTMLElement, | ||||
|         private val licensesView: LicensesView, | ||||
|         client: HttpClient, | ||||
|         scope: CoroutineScope | ||||
|     ) : ListView<License>(rootElement, useSimpleDiffStrategy = true) { | ||||
|         private var licensesTemplates: List<License> = emptyList() | ||||
|  | ||||
|         init { | ||||
|             scope.launch { | ||||
|                 licensesTemplates = client.getLicenses().values.toList() | ||||
|                 changeActor.send(Unit) // update list of searches | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private val changeActor: SendChannel<Unit> = scope.run { | ||||
|             val onChangeActor = Channel<Unit>(Channel.CONFLATED) | ||||
|             onChangeActor.consumeAsFlow().subscribeSafelyWithoutExceptions(scope) { | ||||
|                 val lowercased = searchString | ||||
|                 data = if (lowercased.isEmpty()) { | ||||
|                     emptyList() | ||||
|                 } else { | ||||
|                     licensesTemplates.filter { | ||||
|                         val lowercasedTitle = it.title.lowercase() | ||||
|                         lowercased.all { it in lowercasedTitle } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             onChangeActor | ||||
|         } | ||||
|         private val searchElement = rootElement.createTextField("Quick add", "Type some license name part to find it").apply { | ||||
|             oninput = { | ||||
|                 changeActor.trySend(Unit) | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|         private var searchString: String | ||||
|             get() = searchElement.value.lowercase() | ||||
|             set(value) { | ||||
|                 searchElement.value = value | ||||
|             } | ||||
|  | ||||
|         override fun HTMLElement.placeElement(value: License) { | ||||
|             createCommonButton(value.title).onclick = { | ||||
|                 searchString = "" | ||||
|                 licensesView.licenses += value | ||||
|                 changeActor.trySend(Unit) | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         override fun HTMLElement.updateElement(from: License, to: License) { | ||||
|             getElementsByTagName("button")[0] ?.remove() | ||||
|             placeElement(to) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private val licensesOffersList = LicenseOfferList( | ||||
|         rootElement.appendElement("div") { classList.add("uk-padding-small") } as HTMLElement, | ||||
|         this, | ||||
|         client, | ||||
|         scope | ||||
|     ) | ||||
|  | ||||
|     var licenses: List<License> | ||||
|         get() = elements.map { | ||||
|             License(it.idElement.value, it.titleElement.value, it.urlElement.value) | ||||
|         } | ||||
|         set(value) { | ||||
|             data = value | ||||
|         } | ||||
|  | ||||
|     override fun createPlainObject(): License = License("", "", "") | ||||
|  | ||||
|     override fun HTMLElement.addContentBeforeRemoveButton(value: License) { | ||||
|         createTextField("License Id", "Short name like \"Apache-2.0\"").value = value.id | ||||
|         createTextField("License Title", "Official title of license (like \"Apache Software License 2.0\")").value = value.title | ||||
|         createTextField("License URL", "Link to your LICENSE file OR official license file (like \"https://opensource.org/licenses/Apache-2.0\")").value = value.url ?: "" | ||||
|     } | ||||
|  | ||||
|     override fun HTMLElement.updateElement(from: License, to: License) { | ||||
|         idElement.value = to.id | ||||
|         titleElement.value = to.title | ||||
|         urlElement.value = to.url ?: "" | ||||
|     } | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.micro_utils.common.calculateStrictDiff | ||||
| import kotlinx.dom.appendElement | ||||
| import org.w3c.dom.HTMLElement | ||||
|  | ||||
| abstract class ListView<T>( | ||||
|     protected val rootElement: HTMLElement, | ||||
|     useSimpleDiffStrategy: Boolean = false | ||||
| ) : View { | ||||
|     protected val elements = mutableListOf<HTMLElement>() | ||||
|     private val diffHandling: (old: List<T>, new: List<T>) -> Unit = if (useSimpleDiffStrategy) { | ||||
|         { _, new -> | ||||
|             elements.forEach { it.remove() } | ||||
|             elements.clear() | ||||
|             new.forEach { | ||||
|                 val element = instantiateElement() | ||||
|                 elements.add(element) | ||||
|                 element.placeElement(it) | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         {  old, new -> | ||||
|             val diff = old.calculateStrictDiff(new) | ||||
|             diff.removed.forEach { | ||||
|                 elements[it.index].remove() | ||||
|                 elements.removeAt(it.index) | ||||
|                 println(it.value) | ||||
|             } | ||||
|             diff.added.forEach { | ||||
|                 val element = instantiateElement() | ||||
|                 elements.add(element) | ||||
|                 element.placeElement(it.value) | ||||
|             } | ||||
|             diff.replaced.forEach { (old, new) -> | ||||
|                 val element = elements.getOrNull(old.index) ?.also { it.updateElement(old.value, new.value) } | ||||
|                 if (element == null) { | ||||
|                     val newElement = instantiateElement() | ||||
|                     newElement.placeElement(new.value) | ||||
|                     elements[new.index] = newElement | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     protected var data: List<T> = emptyList() | ||||
|         set(value) { | ||||
|             val old = field | ||||
|             field = value | ||||
|             diffHandling(old, value) | ||||
|         } | ||||
|  | ||||
|     protected abstract fun HTMLElement.placeElement(value: T) | ||||
|     protected abstract fun HTMLElement.updateElement(from: T, to: T) | ||||
|  | ||||
|     private fun instantiateElement() = rootElement.appendElement("div") { | ||||
|         classList.add("uk-padding-small") | ||||
|     } as HTMLElement | ||||
| } | ||||
| @@ -1,82 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.* | ||||
| import dev.inmo.kmppscriptbuilder.web.utils.ukActive | ||||
| import kotlinx.browser.document | ||||
| import org.w3c.dom.HTMLElement | ||||
| import org.w3c.dom.HTMLInputElement | ||||
|  | ||||
| class MavenProjectInfoView : View { | ||||
|     private val nameElement = document.getElementById("projectNameInput") as HTMLInputElement | ||||
|     private val descriptionElement = document.getElementById("projectDescriptionInput") as HTMLInputElement | ||||
|     private val urlElement = document.getElementById("projectUrlInput") as HTMLInputElement | ||||
|     private val vcsUrlElement = document.getElementById("projectVCSUrlInput") as HTMLInputElement | ||||
|     private val disableGpgSigningElement = document.getElementById("disableGpgSigning") as HTMLElement | ||||
|     private val optionalGpgSigningElement = document.getElementById("optionalGpgSigning") as HTMLElement | ||||
|     private val enableGpgSigningElement = document.getElementById("enableGpgSigning") as HTMLElement | ||||
|     private val includeMavenCentralElement = document.getElementById("includeMavenCentralTargetRepoToggle") as HTMLInputElement | ||||
|     private val developersView = DevelopersView(document.getElementById("developersListDiv") as HTMLElement) | ||||
|     private val repositoriesView = RepositoriesView(document.getElementById("repositoriesListDiv") as HTMLElement) | ||||
|  | ||||
|     private var gpgSignMode: GpgSigning = GpgSigning.Disabled | ||||
|         set(value) { | ||||
|             field = value | ||||
|             when (value) { | ||||
|                 GpgSigning.Enabled -> { | ||||
|                     enableGpgSigningElement.ukActive = true | ||||
|                     disableGpgSigningElement.ukActive = false | ||||
|                     optionalGpgSigningElement.ukActive = false | ||||
|                 } | ||||
|                 GpgSigning.Optional -> { | ||||
|                     enableGpgSigningElement.ukActive = false | ||||
|                     disableGpgSigningElement.ukActive = false | ||||
|                     optionalGpgSigningElement.ukActive = true | ||||
|                 } | ||||
|                 GpgSigning.Disabled -> { | ||||
|                     enableGpgSigningElement.ukActive = false | ||||
|                     disableGpgSigningElement.ukActive = true | ||||
|                     optionalGpgSigningElement.ukActive = false | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     var mavenConfig: MavenConfig | ||||
|         get() = MavenConfig( | ||||
|             nameElement.value.ifBlank { defaultProjectName }, | ||||
|             descriptionElement.value.ifBlank { defaultProjectDescription }, | ||||
|             urlElement.value, | ||||
|             vcsUrlElement.value, | ||||
|             developersView.developers, | ||||
|             repositoriesView.repositories + if (includeMavenCentralElement.checked) { | ||||
|                 listOf(SonatypeRepository) | ||||
|             } else { | ||||
|                 emptyList() | ||||
|             }, | ||||
|             when { | ||||
|                 optionalGpgSigningElement.ukActive -> GpgSigning.Optional | ||||
|                 enableGpgSigningElement.ukActive -> GpgSigning.Enabled | ||||
|                 else -> GpgSigning.Disabled | ||||
|             } | ||||
|         ) | ||||
|         set(value) { | ||||
|             nameElement.value = value.name | ||||
|             descriptionElement.value = value.description | ||||
|             urlElement.value = value.url | ||||
|             vcsUrlElement.value = value.vcsUrl | ||||
|             gpgSignMode = if (value.includeGpgSigning) { | ||||
|                 GpgSigning.Enabled | ||||
|             } else { | ||||
|                 value.gpgSigning | ||||
|             } | ||||
|             developersView.developers = value.developers | ||||
|             val reposWithoutSonatype = value.repositories.filter { it != SonatypeRepository } | ||||
|             includeMavenCentralElement.checked = value.repositories.size != reposWithoutSonatype.size | ||||
|             repositoriesView.repositories = reposWithoutSonatype | ||||
|         } | ||||
|  | ||||
|     init { | ||||
|         enableGpgSigningElement.onclick = { gpgSignMode = GpgSigning.Enabled; Unit } | ||||
|         disableGpgSigningElement.onclick = { gpgSignMode = GpgSigning.Disabled; Unit } | ||||
|         optionalGpgSigningElement.onclick = { gpgSignMode = GpgSigning.Optional; Unit } | ||||
|     } | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.web.utils.keepScrolling | ||||
| import org.w3c.dom.HTMLElement | ||||
|  | ||||
| abstract class MutableListView<T>( | ||||
|     rootElement: HTMLElement, | ||||
|     addButtonText: String = "Add", | ||||
|     private val removeButtonText: String = "Remove" | ||||
| ) : ListView<T>(rootElement) { | ||||
|     init { | ||||
|         rootElement.createPrimaryButton(addButtonText).apply { | ||||
|             onclick = { | ||||
|                 keepScrolling { | ||||
|                     val newObject = createPlainObject() | ||||
|                     data += newObject | ||||
|                 } | ||||
|                 false | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected abstract fun createPlainObject(): T | ||||
|     protected open fun HTMLElement.addContentBeforeRemoveButton(value: T) {} | ||||
|     protected open fun HTMLElement.addContentAfterRemoveButton(value: T) {} | ||||
|     final override fun HTMLElement.placeElement(value: T) { | ||||
|         addContentBeforeRemoveButton(value) | ||||
|         addRemoveButton() | ||||
|         addContentAfterRemoveButton(value) | ||||
|     } | ||||
|  | ||||
|     private fun HTMLElement.addRemoveButton() { | ||||
|         val button = createPrimaryButton(removeButtonText) | ||||
|         button.onclick = { | ||||
|             elements.indexOf(button.parentElement).takeIf { it > -1 } ?.also { | ||||
|                 data -= data[it] | ||||
|             } ?: rootElement.removeChild(this@addRemoveButton) | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.* | ||||
| import dev.inmo.kmppscriptbuilder.web.utils.ukActive | ||||
| import kotlinx.browser.document | ||||
| import org.w3c.dom.HTMLElement | ||||
|  | ||||
| class ProjectTypeView : View { | ||||
|     private val mppProjectTypeElement = document.getElementById("mppProjectType") as HTMLElement | ||||
|     private val jvmProjectTypeElement = document.getElementById("jvmProjectType") as HTMLElement | ||||
|     private val jsProjectTypeElement = document.getElementById("jsProjectType") as HTMLElement | ||||
|  | ||||
|     var projectType: ProjectType | ||||
|         get() = when { | ||||
|             jvmProjectTypeElement.ukActive -> JVMProjectType | ||||
|             jsProjectTypeElement.ukActive -> JSProjectType | ||||
|             else -> MultiplatformProjectType | ||||
|         } | ||||
|         set(value) { | ||||
|             mppProjectTypeElement.ukActive = value == MultiplatformProjectType | ||||
|             jvmProjectTypeElement.ukActive = value == JVMProjectType | ||||
|             jsProjectTypeElement.ukActive = value == JSProjectType | ||||
|         } | ||||
|  | ||||
|     init { | ||||
|         mppProjectTypeElement.onclick = { | ||||
|             projectType = MultiplatformProjectType | ||||
|             Unit | ||||
|         } | ||||
|         jvmProjectTypeElement.onclick = { | ||||
|             projectType = JVMProjectType | ||||
|             Unit | ||||
|         } | ||||
|         jsProjectTypeElement.onclick = { | ||||
|             projectType = JSProjectType | ||||
|             Unit | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import dev.inmo.kmppscriptbuilder.core.models.MavenPublishingRepository | ||||
| import org.w3c.dom.* | ||||
|  | ||||
| class RepositoriesView(rootElement: HTMLElement) : MutableListView<MavenPublishingRepository>(rootElement, "Add repository", "Remove repository") { | ||||
|     private val HTMLElement.nameElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[0] as HTMLInputElement | ||||
|     private val HTMLElement.urlElement: HTMLInputElement | ||||
|         get() = getElementsByTagName("input")[1] as HTMLInputElement | ||||
|  | ||||
|     var repositories: List<MavenPublishingRepository> | ||||
|         get() = elements.map { | ||||
|             MavenPublishingRepository(it.nameElement.value, it.urlElement.value) | ||||
|         } | ||||
|         set(value) { | ||||
|             data = value | ||||
|         } | ||||
|  | ||||
|     override fun createPlainObject(): MavenPublishingRepository = MavenPublishingRepository("", "") | ||||
|  | ||||
|     override fun HTMLElement.addContentBeforeRemoveButton(value: MavenPublishingRepository) { | ||||
|         createTextField("Repository name", "This name will be used to identify repository in grade").value = value.name | ||||
|         createTextField("Repository URL", "For example: https://repo.maven.apache.org/maven2/").value = value.url | ||||
|     } | ||||
|  | ||||
|     override fun HTMLElement.updateElement(from: MavenPublishingRepository, to: MavenPublishingRepository) { | ||||
|         nameElement.value = to.name | ||||
|         urlElement.value = to.url | ||||
|     } | ||||
| } | ||||
| @@ -1,3 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| interface View | ||||
| @@ -1,35 +0,0 @@ | ||||
| package dev.inmo.kmppscriptbuilder.web.views | ||||
|  | ||||
| import kotlinx.dom.appendElement | ||||
| import org.w3c.dom.* | ||||
|  | ||||
| fun HTMLElement.createTextField( | ||||
|     label: String, | ||||
|     placeholder: String | ||||
| ): HTMLInputElement { | ||||
|     return appendElement("div") { | ||||
|         classList.add("uk-margin", "uk-width-1-1") | ||||
|     }.appendElement("label") { | ||||
|         classList.add("uk-form-label") | ||||
|         innerHTML = label | ||||
|     }.run { | ||||
|         val input = appendElement("input") { | ||||
|             classList.add("uk-input", "uk-width-expand") | ||||
|             setAttribute("type", "text") | ||||
|             setAttribute("placeholder", placeholder) | ||||
|         } as HTMLInputElement | ||||
|         input | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun HTMLElement.createPrimaryButton(text: String): HTMLButtonElement = (appendElement("button") { | ||||
|     classList.add("uk-button", "uk-button-primary") | ||||
| } as HTMLButtonElement).apply { | ||||
|     innerText = text | ||||
| } | ||||
|  | ||||
| fun HTMLElement.createCommonButton(text: String): HTMLButtonElement = (appendElement("button") { | ||||
|     classList.add("uk-button", "uk-button-default") | ||||
| } as HTMLButtonElement).apply { | ||||
|     innerText = text | ||||
| } | ||||
| @@ -1,85 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>Kotlin Publication Scripts Builder</title> | ||||
|     <!-- UIkit CSS --> | ||||
|     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.6.17/dist/css/uikit.min.css" /> | ||||
| </head> | ||||
| <body> | ||||
|     <nav class="uk-navbar-container" uk-navbar> | ||||
|         <div class="uk-navbar-left"> | ||||
|             <div class="uk-padding-small uk-text-lead">Kotlin Publication Scripts Builder</div> | ||||
|             <div class="uk-padding-small"><a href="https://github.com/InsanusMokrassar/KotlinPublicationScriptsBuilder"><img src="https://img.shields.io/github/stars/InsanusMokrassar/KotlinPublicationScriptsBuilder?label=Github&style=plastic"/></a></div> | ||||
|         </div> | ||||
|         <div class="uk-navbar-right"> | ||||
|             <ul class="uk-navbar-nav"> | ||||
|                 <li uk-tooltip="title: Open config" id="openConfig"><a href="#"><span uk-icon="icon: pull"></span></a></li><!--Open file--> | ||||
|                 <li uk-tooltip="title: Save config" id="saveConfig"><a href="#"><span uk-icon="icon: push"></span></a></li><!--Save file--> | ||||
|                 <li uk-tooltip="title: Export script" id="exportScript"><a href="#"><span uk-icon="icon: upload"></span></a></li><!--Save file--> | ||||
|             </ul> | ||||
|         </div> | ||||
|     </nav> | ||||
|     <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="https://cdn.jsdelivr.net/npm/uikit@3.6.17/dist/js/uikit.min.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/uikit@3.6.17/dist/js/uikit-icons.min.js"></script> | ||||
|     <!-- Internal JS --> | ||||
|     <script src="kmppscriptbuilder.web.js"></script> | ||||
| </body> | ||||
| </html> | ||||