Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .idea/appInsightsSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions .idea/migrations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ DataTable(
}
```

Draw a paginated table
Draw a paginated table with fixed size
```kotlin
PaginatedDataTable(
columns = listOf(
Expand All @@ -70,7 +70,7 @@ PaginatedDataTable(
Text("Column3")
},
),
state = rememberPaginatedDataTableState(5),
state = rememberPaginatedDataTableState(initialSize = PageSize.FixedSize(5)),
) {
for (rowIndex in 0 until 100) {
row {
Expand All @@ -88,3 +88,39 @@ PaginatedDataTable(
}
}
```


Draw paginated table with dynamic size (fill available height)

```kotlin
PaginatedDataTable(
columns = listOf(
DataColumn {
Text("Column1")
},
DataColumn {
Text("Column2")
},
DataColumn {
Text("Column3")
},
),
state = rememberPaginatedDataTableState(initialSize = PageSize.FillMaxHeight),
) {
for (rowIndex in 0 until 100) {
row {
onClick = { println("Row clicked: $rowIndex") }
cell {
Text("Row $rowIndex, column 1")
}
cell {
Text("Row $rowIndex, column 2")
}
cell {
Text("Row $rowIndex, column 3")
}
}
}
}
```

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.seanproctor.datatable.material3
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
Expand All @@ -12,15 +13,20 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.seanproctor.datatable.DataColumn
import com.seanproctor.datatable.DataTableScope
import com.seanproctor.datatable.paging.BasicPaginatedDataTable
import com.seanproctor.datatable.paging.PaginatedDataTableState
import com.seanproctor.datatable.paging.rememberPaginatedDataTableState
import com.seanproctor.datatable_material3.generated.resources.Res
import com.seanproctor.datatable_material3.generated.resources.chevron_left
import com.seanproctor.datatable_material3.generated.resources.chevron_right
Expand Down Expand Up @@ -55,15 +61,15 @@ fun PaginatedDataTable(
headerBackgroundColor = headerBackgroundColor,
footerBackgroundColor = footerBackgroundColor,
state = state,
footer = {
footer = { pageSize ->
Row(
modifier = Modifier.height(rowHeight).padding(horizontal = 16.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.End),
verticalAlignment = Alignment.CenterVertically,
) {
val start = min(state.pageIndex * state.pageSize + 1, state.count)
val end = min(start + state.pageSize - 1, state.count)
val pageCount = (state.count + state.pageSize - 1) / state.pageSize
val start = min(state.pageIndex * pageSize + 1, state.count)
val end = min(start + pageSize - 1, state.count)
val pageCount = (state.count + pageSize - 1) / pageSize
Text("$start-$end of ${state.count}")
IconButton(
onClick = { state.pageIndex = 0 },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
package com.seanproctor.datatable.paging

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.seanproctor.datatable.*
import com.seanproctor.datatable.BasicDataTable
import com.seanproctor.datatable.CellContentProvider
import com.seanproctor.datatable.DataColumn
import com.seanproctor.datatable.DataTableScope
import com.seanproctor.datatable.DataTableState
import com.seanproctor.datatable.DefaultCellContentProvider

@Composable
fun BasicPaginatedDataTable(
Expand All @@ -19,34 +32,56 @@ fun BasicPaginatedDataTable(
contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp),
headerBackgroundColor: Color = Color.Unspecified,
footerBackgroundColor: Color = Color.Unspecified,
footer: @Composable () -> Unit = { },
footer: @Composable (Int) -> Unit = { },
cellContentProvider: CellContentProvider = DefaultCellContentProvider,
sortColumnIndex: Int? = null,
sortAscending: Boolean = true,
logger: ((String) -> Unit)? = null,
content: DataTableScope.() -> Unit
) {
BasicDataTable(
columns = columns,
modifier = modifier,
state = remember(state.pageSize, state.pageIndex) { DataTableState() },
separator = separator,
headerHeight = headerHeight,
rowHeight = rowHeight,
contentPadding = contentPadding,
headerBackgroundColor = headerBackgroundColor,
footerBackgroundColor = footerBackgroundColor,
footer = footer,
cellContentProvider = cellContentProvider,
sortColumnIndex = sortColumnIndex,
sortAscending = sortAscending,
logger = logger,
var pageSize by remember { mutableStateOf(state.pageSize) }
val density = LocalDensity.current

Box(
Modifier
.fillMaxSize()
.onGloballyPositioned { coords ->
/**
* The table size is calculated to fit the screen height.
* This is if pageSize is equal to 'PAGE_SIZE_FIXED_FLAG (-1)'
*
* Otherwise, the table has a fixed size
*/
if (state.pageSize == PAGE_SIZE_FIXED_FLAG) {
val heightPx = coords.size.height.toFloat()
val rowHeightPx = with(density) { rowHeight.toPx() }
val rows = (heightPx / rowHeightPx).toInt()
pageSize = rows - 3
}
}
) {
val start = state.pageIndex * state.pageSize
val scope = PaginatedRowScope(start, start + state.pageSize, this)
content(scope)
if (state.count != scope.index) {
state.count = scope.index
BasicDataTable(
columns = columns,
modifier = modifier,
state = remember(pageSize, state.pageIndex) { DataTableState() },
separator = separator,
headerHeight = headerHeight,
rowHeight = rowHeight,
contentPadding = contentPadding,
headerBackgroundColor = headerBackgroundColor,
footerBackgroundColor = footerBackgroundColor,
footer = { footer(pageSize) },
cellContentProvider = cellContentProvider,
sortColumnIndex = sortColumnIndex,
sortAscending = sortAscending,
logger = logger,
) {
val start = state.pageIndex * pageSize
val scope = PaginatedRowScope(start, start + pageSize, this)
content(scope)
if (state.count != scope.index) {
state.count = scope.index
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
package com.seanproctor.datatable.paging

import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue


/**
* PAGE_SIZE_FIXED_FLAG (-1) indicates dynamic page size.
* This is only used internally for automatic calculation.
*/
const val PAGE_SIZE_FIXED_FLAG = -1

interface PaginatedDataTableState {
var pageSize: Int
Expand Down Expand Up @@ -33,11 +44,18 @@ private class PaginatedDataTableStateImpl (

@Composable
fun rememberPaginatedDataTableState(
initialPageSize: Int,
initialSize: PageSize,
initialPageIndex: Int = 0,
initialCount: Int = 0,
): PaginatedDataTableState {
): PaginatedDataTableState {
return rememberSaveable(saver = PaginatedDataTableStateImpl.Saver) {
PaginatedDataTableStateImpl(initialPageSize, initialPageIndex, initialCount)
val pageSizeValue = if (initialSize is PageSize.FixedSize) initialSize.initialPageSize else PAGE_SIZE_FIXED_FLAG
PaginatedDataTableStateImpl(pageSizeValue, initialPageIndex, initialCount)
}
}
}

sealed class PageSize {
data object FillMaxHeight : PageSize()
data class FixedSize(val initialPageSize: Int) : PageSize()
}

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import com.seanproctor.datatable.TableColumnWidth
import com.seanproctor.datatable.material3.DataTable
import com.seanproctor.datatable.material3.LazyPaginatedDataTable
import com.seanproctor.datatable.material3.PaginatedDataTable
import com.seanproctor.datatable.paging.PageSize
import com.seanproctor.datatable.paging.rememberPaginatedDataTableState
import io.github.oikvpqya.compose.fastscroller.HorizontalScrollbar
import io.github.oikvpqya.compose.fastscroller.VerticalScrollbar
Expand Down Expand Up @@ -135,7 +136,7 @@ fun App(onRowClick: (Int) -> Unit) {
1 -> {
PaginatedDataTable(
columns = columns,
state = rememberPaginatedDataTableState(5),
state = rememberPaginatedDataTableState(initialSize = PageSize.FixedSize(2)),
sortColumnIndex = sortColumnIndex,
sortAscending = sortAscending,
modifier = Modifier.fillMaxWidth().padding(16.dp),
Expand All @@ -152,7 +153,7 @@ fun App(onRowClick: (Int) -> Unit) {
else -> {
LazyPaginatedDataTable(
columns = columns,
state = rememberPaginatedDataTableState(initialPageSize = 5, initialCount = sortedData.size),
state = rememberPaginatedDataTableState(initialSize = PageSize.FillMaxHeight, initialCount = sortedData.size),
sortColumnIndex = sortColumnIndex,
sortAscending = sortAscending,
modifier = Modifier.fillMaxWidth().padding(16.dp),
Expand Down