diff --git a/components/DateFilter.js b/components/DateFilter.js index 2889dde3..7b7838dd 100644 --- a/components/DateFilter.js +++ b/components/DateFilter.js @@ -13,10 +13,12 @@ const filterOptions = [ { label: 'This year', value: '1year' }, ]; -export default function DateFilter({ value, onChange }) { +export default function DateFilter({ value, onChange, className }) { function handleChange(value) { onChange(getDateRange(value)); } - return ; + return ( + + ); } diff --git a/components/DropDown.js b/components/DropDown.js index 0f6d3362..4f36b77a 100644 --- a/components/DropDown.js +++ b/components/DropDown.js @@ -1,7 +1,8 @@ import React, { useState, useEffect, useRef } from 'react'; +import classNames from 'classnames'; import styles from './Dropdown.module.css'; -export default function DropDown({ value, options = [], onChange }) { +export default function DropDown({ value, options = [], onChange, className }) { const [showMenu, setShowMenu] = useState(false); const ref = useRef(); @@ -30,7 +31,7 @@ export default function DropDown({ value, options = [], onChange }) { }, [ref]); return ( -
+
{options.find(e => e.value === value).label}
diff --git a/components/Dropdown.module.css b/components/Dropdown.module.css index b210de2b..1d85f0d8 100644 --- a/components/Dropdown.module.css +++ b/components/Dropdown.module.css @@ -5,6 +5,8 @@ } .value { + white-space: nowrap; + position: relative; padding: 4px 32px 4px 16px; border: 1px solid #b3b3b3; border-radius: 4px; diff --git a/components/MetricsBar.js b/components/MetricsBar.js index c6ca569e..6a6c20a1 100644 --- a/components/MetricsBar.js +++ b/components/MetricsBar.js @@ -1,10 +1,11 @@ import React, { useState, useEffect } from 'react'; +import classNames from 'classnames'; import MetricCard from './MetricCard'; -import { get } from '../lib/web'; +import { get } from 'lib/web'; import { formatShortTime } from 'lib/format'; import styles from './MetricsBar.module.css'; -export default function MetricsBar({ websiteId, startDate, endDate }) { +export default function MetricsBar({ websiteId, startDate, endDate, className }) { const [data, setData] = useState({}); const { pageviews, uniques, bounces, totaltime } = data; @@ -19,10 +20,10 @@ export default function MetricsBar({ websiteId, startDate, endDate }) { useEffect(() => { loadData(); - }, [startDate, endDate]); + }, [websiteId, startDate, endDate]); return ( -
+
+
{children} diff --git a/components/RankingsChart.js b/components/RankingsChart.js index 894553c0..60ed030c 100644 --- a/components/RankingsChart.js +++ b/components/RankingsChart.js @@ -62,18 +62,15 @@ export default function RankingsChart({ } const Row = ({ label, value, percent }) => { - const props = useSpring({ width: percent, from: { width: 0 } }); - const valueProps = useSpring({ y: value, from: { y: 0 } }); + const props = useSpring({ width: percent, y: value, from: { width: 0, y: 0 } }); return (
{label}
- - {valueProps.y.interpolate(n => n.toFixed(0))} - + {props.y.interpolate(n => n.toFixed(0))}
{props.width.interpolate(n => `${n.toFixed(0)}%`)} - +
); diff --git a/components/RankingsChart.module.css b/components/RankingsChart.module.css index 11b04ccf..76d3cdb8 100644 --- a/components/RankingsChart.module.css +++ b/components/RankingsChart.module.css @@ -2,18 +2,7 @@ position: relative; min-height: 430px; font-size: 14px; - border-left: 1px solid #e1e1e1; - border-top: 1px solid #e1e1e1; - padding: 20px; -} - -.container:first-child { - padding-left: 0; - border-left: 0; -} - -.container:last-child { - padding-right: 0; + padding: 20px 0; } .header { diff --git a/components/WebsiteChart.js b/components/WebsiteChart.js index 6b883316..45c7d83c 100644 --- a/components/WebsiteChart.js +++ b/components/WebsiteChart.js @@ -1,4 +1,5 @@ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; +import classNames from 'classnames'; import PageviewsChart from './PageviewsChart'; import { get } from 'lib/web'; import { getDateArray, getDateRange, getTimezone } from 'lib/date'; @@ -6,15 +7,19 @@ import MetricsBar from './MetricsBar'; import QuickButtons from './QuickButtons'; import styles from './WebsiteChart.module.css'; import DateFilter from './DateFilter'; +import useSticky from './hooks/useSticky'; export default function WebsiteChart({ websiteId, defaultDateRange = '7day', + stickHeader = false, onDateChange = () => {}, }) { const [data, setData] = useState(); const [dateRange, setDateRange] = useState(getDateRange(defaultDateRange)); const { startDate, endDate, unit, value } = dateRange; + const [ref, sticky] = useSticky(stickHeader); + const width = useRef(); const [pageviews, uniques] = useMemo(() => { if (data) { @@ -46,15 +51,34 @@ export default function WebsiteChart({ loadData(); }, [websiteId, startDate, endDate, unit]); + useEffect(() => { + width.current = document.querySelector('main').offsetWidth; + }, [sticky]); + return ( -
-
- - + <> +
+ +
- - - -
+
+ + + +
+ ); } diff --git a/components/WebsiteChart.module.css b/components/WebsiteChart.module.css index afdff3d5..f68aa7de 100644 --- a/components/WebsiteChart.module.css +++ b/components/WebsiteChart.module.css @@ -15,3 +15,13 @@ align-items: center; margin-bottom: 10px; } + +.sticky { + position: fixed; + top: 0; + margin: auto; + background: #fff; + padding: 10px 0; + border-bottom: 1px solid #e1e1e1; + z-index: 1; +} diff --git a/components/WebsiteDetails.js b/components/WebsiteDetails.js index c874f852..d87cf878 100644 --- a/components/WebsiteDetails.js +++ b/components/WebsiteDetails.js @@ -40,75 +40,83 @@ export default function WebsiteDetails({ websiteId, defaultDateRange = '7day' })

{data.label}

- +
- - +
+ +
+
+ +
- - - +
+ +
+
+ +
+
+ +
-
- - setCountryData(data)} - /> +
+
+ +
+
+ setCountryData(data)} + /> +
); diff --git a/components/WebsiteDetails.module.css b/components/WebsiteDetails.module.css index b8e18de4..baabd4a7 100644 --- a/components/WebsiteDetails.module.css +++ b/components/WebsiteDetails.module.css @@ -1,3 +1,17 @@ .chart { margin-bottom: 30px; } + +.row { + border-top: 1px solid #e1e1e1; +} + +.row > [class*='col-'] { + border-left: 1px solid #e1e1e1; + padding: 0 20px; +} + +.row > [class*='col-']:first-child { + border-left: 0; + padding-left: 0; +} diff --git a/components/WebsiteList.js b/components/WebsiteList.js index e0667d1b..9bc60542 100644 --- a/components/WebsiteList.js +++ b/components/WebsiteList.js @@ -19,14 +19,14 @@ export default function WebsiteList() {
{data && data.websites.map(({ website_id, website_uuid, label }) => ( - <> +

{label}

- +
))}
); diff --git a/components/WorldMap.module.css b/components/WorldMap.module.css index 464e3847..c2528038 100644 --- a/components/WorldMap.module.css +++ b/components/WorldMap.module.css @@ -1,5 +1,4 @@ .container { overflow: hidden; position: relative; - border-top: 1px solid #e1e1e1; } diff --git a/components/hooks/useSticky.js b/components/hooks/useSticky.js new file mode 100644 index 00000000..751217e1 --- /dev/null +++ b/components/hooks/useSticky.js @@ -0,0 +1,31 @@ +import { useState, useEffect, useCallback, useRef } from 'react'; + +export default function useSticky(enabled) { + const [node, setNode] = useState(null); + const [sticky, setSticky] = useState(false); + const offsetTop = useRef(0); + + const ref = useCallback(node => { + offsetTop.current = node?.offsetTop; + setNode(node); + }, []); + + useEffect(() => { + const checkPosition = () => { + const state = window.pageYOffset > offsetTop.current; + if (node && sticky !== state) { + setSticky(state); + } + }; + + if (enabled) { + window.addEventListener('scroll', checkPosition); + } + + return () => { + window.removeEventListener('scroll', checkPosition); + }; + }, [node, sticky, enabled]); + + return [ref, sticky]; +} diff --git a/pages/_app.js b/pages/_app.js index 9ee10778..aa32a01c 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,6 +1,6 @@ import React from 'react'; -import 'styles/index.css'; import 'styles/bootstrap-grid.css'; +import 'styles/index.css'; export default function App({ Component, pageProps }) { return ; diff --git a/styles/index.css b/styles/index.css index 28f7fcee..20090acd 100644 --- a/styles/index.css +++ b/styles/index.css @@ -43,3 +43,13 @@ select { border: 1px solid #b3b3b3; border-radius: 4px; } + +.row { + margin-right: 0; + margin-left: 0; +} +.row > .col, +.row > [class*='col-'] { + padding-right: 0; + padding-left: 0; +}