Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions packages/bruno-app/src/components/BodyModeSelector/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useRef, forwardRef } from 'react';
import { IconCaretDown } from '@tabler/icons';
import Dropdown from 'components/Dropdown';
import { humanizeRequestBodyMode } from 'utils/collections';

const DEFAULT_MODES = [
{ key: 'multipartForm', label: 'Multipart Form', category: 'Form' },
{ key: 'formUrlEncoded', label: 'Form URL Encoded', category: 'Form' },
{ key: 'json', label: 'JSON', category: 'Raw' },
{ key: 'xml', label: 'XML', category: 'Raw' },
{ key: 'text', label: 'TEXT', category: 'Raw' },
{ key: 'sparql', label: 'SPARQL', category: 'Raw' },
{ key: 'file', label: 'File / Binary', category: 'Other' },
{ key: 'none', label: 'None', category: 'Other' }
];

const BodyModeSelector = ({
currentMode,
onModeChange,
modes = DEFAULT_MODES,
disabled = false,
className = '',
wrapperClassName = '',
showCategories = true,
placement = 'bottom-end'
}) => {
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);

const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center pl-3 py-1 select-none selected-body-mode">
{humanizeRequestBodyMode(currentMode)}
{' '}
<IconCaretDown className="caret ml-2" size={14} strokeWidth={2} />
</div>
);
});

const onModeSelect = (mode) => {
dropdownTippyRef.current.hide();
onModeChange(mode);
};

// Group modes by category for rendering
const groupedModes = modes.reduce((acc, mode) => {
const category = mode.category || 'Other';
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(mode);
return acc;
}, {});

return (
<div className={`inline-flex items-center body-mode-selector ${disabled ? 'cursor-default' : 'cursor-pointer'} ${wrapperClassName}`}>
<Dropdown
onCreate={onDropdownCreate}
icon={<Icon />}
placement={placement}
disabled={disabled}
className={className}
>
{Object.entries(groupedModes).map(([category, categoryModes]) => (
<React.Fragment key={category}>
{showCategories && <div className="label-item font-medium">{category}</div>}
{categoryModes.map((mode) => (
<div
key={mode.key}
className="dropdown-item"
onClick={() => onModeSelect(mode.key)}
>
{mode.label}
</div>
))}
</React.Fragment>
))}
</Dropdown>
</div>
);
};

export default BodyModeSelector;
79 changes: 79 additions & 0 deletions packages/bruno-app/src/components/Checkbox/StyledWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import styled from 'styled-components';

const StyledWrapper = styled.div`
.checkbox-container {
width: 1rem;
height: 1rem;
display: flex;
justify-content: center;
align-items: center;
position: relative;
cursor: pointer;

&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}

.checkbox-checkmark {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
visibility: ${(props) => props.checked ? 'visible' : 'hidden'};
pointer-events: none;
}

.checkbox-input {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 1rem;
height: 1rem;
border: 2px solid ${(props) => {
if (props.checked && props.disabled) {
return props.theme.colors.text.muted;
}

if (props.checked && !props.disabled) {
return props.theme.colors.text.yellow;
}

return props.theme.colors.text.muted;
}};
border-radius: 4px;
background-color: ${(props) => {
if (props.checked && !props.disabled) {
return props.theme.colors.text.yellow;
}

if (props.checked && props.disabled) {
return props.theme.colors.text.muted;
}

return 'transparent';
}};
cursor: pointer;
position: relative;
transition: all 0.2s ease;
outline: none;
box-shadow: none;

&:hover:not(:disabled) {
opacity: 0.8;
}

&:disabled {
cursor: not-allowed;
opacity: 0.5;
}

&:focus {
outline: none;
box-shadow: 0 0 0 2px ${(props) => props.theme.colors.text.yellow}40;
}
}
`;

export default StyledWrapper;
45 changes: 45 additions & 0 deletions packages/bruno-app/src/components/Checkbox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import StyledWrapper from './StyledWrapper';
import IconCheckMark from 'components/Icons/IconCheckMark';
import { useTheme } from 'providers/Theme';

const Checkbox = ({
checked = false,
disabled = false,
onChange,
className = '',
id,
name,
value,
dataTestId = 'checkbox'
}) => {
const { theme } = useTheme();

const handleChange = (e) => {
if (!disabled && onChange) {
onChange(e);
}
};

return (
<StyledWrapper checked={checked} disabled={disabled} className={className}>
<div className="checkbox-container">
<input
type="checkbox"
id={id}
name={name}
value={value}
checked={checked}
disabled={disabled}
onChange={handleChange}
className="checkbox-input"
data-testid={dataTestId}
/>
<IconCheckMark className="checkbox-checkmark" color={theme.examples.checkbox.color} size={14} />
</div>

</StyledWrapper>
);
};

export default Checkbox;
12 changes: 12 additions & 0 deletions packages/bruno-app/src/components/CodeEditor/StyledWrapper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import styled from 'styled-components';

const StyledWrapper = styled.div`
&.read-only {
div.CodeMirror .CodeMirror-lines {
user-select: none !important;
-webkit-user-select: none !important;
-ms-user-select: none !important;
}

div.CodeMirror .CodeMirror-cursor {
display: none !important;
}
}

div.CodeMirror {
background: ${(props) => props.theme.codemirror.bg};
border: solid 1px ${(props) => props.theme.codemirror.border};
Expand Down
8 changes: 6 additions & 2 deletions packages/bruno-app/src/components/CodeEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default class CodeEditor extends React.Component {
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
lint: this.lintOptions,
readOnly: this.props.readOnly,
readOnly: this.props.readOnly ? 'nocursor' : false,
scrollbarStyle: 'overlay',
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
extraKeys: {
Expand Down Expand Up @@ -245,6 +245,10 @@ export default class CodeEditor extends React.Component {
this.editor.setOption('mode', this.props.mode);
}

if (this.props.readOnly !== prevProps.readOnly && this.editor) {
this.editor.setOption('readOnly', this.props.readOnly ? 'nocursor' : false);
}

this.ignoreChangeEvent = false;
}

Expand All @@ -262,7 +266,7 @@ export default class CodeEditor extends React.Component {
}
return (
<StyledWrapper
className="h-full w-full flex flex-col relative graphiql-container"
className={`h-full w-full flex flex-col relative graphiql-container ${this.props.readOnly ? 'read-only' : ''}`}
aria-label="Code Editor"
font={this.props.font}
fontSize={this.props.fontSize}
Expand Down
18 changes: 11 additions & 7 deletions packages/bruno-app/src/components/FilePickerEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { browseFiles } from 'providers/ReduxStore/slices/collections/actions';
import { IconX } from '@tabler/icons';
import { isWindowsOS } from 'utils/common/platform';

const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false }) => {
const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false, readOnly = false }) => {
const dispatch = useDispatch();
const filenames = (isSingleFilePicker ? [value] : value || [])
.filter((v) => v != null && v != '')
Expand Down Expand Up @@ -50,20 +50,24 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa
return filenames.length + ' file(s) selected';
};

const buttonClass = `btn btn-secondary px-1 ${readOnly ? 'view-mode' : 'edit-mode'}`;

return filenames.length > 0 ? (
<div
className="btn btn-secondary px-1"
className={buttonClass}
style={{ fontWeight: 400, width: '100%', textOverflow: 'ellipsis', overflowX: 'hidden' }}
title={title}
>
<button className="align-middle" onClick={clear}>
<IconX size={18} />
</button>
&nbsp;
{!readOnly && (
<button className="align-middle" onClick={clear}>
<IconX size={18} />
</button>
)}
{!readOnly && <>&nbsp;</>}
{renderButtonText(filenames)}
</div>
) : (
<button className="btn btn-secondary px-1" style={{ width: '100%' }} onClick={browse}>
<button className={buttonClass} style={{ width: '100%' }} onClick={!readOnly ? browse : undefined} disabled={readOnly}>
{isSingleFilePicker ? 'Select File' : 'Select Files'}
</button>
);
Expand Down
18 changes: 18 additions & 0 deletions packages/bruno-app/src/components/Icons/IconCaretDown/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';

const IconCaretDown = ({ color = '#8C8C8C', ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<g clipPath="url(#clip0_464_9256)">
<path d="M10.5444 5.75H4.46004C4.26888 5.7509 4.08142 5.78521 3.91637 5.84952C3.75132 5.91383 3.61447 6.00587 3.51947 6.11647C3.42448 6.22706 3.37466 6.35234 3.375 6.47978C3.37534 6.60723 3.42583 6.73238 3.52142 6.84275L6.56492 10.23C6.66228 10.3372 6.79942 10.4258 6.96311 10.4874C7.1268 10.5489 7.31151 10.5813 7.49945 10.5814C7.68739 10.5816 7.8722 10.5494 8.03608 10.4881C8.19995 10.4267 8.33735 10.3383 8.43504 10.2312L11.4763 6.8465C11.573 6.73635 11.6246 6.61118 11.626 6.48355C11.6273 6.35591 11.5783 6.23028 11.4839 6.11924C11.3895 6.0082 11.253 5.91564 11.088 5.85084C10.9231 5.78603 10.7359 5.75126 10.5444 5.75Z" fill="#8C8C8C" />
</g>
<defs>
<clipPath id="clip0_464_9256">
<rect width="9" height="6" fill="white" transform="translate(3 5)" />
</clipPath>
</defs>
</svg>
);
};

export default IconCaretDown;
11 changes: 11 additions & 0 deletions packages/bruno-app/src/components/Icons/IconCheckMark/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

const IconCheckMark = ({ color = '#cccccc', size = 16, ...props }) => {
return (
<svg width={size} height={size} viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<path d="M3.3335 8.49996L6.66683 11.8333L13.3335 5.16663" stroke={color} strokeWidth="1.33333" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
};

export default IconCheckMark;
19 changes: 19 additions & 0 deletions packages/bruno-app/src/components/Icons/IconEdit/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

const IconEdit = ({ color = '#F39D0E', size = 16, ...props }) => {
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clipPath="url(#clip0_464_9527)">
<path d="M12.6665 13.3332H5.66654L2.85988 10.4665C2.73571 10.3416 2.66602 10.1727 2.66602 9.99654C2.66602 9.82042 2.73571 9.65145 2.85988 9.52654L9.52654 2.85988C9.65145 2.73571 9.82042 2.66602 9.99654 2.66602C10.1727 2.66602 10.3416 2.73571 10.4665 2.85988L13.7999 6.19321C13.924 6.31812 13.9937 6.48709 13.9937 6.66321C13.9937 6.83933 13.924 7.0083 13.7999 7.13321L7.66654 13.3332" stroke={color} strokeWidth="1.33333" strokeLinecap="round" strokeLinejoin="round" />
<path d="M11.9998 8.86663L7.7998 4.66663" stroke={color} strokeWidth="1.33333" strokeLinecap="round" strokeLinejoin="round" />
</g>
<defs>
<clipPath id="clip0_464_9527">
<rect width={size} height={size} fill="white" />
</clipPath>
</defs>
</svg>
);
};

export default IconEdit;
21 changes: 21 additions & 0 deletions packages/bruno-app/src/components/Icons/examples/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';

const ExampleIcon = ({ color = 'white', size = 16, ...props }) => {
return (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
<g clipPath="url(#clip0_486_1191)">
<path d="M2.66699 3.33329C2.66699 3.15648 2.73723 2.98691 2.86225 2.86189C2.98728 2.73686 3.15685 2.66663 3.33366 2.66663H12.667C12.8438 2.66663 13.0134 2.73686 13.1384 2.86189C13.2634 2.98691 13.3337 3.15648 13.3337 3.33329V12.6666C13.3337 12.8434 13.2634 13.013 13.1384 13.138C13.0134 13.2631 12.8438 13.3333 12.667 13.3333H3.33366C3.15685 13.3333 2.98728 13.2631 2.86225 13.138C2.73723 13.013 2.66699 12.8434 2.66699 12.6666V3.33329Z" stroke={color} stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M9.33366 5.33337H6.66699V10.6667H9.33366" stroke={color} stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
<path d="M9.33366 8H6.66699" stroke={color} stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_486_1191">
<rect width={size} height={size} fill="white" />
</clipPath>
</defs>
</svg>

);
};

export default ExampleIcon;
Loading
Loading