Merge pull request 'feat(components): revealOnFocus' (#12) from feat/reveal-on-focus into main
Reviewed-on: https://codeberg.org/jlmakiola/floret-kit/pulls/12
This commit is contained in:
commit
0b44d353d2
4 files changed
+65
-3
No files matched your search
@@ -21,6 +21,6 @@ fun OptionalFormSection(visible: Boolean, content: @Composable ColumnScope.() ->
|
||||
enter = expandEnter(),
|
||||
exit = collapseExit(),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth(), content = content)
|
||||
Column(modifier = Modifier.fillMaxWidth().revealOnFocus(), content = content)
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ fun GroupedSurface(
|
||||
position == Position.Top || position == Position.Middle -> Modifier.padding(bottom = 2.dp)
|
||||
else -> Modifier
|
||||
}
|
||||
val base = modifier.fillMaxWidth().then(gap)
|
||||
val base = modifier.fillMaxWidth().then(gap).revealOnFocus()
|
||||
val haptics = LocalHapticFeedback.current
|
||||
when {
|
||||
onLongClick != null -> Surface(
|
||||
|
||||
@@ -91,6 +91,6 @@ fun InlineTextField(
|
||||
innerTextField()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
modifier = modifier.revealOnFocus(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package de.jeanlucmakiola.floret.components
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.imeAnimationTarget
|
||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
|
||||
/**
|
||||
* Scrolls this whole element into view while anything inside it has focus — once
|
||||
* the keyboard has finished opening, and again whenever the element resizes.
|
||||
*
|
||||
* A text field on its own only brings its text line into view, so a card's
|
||||
* padding, its label and whatever appears below the field (suggestions, chips)
|
||||
* stay under the keyboard. [InlineTextField], [GroupedSurface] and
|
||||
* [OptionalFormSection] apply it already; add it to an app's own card or row
|
||||
* that holds an input. Needs a scrolling parent whose viewport shrinks with the
|
||||
* keyboard (`imePadding`).
|
||||
*
|
||||
* The keyboard is only read while focused, so an unfocused element never
|
||||
* recomposes as the keyboard animates.
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
|
||||
fun Modifier.revealOnFocus(): Modifier = composed {
|
||||
val requester = remember { BringIntoViewRequester() }
|
||||
var focused by remember { mutableStateOf(false) }
|
||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||
val density = LocalDensity.current
|
||||
val ime = WindowInsets.ime
|
||||
val imeTarget = WindowInsets.imeAnimationTarget
|
||||
LaunchedEffect(focused) {
|
||||
if (!focused) return@LaunchedEffect
|
||||
// Reveal against the final viewport, not one frame of the keyboard sliding in.
|
||||
snapshotFlow {
|
||||
val now = ime.getBottom(density)
|
||||
if (now == imeTarget.getBottom(density)) now to size else null
|
||||
}
|
||||
.filterNotNull()
|
||||
.distinctUntilChanged()
|
||||
.collect { requester.bringIntoView() }
|
||||
}
|
||||
this
|
||||
.bringIntoViewRequester(requester)
|
||||
.onSizeChanged { size = it }
|
||||
.onFocusChanged { focused = it.hasFocus }
|
||||
}
|
||||
Reference in new issue
Block a user