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