mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-07 14:08:38 +00:00
* Add drag-resize split pane for API Spec viewer Introduce a drag-to-resize split pane for the API Spec viewer and persist left pane width. Adds a new useDragResize hook to manage dragging state and clamping, plus UI: dragbar styles, a loading state for the Swagger preview (onComplete + loader), and memoization of the Swagger renderer. Wire up persisted widths via Redux: add updateApiSpecPanelLeftPaneWidth (apiSpec slice) and updateApiSpecTabLeftPaneWidth (tabs slice), and propagate leftPaneWidth / onLeftPaneWidthChange through ApiSpecPanel, OpenAPISpecTab, RequestTabPanel and SpecViewer. Misc: pass tab uid into OpenAPISpecTab and add .gstack/ to .gitignore. * Refactor SpecViewer and OpenAPISpecTab for improved loading and state management - Updated SpecViewer to enhance loading state handling for Swagger content, ensuring a smoother user experience by preventing flashes of unrendered content. - Refactored OpenAPISpecTab to streamline environment context management, optimizing the loading process for OpenAPI specifications. - Simplified the useDragResize hook by removing unnecessary references and improving the handling of drag events, ensuring better performance and responsiveness during resizing actions. * Enhance useDragResize hook to clamp width seed and improve test coverage - Updated the useDragResize hook to clamp the width seed value, ensuring it stays within defined bounds during drag events. - Added a new test case to verify that an out-of-bounds width seed is correctly clamped and persisted on immediate mouseup, enhancing the robustness of the drag-resize functionality. * Remove .gstack/ from .gitignore Delete the .gstack/ ignore entry and normalize the packages/bruno-converters/dist entry in .gitignore (deduplicated). No code changes; just tidy up ignore rules.
99 lines
3.3 KiB
JavaScript
99 lines
3.3 KiB
JavaScript
import React, { forwardRef, useRef, useCallback } from 'react';
|
|
import find from 'lodash/find';
|
|
import { useSelector, useDispatch } from 'react-redux';
|
|
import { IconFileCode, IconDots } from '@tabler/icons';
|
|
import StyledWrapper from './StyledWrapper';
|
|
import SpecViewer from './SpecViewer';
|
|
import Dropdown from 'components/Dropdown';
|
|
import { openApiSpec, saveApiSpecToFile, updateApiSpecPanelLeftPaneWidth } from 'providers/ReduxStore/slices/apiSpec';
|
|
import { useState } from 'react';
|
|
import CreateApiSpec from 'components/Sidebar/ApiSpecs/CreateApiSpec';
|
|
import toast from 'react-hot-toast';
|
|
|
|
const ApiSpecPanel = () => {
|
|
const dispatch = useDispatch();
|
|
|
|
const [createApiSpecModalOpen, setCreateApiSpecModalOpen] = useState(false);
|
|
|
|
const { apiSpecs, activeApiSpecUid } = useSelector((state) => state.apiSpec);
|
|
|
|
const dropdownTippyRef = useRef();
|
|
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
|
|
|
let apiSpec = find(apiSpecs, (c) => c.uid === activeApiSpecUid);
|
|
const { filename, pathname, raw, uid, leftPaneWidth } = apiSpec || {};
|
|
|
|
const handleLeftPaneWidthChange = useCallback(
|
|
(w) => {
|
|
if (!uid) return;
|
|
dispatch(updateApiSpecPanelLeftPaneWidth({ uid, leftPaneWidth: w }));
|
|
},
|
|
[dispatch, uid]
|
|
);
|
|
|
|
if (!uid) {
|
|
return <div className="p-4 opacity-50">API Spec not found!</div>;
|
|
}
|
|
|
|
const MenuIcon = forwardRef((props, ref) => {
|
|
return (
|
|
<div ref={ref}>
|
|
<IconDots size={22} />
|
|
</div>
|
|
);
|
|
});
|
|
|
|
const handleOpenApiSpec = () => {
|
|
dispatch(openApiSpec()).catch(
|
|
(err) => console.log(err) && toast.error('An error occurred while opening the API spec')
|
|
);
|
|
};
|
|
|
|
return (
|
|
<StyledWrapper className="flex flex-col flex-grow relative">
|
|
{createApiSpecModalOpen ? <CreateApiSpec onClose={() => setCreateApiSpecModalOpen(false)} /> : null}
|
|
<div className="p-3 mb-2 w-full flex flex-row justify-between grid grid-cols-3">
|
|
<div className="flex flex-row justify-start gap-x-4 col-span-1">
|
|
<div className="flex w-fit items-center cursor-pointer">
|
|
<IconFileCode size={18} strokeWidth={1.5} />
|
|
<span className="ml-2 mr-4 font-semibold">API Designer</span>
|
|
</div>
|
|
</div>
|
|
<div className="w-full col-span-1 flex justify-center" title={pathname}>
|
|
{filename}
|
|
</div>
|
|
<div className="menu-icon pr-2 col-span-1 flex justify-end">
|
|
<Dropdown onCreate={onDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
|
|
<div
|
|
className="dropdown-item"
|
|
onClick={(e) => {
|
|
dropdownTippyRef.current.hide();
|
|
setCreateApiSpecModalOpen(true);
|
|
}}
|
|
>
|
|
Create API Spec
|
|
</div>
|
|
<div
|
|
className="dropdown-item"
|
|
onClick={(e) => {
|
|
dropdownTippyRef.current.hide();
|
|
handleOpenApiSpec();
|
|
}}
|
|
>
|
|
Open API Spec
|
|
</div>
|
|
</Dropdown>
|
|
</div>
|
|
</div>
|
|
<SpecViewer
|
|
content={raw}
|
|
onSave={(content) => dispatch(saveApiSpecToFile({ uid, content }))}
|
|
leftPaneWidth={leftPaneWidth ?? null}
|
|
onLeftPaneWidthChange={handleLeftPaneWidthChange}
|
|
/>
|
|
</StyledWrapper>
|
|
);
|
|
};
|
|
|
|
export default ApiSpecPanel;
|