mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 15:33:11 +01:00
feat: Sidebar and StoreProvider
This commit is contained in:
parent
07fc8af7ed
commit
a863f9730d
67
package-lock.json
generated
67
package-lock.json
generated
@ -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",
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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'
|
||||
}}
|
||||
>
|
||||
{/* 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;
|
@ -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;
|
@ -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;
|
@ -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;
|
38
packages/grafnode-components/src/components/Sidebar/index.js
Normal file
38
packages/grafnode-components/src/components/Sidebar/index.js
Normal 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;
|
@ -1,4 +1,7 @@
|
||||
import Navbar from './components/Navbar';
|
||||
import Sidebar from './components/Sidebar';
|
||||
|
||||
export default Navbar;
|
||||
|
||||
export {
|
||||
Navbar,
|
||||
Sidebar
|
||||
};
|
||||
|
13
packages/grafnode-run/jsconfig.json
Normal file
13
packages/grafnode-run/jsconfig.json
Normal 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"]
|
||||
}
|
67
packages/grafnode-run/package-lock.json
generated
67
packages/grafnode-run/package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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;
|
33
packages/grafnode-run/src/pageComponents/Main/index.js
Normal file
33
packages/grafnode-run/src/pageComponents/Main/index.js
Normal 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>
|
||||
)
|
||||
}
|
@ -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
|
||||
|
@ -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>
|
||||
)
|
||||
|
11
packages/grafnode-run/src/providers/Store/actions.js
Normal file
11
packages/grafnode-run/src/providers/Store/actions.js
Normal 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
|
||||
};
|
79
packages/grafnode-run/src/providers/Store/index.js
Normal file
79
packages/grafnode-run/src/providers/Store/index.js
Normal 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;
|
79
packages/grafnode-run/src/providers/Store/reducer.js
Normal file
79
packages/grafnode-run/src/providers/Store/reducer.js
Normal 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;
|
32
packages/grafnode-run/src/providers/Store/utils.js
Normal file
32
packages/grafnode-run/src/providers/Store/utils.js
Normal 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);
|
||||
};
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user