diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d9bbdf2bf..a936846b0cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ * `Okio`: `3.9.0` -> `3.9.1` * `AndroidFragment`: `1.8.2` -> `1.8.3` * `androidx.compose.material3:material3` has been replaced with `org.jetbrains.compose.material3:material3` +* `Common`: + * `JS`: + * Add several useful extensions + * `Compose`: + * `JS`: + * Add several useful extensions ## 0.22.2 diff --git a/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/AttrBuilderContextExtensions.kt b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/AttrBuilderContextExtensions.kt new file mode 100644 index 00000000000..1de48bdd48f --- /dev/null +++ b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/AttrBuilderContextExtensions.kt @@ -0,0 +1,19 @@ +package dev.inmo.micro_utils.common.compose + +import org.jetbrains.compose.web.dom.AttrBuilderContext +import org.w3c.dom.Element + +operator fun AttrBuilderContext?.plus( + other: AttrBuilderContext? +) = when (this) { + null -> other ?: {} + else -> when (other) { + null -> this ?: {} + else -> { + { + invoke(this) + other(this) + } + } + } +} diff --git a/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/MultiRef.kt b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/MultiRef.kt new file mode 100644 index 00000000000..b75ea519673 --- /dev/null +++ b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/MultiRef.kt @@ -0,0 +1,22 @@ +package dev.inmo.micro_utils.common.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.DisposableEffectResult +import androidx.compose.runtime.DisposableEffectScope +import org.jetbrains.compose.web.attributes.AttrsScope +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.Element + +/** + * This function must be called in the context of your tag content. It works like default [AttrsScope.ref], + * but able to be used several times. Uses [DisposableEffect] under the hood + */ +@Composable +fun ElementScope.ref( + block: DisposableEffectScope.(T) -> DisposableEffectResult +) { + DisposableEffect(0) { + block(scopeElement) + } +} diff --git a/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/TagAttributesExtensions.kt b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/TagAttributesExtensions.kt new file mode 100644 index 00000000000..3ab7f798f7d --- /dev/null +++ b/common/compose/src/jsMain/kotlin/dev/inmo/micro_utils/common/compose/TagAttributesExtensions.kt @@ -0,0 +1,11 @@ +package dev.inmo.micro_utils.common.compose + +import org.jetbrains.compose.web.dom.AttrBuilderContext + +fun tagClasses(vararg classnames: String): AttrBuilderContext<*> = { + classes(*classnames) +} + +fun tagId(id: String): AttrBuilderContext<*> = { + id(id) +} diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt new file mode 100644 index 00000000000..3f1d30ed0f6 --- /dev/null +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboard.kt @@ -0,0 +1,13 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window + +fun copyToClipboard(text: String): Boolean { + return runCatching { + window.navigator.clipboard.writeText( + text + ) + }.onFailure { + it.printStackTrace() + }.isSuccess +} diff --git a/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt new file mode 100644 index 00000000000..6edc74d37d4 --- /dev/null +++ b/common/src/jsMain/kotlin/dev/inmo/micro_utils/common/CopyToClipboardImage.kt @@ -0,0 +1,29 @@ +package dev.inmo.micro_utils.common + +import kotlinx.browser.window +import org.w3c.files.Blob +import org.w3c.files.BlobPropertyBag +import kotlin.js.json + +external class ClipboardItem(data: dynamic) + +inline fun Blob.convertToClipboardItem(): ClipboardItem { + val itemData: dynamic = json(this.type to this) + return ClipboardItem(itemData) +} + +suspend fun copyImageURLToClipboard(imageUrl: String): Boolean { + return runCatching { + val response = window.fetch(imageUrl).await() + val blob = response.blob().await() + val data = arrayOf( + Blob( + arrayOf(blob), + BlobPropertyBag("image/png") + ).convertToClipboardItem() + ).asDynamic() + window.navigator.clipboard.write(data) + }.onFailure { + it.printStackTrace() + }.isSuccess +}