forked from extern/bruno
Merge pull request #7 from usebruno/feature/search-collections
feat: search collections, resolves #1
This commit is contained in:
commit
2313afdb82
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useRef, forwardRef } from 'react';
|
import React, { useState, useRef, forwardRef, useEffect } from 'react';
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { IconChevronRight, IconDots } from '@tabler/icons';
|
import { IconChevronRight, IconDots } from '@tabler/icons';
|
||||||
@ -13,10 +13,11 @@ import RenameCollectionItem from './RenameCollectionItem';
|
|||||||
import CloneCollectionItem from './CloneCollectionItem';
|
import CloneCollectionItem from './CloneCollectionItem';
|
||||||
import DeleteCollectionItem from './DeleteCollectionItem';
|
import DeleteCollectionItem from './DeleteCollectionItem';
|
||||||
import { isItemARequest, isItemAFolder, itemIsOpenedInTabs } from 'utils/tabs';
|
import { isItemARequest, isItemAFolder, itemIsOpenedInTabs } from 'utils/tabs';
|
||||||
|
import { doesRequestMatchSearchText, doesFolderHaveItemsMatchSearchText } from 'utils/collections/search';
|
||||||
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
const CollectionItem = ({item, collection}) => {
|
const CollectionItem = ({item, collection, searchText}) => {
|
||||||
const tabs = useSelector((state) => state.tabs.tabs);
|
const tabs = useSelector((state) => state.tabs.tabs);
|
||||||
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
|
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
|
||||||
const isDragging = useSelector((state) => state.app.isDragging);
|
const isDragging = useSelector((state) => state.app.isDragging);
|
||||||
@ -27,6 +28,15 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
const [deleteItemModalOpen, setDeleteItemModalOpen] = useState(false);
|
const [deleteItemModalOpen, setDeleteItemModalOpen] = useState(false);
|
||||||
const [newRequestModalOpen, setNewRequestModalOpen] = useState(false);
|
const [newRequestModalOpen, setNewRequestModalOpen] = useState(false);
|
||||||
const [newFolderModalOpen, setNewFolderModalOpen] = useState(false);
|
const [newFolderModalOpen, setNewFolderModalOpen] = useState(false);
|
||||||
|
const [itemIsCollapsed, setItemisCollapsed] = useState(item.collapsed);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (searchText && searchText.length) {
|
||||||
|
setItemisCollapsed(false);
|
||||||
|
} else {
|
||||||
|
setItemisCollapsed(item.collapsed);
|
||||||
|
}
|
||||||
|
}, [searchText, item]);
|
||||||
|
|
||||||
const dropdownTippyRef = useRef();
|
const dropdownTippyRef = useRef();
|
||||||
const MenuIcon = forwardRef((props, ref) => {
|
const MenuIcon = forwardRef((props, ref) => {
|
||||||
@ -38,7 +48,7 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const iconClassName = classnames({
|
const iconClassName = classnames({
|
||||||
'rotate-90': item.collapsed
|
'rotate-90': !itemIsCollapsed
|
||||||
});
|
});
|
||||||
|
|
||||||
const itemRowClassName = classnames('flex collection-item-name items-center', {
|
const itemRowClassName = classnames('flex collection-item-name items-center', {
|
||||||
@ -73,6 +83,18 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
'is-dragging': isDragging
|
'is-dragging': isDragging
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(searchText && searchText.length) {
|
||||||
|
if(isItemARequest(item)) {
|
||||||
|
if(!doesRequestMatchSearchText(item, searchText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!doesFolderHaveItemsMatchSearchText(item, searchText)) {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className={className}>
|
<StyledWrapper className={className}>
|
||||||
{renameItemModalOpen && <RenameCollectionItem item={item} collection={collection} onClose={() => setRenameItemModalOpen(false)}/>}
|
{renameItemModalOpen && <RenameCollectionItem item={item} collection={collection} onClose={() => setRenameItemModalOpen(false)}/>}
|
||||||
@ -159,13 +181,14 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{item.collapsed ? (
|
{!itemIsCollapsed ? (
|
||||||
<div>
|
<div>
|
||||||
{item.items && item.items.length ? item.items.map((i) => {
|
{item.items && item.items.length ? item.items.map((i) => {
|
||||||
return <CollectionItem
|
return <CollectionItem
|
||||||
key={i.uid}
|
key={i.uid}
|
||||||
item={i}
|
item={i}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
|
searchText={searchText}
|
||||||
/>
|
/>
|
||||||
}) : null}
|
}) : null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, forwardRef, useRef } from 'react';
|
import React, { useState, forwardRef, useRef, useEffect } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { IconChevronRight, IconDots } from '@tabler/icons';
|
import { IconChevronRight, IconDots } from '@tabler/icons';
|
||||||
import Dropdown from 'components/Dropdown';
|
import Dropdown from 'components/Dropdown';
|
||||||
@ -7,14 +7,24 @@ import { useDispatch } from 'react-redux';
|
|||||||
import NewRequest from 'components/Sidebar/NewRequest';
|
import NewRequest from 'components/Sidebar/NewRequest';
|
||||||
import NewFolder from 'components/Sidebar/NewFolder';
|
import NewFolder from 'components/Sidebar/NewFolder';
|
||||||
import CollectionItem from './CollectionItem';
|
import CollectionItem from './CollectionItem';
|
||||||
|
import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search';
|
||||||
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
const Collection = ({collection}) => {
|
const Collection = ({collection, searchText}) => {
|
||||||
const [showNewFolderModal, setShowNewFolderModal] = useState(false);
|
const [showNewFolderModal, setShowNewFolderModal] = useState(false);
|
||||||
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
||||||
|
const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (searchText && searchText.length) {
|
||||||
|
setCollectionIsCollapsed(false);
|
||||||
|
} else {
|
||||||
|
setCollectionIsCollapsed(collection.collapsed);
|
||||||
|
}
|
||||||
|
}, [searchText, collection]);
|
||||||
|
|
||||||
const menuDropdownTippyRef = useRef();
|
const menuDropdownTippyRef = useRef();
|
||||||
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
|
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
|
||||||
const MenuIcon = forwardRef((props, ref) => {
|
const MenuIcon = forwardRef((props, ref) => {
|
||||||
@ -26,13 +36,19 @@ const Collection = ({collection}) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const iconClassName = classnames({
|
const iconClassName = classnames({
|
||||||
'rotate-90': !collection.collapsed
|
'rotate-90': !collectionIsCollapsed
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClick = (event) => {
|
const handleClick = (event) => {
|
||||||
dispatch(collectionClicked(collection.uid));
|
dispatch(collectionClicked(collection.uid));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(searchText && searchText.length) {
|
||||||
|
if(!doesCollectionHaveItemsMatchingSearchText(collection, searchText)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className="flex flex-col">
|
<StyledWrapper className="flex flex-col">
|
||||||
{showNewRequestModal && <NewRequest collection={collection} onClose={() => setShowNewRequestModal(false)}/>}
|
{showNewRequestModal && <NewRequest collection={collection} onClose={() => setShowNewRequestModal(false)}/>}
|
||||||
@ -71,13 +87,14 @@ const Collection = ({collection}) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{!collection.collapsed ? (
|
{!collectionIsCollapsed ? (
|
||||||
<div>
|
<div>
|
||||||
{collection.items && collection.items.length ? collection.items.map((i) => {
|
{collection.items && collection.items.length ? collection.items.map((i) => {
|
||||||
return <CollectionItem
|
return <CollectionItem
|
||||||
key={i.uid}
|
key={i.uid}
|
||||||
item={i}
|
item={i}
|
||||||
collection={collection}
|
collection={collection}
|
||||||
|
searchText={searchText}
|
||||||
/>
|
/>
|
||||||
}) : null}
|
}) : null}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import Collection from './Collection';
|
import Collection from './Collection';
|
||||||
|
|
||||||
const Collections = () => {
|
const Collections = ({searchText}) => {
|
||||||
const collections = useSelector((state) => state.collections.collections);
|
const collections = useSelector((state) => state.collections.collections);
|
||||||
console.log(collections);
|
console.log(collections);
|
||||||
|
|
||||||
@ -10,6 +10,7 @@ const Collections = () => {
|
|||||||
<div className="mt-4 flex flex-col">
|
<div className="mt-4 flex flex-col">
|
||||||
{collections && collections.length ? collections.map((c) => {
|
{collections && collections.length ? collections.map((c) => {
|
||||||
return <Collection
|
return <Collection
|
||||||
|
searchText={searchText}
|
||||||
collection={c}
|
collection={c}
|
||||||
key={c.uid}
|
key={c.uid}
|
||||||
/>
|
/>
|
||||||
|
@ -18,6 +18,7 @@ const Sidebar = () => {
|
|||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [dragging, setDragging] = useState(false);
|
const [dragging, setDragging] = useState(false);
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
|
||||||
const handleMouseMove = (e) => {
|
const handleMouseMove = (e) => {
|
||||||
if(dragging) {
|
if(dragging) {
|
||||||
@ -86,10 +87,11 @@ const Sidebar = () => {
|
|||||||
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
|
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
|
||||||
className="block w-full pl-7 py-1 sm:text-sm"
|
className="block w-full pl-7 py-1 sm:text-sm"
|
||||||
placeholder="search"
|
placeholder="search"
|
||||||
|
onChange={(e) => setSearchText(e.target.value.toLowerCase())}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Collections />
|
<Collections searchText={searchText}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex px-1 py-2 items-center cursor-pointer text-gray-500 select-none">
|
<div className="flex px-1 py-2 items-center cursor-pointer text-gray-500 select-none">
|
||||||
<div className="flex items-center ml-1 text-xs ">
|
<div className="flex items-center ml-1 text-xs ">
|
||||||
|
21
renderer/utils/collections/search.js
Normal file
21
renderer/utils/collections/search.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { flattenItems, isItemARequest } from "./index";
|
||||||
|
import filter from 'lodash/filter';
|
||||||
|
import find from 'lodash/find';
|
||||||
|
|
||||||
|
export const doesRequestMatchSearchText = (request, searchText) => {
|
||||||
|
return request.name.toLowerCase().includes(searchText.toLowerCase());
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doesFolderHaveItemsMatchSearchText = (item, searchText) => {
|
||||||
|
let flattenedItems = flattenItems(item.items);
|
||||||
|
let requestItems = filter(flattenedItems, (item) => isItemARequest(item));
|
||||||
|
|
||||||
|
return find(requestItems, (request) => doesRequestMatchSearchText(request, searchText));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doesCollectionHaveItemsMatchingSearchText = (collection, searchText) => {
|
||||||
|
let flattenedItems = flattenItems(collection.items);
|
||||||
|
let requestItems = filter(flattenedItems, (item) => isItemARequest(item));
|
||||||
|
|
||||||
|
return find(requestItems, (request) => doesRequestMatchSearchText(request, searchText));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user