mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
Feat/v3 UI updates (#6618)
* style: enhance button layout and input styles across multiple components for improved UI consistency * style: update RequestsNotLoaded component with new warning styles and enhance theme color definitions for status indicators * refactor: update theme usage across components for consistency - Changed color references from theme.brand to theme.primary.text in various StyledWrapper components. - Added hover effects to enhance UI interactivity in CollectionSettings and FolderSettings. - Removed unnecessary margin and padding adjustments in several components for cleaner layout. - Improved accessibility by ensuring aria attributes are correctly set in MenuDropdown. - Standardized styling for method indicators in RequestPane components. These changes aim to create a more cohesive look and feel across the application while adhering to the updated theme guidelines. * refactor: clean up method selector styling in NewRequest component * chore: temp playwright test fixes * refactor: update modal sizes across various components for consistency - Changed modal size from "sm" to "md" in RenameWorkspace, CreateApiSpec, CloneCollection, DeleteCollectionItem, and RenameCollection components. - Improved styling in HttpMethodSelector by adding padding for better layout. - Updated theme color references in multiple theme files to use a new palette structure for consistency and maintainability. * refactor: enhance styling and theme integration in TimelineItem components - Updated HttpMethodSelector to clarify padding calculation in comments. - Integrated theme colors for OAuth2 indicator and timestamp in TimelineItem for better visual consistency. - Adjusted Method component to use uppercase styling for method display. - Modified RelativeTime component to apply muted text color for improved readability. - Updated INFO color in dark and light themes for better contrast and accessibility. * refactor: remove duplicate import statements in theme files - Cleaned up import statements in vscode.js and light-pastel.js by removing redundant lines for improved code clarity and maintainability. * refactor: improve styling and theme integration in various components - Added accent color and cursor style for checkbox inputs in Modal's StyledWrapper. - Updated border-radius values in HttpMethodSelector and NewRequest StyledWrapper components to use theme variables for consistency. - Introduced a new textbox class in NewRequest StyledWrapper for better styling control. - Changed modal size from "sm" to "md" in CreateEnvironment for improved layout. --------- Co-authored-by: Bijin A B <bijin@usebruno.com>
This commit is contained in:
@@ -7,7 +7,7 @@ const Wrapper = styled.div`
|
||||
background: transparent;
|
||||
|
||||
.auth-mode-label {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
|
||||
.caret {
|
||||
color: rgb(140, 140, 140);
|
||||
|
||||
@@ -38,7 +38,7 @@ const Wrapper = styled.div`
|
||||
|
||||
.grant-type-label {
|
||||
width: fit-content;
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
justify-content: space-between;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ const Auth = ({ item, collection }) => {
|
||||
const source = getEffectiveAuthSource();
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full mt-2 gap-2">
|
||||
<div className="flex flex-row w-full gap-2">
|
||||
<div>Auth inherited from {source.name}: </div>
|
||||
<div className="inherit-mode-text">{humanizeRequestAuthMode(source.auth?.mode)}</div>
|
||||
</div>
|
||||
@@ -114,7 +114,7 @@ const Auth = ({ item, collection }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full mt-1 overflow-auto">
|
||||
<StyledWrapper className="w-full overflow-auto">
|
||||
{getAuthView()}
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -21,8 +21,6 @@ import HeightBoundContainer from 'ui/HeightBoundContainer';
|
||||
import Settings from 'components/RequestPane/Settings';
|
||||
import ResponsiveTabs from 'ui/ResponsiveTabs';
|
||||
|
||||
const MULTIPLE_CONTENT_TABS = new Set(['script', 'vars', 'auth', 'docs']);
|
||||
|
||||
const TAB_CONFIG = [
|
||||
{ key: 'query', label: 'Query' },
|
||||
{ key: 'variables', label: 'Variables' },
|
||||
@@ -136,8 +134,6 @@ const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handle
|
||||
return <div className="pb-4 px-4">An error occurred!</div>;
|
||||
}
|
||||
|
||||
const isMultipleContentTab = MULTIPLE_CONTENT_TABS.has(requestPaneTab);
|
||||
|
||||
const rightContent = (
|
||||
<div ref={schemaActionsRef}>
|
||||
<GraphQLSchemaActions item={item} collection={collection} onSchemaLoad={setSchema} toggleDocs={toggleDocs} />
|
||||
@@ -154,7 +150,7 @@ const GraphQLRequestPane = ({ item, collection, onSchemaLoad, toggleDocs, handle
|
||||
rightContentRef={schemaActionsRef}
|
||||
/>
|
||||
|
||||
<section className={classnames('flex w-full flex-1', { 'mt-5': !isMultipleContentTab })}>
|
||||
<section className={classnames('flex w-full flex-1 mt-4')}>
|
||||
<HeightBoundContainer>{tabPanel}</HeightBoundContainer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -50,6 +50,7 @@ const Wrapper = styled.div`
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
padding: 4px 0px;
|
||||
padding-top: 0px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
||||
@@ -296,8 +296,8 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
return (
|
||||
<StyledWrapper className="flex items-center relative" data-testid="grpc-query-url-container">
|
||||
<div className="flex items-center h-full method-selector-container">
|
||||
<div className="flex items-center justify-center h-full w-16" data-testid="grpc-method-indicator">
|
||||
<span className="text-xs font-bold" style={{ color: theme.request.grpc }}>gRPC</span>
|
||||
<div className="flex items-center justify-center h-full px-[10px]" data-testid="grpc-method-indicator">
|
||||
<span className="text-xs font-medium" style={{ color: theme.request.grpc }}>gRPC</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center w-full input-container h-full relative">
|
||||
|
||||
@@ -121,7 +121,7 @@ const GrpcAuth = ({ item, collection }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full mt-1 overflow-y-scroll">
|
||||
<StyledWrapper className="w-full overflow-y-scroll">
|
||||
<div className="flex flex-grow justify-start items-center">
|
||||
<GrpcAuthMode item={item} collection={collection} />
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,6 @@ const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const isMultipleContentTab = ['script', 'vars', 'auth', 'docs'].includes(focusedTab.requestPaneTab);
|
||||
const body = getPropertyFromDraftOrRequest(item, 'request.body');
|
||||
const headers = getPropertyFromDraftOrRequest(item, 'request.headers');
|
||||
const docs = getPropertyFromDraftOrRequest(item, 'request.docs');
|
||||
@@ -109,9 +108,7 @@ const GrpcRequestPane = ({ item, collection, handleRun }) => {
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
className={classnames('flex w-full flex-1 h-full', {
|
||||
'mt-2': !isMultipleContentTab
|
||||
})}
|
||||
className={classnames('flex w-full flex-1 h-full mt-4')}
|
||||
>
|
||||
<HeightBoundContainer>
|
||||
{getTabPanel(focusedTab.requestPaneTab)}
|
||||
|
||||
@@ -19,8 +19,6 @@ import ResponsiveTabs from 'ui/ResponsiveTabs';
|
||||
import HeightBoundContainer from 'ui/HeightBoundContainer';
|
||||
import AuthMode from '../Auth/AuthMode/index';
|
||||
|
||||
const MULTIPLE_CONTENT_TABS = new Set(['params', 'script', 'vars', 'auth', 'docs']);
|
||||
|
||||
const TAB_CONFIG = [
|
||||
{ key: 'params', label: 'Params' },
|
||||
{ key: 'body', label: 'Body' },
|
||||
@@ -120,8 +118,6 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
return <div className="pb-4 px-4">An error occurred!</div>;
|
||||
}
|
||||
|
||||
const isMultipleContentTab = MULTIPLE_CONTENT_TABS.has(requestPaneTab);
|
||||
|
||||
const rightContent = requestPaneTab === 'body' ? (
|
||||
<div ref={rightContentRef}>
|
||||
<RequestBodyMode item={item} collection={collection} />
|
||||
@@ -143,7 +139,7 @@ const HttpRequestPane = ({ item, collection }) => {
|
||||
delayedTabs={['body']}
|
||||
/>
|
||||
|
||||
<section className={classnames('flex w-full flex-1', { 'mt-3': !isMultipleContentTab })}>
|
||||
<section className={classnames('flex w-full flex-1 mt-4')}>
|
||||
<HeightBoundContainer>{tabPanel}</HeightBoundContainer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -135,7 +135,7 @@ const QueryParams = ({ item, collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full flex flex-col">
|
||||
<div className="flex-1 mt-2">
|
||||
<div className="flex-1">
|
||||
<div className="mb-3 title text-xs">Query</div>
|
||||
<EditableTable
|
||||
columns={queryColumns}
|
||||
|
||||
@@ -2,13 +2,29 @@ import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.method-selector {
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 2px;
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
|
||||
&:not(.custom-input-mode):hover,
|
||||
&:has(button[aria-expanded="true"]) {
|
||||
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
||||
}
|
||||
|
||||
|
||||
.tippy-box {
|
||||
max-width: 150px !important;
|
||||
@@ -37,20 +53,19 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.method-span {
|
||||
width: fit-content;
|
||||
max-width: 10ch;
|
||||
display: block;
|
||||
max-width: 15ch;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.caret {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
fill: ${(props) => props.theme.colors.text.muted};
|
||||
color: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -10,27 +10,18 @@ const KEY = Object.freeze({ ENTER: 'Enter', ESCAPE: 'Escape' });
|
||||
|
||||
const DEFAULT_METHOD = 'GET';
|
||||
|
||||
const TriggerButton = ({ method, showCaret, ...props }) => {
|
||||
const { theme } = useTheme();
|
||||
const methodColor = useMemo(() => {
|
||||
const colorMap = {
|
||||
...theme.request.methods,
|
||||
...theme.request
|
||||
};
|
||||
return colorMap[method.toLocaleLowerCase()];
|
||||
}, [method, theme]);
|
||||
|
||||
const TriggerButton = ({ method, methodSpanRef, showCaret, ...props }) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer flex items-center text-left w-full select-none"
|
||||
className="cursor-pointer flex items-center gap-2 text-left w-full select-none px-2"
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
className="px-3 truncate method-span"
|
||||
ref={methodSpanRef}
|
||||
className="truncate method-span"
|
||||
id="create-new-request-method"
|
||||
title={method}
|
||||
style={{ color: methodColor }}
|
||||
>
|
||||
{method}
|
||||
</span>
|
||||
@@ -43,6 +34,17 @@ const HttpMethodSelector = ({ method = DEFAULT_METHOD, onMethodSelect, showCaret
|
||||
const [isCustomMode, setIsCustomMode] = useState(false);
|
||||
const inputRef = useRef();
|
||||
const selectedMethodRef = useRef(method);
|
||||
const methodSpanRef = useRef();
|
||||
const [previousMethodWidth, setPreviousMethodWidth] = useState(null);
|
||||
|
||||
const { theme } = useTheme();
|
||||
const methodColor = useMemo(() => {
|
||||
const colorMap = {
|
||||
...theme.request.methods,
|
||||
...theme.request
|
||||
};
|
||||
return colorMap[method.toLocaleLowerCase()];
|
||||
}, [method, theme]);
|
||||
|
||||
const blurInput = () => inputRef.current?.blur();
|
||||
|
||||
@@ -72,6 +74,10 @@ const HttpMethodSelector = ({ method = DEFAULT_METHOD, onMethodSelect, showCaret
|
||||
};
|
||||
|
||||
const handleAddCustomMethod = useCallback(() => {
|
||||
// Capture the width of the current method span before switching to custom mode
|
||||
if (methodSpanRef.current) {
|
||||
setPreviousMethodWidth(methodSpanRef.current.offsetWidth);
|
||||
}
|
||||
setIsCustomMode(true);
|
||||
onMethodSelect('');
|
||||
|
||||
@@ -131,14 +137,28 @@ const HttpMethodSelector = ({ method = DEFAULT_METHOD, onMethodSelect, showCaret
|
||||
|
||||
// If in custom mode, render input field instead of dropdown
|
||||
if (isCustomMode) {
|
||||
// Calculate width based on content length (in ch units), clamped to max 16ch
|
||||
// Add 1ch for cursor space
|
||||
const contentWidth = Math.min(method.length + 1, 16);
|
||||
// Use previous method width as minimum, content-based width as current
|
||||
const minWidthPx = previousMethodWidth ? `${previousMethodWidth}px` : '5ch';
|
||||
// Use calc to add padding space (px-2 = 0.5rem per side = 1rem total) to the ch width
|
||||
const currentWidth = `calc(${Math.max(contentWidth, 1)}ch + 1rem)`;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="flex method-selector">
|
||||
<div className="flex method-selector custom-input-mode" style={{ color: methodColor }}>
|
||||
<div className="flex flex-col w-full">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
className="px-3 w-fit max-w-[10ch] focus:bg-transparent"
|
||||
className="px-2 focus:bg-transparent"
|
||||
style={{
|
||||
minWidth: minWidthPx,
|
||||
width: currentWidth,
|
||||
maxWidth: 'calc(16ch + 1rem)',
|
||||
fontSize: '12px'
|
||||
}}
|
||||
value={method}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleBlur}
|
||||
@@ -154,13 +174,13 @@ const HttpMethodSelector = ({ method = DEFAULT_METHOD, onMethodSelect, showCaret
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="flex method-selector">
|
||||
<div className="flex method-selector" style={{ color: methodColor }}>
|
||||
<MenuDropdown
|
||||
items={menuItems}
|
||||
placement="bottom-start"
|
||||
selectedItemId={selectedItemId}
|
||||
>
|
||||
<TriggerButton method={method} showCaret={showCaret} />
|
||||
<TriggerButton method={method} showCaret={showCaret} methodSpanRef={methodSpanRef} />
|
||||
</MenuDropdown>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -9,7 +9,7 @@ const Wrapper = styled.div`
|
||||
border-radius: 3px;
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ const Script = ({ item, collection }) => {
|
||||
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col pt-4">
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="pre-request">Pre Request</TabsTrigger>
|
||||
|
||||
@@ -9,7 +9,7 @@ const Vars = ({ item, collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full flex flex-col">
|
||||
<div className="mt-2">
|
||||
<div>
|
||||
<div className="mb-3 title text-xs">Pre Request</div>
|
||||
<VarsTable item={item} collection={collection} vars={requestVars} varType="request" />
|
||||
</div>
|
||||
|
||||
@@ -118,7 +118,7 @@ const WSAuth = ({ item, collection }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full mt-1 overflow-y-scroll">
|
||||
<StyledWrapper className="w-full overflow-y-scroll">
|
||||
<div className="flex flex-grow justify-start items-center">
|
||||
<WSAuthMode item={item} collection={collection} />
|
||||
</div>
|
||||
|
||||
@@ -71,7 +71,6 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const isMultipleContentTab = ['script', 'vars', 'auth', 'docs'].includes(focusedTab.requestPaneTab);
|
||||
const headers = getPropertyFromDraftOrRequest(item, 'request.headers');
|
||||
const docs = getPropertyFromDraftOrRequest(item, 'request.docs');
|
||||
const auth = getPropertyFromDraftOrRequest(item, 'request.auth');
|
||||
@@ -107,9 +106,7 @@ const WSRequestPane = ({ item, collection, handleRun }) => {
|
||||
</div>
|
||||
</div>
|
||||
<section
|
||||
className={classnames('flex w-full flex-1 h-full', {
|
||||
'mt-2': !isMultipleContentTab
|
||||
})}
|
||||
className={classnames('flex w-full flex-1 h-full mt-4')}
|
||||
>
|
||||
<HeightBoundContainer>{getTabPanel(focusedTab.requestPaneTab)}</HeightBoundContainer>
|
||||
</section>
|
||||
|
||||
@@ -53,7 +53,7 @@ const WSSettingsPane = ({ item, collection }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="flex flex-col mt-4 gap-4 w-full">
|
||||
<StyledWrapper className="flex flex-col gap-4 w-full">
|
||||
<section className="grid gap-4 items-center grid-cols-2">
|
||||
<div>
|
||||
<label className="font-medium mb-2">Timeout</label>
|
||||
|
||||
@@ -17,7 +17,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ const StyledWrapper = styled.div`
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
padding: 4px 0px;
|
||||
padding-top: 0px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
||||
@@ -124,8 +124,8 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<StyledWrapper>
|
||||
<div className="flex items-center h-full">
|
||||
<div className="flex items-center input-container flex-1 w-full input-container pr-2 h-full relative">
|
||||
<div className="flex items-center justify-center w-16">
|
||||
<span className="text-xs font-bold method-ws">WS</span>
|
||||
<div className="flex items-center justify-center px-[10px]">
|
||||
<span className="text-xs font-medium method-ws">WS</span>
|
||||
</div>
|
||||
<SingleLineEditor
|
||||
value={url}
|
||||
|
||||
Reference in New Issue
Block a user