diff --git a/packages/bruno-app/src/components/Documentation/StyledWrapper.js b/packages/bruno-app/src/components/Documentation/StyledWrapper.js index af80d4c085..0bac92a9bb 100644 --- a/packages/bruno-app/src/components/Documentation/StyledWrapper.js +++ b/packages/bruno-app/src/components/Documentation/StyledWrapper.js @@ -1,8 +1,105 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` + display: flex; + flex-direction: column; + height: 100%; + background-color: #1e1e1e; + color: white; + .editing-mode { cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 4px; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + } + + .docs-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + background-color: #222; + border-bottom: 1px solid #333; + color: white; + font-size: 16px; + font-weight: 500; + + .close-button { + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + width: 24px; + height: 24px; + border-radius: 3px; + color: white; + cursor: pointer; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + } + } + + .docs-footer { + display: flex; + justify-content: flex-end; + padding: 12px 16px; + background-color: #222; + border-top: 1px solid #333; + + .save-button { + background-color: #3d81df; + color: white; + border: none; + border-radius: 4px; + padding: 6px 20px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #2d71cf; + } + } + } + + .editor-container { + display: flex; + flex-direction: column; + height: 100%; + + .CodeMirror { + height: 100%; + font-family: monospace; + background-color: #1e1e1e; + color: #d4d4d4; + + .CodeMirror-gutters { + background-color: #1e1e1e; + border-right: 1px solid #333; + } + + .CodeMirror-linenumber { + color: #858585; + } + } + } + + .markdown-container { + line-height: 1.5; + padding: 16px; + color: #d4d4d4; } `; diff --git a/packages/bruno-app/src/components/Documentation/index.js b/packages/bruno-app/src/components/Documentation/index.js index 0af0d7588c..bde0670966 100644 --- a/packages/bruno-app/src/components/Documentation/index.js +++ b/packages/bruno-app/src/components/Documentation/index.js @@ -8,6 +8,7 @@ import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import Markdown from 'components/MarkDown'; import CodeEditor from 'components/CodeEditor'; import StyledWrapper from './StyledWrapper'; +import { IconEdit, IconX, IconFileText } from '@tabler/icons'; const Documentation = ({ item, collection }) => { const dispatch = useDispatch(); @@ -30,19 +31,53 @@ const Documentation = ({ item, collection }) => { ); }; - const onSave = () => dispatch(saveRequest(item.uid, collection.uid)); + const handleDiscardChanges = () => { + dispatch( + updateRequestDocs({ + itemUid: item.uid, + collectionUid: collection.uid, + docs: docs + }) + ); + toggleViewMode(); + }; + + const onSave = () => { + dispatch(saveRequest(item.uid, collection.uid)); + toggleViewMode(); + }; if (!item) { return null; } return ( - -
- {isEditing ? 'Preview' : 'Edit'} + +
+
+ + Documentation +
+
+ {isEditing ? ( + + ) : ( +
+ +
+ )} +
{isEditing ? ( + <> +
{ onEdit={onEdit} onSave={onSave} mode="application/text" - /> + lineNumbers={true} + /> +
+
+ +
+ ) : ( +
+ {docs?.length > 0 ? ( + ) : ( +
No documentation available. Click the edit button to add documentation.
+ )} +
)}
); diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/StyledWrapper.js index cca5620255..087d00d587 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/StyledWrapper.js @@ -37,6 +37,10 @@ const Wrapper = styled.div` position: relative; display: inline-block; cursor: pointer; + + &.active svg { + color: ${(props) => props.theme.colors.text.yellow} !important; + } } .infotip:hover .infotiptext { diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index 9f3e600d06..bc8c20e1ef 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -5,14 +5,14 @@ import { requestUrlChanged, updateRequestMethod } from 'providers/ReduxStore/sli import { saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import HttpMethodSelector from './HttpMethodSelector'; import { useTheme } from 'providers/Theme'; -import { IconDeviceFloppy, IconArrowRight, IconCode } from '@tabler/icons'; +import { IconDeviceFloppy, IconArrowRight, IconCode, IconBook } from '@tabler/icons'; import SingleLineEditor from 'components/SingleLineEditor'; import { isMacOS } from 'utils/common/platform'; import StyledWrapper from './StyledWrapper'; import GenerateCodeItem from 'components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index'; import toast from 'react-hot-toast'; -const QueryUrl = ({ item, collection, handleRun }) => { +const QueryUrl = ({ item, collection, handleRun, showDocsPanel, toggleDocsPanel }) => { const { theme, storedTheme } = useTheme(); const dispatch = useDispatch(); const method = item.draft ? get(item, 'draft.request.method') : get(item, 'request.method'); @@ -102,6 +102,23 @@ const QueryUrl = ({ item, collection, handleRun }) => { item={item} />
+
{ + e.stopPropagation(); + toggleDocsPanel(); + }} + > + + + Documentation + +
{ diff --git a/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js b/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js index ec0a032171..e1858aa4b9 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestTabPanel/StyledWrapper.js @@ -27,6 +27,23 @@ const StyledWrapper = styled.div` } } + .docs-toggle { + color: ${(props) => props.theme.textLink}; + &:hover { + background-color: ${(props) => props.theme.dropdown.hoverBg}; + } + } + + section.docs-pane { + width: 350px; + min-width: 350px; + height: 100%; + border-left: 1px solid #333; + display: flex; + flex-direction: column; + background-color: #1e1e1e; + } + div.graphql-docs-explorer-container { background: white; outline: none; diff --git a/packages/bruno-app/src/components/RequestTabPanel/index.js b/packages/bruno-app/src/components/RequestTabPanel/index.js index 90c6e2f416..dbb5b09070 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/index.js +++ b/packages/bruno-app/src/components/RequestTabPanel/index.js @@ -16,6 +16,8 @@ import RunnerResults from 'components/RunnerResults'; import VariablesEditor from 'components/VariablesEditor'; import CollectionSettings from 'components/CollectionSettings'; import { DocExplorer } from '@usebruno/graphql-docs'; +import Documentation from 'components/Documentation/index'; +import { IconBook, IconX } from '@tabler/icons'; import StyledWrapper from './StyledWrapper'; import SecuritySettings from 'components/SecuritySettings'; @@ -30,6 +32,7 @@ import { closeTabs } from 'providers/ReduxStore/slices/tabs'; const MIN_LEFT_PANE_WIDTH = 300; const MIN_RIGHT_PANE_WIDTH = 350; const DEFAULT_PADDING = 5; +const DOCS_PANEL_WIDTH = 350; const RequestTabPanel = () => { if (typeof window == 'undefined') { @@ -41,6 +44,8 @@ const RequestTabPanel = () => { const focusedTab = find(tabs, (t) => t.uid === activeTabUid); const { globalEnvironments, activeGlobalEnvironmentUid } = useSelector((state) => state.globalEnvironments); const _collections = useSelector((state) => state.collections.collections); + const [showDocsPanel, setShowDocsPanel] = useState(false); + const toggleDocsPanel = () => setShowDocsPanel(!showDocsPanel); // merge `globalEnvironmentVariables` into the active collection and rebuild `collections` immer proxy object let collections = produce(_collections, (draft) => { @@ -62,10 +67,13 @@ const RequestTabPanel = () => { const screenWidth = useSelector((state) => state.app.screenWidth); let asideWidth = useSelector((state) => state.app.leftSidebarWidth); + + const adjustedScreenWidth = showDocsPanel ? screenWidth - DOCS_PANEL_WIDTH : screenWidth; + const [leftPaneWidth, setLeftPaneWidth] = useState( - focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : (screenWidth - asideWidth) / 2.2 + focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : (adjustedScreenWidth - asideWidth) / 2.2 ); // 2.2 so that request pane is relatively smaller - const [rightPaneWidth, setRightPaneWidth] = useState(screenWidth - asideWidth - leftPaneWidth - DEFAULT_PADDING); + const [rightPaneWidth, setRightPaneWidth] = useState(adjustedScreenWidth - asideWidth - leftPaneWidth - DEFAULT_PADDING); const [dragging, setDragging] = useState(false); // Not a recommended pattern here to have the child component @@ -85,13 +93,13 @@ const RequestTabPanel = () => { }; useEffect(() => { - const leftPaneWidth = (screenWidth - asideWidth) / 2.2; + const leftPaneWidth = (adjustedScreenWidth - asideWidth) / 2.2; setLeftPaneWidth(leftPaneWidth); - }, [screenWidth]); + }, [adjustedScreenWidth]); useEffect(() => { - setRightPaneWidth(screenWidth - asideWidth - leftPaneWidth - DEFAULT_PADDING); - }, [screenWidth, asideWidth, leftPaneWidth]); + setRightPaneWidth(adjustedScreenWidth - asideWidth - leftPaneWidth - DEFAULT_PADDING); + }, [adjustedScreenWidth, asideWidth, leftPaneWidth]); const handleMouseMove = (e) => { if (dragging) { @@ -99,12 +107,12 @@ const RequestTabPanel = () => { let leftPaneXPosition = e.clientX + 2; if ( leftPaneXPosition < asideWidth + DEFAULT_PADDING + MIN_LEFT_PANE_WIDTH || - leftPaneXPosition > screenWidth - MIN_RIGHT_PANE_WIDTH + leftPaneXPosition > (adjustedScreenWidth - MIN_RIGHT_PANE_WIDTH) ) { return; } setLeftPaneWidth(leftPaneXPosition - asideWidth); - setRightPaneWidth(screenWidth - e.clientX - DEFAULT_PADDING); + setRightPaneWidth(adjustedScreenWidth - e.clientX - DEFAULT_PADDING); } }; const handleMouseUp = (e) => { @@ -203,7 +211,13 @@ const RequestTabPanel = () => { return (
- +
@@ -237,6 +251,12 @@ const RequestTabPanel = () => {
+ + {showDocsPanel && ( +
+ +
+ )}
{item.type === 'graphql-request' ? (