Skip to content

Commit

Permalink
Refactor out slider widget
Browse files Browse the repository at this point in the history
  • Loading branch information
DennisTsar committed Jun 15, 2023
1 parent 4e83c02 commit 927c1f2
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 50 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
package io.github.opletter.courseevals.site.core.components.sections.dataPage.options

import androidx.compose.runtime.Composable
import com.varabyte.kobweb.compose.css.Cursor
import com.varabyte.kobweb.compose.foundation.layout.Column
import com.varabyte.kobweb.compose.foundation.layout.Row
import com.varabyte.kobweb.compose.foundation.layout.Spacer
import com.varabyte.kobweb.compose.ui.Alignment
import com.varabyte.kobweb.compose.ui.Modifier
import com.varabyte.kobweb.compose.ui.modifiers.*
import com.varabyte.kobweb.silk.components.icons.fa.FaRotateRight
import com.varabyte.kobweb.compose.ui.modifiers.fontSize
import com.varabyte.kobweb.silk.components.style.toModifier
import com.varabyte.kobweb.silk.components.text.SpanText
import io.github.opletter.courseevals.site.core.components.widgets.LabeledSlider
import io.github.opletter.courseevals.site.core.misc.smallCapsFont
import io.github.opletter.courseevals.site.core.states.MinSemesterVM
import org.jetbrains.compose.web.css.cssRem
import org.jetbrains.compose.web.css.percent
import org.jetbrains.compose.web.dom.RangeInput
import org.jetbrains.compose.web.dom.Text

@Composable
Expand All @@ -26,32 +21,12 @@ fun MinSemOption(state: MinSemesterVM) {
) {
SpanText("Recency Filter", Modifier.fontSize(125.percent).smallCapsFont())
Text("Hide profs with no data since")
Row(
Modifier
.fillMaxWidth()
.columnGap(0.5.cssRem),
verticalAlignment = Alignment.CenterVertically
) {
Spacer()
RangeInput(
value = state.rangeValue,
min = state.bounds.first,
max = state.bounds.second,
attrs = {
// live update range, but only change data on release
onInput { state.setRangeValue(it.value) }
onChange { state.setValue(it.value) }
}
)
if (state.showResetButton) {
FaRotateRight(
Modifier
.flex(1)
.cursor(Cursor.Pointer)
.onClick { state.reset() }
)
} else Spacer() // to keep slider centered
}
Text(state.text)
LabeledSlider(
state.default,
state.bounds,
onRelease = { state.setValue(it) },
onReset = { state.setValue(state.default) },
getText = { state.getText(it) },
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package io.github.opletter.courseevals.site.core.components.widgets

import androidx.compose.runtime.*
import com.varabyte.kobweb.compose.css.*
import com.varabyte.kobweb.compose.foundation.layout.Box
import com.varabyte.kobweb.compose.foundation.layout.BoxScope
import com.varabyte.kobweb.compose.foundation.layout.Column
import com.varabyte.kobweb.compose.ui.Alignment
import com.varabyte.kobweb.compose.ui.Modifier
import com.varabyte.kobweb.compose.ui.modifiers.*
import com.varabyte.kobweb.compose.ui.toAttrs
import com.varabyte.kobweb.silk.components.forms.Button
import com.varabyte.kobweb.silk.components.forms.ButtonStyle
import com.varabyte.kobweb.silk.components.icons.fa.FaRotateRight
import com.varabyte.kobweb.silk.components.style.active
import com.varabyte.kobweb.silk.components.style.addVariant
import com.varabyte.kobweb.silk.components.style.hover
import org.jetbrains.compose.web.css.cssRem
import org.jetbrains.compose.web.css.ms
import org.jetbrains.compose.web.css.px
import org.jetbrains.compose.web.dom.RangeInput
import org.jetbrains.compose.web.dom.Text

val UnstyledButtonVariant by ButtonStyle.addVariant {
base {
Modifier
.color(CSSColor.Unset)
.backgroundColor(BackgroundColor.Unset)
.lineHeight(LineHeight.Unset)
.fontSize(FontSize.Unset)
.padding(0.px)
.borderRadius(0.px)
}
hover {
Modifier.backgroundColor(BackgroundColor.Unset)
}
active {
Modifier.backgroundColor(BackgroundColor.Unset)
}
}

@Composable
fun LabeledSlider(
startValue: Number,
bounds: Pair<Number, Number>,
step: Number = 1,
onRelease: (Number) -> Unit,
modifier: Modifier = Modifier,
onSlide: (Number) -> Unit = {},
onReset: (() -> Unit)? = null,
getText: (Number) -> String = { it.toString() },
resetContent: @Composable BoxScope.() -> Unit = { FaRotateRight() },
) {
var rangeValue by remember { mutableStateOf(startValue) }
Column(modifier, horizontalAlignment = Alignment.CenterHorizontally) {
Slider(
startValue,
bounds,
step,
onRelease,
onSlide = {
rangeValue = it
onSlide(it)
},
onReset = onReset?.let {
{
rangeValue = startValue
it()
}
},
resetContent = resetContent,
modifier = Modifier.fillMaxWidth(),
)
Text(getText(rangeValue))
}
}

@Composable
fun Slider(
startValue: Number,
bounds: Pair<Number, Number>,
step: Number = 1,
onRelease: (Number) -> Unit,
modifier: Modifier = Modifier,
onSlide: (Number) -> Unit = {},
onReset: (() -> Unit)? = null,
resetContent: @Composable BoxScope.() -> Unit = { FaRotateRight() },
) {
var rangeValue by remember { mutableStateOf(startValue) }
var releaseValue by remember { mutableStateOf(startValue) } // used to determine if we should show the reset button
Box(
Modifier
.gridTemplateColumns("1fr auto 1fr")
.columnGap(0.5.cssRem)
.then(modifier),
contentAlignment = Alignment.Center,
) {
RangeInput(
value = rangeValue,
min = bounds.first,
max = bounds.second,
step = step,
attrs = Modifier.gridColumn("2").toAttrs {
onInput {
rangeValue = it.value!!
onSlide(it.value!!)
}
onChange {
releaseValue = rangeValue
onRelease(rangeValue)
}
}
)
ClosableTransitionObject(
open = onReset != null && releaseValue != startValue,
openModifier = Modifier.opacity(1),
closedModifier = Modifier.opacity(0),
) {
Button(
onClick = {
rangeValue = startValue
releaseValue = startValue
onReset!!()
},
Modifier
.gridColumn("3")
.transition(CSSTransition("opacity", 150.ms))
.then(it),
variant = UnstyledButtonVariant,
) {
resetContent()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ class MinSemesterVM(
private val storageKey = "course-evals:$key:minSemester"

val bounds = options.bounds.run { first.numValue to second.numValue }
val default = options.default.numValue

// We use `value` for the actual filter currently applied and `rangeValue` for the value shown on the slider
// as the user drags it. We only want to actually update the filter when the user releases the slider,

var value by mutableStateOf(options.default.numValue)
var value by mutableStateOf(default)
private set

init {
Expand All @@ -35,15 +36,7 @@ class MinSemesterVM(
?.let { value = it }
}

var rangeValue by mutableStateOf(value)
private set

val showResetButton get() = value != options.default.numValue
val text get() = options.builder(rangeValue).toString()

fun setRangeValue(num: Number?) {
num?.let { rangeValue = it.toInt() }
}
fun getText(num: Number) = options.builder(num.toInt()).toString()

fun setValue(num: Number?) {
num?.let {
Expand All @@ -52,10 +45,4 @@ class MinSemesterVM(
refreshState()
}
}

fun reset() {
val default = options.default.numValue
setValue(default)
setRangeValue(default)
}
}

0 comments on commit 927c1f2

Please sign in to comment.