diff --git a/components/CheckVisible.js b/components/CheckVisible.js new file mode 100644 index 00000000..94b97730 --- /dev/null +++ b/components/CheckVisible.js @@ -0,0 +1,34 @@ +import React, { useState, useRef, useEffect } from 'react'; + +function isInViewport(node) { + return ( + window.pageYOffset < node.offsetTop + node.clientHeight || + window.pageXOffset < node.offsetLeft + node.clientWidth + ); +} + +export default function CheckVisible({ children }) { + const [visible, setVisible] = useState(false); + const ref = useRef(); + + useEffect(() => { + const checkPosition = () => { + if (ref.current) { + const state = isInViewport(ref.current); + if (state !== visible) { + setVisible(state); + } + } + }; + + checkPosition(); + + window.addEventListener('scroll', checkPosition); + + return () => { + window.removeEventListener('scroll', checkPosition); + }; + }, [visible]); + + return
{typeof children === 'function' ? children(visible) : children}
; +} diff --git a/components/PageviewsChart.js b/components/PageviewsChart.js index 34cd55d9..9d601243 100644 --- a/components/PageviewsChart.js +++ b/components/PageviewsChart.js @@ -5,7 +5,14 @@ import ChartJS from 'chart.js'; import { format } from 'date-fns'; import styles from './PageviewsChart.module.css'; -export default function PageviewsChart({ websiteId, data, unit, className, children }) { +export default function PageviewsChart({ + websiteId, + data, + unit, + animationDuration = 300, + className, + children, +}) { const canvas = useRef(); const chart = useRef(); const [tooltip, setTooltip] = useState({}); @@ -75,7 +82,7 @@ export default function PageviewsChart({ websiteId, data, unit, className, child }, options: { animation: { - duration: 300, + duration: animationDuration, }, tooltips: { enabled: false, @@ -125,6 +132,7 @@ export default function PageviewsChart({ websiteId, data, unit, className, child datasets[1].data = data.pageviews; options.scales.xAxes[0].time.unit = unit; options.scales.xAxes[0].ticks.callback = renderLabel; + options.animation.duration = animationDuration; chart.current.update(); } @@ -133,6 +141,7 @@ export default function PageviewsChart({ websiteId, data, unit, className, child useEffect(() => { if (data) { draw(); + setTooltip(null); } }, [data]); diff --git a/components/RankingsChart.js b/components/RankingsChart.js index 60ed030c..c445a4fb 100644 --- a/components/RankingsChart.js +++ b/components/RankingsChart.js @@ -14,6 +14,7 @@ export default function RankingsChart({ heading, className, dataFilter, + animate = true, onDataLoad = () => {}, }) { const [data, setData] = useState(); @@ -45,7 +46,7 @@ export default function RankingsChart({ }, [websiteId, startDate, endDate, type]); if (!data) { - return

loading...

; + return null; } return ( @@ -54,14 +55,29 @@ export default function RankingsChart({
{title}
{heading}
- {rankings.map(({ x, y, z }) => ( - - ))} + {rankings.map(({ x, y, z }) => + animate ? ( + + ) : ( + + ), + )} ); } -const Row = ({ label, value, percent }) => { +const Row = ({ label, value, percent }) => ( +
+
{label}
+
{value.toFixed(0)}
+
+
{`${percent.toFixed(0)}%`}
+
+
+
+); + +const AnimatedRow = ({ label, value, percent }) => { const props = useSpring({ width: percent, y: value, from: { width: 0, y: 0 } }); return ( diff --git a/components/WebsiteChart.js b/components/WebsiteChart.js index 5512ebe8..cdcbdb7c 100644 --- a/components/WebsiteChart.js +++ b/components/WebsiteChart.js @@ -13,6 +13,7 @@ export default function WebsiteChart({ websiteId, defaultDateRange = '7day', stickHeader = false, + animate = true, onDateChange = () => {}, }) { const [data, setData] = useState(); @@ -80,6 +81,7 @@ export default function WebsiteChart({ websiteId={websiteId} data={{ pageviews, uniques }} unit={unit} + animationDuration={animate ? 300 : 0} > diff --git a/components/WebsiteDetails.js b/components/WebsiteDetails.js index d87cf878..e13015c5 100644 --- a/components/WebsiteDetails.js +++ b/components/WebsiteDetails.js @@ -3,6 +3,7 @@ import classNames from 'classnames'; import WebsiteChart from './WebsiteChart'; import RankingsChart from './RankingsChart'; import WorldMap from './WorldMap'; +import CheckVisible from './CheckVisible'; import { getDateRange } from 'lib/date'; import { get } from 'lib/web'; import { browserFilter, urlFilter, refFilter, deviceFilter, countryFilter } from 'lib/filters'; @@ -32,7 +33,7 @@ export default function WebsiteDetails({ websiteId, defaultDateRange = '7day' }) }, [websiteId]); if (!data) { - return

loading...

; + return null; } return ( @@ -40,82 +41,121 @@ export default function WebsiteDetails({ websiteId, defaultDateRange = '7day' })

{data.label}

- + + {visible => ( + + )} +
-
+
- + + {visible => ( + + )} +
- + + {visible => ( + + )} +
-
+
- + + {visible => ( + + )} +
- + + {visible => ( + + )} +
- + + {visible => ( + + )} +
-
+
- setCountryData(data)} - /> + + {visible => ( + setCountryData(data)} + animate={visible} + /> + )} +
diff --git a/components/WebsiteDetails.module.css b/components/WebsiteDetails.module.css index baabd4a7..7f90b36f 100644 --- a/components/WebsiteDetails.module.css +++ b/components/WebsiteDetails.module.css @@ -15,3 +15,7 @@ border-left: 0; padding-left: 0; } + +.row > [class*='col-']:last-child { + padding-right: 0; +} diff --git a/lib/filters.js b/lib/filters.js index 5a84c498..a510f392 100644 --- a/lib/filters.js +++ b/lib/filters.js @@ -209,7 +209,7 @@ const isoCountries = { QA: 'Qatar', RE: 'Reunion', RO: 'Romania', - RU: 'Russian Federation', + RU: 'Russia', RW: 'Rwanda', BL: 'Saint Barthelemy', SH: 'Saint Helena',