Browse Source
- Add check picker component - Add filter hook - Add filter component - Add support for favorite filter - Add support for genre filter (AND/OR)master
jeffvli
3 years ago
committed by
Jeff
5 changed files with 259 additions and 11 deletions
@ -0,0 +1,130 @@ |
|||
/* eslint-disable react/destructuring-assignment */ |
|||
import _ from 'lodash'; |
|||
import React, { useEffect, useRef, useState } from 'react'; |
|||
import { RadioGroup } from 'rsuite'; |
|||
import styled from 'styled-components'; |
|||
import { useAppDispatch } from '../../redux/hooks'; |
|||
import { |
|||
StyledCheckbox, |
|||
StyledCheckPicker, |
|||
StyledInputPickerContainer, |
|||
StyledRadio, |
|||
} from '../shared/styled'; |
|||
|
|||
const FilterHeader = styled.h1` |
|||
font-size: 16px; |
|||
line-height: unset; |
|||
`;
|
|||
|
|||
const AdvancedFilters = ({ filteredData, originalData, filter, setAdvancedFilters }: any) => { |
|||
const dispatch = useAppDispatch(); |
|||
const [availableGenres, setAvailableGenres] = useState<any[]>([]); |
|||
const genreFilterPickerContainerRef = useRef<any>(); |
|||
|
|||
useEffect(() => { |
|||
setAvailableGenres( |
|||
_.orderBy( |
|||
_.uniqBy( |
|||
_.flatten( |
|||
_.map( |
|||
filter.properties.starred || filter.properties.genre.type === 'and' |
|||
? filteredData |
|||
: originalData, |
|||
'genre' |
|||
) |
|||
), |
|||
'title' |
|||
), |
|||
[ |
|||
(entry: any) => { |
|||
return typeof entry.title === 'string' |
|||
? entry.title.toLowerCase() || '' |
|||
: +entry.title || ''; |
|||
}, |
|||
] |
|||
) |
|||
); |
|||
}, [filter.properties.genre.type, filter.properties.starred, filteredData, originalData]); |
|||
|
|||
return ( |
|||
<div> |
|||
<FilterHeader>Filters</FilterHeader> |
|||
<StyledCheckbox |
|||
defaultChecked={filter.enabled} |
|||
checked={filter.enabled} |
|||
onChange={(_v: any, e: boolean) => { |
|||
dispatch(setAdvancedFilters({ ...filter, enabled: e })); |
|||
}} |
|||
> |
|||
Enabled |
|||
</StyledCheckbox> |
|||
<StyledCheckbox |
|||
defaultChecked={filter.properties.starred} |
|||
checked={filter.properties.starred} |
|||
onChange={(_v: any, e: boolean) => { |
|||
dispatch( |
|||
setAdvancedFilters({ |
|||
...filter, |
|||
properties: { |
|||
...filter.properties, |
|||
starred: e, |
|||
}, |
|||
}) |
|||
); |
|||
}} |
|||
> |
|||
Is favorite |
|||
</StyledCheckbox> |
|||
<br /> |
|||
<FilterHeader>Genres</FilterHeader> |
|||
<RadioGroup |
|||
inline |
|||
defaultValue={filter.properties.genre.type} |
|||
onChange={(e: string) => { |
|||
dispatch( |
|||
setAdvancedFilters({ |
|||
...filter, |
|||
properties: { |
|||
...filter.properties, |
|||
genre: { |
|||
...filter.properties.genre, |
|||
type: e, |
|||
}, |
|||
}, |
|||
}) |
|||
); |
|||
}} |
|||
> |
|||
<StyledRadio value="and">AND</StyledRadio> |
|||
<StyledRadio value="or">OR</StyledRadio> |
|||
</RadioGroup> |
|||
<StyledInputPickerContainer ref={genreFilterPickerContainerRef}> |
|||
<StyledCheckPicker |
|||
container={() => genreFilterPickerContainerRef.current} |
|||
data={availableGenres} |
|||
value={filter.properties.genre.list} |
|||
labelKey="title" |
|||
valueKey="title" |
|||
sticky |
|||
style={{ width: '250px' }} |
|||
onChange={(e: string[]) => { |
|||
dispatch( |
|||
setAdvancedFilters({ |
|||
...filter, |
|||
properties: { |
|||
...filter.properties, |
|||
genre: { |
|||
...filter.properties.genre, |
|||
list: e, |
|||
}, |
|||
}, |
|||
}) |
|||
); |
|||
}} |
|||
/> |
|||
</StyledInputPickerContainer> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default AdvancedFilters; |
@ -0,0 +1,60 @@ |
|||
import { useState, useEffect } from 'react'; |
|||
import _ from 'lodash'; |
|||
|
|||
interface AdvancedFilters { |
|||
enabled: boolean; |
|||
properties: { |
|||
starred: boolean; |
|||
genre: { |
|||
list: any[]; |
|||
type: 'or' | 'and'; |
|||
}; |
|||
}; |
|||
} |
|||
|
|||
const useAdvancedFilter = (data: any[], filters: AdvancedFilters) => { |
|||
const [filteredData, setFilteredData] = useState<any[]>([]); |
|||
const [filterProps, setFilterProps] = useState(filters); |
|||
|
|||
useEffect(() => { |
|||
setFilterProps(filters); |
|||
if (filterProps.enabled) { |
|||
// Favorite/Star filter
|
|||
const filteredByStarred = filterProps.properties.starred |
|||
? (data || []).filter((entry) => { |
|||
return entry.starred !== undefined; |
|||
}) |
|||
: data; |
|||
|
|||
// Genre filter
|
|||
const genreRegex = new RegExp(filterProps.properties?.genre?.list.join('|'), 'i'); |
|||
const filteredByGenres = |
|||
filterProps.properties.genre.list.length > 0 |
|||
? (filteredByStarred || []).filter((entry) => { |
|||
const entryGenres = _.map(entry.genre, 'title'); |
|||
|
|||
if (filterProps.properties.genre.type === 'or') { |
|||
return entryGenres.some((genre) => genre.match(genreRegex)); |
|||
} |
|||
|
|||
const matches = []; |
|||
for (let i = 0; i < filterProps.properties.genre.list.length; i += 1) { |
|||
if (entryGenres.includes(filterProps.properties.genre.list[i])) { |
|||
matches.push(entry); |
|||
} |
|||
} |
|||
|
|||
return matches.length === filterProps.properties.genre.list.length; |
|||
}) |
|||
: filteredByStarred; |
|||
|
|||
setFilteredData(_.compact(_.uniqBy(filteredByGenres, 'uniqueId'))); |
|||
} else { |
|||
setFilteredData(data); |
|||
} |
|||
}, [data, filterProps, filters]); |
|||
|
|||
return filteredData; |
|||
}; |
|||
|
|||
export default useAdvancedFilter; |
Loading…
Reference in new issue