Skip to content

Commit 3ce9622

Browse files
committed
WIP [1/N]
1 parent 14ce5bd commit 3ce9622

File tree

9 files changed

+309
-2
lines changed

9 files changed

+309
-2
lines changed

docs/demo/build.gradle.kts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
2+
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
3+
4+
plugins {
5+
alias(libs.plugins.compose.compiler)
6+
alias(libs.plugins.jetbrains.compose)
7+
alias(libs.plugins.kotlin.multiplatform)
8+
}
9+
10+
java {
11+
toolchain.languageVersion = JavaLanguageVersion.of(21)
12+
}
13+
14+
kotlin {
15+
jvmToolchain(21)
16+
17+
js(IR) {
18+
browser {
19+
outputModuleName = "demo"
20+
commonWebpackConfig {
21+
outputFileName = "demo.js"
22+
}
23+
}
24+
binaries.executable()
25+
}
26+
27+
sourceSets {
28+
commonMain.dependencies {
29+
implementation(compose.runtime)
30+
implementation(compose.foundation)
31+
implementation(compose.ui)
32+
implementation(compose.components.resources)
33+
implementation("org.jetbrains.androidx.navigation:navigation-compose:2.9.0-beta01")
34+
implementation(project(":miuix"))
35+
}
36+
}
37+
}
38+
39+
rootProject.plugins.withType<YarnPlugin> {
40+
rootProject.the<YarnRootExtension>().lockFileDirectory = rootProject.file("docs/demo").resolve("kotlin-js-store")
41+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import androidx.compose.foundation.background
2+
import androidx.compose.foundation.layout.Box
3+
import androidx.compose.foundation.layout.fillMaxSize
4+
import androidx.compose.foundation.layout.size
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.mutableStateOf
8+
import androidx.compose.runtime.remember
9+
import androidx.compose.runtime.setValue
10+
import androidx.compose.ui.Alignment
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.graphics.Brush
13+
import androidx.compose.ui.graphics.Color
14+
import androidx.compose.ui.unit.dp
15+
import top.yukonga.miuix.kmp.basic.Button
16+
import top.yukonga.miuix.kmp.basic.Icon
17+
import top.yukonga.miuix.kmp.basic.TextButton
18+
import top.yukonga.miuix.kmp.icon.MiuixIcons
19+
import top.yukonga.miuix.kmp.icon.icons.useful.Like
20+
21+
@Composable
22+
fun ButtonDemo() {
23+
Box(
24+
modifier = Modifier
25+
.fillMaxSize()
26+
.background(Brush.linearGradient(listOf(Color(0xFFFF6F61), Color(0xFFFF8A65)))),
27+
contentAlignment = Alignment.Center
28+
) {
29+
var buttonText by remember { mutableStateOf("Click") }
30+
var clickCount1 by remember { mutableStateOf(0) }
31+
var clickCount2 by remember { mutableStateOf(0) }
32+
Button(
33+
onClick = {
34+
clickCount1++
35+
buttonText = "Click: $clickCount1"
36+
}
37+
) {
38+
Icon(
39+
imageVector = MiuixIcons.Useful.Like,
40+
contentDescription = null,
41+
tint = Color.Unspecified,
42+
modifier = Modifier.size(24.dp)
43+
)
44+
}
45+
TextButton(
46+
text = buttonText,
47+
onClick = {
48+
clickCount2++
49+
buttonText = "Click: $clickCount2"
50+
}
51+
)
52+
}
53+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import androidx.compose.foundation.background
2+
import androidx.compose.foundation.layout.Arrangement
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.foundation.layout.systemBarsPadding
9+
import androidx.compose.foundation.layout.widthIn
10+
import androidx.compose.foundation.rememberScrollState
11+
import androidx.compose.foundation.verticalScroll
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.ui.Alignment
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.graphics.Color
16+
import androidx.compose.ui.text.font.FontWeight
17+
import androidx.compose.ui.unit.dp
18+
import androidx.navigation.compose.NavHost
19+
import androidx.navigation.compose.composable
20+
import androidx.navigation.compose.rememberNavController
21+
import top.yukonga.miuix.kmp.basic.Text
22+
23+
@Composable
24+
fun Demo(demoId: String? = null) {
25+
if (demoId == null) {
26+
DemoSelection()
27+
} else {
28+
availableComponents.first { it.id == demoId }.demo()
29+
}
30+
}
31+
32+
private data class AvailableComponent(val name: String, val id: String, val demo: @Composable () -> Unit)
33+
34+
private val availableComponents = listOf(
35+
AvailableComponent("Button", "button") { ButtonDemo() },
36+
)
37+
38+
@Composable
39+
private fun DemoSelection() {
40+
val navController = rememberNavController()
41+
NavHost(
42+
navController = navController,
43+
startDestination = "home"
44+
) {
45+
composable("home") {
46+
Box(
47+
modifier = Modifier.fillMaxSize()
48+
.background(Color(0xFFFAFAFAFA)),
49+
contentAlignment = Alignment.Center
50+
) {
51+
Column(
52+
Modifier
53+
.verticalScroll(rememberScrollState())
54+
.systemBarsPadding()
55+
.padding(16.dp)
56+
.widthIn(max = 600.dp)
57+
.fillMaxWidth(),
58+
verticalArrangement = Arrangement.spacedBy(8.dp)
59+
) {
60+
availableComponents.forEach { demo ->
61+
Text(demo.name, fontWeight = FontWeight.Medium)
62+
}
63+
}
64+
}
65+
}
66+
67+
availableComponents.forEach { component ->
68+
composable(component.id) {
69+
Column {
70+
component.demo()
71+
}
72+
}
73+
}
74+
}
75+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import androidx.compose.foundation.LocalIndication
2+
import androidx.compose.runtime.CompositionLocalProvider
3+
import androidx.compose.ui.ExperimentalComposeUiApi
4+
import androidx.compose.ui.window.CanvasBasedWindow
5+
import kotlinx.browser.document
6+
import org.jetbrains.skiko.wasm.onWasmReady
7+
import org.w3c.dom.url.URLSearchParams
8+
9+
@OptIn(ExperimentalComposeUiApi::class)
10+
fun main() {
11+
val iFrameParams = URLSearchParams(document.location?.search)
12+
val id = iFrameParams.get("id")
13+
onWasmReady {
14+
CanvasBasedWindow(canvasElementId = "ComposeTarget") {
15+
CompositionLocalProvider(LocalIndication provides NoIndication) {
16+
Demo(demoId = id)
17+
}
18+
}
19+
}
20+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import androidx.compose.foundation.IndicationNodeFactory
2+
import androidx.compose.foundation.interaction.FocusInteraction
3+
import androidx.compose.foundation.interaction.HoverInteraction
4+
import androidx.compose.foundation.interaction.InteractionSource
5+
import androidx.compose.foundation.interaction.PressInteraction
6+
import androidx.compose.ui.Modifier
7+
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
8+
import androidx.compose.ui.node.DelegatableNode
9+
import androidx.compose.ui.node.DrawModifierNode
10+
import androidx.compose.ui.node.invalidateDraw
11+
import kotlinx.coroutines.launch
12+
13+
14+
object NoIndication : IndicationNodeFactory {
15+
16+
override fun create(interactionSource: InteractionSource): DelegatableNode =
17+
DefaultDebugIndicationInstance(interactionSource)
18+
19+
override fun hashCode(): Int = -1
20+
21+
override fun equals(other: Any?) = other === this
22+
23+
private class DefaultDebugIndicationInstance(private val interactionSource: InteractionSource) :
24+
Modifier.Node(), DrawModifierNode {
25+
private var isPressed = false
26+
private var isHovered = false
27+
private var isFocused = false
28+
override fun onAttach() {
29+
coroutineScope.launch {
30+
var pressCount = 0
31+
var hoverCount = 0
32+
var focusCount = 0
33+
interactionSource.interactions.collect { interaction ->
34+
when (interaction) {
35+
is PressInteraction.Press -> pressCount++
36+
is PressInteraction.Release -> pressCount--
37+
is PressInteraction.Cancel -> pressCount--
38+
is HoverInteraction.Enter -> hoverCount++
39+
is HoverInteraction.Exit -> hoverCount--
40+
is FocusInteraction.Focus -> focusCount++
41+
is FocusInteraction.Unfocus -> focusCount--
42+
}
43+
val pressed = pressCount > 0
44+
val hovered = hoverCount > 0
45+
val focused = focusCount > 0
46+
var invalidateNeeded = false
47+
if (isPressed != pressed) {
48+
isPressed = pressed
49+
invalidateNeeded = true
50+
}
51+
if (isHovered != hovered) {
52+
isHovered = hovered
53+
invalidateNeeded = true
54+
}
55+
if (isFocused != focused) {
56+
isFocused = focused
57+
invalidateNeeded = true
58+
}
59+
if (invalidateNeeded) invalidateDraw()
60+
}
61+
}
62+
}
63+
64+
override fun ContentDrawScope.draw() {
65+
drawContent()
66+
}
67+
}
68+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta content="width=device-width, initial-scale=1.0" name="viewport">
6+
<title>Miuix</title>
7+
<script src="skiko.js" type="application/javascript"></script>
8+
<style>
9+
body {
10+
margin: 0;
11+
padding: 0;
12+
overflow: hidden;
13+
}
14+
#ComposeTarget {
15+
display: block;
16+
width: 100%;
17+
height: 100vh;
18+
}
19+
</style>
20+
</head>
21+
<body>
22+
<canvas id="ComposeTarget"></canvas>
23+
<script src="demo.js" type="application/javascript"></script>
24+
<script>
25+
// Prevent scroll events on the canvas
26+
const canvas = document.getElementById('ComposeTarget');
27+
28+
// Prevent wheel events (mouse wheel)
29+
canvas.addEventListener('wheel', function(e) {
30+
e.preventDefault();
31+
}, { passive: false });
32+
33+
// Prevent touchmove events (touch scrolling)
34+
canvas.addEventListener('touchmove', function(e) {
35+
e.preventDefault();
36+
}, { passive: false });
37+
</script>
38+
</body>
39+
40+
</html>

example/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
@file:Suppress("UnstableApiUsage")
55

66
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
7+
import org.gradle.kotlin.dsl.withType
78
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
89
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
10+
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin
11+
import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension
912
import java.util.Properties
1013

1114
plugins {
@@ -192,6 +195,10 @@ compose.desktop {
192195
}
193196
}
194197

198+
rootProject.plugins.withType<YarnPlugin> {
199+
rootProject.the<YarnRootExtension>().lockFileDirectory = rootProject.file("example").resolve("kotlin-js-store")
200+
}
201+
195202
spotless {
196203
kotlin {
197204
target("src/**/*.kt")

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[versions]
22
android-gradle-plugin = "8.10.1"
33
androidx-activity-compose = "1.10.1"
4-
androidx-window = "1.3.0"
4+
androidx-window = "1.4.0"
55
androidx-graphics-shapes = "1.0.0-alpha06"
66
compose-plugin = "1.8.1"
77
dokka = "2.0.0"

settings.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ android {
4343
}
4444

4545
rootProject.name = "miuix"
46-
include(":miuix", ":example")
46+
47+
include(":miuix")
48+
include(":example")
49+
include(":docs:demo")

0 commit comments

Comments
 (0)