Skip to content
Snippets Groups Projects
Unverified Commit 0af2987c authored by Joel Jeremy Marquez's avatar Joel Jeremy Marquez Committed by GitHub
Browse files

Hide mobile nav tabs on scroll (#1745)


* Hide mobile nav tabs on scroll

* Release notes

* Reduced navbar bottom padding

* Make hide transition a bit faster

* Update scroll defaults

* VRT snapshots

* Fix: safari scroll bounce effect disrupting the show/hide of bottom nav

* Update release notes

* No tap highlight on MobileNavTabs

---------

Co-authored-by: default avatarMatiss Janis Aboltins <matiss@mja.lv>
parent e266093a
No related branches found
No related tags found
No related merge requests found
Showing
with 155 additions and 51 deletions
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
......@@ -5,7 +5,7 @@ import {
Route,
Routes,
Navigate,
NavLink,
useNavigate,
BrowserRouter,
useLocation,
useHref,
......@@ -20,13 +20,8 @@ import checkForUpdateNotification from 'loot-core/src/client/update-notification
import * as undo from 'loot-core/src/platform/client/undo';
import { useActions } from '../hooks/useActions';
import useNavigate from '../hooks/useNavigate';
import Add from '../icons/v1/Add';
import Cog from '../icons/v1/Cog';
import PiggyBank from '../icons/v1/PiggyBank';
import Wallet from '../icons/v1/Wallet';
import { useResponsive } from '../ResponsiveProvider';
import { theme, styles } from '../style';
import { theme } from '../style';
import { ExposeNavigate } from '../util/router-tools';
import { getIsOutdated, getLatestVersion } from '../util/versions';
......@@ -35,11 +30,13 @@ import { BudgetMonthCountProvider } from './budget/BudgetMonthCountContext';
import View from './common/View';
import GlobalKeys from './GlobalKeys';
import { ManageRulesPage } from './ManageRulesPage';
import MobileNavTabs from './mobile/MobileNavTabs';
import Modals from './Modals';
import Notifications from './Notifications';
import { ManagePayeesPage } from './payees/ManagePayeesPage';
import Reports from './reports';
import { NarrowAlternate, WideComponent } from './responsive';
import ScrollProvider from './ScrollProvider';
import Settings from './settings';
import FloatableSidebar, { SidebarProvider } from './sidebar';
import Titlebar, { TitlebarProvider } from './Titlebar';
......@@ -73,48 +70,6 @@ function WideNotSupported({ children, redirectTo = '/budget' }) {
return isNarrowWidth ? children : null;
}
function NavTab({ icon: TabIcon, name, path }) {
return (
<NavLink
to={path}
style={({ isActive }) => ({
alignItems: 'center',
color: isActive ? theme.mobileNavItemSelected : theme.mobileNavItem,
display: 'flex',
flexDirection: 'column',
textDecoration: 'none',
})}
>
<TabIcon width={22} height={22} style={{ marginBottom: '5px' }} />
{name}
</NavLink>
);
}
function MobileNavTabs() {
const { isNarrowWidth } = useResponsive();
return (
<div
style={{
backgroundColor: theme.mobileNavBackground,
borderTop: `1px solid ${theme.menuBorder}`,
bottom: 0,
...styles.shadow,
display: isNarrowWidth ? 'flex' : 'none',
height: '80px',
justifyContent: 'space-around',
paddingTop: 10,
width: '100%',
}}
>
<NavTab name="Budget" path="/budget" icon={Wallet} />
<NavTab name="Accounts" path="/accounts" icon={PiggyBank} />
<NavTab name="Transaction" path="/transactions/new" icon={Add} />
<NavTab name="Settings" path="/settings" icon={Cog} />
</div>
);
}
function RouterBehaviors({ getAccounts }) {
let navigate = useNavigate();
useEffect(() => {
......@@ -305,7 +260,9 @@ export default function FinancesAppWithContext() {
<BudgetMonthCountProvider>
<PayeesProvider>
<AccountsProvider>
<DndProvider backend={Backend}>{app}</DndProvider>
<DndProvider backend={Backend}>
<ScrollProvider>{app}</ScrollProvider>
</DndProvider>
</AccountsProvider>
</PayeesProvider>
</BudgetMonthCountProvider>
......
import React, {
type ReactNode,
createContext,
useState,
useContext,
useEffect,
} from 'react';
import debounce from 'debounce';
type IScrollContext = {
scrollY: number | undefined;
isBottomReached: boolean;
};
const ScrollContext = createContext<IScrollContext | undefined>(undefined);
type ScrollProviderProps = {
children?: ReactNode;
};
export default function ScrollProvider({ children }: ScrollProviderProps) {
const [scrollY, setScrollY] = useState(undefined);
const [isBottomReached, setIsBottomReached] = useState(false);
useEffect(() => {
const listenToScroll = debounce(e => {
setScrollY(e.target?.scrollTop || 0);
setIsBottomReached(
e.target?.scrollHeight - e.target?.scrollTop <= e.target?.clientHeight,
);
}, 20);
window.addEventListener('scroll', listenToScroll, {
capture: true,
passive: true,
});
return () =>
window.removeEventListener('scroll', listenToScroll, {
capture: true,
});
}, []);
return (
<ScrollContext.Provider
value={{ scrollY, isBottomReached }}
children={children}
/>
);
}
export function useScroll(): IScrollContext {
return useContext(ScrollContext);
}
import React, { type ComponentType, useMemo } from 'react';
import { NavLink } from 'react-router-dom';
import usePrevious from '../../hooks/usePrevious';
import Add from '../../icons/v1/Add';
import Cog from '../../icons/v1/Cog';
import PiggyBank from '../../icons/v1/PiggyBank';
import Wallet from '../../icons/v1/Wallet';
import { useResponsive } from '../../ResponsiveProvider';
import { theme, styles } from '../../style';
import { useScroll } from '../ScrollProvider';
const height = 70;
export default function MobileNavTabs() {
const { isNarrowWidth } = useResponsive();
const { scrollY, isBottomReached } = useScroll();
const previousScrollY = usePrevious(scrollY);
const isVisible = useMemo(
() =>
previousScrollY === undefined ||
(!isBottomReached && previousScrollY > scrollY) ||
previousScrollY < 0,
[scrollY],
);
return (
<div
style={{
backgroundColor: theme.mobileNavBackground,
borderTop: `1px solid ${theme.menuBorder}`,
...styles.shadow,
display: isNarrowWidth ? 'flex' : 'none',
height,
justifyContent: 'space-around',
paddingTop: 10,
paddingBottom: 10,
width: '100%',
position: 'fixed',
zIndex: 100,
bottom: isVisible ? 0 : -height,
transition: 'bottom 0.2s ease-out',
}}
>
<NavTab name="Budget" path="/budget" icon={Wallet} />
<NavTab name="Accounts" path="/accounts" icon={PiggyBank} />
<NavTab name="Transaction" path="/transactions/new" icon={Add} />
<NavTab name="Settings" path="/settings" icon={Cog} />
</div>
);
}
type NavTabIconProps = {
width: number;
height: number;
};
type NavTabProps = {
name: string;
path: string;
icon: ComponentType<NavTabIconProps>;
};
function NavTab({ icon: TabIcon, name, path }: NavTabProps) {
return (
<NavLink
to={path}
style={({ isActive }) => ({
...styles.noTapHighlight,
alignItems: 'center',
color: isActive ? theme.mobileNavItemSelected : theme.mobileNavItem,
display: 'flex',
flexDirection: 'column',
textDecoration: 'none',
})}
>
<TabIcon width={22} height={22} />
{name}
</NavLink>
);
}
......@@ -83,7 +83,6 @@ export const styles = {
fontSize: 16,
// lineHeight: 22.4 // TODO: This seems like trouble, but what's the right value?
},
delayedFadeIn: {
animationName: keyframes({
'0%': { opacity: 0 },
......@@ -98,6 +97,12 @@ export const styles = {
textDecorationThickness: 2,
textDecorationColor: theme.pillBorder,
},
noTapHighlight: {
WebkitTapHighlightColor: 'transparent',
':focus': {
outline: 'none',
},
},
// Dynamically set
lightScrollbar: null as CSSProperties | null,
darkScrollbar: null as CSSProperties | null,
......
---
category: Enhancements
authors: [joel-jeremy,MatissJanis]
---
Hide mobile nav bar when scrolling
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment