diff --git a/packages/bruno-app/src/components/Icons/Grpc/index.js b/packages/bruno-app/src/components/Icons/Grpc/index.js index cb10f810c..37b71c333 100644 --- a/packages/bruno-app/src/components/Icons/Grpc/index.js +++ b/packages/bruno-app/src/components/Icons/Grpc/index.js @@ -1,7 +1,7 @@ import React from 'react'; // UNARY - Single request, single response (Blue) -export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' }) => ( +export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '', color = '#3B82F6' }) => ( {/* Request arrow (top) - right */} - - + + {/* Response arrow (bottom) - left */} - - + + ); // CLIENT_STREAMING - Streaming request, single response (Purple) -export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => ( +export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#8B5CF6' }) => ( {/* Request arrow (top) - right with double heads */} - - - + + + {/* Response arrow (bottom) - left */} - - + + ); // SERVER_STREAMING - Single request, streaming response (Green) -export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => ( +export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#10B981' }) => ( {/* Request arrow (top) - right */} - - + + {/* Response arrow (bottom) - left with double heads */} - - - + + + ); // BIDI_STREAMING - Streaming request, streaming response (Orange) -export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => ( +export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#F97316' }) => ( {/* Request arrow (top) - right with double heads */} - - - + + + {/* Response arrow (bottom) - left with double heads */} - - - + + + ); diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js new file mode 100644 index 000000000..e952cae27 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/StyledWrapper.js @@ -0,0 +1,128 @@ +import styled from 'styled-components'; +import { rgba } from 'polished'; + +const StyledWrapper = styled.div` + .method-dropdown-container { + display: flex; + align-items: center; + height: 100%; + margin-right: 0.5rem; + } + + .method-dropdown-trigger { + display: flex; + align-items: center; + justify-content: center; + margin-left: 0.5rem; + cursor: pointer; + user-select: none; + } + + .method-dropdown-trigger-icon { + margin-right: 0.5rem; + } + + .method-dropdown-trigger-text { + font-size: ${(props) => props.theme.font.size.xs}; + white-space: nowrap; + color: ${(props) => props.theme.dropdown.color}; + } + + .method-dropdown-caret { + margin-left: 0.25rem; + color: ${(props) => props.theme.colors.text.muted}; + fill: ${(props) => props.theme.colors.text.muted}; + } + + .method-dropdown-list { + max-height: 24rem; + overflow-y: auto; + width: 24rem; + min-width: 15rem; + } + + .method-dropdown-service-group { + margin-bottom: 0.5rem; + } + + .method-dropdown-service-header { + padding: 0.25rem 0.75rem; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + position: sticky; + top: 0; + z-index: 10; + background-color: ${(props) => props.theme.dropdown.separator}; + color: ${(props) => props.theme.dropdown.color}; + } + + .method-dropdown-method-item { + padding: 0.5rem 0.75rem; + width: 100%; + border-left-width: 2px; + border-left-style: solid; + border-left-color: transparent; + transition: all 200ms; + position: relative; + cursor: pointer; + + &:hover { + background-color: ${(props) => props.theme.dropdown.hoverBg}; + } + + &--selected { + border-left-color: ${(props) => props.theme.dropdown.selectedColor}; + background-color: ${(props) => rgba(props.theme.dropdown.selectedColor, 0.2)}; + } + + &--focused { + background-color: ${(props) => props.theme.dropdown.hoverBg}; + } + } + + .method-dropdown-method-content { + display: flex; + align-items: center; + } + + .method-dropdown-method-icon { + font-size: ${(props) => props.theme.font.size.xs}; + margin-right: 0.75rem; + color: ${(props) => props.theme.dropdown.iconColor}; + } + + .method-dropdown-method-details { + display: flex; + flex-direction: column; + flex: 1; + } + + .method-dropdown-method-name { + font-weight: 500; + color: ${(props) => props.theme.dropdown.color}; + } + + .method-dropdown-method-type { + font-size: ${(props) => props.theme.font.size.xs}; + color: ${(props) => props.theme.dropdown.mutedText}; + } + + .method-dropdown-empty-state { + padding: 0.5rem 0.75rem; + width: 100%; + transition: all 200ms; + position: relative; + } + + .method-dropdown-empty-state-text { + display: flex; + align-items: center; + font-size: ${(props) => props.theme.font.size.xs}; + margin-right: 0.75rem; + color: ${(props) => props.theme.dropdown.mutedText}; + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js index 924eb1e71..3b7ba763d 100644 --- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/MethodDropdown/index.js @@ -9,6 +9,8 @@ import { import SearchInput from 'components/SearchInput/index'; import { search } from 'fast-fuzzy'; import React, { forwardRef, useEffect, useRef, useState } from 'react'; +import { useTheme } from 'providers/Theme'; +import StyledWrapper from './StyledWrapper'; const MethodDropdown = ({ grpcMethods, @@ -16,6 +18,7 @@ const MethodDropdown = ({ onMethodSelect, onMethodDropdownCreate }) => { + const { theme } = useTheme(); const [searchText, setSearchText] = useState(''); const [focusedIndex, setFocusedIndex] = useState(-1); const searchInputRef = useRef(); @@ -58,32 +61,26 @@ const MethodDropdown = ({ const getIconForMethodType = (type) => { switch (type) { case 'unary': - return ; + return ; case 'client-streaming': - return ; + return ; case 'server-streaming': - return ; + return ; case 'bidi-streaming': - return ; + return ; default: - return ; + return ; } }; const MethodsDropdownIcon = forwardRef((props, ref) => { return ( -
- {selectedGrpcMethod &&
{getIconForMethodType(selectedGrpcMethod.type)}
} - - {selectedGrpcMethod ? ( - - {selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path} - - ) : ( - Select Method - )} +
+ {selectedGrpcMethod &&
{getIconForMethodType(selectedGrpcMethod.type)}
} + + {selectedGrpcMethod ? (selectedGrpcMethod.path.split('.').at(-1) || selectedGrpcMethod.path) : 'Select Method'} - +
); }); @@ -145,76 +142,75 @@ const MethodDropdown = ({ } return ( -
- } placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}> - -
- {Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => ( -
-
- {serviceName || 'Default Service'} -
-
- {methods.map((method, methodIndex) => { - const globalMethodIndex - = Object.values(groupedMethods) - .slice(0, serviceIndex) - .reduce((acc, group) => acc + group.length, 0) + methodIndex; - return ( -
handleGrpcMethodSelect(method)} - data-index={globalMethodIndex} - data-testid="grpc-method-item" - > -
-
- {getIconForMethodType(method.type)} -
-
-
- {method.methodName} + +
+ } placement="bottom-end" style={{ maxWidth: 'unset' }} onShow={handleDropdownShow}> + +
+ {Object.entries(groupedMethods).map(([serviceName, methods], serviceIndex) => ( +
+
+ {serviceName || 'Default Service'} +
+
+ {methods.map((method, methodIndex) => { + const globalMethodIndex + = Object.values(groupedMethods) + .slice(0, serviceIndex) + .reduce((acc, group) => acc + group.length, 0) + methodIndex; + const isSelected = selectedGrpcMethod && selectedGrpcMethod.path === method.path; + const isFocused = focusedIndex === globalMethodIndex; + return ( +
handleGrpcMethodSelect(method)} + data-index={globalMethodIndex} + data-testid="grpc-method-item" + > +
+
+ {getIconForMethodType(method.type)}
-
- {method.type} +
+
+ {method.methodName} +
+
+ {method.type} +
-
- ); - })} + ); + })} +
-
- ))} + ))} - {filteredMethods.length === 0 && ( -
-
-
+ {filteredMethods.length === 0 && ( +
+
No methods found for the search term
-
- )} -
- -
+ )} +
+ +
+ ); }; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js new file mode 100644 index 000000000..082d4a67a --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/StyledWrapper.js @@ -0,0 +1,68 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .proto-file-dropdown-container { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + user-select: none; + } + + .proto-file-dropdown-icon { + margin-right: 0.25rem; + color: ${(props) => props.theme.colors.text.muted}; + } + + .proto-file-dropdown-text { + font-size: ${(props) => props.theme.font.size.xs}; + white-space: nowrap; + color: ${(props) => props.theme.dropdown.color}; + } + + .proto-file-dropdown-caret { + margin-left: 0.25rem; + color: ${(props) => props.theme.colors.text.muted}; + fill: ${(props) => props.theme.colors.text.muted}; + } + + .proto-file-dropdown-content { + max-height: fit-content; + overflow-y: auto; + width: 30rem; + } + + .proto-file-dropdown-mode-section { + padding: 0.5rem 0.75rem; + border-bottom: 1px solid ${(props) => props.theme.border.border1}; + } + + .proto-file-dropdown-mode-controls { + display: flex; + align-items: center; + justify-content: space-between; + } + + .proto-file-dropdown-mode-options { + display: flex; + align-items: center; + gap: 0.5rem; + } + + .proto-file-dropdown-mode-option { + font-size: ${(props) => props.theme.font.size.xs}; + color: ${(props) => props.theme.colors.text.muted}; + + &--active { + font-weight: 500; + } + } + + .proto-file-dropdown-reflection-message { + padding: 0.5rem 0.75rem; + color: ${(props) => props.theme.overlay.overlay1}; + margin-bottom: 0.5rem; + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js index 5e1983e54..d507d238e 100644 --- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/ProtoFileDropdown/index.js @@ -10,6 +10,7 @@ import Dropdown from 'components/Dropdown/index'; import ToggleSwitch from 'components/ToggleSwitch/index'; import { TabNavigation, ProtoFilesTab, ImportPathsTab } from '../Tabs'; import useProtoFileManagement from 'hooks/useProtoFileManagement/index'; +import StyledWrapper from './StyledWrapper'; const ProtoFileDropdown = ({ collection, @@ -121,96 +122,95 @@ const ProtoFileDropdown = ({ const ProtoFileDropdownIcon = forwardRef((props, ref) => { return ( -
setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon"> - {isReflectionMode ? (<> - ) : ( - +
setShowProtoDropdown((prev) => !prev)} data-testid="grpc-proto-file-dropdown-icon"> + {!isReflectionMode && ( + )} - + {isReflectionMode ? 'Using Reflection' : (protoFilePath ? getBasename(collection.pathname, protoFilePath) : 'Select Proto File')} - +
); }); return ( -
- } - placement="bottom-end" - visible={showProtoDropdown} - onClickOutside={() => setShowProtoDropdown(false)} - data-testid="grpc-proto-file-dropdown" - > -
-
-
- Mode -
- - Proto File - - - - Reflection - + +
+ } + placement="bottom-end" + visible={showProtoDropdown} + onClickOutside={() => setShowProtoDropdown(false)} + data-testid="grpc-proto-file-dropdown" + > +
+
+
+ Mode +
+ + Proto File + + + + Reflection + +
-
- {!isReflectionMode && ( - - )} + {!isReflectionMode && ( + + )} - {!isReflectionMode && ( - <> - {activeTab === 'protofiles' && ( - - )} + {!isReflectionMode && ( + <> + {activeTab === 'protofiles' && ( + + )} - {activeTab === 'importpaths' && ( - - )} - - )} + {activeTab === 'importpaths' && ( + + )} + + )} - {isReflectionMode && ( -
-
+ {isReflectionMode && ( +
Using server reflection to discover gRPC methods.
-
- )} -
-
-
+ )} +
+ +
+ ); }; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js index f2a21a891..31926e65d 100644 --- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/ImportPathsTab/StyledWrapper.js @@ -99,6 +99,7 @@ const Wrapper = styled.div` margin-right: 0.5rem; cursor: pointer; color: ${(props) => props.theme.grpc.importPaths.item.checkbox.color}; + accent-color: ${(props) => props.theme.colors.accent}; } .item-text { diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js index 9d5314f1b..7aab9761b 100644 --- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/Tabs/TabNavigation/StyledWrapper.js @@ -2,7 +2,7 @@ import styled from 'styled-components'; const Wrapper = styled.div` .tab-container { - background-color: ${(props) => props.theme.grpc.tabNav.container.bg}; + background-color: ${(props) => props.theme.dropdown.separator}; } .tab-button { background-color: ${(props) => props.theme.grpc.tabNav.button.inactive.bg}; diff --git a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js index 30b37842d..3f521d1bc 100644 --- a/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/GrpcQueryUrl/index.js @@ -297,7 +297,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
- gRPC + gRPC
diff --git a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js index 0eeab0d9a..fb4ce8dc4 100644 --- a/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js +++ b/packages/bruno-app/src/components/RequestPane/QueryUrl/index.js @@ -32,7 +32,6 @@ const QueryUrl = ({ item, collection, handleRun }) => { const isMac = isMacOS(); const saveShortcut = isMac ? 'Cmd + S' : 'Ctrl + S'; const editorRef = useRef(null); - const isGrpc = item.type === 'grpc-request'; const isLoading = ['queued', 'sending'].includes(item.requestState); const [generateCodeItemModalOpen, setGenerateCodeItemModalOpen] = useState(false); @@ -372,13 +371,7 @@ const QueryUrl = ({ item, collection, handleRun }) => { return (
- {isGrpc ? ( -
- gRPC -
- ) : ( - - )} +