Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Commit

Permalink
Merge fdf3dc4 into 4fe910b
Browse files Browse the repository at this point in the history
  • Loading branch information
plbabin committed Feb 14, 2019
2 parents 4fe910b + fdf3dc4 commit 218a9cb
Show file tree
Hide file tree
Showing 10 changed files with 725 additions and 3 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

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

34 changes: 34 additions & 0 deletions src/components/Pagination/Pagination.css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import styled from '../styled'
import baseStyles from '../../styles/resets/baseStyles.css.js'
import { getColor } from '../../styles/utilities/color'

export const config = {
color: getColor('grey.700'),
}

export const PaginationUI = styled('div')`
${baseStyles};
padding: 0 10px;
min-height: 36px;
display: flex;
align-items: center;
width: 100%;
`

export const NavigationUI = styled('div')`
margin-left: auto;
flex: 0 0 auto;
`

export const InformationUI = styled('div')`
flex: 1 1 100%;
white-space: nowrap;
color: ${getColor('charcoal.200')};
padding: 10px 0;
`

export const RangeUI = styled('span')`
color: ${getColor('charcoal.600')};
font-weight: 600;
`
219 changes: 219 additions & 0 deletions src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import * as React from 'react'
import getValidProps from '@helpscout/react-utils/dist/getValidProps'
import propConnect from '../PropProvider/propConnect'
import { classNames } from '../../utilities/classNames'
import { namespaceComponent } from '../../utilities/component'
import { noop } from '../../utilities/other'
import { COMPONENT_KEY } from './Pagination.utils'
import pluralize from '../../utilities/pluralize'

import {
PaginationUI,
InformationUI,
NavigationUI,
RangeUI,
} from './Pagination.css.js'
import Text from '../Text'
import Icon from '../Icon'
import Button from '../Button'

export interface Props {
activePage: number
className?: string
innerRef: (node: HTMLElement) => void
onChange: (nextPageNumber: number) => void
rangePerPage: number
separator?: string
showNavigation?: boolean
pluralizeSubject?: string
subject?: string
totalItems: number
}

export class Pagination extends React.PureComponent<Props> {
static defaultProps = {
activePage: 1,
innerRef: noop,
onChange: noop,
rangePerPage: 50,
separator: 'of',
showNavigation: true,
totalItems: 0,
}

getNumberOfPages() {
const { rangePerPage, totalItems } = this.props
return Math.ceil(totalItems / rangePerPage)
}

getCurrentPage() {
const { activePage } = this.props
if (activePage < 1) {
return 1
}
return Math.min(this.getNumberOfPages(), Math.round(activePage))
}

getStartRange() {
const { rangePerPage } = this.props
const page = this.getCurrentPage()
return page * rangePerPage - rangePerPage + 1
}

getEndRange() {
const { rangePerPage, totalItems } = this.props
const page = this.getCurrentPage()
return Math.min(page * rangePerPage, totalItems)
}

getSubject() {
const { totalItems, subject = '', pluralizeSubject } = this.props

if (totalItems === 0) return subject
if (pluralizeSubject && totalItems > 1) return pluralizeSubject

return pluralize(subject, totalItems)
}

isNavigationVisible() {
const { showNavigation } = this.props
return showNavigation && this.getNumberOfPages() > 1
}

handleFirstClick = e => {
e.preventDefault()
const { onChange } = this.props
onChange && onChange(1)
}

handlePrevClick = e => {
e.preventDefault()
const { onChange } = this.props
const currentPage = this.getCurrentPage()
if (currentPage > 1) {
onChange && onChange(currentPage - 1)
}
}

handleNextClick = e => {
e.preventDefault()
const { onChange } = this.props
const currentPage = this.getCurrentPage()
if (currentPage < this.getNumberOfPages()) {
onChange && onChange(this.getCurrentPage() + 1)
}
}

handleEndClick = e => {
e.preventDefault()
const { onChange } = this.props
onChange && onChange(this.getNumberOfPages())
}

renderRange() {
return (
<Text className="c-Pagination__range">
<RangeUI>{this.getStartRange()}</RangeUI>
{` `}-{` `}
<RangeUI>{this.getEndRange()}</RangeUI>
</Text>
)
}

renderTotal() {
const { totalItems, subject } = this.props
return (
<Text>
<span className="c-Pagination__total">{totalItems}</span>
{subject && (
<span className="c-Pagination__subject">{` ${this.getSubject()}`}</span>
)}
</Text>
)
}

renderNavigation() {
const currentPage = this.getCurrentPage()
const isNotFirstPage = currentPage > 1
const isLastPage = currentPage >= this.getNumberOfPages()

return (
<NavigationUI>
{isNotFirstPage && [
<Button
key="firstButton"
version={2}
onClick={this.handleFirstClick}
className="c-Pagination__firstButton"
>
<Icon name="arrow-left-double-large" size="24" center />
</Button>,
<Button
key="prevButton"
version={2}
onClick={this.handlePrevClick}
className="c-Pagination__prevButton"
>
<Icon name="arrow-left-single-large" size="24" center />
</Button>,
]}

<Button
version={2}
disabled={isLastPage}
onClick={this.handleNextClick}
className="c-Pagination__nextButton"
>
<Icon name="arrow-right-single-large" size="24" center />
</Button>
<Button
version={2}
disabled={isLastPage}
onClick={this.handleEndClick}
className="c-Pagination__lastButton"
>
<Icon name="arrow-right-double-large" size="24" center />
</Button>
</NavigationUI>
)
}

render() {
const {
children,
className,
innerRef,
onChange,
separator,
showNavigation,
...rest
} = this.props

const componentClassName = classNames('c-Pagination', className)

return (
<PaginationUI
aria-label="Pagination"
{...getValidProps(rest)}
className={componentClassName}
innerRef={innerRef}
>
<InformationUI>
<Text size={13}>
{this.renderRange()}
{` `}
{separator}
{` `}
{this.renderTotal()}
</Text>
</InformationUI>
{this.isNavigationVisible() && this.renderNavigation()}
</PaginationUI>
)
}
}

namespaceComponent(COMPONENT_KEY)(Pagination)
const PropConnectedComponent = propConnect(COMPONENT_KEY)(Pagination)

export default PropConnectedComponent
1 change: 1 addition & 0 deletions src/components/Pagination/Pagination.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const COMPONENT_KEY = 'Pagination'
22 changes: 22 additions & 0 deletions src/components/Pagination/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Pagination

A Pagination component listing the current items visible for a given range. It can also add a navigation that will let a user update the active page based on the total of items

## Example

```jsx
<Pagination activePage={1} totalItems={250} />
```

## Props

| Prop | Type | Description |
| ---------------- | ---------- | ------------------------------------------------------------------ |
| activePage | `integer` | Current selected page |
| className | `string` | Custom class names to be added to the component. |
| onChange | `function` | Callback when current page is changed. |
| rangePerPage | `integer` | Number of items per page |
| showNavigation | `bool` | Add a navigation to the component |
| subject | `string` | Pagination label after the range |
| pluralizeSubject | `string` | Pluralize subject. If empty subject will be automaticaly pluralize |
| totalItems | `integer` | Total of items |

0 comments on commit 218a9cb

Please sign in to comment.