Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Swipe up mobile navbar to reveal more menus
Browse files Browse the repository at this point in the history
joel-jeremy committed Oct 1, 2023
1 parent 08fa452 commit ebe8d32
Showing 3 changed files with 152 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/desktop-client/package.json
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@
"@types/react-router-dom": "^5.3.3",
"@types/uuid": "^9.0.2",
"@types/webpack-bundle-analyzer": "^4.6.0",
"@use-gesture/react": "^10.3.0",
"chokidar": "^3.5.3",
"cross-env": "^7.0.3",
"date-fns": "^2.29.3",
156 changes: 132 additions & 24 deletions packages/desktop-client/src/components/mobile/MobileNavTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,164 @@
import React, { useRef, type ComponentType, useEffect, useState } from 'react';
import React, { useRef, type ComponentType, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
import { useSpring, animated, config } from 'react-spring';

import { useDrag } from '@use-gesture/react';

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, styles, type CSSProperties } from '../../style';
import View from '../common/View';
import { useScroll } from '../ScrollProvider';

export default function MobileNavTabs() {
const { isNarrowWidth } = useResponsive();
const { scrollY } = useScroll();
const previousScrollY = useRef(0);
const [isVisible, setIsVisible] = useState(true);

const height = 70;
const totalRowCount = 2;
const rowHeight = 60;
const totalHeight = rowHeight * totalRowCount;
const openY = 0;
const closeY = rowHeight;
const hiddenY = totalHeight;

const [{ y }, api] = useSpring(() => ({ y: totalHeight }));

const open = ({ canceled }) => {
// when cancel is true, it means that the user passed the upwards threshold
// so we change the spring config to create a nice wobbly effect
api.start({
y: openY,
immediate: false,
config: canceled ? config.wobbly : config.stiff,
});
};

const close = (velocity = 0) => {
api.start({
y: closeY,
immediate: false,
config: { ...config.stiff, velocity },
});
};

const hide = (velocity = 0) => {
api.start({
y: hiddenY,
immediate: false,
config: { ...config.stiff, velocity },
});
};

useEffect(() => {
if (scrollY > previousScrollY.current && previousScrollY.current !== 0) {
if (isVisible) {
setIsVisible(false);
}
hide();
} else {
if (!isVisible) {
setIsVisible(true);
}
close();
}
previousScrollY.current = scrollY;
}, [scrollY]);

const bind = useDrag(
({
last,
velocity: [, vy],
direction: [, dy],
offset: [, oy],
cancel,
canceled,
}) => {
// if the user drags up passed a threshold, then we cancel
// the drag so that the sheet resets to its open position
if (oy < -rowHeight) {
cancel();
}

// when the user releases the sheet, we check whether it passed
// the threshold for it to close, or if we reset it to its open position
if (last) {
if (oy > rowHeight * 0.5 || (vy > 0.5 && dy > 0)) {
close(vy);
} else {
open({ canceled });
}
} else {
// when the user keeps dragging, we just move the sheet according to
// the cursor position
api.start({ y: oy, immediate: true });
}
},
{
from: () => [0, y.get()],
filterTaps: true,
bounds: { top: rowHeight * 0.5, bottom: totalHeight * 0.5 },
axis: 'y',
rubberband: true,
},
);

const navTabStyle = {
flex: '1 1 33%',
height: rowHeight,
padding: 10,
};

return (
<div
<animated.div
{...bind()}
style={{
y,
touchAction: 'pan-x',
backgroundColor: theme.mobileNavBackground,
borderTop: `1px solid ${theme.menuBorder}`,
...styles.shadow,
display: isNarrowWidth ? 'flex' : 'none',
height: height,
justifyContent: 'space-around',
paddingTop: 10,
paddingBottom: 10,
height: totalHeight,
width: '100%',
position: 'fixed',
zIndex: 100,
bottom: isVisible ? 0 : -height,
transition: 'bottom 0.4s ease-out',
bottom: 0,
display: isNarrowWidth ? 'flex' : 'none',
}}
>
<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>
<View
style={{
flexDirection: 'row',
flexWrap: 'wrap',
height: totalHeight,
width: '100%',
}}
>
<NavTab
style={navTabStyle}
name="Budget"
path="/budget"
icon={Wallet}
/>
<NavTab
style={navTabStyle}
name="Accounts"
path="/accounts"
icon={PiggyBank}
/>
<NavTab
style={navTabStyle}
name="Transaction"
path="/transactions/new"
icon={Add}
/>
<NavTab
style={navTabStyle}
name="Settings"
path="/settings"
icon={Cog}
/>
<div style={navTabStyle} />
<div style={navTabStyle} />
</View>
</animated.div>
);
}

@@ -65,9 +171,10 @@ type NavTabProps = {
name: string;
path: string;
icon: ComponentType<NavTabIconProps>;
style?: CSSProperties;
};

function NavTab({ icon: TabIcon, name, path }: NavTabProps) {
function NavTab({ icon: TabIcon, name, path, style }: NavTabProps) {
return (
<NavLink
to={path}
@@ -77,6 +184,7 @@ function NavTab({ icon: TabIcon, name, path }: NavTabProps) {
display: 'flex',
flexDirection: 'column',
textDecoration: 'none',
...style,
})}
>
<TabIcon width={22} height={22} />
19 changes: 19 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -74,6 +74,7 @@ __metadata:
"@types/react-router-dom": ^5.3.3
"@types/uuid": ^9.0.2
"@types/webpack-bundle-analyzer": ^4.6.0
"@use-gesture/react": ^10.3.0
chokidar: ^3.5.3
cross-env: ^7.0.3
date-fns: ^2.29.3
@@ -4983,6 +4984,24 @@ __metadata:
languageName: node
linkType: hard

"@use-gesture/core@npm:10.3.0":
version: 10.3.0
resolution: "@use-gesture/core@npm:10.3.0"
checksum: cd6782b0cf61ae2306ecee4bd3c30942251427c142e3fd3584778d86e1a93b27e087033246700b54c4ad7063aa78747dc793f0dbb7434925c306215fb18dee82
languageName: node
linkType: hard

"@use-gesture/react@npm:^10.3.0":
version: 10.3.0
resolution: "@use-gesture/react@npm:10.3.0"
dependencies:
"@use-gesture/core": 10.3.0
peerDependencies:
react: ">= 16.8.0"
checksum: d43a2296e536ea8e4885ca082b7c554eabb0e19bb7f89b5db96e0511712c849db879de64c2746c94e3c9a5032e8918c90ace67fc023c754034d75b2ea3b727c4
languageName: node
linkType: hard

"@webassemblyjs/ast@npm:1.11.5, @webassemblyjs/ast@npm:^1.11.5":
version: 1.11.5
resolution: "@webassemblyjs/ast@npm:1.11.5"

0 comments on commit ebe8d32

Please sign in to comment.