diff --git a/package-lock.json b/package-lock.json index 9d577e71b..8d95d677c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/packages/grafnode-components/src/components/Dropdown/StyledWrapper.js b/packages/grafnode-components/src/components/Dropdown/StyledWrapper.js new file mode 100644 index 000000000..83ce5785d --- /dev/null +++ b/packages/grafnode-components/src/components/Dropdown/StyledWrapper.js @@ -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; diff --git a/packages/grafnode-components/src/components/Dropdown/index.js b/packages/grafnode-components/src/components/Dropdown/index.js new file mode 100644 index 000000000..ae30d125f --- /dev/null +++ b/packages/grafnode-components/src/components/Dropdown/index.js @@ -0,0 +1,24 @@ +import React from 'react'; +import Tippy from '@tippyjs/react'; +import StyledWrapper from './StyledWrapper'; + +const Dropdown = ({icon, children, onCreate, placement}) => { + return ( + + + {icon} + + + ); +}; + +export default Dropdown; diff --git a/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js new file mode 100644 index 000000000..e80da781c --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/StyledWrapper.js @@ -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; diff --git a/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/index.js b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/index.js new file mode 100644 index 000000000..57fb896d5 --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/CollectionItem/index.js @@ -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 ( + +
+
+ {indents && indents.length ? indents.map((i) => { + return ( +
+  {/* Indent */} +
+ ); + }) : null} +
+
+ {item.items && item.items.length ? ( + + ) : null} +
+ + {item.name} +
+
+
+ + {item.collapsed ? ( +
+ {item.items && item.items.length ? item.items.map((i) => { + return + }) : null} +
+ ) : null} +
+ ); +}; + +export default CollectionItem; \ No newline at end of file diff --git a/packages/grafnode-components/src/components/Sidebar/Collections/Collection/StyledWrapper.js b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/StyledWrapper.js new file mode 100644 index 000000000..3e50288a2 --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/StyledWrapper.js @@ -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; diff --git a/packages/grafnode-components/src/components/Sidebar/Collections/Collection/index.js b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/index.js new file mode 100644 index 000000000..0d1f4b770 --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/Collections/Collection/index.js @@ -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 ( + +
+ + {collection.name} +
+ +
+ {collection.collapsed ? ( +
+ {collection.items && collection.items.length ? collection.items.map((i) => { + return + }) : null} +
+ ) : null} +
+
+ ); +}; + +export default Collection; \ No newline at end of file diff --git a/packages/grafnode-components/src/components/Sidebar/Collections/index.js b/packages/grafnode-components/src/components/Sidebar/Collections/index.js new file mode 100644 index 000000000..93199b4fe --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/Collections/index.js @@ -0,0 +1,20 @@ +import React from 'react'; +import Collection from './Collection'; + +const Collections = ({collections, actions, dispatch, activeRequestTabId}) => { + return ( +
+ {collections && collections.length ? collections.map((c) => { + return + }) : null} +
+ ); +}; + +export default Collections; \ No newline at end of file diff --git a/packages/grafnode-components/src/components/Sidebar/index.js b/packages/grafnode-components/src/components/Sidebar/index.js new file mode 100644 index 000000000..1ad793e02 --- /dev/null +++ b/packages/grafnode-components/src/components/Sidebar/index.js @@ -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 ( + + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/packages/grafnode-components/src/index.js b/packages/grafnode-components/src/index.js index e4a1c8de5..9753a4832 100644 --- a/packages/grafnode-components/src/index.js +++ b/packages/grafnode-components/src/index.js @@ -1,4 +1,7 @@ import Navbar from './components/Navbar'; +import Sidebar from './components/Sidebar'; -export default Navbar; - +export { + Navbar, + Sidebar +}; diff --git a/packages/grafnode-run/jsconfig.json b/packages/grafnode-run/jsconfig.json new file mode 100644 index 000000000..f0c3aa3ad --- /dev/null +++ b/packages/grafnode-run/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es2017", + "allowSyntheticDefaultImports": false, + "baseUrl": "./", + "paths": { + "components/*": ["src/components/*"], + "pageComponents/*": ["src/pageComponents/*"], + "providers/*": ["src/providers/*"] + } + }, + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/packages/grafnode-run/package-lock.json b/packages/grafnode-run/package-lock.json index 37d59a526..1ab1e9add 100644 --- a/packages/grafnode-run/package-lock.json +++ b/packages/grafnode-run/package-lock.json @@ -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", diff --git a/packages/grafnode-run/package.json b/packages/grafnode-run/package.json index 995554cf5..a70bdef53 100644 --- a/packages/grafnode-run/package.json +++ b/packages/grafnode-run/package.json @@ -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", diff --git a/packages/grafnode-run/src/pageComponents/Main/StyledWrapper.js b/packages/grafnode-run/src/pageComponents/Main/StyledWrapper.js new file mode 100644 index 000000000..2d3ccdd1b --- /dev/null +++ b/packages/grafnode-run/src/pageComponents/Main/StyledWrapper.js @@ -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; diff --git a/packages/grafnode-run/src/pageComponents/Main/index.js b/packages/grafnode-run/src/pageComponents/Main/index.js new file mode 100644 index 000000000..c631f3797 --- /dev/null +++ b/packages/grafnode-run/src/pageComponents/Main/index.js @@ -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 ( +
+ + + +
+ Request & Response Tabs +
+
+
+ ) +} \ No newline at end of file diff --git a/packages/grafnode-run/src/pages/_app.js b/packages/grafnode-run/src/pages/_app.js index 5a8f965a4..f7e259e84 100644 --- a/packages/grafnode-run/src/pages/_app.js +++ b/packages/grafnode-run/src/pages/_app.js @@ -1,8 +1,14 @@ +import { StoreProvider } from 'providers/Store'; + import '../styles/globals.css' import 'tailwindcss/dist/tailwind.min.css'; function MyApp({ Component, pageProps }) { - return + return ( + + + + ); } export default MyApp diff --git a/packages/grafnode-run/src/pages/index.js b/packages/grafnode-run/src/pages/index.js index ee372a94a..413ef924b 100644 --- a/packages/grafnode-run/src/pages/index.js +++ b/packages/grafnode-run/src/pages/index.js @@ -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() {
- +
) diff --git a/packages/grafnode-run/src/providers/Store/actions.js b/packages/grafnode-run/src/providers/Store/actions.js new file mode 100644 index 000000000..dc1a58ac3 --- /dev/null +++ b/packages/grafnode-run/src/providers/Store/actions.js @@ -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 +}; diff --git a/packages/grafnode-run/src/providers/Store/index.js b/packages/grafnode-run/src/providers/Store/index.js new file mode 100644 index 000000000..6fda13cca --- /dev/null +++ b/packages/grafnode-run/src/providers/Store/index.js @@ -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 ; +}; + +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; diff --git a/packages/grafnode-run/src/providers/Store/reducer.js b/packages/grafnode-run/src/providers/Store/reducer.js new file mode 100644 index 000000000..cfea0b46a --- /dev/null +++ b/packages/grafnode-run/src/providers/Store/reducer.js @@ -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; \ No newline at end of file diff --git a/packages/grafnode-run/src/providers/Store/utils.js b/packages/grafnode-run/src/providers/Store/utils.js new file mode 100644 index 000000000..72ebc4e9c --- /dev/null +++ b/packages/grafnode-run/src/providers/Store/utils.js @@ -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); +}; diff --git a/packages/grafnode-run/src/styles/globals.css b/packages/grafnode-run/src/styles/globals.css index e5e2dcc23..78178bcd3 100644 --- a/packages/grafnode-run/src/styles/globals.css +++ b/packages/grafnode-run/src/styles/globals.css @@ -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; +} \ No newline at end of file