Skip to content

Instantly share code, notes, and snippets.

@nmoinvaz
Last active November 10, 2023 22:33
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nmoinvaz/b54e48969ccad25b047bc9fffcb38def to your computer and use it in GitHub Desktop.
Save nmoinvaz/b54e48969ccad25b047bc9fffcb38def to your computer and use it in GitHub Desktop.
Antd Cursor Paginated Table Component
import React, { useEffect, useState } from "react";
import { Table, Button, Space, Row, Col } from "antd";
import { LeftOutlined, RightOutlined } from "@ant-design/icons";
const CursorPagination = ({ startKey, lastEvaluatedKey, onChange, size }) => {
/* Use stack to keep track of which evaluated keys have been previously seen */
const [ prevEvaluatedKeys, setPrevEvaluatedKeys ] = useState([]);
/* Keep track of the current evaluated key */
const [ currentEvaluatedKey, setCurrentEvaluatedKey ] = useState(null);
/* Button style changes based on size */
const [ buttonStyle, setButtonStyle ] = useState({});
const popEvaluatedKey = () => {
const prevEvaluatedKey = prevEvaluatedKeys.pop();
setCurrentEvaluatedKey(prevEvaluatedKey);
return prevEvaluatedKey;
};
const pushEvaluatedKey = (evaluatedKey) => {
setPrevEvaluatedKeys([...prevEvaluatedKeys, currentEvaluatedKey]);
setCurrentEvaluatedKey(evaluatedKey);
return evaluatedKey;
};
const extra = { action: "paginate" };
useEffect(() => {
switch (size) {
case "small":
setButtonStyle({paddingLeft: "4px", paddingRight: "4px", borderColor: "transparent"});
break;
case "default":
default:
setButtonStyle({paddingLeft: "7px", paddingRight: "7px"});
break;
}
}, [size]);
useEffect(() => {
/* Reset stack of keys when pagination is reset back to the beginning */
if (!startKey) {
setPrevEvaluatedKeys([]);
setCurrentEvaluatedKey(null);
}
}, [startKey]);
/* Display forward and back buttons */
return (
<Space direction="horizontal">
<Button tabIndex={-1} size={size}
disabled={!prevEvaluatedKeys.length && prevEvaluatedKeys.length <= 1}
style={buttonStyle}
onClick={() => onChange(popEvaluatedKey(), undefined, undefined, extra)}>
<LeftOutlined/>
</Button>
<Button tabIndex={-1} size={size}
disabled={!lastEvaluatedKey}
style={buttonStyle}
onClick={() => onChange(pushEvaluatedKey(lastEvaluatedKey), undefined, undefined, extra)}>
<RightOutlined/>
</Button>
</Space>
);
};
export default function CursorTable(props) {
const { children, pagination, dataSource, onChange } = props;
const { position = "bottomRight", size = "default" } = pagination || {};
const [ lastEvaluatedKey, setLastEvaluatedKey ] = useState(null);
const [ verticalPosition, alignmentPosition ] =
position.match(/[A-Z]?[a-z]+/g);
const justifyPosition = {
left: "start",
center: "center",
right: "end"
}[alignmentPosition.toLowerCase()];
useEffect(() => {
/* Get last evaluated key from last item in data source */
const [ lastDataItem ] = (dataSource || []).slice(-1);
setLastEvaluatedKey(lastDataItem?.lastEvaluatedKey);
}, [dataSource]);
return (
<Space direction="vertical" style={{width: "100%"}}>
{verticalPosition === "top" &&
<Row justify={justifyPosition}>
<Col>
<CursorPagination
lastEvaluatedKey={lastEvaluatedKey}
onChange={onChange}
size={size}/>
</Col>
</Row>}
<Table {...props} pagination={{position: ["none", "none"]}}>
{children}
</Table>
{verticalPosition === "bottom" &&
<Row justify={justifyPosition}>
<Col>
<CursorPagination
lastEvaluatedKey={lastEvaluatedKey}
onChange={onChange}
size={size}/>
</Col>
</Row>}
</Space>
);
}
@silicakes
Copy link

You're not using evaluatedKey in your pushEvaluatedKey/popEvaluatedKey, is that intentional?

@nmoinvaz
Copy link
Author

Good catch I have fixed it.

@ruimarques
Copy link

ruimarques commented Feb 15, 2022

You can augment the CursorPagination component with a handy page size select:

import { Select } from 'antd';

const { Option } = Select;

const [pageSize, setPageSize] = useState(defaultPageSize);
const [sizeOptions, setSizeOptions] = useState([10, 20, 50, 100]);
const handleChangePageSize = () => {};

<Select
        defaultValue={pageSize}
        style={{ width: 100 }}
        onChange={handleChangePageSize}
      >
        {sizeOptions.map((option) => (
          <Option key={option} value={option}>
            {option}
          </Option>
        ))}
</Select>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment