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:
@@ -19,7 +19,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
&.card {
|
||||
background-color: ${(props) => props.theme.requestTabPanel.card.bg};
|
||||
|
||||
.title {
|
||||
border-top: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-left: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-right: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-top: 1px solid ${(props) => props.theme.table.border};
|
||||
border-left: 1px solid ${(props) => props.theme.table.border};
|
||||
border-right: 1px solid ${(props) => props.theme.table.border};
|
||||
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
|
||||
background-color: ${(props) => props.theme.status.warning.background};
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
color: ${(props) => props.theme.status.warning.text};
|
||||
}
|
||||
|
||||
.table {
|
||||
|
||||
@@ -41,8 +41,8 @@ const RequestsNotLoaded = ({ collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full card my-2">
|
||||
<div className="flex items-center gap-2 px-3 py-2 title bg-yellow-50 dark:bg-yellow-900/20">
|
||||
<IconAlertTriangle size={16} className="text-yellow-500" />
|
||||
<div className="flex items-center gap-2 px-3 py-2 title">
|
||||
<IconAlertTriangle size={16} className="warning-icon" />
|
||||
<span className="font-medium">Following requests were not loaded</span>
|
||||
</div>
|
||||
<table className="w-full border-collapse">
|
||||
|
||||
@@ -36,7 +36,7 @@ const PresetsSettings = ({ collection }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper className="h-full w-full">
|
||||
<div className="text-xs mb-4 mt-4 text-muted">
|
||||
<div className="text-xs mb-4 text-muted">
|
||||
These presets will be used as the default values for new requests in this collection.
|
||||
</div>
|
||||
<div className="bruno-form">
|
||||
|
||||
@@ -56,7 +56,7 @@ const Script = ({ collection }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full flex flex-col h-full pt-4">
|
||||
<StyledWrapper className="w-full flex flex-col h-full">
|
||||
<div className="text-xs mb-4 text-muted">
|
||||
Write pre and post-request scripts that will run before and after any request in this collection is sent.
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,10 @@ const StyledWrapper = styled.div`
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
|
||||
@@ -14,7 +14,7 @@ const Vars = ({ 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">Pre Request</div>
|
||||
<VarsTable collection={collection} vars={requestVars} varType="request" />
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ const StyledWrapper = styled.div`
|
||||
/* Checkbox */
|
||||
input[type='checkbox'] {
|
||||
cursor: pointer;
|
||||
accent-color: ${(props) => props.theme.colors.accent};
|
||||
accent-color: ${(props) => props.theme.primary.solid};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ const ConfirmCloseEnvironment = ({ onCancel, onCloseWithoutSave, onSaveAndClose,
|
||||
Don't Save
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="mr-2" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onSaveAndClose}>
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = 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 Documentation = ({ collection, folder }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-1 w-full relative flex flex-col">
|
||||
<StyledWrapper className="w-full relative flex flex-col">
|
||||
<div className="editing-mode flex justify-between items-center flex-shrink-0" role="tab" onClick={toggleViewMode}>
|
||||
{isEditing ? 'Preview' : 'Edit'}
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ const Script = ({ collection, folder }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full flex flex-col h-full pt-4">
|
||||
<StyledWrapper className="w-full flex flex-col h-full">
|
||||
<div className="text-xs mb-4 text-muted">
|
||||
Pre and post-request scripts that will run before and after any request inside this folder is sent.
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,10 @@ const StyledWrapper = styled.div`
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
|
||||
@@ -14,7 +14,7 @@ const Vars = ({ collection, folder }) => {
|
||||
|
||||
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">Pre Request</div>
|
||||
<VarsTable folder={folder} collection={collection} vars={requestVars} varType="request" />
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ const RenameWorkspace = ({ onClose, workspace }) => {
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
size="sm"
|
||||
size="md"
|
||||
title="Rename Workspace"
|
||||
confirmText="Rename"
|
||||
handleConfirm={onSubmit}
|
||||
|
||||
@@ -127,6 +127,15 @@ const Wrapper = styled.div`
|
||||
}
|
||||
}
|
||||
|
||||
select.textbox {
|
||||
appearance: none;
|
||||
padding-right: 30px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.5rem center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bruno-form {
|
||||
color: ${(props) => props.theme.modal.body.color};
|
||||
}
|
||||
@@ -167,6 +176,16 @@ const Wrapper = styled.div`
|
||||
border-bottom-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
}
|
||||
}
|
||||
|
||||
input[type='radio'] {
|
||||
cursor: pointer;
|
||||
accent-color: ${(props) => props.theme.primary.solid};
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
cursor: pointer;
|
||||
accent-color: ${(props) => props.theme.primary.solid};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -367,6 +367,7 @@ const General = ({ close }) => {
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
readOnly={true}
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.defaultCollectionLocation || ''}
|
||||
onClick={browseDefaultLocation}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -34,8 +34,8 @@ const ConfirmCollectionClose = ({ collection, onCancel, onCloseWithoutSave, onSa
|
||||
Don't Save
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="mr-2" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onSaveAndClose}>
|
||||
|
||||
@@ -34,8 +34,8 @@ const ConfirmFolderClose = ({ folder, onCancel, onCloseWithoutSave, onSaveAndClo
|
||||
Don't Save
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="mr-2" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onSaveAndClose}>
|
||||
|
||||
@@ -38,8 +38,8 @@ const ConfirmRequestClose = ({ item, example, onCancel, onCloseWithoutSave, onSa
|
||||
Don't Save
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button className="mr-2" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onSaveAndClose}>Save</Button>
|
||||
|
||||
@@ -8,7 +8,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'collection-settings': {
|
||||
return (
|
||||
<>
|
||||
<IconSettings size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconSettings size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Collection</span>
|
||||
</>
|
||||
);
|
||||
@@ -16,7 +16,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'collection-overview': {
|
||||
return (
|
||||
<>
|
||||
<IconSettings size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconSettings size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Overview</span>
|
||||
</>
|
||||
);
|
||||
@@ -24,7 +24,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'folder-settings': {
|
||||
return (
|
||||
<>
|
||||
<IconFolder size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconFolder size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">{tabName || 'Folder'}</span>
|
||||
</>
|
||||
);
|
||||
@@ -32,7 +32,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'variables': {
|
||||
return (
|
||||
<>
|
||||
<IconVariable size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconVariable size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Variables</span>
|
||||
</>
|
||||
);
|
||||
@@ -40,7 +40,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'collection-runner': {
|
||||
return (
|
||||
<>
|
||||
<IconRun size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconRun size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Runner</span>
|
||||
</>
|
||||
);
|
||||
@@ -48,7 +48,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'environment-settings': {
|
||||
return (
|
||||
<>
|
||||
<IconDatabase size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconDatabase size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Environments</span>
|
||||
</>
|
||||
);
|
||||
@@ -56,7 +56,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
case 'global-environment-settings': {
|
||||
return (
|
||||
<>
|
||||
<IconWorld size={14} strokeWidth={1.5} className="text-yellow-600 flex-shrink-0" />
|
||||
<IconWorld size={14} strokeWidth={1.5} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">Global Environments</span>
|
||||
</>
|
||||
);
|
||||
@@ -67,7 +67,7 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="flex items-baseline tab-label"
|
||||
className="flex items-center tab-label"
|
||||
onDoubleClick={handleDoubleClick}
|
||||
>
|
||||
{getTabInfo(type, tabName)}
|
||||
|
||||
@@ -17,8 +17,6 @@ const Wrapper = styled.div`
|
||||
.tabs-scroll-container {
|
||||
overflow-x: auto;
|
||||
overflow-y: clip;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
@@ -197,6 +195,10 @@ const Wrapper = styled.div`
|
||||
&.has-chevrons ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.special-tab-icon {
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -12,6 +12,7 @@ import StyledWrapper from './StyledWrapper';
|
||||
import DraggableTab from './DraggableTab';
|
||||
import CreateUntitledRequest from 'components/CreateUntitledRequest';
|
||||
import { IconPlus } from '@tabler/icons';
|
||||
import ActionIcon from 'ui/ActionIcon/index';
|
||||
|
||||
const RequestTabs = () => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -116,22 +117,19 @@ const RequestTabs = () => {
|
||||
{collectionRequestTabs && collectionRequestTabs.length ? (
|
||||
<>
|
||||
<CollectionToolBar collection={activeCollection} />
|
||||
<div className="flex items-center pl-2" ref={collectionTabsRef}>
|
||||
<ul role="tablist">
|
||||
{showChevrons ? (
|
||||
<li className="select-none short-tab" onClick={leftSlide}>
|
||||
<div className="flex items-center">
|
||||
<IconChevronLeft size={18} strokeWidth={1.5} />
|
||||
</div>
|
||||
</li>
|
||||
) : null}
|
||||
{/* Moved to post mvp */}
|
||||
{/* <li className="select-none new-tab mr-1" onClick={createNewTab}>
|
||||
<div className="flex items-center home-icon-container">
|
||||
<IconHome2 size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
</li> */}
|
||||
</ul>
|
||||
<div className="flex items-center gap-2 pl-2" ref={collectionTabsRef}>
|
||||
|
||||
{showChevrons ? (
|
||||
<ActionIcon size="lg" onClick={leftSlide} aria-label="Left Chevron" style={{ marginBottom: '3px' }}>
|
||||
<IconChevronLeft size={18} strokeWidth={1.5} />
|
||||
</ActionIcon>
|
||||
) : null}
|
||||
{/* Moved to post mvp */}
|
||||
{/* <li className="select-none new-tab mr-1" onClick={createNewTab}>
|
||||
<div className="flex items-center home-icon-container">
|
||||
<IconHome2 size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
</li> */}
|
||||
<div className="tabs-scroll-container" style={{ maxWidth: maxTablistWidth }} ref={scrollContainerRef}>
|
||||
<ul role="tablist" ref={tabsRef}>
|
||||
{collectionRequestTabs && collectionRequestTabs.length
|
||||
@@ -169,30 +167,27 @@ const RequestTabs = () => {
|
||||
</div>
|
||||
|
||||
{activeCollection && (
|
||||
<div className="flex items-center cursor-pointer short-tab px-2" onClick={() => setNewRequestModalOpen(true)}>
|
||||
<ActionIcon onClick={() => setNewRequestModalOpen(true)} aria-label="New Request" size="lg" style={{ marginBottom: '3px' }}>
|
||||
<IconPlus
|
||||
size={18}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</div>
|
||||
</ActionIcon>
|
||||
)}
|
||||
<ul role="tablist">
|
||||
{showChevrons ? (
|
||||
<li className="select-none short-tab" onClick={rightSlide}>
|
||||
<div className="flex items-center">
|
||||
<IconChevronRight size={18} strokeWidth={1.5} />
|
||||
</div>
|
||||
</li>
|
||||
) : null}
|
||||
{/* Moved to post mvp */}
|
||||
{/* <li className="select-none new-tab choose-request">
|
||||
|
||||
{showChevrons ? (
|
||||
<ActionIcon size="lg" onClick={rightSlide} aria-label="Right Chevron" style={{ marginBottom: '3px' }}>
|
||||
<IconChevronRight size={18} strokeWidth={1.5} />
|
||||
</ActionIcon>
|
||||
) : null}
|
||||
{/* Moved to post mvp */}
|
||||
{/* <li className="select-none new-tab choose-request">
|
||||
<div className="flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</li> */}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -26,12 +26,12 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
|
||||
&.cursor-default {
|
||||
opacity: 0.6;
|
||||
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
@@ -53,11 +53,11 @@ const StyledWrapper = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
@@ -18,7 +18,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.brand};
|
||||
color: ${(props) => props.theme.primary.text};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ const StyledWrapper = styled.div`
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
|
||||
@@ -6,7 +6,6 @@ const StyledWrapper = styled.div`
|
||||
|
||||
.empty-state {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.responses-container {
|
||||
|
||||
@@ -141,7 +141,7 @@ const GrpcResponsePane = ({ item, collection }) => {
|
||||
) : null}
|
||||
</div>
|
||||
<section
|
||||
className={`flex flex-col flex-grow pl-3 pr-4 h-0 ${focusedTab.responsePaneTab === 'response' ? '' : 'mt-4'}`}
|
||||
className="flex flex-col flex-grow pl-3 pr-4 h-0 mt-4"
|
||||
>
|
||||
{isLoading ? <Overlay item={item} collection={collection} /> : null}
|
||||
{!item?.response ? (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
import { cancelRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StopWatch from '../../StopWatch';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import Button from 'ui/Button/index';
|
||||
|
||||
const ResponseLoadingOverlay = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -23,13 +24,12 @@ const ResponseLoadingOverlay = ({ item, collection }) => {
|
||||
</div>
|
||||
</div>
|
||||
<IconRefresh size={24} className="loading-icon" />
|
||||
<button
|
||||
<Button
|
||||
onClick={handleCancelRequest}
|
||||
className="mt-4 uppercase btn-sm rounded btn-secondary ease-linear transition-all duration-150 relative z-50"
|
||||
type="button"
|
||||
className="mt-4"
|
||||
>
|
||||
Cancel Request
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -20,7 +20,7 @@ const StyledMenuIcon = styled.button`
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.workspace.button.bg};
|
||||
border-color: ${(props) => props.theme.app.collection.toolbar.environmentSelector.hoverBorder} !important;
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -10,8 +10,11 @@ const StyledWrapper = styled.div`
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
border-color: ${(props) => props.theme.border.border1};
|
||||
}
|
||||
|
||||
.timeline-event {
|
||||
padding: 8px 0 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -65,7 +68,7 @@ const StyledWrapper = styled.div`
|
||||
.tabs-switcher {
|
||||
border-bottom: 1px solid ${(props) => props.theme.border.border1};
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
button {
|
||||
position: relative;
|
||||
padding: 8px 16px;
|
||||
@@ -126,7 +129,7 @@ const StyledWrapper = styled.div`
|
||||
color: ${(props) => props.theme.colors.text.purple};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.request-label {
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
padding: 2px 6px;
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
|
||||
const Method = ({ method }) => {
|
||||
const { theme } = useTheme();
|
||||
const methodUpper = method?.toUpperCase();
|
||||
const methodColor = theme.request.methods[methodUpper?.toLowerCase()] || theme.text;
|
||||
|
||||
const methodColor = useMemo(() => {
|
||||
const methodLower = method?.toLowerCase();
|
||||
return theme.request.methods[methodLower] || theme.text;
|
||||
}, [method, theme]);
|
||||
|
||||
return (
|
||||
<span className="timeline-method" style={{ color: methodColor, fontWeight: 'bold' }}>
|
||||
{methodUpper}
|
||||
<span className="font-medium uppercase" style={{ color: methodColor, fontSize: theme.font.size.xs }}>
|
||||
{method}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
|
||||
const getRelativeTime = (date) => {
|
||||
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
|
||||
@@ -23,6 +24,7 @@ const getRelativeTime = (date) => {
|
||||
|
||||
export const RelativeTime = ({ timestamp }) => {
|
||||
const [relativeTime, setRelativeTime] = useState(getRelativeTime(new Date(timestamp)));
|
||||
const { theme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
@@ -32,5 +34,15 @@ export const RelativeTime = ({ timestamp }) => {
|
||||
return () => clearInterval(interval);
|
||||
}, [timestamp]);
|
||||
|
||||
return <pre className="text-xs">{relativeTime}</pre>;
|
||||
return (
|
||||
<span
|
||||
title={new Date(timestamp).toLocaleString()}
|
||||
style={{
|
||||
fontSize: theme.font.size.xs,
|
||||
color: theme.colors.text.muted
|
||||
}}
|
||||
>
|
||||
{relativeTime}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,11 +3,11 @@ import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.timeline-item {
|
||||
border-bottom: 2px solid ${(props) => rgba(props.theme.colors.text.warning, 0.5)};
|
||||
border-bottom: 1px solid ${(props) => props.theme.border.border1};
|
||||
padding: 0.5rem 0;
|
||||
|
||||
&--oauth2 {
|
||||
border-bottom: 2px solid ${(props) => rgba(props.theme.primary.solid, 0.5)};
|
||||
border-bottom: 1px solid ${(props) => props.theme.border.border1};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import Network from './Network/index';
|
||||
import Request from './Request/index';
|
||||
import Response from './Response/index';
|
||||
@@ -8,6 +9,7 @@ import { RelativeTime } from './Common/Time/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const TimelineItem = ({ timestamp, request, response, item, collection, isOauth2, hideTimestamp = false }) => {
|
||||
const { theme } = useTheme();
|
||||
const [isCollapsed, _toggleCollapse] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('request');
|
||||
const toggleCollapse = () => _toggleCollapse((prev) => !prev);
|
||||
@@ -18,24 +20,18 @@ const TimelineItem = ({ timestamp, request, response, item, collection, isOauth2
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className={`timeline-item ${isOauth2 ? 'timeline-item--oauth2' : ''}`}>
|
||||
<div className="timeline-item-header" onClick={toggleCollapse}>
|
||||
<div className="timeline-item-header-content">
|
||||
<div className="timeline-item-header-items">
|
||||
<Status statusCode={responseStatus || responseStatusCode} statusText={responseStatusText} />
|
||||
<Method method={method} />
|
||||
<Status statusCode={status || statusCode} statusText={statusText} />
|
||||
{isOauth2 && <pre className="timeline-item-oauth-label">[oauth2.0]</pre>}
|
||||
{!hideTimestamp && (
|
||||
<>
|
||||
<pre className="timeline-item-timestamp-iso">[{new Date(timestamp).toISOString()}]</pre>
|
||||
<span className="timeline-item-timestamp">
|
||||
<RelativeTime timestamp={timestamp} />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="oauth-request-item-header relative cursor-pointer flex items-center justify-between gap-3 min-w-0" onClick={toggleCollapse}>
|
||||
<Status statusCode={responseStatus || responseStatusCode} statusText={responseStatusText} />
|
||||
<div className="flex items-center gap-1">
|
||||
<Method method={method} />
|
||||
<div className="truncate flex-1 min-w-0">{url}</div>
|
||||
{isOauth2 && <span className="text-xs flex-shrink-0" style={{ color: theme.colors.text.muted }}>[oauth2.0]</span>}
|
||||
</div>
|
||||
<div className="timeline-item-url">{url}</div>
|
||||
{!hideTimestamp && (
|
||||
<span className="flex-shrink-0 ml-auto">
|
||||
<RelativeTime timestamp={timestamp} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{isCollapsed && (
|
||||
<div className="timeline-item-content">
|
||||
|
||||
@@ -185,7 +185,7 @@ const WSMessagesList = ({ order = -1, messages = [] }) => {
|
||||
}
|
||||
const ordered = order === -1 ? messages : messages.slice().reverse();
|
||||
return (
|
||||
<StyledWrapper className="ws-messages-list mt-2 flex flex-col">
|
||||
<StyledWrapper className="ws-messages-list flex flex-col">
|
||||
{ordered.map((msg, idx, src) => {
|
||||
const inFocus = order === -1 ? src.length - 1 === idx : idx === 0;
|
||||
return <WSMessageItem key={msg.timestamp} inFocus={inFocus} id={idx} message={msg} />;
|
||||
|
||||
@@ -135,7 +135,7 @@ const WSResponsePane = ({ item, collection }) => {
|
||||
) : null}
|
||||
</div>
|
||||
<section
|
||||
className={`flex flex-col flex-grow pl-3 pr-4 h-0 ${focusedTab.responsePaneTab === 'response' ? '' : 'mt-4'}`}
|
||||
className="flex flex-col flex-grow pl-3 pr-4 h-0 mt-4"
|
||||
>
|
||||
{isLoading ? <Overlay item={item} collection={collection} /> : null}
|
||||
{!item?.response ? (
|
||||
|
||||
@@ -284,7 +284,7 @@ const ResponsePane = ({ item, collection }) => {
|
||||
/>
|
||||
</div>
|
||||
<section
|
||||
className="flex flex-col min-h-0 relative px-4 pt-3 auto overflow-auto"
|
||||
className="flex flex-col min-h-0 relative px-4 auto overflow-auto mt-4"
|
||||
style={{
|
||||
flex: '1 1 0',
|
||||
height: hasScriptError && showScriptErrorCard ? 'auto' : '100%'
|
||||
|
||||
@@ -166,7 +166,7 @@ const CreateApiSpec = ({ onClose }) => {
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Modal size="sm" title="Create API Spec" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<Modal size="md" title="Create API Spec" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={(e) => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="api-spec-location" className="block font-semibold mb-2">
|
||||
|
||||
@@ -85,7 +85,7 @@ const CloneCollection = ({ onClose, collectionUid }) => {
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
|
||||
return (
|
||||
<Modal size="sm" title="Clone Collection" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<Modal size="md" title="Clone Collection" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={(e) => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="collection-name" className="flex items-center font-medium">
|
||||
|
||||
@@ -40,7 +40,7 @@ const DeleteCollectionItem = ({ onClose, item, collectionUid }) => {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Modal
|
||||
size="sm"
|
||||
size="md"
|
||||
title={`Delete ${isFolder ? 'Folder' : 'Request'}`}
|
||||
confirmText="Delete"
|
||||
confirmButtonColor="danger"
|
||||
|
||||
@@ -42,7 +42,7 @@ const RenameCollection = ({ collectionUid, onClose }) => {
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
|
||||
return (
|
||||
<Modal size="sm" title="Rename Collection" confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<Modal size="md" title="Rename Collection" confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={(e) => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="name" className="block font-medium">
|
||||
|
||||
@@ -30,13 +30,12 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
|
||||
border-color: ${(props) => props.theme.sidebar.muted}40;
|
||||
border-color: ${(props) => props.theme.input.border};
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: ${(props) => props.theme.sidebar.bg};
|
||||
border-color: ${(props) => props.theme.sidebar.muted}80;
|
||||
background: ${(props) => props.theme.input.bg};
|
||||
border-color: ${(props) => props.theme.input.border};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) =>
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
readOnly={true}
|
||||
value={formik.values.collectionLocation || ''}
|
||||
onClick={browse}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -5,11 +5,8 @@ const StyledWrapper = styled.div`
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
border-right: none;
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
.method-selector {
|
||||
min-width: 80px;
|
||||
}
|
||||
border-top-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-bottom-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
}
|
||||
div.method-selector-container,
|
||||
div.input-container {
|
||||
@@ -18,8 +15,8 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
div.input-container {
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-top-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-bottom-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
input {
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
outline: none;
|
||||
@@ -31,12 +28,17 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
|
||||
.textbox {
|
||||
border-radius: ${(props) => props.theme.border.radius.base} !important;
|
||||
height: 2.1rem;
|
||||
}
|
||||
|
||||
textarea.curl-command {
|
||||
min-height: 150px;
|
||||
}
|
||||
.dropdown {
|
||||
width: fit-content;
|
||||
|
||||
|
||||
.dropdown-item {
|
||||
padding: 0.2rem 0.6rem !important;
|
||||
}
|
||||
|
||||
@@ -503,7 +503,7 @@ const NewRequest = ({ collectionUid, item, isEphemeral, onClose }) => {
|
||||
</label>
|
||||
<div className="flex items-center mt-2 ">
|
||||
{!['grpc-request', 'ws-request'].includes(formik.values.requestType) ? (
|
||||
<div className="flex items-center h-full method-selector-container w-1/5">
|
||||
<div className="flex items-center h-full method-selector-container">
|
||||
<HttpMethodSelector
|
||||
method={formik.values.requestMethod}
|
||||
onMethodSelect={(val) => formik.setFieldValue('requestMethod', val)}
|
||||
|
||||
@@ -61,7 +61,7 @@ const CreateEnvironment = ({ onClose, onEnvironmentCreated }) => {
|
||||
return (
|
||||
<Portal>
|
||||
<Modal
|
||||
size="sm"
|
||||
size="md"
|
||||
title="Create Global Environment"
|
||||
confirmText="Create"
|
||||
handleConfirm={onSubmit}
|
||||
|
||||
Reference in New Issue
Block a user