Skip to content

Commit

Permalink
✨ Add Pagination component
Browse files Browse the repository at this point in the history
  • Loading branch information
Frontendland committed Sep 9, 2024
1 parent ff69141 commit 13cf23c
Show file tree
Hide file tree
Showing 18 changed files with 791 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ import { Accordion } from 'webcoreui/react'
- [List](https://github.com/Frontendland/webcoreui/tree/main/src/components/List)
- [Menu](https://github.com/Frontendland/webcoreui/tree/main/src/components/Menu)
- [Modal](https://github.com/Frontendland/webcoreui/tree/main/src/components/Modal)
- [Pagination](https://github.com/Frontendland/webcoreui/tree/main/src/components/Pagination)
- [Popover](https://github.com/Frontendland/webcoreui/tree/main/src/components/Popover)
- [Progress](https://github.com/Frontendland/webcoreui/tree/main/src/components/Progress)
- [Radio](https://github.com/Frontendland/webcoreui/tree/main/src/components/Radio)
Expand Down
1 change: 1 addition & 0 deletions scripts/buildTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const buildTypes = type => {
'Checkbox',
'Input',
'List',
'Pagination',
'Radio',
'Select',
'Slider',
Expand Down
7 changes: 6 additions & 1 deletion src/components/Button/button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

.button {
@include layout(inline-flex, center, xs);
@include typography(default, primary-50, none);
@include typography(default, primary-50, none, normal);
@include spacing(py-sm, px-md);
@include border-radius(xs);
@include border(0);
Expand Down Expand Up @@ -38,6 +38,11 @@
@include typography(primary);
box-shadow: inset 0px 0px 0px 1px var(--w-color-primary);
}

&[disabled] {
@include typography(primary-30);
box-shadow: inset 0px 0px 0px 1px var(--w-color-primary-30);
}
}

&.flat {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export type ButtonProps = {
}

export type SvelteButtonProps = {
onClick?: () => any
onClick?: (() => any) | null
} & ButtonProps

export type ReactButtonProps = {
onClick?: () => any
onClick?: (() => any) | null
children: React.ReactNode
} & ButtonProps
4 changes: 4 additions & 0 deletions src/components/Icon/map.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Alert from '../../icons/alert.svg?raw'
import ArrowDown from '../../icons/arrow-down.svg?raw'
import ArrowLeft from '../../icons/arrow-left.svg?raw'
import ArrowRight from '../../icons/arrow-right.svg?raw'
import Check from '../../icons/check.svg?raw'
import CircleCheck from '../../icons/circle-check.svg?raw'
import Close from '../../icons/close.svg?raw'
Expand All @@ -13,6 +15,8 @@ import Warning from '../../icons/warning.svg?raw'
const iconMap = {
'alert': Alert,
'arrow-down': ArrowDown,
'arrow-left': ArrowLeft,
'arrow-right': ArrowRight,
'check': Check,
'circle-check': CircleCheck,
'close': Close,
Expand Down
179 changes: 179 additions & 0 deletions src/components/Pagination/Pagination.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
---
import type { PaginationProps } from './pagination'
import Button from '../Button/Button.astro'
import ArrowLeft from '../../icons/arrow-left.svg?raw'
import ArrowRight from '../../icons/arrow-right.svg?raw'
import styles from './pagination.module.scss'
interface Props extends PaginationProps {}
const {
type,
showChevrons,
showDots,
disablePrevious,
disableNext,
previousLink,
nextLink,
previousPageLabel = 'Previous',
nextPageLabel = 'Next',
pages,
theme = 'outline',
totalPages,
currentPage,
className
} = Astro.props
const classes = [
styles.pagination,
theme !== 'outline' && (theme === null ? styles.primary : styles[theme]),
type === 'dots' && styles.dots,
className
]
const calculatedCurrentPage = currentPage
|| (pages?.findIndex(page => page.active) || -1) + 1
|| 1
const calculatedTotalPages = totalPages
|| pages?.length
|| 0
---

<ul
class:list={classes}
data-id="w-pagination"
data-total-pages={calculatedTotalPages}
data-current-page={calculatedCurrentPage}
>
{type === 'dots' ? (
<Fragment>
{pages?.map(page => (
<li>
<button
data-active={page.active ? 'true' : undefined}
data-page={page.label}
/>
</li>
))}
</Fragment>
) : (
<li>
<Button
disabled={disablePrevious || (calculatedCurrentPage === 1 && !previousLink)}
href={previousLink}
theme={theme}
data-page="prev"
>
{(showChevrons || type === 'arrows') && <Fragment set:html={ArrowLeft} />}
{type !== 'arrows' && previousPageLabel}
</Button>
</li>
<Fragment>
{type !== 'arrows' && pages?.slice(0, calculatedTotalPages)?.map((page, index) =>
<li>
<Button
href={page.link}
data-active={calculatedCurrentPage === index + 1 ? 'true' : null}
theme={theme}
data-page={page.label}
>
{page.label}
</Button>
</li>
)}
{showDots && (
<li>
<Button theme={theme} className={styles.inactive}>
...
</Button>
</li>
)}
</Fragment>
<li>
<Button
disabled={disableNext || (calculatedCurrentPage === calculatedTotalPages && !nextLink)}
href={nextLink}
theme={theme}
data-page="next"
>
{type !== 'arrows' && nextPageLabel}
{(showChevrons || type === 'arrows') && <Fragment set:html={ArrowRight} />}
</Button>
</li>
)}
</ul>

<script>
import { dispatch } from '../../utils/event'

const getCurrentPage = (pageElements: Element[], current: number, next: string) => {
if (next === 'prev') {
return current - 1
}

if (next === 'next') {
return current + 1
}

return pageElements?.findIndex(child => {
const button = child.children[0] as HTMLButtonElement

return button.dataset.page === next
}) + 1
}

const paginations = document.querySelectorAll('[data-id="w-pagination"]')

Array.from(paginations).forEach(element => {
const pagination = element as HTMLUListElement
const totalPages = Number(pagination.dataset.totalPages)
let currentPage = Number(pagination.dataset.currentPage)

element.addEventListener('click', event => {
const target = event.target as HTMLButtonElement
const navigated = target.nodeName === 'BUTTON'
&& !target.dataset.active
&& !target.disabled

if (navigated) {
const prevPageButton = element.querySelector('[data-page="prev"]') as HTMLButtonElement
const nextPageButton = element.querySelector('[data-page="next"]') as HTMLButtonElement
const currentPageButton = element.querySelector('[data-active]') as HTMLButtonElement

const pageElements = Array
.from(pagination.children)
.filter(child => {
const button = child.children[0] as HTMLButtonElement

return button.dataset.page && !['prev', 'next'].includes(button.dataset.page)
})

currentPage = getCurrentPage(pageElements, currentPage, target.dataset.page || '')
currentPageButton?.removeAttribute('data-active')

const activeButton = pageElements
.find((_, index) => index + 1 === currentPage)
?.children[0] as HTMLButtonElement

if (activeButton) {
activeButton.dataset.active = 'true'
}

if (prevPageButton && nextPageButton) {
prevPageButton.disabled = currentPage === 1
nextPageButton.disabled = currentPage === totalPages
}

dispatch('paginate', {
page: currentPage,
...(activeButton?.dataset.page && { label: activeButton?.dataset.page }),
element
})
}
})
})
</script>
137 changes: 137 additions & 0 deletions src/components/Pagination/Pagination.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<script lang="ts">
import type { SveltePaginationProps } from './pagination'
import Button from '../Button/Button.svelte'
import { classNames } from '../../utils/classNames'
import ArrowLeft from '../../icons/arrow-left.svg?raw'
import ArrowRight from '../../icons/arrow-right.svg?raw'
import styles from './pagination.module.scss'
export let type: SveltePaginationProps['type'] = null
export let showChevrons: SveltePaginationProps['showChevrons'] = false
export let showDots: SveltePaginationProps['showDots'] = false
export let disablePrevious: SveltePaginationProps['disablePrevious'] = false
export let disableNext: SveltePaginationProps['disableNext'] = false
export let previousLink: SveltePaginationProps['previousLink'] = ''
export let nextLink: SveltePaginationProps['nextLink'] = ''
export let previousPageLabel: SveltePaginationProps['previousPageLabel'] = 'Previous'
export let nextPageLabel: SveltePaginationProps['nextPageLabel'] = 'Next'
export let pages: SveltePaginationProps['pages'] = []
export let theme: SveltePaginationProps['theme'] = 'outline'
export let totalPages: SveltePaginationProps['totalPages'] = null
export let currentPage: SveltePaginationProps['currentPage'] = null
export let className: SveltePaginationProps['className'] = ''
export let onChange: SveltePaginationProps['onChange'] = () => {}
const classes = classNames([
styles.pagination,
theme !== 'outline' && (theme === null || theme === undefined ? styles.primary : styles[theme]),
type === 'dots' && styles.dots,
className
])
const calculatedTotalPages = totalPages
|| pages?.length
|| 0
const paginate = (to: string | number) => {
if (to === 'prev') {
calculatedCurrentPage = calculatedCurrentPage - 1
} else if (to === 'next') {
calculatedCurrentPage = calculatedCurrentPage + 1
} else {
calculatedCurrentPage = to as number
}
const label = pages?.[calculatedCurrentPage - 1]?.label
onChange?.({
page: calculatedCurrentPage,
...(label && { label })
})
}
let calculatedCurrentPage = currentPage
|| (pages?.findIndex(page => page.active) || -1) + 1
|| 1
</script>

<ul class={classes}>
{#if type === 'dots' && pages?.length}
{#each pages as _, index}
<li>
<button
data-active={calculatedCurrentPage === index + 1 ? 'true' : null}
on:click={calculatedCurrentPage !== index + 1
? () => paginate(index + 1)
: null
}
/>
</li>
{/each}
{:else}
<li>
<Button
disabled={disablePrevious || (calculatedCurrentPage === 1 && !previousLink)}
href={previousLink}
theme={theme}
onClick={!(disablePrevious || (calculatedCurrentPage === 1 && !previousLink))
? () => paginate('prev')
: null
}
>
{#if showChevrons || type === 'arrows'}
{@html ArrowLeft}
{/if}
{#if type !== 'arrows'}
{previousPageLabel}
{/if}
</Button>
</li>
{#if type !== 'arrows' && pages?.length}
{#each pages?.slice(0, calculatedTotalPages) as page, index}
<li>
<Button
href={page.link}
data-active={calculatedCurrentPage === index + 1 ? 'true' : null}
theme={theme}
onClick={calculatedCurrentPage !== index + 1
? () => paginate(index + 1)
: null
}
>
{page.label}
</Button>
</li>
{/each}
{/if}
{#if showDots}
<li>
<Button theme={theme} className={styles.inactive}>
...
</Button>
</li>
{/if}
<li>
<Button
disabled={disableNext || calculatedCurrentPage === calculatedTotalPages}
href={nextLink}
theme={theme}
onClick={!disableNext
? () => paginate('next')
: null
}
>
{#if type !== 'arrows'}
{nextPageLabel}
{/if}
{#if showChevrons || type === 'arrows'}
{@html ArrowRight}
{/if}
</Button>
</li>
{/if}
</ul>
Loading

0 comments on commit 13cf23c

Please sign in to comment.