From acd40158488cc78c6c87a6cffde0448cc49ca7a9 Mon Sep 17 00:00:00 2001 From: Bart Akeley Date: Sun, 12 Nov 2017 13:06:57 -0600 Subject: [PATCH] Filter modal implementation - Orderby picker - Radius picker - Checkbox styling in ui-theme - handlers for cancel and apply --- js/components/CheckBox.js | 20 ++++++ js/components/FilterModal.js | 117 +++++++++++++++++++++++++-------- js/components/IconButton.js | 8 +-- js/components/Modal.js | 2 +- js/components/OrderbyPicker.js | 42 ++++++++++++ js/components/RadiusPicker.js | 29 ++++++++ js/streams/FilterStream.js | 10 +-- js/ui-theme.js | 5 ++ 8 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 js/components/CheckBox.js create mode 100644 js/components/OrderbyPicker.js create mode 100644 js/components/RadiusPicker.js diff --git a/js/components/CheckBox.js b/js/components/CheckBox.js new file mode 100644 index 0000000..efddcc7 --- /dev/null +++ b/js/components/CheckBox.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import { TouchableOpacity } from 'react-native'; +import { Icon } from 'react-native-material-ui'; + +type Props = { + checked?: boolean, + onChange?: (checked: boolean) => void, + style?: { [string]: string | number }, +}; + +const CheckBox = ({ checked, onChange, style }: Props) => { + return ( + onChange && onChange(!checked)}> + + + ); +}; + +export default CheckBox; diff --git a/js/components/FilterModal.js b/js/components/FilterModal.js index ad65aba..9c888c5 100644 --- a/js/components/FilterModal.js +++ b/js/components/FilterModal.js @@ -1,49 +1,114 @@ // @flow import React from 'react'; import { View, TouchableOpacity, Text } from 'react-native'; -import CheckBox from 'react-native-checkbox'; +// import CheckBox from 'react-native-checkbox'; +import { Checkbox } from 'react-native-material-ui'; import Modal from './Modal'; -// import { Icon } from 'react-native-material-ui'; import { withFilter } from '../enhancers/filterEnhancers'; import typeof FilterRecord from '../records/FilterRecord'; import { CATEGORIES, type Category } from '../constants/CategoryConstants'; import { getCategoryText } from '../helpers/CategoryHelpers'; -// import { Set } from 'immutable'; +import OrderbyPicker, { type Orderby } from './OrderbyPicker'; +import RadiusPicker from './RadiusPicker'; +import { compose, withHandlers, withState, onlyUpdateForKeys } from 'recompose'; + +import theme from '../ui-theme'; +const { palette } = theme; type Props = { isVisible: boolean, onClose: () => void, filter: FilterRecord, setFilter: (f: FilterRecord) => void, + currentFilter: FilterRecord, + setCurrentFilter: (currentFilter: FilterRecord) => void, + toggleCategory: (category: Category, isChecked: boolean) => () => void, + applyChanges: () => void, + updateOrderby: (orderby: Orderby) => void, + updateRadius: (radius: number) => void, }; -const FilterModal = withFilter(({ isVisible, onClose, filter, setFilter }: Props) => { - const { orderby, categories, radius } = filter; - - const toggleCategory = category => checked => { - setFilter( - filter.update('categories', categories => { - return checked ? categories.delete(category) : categories.add(category); - }) - ); - }; +const FilterModal = (props: Props) => { + const { + isVisible, + onClose, + currentFilter: { orderby, categories, radius }, + toggleCategory, + applyChanges, + updateOrderby, + updateRadius, + } = props; return ( - - Filters - {CATEGORIES.map((category: Category) => ( - Filters + + {CATEGORIES.map((category: Category) => { + const isChecked = categories.has(category); + return ( + + {getCategoryText(category)} + + + + + ); + })} + + Sort By + - ))} - + + + Search Radius + + + + + + Cancel + + + Apply + + + + ); -}); +}; -export default FilterModal; +export default compose( + withFilter, + withState('currentFilter', 'setCurrentFilter', (props: Props) => props.filter), + withHandlers({ + toggleCategory: ({ currentFilter, setCurrentFilter }: Props) => ( + category: Category, + isChecked: boolean + ) => () => { + setCurrentFilter( + currentFilter.update('categories', categories => { + return isChecked ? categories.delete(category) : categories.add(category); + }) + ); + }, + updateOrderby: ({ currentFilter, setCurrentFilter }: Props) => (orderby: Orderby) => { + setCurrentFilter(currentFilter.set('orderby', orderby)); + }, + updateRadius: ({ currentFilter, setCurrentFilter }: Props) => (radius: number) => { + setCurrentFilter(currentFilter.set('radius', radius)); + }, + applyChanges: ({ onClose, currentFilter, setFilter }: Props) => () => { + setFilter(currentFilter); + onClose(); + }, + }), + onlyUpdateForKeys(['isVisible', 'currentFilter']) +)(FilterModal); diff --git a/js/components/IconButton.js b/js/components/IconButton.js index dc9ee73..a50bc13 100644 --- a/js/components/IconButton.js +++ b/js/components/IconButton.js @@ -6,13 +6,11 @@ import { Icon } from 'react-native-material-ui'; type Props = { glyph: string, text: string, route: string, onPress: Function, color?: string }; -export default pure(({ glyph, text, color = 'grey', ...others }: Props) => +export default pure(({ glyph, text, color = 'grey', ...others }: Props) => ( - - {text} - + {text} -); +)); diff --git a/js/components/Modal.js b/js/components/Modal.js index dc31da9..1cec605 100644 --- a/js/components/Modal.js +++ b/js/components/Modal.js @@ -8,7 +8,7 @@ import { Icon } from 'react-native-material-ui'; export default pure(({ children, isVisible }) => { return ( - + (selected === current ? selectedColor : defaultColor); + +export type Orderby = 'distance' | 'lastupdated' | 'quantity'; + +export const ORDER_BY = ['distance', 'lastupdated', 'quantity']; + +export const getOrderbyText = (orderby: Orderby) => { + switch (orderby) { + case 'quantity': + return 'Quantity'; + case 'lastupdated': + return 'Most Recently Updated'; + default: + return 'Distance'; + } +}; + +/* eslint-disable react/display-name */ +const renderItem = (selected: Orderby) => (item: Orderby) => ( + +); + +type orderbyPickerProps = { selected: Orderby, onValueChange: Function, style: Object }; +const orderbyPicker = pure(({ selected, onValueChange, style }: orderbyPickerProps) => ( + + + {ORDER_BY.map(renderItem(selected))} + + +)); + +export default orderbyPicker; diff --git a/js/components/RadiusPicker.js b/js/components/RadiusPicker.js new file mode 100644 index 0000000..19d86ae --- /dev/null +++ b/js/components/RadiusPicker.js @@ -0,0 +1,29 @@ +// @flow +import React from 'react'; +import { Picker, View } from 'react-native'; +import theme from '../ui-theme'; +import { pure } from 'recompose'; +import debounce from '../helpers/debounce'; + +const { picker: { color: selectedColor } } = theme; +const defaultColor = 'black'; + +const getItemColor = (selected, current) => (selected === current ? selectedColor : defaultColor); + +export const CHOICES = [5, 10, 15, 20, 25, 30]; + +/* eslint-disable react/display-name */ +const renderItem = (selected: number) => (item: number) => ( + +); + +type radiusPickerProps = { selected: number, onValueChange: Function, style: Object }; +const radiusPicker = pure(({ selected, onValueChange, style }: radiusPickerProps) => ( + + + {CHOICES.map(renderItem(selected))} + + +)); + +export default radiusPicker; diff --git a/js/streams/FilterStream.js b/js/streams/FilterStream.js index a3a89cc..6cc7ced 100644 --- a/js/streams/FilterStream.js +++ b/js/streams/FilterStream.js @@ -1,12 +1,12 @@ // @flow -import { Subject, Observable, ReplaySubject } from 'rxjs'; +import { ReplaySubject } from 'rxjs'; import FilterRecord from '../records/FilterRecord'; -const multicaster = new ReplaySubject(); +const multicaster: ReplaySubject = new ReplaySubject(); -export const emitter = val => multicaster.next(val); - -// Observable.from([new FilterRecord()]).subscribe(multicaster); +export function emitter(val: typeof FilterRecord) { + multicaster.next(val); +} emitter(new FilterRecord()); diff --git a/js/ui-theme.js b/js/ui-theme.js index 16e4630..f5922f9 100644 --- a/js/ui-theme.js +++ b/js/ui-theme.js @@ -23,6 +23,11 @@ export default { elevation: 0, }, }, + checkbox: { + icon: { + color: '#0E6E9E', + }, + }, page: { container: { flex: 1, backgroundColor: COLOR.grey100 }, },