Mobile-friendly search filters (#37)
* Remove affixed filters. Replace with responsive drawer filters. * Fix ts error
This commit is contained in:
parent
0e3c6c6fc0
commit
e4779d1410
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
return (
|
||||
<DocumentTitle title="Proposals">
|
||||
<Row gutter={40}>
|
||||
<Col lg={5} md={24}>
|
||||
const {
|
||||
processedProposals,
|
||||
sort,
|
||||
filters,
|
||||
isFiltersDrawered,
|
||||
isDrawerShowing,
|
||||
} = this.state;
|
||||
const filtersComponent = (
|
||||
<ProposalFilters
|
||||
sort={sort}
|
||||
filters={filters}
|
||||
handleChangeSort={this.handleChangeSort}
|
||||
handleChangeFilters={this.handleChangeFilters}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
return (
|
||||
<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"
|
||||
/>
|
||||
<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}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</DocumentTitle>
|
||||
</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) {
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue