Skip to content

Commit

Permalink
feat(#21): add horizontal/vertical alignment
Browse files Browse the repository at this point in the history
  • Loading branch information
notmedia committed Sep 4, 2022
1 parent 8d6a58b commit 177720f
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 27 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
✅ Multi-tabs.
✅ Shortcuts.
✅ Environments.
✅ Horizontal/Vertical alignment.
✅ Good errors output.

**gRPC**
Expand Down
4 changes: 2 additions & 2 deletions src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ThemeType, useSettingsStore } from './storage';
import { DarkTheme, globalStyles, LightTheme } from './themes';

export const THEMES = {
[ThemeType.Dark]: DarkTheme,
[ThemeType.Light]: LightTheme,
[ThemeType.DARK]: DarkTheme,
[ThemeType.LIGHT]: LightTheme,
};

function App(): JSX.Element {
Expand Down
23 changes: 17 additions & 6 deletions src/app/components/icons/horizontal-layout.icon.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import { useTheme } from '@nextui-org/react';
import React from 'react';

export const HorizontalLayoutIcon: React.FC = () => {
export const HorizontalLayoutIcon: React.FC<React.SVGProps<SVGElement>> = ({ onClick }) => {
const { theme } = useTheme();

const [isHovered, setIsHovered] = React.useState(false);

return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
onClick={onClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<path
d="M0.25 11.6184H23.75V19C23.75 21.6234 21.6234 23.75 19 23.75H5C2.37665 23.75 0.25 21.6234 0.25 19V11.6184Z"
fill={theme?.colors?.accents4.value}
stroke="black"
fill={theme?.colors?.background.value}
stroke={isHovered ? theme?.colors?.warning.value : theme?.colors?.foreground.value}
strokeWidth="0.5"
/>
<path
d="M5 0.25H19C21.6234 0.25 23.75 2.37665 23.75 5V12.3816H0.25V5C0.25 2.37664 2.37665 0.25 5 0.25Z"
fill={theme?.colors?.foreground.value}
stroke="black"
fill={isHovered ? theme?.colors?.yellow100.value : theme?.colors?.accents4.value}
stroke={isHovered ? theme?.colors?.warning.value : theme?.colors?.foreground.value}
strokeWidth="0.5"
/>
</svg>
Expand Down
23 changes: 17 additions & 6 deletions src/app/components/icons/vertical-layout.icon.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
import { useTheme } from '@nextui-org/react';
import React from 'react';

export const VerticalLayoutIcon: React.FC = () => {
export const VerticalLayoutIcon: React.FC<React.SVGProps<SVGElement>> = ({ onClick }) => {
const { theme } = useTheme();

const [isHovered, setIsHovered] = React.useState(false);

return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
onClick={onClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<path
d="M11.6448 23.7277L11.5948 0.273813L18.9494 0.259645C21.5728 0.254592 23.704 2.37714 23.7096 5.00048L23.7393 18.9544C23.7449 21.5777 21.6228 23.7085 18.9995 23.7135L11.6448 23.7277Z"
fill={theme?.colors?.accents4.value}
stroke="black"
fill={theme?.colors?.background.value}
stroke={isHovered ? theme?.colors?.warning.value : theme?.colors?.foreground.value}
strokeWidth="0.5"
/>
<path
d="M0.290449 18.9995L0.260677 5.04563C0.25508 2.42228 2.37719 0.291536 5.00053 0.286483L12.3552 0.272315L12.4053 23.7262L5.05058 23.7404C2.42723 23.7454 0.296046 21.6229 0.290449 18.9995Z"
fill={theme?.colors?.foreground.value}
stroke="black"
fill={isHovered ? theme?.colors?.yellow100.value : theme?.colors?.accents4.value}
stroke={isHovered ? theme?.colors?.warning.value : theme?.colors?.foreground.value}
strokeWidth="0.5"
/>
</svg>
Expand Down
16 changes: 15 additions & 1 deletion src/app/components/resizable-panel/resizable-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const StyledResizable = styled(Resizable, {
height: '100%',

overflow: 'hidden',

transition: 'all 0.1s ease',

variants: {
alignment: {
horizontal: {
Expand Down Expand Up @@ -73,15 +76,26 @@ export const ResizablePanel: React.FC<React.PropsWithChildren<ResizablePanelProp
firstNode,
secondNode,
}) => {
const ref = React.useRef<Resizable>(null);

const enableOptions: ResizableEnableOptions =
alignment === 'vertical' ? { right: true } : { bottom: true };

const defaultSize =
alignment === 'vertical' ? { width: '50%', height: '100%' } : { width: '100%', height: '50%' };

React.useEffect(() => {
ref.current?.updateSize(defaultSize);
}, [alignment]);

return (
<ResizablePanelWrapper alignment={alignment}>
<StyledResizable alignment={alignment} enable={enableOptions} defaultSize={defaultSize}>
<StyledResizable
ref={ref}
alignment={alignment}
enable={enableOptions}
defaultSize={defaultSize}
>
{firstNode}
</StyledResizable>
<StyledSecondNode alignment={alignment}>{secondNode}</StyledSecondNode>
Expand Down
3 changes: 2 additions & 1 deletion src/app/pages/explorer/explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export interface ExplorerProps {
}

export const Explorer: React.FC<ExplorerProps> = ({ children, header, sideBar }) => (
<Grid.Container wrap="nowrap" css={{ height: '100%' }}>
// <Grid.Container wrap="nowrap" css={{ height: '100%' }}>
<Grid.Container gap={0} wrap="nowrap" css={{ flex: 1, overflow: 'hidden' }}>
<Grid css={ExplorerGridStyles}>
<ExplorerHeader>{header}</ExplorerHeader>
{sideBar}
Expand Down
17 changes: 14 additions & 3 deletions src/app/pages/main.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Container } from '@nextui-org/react';
import React from 'react';
import { useEffectOnce } from 'react-use';

Expand All @@ -7,6 +8,7 @@ import { Explorer } from './explorer';
import { Header } from './header';
import { Shortcuts } from './shortcuts';
import { ExplorerSideBar } from './side-bar';
import { StatusBar } from './status-bar';
import { TabsContainer } from './tabs-container';

export const Main = (): JSX.Element => {
Expand All @@ -22,9 +24,18 @@ export const Main = (): JSX.Element => {
return (
<AppProvider value={value}>
<Shortcuts>
<Explorer header={<Header />} sideBar={<ExplorerSideBar />}>
<TabsContainer />
</Explorer>
<Container
gap={0}
responsive={false}
display="flex"
direction="column"
css={{ height: '100%', overflow: 'hidden' }}
>
<Explorer header={<Header />} sideBar={<ExplorerSideBar />}>
<TabsContainer />
</Explorer>
<StatusBar />
</Container>
</Shortcuts>
</AppProvider>
);
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/settings/forms/settings.form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export const SettingsForm: React.FC<SettingsFormProps> = ({
field.onChange(Array.from(keys).join(''));
}}
>
<Dropdown.Item key={ThemeType.Light}>Light</Dropdown.Item>
<Dropdown.Item key={ThemeType.Dark}>Dark</Dropdown.Item>
<Dropdown.Item key={ThemeType.LIGHT}>Light</Dropdown.Item>
<Dropdown.Item key={ThemeType.DARK}>Dark</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}
Expand Down
4 changes: 2 additions & 2 deletions src/app/pages/shortcuts/hooks/use-theme-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ export function useThemeActions() {
name: 'Dark',
keywords: 'dark theme',
parent: 'theme',
perform: () => updateTheme(ThemeType.Dark),
perform: () => updateTheme(ThemeType.DARK),
},
{
id: 'lightTheme',
name: 'Light',
keywords: 'light theme',
parent: 'theme',
perform: () => updateTheme(ThemeType.Light),
perform: () => updateTheme(ThemeType.LIGHT),
},
]);
}
32 changes: 32 additions & 0 deletions src/app/pages/status-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Container } from '@nextui-org/react';
import React from 'react';

import { HorizontalLayoutIcon, VerticalLayoutIcon } from '../components';
import { Alignment, useSettingsStore } from '../storage';

export const StatusBar: React.FC = () => {
const { alignment, updateAlignment } = useSettingsStore((store) => store);

const handleAlignmentChange = (newAlignment: Alignment) => {
updateAlignment(newAlignment);
};

return (
<Container
gap={1}
fluid={false}
responsive={false}
display="flex"
alignItems="center"
css={{ borderTop: 'solid 1px $neutralBorder', height: 20 }}
>
<Container gap={0} responsive={false} display="flex" justify="flex-end">
{alignment === Alignment.VERTICAL ? (
<VerticalLayoutIcon onClick={() => handleAlignmentChange(Alignment.HORIZONTAL)} />
) : (
<HorizontalLayoutIcon onClick={() => handleAlignmentChange(Alignment.VERTICAL)} />
)}
</Container>
</Container>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isGrpcTabClientStreaming,
isGrpcTabServerStreaming,
isGrpcTabUnaryCall,
useSettingsStore,
} from '../../../../storage';
import { Request } from './request';
import {
Expand All @@ -29,6 +30,8 @@ export interface GrpcTabContainerProps {
}

export const GrpcTabContainer: React.FC<GrpcTabContainerProps> = ({ tab }) => {
const alignment = useSettingsStore((store) => store.alignment);

let content;

if (isGrpcTabUnaryCall(tab)) {
Expand All @@ -38,6 +41,7 @@ export const GrpcTabContainer: React.FC<GrpcTabContainerProps> = ({ tab }) => {
<UnaryCallSendHeader tab={tab} />
<Spacer />
<ResizablePanel
alignment={alignment}
firstNode={<Request tab={tab} />}
secondNode={<UnaryCallResponse tab={tab} />}
/>
Expand All @@ -52,6 +56,7 @@ export const GrpcTabContainer: React.FC<GrpcTabContainerProps> = ({ tab }) => {
<ServerStreamingSendHeader tab={tab} />
<Spacer />
<ResizablePanel
alignment={alignment}
firstNode={<Request tab={tab} />}
secondNode={<ServerStreamingResponse tab={tab} />}
/>
Expand All @@ -66,6 +71,7 @@ export const GrpcTabContainer: React.FC<GrpcTabContainerProps> = ({ tab }) => {
<ClientStreamingSendHeader tab={tab} />
<Spacer />
<ResizablePanel
alignment={alignment}
firstNode={<Request tab={tab} />}
secondNode={<ClientStreamingResponse tab={tab} />}
/>
Expand All @@ -80,6 +86,7 @@ export const GrpcTabContainer: React.FC<GrpcTabContainerProps> = ({ tab }) => {
<BidirectionalStreamingSendHeader tab={tab} />
<Spacer />
<ResizablePanel
alignment={alignment}
firstNode={<Request tab={tab} />}
secondNode={<BidirectionalStreamingResponse tab={tab} />}
/>
Expand Down
11 changes: 9 additions & 2 deletions src/app/storage/interfaces/settings.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ export enum Language {
}

export enum ThemeType {
Dark = 'dark',
Light = 'light',
DARK = 'dark',
LIGHT = 'light',
}

export enum Alignment {
VERTICAL = 'vertical',
HORIZONTAL = 'horizontal',
}

export interface Settings {
theme: ThemeType;
language: Language;
alignment: Alignment;
}

export interface SettingsStorage extends Settings {
updateTheme: (theme: ThemeType) => void;
updateAlignment: (alignment: Alignment) => void;
}
12 changes: 10 additions & 2 deletions src/app/storage/settings.storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@ import { produce } from 'immer';
import create from 'zustand';
import { persist } from 'zustand/middleware';

import { Language, SettingsStorage, ThemeType } from './interfaces';
import { Alignment, Language, SettingsStorage, ThemeType } from './interfaces';

export const useSettingsStore = create(
persist<SettingsStorage>(
(set) => ({
theme: ThemeType.Dark,
theme: ThemeType.DARK,
language: Language.EN,
alignment: Alignment.VERTICAL,

updateTheme: (theme) =>
set(
produce<SettingsStorage>((state) => {
state.theme = theme;
})
),

updateAlignment: (alignment) =>
set(
produce<SettingsStorage>((state) => {
state.alignment = alignment;
})
),
}),
{
name: 'settings',
Expand Down

0 comments on commit 177720f

Please sign in to comment.