feat: add responsive spacing and padding system (Closes #3)
This commit was merged in pull request #42.
This commit is contained in:
@@ -122,6 +122,9 @@ private fun AppContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
|
val windowSizeClass = LocalWindowSizeClass.current
|
||||||
|
val contentPadding = UiConstants.Spacing.large(windowSizeClass)
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -133,7 +136,7 @@ private fun AppContent(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.widthIn(max = UiConstants.MAX_CONTENT_WIDTH)
|
.widthIn(max = UiConstants.MAX_CONTENT_WIDTH)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp),
|
.padding(contentPadding),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
AttributesPage(character)
|
AttributesPage(character)
|
||||||
|
|||||||
@@ -1,10 +1,46 @@
|
|||||||
package org.shahondin1624.lib.components
|
package org.shahondin1624.lib.components
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.shahondin1624.theme.LocalWindowSizeClass
|
||||||
|
import org.shahondin1624.theme.WindowSizeClass
|
||||||
|
|
||||||
object UiConstants {
|
object UiConstants {
|
||||||
val SMALL_PADDING = 6.dp
|
val SMALL_PADDING = 6.dp
|
||||||
|
|
||||||
/** Maximum width for the main content area to prevent uncomfortable stretching on ultra-wide displays. */
|
/** Maximum width for the main content area to prevent uncomfortable stretching on ultra-wide displays. */
|
||||||
val MAX_CONTENT_WIDTH = 1200.dp
|
val MAX_CONTENT_WIDTH = 1200.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Responsive spacing tokens that scale with [WindowSizeClass].
|
||||||
|
*
|
||||||
|
* | Token | Compact | Medium | Expanded |
|
||||||
|
* |---------|---------|--------|----------|
|
||||||
|
* | small | 8 dp | 12 dp | 16 dp |
|
||||||
|
* | medium | 12 dp | 16 dp | 20 dp |
|
||||||
|
* | large | 12 dp | 16 dp | 24 dp |
|
||||||
|
*/
|
||||||
|
object Spacing {
|
||||||
|
/** Small spacing/gap (grid spacing, minor gaps). */
|
||||||
|
fun small(sizeClass: WindowSizeClass): Dp = when (sizeClass) {
|
||||||
|
WindowSizeClass.Compact -> 8.dp
|
||||||
|
WindowSizeClass.Medium -> 12.dp
|
||||||
|
WindowSizeClass.Expanded -> 16.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Medium spacing (content padding, section gaps). */
|
||||||
|
fun medium(sizeClass: WindowSizeClass): Dp = when (sizeClass) {
|
||||||
|
WindowSizeClass.Compact -> 12.dp
|
||||||
|
WindowSizeClass.Medium -> 16.dp
|
||||||
|
WindowSizeClass.Expanded -> 20.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Large spacing (outer page padding, major section dividers). */
|
||||||
|
fun large(sizeClass: WindowSizeClass): Dp = when (sizeClass) {
|
||||||
|
WindowSizeClass.Compact -> 12.dp
|
||||||
|
WindowSizeClass.Medium -> 16.dp
|
||||||
|
WindowSizeClass.Expanded -> 24.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,19 +15,25 @@ import androidx.compose.material3.HorizontalDivider
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.shahondin1624.lib.components.UiConstants
|
||||||
import org.shahondin1624.model.charactermodel.ShadowrunCharacter
|
import org.shahondin1624.model.charactermodel.ShadowrunCharacter
|
||||||
|
import org.shahondin1624.theme.LocalWindowSizeClass
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ColumnScope.AttributesPage(character: ShadowrunCharacter) {
|
fun ColumnScope.AttributesPage(character: ShadowrunCharacter) {
|
||||||
Box(modifier = Modifier.fillMaxWidth().weight(1f)) {
|
Box(modifier = Modifier.fillMaxWidth().weight(1f)) {
|
||||||
val gridState = rememberLazyGridState()
|
val gridState = rememberLazyGridState()
|
||||||
|
val windowSizeClass = LocalWindowSizeClass.current
|
||||||
|
val gridSpacing = UiConstants.Spacing.small(windowSizeClass)
|
||||||
|
val gridPadding = UiConstants.Spacing.medium(windowSizeClass)
|
||||||
|
val dividerPadding = UiConstants.Spacing.medium(windowSizeClass)
|
||||||
|
|
||||||
LazyVerticalGrid(
|
LazyVerticalGrid(
|
||||||
columns = GridCells.Adaptive(minSize = 75.dp),
|
columns = GridCells.Adaptive(minSize = 75.dp),
|
||||||
state = gridState,
|
state = gridState,
|
||||||
modifier = Modifier.fillMaxSize().padding(16.dp),
|
modifier = Modifier.fillMaxSize().padding(gridPadding),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(gridSpacing),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(gridSpacing)
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = character.attributes.getAllAttributes(),
|
items = character.attributes.getAllAttributes(),
|
||||||
@@ -39,7 +45,7 @@ fun ColumnScope.AttributesPage(character: ShadowrunCharacter) {
|
|||||||
|
|
||||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.padding(vertical = 16.dp),
|
modifier = Modifier.padding(vertical = dividerPadding),
|
||||||
thickness = 2.dp
|
thickness = 2.dp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package org.shahondin1624
|
||||||
|
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.shahondin1624.lib.components.UiConstants
|
||||||
|
import org.shahondin1624.theme.WindowSizeClass
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class SpacingTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun smallSpacingScalesWithSizeClass() {
|
||||||
|
val compact = UiConstants.Spacing.small(WindowSizeClass.Compact)
|
||||||
|
val medium = UiConstants.Spacing.small(WindowSizeClass.Medium)
|
||||||
|
val expanded = UiConstants.Spacing.small(WindowSizeClass.Expanded)
|
||||||
|
|
||||||
|
assertEquals(8.dp, compact)
|
||||||
|
assertEquals(12.dp, medium)
|
||||||
|
assertEquals(16.dp, expanded)
|
||||||
|
assertTrue(compact < medium, "Compact small should be less than Medium small")
|
||||||
|
assertTrue(medium < expanded, "Medium small should be less than Expanded small")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mediumSpacingScalesWithSizeClass() {
|
||||||
|
val compact = UiConstants.Spacing.medium(WindowSizeClass.Compact)
|
||||||
|
val medium = UiConstants.Spacing.medium(WindowSizeClass.Medium)
|
||||||
|
val expanded = UiConstants.Spacing.medium(WindowSizeClass.Expanded)
|
||||||
|
|
||||||
|
assertEquals(12.dp, compact)
|
||||||
|
assertEquals(16.dp, medium)
|
||||||
|
assertEquals(20.dp, expanded)
|
||||||
|
assertTrue(compact < medium)
|
||||||
|
assertTrue(medium < expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun largeSpacingScalesWithSizeClass() {
|
||||||
|
val compact = UiConstants.Spacing.large(WindowSizeClass.Compact)
|
||||||
|
val medium = UiConstants.Spacing.large(WindowSizeClass.Medium)
|
||||||
|
val expanded = UiConstants.Spacing.large(WindowSizeClass.Expanded)
|
||||||
|
|
||||||
|
assertEquals(12.dp, compact)
|
||||||
|
assertEquals(16.dp, medium)
|
||||||
|
assertEquals(24.dp, expanded)
|
||||||
|
assertTrue(compact < medium)
|
||||||
|
assertTrue(medium < expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun compactSpacingIsWithinRange() {
|
||||||
|
val small = UiConstants.Spacing.small(WindowSizeClass.Compact)
|
||||||
|
val med = UiConstants.Spacing.medium(WindowSizeClass.Compact)
|
||||||
|
val large = UiConstants.Spacing.large(WindowSizeClass.Compact)
|
||||||
|
assertTrue(small >= 8.dp && small <= 12.dp, "Compact small in 8-12dp range")
|
||||||
|
assertTrue(med >= 8.dp && med <= 12.dp, "Compact medium in 8-12dp range")
|
||||||
|
assertTrue(large >= 8.dp && large <= 12.dp, "Compact large in 8-12dp range")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user