diff --git a/.gitignore b/.gitignore index 8a04b36..032b956 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ out/ secret.gradle local.properties +kotlin-js-store/ + publishing.sh diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..88a977e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +## 0.0.1 diff --git a/README.md b/README.md index f3bb736..dc5b6f2 100644 --- a/README.md +++ b/README.md @@ -1,224 +1,41 @@ -# How to use +# JSUIKit Kotlin -That is a template for Kotlin Multiplatform Projects. How to use it: +Hello :) This library is a wrapper for JavaScript/CSS [UIKit](https://getuikit.com) framework. It uses the same +structure as in [UIKit Docs](https://getuikit.com/docs/introduction) and in most cases you may use it. -* Create your repository from this template -* Add `local.properties` file in case you plan to use `Android` target (you must set up location of SDK, it will not be tracked by `git` and it is correct behaviour). In the snippet below you may see approximate content of `local.properties` file: -```properties -## This file must *NOT* be checked into Version Control Systems, -# as it contains information specific to your local configuration. -# -# Location of the SDK. This is only used by Gradle. -# For customization when using a Version Control System, please read the -# header note. -sdk.dir=/your/path/to/android/sdk -``` -* Replace in a whole project `project_group` by your group -* Replace in a whole project `project_name` by your **ROOT** project name -* Update your subproject packages. It is not critical, but recommended especially in case you plan to publish your - library +The main target of this wrapper is a [JetBrains Compose JS](https://github.com/JetBrains/compose-jb) and will be useful +for you in case you are using it too. -## Subprojects +## How to include -In this template there is only one subproject with name `lib`. You are always able to rename it, but remember that in -this case you must rename it in `settings.gradle` file. - -## JVM sources in Android target - -By default JVM code is not included in Android target. In case you wish to include JVM sources in Android build, use -next method in the end of your `build.gradle`: +Last version: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/dev.inmo/jsuikitkotlin/badge.svg)](https://maven-badges.herokuapp.com/maven-central/dev.inmo/jsuikitkotlin) ```groovy -enableIncludingJvmCodeInAndroidPart() +implementation "dev.inmo:jsuikitkotlin:$jsuikitkotlin_version" ``` -In case when you need to be sure that JVM sources are not included in Android part, use this snippet: +**THIS LIBRARY DO NOT ADD ANY JS OR CSS**. So, you must download and include UIKit js/css by yourself. See +[UIKit installation instructions](https://getuikit.com/docs/installation) -```groovy -disableIncludingJvmCodeInAndroidPart() -``` +## How to use -## Types of projects +In this library there are two main entities: -### `mppProjectWithSerialization` +* Builder functions - buttons, spinners, icons, grids, etc. +* Modifiers - `UIKitAlign`, `UIKitAnimation`, etc. -This type of preset have `JVM`, `JS` and `Android` targets and available using -`apply from: "$mppProjectWithSerializationPresetPath"`. Template for project with this preset looks like next snippet: +For example, if you want to add table in your html, you will use next code: -```groovy -plugins { - id "org.jetbrains.kotlin.multiplatform" - id "org.jetbrains.kotlin.plugin.serialization" - id "com.android.library" -} - -apply from: "$mppProjectWithSerializationPresetPath" - -// The code below is optional - -kotlin { - sourceSets { - commonMain { - dependencies { - // common dependencies - } - } - commonTest { - dependencies { - // common test dependencies - } - } - jvmMain { - dependencies { - // jvm dependencies - } - } - jvmTest { - dependencies { - // jvm test dependencies - } - } - jsMain { - dependencies { - // js dependencies - } - } - jsTest { - dependencies { - // js test dependencies - } - } - androidMain { - dependencies { - // android dependencies - } - } - androidTest { - dependencies { - // android test dependencies - } - } - } -} -``` - -### `mppJavaProject` - -This type of preset have only `JVM` target and available using `apply from: "$mppJavaProjectPresetPath"`. Template for -project with this preset looks like next snippet: - -```groovy -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -apply from: "$mppJavaProjectPresetPath" - -// The code below is optional - -kotlin { - sourceSets { - commonMain { - dependencies { - // common dependencies - } - } - commonTest { - dependencies { - // common test dependencies - } - } - jvmMain { - dependencies { - // jvm dependencies - } - } - jvmTest { - dependencies { - // jvm test dependencies - } - } - } -} -``` - -### `mppJsProject` - -This type of preset have only `JS` target and available using `apply from: "mppJsProjectPresetPath"`. Template for -project with this preset looks like next snippet: - -```groovy -plugins { - id "org.jetbrains.kotlin.multiplatform" -} - -apply from: "$mppJsProjectPresetPath" - -// The code below is optional - -kotlin { - sourceSets { - commonMain { - dependencies { - // common dependencies - } - } - commonTest { - dependencies { - // common test dependencies - } - } - jsMain { - dependencies { - // jvm dependencies - } - } - jsTest { - dependencies { - // jvm test dependencies - } - } - } -} -``` - -### `mppAndroidProject` - -This type of preset have only `Android` target and available using `apply from: "$mppAndroidProjectPresetPath"`. Template for -project with this preset looks like next snippet: - -```groovy -plugins { - id "org.jetbrains.kotlin.multiplatform" - id "com.android.library" -} - -apply from: "$mppAndroidProjectPresetPath" - -// The code below is optional - -kotlin { - sourceSets { - commonMain { - dependencies { - // common dependencies - } - } - commonTest { - dependencies { - // common test dependencies - } - } - androidMain { - dependencies { - // android dependencies - } - } - androidTest { - dependencies { - // android test dependencies - } - } +```kotlin +DefaultTable( + listOf("Heading 1", "Heading 2", "Heading 3"), + data, // SnapshotStateList + UIKitTable.Divider // modifier, add dividers +) { i, item -> // i - number of heading, item - item from data; composable callback + when (i) { + 0 -> Text(item.toString()) + 1 -> Text("data 2") + 2 -> Text("data 3") } } ``` diff --git a/build.gradle b/build.gradle index c334c73..abe3c3d 100644 --- a/build.gradle +++ b/build.gradle @@ -7,14 +7,20 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' 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" } } +plugins { + id 'org.jetbrains.kotlin.js' version "$kotlin_version" + id "org.jetbrains.compose" version "$jetbrains_compose_version" +} + +project.version = "$version" +project.group = "$group" + allprojects { repositories { mavenLocal() @@ -23,5 +29,23 @@ allprojects { } } -apply from: "./extensions.gradle" -// apply from: "./github_release.gradle" +kotlin { + js(IR) { + browser() + binaries.executable() + } +} + + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_core_version" + implementation(compose.web.core) + implementation(compose.runtime) + implementation "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" +} + +apply from: "./github_release.gradle" +apply from: "./publish.gradle" + diff --git a/changelogparser.sh b/changelogparser.sh new file mode 100755 index 0000000..5a1caf9 --- /dev/null +++ b/changelogparser.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +function parse() { + version="$1" + + while IFS= read -r line && [ -z "`echo "$line" | grep -e "^#\+ $version"`" ] + do + : # do nothing + done + + while IFS= read -r line && [ -z "`echo "$line" | grep -e "^#\+"`" ] + do + echo "$line" + done +} + +version="$1" +file="$2" + +if [ -n "$file" ]; then + parse "$version" < "$file" +else + parse "$version" +fi + diff --git a/defaultAndroidSettings.gradle b/defaultAndroidSettings.gradle deleted file mode 100644 index 1b1a4f0..0000000 --- a/defaultAndroidSettings.gradle +++ /dev/null @@ -1,66 +0,0 @@ -apply plugin: 'com.getkeepsafe.dexcount' - -android { - ext { - jvmKotlinFolderFile = { - String sep = File.separator - return new File("${project.projectDir}${sep}src${sep}jvmMain${sep}kotlin") - } - - enableIncludingJvmCodeInAndroidPart = { - File jvmKotlinFolder = jvmKotlinFolderFile() - if (jvmKotlinFolder.exists()) { - android.sourceSets.main.java.srcDirs += jvmKotlinFolder.path - } - } - - disableIncludingJvmCodeInAndroidPart = { - File jvmKotlinFolder = jvmKotlinFolderFile() - String[] oldDirs = android.sourceSets.main.java.srcDirs - android.sourceSets.main.java.srcDirs = [] - for (oldDir in oldDirs) { - if (oldDir != jvmKotlinFolder.path) { - android.sourceSets.main.java.srcDirs += oldDir - } - } - } - } - - 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 { - String sep = File.separator - main.java.srcDirs += "src${sep}main${sep}kotlin" - } -} diff --git a/extensions.gradle b/extensions.gradle deleted file mode 100644 index 2e89947..0000000 --- a/extensions.gradle +++ /dev/null @@ -1,25 +0,0 @@ -allprojects { - ext { - projectByName = { String name -> - for (subproject in rootProject.subprojects) { - if (subproject.name == name) { - return subproject - } - } - return null - } - - internalProject = { String name -> - projectByName(name) - } - - 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" - - // publishGradlePath = "${rootProject.projectDir.absolutePath}/publish.gradle" - } -} diff --git a/github_release.gradle b/github_release.gradle new file mode 100644 index 0000000..980b571 --- /dev/null +++ b/github_release.gradle @@ -0,0 +1,31 @@ +private String getCurrentVersionChangelog() { + OutputStream changelogDataOS = new ByteArrayOutputStream() + exec { + commandLine 'chmod', "+x", './changelog_parser.sh' + } + exec { + standardOutput = changelogDataOS + commandLine './changelog_parser.sh', "${project.version}", 'CHANGELOG.md' + } + + return changelogDataOS.toString().trim() +} + +if (new File(projectDir, "secret.gradle").exists()) { + apply from: './secret.gradle' + apply plugin: "com.github.breadmoirai.github-release" + + githubRelease { + token "${project.property('GITHUB_RELEASE_TOKEN')}" + + owner "InsanusMokrassar" + repo "JSUIKitBindings" + + tagName "${project.version}" + releaseName "${project.version}" + targetCommitish "${project.version}" + + body getCurrentVersionChangelog() + } +} + diff --git a/gradle.properties b/gradle.properties index d033bb1..8034368 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,27 +6,18 @@ kotlin.incremental.js=true android.useAndroidX=true android.enableJetifier=true -kotlin_version=1.6.0 +kotlin_version=1.5.31 kotlin_serialisation_core_version=1.3.1 +jetbrains_compose_version=1.0.0 -# github_release_plugin_version=2.2.12 - -# ANDROID - -android_minSdkVersion=21 -android_compileSdkVersion=31 -android_buildToolsVersion=31.0.0 -dexcount_version=3.0.0 -junit_version=4.12 -test_ext_junit_version=1.1.2 -espresso_core=3.3.0 +github_release_plugin_version=2.2.12 # Dokka -dokka_version=1.6.0 +dokka_version=1.5.30 # Project data -group=project_group +group=dev.inmo version=0.0.1 -android_code_version=1 + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..d2880ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/lib/build.gradle b/lib/build.gradle deleted file mode 100644 index 8a22926..0000000 --- a/lib/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id "org.jetbrains.kotlin.multiplatform" - id "org.jetbrains.kotlin.plugin.serialization" - id "com.android.library" -} - -apply from: "$mppProjectWithSerializationPresetPath" - diff --git a/lib/src/commonMain/kotlin/project_group/Library.kt b/lib/src/commonMain/kotlin/project_group/Library.kt deleted file mode 100644 index 424d112..0000000 --- a/lib/src/commonMain/kotlin/project_group/Library.kt +++ /dev/null @@ -1,7 +0,0 @@ -package project_group - -class Library { - fun someLibraryMethod(): Boolean { - return true - } -} diff --git a/lib/src/commonTest/kotlin/project_group/LibraryTest.kt b/lib/src/commonTest/kotlin/project_group/LibraryTest.kt deleted file mode 100644 index 84b573a..0000000 --- a/lib/src/commonTest/kotlin/project_group/LibraryTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * This Kotlin source file was generated by the Gradle 'init' task. - */ -package project_group - -import kotlin.test.Test -import kotlin.test.assertTrue - -class LibraryTest { - @Test fun testSomeLibraryMethod() { - val classUnderTest = Library() - assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'") - } -} diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml deleted file mode 100644 index 03c9554..0000000 --- a/lib/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/mppAndroidProject.gradle b/mppAndroidProject.gradle deleted file mode 100644 index af688ee..0000000 --- a/mppAndroidProject.gradle +++ /dev/null @@ -1,26 +0,0 @@ -project.version = "$version" -project.group = "$group" - -// apply from: "$publishGradlePath" - -kotlin { - android { - publishAllLibraryVariants() - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - } - } - } -} - -apply from: "$defaultAndroidSettingsPresetPath" diff --git a/mppJavaProject.gradle b/mppJavaProject.gradle deleted file mode 100644 index 231bdfc..0000000 --- a/mppJavaProject.gradle +++ /dev/null @@ -1,34 +0,0 @@ -project.version = "$version" -project.group = "$group" - -// apply from: "$publishGradlePath" - -kotlin { - jvm() - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - } - } - - jvmTest { - dependencies { - implementation kotlin('test-junit') - } - } - } -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(8) - } -} diff --git a/mppJsProject.gradle b/mppJsProject.gradle deleted file mode 100644 index ec05237..0000000 --- a/mppJsProject.gradle +++ /dev/null @@ -1,31 +0,0 @@ -project.version = "$version" -project.group = "$group" - -// apply from: "$publishGradlePath" - -kotlin { - js (IR) { - browser() - nodejs() - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - implementation kotlin('test-junit') - } - } - } -} diff --git a/mppProjectWithSerialization.gradle b/mppProjectWithSerialization.gradle deleted file mode 100644 index 12c521b..0000000 --- a/mppProjectWithSerialization.gradle +++ /dev/null @@ -1,56 +0,0 @@ -project.version = "$version" -project.group = "$group" - -// apply from: "$publishGradlePath" - -kotlin { - jvm() - js (IR) { - browser() - nodejs() - } - android { - publishAllLibraryVariants() - } - - sourceSets { - commonMain { - dependencies { - implementation kotlin('stdlib') - api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlin_serialisation_core_version" - } - } - commonTest { - dependencies { - implementation kotlin('test-common') - implementation kotlin('test-annotations-common') - } - } - jvmTest { - dependencies { - implementation kotlin('test-junit') - } - } - jsTest { - dependencies { - implementation kotlin('test-js') - 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" - } - } - } -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(8) - } -} - -apply from: "$defaultAndroidSettingsPresetPath" diff --git a/publish.gradle b/publish.gradle new file mode 100644 index 0000000..97c757c --- /dev/null +++ b/publish.gradle @@ -0,0 +1,75 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + + +task javadocJar(type: Jar) { + classifier = 'javadoc' +} +task sourcesJar(type: Jar) { + kotlin.sourceSets.all { + from(kotlin) + } + classifier = 'sources' +} + +publishing { + publications { + maven(MavenPublication) { + kotlin.js().components.forEach { + from(it) + } + + artifact javadocJar + artifact sourcesJar + + pom { + resolveStrategy = Closure.DELEGATE_FIRST + + description = "${project.name}" + name = "${project.name}" + url = "https://github.com/InsanusMokrassar/JSUIKitKBindings" + + scm { + developerConnection = "scm:git:[fetch=]https://github.com/InsanusMokrassar/JSUIKitKBindings.git[push=]https://github.com/InsanusMokrassar/JSUIKitKBindings.git" + url = "https://github.com/InsanusMokrassar/JSUIKitKBindings.git" + } + + developers { + + developer { + id = "InsanusMokrassar" + name = "Ovsiannikov Aleksei" + email = "ovsyannikov.alexey95@gmail.com" + } + + } + + licenses { + + license { + name = "Apache Software License 2.0" + url = "https://opensource.org/licenses/Apache-2.0" + } + + } + } + repositories { + if ((project.hasProperty('SONATYPE_USER') || System.getenv('SONATYPE_USER') != null) && (project.hasProperty('SONATYPE_PASSWORD') || System.getenv('SONATYPE_PASSWORD') != null)) { + maven { + name = "sonatype" + url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") + credentials { + username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER') + password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD') + } + } + } + } + } + } +} + +signing { + useGpgCmd() + sign publishing.publications +} diff --git a/publish.kpsb b/publish.kpsb new file mode 100644 index 0000000..f60c799 --- /dev/null +++ b/publish.kpsb @@ -0,0 +1 @@ +{"licenses":[{"id":"Apache-2.0","title":"Apache Software License 2.0","url":"https://opensource.org/licenses/Apache-2.0"}],"mavenConfig":{"name":"${project.name}","description":"${project.name}","url":"https://github.com/InsanusMokrassar/JSUIKitKBindings","vcsUrl":"https://github.com/InsanusMokrassar/JSUIKitKBindings.git","includeGpgSigning":true,"developers":[{"id":"InsanusMokrassar","name":"Ovsiannikov Aleksei","eMail":"ovsyannikov.alexey95@gmail.com"}],"repositories":[{"name":"sonatype","url":"https://oss.sonatype.org/service/local/staging/deploy/maven2/"}]},"type":"JVM"} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7bb91d0..71e1e97 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,16 +1,2 @@ -rootProject.name = 'project_name' +rootProject.name = 'jsuikitkotlin' -String[] includes = [ - ":lib" -] - - -includes.each { originalName -> - String projectDirectory = "${rootProject.projectDir.getAbsolutePath()}${originalName.replaceAll(":", File.separator)}" - String projectName = "${rootProject.name}${originalName.replaceAll(":", ".")}" - String projectIdentifier = ":${projectName}" - include projectIdentifier - ProjectDescriptor project = project(projectIdentifier) - project.name = projectName - project.projectDir = new File(projectDirectory) -} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Button.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Button.kt new file mode 100644 index 0000000..91d00ab --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Button.kt @@ -0,0 +1,78 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import androidx.compose.web.events.SyntheticMouseEvent +import dev.inmo.jsuikit.defaults.modifers.* +import org.jetbrains.compose.web.attributes.InputType +import org.jetbrains.compose.web.attributes.disabled +import org.jetbrains.compose.web.dom.* +import org.w3c.dom.HTMLButtonElement +import org.w3c.dom.events.Event + +@Composable +fun DefaultButton( + vararg modifiers: UIKitModifier, + disabled: Boolean = false, + buttonType: UIKitButton.Type = UIKitButton.Type.Default, + onClick: ((SyntheticMouseEvent) -> Unit)? = null, + contentAllocator: ContentBuilder +) { + Button( + { + onClick ?.let { onClick(it) } + classes("uk-button") + include(*modifiers, buttonType) + if (disabled) { + disabled() + } + } + ) { + contentAllocator() + } +} + +@Composable +fun DefaultButton( + text: String, + vararg modifiers: UIKitModifier, + disabled: Boolean = false, + buttonType: UIKitButton.Type = UIKitButton.Type.Default, + preTextContentAllocator: ContentBuilder? = null, + afterTextContentAllocator: ContentBuilder? = null, + onClick: ((SyntheticMouseEvent) -> Unit)? = null +) = DefaultButton(*modifiers, disabled = disabled, buttonType = buttonType, onClick = onClick) { + preTextContentAllocator ?.apply { preTextContentAllocator() } + Text(text) + afterTextContentAllocator ?.apply { afterTextContentAllocator() } +} + +@Composable +fun UploadButton( + text: String, + vararg buttonModifiers: UIKitModifier, + containerModifiers: Array = emptyArray(), + disabled: Boolean = false, + buttonType: UIKitButton.Type = UIKitButton.Type.Default, + onChange: (Event) -> Unit +) { + Div( + { + classes("js-upload", "uk-form-custom") + attr("uk-form-custom", "") + include(*containerModifiers) + } + ) { + Input(InputType.File) { onChange { onChange(it.nativeEvent) } } + Button( + { + classes("uk-button") + include(*buttonModifiers, buttonType) + if (disabled) { + disabled() + } + } + ) { + Text(text) + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Dialog.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Dialog.kt new file mode 100644 index 0000000..d198c60 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Dialog.kt @@ -0,0 +1,94 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffectResult +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.* +import org.w3c.dom.HTMLElement +import org.w3c.dom.events.Event +import kotlin.random.Random +import kotlin.random.nextUInt + +private class DialogDisposableEffectResult( + private val element: HTMLElement, + private val onDispose: (() -> Unit)?, + private val onDisposed: (() -> Unit)? +) : DisposableEffectResult { + override fun dispose() { + onDispose?.invoke() + js("UIkit").modal("#${element.id}") ?.hide() + onDisposed?.invoke() + } +} + +@Composable +fun Dialog( + title: String? = null, + vararg modifiers: UIKitModifier, + hide: (() -> Unit)? = null, + hidden: (() -> Unit)? = null, + footerBuilder: (@Composable () -> Unit)? = null, + bodyBuilder: @Composable () -> Unit +) { + Div( + { + attr("uk-modal", "") + classes("uk-flex-top", "uk-modal") + id("dialog${Random.nextUInt()}") + include(*modifiers) + } + ) { + Div( + { + classes("uk-modal-dialog", "uk-margin-auto-vertical") + } + ) { + title ?.let { + Div( + { + classes("uk-modal-header") + } + ) { + H2({ classes("uk-modal-title") }) { + Text(title) + } + } + } + Div( + { + classes("uk-modal-body") + } + ) { + bodyBuilder() + } + footerBuilder ?.let { + Div( + { + classes("uk-modal-footer", "uk-text-right") + } + ) { + footerBuilder() + } + } + } + + DisposableRefEffect { + DialogDisposableEffectResult(it, hide, hidden) + } + + DomSideEffect { htmlElement -> + var wrapper: (Event) -> Unit = {} + wrapper = { it: Event -> + htmlElement.removeEventListener("hidden", wrapper) + htmlElement.remove() + hidden ?.invoke() + } + htmlElement.addEventListener("hidden", wrapper) + + val dialog = js("UIkit").modal("#${htmlElement.id}") + dialog.show() + Unit + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Divider.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Divider.kt new file mode 100644 index 0000000..9155309 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Divider.kt @@ -0,0 +1,7 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import org.jetbrains.compose.web.dom.Hr + +@Composable +fun Divider() = Hr({ classes("uk-divider-icon") }) diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Dropdown.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Dropdown.kt new file mode 100644 index 0000000..e39e838 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Dropdown.kt @@ -0,0 +1,46 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.* +import dev.inmo.jsuikit.defaults.utils.Milliseconds +import org.jetbrains.compose.web.dom.ContentBuilder +import org.jetbrains.compose.web.dom.Div +import org.w3c.dom.HTMLDivElement + +@Composable +fun Dropdown( + vararg modifiers: UIKitModifier, + toggle: String? = null, + pos: UIKitDropdown.Position? = null, + mode: UIKitDropdown.Mode? = null, + delayShow: Milliseconds? = null, + delayHide: Milliseconds? = null, + boundary: String? = null, + boundaryAlign: Boolean? = null, + flip: UIKitDropdown.Flip? = null, + offset: Int? = null, + animation: UIKitAnimation? = null, + duration: Milliseconds? = null, + contentBuilder: ContentBuilder +) { + Div( + { + buildAndAddAttribute("uk-dropdown") { + "toggle" to toggle + "pos" to pos + "mode" to mode + "delayShow" to delayShow + "delayHide" to delayHide + "boundary" to boundary + "boundaryAlign" to boundaryAlign + "flip" to flip + "offset" to offset + "animation" to animation + "duration" to duration + } + classes("uk-dropdown") + include(*modifiers) + }, + contentBuilder + ) +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Flex.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Flex.kt new file mode 100644 index 0000000..b581cd0 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Flex.kt @@ -0,0 +1,23 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.HTMLDivElement + +@Composable +fun Flex( + vararg modifiers: UIKitModifier, + filler: @Composable ElementScope.() -> Unit +) { + Div( + { + classes("uk-flex") + include(*modifiers) + } + ) { + filler() + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Grid.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Grid.kt new file mode 100644 index 0000000..7059f5c --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Grid.kt @@ -0,0 +1,48 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.HTMLDivElement + +@Composable +fun GridColumn( + vararg modifiers: UIKitModifier, + builder: @Composable ElementScope.() -> Unit +) { + Div( + { + include(*modifiers) + } + ) { + builder() + } +} + +@Composable +fun Grid( + vararg modifiers: UIKitModifier, + masonry: Boolean = false, + parallax: Int? = null, + marginClass: String? = null, + firstColumnClass: String? = null, + builder: @Composable ElementScope.() -> Unit +) { + val attrs = listOfNotNull( + if (masonry) "masonry" to "true" else null, + parallax ?.let { "parallax" to it.toString() }, + marginClass ?.let { "margin" to it }, + firstColumnClass ?.let { "first-column" to it }, + ) + Div( + { + attr("uk-grid", attrs.joinToString(";") { (k, v) -> "$k: $v" }) + classes("uk-grid") + include(*modifiers) + } + ) { + builder() + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Icon.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Icon.kt new file mode 100644 index 0000000..0c18434 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Icon.kt @@ -0,0 +1,250 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.* +import org.jetbrains.compose.web.dom.Button +import org.jetbrains.compose.web.dom.Span +import org.w3c.dom.events.Event + +sealed class Icon(val name: String) { + sealed class App(iconName: String) : Icon(iconName) { + object Home : App("home") + sealed class Sign(iconName: String) : App("sign-$iconName") { + object In : Sign("in") + object Out : Sign("out") + } + object User : App("user") + object Users : App("users") + object Lock : App("lock") + object Unlock : App("unlock") + object Settings : App("settings") + object Cog : App("cog") + object Nut : App("nut") + object Comment : App("comment") + object Commenting : App("commenting") + object Comments : App("comments") + object Hashtag : App("hashtag") + object Tag : App("tag") + object Cart : App("cart") + object Bag : App("bag") + sealed class Credit(iconName: String) : App("credit-$iconName") { + object Card : Credit("card") + } + object Mail : App("mail") + object Receiver : App("receiver") + object Print : App("print") + object Search : App("search") + object Location : App("location") + object Bookmark : App("bookmark") + object Code : App("code") + sealed class Paint(iconName: String) : App("paint-$iconName") { + object Bucket : Paint("bucket") + } + object Camera : App("camera") + sealed class Video(iconName: String) : App("video-$iconName") { + object Camera : Video("camera") + } + object Bell : App("bell") + object Microphone : App("microphone") + object Bolt : App("bolt") + object Star : App("star") + object Heart : App("heart") + object Happy : App("happy") + object Lifesaver : App("lifesaver") + object Rss : App("rss") + object Social : App("social") + sealed class Git(iconName: String) : App("git-$iconName") { + object Branch : Git("branch") + object Fork : Git("fork") + } + object World : App("world") + object Calendar : App("calendar") + object Clock : App("clock") + object History : App("history") + object Future : App("future") + object Pencil : App("pencil") + object Trash : App("trash") + object Move : App("move") + object Link : App("link") + object Question : App("question") + object Info : App("info") + object Warning : App("warning") + object Image : App("image") + object Thumbnails : App("thumbnails") + object Table : App("table") + object List : App("list") + object Menu : App("menu") + object Grid : App("grid") + sealed class More(iconName: String) : App("more${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Vertical : More("vertical") + companion object : More("more") + } + sealed class Plus(iconName: String) : App("plus${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Circle : Plus("circle") + companion object : Plus("plus") + } + sealed class Minus(iconName: String) : App("minus${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Circle : Minus("circle") + companion object : Minus("minus") + } + object Close : App("close") + object Check : App("check") + object Ban : App("ban") + object Refresh : App("refresh") + sealed class Play(iconName: String) : App("play${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Circle : Play("circle") + companion object : Play("play") + } + } + sealed class Devices(iconName: String) : Icon(iconName) { + object Tv : Devices("tv") + object Desktop : Devices("desktop") + object Laptop : Devices("laptop") + sealed class Tablet(iconName: String) : Devices("tablet${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Landscape : Tablet("landscape") + companion object : Tablet("tablet") + } + sealed class Phone(iconName: String) : Devices("phone${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Landscape : Phone("landscape") + companion object : Phone("phone") + } + } + sealed class Storage(iconName: String) : Icon(iconName) { + sealed class File(iconName: String) : Storage("file${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Text : File("text") + object Pdf : File("pdf") + object Edit : File("edit") + companion object : File("file") + } + object Copy : Storage("copy") + object Folder : Storage("folder") + object Album : Storage("album") + object Push : Storage("push") + object Pull : Storage("pull") + object Server : Storage("server") + object Database : Storage("database") + sealed class Cloud(iconName: String) : Storage("cloud-$iconName") { + object Upload : Cloud("upload") + object Download : Cloud("download") + } + object Download : Storage("download") + object Upload : Storage("upload") + } + sealed class Direction(iconName: String) : Icon(iconName) { + object Reply : Direction("reply") + object Forward : Direction("forward") + object Expand : Direction("expand") + object Shrink : Direction("shrink") + sealed class Arrow(iconName: String) : Direction("arrow-$iconName") { + object Up : Arrow("up") + object Down : Arrow("down") + object Left : Arrow("left") + object Right : Arrow("right") + } + sealed class Chevron(iconName: String) : Direction("chevron-$iconName") { + object Up : Chevron("up") + object Down : Chevron("down") + object Left : Chevron("left") + object Right : Chevron("right") + sealed class Double(iconName: String) : Chevron("double-$iconName") { + object Left : Double("left") + object Right : Double("right") + } + } + sealed class Triangle(iconName: String) : Direction("triangle-$iconName") { + object Up : Triangle("up") + object Down : Triangle("down") + object Left : Triangle("left") + object Right : Triangle("right") + } + } + sealed class Editor(iconName: String) : Icon(iconName) { + object Bold : Editor("bold") + object Italic : Editor("italic") + object Strikethrough : Editor("strikethrough") + sealed class Quote(iconName: String) : Editor("quote-$iconName") { + object Right : Quote("right") + } + } + sealed class Brands(iconName: String) : Icon(iconName) { + object _500px : Brands("500px") + object Behance : Brands("behance") + object Discord : Brands("discord") + object Dribbble : Brands("dribbble") + object Etsy : Brands("etsy") + object Facebook : Brands("facebook") + object Flickr : Brands("flickr") + object Foursquare : Brands("foursquare") + sealed class Github(iconName: String) : Brands("github${iconName.takeIf { it.isNotEmpty() } ?.let { "-$it" } ?: "" }") { + object Alt : Github("alt") + companion object : Github("github") + } + object Gitter : Brands("gitter") + object Google : Brands("google") + object Instagram : Brands("instagram") + object Joomla : Brands("joomla") + object Linkedin : Brands("linkedin") + object Pagekit : Brands("pagekit") + object Pinterest : Brands("pinterest") + object Reddit : Brands("reddit") + object Soundcloud : Brands("soundcloud") + object Tiktok : Brands("tiktok") + object Tripadvisor : Brands("tripadvisor") + object Tumblr : Brands("tumblr") + object Twitch : Brands("twitch") + object Twitter : Brands("twitter") + object Uikit : Brands("uikit") + object Vimeo : Brands("vimeo") + object Whatsapp : Brands("whatsapp") + object Wordpress : Brands("wordpress") + object Xing : Brands("xing") + object Yelp : Brands("yelp") + object Youtube : Brands("youtube") + } + + @Composable + operator fun invoke( + vararg modifier: UIKitModifier, + type: UIKitIconType = UIKitIconType.Default, + ratio: Float? = null, + onClick: ((Event) -> Unit)? = null + ) { + if (type == UIKitIconType.Button) { + Button( + { + classes("uk-icon") + include(*modifier, type) + attr("uk-icon", "icon: $name${if (ratio != null) { "; ratio: $ratio" } else ""}") + onClick ?.let { _ -> + onClick { onClick(it.nativeEvent) } + } + } + ) + } else { + Span( + { + classes("uk-icon") + include(*modifier, type) + attr("uk-icon", "icon: $name${if (ratio != null) { "; ratio: $ratio" } else ""}") + onClick ?.let { _ -> + onClick { onClick(it.nativeEvent) } + } + } + ) + } + } + + @Composable + fun drawAsButton( + vararg modifier: UIKitModifier, + ratio: Float? = null, + onClick: ((Event) -> Unit)? = null + ) = invoke(*modifier, type = UIKitIconType.Button, ratio = ratio, onClick = onClick) + + @Composable + fun drawAsIcon( + vararg modifier: UIKitModifier, + ratio: Float? = null, + onClick: ((Event) -> Unit)? = null + ) = invoke(*modifier, type = UIKitIconType.Default, ratio = ratio, onClick = onClick) +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Labels.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Labels.kt new file mode 100644 index 0000000..71a5b15 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Labels.kt @@ -0,0 +1,39 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.Span +import org.jetbrains.compose.web.dom.Text + +sealed interface Label { + val suffix: String + + @Composable + operator fun invoke( + text: String, + vararg modifiers: UIKitModifier + ) = Span( + { + classes("uk-label", "uk-label-$suffix") + include(*modifiers) + } + ) { + Text(text) + } + + object Success : Label { + override val suffix: String + get() = "success" + } + + object Warning : Label { + override val suffix: String + get() = "warning" + } + + object Error : Label { + override val suffix: String + get() = "danger" + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/List.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/List.kt new file mode 100644 index 0000000..d18ded1 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/List.kt @@ -0,0 +1,33 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.snapshots.SnapshotStateList +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.* +import org.w3c.dom.HTMLUListElement + +@Composable +fun List( + title: String, + data: SnapshotStateList, + titleModifiers: Array = emptyArray(), + ulModifiers: Array = emptyArray(), + besidesTitleAndList: (@Composable () -> Unit)? = null, + elementAllocator: @Composable ElementScope.(T) -> Unit +) { + H4({ include(*titleModifiers) }) { + Text(title) + } + besidesTitleAndList ?.invoke() + Ul( + { + classes("uk-list") + include(*ulModifiers) + } + ) { + data.forEach { + elementAllocator(it) + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Nav.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Nav.kt new file mode 100644 index 0000000..8ea3889 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Nav.kt @@ -0,0 +1,49 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.* +import dev.inmo.jsuikit.defaults.utils.Milliseconds +import org.jetbrains.compose.web.dom.* +import org.w3c.dom.HTMLLIElement +import org.w3c.dom.HTMLUListElement + +@Composable +fun Nav( + vararg modifiers: UIKitModifier, + multiple: Boolean? = null, + collapsible: Boolean? = null, + animation: UIKitAnimation? = null, + duration: Milliseconds? = null, + dataAllocator: ContentBuilder +) { + Ul( + { + buildAndAddAttribute("uk-nav") { + "multiple" to multiple ?.toString() + "collapsible" to collapsible ?.toString() + "animation" to animation + "duration" to duration ?.toString() + } + classes("uk-nav") + include(*modifiers) + } + ) { + dataAllocator() + } +} + +@Composable +fun NavElement( + vararg modifiers: UIKitModifier, + attributesAllocator: (AttrBuilderContext)? = null, + contentAllocator: ContentBuilder +) { + Li( + { + attributesAllocator ?.apply { attributesAllocator() } + include(*modifiers) + } + ) { + contentAllocator() + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Navbar.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Navbar.kt new file mode 100644 index 0000000..b73e1c4 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Navbar.kt @@ -0,0 +1,33 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.Nav + +@Composable +fun Navbar( + vararg navModifiers: UIKitModifier, + leftBuilder: NavbarNavBuilder? = null, + centerBuilder: NavbarNavBuilder? = null, + rightBuilder: NavbarNavBuilder? = null, +) { + Nav( + { + attr("uk-navbar", "") + classes("uk-navbar-container", "uk-navbar") + include(*navModifiers) + } + ) { + leftBuilder ?.let { + Div({ classes("uk-navbar-left") }) { it.draw() } + } + centerBuilder ?.let { + Div({ classes("uk-navbar-center") }) { it.draw() } + } + rightBuilder ?.let { + Div({ classes("uk-navbar-right") }) { it.draw() } + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/NavbarNavBuilder.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/NavbarNavBuilder.kt new file mode 100644 index 0000000..ccb5197 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/NavbarNavBuilder.kt @@ -0,0 +1,58 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.attributes.AttrsBuilder +import org.jetbrains.compose.web.dom.* +import org.w3c.dom.HTMLLIElement + +interface NavbarNavElement { + fun AttrsBuilder.setup() {} + @Composable + fun ElementScope.fill() + + class Default( + private val setupCallback: AttrsBuilder.() -> Unit, + private val fillCallback: @Composable ElementScope.() -> Unit + ) : NavbarNavElement { + override fun AttrsBuilder.setup() { + setupCallback() + } + + @Composable + override fun ElementScope.fill() { + fillCallback() + } + } +} + +class NavbarNavBuilder( + private val modifiers: Array, + private val elements: List +) { + constructor( + modifiers: Array, + vararg elements: NavbarNavElement + ) : this(modifiers, elements.toList()) + constructor( + vararg elements: NavbarNavElement + ) : this(emptyArray(), elements.toList()) + @Composable + fun draw() { + Ul( + { + classes("uk-navbar-nav") + include(*modifiers) + } + ) { + elements.forEach { element -> + Li( + { element.apply { setup() } } + ) { + element.apply { fill() } + } + } + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Progress.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Progress.kt new file mode 100644 index 0000000..79dd540 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Progress.kt @@ -0,0 +1,22 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.dom.Progress + +@Composable +fun Progress( + value: Int, + vararg modifiers: UIKitModifier, + max: Int = 100 +) { + Progress( + { + classes("uk-progress") + include(*modifiers) + attr("max", max.toString()) + attr("value", value.toString()) + } + ) +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Spinner.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Spinner.kt new file mode 100644 index 0000000..c9ba980 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Spinner.kt @@ -0,0 +1,22 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import dev.inmo.jsuikit.defaults.modifers.* +import org.jetbrains.compose.web.dom.Div + +@Composable +fun Spinner( + vararg modifier: UIKitModifier, + ratio: Float? = null +) { + Div( + { + attr("uk-spinner", ratio ?.let { "ratio: $it" } ?: "") + classes("uk-icon", "uk-spinner") + include(*modifier) + } + ) +} + +@Composable +fun DefaultSpinner() = Spinner(UIKitAlign.Center, UIKitMargin.Small, UIKitText.Alignment.Center) diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/Table.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/Table.kt new file mode 100644 index 0000000..e01f3ff --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/Table.kt @@ -0,0 +1,47 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.snapshots.SnapshotStateList +import dev.inmo.jsuikit.defaults.modifers.* +import org.jetbrains.compose.web.dom.* + +@Composable +fun DefaultTable( + heading: List, + dataList: SnapshotStateList, + vararg tableModifiers: UIKitModifier, + cellFiller: @Composable (i: Int, t: T) -> Unit +) { + val headingIndexes = heading.indices + Table( + { + classes("uk-table") + include(*tableModifiers) + } + ) { + Thead { + Tr { + heading.forEach { + Th( + { + include(UIKitExtension.TextTransformUnset) + } + ) { + Text(it) + } + } + } + } + Tbody { + dataList.forEach { + Tr { + headingIndexes.forEach { i -> + Td { + cellFiller(i, it) + } + } + } + } + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/TextField.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/TextField.kt new file mode 100644 index 0000000..0aa7675 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/TextField.kt @@ -0,0 +1,30 @@ +package dev.inmo.jsuikit.defaults + +import androidx.compose.runtime.* +import dev.inmo.jsuikit.defaults.modifers.UIKitModifier +import dev.inmo.jsuikit.defaults.modifers.include +import org.jetbrains.compose.web.attributes.* +import org.jetbrains.compose.web.dom.Input + +@Composable +fun TextField( + type: InputType, + state: MutableState, + disabledState: State? = null, + placeholder: String? = null, + vararg modifiers: UIKitModifier +) { + Input(type) { + classes("uk-input") + include(*modifiers) + + placeholder ?.let(::placeholder) + + onChange { state.value = it.value } + disabledState ?.let { + if (it.value) { + disabled() + } + } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/UIKitAttributeValueBuilder.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/UIKitAttributeValueBuilder.kt new file mode 100644 index 0000000..8e0aa37 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/UIKitAttributeValueBuilder.kt @@ -0,0 +1,27 @@ +package dev.inmo.jsuikit.defaults + +import dev.inmo.jsuikit.defaults.modifers.AttributeValue +import org.jetbrains.compose.web.attributes.AttrsBuilder + +class UIKitAttributeValueBuilder { + private val attrs = mutableListOf>() + + infix fun String.to(other: String?) { + other ?.let { + attrs.add(Pair(this, other)) + } + } + + infix fun String.to(other: AttributeValue?) { + this to other ?.name + } + + fun build(): String = attrs.joinToString(";") { (k, v) -> "$k: $v" } +} + +fun AttrsBuilder<*>.buildAndAddAttribute( + attributeName: String, + block: UIKitAttributeValueBuilder.() -> Unit +) { + attr(attributeName, UIKitAttributeValueBuilder().apply(block).build()) +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/AttributeValue.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/AttributeValue.kt new file mode 100644 index 0000000..491896c --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/AttributeValue.kt @@ -0,0 +1,3 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class AttributeValue(val name: String) diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKit.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKit.kt new file mode 100644 index 0000000..8b8c520 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKit.kt @@ -0,0 +1,4 @@ +package dev.inmo.jsuikit.defaults.modifers + +inline val UIKit + get() = js("UIkit") diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAlign.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAlign.kt new file mode 100644 index 0000000..42bcac6 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAlign.kt @@ -0,0 +1,8 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitAlign(classnameSuffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-align-$classnameSuffix") + object Left : UIKitAlign("left") + object Center : UIKitAlign("center") + object Right : UIKitAlign("right") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAnimation.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAnimation.kt new file mode 100644 index 0000000..08ad8f7 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitAnimation.kt @@ -0,0 +1,63 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitAnimation (name: String) : UIKitModifier, AttributeValue(name) { + override val classes: Array = arrayOf("uk-animation-$name") + + object Fade : UIKitAnimation("fade") + + sealed class Scale(suffix: String) : UIKitAnimation("scale-$suffix") { + + object Up : Scale("up") + object Down : Scale("down") + + } + + sealed class Slide(suffix: String) : UIKitAnimation("slide-$suffix") { + + sealed class Top(suffixWithStroke: String) : Slide("top$suffixWithStroke") { + + object Small : Top("-small") + object Medium : Top("-medium") + + companion object : Top("") + + } + + sealed class Bottom(suffixWithStroke: String) : Slide("bottom$suffixWithStroke") { + + object Small : Bottom("-small") + object Medium : Bottom("-medium") + + companion object : Bottom("") + + } + + sealed class Left(suffixWithStroke: String) : Slide("left$suffixWithStroke") { + + object Small : Left("-small") + object Medium : Left("-medium") + + companion object : Left("") + + } + + sealed class Right(suffixWithStroke: String) : Slide("right$suffixWithStroke") { + + object Small : Right("-small") + object Medium : Right("-medium") + + companion object : Right("") + + } + + } + + object Shake : UIKitAnimation("shake") + + object Stroke : UIKitAnimation("stroke") + + object Reverse : UIKitAnimation("reverse") + + object Fast : UIKitAnimation("fast") + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitBackground.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitBackground.kt new file mode 100644 index 0000000..8b07a49 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitBackground.kt @@ -0,0 +1,46 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitBackground(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-background-$suffix") + + sealed class Color(suffix: String) : UIKitBackground(suffix) { + object Default : Color("default") + object Muted : Color("muted") + object Primary : Color("primary") + object Secondary : Color("secondary") + } + + sealed class Size(suffix: String) : UIKitBackground(suffix) { + object Cover : Size("cover") + object Contain : Size("contain") + object FullWidth : Size("width-1-1") + object FullHeight : Size("height-1-1") + } + + sealed class Position(suffix: String) : UIKitBackground(suffix) { + + sealed class Top(suffix: String) : Position("top-$suffix") { + object Left : Top("left") + object Center : Top("center") + object Right : Top("right") + } + + sealed class Center(suffix: String) : Position("center-$suffix") { + object Left : Position.Center("left") + object Center : Position.Center("center") + object Right : Position.Center("right") + } + + sealed class Bottom(suffix: String) : Position("bottom-$suffix") { + object Left : Bottom("left") + object Center : Bottom("center") + object Right : Bottom("right") + } + + } + + object NoRepeat : Size("norepeat") + + object Fixed : Size("fixed") + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitButton.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitButton.kt new file mode 100644 index 0000000..8a40f1c --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitButton.kt @@ -0,0 +1,14 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitButton(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-button-$suffix") + + sealed class Type(suffix: String) : UIKitButton(suffix) { + object Default : Type("default") + object Primary : Type("primary") + object Secondary : Type("secondary") + object Danger : Type("danger") + object Text : Type("text") + object Link : Type("link") + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitDropdown.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitDropdown.kt new file mode 100644 index 0000000..013526e --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitDropdown.kt @@ -0,0 +1,64 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitDropdown(classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + object Nav : UIKitDropdown("uk-dropdown-nav") + + object Grid : UIKitDropdown("uk-dropdown-grid") + + sealed class Position(name: String) : AttributeValue(name) { + + sealed class Bottom(name: String) : Position("bottom-$name") { + + object Left : Bottom("left") + object Center : Bottom("center") + object Right : Bottom("right") + object Justify : Bottom("justify") + + } + + sealed class Top(name: String) : Position("top-$name") { + + object Left : Top("left") + object Center : Top("center") + object Right : Top("right") + object Justify : Top("justify") + + } + + sealed class Left(name: String) : Position("left-$name") { + + object Top : Left("top") + object Center : Left("center") + object Bottom : Left("bottom") + + } + + sealed class Right(name: String) : Position("right-$name") { + + object Top : Right("top") + object Center : Right("center") + object Bottom : Right("bottom") + + } + + } + + sealed class Mode(name: String) : AttributeValue(name) { + + object Click : Mode("click") + object Hover : Mode("hover") + + } + + sealed class Flip(name: String) : AttributeValue(name) { + + object True : Flip("true") + object False : Flip("false") + object X : Flip("x") + object Y : Flip("y") + + } + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitExtensions.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitExtensions.kt new file mode 100644 index 0000000..8448016 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitExtensions.kt @@ -0,0 +1,7 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitExtension(classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + object TextTransformUnset : UIKitExtension("text_transform_unset") + object CursorPointer : UIKitExtension("cursor_pointer") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitFlex.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitFlex.kt new file mode 100644 index 0000000..2866e26 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitFlex.kt @@ -0,0 +1,79 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitFlex(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-flex-$suffix") + + object Inline : UIKitFlex("inline") + + sealed class Alignment(suffix: String) : UIKitFlex(suffix) { + + sealed class Horizontal(suffix: String) : Alignment(suffix) { + + object Left : Horizontal("left") + object Center : Horizontal("center") + object Right : Horizontal("right") + object Between : Horizontal("between") + object Around : Horizontal("around") + + } + + sealed class Vertical(suffix: String) : Alignment(suffix) { + + object Stretch : Vertical("stretch") + object Top : Vertical("top") + object Middle : Vertical("middle") + object Bottom : Vertical("bottom") + + } + + } + + sealed class Direction(suffix: String) : UIKitFlex(suffix) { + + sealed class Row(suffix: String) : Direction(suffix) { + + object Reverse : Row("row-reverse") + companion object : Row("row") + + } + + sealed class Column(suffix: String) : Direction(suffix) { + + object Reverse : Column("column-reverse") + companion object : Column("column") + + } + + } + + sealed class Wrap(suffix: String) : UIKitFlex(suffix) { + + object Reverse : Wrap("wrap-reverse") + object Stretch : Wrap("wrap-stretch") + object Between : Wrap("wrap-between") + object Around : Wrap("wrap-around") + object Top : Wrap("wrap-top") + object Middle : Wrap("wrap-middle") + object Bottom : Wrap("wrap-bottom") + companion object : Wrap("wrap") + + } + + sealed class Order(suffix: String) : UIKitFlex(suffix) { + + object First : Order("first") + object Last : Order("last") + + } + + sealed class Dimensions(suffix: String) : UIKitFlex(suffix) { + + object None : Dimensions("none") + object Auto : Dimensions("auto") + object BasedOnFlex : Dimensions("1") + + } + + object NoWrap : UIKitFlex("nowrap") + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitGrid.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitGrid.kt new file mode 100644 index 0000000..25b1e45 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitGrid.kt @@ -0,0 +1,20 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitGrid(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-grid-$suffix") + + sealed class Gap(suffix: String) : UIKitGrid(suffix) { + + object Small : Gap("small") + object Medium : Gap("medium") + object Large : Gap("large") + object Collapse : Gap("collapse") + + } + + object Divider : UIKitGrid("divider") + + object MatchHeight : UIKitGrid("match") + object ItemMatchHeight : UIKitGrid("item-match") + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitIconType.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitIconType.kt new file mode 100644 index 0000000..ae1dcfa --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitIconType.kt @@ -0,0 +1,11 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitIconType(suffix: String?) : UIKitModifier { + override val classes: Array = suffix ?.let { + arrayOf("uk-icon-$suffix") + } ?: emptyArray() + + object Default : UIKitIconType(null) + object Link : UIKitIconType("link") + object Button : UIKitIconType("button") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitMargin.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitMargin.kt new file mode 100644 index 0000000..91e9dab --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitMargin.kt @@ -0,0 +1,72 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitMargin(val classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + object Top : Small("uk-margin-top") + object Bottom : Small("uk-margin-bottom") + object Left : Small("uk-margin-left") + object Right : Small("uk-margin-right") + companion object : Small("uk-margin") + + sealed class Small(classname: String) : UIKitMargin(classname) { + + object Top : Small("uk-margin-small-top") + object Bottom : Small("uk-margin-small-bottom") + object Left : Small("uk-margin-small-left") + object Right : Small("uk-margin-small-right") + companion object : Small("uk-margin-small") + + } + sealed class Medium(classname: String) : UIKitMargin(classname) { + + object Top : Medium("uk-margin-medium-top") + object Bottom : Medium("uk-margin-medium-bottom") + object Left : Medium("uk-margin-medium-left") + object Right : Medium("uk-margin-medium-right") + companion object : Medium("uk-margin-medium") + + } + sealed class Large(classname: String) : UIKitMargin(classname) { + + object Top : Large("uk-margin-large-top") + object Bottom : Large("uk-margin-large-bottom") + object Left : Large("uk-margin-large-left") + object Right : Large("uk-margin-large-right") + companion object : Large("uk-margin-large") + + } + sealed class XLarge(classname: String) : UIKitMargin(classname) { + + object Top : XLarge("uk-margin-xlarge-top") + object Bottom : XLarge("uk-margin-xlarge-bottom") + object Left : XLarge("uk-margin-xlarge-left") + object Right : XLarge("uk-margin-xlarge-right") + companion object : XLarge("uk-margin-xlarge") + + } + sealed class Auto(classname: String) : UIKitMargin(classname) { + + object Top : Auto("uk-margin-auto-top") + object Bottom : Auto("uk-margin-auto-bottom") + object Left : Auto("uk-margin-auto-left") + object Right : Auto("uk-margin-auto-right") + object Vertical : Auto("uk-margin-auto-vertical") + companion object : Auto("uk-margin-auto") + + } + + sealed class Remove(classname: String) : UIKitMargin(classname) { + + object Top : Remove("uk-margin-remove-top") + object Bottom : Remove("uk-margin-remove-bottom") + object Left : Remove("uk-margin-remove-left") + object Right : Remove("uk-margin-remove-right") + object Vertical : Remove("uk-margin-remove-vertical") + object Adjacent : Remove("uk-margin-remove-adjacent") + object FirstChild : Remove("uk-margin-remove-first-child") + object LastChild : Remove("uk-margin-remove-last-child") + companion object : Remove("uk-margin-remove") + + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitModifier.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitModifier.kt new file mode 100644 index 0000000..728729e --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitModifier.kt @@ -0,0 +1,17 @@ +package dev.inmo.jsuikit.defaults.modifers + +import org.jetbrains.compose.web.attributes.AttrsBuilder + +interface UIKitModifier { + val classes: Array + get() = emptyArray() + val otherAttrs: Map + get() = emptyMap() +} + +fun AttrsBuilder<*>.include(vararg container: UIKitModifier?) { + container.forEach { + it ?.classes ?.let { newClasses -> classes(*newClasses) } + it ?.otherAttrs ?.let { attrs -> attrs.forEach { (k, v) -> attr(k, v) } } + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNav.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNav.kt new file mode 100644 index 0000000..638eb9c --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNav.kt @@ -0,0 +1,17 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitNav(classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + object Accordion : UIKitNav("uk-nav-parent-icon") + object Subnav : UIKitNav("uk-nav-sub") + + object Header : UIKitNav("uk-nav-header") + object Divider : UIKitNav("uk-nav-divider") + + object Default : UIKitNav("uk-nav-default") + + object Primary : UIKitNav("uk-nav-primary") + + object Center : UIKitNav("uk-nav-center") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNavbar.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNavbar.kt new file mode 100644 index 0000000..fd9c25a --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitNavbar.kt @@ -0,0 +1,17 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitNavbar(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-navbar-$suffix") + + object Transparent : UIKitNavbar("transparent") + sealed class Dropdown(suffix: String?) : UIKitNavbar("dropdown${suffix ?.let { "-$it" } ?: ""}") { + object Nav : Dropdown("nav") + + companion object : Dropdown(null) + } + object Item : UIKitNavbar("item") + + companion object { + val Logo = UIKitUtility.Logo + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPadding.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPadding.kt new file mode 100644 index 0000000..6d001ff --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPadding.kt @@ -0,0 +1,29 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitPadding(suffix: String?) : UIKitModifier { + override val classes: Array = arrayOf("uk-padding${suffix ?.let { "-$it" } ?: ""}") + + sealed class Size(suffix: String?) : UIKitPadding(suffix) { + + object Small : Size("small") + object Large : Size("large") + + companion object : Size(null) + + } + + sealed class Remove(suffix: String?) : UIKitPadding("remove${suffix ?.let { "-$it" } ?: ""}") { + + object Top : Remove("top") + object Bottom : Remove("bottom") + object Left : Remove("left") + object Right : Remove("right") + object Vertical : Remove("vertical") + object Horizontal : Remove("horizontal") + + companion object : Remove(null) + + } + + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPosition.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPosition.kt new file mode 100644 index 0000000..38109c0 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitPosition.kt @@ -0,0 +1,53 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitPosition(classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + sealed class Top(classname: String) : UIKitPosition(classname) { + companion object : Top("uk-position-top") + + object Left : Top("uk-position-top-left") + object Center : Top("uk-position-top-center") + object Right : Top("uk-position-top-right") + } + sealed class Left(classname: String) : UIKitPosition(classname) { + companion object : Left("uk-position-left") + } + sealed class Right(classname: String) : UIKitPosition(classname) { + companion object : Right("uk-position-right") + } + sealed class Center(classname: String) : UIKitPosition(classname) { + companion object : Center("uk-position-center") + + sealed class Left(classname: String) : Center(classname) { + object Out : Left("uk-position-center-left-out") + + companion object : Left("uk-position-center-left") + } + sealed class Right(classname: String) : Center(classname) { + object Out : Right("uk-position-center-right-out") + + companion object : Right("uk-position-center-right") + } + } + sealed class Bottom(classname: String) : UIKitPosition(classname) { + companion object : Bottom("uk-position-bottom") + + object Left : Bottom("uk-position-bottom-left") + object Center : Bottom("uk-position-bottom-center") + object Right : Bottom("uk-position-bottom-right") + } + + sealed class Size(classname: String) : UIKitPosition(classname) { + object Small : Size("uk-position-small") + object Medium : Size("uk-position-medium") + object Large : Size("uk-position-large") + } + + object Relative : UIKitPosition("uk-position-relative") + object Absolute : UIKitPosition("uk-position-absolute") + object Fixed : UIKitPosition("uk-position-fixed") + object ZIndex : UIKitPosition("uk-position-z-index") + object Cover : UIKitPosition("uk-position-cover") + +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitScreenSizeModifier.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitScreenSizeModifier.kt new file mode 100644 index 0000000..9a3b6d8 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitScreenSizeModifier.kt @@ -0,0 +1,20 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitScreenSizeModifier(val name: String) { + class UIKitScreenSizeModifierModified ( + val modifier: UIKitScreenSizeModifier, + val base: UIKitModifier + ) : UIKitModifier { + override val classes: Array = base.classes.map { "$it@${modifier.name}" }.toTypedArray() + } + + object Small : UIKitScreenSizeModifier("s") + object Medium : UIKitScreenSizeModifier("m") + object Large : UIKitScreenSizeModifier("l") + object XLarge : UIKitScreenSizeModifier("xl") + + fun modify(modifier: UIKitModifier): UIKitModifier = UIKitScreenSizeModifierModified( + this, + modifier + ) +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTable.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTable.kt new file mode 100644 index 0000000..65e1163 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTable.kt @@ -0,0 +1,13 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitTable(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-table-$suffix") + + object Divider : UIKitTable("divider") + object Size { + object Small : UIKitTable("small") + object Large : UIKitTable("large") + } + object Hover : UIKitTable("hover") + object Justify : UIKitTable("justify") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitText.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitText.kt new file mode 100644 index 0000000..8415c5a --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitText.kt @@ -0,0 +1,25 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitText(suffix: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-text-$suffix") + + object Lead : UIKitText("lead") + object Meta : UIKitText("meta") + + sealed class Alignment(suffix: String) : UIKitText(suffix) { + object Left : Alignment("left") + object Right : Alignment("right") + object Center : Alignment("center") + object Justify : Alignment("justify") + } + + sealed class Color(suffix: String) : UIKitText(suffix) { + object Muted : Color("muted") + object Emphasis : Color("emphasis") + object Primary : Color("primary") + object Secondary : Color("secondary") + object Success : Color("success") + object Warning : Color("warning") + object Danger : Color("danger") + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTooltipModifier.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTooltipModifier.kt new file mode 100644 index 0000000..2b396c9 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitTooltipModifier.kt @@ -0,0 +1,46 @@ +package dev.inmo.jsuikit.defaults.modifers + +class UIKitTooltipModifier( + text: String, + align: Align? = null, + delay: Int? = null, + offset: Int? = null, + duration: Int? = null, + animation: UIKitAnimation? = null +) : UIKitModifier { + private val parametersMap = listOfNotNull( + "title" to text, + align ?.let { it.k to it.v }, + delay ?.let { "delay" to it.toString() }, + offset ?.let { "offset" to it.toString() }, + duration ?.let { "duration" to it.toString() }, + animation ?.let { "animation" to it.name }, + ) + override val otherAttrs: Map = mapOf( + "uk-tooltip" to parametersMap.joinToString(";") { (k, v) -> "$k: $v" } + ) + + sealed class Align(name: String) { + val k = "pos" + val v = name + + sealed class Top(suffix: String) : Align("top$suffix") { + + object Center : Top("") + object Left : Top("-left") + object Right : Top("-right") + + } + + sealed class Bottom(suffix: String) : Align("bottom$suffix") { + + object Center : Bottom("") + object Left : Bottom("-left") + object Right : Bottom("-right") + + } + + object Left : Align("left") + object Right : Align("right") + } +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitUtility.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitUtility.kt new file mode 100644 index 0000000..198e0a3 --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitUtility.kt @@ -0,0 +1,12 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitUtility(classname: String) : UIKitModifier { + override val classes: Array = arrayOf(classname) + + sealed class Overflow(suffix: String) : UIKitUtility("uk-overflow-$suffix") { + object Hidden : Overflow("hidden") + object Auto : Overflow("auto") + } + + object Logo : UIKitUtility("uk-logo") +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitWidth.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitWidth.kt new file mode 100644 index 0000000..8d68bba --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/modifers/UIKitWidth.kt @@ -0,0 +1,33 @@ +package dev.inmo.jsuikit.defaults.modifers + +sealed class UIKitWidth(classname: String) : UIKitModifier { + override val classes: Array = arrayOf("uk-width-$classname") + + object Full : UIKitWidth("1-1") + object Expand : UIKitWidth("expand") + + object Half : UIKitWidth("1-2") + + object OneThird : UIKitWidth("1-3") + object TwoThird : UIKitWidth("2-3") + + object OneFourth : UIKitWidth("1-4") + val TwoFourth get() = Half + object ThreeFourth : UIKitWidth("3-4") + val FourFourth get() = Full + + object OneFifth : UIKitWidth("1-5") + object TwoFifth : UIKitWidth("2-5") + object ThreeFifth : UIKitWidth("3-5") + object FourFifth : UIKitWidth("4-5") + val FiveFifth get() = Full + + object OneSixth : UIKitWidth("1-6") + val TwoSixth get() = OneThird + val ThreeSixth get() = Half + val FourSixth get() = TwoThird + object FiveSixth : UIKitWidth("5-6") + val SixSixth get() = Full + + override fun toString() = classes.first() +} diff --git a/src/main/kotlin/dev/inmo/jsuikit/defaults/utils/Milliseconds.kt b/src/main/kotlin/dev/inmo/jsuikit/defaults/utils/Milliseconds.kt new file mode 100644 index 0000000..88e3b0b --- /dev/null +++ b/src/main/kotlin/dev/inmo/jsuikit/defaults/utils/Milliseconds.kt @@ -0,0 +1,3 @@ +package dev.inmo.jsuikit.defaults.utils + +typealias Milliseconds = Long diff --git a/src/test/kotlin/IconsGenerator.kt b/src/test/kotlin/IconsGenerator.kt new file mode 100644 index 0000000..1021087 --- /dev/null +++ b/src/test/kotlin/IconsGenerator.kt @@ -0,0 +1,244 @@ +val src = """App +home +sign-in +sign-out +user +users +lock +unlock +settings +cog +nut +comment +commenting +comments +hashtag +tag +cart +bag +credit-card +mail +receiver +print +search +location +bookmark +code +paint-bucket +camera +video-camera +bell +microphone +bolt +star +heart +happy +lifesaver +rss +social +git-branch +git-fork +world +calendar +clock +history +future +pencil +trash +move +link +question +info +warning +image +thumbnails +table +list +menu +grid +more +more-vertical +plus +plus-circle +minus +minus-circle +close +check +ban +refresh +play +play-circle +Devices +tv +desktop +laptop +tablet +phone +tablet-landscape +phone-landscape +Storage +file +file-text +file-pdf +copy +file-edit +folder +album +push +pull +server +database +cloud-upload +cloud-download +download +upload +Direction +reply +forward +expand +shrink +arrow-up +arrow-down +arrow-left +arrow-right +chevron-up +chevron-down +chevron-left +chevron-right +chevron-double-left +chevron-double-right +triangle-up +triangle-down +triangle-left +triangle-right +Editor +bold +italic +strikethrough +quote-right +Brands +500px +behance +discord +dribbble +etsy +facebook +flickr +foursquare +github +github-alt +gitter +google +instagram +joomla +linkedin +pagekit +pinterest +reddit +soundcloud +tiktok +tripadvisor +tumblr +twitch +twitter +uikit +vimeo +whatsapp +wordpress +xing +yelp +youtube""" + +data class IconsPart( + val src: String, + val subelements: List, + val classname: String = src.split("-").joinToString("") { it.replaceFirstChar { it.uppercase() } }, + val isSelfIcon: Boolean = true +) { + private val prefix = if (subelements.isEmpty()) "object" else "sealed class" + private val classnameArgs = if (subelements.isEmpty()) "" else "(iconName: String)" + private val parentClassnameArgs = if (subelements.isEmpty()) + "(\"$src\")" else if (src.first().isLowerCase()) "(\"$src${if (isSelfIcon) "\${iconName.takeIf { it.isNotEmpty() } ?.let { \"-\$it\" } ?: \"\" }" else "-\$iconName" }\")" + else + "(iconName)" + private fun body(linePrefix: String = ""): String { + val newLinePrefix = "$linePrefix " + val postfix = if (isSelfIcon) { + "${newLinePrefix}companion object : $classname(\"$src\")\n" + } else { + "" + } + return subelements.takeIf { + it.isNotEmpty() + } ?.joinToString("\n", " {\n", "\n$postfix$linePrefix}") { + it.generateString(this, newLinePrefix) + } ?: "" + } + + fun generateString(parent: IconsPart?, linePrefix: String = ""): String { + return "$linePrefix$prefix $classname$classnameArgs ${parent ?.let { ": ${it.classname}$parentClassnameArgs" } ?: ""}${body(linePrefix)}" + } +} + +class SimpleElement( + val src: String, + private val subelements: List, + private val isElement: Boolean +) { + val allSubelements: List = listOf(src) + subelements.flatMap { + it.allSubelements.map { + if (src.first().isLowerCase()) { + "$src-$it" + } else { + it + } + } + } + + fun toIconElement(): IconsPart = IconsPart( + src, + subelements.map { it.toIconElement() }, + isSelfIcon = isElement + ) +} + +fun split(list: List): SimpleElement { + val currentSrc = list.first().takeWhile { it != '-' } + val withoutFirst = list.drop(1) + val resultSubelements = mutableListOf() + + val withoutPrefix = when { + currentSrc.first().isUpperCase() -> withoutFirst.takeWhile { !it.first().isUpperCase() } + else -> list.filter { it.startsWith("$currentSrc-") }.map { it.removePrefix("$currentSrc-") } + } + + var left: List = withoutPrefix + + while (left.isNotEmpty()) { + val newElement = split(left) + + resultSubelements.add(newElement) + + left = left - (newElement.allSubelements.toSet() + newElement.src) + } + + return SimpleElement(currentSrc, resultSubelements, currentSrc.first().isLowerCase() && list.any { it.endsWith(currentSrc) }) +} + +fun main() { + val splitted = src.split("\n") + + var leftList = splitted + val resultSubelements = mutableListOf() + + while (leftList.isNotEmpty()) { + val subelement = split(leftList) + leftList = leftList - (subelement.allSubelements.toSet() + subelement.src) + + resultSubelements.add(subelement) + } + + val rootElement = SimpleElement("Icon", resultSubelements, false).toIconElement() + + println(rootElement.generateString(null)) +}