feat: Added Responsive Sidebar (#3303)

* feat: UIContext Added

* feat: window size hook added

* feat: UIContext provider attached to app

* fix: UI Changes for Responsive Sidebar

* fix: underscore removed from toggle option

* fix: dependency issue resolved

* fix: window size hooks resolved

* fix: handle resize function position changed
diff --git a/config-ui/src/components/Nav.jsx b/config-ui/src/components/Nav.jsx
index 403c9d7..347865f 100644
--- a/config-ui/src/components/Nav.jsx
+++ b/config-ui/src/components/Nav.jsx
@@ -15,73 +15,108 @@
  * limitations under the License.
  *
  */
-import React from 'react'
-import { Alignment, Position, Popover, Navbar, Icon } from '@blueprintjs/core'
+import React, { useCallback, useContext, useEffect, useState } from 'react'
+import { Position, Popover, Navbar, Icon } from '@blueprintjs/core'
 import '@/styles/nav.scss'
 import { ReactComponent as SlackIcon } from '@/images/slack-mark-monochrome-black.svg'
 import { ReactComponent as SlackLogo } from '@/images/slack-rgb.svg'
+import UIContext from '@/store/UIContext'
+import useWindowSize from '@/hooks/useWIndowSize'
 
 const Nav = () => {
+  const { changeSidebarVisibility, desktopBreakpointWidth, sidebarVisible } =
+    useContext(UIContext)
+  const [menuClass, setMenuClass] = useState('navbarMenuButton')
+  const size = useWindowSize()
+
+  const toggleSidebarOpen = useCallback(
+    (open) => {
+      changeSidebarVisibility(open)
+      setMenuClass(() =>
+        open ? 'navbarMenuButtonSidebarOpened' : 'navbarMenuButton'
+      )
+    },
+    [changeSidebarVisibility]
+  )
+
+  useEffect(
+    () =>
+      desktopBreakpointWidth <= size.width
+        ? toggleSidebarOpen(true)
+        : toggleSidebarOpen(false),
+    [size, toggleSidebarOpen, desktopBreakpointWidth]
+  )
+
   return (
     <Navbar className='navbar'>
-      <Navbar.Group align={Alignment.RIGHT}>
-        <a
-          href='https://github.com/apache/incubator-devlake'
-          rel='noreferrer'
-          target='_blank'
-          className='navIconLink'
-        >
-          <Icon icon='git-branch' size={16} />
-        </a>
-        <Navbar.Divider />
-        <a
-          href='mailto:hello@merico.dev'
-          rel='noreferrer'
-          target='_blank'
-          className='navIconLink'
-        >
-          <Icon icon='envelope' size={16} />
-        </a>
-        <Navbar.Divider />
-        {/* DISCORD: !DISABLED! */}
-        {/* <a href='https://discord.com/invite/83rDG6ydVZ' rel='noreferrer' target='_blank' className='navIconLink'>
+      <Navbar.Group className={menuClass}>
+        <Icon
+          icon={sidebarVisible ? 'menu-closed' : 'menu-open'}
+          onClick={(e) => toggleSidebarOpen(!sidebarVisible)}
+          size={16}
+        />
+      </Navbar.Group>
+      {!sidebarVisible && (
+        <Navbar.Group className='navbarItems'>
+          <a
+            href='https://github.com/apache/incubator-devlake'
+            rel='noreferrer'
+            target='_blank'
+            className='navIconLink'
+          >
+            <Icon icon='git-branch' size={16} />
+          </a>
+          <Navbar.Divider />
+          <a
+            href='mailto:hello@merico.dev'
+            rel='noreferrer'
+            target='_blank'
+            className='navIconLink'
+          >
+            <Icon icon='envelope' size={16} />
+          </a>
+          <Navbar.Divider />
+          {/* DISCORD: !DISABLED! */}
+          {/* <a href='https://discord.com/invite/83rDG6ydVZ' rel='noreferrer' target='_blank' className='navIconLink'>
           <DiscordIcon className='discordIcon' width={16} height={16} />
         </a> */}
-        {/* SLACK: ENABLED (Primary) */}
-        <Popover position={Position.LEFT}>
-          <SlackIcon
-            className='slackIcon'
-            width={16}
-            height={16}
-            style={{ cursor: 'pointer' }}
-          />
-          <>
-            <div
-              style={{ maxWidth: '200px', padding: '10px', fontSize: '11px' }}
-            >
-              <SlackLogo
-                width={131}
-                height={49}
-                style={{ display: 'block', margin: '0 auto' }}
-              />
-              <p style={{ textAlign: 'center' }}>
-                Want to interact with the <strong>Merico Community</strong>?
-                Join us on our Slack Channel.
-                <br />
-                <a
-                  href='https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ'
-                  rel='noreferrer'
-                  target='_blank'
-                  className='bp3-button bp3-intent-warning bp3-elevation-1 bp3-small'
-                  style={{ marginTop: '10px' }}
-                >
-                  Message us on&nbsp;<strong>Slack</strong>
-                </a>
-              </p>
-            </div>
-          </>
-        </Popover>
-      </Navbar.Group>
+          {/* SLACK: ENABLED (Primary) */}
+          <Popover position={Position.LEFT}>
+            <SlackIcon
+              className='slackIcon'
+              width={16}
+              height={16}
+              style={{ cursor: 'pointer' }}
+            />
+            <>
+              <div
+                style={{ maxWidth: '200px', padding: '10px', fontSize: '11px' }}
+              >
+                <SlackLogo
+                  width={131}
+                  height={49}
+                  style={{ display: 'block', margin: '0 auto' }}
+                />
+                <p style={{ textAlign: 'center' }}>
+                  Want to interact with the <strong>Merico Community</strong>?
+                  Join us on our Slack Channel.
+                  <br />
+                  <a
+                    href='https://join.slack.com/t/devlake-io/shared_invite/zt-17b6vuvps-x98pqseoUagM7EAmKC82xQ'
+                    rel='noreferrer'
+                    target='_blank'
+                    className='bp3-button bp3-intent-warning bp3-elevation-1 bp3-small'
+                    style={{ marginTop: '10px' }}
+                  >
+                    Message us on&nbsp
+                    <strong>Slack</strong>
+                  </a>
+                </p>
+              </div>
+            </>
+          </Popover>
+        </Navbar.Group>
+      )}
     </Navbar>
   )
 }
diff --git a/config-ui/src/components/Sidebar.jsx b/config-ui/src/components/Sidebar.jsx
index 496a4b4..3c3356d 100644
--- a/config-ui/src/components/Sidebar.jsx
+++ b/config-ui/src/components/Sidebar.jsx
@@ -15,7 +15,7 @@
  * limitations under the License.
  *
  */
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useState, useContext } from 'react'
 import {
   // BrowserRouter as Router,
   useRouteMatch
@@ -29,9 +29,11 @@
 import { ReactComponent as LogoText } from '@/images/devlake-textmark.svg'
 
 import '@/styles/sidebar.scss'
+import UIContext from '../store/UIContext'
 
 const Sidebar = () => {
   const activeRoute = useRouteMatch()
+  const uiContext = useContext(UIContext)
 
   const [menu, setMenu] = useState(MenuConfiguration(activeRoute))
   const [versionTag, setVersionTag] = useState('')
@@ -56,7 +58,7 @@
     fetchVersion()
   }, [])
 
-  return (
+  return uiContext.sidebarVisible ? (
     <Card
       interactive={false}
       elevation={Elevation.ZERO}
@@ -90,6 +92,8 @@
         <br />
       </span>
     </Card>
+  ) : (
+    <></>
   )
 }
 
diff --git a/config-ui/src/hooks/useWIndowSize.jsx b/config-ui/src/hooks/useWIndowSize.jsx
new file mode 100644
index 0000000..b26c050
--- /dev/null
+++ b/config-ui/src/hooks/useWIndowSize.jsx
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useEffect, useState } from 'react'
+
+function useWindowSize() {
+  const [windowSize, setWindowSize] = useState({
+    width: 320,
+    height: 240
+  })
+
+  useEffect(() => {
+    function handleResize() {
+      setWindowSize({
+        width: window.innerWidth,
+        height: window.innerHeight
+      })
+    }
+    if (typeof window !== 'undefined') {
+      window.addEventListener('resize', handleResize)
+      handleResize()
+    }
+    return () => window.removeEventListener('resize', handleResize)
+  }, [])
+
+  return windowSize
+}
+
+export default useWindowSize
diff --git a/config-ui/src/index.js b/config-ui/src/index.js
index bc85b9d..0a1693a 100644
--- a/config-ui/src/index.js
+++ b/config-ui/src/index.js
@@ -18,7 +18,12 @@
 
 import React from 'react'
 import ReactDOM from 'react-dom'
-
 import App from './App'
+import { UIContextProvider } from '@/store/UIContext'
 
-ReactDOM.render(<App />, document.getElementById('app'))
+ReactDOM.render(
+  <UIContextProvider>
+    <App />
+  </UIContextProvider>,
+  document.getElementById('app')
+)
diff --git a/config-ui/src/store/UIContext.jsx b/config-ui/src/store/UIContext.jsx
new file mode 100644
index 0000000..6e1e22f
--- /dev/null
+++ b/config-ui/src/store/UIContext.jsx
@@ -0,0 +1,42 @@
+/*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  *
+  */
+import React, { useState } from 'react'
+
+const UIContext = React.createContext({
+  sidebarVisible: false,
+  desktopBreakpointWidth: 850,
+  changeSidebarVisibility: () => {}
+})
+
+export const UIContextProvider = (props) => {
+  const [sidebarVisible, setSidebarVisible] = useState(false)
+
+  const contextValue = {
+    sidebarVisible: sidebarVisible,
+    changeSidebarVisibility: setSidebarVisible,
+    desktopBreakpointWidth: 850
+  }
+
+  return (
+    <UIContext.Provider value={contextValue}>
+      {props.children}
+    </UIContext.Provider>
+  )
+}
+
+export default UIContext
\ No newline at end of file
diff --git a/config-ui/src/styles/common.scss b/config-ui/src/styles/common.scss
index 2e675f4..5d0077d 100644
--- a/config-ui/src/styles/common.scss
+++ b/config-ui/src/styles/common.scss
@@ -19,6 +19,11 @@
   padding-top: 50px;
   min-height: 100vh;
   overflow: hidden;
+
+  @media only screen and (max-width: 850px) {
+    justify-content: flex-start;
+    padding: 50px 30px;
+  }
 }
 
 .main {
@@ -31,6 +36,12 @@
   flex-direction: column;
   justify-content: flex-start;
   align-items: center;
+
+  @media only screen and (max-width: 850px) {
+    margin-left: 0;
+    max-width: 100vw;
+    padding: 0rem;
+  }
 }
 
 .description {
diff --git a/config-ui/src/styles/nav.scss b/config-ui/src/styles/nav.scss
index 501dbbb..e4f625a 100644
--- a/config-ui/src/styles/nav.scss
+++ b/config-ui/src/styles/nav.scss
@@ -22,6 +22,39 @@
   z-index: 1;
 }
 
+.navbarMenuButton {
+  position: absolute;
+  display: none;
+  color: #7497F7;
+  &:hover {
+    color: #E8471C; 
+  }
+
+  @media only screen and (max-width: 850px) {
+    display: flex;
+    left: 0px;
+  }
+}
+
+.navbarMenuButtonSidebarOpened {
+  position: absolute;
+  display: none;
+  color: #7497F7;
+  &:hover {
+    color: #E8471C; 
+  }
+
+  @media only screen and (max-width: 850px) {
+    display: flex;
+    left: 240px;
+  }
+}
+
+.navbarItems {
+  position:absolute;
+  right:40px;
+}
+
 .navIconLink:hover {
   color: #E8471C;
 }