@@ -9,16 +9,20 @@ import xyz.junerver.compose.hooks.useEffect
99import xyz.junerver.compose.hooks.useLatestRef
1010import xyz.junerver.compose.hooks.userequest.utils.CachedData
1111import xyz.junerver.compose.hooks.utils.CacheManager
12+ import xyz.junerver.compose.hooks.usetable.state.SortDescriptor
1213
1314/* *
1415 * Paged request parameters.
1516 *
1617 * @param page Page index (0-based)
1718 * @param pageSize Number of items per page
1819 */
19- data class PageParams (
20+ data class TableRequestParams (
2021 val page : Int = 0 ,
21- val pageSize : Int = 10
22+ val pageSize : Int = 10 ,
23+ val sorting : List <SortDescriptor > = emptyList(),
24+ val globalFilter : String = " " ,
25+ val columnFilters : Map <String , Any ?> = emptyMap()
2226)
2327
2428/* *
@@ -41,6 +45,13 @@ data class TableResult<T>(
4145 * Options for useTableRequest hook.
4246 */
4347class UseTableRequestOptions <TData : Any > {
48+ var initialSorting: List <SortDescriptor > = emptyList()
49+ var initialGlobalFilter: String = " "
50+ var initialColumnFilters: Map <String , Any ?> = emptyMap()
51+
52+ var triggerOnSortingChange: Boolean = true
53+ var triggerOnFilteringChange: Boolean = true
54+
4455 /* *
4556 * Initial page number (default: 0)
4657 */
@@ -54,7 +65,11 @@ class UseTableRequestOptions<TData : Any> {
5465 /* *
5566 * All useRequest options (cacheKey, staleTime, onSuccess, etc.)
5667 */
57- var requestOptions: UseRequestOptions <PageParams , TData >.() -> Unit = {}
68+ var requestOptions: UseRequestOptions <TableRequestParams , TData >.() -> Unit = {}
69+ var mergeCacheKey: ((baseKey: String , params: TableRequestParams ) -> String )? = null
70+ var setCache: ((data: CachedData <TData >) -> Unit )? = null
71+ var getCache: ((params: TableRequestParams ) -> CachedData <TData >? )? = null
72+ var onRequestParams: ((TableRequestParams ) -> Unit )? = null
5873}
5974
6075/* *
@@ -75,12 +90,23 @@ data class TableRequestHolder<T>(
7590 val pageSize : State <Int >,
7691 val total : State <Int >,
7792
93+ // Sorting & filtering states
94+ val sorting : State <List <SortDescriptor >>,
95+ val globalFilter : State <String >,
96+ val columnFilters : State <Map <String , Any ?>>,
97+
7898 // Request controls
7999 val refresh : () -> Unit ,
80100 val cancel : () -> Unit ,
81101
82102 // Pagination controls
83- val onPageChange : (page: Int , pageSize: Int ) -> Unit
103+ val onPageChange : (page: Int , pageSize: Int ) -> Unit ,
104+
105+ // Sorting & filtering controls
106+ val setSorting : (List <SortDescriptor >) -> Unit ,
107+ val setGlobalFilter : (String ) -> Unit ,
108+ val setColumnFilter : (String , Any? ) -> Unit ,
109+ val clearFilters : () -> Unit
84110)
85111
86112/* *
@@ -92,7 +118,7 @@ data class TableRequestHolder<T>(
92118 *
93119 * ## Key Design Principles:
94120 * 1. **Standard Container**: TableResult<T> abstracts away API response differences
95- * 2. **Fixed Function Signature**: (page, pageSize) -> TableResult<T>
121+ * 2. **Fixed Function Signature**: TableRequestParams -> TableResult<T>
96122 * 3. **User Does Mapping**: Convert API response to TableResult in requestFn
97123 * 4. **Avoid Unnecessary Recompositions**: rows wrapped in State
98124 *
@@ -103,8 +129,8 @@ data class TableRequestHolder<T>(
103129 *
104130 * // Use the hook - map API response to TableResult
105131 * val tableRequest = useTableRequest<User>(
106- * requestFn = { page, pageSize ->
107- * val response = api.getUsers(page, pageSize)
132+ * requestFn = { params ->
133+ * val response = api.getUsers(params. page, params. pageSize)
108134 * // Map your API format to TableResult (plain data class)
109135 * TableResult(
110136 * rows = response.items,
@@ -138,12 +164,12 @@ data class TableRequestHolder<T>(
138164 * ```
139165 *
140166 * @param T Row data type
141- * @param requestFn Async function: (page, pageSize ) -> TableResult<T>
167+ * @param requestFn Async function: (params ) -> TableResult<T>
142168 * @param optionsOf Configuration DSL
143169 */
144170@Composable
145171fun <T > useTableRequest (
146- requestFn : suspend (page: Int , pageSize: Int ) -> TableResult <T >,
172+ requestFn : suspend (params: TableRequestParams ) -> TableResult <T >,
147173 optionsOf : UseTableRequestOptions <TableResult <T >>.() -> Unit = {}
148174): TableRequestHolder <T > {
149175 val options = UseTableRequestOptions <TableResult <T >>().apply (optionsOf)
@@ -154,45 +180,104 @@ fun <T> useTableRequest(
154180 val currentPage = pageState.value
155181 val currentPageSize = pageSizeState.value
156182
157- // 2. Use refs for latest pagination values
183+ // 2. Sorting & filtering state
184+ val sortingState = _useState (options.initialSorting)
185+ val globalFilterState = _useState (options.initialGlobalFilter)
186+ val columnFiltersState = _useState (options.initialColumnFilters)
187+
188+ val currentSorting = sortingState.value
189+ val currentGlobalFilter = globalFilterState.value
190+ val currentColumnFilters = columnFiltersState.value
191+
192+ val sortingDeps = if (options.triggerOnSortingChange) currentSorting else null
193+ val globalFilterDep = if (options.triggerOnFilteringChange) currentGlobalFilter else null
194+ val columnFiltersDep = if (options.triggerOnFilteringChange) currentColumnFilters else null
195+
196+ // 3. Use refs for latest values
158197 val latestPage = useLatestRef(currentPage)
159198 val latestPageSize = useLatestRef(currentPageSize)
199+ val latestSorting = useLatestRef(currentSorting)
200+ val latestGlobalFilter = useLatestRef(currentGlobalFilter)
201+ val latestColumnFilters = useLatestRef(currentColumnFilters)
202+
203+ val requestParams = TableRequestParams (
204+ page = latestPage.current,
205+ pageSize = latestPageSize.current,
206+ sorting = latestSorting.current,
207+ globalFilter = latestGlobalFilter.current,
208+ columnFilters = latestColumnFilters.current
209+ )
210+
211+ val requestParamsState = _useState (requestParams)
160212
161- // 3 . Use useRequest with manual mode
162- val requestHolder = useRequest<PageParams , TableResult <T >>(
163- requestFn = { params -> requestFn(params.page, params.pageSize) } ,
213+ // 4 . Use useRequest with manual mode (force manual to avoid duplicate auto-run)
214+ val requestHolder = useRequest<TableRequestParams , TableResult <T >>(
215+ requestFn = requestFn,
164216 optionsOf = {
165- manual = true
166- defaultParams = PageParams (latestPage.current, latestPageSize.current)
167217 options.requestOptions(this )
168- // Per-page caching: if user set cacheKey, use custom cache functions
169- // to generate unique keys for each page
218+ manual = true
219+ defaultParams = requestParamsState.value
220+
221+ val customTableSetCache = options.setCache
222+ val customTableGetCache = options.getCache
223+ if (customTableSetCache != null ) {
224+ setCache = { cachedData ->
225+ customTableSetCache(cachedData)
226+ }
227+ }
228+ if (customTableGetCache != null ) {
229+ getCache = { params ->
230+ customTableGetCache(params)
231+ }
232+ }
233+
170234 if (cacheKey.isNotEmpty()) {
171235 val baseCacheKey = cacheKey
236+ val mergeFullKey = options.mergeCacheKey
237+ val resolveKey: (TableRequestParams ) -> String = { params ->
238+ mergeFullKey?.invoke(baseCacheKey, params)
239+ ? : " ${baseCacheKey} _p${params.page} _s${params.pageSize} "
240+ }
241+ val fallbackSetCache = setCache
242+ val fallbackGetCache = getCache
172243 setCache = { cachedData ->
173- val params = cachedData.params as ? PageParams
244+ val params = cachedData.params as ? TableRequestParams
174245 val key = if (params != null ) {
175- " ${baseCacheKey} _p ${ params.page} _s ${params.pageSize} "
246+ resolveKey( params)
176247 } else {
177- baseCacheKey
248+ resolveKey(requestParamsState.value)
178249 }
179250 CacheManager .saveCache(key, cacheTime, cachedData)
251+ fallbackSetCache?.invoke(cachedData)
180252 }
181253 getCache = { params ->
182- val key = " ${baseCacheKey} _p ${ params.page} _s ${params.pageSize} "
254+ val key = resolveKey( params)
183255 CacheManager .getCache<TableResult <T >>(key)
256+ ? : fallbackGetCache?.invoke(params)
184257 }
185258 }
186259 }
187260 )
188261
189- // 4. Auto-fetch when pagination changes
190- useEffect(currentPage, currentPageSize) {
191- val pageParams = PageParams (latestPage.current, latestPageSize.current)
192- requestHolder.request(pageParams)
262+ val refresh: () -> Unit = {
263+ requestHolder.request(requestParamsState.value)
193264 }
194265
195- // 5. Extract rows and total from TableResult (now plain data, wrap in State at Holder level)
266+ // 5. Auto-fetch when pagination/sorting/filtering changes
267+ useEffect(currentPage, currentPageSize, sortingDeps, globalFilterDep, columnFiltersDep) {
268+ val newParams = TableRequestParams (
269+ page = latestPage.current,
270+ pageSize = latestPageSize.current,
271+ sorting = latestSorting.current,
272+ globalFilter = latestGlobalFilter.current,
273+ columnFilters = latestColumnFilters.current
274+ )
275+ requestParamsState.value = newParams
276+ options.onRequestParams?.invoke(newParams)
277+ requestHolder.request(newParams)
278+ }
279+
280+ // 6. Extract rows and total from TableResult (now plain data, wrap in State at Holder level)
196281 val rows = remember(requestHolder.data.value) {
197282 derivedStateOf {
198283 requestHolder.data.value?.rows ? : emptyList()
@@ -205,7 +290,7 @@ fun <T> useTableRequest(
205290 }
206291 }
207292
208- // 6 . Pagination controls
293+ // 7 . Pagination controls
209294 val onPageChange: (Int , Int ) -> Unit = { newPage, newSize ->
210295 if (newSize != currentPageSize) {
211296 pageSizeState.value = newSize
@@ -215,23 +300,63 @@ fun <T> useTableRequest(
215300 }
216301 }
217302
218- // 7. Return holder
303+ // 8. Sorting & filtering controls
304+ val setSorting: (List <SortDescriptor >) -> Unit = { sorting ->
305+ sortingState.value = sorting
306+ }
307+
308+ val setGlobalFilter: (String ) -> Unit = { filter ->
309+ globalFilterState.value = filter
310+ }
311+
312+ val setColumnFilter: (String , Any? ) -> Unit = { columnId, value ->
313+ columnFiltersState.value = columnFiltersState.value + (columnId to value)
314+ }
315+
316+ val clearFilters: () -> Unit = {
317+ columnFiltersState.value = emptyMap()
318+ globalFilterState.value = " "
319+ }
320+
321+ // 9. Return holder
219322 return TableRequestHolder (
220323 rows = rows,
221324 isLoading = requestHolder.isLoading,
222325 error = requestHolder.error,
223- refresh = requestHolder.refresh,
224- cancel = requestHolder.cancel,
225326 currentPage = pageState,
226327 pageSize = pageSizeState,
227328 total = total,
228- onPageChange = onPageChange
329+ sorting = sortingState,
330+ globalFilter = globalFilterState,
331+ columnFilters = columnFiltersState,
332+ refresh = refresh,
333+ cancel = requestHolder.cancel,
334+ onPageChange = onPageChange,
335+ setSorting = setSorting,
336+ setGlobalFilter = setGlobalFilter,
337+ setColumnFilter = setColumnFilter,
338+ clearFilters = clearFilters
229339 )
230340}
231341
232342/* *
233343 * Alias for [useTableRequest] following Compose naming convention.
234344 */
345+ @Composable
346+ fun <T > rememberTableRequest (
347+ requestFn : suspend (params: TableRequestParams ) -> TableResult <T >,
348+ optionsOf : UseTableRequestOptions <TableResult <T >>.() -> Unit = {}
349+ ): TableRequestHolder <T > = useTableRequest(requestFn, optionsOf)
350+
351+ @Composable
352+ fun <T > useTableRequest (
353+ requestFn : suspend (page: Int , pageSize: Int ) -> TableResult <T >,
354+ optionsOf : UseTableRequestOptions <TableResult <T >>.() -> Unit = {}
355+ ): TableRequestHolder <T > = useTableRequest(
356+ requestFn = { params: TableRequestParams -> requestFn(params.page, params.pageSize) },
357+ optionsOf = optionsOf
358+ )
359+
235360@Composable
236361fun <T > rememberTableRequest (
237362 requestFn : suspend (page: Int , pageSize: Int ) -> TableResult <T >,
0 commit comments