Skip to content

Commit ebeaa22

Browse files
committed
Review and fix the slider component and add the RangeSlider variant
Reviewed locally with Copilot.
1 parent 7e3e945 commit ebeaa22

File tree

5 files changed

+98
-20
lines changed

5 files changed

+98
-20
lines changed

demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,25 @@ fun Material3(/*modifier: Modifier = Modifier*/
274274
HorizontalDivider()
275275

276276
// Slider
277-
var sliderValue by remember { mutableStateOf(0.5f) }
278277
Column {
279-
Text("Slider value: ${(sliderValue * 100).toInt()}%")
278+
var sliderPosition by remember { mutableStateOf(0.5f) }
279+
Text("Slider position: $sliderPosition")
280280
Slider(
281-
value = sliderValue,
282-
onValueChange = { sliderValue = it },
283-
valueRange = 0f..1f
281+
value = sliderPosition,
282+
onValueChange = { sliderPosition = it }
283+
)
284+
var discreteSliderPosition by remember { mutableStateOf(0.5f) }
285+
Text("Discrete slider position: $discreteSliderPosition")
286+
Slider(
287+
value = discreteSliderPosition,
288+
onValueChange = { discreteSliderPosition = it },
289+
steps = 3
290+
)
291+
var rangeSliderPosition by remember { mutableStateOf(0.25f..0.75f) }
292+
Text("Range slider position: $rangeSliderPosition")
293+
RangeSlider(
294+
value = rangeSliderPosition,
295+
onValueChange = { rangeSliderPosition = it }
284296
)
285297
}
286298

material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Slider.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ expect fun Slider(
1616
modifier: Modifier = Modifier,
1717
enabled: Boolean = true,
1818
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
19-
steps: Int = 0,
19+
/*@IntRange(from = 0)*/ steps: Int = 0,
20+
onValueChangeFinished: (() -> Unit)? = null
21+
)
22+
23+
@Composable
24+
expect fun RangeSlider(
25+
value: ClosedFloatingPointRange<Float>,
26+
onValueChange: (ClosedFloatingPointRange<Float>) -> Unit,
27+
modifier: Modifier = Modifier,
28+
enabled: Boolean = true,
29+
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
30+
/*@IntRange(from = 0)*/ steps: Int = 0,
2031
onValueChangeFinished: (() -> Unit)? = null
2132
)

material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import com.huanshankeji.compose.ui.Modifier
66
import com.huanshankeji.compose.ui.graphics.Color
77

88
/**
9-
* The `com.huanshankeji.compose.material.Text` function
9+
* The `com.huanshankeji.compose.material3.Text` function
1010
* can be easily confused with other Composable functions named `Text`
11-
* such as `androidx.compose.material.Text` and `org.jetbrains.compose.web.dom.Text`
11+
* such as `androidx.compose.material3.Text` and `org.jetbrains.compose.web.dom.Text`
1212
* if not careful.
1313
* [TaglessText] is recommended over this one when there is no custom [modifier].
1414
* @see com.huanshankeji.compose.material3.ext.MaterialText

material3/src/composeUiMain/kotlin/com/huanshankeji/compose/material3/Slider.composeUi.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.huanshankeji.compose.material3
22

3+
import androidx.annotation.IntRange
34
import androidx.compose.runtime.Composable
45
import com.huanshankeji.compose.ui.Modifier
56

@@ -10,7 +11,7 @@ actual fun Slider(
1011
modifier: Modifier,
1112
enabled: Boolean,
1213
valueRange: ClosedFloatingPointRange<Float>,
13-
steps: Int,
14+
@IntRange(from = 0) steps: Int,
1415
onValueChangeFinished: (() -> Unit)?
1516
) =
1617
androidx.compose.material3.Slider(
@@ -22,3 +23,23 @@ actual fun Slider(
2223
steps,
2324
onValueChangeFinished
2425
)
26+
27+
@Composable
28+
actual fun RangeSlider(
29+
value: ClosedFloatingPointRange<Float>,
30+
onValueChange: (ClosedFloatingPointRange<Float>) -> Unit,
31+
modifier: Modifier,
32+
enabled: Boolean,
33+
valueRange: ClosedFloatingPointRange<Float>,
34+
@IntRange(from = 0) steps: Int,
35+
onValueChangeFinished: (() -> Unit)?
36+
) =
37+
androidx.compose.material3.RangeSlider(
38+
value,
39+
onValueChange,
40+
modifier.platformModifier,
41+
enabled,
42+
valueRange,
43+
steps,
44+
onValueChangeFinished
45+
)

material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Slider.js.kt

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import androidx.compose.runtime.Composable
44
import com.huanshankeji.compose.html.material3.MdSlider
55
import com.huanshankeji.compose.ui.Modifier
66
import com.huanshankeji.compose.ui.toAttrs
7-
import com.huanshankeji.compose.web.attributes.ext.disabled
87
import com.huanshankeji.compose.web.attributes.ext.onInput
98
import com.huanshankeji.compose.web.attributes.isFalseOrNull
10-
import org.w3c.dom.HTMLElement
11-
import org.w3c.dom.events.Event
9+
10+
/**
11+
* Calculate the step to match the Compose UI behavior.
12+
*/
13+
private fun calculateStep(steps: Int, valueRange: ClosedFloatingPointRange<Float>): Float =
14+
// We can also use `Double.MIN_VALUE` and change the return type to `Number`, but `Float.MIN_VALUE` should be sufficient here.
15+
if (steps > 0) (valueRange.endInclusive - valueRange.start) / (steps + 1) else Float.MIN_VALUE
1216

1317
@Composable
1418
actual fun Slider(
@@ -17,23 +21,53 @@ actual fun Slider(
1721
modifier: Modifier,
1822
enabled: Boolean,
1923
valueRange: ClosedFloatingPointRange<Float>,
20-
steps: Int,
24+
/*@IntRange(from = 0)*/ steps: Int,
2125
onValueChangeFinished: (() -> Unit)?
2226
) =
2327
MdSlider(
24-
min = valueRange.start,
25-
max = valueRange.endInclusive,
26-
value = value,
27-
step = if (steps > 0) (valueRange.endInclusive - valueRange.start) / (steps + 1) else null,
28+
valueRange.start,
29+
valueRange.endInclusive,
30+
value,
31+
step = calculateStep(steps, valueRange),
32+
disabled = enabled.isFalseOrNull(),
2833
attrs = modifier.toAttrs {
29-
disabled(enabled.isFalseOrNull())
3034
onInput { event ->
3135
// MdSlider uses input event for value changes
36+
//console.log("Slider input event: ", event)
3237
val target = event.target.asDynamic()
33-
val newValue = (target.value as? Number)?.toFloat() ?: (target.value as? String)?.toFloatOrNull()
34-
newValue?.let { onValueChange(it) }
38+
//console.log("`target.value` type: " + jsTypeOf(target.value)) // result: `number`
39+
val value = target.value as Float
40+
onValueChange(value)
3541
}
3642
// Note: onValueChangeFinished is not currently supported in the JS implementation
3743
// TODO: Add support for onValueChangeFinished using appropriate event listener
3844
}
3945
)
46+
47+
@Composable
48+
actual fun RangeSlider(
49+
value: ClosedFloatingPointRange<Float>,
50+
onValueChange: (ClosedFloatingPointRange<Float>) -> Unit,
51+
modifier: Modifier,
52+
enabled: Boolean,
53+
valueRange: ClosedFloatingPointRange<Float>,
54+
/*@IntRange(from = 0)*/ steps: Int,
55+
onValueChangeFinished: (() -> Unit)?
56+
) =
57+
MdSlider(
58+
valueRange.start,
59+
valueRange.endInclusive,
60+
valueStart = value.start,
61+
valueEnd = value.endInclusive,
62+
step = calculateStep(steps, valueRange),
63+
range = true,
64+
disabled = enabled.isFalseOrNull(),
65+
attrs = modifier.toAttrs {
66+
onInput { event ->
67+
val target = event.target.asDynamic()
68+
//console.log("Range slider input event: ", event)
69+
val newStart = target.valueStart as Float
70+
val newEnd = target.valueEnd as Float
71+
onValueChange(newStart..newEnd)
72+
}
73+
})

0 commit comments

Comments
 (0)