start 0.0.1 and actualize

This commit is contained in:
InsanusMokrassar 2021-12-22 14:38:12 +06:00
parent 7ee648aa18
commit f1abef9fcd
63 changed files with 2022 additions and 511 deletions

2
.gitignore vendored
View File

@ -12,4 +12,6 @@ out/
secret.gradle
local.properties
kotlin-js-store/
publishing.sh

1
CHANGELOG.md Normal file
View File

@ -0,0 +1 @@
## 0.0.1

233
README.md
View File

@ -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<T>
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")
}
}
```

View File

@ -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"

25
changelogparser.sh Executable file
View File

@ -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

View File

@ -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"
}
}

View File

@ -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"
}
}

31
github_release.gradle Normal file
View File

@ -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()
}
}

View File

@ -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

View File

@ -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

View File

@ -1,8 +0,0 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
id "com.android.library"
}
apply from: "$mppProjectWithSerializationPresetPath"

View File

@ -1,7 +0,0 @@
package project_group
class Library {
fun someLibraryMethod(): Boolean {
return true
}
}

View File

@ -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'")
}
}

View File

@ -1 +0,0 @@
<manifest package="project_group.lib"/>

View File

@ -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"

View File

@ -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)
}
}

View File

@ -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')
}
}
}
}

View File

@ -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"

75
publish.gradle Normal file
View File

@ -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
}

1
publish.kpsb Normal file
View File

@ -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"}

View File

@ -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)
}

View File

@ -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<HTMLButtonElement>
) {
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<HTMLButtonElement>? = null,
afterTextContentAllocator: ContentBuilder<HTMLButtonElement>? = 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<UIKitModifier> = 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)
}
}
}

View File

@ -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
}
}
}

View File

@ -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") })

View File

@ -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<HTMLDivElement>
) {
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
)
}

View File

@ -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<HTMLDivElement>.() -> Unit
) {
Div(
{
classes("uk-flex")
include(*modifiers)
}
) {
filler()
}
}

View File

@ -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<HTMLDivElement>.() -> 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<HTMLDivElement>.() -> 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()
}
}

View File

@ -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)
}

View File

@ -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"
}
}

View File

@ -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 <T> List(
title: String,
data: SnapshotStateList<T>,
titleModifiers: Array<UIKitModifier> = emptyArray(),
ulModifiers: Array<UIKitModifier> = emptyArray(),
besidesTitleAndList: (@Composable () -> Unit)? = null,
elementAllocator: @Composable ElementScope<HTMLUListElement>.(T) -> Unit
) {
H4({ include(*titleModifiers) }) {
Text(title)
}
besidesTitleAndList ?.invoke()
Ul(
{
classes("uk-list")
include(*ulModifiers)
}
) {
data.forEach {
elementAllocator(it)
}
}
}

View File

@ -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<HTMLUListElement>
) {
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<HTMLLIElement>)? = null,
contentAllocator: ContentBuilder<HTMLLIElement>
) {
Li(
{
attributesAllocator ?.apply { attributesAllocator() }
include(*modifiers)
}
) {
contentAllocator()
}
}

View File

@ -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() }
}
}
}

View File

@ -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<HTMLLIElement>.setup() {}
@Composable
fun ElementScope<HTMLLIElement>.fill()
class Default(
private val setupCallback: AttrsBuilder<HTMLLIElement>.() -> Unit,
private val fillCallback: @Composable ElementScope<HTMLLIElement>.() -> Unit
) : NavbarNavElement {
override fun AttrsBuilder<HTMLLIElement>.setup() {
setupCallback()
}
@Composable
override fun ElementScope<HTMLLIElement>.fill() {
fillCallback()
}
}
}
class NavbarNavBuilder(
private val modifiers: Array<UIKitModifier>,
private val elements: List<NavbarNavElement>
) {
constructor(
modifiers: Array<UIKitModifier>,
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() }
}
}
}
}
}

View File

@ -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())
}
)
}

View File

@ -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)

View File

@ -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 <T> DefaultTable(
heading: List<String>,
dataList: SnapshotStateList<T>,
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)
}
}
}
}
}
}
}

View File

@ -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 <T> TextField(
type: InputType<T>,
state: MutableState<T>,
disabledState: State<Boolean>? = 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()
}
}
}
}

View File

@ -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<Pair<String, String>>()
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())
}

View File

@ -0,0 +1,3 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class AttributeValue(val name: String)

View File

@ -0,0 +1,4 @@
package dev.inmo.jsuikit.defaults.modifers
inline val UIKit
get() = js("UIkit")

View File

@ -0,0 +1,8 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitAlign(classnameSuffix: String) : UIKitModifier {
override val classes: Array<String> = arrayOf("uk-align-$classnameSuffix")
object Left : UIKitAlign("left")
object Center : UIKitAlign("center")
object Right : UIKitAlign("right")
}

View File

@ -0,0 +1,63 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitAnimation (name: String) : UIKitModifier, AttributeValue(name) {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,46 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitBackground(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,14 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitButton(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}
}

View File

@ -0,0 +1,64 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitDropdown(classname: String) : UIKitModifier {
override val classes: Array<String> = 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")
}
}

View File

@ -0,0 +1,7 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitExtension(classname: String) : UIKitModifier {
override val classes: Array<String> = arrayOf(classname)
object TextTransformUnset : UIKitExtension("text_transform_unset")
object CursorPointer : UIKitExtension("cursor_pointer")
}

View File

@ -0,0 +1,79 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitFlex(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,20 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitGrid(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,11 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitIconType(suffix: String?) : UIKitModifier {
override val classes: Array<String> = suffix ?.let {
arrayOf("uk-icon-$suffix")
} ?: emptyArray()
object Default : UIKitIconType(null)
object Link : UIKitIconType("link")
object Button : UIKitIconType("button")
}

View File

@ -0,0 +1,72 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitMargin(val classname: String) : UIKitModifier {
override val classes: Array<String> = 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")
}
}

View File

@ -0,0 +1,17 @@
package dev.inmo.jsuikit.defaults.modifers
import org.jetbrains.compose.web.attributes.AttrsBuilder
interface UIKitModifier {
val classes: Array<String>
get() = emptyArray()
val otherAttrs: Map<String, String>
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) } }
}
}

View File

@ -0,0 +1,17 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitNav(classname: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,17 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitNavbar(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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
}
}

View File

@ -0,0 +1,29 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitPadding(suffix: String?) : UIKitModifier {
override val classes: Array<String> = 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)
}
}

View File

@ -0,0 +1,53 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitPosition(classname: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -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<String> = 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
)
}

View File

@ -0,0 +1,13 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitTable(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}

View File

@ -0,0 +1,25 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitText(suffix: String) : UIKitModifier {
override val classes: Array<String> = 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")
}
}

View File

@ -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<String, String> = 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")
}
}

View File

@ -0,0 +1,12 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitUtility(classname: String) : UIKitModifier {
override val classes: Array<String> = arrayOf(classname)
sealed class Overflow(suffix: String) : UIKitUtility("uk-overflow-$suffix") {
object Hidden : Overflow("hidden")
object Auto : Overflow("auto")
}
object Logo : UIKitUtility("uk-logo")
}

View File

@ -0,0 +1,33 @@
package dev.inmo.jsuikit.defaults.modifers
sealed class UIKitWidth(classname: String) : UIKitModifier {
override val classes: Array<String> = 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()
}

View File

@ -0,0 +1,3 @@
package dev.inmo.jsuikit.defaults.utils
typealias Milliseconds = Long

View File

@ -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<IconsPart>,
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<SimpleElement>,
private val isElement: Boolean
) {
val allSubelements: List<String> = 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<String>): SimpleElement {
val currentSrc = list.first().takeWhile { it != '-' }
val withoutFirst = list.drop(1)
val resultSubelements = mutableListOf<SimpleElement>()
val withoutPrefix = when {
currentSrc.first().isUpperCase() -> withoutFirst.takeWhile { !it.first().isUpperCase() }
else -> list.filter { it.startsWith("$currentSrc-") }.map { it.removePrefix("$currentSrc-") }
}
var left: List<String> = 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<SimpleElement>()
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))
}