-
-
- {apiSpecs && apiSpecs.length
- ? apiSpecs.map((apiSpec) => {
- return
;
- })
- : null}
-
+
+ {apiSpecs && apiSpecs.length
+ ? apiSpecs.map((apiSpec) => {
+ return
;
+ })
+ : null}
);
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
index 822559324..07af84710 100644
--- a/packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
+++ b/packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
@@ -3,32 +3,20 @@ import styled from 'styled-components';
const Wrapper = styled.div`
display: flex;
flex-direction: column;
- flex: 1;
+ flex: 1 1 0%;
min-height: 0;
overflow: hidden;
padding-top: 4px;
+ padding-bottom: 4px;
.collections-list {
+ flex: 1 1 0%;
min-height: 0;
padding-top: 4px;
+ padding-bottom: 4px;
overflow-y: auto;
+ overflow-x: hidden;
- &::-webkit-scrollbar {
- width: 6px;
- }
-
- &::-webkit-scrollbar-track {
- background: transparent;
- }
-
- &::-webkit-scrollbar-thumb {
- background: ${(props) => props.theme.scrollbar.color};
- border-radius: 3px;
- }
-
- &::-webkit-scrollbar-thumb:hover {
- background: ${(props) => props.theme.scrollbar.color};
- }
}
`;
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js
index f38abccf1..60ac62a6a 100644
--- a/packages/bruno-app/src/components/Sidebar/Collections/index.js
+++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js
@@ -7,7 +7,6 @@ import CreateOrOpenCollection from './CreateOrOpenCollection';
import CollectionSearch from './CollectionSearch/index';
import { useMemo } from 'react';
import { normalizePath } from 'utils/common/path';
-import ApiSpecs from '../ApiSpecs/index';
const Collections = ({ showSearch }) => {
const [searchText, setSearchText] = useState('');
@@ -44,7 +43,7 @@ const Collections = ({ showSearch }) => {
)}
-
+
{workspaceCollections && workspaceCollections.length
? workspaceCollections.map((c) => {
return (
@@ -52,8 +51,6 @@ const Collections = ({ showSearch }) => {
);
})
: null}
-
-
);
diff --git a/packages/bruno-app/src/components/Sidebar/Sections/ApiSpecsSection/index.js b/packages/bruno-app/src/components/Sidebar/Sections/ApiSpecsSection/index.js
new file mode 100644
index 000000000..23d84966e
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/Sections/ApiSpecsSection/index.js
@@ -0,0 +1,79 @@
+import { useState } from 'react';
+import toast from 'react-hot-toast';
+import { useDispatch } from 'react-redux';
+import { IconFileCode, IconPlus } from '@tabler/icons';
+
+import { openApiSpec } from 'providers/ReduxStore/slices/apiSpec';
+import MenuDropdown from 'ui/MenuDropdown';
+import ActionIcon from 'ui/ActionIcon';
+import CreateApiSpec from 'components/Sidebar/ApiSpecs/CreateApiSpec';
+import ApiSpecs from 'components/Sidebar/ApiSpecs';
+import SidebarSection from 'components/Sidebar/SidebarSection';
+
+const ApiSpecsSection = () => {
+ const dispatch = useDispatch();
+ const [createApiSpecModalOpen, setCreateApiSpecModalOpen] = useState(false);
+
+ const handleOpenApiSpec = () => {
+ dispatch(openApiSpec()).catch((err) => {
+ console.error(err);
+ toast.error('An error occurred while opening the API spec');
+ });
+ };
+
+ const addDropdownItems = [
+ {
+ id: 'create-api-spec',
+ leftSection: IconPlus,
+ label: 'Create API Spec',
+ onClick: () => {
+ setCreateApiSpecModalOpen(true);
+ }
+ },
+ {
+ id: 'open-api-spec',
+ leftSection: IconFileCode,
+ label: 'Open API Spec',
+ onClick: () => {
+ handleOpenApiSpec();
+ }
+ }
+ ];
+
+ const sectionActions = (
+ <>
+
+
+
+
+
+ >
+ );
+
+ return (
+ <>
+ {createApiSpecModalOpen && (
+
setCreateApiSpecModalOpen(false)}
+ />
+ )}
+
+
+
+ >
+ );
+};
+
+export default ApiSpecsSection;
diff --git a/packages/bruno-app/src/components/Sidebar/Sections/CollectionsSection/index.js b/packages/bruno-app/src/components/Sidebar/Sections/CollectionsSection/index.js
new file mode 100644
index 000000000..e28414959
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/Sections/CollectionsSection/index.js
@@ -0,0 +1,255 @@
+import { useState } from 'react';
+import toast from 'react-hot-toast';
+import { useDispatch, useSelector } from 'react-redux';
+import {
+ IconArrowsSort,
+ IconDotsVertical,
+ IconDownload,
+ IconFolder,
+ IconPlus,
+ IconSearch,
+ IconSortAscendingLetters,
+ IconSortDescendingLetters,
+ IconSquareX
+} from '@tabler/icons';
+
+import { importCollection, openCollection } from 'providers/ReduxStore/slices/collections/actions';
+import { sortCollections } from 'providers/ReduxStore/slices/collections/index';
+import { importCollectionInWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
+
+import MenuDropdown from 'ui/MenuDropdown';
+import ActionIcon from 'ui/ActionIcon';
+import ImportCollection from 'components/Sidebar/ImportCollection';
+import ImportCollectionLocation from 'components/Sidebar/ImportCollectionLocation';
+import RemoveCollectionsModal from 'components/Sidebar/Collections/RemoveCollectionsModal/index';
+import CreateCollection from 'components/Sidebar/CreateCollection';
+import Collections from 'components/Sidebar/Collections';
+import SidebarSection from 'components/Sidebar/SidebarSection';
+import { IconBox } from '@tabler/icons';
+
+const CollectionsSection = () => {
+ const [showSearch, setShowSearch] = useState(false);
+ const dispatch = useDispatch();
+
+ const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces);
+ const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
+
+ const { collections } = useSelector((state) => state.collections);
+ const { collectionSortOrder } = useSelector((state) => state.collections);
+ const [collectionsToClose, setCollectionsToClose] = useState([]);
+
+ const [importData, setImportData] = useState(null);
+ const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false);
+ const [importCollectionModalOpen, setImportCollectionModalOpen] = useState(false);
+ const [importCollectionLocationModalOpen, setImportCollectionLocationModalOpen] = useState(false);
+
+ const handleImportCollection = ({ rawData, type }) => {
+ setImportCollectionModalOpen(false);
+
+ if (activeWorkspace && activeWorkspace.type !== 'default') {
+ dispatch(importCollectionInWorkspace(rawData, activeWorkspace.uid, undefined, type))
+ .catch((err) => {
+ toast.error('An error occurred while importing the collection');
+ });
+ } else {
+ setImportData({ rawData, type });
+ setImportCollectionLocationModalOpen(true);
+ }
+ };
+
+ const handleImportCollectionLocation = (convertedCollection, collectionLocation) => {
+ dispatch(importCollection(convertedCollection, collectionLocation))
+ .then(() => {
+ setImportCollectionLocationModalOpen(false);
+ setImportData(null);
+ toast.success('Collection imported successfully');
+ })
+ .catch((err) => {
+ console.error(err);
+ toast.error('An error occurred while importing the collection');
+ });
+ };
+
+ const handleToggleSearch = () => {
+ setShowSearch((prev) => !prev);
+ };
+
+ const handleSortCollections = () => {
+ let order;
+ switch (collectionSortOrder) {
+ case 'default':
+ order = 'alphabetical';
+ break;
+ case 'alphabetical':
+ order = 'reverseAlphabetical';
+ break;
+ case 'reverseAlphabetical':
+ order = 'default';
+ break;
+ default:
+ order = 'default';
+ break;
+ }
+ dispatch(sortCollections({ order }));
+ };
+
+ const getSortIcon = () => {
+ switch (collectionSortOrder) {
+ case 'alphabetical':
+ return IconSortDescendingLetters;
+ case 'reverseAlphabetical':
+ return IconArrowsSort;
+ default:
+ return IconSortAscendingLetters;
+ }
+ };
+
+ const getSortLabel = () => {
+ switch (collectionSortOrder) {
+ case 'alphabetical':
+ return 'Sort Z-A';
+ case 'reverseAlphabetical':
+ return 'Clear sort';
+ default:
+ return 'Sort A-Z';
+ }
+ };
+
+ const selectAllCollectionsToClose = () => {
+ setCollectionsToClose(collections.map((c) => c.uid));
+ };
+
+ const clearCollectionsToClose = () => {
+ setCollectionsToClose([]);
+ };
+
+ const handleOpenCollection = () => {
+ const options = {};
+ if (activeWorkspace?.pathname) {
+ options.workspaceId = activeWorkspace.pathname;
+ }
+
+ dispatch(openCollection(options)).catch((err) => {
+ toast.error('An error occurred while opening the collection');
+ });
+ };
+
+ const addDropdownItems = [
+ {
+ id: 'create',
+ leftSection: IconPlus,
+ label: 'Create collection',
+ onClick: () => {
+ setCreateCollectionModalOpen(true);
+ }
+ },
+ {
+ id: 'import',
+ leftSection: IconDownload,
+ label: 'Import collection',
+ onClick: () => {
+ setImportCollectionModalOpen(true);
+ }
+ },
+ {
+ id: 'open',
+ leftSection: IconFolder,
+ label: 'Open collection',
+ onClick: () => {
+ handleOpenCollection();
+ }
+ }
+ ];
+
+ const actionsDropdownItems = [
+ {
+ id: 'sort',
+ leftSection: getSortIcon(),
+ label: getSortLabel(),
+ onClick: () => {
+ handleSortCollections();
+ }
+ },
+ {
+ id: 'close-all',
+ leftSection: IconSquareX,
+ label: 'Close all',
+ onClick: () => {
+ selectAllCollectionsToClose();
+ }
+ }
+ ];
+
+ const sectionActions = (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {collectionsToClose.length > 0 && (
+
+ )}
+ >
+ );
+
+ return (
+ <>
+ {createCollectionModalOpen && (
+ setCreateCollectionModalOpen(false)}
+ />
+ )}
+ {importCollectionModalOpen && (
+ setImportCollectionModalOpen(false)}
+ handleSubmit={handleImportCollection}
+ />
+ )}
+ {importCollectionLocationModalOpen && importData && (
+ setImportCollectionLocationModalOpen(false)}
+ handleSubmit={handleImportCollectionLocation}
+ />
+ )}
+
+
+
+ >
+ );
+};
+
+export default CollectionsSection;
diff --git a/packages/bruno-app/src/components/Sidebar/SidebarAccordionContext.js b/packages/bruno-app/src/components/Sidebar/SidebarAccordionContext.js
new file mode 100644
index 000000000..d093753d6
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/SidebarAccordionContext.js
@@ -0,0 +1,61 @@
+import React, { createContext, useContext, useState, useCallback } from 'react';
+
+const SidebarAccordionContext = createContext();
+
+export const useSidebarAccordion = () => {
+ const context = useContext(SidebarAccordionContext);
+ if (!context) {
+ throw new Error('useSidebarAccordion must be used within SidebarAccordionProvider');
+ }
+ return context;
+};
+
+export const SidebarAccordionProvider = ({ children, defaultExpanded = ['collections'] }) => {
+ const [expandedSections, setExpandedSections] = useState(new Set(defaultExpanded));
+
+ const toggleSection = useCallback((sectionId) => {
+ setExpandedSections((prev) => {
+ const newSet = new Set(prev);
+ if (newSet.has(sectionId)) {
+ newSet.delete(sectionId);
+ } else {
+ newSet.add(sectionId);
+ }
+ return newSet;
+ });
+ }, []);
+
+ const setSectionExpanded = useCallback((sectionId, expanded) => {
+ setExpandedSections((prev) => {
+ const newSet = new Set(prev);
+ if (expanded) {
+ newSet.add(sectionId);
+ } else {
+ newSet.delete(sectionId);
+ }
+ return newSet;
+ });
+ }, []);
+
+ const isExpanded = useCallback((sectionId) => {
+ return expandedSections.has(sectionId);
+ }, [expandedSections]);
+
+ const getExpandedCount = useCallback(() => {
+ return expandedSections.size;
+ }, [expandedSections]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/packages/bruno-app/src/components/Sidebar/SidebarContent.js b/packages/bruno-app/src/components/Sidebar/SidebarContent.js
new file mode 100644
index 000000000..8c913ffec
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/SidebarContent.js
@@ -0,0 +1,69 @@
+import { useSidebarAccordion } from './SidebarAccordionContext';
+
+/**
+ * Sections configuration
+ *
+ * All sections use the same generic accordion behavior with the class 'accordion-section-wrapper'.
+ * Layout behavior is fully automatic based on section order and expansion state:
+ * - Single expanded: When only one section is expanded, it fills available space
+ * - Multi-expanded: When multiple sections are expanded, they split space equally
+ * - Automatic pinning: Sections below an expanded section are automatically pinned to bottom
+ *
+ * To add a new section, simply add a new entry to this array:
+ *
+ * {
+ * id: 'my-section', // Unique identifier
+ * component: MySectionComponent, // React component to render
+ * getProps: (context) => ({ ... }) // Function to get props for component
+ * }
+ */
+
+const SidebarContent = ({ sections }) => {
+ const { isExpanded, getExpandedCount } = useSidebarAccordion();
+
+ const expandedCount = getExpandedCount();
+
+ const getWrapperClassName = (section, sectionIndex) => {
+ const sectionExpanded = isExpanded(section.id);
+ // Use generic accordion-section-wrapper class for all sections
+ const classes = ['accordion-section-wrapper'];
+
+ // Multi-expanded: when multiple sections are expanded
+ if (expandedCount > 1 && sectionExpanded) {
+ classes.push('multi-expanded');
+ }
+
+ // Single expanded wrapper behavior: when only one section is expanded, it fills space
+ if (sectionExpanded && expandedCount === 1) {
+ classes.push('single-expanded-wrapper');
+ }
+
+ // Automatic pinning: if section is not expanded and any section above it (earlier in array) is expanded
+ if (!sectionExpanded) {
+ // Check if any section before this one (earlier in array) is expanded
+ const hasExpandedAbove = sections.slice(0, sectionIndex).some((s) => isExpanded(s.id));
+ if (hasExpandedAbove) {
+ classes.push('pinned-to-bottom');
+ }
+ }
+
+ return classes.join(' ');
+ };
+
+ return (
+ <>
+ {sections.map((section, index) => {
+ const SectionComponent = section.component;
+ const wrapperClassName = getWrapperClassName(section, index);
+
+ return (
+
+
+
+ );
+ })}
+ >
+ );
+};
+
+export default SidebarContent;
diff --git a/packages/bruno-app/src/components/Sidebar/SidebarSection/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/SidebarSection/StyledWrapper.js
new file mode 100644
index 000000000..dff0ee5d8
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/SidebarSection/StyledWrapper.js
@@ -0,0 +1,116 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div`
+ height: 100%;
+
+ .sidebar-section {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ height: 100%;
+
+ &.expanded {
+ flex: 1 1 0%;
+ min-height: 0;
+ }
+
+ &:not(.expanded) {
+ flex: 0 0 auto;
+ }
+
+ &.multi-expanded {
+ flex: 1 1 0%;
+ margin-bottom: 0;
+ }
+ }
+
+ .section-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 6px 4px 6px 8px;
+ min-height: 28px;
+ height: 28px;
+ user-select: none;
+ transition: background-color 0.15s ease;
+ flex-shrink: 0;
+ border-bottom: 1px solid transparent;
+
+ .section-header-left {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex: 1;
+ min-width: 0;
+ cursor: pointer;
+
+
+ &:hover {
+ .section-toggle {
+ display: flex;
+ }
+
+ .section-toggle {
+ background: ${(props) => props.theme.dropdown.hoverBg};
+ color: ${(props) => props.theme.text} !important;
+ }
+
+ .section-icon {
+ display: none;
+ }
+ }
+ }
+ }
+
+ .section-icon-wrapper {
+ width: 24px;
+ height: 24px;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .section-toggle {
+ display: none;
+ }
+
+ .section-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 16px;
+ height: 16px;
+ color: ${(props) => props.theme.sidebar.muted};
+ }
+
+ .section-title {
+ color: ${(props) => props.theme.sidebar.color};
+ font-size: 12px;
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .section-actions {
+ display: flex;
+ align-items: center;
+ gap: 1px;
+ flex-shrink: 0;
+ }
+ }
+
+ .section-content {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 0%;
+ min-height: 0;
+ overflow-y: auto;
+ overflow-x: hidden;
+ position: relative;
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/Sidebar/SidebarSection/index.js b/packages/bruno-app/src/components/Sidebar/SidebarSection/index.js
new file mode 100644
index 000000000..c43e2ccf2
--- /dev/null
+++ b/packages/bruno-app/src/components/Sidebar/SidebarSection/index.js
@@ -0,0 +1,85 @@
+import { useState, useEffect, useRef } from 'react';
+import { IconChevronRight, IconChevronDown } from '@tabler/icons';
+import StyledWrapper from './StyledWrapper';
+import { useSidebarAccordion } from '../SidebarAccordionContext';
+import ActionIcon from 'ui/ActionIcon/index';
+
+const SidebarSection = ({
+ id,
+ title,
+ icon: Icon,
+ actions,
+ children,
+ className = ''
+}) => {
+ const { isExpanded, setSectionExpanded, getExpandedCount } = useSidebarAccordion();
+ const [localExpanded, setLocalExpanded] = useState(() => isExpanded(id));
+ const sectionRef = useRef(null);
+
+ // Sync with context
+ useEffect(() => {
+ const expanded = isExpanded(id);
+ setLocalExpanded(expanded);
+ }, [id, isExpanded]);
+
+ const handleToggle = () => {
+ const newExpanded = !localExpanded;
+ setLocalExpanded(newExpanded);
+ setSectionExpanded(id, newExpanded);
+ };
+
+ const expandedCount = getExpandedCount();
+ // Check if this is the only expanded section
+ const isOnlyExpanded = expandedCount === 1 && localExpanded;
+
+ return (
+
+ 1 && localExpanded ? 'multi-expanded' : ''}`}
+ >
+
+
+
{
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault(); handleToggle();
+ }
+ }}
+ >
+
+ {localExpanded ? (
+
+ ) : (
+
+ )}
+
+ {Icon &&
}
+
+
{title}
+
+ {actions && (
+
e.stopPropagation()}
+ >
+ {actions}
+
+ )}
+
+ {localExpanded && (
+
+ {children}
+
+ )}
+
+
+ );
+};
+
+export default SidebarSection;
diff --git a/packages/bruno-app/src/components/Sidebar/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/StyledWrapper.js
index 40ab5a299..fd48f9c32 100644
--- a/packages/bruno-app/src/components/Sidebar/StyledWrapper.js
+++ b/packages/bruno-app/src/components/Sidebar/StyledWrapper.js
@@ -2,32 +2,104 @@ import styled from 'styled-components';
const Wrapper = styled.div`
color: ${(props) => props.theme.sidebar.color};
+ max-height: 100%;
aside {
background-color: ${(props) => props.theme.sidebar.bg};
overflow: hidden;
- .collection-title {
- line-height: 1.5;
- .collection-dropdown {
- .dropdown-icon {
- display: none;
- color: rgb(110 110 110);
- }
- }
+ .sidebar-sections-container {
+ display: flex;
+ flex-direction: column;
+ }
- &:hover {
- background: #f7f7f7;
- .dropdown-icon {
- display: flex;
- }
- }
+ .sidebar-sections {
+ min-height: 0;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ }
- div.tippy-box {
- position: relative;
- top: -0.625rem;
+ /* Expanded sections grow to fill available space but are constrained */
+ .sidebar-section.expanded {
+ flex: 1 1 0%;
+ min-height: 0;
+
+ .section-header {
+ border-bottom: 1px solid ${(props) => props.theme.sidebar.collection.item.hoverBg};
}
}
+
+ /* Single expanded section: add margin-bottom to push others down */
+ .sidebar-section.single-expanded {
+ margin-bottom: auto !important;
+ flex: 1 1 0% !important;
+ min-height: 0;
+ max-height: 100%;
+ }
+
+ /* Multiple expanded sections: equal split, no margin-bottom */
+ .sidebar-section.multi-expanded {
+ margin-bottom: 0;
+ flex: 1 1 0% !important;
+
+ min-height: 0;
+ overflow: hidden;
+ max-height: 100%;
+ }
+
+ /* Collapsed sections only take header height */
+ .sidebar-section:not(.expanded) {
+ flex: 0 0 auto;
+ }
+
+ /* Always push bottom accordions wrapper to the bottom */
+ .bottom-accordions-wrapper {
+ display: flex;
+ flex-direction: column;
+ flex: 0 0 auto;
+ }
+
+ /* Generic accordion section wrapper - applies to all accordion sections */
+ .accordion-section-wrapper {
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ position: relative;
+ overflow: visible;
+ }
+
+ /* Add border-top to all accordion items except the first child */
+ .accordion-section-wrapper:not(:first-child) {
+ border-top: 1px solid ${(props) => props.theme.sidebar.collection.item.hoverBg};
+ }
+
+ /* When a section is single expanded, wrapper should fill space but respect pinned sections */
+ .accordion-section-wrapper.single-expanded-wrapper {
+ flex: 1 1 0% !important;
+ min-height: 0;
+ overflow: hidden;
+ }
+
+ /* Normal flow: sections not pinned and not multi-expanded */
+ .accordion-section-wrapper:not(.pinned-to-bottom):not(.multi-expanded) {
+ flex: 0 0 auto;
+ }
+
+ /* When a section is pinned to bottom */
+ .accordion-section-wrapper.pinned-to-bottom {
+ flex: 0 0 auto;
+ margin-top: auto;
+ }
+
+ /* When multiple sections are expanded, split space equally */
+ .accordion-section-wrapper.multi-expanded {
+ flex: 1 1 0% !important;
+ min-height: 0;
+ margin-top: 0 !important;
+ height: auto !important;
+ }
+
}
div.sidebar-drag-handle {
diff --git a/packages/bruno-app/src/components/Sidebar/index.js b/packages/bruno-app/src/components/Sidebar/index.js
index 2071909ea..050d1a456 100644
--- a/packages/bruno-app/src/components/Sidebar/index.js
+++ b/packages/bruno-app/src/components/Sidebar/index.js
@@ -1,20 +1,32 @@
-import SidebarHeader from './SidebarHeader';
-import Collections from './Collections';
+import { SidebarAccordionProvider } from './SidebarAccordionContext';
+import SidebarContent from './SidebarContent';
import StyledWrapper from './StyledWrapper';
import { useState, useEffect, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { updateLeftSidebarWidth, updateIsDragging } from 'providers/ReduxStore/slices/app';
+import CollectionsSection from './Sections/CollectionsSection/index';
+import ApiSpecsSection from './Sections/ApiSpecsSection/index';
const MIN_LEFT_SIDEBAR_WIDTH = 220;
const MAX_LEFT_SIDEBAR_WIDTH = 600;
+const SIDEBAR_SECTIONS = [
+ {
+ id: 'collections',
+ component: CollectionsSection
+ },
+ {
+ id: 'api-specs',
+ component: ApiSpecsSection
+ }
+];
+
const Sidebar = () => {
const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth);
const sidebarCollapsed = useSelector((state) => state.app.sidebarCollapsed);
const [asideWidth, setAsideWidth] = useState(leftSidebarWidth);
const lastWidthRef = useRef(leftSidebarWidth);
- const [showSearch, setShowSearch] = useState(false);
const dispatch = useDispatch();
const [dragging, setDragging] = useState(false);
@@ -77,26 +89,29 @@ const Sidebar = () => {
}, [leftSidebarWidth]);
return (
-
-