Skip to content

Commit

Permalink
Redesign thread paginator (#1607)
Browse files Browse the repository at this point in the history
* Redesign thread paginator

* Shrink toolbar items instead of overflowing them

* Fix bugs, add third toolbar on thread page
  • Loading branch information
rafalp authored May 23, 2023
1 parent c402855 commit 7c8102c
Show file tree
Hide file tree
Showing 25 changed files with 397 additions and 207 deletions.
18 changes: 15 additions & 3 deletions frontend/src/components/Dropdown/Dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ export default class Dropdown extends React.Component {
window.removeEventListener("click", this.handleClick)
}

componentDidUpdate(prevProps, prevState) {
const didUpdate = prevState.isOpen !== this.state.isOpen
if (didUpdate) {
if (this.state.isOpen && this.props.onOpen) {
this.props.onOpen(this.root)
}

if (!this.state.isOpen && this.props.onClose) {
this.props.onClose(this.root)
}
}
}

handleClick = (event) => {
if (
this.state.isOpen &&
Expand All @@ -43,10 +56,9 @@ export default class Dropdown extends React.Component {

render() {
const { isOpen } = this.state
const RootElement = this.props.listItem ? "li" : "div"

return (
<RootElement
<div
id={this.props.id}
className={classnames(
"dropdown",
Expand Down Expand Up @@ -79,7 +91,7 @@ export default class Dropdown extends React.Component {
>
{this.props.children({ isOpen, close: this.close })}
</div>
</RootElement>
</div>
)
}
}
Expand Down
99 changes: 0 additions & 99 deletions frontend/src/components/thread/ThreadPagination.jsx

This file was deleted.

160 changes: 160 additions & 0 deletions frontend/src/components/thread/ThreadPaginator.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React from "react"
import { Link, withRouter } from "react-router"
import { Dropdown } from "../Dropdown"

const ThreadPaginator = ({ router, baseUrl, posts, scrollToTop }) => (
<div className="thread-paginator">
{posts.isLoaded && posts.first ? (
<Link
className="btn btn-default btn-outline btn-icon"
to={baseUrl}
title={pgettext("paginator", "Go to first page")}
onClick={scrollToTop ? resetScroll : null}
>
<span className="material-icon">first_page</span>
</Link>
) : (
<button
className="btn btn-default btn-outline btn-icon"
title={pgettext("paginator", "Go to first page")}
type="button"
disabled
>
<span className="material-icon">first_page</span>
</button>
)}
{posts.isLoaded && posts.previous ? (
<Link
className="btn btn-default btn-outline btn-icon"
to={baseUrl + (posts.previous > 1 ? posts.previous + "/" : "")}
title={pgettext("paginator", "Go to previous page")}
onClick={scrollToTop ? resetScroll : null}
>
<span className="material-icon">chevron_left</span>
</Link>
) : (
<button
className="btn btn-default btn-outline btn-icon"
title={pgettext("paginator", "Go to previous page")}
type="button"
disabled
>
<span className="material-icon">chevron_left</span>
</button>
)}
<Dropdown
toggle={({ aria, toggle }) => (
<button
{...aria}
className="btn btn-default btn-block btn-outline"
type="button"
disabled={!posts.isLoaded}
onClick={toggle}
>
{getLabel(posts.page, posts.pages)}
</button>
)}
onOpen={(dropdown) => {
dropdown.querySelector("input").focus()
}}
>
{({ close }) => (
<form
className="thread-paginator-form"
onSubmit={(event) => {
if (posts.isLoaded) {
const formData = new FormData(event.target)
const page = parseInt(formData.get("page"))

if (
page &&
page != posts.page &&
page >= 1 &&
page <= posts.pages
) {
const url = page > 1 ? baseUrl + page + "/" : baseUrl
router.push({ pathname: url })
}
}

event.preventDefault()
close()

if (scrollToTop) {
resetScroll()
}
}}
>
<input
className="form-control"
name="page"
type="number"
min={1}
max={posts.pages}
placeholder={pgettext("paginator input", "Page")}
disabled={!posts.isLoaded}
/>
<button
className="btn btn-primary"
type="submit"
disabled={!posts.isLoaded}
>
{pgettext("paginator", "Go")}
</button>
</form>
)}
</Dropdown>
{posts.isLoaded && posts.next ? (
<Link
className="btn btn-default btn-outline btn-icon"
to={baseUrl + posts.next + "/"}
title={pgettext("paginator", "Go to next page")}
onClick={scrollToTop ? resetScroll : null}
>
<span className="material-icon">chevron_right</span>
</Link>
) : (
<button
className="btn btn-default btn-outline btn-icon"
title={pgettext("paginator", "Go to next page")}
type="button"
disabled
>
<span className="material-icon">chevron_right</span>
</button>
)}
{posts.isLoaded && posts.last ? (
<Link
className="btn btn-default btn-outline btn-icon"
to={baseUrl + posts.last + "/"}
title={pgettext("paginator", "Go to last page")}
onClick={scrollToTop ? resetScroll : null}
>
<span className="material-icon">last_page</span>
</Link>
) : (
<button
className="btn btn-default btn-outline btn-icon"
title={pgettext("paginator", "Go to last page")}
type="button"
disabled
>
<span className="material-icon">last_page</span>
</button>
)}
</div>
)

function getLabel(page, pages) {
return pgettext("paginator", "Page %(page)s of %(pages)s")
.replace("%(page)s", page)
.replace("%(pages)s", pages)
}

function resetScroll() {
window.scrollTo(0, 0)
}

const ThreadPaginatorConnected = withRouter(ThreadPaginator)

export default ThreadPaginatorConnected
23 changes: 0 additions & 23 deletions frontend/src/components/thread/ThreadPostsLeft.jsx

This file was deleted.

Loading

0 comments on commit 7c8102c

Please sign in to comment.