Commit d8ddb8b9 authored by Nicolas Frandeboeuf's avatar Nicolas Frandeboeuf Committed by Nicolas Frandeboeuf
Browse files

Operations: add toolbar

parent f21735d5
......@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { translate as $t, formatDate } from '../../helpers';
import { get } from '../../store';
import { get, actions } from '../../store';
import InfiniteList from '../ui/infinite-list';
......@@ -18,6 +18,29 @@ import AddOperationModalButton from './add-operation-button';
const OPERATION_BALLAST = 10;
const CONTAINER_ID = 'content';
const SearchButton = connect(
null,
dispatch => {
return {
handleClick() {
actions.toggleSearchDetails(dispatch);
}
};
}
)(props => {
return (
<button
type="button"
className="btn transparent"
aria-label={$t('client.search.title')}
onClick={props.handleClick}
title={$t('client.search.title')}>
<span className="fa fa-search" />
<span className="label">{$t('client.search.title')}</span>
</button>
);
});
// Keep in sync with style.css.
function getOperationHeight(isSmallScreen) {
return isSmallScreen ? 41 : 55;
......@@ -123,19 +146,23 @@ class OperationsComponent extends React.Component {
/>
</div>
<SearchComponent />
<div className="operation-toolbar">
<ul>
<li>
<SearchButton />
</li>
<li>
<SyncButton account={this.props.account} />
</li>
<li>
<AddOperationModalButton accountId={this.props.account.id} />
</li>
</ul>
<SearchComponent />
</div>
<table className="operation-table" ref={this.refOperationTable}>
<caption ref={this.refTableCaption}>
{/* captions cannot be set a 'display: flex' so a div child is used here */}
<div>
<h3>{$t('client.operations.title')}</h3>
<div className="actions">
<SyncButton account={this.props.account} />
<AddOperationModalButton accountId={this.props.account.id} />
</div>
</div>
</caption>
<caption ref={this.refTableCaption}>{$t('client.operations.title')}</caption>
<thead ref={this.refThead}>
<tr>
{maybeDetailsButtonHeader}
......
......@@ -8,8 +8,8 @@ import { translate as $t, UNKNOWN_OPERATION_TYPE, NONE_CATEGORY_ID } from '../..
import { get, actions } from '../../store';
import AmountInput from '../ui/amount-input';
import ClearableInput from '../ui/clearable-input';
import DatePicker from '../ui/date-picker';
import FoldablePanel from '../ui/foldable-panel';
import FuzzyOrNativeSelect from '../ui/fuzzy-or-native-select';
const ANY_TYPE_ID = '';
......@@ -114,11 +114,10 @@ const SearchCategorySelect = connect(
});
const MinDatePicker = connect(
(state, props) => {
state => {
return {
value: get.searchFields(state).dateLow,
maxDate: get.searchFields(state).dateHigh,
ref: props.refCb
maxDate: get.searchFields(state).dateHigh
};
},
dispatch => {
......@@ -135,11 +134,10 @@ const MinDatePicker = connect(
)(DatePicker);
const MaxDatePicker = connect(
(state, props) => {
state => {
return {
value: get.searchFields(state).dateHigh,
minDate: get.searchFields(state).dateLow,
ref: props.refCb
minDate: get.searchFields(state).dateLow
};
},
dispatch => {
......@@ -160,22 +158,30 @@ class SearchComponent extends React.Component {
super(props);
this.handleClearSearchNoClose = this.handleClearSearch.bind(this, false);
this.handleClearSearchAndClose = this.handleClearSearch.bind(this, true);
this.searchForm = null;
this.lowAmountInput = null;
this.highAmountInput = null;
this.lowDatePicker = null;
this.highDatePicker = null;
}
refKeywordsInput = React.createRef();
refLowAmountInput = React.createRef();
refHighAmountInput = React.createRef();
handleKeyword = value => {
this.props.setKeywords(value);
};
handleAmountLow = value => {
this.props.setAmountLow(Number.isNaN(value) ? null : value);
};
handleAmountHigh = value => {
this.props.setAmountHigh(Number.isNaN(value) ? null : value);
};
handleClearSearch(close, event) {
this.searchForm.reset();
this.lowAmountInput.clear();
this.highAmountInput.clear();
this.props.resetAll();
if (close) {
this.refSearchPanel.handleToggleExpand();
}
this.refKeywordsInput.current.clear();
this.refLowAmountInput.current.clear();
this.refHighAmountInput.current.clear();
this.props.resetAll(!close);
event.preventDefault();
}
......@@ -184,109 +190,73 @@ class SearchComponent extends React.Component {
}
render() {
let handleKeyword = event => {
this.props.setKeywords(event.target.value);
};
let handleAmountLow = value => {
this.props.setAmountLow(Number.isNaN(value) ? null : value);
};
let handleAmountHigh = value => {
this.props.setAmountHigh(Number.isNaN(value) ? null : value);
};
let refSearchPanel = node => {
this.refSearchPanel = node;
};
let refSearchForm = node => {
this.searchForm = node;
};
let refLowAmountInput = node => {
this.lowAmountInput = node;
};
let refHighAmountInput = node => {
this.highAmountInput = node;
};
let refLowDatePicker = node => {
this.lowDatePicker = node;
};
let refHighDatePicker = node => {
this.highDatePicker = node;
};
return (
<FoldablePanel
className="search-panel"
title={$t('client.search.title')}
initiallyExpanded={this.props.displaySearchDetails}
ref={refSearchPanel}>
<form ref={refSearchForm} className="search">
<p className="search-keywords">
<label htmlFor="keywords">{$t('client.search.keywords')}</label>
<input
type="text"
className="form-element-block"
onChange={handleKeyword}
id="keywords"
/>
</p>
<div className="search-categories-types">
<label htmlFor="category-selector">{$t('client.search.category')}</label>
<SearchCategorySelect id="category-selector" />
<label htmlFor="type-selector">{$t('client.search.type')}</label>
<SearchTypeSelect id="type-selector" />
</div>
<div className="search-amounts">
<label htmlFor="amount-low">{$t('client.search.amount_low')}</label>
<AmountInput
onChange={handleAmountLow}
id="amount-low"
ref={refLowAmountInput}
signId="search-sign-amount-low"
/>
<label htmlFor="amount-high">{$t('client.search.amount_high')}</label>
<AmountInput
onChange={handleAmountHigh}
id="amount-high"
ref={refHighAmountInput}
signId="search-sign-amount-high"
/>
</div>
<div className="search-dates">
<label htmlFor="date-low">{$t('client.search.date_low')}</label>
<MinDatePicker id="date-low" refCb={refLowDatePicker} />
<label htmlFor="date-high">{$t('client.search.date_high')}</label>
<MaxDatePicker id="date-high" refCb={refHighDatePicker} />
</div>
<p className="search-buttons">
<button
className="btn warning"
type="button"
onClick={this.handleClearSearchNoClose}>
{$t('client.search.clear')}
</button>
<button
className="btn warning"
type="button"
onClick={this.handleClearSearchAndClose}>
{$t('client.search.clearAndClose')}
</button>
</p>
</form>
</FoldablePanel>
<form className="search" hidden={!this.props.displaySearchDetails}>
<div className="search-keywords">
<label htmlFor="keywords">{$t('client.search.keywords')}</label>
<ClearableInput
ref={this.refKeywordsInput}
onChange={this.handleKeyword}
id="keywords"
/>
</div>
<div className="search-categories-types">
<label>{$t('client.search.category')}</label>
<SearchCategorySelect />
<label>{$t('client.search.type')}</label>
<SearchTypeSelect />
</div>
<div className="search-amounts">
<label htmlFor="amount-low">{$t('client.search.amount_low')}</label>
<AmountInput
onChange={this.handleAmountLow}
id="amount-low"
ref={this.refLowAmountInput}
signId="search-sign-amount-low"
/>
<label htmlFor="amount-high">{$t('client.search.amount_high')}</label>
<AmountInput
onChange={this.handleAmountHigh}
id="amount-high"
ref={this.refHighAmountInput}
signId="search-sign-amount-high"
/>
</div>
<div className="search-dates">
<label htmlFor="date-low">{$t('client.search.date_low')}</label>
<MinDatePicker id="date-low" />
<label htmlFor="date-high">{$t('client.search.date_high')}</label>
<MaxDatePicker id="date-high" />
</div>
<p className="search-buttons">
<button
className="btn warning"
type="button"
onClick={this.handleClearSearchNoClose}>
{$t('client.search.clear')}
</button>
<button
className="btn warning"
type="button"
onClick={this.handleClearSearchAndClose}>
{$t('client.search.clearAndClose')}
</button>
</p>
</form>
);
}
}
......
......@@ -24,6 +24,10 @@ class ClearableInput extends React.PureComponent {
this.onChange('');
};
clear = () => {
this.setState({ value: '' });
};
render() {
return (
<div className="input-with-addon clearable-input">
......
......@@ -289,22 +289,11 @@ main nav .bank-details > li > .accounts > li a.active .amount.negative {
align-self: center;
}
.operation-table caption {
.operation-toolbar ul {
background-color: transparent;
border: none;
}
.operation-table caption > div {
justify-content: flex-end;
}
@media (max-width: 620px) {
.operation-table caption > div {
justify-content: center;
}
}
.operation-table caption h3 {
.operation-table caption {
display: none;
}
......
......@@ -432,6 +432,41 @@ main nav .bank-details > li > .accounts > li a {
}
}
.operation-toolbar {
margin-bottom: 1em;
padding: 0;
border: 1px solid #CFD9DB;
border-radius: 3px;
}
.operation-toolbar ul {
display: flex;
min-height: 40px;
margin: 0;
padding: 0 1em;
background-color: #F1F1F1;
align-items: center;
}
.operation-toolbar ul li {
margin-left: 1em;
list-style: none;
}
.operation-toolbar ul li:first-child {
margin-left: 0;
flex-grow: 1;
}
.operation-toolbar ul li button {
white-space: normal;
}
.operation-toolbar > form {
border-top: 1px solid #CFD9DB;
padding: 1em;
}
/* Search */
form.search > * {
display: grid;
......@@ -500,8 +535,12 @@ form.search p.search-buttons button:first-child {
font-size: 14px;
}
.operation-table caption .actions > button {
text-align: inherit;
.operation-table caption {
height: 40px;
line-height: 40px;
font-size: 1em;
color: #777777;
font-weight: 600;
}
.operation-table > tbody > tr {
......@@ -591,7 +630,7 @@ button.budget-assignment {
* background.
*/
@media screen and (max-width: 768px) {
.operation-table caption button .label {
.operation-toolbar button .label {
display: none;
}
......
......@@ -218,30 +218,14 @@ main nav .bank-details > li > .accounts > li a.active .amount.negative {
align-self: center;
}
.operation-table caption {
border: none;
padding-left: 0;
padding-right: 0;
}
.operation-table caption > div {
justify-content: flex-end;
}
@media (max-width: 620px) {
.operation-table caption > div {
justify-content: center;
}
.operation-toolbar ul {
background-color: transparent;
}
.operation-table caption h3 {
.operation-table caption {
display: none;
}
.operation-table caption .actions {
margin-left: 0;
}
.operation-table thead th {
border-top: none;
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment