Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Luc Didry
kresus
Commits
d8ddb8b9
Commit
d8ddb8b9
authored
Feb 28, 2019
by
Nicolas Frandeboeuf
Committed by
Nicolas Frandeboeuf
Mar 05, 2019
Browse files
Operations: add toolbar
parent
f21735d5
Changes
6
Hide whitespace changes
Inline
Side-by-side
client/components/operations/index.js
View file @
d8ddb8b9
...
...
@@ -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
}
...
...
client/components/operations/search.js
View file @
d8ddb8b9
...
...
@@ -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
>
);
}
}
...
...
client/components/ui/clearable-input.js
View file @
d8ddb8b9
...
...
@@ -24,6 +24,10 @@ class ClearableInput extends React.PureComponent {
this
.
onChange
(
''
);
};
clear
=
()
=>
{
this
.
setState
({
value
:
''
});
};
render
()
{
return
(
<
div
className
=
"
input-with-addon clearable-input
"
>
...
...
client/themes/dark/style.css
View file @
d8ddb8b9
...
...
@@ -289,22 +289,11 @@ main nav .bank-details > li > .accounts > li a.active .amount.negative {
align-self
:
center
;
}
.operation-t
able
caption
{
.operation-t
oolbar
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
;
}
...
...
client/themes/default/style.css
View file @
d8ddb8b9
...
...
@@ -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-t
able
caption
button
.label
{
.operation-t
oolbar
button
.label
{
display
:
none
;
}
...
...
client/themes/light/style.css
View file @
d8ddb8b9
...
...
@@ -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
;
}
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment