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 { IconLabel } from '@/components/common/IconLabel';
|
||||
import Link from '@/components/common/Link';
|
||||
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||
import { useGlobalState, useMessages, useNavigation } from '@/components/hooks';
|
||||
import {
|
||||
Globe,
|
||||
@@ -103,7 +104,7 @@ export function SideNav(props: any) {
|
||||
<PanelButton />
|
||||
</Row>
|
||||
</Row>
|
||||
<Column flexGrow="1" minHeight="0" style={{ overflowY: 'auto', overflowX: 'hidden' }}>
|
||||
<OverlayScrollArea style={{ flexGrow: 1, minHeight: 0 }}>
|
||||
{websiteId ? (
|
||||
<WebsiteNav websiteId={websiteId} isCollapsed={isCollapsed} />
|
||||
) : pathname.includes('/settings') ? (
|
||||
@@ -147,7 +148,7 @@ export function SideNav(props: any) {
|
||||
})}
|
||||
</Column>
|
||||
)}
|
||||
</Column>
|
||||
</OverlayScrollArea>
|
||||
<Row
|
||||
paddingTop="2"
|
||||
width="100%"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Column, Grid, Row } from '@umami/react-zen';
|
||||
import { WebsiteExpandedMenu } from '@/app/(main)/websites/[websiteId]/WebsiteExpandedMenu';
|
||||
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||
import { MetricsExpandedTable } from '@/components/metrics/MetricsExpandedTable';
|
||||
@@ -40,9 +41,12 @@ export function WebsiteExpandedView({
|
||||
gap="6"
|
||||
border="right"
|
||||
paddingRight="3"
|
||||
overflow="auto"
|
||||
minHeight="0"
|
||||
overflow="hidden"
|
||||
>
|
||||
<WebsiteExpandedMenu excludedIds={excludedIds} />
|
||||
<OverlayScrollArea style={{ height: '100%' }}>
|
||||
<WebsiteExpandedMenu excludedIds={excludedIds} />
|
||||
</OverlayScrollArea>
|
||||
</Column>
|
||||
<Column id="metrics-expanded-table" overflow="hidden">
|
||||
<MetricsExpandedTable title={title} type={view} websiteId={websiteId} onClose={onClose} />
|
||||
|
||||
@@ -18,29 +18,6 @@ html[style*="padding-right"] {
|
||||
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 */
|
||||
input:is(:-webkit-autofill, :autofill),
|
||||
input:is(:-webkit-autofill, :autofill):hover,
|
||||
|
||||
@@ -52,17 +52,17 @@ export function NavMenu({
|
||||
};
|
||||
|
||||
return (
|
||||
<Column gap overflowY="auto" justifyContent="space-between" position="sticky">
|
||||
<Column gap justifyContent="space-between" position="sticky">
|
||||
{title && (
|
||||
<Row padding>
|
||||
<Heading size="lg">{title}</Heading>
|
||||
</Row>
|
||||
)}
|
||||
<Column gap="6" {...props}>
|
||||
<Column gap="3" {...props}>
|
||||
{items?.map(({ label, items }, index) => {
|
||||
if (label) {
|
||||
return (
|
||||
<Column key={`${label}${index}`} gap="1" marginBottom="3" minHeight="40px">
|
||||
<Column key={`${label}${index}`} gap="1" minHeight="40px">
|
||||
<Row paddingX="3" marginTop="2">
|
||||
<Text weight="bold">{label}</Text>
|
||||
</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 { type ReactNode, useState } from 'react';
|
||||
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
||||
import { OverlayScrollArea } from '@/components/common/OverlayScrollArea';
|
||||
import { useMessages, useWebsiteExpandedMetricsQuery } from '@/components/hooks';
|
||||
import { X } from '@/components/icons';
|
||||
import { DownloadButton } from '@/components/input/DownloadButton';
|
||||
@@ -76,55 +77,57 @@ export function MetricsExpandedTable({
|
||||
height="100%"
|
||||
loadingIcon="spinner"
|
||||
>
|
||||
<Column overflow="auto" minHeight="0" height="100%" paddingRight="3">
|
||||
{items && (
|
||||
<DataTable data={items}>
|
||||
<DataColumn id="label" label={title} width="minmax(200px, 2fr)" align="start">
|
||||
{row => (
|
||||
<Row overflow="hidden">
|
||||
<MetricLabel type={type} data={row} />
|
||||
</Row>
|
||||
)}
|
||||
</DataColumn>
|
||||
<DataColumn id="visitors" label={t(labels.visitors)} align="end" width="120px">
|
||||
{row => row?.visitors?.toLocaleString()}
|
||||
</DataColumn>
|
||||
<DataColumn id="visits" label={t(labels.visits)} align="end" width="120px">
|
||||
{row => row?.visits?.toLocaleString()}
|
||||
</DataColumn>
|
||||
<DataColumn id="pageviews" label={t(labels.views)} align="end" width="120px">
|
||||
{row => row?.pageviews?.toLocaleString()}
|
||||
</DataColumn>
|
||||
{showBounceDuration && [
|
||||
<DataColumn
|
||||
key="bounceRate"
|
||||
id="bounceRate"
|
||||
label={t(labels.bounceRate)}
|
||||
align="end"
|
||||
width="120px"
|
||||
>
|
||||
{row => {
|
||||
const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
|
||||
return `${Math.round(+n)}%`;
|
||||
}}
|
||||
</DataColumn>,
|
||||
<OverlayScrollArea orientation="both" style={{ height: '100%', minHeight: 0 }}>
|
||||
<Column paddingRight="3">
|
||||
{items && (
|
||||
<DataTable data={items}>
|
||||
<DataColumn id="label" label={title} width="minmax(200px, 2fr)" align="start">
|
||||
{row => (
|
||||
<Row overflow="hidden">
|
||||
<MetricLabel type={type} data={row} />
|
||||
</Row>
|
||||
)}
|
||||
</DataColumn>
|
||||
<DataColumn id="visitors" label={t(labels.visitors)} align="end" width="120px">
|
||||
{row => row?.visitors?.toLocaleString()}
|
||||
</DataColumn>
|
||||
<DataColumn id="visits" label={t(labels.visits)} align="end" width="120px">
|
||||
{row => row?.visits?.toLocaleString()}
|
||||
</DataColumn>
|
||||
<DataColumn id="pageviews" label={t(labels.views)} align="end" width="120px">
|
||||
{row => row?.pageviews?.toLocaleString()}
|
||||
</DataColumn>
|
||||
{showBounceDuration && [
|
||||
<DataColumn
|
||||
key="bounceRate"
|
||||
id="bounceRate"
|
||||
label={t(labels.bounceRate)}
|
||||
align="end"
|
||||
width="120px"
|
||||
>
|
||||
{row => {
|
||||
const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
|
||||
return `${Math.round(+n)}%`;
|
||||
}}
|
||||
</DataColumn>,
|
||||
|
||||
<DataColumn
|
||||
key="visitDuration"
|
||||
id="visitDuration"
|
||||
label={t(labels.visitDuration)}
|
||||
align="end"
|
||||
width="120px"
|
||||
>
|
||||
{row => {
|
||||
const n = row?.totaltime / row?.visits;
|
||||
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
|
||||
}}
|
||||
</DataColumn>,
|
||||
]}
|
||||
</DataTable>
|
||||
)}
|
||||
</Column>
|
||||
<DataColumn
|
||||
key="visitDuration"
|
||||
id="visitDuration"
|
||||
label={t(labels.visitDuration)}
|
||||
align="end"
|
||||
width="120px"
|
||||
>
|
||||
{row => {
|
||||
const n = row?.totaltime / row?.visits;
|
||||
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
|
||||
}}
|
||||
</DataColumn>,
|
||||
]}
|
||||
</DataTable>
|
||||
)}
|
||||
</Column>
|
||||
</OverlayScrollArea>
|
||||
</LoadingPanel>
|
||||
</>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user