Skip to content

Commit ba6873e

Browse files
committed
Extract ticky filter list logic to a re-usable class
1 parent ae4aae4 commit ba6873e

File tree

3 files changed

+373
-98
lines changed

3 files changed

+373
-98
lines changed

src/components/filters/category-filter.js

Lines changed: 4 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,11 @@
1-
import React, { useEffect } from "react"
2-
import styled from "styled-components"
3-
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
4-
5-
import Title from "./title"
1+
import React from "react"
62
import prettyCategory from "../util/pretty-category"
7-
import { getQueryParams, useQueryParamString } from "react-use-query-param-string"
8-
9-
const Element = styled.div`
10-
padding-top: 36px;
11-
display: flex;
12-
flex-direction: column;
13-
justify-content: flex-start;
14-
align-items: flex-start;
15-
`
16-
17-
const Category = styled.li`
18-
font-size: var(--font-size-16);
19-
color: var(--main-text-color);
20-
display: flex;
21-
padding: 0;
22-
gap: 8px;
23-
`
24-
25-
const Categories = styled.ul`
26-
list-style: none;
27-
padding: 0;
28-
margin: 10px;
29-
line-height: 23px;
30-
`
31-
32-
const TickyBox = styled(props => <FontAwesomeIcon {...props} />)`
33-
font-size: 16px;
34-
color: var(--main-text-color);
35-
`
36-
37-
const separator = ","
3+
import TickyFilter from "./ticky-filter"
384

39-
const toggleCategory = (
40-
category,
41-
tickedCategories,
42-
setTickedCategories,
43-
filterer
44-
) => {
45-
if (tickedCategories.includes(category)) {
46-
tickedCategories = tickedCategories.filter(item => item !== category)
47-
} else {
48-
tickedCategories = [...tickedCategories, category] // It's important to make a new array or nothing will be re-rendered
49-
}
50-
if (tickedCategories.length > 0) {
51-
setTickedCategories(tickedCategories?.join(separator))
52-
} else {
53-
// Clear this filter from the URL bar if there's nothing in it
54-
setTickedCategories(undefined)
55-
}
56-
filterer && filterer(tickedCategories)
57-
}
58-
59-
const key = "categories"
605
const CategoryFilter = ({ categories, filterer }) => {
61-
const [stringedTickedCategories, setTickedCategories, initialized] = useQueryParamString(key, undefined, true)
62-
const realStringedTickedCategories = initialized ? stringedTickedCategories : getQueryParams() ? getQueryParams()[key] : undefined
63-
64-
const tickedCategories = stringedTickedCategories ? stringedTickedCategories.split(separator) : []
65-
66-
const onClick = category => () =>
67-
toggleCategory(
68-
category,
69-
tickedCategories,
70-
setTickedCategories,
71-
filterer
72-
)
73-
74-
useEffect(() => { // Make sure that even if the url is pasted in a browser, the list updates with the right value
75-
if (realStringedTickedCategories && realStringedTickedCategories.length > 0) {
76-
filterer(realStringedTickedCategories.split(separator))
77-
}
78-
}, [realStringedTickedCategories], filterer)
79-
806
return (
81-
categories && <Element>
82-
<Title>Category</Title>
83-
<Categories>
84-
{categories &&
85-
categories.map(category => (
86-
<Category
87-
key={category}
88-
onClick={onClick(category)
89-
}
90-
>
91-
<div>
92-
{tickedCategories.includes(category) ? (
93-
<TickyBox icon="square-check" title="ticked" />
94-
) : (
95-
<TickyBox icon={["far", "square"]} title="unticked" />
96-
)}
97-
</div>
98-
<div>{prettyCategory(category)}</div>
99-
</Category>
100-
))}
101-
</Categories>
102-
</Element>
7+
categories && <TickyFilter label="Category" queryKey="categories" entries={categories} filterer={filterer}
8+
prettify={prettyCategory} />
1039
)
10410
}
10511

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import React, { useEffect } from "react"
2+
import styled from "styled-components"
3+
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
4+
5+
import Title from "./title"
6+
import { getQueryParams, useQueryParamString } from "react-use-query-param-string"
7+
8+
const Element = styled.div`
9+
padding-top: 36px;
10+
display: flex;
11+
flex-direction: column;
12+
justify-content: flex-start;
13+
align-items: flex-start;
14+
`
15+
16+
const Entry = styled.li`
17+
font-size: var(--font-size-16);
18+
color: var(--main-text-color);
19+
display: flex;
20+
padding: 0;
21+
gap: 8px;
22+
`
23+
24+
const Entries = styled.ul`
25+
list-style: none;
26+
padding: 0;
27+
margin: 10px;
28+
line-height: 23px;
29+
`
30+
31+
const TickyBox = styled(props => <FontAwesomeIcon {...props} />)`
32+
font-size: 16px;
33+
color: var(--main-text-color);
34+
`
35+
36+
const separator = ","
37+
const noop = a => a
38+
39+
const toggleEntry = (
40+
entry,
41+
tickedEntries,
42+
setTickedEntries,
43+
filterer
44+
) => {
45+
if (tickedEntries.includes(entry)) {
46+
tickedEntries = tickedEntries.filter(item => item !== entry)
47+
} else {
48+
tickedEntries = [...tickedEntries, entry] // It's important to make a new array or nothing will be re-rendered
49+
}
50+
if (tickedEntries.length > 0) {
51+
setTickedEntries(tickedEntries?.join(separator))
52+
} else {
53+
// Clear this filter from the URL bar if there's nothing in it
54+
setTickedEntries(undefined)
55+
}
56+
filterer && filterer(tickedEntries)
57+
}
58+
59+
const TickyFilter = ({ entries, filterer, prettify, label, queryKey }) => {
60+
const key = queryKey || label.toLowerCase().replace(" ", "-")
61+
62+
const [stringedTickedEntries, setTickedEntries, initialized] = useQueryParamString(key, undefined, true)
63+
const realStringedTickedEntries = initialized ? stringedTickedEntries : getQueryParams() ? getQueryParams()[key] : undefined
64+
65+
const tickedEntries = stringedTickedEntries ? stringedTickedEntries.split(separator) : []
66+
67+
prettify = prettify || noop
68+
69+
const onClick = entry => () =>
70+
toggleEntry(
71+
entry,
72+
tickedEntries,
73+
setTickedEntries,
74+
filterer
75+
)
76+
77+
useEffect(() => { // Make sure that even if the url is pasted in a browser, the list updates with the right value
78+
if (realStringedTickedEntries && realStringedTickedEntries.length > 0) {
79+
filterer(realStringedTickedEntries.split(separator))
80+
}
81+
}, [realStringedTickedEntries, filterer], filterer)
82+
83+
return (
84+
entries && <Element>
85+
<Title>{label}</Title>
86+
<Entries>
87+
{entries &&
88+
entries.map(entry => (
89+
<Entry
90+
key={entry}
91+
onClick={onClick(entry)
92+
}
93+
>
94+
<div>
95+
{tickedEntries.includes(entry) ? (
96+
<TickyBox icon="square-check" title="ticked" />
97+
) : (
98+
<TickyBox icon={["far", "square"]} title="unticked" />
99+
)}
100+
</div>
101+
<div>{prettify(entry)}</div>
102+
</Entry>
103+
))}
104+
</Entries>
105+
</Element>
106+
)
107+
}
108+
109+
export default TickyFilter

0 commit comments

Comments
 (0)