feat: Sidebar and StoreProvider

This commit is contained in:
Anoop M D 2021-12-04 01:07:38 +05:30
parent 07fc8af7ed
commit a863f9730d
22 changed files with 759 additions and 18 deletions

67
package-lock.json generated
View File

@ -14,10 +14,13 @@
"@fortawesome/react-fontawesome": "^0.1.16",
"@grafnode/www": "^0.0.1",
"@tabler/icons": "^1.46.0",
"@tippyjs/react": "^4.2.6",
"babel-plugin-styled-components": "^2.0.2",
"babel-preset-next": "^1.4.0",
"eslint": "7.32.0",
"eslint-config-next": "12.0.4",
"immer": "^9.0.7",
"nanoid": "^3.1.30",
"next": "12.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",
@ -3529,6 +3532,15 @@
"@octokit/openapi-types": "^11.2.0"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -3547,6 +3559,18 @@
"react-dom": "^16.x || 17.x"
}
},
"node_modules/@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
"dependencies": {
"tippy.js": "^6.3.1"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -7905,6 +7929,15 @@
"node": ">=12.0.0"
}
},
"node_modules/immer": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz",
"integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz",
@ -13326,6 +13359,14 @@
"node": ">=0.6.0"
}
},
"node_modules/tippy.js": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"dependencies": {
"@popperjs/core": "^2.9.0"
}
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -16858,6 +16899,11 @@
"@octokit/openapi-types": "^11.2.0"
}
},
"@popperjs/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
},
"@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -16869,6 +16915,14 @@
"integrity": "sha512-GufZYxw32OcqejSUpn5XdZi7zP/d+tUZH3S+mMlv3AnMn6MStOBKXOxqWYrJ529hjj1m5JHeghwHmHpj3SRJYg==",
"requires": {}
},
"@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
"requires": {
"tippy.js": "^6.3.1"
}
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@ -20274,6 +20328,11 @@
"queue": "6.0.2"
}
},
"immer": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz",
"integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA=="
},
"import-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz",
@ -24425,6 +24484,14 @@
"setimmediate": "^1.0.4"
}
},
"tippy.js": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"requires": {
"@popperjs/core": "^2.9.0"
}
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",

View File

@ -0,0 +1,34 @@
import styled from 'styled-components';
const Wrapper = styled.div`
.dropdown-toggle {
&:hover {
color: black;
}
}
.tippy-box {
min-width: 135px;
background-color: white;
color: rgb(82 82 82);
box-shadow: rgb(50 50 93 / 25%) 0px 6px 12px -2px, rgb(0 0 0 / 30%) 0px 3px 7px -3px;
.tippy-content {
padding-left: 0;
padding-right: 0;
.dropdown-item {
display: flex;
align-items: center;
padding: .3rem .5rem;
cursor: pointer;
&:hover {
background-color: #eee;
}
}
}
}
`;
export default Wrapper;

View File

@ -0,0 +1,24 @@
import React from 'react';
import Tippy from '@tippyjs/react';
import StyledWrapper from './StyledWrapper';
const Dropdown = ({icon, children, onCreate, placement}) => {
return (
<StyledWrapper className="dropdown">
<Tippy
content={children}
placement={placement || "bottom-end"}
animation={false}
arrow={false}
onCreate={onCreate}
interactive={true}
trigger="click"
appendTo="parent"
>
{icon}
</Tippy>
</StyledWrapper>
);
};
export default Dropdown;

View File

@ -0,0 +1,19 @@
import styled from 'styled-components';
const Wrapper = styled.div`
.collection-item-name {
height: 1.875rem;
cursor: pointer;
user-select: none;
.rotate-90 {
transform: rotateZ(90deg);
}
&.item-focused-in-tab, &:hover {
background:#ededed;
}
}
`;
export default Wrapper;

View File

@ -0,0 +1,84 @@
import React from 'react';
import range from 'lodash/range';
import { IconChevronRight } from '@tabler/icons';
import classnames from 'classnames';
import StyledWrapper from './StyledWrapper';
const CollectionItem = ({item, collectionId, actions, dispatch, activeRequestTabId}) => {
const iconClassName = classnames({
'rotate-90': item.collapsed
});
const itemRowClassName = classnames('flex collection-item-name items-center', {
'item-focused-in-tab': item.id == activeRequestTabId
});
const handleClick = () => {
dispatch({
type: actions.SIDEBAR_COLLECTION_ITEM_CLICK,
itemId: item.id,
collectionId: collectionId
});
};
let indents = range(item.depth);
return (
<StyledWrapper className="flex flex-col">
<div
className={itemRowClassName}
onClick={handleClick}
>
<div className="flex items-center h-full w-full">
{indents && indents.length ? indents.map((i) => {
return (
<div
key={i}
style = {{
width: 16,
height: '100%',
borderRight: 'solid 1px #e1e1e1'
}}
>
&nbsp;{/* Indent */}
</div>
);
}) : null}
<div
className="flex items-center"
style = {{
paddingLeft: 8
}}
>
<div style={{width:16}}>
{item.items && item.items.length ? (
<IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{color: 'rgb(160 160 160)'}}/>
) : null}
</div>
<span className="ml-1">{item.name}</span>
</div>
</div>
</div>
{item.collapsed ? (
<div>
{item.items && item.items.length ? item.items.map((i) => {
return <CollectionItem
key={i.name}
item={i}
collectionId={collectionId}
actions={actions}
dispatch={dispatch}
activeRequestTabId={activeRequestTabId}
/>
}) : null}
</div>
) : null}
</StyledWrapper>
);
};
export default CollectionItem;

View File

@ -0,0 +1,21 @@
import styled from 'styled-components';
const Wrapper = styled.div`
.collection-name {
height: 1.875rem;
cursor: pointer;
user-select: none;
padding-left: 8px;
padding-right: 8px;
.rotate-90 {
transform: rotateZ(90deg);
}
&:hover {
background:#ededed;
}
}
`;
export default Wrapper;

View File

@ -0,0 +1,48 @@
import React from 'react';
import { IconChevronRight } from '@tabler/icons';
import CollectionItem from './CollectionItem';
import classnames from 'classnames';
import StyledWrapper from './StyledWrapper';
const Collection = ({collection, actions, dispatch, activeRequestTabId}) => {
const iconClassName = classnames({
'rotate-90': collection.collapsed
});
const handleClick = () => {
dispatch({
type: actions.SIDEBAR_COLLECTION_CLICK,
id: collection.id
});
};
return (
<StyledWrapper className="flex flex-col">
<div className="flex py-1 collection-name items-center" onClick={handleClick}>
<IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{width:16, color: 'rgb(160 160 160)'}}/>
<span className="ml-1">{collection.name}</span>
</div>
<div>
{collection.collapsed ? (
<div>
{collection.items && collection.items.length ? collection.items.map((i) => {
return <CollectionItem
key={i.name}
item={i}
collectionId={collection.id}
actions={actions}
dispatch={dispatch}
activeRequestTabId={activeRequestTabId}
/>
}) : null}
</div>
) : null}
</div>
</StyledWrapper>
);
};
export default Collection;

View File

@ -0,0 +1,20 @@
import React from 'react';
import Collection from './Collection';
const Collections = ({collections, actions, dispatch, activeRequestTabId}) => {
return (
<div className="mt-4 flex flex-col">
{collections && collections.length ? collections.map((c) => {
return <Collection
collection={c}
key={c.id}
actions={actions}
dispatch={dispatch}
activeRequestTabId={activeRequestTabId}
/>
}) : null}
</div>
);
};
export default Collections;

View File

@ -0,0 +1,38 @@
import React from 'react';
import Collections from './Collections';
import { IconDatabase, IconSearch } from '@tabler/icons';
const Sidebar = ({collections, actions, dispatch, activeRequestTabId}) => {
return (
<aside>
<div className="mt-4 px-2 flex">
<IconDatabase size={20} strokeWidth={1.5}/>
<span className="ml-1">No Environment</span>
</div>
<div className="mt-4 relative collection-filter px-2">
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<span className="text-gray-500 sm:text-sm">
<IconSearch size={16} strokeWidth={1.5}/>
</span>
</div>
<input
type="text"
name="price"
id="price"
className="block w-full pl-7 py-1 sm:text-sm"
placeholder="search"
/>
</div>
<Collections
collections={collections}
actions={actions}
dispatch={dispatch}
activeRequestTabId={activeRequestTabId}
/>
</aside>
);
};
export default Sidebar;

View File

@ -1,4 +1,7 @@
import Navbar from './components/Navbar';
import Sidebar from './components/Sidebar';
export default Navbar;
export {
Navbar,
Sidebar
};

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": false,
"baseUrl": "./",
"paths": {
"components/*": ["src/components/*"],
"pageComponents/*": ["src/pageComponents/*"],
"providers/*": ["src/providers/*"]
}
},
"exclude": ["node_modules", "dist"]
}

View File

@ -13,6 +13,9 @@
"@fortawesome/react-fontawesome": "^0.1.16",
"@grafnode/www": "^0.0.1",
"@tabler/icons": "^1.46.0",
"@tippyjs/react": "^4.2.6",
"immer": "^9.0.7",
"nanoid": "^3.1.30",
"next": "12.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",
@ -2623,6 +2626,15 @@
"node": ">= 8"
}
},
"node_modules/@popperjs/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -2642,6 +2654,18 @@
"react-dom": "^16.x || 17.x"
}
},
"node_modules/@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
"dependencies": {
"tippy.js": "^6.3.1"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -5059,6 +5083,15 @@
"node": ">=12.0.0"
}
},
"node_modules/immer": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz",
"integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/import-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz",
@ -7565,6 +7598,14 @@
"node": ">=0.6.0"
}
},
"node_modules/tippy.js": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"dependencies": {
"@popperjs/core": "^2.9.0"
}
},
"node_modules/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@ -9782,6 +9823,11 @@
"fastq": "^1.6.0"
}
},
"@popperjs/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.0.tgz",
"integrity": "sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ=="
},
"@rushstack/eslint-patch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz",
@ -9794,6 +9840,14 @@
"integrity": "sha512-GufZYxw32OcqejSUpn5XdZi7zP/d+tUZH3S+mMlv3AnMn6MStOBKXOxqWYrJ529hjj1m5JHeghwHmHpj3SRJYg==",
"requires": {}
},
"@tippyjs/react": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/@tippyjs/react/-/react-4.2.6.tgz",
"integrity": "sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==",
"requires": {
"tippy.js": "^6.3.1"
}
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -11646,6 +11700,11 @@
"queue": "6.0.2"
}
},
"immer": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.7.tgz",
"integrity": "sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA=="
},
"import-cwd": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz",
@ -13517,6 +13576,14 @@
"setimmediate": "^1.0.4"
}
},
"tippy.js": {
"version": "6.3.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
"requires": {
"@popperjs/core": "^2.9.0"
}
},
"tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",

View File

@ -25,6 +25,9 @@
"@fortawesome/react-fontawesome": "^0.1.16",
"@grafnode/www": "^0.0.1",
"@tabler/icons": "^1.46.0",
"@tippyjs/react": "^4.2.6",
"immer": "^9.0.7",
"nanoid": "^3.1.30",
"next": "12.0.4",
"react": "17.0.2",
"react-dom": "17.0.2",

View File

@ -0,0 +1,60 @@
import styled from 'styled-components';
const Wrapper = styled.div`
display: flex;
width: 100%;
height: 100%;
min-height: calc(100vh - 38px);
aside {
min-width: 230px;
border-right: solid 1px #e1e1e1;
}
section.main {
display: flex;
section.request-pane, section.response-pane {
}
}
div.drag-request {
display: flex;
width: 1px;
padding: 0;
cursor: col-resize;
background: #e1e1e1;
&:hover {
background: silver;
}
}
.fw-600 {
font-weight: 600;
}
.react-tabs {
.react-tabs__tab-list {
padding-left: 1rem;
border-bottom: 1px solid #cfcfcf;
.react-tabs__tab--selected {
border-color: #cfcfcf;
}
}
}
.collection-filter {
input {
border: 1px solid rgb(211 211 211);
border-radius: 2px;
&:focus {
outline: none;
}
}
}
`;
export default Wrapper;

View File

@ -0,0 +1,33 @@
import React from 'react';
import {Navbar, Sidebar} from '@grafnode/components';
import actions from 'providers/Store/actions';
import { useStore } from 'providers/Store';
import StyledWrapper from './StyledWrapper';
export default function Main() {
const [state, dispatch] = useStore();
const {
collections,
activeRequestTabId
} = state;
console.log(actions);
return (
<div>
<Navbar />
<StyledWrapper>
<Sidebar
collections={collections}
actions={actions}
dispatch={dispatch}
activeRequestTabId={activeRequestTabId}
/>
<section className='mt-4 flex flex-grow flex-col'>
Request & Response Tabs
</section>
</StyledWrapper>
</div>
)
}

View File

@ -1,8 +1,14 @@
import { StoreProvider } from 'providers/Store';
import '../styles/globals.css'
import 'tailwindcss/dist/tailwind.min.css';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
return (
<StoreProvider>
<Component {...pageProps} />
</StoreProvider>
);
}
export default MyApp

View File

@ -1,5 +1,5 @@
import Head from 'next/head';
import Navbar from '@grafnode/components';
import Main from 'pageComponents/Main';
export default function Home() {
return (
@ -10,7 +10,7 @@ export default function Home() {
</Head>
<main>
<Navbar />
<Main />
</main>
</div>
)

View File

@ -0,0 +1,11 @@
const SIDEBAR_COLLECTION_CLICK = "SIDEBAR_COLLECTION_CLICK";
const SIDEBAR_COLLECTION_ITEM_CLICK = "SIDEBAR_COLLECTION_ITEM_CLICK";
const REQUEST_TAB_CLICK = "REQUEST_TAB_CLICK";
const REQUEST_TAB_CLOSE = "REQUEST_TAB_CLOSE";
export default {
SIDEBAR_COLLECTION_CLICK,
SIDEBAR_COLLECTION_ITEM_CLICK,
REQUEST_TAB_CLICK,
REQUEST_TAB_CLOSE
};

View File

@ -0,0 +1,79 @@
import React, { useContext, useReducer, createContext } from 'react';
import reducer from './reducer';
import { nanoid } from 'nanoid';
export const StoreContext = createContext();
const tabId1 = nanoid();
const collection = {
"id": nanoid(),
"name": "SpaceX",
"items": [
{
"id": nanoid(),
"name": "Launches",
"depth": 1,
"items": [
{
"id": nanoid(),
"depth": 2,
"name": "Capsules",
"request": {
"url": "https://api.spacex.land/graphql/",
"method": "POST",
"headers": [],
"body": {
"mimeType": "application/graphql",
"graphql": {
"query": "{\n launchesPast(limit: 10) {\n mission_name\n launch_date_local\n launch_site {\n site_name_long\n }\n links {\n article_link\n video_link\n }\n rocket {\n rocket_name\n first_stage {\n cores {\n flight\n core {\n reuse_count\n status\n }\n }\n }\n second_stage {\n payloads {\n payload_type\n payload_mass_kg\n payload_mass_lbs\n }\n }\n }\n ships {\n name\n home_port\n image\n }\n }\n}",
"variables": ""
}
}
}
},
{
"id": nanoid(),
"depth": 2,
"name": "Missions",
"request": {
"url": "https://api.spacex.land/graphql/",
"method": "POST",
"headers": [],
"body": {
"mimeType": "application/graphql",
"graphql": {
"query": "{\n launches {\n launch_site {\n site_id\n site_name\n site_name_long\n }\n launch_success\n }\n}",
"variables": ""
}
}
}
}
]
}
]
};
const initialState = {
collections: [collection],
activeRequestTabId: null,
requestTabs: []
};
export const StoreProvider = props => {
const [state, dispatch] = useReducer(reducer, initialState);
return <StoreContext.Provider value={[state, dispatch]} {...props} />;
};
export const useStore = () => {
const context = useContext(StoreContext);
if (context === undefined) {
throw new Error(`useStore must be used within a StoreProvider`);
}
return context;
};
export default StoreProvider;

View File

@ -0,0 +1,79 @@
import produce from 'immer';
import find from 'lodash/find';
import filter from 'lodash/filter';
import actions from './actions';
import {
flattenItems,
findItem,
isItemARequest,
itemIsOpenedInTabs
} from './utils';
const reducer = (state, action) => {
switch (action.type) {
case actions.SIDEBAR_COLLECTION_CLICK: {
return produce(state, (draft) => {
const collecton = find(draft.collections, (c) => c.id === action.id);
if(collecton) {
collecton.collapsed = !collecton.collapsed;
}
});
}
case actions.SIDEBAR_COLLECTION_ITEM_CLICK: {
return produce(state, (draft) => {
const collecton = find(draft.collections, (c) => c.id === action.collectionId);
if(collecton) {
let flattenedItems = flattenItems(collecton.items);
let item = findItem(flattenedItems, action.itemId);
if(item) {
item.collapsed = !item.collapsed;
if(isItemARequest(item)) {
if(itemIsOpenedInTabs(item, draft.requestTabs)) {
draft.activeRequestTabId = item.id;
} else {
draft.requestTabs.push({
id: item.id,
name: item.name,
method: item.request.method,
collectionId: collecton.id
});
draft.activeRequestTabId = item.id;
}
}
}
}
});
}
case actions.REQUEST_TAB_CLICK: {
return produce(state, (draft) => {
draft.activeRequestTabId = action.requestTab.id;
});
}
case actions.REQUEST_TAB_CLOSE: {
return produce(state, (draft) => {
draft.requestTabs = filter(draft.requestTabs, (rt) => rt.id !== action.requestTab.id);
if(draft.requestTabs && draft.requestTabs.length) {
draft.activeRequestTabId = draft.requestTabs[0].id;
console.log(draft.activeRequestTabId);
} else {
draft.activeRequestTabId = null;
}
});
}
default: {
return state;
}
}
}
export default reducer;

View File

@ -0,0 +1,32 @@
import each from 'lodash/each';
import find from 'lodash/find';
export const flattenItems = (items = []) => {
const flattenedItems = [];
const flatten = (itms, flattened) => {
each(itms, (i) => {
flattened.push(i);
if(i.items && i.items.length) {
flatten(i.items, flattened);
}
})
}
flatten(items, flattenedItems);
return flattenedItems;
};
export const findItem = (items = [], itemId) => {
return find(items, (i) => i.id === itemId);
};
export const isItemARequest = (item) => {
return item.hasOwnProperty('request');
};
export const itemIsOpenedInTabs = (item, tabs) => {
return find(tabs, (t) => t.id === item.id);
};

View File

@ -1,16 +1,16 @@
html,
body {
padding: 0;
html, body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
padding: 0;
font-size: 1rem;
color: rgb(62 62 62);
font-kerning: none;
text-rendering: optimizeSpeed;
letter-spacing: normal;
/* font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; */
font-family: Inter, sans-serif !important;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
body {
font-size: 0.875rem;
}