Skip to content

Commit

Permalink
Merge pull request #3 from bryanmylee/plugins/usePagination
Browse files Browse the repository at this point in the history
`usePagination` plugin
  • Loading branch information
bryanmylee committed May 12, 2022
2 parents 2a4f3cc + 77359d3 commit 8516861
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 7 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "svelte-headless-table",
"version": "0.2.3",
"version": "0.3.0",
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
Expand Down
1 change: 1 addition & 0 deletions src/lib/plugins/index.ts
Expand Up @@ -8,5 +8,6 @@ export {
} from './useColumnFilters';
export { useColumnOrder } from './useColumnOrder';
export { useHiddenColumns } from './useHiddenColumns';
export { usePagination } from './usePagination';
export { useSortBy } from './useSortBy';
export { useTableFilter } from './useTableFilter';
108 changes: 108 additions & 0 deletions src/lib/plugins/usePagination.ts
@@ -0,0 +1,108 @@
import type { BodyRow } from '$lib/bodyRows';
import type { NewTablePropSet, TablePlugin } from '$lib/types/TablePlugin';
import { derived, writable, type Readable, type Updater, type Writable } from 'svelte/store';

export interface PaginationConfig {
initialPageIndex?: number;
initialPageSize?: number;
}

export interface PaginationState<Item> {
pageSize: Writable<number>;
pageIndex: Writable<number>;
pageCount: Readable<number>;
prePaginatedRows: Readable<BodyRow<Item>[]>;
hasPreviousPage: Readable<boolean>;
hasNextPage: Readable<boolean>;
}

const MIN_PAGE_SIZE = 1;

export const usePageStore = ({ items, initialPageSize, initialPageIndex }: PageStoreConfig) => {
const pageSize = writable(initialPageSize);
const updatePageSize = (fn: Updater<number>) => {
pageSize.update(($pageSize) => {
const newPageSize = fn($pageSize);
return Math.max(newPageSize, MIN_PAGE_SIZE);
});
};
const setPageSize = (newPageSize: number) => updatePageSize(() => newPageSize);

const pageCount = derived([pageSize, items], ([$pageSize, $items]) => {
const $pageCount = Math.ceil($items.length / $pageSize);
pageIndex.update(($pageIndex) => {
if ($pageIndex >= $pageCount) {
return $pageCount - 1;
}
return $pageIndex;
});
return $pageCount;
});

const pageIndex = writable(initialPageIndex);

const hasPreviousPage = derived(pageIndex, ($pageIndex) => {
return $pageIndex > 0;
});
const hasNextPage = derived([pageIndex, pageCount], ([$pageIndex, $pageCount]) => {
return $pageIndex < $pageCount - 1;
});

return {
pageSize: {
subscribe: pageSize.subscribe,
update: updatePageSize,
set: setPageSize,
},
pageCount,
pageIndex,
hasPreviousPage,
hasNextPage,
};
};

export interface PageStoreConfig {
items: Readable<unknown[]>;
initialPageSize?: number;
initialPageIndex?: number;
}

export const usePagination =
<Item>({ initialPageIndex = 0, initialPageSize = 10 }: PaginationConfig = {}): TablePlugin<
Item,
PaginationState<Item>,
Record<string, never>,
NewTablePropSet<never>
> =>
() => {
const prePaginatedRows = writable<BodyRow<Item>[]>([]);
const paginatedRows = writable<BodyRow<Item>[]>([]);
const { pageSize, pageCount, pageIndex, hasPreviousPage, hasNextPage } = usePageStore({
items: prePaginatedRows,
initialPageIndex,
initialPageSize,
});
const pluginState: PaginationState<Item> = {
pageSize,
pageIndex,
prePaginatedRows,
pageCount,
hasPreviousPage,
hasNextPage,
};

const transformRowsFn = derived([pageSize, pageIndex], ([$pageSize, $pageIndex]) => {
return (rows: BodyRow<Item>[]) => {
prePaginatedRows.set(rows);
const startIdx = $pageIndex * $pageSize;
const _paginatedRows = rows.slice(startIdx, startIdx + $pageSize);
paginatedRows.set(_paginatedRows);
return _paginatedRows;
};
});

return {
pluginState,
transformRowsFn,
};
};
20 changes: 16 additions & 4 deletions src/routes/index.svelte
Expand Up @@ -6,11 +6,12 @@
useColumnOrder,
useHiddenColumns,
useSortBy,
useTableFilter,
usePagination,
matchFilter,
numberRangeFilter,
textPrefixFilter,
} from '$lib/plugins';
import { useTableFilter } from '$lib/plugins/useTableFilter';
import { getShuffled } from './_getShuffled';
import { createSamples } from './_createSamples';
import Italic from './_Italic.svelte';
Expand All @@ -20,7 +21,7 @@
import NumberRangeFilter from './_NumberRangeFilter.svelte';
import SelectFilter from './_SelectFilter.svelte';
const data = readable(createSamples(10));
const data = readable(createSamples(100));
const table = createTable(data, {
sort: useSortBy(),
Expand All @@ -32,6 +33,7 @@
initialColumnIdOrder: ['firstName', 'lastName'],
}),
hideColumns: useHiddenColumns(),
page: usePagination(),
});
const columns = table.createColumns([
Expand Down Expand Up @@ -136,12 +138,20 @@
const { filterValue } = pluginStates.tableFilter;
$columnIdOrder = ['firstName', 'lastName'];
const { hiddenColumnIds } = pluginStates.hideColumns;
const { pageIndex, pageCount, pageSize, hasPreviousPage, hasNextPage } = pluginStates.page;
$hiddenColumnIds = ['progress'];
</script>

<h1>svelte-headless-table</h1>

<button on:click={() => ($columnIdOrder = getShuffled($columnIdOrder))}>Shuffle columns</button>
<div>
<button on:click={() => $pageIndex--} disabled={!$hasPreviousPage}>Previous page</button>
{$pageIndex + 1} of {$pageCount}
<button on:click={() => $pageIndex++} disabled={!$hasNextPage}>Next page</button>
<label for="page-size">Page size</label>
<input id="page-size" type="number" min={1} bind:value={$pageSize} />
</div>

<table>
<thead>
Expand Down Expand Up @@ -207,10 +217,12 @@
)}</pre>

<style>
h1,
table {
* {
font-family: sans-serif;
}
pre {
font-family: monospace;
}
table {
border-spacing: 0;
Expand Down

0 comments on commit 8516861

Please sign in to comment.