mirror of
https://gitlab.com/wheres-the-tp/ui-mobile.git
synced 2026-01-25 05:54:56 -06:00
Filter modal implementation
- Orderby picker - Radius picker - Checkbox styling in ui-theme - handlers for cancel and apply
This commit is contained in:
parent
e15c5b4df6
commit
acd4015848
8 changed files with 196 additions and 37 deletions
20
js/components/CheckBox.js
Normal file
20
js/components/CheckBox.js
Normal file
|
|
@ -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 (
|
||||
<TouchableOpacity onPress={() => onChange && onChange(!checked)}>
|
||||
<Icon glyph={checked ? 'checkbox-marked' : 'checkbox-blank-outline'} style={style} />
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckBox;
|
||||
|
|
@ -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 (
|
||||
<Modal isVisible={isVisible}>
|
||||
<TouchableOpacity onPress={onClose}>
|
||||
<Text style={{ fontSize: 30, fontWeight: 'bold' }}>Filters</Text>
|
||||
{CATEGORIES.map((category: Category) => (
|
||||
<CheckBox
|
||||
key={category}
|
||||
style={{ margin: 20 }}
|
||||
checked={categories.has(category)}
|
||||
onChange={toggleCategory(category)}
|
||||
label={getCategoryText(category)}
|
||||
<Text style={{ marginLeft: 20, marginTop: 15, fontSize: 20, fontWeight: 'bold' }}>Filters</Text>
|
||||
<View style={{ padding: 15, flexDirection: 'column', justifyContent: 'space-around', height: 400 }}>
|
||||
{CATEGORIES.map((category: Category) => {
|
||||
const isChecked = categories.has(category);
|
||||
return (
|
||||
<View key={category} style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<Text style={{ fontSize: 15 }}>{getCategoryText(category)}</Text>
|
||||
<View style={{ width: 40 }}>
|
||||
<Checkbox
|
||||
value={isChecked ? 0 : 1}
|
||||
checked={isChecked}
|
||||
onCheck={toggleCategory(category, isChecked)}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text style={{ fontSize: 15 }}>Sort By</Text>
|
||||
<OrderbyPicker
|
||||
selected={orderby}
|
||||
onValueChange={updateOrderby}
|
||||
style={{ height: 40, width: 150 }}
|
||||
/>
|
||||
))}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text style={{ fontSize: 15 }}>Search Radius</Text>
|
||||
<RadiusPicker selected={radius} onValueChange={updateRadius} style={{ height: 40, width: 150 }} />
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
<View style={{ width: 100, flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<TouchableOpacity onPress={onClose}>
|
||||
<Text style={{ color: palette.accentColor }}>Cancel</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={applyChanges}>
|
||||
<Text style={{ color: palette.accentColor }}>Apply</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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) => (
|
||||
<TouchableOpacity {...others}>
|
||||
<View style={{ flexDirection: 'row', marginTop: 5 }}>
|
||||
<Icon name={glyph} size={35} color={color} style={{ marginRight: 10 }} />
|
||||
<Text style={{ lineHeight: 26 }}>
|
||||
{text}
|
||||
</Text>
|
||||
<Text style={{ lineHeight: 26 }}>{text}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
));
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { Icon } from 'react-native-material-ui';
|
|||
|
||||
export default pure(({ children, isVisible }) => {
|
||||
return (
|
||||
<Modal isVisible={isVisible} backdropColor="black" backdropOpacity={1.0}>
|
||||
<Modal isVisible={isVisible} backdropColor="black" backdropOpacity={0.7}>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'column',
|
||||
|
|
|
|||
42
js/components/OrderbyPicker.js
Normal file
42
js/components/OrderbyPicker.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// @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 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) => (
|
||||
<Picker.Item key={item} label={getOrderbyText(item)} value={item} color={getItemColor(selected, item)} />
|
||||
);
|
||||
|
||||
type orderbyPickerProps = { selected: Orderby, onValueChange: Function, style: Object };
|
||||
const orderbyPicker = pure(({ selected, onValueChange, style }: orderbyPickerProps) => (
|
||||
<View style={style}>
|
||||
<Picker selectedValue={selected} onValueChange={debounce(onValueChange)} style={{ flex: 1 }}>
|
||||
{ORDER_BY.map(renderItem(selected))}
|
||||
</Picker>
|
||||
</View>
|
||||
));
|
||||
|
||||
export default orderbyPicker;
|
||||
29
js/components/RadiusPicker.js
Normal file
29
js/components/RadiusPicker.js
Normal file
|
|
@ -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) => (
|
||||
<Picker.Item key={item} label={`${item}`} value={item} color={getItemColor(selected, item)} />
|
||||
);
|
||||
|
||||
type radiusPickerProps = { selected: number, onValueChange: Function, style: Object };
|
||||
const radiusPicker = pure(({ selected, onValueChange, style }: radiusPickerProps) => (
|
||||
<View style={style}>
|
||||
<Picker selectedValue={selected} onValueChange={debounce(onValueChange)} style={{ flex: 1 }}>
|
||||
{CHOICES.map(renderItem(selected))}
|
||||
</Picker>
|
||||
</View>
|
||||
));
|
||||
|
||||
export default radiusPicker;
|
||||
|
|
@ -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<FilterRecord> = 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());
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ export default {
|
|||
elevation: 0,
|
||||
},
|
||||
},
|
||||
checkbox: {
|
||||
icon: {
|
||||
color: '#0E6E9E',
|
||||
},
|
||||
},
|
||||
page: {
|
||||
container: { flex: 1, backgroundColor: COLOR.grey100 },
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue