Mobile-friendly search filters (#37)

* Remove affixed filters. Replace with responsive drawer filters.

* Fix ts error
This commit is contained in:
William O'Beirne 2018-09-13 17:49:44 -04:00 committed by Daniel Ternyak
parent 0e3c6c6fc0
commit e4779d1410
3 changed files with 124 additions and 26 deletions

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Select, Checkbox, Radio, Card, Divider, Affix } from 'antd';
import { Select, Checkbox, Radio, Card, Divider } from 'antd';
import { RadioChangeEvent } from 'antd/lib/radio';
import {
PROPOSAL_SORT,
@ -27,7 +27,7 @@ export default class ProposalFilters extends React.Component<Props> {
const { sort, filters, handleChangeSort } = this.props;
return (
<Affix offsetTop={20}>
<div>
<Card title="Sort">
<Select onChange={handleChangeSort} value={sort} style={{ width: '100%' }}>
{Object.keys(PROPOSAL_SORT).map((s: PROPOSAL_SORT) => (
@ -70,7 +70,7 @@ export default class ProposalFilters extends React.Component<Props> {
</div>
))}
</Card>
</Affix>
</div>
);
}

View File

@ -1,5 +1,4 @@
import React from 'react';
import DocumentTitle from 'react-document-title';
import { compose } from 'recompose';
import { connect } from 'react-redux';
import { proposalActions } from 'modules/proposals';
@ -7,11 +6,12 @@ import { getProposals } from 'modules/proposals/selectors';
import { ProposalWithCrowdFund } from 'modules/proposals/reducers';
import { bindActionCreators, Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import { Input, Row, Col, Divider, Spin } from 'antd';
import { Input, Divider, Spin, Drawer, Icon, Button } from 'antd';
import ProposalResults from './Results';
import ProposalFilters, { Filters } from './Filters';
import { PROPOSAL_SORT } from 'api/constants';
import Web3Container from 'lib/Web3Container';
import * as Styled from './styled';
type ProposalSortFn = (p1: ProposalWithCrowdFund, p2: ProposalWithCrowdFund) => number;
const sortFunctions: { [key in PROPOSAL_SORT]: ProposalSortFn } = {
@ -64,6 +64,8 @@ interface State {
searchQuery: string;
sort: PROPOSAL_SORT;
filters: Filters;
isFiltersDrawered: boolean;
isDrawerShowing: boolean;
}
class Proposals extends React.Component<Props, State> {
@ -113,42 +115,84 @@ class Proposals extends React.Component<Props, State> {
categories: [],
stage: null,
},
isFiltersDrawered: false,
isDrawerShowing: false,
};
componentDidMount() {
this.props.fetchProposals();
window.addEventListener('resize', this.handleResize);
this.handleResize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
render() {
const { proposalsError, isFetchingProposals } = this.props;
const { processedProposals, sort, filters } = this.state;
const {
processedProposals,
sort,
filters,
isFiltersDrawered,
isDrawerShowing,
} = this.state;
const filtersComponent = (
<ProposalFilters
sort={sort}
filters={filters}
handleChangeSort={this.handleChangeSort}
handleChangeFilters={this.handleChangeFilters}
/>
);
return (
<DocumentTitle title="Proposals">
<Row gutter={40}>
<Col lg={5} md={24}>
<ProposalFilters
sort={sort}
filters={filters}
handleChangeSort={this.handleChangeSort}
handleChangeFilters={this.handleChangeFilters}
/>
</Col>
<Styled.Container>
{isFiltersDrawered ? (
<Drawer
placement="right"
visible={isDrawerShowing}
onClose={this.closeFilterDrawer}
closable={false}
width={300}
>
{filtersComponent}
<Button
type="primary"
onClick={this.closeFilterDrawer}
style={{ marginTop: '1rem' }}
block
>
Done
</Button>
</Drawer>
) : (
<Styled.Filters>{filtersComponent}</Styled.Filters>
)}
<Col lg={19} md={24}>
<Styled.Results>
<Styled.Search>
<Input.Search
placeholder="Search for a proposal"
onChange={this.handleChangeSearch}
size="large"
/>
<Divider />
<ProposalResults
proposals={processedProposals}
proposalsError={proposalsError}
isFetchingProposals={isFetchingProposals}
/>
</Col>
</Row>
</DocumentTitle>
<Styled.SearchFilterButton
type="primary"
size="large"
onClick={this.openFilterDrawer}
>
<Icon type="filter" /> Filters
</Styled.SearchFilterButton>
</Styled.Search>
<Divider />
<ProposalResults
proposals={processedProposals}
proposalsError={proposalsError}
isFetchingProposals={isFetchingProposals}
/>
</Styled.Results>
</Styled.Container>
);
}
@ -166,6 +210,23 @@ class Proposals extends React.Component<Props, State> {
private handleChangeFilters = (filters: Filters) => {
this.setState({ filters });
};
private handleResize = () => {
if (this.state.isFiltersDrawered && window.innerWidth > 640) {
this.setState({
isFiltersDrawered: false,
isDrawerShowing: false,
});
} else if (!this.state.isFiltersDrawered && window.innerWidth <= 640) {
this.setState({
isFiltersDrawered: true,
isDrawerShowing: false,
});
}
};
private openFilterDrawer = () => this.setState({ isDrawerShowing: true });
private closeFilterDrawer = () => this.setState({ isDrawerShowing: false });
}
function mapStateToProps(state: AppState) {

View File

@ -0,0 +1,37 @@
import styled from 'styled-components';
import { Button } from 'antd';
const smallQuery = '@media (max-width: 640px)';
export const Container = styled.div`
display: flex;
flex-direction: row;
`;
export const Filters = styled.div`
width: 220px;
margin-right: 3rem;
${smallQuery} {
display: none;
}
`;
export const Results = styled.div`
flex: 1;
width: 100%;
`;
export const Search = styled.div`
display: flex;
`;
// Typescript throws errors if we don't cast as Button.
export const SearchFilterButton: typeof Button = styled(Button)`
display: none;
margin-left: 0.5rem;
${smallQuery} {
display: block;
}
` as any;