feat: implement sidebar collapsing

This commit is contained in:
jamaljsr 2020-04-21 00:50:20 -04:00
parent 1e6ac168ba
commit abeb399688
6 changed files with 101 additions and 35 deletions

View file

@ -1,11 +1,8 @@
import React from 'react';
import 'mobx-react-lite/batchingForReactDom';
import { addDecorator } from '@storybook/react';
import { addDecorator, addParameters } from '@storybook/react';
import '../src/App.scss';
import ChannelAction from '../src/action/channel';
import NodeAction from '../src/action/node';
import SwapAction from '../src/action/swap';
import { LndApi, LoopApi } from '../src/api';
import { createActions } from '../src/action';
import { Background } from '../src/components/common/base';
import { ThemeProvider } from '../src/components/theme';
import { Store, StoreProvider } from '../src/store';
@ -31,23 +28,13 @@ const grpc = {
return Promise.resolve(response);
},
};
const lndApi = new LndApi(grpc);
const loopApi = new LoopApi(grpc);
// actions exposed to UI components
const node = new NodeAction(store, lndApi);
const channel = new ChannelAction(store, lndApi);
const swap = new SwapAction(store, loopApi);
const actions = {
node,
channel,
swap,
};
const actions = createActions(store, grpc);
// execute actions to populate the store data with the sample API responses
actions.node.getBalances();
addParameters({ store });
/**
* decorator function to wrap all stories with the necessary providers
*/

View file

@ -1,4 +1,6 @@
import React from 'react';
import React, { useEffect } from 'react';
import { StoryContext } from '@storybook/addons';
import { Store } from 'store';
import { Layout } from '../components/layout';
export default {
@ -6,11 +8,9 @@ export default {
component: Layout,
};
export const Empty = () => <Layout />;
export const WithContent = () => (
<Layout>
<h1>Lorem ipsum dolor sit amet</h1>
const SampleContent = () => (
<>
<h1 style={{ textAlign: 'center' }}>Lorem ipsum dolor sit amet</h1>
{[...Array(10)].map((_, i) => (
<p key={i}>
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis
@ -26,5 +26,32 @@ export const WithContent = () => (
consequatur aut perferendis doloribus asperiores repellat.
</p>
))}
</>
);
export const Default = () => <Layout />;
export const WithContent = () => (
<Layout>
<SampleContent />
</Layout>
);
export const Collapsed = (ctx: StoryContext) => {
useEffect(() => {
// grab the store from the Storybook parameter defined in preview.tsx
const store = ctx.parameters.store as Store;
store.sidebarCollapsed = true;
// change back to expanded when the component is unmounted
return () => {
store.sidebarCollapsed = false;
};
}, []);
return (
<Layout>
<SampleContent />
</Layout>
);
};

25
app/src/action/app.ts Normal file
View file

@ -0,0 +1,25 @@
import { action, toJS } from 'mobx';
import { log } from 'util/log';
import { Store } from 'store';
/**
* Action used to update app level state
*/
class AppAction {
private _store: Store;
constructor(store: Store) {
this._store = store;
}
/**
* toggle the sidebar to be collapsed or expanded
*/
@action.bound toggleSidebar() {
log.info('toggling sidebar');
this._store.sidebarCollapsed = !this._store.sidebarCollapsed;
log.info('updated store.sidebarCollapsed', toJS(this._store.sidebarCollapsed));
}
}
export default AppAction;

View file

@ -2,11 +2,13 @@ import GrpcClient from 'api/grpc';
import LndApi from 'api/lnd';
import LoopApi from 'api/loop';
import { Store } from 'store';
import AppAction from './app';
import ChannelAction from './channel';
import NodeAction from './node';
import SwapAction from './swap';
export interface StoreActions {
app: AppAction;
node: NodeAction;
channel: ChannelAction;
swap: SwapAction;
@ -15,19 +17,22 @@ export interface StoreActions {
/**
* Creates actions that modify the state of the given mobx store
* @param store the Store instance that the actions will modify
* @param grpcClient optionally provide an alternate grpc client if necessary
*/
export const createActions = (store: Store): StoreActions => {
export const createActions = (store: Store, grpcClient?: GrpcClient): StoreActions => {
// low level dependencies
const grpc = new GrpcClient();
const grpc = grpcClient || new GrpcClient();
const lndApi = new LndApi(grpc);
const loopApi = new LoopApi(grpc);
// actions exposed to UI components
const app = new AppAction(store);
const node = new NodeAction(store, lndApi);
const channel = new ChannelAction(store, lndApi);
const swap = new SwapAction(store, loopApi);
return {
app,
node,
channel,
swap,

View file

@ -1,9 +1,15 @@
import React from 'react';
import { observer } from 'mobx-react-lite';
import { useActions, useStore } from 'store';
import { Background } from 'components/common/base';
import { Menu } from 'components/common/icons';
import { styled } from 'components/theme';
import Sidebar from './Sidebar';
interface CollapsedProps {
collapsed: boolean;
}
const Styled = {
Container: styled.div`
position: relative;
@ -18,30 +24,43 @@ const Styled = {
z-index: 1;
cursor: pointer;
`,
Aside: styled.aside`
Aside: styled.aside<CollapsedProps>`
position: fixed;
top: 0;
height: 100vh;
background-color: ${props => props.theme.colors.darkBlue};
width: 285px;
padding: 15px;
overflow: hidden;
/* change sidebar dimensions based on collapsed toggle */
width: ${props => (props.collapsed ? '0' : '285px')};
padding: ${props => (props.collapsed ? '0' : '15px')};
transition: all 0.2s;
/* set a width on the child to improve the collapse animation */
& > div {
width: 285px;
}
`,
Content: styled.div`
margin-left: 285px;
Content: styled.div<CollapsedProps>`
margin-left: ${props => (props.collapsed ? '0' : '285px')};
padding: 15px;
transition: all 0.2s;
`,
};
const Layout: React.FC = ({ children }) => {
const { sidebarCollapsed } = useStore();
const { app } = useActions();
const { Container, MenuIcon, Aside, Content } = Styled;
return (
<Background>
<Container>
<MenuIcon />
<Aside>
<MenuIcon onClick={app.toggleSidebar} />
<Aside collapsed={sidebarCollapsed}>
<Sidebar />
</Aside>
<Content>
<Content collapsed={sidebarCollapsed}>
<div className="container">{children}</div>
</Content>
</Container>
@ -49,4 +68,4 @@ const Layout: React.FC = ({ children }) => {
);
};
export default Layout;
export default observer(Layout);

View file

@ -5,6 +5,9 @@ import { Channel, NodeBalances, NodeInfo, Swap } from 'types/state';
* The store used to manage global app state
*/
export class Store {
// App state
@observable sidebarCollapsed = false;
// API data
@observable info?: NodeInfo = undefined;
@observable balances?: NodeBalances = undefined;
@observable channels: Channel[] = [];