now project is multiplatform and correctly working

This commit is contained in:
2022-01-13 19:56:39 +06:00
parent 5880db3db1
commit 0beb781e2c
46 changed files with 92 additions and 98 deletions

View File

@@ -0,0 +1,27 @@
package dev.inmo.jsuikit
import dev.inmo.jsuikit.modifiers.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,84 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import androidx.compose.web.events.SyntheticMouseEvent
import dev.inmo.jsuikit.modifiers.*
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.HTMLDivElement
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,
attributesCustomizer: AttrBuilderContext<HTMLButtonElement> = {},
contentAllocator: ContentBuilder<HTMLButtonElement>
) {
Button(
{
onClick ?.let { onClick(it) }
classes("uk-button")
include(*modifiers, buttonType)
if (disabled) {
disabled()
}
attributesCustomizer()
}
) {
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,
attributesCustomizer: AttrBuilderContext<HTMLButtonElement> = {},
onClick: ((SyntheticMouseEvent) -> Unit)? = null
) = DefaultButton(*modifiers, disabled = disabled, buttonType = buttonType, onClick = onClick, attributesCustomizer = attributesCustomizer) {
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,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
onChange: (Event) -> Unit
) {
Div(
{
classes("js-upload", "uk-form-custom")
attr("uk-form-custom", "")
include(*containerModifiers)
attributesCustomizer()
}
) {
Input(InputType.File) { onChange { onChange(it.nativeEvent) } }
Button(
{
classes("uk-button")
include(*buttonModifiers, buttonType)
if (disabled) {
disabled()
}
}
) {
Text(text)
}
}
}

View File

@@ -0,0 +1,97 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffectResult
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.dom.Text
import org.w3c.dom.*
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,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
bodyBuilder: @Composable () -> Unit
) {
Div(
{
attr("uk-modal", "")
classes("uk-flex-top", "uk-modal")
id("dialog${Random.nextUInt()}")
include(*modifiers)
attributesCustomizer()
}
) {
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.elements
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,48 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.buildAndAddAttribute
import dev.inmo.jsuikit.modifiers.*
import dev.inmo.jsuikit.utils.Milliseconds
import org.jetbrains.compose.web.dom.*
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,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
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)
attributesCustomizer()
},
contentBuilder
)
}

View File

@@ -0,0 +1,24 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLDivElement
@Composable
fun Flex(
vararg modifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
filler: @Composable ElementScope<HTMLDivElement>.() -> Unit
) {
Div(
{
classes("uk-flex")
include(*modifiers)
attributesCustomizer()
}
) {
filler()
}
}

View File

@@ -0,0 +1,51 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLDivElement
@Composable
fun GridColumn(
vararg modifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
builder: @Composable ElementScope<HTMLDivElement>.() -> Unit
) {
Div(
{
include(*modifiers)
attributesCustomizer()
}
) {
builder()
}
}
@Composable
fun Grid(
vararg modifiers: UIKitModifier,
masonry: Boolean = false,
parallax: Int? = null,
marginClass: String? = null,
firstColumnClass: String? = null,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {},
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)
attributesCustomizer()
}
) {
builder()
}
}

View File

@@ -0,0 +1,244 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.*
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.*
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 modifiers: UIKitModifier,
type: UIKitIconType = UIKitIconType.Default,
ratio: Float? = null,
attributesCustomizer: AttrBuilderContext<out HTMLElement> = {},
onClick: ((Event) -> Unit)? = null
) {
val configurer: AttrBuilderContext<out HTMLElement> = {
classes("uk-icon")
include(*modifiers, type)
attr("uk-icon", "icon: $name${if (ratio != null) { "; ratio: $ratio" } else ""}")
onClick ?.let { _ ->
onClick { onClick(it.nativeEvent) }
}
attributesCustomizer()
}
if (type == UIKitIconType.Button) {
Button(configurer)
} else {
Span(configurer)
}
}
@Composable
fun drawAsButton(
vararg modifiers: UIKitModifier,
ratio: Float? = null,
attributesCustomizer: AttrBuilderContext<out HTMLElement> = {},
onClick: ((Event) -> Unit)? = null
) = invoke(*modifiers, type = UIKitIconType.Button, ratio = ratio, onClick = onClick, attributesCustomizer = attributesCustomizer)
@Composable
fun drawAsIcon(
vararg modifiers: UIKitModifier,
ratio: Float? = null,
attributesCustomizer: AttrBuilderContext<out HTMLElement> = {},
onClick: ((Event) -> Unit)? = null
) = invoke(*modifiers, type = UIKitIconType.Default, ratio = ratio, onClick = onClick, attributesCustomizer = attributesCustomizer)
}

View File

@@ -0,0 +1,41 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLSpanElement
sealed interface Label {
val suffix: String
@Composable
fun draw(
text: String,
vararg modifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLSpanElement> = {},
) = Span(
{
classes("uk-label", "uk-label-$suffix")
include(*modifiers)
attributesCustomizer()
}
) {
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,37 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import androidx.compose.runtime.snapshots.SnapshotStateList
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLHeadingElement
import org.w3c.dom.HTMLUListElement
@Composable
fun <T> List(
title: String,
data: SnapshotStateList<T>,
vararg titleModifiers: UIKitModifier,
ulModifiers: Array<UIKitModifier> = emptyArray(),
besidesTitleAndList: (@Composable () -> Unit)? = null,
titleCustomizer: AttrBuilderContext<HTMLHeadingElement> = {},
ulCustomizer: AttrBuilderContext<HTMLUListElement> = {},
elementAllocator: @Composable ElementScope<HTMLUListElement>.(T) -> Unit
) {
H4({ include(*titleModifiers); titleCustomizer() }) {
Text(title)
}
besidesTitleAndList ?.invoke()
Ul(
{
classes("uk-list")
include(*ulModifiers)
ulCustomizer()
}
) {
data.forEach {
elementAllocator(it)
}
}
}

View File

@@ -0,0 +1,52 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.buildAndAddAttribute
import dev.inmo.jsuikit.modifiers.*
import dev.inmo.jsuikit.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,
attributesCustomizer: AttrBuilderContext<HTMLUListElement> = {},
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)
attributesCustomizer()
}
) {
dataAllocator()
}
}
@Composable
fun NavElement(
vararg modifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLLIElement> = {},
contentAllocator: ContentBuilder<HTMLLIElement>
) {
Li(
{
include(*modifiers)
attributesCustomizer
}
) {
contentAllocator()
}
}

View File

@@ -0,0 +1,35 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLElement
@Composable
fun Navbar(
leftBuilder: NavbarNavBuilder? = null,
centerBuilder: NavbarNavBuilder? = null,
rightBuilder: NavbarNavBuilder? = null,
vararg navModifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLElement> = {},
) {
Nav(
{
attr("uk-navbar", "")
classes("uk-navbar-container", "uk-navbar")
include(*navModifiers)
attributesCustomizer()
}
) {
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,63 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.attributes.AttrsBuilder
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLLIElement
import org.w3c.dom.HTMLUListElement
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>,
private val attributesCustomizer: AttrBuilderContext<HTMLUListElement> = {}
) {
constructor(
modifiers: Array<UIKitModifier>,
vararg elements: NavbarNavElement,
attributesCustomizer: AttrBuilderContext<HTMLUListElement> = {}
) : this(modifiers, elements.toList(), attributesCustomizer)
constructor(
vararg elements: NavbarNavElement,
attributesCustomizer: AttrBuilderContext<HTMLUListElement> = {}
) : this(emptyArray(), elements.toList(), attributesCustomizer)
@Composable
fun draw() {
Ul(
{
classes("uk-navbar-nav")
include(*modifiers)
attributesCustomizer()
}
) {
elements.forEach { element ->
Li(
{ element.apply { setup() } }
) {
element.apply { fill() }
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.dom.AttrBuilderContext
import org.jetbrains.compose.web.dom.Progress
import org.w3c.dom.HTMLProgressElement
@Composable
fun Progress(
value: Int,
vararg modifiers: UIKitModifier,
max: Int = 100,
attributesCustomizer: AttrBuilderContext<HTMLProgressElement> = {}
) {
Progress(
{
classes("uk-progress")
include(*modifiers)
attr("max", max.toString())
attr("value", value.toString())
attributesCustomizer()
}
)
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import dev.inmo.jsuikit.modifiers.*
import org.jetbrains.compose.web.dom.AttrBuilderContext
import org.jetbrains.compose.web.dom.Div
import org.w3c.dom.HTMLDivElement
@Composable
fun Spinner(
vararg modifiers: UIKitModifier,
ratio: Float? = null,
attributesCustomizer: AttrBuilderContext<HTMLDivElement> = {}
) {
Div(
{
attr("uk-spinner", ratio ?.let { "ratio: $it" } ?: "")
classes("uk-icon", "uk-spinner")
include(*modifiers)
attributesCustomizer()
}
)
}
@Composable
fun DefaultSpinner() = Spinner(UIKitAlign.Center, UIKitMargin.Small, UIKitText.Alignment.Center)

View File

@@ -0,0 +1,51 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.Composable
import androidx.compose.runtime.snapshots.SnapshotStateList
import dev.inmo.jsuikit.modifiers.*
import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLProgressElement
import org.w3c.dom.HTMLTableElement
@Composable
fun <T> DefaultTable(
heading: List<String>,
dataList: SnapshotStateList<T>,
vararg tableModifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLTableElement> = {},
cellFiller: @Composable (i: Int, t: T) -> Unit
) {
val headingIndexes = heading.indices
Table(
{
classes("uk-table")
include(*tableModifiers)
attributesCustomizer()
}
) {
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,34 @@
package dev.inmo.jsuikit.elements
import androidx.compose.runtime.*
import dev.inmo.jsuikit.modifiers.UIKitModifier
import dev.inmo.jsuikit.modifiers.include
import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.dom.AttrBuilderContext
import org.jetbrains.compose.web.dom.Input
import org.w3c.dom.HTMLInputElement
@Composable
fun <T> TextField(
type: InputType<T>,
state: MutableState<T>,
disabledState: State<Boolean>? = null,
placeholder: String? = null,
vararg modifiers: UIKitModifier,
attributesCustomizer: AttrBuilderContext<HTMLInputElement> = {},
) {
Input(type) {
classes("uk-input")
include(*modifiers)
placeholder ?.let(::placeholder)
onChange { state.value = it.value }
disabledState ?.let {
if (it.value) {
disabled()
}
}
attributesCustomizer()
}
}

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
package dev.inmo.jsuikit.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.modifiers
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.utils
typealias Milliseconds = Long