Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
code
data-on-a-map-app
Commits
bd8510c1
Commit
bd8510c1
authored
Nov 29, 2019
by
alain
🐙
Browse files
support multiple datastreams for sources
parent
aed1f90e
Changes
12
Hide whitespace changes
Inline
Side-by-side
config/example/texts.js
View file @
bd8510c1
...
...
@@ -8,7 +8,7 @@ export const texts = {
loading
:
"
Bezig met laden...
"
,
loadingError
:
"
Laden mislukt...
"
,
loadingRetry
:
"
Probeer opnieuw
"
,
nodata
:
"
G
een data...
"
,
nodata
:
"
g
een data...
"
,
lastMean
:
"
laatste uurgemiddelde
"
,
lastPeak
:
"
piekwaarde laatste uur
"
,
mean
:
"
gemiddelde
"
,
...
...
src/DeckLayers/StationLayer.js
View file @
bd8510c1
...
...
@@ -2,7 +2,7 @@ import { CompositeLayer } from '@deck.gl/core'
import
{
ColumnLayer
}
from
'
@deck.gl/layers
'
import
{
scaleLinear
,
scaleLog
}
from
"
d3-scale
"
import
{
c
olor
,
getColorArray
}
from
"
../util/color.js
"
import
{
getC
olor
,
getColorArray
}
from
"
../util/color.js
"
import
{
setMetersOffset
}
from
"
../util/plot
"
...
...
@@ -59,39 +59,30 @@ class StationLayer extends CompositeLayer {
}
return
[
// // max
// new ColumnLayer({
// ...this.props,
// id: `${id}-column-layer-max`,
// data,
// radius
// diskResolution: sides,
// opacity: 1,
// lightSettings,
// getPosition: d => centroid(d.coordinates, d.offset),
// getFillColor: d => getColorArray(lightenBy(color(d.max, legend), 0.25)),
// getElevation: d => elevation(d.max, zoom),
// updateTriggers: {
// getElevation: [zoom],
// getPosition: [radius]
// }
// }),
//mean
new
ColumnLayer
({
...
this
.
props
,
id
:
`
${
id
}
-column-layer-mean`
,
data
,
radius
,
diskResolution
:
sides
,
opacity
:
1
,
opacity
:
0.95
,
lightSettings
,
getElevation
:
d
=>
getHeight
(
d
.
mean
,
zoom
),
getPosition
:
d
=>
this
.
getCoordinates
(
d
,
count
,
radius
),
getFillColor
:
d
=>
{
if
(
d
.
mean
===
null
)
{
return
getColorArray
(
mapItemSettings
.
colorOffline
)
let
color
=
getColorArray
(
mapItemSettings
.
colorOffline
)
color
[
3
]
=
color
[
3
]
*
0.5
return
color
}
else
{
return
getColorArray
(
color
(
d
.
mean
,
legend
,
(
scale
===
'
log
'
)))
let
color
=
getColorArray
(
getColor
(
d
.
mean
,
legend
,
(
scale
===
'
log
'
)))
//if(d.dataAge > 3) color = changeColorLuminance(color, -0.015 * d.dataAge)
//if(d.dataAge > 4)
color
[
3
]
=
color
[
3
]
*
(
1
-
0.1
*
d
.
dataAge
)
return
color
}
},
updateTriggers
:
{
...
...
src/DeckLayers/StationTooltip.js
View file @
bd8510c1
...
...
@@ -3,27 +3,30 @@ import React from 'react'
import
{
texts
}
from
"
../../../config/texts
"
import
{
roundBy
}
from
"
../util/math.js
"
import
{
moment
}
from
"
../util/time.js
"
const
SensorTooltip
=
props
=>
{
const
{
d
,
label
,
unit
}
=
props
//const dataAgeText = d.dataAge < 1 ? texts.lastHour : `${roundBy(d.dataAge, 0)} ${texts.hoursAgo}`
const
dataAgeText
=
`
${
moment
(
d
.
timestamp
).
format
(
"
HH
"
)
}
-
${
moment
(
d
.
timestamp
).
add
(
1
,
'
hours
'
).
format
(
"
HH
"
)
}${
texts
.
hourShort
}
`
return
(
<
div
>
<
h4
>
{
d
.
name
}
<
/h4
>
<
em
>
{
label
}
<
/em
>
<
table
className
=
"
data
"
><
tbody
>
{
d
.
mean
!==
null
&&
<
tr
>
<
td
>
{
texts
.
lastMean
}:
<
/td
>
<
td
><
strong
>
{
roundBy
(
d
.
mean
,
1
)
}{
unit
}
<
/strong></
td
>
<
/tr
>
<
tr
>
<
td
>
{
`
${
texts
.
lastMean
}
(
${
dataAgeText
}
):`
}
<
/td
>
<
td
><
strong
>
{
roundBy
(
d
.
mean
,
1
)
}{
unit
}
<
/strong></
td
>
<
/tr
>
}
{
!!
d
.
max
&&
<
tr
>
<
td
>
{
texts
.
lastPeak
}:
<
/td
>
<
td
><
strong
>
{
roundBy
(
d
.
max
,
1
)
}{
unit
}
<
/strong></
td
>
<
/tr
>
{
d
.
mean
===
null
&&
<
tr
>
<
td
>
{
texts
.
nodata
}
<
/td
>
<
/tr
>
}
<
/tbody></
table
>
<
/div
>
...
...
src/Map/MapAttributions.js
View file @
bd8510c1
...
...
@@ -8,7 +8,7 @@ class MapAttributions extends React.Component {
<
div
className
=
"
mapboxgl-ctrl-bottom-right
"
>
<
div
className
=
"
mapboxgl-ctrl mapboxgl-ctrl-attrib
"
>
<
div
className
=
"
mapboxgl-ctrl-attrib-inner
"
>
<
span
>
v
.
1
8
/
11
/
2019
<
/span
>
<
span
>
v
.
2
8
/
11
/
2019
<
/span
>
<
span
><
a
href
=
"
https://waag.org
"
target
=
"
_blank
"
rel
=
"
noopener noreferrer
"
>
waag
<
/a></
span
>
<
span
>
©
<
a
href
=
"
http://www.openstreetmap.org/about/
"
target
=
"
_blank
"
rel
=
"
noopener noreferrer
"
>
OpenStreetMap
<
/a></
span
>
<
/div
>
...
...
src/Modal/ChartTooltip.js
View file @
bd8510c1
...
...
@@ -7,29 +7,49 @@ import { moment } from "../util/time.js"
const
ChartTooltip
=
(
props
)
=>
{
const
{
active
,
payload
,
label
,
unit
}
=
props
const
{
active
,
payload
,
label
,
unit
,
dataStreamsState
}
=
props
if
(
active
)
{
let
avg
,
max
const
title
=
<
h4
>
{
moment
(
label
).
format
(
"
D MMM
"
)
}
{
moment
(
label
).
format
(
"
HH:mm
"
)
}
-
{
moment
(
label
).
add
(
1
,
'
hours
'
).
format
(
"
HH:mm
"
)
}
{
texts
.
hour
}
<
/h4
>
let
body
if
(
payload
&&
payload
.
length
>
1
)
{
avg
=
payload
[
1
].
value
max
=
payload
[
0
].
value
[
1
]
}
else
if
(
payload
&&
payload
.
length
>
0
)
{
avg
=
payload
[
0
].
value
if
(
!
payload
)
{
body
=
<
p
>
{
texts
.
nodata
}
<
/p
>
}
else
{
avg
=
"
geen data
"
const
dataStreams
=
payload
[
0
]
?
payload
[
0
].
payload
:
{}
let
rows
=
[]
Object
.
keys
(
dataStreamsState
).
forEach
(
i
=>
{
const
headerRow
=
<
tr
key
=
{
i
}
><
th
>
{
texts
.
dataStreams
[
i
]}:
<
/th><th></
th
><
/tr
>
let
subRows
=
[]
Object
.
keys
(
dataStreamsState
[
i
]).
forEach
(
j
=>
{
const
s
=
dataStreamsState
[
i
][
j
]
if
(
s
.
active
)
{
const
value
=
dataStreams
[
s
.
key
]
let
text
text
=
Array
.
isArray
(
value
)
?
`
${
roundBy
(
value
[
0
],
1
)
}
-
${
roundBy
(
value
[
1
],
1
)
}
${
unit
}
`
:
`
${
roundBy
(
value
,
1
)}
${
unit
}
`
if
(
!
value
)
text
=
texts
.
nodata
subRows
.
push
(
<
tr
key
=
{
s
.
key
}
><
td
>
{
texts
.
dataStreams
[
j
]
}:
<
/td><td><strong>{ text }</
strong
><
/td></
tr
>
)
}
})
if
(
subRows
.
length
>
0
&&
Object
.
keys
(
dataStreamsState
).
length
>
1
)
rows
.
push
(
headerRow
)
rows
=
rows
.
concat
(
subRows
)
})
body
=
<
table
><
tbody
>
{
rows
}
<
/tbody></
table
>
}
return
(
<
div
className
=
"
tooltip-data
"
>
<
h4
>
{
moment
(
label
).
format
(
"
D MMM
"
)
}
{
moment
(
label
).
subtract
(
1
,
'
hours
'
).
format
(
"
HH:mm
"
)
}
-
{
moment
(
label
).
format
(
"
HH:mm
"
)
}
<
/h4
>
<
table
><
tbody
>
<
tr
><
td
>
{
texts
.
mean
}:
<
/td><td>{ roundBy
(
avg, 1
)
}{unit}</
td
><
/tr
>
{
!!
max
&&
<
tr
><
td
>
{
texts
.
peak
}:
<
/td><td>{ roundBy
(
max, 1
)
}{unit}</
td
><
/tr>
}
<
/tbody></
table
>
<
/div>
{
title
}
{
body
}
<
/div
>
)
}
return
null
...
...
src/Modal/StationInfo.js
View file @
bd8510c1
...
...
@@ -15,11 +15,11 @@ import IconArrowDown from "../Icons/IconArrowDown"
import
{
texts
}
from
"
../../../config/texts
"
import
{
appSettings
}
from
"
../../../config/app
"
import
{
moment
,
getNowISO
,
getDaysAgoHourISO
}
from
"
../util/time.js
"
import
{
moment
,
getNow
Hour
ISO
,
getDaysAgoHourISO
}
from
"
../util/time.js
"
const
start
=
getDaysAgoHourISO
(
90
)
const
end
=
getNowISO
()
const
end
=
getNow
Hour
ISO
()
class
StationInfo
extends
React
.
Component
{
...
...
@@ -36,7 +36,7 @@ class StationInfo extends React.Component {
downloadStart
:
moment
(
start
).
format
(
"
YYYY-MM-DD
"
),
downloadEnd
:
moment
(
end
).
format
(
"
YYYY-MM-DD
"
),
data
:
null
,
stationMeta
:
null
,
stationMeta
:
null
}
this
.
updateChartHeight
=
this
.
updateChartHeight
.
bind
(
this
)
...
...
@@ -57,7 +57,7 @@ class StationInfo extends React.Component {
const
daysToFetch
=
source
.
daysToFetch
const
start
=
getDaysAgoHourISO
(
daysToFetch
)
const
end
=
getNowISO
()
const
end
=
getNow
Hour
ISO
()
const
granularity
=
this
.
state
.
granularity
...
...
@@ -65,16 +65,14 @@ class StationInfo extends React.Component {
this
.
setState
({
stationMeta
})
source
.
stationData
(
station
,
parameter
.
id
,
start
,
end
,
granularity
).
then
(
response
=>
{
source
.
stationData
(
station
,
parameter
.
id
,
start
,
end
,
granularity
).
then
(
({
status
,
data
})
=>
{
if
(
this
.
mounted
)
{
if
(
response
===
'
error
'
)
{
if
(
status
===
'
error
'
)
{
this
.
setState
({
error
:
true
})
}
else
{
let
data
=
response
const
unit
=
(
parameter
.
units
?
parameter
.
units
.
find
(
x
=>
x
.
id
===
this
.
props
.
unitStatus
[
parameter
.
id
])
:
null
)
if
(
unit
.
conversion
)
{
data
=
response
.
map
(
d
=>
{
data
=
data
.
map
(
d
=>
{
d
.
value
=
unit
.
conversion
(
d
.
value
)
if
(
d
.
minmax
)
{
d
.
minmax
[
0
]
=
unit
.
conversion
(
d
.
minmax
[
0
])
...
...
@@ -84,7 +82,9 @@ class StationInfo extends React.Component {
})
}
this
.
setState
({
data
,
unit
})
this
.
setPlotSettings
(
source
.
dataStreams
[
parameter
.
id
])
this
.
setState
({
data
,
unit
})
if
(
data
.
length
>
0
)
{
const
startIndex
=
(
data
.
length
-
7
*
24
>
0
?
data
.
length
-
7
*
24
:
0
)
...
...
@@ -128,28 +128,91 @@ class StationInfo extends React.Component {
}
setPlotSettings
(
dataStreams
)
{
let
last
=
null
let
brushData
=
null
Object
.
keys
(
dataStreams
).
forEach
(
i
=>
{
Object
.
keys
(
dataStreams
[
i
]).
forEach
(
j
=>
{
if
(
dataStreams
[
i
][
j
].
type
===
"
line
"
)
brushData
=
dataStreams
[
i
][
j
].
key
})
})
Object
.
keys
(
dataStreams
).
forEach
(
i
=>
{
Object
.
keys
(
dataStreams
[
i
]).
forEach
(
j
=>
{
const
s
=
dataStreams
[
i
][
j
]
let
attrs
=
{}
if
(
s
.
active
&&
s
.
type
===
"
area
"
)
{
attrs
.
className
=
(
s
.
color
===
false
?
"
area secondary
"
:
"
area primary
"
)
}
if
(
s
.
active
&&
s
.
type
===
"
line
"
)
{
attrs
.
className
=
(
last
===
"
area
"
?
"
line-on-area
"
:
"
line
"
)
attrs
.
className
+=
(
s
.
color
===
false
?
"
secondary
"
:
"
primary
"
)
}
if
(
s
.
active
)
last
=
s
.
type
dataStreams
[
i
][
j
].
attrs
=
attrs
})
})
this
.
setState
({
dataStreams
,
brushData
})
}
toggleDataStream
(
i
,
j
)
{
const
dataStreams
=
this
.
state
.
dataStreams
dataStreams
[
i
][
j
].
active
=
!
dataStreams
[
i
][
j
].
active
this
.
setPlotSettings
(
dataStreams
)
}
render
()
{
moment
.
locale
(
appSettings
.
language
)
const
{
error
,
stationMeta
,
data
,
chartHeight
,
downloadForm
,
unit
,
tickFormat
,
ticks
}
=
this
.
state
const
{
id
,
source
}
=
this
.
props
.
clickedObject
const
{
error
,
stationMeta
,
data
,
dataStreams
,
brushData
,
chartHeight
,
downloadForm
,
unit
,
tickFormat
,
ticks
}
=
this
.
state
const
{
id
}
=
this
.
props
.
clickedObject
const
parameter
=
this
.
props
.
activeLayer
const
source
=
parameter
.
sources
.
find
(
o
=>
{
return
o
.
name
===
this
.
props
.
clickedObject
.
source
})
let
yScale
=
scaleLinear
()
if
(
unit
)
{
yScale
=
(
unit
.
scale
===
'
log
'
?
scaleLog
().
domain
(
unit
.
range
)
:
scaleLinear
().
domain
(
unit
.
range
))
}
const
lineStyle
=
(
data
&&
data
.
length
>
0
&&
data
[
0
].
minmax
?
{
stroke
:
"
#000000
"
}
:
{
stroke
:
"
url(#yaxis)
"
}
)
const
lineClass
=
(
data
&&
data
.
length
>
0
&&
data
[
0
].
minmax
?
"
dashed
"
:
"
solid
"
)
var
gradients
let
plots
=
[]
const
sourceObject
=
this
.
props
.
activeLayer
.
sources
.
find
(
o
=>
{
return
o
.
name
===
source
})
var
downloadJSX
if
(
sourceObject
.
download
)
{
if
(
data
)
{
const
stops
=
Object
.
keys
(
unit
.
legend
).
sort
((
a
,
b
)
=>
b
-
a
).
map
(
s
=>
<
stop
key
=
{
s
}
offset
=
{
`
${
100
-
(
yScale
(
s
)
*
100
)}
%`
}
stopColor
=
{
unit
.
legend
[
s
].
color
}
/>
)
gradients
=
(
<
defs
>
<
linearGradient
id
=
"
yaxis
"
x1
=
"
0
"
y1
=
"
0
"
x2
=
"
0
"
y2
=
{
chartHeight
-
80
}
gradientUnits
=
"
userSpaceOnUse
"
>
{
stops
}
<
/linearGradient
>
<
linearGradient
id
=
"
legend
"
x1
=
"
0
"
y1
=
"
0
"
x2
=
"
0
"
y2
=
"
12
"
gradientUnits
=
"
userSpaceOnUse
"
>
{
stops
}
<
/linearGradient
>
<
/defs
>
)
Object
.
keys
(
dataStreams
).
forEach
(
i
=>
{
Object
.
keys
(
dataStreams
[
i
]).
forEach
(
j
=>
{
const
s
=
dataStreams
[
i
][
j
]
if
(
s
.
active
&&
s
.
type
===
"
area
"
)
plots
.
push
(
<
Area
key
=
{
s
.
key
}
dataKey
=
{
s
.
key
}
{
...
s
.
attrs
}
type
=
"
natural
"
animationDuration
=
{
1
}
/>
)
if
(
s
.
active
&&
s
.
type
===
"
line
"
)
plots
.
push
(
<
Line
key
=
{
s
.
key
}
dataKey
=
{
s
.
key
}
{
...
s
.
attrs
}
type
=
"
natural
"
animationDuration
=
{
1
}
dot
=
{
false
}
activeDot
=
{{
r
:
3
}}
/>
)
})
})
}
let
downloadJSX
if
(
source
.
download
)
{
const
startValue
=
moment
(
start
).
format
(
"
YYYY-MM-DD
"
)
const
minValue
=
moment
(
source
Object
.
dataStart
).
format
(
"
YYYY-MM-DD
"
)
const
minValue
=
moment
(
source
.
dataStart
).
format
(
"
YYYY-MM-DD
"
)
const
maxValue
=
moment
(
end
).
format
(
"
YYYY-MM-DD
"
)
downloadJSX
=
...
...
@@ -161,7 +224,7 @@ class StationInfo extends React.Component {
<
div
>
{
texts
.
endDate
}:
<
input
className
=
"
input-text
"
type
=
"
date
"
id
=
"
end
"
name
=
"
end
"
defaultValue
=
{
maxValue
}
min
=
{
minValue
}
max
=
{
maxValue
}
onChange
=
{(
e
)
=>
{
this
.
setState
({
downloadEnd
:
e
.
target
.
value
})
}
}
/
>
<
/div
>
<
button
className
=
"
button-text
"
onClick
=
{
e
=>
{
window
.
location
.
href
=
source
Object
.
download
(
id
,
parameter
.
id
,
this
.
state
.
downloadStart
,
this
.
state
.
downloadEnd
)
}
}
>
<
button
className
=
"
button-text
"
onClick
=
{
e
=>
{
window
.
location
.
href
=
source
.
download
(
id
,
parameter
.
id
,
this
.
state
.
downloadStart
,
this
.
state
.
downloadEnd
)
}
}
>
{
texts
.
downloadCsv
}
<
/button
>
<
/div
>
...
...
@@ -186,43 +249,65 @@ class StationInfo extends React.Component {
{
(
data
&&
data
.
length
>
0
)
&&
<
div
>
<
div
id
=
"
chart-header
"
>
<
header
id
=
"
chart-header
"
>
<
h3
>
{
parameter
.
label
}
{
unit
.
label
}
{
texts
.
chartHeaderAddition
}
<
/h3
>
{
source
Object
.
download
&&
<
span
className
=
{
`toggle-link
${(
downloadForm
?
"
active
"
:
""
)}
`
}
onClick
=
{
()
=>
{
this
.
setState
({
downloadForm
:
!
downloadForm
})
}
}
>
{
source
.
download
&&
<
span
className
=
{
`toggle-link
${(
downloadForm
?
"
active
"
:
""
)}
`
}
onClick
=
{
()
=>
{
this
.
setState
({
downloadForm
:
!
downloadForm
})
}
}
>
{
texts
.
downloadData
}
<
IconArrowDown
/>
<
/span>
}
<
/
div
>
<
/
header
>
{
downloadJSX
}
<
div
id
=
"
chart-body
"
>
<
ResponsiveContainer
width
=
"
100%
"
height
=
{
chartHeight
}
>
<
ComposedChart
data
=
{
data
}
margin
=
{{
top
:
0
,
right
:
30
,
left
:
0
,
bottom
:
10
}}
>
{
gradients
}
<
XAxis
type
=
"
number
"
scale
=
"
time
"
domain
=
{[
'
auto
'
,
'
auto
'
]}
dataKey
=
"
timestamp
"
height
=
{
20
}
padding
=
{{
left
:
6
}}
tickFormatter
=
{()
=>
"
###
"
}
tickSize
=
{
4
}
ticks
=
{
ticks
[
0
]}
tick
=
{
<
ChartTick
tickFormat
=
{
tickFormat
[
0
]}
/>} /
>
<
XAxis
xAxisId
=
"
day
"
type
=
"
number
"
scale
=
"
time
"
domain
=
{[
'
auto
'
,
'
auto
'
]}
dataKey
=
"
timestamp
"
height
=
{
20
}
padding
=
{{
left
:
6
}}
axisLine
=
{
false
}
tickFormatter
=
{()
=>
"
####
"
}
tickSize
=
{
0
}
ticks
=
{
ticks
[
1
]}
tick
=
{
<
ChartTick
tickFormat
=
{
tickFormat
[
1
]}
/>} /
>
<
YAxis
type
=
"
number
"
strokeWidth
=
"
6
"
stroke
=
"
url(#yaxis)
"
width
=
{
30
}
ticks
=
{(
unit
.
ticks
?
unit
.
ticks
:
Object
.
keys
(
unit
.
legend
))}
height
=
{
chartHeight
}
domain
=
{
unit
.
range
}
tickSize
=
{
2
}
tickLine
=
{{
strokeWidth
:
1
}}
allowDataOverflow
=
{
true
}
scale
=
{
yScale
}
/
>
<
Tooltip
content
=
{
ChartTooltip
}
animationDuration
=
{
0
}
unit
=
{
unit
.
label
}
dataStreamsState
=
{
dataStreams
}
/
>
{
plots
}
<
Brush
dataKey
=
"
timestamp
"
height
=
{
40
}
fill
=
"
#eee
"
stroke
=
"
none
"
travellerWidth
=
{
4
}
onChange
=
{
indexes
=>
{
this
.
handleRangeChange
(
indexes
)
}
}
startIndex
=
{
(
data
.
length
-
7
*
24
>
0
?
data
.
length
-
7
*
24
:
0
)
}
tickFormatter
=
{
this
.
xAxisTickFormatter
}
>
<
LineChart
>
<
Line
type
=
"
natural
"
dataKey
=
{
brushData
}
stroke
=
"
#aaa
"
dot
=
{
false
}
/
>
<
YAxis
domain
=
{
unit
.
range
}
tick
=
{
false
}
width
=
{
0
}
scale
=
{
yScale
}
/
>
<
/LineChart
>
<
/Brush
>
<
/ComposedChart
>
<
/ResponsiveContainer
>
<
/div
>
<
ResponsiveContainer
width
=
"
100%
"
height
=
{
chartHeight
}
>
<
ComposedChart
data
=
{
data
}
margin
=
{{
top
:
0
,
right
:
30
,
left
:
0
,
bottom
:
10
}}
>
<
defs
>
<
linearGradient
id
=
"
yaxis
"
x1
=
"
0
"
y1
=
"
0
"
x2
=
"
0
"
y2
=
{
chartHeight
-
80
}
gradientUnits
=
"
userSpaceOnUse
"
>
{
Object
.
keys
(
unit
.
legend
).
sort
((
a
,
b
)
=>
b
-
a
).
map
(
s
=>
<
stop
key
=
{
s
}
offset
=
{
`
${
100
-
(
yScale
(
s
)
*
100
)}
%`
}
stopColor
=
{
unit
.
legend
[
s
].
color
}
/>
)
}
<
/linearGradient
>
<
/defs
>
<
XAxis
type
=
"
number
"
scale
=
"
time
"
domain
=
{[
'
auto
'
,
'
auto
'
]}
dataKey
=
"
timestamp
"
height
=
{
20
}
padding
=
{{
left
:
6
}}
tickFormatter
=
{()
=>
"
###
"
}
tickSize
=
{
4
}
ticks
=
{
ticks
[
0
]}
tick
=
{
<
ChartTick
tickFormat
=
{
tickFormat
[
0
]}
/>} /
>
<
XAxis
xAxisId
=
"
day
"
type
=
"
number
"
scale
=
"
time
"
domain
=
{[
'
auto
'
,
'
auto
'
]}
dataKey
=
"
timestamp
"
height
=
{
20
}
padding
=
{{
left
:
6
}}
axisLine
=
{
false
}
tickFormatter
=
{()
=>
"
####
"
}
tickSize
=
{
0
}
ticks
=
{
ticks
[
1
]}
tick
=
{
<
ChartTick
tickFormat
=
{
tickFormat
[
1
]}
/>} /
>
<
YAxis
type
=
"
number
"
strokeWidth
=
"
6
"
stroke
=
"
url(#yaxis)
"
width
=
{
30
}
ticks
=
{(
unit
.
ticks
?
unit
.
ticks
:
Object
.
keys
(
unit
.
legend
))}
height
=
{
chartHeight
}
domain
=
{
unit
.
range
}
tickSize
=
{
2
}
tickLine
=
{{
strokeWidth
:
1
}}
allowDataOverflow
=
{
true
}
scale
=
{
yScale
}
/
>
<
Tooltip
content
=
{
ChartTooltip
}
animationDuration
=
{
0
}
unit
=
{
unit
.
label
}
/
>
<
Area
type
=
"
natural
"
dataKey
=
"
minmax
"
stroke
=
"
none
"
fill
=
"
url(#yaxis)
"
fillOpacity
=
{
0.5
}
/
>
<
Line
className
=
{
lineClass
}
type
=
"
natural
"
dataKey
=
"
value
"
{
...
lineStyle
}
dot
=
{
false
}
strokeWidth
=
{
1
}
activeDot
=
{{
r
:
3
}}
/
>
<
Brush
dataKey
=
"
timestamp
"
height
=
{
40
}
fill
=
"
#eee
"
stroke
=
"
none
"
travellerWidth
=
{
4
}
onChange
=
{
indexes
=>
{
this
.
handleRangeChange
(
indexes
)
}
}
startIndex
=
{
(
data
.
length
-
7
*
24
>
0
?
data
.
length
-
7
*
24
:
0
)
}
tickFormatter
=
{
this
.
xAxisTickFormatter
}
>
<
LineChart
>
<
Line
type
=
"
natural
"
dataKey
=
"
value
"
stroke
=
"
#aaa
"
dot
=
{
false
}
/
>
<
YAxis
domain
=
{
unit
.
range
}
tick
=
{
false
}
width
=
{
0
}
scale
=
{
yScale
}
/
>
<
/LineChart
>
<
/Brush
>
<
/ComposedChart
>
<
/ResponsiveContainer
>
<
footer
id
=
"
chart-footer
"
>
<
span
className
=
"
hint-time-selection
"
>
{
texts
.
timeSelectionHint
}
<
/span
>
{
Object
.
keys
(
dataStreams
).
length
>
1
&&
<
ul
id
=
"
chart-data-selection
"
>
{
Object
.
keys
(
dataStreams
).
map
(
i
=>
<
li
key
=
{
i
}
className
=
"
data-stream
"
>
<
h4
>
{
texts
.
dataStreams
[
i
]
}
<
/h4
>
<
ul
>
{
Object
.
keys
(
dataStreams
[
i
]).
map
(
j
=>
<
li
key
=
{
j
}
className
=
{
dataStreams
[
i
][
j
].
active
?
"
option active
"
:
"
option
"
}
onClick
=
{
e
=>
{
this
.
toggleDataStream
(
i
,
j
)
}
}
>
<
span
className
=
"
switch
"
><
/span
>
{
texts
.
dataStreams
[
j
]
}
{
dataStreams
[
i
][
j
].
active
&&
<
svg
width
=
"
16
"
height
=
"
16
"
>
{
dataStreams
[
i
][
j
].
type
===
"
area
"
&&
<
path
d
=
"
M0 16v-4c5 0 6-7 8-7s3 7 8 7v4c-3 0-5-3-8-3s-5 3-8 3z
"
{
...
dataStreams
[
i
][
j
].
attrs
}
/>
}
{
dataStreams
[
i
][
j
].
type
===
"
line
"
&&
<
path
d
=
"
M0 13.5c3 0 4-5 8-5s5 5 8 5
"
fill
=
"
none
"
stroke
=
"
#000
"
{
...
dataStreams
[
i
][
j
].
attrs
}
/>
}
<
/svg
>
}
<
/li>
)
}
<
/ul
>
<
/li
>
)
}
<
/ul
>
}
<
/footer
>
<
/div
>
}
<
/div
>
...
...
src/css/_recharts.scss
View file @
bd8510c1
...
...
@@ -13,6 +13,36 @@
}
}
#chart-footer
{
display
:
flex
;
flex-direction
:
row-reverse
;
justify-content
:
space-between
;
padding
:
0
30px
;
margin-top
:
0
;
@media
(
max-width
:
600px
)
{
flex-direction
:
column
;
.hint-time-selection
{
margin-bottom
:
1rem
;
text-align
:
right
;
}
}
@media
(
max-width
:
600px
)
{
padding
:
0
;
}
ul
{
margin
:
0
;
padding-left
:
0
;
}
li
{
list-style
:
none
;
}
}
#download-data
{
margin
:
-1em
0
1em
0
;
max-height
:
0
;
...
...
@@ -39,14 +69,59 @@
}
input
{
//margin-bottom: 0.25em;
font-size
:
0
.9rem
;
}
.button-text
{
margin-right
:
0
;
margin-bottom
:
0
;
}
}
#download-data
{
a
.more
{
margin-right
:
0
;
&
:hover
{
margin-right
:
-0
.3em
;
}
}
}
#chart-data-selection
{
display
:
flex
;
flex-wrap
:
wrap
;
.data-stream
{
margin
:
0
.5rem
1rem
0
.5rem
0
;
}
.option
{
position
:
relative
;
height
:
16px
;
margin-top
:
0
.25rem
;
padding-right
:
20px
;
cursor
:
pointer
;
svg
{
position
:
absolute
;
top
:
-2px
;
right
:
0
;
}
}
}
.recharts-wrapper
{
user-select
:
none
;
}
#chart-body
{
@media
(
max-width
:
400px
)
{
margin
:
0
-20px
;
}
}
.xAxis
,
.yAxis
{
text
{
...
...
@@ -94,10 +169,52 @@ rect.recharts-brush-slide {
}
}
.recharts-line.dashed
path
{
stroke-dasharray
:
3px
;
@mixin
line-on-area
{
stroke-width
:
1px
;
stroke-dasharray
:
2px
;
}
@mixin
line-on-area-primary
{
@include
line-on-area
;
stroke
:
#000
;
}
@mixin
line-on-area-secondary
{
@include
line-on-area
;
stroke
:
#bbb
;
}
.recharts-line.solid
path
{
stroke-width
:
1
.5px
;
}
\ No newline at end of file
@mixin
line
{
stroke-width
:
1
.5px
;
}
@mixin
line-primary
{
@include
line
;
stroke
:
url(#yaxis)
;
}
@mixin
line-secondary
{