2025-04-16 16:55:38 -07:00
|
|
|
import { LoadingOutlined, SearchOutlined } from '@ant-design/icons';
|
2025-04-02 12:16:50 -07:00
|
|
|
import { SearchBar } from '@components';
|
|
|
|
import React, { useState } from 'react';
|
|
|
|
import { useDebounce } from 'react-use';
|
2025-01-29 20:42:01 -05:00
|
|
|
import styled from 'styled-components/macro';
|
2025-04-16 16:55:38 -07:00
|
|
|
|
|
|
|
import DomainSearchResultItem from '@app/domainV2/DomainSearchResultItem';
|
|
|
|
import { REDESIGN_COLORS } from '@app/entityV2/shared/constants';
|
|
|
|
import ClickOutside from '@app/shared/ClickOutside';
|
|
|
|
import { useEntityRegistry } from '@app/useEntityRegistry';
|
|
|
|
|
|
|
|
import { useGetAutoCompleteResultsQuery } from '@graphql/search.generated';
|
|
|
|
import { EntityType } from '@types';
|
2025-01-29 20:42:01 -05:00
|
|
|
|
|
|
|
const DomainSearchWrapper = styled.div`
|
|
|
|
flex-shrink: 0;
|
|
|
|
position: relative;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const ResultsWrapper = styled.div`
|
|
|
|
background-color: white;
|
|
|
|
border-radius: 5px;
|
2025-04-16 16:55:38 -07:00
|
|
|
box-shadow:
|
|
|
|
0 3px 6px -4px rgb(0 0 0 / 12%),
|
|
|
|
0 6px 16px 0 rgb(0 0 0 / 8%),
|
|
|
|
0 9px 28px 8px rgb(0 0 0 / 5%);
|
2025-01-29 20:42:01 -05:00
|
|
|
padding: 8px;
|
|
|
|
position: absolute;
|
|
|
|
max-height: 210px;
|
|
|
|
overflow: auto;
|
2025-04-22 17:50:57 -04:00
|
|
|
width: calc(100% - 8px);
|
|
|
|
left: 4px;
|
2025-01-29 20:42:01 -05:00
|
|
|
top: 55px;
|
|
|
|
z-index: 1;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const LoadingWrapper = styled(ResultsWrapper)`
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
padding: 16px 0;
|
|
|
|
font-size: 16px;
|
|
|
|
`;
|
|
|
|
|
2025-04-02 12:16:50 -07:00
|
|
|
const InputWrapper = styled.div`
|
|
|
|
padding: 12px;
|
|
|
|
`;
|
|
|
|
|
2025-01-29 20:42:01 -05:00
|
|
|
const SearchIcon = styled(SearchOutlined)`
|
|
|
|
color: ${REDESIGN_COLORS.TEXT_HEADING_SUB_LINK};
|
|
|
|
padding: 16px;
|
|
|
|
width: 100%;
|
|
|
|
font-size: 20px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
isCollapsed?: boolean;
|
|
|
|
unhideSidebar?: () => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
function DomainSearch({ isCollapsed, unhideSidebar }: Props) {
|
2025-04-02 12:16:50 -07:00
|
|
|
const [searchInput, setSearchInput] = useState('');
|
2025-01-29 20:42:01 -05:00
|
|
|
const [query, setQuery] = useState('');
|
|
|
|
const [isSearchBarFocused, setIsSearchBarFocused] = useState(false);
|
|
|
|
const entityRegistry = useEntityRegistry();
|
2025-04-02 12:16:50 -07:00
|
|
|
|
|
|
|
useDebounce(() => setQuery(searchInput), 200, [searchInput]);
|
2025-01-29 20:42:01 -05:00
|
|
|
const { data, loading } = useGetAutoCompleteResultsQuery({
|
|
|
|
variables: {
|
|
|
|
input: {
|
|
|
|
type: EntityType.Domain,
|
|
|
|
query,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
skip: !query,
|
|
|
|
});
|
|
|
|
|
|
|
|
const entities = data?.autoComplete?.entities || [];
|
|
|
|
|
|
|
|
return (
|
|
|
|
<DomainSearchWrapper>
|
|
|
|
{isCollapsed && unhideSidebar ? (
|
|
|
|
<SearchIcon onClick={unhideSidebar} />
|
|
|
|
) : (
|
|
|
|
<ClickOutside onClickOutside={() => setIsSearchBarFocused(false)}>
|
2025-04-02 12:16:50 -07:00
|
|
|
<InputWrapper>
|
|
|
|
<SearchBar
|
|
|
|
placeholder="Search"
|
|
|
|
value={searchInput}
|
|
|
|
onChange={setSearchInput}
|
|
|
|
onFocus={() => setIsSearchBarFocused(true)}
|
|
|
|
/>
|
|
|
|
</InputWrapper>
|
2025-01-29 20:42:01 -05:00
|
|
|
{loading && isSearchBarFocused && (
|
|
|
|
<LoadingWrapper>
|
|
|
|
<LoadingOutlined />
|
|
|
|
</LoadingWrapper>
|
|
|
|
)}
|
|
|
|
{!loading && isSearchBarFocused && !!entities?.length && (
|
|
|
|
<ResultsWrapper>
|
|
|
|
{entities?.map((entity) => (
|
|
|
|
<DomainSearchResultItem
|
|
|
|
key={entity.urn}
|
|
|
|
entity={entity}
|
|
|
|
entityRegistry={entityRegistry}
|
|
|
|
query={query}
|
|
|
|
onResultClick={() => setIsSearchBarFocused(false)}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</ResultsWrapper>
|
|
|
|
)}
|
|
|
|
</ClickOutside>
|
|
|
|
)}
|
|
|
|
</DomainSearchWrapper>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default DomainSearch;
|