Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Table): add pagination feature #2654

Draft
wants to merge 4 commits into
base: v3
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions playground/app/pages/components/table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ onMounted(() => {
:columns="columns"
:column-pinning="columnPinning"
:loading="loading"
:pagination="{ pageSize: 10, pageIndex: 0 }"
sticky
class="border border-[var(--ui-border-accented)] rounded-[var(--ui-radius)] flex-1"
>
Expand Down
43 changes: 40 additions & 3 deletions src/runtime/components/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import type {
ExpandedOptions,
SortingOptions,
RowSelectionOptions,
PaginationOptions,
Updater,
CellContext,
HeaderContext
HeaderContext,
PaginationState
} from '@tanstack/vue-table'
import _appConfig from '#build/app.config'
import theme from '#build/ui/table'
Expand Down Expand Up @@ -86,6 +88,11 @@ export interface TableProps<T> {
* @link [Guide](https://tanstack.com/table/v8/docs/guide/row-selection)
*/
rowSelectionOptions?: Omit<RowSelectionOptions<T>, 'onRowSelectionChange'>
/**
* @link [API Docs](https://tanstack.com/table/v8/docs/api/features/pagination#table-options)
* @link [Guide](https://tanstack.com/table/v8/docs/guide/pagination)
*/
paginationOptions?: Omit<PaginationOptions, 'onPaginationChange'>
class?: any
ui?: Partial<typeof table.slots>
}
Expand All @@ -97,22 +104,25 @@ export type TableSlots<T> = {
expanded: (props: { row: Row<T> }) => any
empty: (props?: {}) => any
caption: (props?: {}) => any
pagination: (props?: {}) => any
} & DynamicHeaderSlots<T> & DynamicCellSlots<T>

</script>

<script setup lang="ts" generic="T extends TableData">
import { computed } from 'vue'
import { computed, ref, watch } from 'vue'
import {
FlexRender,
getCoreRowModel,
getFilteredRowModel,
getSortedRowModel,
getExpandedRowModel,
useVueTable
useVueTable,
getPaginationRowModel
} from '@tanstack/vue-table'
import { upperFirst } from 'scule'
import { useLocale } from '../composables/useLocale'
import UPagination from './Pagination.vue'

const props = defineProps<TableProps<T>>()
defineSlots<TableSlots<T>>()
Expand All @@ -128,13 +138,23 @@ const ui = computed(() => table({
loadingAnimation: props.loadingAnimation
}))

// workaround to disable pagination when no pagination options are provided because passing
// getPaginationRowModel to useVueTable will automatically enable pagination
const paginationStateOverride = computed(() => !paginationState.value && !props.paginationOptions
? {
pageSize: data.value.length,
pageIndex: 0
}
: paginationState.value)

const globalFilterState = defineModel<string>('globalFilter', { default: undefined })
const columnFiltersState = defineModel<ColumnFiltersState>('columnFilters', { default: [] })
const columnVisibilityState = defineModel<VisibilityState>('columnVisibility', { default: {} })
const columnPinningState = defineModel<ColumnPinningState>('columnPinning', { default: {} })
const rowSelectionState = defineModel<RowSelectionState>('rowSelection', { default: {} })
const sortingState = defineModel<SortingState>('sorting', { default: [] })
const expandedState = defineModel<ExpandedState>('expanded', { default: {} })
const paginationState = defineModel<PaginationState>('pagination', { default: undefined })

const tableApi = useVueTable({
data,
Expand All @@ -157,6 +177,9 @@ const tableApi = useVueTable({
...(props.expandedOptions || {}),
getExpandedRowModel: getExpandedRowModel(),
onExpandedChange: updaterOrValue => valueUpdater(updaterOrValue, expandedState),
getPaginationRowModel: getPaginationRowModel(),
...(props.paginationOptions || {}),
onPaginationChange: updaterOrValue => valueUpdater(updaterOrValue, paginationState),
state: {
get globalFilter() {
return globalFilterState.value
Expand All @@ -178,10 +201,19 @@ const tableApi = useVueTable({
},
get sorting() {
return sortingState.value
},
get pagination() {
return paginationStateOverride.value
}
}
})

const page = ref(1)

watch(page, () => {
tableApi.setPageIndex(page.value - 1)
})

function valueUpdater<T extends Updater<any>>(updaterOrValue: T, ref: Ref) {
ref.value = typeof updaterOrValue === 'function' ? updaterOrValue(ref.value) : updaterOrValue
}
Expand Down Expand Up @@ -247,5 +279,10 @@ defineExpose({
</tr>
</tbody>
</table>
<slot name="pagination">
<div :class="ui.pagination({ class: [props.ui?.pagination] })">
<UPagination v-if="tableApi.getPageCount() > 1" v-model:page="page" :items-per-page="1" :total="tableApi.getPageCount()" />
</div>
</slot>
</div>
</template>
3 changes: 2 additions & 1 deletion src/theme/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export default (options: Required<ModuleOptions>) => ({
tr: 'data-[selected=true]:bg-[var(--ui-bg-elevated)]/50',
th: 'px-4 py-3.5 text-sm text-[var(--ui-text-highlighted)] text-left rtl:text-right font-semibold [&:has([role=checkbox])]:pe-0',
td: 'p-4 text-sm text-[var(--ui-text-muted)] whitespace-nowrap [&:has([role=checkbox])]:pe-0',
empty: 'py-6 text-center text-sm text-[var(--ui-text-muted)]'
empty: 'py-6 text-center text-sm text-[var(--ui-text-muted)]',
pagination: 'flex justify-center items-center py-4'
},
variants: {
pinned: {
Expand Down