mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Updated sticky header logic.
This commit is contained in:
parent
8532c673fe
commit
f3e1f18e1b
@ -1,32 +0,0 @@
|
|||||||
import { useMeasure } from 'react-basics';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import useSticky from 'hooks/useSticky';
|
|
||||||
|
|
||||||
export default function StickyHeader({
|
|
||||||
className,
|
|
||||||
stickyClassName,
|
|
||||||
stickyStyle,
|
|
||||||
enabled = true,
|
|
||||||
scrollElement,
|
|
||||||
children,
|
|
||||||
}) {
|
|
||||||
const { ref: scrollRef, isSticky } = useSticky({ scrollElement });
|
|
||||||
const { ref: measureRef, dimensions } = useMeasure();
|
|
||||||
const active = enabled && isSticky;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={measureRef}
|
|
||||||
data-sticky={active}
|
|
||||||
style={active ? { height: dimensions.height } : null}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={scrollRef}
|
|
||||||
className={classNames(className, { [stickyClassName]: active })}
|
|
||||||
style={active ? { ...stickyStyle, width: dimensions.width } : null}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -2,11 +2,11 @@ import { useMemo } from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Button, Icon, Text, Row, Column } from 'react-basics';
|
import { Button, Icon, Text, Row, Column } from 'react-basics';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import classNames from 'classnames';
|
||||||
import PageviewsChart from './PageviewsChart';
|
import PageviewsChart from './PageviewsChart';
|
||||||
import MetricsBar from './MetricsBar';
|
import MetricsBar from './MetricsBar';
|
||||||
import WebsiteHeader from './WebsiteHeader';
|
import WebsiteHeader from './WebsiteHeader';
|
||||||
import DateFilter from 'components/input/DateFilter';
|
import DateFilter from 'components/input/DateFilter';
|
||||||
import StickyHeader from 'components/common/StickyHeader';
|
|
||||||
import ErrorMessage from 'components/common/ErrorMessage';
|
import ErrorMessage from 'components/common/ErrorMessage';
|
||||||
import FilterTags from 'components/metrics/FilterTags';
|
import FilterTags from 'components/metrics/FilterTags';
|
||||||
import RefreshButton from 'components/input/RefreshButton';
|
import RefreshButton from 'components/input/RefreshButton';
|
||||||
@ -16,9 +16,9 @@ import useTimezone from 'hooks/useTimezone';
|
|||||||
import usePageQuery from 'hooks/usePageQuery';
|
import usePageQuery from 'hooks/usePageQuery';
|
||||||
import { getDateArray, getDateLength } from 'lib/date';
|
import { getDateArray, getDateLength } from 'lib/date';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import { UI_LAYOUT_BODY } from 'lib/constants';
|
|
||||||
import { labels } from 'components/messages';
|
import { labels } from 'components/messages';
|
||||||
import styles from './WebsiteChart.module.css';
|
import styles from './WebsiteChart.module.css';
|
||||||
|
import useSticky from '../../hooks/useSticky';
|
||||||
|
|
||||||
export default function WebsiteChart({
|
export default function WebsiteChart({
|
||||||
websiteId,
|
websiteId,
|
||||||
@ -37,6 +37,7 @@ export default function WebsiteChart({
|
|||||||
query: { url, referrer, os, browser, device, country },
|
query: { url, referrer, os, browser, device, country },
|
||||||
} = usePageQuery();
|
} = usePageQuery();
|
||||||
const { get, useQuery } = useApi();
|
const { get, useQuery } = useApi();
|
||||||
|
const { ref, isSticky } = useSticky({ enabled: stickyHeader });
|
||||||
|
|
||||||
const { data, isLoading, error } = useQuery(
|
const { data, isLoading, error } = useQuery(
|
||||||
['websites:pageviews', websiteId, modified, url, referrer, os, browser, device, country],
|
['websites:pageviews', websiteId, modified, url, referrer, os, browser, device, country],
|
||||||
@ -81,12 +82,13 @@ export default function WebsiteChart({
|
|||||||
)}
|
)}
|
||||||
</WebsiteHeader>
|
</WebsiteHeader>
|
||||||
<FilterTags websiteId={websiteId} params={{ url, referrer, os, browser, device, country }} />
|
<FilterTags websiteId={websiteId} params={{ url, referrer, os, browser, device, country }} />
|
||||||
<StickyHeader
|
<Row
|
||||||
stickyClassName={styles.sticky}
|
ref={ref}
|
||||||
enabled={stickyHeader}
|
className={classNames(styles.header, {
|
||||||
scrollElement={document.getElementById(UI_LAYOUT_BODY)}
|
[styles.sticky]: stickyHeader,
|
||||||
|
[styles.isSticky]: isSticky,
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<Row className={styles.header}>
|
|
||||||
<Column>
|
<Column>
|
||||||
<MetricsBar websiteId={websiteId} />
|
<MetricsBar websiteId={websiteId} />
|
||||||
</Column>
|
</Column>
|
||||||
@ -95,7 +97,6 @@ export default function WebsiteChart({
|
|||||||
<DateFilter websiteId={websiteId} value={value} className={styles.dropdown} />
|
<DateFilter websiteId={websiteId} value={value} className={styles.dropdown} />
|
||||||
</Column>
|
</Column>
|
||||||
</Row>
|
</Row>
|
||||||
</StickyHeader>
|
|
||||||
<Row>
|
<Row>
|
||||||
<Column className={styles.chart}>
|
<Column className={styles.chart}>
|
||||||
{error && <ErrorMessage />}
|
{error && <ErrorMessage />}
|
||||||
|
@ -17,22 +17,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 10px 0;
|
||||||
min-height: 90px;
|
min-height: 90px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
background: var(--base50);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sticky {
|
.sticky {
|
||||||
position: fixed;
|
position: sticky;
|
||||||
top: 0;
|
top: -1px;
|
||||||
background: var(--base50);
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isSticky {
|
||||||
border-bottom: 1px solid var(--base300);
|
border-bottom: 1px solid var(--base300);
|
||||||
z-index: 3;
|
|
||||||
width: inherit;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
@ -6,7 +6,6 @@ import firstBy from 'thenby';
|
|||||||
import { GridRow, GridColumn } from 'components/layout/Grid';
|
import { GridRow, GridColumn } from 'components/layout/Grid';
|
||||||
import Page from 'components/layout/Page';
|
import Page from 'components/layout/Page';
|
||||||
import RealtimeChart from 'components/metrics/RealtimeChart';
|
import RealtimeChart from 'components/metrics/RealtimeChart';
|
||||||
import StickyHeader from 'components/common/StickyHeader';
|
|
||||||
import PageHeader from 'components/layout/PageHeader';
|
import PageHeader from 'components/layout/PageHeader';
|
||||||
import WorldMap from 'components/common/WorldMap';
|
import WorldMap from 'components/common/WorldMap';
|
||||||
import RealtimeLog from 'components/pages/realtime/RealtimeLog';
|
import RealtimeLog from 'components/pages/realtime/RealtimeLog';
|
||||||
@ -104,9 +103,7 @@ export default function RealtimeDashboard({ websiteId }) {
|
|||||||
<PageHeader title={formatMessage(labels.realtime)}>
|
<PageHeader title={formatMessage(labels.realtime)}>
|
||||||
<WebsiteSelect websiteId={websiteId} onSelect={handleSelect} />
|
<WebsiteSelect websiteId={websiteId} onSelect={handleSelect} />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<StickyHeader stickyClassName={styles.sticky}>
|
|
||||||
<RealtimeHeader websiteId={websiteId} data={currentData} />
|
<RealtimeHeader websiteId={websiteId} data={currentData} />
|
||||||
</StickyHeader>
|
|
||||||
<div className={styles.chart}>
|
<div className={styles.chart}>
|
||||||
<RealtimeChart data={realtimeData} unit="minute" records={REALTIME_RANGE} />
|
<RealtimeChart data={realtimeData} unit="minute" records={REALTIME_RANGE} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
|
||||||
export default function useSticky({ scrollElement = document, defaultSticky = false }) {
|
export default function useSticky({ defaultSticky = false, enabled = true }) {
|
||||||
const [isSticky, setIsSticky] = useState(defaultSticky);
|
const [isSticky, setIsSticky] = useState(defaultSticky);
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const initialTop = useRef(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
let observer;
|
||||||
setIsSticky((scrollElement?.scrollTop ?? window.scrollY) > initialTop.current);
|
const handler = ([entry]) => setIsSticky(entry.intersectionRatio < 1);
|
||||||
};
|
|
||||||
|
|
||||||
if (initialTop.current === null) {
|
if (enabled && ref.current) {
|
||||||
initialTop.current = ref?.current?.offsetTop;
|
observer = new IntersectionObserver(handler, { threshold: [1] });
|
||||||
|
observer.observe(ref.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollElement.addEventListener('scroll', handleScroll, true);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
scrollElement.removeEventListener('scroll', handleScroll, true);
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [ref, setIsSticky, scrollElement]);
|
}, [ref]);
|
||||||
|
|
||||||
return { ref, isSticky };
|
return { ref, isSticky };
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
"node-fetch": "^3.2.8",
|
"node-fetch": "^3.2.8",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-basics": "^0.73.0",
|
"react-basics": "^0.74.0",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-intl": "^5.24.7",
|
"react-intl": "^5.24.7",
|
||||||
|
@ -7076,10 +7076,10 @@ rc@^1.2.7:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
react-basics@^0.73.0:
|
react-basics@^0.74.0:
|
||||||
version "0.73.0"
|
version "0.74.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.73.0.tgz#9555563f3407ac417dc833dfca47588123d55535"
|
resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.74.0.tgz#153433bc485d6b71d8edf377d1a83f1d55133e24"
|
||||||
integrity sha512-eEK8yWWrXO7JATBlPKBfFQlD1hNZoNeEtlYNx+QjOCLKu1qjClutP5nXWHmX4gHE97XFwUKzbTU35NkNEy5C0w==
|
integrity sha512-Z9XwgEOSRvcPqFqFZL6HR59t/XrqhIB8uoYwbmon3IFX2W0kOPqkX1Box0c+2BibJoHp4N4mbfuZWK2kSEnq9g==
|
||||||
dependencies:
|
dependencies:
|
||||||
classnames "^2.3.1"
|
classnames "^2.3.1"
|
||||||
date-fns "^2.29.3"
|
date-fns "^2.29.3"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user