Skip to content
Snippets Groups Projects
Unverified Commit f129b07d authored by Yusef Ouda's avatar Yusef Ouda Committed by GitHub
Browse files

Adds ability to resize sidebar (#2993)

* Adds ability to resize sidebar

* Adds release notes

* Changes to feature

* lint

* change translateX to use % for both states

* vrt

* set max sidebar width, cleanup

* set min and max widths

* min width to 200px

* changes resizable sidebar to use re-resizable instead off css resize

* vrt

* vrt
parent f1caf21d
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"pikaday": "1.8.2", "pikaday": "1.8.2",
"promise-retry": "^2.0.1", "promise-retry": "^2.0.1",
"re-resizable": "^6.9.17",
"react": "18.2.0", "react": "18.2.0",
"react-aria-components": "^1.2.1", "react-aria-components": "^1.2.1",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
......
import React, { useRef, useState } from 'react'; import React, { useRef, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { Resizable } from 're-resizable';
import { import {
closeBudget, closeBudget,
moveAccount, moveAccount,
...@@ -12,9 +14,11 @@ import { useAccounts } from '../../hooks/useAccounts'; ...@@ -12,9 +14,11 @@ import { useAccounts } from '../../hooks/useAccounts';
import { useGlobalPref } from '../../hooks/useGlobalPref'; import { useGlobalPref } from '../../hooks/useGlobalPref';
import { useLocalPref } from '../../hooks/useLocalPref'; import { useLocalPref } from '../../hooks/useLocalPref';
import { useNavigate } from '../../hooks/useNavigate'; import { useNavigate } from '../../hooks/useNavigate';
import { useResizeObserver } from '../../hooks/useResizeObserver';
import { SvgExpandArrow } from '../../icons/v0'; import { SvgExpandArrow } from '../../icons/v0';
import { SvgReports, SvgWallet } from '../../icons/v1'; import { SvgReports, SvgWallet } from '../../icons/v1';
import { SvgCalendar } from '../../icons/v2'; import { SvgCalendar } from '../../icons/v2';
import { useResponsive } from '../../ResponsiveProvider';
import { styles, theme } from '../../style'; import { styles, theme } from '../../style';
import { Button } from '../common/Button'; import { Button } from '../common/Button';
import { InitialFocus } from '../common/InitialFocus'; import { InitialFocus } from '../common/InitialFocus';
...@@ -30,20 +34,28 @@ import { useSidebar } from './SidebarProvider'; ...@@ -30,20 +34,28 @@ import { useSidebar } from './SidebarProvider';
import { ToggleButton } from './ToggleButton'; import { ToggleButton } from './ToggleButton';
import { Tools } from './Tools'; import { Tools } from './Tools';
export const SIDEBAR_WIDTH = 240;
export function Sidebar() { export function Sidebar() {
const hasWindowButtons = !Platform.isBrowser && Platform.OS === 'mac'; const hasWindowButtons = !Platform.isBrowser && Platform.OS === 'mac';
const dispatch = useDispatch(); const dispatch = useDispatch();
const sidebar = useSidebar(); const sidebar = useSidebar();
const accounts = useAccounts(); const accounts = useAccounts();
const { width } = useResponsive();
const [showClosedAccounts, setShowClosedAccountsPref] = useLocalPref( const [showClosedAccounts, setShowClosedAccountsPref] = useLocalPref(
'ui.showClosedAccounts', 'ui.showClosedAccounts',
); );
const [isFloating = false, setFloatingSidebarPref] = const [isFloating = false, setFloatingSidebarPref] =
useGlobalPref('floatingSidebar'); useGlobalPref('floatingSidebar');
const [_sidebarWidth, setSidebarWidth] = useLocalPref('sidebarWidth');
const DEFAULT_SIDEBAR_WIDTH = 240;
const MAX_SIDEBAR_WIDTH = width / 3;
const MIN_SIDEBAR_WIDTH = 200;
const sidebarWidth = Math.min(
MAX_SIDEBAR_WIDTH,
Math.max(MIN_SIDEBAR_WIDTH, _sidebarWidth || DEFAULT_SIDEBAR_WIDTH),
);
async function onReorder( async function onReorder(
id: string, id: string,
dropPos: 'top' | 'bottom', dropPos: 'top' | 'bottom',
...@@ -70,72 +82,96 @@ export function Sidebar() { ...@@ -70,72 +82,96 @@ export function Sidebar() {
setShowClosedAccountsPref(!showClosedAccounts); setShowClosedAccountsPref(!showClosedAccounts);
}; };
const containerRef = useResizeObserver(rect => {
setSidebarWidth(rect.width);
});
return ( return (
<View <Resizable
style={{ defaultSize={{
width: SIDEBAR_WIDTH, width: sidebarWidth,
color: theme.sidebarItemText, height: '100%',
backgroundColor: theme.sidebarBackground, }}
'& .float': { maxWidth={MAX_SIDEBAR_WIDTH}
opacity: isFloating ? 1 : 0, minWidth={MIN_SIDEBAR_WIDTH}
transition: 'opacity .25s, width .25s', enable={{
width: hasWindowButtons || isFloating ? null : 0, top: false,
}, right: true,
'&:hover .float': { bottom: false,
opacity: 1, left: false,
width: hasWindowButtons ? null : 'auto', topRight: false,
}, bottomRight: false,
flex: 1, bottomLeft: false,
...styles.darkScrollbar, topLeft: false,
}} }}
> >
<View <View
innerRef={containerRef}
style={{ style={{
paddingTop: 35, color: theme.sidebarItemText,
height: 30, height: '100%',
flexDirection: 'row', backgroundColor: theme.sidebarBackground,
alignItems: 'center', '& .float': {
margin: '0 8px 23px 20px', opacity: isFloating ? 1 : 0,
transition: 'padding .4s', transition: 'opacity .25s, width .25s',
...(hasWindowButtons && { width: hasWindowButtons || isFloating ? null : 0,
paddingTop: 20, },
justifyContent: 'flex-start', '&:hover .float': {
}), opacity: 1,
width: hasWindowButtons ? null : 'auto',
},
flex: 1,
...styles.darkScrollbar,
}} }}
> >
<EditableBudgetName /> <View
style={{
paddingTop: 35,
height: 30,
flexDirection: 'row',
alignItems: 'center',
margin: '0 8px 23px 20px',
transition: 'padding .4s',
...(hasWindowButtons && {
paddingTop: 20,
justifyContent: 'flex-start',
}),
}}
>
<EditableBudgetName />
<View style={{ flex: 1, flexDirection: 'row' }} /> <View style={{ flex: 1, flexDirection: 'row' }} />
{!sidebar.alwaysFloats && ( {!sidebar.alwaysFloats && (
<ToggleButton isFloating={isFloating} onFloat={onFloat} /> <ToggleButton isFloating={isFloating} onFloat={onFloat} />
)} )}
</View> </View>
<View style={{ overflow: 'auto' }}> <View style={{ overflow: 'auto' }}>
<Item title="Budget" Icon={SvgWallet} to="/budget" /> <Item title="Budget" Icon={SvgWallet} to="/budget" />
<Item title="Reports" Icon={SvgReports} to="/reports" /> <Item title="Reports" Icon={SvgReports} to="/reports" />
<Item title="Schedules" Icon={SvgCalendar} to="/schedules" /> <Item title="Schedules" Icon={SvgCalendar} to="/schedules" />
<Tools /> <Tools />
<View <View
style={{ style={{
height: 1, height: 1,
backgroundColor: theme.sidebarItemBackgroundHover, backgroundColor: theme.sidebarItemBackgroundHover,
marginTop: 15, marginTop: 15,
flexShrink: 0, flexShrink: 0,
}} }}
/> />
<Accounts <Accounts
onAddAccount={onAddAccount} onAddAccount={onAddAccount}
onToggleClosedAccounts={onToggleClosedAccounts} onToggleClosedAccounts={onToggleClosedAccounts}
onReorder={onReorder} onReorder={onReorder}
/> />
</View>
</View> </View>
</View> </Resizable>
); );
} }
......
...@@ -4,7 +4,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref'; ...@@ -4,7 +4,7 @@ import { useGlobalPref } from '../../hooks/useGlobalPref';
import { useResponsive } from '../../ResponsiveProvider'; import { useResponsive } from '../../ResponsiveProvider';
import { View } from '../common/View'; import { View } from '../common/View';
import { SIDEBAR_WIDTH, Sidebar } from './Sidebar'; import { Sidebar } from './Sidebar';
import { useSidebar } from './SidebarProvider'; import { useSidebar } from './SidebarProvider';
export function FloatableSidebar() { export function FloatableSidebar() {
...@@ -42,10 +42,8 @@ export function FloatableSidebar() { ...@@ -42,10 +42,8 @@ export function FloatableSidebar() {
: '0 15px 30px 0 rgba(0,0,0,0.25), 0 3px 15px 0 rgba(0,0,0,.5)', : '0 15px 30px 0 rgba(0,0,0,0.25), 0 3px 15px 0 rgba(0,0,0,.5)',
transform: `translateY(${!sidebarShouldFloat ? -12 : 0}px) transform: `translateY(${!sidebarShouldFloat ? -12 : 0}px)
translateX(${ translateX(${
sidebarShouldFloat && sidebar.hidden sidebarShouldFloat && sidebar.hidden ? '-100' : '0'
? -SIDEBAR_WIDTH }%)`,
: 0
}px)`,
transition: transition:
'transform .5s, box-shadow .5s, border-radius .5s, bottom .5s', 'transform .5s, box-shadow .5s, border-radius .5s, bottom .5s',
}} }}
......
...@@ -53,6 +53,7 @@ export type LocalPrefs = Partial< ...@@ -53,6 +53,7 @@ export type LocalPrefs = Partial<
reportsViewLegend: boolean; reportsViewLegend: boolean;
reportsViewSummary: boolean; reportsViewSummary: boolean;
reportsViewLabel: boolean; reportsViewLabel: boolean;
sidebarWidth: number;
'mobile.showSpentColumn': boolean; 'mobile.showSpentColumn': boolean;
} & Record<`flags.${FeatureFlag}`, boolean> } & Record<`flags.${FeatureFlag}`, boolean>
>; >;
......
---
category: Features
authors: [YusefOuda]
---
Adds ability to resize sidebar.
...@@ -100,6 +100,7 @@ __metadata: ...@@ -100,6 +100,7 @@ __metadata:
memoize-one: "npm:^6.0.0" memoize-one: "npm:^6.0.0"
pikaday: "npm:1.8.2" pikaday: "npm:1.8.2"
promise-retry: "npm:^2.0.1" promise-retry: "npm:^2.0.1"
re-resizable: "npm:^6.9.17"
react: "npm:18.2.0" react: "npm:18.2.0"
react-aria-components: "npm:^1.2.1" react-aria-components: "npm:^1.2.1"
react-dnd: "npm:^16.0.1" react-dnd: "npm:^16.0.1"
...@@ -15469,6 +15470,16 @@ __metadata: ...@@ -15469,6 +15470,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
   
"re-resizable@npm:^6.9.17":
version: 6.9.17
resolution: "re-resizable@npm:6.9.17"
peerDependencies:
react: ^16.13.1 || ^17.0.0 || ^18.0.0
react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0
checksum: 768c3a0fe39d6916caf4e003240d326d62c4d7512c7d3115cc2a98085416fdba80097afdbb93df57b69543c41ce56b33589f2fea6987cd5149faa83cf11c8ba1
languageName: node
linkType: hard
"react-aria-components@npm:^1.2.1": "react-aria-components@npm:^1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "react-aria-components@npm:1.2.1" resolution: "react-aria-components@npm:1.2.1"
......
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