Skip to content

Commit 4bc92e3

Browse files
committed
Add search field
Closes #3
1 parent 8a2e887 commit 4bc92e3

13 files changed

Lines changed: 138 additions & 19 deletions

src/App.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,14 @@
3636
display: none !important;
3737
}
3838
}
39+
40+
.sr-only {
41+
position: absolute;
42+
width: 1px;
43+
height: 1px;
44+
padding: 0;
45+
overflow: hidden;
46+
clip-path: rect(0 0 0 0);
47+
overflow-wrap: normal;
48+
border: 0;
49+
}

src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {Header} from './Header'
1212
import {FiltersAndTable} from './FiltersAndTable'
1313
import themeOverrides from './theme-overrides'
1414
import {SortProvider} from './SortContext'
15+
import {SearchContextProvider} from './contexts/SearchContextProvider'
1516

1617
const App = () => (
1718
<ProviderStack>
@@ -37,7 +38,9 @@ const ProviderStack = ({children}: PropsWithChildren) => (
3738
<RequireAnchorProvider>
3839
<RequireCosmoProvider>
3940
<SortProvider>
40-
<ColorsProvider>{children}</ColorsProvider>
41+
<SearchContextProvider>
42+
<ColorsProvider>{children}</ColorsProvider>
43+
</SearchContextProvider>
4144
</SortProvider>
4245
</RequireCosmoProvider>
4346
</RequireAnchorProvider>

src/ColorsContext.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {createContext, type PropsWithChildren, useContext, useMemo} from 'react'
22
import {useRequireAnchor} from './RequireAnchorContext'
33
import {useRequireJpCoats} from './RequireJpCoatsContext'
44
import {useRequireCosmo} from './contexts/cosmo-context'
5+
import {useSearchContext} from './contexts/search-context'
56
import dmcNamedColorCodes from './assets/dmc-color-codes-names.json'
67
import dmcNewJpCoatsColors from './assets/dmc-old-new-jp-coats-colors.json'
78
import dmcCosmoColors from './assets/dmc-cosmo-colors.json'
@@ -20,6 +21,7 @@ export function ColorsProvider({children}: PropsWithChildren) {
2021
const {requireJpCoats} = useRequireJpCoats()
2122
const {requireCosmo} = useRequireCosmo()
2223
const {sortOption} = useSort()
24+
const {query, field} = useSearchContext()
2325
const dataByDmcCode = useMemo<Record<string, EmbroideryFlossColor>>(() => {
2426
const result: Record<string, EmbroideryFlossColor> = {}
2527
dmcNamedColorCodes.forEach(data => {
@@ -50,19 +52,42 @@ export function ColorsProvider({children}: PropsWithChildren) {
5052
const contextProps = useMemo(
5153
() => ({
5254
colors: colors
53-
.filter(({anchorCode, cosmoCodes, jpCoatsOld, jpCoatsNew}) => {
55+
.filter(({anchorCode, cosmoCodes, jpCoatsOld, jpCoatsNew, dmcCode, dmcName}) => {
5456
if (requireAnchor && anchorCode === undefined) return false
5557
if (requireJpCoats && jpCoatsOld === undefined && jpCoatsNew === undefined) {
5658
return false
5759
}
5860
if (requireCosmo && (cosmoCodes === undefined || cosmoCodes.length < 1)) {
5961
return false
6062
}
63+
if (query.trim() !== '') {
64+
switch (field) {
65+
case 'dmcCode':
66+
if (!dmcCode.startsWith(query)) return false
67+
break
68+
case 'dmcName':
69+
if (!dmcName.startsWith(query)) return false
70+
break
71+
case 'anchor':
72+
if (anchorCode === undefined || !anchorCode.startsWith(query)) return false
73+
break
74+
case 'cosmo':
75+
if (cosmoCodes === undefined || cosmoCodes.length < 1) return false
76+
if (!cosmoCodes.some(c => c.startsWith(query))) return false
77+
break
78+
case 'jpcNew':
79+
if (jpCoatsNew === undefined || !jpCoatsNew.startsWith(query)) return false
80+
break
81+
case 'jpcOld':
82+
if (jpCoatsOld === undefined || !jpCoatsOld.startsWith(query)) return false
83+
break
84+
}
85+
}
6186
return true
6287
})
6388
.sort(colorCompareFunction(sortOption)),
6489
}),
65-
[colors, requireAnchor, requireCosmo, requireJpCoats, sortOption]
90+
[colors, field, query, requireAnchor, requireCosmo, requireJpCoats, sortOption]
6691
)
6792
return <ColorsContext.Provider value={contextProps}>{children}</ColorsContext.Provider>
6893
}

src/Filters.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@
22
display: flex;
33
gap: 4px;
44
align-items: center;
5+
flex-wrap: wrap;
6+
justify-content: space-between;
7+
}
8+
9+
.filterCheckboxes {
10+
display: flex;
11+
gap: 16px;
12+
align-items: center;
513
}

src/Filters.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Fieldset} from './Fieldset'
33
import {useRequireJpCoats} from './RequireJpCoatsContext'
44
import {useRequireAnchor} from './RequireAnchorContext'
55
import {useRequireCosmo} from './contexts/cosmo-context'
6+
import {Search} from './Search'
67
import './Filters.css'
78

89
export const Filters = () => {
@@ -12,18 +13,21 @@ export const Filters = () => {
1213

1314
return (
1415
<Fieldset legend="Filters" className="filtersFieldset">
15-
<FormControl>
16-
<Checkbox checked={requireAnchor} onChange={() => setRequireAnchor(!requireAnchor)} />
17-
<FormControl.Label>Anchor</FormControl.Label>
18-
</FormControl>
19-
<FormControl>
20-
<Checkbox checked={requireCosmo} onChange={() => setRequireCosmo(!requireCosmo)} />
21-
<FormControl.Label>Cosmo</FormControl.Label>
22-
</FormControl>
23-
<FormControl>
24-
<Checkbox checked={requireJpCoats} onChange={() => setRequireJpCoats(!requireJpCoats)} />
25-
<FormControl.Label>J&amp;P Coats</FormControl.Label>
26-
</FormControl>
16+
<div className="filterCheckboxes">
17+
<FormControl>
18+
<Checkbox checked={requireAnchor} onChange={() => setRequireAnchor(!requireAnchor)} />
19+
<FormControl.Label>Anchor</FormControl.Label>
20+
</FormControl>
21+
<FormControl>
22+
<Checkbox checked={requireCosmo} onChange={() => setRequireCosmo(!requireCosmo)} />
23+
<FormControl.Label>Cosmo</FormControl.Label>
24+
</FormControl>
25+
<FormControl>
26+
<Checkbox checked={requireJpCoats} onChange={() => setRequireJpCoats(!requireJpCoats)} />
27+
<FormControl.Label>J&amp;P&nbsp;Coats</FormControl.Label>
28+
</FormControl>
29+
</div>
30+
<Search />
2731
</Fieldset>
2832
)
2933
}

src/RequireAnchorContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const RequireAnchorContext = createContext<RequireAnchor | undefined>(undefined)
99

1010
export function RequireAnchorProvider({children}: PropsWithChildren) {
1111
const [requireAnchor, setRequireAnchor] = useState(false)
12-
const contextProps = useMemo(() => ({requireAnchor, setRequireAnchor}), [requireAnchor, setRequireAnchor])
12+
const contextProps = useMemo(() => ({requireAnchor, setRequireAnchor}), [requireAnchor])
1313
return <RequireAnchorContext.Provider value={contextProps}>{children}</RequireAnchorContext.Provider>
1414
}
1515

src/RequireJpCoatsContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const RequireJpCoatsContext = createContext<RequireJpCoats | undefined>(undefine
99

1010
export const RequireJpCoatsProvider = ({children}: PropsWithChildren) => {
1111
const [requireJpCoats, setRequireJpCoats] = useState(false)
12-
const contextProps = useMemo(() => ({requireJpCoats, setRequireJpCoats}), [requireJpCoats, setRequireJpCoats])
12+
const contextProps = useMemo(() => ({requireJpCoats, setRequireJpCoats}), [requireJpCoats])
1313
return <RequireJpCoatsContext.Provider value={contextProps}>{children}</RequireJpCoatsContext.Provider>
1414
}
1515

src/Search.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.searchControls {
2+
display: flex;
3+
align-items: center;
4+
flex-direction: row;
5+
gap: 4px;
6+
}

src/Search.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import {ActionList, ActionMenu, FormControl, TextInput} from '@primer/react'
2+
import {sortLabels, sortOptions} from './SortContext'
3+
import {useSearchContext} from './contexts/search-context'
4+
import './Search.css'
5+
6+
export function Search() {
7+
const {field, setField, query, setQuery} = useSearchContext()
8+
return (
9+
<div className="searchControls">
10+
<ActionMenu>
11+
<ActionMenu.Button><span className="sr-only">Search by</span> {sortLabels[field]}</ActionMenu.Button>
12+
<ActionMenu.Overlay width="small">
13+
<ActionList selectionVariant="single">
14+
{sortOptions.map(searchOption => (
15+
<ActionList.Item
16+
key={searchOption}
17+
selected={searchOption === field}
18+
onSelect={() => setField(searchOption)}
19+
>
20+
<span className="sr-only">Search by</span>
21+
{sortLabels[searchOption]}
22+
</ActionList.Item>
23+
))}
24+
</ActionList>
25+
</ActionMenu.Overlay>
26+
</ActionMenu>
27+
<FormControl>
28+
<FormControl.Label visuallyHidden>Search {sortLabels[field]}</FormControl.Label>
29+
<TextInput placeholder="Search" value={query} onChange={e => setQuery(e.currentTarget.value)} />
30+
</FormControl>
31+
</div>
32+
)
33+
}

src/SortContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const SortContext = createContext<Sort | undefined>(undefined)
2121

2222
export function SortProvider({children}: PropsWithChildren) {
2323
const [sortOption, setSortOption] = useState<SortOption>('dmcCode')
24-
const contextProps = useMemo(() => ({sortOption, setSortOption}), [sortOption, setSortOption])
24+
const contextProps = useMemo(() => ({sortOption, setSortOption}), [sortOption])
2525
return <SortContext.Provider value={contextProps}>{children}</SortContext.Provider>
2626
}
2727

0 commit comments

Comments
 (0)