Skip to content

Commit

Permalink
Extract ticky filter list logic to a re-usable class
Browse files Browse the repository at this point in the history
  • Loading branch information
holly-cummins committed Nov 7, 2024
1 parent 650a38a commit e0d5a44
Show file tree
Hide file tree
Showing 3 changed files with 373 additions and 98 deletions.
102 changes: 4 additions & 98 deletions src/components/filters/category-filter.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,11 @@
import React, { useEffect } from "react"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"

import Title from "./title"
import React from "react"
import prettyCategory from "../util/pretty-category"
import { getQueryParams, useQueryParamString } from "react-use-query-param-string"

const Element = styled.div`
padding-top: 36px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
`

const Category = styled.li`
font-size: var(--font-size-16);
color: var(--main-text-color);
display: flex;
padding: 0;
gap: 8px;
`

const Categories = styled.ul`
list-style: none;
padding: 0;
margin: 10px;
line-height: 23px;
`

const TickyBox = styled(props => <FontAwesomeIcon {...props} />)`
font-size: 16px;
color: var(--main-text-color);
`

const separator = ","
import TickyFilter from "./ticky-filter"

const toggleCategory = (
category,
tickedCategories,
setTickedCategories,
filterer
) => {
if (tickedCategories.includes(category)) {
tickedCategories = tickedCategories.filter(item => item !== category)
} else {
tickedCategories = [...tickedCategories, category] // It's important to make a new array or nothing will be re-rendered
}
if (tickedCategories.length > 0) {
setTickedCategories(tickedCategories?.join(separator))
} else {
// Clear this filter from the URL bar if there's nothing in it
setTickedCategories(undefined)
}
filterer && filterer(tickedCategories)
}

const key = "categories"
const CategoryFilter = ({ categories, filterer }) => {
const [stringedTickedCategories, setTickedCategories, initialized] = useQueryParamString(key, undefined, true)
const realStringedTickedCategories = initialized ? stringedTickedCategories : getQueryParams() ? getQueryParams()[key] : undefined

const tickedCategories = stringedTickedCategories ? stringedTickedCategories.split(separator) : []

const onClick = category => () =>
toggleCategory(
category,
tickedCategories,
setTickedCategories,
filterer
)

useEffect(() => { // Make sure that even if the url is pasted in a browser, the list updates with the right value
if (realStringedTickedCategories && realStringedTickedCategories.length > 0) {
filterer(realStringedTickedCategories.split(separator))
}
}, [realStringedTickedCategories], filterer)

return (
categories && <Element>
<Title>Category</Title>
<Categories>
{categories &&
categories.map(category => (
<Category
key={category}
onClick={onClick(category)
}
>
<div>
{tickedCategories.includes(category) ? (
<TickyBox icon="square-check" title="ticked" />
) : (
<TickyBox icon={["far", "square"]} title="unticked" />
)}
</div>
<div>{prettyCategory(category)}</div>
</Category>
))}
</Categories>
</Element>
categories && <TickyFilter label="Category" queryKey="categories" entries={categories} filterer={filterer}
prettify={prettyCategory} />
)
}

Expand Down
109 changes: 109 additions & 0 deletions src/components/filters/ticky-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useEffect } from "react"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"

import Title from "./title"
import { getQueryParams, useQueryParamString } from "react-use-query-param-string"

const Element = styled.div`
padding-top: 36px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
`

const Entry = styled.li`
font-size: var(--font-size-16);
color: var(--main-text-color);
display: flex;
padding: 0;
gap: 8px;
`

const Entries = styled.ul`
list-style: none;
padding: 0;
margin: 10px;
line-height: 23px;
`

const TickyBox = styled(props => <FontAwesomeIcon {...props} />)`
font-size: 16px;
color: var(--main-text-color);
`

const separator = ","
const noop = a => a

const toggleEntry = (
entry,
tickedEntries,
setTickedEntries,
filterer
) => {
if (tickedEntries.includes(entry)) {
tickedEntries = tickedEntries.filter(item => item !== entry)
} else {
tickedEntries = [...tickedEntries, entry] // It's important to make a new array or nothing will be re-rendered
}
if (tickedEntries.length > 0) {
setTickedEntries(tickedEntries?.join(separator))
} else {
// Clear this filter from the URL bar if there's nothing in it
setTickedEntries(undefined)
}
filterer && filterer(tickedEntries)
}

const TickyFilter = ({ entries, filterer, prettify, label, queryKey }) => {
const key = queryKey || label.toLowerCase().replace(" ", "-")

const [stringedTickedEntries, setTickedEntries, initialized] = useQueryParamString(key, undefined, true)
const realStringedTickedEntries = initialized ? stringedTickedEntries : getQueryParams() ? getQueryParams()[key] : undefined

const tickedEntries = stringedTickedEntries ? stringedTickedEntries.split(separator) : []

prettify = prettify || noop

const onClick = entry => () =>
toggleEntry(
entry,
tickedEntries,
setTickedEntries,
filterer
)

useEffect(() => { // Make sure that even if the url is pasted in a browser, the list updates with the right value
if (realStringedTickedEntries && realStringedTickedEntries.length > 0) {
filterer(realStringedTickedEntries.split(separator))
}
}, [realStringedTickedEntries, filterer], filterer)

return (
entries && <Element>
<Title>{label}</Title>
<Entries>
{entries &&
entries.map(entry => (
<Entry
key={entry}
onClick={onClick(entry)
}
>
<div>
{tickedEntries.includes(entry) ? (
<TickyBox icon="square-check" title="ticked" />
) : (
<TickyBox icon={["far", "square"]} title="unticked" />
)}
</div>
<div>{prettify(entry)}</div>
</Entry>
))}
</Entries>
</Element>
)
}

export default TickyFilter
Loading

0 comments on commit e0d5a44

Please sign in to comment.