Replace global scrollbars with themed overlay scroll areas
This commit is contained in:
1 parent
85c4c1863d
commit
8a05dfee58
7 files changed
+114
-78
No files matched your search
@@ -13,6 +13,7 @@ import { SettingsNav } from '@/app/(main)/settings/SettingsNav';
|
|||||||
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
|
||||||
import { IconLabel } from '@/components/common/IconLabel';
|
import { IconLabel } from '@/components/common/IconLabel';
|
||||||
import Link from '@/components/common/Link';
|
import Link from '@/components/common/Link';
|
||||||
|
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||||
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
|
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
|
||||||
import {
|
import {
|
||||||
Globe,
|
Globe,
|
||||||
@@ -103,7 +104,7 @@ export function SideNav(props: any) {
|
|||||||
<PanelButton />
|
<PanelButton />
|
||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
<Column flexGrow="1" minHeight="0" style={{ overflowY: 'auto', overflowX: 'hidden' }}>
|
<OverlayScrollArea style={{ flexGrow: 1, minHeight: 0 }}>
|
||||||
{websiteId ? (
|
{websiteId ? (
|
||||||
<WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} />
|
<WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} />
|
||||||
) : pathname.includes('/settings') ? (
|
) : pathname.includes('/settings') ? (
|
||||||
@@ -147,7 +148,7 @@ export function SideNav(props: any) {
|
|||||||
})}
|
})}
|
||||||
</Column>
|
</Column>
|
||||||
)}
|
)}
|
||||||
</Column>
|
</OverlayScrollArea>
|
||||||
<Row
|
<Row
|
||||||
paddingTop="2"
|
paddingTop="2"
|
||||||
width="100%"
|
width="100%"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Column, Grid, Row } from '@umami/react-zen';
|
import { Column, Grid, Row } from '@umami/react-zen';
|
||||||
import { WebsiteExpandedMenu } from '@/app/(main)/websites/[websiteId]/WebsiteExpandedMenu';
|
import { WebsiteExpandedMenu } from '@/app/(main)/websites/[websiteId]/WebsiteExpandedMenu';
|
||||||
|
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||||
import { useMessages, useNavigation } from '@/components/hooks';
|
import { useMessages, useNavigation } from '@/components/hooks';
|
||||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||||
import { MetricsExpandedTable } from '@/components/metrics/MetricsExpandedTable';
|
import { MetricsExpandedTable } from '@/components/metrics/MetricsExpandedTable';
|
||||||
@@ -40,9 +41,12 @@ export function WebsiteExpandedView({
|
|||||||
gap="6"
|
gap="6"
|
||||||
border="right"
|
border="right"
|
||||||
paddingRight="3"
|
paddingRight="3"
|
||||||
overflow="auto"
|
minHeight="0"
|
||||||
|
overflow="hidden"
|
||||||
>
|
>
|
||||||
<WebsiteExpandedMenu excludedIds={excludedIds} />
|
<OverlayScrollArea style={{ height: '100%' }}>
|
||||||
|
<WebsiteExpandedMenu excludedIds={excludedIds} />
|
||||||
|
</OverlayScrollArea>
|
||||||
</Column>
|
</Column>
|
||||||
<Column id="metrics-expanded-table" overflow="hidden">
|
<Column id="metrics-expanded-table" overflow="hidden">
|
||||||
<MetricsExpandedTable title={title} type={view} websiteId={websiteId} onClose={onClose} />
|
<MetricsExpandedTable title={title} type={view} websiteId={websiteId} onClose={onClose} />
|
||||||
|
|||||||
@@ -18,29 +18,6 @@ html[style*="padding-right"] {
|
|||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 15px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
border: 7px solid rgba(0, 0, 0, 0);
|
|
||||||
background-color: var(--border-default);
|
|
||||||
background-clip: padding-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
border: 7px solid rgba(0, 0, 0, 0);
|
|
||||||
background-color: var(--text-disabled);
|
|
||||||
border-radius: var(--border-radius-full);
|
|
||||||
background-clip: padding-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
border: 4px solid rgba(0, 0, 0, 0);
|
|
||||||
background-clip: padding-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix autofill background color to match theme */
|
/* Fix autofill background color to match theme */
|
||||||
input:is(:-webkit-autofill, :autofill),
|
input:is(:-webkit-autofill, :autofill),
|
||||||
input:is(:-webkit-autofill, :autofill):hover,
|
input:is(:-webkit-autofill, :autofill):hover,
|
||||||
|
|||||||
@@ -52,17 +52,17 @@ export function NavMenu({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap overflowY="auto" justifyContent="space-between" position="sticky">
|
<Column gap justifyContent="space-between" position="sticky">
|
||||||
{title && (
|
{title && (
|
||||||
<Row padding>
|
<Row padding>
|
||||||
<Heading size="lg">{title}</Heading>
|
<Heading size="lg">{title}</Heading>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
<Column gap="6" {...props}>
|
<Column gap="3" {...props}>
|
||||||
{items?.map(({ label, items }, index) => {
|
{items?.map(({ label, items }, index) => {
|
||||||
if (label) {
|
if (label) {
|
||||||
return (
|
return (
|
||||||
<Column key={`${label}${index}`} gap="1" marginBottom="3" minHeight="40px">
|
<Column key={`${label}${index}`} gap="1" minHeight="40px">
|
||||||
<Row paddingX="3" marginTop="2">
|
<Row paddingX="3" marginTop="2">
|
||||||
<Text weight="bold">{label}</Text>
|
<Text weight="bold">{label}</Text>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
.root {
|
||||||
|
--overlay-scrollbar-track: var(--gray-50);
|
||||||
|
--overlay-scrollbar-thumb: var(--gray-300);
|
||||||
|
--overlay-scrollbar-thumb-hover: var(--gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation="vertical"] {
|
||||||
|
width: 4px;
|
||||||
|
margin: 16px 2px;
|
||||||
|
background-color: var(--overlay-scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation="horizontal"] {
|
||||||
|
height: 4px;
|
||||||
|
margin: 2px 16px;
|
||||||
|
background-color: var(--overlay-scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation] > div {
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--overlay-scrollbar-thumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation] > div:hover,
|
||||||
|
.root > [data-orientation] > div:active {
|
||||||
|
background-color: var(--overlay-scrollbar-thumb-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation="vertical"] > div::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 -4px;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.root > [data-orientation="horizontal"] > div::before {
|
||||||
|
position: absolute;
|
||||||
|
inset: -4px 0;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
:global([data-theme="dark"]) .root {
|
||||||
|
--overlay-scrollbar-track: var(--gray-800);
|
||||||
|
--overlay-scrollbar-thumb: var(--gray-600);
|
||||||
|
--overlay-scrollbar-thumb-hover: var(--gray-500);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { cn, type ScrollAreaProps, ScrollArea as ZenScrollArea } from '@umami/react-zen';
|
||||||
|
import styles from './OverlayScrollArea.module.css';
|
||||||
|
|
||||||
|
export function OverlayScrollArea({ className, ...props }: ScrollAreaProps) {
|
||||||
|
return <ZenScrollArea className={cn(styles.root, className)} {...props} />;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Button, Column, DataColumn, DataTable, Icon, Row, SearchField } from '@umami/react-zen';
|
import { Button, Column, DataColumn, DataTable, Icon, Row, SearchField } from '@umami/react-zen';
|
||||||
import { type ReactNode, useState } from 'react';
|
import { type ReactNode, useState } from 'react';
|
||||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||||
|
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||||
import { useMessages, useWebsiteExpandedMetricsQuery } from '@/components/hooks';
|
import { useMessages, useWebsiteExpandedMetricsQuery } from '@/components/hooks';
|
||||||
import { X } from '@/components/icons';
|
import { X } from '@/components/icons';
|
||||||
import { DownloadButton } from '@/components/input/DownloadButton';
|
import { DownloadButton } from '@/components/input/DownloadButton';
|
||||||
@@ -76,55 +77,57 @@ export function MetricsExpandedTable({
|
|||||||
height="100%"
|
height="100%"
|
||||||
loadingIcon="spinner"
|
loadingIcon="spinner"
|
||||||
>
|
>
|
||||||
<Column overflow="auto" minHeight="0" height="100%" paddingRight="3">
|
<OverlayScrollArea orientation="both" style={{ height: '100%', minHeight: 0 }}>
|
||||||
{items && (
|
<Column paddingRight="3">
|
||||||
<DataTable data={items}>
|
{items && (
|
||||||
<DataColumn id="label" label={title} width="minmax(200px, 2fr)" align="start">
|
<DataTable data={items}>
|
||||||
{row => (
|
<DataColumn id="label" label={title} width="minmax(200px, 2fr)" align="start">
|
||||||
<Row overflow="hidden">
|
{row => (
|
||||||
<MetricLabel type={type} data={row} />
|
<Row overflow="hidden">
|
||||||
</Row>
|
<MetricLabel type={type} data={row} />
|
||||||
)}
|
</Row>
|
||||||
</DataColumn>
|
)}
|
||||||
<DataColumn id="visitors" label={t(labels.visitors)} align="end" width="120px">
|
</DataColumn>
|
||||||
{row => row?.visitors?.toLocaleString()}
|
<DataColumn id="visitors" label={t(labels.visitors)} align="end" width="120px">
|
||||||
</DataColumn>
|
{row => row?.visitors?.toLocaleString()}
|
||||||
<DataColumn id="visits" label={t(labels.visits)} align="end" width="120px">
|
</DataColumn>
|
||||||
{row => row?.visits?.toLocaleString()}
|
<DataColumn id="visits" label={t(labels.visits)} align="end" width="120px">
|
||||||
</DataColumn>
|
{row => row?.visits?.toLocaleString()}
|
||||||
<DataColumn id="pageviews" label={t(labels.views)} align="end" width="120px">
|
</DataColumn>
|
||||||
{row => row?.pageviews?.toLocaleString()}
|
<DataColumn id="pageviews" label={t(labels.views)} align="end" width="120px">
|
||||||
</DataColumn>
|
{row => row?.pageviews?.toLocaleString()}
|
||||||
{showBounceDuration && [
|
</DataColumn>
|
||||||
<DataColumn
|
{showBounceDuration && [
|
||||||
key="bounceRate"
|
<DataColumn
|
||||||
id="bounceRate"
|
key="bounceRate"
|
||||||
label={t(labels.bounceRate)}
|
id="bounceRate"
|
||||||
align="end"
|
label={t(labels.bounceRate)}
|
||||||
width="120px"
|
align="end"
|
||||||
>
|
width="120px"
|
||||||
{row => {
|
>
|
||||||
const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
|
{row => {
|
||||||
return `${Math.round(+n)}%`;
|
const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
|
||||||
}}
|
return `${Math.round(+n)}%`;
|
||||||
</DataColumn>,
|
}}
|
||||||
|
</DataColumn>,
|
||||||
|
|
||||||
<DataColumn
|
<DataColumn
|
||||||
key="visitDuration"
|
key="visitDuration"
|
||||||
id="visitDuration"
|
id="visitDuration"
|
||||||
label={t(labels.visitDuration)}
|
label={t(labels.visitDuration)}
|
||||||
align="end"
|
align="end"
|
||||||
width="120px"
|
width="120px"
|
||||||
>
|
>
|
||||||
{row => {
|
{row => {
|
||||||
const n = row?.totaltime / row?.visits;
|
const n = row?.totaltime / row?.visits;
|
||||||
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
|
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
|
||||||
}}
|
}}
|
||||||
</DataColumn>,
|
</DataColumn>,
|
||||||
]}
|
]}
|
||||||
</DataTable>
|
</DataTable>
|
||||||
)}
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
|
</OverlayScrollArea>
|
||||||
</LoadingPanel>
|
</LoadingPanel>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user