@@ -7,6 +7,8 @@ import androidx.compose.foundation.border
77import androidx.compose.foundation.clickable
88import androidx.compose.foundation.combinedClickable
99import androidx.compose.foundation.interaction.MutableInteractionSource
10+ import androidx.compose.foundation.lazy.LazyListState
11+ import androidx.compose.foundation.lazy.grid.LazyGridState
1012import androidx.compose.material.ripple.rememberRipple
1113import androidx.compose.runtime.Composable
1214import androidx.compose.runtime.getValue
@@ -27,6 +29,7 @@ import androidx.compose.ui.graphics.RectangleShape
2729import androidx.compose.ui.graphics.Shape
2830import androidx.compose.ui.graphics.drawscope.ContentDrawScope
2931import androidx.compose.ui.graphics.graphicsLayer
32+ import androidx.compose.ui.graphics.takeOrElse
3033import androidx.compose.ui.input.pointer.pointerInteropFilter
3134import androidx.compose.ui.layout.layout
3235import androidx.compose.ui.layout.onPlaced
@@ -37,6 +40,7 @@ import androidx.compose.ui.unit.DpSize
3740import androidx.compose.ui.unit.dp
3841import androidx.compose.ui.unit.offset
3942import com.getcode.theme.BrandLight
43+ import com.getcode.theme.CodeTheme
4044
4145inline fun Modifier.addIf (
4246 predicate : Boolean ,
@@ -235,4 +239,130 @@ fun Modifier.drawWithGradient(
235239 blendMode = blendMode
236240 )
237241 }
242+ }
243+
244+ private val gradientSize
245+ @Composable get() = CodeTheme .dimens.staticGrid.x8
246+
247+ fun Modifier.verticalScrollStateGradient (
248+ scrollState : LazyListState ,
249+ color : Color = Color .Unspecified ,
250+ showAtStart : Boolean = true,
251+ showAtEnd : Boolean = true,
252+ isLongGradient : Boolean = false,
253+ ): Modifier = composed {
254+ val backgroundColor = color.takeOrElse { CodeTheme .colors.background }
255+ val gradientSizePx =
256+ with (LocalDensity .current) { gradientSize.toPx() } * if (isLongGradient) 1.5f else 1f
257+ this
258+ .addIf(showAtStart && ! scrollState.isScrolledToStart()) {
259+ Modifier .drawWithGradient(
260+ color = backgroundColor,
261+ startY = { gradientSizePx },
262+ endY = { 0f },
263+ )
264+ }
265+ .addIf(showAtEnd && ! scrollState.isScrolledToEnd()) {
266+ Modifier .drawWithGradient(
267+ color = backgroundColor,
268+ startY = { size.height - gradientSizePx },
269+ )
270+ }
271+ }
272+
273+ fun Modifier.horizontalScrollStateGradient (
274+ scrollState : LazyListState ,
275+ color : Color ,
276+ showAtStart : Boolean = true,
277+ showAtEnd : Boolean = true,
278+ ): Modifier = composed {
279+ val gradientSizePx =
280+ with (LocalDensity .current) { gradientSize.toPx() }
281+ this
282+ .addIf(showAtStart && ! scrollState.isScrolledToStart()) {
283+ Modifier .drawWithContent {
284+ drawContent()
285+ drawRect(
286+ brush = Brush .horizontalGradient(
287+ colors = listOf (
288+ color,
289+ Color .Transparent ,
290+ ),
291+ startX = 0f ,
292+ endX = gradientSizePx,
293+ ),
294+ )
295+ }
296+ }
297+ .addIf(showAtEnd && ! scrollState.isScrolledToEnd()) {
298+ Modifier .drawWithContent {
299+ drawContent()
300+ drawRect(
301+ brush = Brush .horizontalGradient(
302+ colors = listOf (
303+ Color .Transparent ,
304+ color,
305+ ),
306+ startX = size.width - gradientSizePx,
307+ endX = size.width,
308+ ),
309+ )
310+ }
311+ }
312+ }
313+
314+ fun Modifier.verticalScrollStateGradient (
315+ scrollState : LazyGridState ,
316+ color : Color ,
317+ showAtStart : Boolean = true,
318+ showAtEnd : Boolean = true,
319+ ): Modifier = composed {
320+ val gradientSizePx =
321+ with (LocalDensity .current) { gradientSize.toPx() }
322+ this
323+ .addIf(showAtStart && ! scrollState.isVerticallyScrolledToStart()) {
324+ Modifier .drawWithGradient(
325+ color = color,
326+ startY = { gradientSizePx },
327+ endY = { 0f },
328+ )
329+ }
330+ .addIf(showAtEnd && ! scrollState.isVerticallyScrolledToEnd()) {
331+ Modifier .drawWithGradient(
332+ color = color,
333+ startY = { size.height - gradientSizePx },
334+ )
335+ }
336+ }
337+
338+ fun LazyListState.isScrolledToEnd (): Boolean {
339+ val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
340+ return lastItem == null ||
341+ (lastItem.index == layoutInfo.totalItemsCount - 1 && lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset)
342+ }
343+
344+ fun LazyListState.isScrolledToStart (): Boolean {
345+ val firstItem = layoutInfo.visibleItemsInfo.firstOrNull()
346+ return firstItem == null || firstItem.offset == 0
347+ }
348+
349+ fun LazyGridState.isVerticallyScrolledToEnd (): Boolean {
350+ val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
351+ return lastItem == null ||
352+ (lastItem.index == layoutInfo.totalItemsCount - 1 && lastItem.size.height + lastItem.offset.y <= layoutInfo.viewportEndOffset)
353+ }
354+
355+ fun LazyGridState.isVerticallyScrolledToStart (): Boolean {
356+ val firstItem = layoutInfo.visibleItemsInfo.firstOrNull()
357+ return firstItem == null || firstItem.offset.y == 0
358+ }
359+
360+ fun Modifier.footerShadow () = this .drawWithContent {
361+ drawContent()
362+ drawRect(
363+ brush = Brush .verticalGradient(
364+ endY = size.height * 0.12f ,
365+ colors = listOf (Color (0x10000000 ), Color .Transparent ),
366+ ),
367+ )
238368}
0 commit comments